DBGPlugInWinNt.cpp revision e9525bea57dc13d82fd3392913aebb33d2cb79e3
/* $Id$ */
/** @file
* DBGPlugInWindows - Debugger and Guest OS Digger Plugin For Windows NT.
*/
/*
* Copyright (C) 2009-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include "DBGPlugIns.h"
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/** @name Internal WinNT structures
* @{ */
/**
* PsLoadedModuleList entry for 32-bit NT aka LDR_DATA_TABLE_ENTRY.
* Tested with XP.
*/
typedef struct NTMTE32
{
struct
{
struct
{
} FullDllName,
/* ... there is more ... */
} NTMTE32;
/**
* PsLoadedModuleList entry for 32-bit NT aka LDR_DATA_TABLE_ENTRY.
* Tested with XP.
*
* @todo This is incomplete and just to get rid of warnings.
*/
typedef struct NTMTE64
{
struct
{
struct
{
} FullDllName,
/* ... there is more ... */
} NTMTE64;
/** MTE union. */
typedef union NTMTE
{
} NTMTE;
/**
* The essential bits of the KUSER_SHARED_DATA structure.
*/
typedef struct NTKUSERSHAREDDATA
{
struct
{
/* uint8_t ProcessorFeatures[64];
...
*/
typedef NTKUSERSHAREDDATA *PNTKUSERSHAREDDATA;
/** KI_USER_SHARED_DATA for i386 */
/** KI_USER_SHARED_DATA for AMD64 */
/** NTKUSERSHAREDDATA::NtProductType */
typedef enum NTPRODUCTTYPE
{
kNtProductType_WinNt = 1,
/** NT image header union. */
typedef union NTHDRSU
{
} NTHDRS;
/** Pointer to NT image header union. */
/** Pointer to const NT image header union. */
/** @} */
typedef enum DBGDIGGERWINNTVER
{
/**
* WinNT guest OS digger instance data.
*/
typedef struct DBGDIGGERWINNT
{
/** Whether the information is valid or not.
* (For fending off illegal interface method calls.) */
bool fValid;
/** 32-bit (true) or 64-bit (false) */
bool f32Bit;
/** The NT version. */
/** NTKUSERSHAREDDATA::NtProductType */
/** NTKUSERSHAREDDATA::NtMajorVersion */
/** NTKUSERSHAREDDATA::NtMinorVersion */
/** The address of the ntoskrnl.exe image. */
/** The address of the ntoskrnl.exe module table entry. */
/** The address of PsLoadedModuleList. */
/** Pointer to the linux guest OS digger instance data. */
typedef DBGDIGGERWINNT *PDBGDIGGERWINNT;
/**
* The WinNT digger's loader reader instance data.
*/
typedef struct DBGDIGGERWINNTRDR
{
/** The VM handle (referenced). */
/** The image base. */
/** The image size. */
/** The file offset of the SizeOfImage field in the optional header if it
* needs patching, otherwise set to UINT32_MAX. */
/** The correct image size. */
/** Number of entries in the aMappings table. */
/** Mapping hint. */
/** Mapping file offset to memory offsets, ordered by file offset. */
struct
{
/** The file offset. */
/** The size of this mapping. */
/** The offset to the memory from the start of the image. */
} aMappings[1];
/** Pointer a WinNT loader reader instance data. */
typedef DBGDIGGERWINNTRDR *PDBGDIGGERWINNTRDR;
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** Validates a 32-bit Windows NT kernel address */
#define WINNT32_VALID_ADDRESS(Addr) ((Addr) > UINT32_C(0x80000000) && (Addr) < UINT32_C(0xfffff000))
/** Validates a 64-bit Windows NT kernel address */
#define WINNT64_VALID_ADDRESS(Addr) ((Addr) > UINT64_C(0xffffffff80000000) && (Addr) < UINT64_C(0xfffffffffffff000))
/** Validates a kernel address. */
#define WINNT_VALID_ADDRESS(pThis, Addr) ((pThis)->f32Bit ? WINNT32_VALID_ADDRESS(Addr) : WINNT64_VALID_ADDRESS(Addr))
/** Versioned and bitness wrapper. */
#define WINNT_UNION(pThis, pUnion, Member) ((pThis)->f32Bit ? (pUnion)->vX_32. Member : (pUnion)->vX_64. Member )
/** The length (in chars) of the kernel file name (no path). */
#define WINNT_KERNEL_BASE_NAME_LEN 12
/** WindowsNT on little endian ASCII systems. */
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/** Kernel names. */
{
{ 'n', 't', 'o', 's', 'k', 'r', 'n', 'l', '.', 'e', 'x', 'e' }
};
/** @callback_method_impl{PFNRTLDRRDRMEMREAD} */
{
{
while (i-- > 0)
break;
}
while (cb > 0)
{
/* Read file bits backed by memory. */
{
if (RT_FAILURE(rc))
return rc;
/* Patch it? */
{
while (cbPatch-- > 0)
{
if (offPatch >= 0)
offPatch++;
pbPatch++;
SizeOfImage >>= 8;
}
}
/* Done? */
break;
}
/* Mind the gap. */
if (offNextMap > offFile)
{
{
break;
}
}
}
return VINF_SUCCESS;
}
/** @callback_method_impl{PFNRTLDRRDRMEMDTOR} */
{
}
/**
* Checks if the section headers look okay.
*
* @returns true / false.
* @param paShs Pointer to the section headers.
* @param cShs Number of headers.
* @param cbImage The image size.
*/
static bool dbgDiggerWinNtIsSectionHeaderOk(PCIMAGE_SECTION_HEADER paShs, uint32_t cShs, uint32_t cbImage)
{
{
{
Log(("DigWinNt: Section header #%u has no name\n", i));
return false;
}
continue;
{
Log(("DigWinNt: Section header #%u has a virtual address beyond the image: %#x cbImage=%#x\n",
return false;
}
if (paShs[i].Misc.VirtualSize >= cbImage) /* we don't check too strictly here becuase NT4 SP1 ntfs.sys. */
{
Log(("DigWinNt: Section header #%u has a end beyond the image: VirtualAddress=%#x VirtualSize=%#x cbImage=%#x\n",
return false;
}
}
return true;
}
/**
* Create a loader module for the in-guest-memory PE module.
*/
static int dbgDiggerWinNtCreateLdrMod(PDBGDIGGERWINNT pThis, PUVM pUVM, const char *pszName, PCDBGFADDRESS pImageAddr,
{
/*
* Allocate and create a reader instance.
*/
PDBGDIGGERWINNTRDR pRdr = (PDBGDIGGERWINNTRDR)RTMemAlloc(RT_OFFSETOF(DBGDIGGERWINNTRDR, aMappings[cShs + 2]));
if (!pRdr)
return VERR_NO_MEMORY;
/*
* Use the section table to construct a more accurate view of the file/
* image if it's in the buffer (it should be).
*/
{
if ( paShs[i].SizeOfRawData > 0
&& paShs[i].PointerToRawData > 0)
{
uint32_t j = 1;
else
{
j++;
memmove(&pRdr->aMappings[j + 1], &pRdr->aMappings[j], (pRdr->cMappings - j) * sizeof(pRdr->aMappings));
}
}
/* Insert the mapping of the headers that isn't covered by the section table. */
while (j-- > 0)
{
}
}
else
{
/*
* Fallback, fake identity mapped file data.
*/
}
/*
* Call the loader to open the PE image for debugging.
* Note! It always calls pfnDtor.
*/
&hLdrMod);
if (RT_SUCCESS(rc))
else
*phLdrMod = NIL_RTLDRMOD;
return rc;
}
/**
* Process a PE image found in guest memory.
*
* @param pThis The instance data.
* @param pUVM The user mode VM handle.
* @param pszName The image name.
* @param pImageAddr The image address.
* @param cbImage The size of the image.
* @param pbBuf Scratch buffer containing the first
* RT_MIN(cbBuf, cbImage) bytes of the image.
* @param cbBuf The scratch buffer size.
*/
{
/*
* Do some basic validation first.
* This is the usual exteremely verbose and messy code...
*/
if ( cbImage < sizeof(IMAGE_NT_HEADERS64)
{
return;
}
{
offHdrs = 0;
}
{
return;
}
{
}
else
{
return;
}
{
return;
}
/* The file header is the same on both archs */
if (pHdrs->vX_32.FileHeader.Machine != (pThis->f32Bit ? IMAGE_FILE_MACHINE_I386 : IMAGE_FILE_MACHINE_AMD64))
{
return;
}
if (pHdrs->vX_32.FileHeader.SizeOfOptionalHeader != (pThis->f32Bit ? sizeof(IMAGE_OPTIONAL_HEADER32) : sizeof(IMAGE_OPTIONAL_HEADER64)))
{
Log(("DigWinNt: %s: Invalid FH.SizeOfOptionalHeader: %#x\n", pszName, pHdrs->vX_32.FileHeader.SizeOfOptionalHeader));
return;
}
{
Log(("DigWinNt: %s: Too many sections: %#x\n", pszName, WINNT_UNION(pThis, pHdrs, FileHeader.NumberOfSections)));
return;
}
/* The optional header is not... */
if (WINNT_UNION(pThis, pHdrs, OptionalHeader.Magic) != (pThis->f32Bit ? IMAGE_NT_OPTIONAL_HDR32_MAGIC : IMAGE_NT_OPTIONAL_HDR64_MAGIC))
{
Log(("DigWinNt: %s: Invalid OH.Magic: %#x\n", pszName, WINNT_UNION(pThis, pHdrs, OptionalHeader.Magic)));
return;
}
{
Log(("DigWinNt: %s: Invalid OH.SizeOfImage: %#x, expected %#x\n", pszName, cbImageFromHdr, cbImage));
return;
}
if (WINNT_UNION(pThis, pHdrs, OptionalHeader.NumberOfRvaAndSizes) != IMAGE_NUMBEROF_DIRECTORY_ENTRIES)
{
Log(("DigWinNt: %s: Invalid OH.NumberOfRvaAndSizes: %#x\n", pszName, WINNT_UNION(pThis, pHdrs, OptionalHeader.NumberOfRvaAndSizes)));
return;
}
/*
* Create the module using the in memory image first, falling back
* on cached image.
*/
int rc = dbgDiggerWinNtCreateLdrMod(pThis, pUVM, pszName, pImageAddr, cbImage, pbBuf, cbBuf, offHdrs, pHdrs,
&hLdrMod);
if (RT_FAILURE(rc))
if (RT_FAILURE(rc))
{
/*
* Final fallback is a container module.
*/
if (RT_FAILURE(rc))
return;
}
/* Tag the module. */
/*
* Link the module.
*/
if (hAs != NIL_RTDBGAS)
else
}
/**
* @copydoc DBGFOSREG::pfnQueryInterface
*/
static DECLCALLBACK(void *) dbgDiggerWinNtQueryInterface(PUVM pUVM, void *pvData, DBGFOSINTERFACE enmIf)
{
return NULL;
}
/**
* @copydoc DBGFOSREG::pfnQueryVersion
*/
static DECLCALLBACK(int) dbgDiggerWinNtQueryVersion(PUVM pUVM, void *pvData, char *pszVersion, size_t cchVersion)
{
const char *pszNtProductType;
switch (pThis->NtProductType)
{
default: pszNtProductType = ""; break;
}
RTStrPrintf(pszVersion, cchVersion, "%u.%u%s", pThis->NtMajorVersion, pThis->NtMinorVersion, pszNtProductType);
return VINF_SUCCESS;
}
/**
* @copydoc DBGFOSREG::pfnTerm
*/
{
}
/**
* @copydoc DBGFOSREG::pfnRefresh
*/
{
/*
* For now we'll flush and reload everything.
*/
if (hDbgAs != NIL_RTDBGAS)
{
while (iMod-- > 0)
{
if (hMod != NIL_RTDBGMOD)
{
{
}
}
}
}
}
/**
* @copydoc DBGFOSREG::pfnInit
*/
{
union
{
} u;
int rc;
/*
* Figure the NT version.
*/
DBGFR3AddrFromFlat(pUVM, &Addr, pThis->f32Bit ? NTKUSERSHAREDDATA_WINNT32 : NTKUSERSHAREDDATA_WINNT64);
if (RT_FAILURE(rc))
return rc;
pThis->NtProductType = u.UserSharedData.ProductTypeIsValid && u.UserSharedData.NtProductType <= kNtProductType_Server
/*
* Dig out the module chain.
*/
do
{
/* Read the validate the MTE. */
rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &Addr, &Mte, pThis->f32Bit ? sizeof(Mte.vX_32) : sizeof(Mte.vX_64));
if (RT_FAILURE(rc))
break;
{
break;
}
{
break;
}
{
Log(("DigWinNt: Bad Mte at %RGv - BaseDllName=%llx\n", Addr.FlatPtr, WINNT_UNION(pThis, &Mte, BaseDllName.Buffer)));
break;
}
{
Log(("DigWinNt: Bad Mte at %RGv - FullDllName=%llx\n", Addr.FlatPtr, WINNT_UNION(pThis, &Mte, FullDllName.Buffer)));
break;
}
|| WINNT_UNION(pThis, &Mte, EntryPoint) - WINNT_UNION(pThis, &Mte, DllBase) > WINNT_UNION(pThis, &Mte, SizeOfImage) )
{
Log(("DigWinNt: Bad Mte at %RGv - EntryPoint=%llx SizeOfImage=%x DllBase=%llx\n",
Addr.FlatPtr, WINNT_UNION(pThis, &Mte, EntryPoint), WINNT_UNION(pThis, &Mte, SizeOfImage), WINNT_UNION(pThis, &Mte, DllBase)));
break;
}
/* Read the full name. */
if (cbName < sizeof(u))
else
if (RT_FAILURE(rc))
{
if (cbName < sizeof(u))
else
}
if (RT_SUCCESS(rc))
{
char *pszName;
if (RT_SUCCESS(rc))
{
/* Read the start of the PE image and pass it along to a worker. */
if (RT_SUCCESS(rc))
pUVM,
&u.au8[0],
sizeof(u));
}
}
/* next */
return VINF_SUCCESS;
}
/**
* @copydoc DBGFOSREG::pfnProbe
*/
{
union
{
} u;
/*
* Look for the PAGELK section name that seems to be a part of all kernels.
* Then try find the module table entry for it. Since it's the first entry
* in the PsLoadedModuleList we can easily validate the list head and report
* success.
*/
if (enmMode == CPUMMODE_LONG)
{
/** @todo when 32-bit is working, add support for 64-bit windows nt. */
}
else
{
{
if (RT_FAILURE(rc))
break;
/* MZ + PE header. */
if ( RT_SUCCESS(rc)
{
&& (pHdrs->FileHeader.Characteristics & (IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DLL)) == IMAGE_FILE_EXECUTABLE_IMAGE
/** @todo need more ntoskrnl signs? */
)
{
/* Find the MTE. */
while (RT_SUCCESS(rc))
{
/* check the name. */
if ( RT_SUCCESS(rc)
)
{
if ( RT_SUCCESS(rc)
/* || !RTUtf16ICmp(u.wsz, g_wszKernelNames[1]) */
)
)
{
if ( RT_SUCCESS(rc)
{
Log(("DigWinNt: MteAddr=%RGv KernelAddr=%RGv SizeOfImage=%x &PsLoadedModuleList=%RGv (32-bit)\n",
return true;
}
}
}
/* next */
else
}
}
}
}
}
return false;
}
/**
* @copydoc DBGFOSREG::pfnDestruct
*/
{
}
/**
* @copydoc DBGFOSREG::pfnConstruct
*/
{
return VINF_SUCCESS;
}
const DBGFOSREG g_DBGDiggerWinNt =
{
/* .u32Magic = */ DBGFOSREG_MAGIC,
/* .fFlags = */ 0,
/* .cbData = */ sizeof(DBGDIGGERWINNT),
/* .szName = */ "WinNT",
/* .pfnConstruct = */ dbgDiggerWinNtConstruct,
/* .pfnDestruct = */ dbgDiggerWinNtDestruct,
/* .pfnProbe = */ dbgDiggerWinNtProbe,
/* .pfnInit = */ dbgDiggerWinNtInit,
/* .pfnRefresh = */ dbgDiggerWinNtRefresh,
/* .pfnTerm = */ dbgDiggerWinNtTerm,
/* .pfnQueryVersion = */ dbgDiggerWinNtQueryVersion,
/* .pfnQueryInterface = */ dbgDiggerWinNtQueryInterface,
/* .u32EndMagic = */ DBGFOSREG_MAGIC
};