/*++ Copyright (c) 1992-2000 Microsoft Corporation Module Name: handle.c Abstract: WinDbg Extension Api Revision History: --*/ #include "precomp.h" #pragma hdrstop BOOL DumpHandles ( IN ULONG64 RealProcessBase, IN ULONG64 HandleToDump, IN ULONG64 pObjectType, IN ULONG Flags ); BOOLEAN DumpHandle( IN ULONG64 pHandleTableEntry, IN ULONG64 Handle, IN ULONG64 pObjectType, IN ULONG Flags ); DECLARE_API( handle ) /*++ Routine Description: Dump the active handles Arguments: args - [handle-to-dump [flags [process-to-dump [TypeName]]]] if handle-to-dump is 0 dump all, otherwise it's the handle to dump. if process-to-dump is 0 dump all. if nonzero, it can be either an EPROCESS pointer or a PID. flags bit meanings: 0x2 Dump the object 0x4 Dump free entries 0x10 Dump kernel handle table 0x20 Dump psp cid handle table Examples: !handle 0 3 ffffffff Section means dump all Section handles (using verbosity flags=3) for the current process. ----- !handle 0 3 ffffffff means dump all handles (using verbosity flags=3) for the current process. ----- !handle 0 3 0 means dump all handles (using verbosity flags=3) for all processes. ----- !handle 0 10 ffffffff Section means dump Section entries in the kernel handle table (using verbosity flags=3). Return Value: None --*/ { ULONG64 ProcessToDump; ULONG64 HandleToDump; ULONG Flags; ULONG Result; ULONG nArgs; ULONG64 Next = 0; ULONG64 ProcessHead = 1; ULONG64 Process; char TypeName[ MAX_PATH ]; ULONG64 pObjectType; ULONG64 UserProbeAddress; ULONG ActiveProcessLinksOffset=0; ULONG64 UniqueProcessId=0, ActiveProcessLinks_Flink=0; FIELD_INFO procLink[] = { {"ActiveProcessLinks", "", 0, DBG_DUMP_FIELD_RETURN_ADDRESS, 0, NULL}, {"UniqueProcessId", "", 0, DBG_DUMP_FIELD_COPY_FIELD_DATA, 0, (PVOID) &UniqueProcessId}, {"ActiveProcessLinks.Flink","", 0, DBG_DUMP_FIELD_COPY_FIELD_DATA, 0, (PVOID) &ActiveProcessLinks_Flink}, }; SYM_DUMP_PARAM EProc = { sizeof (SYM_DUMP_PARAM), "nt!_EPROCESS", DBG_DUMP_NO_PRINT, 0, NULL, NULL, NULL, 3, &procLink[0], }; ULONG dwProcessor=0; CHAR Addr1[100], Addr2[100]; GetCurrentProcessor(Client, &dwProcessor, NULL); HandleToDump = 0; Flags = 0x3; //by default dump bodies and objects for in use entries ProcessToDump = -1; UserProbeAddress = GetNtDebuggerDataValue(MmUserProbeAddress); dprintf("processor number %d\n", dwProcessor); Addr1[0] = 0; Addr2[0] = 0; nArgs = 0; if (GetExpressionEx(args, &HandleToDump, &args)) { ULONG64 tmp; ++nArgs; if (GetExpressionEx(args, &tmp, &args) && args) { ULONG i; Flags = (ULONG) tmp; ++nArgs; while (*args == ' ') { ++args; } // Do not use GetExpressionEx since it will search for TypeName // in symbols i=0; while (*args && (*args != ' ')) { Addr1[i++] = *args++; } Addr1[i] = 0; if (Addr1[0]) { ProcessToDump = GetExpression(Addr1); ++nArgs; while (*args == ' ') { ++args; } if (StringCchCopy(TypeName, sizeof(TypeName), args) != S_OK) { TypeName[0] = 0; } if (TypeName[0]) ++nArgs; } } } pObjectType = 0; if (nArgs > 3 && FetchObjectManagerVariables(FALSE)) { pObjectType = FindObjectType( TypeName ); } if (ProcessToDump == 0) { dprintf("**** NT ACTIVE PROCESS HANDLE DUMP ****\n"); if (Flags == 0xFFFFFFFF) { Flags = 1; } } else if (ProcessToDump == -1) { GetCurrentProcessAddr( dwProcessor, 0, &ProcessToDump ); if (ProcessToDump == 0) { dprintf("Unable to get current process pointer.\n"); return E_INVALIDARG; } } if (ProcessToDump < UserProbeAddress) { // // If a process id is specified, then search the active process list // for the specified process id. // ULONG64 List_Flink=0, List_Blink=0; FIELD_INFO listFields[] = { {"Flink", "", 0, DBG_DUMP_FIELD_COPY_FIELD_DATA, 0, (PVOID) &List_Flink}, {"Blink", "", 0, DBG_DUMP_FIELD_COPY_FIELD_DATA, 0, (PVOID) &List_Blink}, }; SYM_DUMP_PARAM Lst = { sizeof (SYM_DUMP_PARAM), "nt!_LIST_ENTRY", DBG_DUMP_NO_PRINT, 0, NULL, NULL, NULL, 2, &listFields[0] }; ProcessHead = GetNtDebuggerData( PsActiveProcessHead ); if ( !ProcessHead ) { dprintf("Unable to get value of PsActiveProcessHead\n"); return E_INVALIDARG; } Lst.addr = ProcessHead; if (Ioctl(IG_DUMP_SYMBOL_INFO, &Lst, Lst.size)) { dprintf("Unable to find _LIST_ENTRY type, ProcessHead: %08I64x\n", ProcessHead); return E_INVALIDARG; } if (ProcessToDump != 0) { dprintf("Searching for Process with Cid == %I64lx\n", ProcessToDump); } Next = List_Flink; if (Next == 0) { dprintf("PsActiveProcessHead is NULL!\n"); return E_INVALIDARG; } } if (GetFieldOffset("nt!_EPROCESS", "ActiveProcessLinks", &ActiveProcessLinksOffset)) { dprintf("Unable to find _EPROCESS type\n"); return E_INVALIDARG; } if (pObjectType != 0) { dprintf("Searching for handles of type %s\n", TypeName); } while (Next != ProcessHead) { if ( CheckControlC() ) { return E_INVALIDARG; } if (Next != 0) { Process = Next - ActiveProcessLinksOffset; } else { Process = ProcessToDump; } EProc.addr = Process; if (Ioctl(IG_DUMP_SYMBOL_INFO, &EProc, EProc.size)) { dprintf("_EPROCESS Ioctl failed at %p\n",Process); return E_INVALIDARG; } if (ProcessToDump == 0 || ProcessToDump < UserProbeAddress && ProcessToDump == UniqueProcessId || ProcessToDump >= UserProbeAddress && ProcessToDump == Process ) { if (DumpProcess ("", Process, 0, NULL)) { if (!DumpHandles ( Process, HandleToDump, pObjectType, Flags)) { break; } } else { break; } } if (Next == 0) { break; } Next = ActiveProcessLinks_Flink; } return S_OK; } #define KERNEL_HANDLE_MASK 0x80000000 //+--------------------------------------------------------------------------- // // Function: DumpHandles // // Synopsis: Dump the handle table for the given process // // Arguments: [RealProcessBase] -- base address of the process // [HandleToDump] -- handle to look for - if 0 dump all // [pObjectType] -- object type to look for // [Flags] -- flags passed thru to DumpHandle // if 0x10 is set dump the kernel handle table // // Returns: TRUE if successful // // History: 1-12-1998 benl Created // // Notes: Each segment of table has 0xFF or 8 bits worth of entries // the handle number's lowest 2 bit are application defined // so the indexes are gotten from the 3 8 bits ranges after // the first 2 bits // //---------------------------------------------------------------------------- BOOL DumpHandles ( IN ULONG64 RealProcessBase, IN ULONG64 HandleToDump, IN ULONG64 pObjectType, IN ULONG Flags ) { ULONG64 ObjectTable=0; ULONG ulRead; ULONG64 ulTopLevel; ULONG64 ulMidLevel; ULONG ulHandleNum = ((ULONG)(HandleToDump) >> 2); ULONG iIndex1; ULONG iIndex2; ULONG iIndex3; ULONG ptrSize, hTableEntrySize, hTableEntryPointerSize; ULONG64 tablePtr; ULONG64 HandleCount = 0; ULONG64 Table = 0; ULONG64 UniqueProcessId = 0; BOOL KernelHandle = FALSE, CidHandle = FALSE; ULONG LowLevelCounts = 256; ULONG MidLevelCounts = 256; ULONG HighLevelCounts = 256; ULONG TableLevel = 2; BOOLEAN NewStyle = FALSE; // // Typeinfo parsing structures // FIELD_INFO procFields[] = { {"ObjectTable", "", 0, DBG_DUMP_FIELD_COPY_FIELD_DATA, 0, (PVOID) &ObjectTable}, }; FIELD_INFO handleTblFields[] = { {"HandleCount", "", 0, DBG_DUMP_FIELD_COPY_FIELD_DATA, 0, (PVOID) &HandleCount}, {"UniqueProcessId", "", 0, DBG_DUMP_FIELD_COPY_FIELD_DATA, 0, (PVOID) &UniqueProcessId}, }; SYM_DUMP_PARAM handleSym = { sizeof (SYM_DUMP_PARAM), "nt!_EPROCESS", DBG_DUMP_NO_PRINT, RealProcessBase, NULL, NULL, NULL, 1, &procFields[0] }; // // Check for kernel handle table // if ((Flags & 0x10) || ((ulHandleNum != 0) && (((ULONG_PTR)HandleToDump & KERNEL_HANDLE_MASK) == KERNEL_HANDLE_MASK))) { ULONG64 KernelTableAddr; KernelHandle = TRUE; KernelTableAddr = GetExpression( "nt!ObpKernelHandleTable" ); if (!KernelTableAddr) { dprintf( "Unable to find ObpKernelHandleTable\n" ); return FALSE; } if (!ReadPointer(KernelTableAddr, &ObjectTable)) { dprintf( "Unable to find ObpKernelHandleTable at %p\n", KernelTableAddr ); return FALSE; } } else if (Flags & 0x20) { CidHandle = TRUE; ObjectTable = GetNtDebuggerDataValue(PspCidTable); if (!ObjectTable) { dprintf( "Unable to find PspCidTable\n" ); return FALSE; } } else { if (Ioctl(IG_DUMP_SYMBOL_INFO, &handleSym, handleSym.size)) { dprintf("Unable to get ObjectTable address from process %I64x\n", RealProcessBase); return FALSE; } } ptrSize = DBG_PTR_SIZE; if (!ptrSize) { dprintf("Cannot get pointer size\n"); return FALSE; } handleSym.sName = "nt!_HANDLE_TABLE"; handleSym.addr = ObjectTable; handleSym.nFields = sizeof (handleTblFields) / sizeof (FIELD_INFO); handleSym.Fields = &handleTblFields[0]; if (!ObjectTable || Ioctl(IG_DUMP_SYMBOL_INFO, &handleSym, handleSym.size)) { dprintf("%08p: Unable to read handle table\n", ObjectTable); return FALSE; } if (GetFieldValue(ObjectTable, "nt!_HANDLE_TABLE", "TableCode", Table)) { // Could be older build } else if (!IsPtr64()) { // // GetFieldValue doesn't sign extend it since TableCode is defined as dword (not pointer) // Table = (ULONG64) (LONG64) (LONG) Table; } hTableEntrySize = GetTypeSize("nt!_HANDLE_TABLE_ENTRY"); hTableEntryPointerSize = IsPtr64() ? 8 : 4; if (hTableEntrySize == 0) { dprintf("Cannot get size of nt!_HANDLE_TABLE_ENTRY \n"); return FALSE; } if (Table != 0) { NewStyle = TRUE; LowLevelCounts = PageSize / hTableEntrySize; MidLevelCounts = PageSize / hTableEntryPointerSize; HighLevelCounts = (1<<24) / (LowLevelCounts * MidLevelCounts); TableLevel = (ULONG)(Table & 3); Table &= ~((ULONG64)3); } else if (GetFieldValue(ObjectTable, "nt!_HANDLE_TABLE", "Table", Table) ) { dprintf("%08p: Unable to read Table field from _HANDLE_TABLE\n", ObjectTable); return FALSE; } if (KernelHandle) { dprintf( "Kernel " ); } else if (CidHandle) { dprintf( "Cid " ); } dprintf("%s version of handle table at %p with %I64d %s in use\n", (NewStyle ? "New" : "Old"), Table, HandleCount, ((HandleCount == 1) ? "Entry" : "Entries")); if (ulHandleNum != 0) { if (NewStyle) { ULONG64 CrtTable = Table; ULONG64 CrtHandleNum = ulHandleNum; if (TableLevel == 2) { ULONG64 HighLevelIndex = ulHandleNum / (LowLevelCounts * MidLevelCounts); CrtTable = CrtTable + HighLevelIndex * ptrSize; if (!ReadPointer(CrtTable, &CrtTable)) { dprintf("%08p: Unable to read handle table level 3\n", CrtTable ); return FALSE; } CrtHandleNum = ulHandleNum - HighLevelIndex * (LowLevelCounts * MidLevelCounts); } if (TableLevel == 1) { CrtTable = CrtTable + (CrtHandleNum / LowLevelCounts) * ptrSize; if (!ReadPointer(CrtTable, &CrtTable)) { dprintf("%08p: Unable to read handle table level 2\n", CrtTable ); return FALSE; } CrtHandleNum = CrtHandleNum % LowLevelCounts; } tablePtr = CrtTable + CrtHandleNum * hTableEntrySize; } else { // // Read the 3 level table stage by stage to find the specific entry // tablePtr = Table + ((ulHandleNum & 0x00FF0000) >> 16) * ptrSize; ulTopLevel = 0; if (!ReadPointer(tablePtr, &ulTopLevel)) { dprintf("%08p: Unable to read handle table level 3\n", tablePtr ); return FALSE; } if (!ulTopLevel) { dprintf("Invalid handle: 0x%x\n", ulHandleNum); return FALSE; } tablePtr = ulTopLevel + ((ulHandleNum & 0x0000FF00) >> 8) * ptrSize; ulMidLevel = 0; if (!ReadPointer(tablePtr, &ulMidLevel)) { dprintf("%08p: Unable to read handle table level 2\n", tablePtr); return FALSE; } if (!ulMidLevel) { dprintf("Invalid handle: 0x%x\n", ulHandleNum); return FALSE; } // // Read the specific entry req. and dump it // tablePtr = (ulMidLevel + (0x000000ff & ulHandleNum) * hTableEntrySize); } DumpHandle(tablePtr, HandleToDump, pObjectType, Flags); } else { // // loop over all the possible parts of the table // for (iIndex1=0; iIndex1 < HighLevelCounts; iIndex1++) { // // check for ctrl-c to abort // if (CheckControlC()) { return FALSE; } if (TableLevel < 2) { tablePtr = Table; ulTopLevel = tablePtr; // // We break the loop second time if we don't have the table level 2 // if (iIndex1 > 0) { break; } } else { // // Read the 3 level table stage by stage to find the specific entry // tablePtr = Table + iIndex1 * ptrSize; ulTopLevel = 0; if (!ReadPointer(tablePtr, &ulTopLevel)) { dprintf("%08p: Unable to read handle table top level\n", tablePtr); return FALSE; } if (!ulTopLevel) { continue; } } for (iIndex2=0; iIndex2 < MidLevelCounts; iIndex2++) { // // check for ctrl-c to abort // if (CheckControlC()) { return FALSE; } if (TableLevel < 1) { tablePtr = Table; ulMidLevel = tablePtr; // // We break the loop second time if we don't have the table level 1 // if (iIndex2 > 0) { break; } } else { tablePtr = ulTopLevel + iIndex2 * ptrSize; ulMidLevel = 0; if (!ReadPointer(tablePtr, &ulMidLevel)) { dprintf("%08p: Unable to read handle table middle level\n", tablePtr); return FALSE; } if (!ulMidLevel) { continue; } } // // now read all the entries in this segment of the table and dump them // Note: Handle Number = 6 unused bits + 8 bits high + 8 bits mid + // 8 bits low + 2 bits user defined // for (iIndex3 = 0; iIndex3 < LowLevelCounts; iIndex3++) { // // check for ctrl-c to abort // if (CheckControlC()) { return FALSE; } DumpHandle(ulMidLevel + iIndex3*hTableEntrySize, (iIndex3 + (iIndex2 + iIndex1 * MidLevelCounts) * LowLevelCounts) * 4, pObjectType, Flags); } } } // end outermost for } // endif on a specific handle return TRUE; } // DumpHandles //+--------------------------------------------------------------------------- // // Function: DumpHandle // // Synopsis: Dump a particular Handle // // Arguments: [pHandleTableEntry] -- entry to dump // [Handle] -- handle number of entry // [pObjectType] -- only dump if object type matches this // if NULL dump everything // [Flags] -- flags if 0x2 also dump the object // if 0x4 dump free entries // // Returns: // // History: 1-12-1998 benl Created // // Notes: // //---------------------------------------------------------------------------- BOOLEAN DumpHandle( IN ULONG64 pHandleTableEntry, IN ULONG64 Handle, IN ULONG64 pObjectType, IN ULONG Flags ) { ULONG64 ulObjectHeaderAddr; ULONG Result; ULONG HandleAttributes; // OBJECT_HEADER ObjectHeader; ULONG64 ObjectBody; ULONG GrantedAccess=0; ULONG64 Object=0, ObjType=0; FIELD_INFO hTableEntryFields[] = { {"Object", "", 0, DBG_DUMP_FIELD_COPY_FIELD_DATA, 0, (PVOID) &Object}, {"GrantedAccess", "", 0, DBG_DUMP_FIELD_COPY_FIELD_DATA, 0, (PVOID) &GrantedAccess}, }; FIELD_INFO ObjHeaderFields[] = { {"Type", "", 0, DBG_DUMP_FIELD_COPY_FIELD_DATA, 0, (PVOID) &ObjType}, {"Body", "", 0, DBG_DUMP_FIELD_RETURN_ADDRESS, 0, NULL}, }; SYM_DUMP_PARAM ObjSym = { sizeof (SYM_DUMP_PARAM), "nt!_HANDLE_TABLE_ENTRY", DBG_DUMP_NO_PRINT, pHandleTableEntry, NULL, NULL, NULL, sizeof (hTableEntryFields) /sizeof (FIELD_INFO), &hTableEntryFields[0], }; if (Ioctl (IG_DUMP_SYMBOL_INFO, &ObjSym, ObjSym.size)) { dprintf("Unable to get _HANDLE_TABLE_ENTRY : %p\n", pHandleTableEntry); return FALSE; } if (!(Object)) { //only print if flag is set to 4 if (Flags & 4) { dprintf("%04lx: free handle, Entry address %p, Next Entry %p\n", (ULONG)Handle, pHandleTableEntry, GrantedAccess); } return TRUE; } if (BuildNo > 2230) { // if (GetExpression( "nt!ObpAccessProtectCloseBit" )) { // // we have a new handle table style // //actual hdr has the lowest 3 bits cancelled out //lower 3 bits mark auditing, inheritance and lock ulObjectHeaderAddr = (Object) & ~(0x7); // // Apply the sign extension, if the highest bit is set // if ( !IsPtr64() && (Object & 0x80000000)) { ulObjectHeaderAddr |= 0xFFFFFFFF00000000L; } } else { //actual hdr is sign extend value with the lowest 3 bits cancelled out //top bit marks whether entry is locked //lower 3 bits mark auditing, inheritance and protection if (!IsPtr64()) { ulObjectHeaderAddr = ((Object) & ~(0x7)) | 0xFFFFFFFF80000000L; } else { ulObjectHeaderAddr = ((Object) & ~(0x7)) | 0x8000000000000000L; } } ObjSym.sName = "nt!_OBJECT_HEADER"; ObjSym.addr = ulObjectHeaderAddr; ObjSym.nFields = sizeof (ObjHeaderFields) / sizeof (FIELD_INFO); ObjSym.Fields = &ObjHeaderFields[0]; if (Ioctl ( IG_DUMP_SYMBOL_INFO, &ObjSym, ObjSym.size)) { dprintf("%08p: Unable to read nonpaged object header\n", ulObjectHeaderAddr); return FALSE; } if (pObjectType != 0 && ObjType != pObjectType) { return TRUE; } if (Flags & 0x20) { // // PspCidTable contains pointer to object, not object header // Compute header address based on object. // ObjectBody = ulObjectHeaderAddr; ulObjectHeaderAddr -= ObjHeaderFields[1].address-ulObjectHeaderAddr; } else { ObjectBody = ObjHeaderFields[1].address; } dprintf("%04I64lx: Object: %08p GrantedAccess: %08lx", Handle, ObjectBody, (GrantedAccess & ~MAXIMUM_ALLOWED)); if (BuildNo > 2230) { // if (GetExpression( "nt!ObpAccessProtectCloseBit" )) { // // New handle table style // if (((ULONG) Object & 1) == 0) { dprintf(" (Locked)"); } if (GrantedAccess & MAXIMUM_ALLOWED) { dprintf(" (Protected)"); } } else { if (IsPtr64()) { if (Object & 0x8000000000000000L) { dprintf(" (Locked)"); } } else if ((ULONG) Object & 0x80000000) { dprintf(" (Locked)"); } if (Object & 1) { dprintf(" (Protected)"); } } if (Object & 2) { dprintf(" (Inherit)"); } if (Object & 4) { dprintf(" (Audit)"); } dprintf("\n"); if (Flags & 2) { DumpObject( " ", ObjectBody, Flags ); } EXPRLastDump = ObjectBody; dprintf("\n"); return TRUE; }