Message ID | 20191114045543.6759-1-julian.tuminaro@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | [V2] kdd.c: Add support for initial handshake in KD protocol for Win 7, 8 and 10 (64 bit) | expand |
Hi, At 23:55 -0500 on 13 Nov (1573689341), Julian Tuminaro wrote: > From: Julian Tuminaro and Jenish Rakholiya <julian.tuminaro@gmail.com and rakholiyajenish.07@gmail.com> > > Current implementation of find_os is based on the hard-coded values for > different Windows version. It uses the value for get the address to > start looking for DOS header in the given specified range. However, this > is not scalable to all version of Windows as it will require us to keep > adding new entries and also due to KASLR, chances of not hitting the PE > header is significant. We implement a way for 64-bit systems to use IDT > entry to get a valid exception/interrupt handler and then move back into > the memory to find the valid DOS header. Since IDT entries are protected > by PatchGuard, we think our assumption that IDT entries will not be > corrupted is valid for our purpose. Once we have the image base, we > search for the DBGKD_GET_VERSION64 structure type in .data section to > get information required for handshake. Thanks for the updates, this looks good! Reviewed-by: Tim Deegan <tim@xen.org>
On Thu, 14 Nov 2019 at 04:57, Julian Tuminaro <julian.tuminaro@gmail.com> wrote: > > From: Julian Tuminaro and Jenish Rakholiya <julian.tuminaro@gmail.com and rakholiyajenish.07@gmail.com> > > Current implementation of find_os is based on the hard-coded values for > different Windows version. It uses the value for get the address to > start looking for DOS header in the given specified range. However, this > is not scalable to all version of Windows as it will require us to keep > adding new entries and also due to KASLR, chances of not hitting the PE > header is significant. We implement a way for 64-bit systems to use IDT > entry to get a valid exception/interrupt handler and then move back into > the memory to find the valid DOS header. Since IDT entries are protected > by PatchGuard, we think our assumption that IDT entries will not be > corrupted is valid for our purpose. Once we have the image base, we > search for the DBGKD_GET_VERSION64 structure type in .data section to > get information required for handshake. > > Currently, this is a work in progress feature and current patch only > supports the handshake and memory read/write on 64-bit systems. > > NOTE: This is the Updated version of the previous patch submitted > NOTE: This has currently been only tested when debugging was not enabled > on the guest Windows. > > Signed-off-by: Jenish Rakholiya <rjenish@cmu.edu> > Signed-off-by: Julian Tuminaro <jtuminar@andrew.cmu.edu> LGTM. Reviewed-by: Paul Durrant <paul@xen.org> > --- > tools/debugger/kdd/kdd.c | 392 ++++++++++++++++++++++++++++++++++++--- > 1 file changed, 366 insertions(+), 26 deletions(-) > > diff --git a/tools/debugger/kdd/kdd.c b/tools/debugger/kdd/kdd.c > index fb8c645355..6d3febefda 100644 > --- a/tools/debugger/kdd/kdd.c > +++ b/tools/debugger/kdd/kdd.c > @@ -41,6 +41,7 @@ > #include <errno.h> > #include <inttypes.h> > #include <netdb.h> > +#include <stddef.h> > > #include <sys/socket.h> > #include <sys/types.h> > @@ -51,6 +52,16 @@ > > #include "kdd.h" > > +/* > + * TODO: kdd_os is a type which is used to represent os array. Adding a > + * variable here would result in adding a new field to each element in array. > + * However, since most of the fields are part of the same struct that we are > + * trying to read from memory, we have added kddl to this structure. If > + * required, we can possibly separate the kddl value to someplace else > + * > + * We also use kddl of size uint32_t which is actually used to represent the > + * offset from image base rather than actual address > + */ > /* Windows version details */ > typedef struct { > uint32_t build; > @@ -62,6 +73,7 @@ typedef struct { > uint32_t version; /* +-> NtBuildNumber */ > uint32_t modules; /* +-> PsLoadedModuleList */ > uint32_t prcbs; /* +-> KiProcessorBlock */ > + uint32_t kddl; /* +-> KdDebuggerList */ > } kdd_os; > > /* State of the debugger stub */ > @@ -85,6 +97,117 @@ typedef struct { > kdd_os os; /* OS-specific magic numbers */ > } kdd_state; > > +/** > + * @brief Structure to represent DBGKD_GET_VERSION64 > + * > + * reference: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdbgexts/ns-wdbgexts-_dbgkd_get_version64 > + */ > +typedef struct { > + uint16_t MajorVersion; /* usually 0xf for free build */ > + uint16_t MinorVersion; /* build number of target OS */ > + uint8_t ProtocolVersion; /* version of the debugger protocol */ > + uint8_t KdSecondaryVersion; /* secondary version number */ > + uint16_t Flags; /* set of bit flags for the current debugging session */ > + uint16_t MachineType; /* type of the target's processor */ > + uint8_t MaxPacketType; /* one plus the highest number for a debugger */ > + /* packet type recognized by the target */ > + uint8_t MaxStateChagne; /* one plus the highest number for a state */ > + /* change generated by the target */ > + uint8_t MaxManipulate; /* one more that the highest number, recognized */ > + /* by the target, for a command to manipulate the target */ > + uint8_t Simulation; /* indication if target is in simulated execution */ > + uint16_t Unused[1]; > + uint64_t KernBase; /* base address of the kernel image */ > + uint64_t PsLoadedModuleList; /* value of the kernel variable */ > + /* PsLoadedModuleList */ > + uint64_t DebuggerDataList; /* value of the kernel variable */ > + /* KdDebuggerDataBlock */ > +} PACKED DBGKD_GET_VERSION64; > + > +/** > + * @brief Structure to represent the section in PE headers > + * > + * reference: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#section-table-section-headers > + */ > +typedef struct { > + uint8_t Name[8]; /* name of section */ > + uint32_t VirtualSize; /* total size of section in memory */ > + uint32_t VirtualAddr; /* offset from image base */ > + uint32_t SizeOfRawData; /* size of section in for object files */ > + uint32_t PointerToRawData; /* file pointer to first page in COFF */ > + uint32_t PointerToRelocations; /* file pointer to beginning of relocation entry */ > + uint32_t PointerToLinenumbers; /* file pointer to the beginning of line-number entries */ > + uint16_t NumberOfRelocations; /* number of relocation entries for the section */ > + uint16_t NumberOfLinenumbers; /* number of line-number entries for the section */ > + uint32_t Characteristics; /* flags that describe the characteristics of the section */ > +} PACKED PE_SECTION_ENTRY; > + > +/** > + * @brief Size of pointer on 64 machine > + */ > +#define SIZE_PTR64 8 > + > +/** > + * @brief Size of pointer on 32 machine > + */ > +#define SIZE_PTR32 4 > + > + > +/***************************************************************************** > + * PE and DOS Header related offsets > + */ > + > +/** > + * @brief Offset in DOS header to look for PE header > + */ > +#define DOS_HDR_PE_OFF 0x3c > + > +/** > + * @brief Size of PE header offset field in DOS header > + */ > +#define DOS_HDR_PE_SZ 4 > + > +/** > + * @brief Offset of number of sections field in PE header > + */ > +#define PE_NUM_SECTION_OFF 0x6 > + > +/** > + * @brief Size of number of sections field in PE header > + */ > +#define PE_NUM_SECTION_SZ 2 > + > +/** > + * @brief Offset of optional header size field in PE header > + */ > +#define PE_OPT_HDR_SZ_OFF 0x14 > + > +/** > + * @brief Size of optional header size field in PE header > + */ > +#define PE_OPT_HDR_SZ_SZ 2 > + > +/** > + * @brief Size of PE header > + */ > +#define PE_HDR_SZ 0x18 > + > +/** > + * @brief MZ header > + */ > +#define MZ_HEADER 0x5a4d > + > +/** > + * @brief Limit on the number of sections to look for while iterating through > + * PE sections > + */ > +#define NUM_SECT_LIMIT 100 > + > +/** > + * @brief Major Version for the DBGKD_GET_VERSION64 structure > + */ > +#define NT_MAJOR_VERSION 0xf > + > /***************************************************************************** > * Utility functions > */ > @@ -293,41 +416,41 @@ static uint32_t kdd_write_virtual(kdd_state *s, int cpuid, uint64_t addr, > */ > > static kdd_os os[] = { > - /* Build 64 MP Name &Kernel search base Range +Version +Modules +PRCBs (64b) */ > - {2195, 0, 0, "w2k sp4 x32 UP", 0xffffffff80400000ULL, 0x00000000, 0x0006d57c, 0x0006e1b8, 0x0}, > - {2195, 0, 1, "w2k sp4 x32 SMP", 0xffffffff80400000ULL, 0x00000000, 0x0006fa1c, 0x00084520, 0x0}, > + /* Build 64 MP Name &Kernel search base Range +Version +Modules +PRCBs (64b) +KDDL */ > + {2195, 0, 0, "w2k sp4 x32 UP", 0xffffffff80400000ULL, 0x00000000, 0x0006d57c, 0x0006e1b8, 0x0, 0}, > + {2195, 0, 1, "w2k sp4 x32 SMP", 0xffffffff80400000ULL, 0x00000000, 0x0006fa1c, 0x00084520, 0x0, 0}, > // PAE/UP, PAE/SMP > > - {2600, 0, 0, "xp sp2 x32 UP", 0xffffffff804d7000ULL, 0x00000000, 0x00075568, 0x00083b20, 0x0}, > - {2600, 0, 1, "xp sp2 x32 SMP", 0xffffffff804d7000ULL, 0x00000000, 0x0007d0e8, 0x0008d4a0, 0x0}, > + {2600, 0, 0, "xp sp2 x32 UP", 0xffffffff804d7000ULL, 0x00000000, 0x00075568, 0x00083b20, 0x0, 0}, > + {2600, 0, 1, "xp sp2 x32 SMP", 0xffffffff804d7000ULL, 0x00000000, 0x0007d0e8, 0x0008d4a0, 0x0, 0}, > // PAE/UP, PAE/SMP > > - {2600, 0, 0, "xp sp3 x32 UP", 0xffffffff804d7000ULL, 0x00000000, 0x00075be8, 0x000841c0, 0x0}, > - {2600, 0, 1, "xp sp3 x32 SMP", 0xffffffff804d7000ULL, 0x00000000, 0x0007c0e8, 0x0008c4c0, 0x0}, > - {2600, 0, 0, "xp sp3 x32p UP", 0xffffffff804d7000ULL, 0x00000000, 0x0006e8e8, 0x0007cfc0, 0x0}, > - {2600, 0, 1, "xp sp3 x32p SMP", 0xffffffff804d7000ULL, 0x00000000, 0x000760e8, 0x00086720, 0x0}, > + {2600, 0, 0, "xp sp3 x32 UP", 0xffffffff804d7000ULL, 0x00000000, 0x00075be8, 0x000841c0, 0x0, 0}, > + {2600, 0, 1, "xp sp3 x32 SMP", 0xffffffff804d7000ULL, 0x00000000, 0x0007c0e8, 0x0008c4c0, 0x0, 0}, > + {2600, 0, 0, "xp sp3 x32p UP", 0xffffffff804d7000ULL, 0x00000000, 0x0006e8e8, 0x0007cfc0, 0x0, 0}, > + {2600, 0, 1, "xp sp3 x32p SMP", 0xffffffff804d7000ULL, 0x00000000, 0x000760e8, 0x00086720, 0x0, 0}, > > - {3790, 0, 0, "w2k3 sp2 x32 UP", 0xffffffff80800000ULL, 0x00000000, 0x00097128, 0x000a8e48, 0x0}, > - {3790, 0, 1, "w2k3 sp2 x32 SMP", 0xffffffff80800000ULL, 0x00000000, 0x0009d128, 0x000af9c8, 0x0}, > - {3790, 0, 0, "w2k3 sp2 x32p UP", 0xffffffff80800000ULL, 0x00000000, 0x0008e128, 0x0009ffa8, 0x0}, > - {3790, 0, 1, "w2k3 sp2 x32p SMP", 0xffffffff80800000ULL, 0x00000000, 0x00094128, 0x000a6ea8, 0x0}, > - {3790, 1, 0, "w2k3 sp2 x64 UP", 0xfffff80001000000ULL, 0x00000000, 0x001765d0, 0x0019aae0, 0x0017b100}, > - {3790, 1, 1, "w2k3 sp2 x64 SMP", 0xfffff80001000000ULL, 0x00000000, 0x001b05e0, 0x001d5100, 0x001b5300}, > + {3790, 0, 0, "w2k3 sp2 x32 UP", 0xffffffff80800000ULL, 0x00000000, 0x00097128, 0x000a8e48, 0x0, 0}, > + {3790, 0, 1, "w2k3 sp2 x32 SMP", 0xffffffff80800000ULL, 0x00000000, 0x0009d128, 0x000af9c8, 0x0, 0}, > + {3790, 0, 0, "w2k3 sp2 x32p UP", 0xffffffff80800000ULL, 0x00000000, 0x0008e128, 0x0009ffa8, 0x0, 0}, > + {3790, 0, 1, "w2k3 sp2 x32p SMP", 0xffffffff80800000ULL, 0x00000000, 0x00094128, 0x000a6ea8, 0x0, 0}, > + {3790, 1, 0, "w2k3 sp2 x64 UP", 0xfffff80001000000ULL, 0x00000000, 0x001765d0, 0x0019aae0, 0x0017b100, 0}, > + {3790, 1, 1, "w2k3 sp2 x64 SMP", 0xfffff80001000000ULL, 0x00000000, 0x001b05e0, 0x001d5100, 0x001b5300, 0}, > > - {6000, 0, 1, "vista sp0 x32p", 0xffffffff81800000ULL, 0x00000000, 0x000a4de4, 0x00111db0, 0x0}, > - {6001, 0, 1, "vista sp1 x32p", 0xffffffff81000000ULL, 0x0f000000, 0x000af0c4, 0x00117c70, 0x0}, > + {6000, 0, 1, "vista sp0 x32p", 0xffffffff81800000ULL, 0x00000000, 0x000a4de4, 0x00111db0, 0x0, 0}, > + {6001, 0, 1, "vista sp1 x32p", 0xffffffff81000000ULL, 0x0f000000, 0x000af0c4, 0x00117c70, 0x0, 0}, > > - {6001, 1, 1, "w2k8 sp0 x64", 0xfffff80001000000ULL, 0x0f000000, 0x00140bf0, 0x001c5db0, 0x00229640}, > + {6001, 1, 1, "w2k8 sp0 x64", 0xfffff80001000000ULL, 0x0f000000, 0x00140bf0, 0x001c5db0, 0x00229640, 0}, > > - {7600, 1, 1, "win7 sp0 x64", 0xfffff80001000000ULL, 0x0f000000, 0x001af770, 0x0023de50, 0x002a8900}, > + {7600, 1, 1, "win7 sp0 x64", 0xfffff80001000000ULL, 0x0f000000, 0x001af770, 0x0023de50, 0x002a8900, 0}, > > - {7601, 0, 1, "win7 sp1 x32p", 0xffffffff81800000ULL, 0x0f000000, 0x000524c4, 0x00149850, 0x0}, > - {7601, 1, 1, "win7 sp1 x64", 0xfffff80001000000ULL, 0x0f000000, 0x001b2770, 0x00240e90, 0x002ab900}, > + {7601, 0, 1, "win7 sp1 x32p", 0xffffffff81800000ULL, 0x0f000000, 0x000524c4, 0x00149850, 0x0, 0}, > + {7601, 1, 1, "win7 sp1 x64", 0xfffff80001000000ULL, 0x0f000000, 0x001b2770, 0x00240e90, 0x002ab900, 0}, > }; > > // 1381, 0, 0, "NT4 sp?", 0xffffffff80100000, ?, ? > > -static kdd_os unknown_os = {0, 0, 0, "unknown OS", 0, 0, 0, 0, 0}; > +static kdd_os unknown_os = {0, 0, 0, "unknown OS", 0, 0, 0, 0, 0, 0}; > > static int check_os(kdd_state *s) > { > @@ -367,11 +490,226 @@ static int check_os(kdd_state *s) > return 1; > } > > +/** > + * @brief Parse the memory at \a filebase as a valid DOS header and get virtual > + * address offset and size for any given section name (if it exists) > + * > + * @param s Pointer to the kdd_state structure > + * @param filebase Base address of the file structure > + * @param sectname Pointer to the section name c-string to look for > + * @param vaddr Pointer to write the virtual address of section start to > + * (if found) > + * @param visze Pointer to write the section size to (if found) > + * > + * @return -1 on failure to find the section name > + * @return 0 on success > + */ > +static int get_pe64_sections(kdd_state *s, uint64_t filebase, char *sectname, > + uint64_t *vaddr, uint32_t *vsize) > +{ > + uint64_t pe_hdr = 0; > + uint64_t sect_start = 0; > + uint16_t num_sections = 0; > + uint16_t opt_hdr_sz = 0; > + PE_SECTION_ENTRY pe_sect; > + > + if (!s->os.w64) > + return -1; > + > + /* read PE header offset */ > + if (kdd_read_virtual(s, s->cpuid, filebase + DOS_HDR_PE_OFF, DOS_HDR_PE_SZ, > + &pe_hdr) != DOS_HDR_PE_SZ) > + return -1; > + > + pe_hdr += filebase; > + > + /* read number of sections */ > + if (kdd_read_virtual(s, s->cpuid, pe_hdr + PE_NUM_SECTION_OFF, > + PE_NUM_SECTION_SZ, &num_sections) != PE_NUM_SECTION_SZ) > + return -1; > + > + /* read number of section upto a limit */ > + if (num_sections > NUM_SECT_LIMIT) > + num_sections = NUM_SECT_LIMIT; > + > + /* read size of optional header */ > + if (kdd_read_virtual(s, s->cpuid, pe_hdr + PE_OPT_HDR_SZ_OFF, > + PE_OPT_HDR_SZ_SZ, &opt_hdr_sz) != PE_OPT_HDR_SZ_SZ) > + return -1; > + > + /* 0x18 is the size of PE header */ > + sect_start = pe_hdr + PE_HDR_SZ + opt_hdr_sz; > + > + for (int i = 0; i < num_sections; i++) { > + if (kdd_read_virtual(s, s->cpuid, sect_start + (i * sizeof(pe_sect)), > + sizeof(pe_sect), &pe_sect) != sizeof(pe_sect)) > + return -1; > + > + if (!strncmp(sectname, (char *)pe_sect.Name, sizeof(pe_sect.Name))) { > + *vaddr = filebase + pe_sect.VirtualAddr; > + *vsize = pe_sect.VirtualSize; > + return 0; > + } > + } > + > + return -1; > +} > + > +/** > + * @brief Get the OS information like base address, minor version, > + * PsLoadedModuleList and DebuggerDataList (basically the fields of > + * DBGKD_GET_VERSION64 struture required to do handshake?). > + * > + * This is done by reading the IDT entry for divide-by-zero exception and > + * searching back into the memory for DOS header (which is our kernel base). > + * Once we have the kernel base, we parse the PE header and look for kernel > + * base address in the .data section. Once we have possible values, we look for > + * DBGKD_GET_VERSION64 block by using following heuristics on the address which > + * has the kernel base: > + * > + * - at address [-0x10], it should have 0xf as the MajorVersion > + * - at address [+0x8], it should have a valid kernel memory address pointing > + * in .data > + * - at address [+0x10], it should have a valid kernel memory address pointing > + * in .data > + * > + * @param s Pointer to the kdd state > + */ > +static void get_os_info_64(kdd_state *s) > +{ > + kdd_ctrl ctrl; > + int ret; > + uint64_t buf; > + uint64_t idt0_addr; > + uint64_t base; > + uint64_t caddr; > + uint64_t data_base; > + uint32_t data_size; > + uint64_t modptr = 0; > + uint64_t kddl = 0; > + uint16_t minor = 0; > + uint64_t dbgkd_addr; > + DBGKD_GET_VERSION64 dbgkd_get_version64; > + /* Maybe 1GB is too big for the limit to search? */ > + uint32_t search_limit = (1024 * 1024 * 1024) / PAGE_SIZE; /*1GB/PageSize*/ > + uint64_t efer; > + > + /* if we are not in 64-bit mode, fail */ > + if (kdd_rdmsr(s->guest, s->cpuid, 0xc0000080, &efer) || !(efer & (1 << 8))) > + goto fail; > + > + s->os.w64 = 1; > + > + /* get control registers for our os */ > + ret = kdd_get_ctrl(s->guest, s->cpuid, &ctrl, s->os.w64); > + if (ret) > + goto fail; > + > + /* read the div-by-zero handler function address */ > + kdd_read_virtual(s, s->cpuid, ctrl.c64.idt_base + 8, 8, &buf); > + idt0_addr = ((uint64_t)buf << 32) & 0xffffffff00000000; > + > + kdd_read_virtual(s, s->cpuid, ctrl.c64.idt_base, 8, &buf); > + idt0_addr |= ((buf >> 32) & 0xffff0000); > + idt0_addr |= (buf & 0xffff); > + > + KDD_LOG(s, "idt0 addr: %p\n", (void *)idt0_addr); > + > + /* > + * get the page start and look for "MZ" file header - we limit the search > + * in 1GB range above the current page base address > + */ > + > + base = idt0_addr & ~(PAGE_SIZE - 1); > + KDD_LOG(s, "%p\n", (void *)base); > + > + while (search_limit) { > + uint16_t val; > + if (kdd_read_virtual(s, s->cpuid, base, 2, &val) != 2) { > + /* just move going back?? this is bad though */ > + KDD_LOG(s, "ran into unmapped region without finding PE header\n"); > + goto fail; > + } > + > + if (val == MZ_HEADER) // MZ > + break; > + > + base -= PAGE_SIZE; > + search_limit -= 1; > + } > + > + KDD_LOG(s, "base: %p\n", (void *)base); > + > + /* found the data section start */ > + if (get_pe64_sections(s, base, ".data", &data_base, &data_size)) > + goto fail; > + > + /* look for addresses which has kernel base written into it */ > + caddr = data_base; > + > + search_limit = (1024 * 1024 * 512) / SIZE_PTR64; > + while (caddr < data_base + data_size && search_limit) { > + if (kdd_read_virtual(s, s->cpuid, caddr, SIZE_PTR64, &buf) != > + SIZE_PTR64) > + goto fail; /* reached end and found nothing */ > + > + /* if we found base in the memory addresses */ > + if (buf == base) { > + /* read the DBGKD_GET_VERSION64 struct */ > + dbgkd_addr = caddr - offsetof(DBGKD_GET_VERSION64, KernBase); > + if (kdd_read_virtual(s, s->cpuid, dbgkd_addr, > + sizeof(DBGKD_GET_VERSION64), &dbgkd_get_version64) == > + sizeof(DBGKD_GET_VERSION64)) { > + /* check if major version is 0xf */ > + if (dbgkd_get_version64.MajorVersion == NT_MAJOR_VERSION) { > + > + /* read minor version, PsLoadedModuleList pointer and > + * DebuggerDataList > + */ > + modptr = dbgkd_get_version64.PsLoadedModuleList; > + kddl = dbgkd_get_version64.DebuggerDataList; > + minor = dbgkd_get_version64.MinorVersion; > + > + /* do heuristic check */ > + if (modptr && kddl && modptr != kddl && kddl != base && > + base != modptr && modptr >= data_base && > + modptr < (data_base + data_size) && > + kddl >= data_base && > + kddl < (data_base + data_size)) > + break; > + } > + } > + > + } > + > + caddr += SIZE_PTR64; > + search_limit -= 1; > + } > + > + if (caddr < data_base + data_size) { > + /* if found, set the field and return */ > + > + KDD_LOG(s, "base: %p\n", (void *)base); > + KDD_LOG(s, "modules list: %p\n", (void *)modptr); > + KDD_LOG(s, "kddl: %p\n", (void *)kddl); > + KDD_LOG(s, "minor version: 0x%hx\n", minor); > + > + s->os.base = base; > + s->os.modules = modptr - base; > + s->os.kddl = kddl - base; > + s->os.build = (uint32_t) minor; > + return; > + } > + > +fail: > + s->os = unknown_os; > +} > + > /* Figure out what OS we're dealing with */ > static void find_os(kdd_state *s) > { > int i; > - uint64_t limit; > + uint64_t limit; > > /* We may already have the right one */ > if (check_os(s)) > @@ -387,7 +725,8 @@ static void find_os(kdd_state *s) > if (check_os(s)) > return; > } > - s->os = unknown_os; > + > + get_os_info_64(s); > } > > > @@ -534,13 +873,14 @@ static void kdd_handle_handshake(kdd_state *s) > { > /* Figure out what we're looking at */ > find_os(s); > + > kdd_send_string(s, "[kdd: %s @0x%"PRIx64"]\r\n", s->os.name, s->os.base); > > /* Respond with some details about the debugger stub we simulate */ > s->txp.cmd.shake.u1 = 0x01010101; > s->txp.cmd.shake.status = KDD_STATUS_SUCCESS; > s->txp.cmd.shake.u2 = 0x02020202; > - s->txp.cmd.shake.v_major = 0xf; > + s->txp.cmd.shake.v_major = NT_MAJOR_VERSION; > s->txp.cmd.shake.v_minor = s->os.build; > s->txp.cmd.shake.proto = 6; > s->txp.cmd.shake.flags = (0x02 /* ??? */ > @@ -555,7 +895,7 @@ static void kdd_handle_handshake(kdd_state *s) > s->txp.cmd.shake.u3[2] = 0x55; > s->txp.cmd.shake.kern_addr = s->os.base; > s->txp.cmd.shake.mods_addr = s->os.base + s->os.modules; > - s->txp.cmd.shake.data_addr = 0; /* Debugger data probably doesn't exist */ > + s->txp.cmd.shake.data_addr = s->os.kddl ? s->os.base + s->os.kddl : 0; > > KDD_LOG(s, "Client initial handshake: %s\n", s->os.name); > kdd_send_cmd(s, KDD_CMD_SHAKE, 0); > -- > 2.17.1 >
Hi Julian and Jenish I have queued this patch to my for-next branch based on Paul and Tim's review. Note that Xen is currently frozen. This patch will get committed once the tree is open for new features. Wei.
Hi, I am not commenting on the code itself but the process. On Thu, 14 Nov 2019, 07:59 Julian Tuminaro, <julian.tuminaro@gmail.com> wrote: > From: Julian Tuminaro and Jenish Rakholiya <julian.tuminaro@gmail.com and > rakholiyajenish.07@gmail.com> > AFAICT this is the first time we have such format for "From". We usually have one person listed per tag and I think we should stick with it. Otherwise this is possibly going to break tools like get_maintainers.pl that tends to also output the list of contributors (depending on the option) and stat tools. Although, I am not entirely sure how to encode 2 authors here. Maybe 2 From tag? > Current implementation of find_os is based on the hard-coded values for > different Windows version. It uses the value for get the address to > start looking for DOS header in the given specified range. However, this > is not scalable to all version of Windows as it will require us to keep > adding new entries and also due to KASLR, chances of not hitting the PE > header is significant. We implement a way for 64-bit systems to use IDT > entry to get a valid exception/interrupt handler and then move back into > the memory to find the valid DOS header. Since IDT entries are protected > by PatchGuard, we think our assumption that IDT entries will not be > corrupted is valid for our purpose. Once we have the image base, we > search for the DBGKD_GET_VERSION64 structure type in .data section to > get information required for handshake. > > Currently, this is a work in progress feature and current patch only > supports the handshake and memory read/write on 64-bit systems. > > NOTE: This is the Updated version of the previous patch submitted This paragraph is not useful after committing. We tend to add them after "---" so it get stripped by git am. NOTE: This has currently been only tested when debugging was not enabled > on the guest Windows. This one is arguable, I think someone should have done the testing in most of the configurations before committing. So it can be put after "---" to inform the reviewer the state if the patch. Cheers, > Signed-off-by: Jenish Rakholiya <rjenish@cmu.edu> > Signed-off-by: Julian Tuminaro <jtuminar@andrew.cmu.edu> > --- > tools/debugger/kdd/kdd.c | 392 ++++++++++++++++++++++++++++++++++++--- > 1 file changed, 366 insertions(+), 26 deletions(-) > > diff --git a/tools/debugger/kdd/kdd.c b/tools/debugger/kdd/kdd.c > index fb8c645355..6d3febefda 100644 > --- a/tools/debugger/kdd/kdd.c > +++ b/tools/debugger/kdd/kdd.c > @@ -41,6 +41,7 @@ > #include <errno.h> > #include <inttypes.h> > #include <netdb.h> > +#include <stddef.h> > > #include <sys/socket.h> > #include <sys/types.h> > @@ -51,6 +52,16 @@ > > #include "kdd.h" > > +/* > + * TODO: kdd_os is a type which is used to represent os array. Adding a > + * variable here would result in adding a new field to each element in > array. > + * However, since most of the fields are part of the same struct that we > are > + * trying to read from memory, we have added kddl to this structure. If > + * required, we can possibly separate the kddl value to someplace else > + * > + * We also use kddl of size uint32_t which is actually used to represent > the > + * offset from image base rather than actual address > + */ > /* Windows version details */ > typedef struct { > uint32_t build; > @@ -62,6 +73,7 @@ typedef struct { > uint32_t version; /* +-> NtBuildNumber */ > uint32_t modules; /* +-> PsLoadedModuleList */ > uint32_t prcbs; /* +-> KiProcessorBlock */ > + uint32_t kddl; /* +-> KdDebuggerList */ > } kdd_os; > > /* State of the debugger stub */ > @@ -85,6 +97,117 @@ typedef struct { > kdd_os os; /* OS-specific magic > numbers */ > } kdd_state; > > +/** > + * @brief Structure to represent DBGKD_GET_VERSION64 > + * > + * reference: > https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdbgexts/ns-wdbgexts-_dbgkd_get_version64 > + */ > +typedef struct { > + uint16_t MajorVersion; /* usually 0xf for free > build */ > + uint16_t MinorVersion; /* build number of target > OS */ > + uint8_t ProtocolVersion; /* version of the debugger > protocol */ > + uint8_t KdSecondaryVersion; /* secondary version > number */ > + uint16_t Flags; /* set of bit flags for the current debugging > session */ > + uint16_t MachineType; /* type of the target's > processor */ > + uint8_t MaxPacketType; /* one plus the highest number for a > debugger */ > + /* packet type recognized by the > target */ > + uint8_t MaxStateChagne; /* one plus the highest number for a > state */ > + /* change generated by the > target */ > + uint8_t MaxManipulate; /* one more that the highest number, > recognized */ > + /* by the target, for a command to manipulate the > target */ > + uint8_t Simulation; /* indication if target is in simulated > execution */ > + uint16_t Unused[1]; > + uint64_t KernBase; /* base address of the kernel > image */ > + uint64_t PsLoadedModuleList; /* value of the kernel > variable */ > + /* > PsLoadedModuleList */ > + uint64_t DebuggerDataList; /* value of the kernel > variable */ > + /* > KdDebuggerDataBlock */ > +} PACKED DBGKD_GET_VERSION64; > + > +/** > + * @brief Structure to represent the section in PE headers > + * > + * reference: > https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#section-table-section-headers > + */ > +typedef struct { > + uint8_t Name[8]; /* name of section */ > + uint32_t VirtualSize; /* total size of section in memory */ > + uint32_t VirtualAddr; /* offset from image base */ > + uint32_t SizeOfRawData; /* size of section in for object > files */ > + uint32_t PointerToRawData; /* file pointer to first page in COFF > */ > + uint32_t PointerToRelocations; /* file pointer to beginning of > relocation entry */ > + uint32_t PointerToLinenumbers; /* file pointer to the beginning of > line-number entries */ > + uint16_t NumberOfRelocations; /* number of relocation entries for > the section */ > + uint16_t NumberOfLinenumbers; /* number of line-number entries for > the section */ > + uint32_t Characteristics; /* flags that describe the > characteristics of the section */ > +} PACKED PE_SECTION_ENTRY; > + > +/** > + * @brief Size of pointer on 64 machine > + */ > +#define SIZE_PTR64 8 > + > +/** > + * @brief Size of pointer on 32 machine > + */ > +#define SIZE_PTR32 4 > + > + > > +/***************************************************************************** > + * PE and DOS Header related offsets > + */ > + > +/** > + * @brief Offset in DOS header to look for PE header > + */ > +#define DOS_HDR_PE_OFF 0x3c > + > +/** > + * @brief Size of PE header offset field in DOS header > + */ > +#define DOS_HDR_PE_SZ 4 > + > +/** > + * @brief Offset of number of sections field in PE header > + */ > +#define PE_NUM_SECTION_OFF 0x6 > + > +/** > + * @brief Size of number of sections field in PE header > + */ > +#define PE_NUM_SECTION_SZ 2 > + > +/** > + * @brief Offset of optional header size field in PE header > + */ > +#define PE_OPT_HDR_SZ_OFF 0x14 > + > +/** > + * @brief Size of optional header size field in PE header > + */ > +#define PE_OPT_HDR_SZ_SZ 2 > + > +/** > + * @brief Size of PE header > + */ > +#define PE_HDR_SZ 0x18 > + > +/** > + * @brief MZ header > + */ > +#define MZ_HEADER 0x5a4d > + > +/** > + * @brief Limit on the number of sections to look for while iterating > through > + * PE sections > + */ > +#define NUM_SECT_LIMIT 100 > + > +/** > + * @brief Major Version for the DBGKD_GET_VERSION64 structure > + */ > +#define NT_MAJOR_VERSION 0xf > + > > /***************************************************************************** > * Utility functions > */ > @@ -293,41 +416,41 @@ static uint32_t kdd_write_virtual(kdd_state *s, int > cpuid, uint64_t addr, > */ > > static kdd_os os[] = { > - /* Build 64 MP Name &Kernel search base Range > +Version +Modules +PRCBs (64b) */ > - {2195, 0, 0, "w2k sp4 x32 UP", 0xffffffff80400000ULL, 0x00000000, > 0x0006d57c, 0x0006e1b8, 0x0}, > - {2195, 0, 1, "w2k sp4 x32 SMP", 0xffffffff80400000ULL, 0x00000000, > 0x0006fa1c, 0x00084520, 0x0}, > + /* Build 64 MP Name &Kernel search base Range > +Version +Modules +PRCBs (64b) +KDDL */ > + {2195, 0, 0, "w2k sp4 x32 UP", 0xffffffff80400000ULL, 0x00000000, > 0x0006d57c, 0x0006e1b8, 0x0, 0}, > + {2195, 0, 1, "w2k sp4 x32 SMP", 0xffffffff80400000ULL, 0x00000000, > 0x0006fa1c, 0x00084520, 0x0, 0}, > // PAE/UP, PAE/SMP > > - {2600, 0, 0, "xp sp2 x32 UP", 0xffffffff804d7000ULL, 0x00000000, > 0x00075568, 0x00083b20, 0x0}, > - {2600, 0, 1, "xp sp2 x32 SMP", 0xffffffff804d7000ULL, 0x00000000, > 0x0007d0e8, 0x0008d4a0, 0x0}, > + {2600, 0, 0, "xp sp2 x32 UP", 0xffffffff804d7000ULL, 0x00000000, > 0x00075568, 0x00083b20, 0x0, 0}, > + {2600, 0, 1, "xp sp2 x32 SMP", 0xffffffff804d7000ULL, 0x00000000, > 0x0007d0e8, 0x0008d4a0, 0x0, 0}, > // PAE/UP, PAE/SMP > > - {2600, 0, 0, "xp sp3 x32 UP", 0xffffffff804d7000ULL, 0x00000000, > 0x00075be8, 0x000841c0, 0x0}, > - {2600, 0, 1, "xp sp3 x32 SMP", 0xffffffff804d7000ULL, 0x00000000, > 0x0007c0e8, 0x0008c4c0, 0x0}, > - {2600, 0, 0, "xp sp3 x32p UP", 0xffffffff804d7000ULL, 0x00000000, > 0x0006e8e8, 0x0007cfc0, 0x0}, > - {2600, 0, 1, "xp sp3 x32p SMP", 0xffffffff804d7000ULL, 0x00000000, > 0x000760e8, 0x00086720, 0x0}, > + {2600, 0, 0, "xp sp3 x32 UP", 0xffffffff804d7000ULL, 0x00000000, > 0x00075be8, 0x000841c0, 0x0, 0}, > + {2600, 0, 1, "xp sp3 x32 SMP", 0xffffffff804d7000ULL, 0x00000000, > 0x0007c0e8, 0x0008c4c0, 0x0, 0}, > + {2600, 0, 0, "xp sp3 x32p UP", 0xffffffff804d7000ULL, 0x00000000, > 0x0006e8e8, 0x0007cfc0, 0x0, 0}, > + {2600, 0, 1, "xp sp3 x32p SMP", 0xffffffff804d7000ULL, 0x00000000, > 0x000760e8, 0x00086720, 0x0, 0}, > > - {3790, 0, 0, "w2k3 sp2 x32 UP", 0xffffffff80800000ULL, 0x00000000, > 0x00097128, 0x000a8e48, 0x0}, > - {3790, 0, 1, "w2k3 sp2 x32 SMP", 0xffffffff80800000ULL, 0x00000000, > 0x0009d128, 0x000af9c8, 0x0}, > - {3790, 0, 0, "w2k3 sp2 x32p UP", 0xffffffff80800000ULL, 0x00000000, > 0x0008e128, 0x0009ffa8, 0x0}, > - {3790, 0, 1, "w2k3 sp2 x32p SMP", 0xffffffff80800000ULL, 0x00000000, > 0x00094128, 0x000a6ea8, 0x0}, > - {3790, 1, 0, "w2k3 sp2 x64 UP", 0xfffff80001000000ULL, 0x00000000, > 0x001765d0, 0x0019aae0, 0x0017b100}, > - {3790, 1, 1, "w2k3 sp2 x64 SMP", 0xfffff80001000000ULL, 0x00000000, > 0x001b05e0, 0x001d5100, 0x001b5300}, > + {3790, 0, 0, "w2k3 sp2 x32 UP", 0xffffffff80800000ULL, 0x00000000, > 0x00097128, 0x000a8e48, 0x0, 0}, > + {3790, 0, 1, "w2k3 sp2 x32 SMP", 0xffffffff80800000ULL, 0x00000000, > 0x0009d128, 0x000af9c8, 0x0, 0}, > + {3790, 0, 0, "w2k3 sp2 x32p UP", 0xffffffff80800000ULL, 0x00000000, > 0x0008e128, 0x0009ffa8, 0x0, 0}, > + {3790, 0, 1, "w2k3 sp2 x32p SMP", 0xffffffff80800000ULL, 0x00000000, > 0x00094128, 0x000a6ea8, 0x0, 0}, > + {3790, 1, 0, "w2k3 sp2 x64 UP", 0xfffff80001000000ULL, 0x00000000, > 0x001765d0, 0x0019aae0, 0x0017b100, 0}, > + {3790, 1, 1, "w2k3 sp2 x64 SMP", 0xfffff80001000000ULL, 0x00000000, > 0x001b05e0, 0x001d5100, 0x001b5300, 0}, > > - {6000, 0, 1, "vista sp0 x32p", 0xffffffff81800000ULL, 0x00000000, > 0x000a4de4, 0x00111db0, 0x0}, > - {6001, 0, 1, "vista sp1 x32p", 0xffffffff81000000ULL, 0x0f000000, > 0x000af0c4, 0x00117c70, 0x0}, > + {6000, 0, 1, "vista sp0 x32p", 0xffffffff81800000ULL, 0x00000000, > 0x000a4de4, 0x00111db0, 0x0, 0}, > + {6001, 0, 1, "vista sp1 x32p", 0xffffffff81000000ULL, 0x0f000000, > 0x000af0c4, 0x00117c70, 0x0, 0}, > > - {6001, 1, 1, "w2k8 sp0 x64", 0xfffff80001000000ULL, 0x0f000000, > 0x00140bf0, 0x001c5db0, 0x00229640}, > + {6001, 1, 1, "w2k8 sp0 x64", 0xfffff80001000000ULL, 0x0f000000, > 0x00140bf0, 0x001c5db0, 0x00229640, 0}, > > - {7600, 1, 1, "win7 sp0 x64", 0xfffff80001000000ULL, 0x0f000000, > 0x001af770, 0x0023de50, 0x002a8900}, > + {7600, 1, 1, "win7 sp0 x64", 0xfffff80001000000ULL, 0x0f000000, > 0x001af770, 0x0023de50, 0x002a8900, 0}, > > - {7601, 0, 1, "win7 sp1 x32p", 0xffffffff81800000ULL, 0x0f000000, > 0x000524c4, 0x00149850, 0x0}, > - {7601, 1, 1, "win7 sp1 x64", 0xfffff80001000000ULL, 0x0f000000, > 0x001b2770, 0x00240e90, 0x002ab900}, > + {7601, 0, 1, "win7 sp1 x32p", 0xffffffff81800000ULL, 0x0f000000, > 0x000524c4, 0x00149850, 0x0, 0}, > + {7601, 1, 1, "win7 sp1 x64", 0xfffff80001000000ULL, 0x0f000000, > 0x001b2770, 0x00240e90, 0x002ab900, 0}, > }; > > // 1381, 0, 0, "NT4 sp?", 0xffffffff80100000, ?, ? > > -static kdd_os unknown_os = {0, 0, 0, "unknown OS", 0, 0, 0, 0, 0}; > +static kdd_os unknown_os = {0, 0, 0, "unknown OS", 0, 0, 0, 0, 0, 0}; > > static int check_os(kdd_state *s) > { > @@ -367,11 +490,226 @@ static int check_os(kdd_state *s) > return 1; > } > > +/** > + * @brief Parse the memory at \a filebase as a valid DOS header and get > virtual > + * address offset and size for any given section name (if it exists) > + * > + * @param s Pointer to the kdd_state structure > + * @param filebase Base address of the file structure > + * @param sectname Pointer to the section name c-string to look for > + * @param vaddr Pointer to write the virtual address of section start to > + * (if found) > + * @param visze Pointer to write the section size to (if found) > + * > + * @return -1 on failure to find the section name > + * @return 0 on success > + */ > +static int get_pe64_sections(kdd_state *s, uint64_t filebase, char > *sectname, > + uint64_t *vaddr, uint32_t *vsize) > +{ > + uint64_t pe_hdr = 0; > + uint64_t sect_start = 0; > + uint16_t num_sections = 0; > + uint16_t opt_hdr_sz = 0; > + PE_SECTION_ENTRY pe_sect; > + > + if (!s->os.w64) > + return -1; > + > + /* read PE header offset */ > + if (kdd_read_virtual(s, s->cpuid, filebase + DOS_HDR_PE_OFF, > DOS_HDR_PE_SZ, > + &pe_hdr) != DOS_HDR_PE_SZ) > + return -1; > + > + pe_hdr += filebase; > + > + /* read number of sections */ > + if (kdd_read_virtual(s, s->cpuid, pe_hdr + PE_NUM_SECTION_OFF, > + PE_NUM_SECTION_SZ, &num_sections) != PE_NUM_SECTION_SZ) > + return -1; > + > + /* read number of section upto a limit */ > + if (num_sections > NUM_SECT_LIMIT) > + num_sections = NUM_SECT_LIMIT; > + > + /* read size of optional header */ > + if (kdd_read_virtual(s, s->cpuid, pe_hdr + PE_OPT_HDR_SZ_OFF, > + PE_OPT_HDR_SZ_SZ, &opt_hdr_sz) != PE_OPT_HDR_SZ_SZ) > + return -1; > + > + /* 0x18 is the size of PE header */ > + sect_start = pe_hdr + PE_HDR_SZ + opt_hdr_sz; > + > + for (int i = 0; i < num_sections; i++) { > + if (kdd_read_virtual(s, s->cpuid, sect_start + (i * > sizeof(pe_sect)), > + sizeof(pe_sect), &pe_sect) != sizeof(pe_sect)) > + return -1; > + > + if (!strncmp(sectname, (char *)pe_sect.Name, > sizeof(pe_sect.Name))) { > + *vaddr = filebase + pe_sect.VirtualAddr; > + *vsize = pe_sect.VirtualSize; > + return 0; > + } > + } > + > + return -1; > +} > + > +/** > + * @brief Get the OS information like base address, minor version, > + * PsLoadedModuleList and DebuggerDataList (basically the fields of > + * DBGKD_GET_VERSION64 struture required to do handshake?). > + * > + * This is done by reading the IDT entry for divide-by-zero exception and > + * searching back into the memory for DOS header (which is our kernel > base). > + * Once we have the kernel base, we parse the PE header and look for > kernel > + * base address in the .data section. Once we have possible values, we > look for > + * DBGKD_GET_VERSION64 block by using following heuristics on the address > which > + * has the kernel base: > + * > + * - at address [-0x10], it should have 0xf as the MajorVersion > + * - at address [+0x8], it should have a valid kernel memory address > pointing > + * in .data > + * - at address [+0x10], it should have a valid kernel memory address > pointing > + * in .data > + * > + * @param s Pointer to the kdd state > + */ > +static void get_os_info_64(kdd_state *s) > +{ > + kdd_ctrl ctrl; > + int ret; > + uint64_t buf; > + uint64_t idt0_addr; > + uint64_t base; > + uint64_t caddr; > + uint64_t data_base; > + uint32_t data_size; > + uint64_t modptr = 0; > + uint64_t kddl = 0; > + uint16_t minor = 0; > + uint64_t dbgkd_addr; > + DBGKD_GET_VERSION64 dbgkd_get_version64; > + /* Maybe 1GB is too big for the limit to search? */ > + uint32_t search_limit = (1024 * 1024 * 1024) / PAGE_SIZE; > /*1GB/PageSize*/ > + uint64_t efer; > + > + /* if we are not in 64-bit mode, fail */ > + if (kdd_rdmsr(s->guest, s->cpuid, 0xc0000080, &efer) || !(efer & (1 > << 8))) > + goto fail; > + > + s->os.w64 = 1; > + > + /* get control registers for our os */ > + ret = kdd_get_ctrl(s->guest, s->cpuid, &ctrl, s->os.w64); > + if (ret) > + goto fail; > + > + /* read the div-by-zero handler function address */ > + kdd_read_virtual(s, s->cpuid, ctrl.c64.idt_base + 8, 8, &buf); > + idt0_addr = ((uint64_t)buf << 32) & 0xffffffff00000000; > + > + kdd_read_virtual(s, s->cpuid, ctrl.c64.idt_base, 8, &buf); > + idt0_addr |= ((buf >> 32) & 0xffff0000); > + idt0_addr |= (buf & 0xffff); > + > + KDD_LOG(s, "idt0 addr: %p\n", (void *)idt0_addr); > + > + /* > + * get the page start and look for "MZ" file header - we limit the > search > + * in 1GB range above the current page base address > + */ > + > + base = idt0_addr & ~(PAGE_SIZE - 1); > + KDD_LOG(s, "%p\n", (void *)base); > + > + while (search_limit) { > + uint16_t val; > + if (kdd_read_virtual(s, s->cpuid, base, 2, &val) != 2) { > + /* just move going back?? this is bad though */ > + KDD_LOG(s, "ran into unmapped region without finding PE > header\n"); > + goto fail; > + } > + > + if (val == MZ_HEADER) // MZ > + break; > + > + base -= PAGE_SIZE; > + search_limit -= 1; > + } > + > + KDD_LOG(s, "base: %p\n", (void *)base); > + > + /* found the data section start */ > + if (get_pe64_sections(s, base, ".data", &data_base, &data_size)) > + goto fail; > + > + /* look for addresses which has kernel base written into it */ > + caddr = data_base; > + > + search_limit = (1024 * 1024 * 512) / SIZE_PTR64; > + while (caddr < data_base + data_size && search_limit) { > + if (kdd_read_virtual(s, s->cpuid, caddr, SIZE_PTR64, &buf) != > + SIZE_PTR64) > + goto fail; /* reached end and found nothing */ > + > + /* if we found base in the memory addresses */ > + if (buf == base) { > + /* read the DBGKD_GET_VERSION64 struct */ > + dbgkd_addr = caddr - offsetof(DBGKD_GET_VERSION64, KernBase); > + if (kdd_read_virtual(s, s->cpuid, dbgkd_addr, > + sizeof(DBGKD_GET_VERSION64), > &dbgkd_get_version64) == > + sizeof(DBGKD_GET_VERSION64)) { > + /* check if major version is 0xf */ > + if (dbgkd_get_version64.MajorVersion == NT_MAJOR_VERSION) > { > + > + /* read minor version, PsLoadedModuleList pointer and > + * DebuggerDataList > + */ > + modptr = dbgkd_get_version64.PsLoadedModuleList; > + kddl = dbgkd_get_version64.DebuggerDataList; > + minor = dbgkd_get_version64.MinorVersion; > + > + /* do heuristic check */ > + if (modptr && kddl && modptr != kddl && kddl != base > && > + base != modptr && modptr >= data_base && > + modptr < (data_base + data_size) && > + kddl >= data_base && > + kddl < (data_base + data_size)) > + break; > + } > + } > + > + } > + > + caddr += SIZE_PTR64; > + search_limit -= 1; > + } > + > + if (caddr < data_base + data_size) { > + /* if found, set the field and return */ > + > + KDD_LOG(s, "base: %p\n", (void *)base); > + KDD_LOG(s, "modules list: %p\n", (void *)modptr); > + KDD_LOG(s, "kddl: %p\n", (void *)kddl); > + KDD_LOG(s, "minor version: 0x%hx\n", minor); > + > + s->os.base = base; > + s->os.modules = modptr - base; > + s->os.kddl = kddl - base; > + s->os.build = (uint32_t) minor; > + return; > + } > + > +fail: > + s->os = unknown_os; > +} > + > /* Figure out what OS we're dealing with */ > static void find_os(kdd_state *s) > { > int i; > - uint64_t limit; > + uint64_t limit; > > /* We may already have the right one */ > if (check_os(s)) > @@ -387,7 +725,8 @@ static void find_os(kdd_state *s) > if (check_os(s)) > return; > } > - s->os = unknown_os; > + > + get_os_info_64(s); > } > > > @@ -534,13 +873,14 @@ static void kdd_handle_handshake(kdd_state *s) > { > /* Figure out what we're looking at */ > find_os(s); > + > kdd_send_string(s, "[kdd: %s @0x%"PRIx64"]\r\n", s->os.name, > s->os.base); > > /* Respond with some details about the debugger stub we simulate */ > s->txp.cmd.shake.u1 = 0x01010101; > s->txp.cmd.shake.status = KDD_STATUS_SUCCESS; > s->txp.cmd.shake.u2 = 0x02020202; > - s->txp.cmd.shake.v_major = 0xf; > + s->txp.cmd.shake.v_major = NT_MAJOR_VERSION; > s->txp.cmd.shake.v_minor = s->os.build; > s->txp.cmd.shake.proto = 6; > s->txp.cmd.shake.flags = (0x02 /* ??? */ > @@ -555,7 +895,7 @@ static void kdd_handle_handshake(kdd_state *s) > s->txp.cmd.shake.u3[2] = 0x55; > s->txp.cmd.shake.kern_addr = s->os.base; > s->txp.cmd.shake.mods_addr = s->os.base + s->os.modules; > - s->txp.cmd.shake.data_addr = 0; /* Debugger data probably doesn't > exist */ > + s->txp.cmd.shake.data_addr = s->os.kddl ? s->os.base + s->os.kddl : 0; > > KDD_LOG(s, "Client initial handshake: %s\n", s->os.name); > kdd_send_cmd(s, KDD_CMD_SHAKE, 0); > -- > 2.17.1 > > > _______________________________________________ > Xen-devel mailing list > Xen-devel@lists.xenproject.org > https://lists.xenproject.org/mailman/listinfo/xen-devel
CC Wei's correct e-mail address. On Sat, 16 Nov 2019, 05:44 Julien Grall, <julien.grall@gmail.com> wrote: > Hi, > > I am not commenting on the code itself but the process. > > On Thu, 14 Nov 2019, 07:59 Julian Tuminaro, <julian.tuminaro@gmail.com> > wrote: > >> From: Julian Tuminaro and Jenish Rakholiya <julian.tuminaro@gmail.com >> and rakholiyajenish.07@gmail.com> >> > > AFAICT this is the first time we have such format for "From". > > We usually have one person listed per tag and I think we should stick with > it. > > Otherwise this is possibly going to break tools like get_maintainers.pl > that tends to also output the list of contributors (depending on the > option) and stat tools. > > Although, I am not entirely sure how to encode 2 authors here. Maybe 2 > From tag? > > >> Current implementation of find_os is based on the hard-coded values for >> different Windows version. It uses the value for get the address to >> start looking for DOS header in the given specified range. However, this >> is not scalable to all version of Windows as it will require us to keep >> adding new entries and also due to KASLR, chances of not hitting the PE >> header is significant. We implement a way for 64-bit systems to use IDT >> entry to get a valid exception/interrupt handler and then move back into >> the memory to find the valid DOS header. Since IDT entries are protected >> by PatchGuard, we think our assumption that IDT entries will not be >> corrupted is valid for our purpose. Once we have the image base, we >> search for the DBGKD_GET_VERSION64 structure type in .data section to >> get information required for handshake. >> >> Currently, this is a work in progress feature and current patch only >> supports the handshake and memory read/write on 64-bit systems. >> >> NOTE: This is the Updated version of the previous patch submitted > > > This paragraph is not useful after committing. We tend to add them after > "---" so it get stripped by git am. > > NOTE: This has currently been only tested when debugging was not enabled >> on the guest Windows. > > > This one is arguable, I think someone should have done the testing in most > of the configurations before committing. So it can be put after "---" to > inform the reviewer the state if the patch. > > Cheers, > > >> Signed-off-by: Jenish Rakholiya <rjenish@cmu.edu> >> Signed-off-by: Julian Tuminaro <jtuminar@andrew.cmu.edu> >> --- >> tools/debugger/kdd/kdd.c | 392 ++++++++++++++++++++++++++++++++++++--- >> 1 file changed, 366 insertions(+), 26 deletions(-) >> >> diff --git a/tools/debugger/kdd/kdd.c b/tools/debugger/kdd/kdd.c >> index fb8c645355..6d3febefda 100644 >> --- a/tools/debugger/kdd/kdd.c >> +++ b/tools/debugger/kdd/kdd.c >> @@ -41,6 +41,7 @@ >> #include <errno.h> >> #include <inttypes.h> >> #include <netdb.h> >> +#include <stddef.h> >> >> #include <sys/socket.h> >> #include <sys/types.h> >> @@ -51,6 +52,16 @@ >> >> #include "kdd.h" >> >> +/* >> + * TODO: kdd_os is a type which is used to represent os array. Adding a >> + * variable here would result in adding a new field to each element in >> array. >> + * However, since most of the fields are part of the same struct that we >> are >> + * trying to read from memory, we have added kddl to this structure. If >> + * required, we can possibly separate the kddl value to someplace else >> + * >> + * We also use kddl of size uint32_t which is actually used to represent >> the >> + * offset from image base rather than actual address >> + */ >> /* Windows version details */ >> typedef struct { >> uint32_t build; >> @@ -62,6 +73,7 @@ typedef struct { >> uint32_t version; /* +-> NtBuildNumber */ >> uint32_t modules; /* +-> PsLoadedModuleList */ >> uint32_t prcbs; /* +-> KiProcessorBlock */ >> + uint32_t kddl; /* +-> KdDebuggerList */ >> } kdd_os; >> >> /* State of the debugger stub */ >> @@ -85,6 +97,117 @@ typedef struct { >> kdd_os os; /* OS-specific magic >> numbers */ >> } kdd_state; >> >> +/** >> + * @brief Structure to represent DBGKD_GET_VERSION64 >> + * >> + * reference: >> https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdbgexts/ns-wdbgexts-_dbgkd_get_version64 >> + */ >> +typedef struct { >> + uint16_t MajorVersion; /* usually 0xf for free >> build */ >> + uint16_t MinorVersion; /* build number of >> target OS */ >> + uint8_t ProtocolVersion; /* version of the debugger >> protocol */ >> + uint8_t KdSecondaryVersion; /* secondary version >> number */ >> + uint16_t Flags; /* set of bit flags for the current debugging >> session */ >> + uint16_t MachineType; /* type of the target's >> processor */ >> + uint8_t MaxPacketType; /* one plus the highest number for a >> debugger */ >> + /* packet type recognized by the >> target */ >> + uint8_t MaxStateChagne; /* one plus the highest number for a >> state */ >> + /* change generated by the >> target */ >> + uint8_t MaxManipulate; /* one more that the highest number, >> recognized */ >> + /* by the target, for a command to manipulate the >> target */ >> + uint8_t Simulation; /* indication if target is in simulated >> execution */ >> + uint16_t Unused[1]; >> + uint64_t KernBase; /* base address of the kernel >> image */ >> + uint64_t PsLoadedModuleList; /* value of the kernel >> variable */ >> + /* >> PsLoadedModuleList */ >> + uint64_t DebuggerDataList; /* value of the kernel >> variable */ >> + /* >> KdDebuggerDataBlock */ >> +} PACKED DBGKD_GET_VERSION64; >> + >> +/** >> + * @brief Structure to represent the section in PE headers >> + * >> + * reference: >> https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#section-table-section-headers >> + */ >> +typedef struct { >> + uint8_t Name[8]; /* name of section */ >> + uint32_t VirtualSize; /* total size of section in memory */ >> + uint32_t VirtualAddr; /* offset from image base */ >> + uint32_t SizeOfRawData; /* size of section in for object >> files */ >> + uint32_t PointerToRawData; /* file pointer to first page in >> COFF */ >> + uint32_t PointerToRelocations; /* file pointer to beginning of >> relocation entry */ >> + uint32_t PointerToLinenumbers; /* file pointer to the beginning of >> line-number entries */ >> + uint16_t NumberOfRelocations; /* number of relocation entries for >> the section */ >> + uint16_t NumberOfLinenumbers; /* number of line-number entries for >> the section */ >> + uint32_t Characteristics; /* flags that describe the >> characteristics of the section */ >> +} PACKED PE_SECTION_ENTRY; >> + >> +/** >> + * @brief Size of pointer on 64 machine >> + */ >> +#define SIZE_PTR64 8 >> + >> +/** >> + * @brief Size of pointer on 32 machine >> + */ >> +#define SIZE_PTR32 4 >> + >> + >> >> +/***************************************************************************** >> + * PE and DOS Header related offsets >> + */ >> + >> +/** >> + * @brief Offset in DOS header to look for PE header >> + */ >> +#define DOS_HDR_PE_OFF 0x3c >> + >> +/** >> + * @brief Size of PE header offset field in DOS header >> + */ >> +#define DOS_HDR_PE_SZ 4 >> + >> +/** >> + * @brief Offset of number of sections field in PE header >> + */ >> +#define PE_NUM_SECTION_OFF 0x6 >> + >> +/** >> + * @brief Size of number of sections field in PE header >> + */ >> +#define PE_NUM_SECTION_SZ 2 >> + >> +/** >> + * @brief Offset of optional header size field in PE header >> + */ >> +#define PE_OPT_HDR_SZ_OFF 0x14 >> + >> +/** >> + * @brief Size of optional header size field in PE header >> + */ >> +#define PE_OPT_HDR_SZ_SZ 2 >> + >> +/** >> + * @brief Size of PE header >> + */ >> +#define PE_HDR_SZ 0x18 >> + >> +/** >> + * @brief MZ header >> + */ >> +#define MZ_HEADER 0x5a4d >> + >> +/** >> + * @brief Limit on the number of sections to look for while iterating >> through >> + * PE sections >> + */ >> +#define NUM_SECT_LIMIT 100 >> + >> +/** >> + * @brief Major Version for the DBGKD_GET_VERSION64 structure >> + */ >> +#define NT_MAJOR_VERSION 0xf >> + >> >> /***************************************************************************** >> * Utility functions >> */ >> @@ -293,41 +416,41 @@ static uint32_t kdd_write_virtual(kdd_state *s, int >> cpuid, uint64_t addr, >> */ >> >> static kdd_os os[] = { >> - /* Build 64 MP Name &Kernel search base Range >> +Version +Modules +PRCBs (64b) */ >> - {2195, 0, 0, "w2k sp4 x32 UP", 0xffffffff80400000ULL, 0x00000000, >> 0x0006d57c, 0x0006e1b8, 0x0}, >> - {2195, 0, 1, "w2k sp4 x32 SMP", 0xffffffff80400000ULL, 0x00000000, >> 0x0006fa1c, 0x00084520, 0x0}, >> + /* Build 64 MP Name &Kernel search base Range >> +Version +Modules +PRCBs (64b) +KDDL */ >> + {2195, 0, 0, "w2k sp4 x32 UP", 0xffffffff80400000ULL, 0x00000000, >> 0x0006d57c, 0x0006e1b8, 0x0, 0}, >> + {2195, 0, 1, "w2k sp4 x32 SMP", 0xffffffff80400000ULL, 0x00000000, >> 0x0006fa1c, 0x00084520, 0x0, 0}, >> // PAE/UP, PAE/SMP >> >> - {2600, 0, 0, "xp sp2 x32 UP", 0xffffffff804d7000ULL, 0x00000000, >> 0x00075568, 0x00083b20, 0x0}, >> - {2600, 0, 1, "xp sp2 x32 SMP", 0xffffffff804d7000ULL, 0x00000000, >> 0x0007d0e8, 0x0008d4a0, 0x0}, >> + {2600, 0, 0, "xp sp2 x32 UP", 0xffffffff804d7000ULL, 0x00000000, >> 0x00075568, 0x00083b20, 0x0, 0}, >> + {2600, 0, 1, "xp sp2 x32 SMP", 0xffffffff804d7000ULL, 0x00000000, >> 0x0007d0e8, 0x0008d4a0, 0x0, 0}, >> // PAE/UP, PAE/SMP >> >> - {2600, 0, 0, "xp sp3 x32 UP", 0xffffffff804d7000ULL, 0x00000000, >> 0x00075be8, 0x000841c0, 0x0}, >> - {2600, 0, 1, "xp sp3 x32 SMP", 0xffffffff804d7000ULL, 0x00000000, >> 0x0007c0e8, 0x0008c4c0, 0x0}, >> - {2600, 0, 0, "xp sp3 x32p UP", 0xffffffff804d7000ULL, 0x00000000, >> 0x0006e8e8, 0x0007cfc0, 0x0}, >> - {2600, 0, 1, "xp sp3 x32p SMP", 0xffffffff804d7000ULL, 0x00000000, >> 0x000760e8, 0x00086720, 0x0}, >> + {2600, 0, 0, "xp sp3 x32 UP", 0xffffffff804d7000ULL, 0x00000000, >> 0x00075be8, 0x000841c0, 0x0, 0}, >> + {2600, 0, 1, "xp sp3 x32 SMP", 0xffffffff804d7000ULL, 0x00000000, >> 0x0007c0e8, 0x0008c4c0, 0x0, 0}, >> + {2600, 0, 0, "xp sp3 x32p UP", 0xffffffff804d7000ULL, 0x00000000, >> 0x0006e8e8, 0x0007cfc0, 0x0, 0}, >> + {2600, 0, 1, "xp sp3 x32p SMP", 0xffffffff804d7000ULL, 0x00000000, >> 0x000760e8, 0x00086720, 0x0, 0}, >> >> - {3790, 0, 0, "w2k3 sp2 x32 UP", 0xffffffff80800000ULL, 0x00000000, >> 0x00097128, 0x000a8e48, 0x0}, >> - {3790, 0, 1, "w2k3 sp2 x32 SMP", 0xffffffff80800000ULL, 0x00000000, >> 0x0009d128, 0x000af9c8, 0x0}, >> - {3790, 0, 0, "w2k3 sp2 x32p UP", 0xffffffff80800000ULL, 0x00000000, >> 0x0008e128, 0x0009ffa8, 0x0}, >> - {3790, 0, 1, "w2k3 sp2 x32p SMP", 0xffffffff80800000ULL, 0x00000000, >> 0x00094128, 0x000a6ea8, 0x0}, >> - {3790, 1, 0, "w2k3 sp2 x64 UP", 0xfffff80001000000ULL, 0x00000000, >> 0x001765d0, 0x0019aae0, 0x0017b100}, >> - {3790, 1, 1, "w2k3 sp2 x64 SMP", 0xfffff80001000000ULL, 0x00000000, >> 0x001b05e0, 0x001d5100, 0x001b5300}, >> + {3790, 0, 0, "w2k3 sp2 x32 UP", 0xffffffff80800000ULL, 0x00000000, >> 0x00097128, 0x000a8e48, 0x0, 0}, >> + {3790, 0, 1, "w2k3 sp2 x32 SMP", 0xffffffff80800000ULL, 0x00000000, >> 0x0009d128, 0x000af9c8, 0x0, 0}, >> + {3790, 0, 0, "w2k3 sp2 x32p UP", 0xffffffff80800000ULL, 0x00000000, >> 0x0008e128, 0x0009ffa8, 0x0, 0}, >> + {3790, 0, 1, "w2k3 sp2 x32p SMP", 0xffffffff80800000ULL, 0x00000000, >> 0x00094128, 0x000a6ea8, 0x0, 0}, >> + {3790, 1, 0, "w2k3 sp2 x64 UP", 0xfffff80001000000ULL, 0x00000000, >> 0x001765d0, 0x0019aae0, 0x0017b100, 0}, >> + {3790, 1, 1, "w2k3 sp2 x64 SMP", 0xfffff80001000000ULL, 0x00000000, >> 0x001b05e0, 0x001d5100, 0x001b5300, 0}, >> >> - {6000, 0, 1, "vista sp0 x32p", 0xffffffff81800000ULL, 0x00000000, >> 0x000a4de4, 0x00111db0, 0x0}, >> - {6001, 0, 1, "vista sp1 x32p", 0xffffffff81000000ULL, 0x0f000000, >> 0x000af0c4, 0x00117c70, 0x0}, >> + {6000, 0, 1, "vista sp0 x32p", 0xffffffff81800000ULL, 0x00000000, >> 0x000a4de4, 0x00111db0, 0x0, 0}, >> + {6001, 0, 1, "vista sp1 x32p", 0xffffffff81000000ULL, 0x0f000000, >> 0x000af0c4, 0x00117c70, 0x0, 0}, >> >> - {6001, 1, 1, "w2k8 sp0 x64", 0xfffff80001000000ULL, 0x0f000000, >> 0x00140bf0, 0x001c5db0, 0x00229640}, >> + {6001, 1, 1, "w2k8 sp0 x64", 0xfffff80001000000ULL, 0x0f000000, >> 0x00140bf0, 0x001c5db0, 0x00229640, 0}, >> >> - {7600, 1, 1, "win7 sp0 x64", 0xfffff80001000000ULL, 0x0f000000, >> 0x001af770, 0x0023de50, 0x002a8900}, >> + {7600, 1, 1, "win7 sp0 x64", 0xfffff80001000000ULL, 0x0f000000, >> 0x001af770, 0x0023de50, 0x002a8900, 0}, >> >> - {7601, 0, 1, "win7 sp1 x32p", 0xffffffff81800000ULL, 0x0f000000, >> 0x000524c4, 0x00149850, 0x0}, >> - {7601, 1, 1, "win7 sp1 x64", 0xfffff80001000000ULL, 0x0f000000, >> 0x001b2770, 0x00240e90, 0x002ab900}, >> + {7601, 0, 1, "win7 sp1 x32p", 0xffffffff81800000ULL, 0x0f000000, >> 0x000524c4, 0x00149850, 0x0, 0}, >> + {7601, 1, 1, "win7 sp1 x64", 0xfffff80001000000ULL, 0x0f000000, >> 0x001b2770, 0x00240e90, 0x002ab900, 0}, >> }; >> >> // 1381, 0, 0, "NT4 sp?", 0xffffffff80100000, ?, ? >> >> -static kdd_os unknown_os = {0, 0, 0, "unknown OS", 0, 0, 0, 0, 0}; >> +static kdd_os unknown_os = {0, 0, 0, "unknown OS", 0, 0, 0, 0, 0, 0}; >> >> static int check_os(kdd_state *s) >> { >> @@ -367,11 +490,226 @@ static int check_os(kdd_state *s) >> return 1; >> } >> >> +/** >> + * @brief Parse the memory at \a filebase as a valid DOS header and get >> virtual >> + * address offset and size for any given section name (if it exists) >> + * >> + * @param s Pointer to the kdd_state structure >> + * @param filebase Base address of the file structure >> + * @param sectname Pointer to the section name c-string to look for >> + * @param vaddr Pointer to write the virtual address of section start to >> + * (if found) >> + * @param visze Pointer to write the section size to (if found) >> + * >> + * @return -1 on failure to find the section name >> + * @return 0 on success >> + */ >> +static int get_pe64_sections(kdd_state *s, uint64_t filebase, char >> *sectname, >> + uint64_t *vaddr, uint32_t *vsize) >> +{ >> + uint64_t pe_hdr = 0; >> + uint64_t sect_start = 0; >> + uint16_t num_sections = 0; >> + uint16_t opt_hdr_sz = 0; >> + PE_SECTION_ENTRY pe_sect; >> + >> + if (!s->os.w64) >> + return -1; >> + >> + /* read PE header offset */ >> + if (kdd_read_virtual(s, s->cpuid, filebase + DOS_HDR_PE_OFF, >> DOS_HDR_PE_SZ, >> + &pe_hdr) != DOS_HDR_PE_SZ) >> + return -1; >> + >> + pe_hdr += filebase; >> + >> + /* read number of sections */ >> + if (kdd_read_virtual(s, s->cpuid, pe_hdr + PE_NUM_SECTION_OFF, >> + PE_NUM_SECTION_SZ, &num_sections) != PE_NUM_SECTION_SZ) >> + return -1; >> + >> + /* read number of section upto a limit */ >> + if (num_sections > NUM_SECT_LIMIT) >> + num_sections = NUM_SECT_LIMIT; >> + >> + /* read size of optional header */ >> + if (kdd_read_virtual(s, s->cpuid, pe_hdr + PE_OPT_HDR_SZ_OFF, >> + PE_OPT_HDR_SZ_SZ, &opt_hdr_sz) != PE_OPT_HDR_SZ_SZ) >> + return -1; >> + >> + /* 0x18 is the size of PE header */ >> + sect_start = pe_hdr + PE_HDR_SZ + opt_hdr_sz; >> + >> + for (int i = 0; i < num_sections; i++) { >> + if (kdd_read_virtual(s, s->cpuid, sect_start + (i * >> sizeof(pe_sect)), >> + sizeof(pe_sect), &pe_sect) != sizeof(pe_sect)) >> + return -1; >> + >> + if (!strncmp(sectname, (char *)pe_sect.Name, >> sizeof(pe_sect.Name))) { >> + *vaddr = filebase + pe_sect.VirtualAddr; >> + *vsize = pe_sect.VirtualSize; >> + return 0; >> + } >> + } >> + >> + return -1; >> +} >> + >> +/** >> + * @brief Get the OS information like base address, minor version, >> + * PsLoadedModuleList and DebuggerDataList (basically the fields of >> + * DBGKD_GET_VERSION64 struture required to do handshake?). >> + * >> + * This is done by reading the IDT entry for divide-by-zero exception and >> + * searching back into the memory for DOS header (which is our kernel >> base). >> + * Once we have the kernel base, we parse the PE header and look for >> kernel >> + * base address in the .data section. Once we have possible values, we >> look for >> + * DBGKD_GET_VERSION64 block by using following heuristics on the >> address which >> + * has the kernel base: >> + * >> + * - at address [-0x10], it should have 0xf as the MajorVersion >> + * - at address [+0x8], it should have a valid kernel memory address >> pointing >> + * in .data >> + * - at address [+0x10], it should have a valid kernel memory address >> pointing >> + * in .data >> + * >> + * @param s Pointer to the kdd state >> + */ >> +static void get_os_info_64(kdd_state *s) >> +{ >> + kdd_ctrl ctrl; >> + int ret; >> + uint64_t buf; >> + uint64_t idt0_addr; >> + uint64_t base; >> + uint64_t caddr; >> + uint64_t data_base; >> + uint32_t data_size; >> + uint64_t modptr = 0; >> + uint64_t kddl = 0; >> + uint16_t minor = 0; >> + uint64_t dbgkd_addr; >> + DBGKD_GET_VERSION64 dbgkd_get_version64; >> + /* Maybe 1GB is too big for the limit to search? */ >> + uint32_t search_limit = (1024 * 1024 * 1024) / PAGE_SIZE; >> /*1GB/PageSize*/ >> + uint64_t efer; >> + >> + /* if we are not in 64-bit mode, fail */ >> + if (kdd_rdmsr(s->guest, s->cpuid, 0xc0000080, &efer) || !(efer & (1 >> << 8))) >> + goto fail; >> + >> + s->os.w64 = 1; >> + >> + /* get control registers for our os */ >> + ret = kdd_get_ctrl(s->guest, s->cpuid, &ctrl, s->os.w64); >> + if (ret) >> + goto fail; >> + >> + /* read the div-by-zero handler function address */ >> + kdd_read_virtual(s, s->cpuid, ctrl.c64.idt_base + 8, 8, &buf); >> + idt0_addr = ((uint64_t)buf << 32) & 0xffffffff00000000; >> + >> + kdd_read_virtual(s, s->cpuid, ctrl.c64.idt_base, 8, &buf); >> + idt0_addr |= ((buf >> 32) & 0xffff0000); >> + idt0_addr |= (buf & 0xffff); >> + >> + KDD_LOG(s, "idt0 addr: %p\n", (void *)idt0_addr); >> + >> + /* >> + * get the page start and look for "MZ" file header - we limit the >> search >> + * in 1GB range above the current page base address >> + */ >> + >> + base = idt0_addr & ~(PAGE_SIZE - 1); >> + KDD_LOG(s, "%p\n", (void *)base); >> + >> + while (search_limit) { >> + uint16_t val; >> + if (kdd_read_virtual(s, s->cpuid, base, 2, &val) != 2) { >> + /* just move going back?? this is bad though */ >> + KDD_LOG(s, "ran into unmapped region without finding PE >> header\n"); >> + goto fail; >> + } >> + >> + if (val == MZ_HEADER) // MZ >> + break; >> + >> + base -= PAGE_SIZE; >> + search_limit -= 1; >> + } >> + >> + KDD_LOG(s, "base: %p\n", (void *)base); >> + >> + /* found the data section start */ >> + if (get_pe64_sections(s, base, ".data", &data_base, &data_size)) >> + goto fail; >> + >> + /* look for addresses which has kernel base written into it */ >> + caddr = data_base; >> + >> + search_limit = (1024 * 1024 * 512) / SIZE_PTR64; >> + while (caddr < data_base + data_size && search_limit) { >> + if (kdd_read_virtual(s, s->cpuid, caddr, SIZE_PTR64, &buf) != >> + SIZE_PTR64) >> + goto fail; /* reached end and found nothing */ >> + >> + /* if we found base in the memory addresses */ >> + if (buf == base) { >> + /* read the DBGKD_GET_VERSION64 struct */ >> + dbgkd_addr = caddr - offsetof(DBGKD_GET_VERSION64, KernBase); >> + if (kdd_read_virtual(s, s->cpuid, dbgkd_addr, >> + sizeof(DBGKD_GET_VERSION64), >> &dbgkd_get_version64) == >> + sizeof(DBGKD_GET_VERSION64)) { >> + /* check if major version is 0xf */ >> + if (dbgkd_get_version64.MajorVersion == >> NT_MAJOR_VERSION) { >> + >> + /* read minor version, PsLoadedModuleList pointer and >> + * DebuggerDataList >> + */ >> + modptr = dbgkd_get_version64.PsLoadedModuleList; >> + kddl = dbgkd_get_version64.DebuggerDataList; >> + minor = dbgkd_get_version64.MinorVersion; >> + >> + /* do heuristic check */ >> + if (modptr && kddl && modptr != kddl && kddl != base >> && >> + base != modptr && modptr >= data_base && >> + modptr < (data_base + data_size) && >> + kddl >= data_base && >> + kddl < (data_base + data_size)) >> + break; >> + } >> + } >> + >> + } >> + >> + caddr += SIZE_PTR64; >> + search_limit -= 1; >> + } >> + >> + if (caddr < data_base + data_size) { >> + /* if found, set the field and return */ >> + >> + KDD_LOG(s, "base: %p\n", (void *)base); >> + KDD_LOG(s, "modules list: %p\n", (void *)modptr); >> + KDD_LOG(s, "kddl: %p\n", (void *)kddl); >> + KDD_LOG(s, "minor version: 0x%hx\n", minor); >> + >> + s->os.base = base; >> + s->os.modules = modptr - base; >> + s->os.kddl = kddl - base; >> + s->os.build = (uint32_t) minor; >> + return; >> + } >> + >> +fail: >> + s->os = unknown_os; >> +} >> + >> /* Figure out what OS we're dealing with */ >> static void find_os(kdd_state *s) >> { >> int i; >> - uint64_t limit; >> + uint64_t limit; >> >> /* We may already have the right one */ >> if (check_os(s)) >> @@ -387,7 +725,8 @@ static void find_os(kdd_state *s) >> if (check_os(s)) >> return; >> } >> - s->os = unknown_os; >> + >> + get_os_info_64(s); >> } >> >> >> @@ -534,13 +873,14 @@ static void kdd_handle_handshake(kdd_state *s) >> { >> /* Figure out what we're looking at */ >> find_os(s); >> + >> kdd_send_string(s, "[kdd: %s @0x%"PRIx64"]\r\n", s->os.name, >> s->os.base); >> >> /* Respond with some details about the debugger stub we simulate */ >> s->txp.cmd.shake.u1 = 0x01010101; >> s->txp.cmd.shake.status = KDD_STATUS_SUCCESS; >> s->txp.cmd.shake.u2 = 0x02020202; >> - s->txp.cmd.shake.v_major = 0xf; >> + s->txp.cmd.shake.v_major = NT_MAJOR_VERSION; >> s->txp.cmd.shake.v_minor = s->os.build; >> s->txp.cmd.shake.proto = 6; >> s->txp.cmd.shake.flags = (0x02 /* ??? */ >> @@ -555,7 +895,7 @@ static void kdd_handle_handshake(kdd_state *s) >> s->txp.cmd.shake.u3[2] = 0x55; >> s->txp.cmd.shake.kern_addr = s->os.base; >> s->txp.cmd.shake.mods_addr = s->os.base + s->os.modules; >> - s->txp.cmd.shake.data_addr = 0; /* Debugger data probably doesn't >> exist */ >> + s->txp.cmd.shake.data_addr = s->os.kddl ? s->os.base + s->os.kddl : >> 0; >> >> KDD_LOG(s, "Client initial handshake: %s\n", s->os.name); >> kdd_send_cmd(s, KDD_CMD_SHAKE, 0); >> -- >> 2.17.1 >> >> >> _______________________________________________ >> Xen-devel mailing list >> Xen-devel@lists.xenproject.org >> https://lists.xenproject.org/mailman/listinfo/xen-devel > >
On 11/13/19 10:55 PM, Julian Tuminaro wrote: > From: Julian Tuminaro and Jenish Rakholiya <julian.tuminaro@gmail.com and rakholiyajenish.07@gmail.com> > > Current implementation of find_os is based on the hard-coded values for > different Windows version. It uses the value for get the address to > start looking for DOS header in the given specified range. However, this > is not scalable to all version of Windows as it will require us to keep > adding new entries and also due to KASLR, chances of not hitting the PE > header is significant. We implement a way for 64-bit systems to use IDT > entry to get a valid exception/interrupt handler and then move back into > the memory to find the valid DOS header. Since IDT entries are protected > by PatchGuard, we think our assumption that IDT entries will not be > corrupted is valid for our purpose. Once we have the image base, we > search for the DBGKD_GET_VERSION64 structure type in .data section to > get information required for handshake. > > Currently, this is a work in progress feature and current patch only > supports the handshake and memory read/write on 64-bit systems. > > NOTE: This is the Updated version of the previous patch submitted > NOTE: This has currently been only tested when debugging was not enabled > on the guest Windows. > > Signed-off-by: Jenish Rakholiya <rjenish@cmu.edu> > Signed-off-by: Julian Tuminaro <jtuminar@andrew.cmu.edu> > --- This commit has broken the build of the staging tree. For a full log see: https://gitlab.com/xen-project/xen/-/jobs/365398313#L5184 But the relevant bit is likely: || |gcc -m32 -march=i686 -DBUILD_ID -fno-strict-aliasing -std=gnu99 -Wall -Wstrict-prototypes -Wdeclaration-after-statement -Wno-unused-but-set-variable -Wno-unused-local-typedefs -O2 -fomit-frame-pointer -D__XEN_INTERFACE_VERSION__=__XEN_LATEST_INTERFACE_VERSION__ -MMD -MF .kdd-xen.o.d -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -mno-tls-direct-seg-refs -Werror -I/builds/xen-project/xen/tools/debugger/kdd/../../../tools/libxc/include -I/builds/xen-project/xen/tools/debugger/kdd/../../../tools/libs/toollog/include -I/builds/xen-project/xen/tools/debugger/kdd/../../../tools/include -I/builds/xen-project/xen/tools/debugger/kdd/../../../tools/libs/foreignmemory/include -I/builds/xen-project/xen/tools/debugger/kdd/../../../tools/include -I/builds/xen-project/xen/tools/debugger/kdd/../../../tools/libs/devicemodel/include -I/builds/xen-project/xen/tools/debugger/kdd/../../../tools/include -I/builds/xen-project/xen/tools/debugger/kdd/../../../tools/include -D__XEN_TOOLS__ -DXC_WANT_COMPAT_MAP_FOREIGN_API -c -o kdd-xen.o kdd-xen.c | | In file included from kdd.c:53:0: kdd.c: In function 'get_os_info_64': kdd.c:616:35: error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast] KDD_LOG(s, "idt0 addr: %p\n", (void *)idt0_addr); |
On Fri, Nov 15, 2019 at 1:31 PM Paul Durrant <pdurrant@gmail.com> wrote: > On Thu, 14 Nov 2019 at 04:57, Julian Tuminaro <julian.tuminaro@gmail.com> > wrote: > > > > From: Julian Tuminaro and Jenish Rakholiya <julian.tuminaro@gmail.com > and rakholiyajenish.07@gmail.com> > > > > Current implementation of find_os is based on the hard-coded values for > > different Windows version. It uses the value for get the address to > > start looking for DOS header in the given specified range. However, this > > is not scalable to all version of Windows as it will require us to keep > > adding new entries and also due to KASLR, chances of not hitting the PE > > header is significant. We implement a way for 64-bit systems to use IDT > > entry to get a valid exception/interrupt handler and then move back into > > the memory to find the valid DOS header. Since IDT entries are protected > > by PatchGuard, we think our assumption that IDT entries will not be > > corrupted is valid for our purpose. Once we have the image base, we > > search for the DBGKD_GET_VERSION64 structure type in .data section to > > get information required for handshake. > > > > Currently, this is a work in progress feature and current patch only > > supports the handshake and memory read/write on 64-bit systems. > > > > NOTE: This is the Updated version of the previous patch submitted > > NOTE: This has currently been only tested when debugging was not enabled > > on the guest Windows. > > > > Signed-off-by: Jenish Rakholiya <rjenish@cmu.edu> > > Signed-off-by: Julian Tuminaro <jtuminar@andrew.cmu.edu> > > LGTM. > > Reviewed-by: Paul Durrant <paul@xen.org> > Paul, is this something worth adding a line to CHANGELOG about? -George
De-htmling... ----- From: Xen-devel <xen-devel-bounces@lists.xenproject.org> On Behalf Of George Dunlap Sent: 09 June 2020 15:42 To: Paul Durrant <pdurrant@gmail.com> Cc: Wei Liu <wei.liu2@citrix.com>; Tim Deegan <tim@xen.org>; Jenish Rakholiya <rjenish@cmu.edu>; Ian Jackson <ian.jackson@eu.citrix.com>; Julian Tuminaro <jtuminar@andrew.cmu.edu>; George Dunlap <george.dunlap@citrix.com>; xen-devel <xen-devel@lists.xenproject.org>; Julian Tuminaro <julian.tuminaro@gmail.com> Subject: Re: [Xen-devel] [PATCH V2] kdd.c: Add support for initial handshake in KD protocol for Win 7, 8 and 10 (64 bit) On Fri, Nov 15, 2019 at 1:31 PM Paul Durrant <mailto:pdurrant@gmail.com> wrote: On Thu, 14 Nov 2019 at 04:57, Julian Tuminaro <mailto:julian.tuminaro@gmail.com> wrote: > > From: Julian Tuminaro and Jenish Rakholiya <mailto:julian.tuminaro@gmail.com and mailto:rakholiyajenish.07@gmail.com> > > Current implementation of find_os is based on the hard-coded values for > different Windows version. It uses the value for get the address to > start looking for DOS header in the given specified range. However, this > is not scalable to all version of Windows as it will require us to keep > adding new entries and also due to KASLR, chances of not hitting the PE > header is significant. We implement a way for 64-bit systems to use IDT > entry to get a valid exception/interrupt handler and then move back into > the memory to find the valid DOS header. Since IDT entries are protected > by PatchGuard, we think our assumption that IDT entries will not be > corrupted is valid for our purpose. Once we have the image base, we > search for the DBGKD_GET_VERSION64 structure type in .data section to > get information required for handshake. > > Currently, this is a work in progress feature and current patch only > supports the handshake and memory read/write on 64-bit systems. > > NOTE: This is the Updated version of the previous patch submitted > NOTE: This has currently been only tested when debugging was not enabled > on the guest Windows. > > Signed-off-by: Jenish Rakholiya <mailto:rjenish@cmu.edu> > Signed-off-by: Julian Tuminaro <mailto:jtuminar@andrew.cmu.edu> LGTM. Reviewed-by: Paul Durrant <mailto:paul@xen.org> Paul, is this something worth adding a line to CHANGELOG about? -George ----- Yes, I'd completely forgotten this had fallen in the 4.14 timeline. I'll send a patch. Paul
diff --git a/tools/debugger/kdd/kdd.c b/tools/debugger/kdd/kdd.c index fb8c645355..6d3febefda 100644 --- a/tools/debugger/kdd/kdd.c +++ b/tools/debugger/kdd/kdd.c @@ -41,6 +41,7 @@ #include <errno.h> #include <inttypes.h> #include <netdb.h> +#include <stddef.h> #include <sys/socket.h> #include <sys/types.h> @@ -51,6 +52,16 @@ #include "kdd.h" +/* + * TODO: kdd_os is a type which is used to represent os array. Adding a + * variable here would result in adding a new field to each element in array. + * However, since most of the fields are part of the same struct that we are + * trying to read from memory, we have added kddl to this structure. If + * required, we can possibly separate the kddl value to someplace else + * + * We also use kddl of size uint32_t which is actually used to represent the + * offset from image base rather than actual address + */ /* Windows version details */ typedef struct { uint32_t build; @@ -62,6 +73,7 @@ typedef struct { uint32_t version; /* +-> NtBuildNumber */ uint32_t modules; /* +-> PsLoadedModuleList */ uint32_t prcbs; /* +-> KiProcessorBlock */ + uint32_t kddl; /* +-> KdDebuggerList */ } kdd_os; /* State of the debugger stub */ @@ -85,6 +97,117 @@ typedef struct { kdd_os os; /* OS-specific magic numbers */ } kdd_state; +/** + * @brief Structure to represent DBGKD_GET_VERSION64 + * + * reference: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdbgexts/ns-wdbgexts-_dbgkd_get_version64 + */ +typedef struct { + uint16_t MajorVersion; /* usually 0xf for free build */ + uint16_t MinorVersion; /* build number of target OS */ + uint8_t ProtocolVersion; /* version of the debugger protocol */ + uint8_t KdSecondaryVersion; /* secondary version number */ + uint16_t Flags; /* set of bit flags for the current debugging session */ + uint16_t MachineType; /* type of the target's processor */ + uint8_t MaxPacketType; /* one plus the highest number for a debugger */ + /* packet type recognized by the target */ + uint8_t MaxStateChagne; /* one plus the highest number for a state */ + /* change generated by the target */ + uint8_t MaxManipulate; /* one more that the highest number, recognized */ + /* by the target, for a command to manipulate the target */ + uint8_t Simulation; /* indication if target is in simulated execution */ + uint16_t Unused[1]; + uint64_t KernBase; /* base address of the kernel image */ + uint64_t PsLoadedModuleList; /* value of the kernel variable */ + /* PsLoadedModuleList */ + uint64_t DebuggerDataList; /* value of the kernel variable */ + /* KdDebuggerDataBlock */ +} PACKED DBGKD_GET_VERSION64; + +/** + * @brief Structure to represent the section in PE headers + * + * reference: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#section-table-section-headers + */ +typedef struct { + uint8_t Name[8]; /* name of section */ + uint32_t VirtualSize; /* total size of section in memory */ + uint32_t VirtualAddr; /* offset from image base */ + uint32_t SizeOfRawData; /* size of section in for object files */ + uint32_t PointerToRawData; /* file pointer to first page in COFF */ + uint32_t PointerToRelocations; /* file pointer to beginning of relocation entry */ + uint32_t PointerToLinenumbers; /* file pointer to the beginning of line-number entries */ + uint16_t NumberOfRelocations; /* number of relocation entries for the section */ + uint16_t NumberOfLinenumbers; /* number of line-number entries for the section */ + uint32_t Characteristics; /* flags that describe the characteristics of the section */ +} PACKED PE_SECTION_ENTRY; + +/** + * @brief Size of pointer on 64 machine + */ +#define SIZE_PTR64 8 + +/** + * @brief Size of pointer on 32 machine + */ +#define SIZE_PTR32 4 + + +/***************************************************************************** + * PE and DOS Header related offsets + */ + +/** + * @brief Offset in DOS header to look for PE header + */ +#define DOS_HDR_PE_OFF 0x3c + +/** + * @brief Size of PE header offset field in DOS header + */ +#define DOS_HDR_PE_SZ 4 + +/** + * @brief Offset of number of sections field in PE header + */ +#define PE_NUM_SECTION_OFF 0x6 + +/** + * @brief Size of number of sections field in PE header + */ +#define PE_NUM_SECTION_SZ 2 + +/** + * @brief Offset of optional header size field in PE header + */ +#define PE_OPT_HDR_SZ_OFF 0x14 + +/** + * @brief Size of optional header size field in PE header + */ +#define PE_OPT_HDR_SZ_SZ 2 + +/** + * @brief Size of PE header + */ +#define PE_HDR_SZ 0x18 + +/** + * @brief MZ header + */ +#define MZ_HEADER 0x5a4d + +/** + * @brief Limit on the number of sections to look for while iterating through + * PE sections + */ +#define NUM_SECT_LIMIT 100 + +/** + * @brief Major Version for the DBGKD_GET_VERSION64 structure + */ +#define NT_MAJOR_VERSION 0xf + /***************************************************************************** * Utility functions */ @@ -293,41 +416,41 @@ static uint32_t kdd_write_virtual(kdd_state *s, int cpuid, uint64_t addr, */ static kdd_os os[] = { - /* Build 64 MP Name &Kernel search base Range +Version +Modules +PRCBs (64b) */ - {2195, 0, 0, "w2k sp4 x32 UP", 0xffffffff80400000ULL, 0x00000000, 0x0006d57c, 0x0006e1b8, 0x0}, - {2195, 0, 1, "w2k sp4 x32 SMP", 0xffffffff80400000ULL, 0x00000000, 0x0006fa1c, 0x00084520, 0x0}, + /* Build 64 MP Name &Kernel search base Range +Version +Modules +PRCBs (64b) +KDDL */ + {2195, 0, 0, "w2k sp4 x32 UP", 0xffffffff80400000ULL, 0x00000000, 0x0006d57c, 0x0006e1b8, 0x0, 0}, + {2195, 0, 1, "w2k sp4 x32 SMP", 0xffffffff80400000ULL, 0x00000000, 0x0006fa1c, 0x00084520, 0x0, 0}, // PAE/UP, PAE/SMP - {2600, 0, 0, "xp sp2 x32 UP", 0xffffffff804d7000ULL, 0x00000000, 0x00075568, 0x00083b20, 0x0}, - {2600, 0, 1, "xp sp2 x32 SMP", 0xffffffff804d7000ULL, 0x00000000, 0x0007d0e8, 0x0008d4a0, 0x0}, + {2600, 0, 0, "xp sp2 x32 UP", 0xffffffff804d7000ULL, 0x00000000, 0x00075568, 0x00083b20, 0x0, 0}, + {2600, 0, 1, "xp sp2 x32 SMP", 0xffffffff804d7000ULL, 0x00000000, 0x0007d0e8, 0x0008d4a0, 0x0, 0}, // PAE/UP, PAE/SMP - {2600, 0, 0, "xp sp3 x32 UP", 0xffffffff804d7000ULL, 0x00000000, 0x00075be8, 0x000841c0, 0x0}, - {2600, 0, 1, "xp sp3 x32 SMP", 0xffffffff804d7000ULL, 0x00000000, 0x0007c0e8, 0x0008c4c0, 0x0}, - {2600, 0, 0, "xp sp3 x32p UP", 0xffffffff804d7000ULL, 0x00000000, 0x0006e8e8, 0x0007cfc0, 0x0}, - {2600, 0, 1, "xp sp3 x32p SMP", 0xffffffff804d7000ULL, 0x00000000, 0x000760e8, 0x00086720, 0x0}, + {2600, 0, 0, "xp sp3 x32 UP", 0xffffffff804d7000ULL, 0x00000000, 0x00075be8, 0x000841c0, 0x0, 0}, + {2600, 0, 1, "xp sp3 x32 SMP", 0xffffffff804d7000ULL, 0x00000000, 0x0007c0e8, 0x0008c4c0, 0x0, 0}, + {2600, 0, 0, "xp sp3 x32p UP", 0xffffffff804d7000ULL, 0x00000000, 0x0006e8e8, 0x0007cfc0, 0x0, 0}, + {2600, 0, 1, "xp sp3 x32p SMP", 0xffffffff804d7000ULL, 0x00000000, 0x000760e8, 0x00086720, 0x0, 0}, - {3790, 0, 0, "w2k3 sp2 x32 UP", 0xffffffff80800000ULL, 0x00000000, 0x00097128, 0x000a8e48, 0x0}, - {3790, 0, 1, "w2k3 sp2 x32 SMP", 0xffffffff80800000ULL, 0x00000000, 0x0009d128, 0x000af9c8, 0x0}, - {3790, 0, 0, "w2k3 sp2 x32p UP", 0xffffffff80800000ULL, 0x00000000, 0x0008e128, 0x0009ffa8, 0x0}, - {3790, 0, 1, "w2k3 sp2 x32p SMP", 0xffffffff80800000ULL, 0x00000000, 0x00094128, 0x000a6ea8, 0x0}, - {3790, 1, 0, "w2k3 sp2 x64 UP", 0xfffff80001000000ULL, 0x00000000, 0x001765d0, 0x0019aae0, 0x0017b100}, - {3790, 1, 1, "w2k3 sp2 x64 SMP", 0xfffff80001000000ULL, 0x00000000, 0x001b05e0, 0x001d5100, 0x001b5300}, + {3790, 0, 0, "w2k3 sp2 x32 UP", 0xffffffff80800000ULL, 0x00000000, 0x00097128, 0x000a8e48, 0x0, 0}, + {3790, 0, 1, "w2k3 sp2 x32 SMP", 0xffffffff80800000ULL, 0x00000000, 0x0009d128, 0x000af9c8, 0x0, 0}, + {3790, 0, 0, "w2k3 sp2 x32p UP", 0xffffffff80800000ULL, 0x00000000, 0x0008e128, 0x0009ffa8, 0x0, 0}, + {3790, 0, 1, "w2k3 sp2 x32p SMP", 0xffffffff80800000ULL, 0x00000000, 0x00094128, 0x000a6ea8, 0x0, 0}, + {3790, 1, 0, "w2k3 sp2 x64 UP", 0xfffff80001000000ULL, 0x00000000, 0x001765d0, 0x0019aae0, 0x0017b100, 0}, + {3790, 1, 1, "w2k3 sp2 x64 SMP", 0xfffff80001000000ULL, 0x00000000, 0x001b05e0, 0x001d5100, 0x001b5300, 0}, - {6000, 0, 1, "vista sp0 x32p", 0xffffffff81800000ULL, 0x00000000, 0x000a4de4, 0x00111db0, 0x0}, - {6001, 0, 1, "vista sp1 x32p", 0xffffffff81000000ULL, 0x0f000000, 0x000af0c4, 0x00117c70, 0x0}, + {6000, 0, 1, "vista sp0 x32p", 0xffffffff81800000ULL, 0x00000000, 0x000a4de4, 0x00111db0, 0x0, 0}, + {6001, 0, 1, "vista sp1 x32p", 0xffffffff81000000ULL, 0x0f000000, 0x000af0c4, 0x00117c70, 0x0, 0}, - {6001, 1, 1, "w2k8 sp0 x64", 0xfffff80001000000ULL, 0x0f000000, 0x00140bf0, 0x001c5db0, 0x00229640}, + {6001, 1, 1, "w2k8 sp0 x64", 0xfffff80001000000ULL, 0x0f000000, 0x00140bf0, 0x001c5db0, 0x00229640, 0}, - {7600, 1, 1, "win7 sp0 x64", 0xfffff80001000000ULL, 0x0f000000, 0x001af770, 0x0023de50, 0x002a8900}, + {7600, 1, 1, "win7 sp0 x64", 0xfffff80001000000ULL, 0x0f000000, 0x001af770, 0x0023de50, 0x002a8900, 0}, - {7601, 0, 1, "win7 sp1 x32p", 0xffffffff81800000ULL, 0x0f000000, 0x000524c4, 0x00149850, 0x0}, - {7601, 1, 1, "win7 sp1 x64", 0xfffff80001000000ULL, 0x0f000000, 0x001b2770, 0x00240e90, 0x002ab900}, + {7601, 0, 1, "win7 sp1 x32p", 0xffffffff81800000ULL, 0x0f000000, 0x000524c4, 0x00149850, 0x0, 0}, + {7601, 1, 1, "win7 sp1 x64", 0xfffff80001000000ULL, 0x0f000000, 0x001b2770, 0x00240e90, 0x002ab900, 0}, }; // 1381, 0, 0, "NT4 sp?", 0xffffffff80100000, ?, ? -static kdd_os unknown_os = {0, 0, 0, "unknown OS", 0, 0, 0, 0, 0}; +static kdd_os unknown_os = {0, 0, 0, "unknown OS", 0, 0, 0, 0, 0, 0}; static int check_os(kdd_state *s) { @@ -367,11 +490,226 @@ static int check_os(kdd_state *s) return 1; } +/** + * @brief Parse the memory at \a filebase as a valid DOS header and get virtual + * address offset and size for any given section name (if it exists) + * + * @param s Pointer to the kdd_state structure + * @param filebase Base address of the file structure + * @param sectname Pointer to the section name c-string to look for + * @param vaddr Pointer to write the virtual address of section start to + * (if found) + * @param visze Pointer to write the section size to (if found) + * + * @return -1 on failure to find the section name + * @return 0 on success + */ +static int get_pe64_sections(kdd_state *s, uint64_t filebase, char *sectname, + uint64_t *vaddr, uint32_t *vsize) +{ + uint64_t pe_hdr = 0; + uint64_t sect_start = 0; + uint16_t num_sections = 0; + uint16_t opt_hdr_sz = 0; + PE_SECTION_ENTRY pe_sect; + + if (!s->os.w64) + return -1; + + /* read PE header offset */ + if (kdd_read_virtual(s, s->cpuid, filebase + DOS_HDR_PE_OFF, DOS_HDR_PE_SZ, + &pe_hdr) != DOS_HDR_PE_SZ) + return -1; + + pe_hdr += filebase; + + /* read number of sections */ + if (kdd_read_virtual(s, s->cpuid, pe_hdr + PE_NUM_SECTION_OFF, + PE_NUM_SECTION_SZ, &num_sections) != PE_NUM_SECTION_SZ) + return -1; + + /* read number of section upto a limit */ + if (num_sections > NUM_SECT_LIMIT) + num_sections = NUM_SECT_LIMIT; + + /* read size of optional header */ + if (kdd_read_virtual(s, s->cpuid, pe_hdr + PE_OPT_HDR_SZ_OFF, + PE_OPT_HDR_SZ_SZ, &opt_hdr_sz) != PE_OPT_HDR_SZ_SZ) + return -1; + + /* 0x18 is the size of PE header */ + sect_start = pe_hdr + PE_HDR_SZ + opt_hdr_sz; + + for (int i = 0; i < num_sections; i++) { + if (kdd_read_virtual(s, s->cpuid, sect_start + (i * sizeof(pe_sect)), + sizeof(pe_sect), &pe_sect) != sizeof(pe_sect)) + return -1; + + if (!strncmp(sectname, (char *)pe_sect.Name, sizeof(pe_sect.Name))) { + *vaddr = filebase + pe_sect.VirtualAddr; + *vsize = pe_sect.VirtualSize; + return 0; + } + } + + return -1; +} + +/** + * @brief Get the OS information like base address, minor version, + * PsLoadedModuleList and DebuggerDataList (basically the fields of + * DBGKD_GET_VERSION64 struture required to do handshake?). + * + * This is done by reading the IDT entry for divide-by-zero exception and + * searching back into the memory for DOS header (which is our kernel base). + * Once we have the kernel base, we parse the PE header and look for kernel + * base address in the .data section. Once we have possible values, we look for + * DBGKD_GET_VERSION64 block by using following heuristics on the address which + * has the kernel base: + * + * - at address [-0x10], it should have 0xf as the MajorVersion + * - at address [+0x8], it should have a valid kernel memory address pointing + * in .data + * - at address [+0x10], it should have a valid kernel memory address pointing + * in .data + * + * @param s Pointer to the kdd state + */ +static void get_os_info_64(kdd_state *s) +{ + kdd_ctrl ctrl; + int ret; + uint64_t buf; + uint64_t idt0_addr; + uint64_t base; + uint64_t caddr; + uint64_t data_base; + uint32_t data_size; + uint64_t modptr = 0; + uint64_t kddl = 0; + uint16_t minor = 0; + uint64_t dbgkd_addr; + DBGKD_GET_VERSION64 dbgkd_get_version64; + /* Maybe 1GB is too big for the limit to search? */ + uint32_t search_limit = (1024 * 1024 * 1024) / PAGE_SIZE; /*1GB/PageSize*/ + uint64_t efer; + + /* if we are not in 64-bit mode, fail */ + if (kdd_rdmsr(s->guest, s->cpuid, 0xc0000080, &efer) || !(efer & (1 << 8))) + goto fail; + + s->os.w64 = 1; + + /* get control registers for our os */ + ret = kdd_get_ctrl(s->guest, s->cpuid, &ctrl, s->os.w64); + if (ret) + goto fail; + + /* read the div-by-zero handler function address */ + kdd_read_virtual(s, s->cpuid, ctrl.c64.idt_base + 8, 8, &buf); + idt0_addr = ((uint64_t)buf << 32) & 0xffffffff00000000; + + kdd_read_virtual(s, s->cpuid, ctrl.c64.idt_base, 8, &buf); + idt0_addr |= ((buf >> 32) & 0xffff0000); + idt0_addr |= (buf & 0xffff); + + KDD_LOG(s, "idt0 addr: %p\n", (void *)idt0_addr); + + /* + * get the page start and look for "MZ" file header - we limit the search + * in 1GB range above the current page base address + */ + + base = idt0_addr & ~(PAGE_SIZE - 1); + KDD_LOG(s, "%p\n", (void *)base); + + while (search_limit) { + uint16_t val; + if (kdd_read_virtual(s, s->cpuid, base, 2, &val) != 2) { + /* just move going back?? this is bad though */ + KDD_LOG(s, "ran into unmapped region without finding PE header\n"); + goto fail; + } + + if (val == MZ_HEADER) // MZ + break; + + base -= PAGE_SIZE; + search_limit -= 1; + } + + KDD_LOG(s, "base: %p\n", (void *)base); + + /* found the data section start */ + if (get_pe64_sections(s, base, ".data", &data_base, &data_size)) + goto fail; + + /* look for addresses which has kernel base written into it */ + caddr = data_base; + + search_limit = (1024 * 1024 * 512) / SIZE_PTR64; + while (caddr < data_base + data_size && search_limit) { + if (kdd_read_virtual(s, s->cpuid, caddr, SIZE_PTR64, &buf) != + SIZE_PTR64) + goto fail; /* reached end and found nothing */ + + /* if we found base in the memory addresses */ + if (buf == base) { + /* read the DBGKD_GET_VERSION64 struct */ + dbgkd_addr = caddr - offsetof(DBGKD_GET_VERSION64, KernBase); + if (kdd_read_virtual(s, s->cpuid, dbgkd_addr, + sizeof(DBGKD_GET_VERSION64), &dbgkd_get_version64) == + sizeof(DBGKD_GET_VERSION64)) { + /* check if major version is 0xf */ + if (dbgkd_get_version64.MajorVersion == NT_MAJOR_VERSION) { + + /* read minor version, PsLoadedModuleList pointer and + * DebuggerDataList + */ + modptr = dbgkd_get_version64.PsLoadedModuleList; + kddl = dbgkd_get_version64.DebuggerDataList; + minor = dbgkd_get_version64.MinorVersion; + + /* do heuristic check */ + if (modptr && kddl && modptr != kddl && kddl != base && + base != modptr && modptr >= data_base && + modptr < (data_base + data_size) && + kddl >= data_base && + kddl < (data_base + data_size)) + break; + } + } + + } + + caddr += SIZE_PTR64; + search_limit -= 1; + } + + if (caddr < data_base + data_size) { + /* if found, set the field and return */ + + KDD_LOG(s, "base: %p\n", (void *)base); + KDD_LOG(s, "modules list: %p\n", (void *)modptr); + KDD_LOG(s, "kddl: %p\n", (void *)kddl); + KDD_LOG(s, "minor version: 0x%hx\n", minor); + + s->os.base = base; + s->os.modules = modptr - base; + s->os.kddl = kddl - base; + s->os.build = (uint32_t) minor; + return; + } + +fail: + s->os = unknown_os; +} + /* Figure out what OS we're dealing with */ static void find_os(kdd_state *s) { int i; - uint64_t limit; + uint64_t limit; /* We may already have the right one */ if (check_os(s)) @@ -387,7 +725,8 @@ static void find_os(kdd_state *s) if (check_os(s)) return; } - s->os = unknown_os; + + get_os_info_64(s); } @@ -534,13 +873,14 @@ static void kdd_handle_handshake(kdd_state *s) { /* Figure out what we're looking at */ find_os(s); + kdd_send_string(s, "[kdd: %s @0x%"PRIx64"]\r\n", s->os.name, s->os.base); /* Respond with some details about the debugger stub we simulate */ s->txp.cmd.shake.u1 = 0x01010101; s->txp.cmd.shake.status = KDD_STATUS_SUCCESS; s->txp.cmd.shake.u2 = 0x02020202; - s->txp.cmd.shake.v_major = 0xf; + s->txp.cmd.shake.v_major = NT_MAJOR_VERSION; s->txp.cmd.shake.v_minor = s->os.build; s->txp.cmd.shake.proto = 6; s->txp.cmd.shake.flags = (0x02 /* ??? */ @@ -555,7 +895,7 @@ static void kdd_handle_handshake(kdd_state *s) s->txp.cmd.shake.u3[2] = 0x55; s->txp.cmd.shake.kern_addr = s->os.base; s->txp.cmd.shake.mods_addr = s->os.base + s->os.modules; - s->txp.cmd.shake.data_addr = 0; /* Debugger data probably doesn't exist */ + s->txp.cmd.shake.data_addr = s->os.kddl ? s->os.base + s->os.kddl : 0; KDD_LOG(s, "Client initial handshake: %s\n", s->os.name); kdd_send_cmd(s, KDD_CMD_SHAKE, 0);