2972 lines
84 KiB
C++
2972 lines
84 KiB
C++
/*++
|
|
|
|
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;
|
|
}
|