diff mbox series

[v1,3/3] contrib/elf2dmp: add PE name check and Windows Server 2022 support

Message ID 20221130000320.287976-4-viktor@daynix.com
State New
Headers show
Series contrib/elf2dmp: Windows Server 2022 support | expand

Commit Message

Viktor Prutyanov Nov. 30, 2022, 12:03 a.m. UTC
Since its inception elf2dmp has checked MZ signatures within an
address space above IDT[0] interrupt vector and took first PE image
found as Windows Kernel.
But in Windows Server 2022 memory dump this address space range is
full of invalid PE fragments and the tool must check that PE image
is 'ntoskrnl.exe' actually.
So, introduce additional validation by checking image name from
Export Directory against 'ntoskrnl.exe'.

Signed-off-by: Viktor Prutyanov <viktor@daynix.com>
Tested-by: Yuri Benditovich <yuri.benditovich@daynix.com>
---
 contrib/elf2dmp/main.c | 28 ++++++++++++++++++++++++++--
 contrib/elf2dmp/pe.h   | 15 +++++++++++++++
 2 files changed, 41 insertions(+), 2 deletions(-)

Comments

Annie.li Feb. 22, 2023, 7:07 p.m. UTC | #1
On 11/29/2022 7:03 PM, Viktor Prutyanov wrote:
> Since its inception elf2dmp has checked MZ signatures within an
> address space above IDT[0] interrupt vector and took first PE image
> found as Windows Kernel.
> But in Windows Server 2022 memory dump this address space range is
> full of invalid PE fragments and the tool must check that PE image
> is 'ntoskrnl.exe' actually.
> So, introduce additional validation by checking image name from
> Export Directory against 'ntoskrnl.exe'.
>
> Signed-off-by: Viktor Prutyanov <viktor@daynix.com>
> Tested-by: Yuri Benditovich <yuri.benditovich@daynix.com>
> ---
>   contrib/elf2dmp/main.c | 28 ++++++++++++++++++++++++++--
>   contrib/elf2dmp/pe.h   | 15 +++++++++++++++
>   2 files changed, 41 insertions(+), 2 deletions(-)
>
> diff --git a/contrib/elf2dmp/main.c b/contrib/elf2dmp/main.c
> index f3052b3c64..f7de82a03e 100644
> --- a/contrib/elf2dmp/main.c
> +++ b/contrib/elf2dmp/main.c
> @@ -17,6 +17,7 @@
>   
>   #define SYM_URL_BASE    "https://msdl.microsoft.com/download/symbols/"
>   #define PDB_NAME    "ntkrnlmp.pdb"
> +#define PE_NAME     "ntoskrnl.exe"

As what has been clarified earlier in the meeting, this elf2dmp is only for
64bits systems, so the name "ntoskrnl.exe" suffices here. Otherwise, it 
won't work
for 32bits PAE systems.

A question about elf2dmp on ARM platform, has it been validated there?

Thanks

Annie

