diff mbox series

[V2] kdd.c: Add support for initial handshake in KD protocol for Win 7, 8 and 10 (64 bit)

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

Commit Message

Julian Tuminaro Nov. 14, 2019, 4:55 a.m. UTC
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>
---
 tools/debugger/kdd/kdd.c | 392 ++++++++++++++++++++++++++++++++++++---
 1 file changed, 366 insertions(+), 26 deletions(-)

Comments

Tim Deegan Nov. 14, 2019, 10:59 p.m. UTC | #1
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>
Paul Durrant Nov. 15, 2019, 1:30 p.m. UTC | #2
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
>
Wei Liu Nov. 15, 2019, 2:37 p.m. UTC | #3
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.
Julien Grall Nov. 16, 2019, 2:44 a.m. UTC | #4
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
Julien Grall Nov. 16, 2019, 2:46 a.m. UTC | #5
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
>
>
Douglas Goldstein Nov. 30, 2019, 12:18 a.m. UTC | #6
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);
|
George Dunlap June 9, 2020, 2:42 p.m. UTC | #7
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
Paul Durrant June 9, 2020, 3:06 p.m. UTC | #8
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 mbox series

Patch

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);