windows-nt/Source/XPSP1/NT/base/tools/kdexts2/process.cpp

2972 lines
84 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
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;
}