>   
>   #define INITIAL_MXCSR   0x1f80
>   
> @@ -400,6 +401,25 @@ static int write_dump(struct pa_space *ps,
>       return fclose(dmp_file);
>   }
>   
> +static bool pe_check_export_name(uint64_t base, void *start_addr,
> +        struct va_space *vs)
> +{
> +    IMAGE_EXPORT_DIRECTORY export_dir;
> +    const char *pe_name;
> +
> +    if (pe_get_data_dir_entry(base, start_addr, IMAGE_FILE_EXPORT_DIRECTORY,
> +                &export_dir, sizeof(export_dir), vs)) {
> +        return false;
> +    }
> +
> +    pe_name = va_space_resolve(vs, base + export_dir.Name);
> +    if (!pe_name) {
> +        return false;
> +    }
> +
> +    return !strcmp(pe_name, PE_NAME);
> +}
> +
>   static int pe_get_pdb_symstore_hash(uint64_t base, void *start_addr,
>           char *hash, struct va_space *vs)
>   {
> @@ -484,6 +504,7 @@ int main(int argc, char *argv[])
>       uint64_t KdDebuggerDataBlock;
>       KDDEBUGGER_DATA64 *kdbg;
>       uint64_t KdVersionBlock;
> +    bool kernel_found = false;
>   
>       if (argc != 3) {
>           eprintf("usage:\n\t%s elf_file dmp_file\n", argv[0]);
> @@ -531,11 +552,14 @@ int main(int argc, char *argv[])
>           }
>   
>           if (*(uint16_t *)nt_start_addr == 0x5a4d) { /* MZ */
> -            break;
> +            if (pe_check_export_name(KernBase, nt_start_addr, &vs)) {
> +                kernel_found = true;
> +                break;
> +            }
>           }
>       }
>   
> -    if (!nt_start_addr) {
> +    if (!kernel_found) {
>           eprintf("Failed to find NT kernel image\n");
>           err = 1;
>           goto out_ps;
> diff --git a/contrib/elf2dmp/pe.h b/contrib/elf2dmp/pe.h
> index 807d006364..71126af1ac 100644
> --- a/contrib/elf2dmp/pe.h
> +++ b/contrib/elf2dmp/pe.h
> @@ -88,6 +88,20 @@ typedef struct IMAGE_NT_HEADERS64 {
>       IMAGE_OPTIONAL_HEADER64 OptionalHeader;
>   } __attribute__ ((packed)) IMAGE_NT_HEADERS64;
>   
> +typedef struct IMAGE_EXPORT_DIRECTORY {
> +    uint32_t    Characteristics;
> +    uint32_t    TimeDateStamp;
> +    uint16_t    MajorVersion;
> +    uint16_t    MinorVersion;
> +    uint32_t    Name;
> +    uint32_t    Base;
> +    uint32_t    NumberOfFunctions;
> +    uint32_t    NumberOfNames;
> +    uint32_t    AddressOfFunctions;
> +    uint32_t    AddressOfNames;
> +    uint32_t    AddressOfNameOrdinals;
> +} __attribute__ ((packed)) IMAGE_EXPORT_DIRECTORY;
> +
>   typedef struct IMAGE_DEBUG_DIRECTORY {
>       uint32_t Characteristics;
>       uint32_t TimeDateStamp;
> @@ -102,6 +116,7 @@ typedef struct IMAGE_DEBUG_DIRECTORY {
>   #define IMAGE_DEBUG_TYPE_CODEVIEW   2
>   #endif
>   
> +#define IMAGE_FILE_EXPORT_DIRECTORY 0
>   #define IMAGE_FILE_DEBUG_DIRECTORY  6
>   
>   typedef struct guid_t {
Viktor Prutyanov Feb. 22, 2023, 7:55 p.m. UTC | #2
On Wed, Feb 22, 2023 at 10:07 PM Annie.li <annie.li@oracle.com> wrote:
>
>
> On 11/29/2022 7:03 PM, Viktor Prutyanov wrote:
> > Since its inception elf2dmp has checked MZ signatures within an
> > address space above IDT[0] interrupt vector and took first PE image
> > found as Windows Kernel.
> > But in Windows Server 2022 memory dump this address space range is
> > full of invalid PE fragments and the tool must check that PE image
> > is 'ntoskrnl.exe' actually.
> > So, introduce additional validation by checking image name from
> > Export Directory against 'ntoskrnl.exe'.
> >
> > Signed-off-by: Viktor Prutyanov <viktor@daynix.com>
> > Tested-by: Yuri Benditovich <yuri.benditovich@daynix.com>
> > ---
> >   contrib/elf2dmp/main.c | 28 ++++++++++++++++++++++++++--
> >   contrib/elf2dmp/pe.h   | 15 +++++++++++++++
> >   2 files changed, 41 insertions(+), 2 deletions(-)
> >
> > diff --git a/contrib/elf2dmp/main.c b/contrib/elf2dmp/main.c
> > index f3052b3c64..f7de82a03e 100644
> > --- a/contrib/elf2dmp/main.c
> > +++ b/contrib/elf2dmp/main.c
> > @@ -17,6 +17,7 @@
> >
> >   #define SYM_URL_BASE    "https://msdl.microsoft.com/download/symbols/"
> >   #define PDB_NAME    "ntkrnlmp.pdb"
> > +#define PE_NAME     "ntoskrnl.exe"
>
> As what has been clarified earlier in the meeting, this elf2dmp is only for
> 64bits systems, so the name "ntoskrnl.exe" suffices here. Otherwise, it
> won't work
> for 32bits PAE systems.
>
> A question about elf2dmp on ARM platform, has it been validated there?
>
> Thanks
>
> Annie
>
> >
> >   #define INITIAL_MXCSR   0x1f80
> >
> > @@ -400,6 +401,25 @@ static int write_dump(struct pa_space *ps,
> >       return fclose(dmp_file);
> >   }
> >
> > +static bool pe_check_export_name(uint64_t base, void *start_addr,
> > +        struct va_space *vs)
> > +{
> > +    IMAGE_EXPORT_DIRECTORY export_dir;
> > +    const char *pe_name;
> > +
> > +    if (pe_get_data_dir_entry(base, start_addr, IMAGE_FILE_EXPORT_DIRECTORY,
> > +                &export_dir, sizeof(export_dir), vs)) {
> > +        return false;
> > +    }
> > +
> > +    pe_name = va_space_resolve(vs, base + export_dir.Name);
> > +    if (!pe_name) {
> > +        return false;
> > +    }
> > +
> > +    return !strcmp(pe_name, PE_NAME);
> > +}
> > +
> >   static int pe_get_pdb_symstore_hash(uint64_t base, void *start_addr,
> >           char *hash, struct va_space *vs)
> >   {
> > @@ -484,6 +504,7 @@ int main(int argc, char *argv[])
> >       uint64_t KdDebuggerDataBlock;
> >       KDDEBUGGER_DATA64 *kdbg;
> >       uint64_t KdVersionBlock;
> > +    bool kernel_found = false;
> >
> >       if (argc != 3) {
> >           eprintf("usage:\n\t%s elf_file dmp_file\n", argv[0]);
> > @@ -531,11 +552,14 @@ int main(int argc, char *argv[])
> >           }
> >
> >           if (*(uint16_t *)nt_start_addr == 0x5a4d) { /* MZ */
> > -            break;
> > +            if (pe_check_export_name(KernBase, nt_start_addr, &vs)) {
> > +                kernel_found = true;
> > +                break;
> > +            }
> >           }
> >       }
> >
> > -    if (!nt_start_addr) {
> > +    if (!kernel_found) {
> >           eprintf("Failed to find NT kernel image\n");
> >           err = 1;
> >           goto out_ps;
> > diff --git a/contrib/elf2dmp/pe.h b/contrib/elf2dmp/pe.h
> > index 807d006364..71126af1ac 100644
> > --- a/contrib/elf2dmp/pe.h
> > +++ b/contrib/elf2dmp/pe.h
> > @@ -88,6 +88,20 @@ typedef struct IMAGE_NT_HEADERS64 {
> >       IMAGE_OPTIONAL_HEADER64 OptionalHeader;
> >   } __attribute__ ((packed)) IMAGE_NT_HEADERS64;
> >
> > +typedef struct IMAGE_EXPORT_DIRECTORY {
> > +    uint32_t    Characteristics;
> > +    uint32_t    TimeDateStamp;
> > +    uint16_t    MajorVersion;
> > +    uint16_t    MinorVersion;
> > +    uint32_t    Name;
> > +    uint32_t    Base;
> > +    uint32_t    NumberOfFunctions;
> > +    uint32_t    NumberOfNames;
> > +    uint32_t    AddressOfFunctions;
> > +    uint32_t    AddressOfNames;
> > +    uint32_t    AddressOfNameOrdinals;
> > +} __attribute__ ((packed)) IMAGE_EXPORT_DIRECTORY;
> > +
> >   typedef struct IMAGE_DEBUG_DIRECTORY {
> >       uint32_t Characteristics;
> >       uint32_t TimeDateStamp;
> > @@ -102,6 +116,7 @@ typedef struct IMAGE_DEBUG_DIRECTORY {
> >   #define IMAGE_DEBUG_TYPE_CODEVIEW   2
> >   #endif
> >
> > +#define IMAGE_FILE_EXPORT_DIRECTORY 0
> >   #define IMAGE_FILE_DEBUG_DIRECTORY  6
> >
> >   typedef struct guid_t {

Hi Annie,

Thank you for the review!
At the moment, elf2dmp only addresses the x86_64 platform.

Best regards,
Viktor Prutyanov
diff mbox series

Patch

diff --git a/contrib/elf2dmp/main.c b/contrib/elf2dmp/main.c
index f3052b3c64..f7de82a03e 100644
--- a/contrib/elf2dmp/main.c
+++ b/contrib/elf2dmp/main.c
@@ -17,6 +17,7 @@ 
 
 #define SYM_URL_BASE    "https://msdl.microsoft.com/download/symbols/"
 #define PDB_NAME    "ntkrnlmp.pdb"
+#define PE_NAME     "ntoskrnl.exe"
 
 #define INITIAL_MXCSR   0x1f80
 
@@ -400,6 +401,25 @@  static int write_dump(struct pa_space *ps,
     return fclose(dmp_file);
 }
 
+static bool pe_check_export_name(uint64_t base, void *start_addr,
+        struct va_space *vs)
+{
+    IMAGE_EXPORT_DIRECTORY export_dir;
+    const char *pe_name;
+
+    if (pe_get_data_dir_entry(base, start_addr, IMAGE_FILE_EXPORT_DIRECTORY,
+                &export_dir, sizeof(export_dir), vs)) {
+        return false;
+    }
+
+    pe_name = va_space_resolve(vs, base + export_dir.Name);
+    if (!pe_name) {
+        return false;
+    }
+
+    return !strcmp(pe_name, PE_NAME);
+}
+
 static int pe_get_pdb_symstore_hash(uint64_t base, void *start_addr,
         char *hash, struct va_space *vs)
 {
@@ -484,6 +504,7 @@  int main(int argc, char *argv[])
     uint64_t KdDebuggerDataBlock;
     KDDEBUGGER_DATA64 *kdbg;
     uint64_t KdVersionBlock;
+    bool kernel_found = false;
 
     if (argc != 3) {
         eprintf("usage:\n\t%s elf_file dmp_file\n", argv[0]);
@@ -531,11 +552,14 @@  int main(int argc, char *argv[])
         }
 
         if (*(uint16_t *)nt_start_addr == 0x5a4d) { /* MZ */
-            break;
+            if (pe_check_export_name(KernBase, nt_start_addr, &vs)) {
+                kernel_found = true;
+                break;
+            }
         }
     }
 
-    if (!nt_start_addr) {
+    if (!kernel_found) {
         eprintf("Failed to find NT kernel image\n");
         err = 1;
         goto out_ps;
diff --git a/contrib/elf2dmp/pe.h b/contrib/elf2dmp/pe.h
index 807d006364..71126af1ac 100644
--- a/contrib/elf2dmp/pe.h
+++ b/contrib/elf2dmp/pe.h
@@ -88,6 +88,20 @@  typedef struct IMAGE_NT_HEADERS64 {
     IMAGE_OPTIONAL_HEADER64 OptionalHeader;
 } __attribute__ ((packed)) IMAGE_NT_HEADERS64;
 
+typedef struct IMAGE_EXPORT_DIRECTORY {
+    uint32_t    Characteristics;
+    uint32_t    TimeDateStamp;
+    uint16_t    MajorVersion;
+    uint16_t    MinorVersion;
+    uint32_t    Name;
+    uint32_t    Base;
+    uint32_t    NumberOfFunctions;
+    uint32_t    NumberOfNames;
+    uint32_t    AddressOfFunctions;
+    uint32_t    AddressOfNames;
+    uint32_t    AddressOfNameOrdinals;
+} __attribute__ ((packed)) IMAGE_EXPORT_DIRECTORY;
+
 typedef struct IMAGE_DEBUG_DIRECTORY {
     uint32_t Characteristics;
     uint32_t TimeDateStamp;
@@ -102,6 +116,7 @@  typedef struct IMAGE_DEBUG_DIRECTORY {
 #define IMAGE_DEBUG_TYPE_CODEVIEW   2
 #endif
 
+#define IMAGE_FILE_EXPORT_DIRECTORY 0
 #define IMAGE_FILE_DEBUG_DIRECTORY  6
 
 typedef struct guid_t {