|
|
/*++
Copyright (c) 1992-1999 Microsoft Corporation
Module Name:
process.c
Abstract:
WinDbg Extension Api
Environment:
User Mode.
Revision History: Kshitiz K. Sharma (kksharma) Using debugger type info. --*/
#include "precomp.h"
#pragma hdrstop
typedef enum _KTHREAD_STATE { Initialized, Ready, Running, Standby, Terminated, Waiting, Transition } KTHREAD_STATE;
CHAR *WaitReasonList[] = { "Executive", "FreePage", "PageIn", "PoolAllocation", "DelayExecution", "Suspended", "UserRequest", "WrExecutive", "WrFreePage", "WrPageIn", "WrPoolAllocation", "WrDelayExecution", "WrSuspended", "WrUserRequest", "WrEventPairHigh", "WrEventPairLow", "WrLpcReceive", "WrLpcReply", "WrVirtualMemory", "WrPageOut", "Spare1", "Spare2", "Spare3", "Spare4", "Spare5", "Spare6", "Spare7"};
extern ULONG64 STeip, STebp, STesp; #if 0 // MAKE IT BUILD
static PHANDLE_TABLE PspCidTable; static HANDLE_TABLE CapturedPspCidTable; #endif
ULONG64 ProcessLastDump; ULONG64 ThreadLastDump; ULONG TotalProcessCommit;
CHAR * SecImpLevel[] = { "Anonymous", "Identification", "Impersonation", "Delegation" };
#define SecImpLevels(x) (x < sizeof( SecImpLevel ) / sizeof( PSTR ) ? \
SecImpLevel[ x ] : "Illegal Value" )
typedef BOOLEAN (WINAPI *PENUM_PROCESS_CALLBACK)(PVOID ProcessAddress, PVOID Process, PVOID ThreadAddress, PVOID Thread);
BOOLEAN GetTheSystemTime ( OUT PLARGE_INTEGER Time ) { BYTE readTime[20]={0}; PCHAR SysTime; ULONG err;
ZeroMemory( Time, sizeof(*Time) );
SysTime = "SystemTime";
if (err = GetFieldValue(MM_SHARED_USER_DATA_VA, "nt!_KUSER_SHARED_DATA", SysTime, readTime)) { if (err == MEMORY_READ_ERROR) { dprintf( "unable to read memory @ %lx\n", MM_SHARED_USER_DATA_VA); } else { dprintf("type nt!_KUSER_SHARED_DATA not found.\n"); } return FALSE; }
*Time = *(LARGE_INTEGER UNALIGNED *)&readTime[0]; return TRUE; }
VOID dumpSymbolicAddress( ULONG64 Address, PCHAR Buffer, BOOL AlwaysShowHex ) { ULONG64 displacement; PCHAR s;
Buffer[0] = '!'; GetSymbol((ULONG64)Address, Buffer, &displacement); s = (PCHAR) Buffer + strlen( (PCHAR) Buffer ); if (s == (PCHAR) Buffer) { sprintf( s, (IsPtr64() ? "0x%016I64x" : "0x%08x"), Address ); } else { if (displacement != 0) { sprintf( s, (IsPtr64() ? "+0x%016I64x" : "+0x%08x"), displacement ); } if (AlwaysShowHex) { sprintf( s, (IsPtr64() ? " (0x%016I64x)" : " (0x%08x)"), Address ); } }
return; }
BOOL GetProcessHead(PULONG64 Head, PULONG64 First) { ULONG64 List_Flink = 0; *Head = GetNtDebuggerData( PsActiveProcessHead ); if (!*Head) { dprintf("Unable to get value of PsActiveProcessHead\n"); return FALSE; }
if (GetFieldValue(*Head, "nt!_LIST_ENTRY", "Flink", List_Flink)) { dprintf("Unable to read _LIST_ENTRY @ %p \n", *Head); return FALSE; }
if (List_Flink == 0) { dprintf("NULL value in PsActiveProcess List\n"); return FALSE; } *First = List_Flink; return TRUE; }
ULONG64 LookupProcessByName(PCSTR Name, BOOL Verbose) { ULONG64 ProcessHead, Process; ULONG64 ProcessNext; ULONG Off;
if (!GetProcessHead(&ProcessHead, &ProcessNext)) { return 0; }
//
// Walk through the list and find the process with the desired name.
//
if (GetFieldOffset("nt!_EPROCESS", "ActiveProcessLinks", &Off)) { dprintf("Unable to get EPROCESS.ActiveProcessLinks offset\n"); return 0; } while (ProcessNext != 0 && ProcessNext != ProcessHead) { char ImageFileName[64]; Process = ProcessNext - Off;
if (GetFieldValue(Process, "nt!_EPROCESS", "ImageFileName", ImageFileName)) { dprintf("Cannot read EPROCESS at %p\n", Process); } else { if (Verbose) { dprintf(" Checking process %s\n", ImageFileName); } if (!_strcmpi(Name, ImageFileName)) { return Process; } }
if (!ReadPointer(ProcessNext, &ProcessNext)) { dprintf("Cannot read EPROCESS at %p\n", Process); return 0; } if (CheckControlC()) { return 0; } } return 0; }
BOOL WaitForExceptionEvent(ULONG Code, ULONG FirstChance, ULONG64 Process) { HRESULT Status; Status = g_ExtControl->WaitForEvent(DEBUG_WAIT_DEFAULT, INFINITE); if (Status != S_OK) { dprintf("Unable to wait, 0x%X\n", Status); return Status; }
//
// Got some kind of event. Make sure it's the right kind.
//
ULONG Type, ProcId, ThreadId; DEBUG_LAST_EVENT_INFO_EXCEPTION ExInfo; if ((Status = g_ExtControl-> GetLastEventInformation(&Type, &ProcId, &ThreadId, &ExInfo, sizeof(ExInfo), NULL, NULL, 0, NULL)) != S_OK) { dprintf("Unable to get event information\n"); return Status; }
if (Type != DEBUG_EVENT_EXCEPTION || (ULONG)ExInfo.ExceptionRecord.ExceptionCode != Code || ExInfo.FirstChance != FirstChance) { dprintf("Unexpected event occurred\n"); return E_UNEXPECTED; }
if (Process) { ULONG Processor; ULONG64 EventProcess; if (!GetCurrentProcessor(g_ExtClient, &Processor, NULL)) { Processor = 0; } GetCurrentProcessAddr(Processor, 0, &EventProcess); if (EventProcess != Process) { dprintf("Event occurred in wrong process\n"); return E_UNEXPECTED; } }
return S_OK; }
BOOL WaitForSingleStep(ULONG64 Process) { HRESULT Status; Status = g_ExtControl->WaitForEvent(DEBUG_WAIT_DEFAULT, INFINITE); if (Status != S_OK) { dprintf("Unable to wait, 0x%X\n", Status); return Status; }
//
// Got some kind of event. Make sure it's the right kind.
//
ULONG Type, ProcId, ThreadId; if ((Status = g_ExtControl-> GetLastEventInformation(&Type, &ProcId, &ThreadId, NULL, 0, NULL, NULL, 0, NULL)) != S_OK) { dprintf("Unable to get event information\n"); return Status; }
if (Type != 0) { dprintf("Unexpected event occurred\n"); return E_UNEXPECTED; }
if (Process) { ULONG Processor; ULONG64 EventProcess; if (!GetCurrentProcessor(g_ExtClient, &Processor, NULL)) { Processor = 0; } GetCurrentProcessAddr(Processor, 0, &EventProcess); if (EventProcess != Process) { dprintf("Event occurred in wrong process\n"); return E_UNEXPECTED; } }
return S_OK; }
DECLARE_API( bpid )
/*++
Routine Description:
Uses winlogon to cause a user-mode break in the given process.
Arguments:
None.
Return Value:
None.
--*/
{ INIT_API();
if (BuildNo < 2195) { dprintf("bpid only works on 2195 or above\n"); goto Exit; } if (TargetMachine != IMAGE_FILE_MACHINE_I386 && TargetMachine != IMAGE_FILE_MACHINE_IA64) { dprintf("bpid is only implemented for x86 and IA64\n"); goto Exit; }
BOOL StopInWinlogon; BOOL Verbose; BOOL WritePidToMemory; ULONG WhichGlobal; PSTR WhichGlobalName;
StopInWinlogon = FALSE; Verbose = FALSE; WritePidToMemory = FALSE; WhichGlobal = 1; WhichGlobalName = "Breakin"; for (;;) { while (*args == ' ' || *args == '\t') { args++; }
if (*args == '-' || *args == '/') { switch(*++args) { case 'a': // Set g_AttachProcessId instead of
// g_BreakinProcessId.
WhichGlobal = 2; WhichGlobalName = "Attach"; break; case 's': StopInWinlogon = TRUE; break; case 'v': Verbose = TRUE; break; case 'w': WritePidToMemory = TRUE; break; default: dprintf("Unknown option '%c'\n", *args); goto Exit; }
args++; } else { break; } } ULONG64 Pid; if (!GetExpressionEx(args, &Pid, &args)) { dprintf("Usage: bpid <pid>\n"); goto Exit; }
ULONG64 Winlogon; ULONG64 WinlToken; dprintf("Finding winlogon...\n"); Winlogon = LookupProcessByName("winlogon.exe", Verbose); if (Winlogon == 0) { dprintf("Unable to find winlogon\n"); goto Exit; } if (GetFieldValue(Winlogon, "nt!_EPROCESS", "Token", WinlToken)) { dprintf("Unable to read winlogon process token\n"); goto Exit; } // Low bits of the token value are flags. Mask off to get pointer.
if (IsPtr64()) { WinlToken &= ~15; } else { WinlToken = (ULONG64)(LONG64)(LONG)(WinlToken & ~7); }
ULONG ExpOff;
//
// winlogon checks its token expiration time. If it's
// zero it breaks in and checks a few things, one of which is whether
// it should inject a DebugBreak into a process. First,
// set the token expiration to zero so that winlogon breaks in.
//
if (GetFieldOffset("nt!_TOKEN", "ExpirationTime", &ExpOff)) { dprintf("Unable to get TOKEN.ExpirationTime offset\n"); goto Exit; } WinlToken += ExpOff; ULONG64 Expiration, Zero; ULONG Done;
// Save the expiration time.
if (!ReadMemory(WinlToken, &Expiration, sizeof(Expiration), &Done) || Done != sizeof(Expiration)) { dprintf("Unable to read token expiration\n"); goto Exit; }
// Zero it.
Zero = 0; if (!WriteMemory(WinlToken, &Zero, sizeof(Zero), &Done) || Done != sizeof(Zero)) { dprintf("Unable to write token expiration\n"); goto Exit; }
HRESULT Hr;
// Get things running.
if (g_ExtControl->SetExecutionStatus(DEBUG_STATUS_GO) != S_OK) { dprintf("Unable to go\n"); goto RestoreExp; } // Wait for a breakin.
dprintf("Waiting for winlogon to break. " "This can take a couple of minutes...\n"); Hr = WaitForExceptionEvent(STATUS_BREAKPOINT, TRUE, Winlogon); if (Hr != S_OK) { goto RestoreExp; }
//
// We broke into winlogon.
// We need to set winlogon!g_[Breakin|Attach]ProcessId to
// the process we want to break into. Relying on symbols
// is pretty fragile as the image header may be paged out
// or the symbol path may be wrong. Even if we had good symbols
// the variable itself may not be paged in at this point.
// The approach taken here is to single-step out to where
// the global is checked and insert the value at that
// point. winlogon currently checks two globals after the
// DebugBreak. g_BreakinProcessId is the first one and
// g_AttachProcessId is the second.
//
ULONG Steps; ULONG Globals; ULONG64 BpiAddr; ULONG64 UserProbeAddress; PSTR RegDst; dprintf("Stepping to g_%sProcessId check...\n", WhichGlobalName); Steps = 0; Globals = 0; UserProbeAddress = GetNtDebuggerDataValue(MmUserProbeAddress); while (Globals < WhichGlobal) { if (CheckControlC()) { goto RestoreExp; } if (g_ExtControl->SetExecutionStatus(DEBUG_STATUS_STEP_OVER) != S_OK) { dprintf("Unable to start step\n"); goto RestoreExp; } Hr = WaitForSingleStep(Winlogon); if (Hr != S_OK) { goto RestoreExp; }
char DisStr[128]; int DisStrLen; ULONG64 Pc; // Check whether this is a global load.
if (g_ExtRegisters->GetInstructionOffset(&Pc) != S_OK || g_ExtControl->Disassemble(Pc, 0, DisStr, sizeof(DisStr), NULL, &Pc) != S_OK) { dprintf("Unable to check step\n"); goto RestoreExp; }
// Remove newline at end.
DisStrLen = strlen(DisStr); if (DisStrLen > 0 && DisStr[DisStrLen - 1] == '\n') { DisStr[--DisStrLen] = 0; } if (Verbose) { dprintf(" Step to '%s'\n", DisStr); } BpiAddr = 0; RegDst = NULL; PSTR OffStr; switch(TargetMachine) { case IMAGE_FILE_MACHINE_I386: if (strstr(DisStr, "mov") != NULL && strstr(DisStr, " eax,[") != NULL && DisStr[DisStrLen - 1] == ']' && (OffStr = strchr(DisStr, '[')) != NULL) {
RegDst = "eax"; //
// Found a load. Parse the offset.
//
PSTR SymTailStr = strchr(OffStr + 1, '('); if (SymTailStr != NULL) { // There's a symbol name in the reference. We
// can't check the actual symbol name as symbols
// aren't necessarily correct, so just skip
// to the open paren.
OffStr = SymTailStr + 1; } for (;;) { OffStr++; if (*OffStr >= '0' && *OffStr <= '9') { BpiAddr = BpiAddr * 16 + (*OffStr - '0'); } else if (*OffStr >= 'a' && *OffStr <= 'f') { BpiAddr = BpiAddr * 16 + (*OffStr - 'a'); } else { BpiAddr = (ULONG64)(LONG64)(LONG)BpiAddr; break; } } if (*OffStr != ']' && *OffStr != ')') { BpiAddr = 0; } } break;
case IMAGE_FILE_MACHINE_IA64: if (strstr(DisStr, "ld4") != NULL && (OffStr = strchr(DisStr, '[')) != NULL) {
// Extract destination register name.
RegDst = OffStr - 1; if (*RegDst != '=') { break; } *RegDst-- = 0; while (RegDst > DisStr && *RegDst != ' ') { RegDst--; } if (*RegDst != ' ') { break; } RegDst++;
// Extract source register name and value.
PSTR RegSrc = ++OffStr; while (*OffStr && *OffStr != ']') { OffStr++; } if (*OffStr == ']') { *OffStr = 0;
DEBUG_VALUE RegVal; ULONG RegIdx; if (g_ExtRegisters->GetIndexByName(RegSrc, &RegIdx) == S_OK && g_ExtRegisters->GetValue(RegIdx, &RegVal) == S_OK && RegVal.Type == DEBUG_VALUE_INT64) { BpiAddr = RegVal.I64; } } } break; }
if (RegDst != NULL && BpiAddr >= 0x10000 && BpiAddr < UserProbeAddress) { // Looks like a reasonable global load.
Globals++; } if (++Steps > 30) { dprintf("Unable to find g_%sProcessId load\n", WhichGlobalName); goto RestoreExp; } }
//
// We're at the mov eax,[g_BreakinProcessId] instruction.
// Execute the instruction to accomplish two things:
// 1. The page will be made available so we can write
// to it if we need to.
// 2. If we don't want to write the actual memory we
// can just set eax to do a one-time break.
//
if (g_ExtControl->SetExecutionStatus(DEBUG_STATUS_STEP_OVER) != S_OK) { dprintf("Unable to start step\n"); goto RestoreExp; } Hr = WaitForSingleStep(Winlogon); if (Hr != S_OK) { goto RestoreExp; }
char RegCmd[64];
//
// Update the register and write memory if necessary.
//
sprintf(RegCmd, "r %s=0x0`%x", RegDst, (ULONG)Pid); if (g_ExtControl->Execute(DEBUG_OUTCTL_IGNORE, RegCmd, DEBUG_EXECUTE_NOT_LOGGED | DEBUG_EXECUTE_NO_REPEAT) != S_OK) { goto RestoreExp; } if (WritePidToMemory) { if (!WriteMemory(BpiAddr, &Pid, sizeof(ULONG), &Done) || Done != sizeof(ULONG)) { dprintf("Unable to write pid to g_%sProcessId, continuing\n", WhichGlobalName); } }
// Everything is set. Resume execution and the break should
// occur.
dprintf("Break into process %x set. " "The next break should be in the desired process.\n", (ULONG)Pid);
if (!StopInWinlogon) { if (g_ExtControl->SetExecutionStatus(DEBUG_STATUS_GO) != S_OK) { dprintf("Unable to go\n"); } } else { dprintf("Stopping in winlogon\n"); } RestoreExp: if (!WriteMemory(WinlToken, &Expiration, sizeof(Expiration), &Done) || Done != sizeof(Expiration)) { dprintf("Unable to restore token expiration\n"); } Exit: EXIT_API(); return S_OK; }
BOOL GetProcessSessionId(ULONG64 Process, PULONG SessionId) { *SessionId = 0; if (BuildNo && BuildNo < 2280) { GetFieldValue(Process, "nt!_EPROCESS", "SessionId", *SessionId); } else { ULONG64 SessionPointer;
GetFieldValue(Process, "nt!_EPROCESS", "Session", SessionPointer); if (SessionPointer != 0) { if (GetFieldValue(SessionPointer, "nt!_MM_SESSION_SPACE", "SessionId", *SessionId)) { dprintf("Could not find _MM_SESSION_SPACE type at %p.\n", SessionPointer); return FALSE; } } }
return TRUE; }
BOOL DumpProcess( IN char * pad, IN ULONG64 RealProcessBase, IN ULONG Flags, IN OPTIONAL PCHAR ImageFileName ) { LARGE_INTEGER RunTime; BOOL LongAddrs = IsPtr64(); TIME_FIELDS Times; ULONG TimeIncrement; STRING string1, string2; ULONG64 ThreadListHead_Flink=0, ActiveProcessLinks_Flink=0; ULONG64 UniqueProcessId=0, Peb=0, InheritedFromUniqueProcessId=0, NumberOfHandles=0; ULONG64 ObjectTable=0, NumberOfPrivatePages=0, ModifiedPageCount=0, NumberOfLockedPages=0; ULONG NVads = 0; ULONG64 VadRoot=0, CloneRoot=0, DeviceMap=0, Token=0; ULONG64 CreateTime_QuadPart=0, Pcb_UserTime=0, Pcb_KernelTime=0; ULONG64 Vm_WorkingSetSize=0, Vm_MinimumWorkingSetSize=0, Vm_MaximumWorkingSetSize=0; ULONG64 Vm_PeakWorkingSetSize=0, VirtualSize=0, PeakVirtualSize=0, Vm_PageFaultCount=0; ULONG64 Vm_MemoryPriority=0, Pcb_BasePriority=0, CommitCharge=0, DebugPort=0, Job=0; ULONG SessionId, Pcb_Header_Type=0; CHAR Pcb_DirectoryTableBase[16]={0}, QuotaPoolUsage[16]={0}, ImageFileName_Read[32] = {0}; TCHAR procType[] = "_EPROCESS";
if (GetFieldValue(RealProcessBase, "nt!_EPROCESS", "UniqueProcessId", UniqueProcessId)) { dprintf("Could not find _EPROCESS type at %p.\n", RealProcessBase); return FALSE; }
if (!GetProcessSessionId(RealProcessBase, &SessionId)) { return FALSE; } GetFieldValue(RealProcessBase, "nt!_EPROCESS", "Peb", Peb); GetFieldValue(RealProcessBase, "nt!_EPROCESS", "InheritedFromUniqueProcessId",InheritedFromUniqueProcessId); GetFieldValue(RealProcessBase, "nt!_EPROCESS", "Pcb.DirectoryTableBase", Pcb_DirectoryTableBase); GetFieldValue(RealProcessBase, "nt!_EPROCESS", "Pcb.Header.Type", Pcb_Header_Type); GetFieldValue(RealProcessBase, "nt!_EPROCESS", "ObjectTable", ObjectTable); GetFieldValue(RealProcessBase, "nt!_EPROCESS", "ImageFileName", ImageFileName_Read); GetFieldValue(RealProcessBase, "nt!_EPROCESS", "NumberOfVads", NVads); GetFieldValue(RealProcessBase, "nt!_EPROCESS", "VadRoot", VadRoot); GetFieldValue(RealProcessBase, "nt!_EPROCESS", "CloneRoot", CloneRoot); GetFieldValue(RealProcessBase, "nt!_EPROCESS", "NumberOfPrivatePages", NumberOfPrivatePages); GetFieldValue(RealProcessBase, "nt!_EPROCESS", "ModifiedPageCount", ModifiedPageCount); GetFieldValue(RealProcessBase, "nt!_EPROCESS", "NumberOfLockedPages", NumberOfLockedPages); GetFieldValue(RealProcessBase, "nt!_EPROCESS", "DeviceMap", DeviceMap); GetFieldValue(RealProcessBase, "nt!_EPROCESS", "Token", Token); if (IsPtr64()) { Token = Token & ~(ULONG64)15; } else { Token = Token & ~(ULONG64)7; } GetFieldValue(RealProcessBase, "nt!_EPROCESS", "CreateTime.QuadPart", CreateTime_QuadPart); GetFieldValue(RealProcessBase, "nt!_EPROCESS", "Pcb.UserTime", Pcb_UserTime); GetFieldValue(RealProcessBase, "nt!_EPROCESS", "Pcb.KernelTime", Pcb_KernelTime); GetFieldValue(RealProcessBase, "nt!_EPROCESS", "QuotaPoolUsage", QuotaPoolUsage); GetFieldValue(RealProcessBase, "nt!_EPROCESS", "Vm.WorkingSetSize", Vm_WorkingSetSize); GetFieldValue(RealProcessBase, "nt!_EPROCESS", "Vm.MinimumWorkingSetSize",Vm_MinimumWorkingSetSize); GetFieldValue(RealProcessBase, "nt!_EPROCESS", "Vm.MaximumWorkingSetSize",Vm_MaximumWorkingSetSize); GetFieldValue(RealProcessBase, "nt!_EPROCESS", "Vm.PeakWorkingSetSize", Vm_PeakWorkingSetSize); GetFieldValue(RealProcessBase, "nt!_EPROCESS", "VirtualSize", VirtualSize); GetFieldValue(RealProcessBase, "nt!_EPROCESS", "PeakVirtualSize", PeakVirtualSize); GetFieldValue(RealProcessBase, "nt!_EPROCESS", "Vm.PageFaultCount", Vm_PageFaultCount); GetFieldValue(RealProcessBase, "nt!_EPROCESS", "Vm.MemoryPriority", Vm_MemoryPriority); GetFieldValue(RealProcessBase, "nt!_EPROCESS", "Pcb.BasePriority", Pcb_BasePriority); GetFieldValue(RealProcessBase, "nt!_EPROCESS", "CommitCharge", CommitCharge); GetFieldValue(RealProcessBase, "nt!_EPROCESS", "DebugPort", DebugPort); GetFieldValue(RealProcessBase, "nt!_EPROCESS", "Job", Job); GetFieldValue(RealProcessBase, "nt!_EPROCESS", "Pcb.ThreadListHead.Flink",ThreadListHead_Flink); GetFieldValue(RealProcessBase, "nt!_EPROCESS", "ActiveProcessLinks.Flink",ActiveProcessLinks_Flink); GetFieldValue(ObjectTable, "nt!_HANDLE_TABLE", "HandleCount", NumberOfHandles); // dprintf( " Proc list Next:%I64x, ProceDump:%I64x, Head:%I64x...\n", Next, ProcessToDump, ProcessHead);
// dprintf("Proc %I64x, %8p %16.16I64x", ProcessToDump, ProcessToDump, ProcessToDump);
if (Pcb_Header_Type != ProcessObject) { dprintf("TYPE mismatch for process object at %p\n", RealProcessBase); return FALSE; }
//
// Get the image file name
//
if (ImageFileName_Read[0] == 0 ) { strcpy(ImageFileName_Read,"System Process"); }
if (ImageFileName != NULL) { RtlInitString(&string1, ImageFileName); RtlInitString(&string2, ImageFileName_Read); if (RtlCompareString(&string1, &string2, TRUE) != 0) { return TRUE; } }
dprintf("%sPROCESS %08p", pad, RealProcessBase);
dprintf("%s%sSessionId: %u Cid: %04I64x Peb: %08I64x ParentCid: %04I64x\n", (LongAddrs ? "\n " : " "), (LongAddrs ? pad : " "), SessionId, UniqueProcessId, Peb, InheritedFromUniqueProcessId );
if (LongAddrs) { dprintf("%s DirBase: %08I64lx ObjectTable: %08p TableSize: %3u.\n", pad, *((ULONG64 *) &Pcb_DirectoryTableBase[ 0 ]), ObjectTable, (ULONG) NumberOfHandles ); } else { dprintf("%s DirBase: %08lx ObjectTable: %08p TableSize: %3u.\n", pad, *((ULONG *) &Pcb_DirectoryTableBase[ 0 ]), ObjectTable, (ULONG) NumberOfHandles ); }
dprintf("%s Image: %s\n",pad,ImageFileName_Read);
if (!(Flags & 1)) { dprintf("\n"); return TRUE; }
dprintf("%s VadRoot %p Vads %ld Clone %1p Private %I64d. Modified %I64ld. Locked %I64d.\n", pad, VadRoot , NVads, CloneRoot, NumberOfPrivatePages, ModifiedPageCount, NumberOfLockedPages);
dprintf("%s DeviceMap %p\n", pad, DeviceMap );
//
// Primary token
//
dprintf("%s Token %p\n", pad, Token);
//
// Get the time increment value which is used to compute runtime.
//
TimeIncrement = GetNtDebuggerDataValue( KeTimeIncrement );
GetTheSystemTime (&RunTime); RunTime.QuadPart -= CreateTime_QuadPart; RtlTimeToElapsedTimeFields ( &RunTime, &Times); dprintf("%s ElapsedTime %3ld:%02ld:%02ld.%04ld\n", pad, Times.Hour, Times.Minute, Times.Second, Times.Milliseconds);
RunTime.QuadPart = UInt32x32To64(Pcb_UserTime, TimeIncrement); RtlTimeToElapsedTimeFields ( &RunTime, &Times); dprintf("%s UserTime %3ld:%02ld:%02ld.%04ld\n", pad, Times.Hour, Times.Minute, Times.Second, Times.Milliseconds);
RunTime.QuadPart = UInt32x32To64(Pcb_KernelTime, TimeIncrement); RtlTimeToElapsedTimeFields ( &RunTime, &Times); dprintf("%s KernelTime %3ld:%02ld:%02ld.%04ld\n", pad, Times.Hour, Times.Minute, Times.Second, Times.Milliseconds);
if (!LongAddrs) { dprintf("%s QuotaPoolUsage[PagedPool] %ld\n", pad,*((ULONG *) &QuotaPoolUsage[PagedPool*4]) ); dprintf("%s QuotaPoolUsage[NonPagedPool] %ld\n", pad,*((ULONG *) &QuotaPoolUsage[NonPagedPool*4]) ); } else { dprintf("%s QuotaPoolUsage[PagedPool] %I64ld\n", pad,*((ULONG64 *) &QuotaPoolUsage[PagedPool*8]) ); dprintf("%s QuotaPoolUsage[NonPagedPool] %I64ld\n", pad,*((ULONG64 *) &QuotaPoolUsage[NonPagedPool*8]) ); }
dprintf("%s Working Set Sizes (now,min,max) (%I64ld, %I64ld, %I64ld) (%I64ldKB, %I64ldKB, %I64ldKB)\n", pad, Vm_WorkingSetSize, Vm_MinimumWorkingSetSize, Vm_MaximumWorkingSetSize, _KB*Vm_WorkingSetSize, _KB*Vm_MinimumWorkingSetSize, _KB*Vm_MaximumWorkingSetSize ); dprintf("%s PeakWorkingSetSize %I64ld\n", pad, Vm_PeakWorkingSetSize ); dprintf("%s VirtualSize %I64ld Mb\n", pad, VirtualSize /(1024*1024) ); dprintf("%s PeakVirtualSize %I64ld Mb\n", pad, PeakVirtualSize/(1024*1024) ); dprintf("%s PageFaultCount %I64ld\n", pad, Vm_PageFaultCount ); dprintf("%s MemoryPriority %s\n", pad, Vm_MemoryPriority ? "FOREGROUND" : "BACKGROUND" ); dprintf("%s BasePriority %I64ld\n", pad, Pcb_BasePriority); dprintf("%s CommitCharge %I64ld\n", pad, CommitCharge ); if ( DebugPort ) { dprintf("%s DebugPort %p\n", pad, DebugPort ); } if ( Job ) { dprintf("%s Job %p\n", pad, Job ); }
dprintf("\n");
return TRUE;
}
//
// This is to be called from .c file extensions which do not do INIT_API
// that is they do not set g_ExtControl needed for stacktrace in DumpThread
//
// It will set the globals needed to dump stacktrace and call DumpThread
//
BOOL DumpThreadEx ( IN ULONG Processor, IN char *Pad, IN ULONG64 RealThreadBase, IN ULONG Flags, IN PDEBUG_CLIENT pDbgClient ) { BOOL retval = FALSE; if (pDbgClient && (ExtQuery(pDbgClient) == S_OK)) { retval = DumpThread(Processor, Pad, RealThreadBase, Flags);
ExtRelease(); } return retval; }
BOOL DumpThread ( IN ULONG Processor, IN char *Pad, IN ULONG64 RealThreadBase, IN ULONG64 Flags ) { #define MAX_STACK_FRAMES 40
TIME_FIELDS Times; LARGE_INTEGER RunTime; ULONG64 Address; ULONG WaitOffset; ULONG64 Process; CHAR Buffer[256]; ULONG TimeIncrement; ULONG frames = 0; ULONG i; ULONG err; ULONG64 displacement; DEBUG_STACK_FRAME stk[MAX_STACK_FRAMES]; BOOL LongAddrs = IsPtr64(); ULONG Tcb_Alertable = 0, Tcb_Proc; ULONG64 ActiveImpersonationInfo=0, Cid_UniqueProcess=0, Cid_UniqueThread=0, ImpersonationInfo=0, ImpersonationInfo_ImpersonationLevel=0, ImpersonationInfo_Token=0, IrpList_Flink=0, IrpList_Blink=0, LpcReceivedMessageId=0, LpcReceivedMsgIdValid=0, LpcReplyMessage=0, LpcReplyMessageId=0, PerformanceCountHigh=0, PerformanceCountLow=0, StartAddress=0, Tcb_ApcState_Process=0, Tcb_BasePriority=0, Tcb_CallbackStack=0, Tcb_ContextSwitches=0, Tcb_DecrementCount=0, Tcb_EnableStackSwap=0, Tcb_FreezeCount=0, Tcb_Header_Type=0, Tcb_InitialStack=0, Tcb_KernelStack=0, Tcb_KernelStackResident=0, Tcb_KernelTime=0, Tcb_LargeStack=0, Tcb_NextProcessor=0, Tcb_Priority=0, Tcb_PriorityDecrement=0, Tcb_StackBase=0, Tcb_StackLimit=0, Tcb_State=0, Tcb_SuspendCount=0, Tcb_Teb=0, Tcb_UserTime=0, Tcb_WaitBlockList=0, Tcb_WaitMode=0, Tcb_WaitReason=0, Tcb_WaitTime=0, Tcb_Win32Thread=0, Win32StartAddress=0, ObpLUIDDeviceMapsEnabled=0; TCHAR threadTyp[] = "nt!_ETHREAD"; FIELD_INFO threadFlds[] = { {(PUCHAR) "IrpList", NULL, 0, DBG_DUMP_FIELD_RETURN_ADDRESS, 0, NULL}, }; SYM_DUMP_PARAM sThread = { sizeof (SYM_DUMP_PARAM), (PUCHAR) &threadTyp[0], DBG_DUMP_NO_PRINT, RealThreadBase, NULL, NULL, NULL, 1, &threadFlds[0], };
if (Ioctl(IG_DUMP_SYMBOL_INFO, (PBYTE) &sThread, sizeof(SYM_DUMP_PARAM))) { // To get address of IrpList
dprintf("Cannot find _ETHREAD type.\n"); return FALSE; }
if (CheckControlC()) { return FALSE; }
InitTypeRead(RealThreadBase, nt!_ETHREAD);
Tcb_Header_Type = ReadField(Tcb.Header.Type); Cid_UniqueProcess = ReadField(Cid.UniqueProcess); Cid_UniqueThread = ReadField(Cid.UniqueThread); Tcb_Teb = ReadField(Tcb.Teb); Tcb_Win32Thread = ReadField(Tcb.Win32Thread); Tcb_State = ReadField(Tcb.State); Tcb_WaitReason = ReadField(Tcb.WaitReason); Tcb_WaitMode = ReadField(Tcb.WaitMode); Tcb_Alertable = (ULONG) ReadField(Tcb.Alertable);
if (Tcb_Header_Type != ThreadObject) { dprintf("TYPE mismatch for thread object at %p\n",RealThreadBase); return FALSE; }
dprintf("%sTHREAD %p Cid %1p.%1p Teb: %p %s%sWin32Thread: %p ", Pad, RealThreadBase, Cid_UniqueProcess, Cid_UniqueThread, Tcb_Teb, (LongAddrs ? "\n" : ""), (LongAddrs ? Pad : ""), Tcb_Win32Thread);
switch (Tcb_State) { case Initialized: dprintf("%s\n","INITIALIZED");break; case Ready: dprintf("%s\n","READY");break; case Running: dprintf("%s%s%s on processor %lx\n", (!LongAddrs ? "\n" : ""), (!LongAddrs ? Pad : ""), "RUNNING", Tcb_Proc = (ULONG) ReadField(Tcb.NextProcessor)); break; case Standby: dprintf("%s\n","STANDBY");break; case Terminated: dprintf("%s\n","TERMINATED");break; case Waiting: dprintf("%s","WAIT");break; case Transition: dprintf("%s","TRANSITION");break; }
if (!(Flags & 2)) { dprintf("\n"); return TRUE; } Tcb_SuspendCount = ReadField(Tcb.SuspendCount); Tcb_FreezeCount = ReadField(Tcb.FreezeCount); Tcb_WaitBlockList = ReadField(Tcb.WaitBlockList); LpcReplyMessageId = ReadField(LpcReplyMessageId); LpcReplyMessage = ReadField(LpcReplyMessage); IrpList_Flink = ReadField(IrpList.Flink); IrpList_Blink = ReadField(IrpList.Blink); ActiveImpersonationInfo = ReadField(ActiveImpersonationInfo); ImpersonationInfo = ReadField(ImpersonationInfo); Tcb_ApcState_Process = ReadField(Tcb.ApcState.Process); Tcb_WaitTime = ReadField(Tcb.WaitTime); Tcb_ContextSwitches = ReadField(Tcb.ContextSwitches); Tcb_EnableStackSwap = ReadField(Tcb.EnableStackSwap); Tcb_LargeStack = ReadField(Tcb.LargeStack); Tcb_UserTime = ReadField(Tcb.UserTime); Tcb_KernelTime = ReadField(Tcb.KernelTime); PerformanceCountHigh = ReadField(PerformanceCountHigh); PerformanceCountLow = ReadField(PerformanceCountLow); StartAddress = ReadField(StartAddress); Win32StartAddress = ReadField(Win32StartAddress); LpcReceivedMsgIdValid = ReadField(LpcReceivedMsgIdValid); LpcReceivedMessageId = ReadField(LpcReceivedMessageId); Tcb_InitialStack = ReadField(Tcb.InitialStack); Tcb_KernelStack = ReadField(Tcb.KernelStack); Tcb_StackBase = ReadField(Tcb.StackBase); Tcb_StackLimit = ReadField(Tcb.StackLimit); Tcb_CallbackStack = ReadField(Tcb.CallbackStack); Tcb_Priority = ReadField(Tcb.Priority); Tcb_BasePriority = ReadField(Tcb.BasePriority); Tcb_PriorityDecrement = ReadField(Tcb.PriorityDecrement); Tcb_DecrementCount = ReadField(Tcb.DecrementCount); Tcb_KernelStackResident = ReadField(Tcb.KernelStackResident); Tcb_NextProcessor = ReadField(Tcb.NextProcessor);
if (Tcb_State == Waiting) { ULONG64 WaitBlock_Object=0, WaitBlock_NextWaitBlock=0;
dprintf(": (%s) %s %s\n", WaitReasonList[Tcb_WaitReason], (Tcb_WaitMode==0) ? "KernelMode" : "UserMode", Tcb_Alertable ? "Alertable" : "Non-Alertable"); if ( Tcb_SuspendCount ) { dprintf("SuspendCount %lx\n",Tcb_SuspendCount); } if ( Tcb_FreezeCount ) { dprintf("FreezeCount %lx\n",Tcb_FreezeCount); }
WaitOffset = (ULONG) (Tcb_WaitBlockList - RealThreadBase);
if (err = GetFieldValue(Tcb_WaitBlockList, "nt!_KWAIT_BLOCK", "Object", WaitBlock_Object)) { dprintf("%sCannot read nt!_KWAIT_BLOCK at %p - error %lx\n", Pad, Tcb_WaitBlockList, err); goto BadWaitBlock; }
GetFieldValue(Tcb_WaitBlockList, "nt!_KWAIT_BLOCK", "NextWaitBlock", WaitBlock_NextWaitBlock);
do { TCHAR MutantListEntry[16]={0}; ULONG64 OwnerThread=0, Header_Type=0;
dprintf("%s %p ",Pad, WaitBlock_Object);
GetFieldValue(WaitBlock_Object, "nt!_KMUTANT", "Header.Type", Header_Type); GetFieldValue(WaitBlock_Object, "nt!_KMUTANT", "MutantListEntry", MutantListEntry); GetFieldValue(WaitBlock_Object, "nt!_KMUTANT", "OwnerThread", OwnerThread); switch (Header_Type) { case EventNotificationObject: dprintf("NotificationEvent\n"); break; case EventSynchronizationObject: dprintf("SynchronizationEvent\n"); break; case SemaphoreObject: dprintf("Semaphore Limit 0x%lx\n", *((ULONG *) &MutantListEntry[0])); break; case ThreadObject: dprintf("Thread\n"); break; case TimerNotificationObject: dprintf("NotificationTimer\n"); break; case TimerSynchronizationObject: dprintf("SynchronizationTimer\n"); break; case EventPairObject: dprintf("EventPair\n"); break; case ProcessObject: dprintf("ProcessObject\n"); break; case MutantObject: dprintf("Mutant - owning thread %lp\n", OwnerThread); break; default: dprintf("Unknown\n"); // goto BadWaitBlock;
break; }
if ( WaitBlock_NextWaitBlock == Tcb_WaitBlockList) { break; goto BadWaitBlock; }
if (err = GetFieldValue(WaitBlock_NextWaitBlock, "nt!_KWAIT_BLOCK", "Object", WaitBlock_Object)) { dprintf("%sCannot read nt!_KWAIT_BLOCK at %p - error %lx\n", Pad, WaitBlock_NextWaitBlock, err); goto BadWaitBlock; } GetFieldValue(WaitBlock_NextWaitBlock, "nt!_KWAIT_BLOCK", "NextWaitBlock", WaitBlock_NextWaitBlock); if (CheckControlC()) { return FALSE; } } while ( TRUE ); }
BadWaitBlock: if (!(Flags & 4)) { dprintf("\n"); return TRUE; }
if (LpcReplyMessageId != 0) { dprintf("%sWaiting for reply to LPC MessageId %08p:\n",Pad,LpcReplyMessageId); }
if (LpcReplyMessage) {
if (LpcReplyMessage & 1) { dprintf("%sCurrent LPC port %08lp\n",Pad, (LpcReplyMessage & ~((ULONG64)1))); } else {
ULONG64 Entry_Flink, Entry_Blink;
dprintf("%sPending LPC Reply Message:\n",Pad); Address = (ULONG64) LpcReplyMessage;
GetFieldValue(Address, "nt!_LPCP_MESSAGE", "Entry.Flink", Entry_Flink); GetFieldValue(Address, "nt!_LPCP_MESSAGE", "Entry.Blink", Entry_Blink);
dprintf("%s %08lp: [%08lp,%08lp]\n", Pad, Address, Entry_Blink, Entry_Flink ); } }
if (IrpList_Flink && (IrpList_Flink != IrpList_Blink || IrpList_Flink != threadFlds[0].address) ) {
ULONG64 IrpListHead = threadFlds[0].address; ULONG64 Next; ULONG Counter = 0; FIELD_INFO fldAddress = {(PUCHAR) "ThreadListEntry", NULL, 0, DBG_DUMP_FIELD_RETURN_ADDRESS, 0, NULL}; SYM_DUMP_PARAM IrpSym = { sizeof (SYM_DUMP_PARAM), (PUCHAR) "nt!_IRP", DBG_DUMP_NO_PRINT, 0, NULL, NULL, NULL, 1, &fldAddress }; // For getting ThreadListEntry Offset in _IRP
Next = IrpList_Flink; if (!Ioctl(IG_DUMP_SYMBOL_INFO, (PBYTE) &IrpSym, sizeof (SYM_DUMP_PARAM))) { dprintf("%sIRP List:\n",Pad); while ((Next != IrpListHead) && (Counter < 17)) { ULONG Irp_Type=0, Irp_Size=0, Irp_Flags=0; ULONG64 Irp_MdlAddress=0; Counter += 1; // subtract threadlistentry offset
Address = Next - fldAddress.address; Next=0; GetFieldValue(Address, "nt!_IRP", "Type", Irp_Type); GetFieldValue(Address, "nt!_IRP", "Size", Irp_Size); GetFieldValue(Address, "nt!_IRP", "Flags", Irp_Flags); GetFieldValue(Address, "nt!_IRP", "MdlAddress", Irp_MdlAddress); GetFieldValue(Address, "nt!_IRP", "ThreadListEntry.Flink", Next);
dprintf("%s %08p: (%04x,%04x) Flags: %08lx Mdl: %08lp\n", Pad,Address,Irp_Type,Irp_Size,Irp_Flags,Irp_MdlAddress);
} } }
//
// Impersonation information
//
if (ActiveImpersonationInfo) { InitTypeRead(ImpersonationInfo, nt!_PS_IMPERSONATION_INFORMATION); ImpersonationInfo_Token = ReadField(Token); ImpersonationInfo_ImpersonationLevel = ReadField(ImpersonationLevel);
if (ImpersonationInfo_Token) { dprintf("%sImpersonation token: %p (Level %s)\n", Pad, ImpersonationInfo_Token, SecImpLevels( ImpersonationInfo_ImpersonationLevel ) ); } else { dprintf("%sUnable to read Impersonation Information at %x\n", Pad, ImpersonationInfo ); } } else { dprintf("%sNot impersonating\n", Pad); }
//
// DeviceMap information
//
// check to see if per-LUID devicemaps are turned on
ULONG64 ObpLUIDDeviceMapsEnabledAddress;
ObpLUIDDeviceMapsEnabledAddress = GetExpression("nt!ObpLUIDDeviceMapsEnabled"); if (ObpLUIDDeviceMapsEnabledAddress) { ObpLUIDDeviceMapsEnabled = GetUlongFromAddress(ObpLUIDDeviceMapsEnabled); } else { ObpLUIDDeviceMapsEnabled = 0; } if (((ULONG)ObpLUIDDeviceMapsEnabled) != 0) {
//
// If we're impersonating, get the DeviceMap information
// from the token.
//
if (ActiveImpersonationInfo) { ImpersonationInfo_Token = ReadField(Token);
// get the LUID from the token
ULONG64 AuthenticationId = 0; GetFieldValue(ImpersonationInfo_Token, "nt!_TOKEN", "AuthenticationId", AuthenticationId);
// find the devmap directory object
UCHAR Path[64]; ULONG64 DeviceMapDirectory = 0; sprintf((PCHAR)Path, "\\Sessions\\0\\DosDevices\\%08x-%08x", (ULONG)((AuthenticationId >> 32) & 0xffffffff), (ULONG)(AuthenticationId & 0xffffffff) ); DeviceMapDirectory = FindObjectByName(Path, 0);
if(DeviceMapDirectory != 0) {
// get the device map itself
ULONG64 DeviceMap = 0; GetFieldValue(DeviceMapDirectory, "nt!_OBJECT_DIRECTORY", "DeviceMap", DeviceMap);
if(DeviceMap != 0) { dprintf("%sDeviceMap %p\n", Pad, DeviceMap); } }
//
// Else, we're not impersonating, so just return the
// DeviceMap from our parent process.
//
} else if (Tcb_ApcState_Process != 0) { // get the devicemap from the process
ULONG64 DeviceMap = 0; GetFieldValue(Tcb_ApcState_Process, "nt!_EPROCESS", "DeviceMap", DeviceMap);
if (DeviceMap != 0) { dprintf("%sDeviceMap %p\n", Pad, DeviceMap); } } }
// Process = CONTAINING_RECORD(Tcb_ApcState_Process,EPROCESS,Pcb);
// Pcb is the 1st element
Process = Tcb_ApcState_Process; dprintf("%sOwning Process %lp\n", Pad, Process);
GetTheSystemTime (&RunTime);
dprintf("%sWaitTime (ticks) %ld\n", Pad, Tcb_WaitTime);
dprintf("%sContext Switch Count %ld", Pad, Tcb_ContextSwitches);
if (!Tcb_EnableStackSwap) { dprintf(" NoStackSwap"); } else { dprintf(" "); }
if (Tcb_LargeStack) { dprintf(" LargeStack"); }
dprintf ("\n");
//
// Get the time increment value which is used to compute runtime.
//
TimeIncrement = GetNtDebuggerDataValue( KeTimeIncrement );
RunTime.QuadPart = UInt32x32To64(Tcb_UserTime, TimeIncrement); RtlTimeToElapsedTimeFields ( &RunTime, &Times); dprintf("%sUserTime %3ld:%02ld:%02ld.%04ld\n", Pad, Times.Hour, Times.Minute, Times.Second, Times.Milliseconds);
RunTime.QuadPart = UInt32x32To64(Tcb_KernelTime, TimeIncrement); RtlTimeToElapsedTimeFields ( &RunTime, &Times); dprintf("%sKernelTime %3ld:%02ld:%02ld.%04ld\n", Pad, Times.Hour, Times.Minute, Times.Second, Times.Milliseconds);
if (PerformanceCountHigh != 0) { dprintf("%sPerfCounterHigh 0x%lx %08lx\n", Pad, PerformanceCountHigh, PerformanceCountHigh); } else if (PerformanceCountLow != 0) { dprintf("%sPerfCounter %lu\n",Pad,PerformanceCountLow); }
dumpSymbolicAddress(StartAddress, Buffer, TRUE); dprintf("%sStart Address %s\n", Pad, Buffer );
if (Win32StartAddress) if (LpcReceivedMsgIdValid) { dprintf("%sLPC Server thread working on message Id %x\n", Pad, LpcReceivedMessageId ); } else { dumpSymbolicAddress(Win32StartAddress, Buffer, TRUE); dprintf("%sWin32 Start Address %s\n", Pad, Buffer ); } dprintf("%sStack Init %lp Current %lp%s%sBase %lp Limit %lp Call %lp\n", Pad, Tcb_InitialStack, Tcb_KernelStack, (LongAddrs ? "\n" : ""), (LongAddrs ? Pad : " " ), Tcb_StackBase, Tcb_StackLimit, Tcb_CallbackStack );
dprintf("%sPriority %I64ld BasePriority %I64ld PriorityDecrement %I64ld DecrementCount %I64ld\n", Pad, Tcb_Priority, Tcb_BasePriority, Tcb_PriorityDecrement, Tcb_DecrementCount );
if (!Tcb_KernelStackResident) { dprintf("Kernel stack not resident.\n", Pad); // dprintf("\n");
// return TRUE;
// Try getting the stack even in this case - this might still be paged in
}
if (// (Tcb_State == Running && Processor == Tcb_Proc) || // Set thread context for everything
Ioctl(IG_SET_THREAD, &RealThreadBase, sizeof(ULONG64))) { g_ExtControl->GetStackTrace(0, 0, 0, stk, MAX_STACK_FRAMES, &frames );
if (frames) { ULONG OutFlags;
OutFlags = (DEBUG_STACK_COLUMN_NAMES | DEBUG_STACK_FUNCTION_INFO | DEBUG_STACK_FRAME_ADDRESSES | DEBUG_STACK_SOURCE_LINE);
if (!(Flags & 0x8)) { OutFlags |= DEBUG_STACK_ARGUMENTS; }
// if (Flags & 0x10)
// {
// OutFlags |= DEBUG_STACK_FRAME_ADDRESSES_RA_ONLY;
// }
g_ExtClient->SetOutputLinePrefix(Pad); g_ExtControl->OutputStackTrace(0, stk, frames, OutFlags); g_ExtClient->SetOutputLinePrefix(NULL); } } dprintf("\n"); return TRUE; }
/**
Routine to get address of the record containing a field on a debugee machine. Returns size of the type on success. ULONG GetContainingRecord ( IN OUT PULONG64 pAddr, IN LPSTR Type, IN LPSTR Field ) { ULONG64 off; ULONG sz;
sz = GetFieldOffset(Type, Field, &off); *pAddr -= off; return sz; } **/
typedef struct THREAD_LIST_DUMP { ULONG dwProcessor; LPSTR pad; ULONG Flags; } THREAD_LIST_DUMP;
ULONG ThreadListCallback ( PFIELD_INFO NextThrd, PVOID Context ) { THREAD_LIST_DUMP *Thread = (THREAD_LIST_DUMP *) Context;
return (!DumpThread(Thread->dwProcessor, Thread->pad, NextThrd->address, Thread->Flags)); }
typedef struct PROCESS_DUMP_CONTEXT { ULONG dwProcessor; PCHAR Pad; ULONG Flag; PCHAR ImageFileName; BOOL DumpCid; ULONG64 Cid; ULONG SessionId; } PROCESS_DUMP_CONTEXT;
ULONG ProcessListCallback( PFIELD_INFO listElement, PVOID Context ) { PROCESS_DUMP_CONTEXT *ProcDumpInfo = (PROCESS_DUMP_CONTEXT *) Context; // address field contains the address of this process
ULONG64 ProcAddress=listElement->address;
//
// Dump the process for which this routine is called
//
if (ProcDumpInfo->DumpCid) { ULONG64 UniqId;
GetFieldValue(ProcAddress, "nt!_EPROCESS", "UniqueProcessId", UniqId);
if (UniqId != ProcDumpInfo->Cid) { return FALSE; } }
// Limit dump to a single session if so requested.
if (ProcDumpInfo->SessionId != -1) { ULONG SessionId; if (!GetProcessSessionId(ProcAddress, &SessionId) || SessionId != ProcDumpInfo->SessionId) { return FALSE; } } if (DumpProcess(ProcDumpInfo->Pad, listElement->address, ProcDumpInfo->Flag, ProcDumpInfo->ImageFileName)) { ULONG64 ProcFlink=0; if (ProcDumpInfo->Flag & 6) { //
// Dump the threads
//
ULONG64 ThreadListHead_Flink=0; THREAD_LIST_DUMP Context = {ProcDumpInfo->dwProcessor, " ", ProcDumpInfo->Flag};
GetFieldValue(ProcAddress, "nt!_EPROCESS", "Pcb.ThreadListHead.Flink", ThreadListHead_Flink);
// dprintf("Listing threads, threadlist.flnik %p\n", ThreadListHead_Flink);
// dprintf("Dumping threads from %I64x to %I64x + %x.\n", Next, RealProcessBase , ThreadListHeadOffset);
ListType("nt!_ETHREAD", ThreadListHead_Flink, 1, "Tcb.ThreadListEntry.Flink", (PVOID) &Context, &ThreadListCallback);
if (CheckControlC()) { return TRUE; }
} if (CheckControlC()) { return TRUE; }
GetFieldValue(ProcAddress, "nt!_EPROCESS", "ActiveProcessLinks.Flink", ProcFlink); // dprintf("Next proc flink %p, this addr %p\n", ProcFlink, listElement->address);
return FALSE; } return TRUE; }
DECLARE_API( process )
/*++
Routine Description:
Dumps the active process list.
Arguments:
None.
Return Value:
None.
--*/
{ ULONG64 ProcessToDump; ULONG Flags = -1; ULONG64 Next; ULONG64 ProcessHead; ULONG64 Process; ULONG64 UserProbeAddress; PCHAR ImageFileName; CHAR Buf[256]; ULONG64 ActiveProcessLinksOffset=0; ULONG64 UniqueProcessId=0; PROCESS_DUMP_CONTEXT Proc={0, "", Flags, NULL, 0, 0}; ULONG dwProcessor=0; HANDLE hCurrentThread=NULL; ULONG64 Expr;
INIT_API();
if (!GetCurrentProcessor(Client, &dwProcessor, &hCurrentThread)) { dwProcessor = 0; hCurrentThread = 0; }
Proc.dwProcessor = dwProcessor; ProcessToDump = (ULONG64) -1;
Proc.SessionId = -1; for (;;) { while (*args == ' ' || *args == '\t') { args++; } if (*args == '/') {
switch(*(++args)) { case 's': args++; if (!GetExpressionEx(args, &Expr, &args)) { dprintf("Invalid argument to /s\n"); } else { Proc.SessionId = (ULONG)Expr; } break; default: dprintf("Unknown option '%c'\n", *args); args++; break; } } else { break; } } RtlZeroMemory(Buf, 256);
if (GetExpressionEx(args,&ProcessToDump, &args)) { if (sscanf(args, "%lx %s", &Flags, Buf) != 2) { Buf[0] = 0; } }
if (Buf[0] != '\0') { Proc.ImageFileName = Buf; ImageFileName = Buf; } else { ImageFileName = NULL; }
if (ProcessToDump == (ULONG64) -1) { GetCurrentProcessAddr( dwProcessor, 0, &ProcessToDump ); if (ProcessToDump == 0) { dprintf("Unable to get current process pointer.\n"); goto processExit; } if (Flags == -1) { Flags = 3; } }
if (!IsPtr64()) { ProcessToDump = (ULONG64) (LONG64) (LONG) ProcessToDump; } if ((ProcessToDump == 0) && (ImageFileName == NULL)) { dprintf("**** NT ACTIVE PROCESS DUMP ****\n"); if (Flags == -1) { Flags = 3; } }
UserProbeAddress = GetNtDebuggerDataValue(MmUserProbeAddress); if (!GetExpression("NT!PsActiveProcessHead")) { dprintf("NT symbols are incorrect, please fix symbols\n"); goto processExit; }
if (ProcessToDump < UserProbeAddress) { if (!GetProcessHead(&ProcessHead, &Next)) { goto processExit; } if (ProcessToDump != 0) { dprintf("Searching for Process with Cid == %I64lx\n", ProcessToDump); Proc.Cid = ProcessToDump; Proc.DumpCid = TRUE; } } else { Next = 0; ProcessHead = 1; } Proc.Flag = Flags;
if (Next != 0) { //
// Dump the process List
//
ListType("nt!_EPROCESS", Next, 1, "ActiveProcessLinks.Flink", &Proc, &ProcessListCallback); goto processExit; } else { Process = ProcessToDump; }
//dprintf("Next: %I64x, \tProcess: %I64x, \nActLnkOff: %I64x, \tProcHead: %I64x\n",
// Next, Process, procLink[0].address, ProcessHead);
if (GetFieldValue(Process, "nt!_EPROCESS", "UniqueProcessId", UniqueProcessId)) { dprintf("Error in reading nt!_EPROCESS at %p\n", Process); goto processExit; }
if (//ProcessToDump == 0 ||
ProcessToDump < UserProbeAddress && ProcessToDump == UniqueProcessId || ProcessToDump >= UserProbeAddress && ProcessToDump == Process ) { FIELD_INFO dummyForCallback = {(PUCHAR) "", NULL, 0, 0, Process, NULL}; ProcessListCallback(&dummyForCallback, &Proc);
goto processExit; } processExit:
EXIT_API(); return S_OK; }
typedef struct THREAD_FIND { ULONG64 StackPointer; ULONG64 Thread; } THREAD_FIND;
ULONG FindThreadCallback( PFIELD_INFO pAddrInfo, PVOID Context ) { ULONG64 stackBaseValue=0, stackLimitValue=0; ULONG64 thread = pAddrInfo->address; THREAD_FIND *pThreadInfo = (THREAD_FIND *) Context;
//
// We need two values from the thread structure: the kernel thread
// base and the kernel thread limit.
//
if (GetFieldValue(thread, "nt!_ETHREAD", "Tcb.StackBase", stackBaseValue)) { dprintf("Unable to get value of stack base of thread(0x%08p)\n", thread); return TRUE; }
if (pThreadInfo->StackPointer <= stackBaseValue) {
if (GetFieldValue(thread, "nt!_ETHREAD", "Tcb.StackLimit", stackLimitValue)) { dprintf("Unable to get value of stack limit\n"); return TRUE; } if (pThreadInfo->StackPointer > stackLimitValue) {
//
// We have found our thread.
//
pThreadInfo->Thread = thread; return TRUE; } }
//
// Look at the next thread
//
return FALSE; // Continue list
}
ULONG64 FindThreadFromStackPointerThisProcess( ULONG64 StackPointer, ULONG64 Process ) { LIST_ENTRY64 listValue={0}; THREAD_FIND ThreadFindContext;
ThreadFindContext.StackPointer = StackPointer; ThreadFindContext.Thread = 0; //
// Read the ThreadListHead within Process structure
//
GetFieldValue(Process, "nt!_EPROCESS", "ThreadListHead.Flink", listValue.Flink); GetFieldValue(Process, "nt!_EPROCESS", "ThreadListHead.Blink", listValue.Blink);
//
// Go through thread list, and try to find thread
//
ListType("nt!_ETHREAD", listValue.Flink, 1, "ThreadListEntry.Flink", (PVOID) &ThreadFindContext, &FindThreadCallback); return ThreadFindContext.Thread; }
ULONG64 FindThreadFromStackPointer( ULONG64 StackPointer ) { ULONG64 processHead; ULONG64 list; LIST_ENTRY64 listValue={0}; ULONG64 next; ULONG64 process=0; ULONG64 thread; ULONG ActiveProcessLinksOffset=0;
//
// First check the idle process, which is not included in the PS
// process list.
//
process = GetExpression( "NT!KeIdleProcess" ); if (process != 0) {
if (ReadPointer( process, &process)) {
thread = FindThreadFromStackPointerThisProcess( StackPointer, process );
if (thread != 0) { return thread; } } }
//
// Now check the PS process list.
//
list = GetNtDebuggerData( PsActiveProcessHead ); if (list == 0) { dprintf("Unable to get address of PsActiveProcessHead\n"); return 0; }
if (!ReadPointer( list, &listValue.Flink)) { dprintf("Unable to read @ %p\n", list); return 0; }
next = listValue.Flink; processHead = list;
//
// Get Offset of ProcessLinks
//
GetFieldOffset("nt!_EPROCESS", "ActiveProcessLinks", &ActiveProcessLinksOffset);
while (next != processHead) {
if (CheckControlC()) { return 0; } //
// Derive a pointer to the process structure
//
process = next - ActiveProcessLinksOffset;
thread = FindThreadFromStackPointerThisProcess( StackPointer, process ); if (thread != 0) {
//
// We have found the thread who's stack range includes
// StackPointer.
//
return thread; }
//
// Get a pointer to the next process
//
if (!ReadPointer( next, &listValue.Flink) ) { dprintf("Unable to read value of ActiveProcessLinks\n"); return 0; } next = listValue.Flink; }
return 0; }
DECLARE_API( thread )
/*++
Routine Description:
Dumps the specified thread.
Arguments:
None.
Return Value:
None.
--*/
{ ULONG64 Address, Tcb_Header_Type=0; ULONG64 Flags; ULONG64 Thread; ULONG64 UserProbeAddress; ULONG dwProcessor; HANDLE hCurrentThread; CHAR Token[100]; INIT_API();
if (!GetCurrentProcessor(Client, &dwProcessor, &hCurrentThread)) { dwProcessor = 0; hCurrentThread = 0; }
if (!GetExpressionEx(args, &Address, &args)) { Address = (ULONG64)-1; }
if (!GetExpressionEx(args, &Flags, &args)) { Flags = 6; }
if (Address == (ULONG64)-1) { GetCurrentThreadAddr( dwProcessor, &Address ); }
UserProbeAddress = GetNtDebuggerDataValue(MmUserProbeAddress);
Thread = Address;
if (GetFieldValue(Address, "nt!_ETHREAD", "Tcb.Header.Type", Tcb_Header_Type)) { dprintf("%08lp: Unable to get thread contents\n", Thread ); goto threadExit; }
if (Tcb_Header_Type != ThreadObject && Address > UserProbeAddress) {
ULONG64 stackThread;
//
// What was passed in was not a thread. Maybe it was a kernel stack
// pointer. Search the thread stack ranges to find out.
//
dprintf("%p is not a thread object, interpreting as stack value...\n",Address); stackThread = FindThreadFromStackPointer( Address ); if (stackThread != 0) { Thread = stackThread; } }
DumpThread (dwProcessor,"", Thread, Flags); EXPRLastDump = Thread; ThreadLastDump = Thread;
threadExit:
EXIT_API(); return S_OK;
}
DECLARE_API( processfields )
/*++
Routine Description:
Displays the field offsets for EPROCESS type.
Arguments:
None.
Return Value:
None.
--*/
{
dprintf(" EPROCESS structure offsets: (use 'dt nt!_EPROCESS')\n\n"); return S_OK; }
DECLARE_API( threadfields )
/*++
Routine Description:
Displays the field offsets for ETHREAD type.
Arguments:
None.
Return Value:
None.
--*/
{
dprintf(" ETHREAD structure offsets: (use 'dt ETHREAD')\n\n"); return S_OK;
}
//+---------------------------------------------------------------------------
//
// Function: GetHandleTableAddress
//
// Synopsis: Return the address of the handle table given a thread handle
//
// Arguments: [Processor] -- processor number
// [hCurrentThread] -- thread handle
//
// Returns: address of handle table or null
//
// History: 9-23-1998 benl Created
//
// Notes:
//
//----------------------------------------------------------------------------
ULONG64 GetHandleTableAddress( USHORT Processor, HANDLE hCurrentThread ) { ULONG64 pThread; ULONG64 pProcess = 0, pObjTable;
GetCurrentThreadAddr( Processor, &pThread ); if (pThread) { GetCurrentProcessAddr( Processor, pThread, &pProcess ); }
if (pProcess) { if (GetFieldValue(pProcess, "nt!_EPROCESS", "ObjectTable", pObjTable) ) { dprintf("%08p: Unable to read _EPROCESS\n", pProcess ); return 0; }
return pObjTable; } else { return 0; } } // GetHandleTableAddress
#if 0
BOOLEAN FetchProcessStructureVariables( VOID ) { ULONG Result; ULONG64 t;
static BOOLEAN HavePspVariables = FALSE;
if (HavePspVariables) { return TRUE; }
t=GetNtDebuggerData( PspCidTable ); PspCidTable = (PHANDLE_TABLE) t; if ( !PspCidTable || !ReadMemory((DWORD)PspCidTable, &PspCidTable, sizeof(PspCidTable), &Result) ) { dprintf("%08lx: Unable to get value of PspCidTable\n",PspCidTable); return FALSE; }
HavePspVariables = TRUE; return TRUE; }
PVOID LookupUniqueId( HANDLE UniqueId ) { return NULL; }
#endif
int __cdecl CmpFunc( const void *pszElem1, const void *pszElem2 ) { PPROCESS_COMMIT_USAGE p1, p2;
p1 = (PPROCESS_COMMIT_USAGE)pszElem1; p2 = (PPROCESS_COMMIT_USAGE)pszElem2; if (p2->CommitCharge == p1->CommitCharge) { ((char*)p2->ClientId - (char*)p1->ClientId); } return (ULONG) (p2->CommitCharge - p1->CommitCharge); }
PPROCESS_COMMIT_USAGE GetProcessCommit ( PULONG64 TotalCommitCharge, PULONG NumberOfProcesses ) { PPROCESS_COMMIT_USAGE p; ULONG n; ULONG64 Next; ULONG64 ProcessHead; ULONG64 Process; ULONG64 Total; ULONG Result; ULONG ActiveProcessLinksOffset;
*TotalCommitCharge = 0; *NumberOfProcesses = 0;
// Get the offset of ActiveProcessLinks in _EPROCESS
if (GetFieldOffset("nt!_EPROCESS", "ActiveProcessLinks", &ActiveProcessLinksOffset)) { return 0; } Total = 0;
n = 0; p = (PPROCESS_COMMIT_USAGE) HeapAlloc( GetProcessHeap(), 0, 1 );
ProcessHead = GetNtDebuggerData( PsActiveProcessHead ); if (!ProcessHead) { dprintf("Unable to get value of PsActiveProcessHead\n"); return 0; }
if (GetFieldValue( ProcessHead, "nt!_LIST_ENTRY", "Flink", Next )) { dprintf("Unable to read _LIST_ENTRY @ %p\n", ProcessHead); return 0; }
while(Next != ProcessHead) { ULONG64 CommitCharge=0, NumberOfPrivatePages=0, NumberOfLockedPages=0; Process = Next - ActiveProcessLinksOffset;
if (GetFieldValue( Process, "nt!_EPROCESS", "CommitCharge", CommitCharge )) { dprintf("Unable to read _EPROCESS at %p\n",Process); return 0; }
Total += CommitCharge;
n += 1; p = (PPROCESS_COMMIT_USAGE) HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, p, n * sizeof( *p ) ); if (p != NULL) { p[n-1].ProcessAddress = Process; GetFieldValue( Process, "nt!_EPROCESS", "ImageFileName", p[ n-1 ].ImageFileName ); GetFieldValue( Process, "nt!_EPROCESS", "NumberOfPrivatePages", p[ n-1 ].NumberOfPrivatePages ); GetFieldValue( Process, "nt!_EPROCESS", "NumberOfLockedPages", p[ n-1 ].NumberOfLockedPages ); GetFieldValue( Process, "nt!_EPROCESS", "UniqueProcessId", p[ n-1 ].ClientId ); p[ n-1 ].CommitCharge = CommitCharge; }
GetFieldValue(Process, "nt!_EPROCESS", "ActiveProcessLinks.Flink", Next);
if (CheckControlC()) { return 0; } }
qsort( p, n, sizeof( *p ), CmpFunc );
*TotalCommitCharge = Total; *NumberOfProcesses = n; return p; }
BOOL DumpJob( ULONG64 RealJobBase, ULONG Flags ) { ULONG64 ProcessListHead_Flink=0, TotalPageFaultCount=0, TotalProcesses=0, ActiveProcesses=0, TotalTerminatedProcesses=0, LimitFlags=0, MinimumWorkingSetSize=0, MaximumWorkingSetSize=0, ActiveProcessLimit=0, PriorityClass=0, UIRestrictionsClass=0, SecurityLimitFlags=0, Token=0, Filter=0; ULONG64 Filter_SidCount=0, Filter_Sids=0, Filter_SidsLength=0, Filter_GroupCount=0, Filter_Groups=0, Filter_GroupsLength=0, Filter_PrivilegeCount=0, Filter_Privileges=0, Filter_PrivilegesLength=0; ULONG ProcessListHeadOffset;
GetFieldValue(RealJobBase, "nt!_EJOB", "ActiveProcesses", ActiveProcesses); GetFieldValue(RealJobBase, "nt!_EJOB", "ActiveProcessLimit", ActiveProcessLimit); GetFieldValue(RealJobBase, "nt!_EJOB", "Filter", Filter); GetFieldValue(RealJobBase, "nt!_EJOB", "LimitFlags", LimitFlags); GetFieldValue(RealJobBase, "nt!_EJOB", "MinimumWorkingSetSize", MinimumWorkingSetSize); GetFieldValue(RealJobBase, "nt!_EJOB", "MaximumWorkingSetSize", MaximumWorkingSetSize); GetFieldValue(RealJobBase, "nt!_EJOB", "PriorityClass", PriorityClass); GetFieldValue(RealJobBase, "nt!_EJOB", "ProcessListHead.Flink", ProcessListHead_Flink); GetFieldValue(RealJobBase, "nt!_EJOB", "SecurityLimitFlags", SecurityLimitFlags); GetFieldValue(RealJobBase, "nt!_EJOB", "Token", Token); GetFieldValue(RealJobBase, "nt!_EJOB", "TotalPageFaultCount", TotalPageFaultCount); GetFieldValue(RealJobBase, "nt!_EJOB", "TotalProcesses", TotalProcesses); GetFieldValue(RealJobBase, "nt!_EJOB", "TotalTerminatedProcesses", TotalTerminatedProcesses); GetFieldValue(RealJobBase, "nt!_EJOB", "UIRestrictionsClass", UIRestrictionsClass); if (GetFieldOffset("_EJOB", "ProcessListHead", &ProcessListHeadOffset)) { dprintf("Can't read job at %p\n", RealJobBase); } if ( Flags & 1 ) { dprintf("Job at %I64x\n", RealJobBase ); dprintf(" TotalPageFaultCount %x\n", TotalPageFaultCount ); dprintf(" TotalProcesses %x\n", TotalProcesses ); dprintf(" ActiveProcesses %x\n", ActiveProcesses ); dprintf(" TotalTerminatedProcesses %x\n", TotalTerminatedProcesses );
dprintf(" LimitFlags %x\n", LimitFlags ); dprintf(" MinimumWorkingSetSize %I64x\n", MinimumWorkingSetSize ); dprintf(" MaximumWorkingSetSize %I64x\n", MaximumWorkingSetSize ); dprintf(" ActiveProcessLimit %x\n", ActiveProcessLimit ); dprintf(" PriorityClass %x\n", PriorityClass );
dprintf(" UIRestrictionsClass %x\n", UIRestrictionsClass );
dprintf(" SecurityLimitFlags %x\n", SecurityLimitFlags ); dprintf(" Token %p\n", Token ); if ( Filter ) { GetFieldValue(Filter, "nt!_PS_JOB_TOKEN_FILTER", "CapturedSidCount", Filter_SidCount ); GetFieldValue(Filter, "nt!_PS_JOB_TOKEN_FILTER", "CapturedSids", Filter_Sids ); GetFieldValue(Filter, "nt!_PS_JOB_TOKEN_FILTER", "CapturedSidsLength", Filter_SidsLength); GetFieldValue(Filter, "nt!_PS_JOB_TOKEN_FILTER", "CapturedGroupCount", Filter_GroupCount); GetFieldValue(Filter, "nt!_PS_JOB_TOKEN_FILTER", "CapturedGroups", Filter_Groups); GetFieldValue(Filter, "nt!_PS_JOB_TOKEN_FILTER", "CapturedGroupsLength", Filter_GroupsLength); GetFieldValue(Filter, "nt!_PS_JOB_TOKEN_FILTER", "CapturedPrivilegeCount", Filter_PrivilegeCount); GetFieldValue(Filter, "nt!_PS_JOB_TOKEN_FILTER", "CapturedPrivileges", Filter_Privileges); GetFieldValue(Filter, "nt!_PS_JOB_TOKEN_FILTER", "CapturedPrivilegesLength",Filter_PrivilegesLength);
dprintf(" Filter\n"); dprintf(" CapturedSidCount %I64x\n", Filter_SidCount ); dprintf(" CapturedSids %p\n", Filter_Sids ); dprintf(" CapturedSidsLength %I64x\n", Filter_SidsLength ); dprintf(" CapturedGroupCount %I64x\n", Filter_GroupCount ); dprintf(" CapturedGroups %p\n", Filter_Groups ); dprintf(" CapturedGroupsLength %I64x\n", Filter_GroupsLength ); dprintf(" CapturedPrivCount %I64x\n", Filter_PrivilegeCount ); dprintf(" CapturedPrivs %p\n", Filter_Privileges ); dprintf(" CapturedPrivLength %I64x\n", Filter_PrivilegesLength ); }
}
if ( Flags & 2 ) { //
// Walk the process list for all the processes in the job
//
ULONG64 Scan, End; ULONG offset ; ULONG64 ProcessBase, NextPrc=0 ;
dprintf(" Processes assigned to this job:\n" );
Scan = ProcessListHead_Flink ; End = ProcessListHeadOffset + RealJobBase; if (GetFieldOffset("nt!_EPROCESS", "JobLinks", &offset)) { while ( Scan != End ) { ProcessBase = Scan - offset;
DumpProcess( " ", ProcessBase, 0, NULL);
if (!GetFieldValue(ProcessBase, "nt!_EPROCESS", "JobLinks.Flink", NextPrc)) { Scan = NextPrc; } else { Scan = End; } } } } return TRUE ; }
DECLARE_API( job )
/*++
Routine Description:
Dumps the specified thread.
Arguments:
None.
Return Value:
None.
--*/
{ ULONG64 Address, JobAddress=0; ULONG Flags; ULONG dwProcessor; HANDLE hCurrentThread; INIT_API();
if (!GetCurrentProcessor(Client, &dwProcessor, &hCurrentThread)) { dwProcessor = 0; hCurrentThread = 0; }
Address = 0; Flags = 1; if (GetExpressionEx(args,&Address,&args)) { Flags = (ULONG) GetExpression(args); if (!Flags) { Flags = 1; } }
if (Address == 0) {
GetCurrentProcessAddr( dwProcessor, 0, &Address ); if (Address == 0) { dprintf("Unable to get current process pointer.\n"); goto jobExit; }
if (GetFieldValue(Address, "nt!_EPROCESS", "Job", JobAddress)) { dprintf("%08p: Unable to get process contents\n", Address ); goto jobExit; } Address = JobAddress; if ( Address == 0 ) { dprintf("Process not part of a job.\n" ); goto jobExit; } }
DumpJob( Address, Flags );
jobExit:
EXIT_API(); return S_OK;
}
ULONG64 ZombieCount; ULONG64 ZombiePool; ULONG64 ZombieCommit; ULONG64 ZombieResidentAvailable;
#define BLOB_LONGS 32
BOOLEAN WINAPI CheckForZombieProcess( IN PCHAR Tag, IN PCHAR Filter, IN ULONG Flags, IN ULONG64 PoolHeader, IN ULONG BlockSize, IN ULONG64 Data, IN PVOID Context ) { ULONG result; // EPROCESS ProcessContents;
ULONG64 Process; ULONG64 KProcess; //OBJECT_HEADER ObjectHeaderContents;
ULONG64 ObjectHeader; ULONG64 Blob[BLOB_LONGS]; ULONG i; ULONG PoolIndex, PoolBlockSize, SizeOfKprocess; ULONG HandleCount, PointerCount; ULONG64 UniqueProcessId;
UNREFERENCED_PARAMETER (Flags); UNREFERENCED_PARAMETER (BlockSize); UNREFERENCED_PARAMETER (Context);
if (PoolHeader == 0) { return FALSE; }
if (GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "PoolIndex", PoolIndex) || GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "BlockSize", PoolBlockSize)) { dprintf("Cannot read nt!_POOL_HEADER at %p.\n", PoolHeader); return FALSE; } if ((PoolIndex & 0x80) == 0) { return FALSE; }
if (!CheckSingleFilter (Tag, Filter)) { return FALSE; }
if ((PoolBlockSize << POOL_BLOCK_SHIFT) < sizeof(Blob)) { return FALSE; }
//
// There must be a better way to find the object header given the start
// of a pool block ?
//
if (!ReadMemory (Data, &Blob[0], sizeof(Blob), &result)) { dprintf ("Could not read process blob at %p\n", Data); return FALSE; } SizeOfKprocess = GetTypeSize("nt!_KPROCESS"); for (i = 0; i < BLOB_LONGS; i += 1) { ULONG Type, Size;
GetFieldValue(Data + i*sizeof(ULONG), "nt!_KPROCESS", "Header.Type", Type); GetFieldValue(Data + i*sizeof(ULONG), "nt!_KPROCESS", "Header.Size", Size); if ((Type == ProcessObject) && (Size == SizeOfKprocess / sizeof(LONG))) {
break; } }
if (i == BLOB_LONGS) { return FALSE; }
ObjectHeader = KD_OBJECT_TO_OBJECT_HEADER (Data + i*sizeof(LONG)); Process = Data + i*sizeof(LONG);
if (GetFieldValue(ObjectHeader, "nt!_OBJECT_HEADER", "HandleCount",HandleCount) || GetFieldValue(ObjectHeader, "nt!_OBJECT_HEADER", "PointerCount",PointerCount) ) { dprintf ("Could not read process object header at %p\n", ObjectHeader); return FALSE; }
if (GetFieldValue( Process, "nt!_EPROCESS", "UniqueProcessId", UniqueProcessId)) {
dprintf ("Could not read process data at %p\n", Process); return FALSE; }
//
// Skip the system process and the idle process.
//
if ((UniqueProcessId == 0) || (UniqueProcessId == 8)) { return FALSE; }
//
// Display any terminated process regardless of object pointer/handle
// counts. This is so leaked process handles don't result in processes
// not getting displayed when they should.
//
// A nulled object table with a non-zero create time indicates a process
// that has finished creation.
//
InitTypeRead(Process, nt!_EPROCESS); if ((ReadField(ObjectTable) == 0) && (ReadField(CreateTime.QuadPart) != 0)) {
dprintf ("HandleCount: %u PointerCount: %u\n", HandleCount, PointerCount); DumpProcess ("", Process, 0, NULL);
ZombieCount += 1; ZombiePool += (PoolBlockSize << POOL_BLOCK_SHIFT); ZombieCommit += (7 * PageSize); // MM_PROCESS_COMMIT_CHARGE
ZombieResidentAvailable += (9 * PageSize); // MM_PROCESS_CREATE_CHARGE
}
return TRUE; }
BOOLEAN WINAPI CheckForZombieThread( IN PCHAR Tag, IN PCHAR Filter, IN ULONG Flags, IN ULONG64 PoolHeader, IN ULONG BlockSize, IN ULONG64 Data, IN PVOID Context ) { ULONG result; ULONG64 Thread; ULONG64 KThread; ULONG64 ObjectHeader; ULONG Blob[BLOB_LONGS]; ULONG i; ULONG64 StackBase; ULONG64 StackLimit; ULONG PoolIndex, PoolBlockSize, SizeOfKthread; ULONG HandleCount, PointerCount;
UNREFERENCED_PARAMETER (Flags); UNREFERENCED_PARAMETER (BlockSize); UNREFERENCED_PARAMETER (Context);
if (PoolHeader == 0) { return FALSE; }
if (GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "PoolIndex", PoolIndex) || GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "BlockSize", PoolBlockSize)) { dprintf("Cannot read POOL_HEADER at %p.\n", PoolHeader); return FALSE; } if ((PoolIndex & 0x80) == 0) { return FALSE; }
if (!CheckSingleFilter (Tag, Filter)) { return FALSE; }
if ((PoolBlockSize << POOL_BLOCK_SHIFT) < sizeof(Blob)) { return FALSE; }
//
// There must be a better way to find the object header given the start
// of a pool block ?
//
if (!ReadMemory ((ULONG) Data, &Blob[0], sizeof(Blob), &result)) { dprintf ("Could not read process blob at %p\n", Data); return FALSE; } SizeOfKthread = GetTypeSize("nt!_KTHREAD"); for (i = 0; i < BLOB_LONGS; i += 1) { ULONG Type, Size;
GetFieldValue(Data + i*sizeof(ULONG), "nt!_KTHREAD", "Header.Type", Type); GetFieldValue(Data + i*sizeof(ULONG), "nt!_KTHREAD", "Header.Size", Size); if ((Type == ThreadObject) && (Size == SizeOfKthread / sizeof(LONG))) {
break; } }
if (i == BLOB_LONGS) { return FALSE; }
ObjectHeader = KD_OBJECT_TO_OBJECT_HEADER (Data + i*sizeof(LONG)); Thread = Data + i*sizeof(LONG);
if (GetFieldValue(ObjectHeader, "nt!_OBJECT_HEADER", "HandleCount",HandleCount) || GetFieldValue(ObjectHeader, "nt!_OBJECT_HEADER", "PointerCount",PointerCount) ) { dprintf ("Could not read process object header at %p\n", ObjectHeader); return FALSE; }
if (GetFieldValue( Thread, "nt!_ETHREAD", "Tcb.StackLimit", StackLimit)) {
dprintf ("Could not read thread data at %p\n", Thread); return FALSE; }
InitTypeRead(Thread, KTHREAD);
if ((ULONG) ReadField(State) != Terminated) { return FALSE; } ZombieCount += 1;
ZombiePool += (PoolBlockSize << POOL_BLOCK_SHIFT); ZombieCommit += (ReadField(StackBase) - StackLimit);
StackBase = (ReadField(StackBase) - 1);
dprintf ("HandleCount: %u PointerCount: %u\n", HandleCount, PointerCount); DumpThread (0, "", Thread, 7);
while (StackBase >= StackLimit) { if (GetAddressState(StackBase) == ADDRESS_VALID) { ZombieResidentAvailable += PageSize; } StackBase = (StackBase - PageSize); }
return TRUE; }
DECLARE_API( zombies )
/*++
Routine Description:
Finds zombie processes and threads in non-paged pool.
Arguments:
None.
Return Value:
None.
--*/
{ ULONG Flags; ULONG64 RestartAddress; ULONG TagName; ULONG64 ZombieProcessCount; ULONG64 ZombieProcessPool; ULONG64 ZombieProcessCommit; ULONG64 ZombieProcessResidentAvailable; ULONG64 tmp; Flags = 1; RestartAddress = 0;
if (GetExpressionEx(args,&tmp, &args)) { RestartAddress = GetExpression(args); Flags = (ULONG) tmp; }
if ((Flags & 0x3) == 0) { dprintf("Invalid parameter for !zombies\n"); return E_INVALIDARG; }
if (Flags & 0x1) {
dprintf("Looking for zombie processes...");
TagName = '?orP';
ZombieCount = 0; ZombiePool = 0; ZombieCommit = 0; ZombieResidentAvailable = 0;
SearchPool (TagName, 0, RestartAddress, &CheckForZombieProcess, NULL); SearchPool (TagName, 2, RestartAddress, &CheckForZombieProcess, NULL);
ZombieProcessCount = ZombieCount; ZombieProcessPool = ZombiePool; ZombieProcessCommit = ZombieCommit; ZombieProcessResidentAvailable = ZombieResidentAvailable; }
if (Flags & 0x2) {
dprintf("Looking for zombie threads...");
TagName = '?rhT'; ZombieCount = 0; ZombiePool = 0; ZombieCommit = 0; ZombieResidentAvailable = 0;
SearchPool (TagName, 0, RestartAddress, &CheckForZombieThread, NULL); SearchPool (TagName, 2, RestartAddress, &CheckForZombieThread, NULL);
}
//
// Print summary statistics last so they don't get lost on screen scroll.
//
if (Flags & 0x1) { if (ZombieProcessCount == 0) { dprintf ("\n\n************ NO zombie processes found ***********\n"); } else { dprintf ("\n\n************ %d zombie processes found ***********\n", ZombieProcessCount); dprintf (" Resident page cost : %8ld Kb\n", ZombieProcessResidentAvailable / 1024); dprintf (" Commit cost : %8ld Kb\n", ZombieProcessCommit / 1024); dprintf (" Pool cost : %8ld bytes\n", ZombieProcessPool); } dprintf ("\n"); }
if (Flags & 0x2) { if (ZombieCount == 0) { dprintf ("\n\n************ NO zombie threads found ***********\n"); } else { dprintf ("\n\n************ %d zombie threads found ***********\n", ZombieCount); dprintf (" Resident page cost : %8ld Kb\n", ZombieResidentAvailable / 1024); dprintf (" Commit cost : %8ld Kb\n", ZombieCommit / 1024); dprintf (" Pool cost : %8ld bytes\n", ZombiePool); } }
return S_OK; }
VOID DumpMmThreads ( VOID )
/*++
Routine Description:
Finds and dumps the interesting memory management threads.
Arguments:
None.
Return Value:
None.
--*/
{ ULONG i; ULONG64 ProcessToDump; ULONG Flags; ULONG64 Next; ULONG64 ProcessHead; ULONG64 Process; ULONG64 Thread; CHAR Buf[256]; STRING string1, string2; ULONG64 InterestingThreads[4]; ULONG ActvOffset, PcbThListOffset, TcbThListOffset;
ProcessToDump = (ULONG64) -1; Flags = 0xFFFFFFFF;
ProcessHead = GetNtDebuggerData( PsActiveProcessHead ); if (!ProcessHead) { dprintf("Unable to get value of PsActiveProcessHead\n"); return; }
if (GetFieldValue( ProcessHead, "nt!_LIST_ENTRY", "Flink", Next )) { dprintf("Unable to read nt!_LIST_ENTRY @ %p\n", ProcessHead); return; }
if (Next == 0) { dprintf("PsActiveProcessHead is NULL!\n"); return; } InterestingThreads[0] = GetExpression ("nt!MiModifiedPageWriter"); InterestingThreads[1] = GetExpression ("nt!MiMappedPageWriter"); InterestingThreads[2] = GetExpression ("nt!MiDereferenceSegmentThread"); InterestingThreads[3] = GetExpression ("nt!KeBalanceSetManager");
RtlInitString(&string1, "System"); GetFieldOffset("nt!_EPROCESS", "ActiveProcessLinks", &ActvOffset); GetFieldOffset("nt!_EPROCESS", "Pcb.ThreadListHead", &PcbThListOffset); GetFieldOffset("nt!_KTHREAD", "ThreadListEntry", &TcbThListOffset);
while(Next != ProcessHead) {
Process = Next - ActvOffset;
if (GetFieldValue( Process, "nt!_EPROCESS", "ImageFileName", Buf )) { dprintf("Unable to read _EPROCESS at %p\n",Process); return; }
// strcpy((PCHAR)Buf,(PCHAR)ProcessContents.ImageFileName);
RtlInitString(&string2, (PCSZ) Buf);
if (RtlCompareString(&string1, &string2, TRUE) == 0) {
//
// Find the threads.
//
GetFieldValue( Process, "nt!_EPROCESS", "Pcb.ThreadListHead.Flink", Next);
while ( Next != Process + PcbThListOffset) { ULONG64 StartAddress;
Thread = Next - TcbThListOffset; if (GetFieldValue(Thread, "nt!_ETHREAD", "StartAddress", StartAddress)) { dprintf("Unable to read _ETHREAD at %p\n",Thread); break; }
for (i = 0; i < 4; i += 1) { if (StartAddress == InterestingThreads[i]) { DumpThread (0," ", Thread, 7); break; } }
GetFieldValue(Thread, "nt!_KTHREAD","ThreadListEntry.Flink", Next);
if (CheckControlC()) { return; } } dprintf("\n"); break; }
GetFieldValue( Process, "nt!_EPROCESS", "ActiveProcessLinks.Flink", Next);
if (CheckControlC()) { return; } } return; }
|