504 lines
17 KiB
C
504 lines
17 KiB
C
/*
|
|
* Process Hacker -
|
|
* Process properties: Modules page
|
|
*
|
|
* Copyright (C) 2009-2016 wj32
|
|
*
|
|
* This file is part of Process Hacker.
|
|
*
|
|
* Process Hacker is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Process Hacker is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <phapp.h>
|
|
#include <cpysave.h>
|
|
#include <emenu.h>
|
|
#include <settings.h>
|
|
#include <procprv.h>
|
|
#include <modprv.h>
|
|
#include <modlist.h>
|
|
#include <actions.h>
|
|
#include <phplug.h>
|
|
#include <extmgri.h>
|
|
#include <windowsx.h>
|
|
#include <uxtheme.h>
|
|
#include <procprpp.h>
|
|
|
|
static PH_STRINGREF EmptyModulesText = PH_STRINGREF_INIT(L"There are no modules to display.");
|
|
|
|
static VOID NTAPI ModuleAddedHandler(
|
|
_In_opt_ PVOID Parameter,
|
|
_In_opt_ PVOID Context
|
|
)
|
|
{
|
|
PPH_MODULES_CONTEXT modulesContext = (PPH_MODULES_CONTEXT)Context;
|
|
|
|
// Parameter contains a pointer to the added module item.
|
|
PhReferenceObject(Parameter);
|
|
PhPushProviderEventQueue(&modulesContext->EventQueue, ProviderAddedEvent, Parameter, PhGetRunIdProvider(&modulesContext->ProviderRegistration));
|
|
}
|
|
|
|
static VOID NTAPI ModuleModifiedHandler(
|
|
_In_opt_ PVOID Parameter,
|
|
_In_opt_ PVOID Context
|
|
)
|
|
{
|
|
PPH_MODULES_CONTEXT modulesContext = (PPH_MODULES_CONTEXT)Context;
|
|
|
|
PhPushProviderEventQueue(&modulesContext->EventQueue, ProviderModifiedEvent, Parameter, PhGetRunIdProvider(&modulesContext->ProviderRegistration));
|
|
}
|
|
|
|
static VOID NTAPI ModuleRemovedHandler(
|
|
_In_opt_ PVOID Parameter,
|
|
_In_opt_ PVOID Context
|
|
)
|
|
{
|
|
PPH_MODULES_CONTEXT modulesContext = (PPH_MODULES_CONTEXT)Context;
|
|
|
|
PhPushProviderEventQueue(&modulesContext->EventQueue, ProviderRemovedEvent, Parameter, PhGetRunIdProvider(&modulesContext->ProviderRegistration));
|
|
}
|
|
|
|
static VOID NTAPI ModulesUpdatedHandler(
|
|
_In_opt_ PVOID Parameter,
|
|
_In_opt_ PVOID Context
|
|
)
|
|
{
|
|
PPH_MODULES_CONTEXT modulesContext = (PPH_MODULES_CONTEXT)Context;
|
|
|
|
PostMessage(modulesContext->WindowHandle, WM_PH_MODULES_UPDATED, PhGetRunIdProvider(&modulesContext->ProviderRegistration), 0);
|
|
}
|
|
|
|
VOID PhpInitializeModuleMenu(
|
|
_In_ PPH_EMENU Menu,
|
|
_In_ HANDLE ProcessId,
|
|
_In_ PPH_MODULE_ITEM *Modules,
|
|
_In_ ULONG NumberOfModules
|
|
)
|
|
{
|
|
PPH_EMENU_ITEM item;
|
|
PPH_STRING inspectExecutables;
|
|
|
|
inspectExecutables = PhaGetStringSetting(L"ProgramInspectExecutables");
|
|
|
|
if (inspectExecutables->Length == 0)
|
|
{
|
|
if (item = PhFindEMenuItem(Menu, 0, NULL, ID_MODULE_INSPECT))
|
|
PhDestroyEMenuItem(item);
|
|
}
|
|
|
|
if (NumberOfModules == 0)
|
|
{
|
|
PhSetFlagsAllEMenuItems(Menu, PH_EMENU_DISABLED, PH_EMENU_DISABLED);
|
|
}
|
|
else if (NumberOfModules == 1)
|
|
{
|
|
// Nothing
|
|
}
|
|
else
|
|
{
|
|
PhSetFlagsAllEMenuItems(Menu, PH_EMENU_DISABLED, PH_EMENU_DISABLED);
|
|
PhEnableEMenuItem(Menu, ID_MODULE_COPY, TRUE);
|
|
}
|
|
}
|
|
|
|
VOID PhShowModuleContextMenu(
|
|
_In_ HWND hwndDlg,
|
|
_In_ PPH_PROCESS_ITEM ProcessItem,
|
|
_In_ PPH_MODULES_CONTEXT Context,
|
|
_In_ PPH_TREENEW_CONTEXT_MENU ContextMenu
|
|
)
|
|
{
|
|
PPH_MODULE_ITEM *modules;
|
|
ULONG numberOfModules;
|
|
|
|
PhGetSelectedModuleItems(&Context->ListContext, &modules, &numberOfModules);
|
|
|
|
if (numberOfModules != 0)
|
|
{
|
|
PPH_EMENU menu;
|
|
PPH_EMENU_ITEM item;
|
|
PH_PLUGIN_MENU_INFORMATION menuInfo;
|
|
|
|
menu = PhCreateEMenu();
|
|
PhLoadResourceEMenuItem(menu, PhInstanceHandle, MAKEINTRESOURCE(IDR_MODULE), 0);
|
|
PhSetFlagsEMenuItem(menu, ID_MODULE_INSPECT, PH_EMENU_DEFAULT, PH_EMENU_DEFAULT);
|
|
|
|
PhpInitializeModuleMenu(menu, ProcessItem->ProcessId, modules, numberOfModules);
|
|
PhInsertCopyCellEMenuItem(menu, ID_MODULE_COPY, Context->ListContext.TreeNewHandle, ContextMenu->Column);
|
|
|
|
if (PhPluginsEnabled)
|
|
{
|
|
PhPluginInitializeMenuInfo(&menuInfo, menu, hwndDlg, 0);
|
|
menuInfo.u.Module.ProcessId = ProcessItem->ProcessId;
|
|
menuInfo.u.Module.Modules = modules;
|
|
menuInfo.u.Module.NumberOfModules = numberOfModules;
|
|
|
|
PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackModuleMenuInitializing), &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(modules);
|
|
}
|
|
|
|
INT_PTR CALLBACK PhpProcessModulesDlgProc(
|
|
_In_ HWND hwndDlg,
|
|
_In_ UINT uMsg,
|
|
_In_ WPARAM wParam,
|
|
_In_ LPARAM lParam
|
|
)
|
|
{
|
|
LPPROPSHEETPAGE propSheetPage;
|
|
PPH_PROCESS_PROPPAGECONTEXT propPageContext;
|
|
PPH_PROCESS_ITEM processItem;
|
|
PPH_MODULES_CONTEXT modulesContext;
|
|
HWND tnHandle;
|
|
|
|
if (PhpPropPageDlgProcHeader(hwndDlg, uMsg, lParam,
|
|
&propSheetPage, &propPageContext, &processItem))
|
|
{
|
|
modulesContext = (PPH_MODULES_CONTEXT)propPageContext->Context;
|
|
|
|
if (modulesContext)
|
|
tnHandle = modulesContext->ListContext.TreeNewHandle;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
switch (uMsg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
// Lots of boilerplate code...
|
|
|
|
modulesContext = propPageContext->Context =
|
|
PhAllocate(PhEmGetObjectSize(EmModulesContextType, sizeof(PH_MODULES_CONTEXT)));
|
|
|
|
modulesContext->Provider = PhCreateModuleProvider(
|
|
processItem->ProcessId
|
|
);
|
|
PhRegisterProvider(
|
|
&PhSecondaryProviderThread,
|
|
PhModuleProviderUpdate,
|
|
modulesContext->Provider,
|
|
&modulesContext->ProviderRegistration
|
|
);
|
|
PhRegisterCallback(
|
|
&modulesContext->Provider->ModuleAddedEvent,
|
|
ModuleAddedHandler,
|
|
modulesContext,
|
|
&modulesContext->AddedEventRegistration
|
|
);
|
|
PhRegisterCallback(
|
|
&modulesContext->Provider->ModuleModifiedEvent,
|
|
ModuleModifiedHandler,
|
|
modulesContext,
|
|
&modulesContext->ModifiedEventRegistration
|
|
);
|
|
PhRegisterCallback(
|
|
&modulesContext->Provider->ModuleRemovedEvent,
|
|
ModuleRemovedHandler,
|
|
modulesContext,
|
|
&modulesContext->RemovedEventRegistration
|
|
);
|
|
PhRegisterCallback(
|
|
&modulesContext->Provider->UpdatedEvent,
|
|
ModulesUpdatedHandler,
|
|
modulesContext,
|
|
&modulesContext->UpdatedEventRegistration
|
|
);
|
|
modulesContext->WindowHandle = hwndDlg;
|
|
|
|
// Initialize the list.
|
|
tnHandle = GetDlgItem(hwndDlg, IDC_LIST);
|
|
BringWindowToTop(tnHandle);
|
|
PhInitializeModuleList(hwndDlg, tnHandle, &modulesContext->ListContext);
|
|
TreeNew_SetEmptyText(tnHandle, &PhpLoadingText, 0);
|
|
PhInitializeProviderEventQueue(&modulesContext->EventQueue, 100);
|
|
modulesContext->LastRunStatus = -1;
|
|
modulesContext->ErrorMessage = NULL;
|
|
|
|
PhEmCallObjectOperation(EmModulesContextType, modulesContext, EmObjectCreate);
|
|
|
|
if (PhPluginsEnabled)
|
|
{
|
|
PH_PLUGIN_TREENEW_INFORMATION treeNewInfo;
|
|
|
|
treeNewInfo.TreeNewHandle = tnHandle;
|
|
treeNewInfo.CmData = &modulesContext->ListContext.Cm;
|
|
treeNewInfo.SystemContext = modulesContext;
|
|
PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackModuleTreeNewInitializing), &treeNewInfo);
|
|
}
|
|
|
|
PhLoadSettingsModuleList(&modulesContext->ListContext);
|
|
|
|
PhSetEnabledProvider(&modulesContext->ProviderRegistration, TRUE);
|
|
PhBoostProvider(&modulesContext->ProviderRegistration, NULL);
|
|
|
|
EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB);
|
|
}
|
|
break;
|
|
case WM_DESTROY:
|
|
{
|
|
PhEmCallObjectOperation(EmModulesContextType, modulesContext, EmObjectDelete);
|
|
|
|
PhUnregisterCallback(
|
|
&modulesContext->Provider->ModuleAddedEvent,
|
|
&modulesContext->AddedEventRegistration
|
|
);
|
|
PhUnregisterCallback(
|
|
&modulesContext->Provider->ModuleModifiedEvent,
|
|
&modulesContext->ModifiedEventRegistration
|
|
);
|
|
PhUnregisterCallback(
|
|
&modulesContext->Provider->ModuleRemovedEvent,
|
|
&modulesContext->RemovedEventRegistration
|
|
);
|
|
PhUnregisterCallback(
|
|
&modulesContext->Provider->UpdatedEvent,
|
|
&modulesContext->UpdatedEventRegistration
|
|
);
|
|
PhUnregisterProvider(&modulesContext->ProviderRegistration);
|
|
PhDereferenceObject(modulesContext->Provider);
|
|
PhDeleteProviderEventQueue(&modulesContext->EventQueue);
|
|
|
|
if (PhPluginsEnabled)
|
|
{
|
|
PH_PLUGIN_TREENEW_INFORMATION treeNewInfo;
|
|
|
|
treeNewInfo.TreeNewHandle = tnHandle;
|
|
treeNewInfo.CmData = &modulesContext->ListContext.Cm;
|
|
PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackModuleTreeNewUninitializing), &treeNewInfo);
|
|
}
|
|
|
|
PhSaveSettingsModuleList(&modulesContext->ListContext);
|
|
PhDeleteModuleList(&modulesContext->ListContext);
|
|
|
|
PhClearReference(&modulesContext->ErrorMessage);
|
|
PhFree(modulesContext);
|
|
|
|
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, modulesContext->ListContext.TreeNewHandle,
|
|
dialogItem, PH_ANCHOR_ALL);
|
|
|
|
PhDoPropPageLayout(hwndDlg);
|
|
|
|
propPageContext->LayoutInitialized = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
case WM_COMMAND:
|
|
{
|
|
switch (LOWORD(wParam))
|
|
{
|
|
case ID_SHOWCONTEXTMENU:
|
|
{
|
|
PhShowModuleContextMenu(hwndDlg, processItem, modulesContext, (PPH_TREENEW_CONTEXT_MENU)lParam);
|
|
}
|
|
break;
|
|
case ID_MODULE_UNLOAD:
|
|
{
|
|
PPH_MODULE_ITEM moduleItem = PhGetSelectedModuleItem(&modulesContext->ListContext);
|
|
|
|
if (moduleItem)
|
|
{
|
|
PhReferenceObject(moduleItem);
|
|
|
|
if (PhUiUnloadModule(hwndDlg, processItem->ProcessId, moduleItem))
|
|
PhDeselectAllModuleNodes(&modulesContext->ListContext);
|
|
|
|
PhDereferenceObject(moduleItem);
|
|
}
|
|
}
|
|
break;
|
|
case ID_MODULE_INSPECT:
|
|
{
|
|
PPH_MODULE_ITEM moduleItem = PhGetSelectedModuleItem(&modulesContext->ListContext);
|
|
|
|
if (moduleItem)
|
|
{
|
|
PhShellExecuteUserString(
|
|
hwndDlg,
|
|
L"ProgramInspectExecutables",
|
|
moduleItem->FileName->Buffer,
|
|
FALSE,
|
|
L"Make sure the PE Viewer executable file is present."
|
|
);
|
|
}
|
|
}
|
|
break;
|
|
case ID_MODULE_SEARCHONLINE:
|
|
{
|
|
PPH_MODULE_ITEM moduleItem = PhGetSelectedModuleItem(&modulesContext->ListContext);
|
|
|
|
if (moduleItem)
|
|
{
|
|
PhSearchOnlineString(hwndDlg, moduleItem->Name->Buffer);
|
|
}
|
|
}
|
|
break;
|
|
case ID_MODULE_OPENFILELOCATION:
|
|
{
|
|
PPH_MODULE_ITEM moduleItem = PhGetSelectedModuleItem(&modulesContext->ListContext);
|
|
|
|
if (moduleItem)
|
|
{
|
|
PhShellExploreFile(hwndDlg, moduleItem->FileName->Buffer);
|
|
}
|
|
}
|
|
break;
|
|
case ID_MODULE_PROPERTIES:
|
|
{
|
|
PPH_MODULE_ITEM moduleItem = PhGetSelectedModuleItem(&modulesContext->ListContext);
|
|
|
|
if (moduleItem)
|
|
{
|
|
PhShellProperties(hwndDlg, moduleItem->FileName->Buffer);
|
|
}
|
|
}
|
|
break;
|
|
case ID_MODULE_COPY:
|
|
{
|
|
PPH_STRING text;
|
|
|
|
text = PhGetTreeNewText(tnHandle, 0);
|
|
PhSetClipboardString(tnHandle, &text->sr);
|
|
PhDereferenceObject(text);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case WM_NOTIFY:
|
|
{
|
|
LPNMHDR header = (LPNMHDR)lParam;
|
|
|
|
switch (header->code)
|
|
{
|
|
case PSN_SETACTIVE:
|
|
PhSetEnabledProvider(&modulesContext->ProviderRegistration, TRUE);
|
|
break;
|
|
case PSN_KILLACTIVE:
|
|
PhSetEnabledProvider(&modulesContext->ProviderRegistration, FALSE);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case WM_PH_MODULES_UPDATED:
|
|
{
|
|
ULONG upToRunId = (ULONG)wParam;
|
|
PPH_PROVIDER_EVENT events;
|
|
ULONG count;
|
|
ULONG i;
|
|
|
|
events = PhFlushProviderEventQueue(&modulesContext->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_MODULE_ITEM moduleItem = PH_PROVIDER_EVENT_OBJECT(events[i]);
|
|
|
|
switch (type)
|
|
{
|
|
case ProviderAddedEvent:
|
|
PhAddModuleNode(&modulesContext->ListContext, moduleItem, events[i].RunId);
|
|
PhDereferenceObject(moduleItem);
|
|
break;
|
|
case ProviderModifiedEvent:
|
|
PhUpdateModuleNode(&modulesContext->ListContext, PhFindModuleNode(&modulesContext->ListContext, moduleItem));
|
|
break;
|
|
case ProviderRemovedEvent:
|
|
PhRemoveModuleNode(&modulesContext->ListContext, PhFindModuleNode(&modulesContext->ListContext, moduleItem));
|
|
break;
|
|
}
|
|
}
|
|
|
|
PhFree(events);
|
|
}
|
|
|
|
PhTickModuleNodes(&modulesContext->ListContext);
|
|
|
|
if (modulesContext->LastRunStatus != modulesContext->Provider->RunStatus)
|
|
{
|
|
NTSTATUS status;
|
|
PPH_STRING message;
|
|
|
|
status = modulesContext->Provider->RunStatus;
|
|
modulesContext->LastRunStatus = status;
|
|
|
|
if (!PH_IS_REAL_PROCESS_ID(processItem->ProcessId))
|
|
status = STATUS_SUCCESS;
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
TreeNew_SetEmptyText(tnHandle, &EmptyModulesText, 0);
|
|
}
|
|
else
|
|
{
|
|
message = PhGetStatusMessage(status, 0);
|
|
PhMoveReference(&modulesContext->ErrorMessage, PhFormatString(L"Unable to query module information:\n%s", PhGetStringOrDefault(message, L"Unknown error.")));
|
|
PhClearReference(&message);
|
|
TreeNew_SetEmptyText(tnHandle, &modulesContext->ErrorMessage->sr, 0);
|
|
}
|
|
|
|
InvalidateRect(tnHandle, NULL, FALSE);
|
|
}
|
|
|
|
if (count != 0)
|
|
TreeNew_SetRedraw(tnHandle, TRUE);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|