44 _In_ HANDLE PortHandle,
46 _Out_writes_bytes_(Length) PVOID PortInformation,
48 _Out_opt_ PULONG ReturnLength
51 typedef struct _ANALYZE_WAIT_CONTEXT
73 _In_opt_ PVOID Context
85 _In_ HANDLE ProcessHandle,
90 _In_ HANDLE ProcessHandle,
92 _In_ ULONG NumberOfHandles,
93 _In_ PHANDLE AddressOfHandles,
95 _In_ BOOLEAN Alertable,
108 static USHORT NumberForWfso = -1;
109 static USHORT NumberForWfmo = -1;
110 static USHORT NumberForRf = -1;
115 _In_ HANDLE ThreadId,
122 HANDLE processHandle;
133 PhShowStatus(hWnd, L
"Unable to open the process", status, 0);
143 NtClose(processHandle);
152 PhShowStatus(hWnd, L
"Unable to open the thread", status, 0);
157 context.ThreadId = ThreadId;
159 context.ProcessHandle = SymbolProvider->ProcessHandle;
160 context.SymbolProvider = SymbolProvider;
168 SymbolProvider->ProcessHandle,
175 NtClose(threadHandle);
198 HANDLE processHandle;
208 PhShowStatus(hWnd, L
"Unable to open the thread", status, 0);
212 if (!
NT_SUCCESS(status = NtQueryInformationThread(
214 ThreadLastSystemCall,
220 NtClose(threadHandle);
227 NtClose(threadHandle);
228 PhShowStatus(hWnd, L
"Unable to open the process", status, 0);
279 NtClose(processHandle);
280 NtClose(threadHandle);
285 _In_opt_ PVOID Context
292 context->SymbolProvider,
293 (ULONG64)StackFrame->PcAddress,
303 context->Found =
TRUE;
305 #define FUNC_MATCH(Name) PhStartsWithString2(name, L##Name, TRUE)
306 #define NT_FUNC_MATCH(Name) ( \
307 PhStartsWithString2(name, L"ntdll.dll!Nt" L##Name, TRUE) || \
308 PhStartsWithString2(name, L"ntdll.dll!Zw" L##Name, TRUE) \
318 &context->StringBuilder,
319 L
"Thread is sleeping. Timeout: %u milliseconds.",
320 (ULONG)StackFrame->Params[0]
325 BOOLEAN alertable = !!StackFrame->Params[0];
326 PVOID timeoutAddress = StackFrame->Params[1];
327 LARGE_INTEGER timeout;
330 context->ProcessHandle,
333 sizeof(LARGE_INTEGER),
337 if (timeout.QuadPart < 0)
340 &context->StringBuilder,
341 L
"Thread is sleeping. Timeout: %I64u milliseconds.",
353 &context->StringBuilder,
354 L
"Thread is sleeping."
360 HANDLE handle = (HANDLE)StackFrame->Params[0];
363 &context->StringBuilder,
364 L
"Thread is waiting for an I/O control request:\r\n"
367 &context->StringBuilder,
373 HANDLE handle = StackFrame->Params[0];
376 &context->StringBuilder,
377 L
"Thread is waiting for a FS control request:\r\n"
380 &context->StringBuilder,
386 HANDLE handle = StackFrame->Params[0];
389 if ((ULONG_PTR)handle % 4 != 0 || !handle)
390 handle = context->PrevParams[1];
393 &context->StringBuilder,
394 L
"Thread is querying an object:\r\n"
397 &context->StringBuilder,
403 HANDLE handle = StackFrame->Params[0];
406 &context->StringBuilder,
407 L
"Thread is waiting for file I/O:\r\n"
410 &context->StringBuilder,
416 HANDLE handle = StackFrame->Params[0];
419 &context->StringBuilder,
420 L
"Thread is waiting for an I/O completion port:\r\n"
423 &context->StringBuilder,
433 HANDLE handle = StackFrame->Params[0];
437 &context->StringBuilder,
441 &context->StringBuilder,
448 &context->StringBuilder,
452 &context->StringBuilder,
464 HANDLE handle = StackFrame->Params[0];
466 if ((ULONG_PTR)handle % 4 != 0 || !handle)
467 handle = context->PrevParams[1];
470 &context->StringBuilder,
471 L
"Thread is waiting (%s) for an event pair:\r\n",
475 &context->StringBuilder,
485 &context->StringBuilder,
486 L
"Thread is waiting for a USER message.\r\n"
489 else if (
FUNC_MATCH(
"user32.dll!NtUserMessageCall"))
494 &context->StringBuilder,
495 L
"Thread is sending a USER message:\r\n"
512 HANDLE handle = StackFrame->Params[0];
515 &context->StringBuilder,
516 L
"Thread is waiting for a debug event:\r\n"
519 &context->StringBuilder,
528 HANDLE handle = StackFrame->Params[0];
529 PVOID key = StackFrame->Params[1];
532 &context->StringBuilder,
533 L
"Thread is waiting (%s) for a keyed event (key 0x%Ix):\r\n",
538 &context->StringBuilder,
544 FUNC_MATCH(
"kernel32.dll!WaitForMultipleObjects")
547 ULONG numberOfHandles = (ULONG)StackFrame->Params[0];
548 PVOID addressOfHandles = StackFrame->Params[1];
550 BOOLEAN alertable = !!StackFrame->Params[3];
552 if (numberOfHandles > MAXIMUM_WAIT_OBJECTS)
554 numberOfHandles = (ULONG)context->PrevParams[1];
555 addressOfHandles = context->PrevParams[2];
556 waitType = (
WAIT_TYPE)context->PrevParams[3];
561 context->ProcessHandle,
567 &context->StringBuilder
572 FUNC_MATCH(
"kernel32.dll!WaitForSingleObject")
575 HANDLE handle = StackFrame->Params[0];
576 BOOLEAN alertable = !!StackFrame->Params[1];
578 if ((ULONG_PTR)handle % 4 != 0 || !handle)
580 handle = context->PrevParams[1];
581 alertable = !!context->PrevParams[2];
585 &context->StringBuilder,
586 L
"Thread is waiting (%s) for:\r\n",
587 alertable ? L
"alertable" : L
"non-alertable"
590 &context->StringBuilder,
596 HANDLE handle = StackFrame->Params[0];
599 &context->StringBuilder,
600 L
"Thread is waiting for work from a worker factory:\r\n"
603 &context->StringBuilder,
609 context->Found =
FALSE;
613 memcpy(&context->PrevParams, StackFrame->Params,
sizeof(StackFrame->Params));
615 return !context->Found;
629 &Context->StringBuilder,
630 L
"Thread is sending a USER message:\r\n"
635 Context->Found =
TRUE;
642 &Context->StringBuilder,
643 L
"Thread is waiting for an ALPC port:\r\n"
648 Context->Found =
TRUE;
652 static BOOLEAN PhpWaitUntilThreadIsWaiting(
653 _In_ HANDLE ThreadHandle
657 BOOLEAN isWaiting =
FALSE;
663 for (attempts = 0; attempts < 5; attempts++)
668 LARGE_INTEGER interval;
671 NtDelayExecution(
FALSE, &interval);
701 NtDelayExecution(
FALSE, &interval);
707 static VOID PhpGetThreadLastSystemCallNumber(
708 _In_ HANDLE ThreadHandle,
709 _Out_ PUSHORT LastSystemCallNumber
716 ThreadLastSystemCall,
726 static NTSTATUS PhpWfsoThreadStart(
731 LARGE_INTEGER timeout;
733 eventHandle = Parameter;
736 NtWaitForSingleObject(eventHandle,
FALSE, &timeout);
738 return STATUS_SUCCESS;
741 static NTSTATUS PhpWfmoThreadStart(
746 LARGE_INTEGER timeout;
748 eventHandle = Parameter;
751 NtWaitForMultipleObjects(1, &eventHandle,
WaitAll,
FALSE, &timeout);
753 return STATUS_SUCCESS;
756 static NTSTATUS PhpRfThreadStart(
764 fileHandle = Parameter;
766 NtReadFile(fileHandle, NULL, NULL, NULL, &isb, &data,
sizeof(ULONG), NULL, NULL);
768 return STATUS_SUCCESS;
780 HANDLE pipeReadHandle;
781 HANDLE pipeWriteHandle;
794 if (threadHandle =
PhCreateThread(0, PhpWfsoThreadStart, eventHandle))
796 if (PhpWaitUntilThreadIsWaiting(threadHandle))
798 PhpGetThreadLastSystemCallNumber(threadHandle, &NumberForWfso);
802 NtSetEvent(eventHandle, NULL);
803 NtClose(threadHandle);
806 NtClose(eventHandle);
815 if (threadHandle =
PhCreateThread(0, PhpWfmoThreadStart, eventHandle))
817 if (PhpWaitUntilThreadIsWaiting(threadHandle))
819 PhpGetThreadLastSystemCallNumber(threadHandle, &NumberForWfmo);
822 NtSetEvent(eventHandle, NULL);
823 NtClose(threadHandle);
826 NtClose(eventHandle);
831 if (CreatePipe(&pipeReadHandle, &pipeWriteHandle, NULL, 0))
833 if (threadHandle =
PhCreateThread(0, PhpRfThreadStart, pipeReadHandle))
838 if (PhpWaitUntilThreadIsWaiting(threadHandle))
840 PhpGetThreadLastSystemCallNumber(threadHandle, &NumberForRf);
843 NtWriteFile(pipeWriteHandle, NULL, NULL, NULL, &isb, &data,
sizeof(data), NULL, NULL);
844 NtClose(threadHandle);
847 NtClose(pipeReadHandle);
848 NtClose(pipeWriteHandle);
856 _In_ HANDLE ProcessHandle,
874 if (typeName && name)
877 L
"Handle 0x%Ix (%s): %s",
886 L
"Handle 0x%Ix: (error querying handle)",
900 _In_ HANDLE ProcessHandle,
901 _In_ BOOLEAN IsWow64,
902 _In_ ULONG NumberOfHandles,
903 _In_ PHANDLE AddressOfHandles,
905 _In_ BOOLEAN Alertable,
910 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
913 status = STATUS_SUCCESS;
915 if (NumberOfHandles <= MAXIMUM_WAIT_OBJECTS)
920 ULONG handles32[MAXIMUM_WAIT_OBJECTS];
926 NumberOfHandles *
sizeof(ULONG),
930 for (i = 0; i < NumberOfHandles; i++)
931 handles[i] = UlongToHandle(handles32[i]);
941 NumberOfHandles *
sizeof(HANDLE),
952 L
"Thread is waiting (%s, %s) for:\r\n",
953 Alertable ? L
"alertable" : L
"non-alertable",
954 WaitType ==
WaitAll ? L
"wait all" : L
"wait any"
957 for (i = 0; i < NumberOfHandles; i++)
971 if (!
NT_SUCCESS(status) || NumberOfHandles > MAXIMUM_WAIT_OBJECTS)
975 L
"Thread is waiting for multiple objects."
991 WCHAR windowClass[64];
998 if (!GetSendMessageReceiver_I)
1001 if (!GetSendMessageReceiver_I)
1004 windowHandle = GetSendMessageReceiver_I(ThreadId);
1009 threadId = GetWindowThreadProcessId(windowHandle, &processId);
1015 if (!GetClassName(windowHandle, windowClass,
sizeof(windowClass) /
sizeof(WCHAR)))
1024 _In_ HANDLE ThreadId
1031 HANDLE threadHandle;
1035 if (!NtAlpcQueryInformation_I)
1038 if (!NtAlpcQueryInformation_I)
1044 bufferLength = 0x110;
1045 serverInfo = PhAllocate(bufferLength);
1046 serverInfo->
In.ThreadHandle = threadHandle;
1048 status = NtAlpcQueryInformation_I(NULL,
AlpcServerInformation, serverInfo, bufferLength, &bufferLength);
1050 if (status == STATUS_INFO_LENGTH_MISMATCH)
1053 serverInfo = PhAllocate(bufferLength);
1054 serverInfo->
In.ThreadHandle = threadHandle;
1056 status = NtAlpcQueryInformation_I(NULL,
AlpcServerInformation, serverInfo, bufferLength, &bufferLength);
1068 string =
PhaFormatString(L
"ALPC Port: %.*s (%s)", serverInfo->
Out.ConnectionPortName.Length / 2, serverInfo->
Out.ConnectionPortName.Buffer, clientIdName->
Buffer);
1072 NtClose(threadHandle);