423 lines
13 KiB
C
423 lines
13 KiB
C
/*
|
|
* Process Hacker Extra Plugins -
|
|
* Pool Table Plugin
|
|
*
|
|
* Copyright (C) 2016 dmex
|
|
*
|
|
* 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 "main.h"
|
|
|
|
static HWND PoolTagDialogHandle = NULL;
|
|
static HANDLE PoolTagDialogThreadHandle = NULL;
|
|
static PH_EVENT PoolTagDialogInitializedEvent = PH_EVENT_INIT;
|
|
|
|
VOID UpdatePoolTagTable(
|
|
_Inout_ PPOOLTAG_CONTEXT Context
|
|
)
|
|
{
|
|
PSYSTEM_POOLTAG_INFORMATION poolTagTable;
|
|
ULONG i;
|
|
|
|
if (!NT_SUCCESS(EnumPoolTagTable(&poolTagTable)))
|
|
{
|
|
PhDereferenceObject(Context);
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < poolTagTable->Count; i++)
|
|
{
|
|
PPOOLTAG_ROOT_NODE node;
|
|
SYSTEM_POOLTAG poolTagInfo;
|
|
|
|
poolTagInfo = poolTagTable->TagInfo[i];
|
|
|
|
if (node = PmFindPoolTagNode(Context, poolTagInfo.TagUlong))
|
|
{
|
|
PhUpdateDelta(&node->PoolItem->PagedAllocsDelta, poolTagInfo.PagedAllocs);
|
|
PhUpdateDelta(&node->PoolItem->PagedFreesDelta, poolTagInfo.PagedFrees);
|
|
PhUpdateDelta(&node->PoolItem->PagedCurrentDelta, poolTagInfo.PagedAllocs - poolTagInfo.PagedFrees);
|
|
PhUpdateDelta(&node->PoolItem->PagedTotalSizeDelta, poolTagInfo.PagedUsed);
|
|
PhUpdateDelta(&node->PoolItem->NonPagedAllocsDelta, poolTagInfo.NonPagedAllocs);
|
|
PhUpdateDelta(&node->PoolItem->NonPagedFreesDelta, poolTagInfo.NonPagedFrees);
|
|
PhUpdateDelta(&node->PoolItem->NonPagedCurrentDelta, poolTagInfo.NonPagedAllocs - poolTagInfo.NonPagedFrees);
|
|
PhUpdateDelta(&node->PoolItem->NonPagedTotalSizeDelta, poolTagInfo.NonPagedUsed);
|
|
|
|
PmUpdatePoolTagNode(Context, node);
|
|
}
|
|
else
|
|
{
|
|
PPOOL_ITEM entry;
|
|
|
|
entry = PhCreateAlloc(sizeof(POOL_ITEM));
|
|
memset(entry, 0, sizeof(POOL_ITEM));
|
|
|
|
entry->TagUlong = poolTagInfo.TagUlong;
|
|
PhZeroExtendToUtf16Buffer(poolTagInfo.Tag, sizeof(poolTagInfo.Tag), entry->TagString);
|
|
|
|
PhUpdateDelta(&entry->PagedAllocsDelta, poolTagInfo.PagedAllocs);
|
|
PhUpdateDelta(&entry->PagedFreesDelta, poolTagInfo.PagedFrees);
|
|
PhUpdateDelta(&entry->PagedCurrentDelta, poolTagInfo.PagedAllocs - poolTagInfo.PagedFrees);
|
|
PhUpdateDelta(&entry->PagedTotalSizeDelta, poolTagInfo.PagedUsed);
|
|
PhUpdateDelta(&entry->NonPagedAllocsDelta, poolTagInfo.NonPagedAllocs);
|
|
PhUpdateDelta(&entry->NonPagedFreesDelta, poolTagInfo.NonPagedFrees);
|
|
PhUpdateDelta(&entry->NonPagedCurrentDelta, poolTagInfo.NonPagedAllocs - poolTagInfo.NonPagedFrees);
|
|
PhUpdateDelta(&entry->NonPagedTotalSizeDelta, poolTagInfo.NonPagedUsed);
|
|
|
|
UpdatePoolTagBinaryName(Context, entry, poolTagInfo.TagUlong);
|
|
|
|
PmAddPoolTagNode(Context, entry);
|
|
}
|
|
}
|
|
|
|
TreeNew_NodesStructured(Context->TreeNewHandle);
|
|
PhDereferenceObject(Context);
|
|
|
|
PhFree(poolTagTable);
|
|
}
|
|
|
|
BOOLEAN WordMatchStringRef(
|
|
_Inout_ PPOOLTAG_CONTEXT Context,
|
|
_In_ PPH_STRINGREF Text
|
|
)
|
|
{
|
|
PH_STRINGREF part;
|
|
PH_STRINGREF remainingPart;
|
|
|
|
remainingPart = Context->SearchboxText->sr;
|
|
|
|
while (remainingPart.Length != 0)
|
|
{
|
|
PhSplitStringRefAtChar(&remainingPart, '|', &part, &remainingPart);
|
|
|
|
if (part.Length != 0)
|
|
{
|
|
if (PhFindStringInStringRef(Text, &part, TRUE) != -1)
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOLEAN WordMatchStringZ(
|
|
_Inout_ PPOOLTAG_CONTEXT Context,
|
|
_In_ PWSTR Text
|
|
)
|
|
{
|
|
PH_STRINGREF text;
|
|
|
|
PhInitializeStringRef(&text, Text);
|
|
return WordMatchStringRef(Context, &text);
|
|
}
|
|
|
|
BOOLEAN PoolTagTreeFilterCallback(
|
|
_In_ PPH_TREENEW_NODE Node,
|
|
_In_opt_ PVOID Context
|
|
)
|
|
{
|
|
PPOOLTAG_CONTEXT context = Context;
|
|
PPOOLTAG_ROOT_NODE poolNode = (PPOOLTAG_ROOT_NODE)Node;
|
|
|
|
if (PhIsNullOrEmptyString(context->SearchboxText))
|
|
return TRUE;
|
|
|
|
if (poolNode->PoolItem->TagString[0] != 0)
|
|
{
|
|
if (WordMatchStringZ(context, poolNode->PoolItem->TagString))
|
|
return TRUE;
|
|
}
|
|
|
|
if (!PhIsNullOrEmptyString(poolNode->PoolItem->BinaryNameString))
|
|
{
|
|
if (WordMatchStringRef(context, &poolNode->PoolItem->BinaryNameString->sr))
|
|
return TRUE;
|
|
}
|
|
|
|
if (!PhIsNullOrEmptyString(poolNode->PoolItem->DescriptionString))
|
|
{
|
|
if (WordMatchStringRef(context, &poolNode->PoolItem->DescriptionString->sr))
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
VOID NTAPI ProcessesUpdatedCallback(
|
|
_In_opt_ PVOID Parameter,
|
|
_In_opt_ PVOID Context
|
|
)
|
|
{
|
|
PPOOLTAG_CONTEXT context = Context;
|
|
|
|
context->ProcessesUpdatedCount++;
|
|
|
|
if (context->ProcessesUpdatedCount < 2)
|
|
return;
|
|
|
|
PhReferenceObject(context);
|
|
UpdatePoolTagTable(Context);
|
|
}
|
|
|
|
INT_PTR CALLBACK PoolMonDlgProc(
|
|
_In_ HWND hwndDlg,
|
|
_In_ UINT uMsg,
|
|
_In_ WPARAM wParam,
|
|
_In_ LPARAM lParam
|
|
)
|
|
{
|
|
PPOOLTAG_CONTEXT context;
|
|
|
|
if (uMsg == WM_INITDIALOG)
|
|
{
|
|
context = PhCreateAlloc(sizeof(POOLTAG_CONTEXT));
|
|
memset(context, 0, sizeof(POOLTAG_CONTEXT));
|
|
|
|
SetProp(hwndDlg, L"Context", (HANDLE)context);
|
|
}
|
|
else
|
|
{
|
|
context = (PPOOLTAG_CONTEXT)GetProp(hwndDlg, L"Context");
|
|
|
|
if (uMsg == WM_DESTROY)
|
|
{
|
|
RemoveProp(hwndDlg, L"Context");
|
|
PhDereferenceObject(context);
|
|
}
|
|
}
|
|
|
|
if (!context)
|
|
return FALSE;
|
|
|
|
switch (uMsg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
context->ParentWindowHandle = hwndDlg;
|
|
context->TreeNewHandle = GetDlgItem(hwndDlg, IDC_POOLTREE);
|
|
context->SearchboxHandle = GetDlgItem(hwndDlg, IDC_SEARCH);
|
|
|
|
PhRegisterDialog(hwndDlg);
|
|
PhCenterWindow(hwndDlg, PhMainWndHandle);
|
|
|
|
PmInitializePoolTagTree(context);
|
|
|
|
PhInitializeLayoutManager(&context->LayoutManager, hwndDlg);
|
|
PhAddLayoutItem(&context->LayoutManager, context->TreeNewHandle, NULL, PH_ANCHOR_ALL);
|
|
PhAddLayoutItem(&context->LayoutManager, context->SearchboxHandle, NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT);
|
|
PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_CLEAR), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT);
|
|
PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDCANCEL), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_RIGHT);
|
|
PhLoadWindowPlacementFromSetting(SETTING_NAME_WINDOW_POSITION, SETTING_NAME_WINDOW_SIZE, hwndDlg);
|
|
|
|
context->SearchboxText = PhReferenceEmptyString();
|
|
context->TreeFilterEntry = PhAddTreeNewFilter(
|
|
&context->FilterSupport,
|
|
PoolTagTreeFilterCallback,
|
|
context
|
|
);
|
|
|
|
LoadPoolTagDatabase(context);
|
|
|
|
PhReferenceObject(context);
|
|
UpdatePoolTagTable(context);
|
|
TreeNew_AutoSizeColumn(context->TreeNewHandle, TREE_COLUMN_ITEM_DESCRIPTION, TN_AUTOSIZE_REMAINING_SPACE);
|
|
|
|
PhRegisterCallback(
|
|
&PhProcessesUpdatedEvent,
|
|
ProcessesUpdatedCallback,
|
|
context,
|
|
&context->ProcessesUpdatedCallbackRegistration
|
|
);
|
|
}
|
|
break;
|
|
case WM_SIZE:
|
|
PhLayoutManagerLayout(&context->LayoutManager);
|
|
TreeNew_AutoSizeColumn(context->TreeNewHandle, TREE_COLUMN_ITEM_DESCRIPTION, TN_AUTOSIZE_REMAINING_SPACE);
|
|
break;
|
|
case WM_DESTROY:
|
|
{
|
|
PhUnregisterCallback(
|
|
&PhProcessesUpdatedEvent,
|
|
&context->ProcessesUpdatedCallbackRegistration
|
|
);
|
|
|
|
PhSaveWindowPlacementToSetting(SETTING_NAME_WINDOW_POSITION, SETTING_NAME_WINDOW_SIZE, hwndDlg);
|
|
PmSaveSettingsTreeList(context);
|
|
|
|
PhDeleteLayoutManager(&context->LayoutManager);
|
|
PhUnregisterDialog(hwndDlg);
|
|
|
|
PhDeleteTreeNewFilterSupport(&context->FilterSupport);
|
|
|
|
PmDeletePoolTagTree(context);
|
|
FreePoolTagDatabase(context);
|
|
|
|
PostQuitMessage(0);
|
|
}
|
|
break;
|
|
case POOL_TABLE_SHOWDIALOG:
|
|
{
|
|
if (IsMinimized(hwndDlg))
|
|
ShowWindow(hwndDlg, SW_RESTORE);
|
|
else
|
|
ShowWindow(hwndDlg, SW_SHOW);
|
|
|
|
SetForegroundWindow(hwndDlg);
|
|
}
|
|
break;
|
|
case WM_COMMAND:
|
|
{
|
|
switch (GET_WM_COMMAND_ID(wParam, lParam))
|
|
{
|
|
case IDCANCEL:
|
|
DestroyWindow(hwndDlg);
|
|
break;
|
|
case IDC_CLEAR:
|
|
{
|
|
SetFocus(context->SearchboxHandle);
|
|
Static_SetText(context->SearchboxHandle, L"");
|
|
}
|
|
break;
|
|
case IDC_SEARCH:
|
|
{
|
|
PPH_STRING newSearchboxText;
|
|
|
|
if (GET_WM_COMMAND_CMD(wParam, lParam) != EN_CHANGE)
|
|
break;
|
|
|
|
newSearchboxText = PH_AUTO(PhGetWindowText(context->SearchboxHandle));
|
|
|
|
if (!PhEqualString(context->SearchboxText, newSearchboxText, FALSE))
|
|
{
|
|
// Cache the current search text for our callback.
|
|
PhSwapReference(&context->SearchboxText, newSearchboxText);
|
|
|
|
PhApplyTreeNewFilters(&context->FilterSupport);
|
|
}
|
|
}
|
|
break;
|
|
case POOL_TABLE_SHOWCONTEXTMENU:
|
|
{
|
|
PPH_EMENU menu;
|
|
PPOOLTAG_ROOT_NODE selectedNode;
|
|
PPH_EMENU_ITEM selectedItem;
|
|
PPH_TREENEW_MOUSE_EVENT mouseEvent = (PPH_TREENEW_MOUSE_EVENT)lParam;
|
|
|
|
if (selectedNode = PmGetSelectedPoolTagNode(context))
|
|
{
|
|
menu = PhCreateEMenu();
|
|
PhInsertEMenuItem(menu, PhCreateEMenuItem(0, 1, L"Show allocations", NULL, NULL), -1);
|
|
PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1);
|
|
PhInsertEMenuItem(menu, PhCreateEMenuItem(0, 2, L"Edit description...", NULL, NULL), -1);
|
|
|
|
selectedItem = PhShowEMenu(
|
|
menu,
|
|
hwndDlg,
|
|
PH_EMENU_SHOW_LEFTRIGHT,
|
|
PH_ALIGN_LEFT | PH_ALIGN_TOP,
|
|
mouseEvent->Location.x,
|
|
mouseEvent->Location.y
|
|
);
|
|
|
|
if (selectedItem && selectedItem->Id != -1)
|
|
{
|
|
switch (selectedItem->Id)
|
|
{
|
|
case 1:
|
|
ShowBigPoolDialog(selectedNode->PoolItem);
|
|
break;
|
|
}
|
|
}
|
|
|
|
PhDestroyEMenu(menu);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
NTSTATUS ShowPoolMonDialogThread(
|
|
_In_ PVOID Parameter
|
|
)
|
|
{
|
|
BOOL result;
|
|
MSG message;
|
|
PH_AUTO_POOL autoPool;
|
|
|
|
PhInitializeAutoPool(&autoPool);
|
|
|
|
PoolTagDialogHandle = CreateDialog(
|
|
PluginInstance->DllBase,
|
|
MAKEINTRESOURCE(IDD_POOL),
|
|
NULL,
|
|
PoolMonDlgProc
|
|
);
|
|
|
|
PhSetEvent(&PoolTagDialogInitializedEvent);
|
|
|
|
PostMessage(PoolTagDialogHandle, POOL_TABLE_SHOWDIALOG, 0, 0);
|
|
|
|
while (result = GetMessage(&message, NULL, 0, 0))
|
|
{
|
|
if (result == -1)
|
|
break;
|
|
|
|
if (!IsDialogMessage(PoolTagDialogHandle, &message))
|
|
{
|
|
TranslateMessage(&message);
|
|
DispatchMessage(&message);
|
|
}
|
|
|
|
PhDrainAutoPool(&autoPool);
|
|
}
|
|
|
|
PhDeleteAutoPool(&autoPool);
|
|
|
|
if (PoolTagDialogThreadHandle)
|
|
{
|
|
NtClose(PoolTagDialogThreadHandle);
|
|
PoolTagDialogThreadHandle = NULL;
|
|
}
|
|
|
|
PhResetEvent(&PoolTagDialogInitializedEvent);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
VOID ShowPoolMonDialog(
|
|
VOID
|
|
)
|
|
{
|
|
if (!PoolTagDialogThreadHandle)
|
|
{
|
|
if (!(PoolTagDialogThreadHandle = PhCreateThread(0, ShowPoolMonDialogThread, NULL)))
|
|
{
|
|
PhShowStatus(PhMainWndHandle, L"Unable to create the pool monitor window.", 0, GetLastError());
|
|
return;
|
|
}
|
|
|
|
PhWaitForEvent(&PoolTagDialogInitializedEvent, NULL);
|
|
}
|
|
|
|
PostMessage(PoolTagDialogHandle, POOL_TABLE_SHOWDIALOG, 0, 0);
|
|
} |