VOID PhAddSetting( _In_ PH_SETTING_TYPE Type, _In_ PPH_STRINGREF Name, _In_ PPH_STRINGREF DefaultValue ) { PH_SETTING setting; setting.Type = Type; setting.Name = *Name; setting.DefaultValue = *DefaultValue; memset(&setting.u, 0, sizeof(setting.u)); PhpSettingFromString(Type, &setting.DefaultValue, NULL, &setting); PhAddEntryHashtable(PhSettingsHashtable, &setting); }
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 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); }
VOID NTAPI RefreshSandboxieInfo( __in_opt PVOID Context, __in BOOLEAN TimerOrWaitFired ) { LONG index; WCHAR boxName[34]; ULONG pids[512]; PBOX_INFO boxInfo; if (!SbieApi_QueryBoxPath || !SbieApi_EnumBoxes || !SbieApi_EnumProcessEx) return; PhAcquireQueuedLockExclusive(&BoxedProcessesLock); PhClearHashtable(BoxedProcessesHashtable); BoxInfoCount = 0; index = -1; while ((index = SbieApi_EnumBoxes(index, boxName)) != -1) { if (SbieApi_EnumProcessEx(boxName, TRUE, 0, pids) == 0) { ULONG count; PULONG pid; count = pids[0]; pid = &pids[1]; while (count != 0) { BOXED_PROCESS boxedProcess; boxedProcess.ProcessId = ULongToHandle(*pid); memcpy(boxedProcess.BoxName, boxName, sizeof(boxName)); PhAddEntryHashtable(BoxedProcessesHashtable, &boxedProcess); count--; pid++; } } if (BoxInfoCount < 16) { ULONG filePathLength = 0; ULONG keyPathLength = 0; ULONG ipcPathLength = 0; boxInfo = &BoxInfo[BoxInfoCount++]; memcpy(boxInfo->BoxName, boxName, sizeof(boxName)); SbieApi_QueryBoxPath( boxName, NULL, NULL, NULL, &filePathLength, &keyPathLength, &ipcPathLength ); if (ipcPathLength < sizeof(boxInfo->IpcRootBuffer)) { boxInfo->IpcRootBuffer[0] = 0; SbieApi_QueryBoxPath( boxName, NULL, NULL, boxInfo->IpcRootBuffer, NULL, NULL, &ipcPathLength ); if (boxInfo->IpcRootBuffer[0] != 0) { PhInitializeStringRef(&boxInfo->IpcRoot, boxInfo->IpcRootBuffer); } else { BoxInfoCount--; } } else { BoxInfoCount--; } } } BoxedProcessesUpdated = TRUE; PhReleaseQueuedLockExclusive(&BoxedProcessesLock); }
LRESULT CALLBACK PhpExtendedListViewWndProc( __in HWND hwnd, __in UINT uMsg, __in WPARAM wParam, __in LPARAM lParam ) { PPH_EXTLV_CONTEXT context; WNDPROC oldWndProc; context = (PPH_EXTLV_CONTEXT)GetProp(hwnd, PhpMakeExtLvContextAtom()); oldWndProc = context->OldWndProc; switch (uMsg) { case WM_DESTROY: { SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)oldWndProc); if (context->TickHashtable) PhDereferenceObject(context->TickHashtable); PhFree(context); RemoveProp(hwnd, PhpMakeExtLvContextAtom()); } break; case WM_NOTIFY: { LPNMHDR header = (LPNMHDR)lParam; switch (header->code) { case HDN_ITEMCLICK: { HWND headerHandle; headerHandle = (HWND)CallWindowProc(context->OldWndProc, hwnd, LVM_GETHEADER, 0, 0); if (header->hwndFrom == headerHandle) { LPNMHEADER header2 = (LPNMHEADER)header; if (header2->iItem == context->SortColumn) { if (context->TriState) { if (context->SortOrder == AscendingSortOrder) context->SortOrder = DescendingSortOrder; else if (context->SortOrder == DescendingSortOrder) context->SortOrder = NoSortOrder; else context->SortOrder = AscendingSortOrder; } else { if (context->SortOrder == AscendingSortOrder) context->SortOrder = DescendingSortOrder; else context->SortOrder = AscendingSortOrder; } } else { context->SortColumn = header2->iItem; context->SortOrder = AscendingSortOrder; } PhSetHeaderSortIcon(headerHandle, context->SortColumn, context->SortOrder); ExtendedListView_SortItems(hwnd); } } break; } } break; case WM_REFLECT + WM_NOTIFY: { LPNMHDR header = (LPNMHDR)lParam; switch (header->code) { case NM_CUSTOMDRAW: { if (header->hwndFrom == hwnd) { LPNMLVCUSTOMDRAW customDraw = (LPNMLVCUSTOMDRAW)header; switch (customDraw->nmcd.dwDrawStage) { case CDDS_PREPAINT: return CDRF_NOTIFYITEMDRAW; case CDDS_ITEMPREPAINT: { LVITEM item; PH_ITEM_STATE itemState; BOOLEAN colorChanged = FALSE; HFONT newFont = NULL; item.mask = LVIF_STATE; item.iItem = (INT)customDraw->nmcd.dwItemSpec; item.iSubItem = 0; item.stateMask = LVIS_STATEIMAGEMASK; CallWindowProc(oldWndProc, hwnd, LVM_GETITEM, 0, (LPARAM)&item); itemState = PH_GET_ITEM_STATE(item.state); if (!context->EnableState || itemState == NormalItemState) { if (context->ItemColorFunction) { customDraw->clrTextBk = context->ItemColorFunction( (INT)customDraw->nmcd.dwItemSpec, (PVOID)customDraw->nmcd.lItemlParam, context->Context ); colorChanged = TRUE; } if (context->ItemFontFunction) { newFont = context->ItemFontFunction( (INT)customDraw->nmcd.dwItemSpec, (PVOID)customDraw->nmcd.lItemlParam, context->Context ); } if (newFont) SelectObject(customDraw->nmcd.hdc, newFont); } else if (itemState == NewItemState) { customDraw->clrTextBk = context->NewColor; colorChanged = TRUE; } else if (itemState == RemovingItemState) { customDraw->clrTextBk = context->RemovingColor; colorChanged = TRUE; } if (colorChanged) { if (PhGetColorBrightness(customDraw->clrTextBk) > 100) // slightly less than half customDraw->clrText = RGB(0x00, 0x00, 0x00); else customDraw->clrText = RGB(0xff, 0xff, 0xff); } if (!newFont) return CDRF_DODEFAULT; else return CDRF_NEWFONT; } break; } } } break; } } break; case WM_SETCURSOR: { if (context->Cursor) { SetCursor(context->Cursor); return TRUE; } } break; case WM_UPDATEUISTATE: { // Disable focus rectangles by setting or masking out the flag where appropriate. switch (LOWORD(wParam)) { case UIS_SET: wParam |= UISF_HIDEFOCUS << 16; break; case UIS_CLEAR: case UIS_INITIALIZE: wParam &= ~(UISF_HIDEFOCUS << 16); break; } } break; case LVM_INSERTITEM: { LPLVITEM item = (LPLVITEM)lParam; INT index; if (!context->EnableState) break; // pass through if (!(item->mask & LVIF_STATE)) { item->mask |= LVIF_STATE; item->stateMask = LVIS_STATEIMAGEMASK; item->state = 0; } else { item->stateMask |= LVIS_STATEIMAGEMASK; item->state &= ~LVIS_STATEIMAGEMASK; } if (context->EnableStateHighlighting > 0) { item->state |= INDEXTOSTATEIMAGEMASK(NewItemState); } else { item->state = NormalItemState; } if ((index = (INT)CallWindowProc(oldWndProc, hwnd, LVM_INSERTITEM, 0, (LPARAM)item)) == -1) return -1; if (context->EnableStateHighlighting > 0) { PH_TICK_ENTRY entry; PhpEnsureTickHashtableCreated(context); entry.Id = ListView_MapIndexToID(hwnd, index); entry.TickCount = GetTickCount(); PhAddEntryHashtable(context->TickHashtable, &entry); } return index; } case LVM_DELETEITEM: { if (!context->EnableState) break; // pass through if (context->EnableStateHighlighting > 0) { LVITEM item; item.mask = LVIF_STATE | LVIF_PARAM; item.iItem = (INT)wParam; item.iSubItem = 0; item.stateMask = LVIS_STATEIMAGEMASK; item.state = INDEXTOSTATEIMAGEMASK(RemovingItemState); // IMPORTANT: // We need to null the param. This is important because the user // will most likely be storing pointers to heap allocations in // here, and free the allocation after it has deleted the // item. The user may allocate sometime in the future and receive // the same pointer as is stored here. The user may call // LVM_FINDITEM or LVM_GETNEXTITEM and find this item, which // is supposed to be deleted. It may then attempt to delete // this item *twice*, which leads to bad things happening, // including *not* deleting the item that the user wanted to delete. item.lParam = (LPARAM)NULL; CallWindowProc(context->OldWndProc, hwnd, LVM_SETITEM, 0, (LPARAM)&item); { PH_TICK_ENTRY localEntry; PPH_TICK_ENTRY entry; PhpEnsureTickHashtableCreated(context); localEntry.Id = ListView_MapIndexToID(hwnd, (INT)wParam); entry = PhAddEntryHashtableEx(context->TickHashtable, &localEntry, NULL); entry->TickCount = GetTickCount(); } return TRUE; } else { // The item may still be under state highlighting. if (context->TickHashtable) { PH_TICK_ENTRY entry; entry.Id = ListView_MapIndexToID(hwnd, (INT)wParam); PhRemoveEntryHashtable(context->TickHashtable, &entry); } } } break; case LVM_GETITEM: { LVITEM item; ULONG itemState; ULONG oldMask; ULONG oldStateMask; if (!context->EnableState) break; // pass through memcpy(&item, (LPLVITEM)lParam, sizeof(LVITEM)); oldMask = item.mask; oldStateMask = item.stateMask; if (!(item.mask & LVIF_STATE)) { item.mask |= LVIF_STATE; item.stateMask = LVIS_STATEIMAGEMASK; } else { item.stateMask |= LVIS_STATEIMAGEMASK; } if (!CallWindowProc(oldWndProc, hwnd, LVM_GETITEM, 0, (LPARAM)&item)) return FALSE; // Check if the item is being deleted. If so, pretend it doesn't // exist. itemState = PH_GET_ITEM_STATE(item.state); if (itemState == RemovingItemState) return FALSE; item.mask = oldMask; item.stateMask = oldStateMask; item.state &= item.stateMask; memcpy((LPLVITEM)lParam, &item, sizeof(LVITEM)); } return TRUE; case ELVM_ADDFALLBACKCOLUMN: { if (context->NumberOfFallbackColumns < PH_MAX_COMPARE_FUNCTIONS) context->FallbackColumns[context->NumberOfFallbackColumns++] = (ULONG)wParam; else return FALSE; } return TRUE; case ELVM_ADDFALLBACKCOLUMNS: { ULONG numberOfColumns = (ULONG)wParam; PULONG columns = (PULONG)lParam; if (context->NumberOfFallbackColumns + numberOfColumns <= PH_MAX_COMPARE_FUNCTIONS) { memcpy( &context->FallbackColumns[context->NumberOfFallbackColumns], columns, numberOfColumns * sizeof(ULONG) ); context->NumberOfFallbackColumns += numberOfColumns; } else { return FALSE; } } return TRUE; case ELVM_ENABLESTATE: { context->EnableState = !!wParam; } return TRUE; case ELVM_INIT: { PhSetHeaderSortIcon(ListView_GetHeader(hwnd), context->SortColumn, context->SortOrder); // HACK to fix tooltips showing behind Always On Top windows. SetWindowPos(ListView_GetToolTips(hwnd), HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); // Make sure focus rectangles are disabled. SendMessage(hwnd, WM_CHANGEUISTATE, MAKELONG(UIS_SET, UISF_HIDEFOCUS), 0); } return TRUE; case ELVM_SETCOLUMNWIDTH: { ULONG column = (ULONG)wParam; LONG width = (LONG)lParam; if (width == ELVSCW_AUTOSIZE_REMAININGSPACE) { RECT clientRect; LONG availableWidth; ULONG i; LVCOLUMN lvColumn; GetClientRect(hwnd, &clientRect); availableWidth = clientRect.right; i = 0; lvColumn.mask = LVCF_WIDTH; while (TRUE) { if (i != column) { if (CallWindowProc(oldWndProc, hwnd, LVM_GETCOLUMN, i, (LPARAM)&lvColumn)) { availableWidth -= lvColumn.cx; } else { break; } } i++; } if (availableWidth >= 40) return CallWindowProc(oldWndProc, hwnd, LVM_SETCOLUMNWIDTH, column, availableWidth); } return CallWindowProc(oldWndProc, hwnd, LVM_SETCOLUMNWIDTH, column, width); } break; case ELVM_SETCOMPAREFUNCTION: { ULONG column = (ULONG)wParam; PPH_COMPARE_FUNCTION compareFunction = (PPH_COMPARE_FUNCTION)lParam; if (column >= PH_MAX_COMPARE_FUNCTIONS) return FALSE; context->CompareFunctions[column] = compareFunction; } return TRUE; case ELVM_SETCONTEXT: { context->Context = (PVOID)lParam; } return TRUE; case ELVM_SETCURSOR: { context->Cursor = (HCURSOR)lParam; } return TRUE; case ELVM_SETHIGHLIGHTINGDURATION: { context->HighlightingDuration = (ULONG)wParam; } return TRUE; case ELVM_SETITEMCOLORFUNCTION: { context->ItemColorFunction = (PPH_EXTLV_GET_ITEM_COLOR)lParam; } return TRUE; case ELVM_SETITEMFONTFUNCTION: { context->ItemFontFunction = (PPH_EXTLV_GET_ITEM_FONT)lParam; } return TRUE; case ELVM_SETNEWCOLOR: { context->NewColor = (COLORREF)wParam; } return TRUE; case ELVM_SETREDRAW: { if (wParam) context->EnableRedraw++; else context->EnableRedraw--; if (context->EnableRedraw == 1) { SendMessage(hwnd, WM_SETREDRAW, TRUE, 0); InvalidateRect(hwnd, NULL, FALSE); } else if (context->EnableRedraw == 0) { SendMessage(hwnd, WM_SETREDRAW, FALSE, 0); } } return TRUE; case ELVM_SETREMOVINGCOLOR: { context->RemovingColor = (COLORREF)wParam; } return TRUE; case ELVM_SETSORT: { context->SortColumn = (ULONG)wParam; context->SortOrder = (PH_SORT_ORDER)lParam; PhSetHeaderSortIcon(ListView_GetHeader(hwnd), context->SortColumn, context->SortOrder); } return TRUE; case ELVM_SETSORTFAST: { context->SortFast = !!wParam; } return TRUE; case ELVM_SETSTATEHIGHLIGHTING: { if (wParam) context->EnableStateHighlighting++; else context->EnableStateHighlighting--; } return TRUE; case ELVM_SETTRISTATE: { context->TriState = !!wParam; } return TRUE; case ELVM_SETTRISTATECOMPAREFUNCTION: { context->TriStateCompareFunction = (PPH_COMPARE_FUNCTION)lParam; } return TRUE; case ELVM_SORTITEMS: { if (context->SortFast) { // This sort method is faster than the normal sort because our comparison function // doesn't have to call the list view window procedure to get the item lParam values. // The disadvantage of this method is that default sorting is not available - if a // column doesn't have a comparison function, it doesn't get sorted at all. ListView_SortItems( hwnd, PhpExtendedListViewCompareFastFunc, (LPARAM)context ); } else { ListView_SortItemsEx( hwnd, PhpExtendedListViewCompareFunc, (LPARAM)context ); } } return TRUE; case ELVM_TICK: { if ( context->EnableStateHighlighting > 0 && context->TickHashtable && context->TickHashtable->Count != 0 ) { PhListTick(context); } } return TRUE; } return CallWindowProc(oldWndProc, hwnd, uMsg, wParam, lParam); }
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++; }