564 lines
13 KiB
C++
564 lines
13 KiB
C++
|
/*++
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
regleaks.cxx
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
Debugger extensions for class store.
|
||
|
|
||
|
Author:
|
||
|
|
||
|
UShaji (Adapted from extensions, MarioGo, MazharM, JRoberts)
|
||
|
|
||
|
--*/
|
||
|
#include <stddef.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <stdio.h>
|
||
|
#include <limits.h>
|
||
|
|
||
|
#include "nt.h"
|
||
|
#include "ntrtl.h"
|
||
|
#include "nturtl.h"
|
||
|
|
||
|
#include "windows.h"
|
||
|
|
||
|
// #include "stkwalk.h"
|
||
|
#include <imagehlp.h>
|
||
|
#include "wdbgexts.h"
|
||
|
#include "regexts.hxx"
|
||
|
|
||
|
|
||
|
//
|
||
|
// globals
|
||
|
//
|
||
|
WINDBG_EXTENSION_APIS ExtensionApis;
|
||
|
USHORT SavedMajorVersion = 0;
|
||
|
USHORT SavedMinorVersion = 0;
|
||
|
HANDLE ProcessHandle = 0;
|
||
|
BOOL fKernelDebug = FALSE;
|
||
|
UEnvReadMemory ReadMemoryExt = ReadMemoryUserMode;
|
||
|
UEnvReadMemory WriteMemoryExt = ReadMemoryUserMode;
|
||
|
|
||
|
//
|
||
|
// macros
|
||
|
//
|
||
|
|
||
|
/*
|
||
|
#define ExtensionRoutinePrologue() if (!fKernelDebug) \
|
||
|
{ \
|
||
|
ExtensionApis = *lpExtensionApis; \
|
||
|
ReadMemoryExt = ReadMemoryUserMode; \
|
||
|
WriteMemoryExt = WriteMemoryUserMode; \
|
||
|
} \
|
||
|
ULONG_PTR dwAddr = GetExpression(lpArgumentString); \
|
||
|
|
||
|
*/
|
||
|
|
||
|
#define ALLOC_SIZE 500
|
||
|
#define MAX_ARGS 4
|
||
|
|
||
|
|
||
|
// define our own operators new and delete, so that we do not have to include the crt
|
||
|
|
||
|
void * __cdecl
|
||
|
::operator new(unsigned int dwBytes)
|
||
|
{
|
||
|
void *p;
|
||
|
p = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwBytes);
|
||
|
return (p);
|
||
|
}
|
||
|
|
||
|
|
||
|
void __cdecl
|
||
|
::operator delete (void *p)
|
||
|
{
|
||
|
HeapFree(GetProcessHeap(), 0, p);
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
ReadMemoryUserMode( HANDLE hProcess, const void* pAddress, void* pBuffer, DWORD dwSize, DWORD* pdwRead )
|
||
|
{
|
||
|
return ReadProcessMemory( hProcess, pAddress, pBuffer, dwSize, pdwRead );
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
ReadMemoryKernelMode( HANDLE, const void* pAddress, void* pBuffer, DWORD dwSize, DWORD* pdwRead )
|
||
|
{
|
||
|
return ReadMemory( (ULONG) pAddress, pBuffer, dwSize, pdwRead );
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
WriteMemoryUserMode( HANDLE hProcess, const void* pAddress, void* pBuffer, DWORD dwSize, DWORD* pdwRead )
|
||
|
{
|
||
|
return WriteProcessMemory( hProcess, (void*) pAddress, pBuffer, dwSize, pdwRead );
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
WriteMemoryKernelMode( HANDLE, const void* pAddress, void* pBuffer, DWORD dwSize, DWORD* pdwRead )
|
||
|
{
|
||
|
return WriteMemory( (ULONG) pAddress, pBuffer, dwSize, pdwRead );
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
GetData(IN DWORD dwAddress, IN LPVOID ptr, IN ULONG size, IN PCSTR type )
|
||
|
{
|
||
|
BOOL b;
|
||
|
ULONG BytesRead;
|
||
|
ULONG count;
|
||
|
|
||
|
if (!fKernelDebug)
|
||
|
{
|
||
|
return ReadMemoryExt(ProcessHandle, (LPVOID) dwAddress, ptr, size, 0);
|
||
|
}
|
||
|
else {
|
||
|
}
|
||
|
|
||
|
while( size > 0 )
|
||
|
{
|
||
|
count = MIN( size, 3000 );
|
||
|
|
||
|
b = ReadMemoryExt(ProcessHandle, (LPVOID) dwAddress, ptr, count, &BytesRead );
|
||
|
|
||
|
if (!b || BytesRead != count )
|
||
|
{
|
||
|
if (NULL == type)
|
||
|
{
|
||
|
type = "unspecified" ;
|
||
|
}
|
||
|
dprintf("Couldn't read memory with error %d\n", GetLastError());
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
dwAddress += count;
|
||
|
size -= count;
|
||
|
ptr = (LPVOID)((ULONG)ptr + count);
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
#define MAX_MESSAGE_BLOCK_SIZE 1024
|
||
|
#define BLOCK_SIZE 2
|
||
|
// could have been bigger but hit the boundary case once.
|
||
|
|
||
|
WCHAR *ReadProcessChar(
|
||
|
unsigned short * Address
|
||
|
)
|
||
|
{
|
||
|
DWORD dwAddr = (DWORD) Address;
|
||
|
|
||
|
char block[BLOCK_SIZE];
|
||
|
WCHAR *Block = (WCHAR *)█
|
||
|
char *string_block = new char[MAX_MESSAGE_BLOCK_SIZE];
|
||
|
WCHAR *String = (WCHAR *)string_block;
|
||
|
int length = 0;
|
||
|
int i = 0;
|
||
|
BOOL b;
|
||
|
BOOL end = FALSE;
|
||
|
|
||
|
if (dwAddr == NULL) {
|
||
|
return (L'\0');
|
||
|
}
|
||
|
|
||
|
for (length = 0; length < MAX_MESSAGE_BLOCK_SIZE/2; ) {
|
||
|
b = GetData( dwAddr, &block, BLOCK_SIZE, NULL);
|
||
|
if (b == FALSE) {
|
||
|
dprintf("couldn't read address %x\n", dwAddr);
|
||
|
return (L'\0');
|
||
|
}
|
||
|
for (i = 0; i < BLOCK_SIZE/2; i++) {
|
||
|
if (Block[i] == L'\0') {
|
||
|
end = TRUE;
|
||
|
}
|
||
|
String[length] = Block[i];
|
||
|
length++;
|
||
|
}
|
||
|
if (end == TRUE) {
|
||
|
break;
|
||
|
}
|
||
|
dwAddr += BLOCK_SIZE;
|
||
|
}
|
||
|
|
||
|
return (String);
|
||
|
}
|
||
|
|
||
|
PCHAR
|
||
|
MapSymbol(DWORD dwAddr)
|
||
|
{
|
||
|
static CHAR Name[256];
|
||
|
DWORD Displacement;
|
||
|
|
||
|
GetSymbol((LPVOID)dwAddr, (UCHAR *)Name, &Displacement);
|
||
|
strcat(Name, "+");
|
||
|
PCHAR p = strchr(Name, '\0');
|
||
|
_ltoa(Displacement, p, 16);
|
||
|
return(Name);
|
||
|
}
|
||
|
|
||
|
|
||
|
DECLARE_API( help )
|
||
|
{
|
||
|
INIT_DPRINTF();
|
||
|
|
||
|
if (lpArgumentString[0] == '\0') {
|
||
|
dprintf("\n"
|
||
|
"regexts help:\n\n"
|
||
|
"\n"
|
||
|
"!keys - Dumps stack for all open reg handles \n"
|
||
|
"!version - Dumps the version numbers \n"
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
BOOL ChkTarget; // is debuggee a CHK build?
|
||
|
#define VER_PRODUCTBUILD 10
|
||
|
EXT_API_VERSION ApiVersion = { 3, 5, EXT_API_VERSION_NUMBER, 0 };
|
||
|
|
||
|
VOID
|
||
|
WinDbgExtensionDllInit(
|
||
|
PWINDBG_EXTENSION_APIS lpExtensionApis,
|
||
|
USHORT MajorVersion,
|
||
|
USHORT MinorVersion
|
||
|
)
|
||
|
{
|
||
|
fKernelDebug = TRUE;
|
||
|
ReadMemoryExt = ReadMemoryKernelMode;
|
||
|
WriteMemoryExt = WriteMemoryKernelMode;
|
||
|
|
||
|
ExtensionApis = *lpExtensionApis ;
|
||
|
SavedMajorVersion = MajorVersion;
|
||
|
SavedMinorVersion = MinorVersion;
|
||
|
ChkTarget = SavedMajorVersion == 0x0c ? TRUE : FALSE;
|
||
|
}
|
||
|
|
||
|
DECLARE_API( version )
|
||
|
{
|
||
|
#if DBG
|
||
|
PCSTR kind = "Checked";
|
||
|
#else
|
||
|
PCSTR kind = "Free";
|
||
|
#endif
|
||
|
|
||
|
dprintf(
|
||
|
"%s SMB Extension dll for Build %d debugging %s kernel for Build %d\n",
|
||
|
kind,
|
||
|
VER_PRODUCTBUILD,
|
||
|
SavedMajorVersion == 0x0c ? "Checked" : "Free",
|
||
|
SavedMinorVersion
|
||
|
);
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
CheckVersion(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
LPEXT_API_VERSION
|
||
|
ExtensionApiVersion(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
return &ApiVersion;
|
||
|
}
|
||
|
|
||
|
DECLARE_API( keys )
|
||
|
{
|
||
|
RegLeakTable* pLeakTable;
|
||
|
|
||
|
INIT_DPRINTF();
|
||
|
|
||
|
|
||
|
if (*lpArgumentString) {
|
||
|
dprintf("Dump keys for table at %s\n", lpArgumentString);
|
||
|
sscanf(lpArgumentString, "%lx", &pLeakTable);
|
||
|
} else {
|
||
|
dprintf("Dump keys for advapi32!gLeakTable\n");
|
||
|
pLeakTable = (RegLeakTable*) GetExpression( "advapi32!gLeakTable" );
|
||
|
|
||
|
if (!pLeakTable) {
|
||
|
dprintf("Unable to resolve advapi32!gLeakTable\n"
|
||
|
"Please fix symbols or specify the address of a leak table"
|
||
|
"to !keys\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
dprintf("Dump keys for table at 0x%x\n", pLeakTable);
|
||
|
}
|
||
|
|
||
|
RegLeakTableDump(pLeakTable);
|
||
|
}
|
||
|
|
||
|
void RegLeakTableDump(RegLeakTable* pLeakTable)
|
||
|
{
|
||
|
TrackObjectData* pData;
|
||
|
DWORD ListHead;
|
||
|
DWORD cKeys;
|
||
|
DWORD KeysAddress;
|
||
|
DWORD dwFlags;
|
||
|
DWORD FlagsAddress;
|
||
|
|
||
|
KeysAddress = ((DWORD) pLeakTable) + 4;
|
||
|
FlagsAddress = ((DWORD) pLeakTable) + 8;
|
||
|
|
||
|
if (!GetData(KeysAddress,
|
||
|
&cKeys,
|
||
|
sizeof(pLeakTable->cKeys),
|
||
|
NULL)) {
|
||
|
dprintf("Error reading key count at 0x%x\n", KeysAddress);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
dprintf("\tKeys = 0x%x\n", cKeys);
|
||
|
|
||
|
if (!GetData(FlagsAddress,
|
||
|
&dwFlags,
|
||
|
sizeof(pLeakTable->pHead),
|
||
|
NULL)) {
|
||
|
dprintf("Error reading list head at 0x%x\n", pLeakTable);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
dprintf("\tFlags = 0x%x", dwFlags);
|
||
|
|
||
|
switch (dwFlags)
|
||
|
{
|
||
|
case LEAK_TRACK_FLAG_NONE:
|
||
|
dprintf("\tNo tracking\n");
|
||
|
return;
|
||
|
|
||
|
case LEAK_TRACK_FLAG_USER:
|
||
|
dprintf("\tOnly subkeys of HKEY_USERS\n");
|
||
|
break;
|
||
|
|
||
|
case LEAK_TRACK_FLAG_ALL:
|
||
|
dprintf("\tAll keys\n");
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
dprintf("\tInvalid flag -- table corrupt\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!GetData((DWORD)pLeakTable,
|
||
|
&ListHead,
|
||
|
sizeof(pLeakTable->pHead),
|
||
|
NULL)) {
|
||
|
dprintf("Error reading list head at 0x%x\n", pLeakTable);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
dprintf("\tList starts at 0x%x\n", ListHead);
|
||
|
|
||
|
TrackObjectData* NextData;
|
||
|
|
||
|
int ikey = 0;
|
||
|
|
||
|
for (pData = (TrackObjectData*) ListHead;
|
||
|
pData != NULL;
|
||
|
pData = NextData)
|
||
|
{
|
||
|
dprintf("\tObject at 0x%x", pData);
|
||
|
|
||
|
TrackObjectDataPrint(pData);
|
||
|
|
||
|
if (!GetData((DWORD) pData, &NextData, sizeof(NextData), NULL)) {
|
||
|
dprintf("Error reading next link for object at 0x%x\n", pData);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void TrackObjectDataPrint(TrackObjectData* pKeyData)
|
||
|
{
|
||
|
NTSTATUS Status;
|
||
|
DWORD dwStackDepth;
|
||
|
DWORD StackAddress;
|
||
|
HKEY hKey;
|
||
|
DWORD hKeyAddress;
|
||
|
DWORD StackDepthAddress;
|
||
|
PVOID* rgStack;
|
||
|
DWORD pStack;
|
||
|
|
||
|
hKeyAddress = ((DWORD) pKeyData) + 8;
|
||
|
StackDepthAddress = ((DWORD) pKeyData) + 12;
|
||
|
|
||
|
rgStack = NULL;
|
||
|
|
||
|
if (!GetData(hKeyAddress, &hKey, sizeof(hKey), NULL)) {
|
||
|
dprintf("Error reading hkey for object at 0x%x\n", pKeyData);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
dprintf("Tracked key data for object 0x%x\n", hKey);
|
||
|
|
||
|
if (!fKernelDebug)
|
||
|
(void) PrintObjectInfo(hKey);
|
||
|
else
|
||
|
dprintf("!!!!!!Broken into kd. do '!handle 0x%x f' for details of the handle\n", hKey);
|
||
|
|
||
|
if (!GetData(StackDepthAddress, &dwStackDepth, sizeof(dwStackDepth), NULL)) {
|
||
|
dprintf("Error reading key object at 0x%x\n", pKeyData);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!dwStackDepth) {
|
||
|
dprintf("\t\tNo stack data\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
dprintf("\t\tStack depth 0x%x\n", dwStackDepth);
|
||
|
|
||
|
StackAddress = ((DWORD) (pKeyData)) + 16;
|
||
|
|
||
|
if (!GetData(StackAddress,
|
||
|
&pStack,
|
||
|
sizeof(PVOID),
|
||
|
NULL)) {
|
||
|
dprintf("Error reading stack frames at 0x%x\n", StackAddress);
|
||
|
return;
|
||
|
}
|
||
|
dprintf("\t\tStack frames at 0x%x\n", pStack);
|
||
|
|
||
|
rgStack = (PVOID*) RtlAllocateHeap(
|
||
|
RtlProcessHeap(),
|
||
|
0,
|
||
|
sizeof(*rgStack) * dwStackDepth);
|
||
|
|
||
|
if (!rgStack) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!GetData(pStack,
|
||
|
rgStack,
|
||
|
sizeof(*rgStack) * dwStackDepth,
|
||
|
NULL)) {
|
||
|
dprintf("Error reading stack frames at 0x%x\n", StackAddress);
|
||
|
RtlFreeHeap(RtlProcessHeap(), 0, rgStack);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
for (int iFrame = 0; iFrame < dwStackDepth; iFrame++)
|
||
|
{
|
||
|
UCHAR Symbol[MAX_SYMBOL_LENGTH];
|
||
|
DWORD_PTR Displacement;
|
||
|
|
||
|
*Symbol = L'\0';
|
||
|
|
||
|
GetSymbol(
|
||
|
rgStack[iFrame],
|
||
|
Symbol,
|
||
|
&Displacement);
|
||
|
|
||
|
dprintf("\t\t0x%x", rgStack[iFrame]);
|
||
|
|
||
|
if (*Symbol) {
|
||
|
dprintf("\t %s", Symbol);
|
||
|
|
||
|
if (Displacement) {
|
||
|
dprintf("+0x%x", Displacement);
|
||
|
}
|
||
|
} else {
|
||
|
dprintf("\t ????????");
|
||
|
}
|
||
|
|
||
|
dprintf("\n");
|
||
|
}
|
||
|
|
||
|
if (rgStack) {
|
||
|
RtlFreeHeap(RtlProcessHeap(), 0, rgStack);
|
||
|
}
|
||
|
|
||
|
dprintf("\n");
|
||
|
}
|
||
|
|
||
|
|
||
|
NTSTATUS PrintObjectInfo(HANDLE Handle)
|
||
|
{
|
||
|
|
||
|
POBJECT_NAME_INFORMATION pNameInfo;
|
||
|
BYTE rgNameInfoBuf[512];
|
||
|
NTSTATUS Status;
|
||
|
HKEY hkDup;
|
||
|
DWORD dwRequired;
|
||
|
|
||
|
Status = NtDuplicateObject(
|
||
|
ProcessHandle,
|
||
|
Handle,
|
||
|
NtCurrentProcess(),
|
||
|
(PHANDLE) &hkDup,
|
||
|
0,
|
||
|
FALSE,
|
||
|
DUPLICATE_SAME_ACCESS);
|
||
|
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
dprintf("Unable to duplicate handle 0x%x from process handle 0x%x, error 0x%x\n",
|
||
|
Handle,
|
||
|
ProcessHandle,
|
||
|
Status);
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
pNameInfo = (POBJECT_NAME_INFORMATION) rgNameInfoBuf;
|
||
|
|
||
|
Status = NtQueryObject(
|
||
|
hkDup,
|
||
|
ObjectNameInformation,
|
||
|
pNameInfo,
|
||
|
sizeof(pNameInfo),
|
||
|
&dwRequired);
|
||
|
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
|
||
|
if (STATUS_INFO_LENGTH_MISMATCH == Status) {
|
||
|
|
||
|
Status = STATUS_NO_MEMORY;
|
||
|
|
||
|
pNameInfo = (POBJECT_NAME_INFORMATION) RtlAllocateHeap(
|
||
|
RtlProcessHeap(),
|
||
|
0,
|
||
|
dwRequired);
|
||
|
|
||
|
if (pNameInfo) {
|
||
|
|
||
|
Status = NtQueryObject(
|
||
|
hkDup,
|
||
|
ObjectNameInformation,
|
||
|
pNameInfo,
|
||
|
dwRequired,
|
||
|
&dwRequired);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
dprintf("Unable to query object information for object error 0x%x\n",
|
||
|
Status);
|
||
|
} else {
|
||
|
dprintf("Object 0x%x\n\tName: %S\n", Handle, pNameInfo->Name.Buffer);
|
||
|
}
|
||
|
|
||
|
NtClose(hkDup);
|
||
|
|
||
|
if ((PBYTE) pNameInfo != rgNameInfoBuf) {
|
||
|
RtlFreeHeap(RtlProcessHeap(), 0, pNameInfo);
|
||
|
}
|
||
|
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|