NTSTATUS PhpThreadQueryWorker( __in PVOID Parameter ) { PPH_THREAD_QUERY_DATA data = (PPH_THREAD_QUERY_DATA)Parameter; LONG newSymbolsLoading; newSymbolsLoading = _InterlockedIncrement(&data->ThreadProvider->SymbolsLoading); if (newSymbolsLoading == 1) PhInvokeCallback(&data->ThreadProvider->LoadingStateChangedEvent, (PVOID)TRUE); // We can't resolve the start address until symbols have // been loaded. PhWaitForEvent(&data->ThreadProvider->SymbolsLoadedEvent, NULL); data->StartAddressString = PhGetSymbolFromAddress( data->ThreadProvider->SymbolProvider, data->ThreadItem->StartAddress, &data->StartAddressResolveLevel, &data->ThreadItem->StartAddressFileName, NULL, NULL ); newSymbolsLoading = _InterlockedDecrement(&data->ThreadProvider->SymbolsLoading); if (newSymbolsLoading == 0) PhInvokeCallback(&data->ThreadProvider->LoadingStateChangedEvent, (PVOID)FALSE); // Get the service tag, and the service name. if ( WINDOWS_HAS_SERVICE_TAGS && data->ThreadProvider->SymbolProvider->IsRealHandle && data->ThreadItem->ThreadHandle ) { PVOID serviceTag; if (NT_SUCCESS(PhGetThreadServiceTag( data->ThreadItem->ThreadHandle, data->ThreadProvider->ProcessHandle, &serviceTag ))) { data->ServiceName = PhGetServiceNameFromTag( data->ThreadProvider->ProcessId, serviceTag ); } } RtlInterlockedPushEntrySList(&data->ThreadProvider->QueryListHead, &data->ListEntry); PhDereferenceObject(data->ThreadProvider); return STATUS_SUCCESS; }
VOID PhpRegisterSymbolProvider( _In_opt_ PPH_SYMBOL_PROVIDER SymbolProvider ) { if (PhBeginInitOnce(&PhSymInitOnce)) { PhInvokeCallback(&PhSymInitCallback, NULL); PhEndInitOnce(&PhSymInitOnce); } if (!SymbolProvider) return; #ifdef PH_SYMBOL_PROVIDER_DELAY_INIT if (PhBeginInitOnce(&SymbolProvider->InitOnce)) { #endif if (SymInitialize_I) { PH_LOCK_SYMBOLS(); SymInitialize_I(SymbolProvider->ProcessHandle, NULL, FALSE); if (SymRegisterCallbackW64_I) SymRegisterCallbackW64_I(SymbolProvider->ProcessHandle, PhpSymbolCallbackFunction, (ULONG64)SymbolProvider); PH_UNLOCK_SYMBOLS(); SymbolProvider->IsRegistered = TRUE; } #ifdef PH_SYMBOL_PROVIDER_DELAY_INIT PhEndInitOnce(&SymbolProvider->InitOnce); } #endif }
static BOOLEAN NTAPI PhpWalkThreadStackCallback( _In_ PPH_THREAD_STACK_FRAME StackFrame, _In_opt_ PVOID Context ) { PTHREAD_STACK_CONTEXT threadStackContext = (PTHREAD_STACK_CONTEXT)Context; PPH_STRING symbol; PTHREAD_STACK_ITEM item; if (threadStackContext->StopWalk) return FALSE; PhAcquireQueuedLockExclusive(&threadStackContext->StatusLock); PhMoveReference(&threadStackContext->StatusMessage, PhFormatString(L"Processing frame %u...", threadStackContext->NewList->Count)); PhReleaseQueuedLockExclusive(&threadStackContext->StatusLock); PostMessage(threadStackContext->ProgressWindowHandle, WM_PH_STATUS_UPDATE, 0, 0); symbol = PhGetSymbolFromAddress( threadStackContext->SymbolProvider, (ULONG64)StackFrame->PcAddress, NULL, NULL, NULL, NULL ); if (symbol && (StackFrame->Flags & PH_THREAD_STACK_FRAME_I386) && !(StackFrame->Flags & PH_THREAD_STACK_FRAME_FPO_DATA_PRESENT)) { PhMoveReference(&symbol, PhConcatStrings2(symbol->Buffer, L" (No unwind info)")); } item = PhAllocate(sizeof(THREAD_STACK_ITEM)); item->StackFrame = *StackFrame; item->Index = threadStackContext->NewList->Count; if (PhPluginsEnabled) { PH_PLUGIN_THREAD_STACK_CONTROL control; control.Type = PluginThreadStackResolveSymbol; control.UniqueKey = threadStackContext; control.u.ResolveSymbol.StackFrame = StackFrame; control.u.ResolveSymbol.Symbol = symbol; PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackThreadStackControl), &control); symbol = control.u.ResolveSymbol.Symbol; } item->Symbol = symbol; PhAddItemList(threadStackContext->NewList, item); return TRUE; }
static NTSTATUS PhpRefreshThreadStackThreadStart( _In_ PVOID Parameter ) { NTSTATUS status; PTHREAD_STACK_CONTEXT threadStackContext = Parameter; CLIENT_ID clientId; BOOLEAN defaultWalk; clientId.UniqueProcess = threadStackContext->ProcessId; clientId.UniqueThread = threadStackContext->ThreadId; defaultWalk = TRUE; if (threadStackContext->CustomWalk) { PH_PLUGIN_THREAD_STACK_CONTROL control; control.Type = PluginThreadStackWalkStack; control.UniqueKey = threadStackContext; control.u.WalkStack.Status = STATUS_UNSUCCESSFUL; control.u.WalkStack.ThreadHandle = threadStackContext->ThreadHandle; control.u.WalkStack.ProcessHandle = threadStackContext->SymbolProvider->ProcessHandle; control.u.WalkStack.ClientId = &clientId; control.u.WalkStack.Flags = PH_WALK_I386_STACK | PH_WALK_AMD64_STACK | PH_WALK_KERNEL_STACK; control.u.WalkStack.Callback = PhpWalkThreadStackCallback; control.u.WalkStack.CallbackContext = threadStackContext; PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackThreadStackControl), &control); status = control.u.WalkStack.Status; if (NT_SUCCESS(status)) defaultWalk = FALSE; } if (defaultWalk) { status = PhWalkThreadStack( threadStackContext->ThreadHandle, threadStackContext->SymbolProvider->ProcessHandle, &clientId, PH_WALK_I386_STACK | PH_WALK_AMD64_STACK | PH_WALK_KERNEL_STACK, PhpWalkThreadStackCallback, threadStackContext ); } if (threadStackContext->NewList->Count != 0) status = STATUS_SUCCESS; threadStackContext->WalkStatus = status; PostMessage(threadStackContext->ProgressWindowHandle, WM_PH_COMPLETED, 0, 0); return STATUS_SUCCESS; }
VOID PhpExecuteCallbackForAllPlugins( _In_ PH_PLUGIN_CALLBACK Callback, _In_ BOOLEAN StartupParameters ) { PPH_AVL_LINKS links; links = PhMinimumElementAvlTree(&PhPluginsByName); while (links) { PPH_PLUGIN plugin = CONTAINING_RECORD(links, PH_PLUGIN, Links); PPH_LIST parameters = NULL; // Find relevant startup parameters for this plugin. if (StartupParameters && PhStartupParameters.PluginParameters) { ULONG i; for (i = 0; i < PhStartupParameters.PluginParameters->Count; i++) { PPH_STRING string = PhStartupParameters.PluginParameters->Items[i]; PH_STRINGREF pluginName; PH_STRINGREF parameter; if (PhSplitStringRefAtChar(&string->sr, ':', &pluginName, ¶meter) && PhEqualStringRef(&pluginName, &plugin->Name, FALSE) && parameter.Length != 0) { if (!parameters) parameters = PhCreateList(3); PhAddItemList(parameters, PhCreateString2(¶meter)); } } } PhInvokeCallback(PhGetPluginCallback(plugin, Callback), parameters); if (parameters) { ULONG i; for (i = 0; i < parameters->Count; i++) PhDereferenceObject(parameters->Items[i]); PhDereferenceObject(parameters); } links = PhSuccessorElementAvlTree(links); } }
VOID PhpLogEntry( _In_ PPH_LOG_ENTRY Entry ) { PPH_LOG_ENTRY oldEntry; oldEntry = PhAddItemCircularBuffer2_PVOID(&PhLogBuffer, Entry); if (oldEntry) PhpFreeLogEntry(oldEntry); PhInvokeCallback(&PhLoggedCallback, Entry); }
NTSTATUS PhpSymbolCallbackWorker( _In_ PVOID Parameter ) { PPH_SYMBOL_EVENT_DATA data = Parameter; dprintf("symbol event %d: %S\n", data->Type, data->FileName->Buffer); PhInvokeCallback(&data->SymbolProvider->EventCallback, data); PhSwapReference(&data->FileName, NULL); PhDereferenceObject(data); return STATUS_SUCCESS; }
static VOID DbgAddLogEntry( _Inout_ PPH_DBGEVENTS_CONTEXT Context, _In_ PDEBUG_LOG_ENTRY Entry ) { if (Context->LogMessageList->Count > PhGetIntegerSetting(SETTING_NAME_MAX_ENTRIES)) { DbgFreeLogEntry(Context->LogMessageList->Items[0]); PhRemoveItemList(Context->LogMessageList, 0); } PhAddItemList(Context->LogMessageList, Entry); PhInvokeCallback(&DbgLoggedCallback, Entry); }
/** * Triggers a plugin menu item. * * \param MenuInfo The plugin menu information structure. * \param Item The menu item chosen by the user. * * \remarks This function is reserved for internal use. */ BOOLEAN PhPluginTriggerEMenuItem( _In_ PPH_PLUGIN_MENU_INFORMATION MenuInfo, _In_ PPH_EMENU_ITEM Item ) { PPH_PLUGIN_MENU_ITEM pluginMenuItem; ULONG i; PPHP_PLUGIN_MENU_HOOK hook; PH_PLUGIN_MENU_HOOK_INFORMATION menuHookInfo; if (MenuInfo->PluginHookList) { for (i = 0; i < MenuInfo->PluginHookList->Count; i++) { hook = MenuInfo->PluginHookList->Items[i]; menuHookInfo.MenuInfo = MenuInfo; menuHookInfo.SelectedItem = Item; menuHookInfo.Context = hook->Context; menuHookInfo.Handled = FALSE; PhInvokeCallback(PhGetPluginCallback(hook->Plugin, PluginCallbackMenuHook), &menuHookInfo); if (menuHookInfo.Handled) return TRUE; } } if (Item->Id != ID_PLUGIN_MENU_ITEM) return FALSE; pluginMenuItem = Item->Context; pluginMenuItem->OwnerWindow = MenuInfo->OwnerWindow; PhInvokeCallback(PhGetPluginCallback(pluginMenuItem->Plugin, PluginCallbackMenuItem), pluginMenuItem); return TRUE; }
VOID PhpRefreshProcessMemoryList( _In_ HWND hwndDlg, _In_ PPH_PROCESS_PROPPAGECONTEXT PropPageContext ) { PPH_MEMORY_CONTEXT memoryContext = PropPageContext->Context; if (memoryContext->MemoryItemListValid) { PhDeleteMemoryItemList(&memoryContext->MemoryItemList); memoryContext->MemoryItemListValid = FALSE; } memoryContext->LastRunStatus = PhQueryMemoryItemList( memoryContext->ProcessId, PH_QUERY_MEMORY_REGION_TYPE | PH_QUERY_MEMORY_WS_COUNTERS, &memoryContext->MemoryItemList ); if (NT_SUCCESS(memoryContext->LastRunStatus)) { if (PhPluginsEnabled) { PH_PLUGIN_MEMORY_ITEM_LIST_CONTROL control; control.Type = PluginMemoryItemListInitialized; control.u.Initialized.List = &memoryContext->MemoryItemList; PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackMemoryItemListControl), &control); } memoryContext->MemoryItemListValid = TRUE; TreeNew_SetEmptyText(memoryContext->ListContext.TreeNewHandle, &EmptyMemoryText, 0); PhReplaceMemoryList(&memoryContext->ListContext, &memoryContext->MemoryItemList); } else { PPH_STRING message; message = PhGetStatusMessage(memoryContext->LastRunStatus, 0); PhMoveReference(&memoryContext->ErrorMessage, PhFormatString(L"Unable to query memory information:\n%s", PhGetStringOrDefault(message, L"Unknown error."))); PhClearReference(&message); TreeNew_SetEmptyText(memoryContext->ListContext.TreeNewHandle, &memoryContext->ErrorMessage->sr, 0); PhReplaceMemoryList(&memoryContext->ListContext, NULL); } }
VOID PhpExecuteCallbackForAllPlugins( _In_ PH_PLUGIN_CALLBACK Callback ) { PPH_AVL_LINKS links; links = PhMinimumElementAvlTree(&PhPluginsByName); while (links) { PPH_PLUGIN plugin = CONTAINING_RECORD(links, PH_PLUGIN, Links); PhInvokeCallback(PhGetPluginCallback(plugin, Callback), NULL); links = PhSuccessorElementAvlTree(links); } }
/** * Triggers a plugin menu item. * * \param OwnerWindow The window that owns the menu containing the menu item. * \param Item The menu item chosen by the user. * * \remarks This function is reserved for internal use. */ BOOLEAN PhPluginTriggerEMenuItem( _In_ HWND OwnerWindow, _In_ PPH_EMENU_ITEM Item ) { PPH_PLUGIN_MENU_ITEM pluginMenuItem; if (Item->Id != ID_PLUGIN_MENU_ITEM) return FALSE; pluginMenuItem = Item->Context; pluginMenuItem->OwnerWindow = OwnerWindow; PhInvokeCallback(PhGetPluginCallback(pluginMenuItem->Plugin, PluginCallbackMenuItem), pluginMenuItem); return TRUE; }
static VOID NTAPI ProcessesUpdatedCallback( __in_opt PVOID Parameter, __in_opt PVOID Context ) { static ULONG runCount = 0; PSLIST_ENTRY listEntry; PLIST_ENTRY ageListEntry; // Process incoming disk event packets. listEntry = RtlInterlockedFlushSList(&EtDiskPacketListHead); while (listEntry) { PETP_DISK_PACKET packet; packet = CONTAINING_RECORD(listEntry, ETP_DISK_PACKET, ListEntry); listEntry = listEntry->Next; EtpProcessDiskPacket(packet, runCount); if (packet->FileName) PhDereferenceObject(packet->FileName); PhFreeToFreeList(&EtDiskPacketFreeList, packet); } // Remove old entries. ageListEntry = EtDiskAgeListHead.Blink; while (ageListEntry != &EtDiskAgeListHead) { PET_DISK_ITEM diskItem; diskItem = CONTAINING_RECORD(ageListEntry, ET_DISK_ITEM, AgeListEntry); ageListEntry = ageListEntry->Blink; if (runCount - diskItem->FreshTime < HISTORY_SIZE) // must compare like this to avoid overflow/underflow problems break; PhInvokeCallback(&EtDiskItemRemovedEvent, diskItem); PhAcquireQueuedLockExclusive(&EtDiskHashtableLock); EtpRemoveDiskItem(diskItem); PhReleaseQueuedLockExclusive(&EtDiskHashtableLock); } // Update existing items. ageListEntry = EtDiskAgeListHead.Flink; while (ageListEntry != &EtDiskAgeListHead) { PET_DISK_ITEM diskItem; diskItem = CONTAINING_RECORD(ageListEntry, ET_DISK_ITEM, AgeListEntry); // Update statistics. if (diskItem->HistoryPosition != 0) diskItem->HistoryPosition--; else diskItem->HistoryPosition = HISTORY_SIZE - 1; diskItem->ReadHistory[diskItem->HistoryPosition] = diskItem->ReadDelta; diskItem->WriteHistory[diskItem->HistoryPosition] = diskItem->WriteDelta; if (diskItem->HistoryCount < HISTORY_SIZE) diskItem->HistoryCount++; if (diskItem->ResponseTimeCount != 0) { diskItem->ResponseTimeAverage = (FLOAT)diskItem->ResponseTimeTotal / diskItem->ResponseTimeCount; // Reset the total once in a while to avoid the number getting too large (and thus losing precision). if (diskItem->ResponseTimeCount >= 1000) { diskItem->ResponseTimeTotal = diskItem->ResponseTimeAverage; diskItem->ResponseTimeCount = 1; } } diskItem->ReadTotal += diskItem->ReadDelta; diskItem->WriteTotal += diskItem->WriteDelta; diskItem->ReadDelta = 0; diskItem->WriteDelta = 0; diskItem->ReadAverage = EtpCalculateAverage(diskItem->ReadHistory, HISTORY_SIZE, diskItem->HistoryPosition, diskItem->HistoryCount, HISTORY_SIZE); diskItem->WriteAverage = EtpCalculateAverage(diskItem->WriteHistory, HISTORY_SIZE, diskItem->HistoryPosition, diskItem->HistoryCount, HISTORY_SIZE); if (diskItem->AddTime != runCount) { BOOLEAN modified = FALSE; PPH_PROCESS_ITEM processItem; if (!diskItem->ProcessName || !diskItem->ProcessIcon || !diskItem->ProcessRecord) { if (processItem = PhReferenceProcessItem(diskItem->ProcessId)) { if (!diskItem->ProcessName) { diskItem->ProcessName = processItem->ProcessName; PhReferenceObject(processItem->ProcessName); modified = TRUE; } if (!diskItem->ProcessIcon) { diskItem->ProcessIcon = EtProcIconReferenceSmallProcessIcon(EtGetProcessBlock(processItem)); if (diskItem->ProcessIcon) modified = TRUE; } if (!diskItem->ProcessRecord) { diskItem->ProcessRecord = processItem->Record; PhReferenceProcessRecord(diskItem->ProcessRecord); } PhDereferenceObject(processItem); } } if (modified) { // Raise the disk item modified event. PhInvokeCallback(&EtDiskItemModifiedEvent, diskItem); } } ageListEntry = ageListEntry->Flink; } PhInvokeCallback(&EtDiskItemsUpdatedEvent, NULL); runCount++; }
VOID EtpProcessDiskPacket( __in PETP_DISK_PACKET Packet, __in ULONG RunId ) { PET_ETW_DISK_EVENT diskEvent; PET_DISK_ITEM diskItem; BOOLEAN added = FALSE; diskEvent = &Packet->Event; // We only process non-zero read/write events. if (diskEvent->Type != EtEtwDiskReadType && diskEvent->Type != EtEtwDiskWriteType) return; if (diskEvent->TransferSize == 0) return; // Ignore packets with no file name - this is useless to the user. if (!Packet->FileName) return; diskItem = EtReferenceDiskItem(diskEvent->ClientId.UniqueProcess, Packet->FileName); if (!diskItem) { PPH_PROCESS_ITEM processItem; // Disk item not found (or the address was re-used), create it. diskItem = EtCreateDiskItem(); diskItem->ProcessId = diskEvent->ClientId.UniqueProcess; diskItem->FileName = Packet->FileName; PhReferenceObject(Packet->FileName); diskItem->FileNameWin32 = PhGetFileName(diskItem->FileName); if (processItem = PhReferenceProcessItem(diskItem->ProcessId)) { diskItem->ProcessName = processItem->ProcessName; PhReferenceObject(processItem->ProcessName); diskItem->ProcessIcon = EtProcIconReferenceSmallProcessIcon(EtGetProcessBlock(processItem)); diskItem->ProcessRecord = processItem->Record; PhReferenceProcessRecord(diskItem->ProcessRecord); PhDereferenceObject(processItem); } // Add the disk item to the age list. diskItem->AddTime = RunId; diskItem->FreshTime = RunId; InsertHeadList(&EtDiskAgeListHead, &diskItem->AgeListEntry); // Add the disk item to the hashtable. PhAcquireQueuedLockExclusive(&EtDiskHashtableLock); PhAddEntryHashtable(EtDiskHashtable, &diskItem); PhReleaseQueuedLockExclusive(&EtDiskHashtableLock); // Raise the disk item added event. PhInvokeCallback(&EtDiskItemAddedEvent, diskItem); added = TRUE; } // The I/O priority number needs to be decoded. diskItem->IoPriority = (diskEvent->IrpFlags >> 17) & 7; if (diskItem->IoPriority == 0) diskItem->IoPriority = IoPriorityNormal; else diskItem->IoPriority--; // Accumulate statistics for this update period. if (diskEvent->Type == EtEtwDiskReadType) diskItem->ReadDelta += diskEvent->TransferSize; else diskItem->WriteDelta += diskEvent->TransferSize; if (EtpPerformanceFrequency.QuadPart != 0) { // Convert the response time to milliseconds. diskItem->ResponseTimeTotal += (FLOAT)diskEvent->HighResResponseTime * 1000 / EtpPerformanceFrequency.QuadPart; diskItem->ResponseTimeCount++; } if (!added) { if (diskItem->FreshTime != RunId) { diskItem->FreshTime = RunId; RemoveEntryList(&diskItem->AgeListEntry); InsertHeadList(&EtDiskAgeListHead, &diskItem->AgeListEntry); } PhDereferenceObject(diskItem); } }
VOID PhModuleProviderUpdate( __in PVOID Object ) { PPH_MODULE_PROVIDER moduleProvider = (PPH_MODULE_PROVIDER)Object; PPH_LIST modules; ULONG i; // If we didn't get a handle when we created the provider, // abort (unless this is the System process - in that case // we don't need a handle). if (!moduleProvider->ProcessHandle && moduleProvider->ProcessId != SYSTEM_PROCESS_ID) return; modules = PhCreateList(20); PhEnumGenericModules( moduleProvider->ProcessId, moduleProvider->ProcessHandle, PH_ENUM_GENERIC_MAPPED_FILES | PH_ENUM_GENERIC_MAPPED_IMAGES, EnumModulesCallback, modules ); // Look for removed modules. { PPH_LIST modulesToRemove = NULL; ULONG enumerationKey = 0; PPH_MODULE_ITEM *moduleItem; while (PhEnumHashtable(moduleProvider->ModuleHashtable, (PPVOID)&moduleItem, &enumerationKey)) { BOOLEAN found = FALSE; // Check if the module still exists. for (i = 0; i < modules->Count; i++) { PPH_MODULE_INFO module = modules->Items[i]; if ((*moduleItem)->BaseAddress == module->BaseAddress) { found = TRUE; break; } } if (!found) { // Raise the module removed event. PhInvokeCallback(&moduleProvider->ModuleRemovedEvent, *moduleItem); if (!modulesToRemove) modulesToRemove = PhCreateList(2); PhAddItemList(modulesToRemove, *moduleItem); } } if (modulesToRemove) { PhAcquireFastLockExclusive(&moduleProvider->ModuleHashtableLock); for (i = 0; i < modulesToRemove->Count; i++) { PhpRemoveModuleItem( moduleProvider, (PPH_MODULE_ITEM)modulesToRemove->Items[i] ); } PhReleaseFastLockExclusive(&moduleProvider->ModuleHashtableLock); PhDereferenceObject(modulesToRemove); } } // Go through the queued thread query data. { PSLIST_ENTRY entry; PPH_MODULE_QUERY_DATA data; entry = RtlInterlockedFlushSList(&moduleProvider->QueryListHead); while (entry) { data = CONTAINING_RECORD(entry, PH_MODULE_QUERY_DATA, ListEntry); entry = entry->Next; data->ModuleItem->VerifyResult = data->VerifyResult; data->ModuleItem->VerifySignerName = data->VerifySignerName; data->ModuleItem->JustProcessed = TRUE; PhDereferenceObject(data->ModuleItem); PhFree(data); } } // Look for new modules. for (i = 0; i < modules->Count; i++) { PPH_MODULE_INFO module = modules->Items[i]; PPH_MODULE_ITEM moduleItem; moduleItem = PhReferenceModuleItem(moduleProvider, module->BaseAddress); if (!moduleItem) { moduleItem = PhCreateModuleItem(); moduleItem->BaseAddress = module->BaseAddress; PhPrintPointer(moduleItem->BaseAddressString, moduleItem->BaseAddress); moduleItem->Size = module->Size; moduleItem->Flags = module->Flags; moduleItem->Type = module->Type; moduleItem->Reserved = 0; moduleItem->LoadCount = module->LoadCount; moduleItem->Name = module->Name; PhReferenceObject(moduleItem->Name); moduleItem->FileName = module->FileName; PhReferenceObject(moduleItem->FileName); PhInitializeImageVersionInfo( &moduleItem->VersionInfo, PhGetString(moduleItem->FileName) ); moduleItem->IsFirst = i == 0; if (moduleItem->Type == PH_MODULE_TYPE_MODULE || moduleItem->Type == PH_MODULE_TYPE_WOW64_MODULE || moduleItem->Type == PH_MODULE_TYPE_MAPPED_IMAGE) { PH_REMOTE_MAPPED_IMAGE remoteMappedImage; // Note: // On Windows 7 the LDRP_IMAGE_NOT_AT_BASE flag doesn't appear to be used // anymore. Instead we'll check ImageBase in the image headers. We read this in // from the process' memory because: // // 1. It (should be) faster than opening the file and mapping it in, and // 2. It contains the correct original image base relocated by ASLR, if present. if (NT_SUCCESS(PhLoadRemoteMappedImage(moduleProvider->ProcessHandle, moduleItem->BaseAddress, &remoteMappedImage))) { moduleItem->ImageTimeDateStamp = remoteMappedImage.NtHeaders->FileHeader.TimeDateStamp; moduleItem->ImageCharacteristics = remoteMappedImage.NtHeaders->FileHeader.Characteristics; if (remoteMappedImage.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { if ((ULONG_PTR)((PIMAGE_OPTIONAL_HEADER32)&remoteMappedImage.NtHeaders->OptionalHeader)->ImageBase != (ULONG_PTR)moduleItem->BaseAddress) moduleItem->Flags |= LDRP_IMAGE_NOT_AT_BASE; moduleItem->ImageDllCharacteristics = ((PIMAGE_OPTIONAL_HEADER32)&remoteMappedImage.NtHeaders->OptionalHeader)->DllCharacteristics; } else if (remoteMappedImage.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { if ((ULONG_PTR)((PIMAGE_OPTIONAL_HEADER64)&remoteMappedImage.NtHeaders->OptionalHeader)->ImageBase != (ULONG_PTR)moduleItem->BaseAddress) moduleItem->Flags |= LDRP_IMAGE_NOT_AT_BASE; moduleItem->ImageDllCharacteristics = ((PIMAGE_OPTIONAL_HEADER64)&remoteMappedImage.NtHeaders->OptionalHeader)->DllCharacteristics; } PhUnloadRemoteMappedImage(&remoteMappedImage); } } if (moduleItem->Type == PH_MODULE_TYPE_MODULE || moduleItem->Type == PH_MODULE_TYPE_KERNEL_MODULE || moduleItem->Type == PH_MODULE_TYPE_WOW64_MODULE || moduleItem->Type == PH_MODULE_TYPE_MAPPED_IMAGE) { // See if the file has already been verified; if not, queue for verification. moduleItem->VerifyResult = PhVerifyFileCached(moduleItem->FileName, &moduleItem->VerifySignerName, TRUE); if (moduleItem->VerifyResult == VrUnknown) PhpQueueModuleQuery(moduleProvider, moduleItem); } // Add the module item to the hashtable. PhAcquireFastLockExclusive(&moduleProvider->ModuleHashtableLock); PhAddEntryHashtable(moduleProvider->ModuleHashtable, &moduleItem); PhReleaseFastLockExclusive(&moduleProvider->ModuleHashtableLock); // Raise the module added event. PhInvokeCallback(&moduleProvider->ModuleAddedEvent, moduleItem); } else { BOOLEAN modified = FALSE; if (moduleItem->JustProcessed) modified = TRUE; moduleItem->JustProcessed = FALSE; if (modified) PhInvokeCallback(&moduleProvider->ModuleModifiedEvent, moduleItem); PhDereferenceObject(moduleItem); } } // Free the modules list. for (i = 0; i < modules->Count; i++) { PPH_MODULE_INFO module = modules->Items[i]; PhDereferenceObject(module->Name); PhDereferenceObject(module->FileName); PhFree(module); } PhDereferenceObject(modules); PhInvokeCallback(&moduleProvider->UpdatedEvent, NULL); }
INT_PTR CALLBACK PhpProcessMemoryDlgProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam ) { LPPROPSHEETPAGE propSheetPage; PPH_PROCESS_PROPPAGECONTEXT propPageContext; PPH_PROCESS_ITEM processItem; PPH_MEMORY_CONTEXT memoryContext; HWND tnHandle; if (PhpPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext, &processItem)) { memoryContext = (PPH_MEMORY_CONTEXT)propPageContext->Context; if (memoryContext) tnHandle = memoryContext->ListContext.TreeNewHandle; } else { return FALSE; } switch (uMsg) { case WM_INITDIALOG: { memoryContext = propPageContext->Context = PhAllocate(PhEmGetObjectSize(EmMemoryContextType, sizeof(PH_MEMORY_CONTEXT))); memset(memoryContext, 0, sizeof(PH_MEMORY_CONTEXT)); memoryContext->ProcessId = processItem->ProcessId; // Initialize the list. tnHandle = GetDlgItem(hwndDlg, IDC_LIST); BringWindowToTop(tnHandle); PhInitializeMemoryList(hwndDlg, tnHandle, &memoryContext->ListContext); TreeNew_SetEmptyText(tnHandle, &PhpLoadingText, 0); memoryContext->LastRunStatus = -1; memoryContext->ErrorMessage = NULL; PhEmCallObjectOperation(EmMemoryContextType, memoryContext, EmObjectCreate); if (PhPluginsEnabled) { PH_PLUGIN_TREENEW_INFORMATION treeNewInfo; treeNewInfo.TreeNewHandle = tnHandle; treeNewInfo.CmData = &memoryContext->ListContext.Cm; treeNewInfo.SystemContext = memoryContext; PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackMemoryTreeNewInitializing), &treeNewInfo); } PhLoadSettingsMemoryList(&memoryContext->ListContext); PhSetOptionsMemoryList(&memoryContext->ListContext, TRUE); Button_SetCheck(GetDlgItem(hwndDlg, IDC_HIDEFREEREGIONS), memoryContext->ListContext.HideFreeRegions ? BST_CHECKED : BST_UNCHECKED); PhpRefreshProcessMemoryList(hwndDlg, propPageContext); } break; case WM_DESTROY: { PhEmCallObjectOperation(EmMemoryContextType, memoryContext, EmObjectDelete); if (PhPluginsEnabled) { PH_PLUGIN_TREENEW_INFORMATION treeNewInfo; treeNewInfo.TreeNewHandle = tnHandle; treeNewInfo.CmData = &memoryContext->ListContext.Cm; PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackMemoryTreeNewUninitializing), &treeNewInfo); } PhSaveSettingsMemoryList(&memoryContext->ListContext); PhDeleteMemoryList(&memoryContext->ListContext); if (memoryContext->MemoryItemListValid) PhDeleteMemoryItemList(&memoryContext->MemoryItemList); PhClearReference(&memoryContext->ErrorMessage); PhFree(memoryContext); PhpPropPageDlgProcDestroy(hwndDlg); } break; case WM_SHOWWINDOW: { if (!propPageContext->LayoutInitialized) { PPH_LAYOUT_ITEM dialogItem; dialogItem = PhAddPropPageLayoutItem(hwndDlg, hwndDlg, PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_STRINGS), dialogItem, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_REFRESH), dialogItem, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); PhAddPropPageLayoutItem(hwndDlg, memoryContext->ListContext.TreeNewHandle, dialogItem, PH_ANCHOR_ALL); PhDoPropPageLayout(hwndDlg); propPageContext->LayoutInitialized = TRUE; } } break; case WM_COMMAND: { switch (LOWORD(wParam)) { case ID_SHOWCONTEXTMENU: { PhShowMemoryContextMenu(hwndDlg, processItem, memoryContext, (PPH_TREENEW_CONTEXT_MENU)lParam); } break; case ID_MEMORY_READWRITEMEMORY: { PPH_MEMORY_NODE memoryNode = PhGetSelectedMemoryNode(&memoryContext->ListContext); if (memoryNode && !memoryNode->IsAllocationBase) { if (memoryNode->MemoryItem->State & MEM_COMMIT) { PPH_SHOWMEMORYEDITOR showMemoryEditor = PhAllocate(sizeof(PH_SHOWMEMORYEDITOR)); memset(showMemoryEditor, 0, sizeof(PH_SHOWMEMORYEDITOR)); showMemoryEditor->ProcessId = processItem->ProcessId; showMemoryEditor->BaseAddress = memoryNode->MemoryItem->BaseAddress; showMemoryEditor->RegionSize = memoryNode->MemoryItem->RegionSize; showMemoryEditor->SelectOffset = -1; showMemoryEditor->SelectLength = 0; ProcessHacker_ShowMemoryEditor(PhMainWndHandle, showMemoryEditor); } else { PhShowError(hwndDlg, L"Unable to edit the memory region because it is not committed."); } } } break; case ID_MEMORY_SAVE: { NTSTATUS status; HANDLE processHandle; PPH_MEMORY_NODE *memoryNodes; ULONG numberOfMemoryNodes; if (!NT_SUCCESS(status = PhOpenProcess( &processHandle, PROCESS_VM_READ, processItem->ProcessId ))) { PhShowStatus(hwndDlg, L"Unable to open the process", status, 0); break; } PhGetSelectedMemoryNodes(&memoryContext->ListContext, &memoryNodes, &numberOfMemoryNodes); if (numberOfMemoryNodes != 0) { static PH_FILETYPE_FILTER filters[] = { { L"Binary files (*.bin)", L"*.bin" }, { L"All files (*.*)", L"*.*" } }; PVOID fileDialog; fileDialog = PhCreateSaveFileDialog(); PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); PhSetFileDialogFileName(fileDialog, PhaConcatStrings2(processItem->ProcessName->Buffer, L".bin")->Buffer); if (PhShowFileDialog(hwndDlg, fileDialog)) { PPH_STRING fileName; PPH_FILE_STREAM fileStream; PVOID buffer; ULONG i; ULONG_PTR offset; fileName = PH_AUTO(PhGetFileDialogFileName(fileDialog)); if (NT_SUCCESS(status = PhCreateFileStream( &fileStream, fileName->Buffer, FILE_GENERIC_WRITE, FILE_SHARE_READ, FILE_OVERWRITE_IF, 0 ))) { buffer = PhAllocatePage(PAGE_SIZE, NULL); // Go through each selected memory item and append the region contents // to the file. for (i = 0; i < numberOfMemoryNodes; i++) { PPH_MEMORY_NODE memoryNode = memoryNodes[i]; PPH_MEMORY_ITEM memoryItem = memoryNode->MemoryItem; if (!memoryNode->IsAllocationBase && !(memoryItem->State & MEM_COMMIT)) continue; for (offset = 0; offset < memoryItem->RegionSize; offset += PAGE_SIZE) { if (NT_SUCCESS(NtReadVirtualMemory( processHandle, PTR_ADD_OFFSET(memoryItem->BaseAddress, offset), buffer, PAGE_SIZE, NULL ))) { PhWriteFileStream(fileStream, buffer, PAGE_SIZE); } } } PhFreePage(buffer); PhDereferenceObject(fileStream); } if (!NT_SUCCESS(status)) PhShowStatus(hwndDlg, L"Unable to create the file", status, 0); } PhFreeFileDialog(fileDialog); } PhFree(memoryNodes); NtClose(processHandle); } break; case ID_MEMORY_CHANGEPROTECTION: { PPH_MEMORY_NODE memoryNode = PhGetSelectedMemoryNode(&memoryContext->ListContext); if (memoryNode) { PhReferenceObject(memoryNode->MemoryItem); PhShowMemoryProtectDialog(hwndDlg, processItem, memoryNode->MemoryItem); PhUpdateMemoryNode(&memoryContext->ListContext, memoryNode); PhDereferenceObject(memoryNode->MemoryItem); } } break; case ID_MEMORY_FREE: { PPH_MEMORY_NODE memoryNode = PhGetSelectedMemoryNode(&memoryContext->ListContext); if (memoryNode) { PhReferenceObject(memoryNode->MemoryItem); PhUiFreeMemory(hwndDlg, processItem->ProcessId, memoryNode->MemoryItem, TRUE); PhDereferenceObject(memoryNode->MemoryItem); // TODO: somehow update the list } } break; case ID_MEMORY_DECOMMIT: { PPH_MEMORY_NODE memoryNode = PhGetSelectedMemoryNode(&memoryContext->ListContext); if (memoryNode) { PhReferenceObject(memoryNode->MemoryItem); PhUiFreeMemory(hwndDlg, processItem->ProcessId, memoryNode->MemoryItem, FALSE); PhDereferenceObject(memoryNode->MemoryItem); } } break; case ID_MEMORY_READWRITEADDRESS: { PPH_STRING selectedChoice = NULL; if (!memoryContext->MemoryItemListValid) break; while (PhaChoiceDialog( hwndDlg, L"Read/Write Address", L"Enter an address:", NULL, 0, NULL, PH_CHOICE_DIALOG_USER_CHOICE, &selectedChoice, NULL, L"MemoryReadWriteAddressChoices" )) { ULONG64 address64; PVOID address; if (selectedChoice->Length == 0) continue; if (PhStringToInteger64(&selectedChoice->sr, 0, &address64)) { PPH_MEMORY_ITEM memoryItem; address = (PVOID)address64; memoryItem = PhLookupMemoryItemList(&memoryContext->MemoryItemList, address); if (memoryItem) { PPH_SHOWMEMORYEDITOR showMemoryEditor = PhAllocate(sizeof(PH_SHOWMEMORYEDITOR)); memset(showMemoryEditor, 0, sizeof(PH_SHOWMEMORYEDITOR)); showMemoryEditor->ProcessId = processItem->ProcessId; showMemoryEditor->BaseAddress = memoryItem->BaseAddress; showMemoryEditor->RegionSize = memoryItem->RegionSize; showMemoryEditor->SelectOffset = (ULONG)((ULONG_PTR)address - (ULONG_PTR)memoryItem->BaseAddress); showMemoryEditor->SelectLength = 0; ProcessHacker_ShowMemoryEditor(PhMainWndHandle, showMemoryEditor); break; } else { PhShowError(hwndDlg, L"Unable to find the memory region for the selected address."); } } } } break; case ID_MEMORY_COPY: { PPH_STRING text; text = PhGetTreeNewText(tnHandle, 0); PhSetClipboardString(tnHandle, &text->sr); PhDereferenceObject(text); } break; case IDC_HIDEFREEREGIONS: { BOOLEAN hide; hide = Button_GetCheck(GetDlgItem(hwndDlg, IDC_HIDEFREEREGIONS)) == BST_CHECKED; PhSetOptionsMemoryList(&memoryContext->ListContext, hide); } break; case IDC_STRINGS: PhShowMemoryStringDialog(hwndDlg, processItem); break; case IDC_REFRESH: PhpRefreshProcessMemoryList(hwndDlg, propPageContext); break; } } break; } return FALSE; }
NTSTATUS PhpThreadQueryWorker( _In_ PVOID Parameter ) { PPH_THREAD_QUERY_DATA data = (PPH_THREAD_QUERY_DATA)Parameter; LONG newSymbolsLoading; if (data->ThreadProvider->Terminating) goto Done; newSymbolsLoading = _InterlockedIncrement(&data->ThreadProvider->SymbolsLoading); if (newSymbolsLoading == 1) PhInvokeCallback(&data->ThreadProvider->LoadingStateChangedEvent, (PVOID)TRUE); if (data->ThreadProvider->SymbolsLoadedRunId == 0) PhLoadSymbolsThreadProvider(data->ThreadProvider); data->StartAddressString = PhGetSymbolFromAddress( data->ThreadProvider->SymbolProvider, data->ThreadItem->StartAddress, &data->StartAddressResolveLevel, &data->ThreadItem->StartAddressFileName, NULL, NULL ); if (data->StartAddressResolveLevel == PhsrlAddress && data->ThreadProvider->SymbolsLoadedRunId < data->RunId) { // The process may have loaded new modules, so load symbols for those and try again. PhLoadSymbolsThreadProvider(data->ThreadProvider); PhClearReference(&data->StartAddressString); PhClearReference(&data->ThreadItem->StartAddressFileName); data->StartAddressString = PhGetSymbolFromAddress( data->ThreadProvider->SymbolProvider, data->ThreadItem->StartAddress, &data->StartAddressResolveLevel, &data->ThreadItem->StartAddressFileName, NULL, NULL ); } newSymbolsLoading = _InterlockedDecrement(&data->ThreadProvider->SymbolsLoading); if (newSymbolsLoading == 0) PhInvokeCallback(&data->ThreadProvider->LoadingStateChangedEvent, (PVOID)FALSE); // Check if the process has services - we'll need to know before getting service tag/name // information. if (WINDOWS_HAS_SERVICE_TAGS && !data->ThreadProvider->HasServicesKnown) { PPH_PROCESS_ITEM processItem; if (processItem = PhReferenceProcessItem(data->ThreadProvider->ProcessId)) { data->ThreadProvider->HasServices = processItem->ServiceList && processItem->ServiceList->Count != 0; PhDereferenceObject(processItem); } data->ThreadProvider->HasServicesKnown = TRUE; } // Get the service tag, and the service name. if (WINDOWS_HAS_SERVICE_TAGS && data->ThreadProvider->SymbolProvider->IsRealHandle && data->ThreadItem->ThreadHandle) { PVOID serviceTag; if (NT_SUCCESS(PhGetThreadServiceTag( data->ThreadItem->ThreadHandle, data->ThreadProvider->ProcessHandle, &serviceTag ))) { data->ServiceName = PhGetServiceNameFromTag( data->ThreadProvider->ProcessId, serviceTag ); } } Done: RtlInterlockedPushEntrySList(&data->ThreadProvider->QueryListHead, &data->ListEntry); PhDereferenceObject(data->ThreadProvider); return STATUS_SUCCESS; }
static INT_PTR CALLBACK PhpThreadStackDlgProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam ) { switch (uMsg) { case WM_INITDIALOG: { NTSTATUS status; PTHREAD_STACK_CONTEXT threadStackContext; PPH_STRING title; HWND lvHandle; PPH_LAYOUT_MANAGER layoutManager; threadStackContext = (PTHREAD_STACK_CONTEXT)lParam; SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)threadStackContext); title = PhFormatString(L"Stack - thread %u", (ULONG)threadStackContext->ThreadId); SetWindowText(hwndDlg, title->Buffer); PhDereferenceObject(title); lvHandle = GetDlgItem(hwndDlg, IDC_LIST); PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 30, L" "); PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 300, L"Name"); PhSetListViewStyle(lvHandle, FALSE, TRUE); PhSetControlTheme(lvHandle, L"explorer"); PhLoadListViewColumnsFromSetting(L"ThreadStackListViewColumns", lvHandle); threadStackContext->ListViewHandle = lvHandle; layoutManager = PhAllocate(sizeof(PH_LAYOUT_MANAGER)); PhInitializeLayoutManager(layoutManager, hwndDlg); SetProp(hwndDlg, L"LayoutManager", (HANDLE)layoutManager); PhAddLayoutItem(layoutManager, lvHandle, NULL, PH_ANCHOR_ALL); PhAddLayoutItem(layoutManager, GetDlgItem(hwndDlg, IDC_COPY), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); PhAddLayoutItem(layoutManager, GetDlgItem(hwndDlg, IDC_REFRESH), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); PhAddLayoutItem(layoutManager, GetDlgItem(hwndDlg, IDOK), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); if (MinimumSize.left == -1) { RECT rect; rect.left = 0; rect.top = 0; rect.right = 190; rect.bottom = 120; MapDialogRect(hwndDlg, &rect); MinimumSize = rect; MinimumSize.left = 0; } PhLoadWindowPlacementFromSetting(NULL, L"ThreadStackWindowSize", hwndDlg); PhCenterWindow(hwndDlg, GetParent(hwndDlg)); if (PhPluginsEnabled) { PH_PLUGIN_THREAD_STACK_CONTROL control; control.Type = PluginThreadStackInitializing; control.UniqueKey = threadStackContext; control.u.Initializing.ProcessId = threadStackContext->ProcessId; control.u.Initializing.ThreadId = threadStackContext->ThreadId; control.u.Initializing.ThreadHandle = threadStackContext->ThreadHandle; control.u.Initializing.SymbolProvider = threadStackContext->SymbolProvider; control.u.Initializing.CustomWalk = FALSE; PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackThreadStackControl), &control); threadStackContext->CustomWalk = control.u.Initializing.CustomWalk; } status = PhpRefreshThreadStack(hwndDlg, threadStackContext); if (status == STATUS_ABANDONED) EndDialog(hwndDlg, IDCANCEL); else if (!NT_SUCCESS(status)) PhShowStatus(hwndDlg, L"Unable to load the stack", status, 0); } break; case WM_DESTROY: { PPH_LAYOUT_MANAGER layoutManager; PTHREAD_STACK_CONTEXT threadStackContext; ULONG i; layoutManager = (PPH_LAYOUT_MANAGER)GetProp(hwndDlg, L"LayoutManager"); PhDeleteLayoutManager(layoutManager); PhFree(layoutManager); threadStackContext = (PTHREAD_STACK_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); if (PhPluginsEnabled) { PH_PLUGIN_THREAD_STACK_CONTROL control; control.Type = PluginThreadStackUninitializing; control.UniqueKey = threadStackContext; PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackThreadStackControl), &control); } for (i = 0; i < threadStackContext->List->Count; i++) PhpFreeThreadStackItem(threadStackContext->List->Items[i]); PhSaveListViewColumnsToSetting(L"ThreadStackListViewColumns", GetDlgItem(hwndDlg, IDC_LIST)); PhSaveWindowPlacementToSetting(NULL, L"ThreadStackWindowSize", hwndDlg); RemoveProp(hwndDlg, PhMakeContextAtom()); RemoveProp(hwndDlg, L"LayoutManager"); } break; case WM_COMMAND: { INT id = LOWORD(wParam); switch (id) { case IDCANCEL: // Esc and X button to close case IDOK: EndDialog(hwndDlg, IDOK); break; case IDC_REFRESH: { NTSTATUS status; if (!NT_SUCCESS(status = PhpRefreshThreadStack( hwndDlg, (PTHREAD_STACK_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()) ))) { PhShowStatus(hwndDlg, L"Unable to load the stack", status, 0); } } break; case IDC_COPY: { HWND lvHandle; lvHandle = GetDlgItem(hwndDlg, IDC_LIST); if (ListView_GetSelectedCount(lvHandle) == 0) PhSetStateAllListViewItems(lvHandle, LVIS_SELECTED, LVIS_SELECTED); PhCopyListView(lvHandle); SetFocus(lvHandle); } break; } } break; case WM_NOTIFY: { LPNMHDR header = (LPNMHDR)lParam; switch (header->code) { case LVN_GETINFOTIP: { LPNMLVGETINFOTIP getInfoTip = (LPNMLVGETINFOTIP)header; HWND lvHandle; PTHREAD_STACK_CONTEXT threadStackContext; lvHandle = GetDlgItem(hwndDlg, IDC_LIST); threadStackContext = (PTHREAD_STACK_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); if (header->hwndFrom == lvHandle) { PTHREAD_STACK_ITEM stackItem; PPH_THREAD_STACK_FRAME stackFrame; if (PhGetListViewItemParam(lvHandle, getInfoTip->iItem, &stackItem)) { PH_STRING_BUILDER stringBuilder; PPH_STRING fileName; PH_SYMBOL_LINE_INFORMATION lineInfo; stackFrame = &stackItem->StackFrame; PhInitializeStringBuilder(&stringBuilder, 40); // There are no params for kernel-mode stack traces. if ((ULONG_PTR)stackFrame->PcAddress <= PhSystemBasicInformation.MaximumUserModeAddress) { PhAppendFormatStringBuilder( &stringBuilder, L"Parameters: 0x%Ix, 0x%Ix, 0x%Ix, 0x%Ix\n", stackFrame->Params[0], stackFrame->Params[1], stackFrame->Params[2], stackFrame->Params[3] ); } if (PhGetLineFromAddress( threadStackContext->SymbolProvider, (ULONG64)stackFrame->PcAddress, &fileName, NULL, &lineInfo )) { PhAppendFormatStringBuilder( &stringBuilder, L"File: %s: line %u\n", fileName->Buffer, lineInfo.LineNumber ); PhDereferenceObject(fileName); } if (stringBuilder.String->Length != 0) PhRemoveStringBuilder(&stringBuilder, stringBuilder.String->Length / 2 - 1, 1); if (PhPluginsEnabled) { PH_PLUGIN_THREAD_STACK_CONTROL control; control.Type = PluginThreadStackGetTooltip; control.UniqueKey = threadStackContext; control.u.GetTooltip.StackFrame = stackFrame; control.u.GetTooltip.StringBuilder = &stringBuilder; PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackThreadStackControl), &control); } PhCopyListViewInfoTip(getInfoTip, &stringBuilder.String->sr); PhDeleteStringBuilder(&stringBuilder); } } } break; } } break; case WM_SIZE: { PPH_LAYOUT_MANAGER layoutManager; layoutManager = (PPH_LAYOUT_MANAGER)GetProp(hwndDlg, L"LayoutManager"); PhLayoutManagerLayout(layoutManager); } break; case WM_SIZING: { PhResizingMinimumSize((PRECT)lParam, wParam, MinimumSize.right, MinimumSize.bottom); } break; } return FALSE; }
BOOLEAN PhCmForwardMessage( _In_ HWND hwnd, _In_ PH_TREENEW_MESSAGE Message, _In_opt_ PVOID Parameter1, _In_opt_ PVOID Parameter2, _In_ PPH_CM_MANAGER Manager ) { PH_PLUGIN_TREENEW_MESSAGE pluginMessage; PPH_PLUGIN plugin; if (Message == TreeNewDestroying) return FALSE; switch (Message) { case TreeNewGetCellText: { PPH_TREENEW_GET_CELL_TEXT getCellText = Parameter1; PH_TREENEW_COLUMN tnColumn; PPH_CM_COLUMN column; if (getCellText->Id < Manager->MinId) return FALSE; if (!TreeNew_GetColumn(hwnd, getCellText->Id, &tnColumn)) return FALSE; column = tnColumn.Context; pluginMessage.SubId = column->SubId; pluginMessage.Context = column->Context; plugin = column->Plugin; } break; case TreeNewCustomDraw: { PPH_TREENEW_CUSTOM_DRAW customDraw = Parameter1; PPH_CM_COLUMN column; if (customDraw->Column->Id < Manager->MinId) return FALSE; column = customDraw->Column->Context; pluginMessage.SubId = column->SubId; pluginMessage.Context = column->Context; plugin = column->Plugin; } break; case TreeNewColumnResized: { PPH_TREENEW_COLUMN tlColumn = Parameter1; PPH_CM_COLUMN column; if (tlColumn->Id < Manager->MinId) return FALSE; column = tlColumn->Context; pluginMessage.SubId = column->SubId; pluginMessage.Context = column->Context; plugin = column->Plugin; } break; default: { // Some plugins want to be notified about all messages. if (Manager->NotifyList) { ULONG i; for (i = 0; i < Manager->NotifyList->Count; i++) { plugin = Manager->NotifyList->Items[i]; pluginMessage.TreeNewHandle = hwnd; pluginMessage.Message = Message; pluginMessage.Parameter1 = Parameter1; pluginMessage.Parameter2 = Parameter2; pluginMessage.SubId = 0; pluginMessage.Context = NULL; PhInvokeCallback(PhGetPluginCallback(plugin, PluginCallbackTreeNewMessage), &pluginMessage); } } } return FALSE; } pluginMessage.TreeNewHandle = hwnd; pluginMessage.Message = Message; pluginMessage.Parameter1 = Parameter1; pluginMessage.Parameter2 = Parameter2; PhInvokeCallback(PhGetPluginCallback(plugin, PluginCallbackTreeNewMessage), &pluginMessage); return TRUE; }
VOID PhShowHandleProperties( _In_ HWND ParentWindowHandle, _In_ HANDLE ProcessId, _In_ PPH_HANDLE_ITEM HandleItem ) { PROPSHEETHEADER propSheetHeader = { sizeof(propSheetHeader) }; PROPSHEETPAGE propSheetPage; HPROPSHEETPAGE pages[16]; HANDLE_PROPERTIES_CONTEXT context; PH_STD_OBJECT_SECURITY stdObjectSecurity; PPH_ACCESS_ENTRY accessEntries; ULONG numberOfAccessEntries; context.ProcessId = ProcessId; context.HandleItem = HandleItem; propSheetHeader.dwFlags = PSH_NOAPPLYNOW | PSH_NOCONTEXTHELP | PSH_PROPTITLE; propSheetHeader.hwndParent = ParentWindowHandle; propSheetHeader.pszCaption = L"Handle"; propSheetHeader.nPages = 0; propSheetHeader.nStartPage = 0; propSheetHeader.phpage = pages; // General page memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); propSheetPage.dwSize = sizeof(PROPSHEETPAGE); propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_HNDLGENERAL); propSheetPage.pfnDlgProc = PhpHandleGeneralDlgProc; propSheetPage.lParam = (LPARAM)&context; pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); // Object-specific page if (!HandleItem->TypeName) { // Dummy } else if (PhEqualString2(HandleItem->TypeName, L"Event", TRUE)) { pages[propSheetHeader.nPages++] = PhCreateEventPage( PhpDuplicateHandleFromProcess, &context ); } else if (PhEqualString2(HandleItem->TypeName, L"EventPair", TRUE)) { pages[propSheetHeader.nPages++] = PhCreateEventPairPage( PhpDuplicateHandleFromProcess, &context ); } else if (PhEqualString2(HandleItem->TypeName, L"Job", TRUE)) { pages[propSheetHeader.nPages++] = PhCreateJobPage( PhpDuplicateHandleFromProcess, &context, NULL ); } else if (PhEqualString2(HandleItem->TypeName, L"Mutant", TRUE)) { pages[propSheetHeader.nPages++] = PhCreateMutantPage( PhpDuplicateHandleFromProcess, &context ); } else if (PhEqualString2(HandleItem->TypeName, L"Section", TRUE)) { pages[propSheetHeader.nPages++] = PhCreateSectionPage( PhpDuplicateHandleFromProcess, &context ); } else if (PhEqualString2(HandleItem->TypeName, L"Semaphore", TRUE)) { pages[propSheetHeader.nPages++] = PhCreateSemaphorePage( PhpDuplicateHandleFromProcess, &context ); } else if (PhEqualString2(HandleItem->TypeName, L"Timer", TRUE)) { pages[propSheetHeader.nPages++] = PhCreateTimerPage( PhpDuplicateHandleFromProcess, &context ); } else if (PhEqualString2(HandleItem->TypeName, L"Token", TRUE)) { pages[propSheetHeader.nPages++] = PhCreateTokenPage( PhpDuplicateHandleFromProcess, &context, NULL ); } // Security page stdObjectSecurity.OpenObject = PhpDuplicateHandleFromProcess; stdObjectSecurity.ObjectType = HandleItem->TypeName->Buffer; stdObjectSecurity.Context = &context; if (PhGetAccessEntries(HandleItem->TypeName->Buffer, &accessEntries, &numberOfAccessEntries)) { pages[propSheetHeader.nPages++] = PhCreateSecurityPage( PhGetStringOrEmpty(HandleItem->BestObjectName), PhStdGetObjectSecurity, PhStdSetObjectSecurity, &stdObjectSecurity, accessEntries, numberOfAccessEntries ); PhFree(accessEntries); } if (PhPluginsEnabled) { PH_PLUGIN_OBJECT_PROPERTIES objectProperties; PH_PLUGIN_HANDLE_PROPERTIES_CONTEXT propertiesContext; propertiesContext.ProcessId = ProcessId; propertiesContext.HandleItem = HandleItem; objectProperties.Parameter = &propertiesContext; objectProperties.NumberOfPages = propSheetHeader.nPages; objectProperties.MaximumNumberOfPages = sizeof(pages) / sizeof(HPROPSHEETPAGE); objectProperties.Pages = pages; PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackHandlePropertiesInitializing), &objectProperties); propSheetHeader.nPages = objectProperties.NumberOfPages; } PhModalPropertySheet(&propSheetHeader); }
PPH_STRING PhGetProcessTooltipText( __in PPH_PROCESS_ITEM Process ) { PH_STRING_BUILDER stringBuilder; PPH_STRING tempString; PhInitializeStringBuilder(&stringBuilder, 200); // Command line if (Process->CommandLine) { PhAppendStringBuilder(&stringBuilder, Process->CommandLine); PhAppendCharStringBuilder(&stringBuilder, '\n'); } // File information tempString = PhFormatImageVersionInfo( Process->FileName, &Process->VersionInfo, L" ", 0 ); if (!PhIsNullOrEmptyString(tempString)) { PhAppendStringBuilder2(&stringBuilder, L"File:\n"); PhAppendStringBuilder(&stringBuilder, tempString); PhAppendCharStringBuilder(&stringBuilder, '\n'); } if (tempString) PhDereferenceObject(tempString); // Known command line information if (Process->CommandLine && Process->QueryHandle) { PH_KNOWN_PROCESS_TYPE knownProcessType; PH_KNOWN_PROCESS_COMMAND_LINE knownCommandLine; if (NT_SUCCESS(PhGetProcessKnownType( Process->QueryHandle, &knownProcessType )) && PhaGetProcessKnownCommandLine( Process->CommandLine, knownProcessType, &knownCommandLine )) { switch (knownProcessType & KnownProcessTypeMask) { case ServiceHostProcessType: PhAppendStringBuilder2(&stringBuilder, L"Service group name:\n "); PhAppendStringBuilder(&stringBuilder, knownCommandLine.ServiceHost.GroupName); PhAppendCharStringBuilder(&stringBuilder, '\n'); break; case RunDllAsAppProcessType: { PH_IMAGE_VERSION_INFO versionInfo; if (PhInitializeImageVersionInfo( &versionInfo, knownCommandLine.RunDllAsApp.FileName->Buffer )) { tempString = PhFormatImageVersionInfo( knownCommandLine.RunDllAsApp.FileName, &versionInfo, L" ", 0 ); if (!PhIsNullOrEmptyString(tempString)) { PhAppendStringBuilder2(&stringBuilder, L"Run DLL target file:\n"); PhAppendStringBuilder(&stringBuilder, tempString); PhAppendCharStringBuilder(&stringBuilder, '\n'); } if (tempString) PhDereferenceObject(tempString); PhDeleteImageVersionInfo(&versionInfo); } } break; case ComSurrogateProcessType: { PH_IMAGE_VERSION_INFO versionInfo; PPH_STRING guidString; PhAppendStringBuilder2(&stringBuilder, L"COM target:\n"); if (knownCommandLine.ComSurrogate.Name) { PhAppendStringBuilder2(&stringBuilder, L" "); PhAppendStringBuilder(&stringBuilder, knownCommandLine.ComSurrogate.Name); PhAppendCharStringBuilder(&stringBuilder, '\n'); } if (guidString = PhFormatGuid(&knownCommandLine.ComSurrogate.Guid)) { PhAppendStringBuilder2(&stringBuilder, L" "); PhAppendStringBuilder(&stringBuilder, guidString); PhDereferenceObject(guidString); PhAppendCharStringBuilder(&stringBuilder, '\n'); } if (knownCommandLine.ComSurrogate.FileName && PhInitializeImageVersionInfo( &versionInfo, knownCommandLine.ComSurrogate.FileName->Buffer )) { tempString = PhFormatImageVersionInfo( knownCommandLine.ComSurrogate.FileName, &versionInfo, L" ", 0 ); if (!PhIsNullOrEmptyString(tempString)) { PhAppendStringBuilder2(&stringBuilder, L"COM target file:\n"); PhAppendStringBuilder(&stringBuilder, tempString); PhAppendCharStringBuilder(&stringBuilder, '\n'); } if (tempString) PhDereferenceObject(tempString); PhDeleteImageVersionInfo(&versionInfo); } } break; } } } // Services if (Process->ServiceList && Process->ServiceList->Count != 0) { ULONG enumerationKey = 0; PPH_SERVICE_ITEM serviceItem; PPH_LIST serviceList; ULONG i; // Copy the service list into our own list so we can sort it. serviceList = PhCreateList(Process->ServiceList->Count); PhAcquireQueuedLockShared(&Process->ServiceListLock); while (PhEnumPointerList( Process->ServiceList, &enumerationKey, &serviceItem )) { PhReferenceObject(serviceItem); PhAddItemList(serviceList, serviceItem); } PhReleaseQueuedLockShared(&Process->ServiceListLock); qsort(serviceList->Items, serviceList->Count, sizeof(PPH_SERVICE_ITEM), ServiceForTooltipCompare); PhAppendStringBuilder2(&stringBuilder, L"Services:\n"); // Add the services. for (i = 0; i < serviceList->Count; i++) { serviceItem = serviceList->Items[i]; PhAppendStringBuilder2(&stringBuilder, L" "); PhAppendStringBuilder(&stringBuilder, serviceItem->Name); PhAppendStringBuilder2(&stringBuilder, L" ("); PhAppendStringBuilder(&stringBuilder, serviceItem->DisplayName); PhAppendStringBuilder2(&stringBuilder, L")\n"); } PhDereferenceObjects(serviceList->Items, serviceList->Count); PhDereferenceObject(serviceList); } // Tasks if (PhEqualString2(Process->ProcessName, L"taskeng.exe", TRUE) || PhEqualString2(Process->ProcessName, L"taskhost.exe", TRUE)) { PH_STRING_BUILDER tasks; PhInitializeStringBuilder(&tasks, 40); PhpFillRunningTasks(Process, &tasks); if (tasks.String->Length != 0) { PhAppendStringBuilder2(&stringBuilder, L"Tasks:\n"); PhAppendStringBuilder(&stringBuilder, tasks.String); } PhDeleteStringBuilder(&tasks); } // Plugin if (PhPluginsEnabled) { PH_PLUGIN_GET_TOOLTIP_TEXT getTooltipText; getTooltipText.Parameter = Process; getTooltipText.StringBuilder = &stringBuilder; PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackGetProcessTooltipText), &getTooltipText); } // Notes { PH_STRING_BUILDER notes; PhInitializeStringBuilder(¬es, 40); if (Process->FileName) { if (Process->VerifyResult == VrTrusted) { if (!PhIsNullOrEmptyString(Process->VerifySignerName)) PhAppendFormatStringBuilder(¬es, L" Signer: %s\n", Process->VerifySignerName->Buffer); else PhAppendStringBuilder2(¬es, L" Signed.\n"); } else if (Process->VerifyResult == VrUnknown) { // Nothing } else if (Process->VerifyResult != VrNoSignature) { PhAppendStringBuilder2(¬es, L" Signature invalid.\n"); } } if (Process->IsPacked) { PhAppendFormatStringBuilder( ¬es, L" Image is probably packed (%u imports over %u modules).\n", Process->ImportFunctions, Process->ImportModules ); } if (Process->ConsoleHostProcessId) { CLIENT_ID clientId; PPH_STRING clientIdString; clientId.UniqueProcess = Process->ConsoleHostProcessId; clientId.UniqueThread = NULL; clientIdString = PhGetClientIdName(&clientId); PhAppendFormatStringBuilder(¬es, L" Console host: %s\n", clientIdString->Buffer); PhDereferenceObject(clientIdString); } if (Process->IsDotNet) PhAppendStringBuilder2(¬es, L" Process is managed (.NET).\n"); if (Process->IsElevated) PhAppendStringBuilder2(¬es, L" Process is elevated.\n"); if (Process->IsInJob) PhAppendStringBuilder2(¬es, L" Process is in a job.\n"); if (Process->IsPosix) PhAppendStringBuilder2(¬es, L" Process is POSIX.\n"); if (Process->IsWow64) PhAppendStringBuilder2(¬es, L" Process is 32-bit (WOW64).\n"); if (notes.String->Length != 0) { PhAppendStringBuilder2(&stringBuilder, L"Notes:\n"); PhAppendStringBuilder(&stringBuilder, notes.String); } PhDeleteStringBuilder(¬es); } // Remove the trailing newline. if (stringBuilder.String->Length != 0) PhRemoveStringBuilder(&stringBuilder, stringBuilder.String->Length / 2 - 1, 1); return PhFinalStringBuilderString(&stringBuilder); }
INT_PTR CALLBACK PhpPluginsDlgProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam ) { switch (uMsg) { case WM_INITDIALOG: { PPH_AVL_LINKS links; PhCenterWindow(hwndDlg, PhMainWndHandle); PluginsLv = GetDlgItem(hwndDlg, IDC_LIST); PhSetListViewStyle(PluginsLv, FALSE, TRUE); PhSetControlTheme(PluginsLv, L"explorer"); PhAddListViewColumn(PluginsLv, 0, 0, 0, LVCFMT_LEFT, 280, L"Name"); PhAddListViewColumn(PluginsLv, 1, 1, 1, LVCFMT_LEFT, 100, L"Author"); PhSetExtendedListView(PluginsLv); ExtendedListView_SetItemColorFunction(PluginsLv, PhpPluginColorFunction); DisabledPluginLookup = PhCreateSimpleHashtable(10); for (links = PhMinimumElementAvlTree(&PhPluginsByName); links; links = PhSuccessorElementAvlTree(links)) { PPH_PLUGIN plugin = CONTAINING_RECORD(links, PH_PLUGIN, Links); INT lvItemIndex; PH_STRINGREF baseNameSr; lvItemIndex = PhAddListViewItem(PluginsLv, MAXINT, plugin->Information.DisplayName ? plugin->Information.DisplayName : plugin->Name.Buffer, plugin); if (plugin->Information.Author) PhSetListViewSubItem(PluginsLv, lvItemIndex, 1, plugin->Information.Author); PhInitializeStringRefLongHint(&baseNameSr, PhpGetPluginBaseName(plugin)); if (PhIsPluginDisabled(&baseNameSr)) PhAddItemSimpleHashtable(DisabledPluginLookup, plugin, NULL); } DisabledPluginInstances = PhCreateList(10); PhpAddDisabledPlugins(); ExtendedListView_SortItems(PluginsLv); SelectedPlugin = NULL; PhpRefreshPluginDetails(hwndDlg); } break; case WM_DESTROY: { ULONG i; for (i = 0; i < DisabledPluginInstances->Count; i++) PhpFreeDisabledPlugin(DisabledPluginInstances->Items[i]); PhDereferenceObject(DisabledPluginInstances); PhDereferenceObject(DisabledPluginLookup); } break; case WM_COMMAND: { switch (LOWORD(wParam)) { case IDCANCEL: case IDOK: EndDialog(hwndDlg, IDOK); break; case IDC_DISABLE: { if (SelectedPlugin) { PWSTR baseName; PH_STRINGREF baseNameRef; BOOLEAN newDisabledState; baseName = PhpGetPluginBaseName(SelectedPlugin); PhInitializeStringRef(&baseNameRef, baseName); newDisabledState = !PhIsPluginDisabled(&baseNameRef); PhSetPluginDisabled(&baseNameRef, newDisabledState); PhpUpdateDisabledPlugin(hwndDlg, PhFindListViewItemByFlags(PluginsLv, -1, LVNI_SELECTED), SelectedPlugin, newDisabledState); SetDlgItemText(hwndDlg, IDC_DISABLE, PhpGetPluginDisableButtonText(baseName)); } } break; case IDC_OPTIONS: { if (SelectedPlugin && IS_PLUGIN_LOADED(SelectedPlugin)) { PhInvokeCallback(PhGetPluginCallback(SelectedPlugin, PluginCallbackShowOptions), hwndDlg); } } break; case IDC_CLEANUP: { if (PhShowMessage(hwndDlg, MB_ICONQUESTION | MB_YESNO, L"Do you want to clean up unused plugin settings?") == IDYES) { PhClearIgnoredSettings(); } } break; case IDC_OPENURL: { NOTHING; } break; } } break; case WM_NOTIFY: { LPNMHDR header = (LPNMHDR)lParam; switch (header->code) { case LVN_ITEMCHANGED: { if (header->hwndFrom == PluginsLv) { if (ListView_GetSelectedCount(PluginsLv) == 1) SelectedPlugin = PhGetSelectedListViewItemParam(PluginsLv); else SelectedPlugin = NULL; PhpRefreshPluginDetails(hwndDlg); } } break; case NM_CLICK: { if (header->hwndFrom == GetDlgItem(hwndDlg, IDC_OPENURL)) { if (SelectedPlugin && IS_PLUGIN_LOADED(SelectedPlugin)) PhShellExecute(hwndDlg, SelectedPlugin->Information.Url, NULL); } } break; case NM_DBLCLK: { if (header->hwndFrom == PluginsLv) { if (SelectedPlugin && IS_PLUGIN_LOADED(SelectedPlugin)) { PhInvokeCallback(PhGetPluginCallback(SelectedPlugin, PluginCallbackShowOptions), hwndDlg); } } } break; } } break; } REFLECT_MESSAGE_DLG(hwndDlg, PluginsLv, uMsg, wParam, lParam); return FALSE; }
VOID PhpThreadProviderUpdate( __in PPH_THREAD_PROVIDER ThreadProvider, __in PVOID ProcessInformation ) { PPH_THREAD_PROVIDER threadProvider = ThreadProvider; PSYSTEM_PROCESS_INFORMATION process; SYSTEM_PROCESS_INFORMATION localProcess; PSYSTEM_THREAD_INFORMATION threads; ULONG numberOfThreads; ULONG i; process = PhFindProcessInformation(ProcessInformation, threadProvider->ProcessId); if (!process) { // The process doesn't exist anymore. Pretend it does but // has no threads. process = &localProcess; process->NumberOfThreads = 0; } threads = process->Threads; numberOfThreads = process->NumberOfThreads; // System Idle Process has one thread per CPU. // They all have a TID of 0, but we can't have // multiple TIDs, so we'll assign unique TIDs. if (threadProvider->ProcessId == SYSTEM_IDLE_PROCESS_ID) { for (i = 0; i < numberOfThreads; i++) { threads[i].ClientId.UniqueThread = (HANDLE)i; } } // Look for dead threads. { PPH_LIST threadsToRemove = NULL; ULONG enumerationKey = 0; PPH_THREAD_ITEM *threadItem; while (PhEnumHashtable(threadProvider->ThreadHashtable, (PPVOID)&threadItem, &enumerationKey)) { BOOLEAN found = FALSE; // Check if the thread still exists. for (i = 0; i < numberOfThreads; i++) { PSYSTEM_THREAD_INFORMATION thread = &threads[i]; if ((*threadItem)->ThreadId == thread->ClientId.UniqueThread) { found = TRUE; break; } } if (!found) { // Raise the thread removed event. PhInvokeCallback(&threadProvider->ThreadRemovedEvent, *threadItem); if (!threadsToRemove) threadsToRemove = PhCreateList(2); PhAddItemList(threadsToRemove, *threadItem); } } if (threadsToRemove) { PhAcquireFastLockExclusive(&threadProvider->ThreadHashtableLock); for (i = 0; i < threadsToRemove->Count; i++) { PhpRemoveThreadItem( threadProvider, (PPH_THREAD_ITEM)threadsToRemove->Items[i] ); } PhReleaseFastLockExclusive(&threadProvider->ThreadHashtableLock); PhDereferenceObject(threadsToRemove); } } // Go through the queued thread query data. { PSLIST_ENTRY entry; PPH_THREAD_QUERY_DATA data; entry = RtlInterlockedFlushSList(&threadProvider->QueryListHead); while (entry) { data = CONTAINING_RECORD(entry, PH_THREAD_QUERY_DATA, ListEntry); entry = entry->Next; if (data->StartAddressResolveLevel == PhsrlFunction && data->StartAddressString) { PhSwapReference(&data->ThreadItem->StartAddressString, data->StartAddressString); data->ThreadItem->StartAddressResolveLevel = data->StartAddressResolveLevel; } PhSwapReference2(&data->ThreadItem->ServiceName, data->ServiceName); data->ThreadItem->JustResolved = TRUE; if (data->StartAddressString) PhDereferenceObject(data->StartAddressString); PhDereferenceObject(data->ThreadItem); PhFree(data); } } // Look for new threads and update existing ones. for (i = 0; i < numberOfThreads; i++) { PSYSTEM_THREAD_INFORMATION thread = &threads[i]; PPH_THREAD_ITEM threadItem; threadItem = PhReferenceThreadItem(threadProvider, thread->ClientId.UniqueThread); if (!threadItem) { ULONG64 cycles; PVOID startAddress = NULL; threadItem = PhCreateThreadItem(thread->ClientId.UniqueThread); threadItem->CreateTime = thread->CreateTime; threadItem->KernelTime = thread->KernelTime; threadItem->UserTime = thread->UserTime; PhUpdateDelta(&threadItem->ContextSwitchesDelta, thread->ContextSwitches); threadItem->Priority = thread->Priority; threadItem->BasePriority = thread->BasePriority; threadItem->State = (KTHREAD_STATE)thread->ThreadState; threadItem->WaitReason = thread->WaitReason; // Try to open a handle to the thread. if (!NT_SUCCESS(PhOpenThread( &threadItem->ThreadHandle, THREAD_QUERY_INFORMATION, threadItem->ThreadId ))) { PhOpenThread( &threadItem->ThreadHandle, ThreadQueryAccess, threadItem->ThreadId ); } // Get the cycle count. if (NT_SUCCESS(PhpGetThreadCycleTime( threadProvider, threadItem, &cycles ))) { PhUpdateDelta(&threadItem->CyclesDelta, cycles); } // Initialize the CPU time deltas. PhUpdateDelta(&threadItem->CpuKernelDelta, threadItem->KernelTime.QuadPart); PhUpdateDelta(&threadItem->CpuUserDelta, threadItem->UserTime.QuadPart); // Try to get the start address. if (threadItem->ThreadHandle) { NtQueryInformationThread( threadItem->ThreadHandle, ThreadQuerySetWin32StartAddress, &startAddress, sizeof(PVOID), NULL ); } if (!startAddress) startAddress = thread->StartAddress; threadItem->StartAddress = (ULONG64)startAddress; // Get the Win32 priority. threadItem->PriorityWin32 = GetThreadPriority(threadItem->ThreadHandle); if (PhTestEvent(&threadProvider->SymbolsLoadedEvent)) { threadItem->StartAddressString = PhpGetThreadBasicStartAddress( threadProvider, threadItem->StartAddress, &threadItem->StartAddressResolveLevel ); } if (!threadItem->StartAddressString) { threadItem->StartAddressResolveLevel = PhsrlAddress; threadItem->StartAddressString = PhCreateStringEx(NULL, PH_PTR_STR_LEN * 2); PhPrintPointer( threadItem->StartAddressString->Buffer, (PVOID)threadItem->StartAddress ); PhTrimToNullTerminatorString(threadItem->StartAddressString); } PhpQueueThreadQuery(threadProvider, threadItem); // Is it a GUI thread? if (threadItem->ThreadHandle && KphIsConnected()) { PVOID win32Thread; if (NT_SUCCESS(KphQueryInformationThread( threadItem->ThreadHandle, KphThreadWin32Thread, &win32Thread, sizeof(PVOID), NULL ))) { threadItem->IsGuiThread = win32Thread != NULL; } } // Add the thread item to the hashtable. PhAcquireFastLockExclusive(&threadProvider->ThreadHashtableLock); PhAddEntryHashtable(threadProvider->ThreadHashtable, &threadItem); PhReleaseFastLockExclusive(&threadProvider->ThreadHashtableLock); // Raise the thread added event. PhInvokeCallback(&threadProvider->ThreadAddedEvent, threadItem); } else { BOOLEAN modified = FALSE; if (threadItem->JustResolved) modified = TRUE; threadItem->KernelTime = thread->KernelTime; threadItem->UserTime = thread->UserTime; threadItem->Priority = thread->Priority; threadItem->BasePriority = thread->BasePriority; threadItem->State = (KTHREAD_STATE)thread->ThreadState; if (threadItem->WaitReason != thread->WaitReason) { threadItem->WaitReason = thread->WaitReason; modified = TRUE; } // If the resolve level is only at address, it probably // means symbols weren't loaded the last time we // tried to get the start address. Try again. if (threadItem->StartAddressResolveLevel == PhsrlAddress) { if (PhTestEvent(&threadProvider->SymbolsLoadedEvent)) { PPH_STRING newStartAddressString; newStartAddressString = PhpGetThreadBasicStartAddress( threadProvider, threadItem->StartAddress, &threadItem->StartAddressResolveLevel ); PhSwapReference2( &threadItem->StartAddressString, newStartAddressString ); modified = TRUE; } } // If we couldn't resolve the start address to a // module+offset, use the StartAddress instead // of the Win32StartAddress and try again. // Note that we check the resolve level again // because we may have changed it in the previous // block. if ( threadItem->JustResolved && threadItem->StartAddressResolveLevel == PhsrlAddress ) { if (threadItem->StartAddress != (ULONG64)thread->StartAddress) { threadItem->StartAddress = (ULONG64)thread->StartAddress; PhpQueueThreadQuery(threadProvider, threadItem); } } // Update the context switch count. { ULONG oldDelta; oldDelta = threadItem->ContextSwitchesDelta.Delta; PhUpdateDelta(&threadItem->ContextSwitchesDelta, thread->ContextSwitches); if (threadItem->ContextSwitchesDelta.Delta != oldDelta) { modified = TRUE; } } // Update the cycle count. { ULONG64 cycles; ULONG64 oldDelta; oldDelta = threadItem->CyclesDelta.Delta; if (NT_SUCCESS(PhpGetThreadCycleTime( threadProvider, threadItem, &cycles ))) { PhUpdateDelta(&threadItem->CyclesDelta, cycles); if (threadItem->CyclesDelta.Delta != oldDelta) { modified = TRUE; } } } // Update the CPU time deltas. PhUpdateDelta(&threadItem->CpuKernelDelta, threadItem->KernelTime.QuadPart); PhUpdateDelta(&threadItem->CpuUserDelta, threadItem->UserTime.QuadPart); // Update the CPU usage. // If the cycle time isn't available, we'll fall back to using the CPU time. if (PhEnableCycleCpuUsage && (threadProvider->ProcessId == SYSTEM_IDLE_PROCESS_ID || threadItem->ThreadHandle)) { threadItem->CpuUsage = (FLOAT)threadItem->CyclesDelta.Delta / PhCpuTotalCycleDelta; } else { threadItem->CpuUsage = (FLOAT)(threadItem->CpuKernelDelta.Delta + threadItem->CpuUserDelta.Delta) / (PhCpuKernelDelta.Delta + PhCpuUserDelta.Delta + PhCpuIdleDelta.Delta); } // Update the Win32 priority. { LONG oldPriorityWin32 = threadItem->PriorityWin32; threadItem->PriorityWin32 = GetThreadPriority(threadItem->ThreadHandle); if (threadItem->PriorityWin32 != oldPriorityWin32) { modified = TRUE; } } // Update the GUI thread status. if (threadItem->ThreadHandle && KphIsConnected()) { PVOID win32Thread; if (NT_SUCCESS(KphQueryInformationThread( threadItem->ThreadHandle, KphThreadWin32Thread, &win32Thread, sizeof(PVOID), NULL ))) { BOOLEAN oldIsGuiThread = threadItem->IsGuiThread; threadItem->IsGuiThread = win32Thread != NULL; if (threadItem->IsGuiThread != oldIsGuiThread) modified = TRUE; } } threadItem->JustResolved = FALSE; if (modified) { // Raise the thread modified event. PhInvokeCallback(&threadProvider->ThreadModifiedEvent, threadItem); } PhDereferenceObject(threadItem); } } PhInvokeCallback(&threadProvider->UpdatedEvent, NULL); threadProvider->RunId++; }
VOID PhServiceProviderUpdate( _In_ PVOID Object ) { static SC_HANDLE scManagerHandle = NULL; static ULONG runCount = 0; static PPH_HASH_ENTRY nameHashSet[256]; static PPHP_SERVICE_NAME_ENTRY nameEntries = NULL; static ULONG nameEntriesCount; static ULONG nameEntriesAllocated = 0; LPENUM_SERVICE_STATUS_PROCESS services; ULONG numberOfServices; ULONG i; PPH_HASH_ENTRY hashEntry; // We always execute the first run, and we only initialize non-polling after the first run. if (PhEnableServiceNonPoll && runCount != 0) { if (!PhpNonPollInitialized) { if (WindowsVersion >= WINDOWS_VISTA) { PhpInitializeServiceNonPoll(); } PhpNonPollInitialized = TRUE; } if (PhpNonPollActive) { if (InterlockedExchange(&PhpNonPollGate, 0) == 0) { // Non-poll gate is closed; skip all processing. goto UpdateEnd; } } } if (!scManagerHandle) { scManagerHandle = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE); if (!scManagerHandle) return; } services = PhEnumServices(scManagerHandle, 0, 0, &numberOfServices); if (!services) return; // Build a hash set containing the service names. // This has caused a massive decrease in background CPU usage, and // is certainly much better than the quadratic-time string comparisons // we were doing before (in the "Look for dead services" section). nameEntriesCount = 0; if (nameEntriesAllocated < numberOfServices) { nameEntriesAllocated = numberOfServices + 32; if (nameEntries) PhFree(nameEntries); nameEntries = PhAllocate(sizeof(PHP_SERVICE_NAME_ENTRY) * nameEntriesAllocated); } PhInitializeHashSet(nameHashSet, PH_HASH_SET_SIZE(nameHashSet)); for (i = 0; i < numberOfServices; i++) { PPHP_SERVICE_NAME_ENTRY entry; entry = &nameEntries[nameEntriesCount++]; PhInitializeStringRefLongHint(&entry->Name, services[i].lpServiceName); entry->ServiceEntry = &services[i]; PhAddEntryHashSet( nameHashSet, PH_HASH_SET_SIZE(nameHashSet), &entry->HashEntry, PhpHashServiceNameEntry(entry) ); } // Look for dead services. { PPH_LIST servicesToRemove = NULL; PH_HASHTABLE_ENUM_CONTEXT enumContext; PPH_SERVICE_ITEM *serviceItem; PhBeginEnumHashtable(PhServiceHashtable, &enumContext); while (serviceItem = PhNextEnumHashtable(&enumContext)) { BOOLEAN found = FALSE; PHP_SERVICE_NAME_ENTRY lookupNameEntry; // Check if the service still exists. lookupNameEntry.Name = (*serviceItem)->Name->sr; hashEntry = PhFindEntryHashSet( nameHashSet, PH_HASH_SET_SIZE(nameHashSet), PhpHashServiceNameEntry(&lookupNameEntry) ); for (; hashEntry; hashEntry = hashEntry->Next) { PPHP_SERVICE_NAME_ENTRY nameEntry; nameEntry = CONTAINING_RECORD(hashEntry, PHP_SERVICE_NAME_ENTRY, HashEntry); if (PhpCompareServiceNameEntry(&lookupNameEntry, nameEntry)) { found = TRUE; break; } } if (!found) { // Remove the service from its process. if ((*serviceItem)->ProcessId) { PPH_PROCESS_ITEM processItem; processItem = PhReferenceProcessItem((HANDLE)(*serviceItem)->ProcessId); if (processItem) { PhpRemoveProcessItemService(processItem, *serviceItem); PhDereferenceObject(processItem); } } // Raise the service removed event. PhInvokeCallback(&PhServiceRemovedEvent, *serviceItem); if (!servicesToRemove) servicesToRemove = PhCreateList(2); PhAddItemList(servicesToRemove, *serviceItem); } } if (servicesToRemove) { PhAcquireQueuedLockExclusive(&PhServiceHashtableLock); for (i = 0; i < servicesToRemove->Count; i++) { PhpRemoveServiceItem((PPH_SERVICE_ITEM)servicesToRemove->Items[i]); } PhReleaseQueuedLockExclusive(&PhServiceHashtableLock); PhDereferenceObject(servicesToRemove); } } // Look for new services and update existing ones. for (i = 0; i < PH_HASH_SET_SIZE(nameHashSet); i++) { for (hashEntry = nameHashSet[i]; hashEntry; hashEntry = hashEntry->Next) { PPH_SERVICE_ITEM serviceItem; PPHP_SERVICE_NAME_ENTRY nameEntry; ENUM_SERVICE_STATUS_PROCESS *serviceEntry; nameEntry = CONTAINING_RECORD(hashEntry, PHP_SERVICE_NAME_ENTRY, HashEntry); serviceEntry = nameEntry->ServiceEntry; serviceItem = PhpLookupServiceItem(&nameEntry->Name); if (!serviceItem) { // Create the service item and fill in basic information. serviceItem = PhCreateServiceItem(serviceEntry); PhpUpdateServiceItemConfig(scManagerHandle, serviceItem); // Add the service to its process, if appropriate. if ( ( serviceItem->State == SERVICE_RUNNING || serviceItem->State == SERVICE_PAUSED ) && serviceItem->ProcessId ) { PPH_PROCESS_ITEM processItem; if (processItem = PhReferenceProcessItem(serviceItem->ProcessId)) { PhpAddProcessItemService(processItem, serviceItem); PhDereferenceObject(processItem); } else { // The process doesn't exist yet (to us). Set the pending // flag and when the process is added this will be // fixed. serviceItem->PendingProcess = TRUE; } } // Add the service item to the hashtable. PhAcquireQueuedLockExclusive(&PhServiceHashtableLock); PhAddEntryHashtable(PhServiceHashtable, &serviceItem); PhReleaseQueuedLockExclusive(&PhServiceHashtableLock); // Raise the service added event. PhInvokeCallback(&PhServiceAddedEvent, serviceItem); } else { if ( serviceItem->Type != serviceEntry->ServiceStatusProcess.dwServiceType || serviceItem->State != serviceEntry->ServiceStatusProcess.dwCurrentState || serviceItem->ControlsAccepted != serviceEntry->ServiceStatusProcess.dwControlsAccepted || serviceItem->ProcessId != UlongToHandle(serviceEntry->ServiceStatusProcess.dwProcessId) || serviceItem->NeedsConfigUpdate ) { PH_SERVICE_MODIFIED_DATA serviceModifiedData; PH_SERVICE_CHANGE serviceChange; // The service has been "modified". serviceModifiedData.Service = serviceItem; memset(&serviceModifiedData.OldService, 0, sizeof(PH_SERVICE_ITEM)); serviceModifiedData.OldService.Type = serviceItem->Type; serviceModifiedData.OldService.State = serviceItem->State; serviceModifiedData.OldService.ControlsAccepted = serviceItem->ControlsAccepted; serviceModifiedData.OldService.ProcessId = serviceItem->ProcessId; // Update the service item. serviceItem->Type = serviceEntry->ServiceStatusProcess.dwServiceType; serviceItem->State = serviceEntry->ServiceStatusProcess.dwCurrentState; serviceItem->ControlsAccepted = serviceEntry->ServiceStatusProcess.dwControlsAccepted; serviceItem->ProcessId = UlongToHandle(serviceEntry->ServiceStatusProcess.dwProcessId); if (serviceItem->ProcessId) PhPrintUInt32(serviceItem->ProcessIdString, HandleToUlong(serviceItem->ProcessId)); else serviceItem->ProcessIdString[0] = 0; // Add/remove the service from its process. serviceChange = PhGetServiceChange(&serviceModifiedData); if ( (serviceChange == ServiceStarted && serviceItem->ProcessId) || (serviceChange == ServiceStopped && serviceModifiedData.OldService.ProcessId) ) { PPH_PROCESS_ITEM processItem; if (serviceChange == ServiceStarted) processItem = PhReferenceProcessItem(serviceItem->ProcessId); else processItem = PhReferenceProcessItem(serviceModifiedData.OldService.ProcessId); if (processItem) { if (serviceChange == ServiceStarted) PhpAddProcessItemService(processItem, serviceItem); else PhpRemoveProcessItemService(processItem, serviceItem); PhDereferenceObject(processItem); } else { if (serviceChange == ServiceStarted) serviceItem->PendingProcess = TRUE; else serviceItem->PendingProcess = FALSE; } } else if ( serviceItem->State == SERVICE_RUNNING && serviceItem->ProcessId != serviceModifiedData.OldService.ProcessId && serviceItem->ProcessId ) { PPH_PROCESS_ITEM processItem; // The service stopped and started, and the only change we have detected // is in the process ID. if (processItem = PhReferenceProcessItem(serviceModifiedData.OldService.ProcessId)) { PhpRemoveProcessItemService(processItem, serviceItem); PhDereferenceObject(processItem); } if (processItem = PhReferenceProcessItem(serviceItem->ProcessId)) { PhpAddProcessItemService(processItem, serviceItem); PhDereferenceObject(processItem); } else { serviceItem->PendingProcess = TRUE; } } // Do a config update if necessary. if (serviceItem->NeedsConfigUpdate) { PhpUpdateServiceItemConfig(scManagerHandle, serviceItem); serviceItem->NeedsConfigUpdate = FALSE; } // Raise the service modified event. PhInvokeCallback(&PhServiceModifiedEvent, &serviceModifiedData); } } } } PhFree(services); UpdateEnd: PhInvokeCallback(&PhServicesUpdatedEvent, NULL); runCount++; }
VOID PhShowServiceProperties( _In_ HWND ParentWindowHandle, _In_ PPH_SERVICE_ITEM ServiceItem ) { PROPSHEETHEADER propSheetHeader = { sizeof(propSheetHeader) }; PROPSHEETPAGE propSheetPage; HPROPSHEETPAGE pages[32]; SERVICE_PROPERTIES_CONTEXT context; PH_STD_OBJECT_SECURITY stdObjectSecurity; PPH_ACCESS_ENTRY accessEntries; ULONG numberOfAccessEntries; propSheetHeader.dwFlags = PSH_NOAPPLYNOW | PSH_NOCONTEXTHELP | PSH_PROPTITLE; propSheetHeader.hwndParent = ParentWindowHandle; propSheetHeader.pszCaption = ServiceItem->Name->Buffer; propSheetHeader.nPages = 0; propSheetHeader.nStartPage = 0; propSheetHeader.phpage = pages; // General memset(&context, 0, sizeof(SERVICE_PROPERTIES_CONTEXT)); context.ServiceItem = ServiceItem; context.Ready = FALSE; context.Dirty = FALSE; memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); propSheetPage.dwSize = sizeof(PROPSHEETPAGE); propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_SRVGENERAL); propSheetPage.pfnDlgProc = PhpServiceGeneralDlgProc; propSheetPage.lParam = (LPARAM)&context; pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); // Security stdObjectSecurity.OpenObject = PhpOpenService; stdObjectSecurity.ObjectType = L"Service"; stdObjectSecurity.Context = ServiceItem; if (PhGetAccessEntries(L"Service", &accessEntries, &numberOfAccessEntries)) { pages[propSheetHeader.nPages++] = PhCreateSecurityPage( ServiceItem->Name->Buffer, PhStdGetObjectSecurity, PhStdSetObjectSecurity, &stdObjectSecurity, accessEntries, numberOfAccessEntries ); PhFree(accessEntries); } if (PhPluginsEnabled) { PH_PLUGIN_OBJECT_PROPERTIES objectProperties; objectProperties.Parameter = ServiceItem; objectProperties.NumberOfPages = propSheetHeader.nPages; objectProperties.MaximumNumberOfPages = sizeof(pages) / sizeof(HPROPSHEETPAGE); objectProperties.Pages = pages; PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackServicePropertiesInitializing), &objectProperties); propSheetHeader.nPages = objectProperties.NumberOfPages; } PropertySheet(&propSheetHeader); }
PPH_STRING PhGetProcessTooltipText( _In_ PPH_PROCESS_ITEM Process, _Out_opt_ PULONG ValidToTickCount ) { PH_STRING_BUILDER stringBuilder; ULONG validForMs = 60 * 60 * 1000; // 1 hour PPH_STRING tempString; PH_KNOWN_PROCESS_TYPE knownProcessType = UnknownProcessType; PhInitializeStringBuilder(&stringBuilder, 200); // Command line if (Process->CommandLine) { tempString = PhEllipsisString(Process->CommandLine, 100 * 10); // This is necessary because the tooltip control seems to use some kind of O(n^9999) word-wrapping // algorithm. PhpAppendStringWithLineBreaks(&stringBuilder, &tempString->sr, 100, NULL); PhAppendCharStringBuilder(&stringBuilder, '\n'); PhDereferenceObject(tempString); } // File information tempString = PhFormatImageVersionInfo( Process->FileName, &Process->VersionInfo, &StandardIndent, 0 ); if (!PhIsNullOrEmptyString(tempString)) { PhAppendStringBuilder2(&stringBuilder, L"File:\n"); PhAppendStringBuilder(&stringBuilder, &tempString->sr); PhAppendCharStringBuilder(&stringBuilder, '\n'); } if (tempString) PhDereferenceObject(tempString); // Known command line information if (Process->QueryHandle) PhGetProcessKnownType(Process->QueryHandle, &knownProcessType); if (Process->CommandLine && Process->QueryHandle) { PH_KNOWN_PROCESS_COMMAND_LINE knownCommandLine; if (knownProcessType != UnknownProcessType && PhaGetProcessKnownCommandLine( Process->CommandLine, knownProcessType, &knownCommandLine )) { switch (knownProcessType & KnownProcessTypeMask) { case ServiceHostProcessType: PhAppendStringBuilder2(&stringBuilder, L"Service group name:\n "); PhAppendStringBuilder(&stringBuilder, &knownCommandLine.ServiceHost.GroupName->sr); PhAppendCharStringBuilder(&stringBuilder, '\n'); break; case RunDllAsAppProcessType: { PH_IMAGE_VERSION_INFO versionInfo; if (PhInitializeImageVersionInfo( &versionInfo, knownCommandLine.RunDllAsApp.FileName->Buffer )) { tempString = PhFormatImageVersionInfo( knownCommandLine.RunDllAsApp.FileName, &versionInfo, &StandardIndent, 0 ); if (!PhIsNullOrEmptyString(tempString)) { PhAppendStringBuilder2(&stringBuilder, L"Run DLL target file:\n"); PhAppendStringBuilder(&stringBuilder, &tempString->sr); PhAppendCharStringBuilder(&stringBuilder, '\n'); } if (tempString) PhDereferenceObject(tempString); PhDeleteImageVersionInfo(&versionInfo); } } break; case ComSurrogateProcessType: { PH_IMAGE_VERSION_INFO versionInfo; PPH_STRING guidString; PhAppendStringBuilder2(&stringBuilder, L"COM target:\n"); if (knownCommandLine.ComSurrogate.Name) { PhAppendStringBuilder(&stringBuilder, &StandardIndent); PhAppendStringBuilder(&stringBuilder, &knownCommandLine.ComSurrogate.Name->sr); PhAppendCharStringBuilder(&stringBuilder, '\n'); } if (guidString = PhFormatGuid(&knownCommandLine.ComSurrogate.Guid)) { PhAppendStringBuilder(&stringBuilder, &StandardIndent); PhAppendStringBuilder(&stringBuilder, &guidString->sr); PhDereferenceObject(guidString); PhAppendCharStringBuilder(&stringBuilder, '\n'); } if (knownCommandLine.ComSurrogate.FileName && PhInitializeImageVersionInfo( &versionInfo, knownCommandLine.ComSurrogate.FileName->Buffer )) { tempString = PhFormatImageVersionInfo( knownCommandLine.ComSurrogate.FileName, &versionInfo, &StandardIndent, 0 ); if (!PhIsNullOrEmptyString(tempString)) { PhAppendStringBuilder2(&stringBuilder, L"COM target file:\n"); PhAppendStringBuilder(&stringBuilder, &tempString->sr); PhAppendCharStringBuilder(&stringBuilder, '\n'); } if (tempString) PhDereferenceObject(tempString); PhDeleteImageVersionInfo(&versionInfo); } } break; } } } // Services if (Process->ServiceList && Process->ServiceList->Count != 0) { ULONG enumerationKey = 0; PPH_SERVICE_ITEM serviceItem; PPH_LIST serviceList; ULONG i; // Copy the service list into our own list so we can sort it. serviceList = PhCreateList(Process->ServiceList->Count); PhAcquireQueuedLockShared(&Process->ServiceListLock); while (PhEnumPointerList( Process->ServiceList, &enumerationKey, &serviceItem )) { PhReferenceObject(serviceItem); PhAddItemList(serviceList, serviceItem); } PhReleaseQueuedLockShared(&Process->ServiceListLock); qsort(serviceList->Items, serviceList->Count, sizeof(PPH_SERVICE_ITEM), ServiceForTooltipCompare); PhAppendStringBuilder2(&stringBuilder, L"Services:\n"); // Add the services. for (i = 0; i < serviceList->Count; i++) { serviceItem = serviceList->Items[i]; PhAppendStringBuilder(&stringBuilder, &StandardIndent); PhAppendStringBuilder(&stringBuilder, &serviceItem->Name->sr); PhAppendStringBuilder2(&stringBuilder, L" ("); PhAppendStringBuilder(&stringBuilder, &serviceItem->DisplayName->sr); PhAppendStringBuilder2(&stringBuilder, L")\n"); } PhDereferenceObjects(serviceList->Items, serviceList->Count); PhDereferenceObject(serviceList); } // Tasks, Drivers switch (knownProcessType & KnownProcessTypeMask) { case TaskHostProcessType: { PH_STRING_BUILDER tasks; PhInitializeStringBuilder(&tasks, 40); PhpFillRunningTasks(Process, &tasks); if (tasks.String->Length != 0) { PhAppendStringBuilder2(&stringBuilder, L"Tasks:\n"); PhAppendStringBuilder(&stringBuilder, &tasks.String->sr); } PhDeleteStringBuilder(&tasks); } break; case UmdfHostProcessType: { PH_STRING_BUILDER drivers; PhInitializeStringBuilder(&drivers, 40); PhpFillUmdfDrivers(Process, &drivers); if (drivers.String->Length != 0) { PhAppendStringBuilder2(&stringBuilder, L"Drivers:\n"); PhAppendStringBuilder(&stringBuilder, &drivers.String->sr); } PhDeleteStringBuilder(&drivers); validForMs = 10 * 1000; // 10 seconds } break; } // Plugin if (PhPluginsEnabled) { PH_PLUGIN_GET_TOOLTIP_TEXT getTooltipText; getTooltipText.Parameter = Process; getTooltipText.StringBuilder = &stringBuilder; getTooltipText.ValidForMs = validForMs; PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackGetProcessTooltipText), &getTooltipText); validForMs = getTooltipText.ValidForMs; } // Notes { PH_STRING_BUILDER notes; PhInitializeStringBuilder(¬es, 40); if (Process->FileName) { if (Process->VerifyResult == VrTrusted) { if (!PhIsNullOrEmptyString(Process->VerifySignerName)) PhAppendFormatStringBuilder(¬es, L" Signer: %s\n", Process->VerifySignerName->Buffer); else PhAppendStringBuilder2(¬es, L" Signed.\n"); } else if (Process->VerifyResult == VrUnknown) { // Nothing } else if (Process->VerifyResult != VrNoSignature) { PhAppendStringBuilder2(¬es, L" Signature invalid.\n"); } } if (Process->IsPacked) { PhAppendFormatStringBuilder( ¬es, L" Image is probably packed (%u imports over %u modules).\n", Process->ImportFunctions, Process->ImportModules ); } if ((ULONG_PTR)Process->ConsoleHostProcessId & ~3) { CLIENT_ID clientId; PWSTR description = L"Console host"; PPH_STRING clientIdString; clientId.UniqueProcess = (HANDLE)((ULONG_PTR)Process->ConsoleHostProcessId & ~3); clientId.UniqueThread = NULL; if ((ULONG_PTR)Process->ConsoleHostProcessId & 2) description = L"Console application"; clientIdString = PhGetClientIdName(&clientId); PhAppendFormatStringBuilder(¬es, L" %s: %s\n", description, clientIdString->Buffer); PhDereferenceObject(clientIdString); } if (Process->PackageFullName) { PhAppendFormatStringBuilder(¬es, L" Package name: %s\n", Process->PackageFullName->Buffer); } if (Process->IsDotNet) PhAppendStringBuilder2(¬es, L" Process is managed (.NET).\n"); if (Process->IsElevated) PhAppendStringBuilder2(¬es, L" Process is elevated.\n"); if (Process->IsImmersive) PhAppendStringBuilder2(¬es, L" Process is a Modern UI app.\n"); if (Process->IsInJob) PhAppendStringBuilder2(¬es, L" Process is in a job.\n"); if (Process->IsPosix) PhAppendStringBuilder2(¬es, L" Process is POSIX.\n"); if (Process->IsWow64) PhAppendStringBuilder2(¬es, L" Process is 32-bit (WOW64).\n"); if (notes.String->Length != 0) { PhAppendStringBuilder2(&stringBuilder, L"Notes:\n"); PhAppendStringBuilder(&stringBuilder, ¬es.String->sr); } PhDeleteStringBuilder(¬es); } if (ValidToTickCount) *ValidToTickCount = GetTickCount() + validForMs; // Remove the trailing newline. if (stringBuilder.String->Length != 0) PhRemoveEndStringBuilder(&stringBuilder, 1); return PhFinalStringBuilderString(&stringBuilder); }
INT_PTR CALLBACK PhpProcessThreadsDlgProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam ) { LPPROPSHEETPAGE propSheetPage; PPH_PROCESS_PROPPAGECONTEXT propPageContext; PPH_PROCESS_ITEM processItem; PPH_THREADS_CONTEXT threadsContext; HWND tnHandle; if (PhpPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext, &processItem)) { threadsContext = (PPH_THREADS_CONTEXT)propPageContext->Context; if (threadsContext) tnHandle = threadsContext->ListContext.TreeNewHandle; } else { return FALSE; } switch (uMsg) { case WM_INITDIALOG: { threadsContext = propPageContext->Context = PhAllocate(PhEmGetObjectSize(EmThreadsContextType, sizeof(PH_THREADS_CONTEXT))); // The thread provider has a special registration mechanism. threadsContext->Provider = PhCreateThreadProvider( processItem->ProcessId ); PhRegisterCallback( &threadsContext->Provider->ThreadAddedEvent, ThreadAddedHandler, threadsContext, &threadsContext->AddedEventRegistration ); PhRegisterCallback( &threadsContext->Provider->ThreadModifiedEvent, ThreadModifiedHandler, threadsContext, &threadsContext->ModifiedEventRegistration ); PhRegisterCallback( &threadsContext->Provider->ThreadRemovedEvent, ThreadRemovedHandler, threadsContext, &threadsContext->RemovedEventRegistration ); PhRegisterCallback( &threadsContext->Provider->UpdatedEvent, ThreadsUpdatedHandler, threadsContext, &threadsContext->UpdatedEventRegistration ); PhRegisterCallback( &threadsContext->Provider->LoadingStateChangedEvent, ThreadsLoadingStateChangedHandler, threadsContext, &threadsContext->LoadingStateChangedEventRegistration ); threadsContext->WindowHandle = hwndDlg; // Initialize the list. tnHandle = GetDlgItem(hwndDlg, IDC_LIST); BringWindowToTop(tnHandle); PhInitializeThreadList(hwndDlg, tnHandle, &threadsContext->ListContext); TreeNew_SetEmptyText(tnHandle, &EmptyThreadsText, 0); PhInitializeProviderEventQueue(&threadsContext->EventQueue, 100); // Use Cycles instead of Context Switches on Vista and above, but only when we can // open the process, since cycle time information requires sufficient access to the // threads. if (WINDOWS_HAS_CYCLE_TIME) { HANDLE processHandle; PROCESS_EXTENDED_BASIC_INFORMATION extendedBasicInfo; // We make a distinction between PROCESS_QUERY_INFORMATION and PROCESS_QUERY_LIMITED_INFORMATION since // the latter can be used when opening audiodg.exe even though we can't access its threads using // THREAD_QUERY_LIMITED_INFORMATION. if (processItem->ProcessId == SYSTEM_IDLE_PROCESS_ID) { threadsContext->ListContext.UseCycleTime = TRUE; } else if (NT_SUCCESS(PhOpenProcess(&processHandle, PROCESS_QUERY_INFORMATION, processItem->ProcessId))) { threadsContext->ListContext.UseCycleTime = TRUE; NtClose(processHandle); } else if (NT_SUCCESS(PhOpenProcess(&processHandle, PROCESS_QUERY_LIMITED_INFORMATION, processItem->ProcessId))) { threadsContext->ListContext.UseCycleTime = TRUE; // We can't use cycle time for protected processes (without KProcessHacker). if (NT_SUCCESS(PhGetProcessExtendedBasicInformation(processHandle, &extendedBasicInfo)) && extendedBasicInfo.IsProtectedProcess) { threadsContext->ListContext.UseCycleTime = FALSE; } NtClose(processHandle); } } if (processItem->ServiceList && processItem->ServiceList->Count != 0 && WINDOWS_HAS_SERVICE_TAGS) threadsContext->ListContext.HasServices = TRUE; PhEmCallObjectOperation(EmThreadsContextType, threadsContext, EmObjectCreate); if (PhPluginsEnabled) { PH_PLUGIN_TREENEW_INFORMATION treeNewInfo; treeNewInfo.TreeNewHandle = tnHandle; treeNewInfo.CmData = &threadsContext->ListContext.Cm; treeNewInfo.SystemContext = threadsContext; PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackThreadTreeNewInitializing), &treeNewInfo); } PhLoadSettingsThreadList(&threadsContext->ListContext); PhThreadProviderInitialUpdate(threadsContext->Provider); PhRegisterThreadProvider(threadsContext->Provider, &threadsContext->ProviderRegistration); SET_BUTTON_ICON(IDC_OPENSTARTMODULE, PH_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(IDI_FOLDER))); } break; case WM_DESTROY: { PhEmCallObjectOperation(EmThreadsContextType, threadsContext, EmObjectDelete); PhUnregisterCallback( &threadsContext->Provider->ThreadAddedEvent, &threadsContext->AddedEventRegistration ); PhUnregisterCallback( &threadsContext->Provider->ThreadModifiedEvent, &threadsContext->ModifiedEventRegistration ); PhUnregisterCallback( &threadsContext->Provider->ThreadRemovedEvent, &threadsContext->RemovedEventRegistration ); PhUnregisterCallback( &threadsContext->Provider->UpdatedEvent, &threadsContext->UpdatedEventRegistration ); PhUnregisterCallback( &threadsContext->Provider->LoadingStateChangedEvent, &threadsContext->LoadingStateChangedEventRegistration ); PhUnregisterThreadProvider(threadsContext->Provider, &threadsContext->ProviderRegistration); PhSetTerminatingThreadProvider(threadsContext->Provider); PhDereferenceObject(threadsContext->Provider); PhDeleteProviderEventQueue(&threadsContext->EventQueue); if (PhPluginsEnabled) { PH_PLUGIN_TREENEW_INFORMATION treeNewInfo; treeNewInfo.TreeNewHandle = tnHandle; treeNewInfo.CmData = &threadsContext->ListContext.Cm; PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackThreadTreeNewUninitializing), &treeNewInfo); } PhSaveSettingsThreadList(&threadsContext->ListContext); PhDeleteThreadList(&threadsContext->ListContext); PhFree(threadsContext); PhpPropPageDlgProcDestroy(hwndDlg); } break; case WM_SHOWWINDOW: { if (!propPageContext->LayoutInitialized) { PPH_LAYOUT_ITEM dialogItem; dialogItem = PhAddPropPageLayoutItem(hwndDlg, hwndDlg, PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), dialogItem, PH_ANCHOR_ALL); #define ADD_BL_ITEM(Id) \ PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, Id), dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_BOTTOM) // Thread details area { ULONG id; for (id = IDC_STATICBL1; id <= IDC_STATICBL11; id++) ADD_BL_ITEM(id); // Not in sequence ADD_BL_ITEM(IDC_STATICBL12); } PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_STARTMODULE), dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_OPENSTARTMODULE), dialogItem, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); ADD_BL_ITEM(IDC_STARTED); ADD_BL_ITEM(IDC_KERNELTIME); ADD_BL_ITEM(IDC_USERTIME); ADD_BL_ITEM(IDC_CONTEXTSWITCHES); ADD_BL_ITEM(IDC_CYCLES); ADD_BL_ITEM(IDC_STATE); ADD_BL_ITEM(IDC_PRIORITY); ADD_BL_ITEM(IDC_BASEPRIORITY); ADD_BL_ITEM(IDC_IOPRIORITY); ADD_BL_ITEM(IDC_PAGEPRIORITY); ADD_BL_ITEM(IDC_IDEALPROCESSOR); PhDoPropPageLayout(hwndDlg); propPageContext->LayoutInitialized = TRUE; } } break; case WM_COMMAND: { INT id = LOWORD(wParam); switch (id) { case ID_SHOWCONTEXTMENU: { PhShowThreadContextMenu(hwndDlg, processItem, threadsContext, (PPH_TREENEW_CONTEXT_MENU)lParam); } break; case ID_THREAD_INSPECT: { PPH_THREAD_ITEM threadItem = PhGetSelectedThreadItem(&threadsContext->ListContext); if (threadItem) { PhReferenceObject(threadsContext->Provider); PhShowThreadStackDialog( hwndDlg, threadsContext->Provider->ProcessId, threadItem->ThreadId, threadsContext->Provider ); PhDereferenceObject(threadsContext->Provider); } } break; case ID_THREAD_TERMINATE: { PPH_THREAD_ITEM *threads; ULONG numberOfThreads; PhGetSelectedThreadItems(&threadsContext->ListContext, &threads, &numberOfThreads); PhReferenceObjects(threads, numberOfThreads); if (PhUiTerminateThreads(hwndDlg, threads, numberOfThreads)) PhDeselectAllThreadNodes(&threadsContext->ListContext); PhDereferenceObjects(threads, numberOfThreads); PhFree(threads); } break; case ID_THREAD_SUSPEND: { PPH_THREAD_ITEM *threads; ULONG numberOfThreads; PhGetSelectedThreadItems(&threadsContext->ListContext, &threads, &numberOfThreads); PhReferenceObjects(threads, numberOfThreads); PhUiSuspendThreads(hwndDlg, threads, numberOfThreads); PhDereferenceObjects(threads, numberOfThreads); PhFree(threads); } break; case ID_THREAD_RESUME: { PPH_THREAD_ITEM *threads; ULONG numberOfThreads; PhGetSelectedThreadItems(&threadsContext->ListContext, &threads, &numberOfThreads); PhReferenceObjects(threads, numberOfThreads); PhUiResumeThreads(hwndDlg, threads, numberOfThreads); PhDereferenceObjects(threads, numberOfThreads); PhFree(threads); } break; case ID_THREAD_AFFINITY: { PPH_THREAD_ITEM threadItem = PhGetSelectedThreadItem(&threadsContext->ListContext); if (threadItem) { PhReferenceObject(threadItem); PhShowProcessAffinityDialog(hwndDlg, NULL, threadItem); PhDereferenceObject(threadItem); } } break; case ID_THREAD_PERMISSIONS: { PPH_THREAD_ITEM threadItem = PhGetSelectedThreadItem(&threadsContext->ListContext); PH_STD_OBJECT_SECURITY stdObjectSecurity; PPH_ACCESS_ENTRY accessEntries; ULONG numberOfAccessEntries; if (threadItem) { stdObjectSecurity.OpenObject = PhpThreadPermissionsOpenThread; stdObjectSecurity.ObjectType = L"Thread"; stdObjectSecurity.Context = threadItem->ThreadId; if (PhGetAccessEntries(L"Thread", &accessEntries, &numberOfAccessEntries)) { PhEditSecurity( hwndDlg, PhaFormatString(L"Thread %u", HandleToUlong(threadItem->ThreadId))->Buffer, PhStdGetObjectSecurity, PhStdSetObjectSecurity, &stdObjectSecurity, accessEntries, numberOfAccessEntries ); PhFree(accessEntries); } } } break; case ID_THREAD_TOKEN: { NTSTATUS status; PPH_THREAD_ITEM threadItem = PhGetSelectedThreadItem(&threadsContext->ListContext); HANDLE threadHandle; if (threadItem) { if (NT_SUCCESS(status = PhOpenThread( &threadHandle, ThreadQueryAccess, threadItem->ThreadId ))) { PhShowTokenProperties( hwndDlg, PhpOpenThreadTokenObject, (PVOID)threadHandle, NULL ); NtClose(threadHandle); } else { PhShowStatus(hwndDlg, L"Unable to open the thread", status, 0); } } } break; case ID_ANALYZE_WAIT: { PPH_THREAD_ITEM threadItem = PhGetSelectedThreadItem(&threadsContext->ListContext); if (threadItem) { PhReferenceObject(threadsContext->Provider->SymbolProvider); PhUiAnalyzeWaitThread( hwndDlg, processItem->ProcessId, threadItem->ThreadId, threadsContext->Provider->SymbolProvider ); PhDereferenceObject(threadsContext->Provider->SymbolProvider); } } break; case ID_PRIORITY_TIMECRITICAL: case ID_PRIORITY_HIGHEST: case ID_PRIORITY_ABOVENORMAL: case ID_PRIORITY_NORMAL: case ID_PRIORITY_BELOWNORMAL: case ID_PRIORITY_LOWEST: case ID_PRIORITY_IDLE: { PPH_THREAD_ITEM threadItem = PhGetSelectedThreadItem(&threadsContext->ListContext); if (threadItem) { ULONG threadPriorityWin32; switch (id) { case ID_PRIORITY_TIMECRITICAL: threadPriorityWin32 = THREAD_PRIORITY_TIME_CRITICAL; break; case ID_PRIORITY_HIGHEST: threadPriorityWin32 = THREAD_PRIORITY_HIGHEST; break; case ID_PRIORITY_ABOVENORMAL: threadPriorityWin32 = THREAD_PRIORITY_ABOVE_NORMAL; break; case ID_PRIORITY_NORMAL: threadPriorityWin32 = THREAD_PRIORITY_NORMAL; break; case ID_PRIORITY_BELOWNORMAL: threadPriorityWin32 = THREAD_PRIORITY_BELOW_NORMAL; break; case ID_PRIORITY_LOWEST: threadPriorityWin32 = THREAD_PRIORITY_LOWEST; break; case ID_PRIORITY_IDLE: threadPriorityWin32 = THREAD_PRIORITY_IDLE; break; } PhReferenceObject(threadItem); PhUiSetPriorityThread(hwndDlg, threadItem, threadPriorityWin32); PhDereferenceObject(threadItem); } } break; case ID_IOPRIORITY_VERYLOW: case ID_IOPRIORITY_LOW: case ID_IOPRIORITY_NORMAL: case ID_IOPRIORITY_HIGH: { PPH_THREAD_ITEM threadItem = PhGetSelectedThreadItem(&threadsContext->ListContext); if (threadItem) { IO_PRIORITY_HINT ioPriority; switch (id) { case ID_IOPRIORITY_VERYLOW: ioPriority = IoPriorityVeryLow; break; case ID_IOPRIORITY_LOW: ioPriority = IoPriorityLow; break; case ID_IOPRIORITY_NORMAL: ioPriority = IoPriorityNormal; break; case ID_IOPRIORITY_HIGH: ioPriority = IoPriorityHigh; break; } PhReferenceObject(threadItem); PhUiSetIoPriorityThread(hwndDlg, threadItem, ioPriority); PhDereferenceObject(threadItem); } } break; case ID_PAGEPRIORITY_VERYLOW: case ID_PAGEPRIORITY_LOW: case ID_PAGEPRIORITY_MEDIUM: case ID_PAGEPRIORITY_BELOWNORMAL: case ID_PAGEPRIORITY_NORMAL: { PPH_THREAD_ITEM threadItem = PhGetSelectedThreadItem(&threadsContext->ListContext); if (threadItem) { ULONG pagePriority; switch (id) { case ID_PAGEPRIORITY_VERYLOW: pagePriority = MEMORY_PRIORITY_VERY_LOW; break; case ID_PAGEPRIORITY_LOW: pagePriority = MEMORY_PRIORITY_LOW; break; case ID_PAGEPRIORITY_MEDIUM: pagePriority = MEMORY_PRIORITY_MEDIUM; break; case ID_PAGEPRIORITY_BELOWNORMAL: pagePriority = MEMORY_PRIORITY_BELOW_NORMAL; break; case ID_PAGEPRIORITY_NORMAL: pagePriority = MEMORY_PRIORITY_NORMAL; break; } PhReferenceObject(threadItem); PhUiSetPagePriorityThread(hwndDlg, threadItem, pagePriority); PhDereferenceObject(threadItem); } } break; case ID_THREAD_COPY: { PPH_STRING text; text = PhGetTreeNewText(tnHandle, 0); PhSetClipboardString(tnHandle, &text->sr); PhDereferenceObject(text); } break; case IDC_OPENSTARTMODULE: { PPH_THREAD_ITEM threadItem = PhGetSelectedThreadItem(&threadsContext->ListContext); if (threadItem && threadItem->StartAddressFileName) { PhShellExploreFile(hwndDlg, threadItem->StartAddressFileName->Buffer); } } break; } } break; case WM_NOTIFY: { LPNMHDR header = (LPNMHDR)lParam; switch (header->code) { case PSN_SETACTIVE: break; case PSN_KILLACTIVE: // Can't disable, it screws up the deltas. break; } } break; case WM_PH_THREADS_UPDATED: { ULONG upToRunId = (ULONG)wParam; BOOLEAN firstRun = !!lParam; PPH_PROVIDER_EVENT events; ULONG count; ULONG i; events = PhFlushProviderEventQueue(&threadsContext->EventQueue, upToRunId, &count); if (events) { TreeNew_SetRedraw(tnHandle, FALSE); for (i = 0; i < count; i++) { PH_PROVIDER_EVENT_TYPE type = PH_PROVIDER_EVENT_TYPE(events[i]); PPH_THREAD_ITEM threadItem = PH_PROVIDER_EVENT_OBJECT(events[i]); switch (type) { case ProviderAddedEvent: PhAddThreadNode(&threadsContext->ListContext, threadItem, firstRun); PhDereferenceObject(threadItem); break; case ProviderModifiedEvent: PhUpdateThreadNode(&threadsContext->ListContext, PhFindThreadNode(&threadsContext->ListContext, threadItem->ThreadId)); break; case ProviderRemovedEvent: PhRemoveThreadNode(&threadsContext->ListContext, PhFindThreadNode(&threadsContext->ListContext, threadItem->ThreadId)); break; } } PhFree(events); } PhTickThreadNodes(&threadsContext->ListContext); if (count != 0) TreeNew_SetRedraw(tnHandle, TRUE); if (propPageContext->PropContext->SelectThreadId) { PPH_THREAD_NODE threadNode; if (threadNode = PhFindThreadNode(&threadsContext->ListContext, propPageContext->PropContext->SelectThreadId)) { if (threadNode->Node.Visible) { TreeNew_SetFocusNode(tnHandle, &threadNode->Node); TreeNew_SetMarkNode(tnHandle, &threadNode->Node); TreeNew_SelectRange(tnHandle, threadNode->Node.Index, threadNode->Node.Index); TreeNew_EnsureVisible(tnHandle, &threadNode->Node); } } propPageContext->PropContext->SelectThreadId = NULL; } PhpUpdateThreadDetails(hwndDlg, threadsContext, FALSE); } break; case WM_PH_THREAD_SELECTION_CHANGED: { PhpUpdateThreadDetails(hwndDlg, threadsContext, TRUE); } break; } return FALSE; }
VOID PhShowMemoryContextMenu( _In_ HWND hwndDlg, _In_ PPH_PROCESS_ITEM ProcessItem, _In_ PPH_MEMORY_CONTEXT Context, _In_ PPH_TREENEW_CONTEXT_MENU ContextMenu ) { PPH_MEMORY_NODE *memoryNodes; ULONG numberOfMemoryNodes; PhGetSelectedMemoryNodes(&Context->ListContext, &memoryNodes, &numberOfMemoryNodes); //if (numberOfMemoryNodes != 0) { PPH_EMENU menu; PPH_EMENU_ITEM item; PH_PLUGIN_MENU_INFORMATION menuInfo; menu = PhCreateEMenu(); PhLoadResourceEMenuItem(menu, PhInstanceHandle, MAKEINTRESOURCE(IDR_MEMORY), 0); PhSetFlagsEMenuItem(menu, ID_MEMORY_READWRITEMEMORY, PH_EMENU_DEFAULT, PH_EMENU_DEFAULT); PhpInitializeMemoryMenu(menu, ProcessItem->ProcessId, memoryNodes, numberOfMemoryNodes); PhInsertCopyCellEMenuItem(menu, ID_MEMORY_COPY, Context->ListContext.TreeNewHandle, ContextMenu->Column); if (PhPluginsEnabled) { PhPluginInitializeMenuInfo(&menuInfo, menu, hwndDlg, 0); menuInfo.u.Memory.ProcessId = ProcessItem->ProcessId; menuInfo.u.Memory.MemoryNodes = memoryNodes; menuInfo.u.Memory.NumberOfMemoryNodes = numberOfMemoryNodes; PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackMemoryMenuInitializing), &menuInfo); } item = PhShowEMenu( menu, hwndDlg, PH_EMENU_SHOW_LEFTRIGHT, PH_ALIGN_LEFT | PH_ALIGN_TOP, ContextMenu->Location.x, ContextMenu->Location.y ); if (item) { BOOLEAN handled = FALSE; handled = PhHandleCopyCellEMenuItem(item); if (!handled && PhPluginsEnabled) handled = PhPluginTriggerEMenuItem(&menuInfo, item); if (!handled) SendMessage(hwndDlg, WM_COMMAND, item->Id, 0); } PhDestroyEMenu(menu); } PhFree(memoryNodes); }