/*++ Copyright (c) 1990-2000 Microsoft Corporation Module Name: ntsdexts.c Abstract: This function contains the default ntsd debugger extensions Revision History: Daniel Mihai (DMihai) 18-Feb-2001 Add !htrace - useful for dumping handle trace information. --*/ #include "ntsdextp.h" VOID DecodeError( PSTR Banner, ULONG Code, BOOL TreatAsStatus ) { HANDLE Dll ; PSTR Source ; UCHAR Message[ 512 ]; PUCHAR s; if ( !TreatAsStatus ) { // // The value "type" is not known. Try and figure out what it // is. // if ( (Code & 0xC0000000) == 0xC0000000 ) { // // Easy: NTSTATUS failure case // Dll = GetModuleHandle( "NTDLL.DLL" ); Source = "NTSTATUS" ; TreatAsStatus = TRUE ; } else if ( ( Code & 0xF0000000 ) == 0xD0000000 ) { // // HRESULT from NTSTATUS // Dll = GetModuleHandle( "NTDLL.DLL" ); Source = "NTSTATUS" ; Code &= 0xCFFFFFFF ; TreatAsStatus = TRUE ; } else if ( ( Code & 0x80000000 ) == 0x80000000 ) { // // Note, this can overlap with NTSTATUS warning area. In that // case, force the NTSTATUS. // Dll = GetModuleHandle( "KERNEL32.DLL" ); Source = "HRESULT" ; } else { // // Sign bit is off. Explore some known ranges: // if ( (Code >= WSABASEERR) && (Code <= WSABASEERR + 1000 )) { Dll = LoadLibrary( "wsock32.dll" ); Source = "Winsock" ; } else if ( ( Code >= NERR_BASE ) && ( Code <= MAX_NERR ) ) { Dll = LoadLibrary( "netmsg.dll" ); Source = "NetAPI" ; } else { Dll = GetModuleHandle( "KERNEL32.DLL" ); Source = "Win32" ; } } } else { Dll = GetModuleHandle( "NTDLL.DLL" ); Source = "NTSTATUS" ; } if (!FormatMessage( FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE, Dll, Code, 0, Message, sizeof( Message ), NULL ) ) { strcpy( Message, "No mapped error code" ); } s = Message ; while (*s) { if (*s < ' ') { *s = ' '; } s++; } if ( !TreatAsStatus ) { dprintf( "%s: (%s) %#x (%u) - %s\n", Banner, Source, Code, Code, Message ); } else { dprintf( "%s: (%s) %#x - %s\n", Banner, Source, Code, Message ); } } DECLARE_API( error ) { ULONG err ; INIT_API(); err = (ULONG) GetExpression( args ); DecodeError( "Error code", err, FALSE ); EXIT_API(); } BOOL DumpLastErrorForTeb( ULONG64 Address ) { TEB Teb; if (ReadMemory( (ULONG_PTR)Address, &Teb, sizeof(Teb), NULL ) ) { DecodeError( "LastErrorValue", Teb.LastErrorValue, FALSE ); DecodeError( "LastStatusValue", Teb.LastStatusValue, TRUE ); return TRUE; } dprintf("Unable to read TEB at %p\n", Address ); return FALSE; } BOOL DumpCurrentThreadLastError( ULONG CurrThreadID, PVOID Context ) { NTSTATUS Status; THREAD_BASIC_INFORMATION ThreadInformation; ULONGLONG Address; TEB Teb; if (Context) { dprintf("Last error for thread %lx:\n", CurrThreadID); } GetTebAddress(&Address); if (Address) { DumpLastErrorForTeb(Address); } else { dprintf("Unable to read thread %lx's TEB\n", CurrThreadID ); } if (Context) { dprintf("\n"); } return TRUE; } DECLARE_API( gle ) { INIT_API(); if (!strcmp(args, "-all")) { EnumerateUModeThreads(&DumpCurrentThreadLastError, Client); } else { DumpCurrentThreadLastError(GetCurrentThreadUserID(), NULL); } EXIT_API(); } DECLARE_API( version ) { OSVERSIONINFOA VersionInformation; HKEY hkey; DWORD cb, dwType; CHAR szCurrentType[128]; CHAR szCSDString[3+128]; INIT_API(); VersionInformation.dwOSVersionInfoSize = sizeof(VersionInformation); if (!GetVersionEx( &VersionInformation )) { dprintf("GetVersionEx failed - %u\n", GetLastError()); goto Exit; } szCurrentType[0] = '\0'; if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows NT\\CurrentVersion", 0, KEY_READ, &hkey ) == NO_ERROR ) { cb = sizeof(szCurrentType); if (hkey) { if (RegQueryValueEx(hkey, "CurrentType", NULL, &dwType, szCurrentType, &cb ) != 0) { szCurrentType[0] = '\0'; } RegCloseKey(hkey); } } if (VersionInformation.szCSDVersion[0]) { sprintf(szCSDString, ": %s", VersionInformation.szCSDVersion); } else { szCSDString[0] = '\0'; } dprintf("Version %d.%d (Build %d%s) %s\n", VersionInformation.dwMajorVersion, VersionInformation.dwMinorVersion, VersionInformation.dwBuildNumber, szCSDString, szCurrentType ); Exit: EXIT_API(); } DECLARE_API( help ) { INIT_API(); while (*args == ' ') args++; if (*args == '\0') { dprintf("ntsdexts help:\n\n"); dprintf("!critSec csAddress - Dump a critical section\n"); dprintf("!dp [v] [pid | pcsr_process] - Dump CSR process\n"); dprintf("!dreg -[d|w] [![ | *]] - Dump registry information\n"); dprintf("!dt [v] pcsr_thread - Dump CSR thread\n"); dprintf("!error value - Decode error value\n"); dprintf("!gatom - Dump the global atom table\n"); dprintf("!gle [-all] - Dump GetLastError value for current/all thread\n"); dprintf("!handle [handle] - Dump handle information\n"); dprintf("!help [cmd] - Displays this list or gives details on command\n"); dprintf("!locks [-v][-o] - Dump all Critical Sections in process\n"); dprintf("!version - Dump system version and build number\n"); dprintf("!vprot [address] - Dump the virtual protect settings\n"); } else { if (*args == '!') args++; if (strcmp( args, "handle") == 0) { dprintf("!handle [handle [flags [type]]] - Dump handle information\n"); dprintf(" If no handle specified, all handles are dumped.\n"); dprintf(" Flags are bits indicating greater levels of detail.\n"); dprintf("If the handle is 0 or -1, all handles are scanned. If the handle is not\n"); dprintf("zero, that particular handle is examined. The flags are as follows:\n"); dprintf(" 1 - Get type information (default)\n"); dprintf(" 2 - Get basic information\n"); dprintf(" 4 - Get name information\n"); dprintf(" 8 - Get object specific info (where available)\n"); dprintf("\n"); dprintf("If Type is specified, only object of that type are scanned. Type is a\n"); dprintf("standard NT type name, e.g. Event, Semaphore, etc. Case sensitive, of\n"); dprintf("course.\n"); dprintf("\n"); dprintf("Examples:\n"); dprintf("\n"); dprintf(" !handle -- dumps the types of all the handles, and a summary table\n"); dprintf(" !handle 0 0 -- dumps a summary table of all the open handles\n"); dprintf(" !handle 0 f -- dumps everything we can find about a handle.\n"); dprintf(" !handle 0 f Event\n"); dprintf(" -- dumps everything we can find about open events\n"); } else if (strcmp( args, "gflag") == 0) { dprintf("If a value is not given then displays the current bits set in\n"); dprintf("NTDLL!NtGlobalFlag variable. Otherwise value can be one of the\n"); dprintf("following:\n"); dprintf("\n"); dprintf(" -? - displays a list of valid flag abbreviations\n"); dprintf(" number - 32-bit number that becomes the new value stored into\n"); dprintf(" NtGlobalFlag\n"); dprintf(" +number - specifies one or more bits to set in NtGlobalFlag\n"); dprintf(" +abbrev - specifies a single bit to set in NtGlobalFlag\n"); dprintf(" -number - specifies one or more bits to clear in NtGlobalFlag\n"); dprintf(" -abbrev - specifies a single bit to clear in NtGlobalFlag\n"); } else { dprintf("Invalid command. No help available\n"); } } EXIT_API(); } VOID DumpStackBackTraceIndex( IN USHORT BackTraceIndex ) { #if i386 BOOL b; PRTL_STACK_TRACE_ENTRY pBackTraceEntry; RTL_STACK_TRACE_ENTRY BackTraceEntry; ULONG i; CHAR Symbol[ 1024 ]; ULONG_PTR Displacement; ULONG NumberOfEntriesAdded; PRTL_STACK_TRACE_ENTRY *EntryIndexArray; // Indexed by [-1 .. -NumberOfEntriesAdded] PSTACK_TRACE_DATABASE *pRtlpStackTraceDataBase; PSTACK_TRACE_DATABASE RtlpStackTraceDataBase; STACK_TRACE_DATABASE StackTraceDataBase; pRtlpStackTraceDataBase = (PSTACK_TRACE_DATABASE *)GetExpression( "NTDLL!RtlpStackTraceDataBase" ); if (pRtlpStackTraceDataBase == NULL) { dprintf( "HEAPEXT: Unable to get address of NTDLL!RtlpStackTraceDataBase\n" ); } if ((BackTraceIndex != 0) && (pRtlpStackTraceDataBase != NULL)) { b = ReadMemory( (ULONG_PTR)pRtlpStackTraceDataBase, &RtlpStackTraceDataBase, sizeof( RtlpStackTraceDataBase ), NULL ); if (!b || RtlpStackTraceDataBase == NULL) { return; } b = ReadMemory( (ULONG_PTR)RtlpStackTraceDataBase, &StackTraceDataBase, sizeof( StackTraceDataBase ), NULL ); if (!b) { return; } if (BackTraceIndex < StackTraceDataBase.NumberOfEntriesAdded) { b = ReadMemory( (ULONG_PTR)(StackTraceDataBase.EntryIndexArray - BackTraceIndex), &pBackTraceEntry, sizeof( pBackTraceEntry ), NULL ); if (!b) { dprintf( " unable to read stack back trace index (%x) entry at %p\n", BackTraceIndex, (StackTraceDataBase.EntryIndexArray - BackTraceIndex) ); return; } b = ReadMemory( (ULONG_PTR)pBackTraceEntry, &BackTraceEntry, sizeof( BackTraceEntry ), NULL ); if (!b) { dprintf( " unable to read stack back trace entry at %p\n", BackTraceIndex, pBackTraceEntry ); return; } dprintf( "\n Stack trace (%u) at %x:\n", BackTraceIndex, pBackTraceEntry ); for (i=0; i 0xf) { pch = HexToString(dw >> 4, pch); dw &= 0xf; } *pch++ = ((dw >= 0xA) ? ('A' - 0xA) : '0') + (CHAR)dw; *pch = 0; return pch; } // // dt == dump thread // // dt [v] pcsr_thread // v == verbose (structure) // // by scottlu // DECLARE_API( dt ) { char chVerbose; CSR_THREAD csrt; ULONG_PTR dw; BOOL b; INIT_API(); while (*args == ' ') args++; chVerbose = ' '; if (*args == 'v') chVerbose = *args++; dw = GetExpression(args); b = ReadMemory( dw, &csrt, sizeof(csrt), NULL); if ( !b ) { dprintf( "NTSDEXTS: Unable to read memory\n" ); goto Exit; } // // Print simple thread info if the user did not ask for verbose. // if (chVerbose == ' ') { dprintf("Thread %08lx, Process %08lx, ClientId %lx.%lx, Flags %lx, Ref Count %lx\n", dw, csrt.Process, csrt.ClientId.UniqueProcess, csrt.ClientId.UniqueThread, csrt.Flags, csrt.ReferenceCount); goto Exit; } dprintf("PCSR_THREAD @ %08lx:\n" "\t+%04lx Link.Flink %08lx\n" "\t+%04lx Link.Blink %08lx\n" "\t+%04lx Process %08lx\n", dw, FIELD_OFFSET(CSR_THREAD, Link.Flink), csrt.Link.Flink, FIELD_OFFSET(CSR_THREAD, Link.Blink), csrt.Link.Blink, FIELD_OFFSET(CSR_THREAD, Process), csrt.Process); dprintf( "\t+%04lx WaitBlock %08lx\n" "\t+%04lx ClientId.UniqueProcess %08lx\n" "\t+%04lx ClientId.UniqueThread %08lx\n" "\t+%04lx ThreadHandle %08lx\n", FIELD_OFFSET(CSR_THREAD, WaitBlock), csrt.WaitBlock, FIELD_OFFSET(CSR_THREAD, ClientId.UniqueProcess), csrt.ClientId.UniqueProcess, FIELD_OFFSET(CSR_THREAD, ClientId.UniqueThread), csrt.ClientId.UniqueThread, FIELD_OFFSET(CSR_THREAD, ThreadHandle), csrt.ThreadHandle); dprintf( "\t+%04lx Flags %08lx\n" "\t+%04lx ReferenceCount %08lx\n" "\t+%04lx HashLinks.Flink %08lx\n" "\t+%04lx HashLinks.Blink %08lx\n", FIELD_OFFSET(CSR_THREAD, Flags), csrt.Flags, FIELD_OFFSET(CSR_THREAD, ReferenceCount), csrt.ReferenceCount, FIELD_OFFSET(CSR_THREAD, HashLinks.Flink), csrt.HashLinks.Flink, FIELD_OFFSET(CSR_THREAD, HashLinks.Blink), csrt.HashLinks.Blink); Exit: EXIT_API(); } // // dp == dump process // // dp [v] [pid | pcsr_process] // v == verbose (structure + thread list) // no process == dump process list // // by scottlu // DECLARE_API( dp ) { PLIST_ENTRY ListHead, ListNext; char ach[80]; char chVerbose; PCSR_PROCESS pcsrpT; CSR_PROCESS csrp; PCSR_PROCESS pcsrpRoot; PCSR_THREAD pcsrt; ULONG_PTR dwProcessId; ULONG_PTR dw; DWORD_PTR dwRootProcess; BOOL b; INIT_API(); while (*args == ' ') args++; chVerbose = ' '; if (*args == 'v') chVerbose = *args++; dwRootProcess = GetExpression("csrsrv!CsrRootProcess"); if ( !dwRootProcess ) { goto Exit; } b = ReadMemory( dwRootProcess, &pcsrpRoot, sizeof(pcsrpRoot), NULL); if ( !b ) { dprintf( "NTSDEXTS: Unable to read RootProcess\n" ); goto Exit; } // // See if user wants all processes. If so loop through them. // if (*args == 0) { ListHead = &pcsrpRoot->ListLink; b = ReadMemory( (ULONG_PTR)(&ListHead->Flink), &ListNext, sizeof(ListNext), NULL); if ( !b ) { dprintf( "NTSDEXTS: Unable to read ListNext\n" ); goto Exit; } while (ListNext != ListHead) { pcsrpT = CONTAINING_RECORD(ListNext, CSR_PROCESS, ListLink); ach[0] = chVerbose; ach[1] = ' '; HexToString((ULONG_PTR)pcsrpT, &ach[2]); dp(Client, ach); b = ReadMemory( (ULONG_PTR)(&ListNext->Flink), &ListNext, sizeof(ListNext), NULL); if ( !b ) { dprintf( "NTSDEXTS: Unable to read ListNext\n" ); goto Exit; } } dprintf("---\n"); goto Exit; } // // User wants specific process structure. Evaluate to find id or process // pointer. // dw = (ULONG)GetExpression(args); ListHead = &pcsrpRoot->ListLink; b = ReadMemory( (ULONG_PTR)(&ListHead->Flink), &ListNext, sizeof(ListNext), NULL); if ( !b ) { dprintf( "NTSDEXTS: Unable to read ListNext\n" ); goto Exit; } while (ListNext != ListHead) { pcsrpT = CONTAINING_RECORD(ListNext, CSR_PROCESS, ListLink); b = ReadMemory( (ULONG_PTR)(&ListNext->Flink), &ListNext, sizeof(ListNext), NULL); if ( !b ) { dprintf( "NTSDEXTS: Unable to read ListNext\n" ); goto Exit; } b = ReadMemory( (ULONG_PTR)(&pcsrpT->ClientId.UniqueProcess), &dwProcessId, sizeof(dwProcessId), NULL); if ( !b ) { dprintf( "NTSDEXTS: Unable to read ListNext\n" ); goto Exit; } if (dw == dwProcessId) { dw = (ULONG_PTR)pcsrpT; break; } } pcsrpT = (PCSR_PROCESS)dw; b = ReadMemory( (ULONG_PTR)pcsrpT, &csrp, sizeof(csrp), NULL); if ( !b ) { dprintf( "NTSDEXTS: Unable to read RootProcess\n" ); goto Exit; } // // If not verbose, print simple process info. // if (chVerbose == ' ') { dprintf("Process %08lx, Id %p, Seq# %lx, Flags %lx, Ref Count %lx\n", pcsrpT, csrp.ClientId.UniqueProcess, csrp.SequenceNumber, csrp.Flags, csrp.ReferenceCount); goto Exit; } dprintf("PCSR_PROCESS @ %08lx:\n" "\t+%04lx ListLink.Flink %08lx\n" "\t+%04lx ListLink.Blink %08lx\n" "\t+%04lx Parent %08lx\n", pcsrpT, FIELD_OFFSET(CSR_PROCESS, ListLink.Flink), csrp.ListLink.Flink, FIELD_OFFSET(CSR_PROCESS, ListLink.Blink), csrp.ListLink.Blink, FIELD_OFFSET(CSR_PROCESS, Parent), csrp.Parent); dprintf( "\t+%04lx ThreadList.Flink %08lx\n" "\t+%04lx ThreadList.Blink %08lx\n" "\t+%04lx NtSession %08lx\n" "\t+%04lx ExpectedVersion %08lx\n", FIELD_OFFSET(CSR_PROCESS, ThreadList.Flink), csrp.ThreadList.Flink, FIELD_OFFSET(CSR_PROCESS, ThreadList.Blink), csrp.ThreadList.Blink, FIELD_OFFSET(CSR_PROCESS, NtSession), csrp.NtSession, FIELD_OFFSET(CSR_PROCESS, ExpectedVersion), csrp.ExpectedVersion); dprintf( "\t+%04lx ClientPort %08lx\n" "\t+%04lx ClientViewBase %08lx\n" "\t+%04lx ClientViewBounds %08lx\n" "\t+%04lx ClientId.UniqueProcess %08lx\n", FIELD_OFFSET(CSR_PROCESS, ClientPort), csrp.ClientPort, FIELD_OFFSET(CSR_PROCESS, ClientViewBase), csrp.ClientViewBase, FIELD_OFFSET(CSR_PROCESS, ClientViewBounds), csrp.ClientViewBounds, FIELD_OFFSET(CSR_PROCESS, ClientId.UniqueProcess), csrp.ClientId.UniqueProcess); dprintf( "\t+%04lx ProcessHandle %08lx\n" "\t+%04lx SequenceNumber %08lx\n" "\t+%04lx Flags %08lx\n" "\t+%04lx DebugFlags %08lx\n", FIELD_OFFSET(CSR_PROCESS, ProcessHandle), csrp.ProcessHandle, FIELD_OFFSET(CSR_PROCESS, SequenceNumber), csrp.SequenceNumber, FIELD_OFFSET(CSR_PROCESS, Flags), csrp.Flags, FIELD_OFFSET(CSR_PROCESS, DebugFlags), csrp.DebugFlags); dprintf( "\t+%04lx DebugUserInterface %08lx\n" "\t+%04lx ReferenceCount %08lx\n" "\t+%04lx ProcessGroupId %08lx\n" "\t+%04lx ProcessGroupSequence %08lx\n", FIELD_OFFSET(CSR_PROCESS, DebugUserInterface.UniqueProcess), csrp.DebugUserInterface.UniqueProcess, FIELD_OFFSET(CSR_PROCESS, ReferenceCount), csrp.ReferenceCount, FIELD_OFFSET(CSR_PROCESS, ProcessGroupId), csrp.ProcessGroupId, FIELD_OFFSET(CSR_PROCESS, ProcessGroupSequence), csrp.ProcessGroupSequence); dprintf( "\t+%04lx fVDM %08lx\n" "\t+%04lx ThreadCount %08lx\n" "\t+%04lx PriorityClass %08lx\n" "\t+%04lx ShutdownLevel %08lx\n" "\t+%04lx ShutdownFlags %08lx\n", FIELD_OFFSET(CSR_PROCESS, fVDM), csrp.fVDM, FIELD_OFFSET(CSR_PROCESS, ThreadCount), csrp.ThreadCount, FIELD_OFFSET(CSR_PROCESS, PriorityClass), csrp.PriorityClass, FIELD_OFFSET(CSR_PROCESS, ShutdownLevel), csrp.ShutdownLevel, FIELD_OFFSET(CSR_PROCESS, ShutdownFlags), csrp.ShutdownFlags); // // Now dump simple thread info for this processes' threads. // ListHead = &pcsrpT->ThreadList; b = ReadMemory( (ULONG_PTR)(&ListHead->Flink), &ListNext, sizeof(ListNext), NULL); if ( !b ) { dprintf( "NTSDEXTS: Unable to read ListNext\n" ); goto Exit; } dprintf("Threads:\n"); while (ListNext != ListHead) { pcsrt = CONTAINING_RECORD(ListNext, CSR_THREAD, Link); // // Make sure this pcsrt is somewhat real so we don't loop forever. // b = ReadMemory( (ULONG_PTR)(&pcsrt->ClientId.UniqueProcess), &dwProcessId, sizeof(dwProcessId), NULL); if ( !b ) { dprintf( "NTSDEXTS: Unable to read ListNext\n" ); goto Exit; } if (dwProcessId != (DWORD_PTR)csrp.ClientId.UniqueProcess) { dprintf("Invalid thread. Probably invalid argument to this extension.\n"); goto Exit; } HexToString((ULONG_PTR)pcsrt, ach); dt(Client, ach); b = ReadMemory( (ULONG_PTR)(&ListNext->Flink), &ListNext, sizeof(ListNext), NULL); if ( !b ) { dprintf( "NTSDEXTS: Unable to read ListNext\n" ); goto Exit; } } Exit: EXIT_API(); } VOID DllsExtension( PCSTR args, PPEB ProcessPeb ); DECLARE_API( gatom ) /*++ Routine Description: This function is called as an NTSD extension to dump the global atom table kept in kernel mode Called as: !gatom Return Value: None. --*/ { NTSTATUS Status; ATOM_TABLE_INFORMATION TableInfo; PATOM_TABLE_INFORMATION pTableInfo; PATOM_BASIC_INFORMATION pBasicInfo; ULONG RequiredLength, MaxLength, i; INIT_API(); dprintf("\nGlobal atom table "); Status = NtQueryInformationAtom( RTL_ATOM_INVALID_ATOM, AtomTableInformation, &TableInfo, sizeof( TableInfo ), &RequiredLength ); if (Status != STATUS_INFO_LENGTH_MISMATCH) { dprintf( " - cant get information - %x\n", Status ); goto Exit; } RequiredLength += 100 * sizeof( RTL_ATOM ); pTableInfo = LocalAlloc( 0, RequiredLength ); if (pTableInfo == NULL) { dprintf( " - cant allocate memory for %u atoms\n", RequiredLength / sizeof( RTL_ATOM ) ); goto Exit; } Status = NtQueryInformationAtom( RTL_ATOM_INVALID_ATOM, AtomTableInformation, pTableInfo, RequiredLength, &RequiredLength ); if (!NT_SUCCESS( Status )) { dprintf( " - cant get information about %x atoms - %x\n", RequiredLength / sizeof( RTL_ATOM ), Status ); LocalFree( pTableInfo ); goto Exit; } MaxLength = sizeof( *pBasicInfo ) + RTL_ATOM_MAXIMUM_NAME_LENGTH; pBasicInfo = LocalAlloc( 0, MaxLength ); if (!pBasicInfo) { dprintf("LocalAlloc failed.\n"); goto Exit; } for (i=0; iNumberOfAtoms; i++) { Status = NtQueryInformationAtom( pTableInfo->Atoms[ i ], AtomBasicInformation, pBasicInfo, MaxLength, &RequiredLength ); if (!NT_SUCCESS( Status )) { dprintf( "%hx *** query failed (%x)\n", Status ); } else { dprintf( "%hx(%2d) = %ls (%d)%s\n", pTableInfo->Atoms[ i ], pBasicInfo->UsageCount, pBasicInfo->Name, pBasicInfo->NameLength, pBasicInfo->Flags & RTL_ATOM_PINNED ? " pinned" : "" ); } } Exit: EXIT_API(); } #include "hleak.c" #include "secexts.c" /*++ Routine Description: This function is called as an NTSD extension to mimic the !handle kd command. This will walk through the debuggee's handle table and duplicate the handle into the ntsd process, then call NtQueryobjectInfo to find out what it is. Called as: !handle [handle [flags [Type]]] If the handle is 0 or -1, all handles are scanned. If the handle is not zero, that particular handle is examined. The flags are as follows (corresponding to secexts.c): 1 - Get type information (default) 2 - Get basic information 4 - Get name information 8 - Get object specific info (where available) If Type is specified, only object of that type are scanned. Type is a standard NT type name, e.g. Event, Semaphore, etc. Case sensitive, of course. Examples: !handle -- dumps the types of all the handles, and a summary table !handle 0 0 -- dumps a summary table of all the open handles !handle 0 f -- dumps everything we can find about a handle. !handle 0 f Event -- dumps everything we can find about open events --*/ DECLARE_API( handle ) { HANDLE hThere; DWORD Type; DWORD Mask; DWORD HandleCount; NTSTATUS Status; DWORD Total; DWORD TypeCounts[TYPE_MAX]; DWORD Handle; DWORD Hits; DWORD Matches; DWORD ObjectType; BOOL GetDirect; ULONG SessionType; ULONG SessionQual; INIT_API(); Mask = GHI_TYPE ; hThere = INVALID_HANDLE_VALUE; Type = 0; while (*args == ' ') { args++; } if ( strcmp( args, "-?" ) == 0 ) { help(Client, "handle" ); goto Exit; } hThere = (PVOID) GetExpression( args ); while (*args && (*args != ' ') ) { args++; } while (*args == ' ') { args++; } if (*args) { Mask = (DWORD)GetExpression( args ); } while (*args && (*args != ' ') ) { args++; } while (*args == ' ') { args++; } if (*args) { Type = GetObjectTypeIndex( (LPSTR)args ); if (Type == (DWORD) -1 ) { dprintf("Unknown type '%s'\n", args ); goto Exit; } } // // if they specified 0, they just want the summary. Make sure nothing // sneaks out. // if ( Mask == 0 ) { Mask = GHI_SILENT; } // // If this is a dump debug session, // check and see whether we can retrieve handle // information through the engine interface. // if (g_ExtControl == NULL || g_ExtControl->lpVtbl-> GetDebuggeeType(g_ExtControl, &SessionType, &SessionQual) != S_OK) { SessionType = DEBUG_CLASS_USER_WINDOWS; SessionQual = DEBUG_USER_WINDOWS_PROCESS; } if (SessionType == DEBUG_CLASS_USER_WINDOWS && SessionQual != DEBUG_USER_WINDOWS_PROCESS) { // This is a dump or remote session so we have to use // the stored handle information accessible // through the interface. if (g_ExtData2 == NULL || g_ExtData2->lpVtbl-> ReadHandleData(g_ExtData2, 0, DEBUG_HANDLE_DATA_TYPE_HANDLE_COUNT, &HandleCount, sizeof(HandleCount), NULL) != S_OK) { dprintf("Unable to read handle information\n"); goto Exit; } GetDirect = FALSE; } else { // This is a live session so we can make direct NT calls. // More information is available this way so we use it // whenever we can. GetDirect = TRUE; } // // hThere of 0 indicates all handles. // if ((hThere == 0) || (hThere == INVALID_HANDLE_VALUE)) { if (GetDirect) { Status = NtQueryInformationProcess( g_hCurrentProcess, ProcessHandleCount, &HandleCount, sizeof( HandleCount ), NULL ); if ( !NT_SUCCESS( Status ) ) { goto Exit; } } Hits = 0; Handle = 0; Matches = 0; ZeroMemory( TypeCounts, sizeof(TypeCounts) ); while ( Hits < HandleCount ) { if ( Type ) { if (GetHandleInfo( GetDirect, g_hCurrentProcess, (HANDLE) (DWORD_PTR) Handle, GHI_TYPE | GHI_SILENT, &ObjectType ) ) { Hits++; if ( ObjectType == Type ) { GetHandleInfo( GetDirect, g_hCurrentProcess, (HANDLE)(DWORD_PTR)Handle, Mask, &ObjectType ); Matches ++; } } } else { if (GetHandleInfo( GetDirect, g_hCurrentProcess, (HANDLE)(DWORD_PTR)Handle, GHI_TYPE | GHI_SILENT, &ObjectType) ) { Hits++; TypeCounts[ ObjectType ] ++; GetHandleInfo( GetDirect, g_hCurrentProcess, (HANDLE)(DWORD_PTR)Handle, Mask, &ObjectType ); } } Handle += 4; } if ( Type == 0 ) { dprintf( "%d Handles\n", Hits ); dprintf( "Type \tCount\n"); for (Type = 0; Type < TYPE_MAX ; Type++ ) { if (TypeCounts[Type]) { dprintf("%-15ws\t%d\n", pszTypeNames[Type], TypeCounts[Type]); } } } else { dprintf("%d handles of type %ws\n", Matches, pszTypeNames[Type] ); } } else { GetHandleInfo( GetDirect, g_hCurrentProcess, hThere, Mask, &Type ); } Exit: EXIT_API(); } DECLARE_API( threadtoken ) { HANDLE hToken ; NTSTATUS Status ; INIT_API(); Status = NtOpenThreadToken( g_hCurrentThread, TOKEN_READ, TRUE, &hToken ); if ( !NT_SUCCESS( Status ) ) { if ( Status == STATUS_ACCESS_DENIED ) { // // Try to get around the ACL: // } if ( Status != STATUS_NO_TOKEN ) { dprintf( "Can't open token, %d\n", RtlNtStatusToDosError( Status ) ); goto Exit; } Status = NtOpenProcessToken( g_hCurrentProcess, TOKEN_READ, &hToken ); if ( !NT_SUCCESS( Status ) ) { dprintf( "Can't open any token, %d\n", RtlNtStatusToDosError( Status ) ); goto Exit; } dprintf( "\n***Thread is not impersonating, using process token***\n" ); } TokenInfo( hToken, 0xFFF ); NtClose( hToken ); Exit: EXIT_API(); } #define PAGE_ALL (PAGE_READONLY|\ PAGE_READWRITE|\ PAGE_WRITECOPY|\ PAGE_EXECUTE|\ PAGE_EXECUTE_READ|\ PAGE_EXECUTE_READWRITE|\ PAGE_EXECUTE_WRITECOPY|\ PAGE_NOACCESS) VOID printflags( DWORD Flags ) { switch (Flags & PAGE_ALL) { case PAGE_READONLY: dprintf("PAGE_READONLY"); break; case PAGE_READWRITE: dprintf("PAGE_READWRITE"); break; case PAGE_WRITECOPY: dprintf("PAGE_WRITECOPY"); break; case PAGE_EXECUTE: dprintf("PAGE_EXECUTE"); break; case PAGE_EXECUTE_READ: dprintf("PAGE_EXECUTE_READ"); break; case PAGE_EXECUTE_READWRITE: dprintf("PAGE_EXECUTE_READWRITE"); break; case PAGE_EXECUTE_WRITECOPY: dprintf("PAGE_EXECUTE_WRITECOPY"); break; case PAGE_NOACCESS: if ((Flags & ~PAGE_NOACCESS) == 0) { dprintf("PAGE_NOACCESS"); break; } // else fall through default: dprintf("*** Invalid page protection ***\n"); return; break; } if (Flags & PAGE_NOCACHE) { dprintf(" + PAGE_NOCACHE"); } if (Flags & PAGE_GUARD) { dprintf(" + PAGE_GUARD"); } dprintf("\n"); } DECLARE_API( vprot ) /*++ Routine Description: This debugger extension dumps the virtual memory info for the address specified. Arguments: Return Value: --*/ { PVOID Address; MEMORY_BASIC_INFORMATION mbi; INIT_API(); while (*args == ' ') { args++; } Address = (PVOID)GetExpression( args ); if (!VirtualQueryEx( g_hCurrentProcess, Address, &mbi, sizeof(mbi))) { dprintf("vprot: VirtualQueryEx failed, error = %d\n", GetLastError()); goto Exit; } dprintf("BaseAddress: %p\n", mbi.BaseAddress); dprintf("AllocationBase: %08x\n", mbi.AllocationBase); dprintf("AllocationProtect: %08x ", mbi.AllocationProtect); printflags(mbi.AllocationProtect); dprintf("RegionSize: %08x\n", mbi.RegionSize); dprintf("State: %08x ", mbi.State); switch (mbi.State) { case MEM_COMMIT: dprintf("MEM_COMMIT\n"); break; case MEM_FREE: dprintf("MEM_FREE\n"); break; case MEM_RESERVE: dprintf("MEM_RESERVE\n"); break; default: dprintf("*** Invalid page state ***\n"); break; } dprintf("Protect: %08x ", mbi.Protect); printflags(mbi.Protect); dprintf("Type: %08x ", mbi.Type); switch(mbi.Type) { case MEM_IMAGE: dprintf("MEM_IMAGE\n"); break; case MEM_MAPPED: dprintf("MEM_MAPPED\n"); break; case MEM_PRIVATE: dprintf("MEM_PRIVATE\n"); break; default: dprintf("*** Invalid page type ***\n"); break; } Exit: EXIT_API(); } #include "leak.c" #include "regexts.c" /*++ Routine Description: This function is called as an NTSD extension to dump registry information Called as: !dreg -[d|w] [![ | *]] Arguments: hCurrentProcess - Supplies a handle to the current process (at the time the extension was called). hCurrentThread - Supplies a handle to the current thread (at the time the extension was called). CurrentPc - Supplies the current pc at the time the extension is called. lpExtensionApis - Supplies the address of the functions callable by this extension. args - Supplies the pattern and expression for this command. Return Value: None. --*/ DECLARE_API( dreg ) { DWORD opts = 1; INIT_API(); // Skip past leading spaces while (*args == ' ') { args++; } if (*args == '-') { args++; switch (*args) { case 'd': opts = 4; break; case 'w': opts = 2; break; default: opts = 1; break; } if (*args) { // expect a space between options args++; // Skip past leading spaces while (*args == ' ') { args++; } } } Idreg(opts, (LPSTR)args); EXIT_API(); } /*++ Routine Description: This function is called as an NTSD extension to dump handle tracing information Called as: !htrace [handle] Arguments: args - Supplies the pattern and expression for this command. Return Value: None. --*/ DECLARE_API( htrace ) { HANDLE Handle; PPROCESS_HANDLE_TRACING_QUERY Info; ULONG_PTR Displacement; NTSTATUS Status; ULONG CurrentBufferSize; ULONG CrtStackTrace; ULONG EntriesDisplayed; ULONG CapturedAddressIndex; PVOID *CrtStack; PVOID CapturedAddress; SYSTEM_BASIC_INFORMATION SysBasicInfo; CHAR Symbol[ 1024 ]; INIT_API(); Info = NULL; CrtStackTrace = 0; EntriesDisplayed = 0; // // Did the user ask for some help? // if (strcmp (args, "-?") == 0 || strcmp (args, "?") == 0 || strcmp (args, "-h") == 0) { dprintf ("!htrace [handle]\n"); goto DoneAll; } // // Get the handle from the command line // Handle = (HANDLE)GetExpression (args); // // Get the stack traces using NtQueryInformationProcess // CurrentBufferSize = sizeof (PROCESS_HANDLE_TRACING_QUERY); while (TRUE) { // // Allocate a new buffer // Info = (PPROCESS_HANDLE_TRACING_QUERY)malloc (CurrentBufferSize); if (Info == NULL) { dprintf ("ERROR: Cannot allocate buffer with size 0x%p\n", CurrentBufferSize); goto DoneAll; } ZeroMemory( Info, CurrentBufferSize ); Status = NtQueryInformationProcess (g_hCurrentProcess, ProcessHandleTracing, Info, CurrentBufferSize, NULL ); if( NT_SUCCESS (Status) ) { // // We have all the information ready // break; } CurrentBufferSize = sizeof (PROCESS_HANDLE_TRACING_QUERY) + Info->TotalTraces * sizeof (Info->HandleTrace[ 0 ]); free (Info); Info = NULL; if( CheckControlC() ) { goto DoneAll; } if (Status != STATUS_INFO_LENGTH_MISMATCH) { // // No reason to try querying again // dprintf ("Query process information failed, status 0x%X\n", Status); goto DoneAll; } // // Try allocating another buffer with the new size // } // // If we have 0 stack traces there is nothing we can dump // if (Info->TotalTraces == 0) { dprintf( "No stack traces available.\n" ); goto DoneAll; } // // Find out the highest user address because // we will skip kernel mode addresses from the stack traces. // Status = NtQuerySystemInformation (SystemBasicInformation, &SysBasicInfo, sizeof (SysBasicInfo), NULL); if (!NT_SUCCESS (Status)) { dprintf ("Query system basic information failed, status 0x%X\n", Status); goto DoneAll; } // // Dump all the stack traces. // for (CrtStackTrace = 0; CrtStackTrace < Info->TotalTraces; CrtStackTrace += 1) { if( CheckControlC() ) { CrtStackTrace += 1; goto DoneDumping; } if (Handle == 0 || Handle == Info->HandleTrace[ CrtStackTrace ].Handle) { EntriesDisplayed += 1; dprintf ("--------------------------------------\n" "Handle = 0x%p - ", Info->HandleTrace[ CrtStackTrace ].Handle); switch( Info->HandleTrace[ CrtStackTrace ].Type ) { case HANDLE_TRACE_DB_OPEN: dprintf( "OPEN:\n" ); break; case HANDLE_TRACE_DB_CLOSE: dprintf( "CLOSE:\n" ); break; case HANDLE_TRACE_DB_BADREF: dprintf( "*** BAD REFERENCE ***:\n" ); break; default: dprintf( "Invalid operation type: %u\n", Info->HandleTrace[ CrtStackTrace ].Type ); goto DoneDumping; } for (CapturedAddressIndex = 0, CrtStack = &Info->HandleTrace[ CrtStackTrace ].Stacks[ 0 ]; CapturedAddressIndex < (sizeof(Info->HandleTrace[ CrtStackTrace ].Stacks) / sizeof(Info->HandleTrace[ CrtStackTrace ].Stacks[0])); CapturedAddressIndex += 1, CrtStack += 1) { if( CheckControlC() ) { CrtStackTrace += 1; goto DoneDumping; } CapturedAddress = *CrtStack; if (CapturedAddress == NULL) { // // Done with dumping this stack trace // break; } if ((ULONG_PTR)CapturedAddress > SysBasicInfo.MaximumUserModeAddress) { // // Skip kernel-mode addresses // continue; } GetSymbol (CapturedAddress, Symbol, &Displacement); dprintf ("0x%p: %s+0x%p\n", CapturedAddress, Symbol, Displacement ); } } } DoneDumping: dprintf ("\n--------------------------------------\n" "Parsed 0x%X stack traces.\n" "Dumped 0x%X stack traces.\n", CrtStackTrace, EntriesDisplayed); DoneAll: if (Info != NULL) { free (Info); } EXIT_API(); }