/*++ Module Name: regleaks.cxx Abstract: Debugger extensions for class store. Author: UShaji (Adapted from extensions, MarioGo, MazharM, JRoberts) --*/ #include #include #include #include #include "nt.h" #include "ntrtl.h" #include "nturtl.h" #include "windows.h" // #include "stkwalk.h" #include #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; }