2025-05-13 19:49:49 +03:00

589 lines
19 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"
#define SORT_FUNCTION(Column) PmPoolTreeNewCompare##Column
#define BEGIN_SORT_FUNCTION(Column) static int __cdecl PmPoolTreeNewCompare##Column( \
_In_ void *_context, \
_In_ const void *_elem1, \
_In_ const void *_elem2 \
) \
{ \
PPOOLTAG_ROOT_NODE node1 = *(PPOOLTAG_ROOT_NODE*)_elem1; \
PPOOLTAG_ROOT_NODE node2 = *(PPOOLTAG_ROOT_NODE*)_elem2; \
PPOOL_ITEM poolItem1 = node1->PoolItem; \
PPOOL_ITEM poolItem2 = node2->PoolItem; \
int sortResult = 0;
#define END_SORT_FUNCTION \
if (sortResult == 0) \
sortResult = uintptrcmp((ULONG_PTR)poolItem1->TagUlong, (ULONG_PTR)poolItem2->TagUlong); \
\
return PhModifySort(sortResult, ((PPOOLTAG_CONTEXT)_context)->TreeNewSortOrder); \
}
VOID PmLoadSettingsTreeList(
_Inout_ PPOOLTAG_CONTEXT Context
)
{
PPH_STRING settings;
PH_INTEGER_PAIR sortSettings;
settings = PhGetStringSetting(SETTING_NAME_TREE_LIST_COLUMNS);
PhCmLoadSettings(Context->TreeNewHandle, &settings->sr);
PhDereferenceObject(settings);
sortSettings = PhGetIntegerPairSetting(SETTING_NAME_TREE_LIST_SORT);
TreeNew_SetSort(Context->TreeNewHandle, (ULONG)sortSettings.X, (PH_SORT_ORDER)sortSettings.Y);
}
VOID PmSaveSettingsTreeList(
_Inout_ PPOOLTAG_CONTEXT Context
)
{
PPH_STRING settings;
PH_INTEGER_PAIR sortSettings;
ULONG sortColumn;
PH_SORT_ORDER sortOrder;
settings = PhCmSaveSettings(Context->TreeNewHandle);
PhSetStringSetting2(SETTING_NAME_TREE_LIST_COLUMNS, &settings->sr);
PhDereferenceObject(settings);
TreeNew_GetSort(Context->TreeNewHandle, &sortColumn, &sortOrder);
sortSettings.X = sortColumn;
sortSettings.Y = sortOrder;
PhSetIntegerPairSetting(SETTING_NAME_TREE_LIST_SORT, sortSettings);
}
BOOLEAN PmPoolTagNodeHashtableEqualFunction(
_In_ PVOID Entry1,
_In_ PVOID Entry2
)
{
PPOOLTAG_ROOT_NODE poolTagNode1 = *(PPOOLTAG_ROOT_NODE *)Entry1;
PPOOLTAG_ROOT_NODE poolTagNode2 = *(PPOOLTAG_ROOT_NODE *)Entry2;
return poolTagNode1->TagUlong == poolTagNode2->TagUlong;
}
ULONG PmPoolTagNodeHashtableHashFunction(
_In_ PVOID Entry
)
{
return (*(PPOOLTAG_ROOT_NODE*)Entry)->TagUlong;
}
VOID PmDestroyPoolTagNode(
_In_ PPOOLTAG_ROOT_NODE PoolTagNode
)
{
PhClearReference(&PoolTagNode->PagedAllocsDeltaString);
PhClearReference(&PoolTagNode->PagedFreesDeltaString);
PhClearReference(&PoolTagNode->PagedCurrentDeltaString);
PhClearReference(&PoolTagNode->PagedTotalSizeDeltaString);
PhClearReference(&PoolTagNode->NonPagedAllocsDeltaString);
PhClearReference(&PoolTagNode->NonPagedFreesDeltaString);
PhClearReference(&PoolTagNode->NonPagedCurrentDeltaString);
PhClearReference(&PoolTagNode->NonPagedTotalSizeDeltaString);
PhFree(PoolTagNode);
}
PPOOLTAG_ROOT_NODE PmAddPoolTagNode(
_Inout_ PPOOLTAG_CONTEXT Context,
_In_ PPOOL_ITEM PoolItem
)
{
PPOOLTAG_ROOT_NODE poolTagNode;
poolTagNode = PhAllocate(sizeof(POOLTAG_ROOT_NODE));
memset(poolTagNode, 0, sizeof(POOLTAG_ROOT_NODE));
PhInitializeTreeNewNode(&poolTagNode->Node);
poolTagNode->PoolItem = PoolItem;
poolTagNode->TagUlong = PoolItem->TagUlong;
memset(poolTagNode->TextCache, 0, sizeof(PH_STRINGREF) * TREE_COLUMN_ITEM_MAXIMUM);
poolTagNode->Node.TextCache = poolTagNode->TextCache;
poolTagNode->Node.TextCacheSize = TREE_COLUMN_ITEM_MAXIMUM;
PhAddEntryHashtable(Context->NodeHashtable, &poolTagNode);
PhAddItemList(Context->NodeList, poolTagNode);
if (Context->FilterSupport.FilterList)
poolTagNode->Node.Visible = PhApplyTreeNewFiltersToNode(&Context->FilterSupport, &poolTagNode->Node);
return poolTagNode;
}
PPOOLTAG_ROOT_NODE PmFindPoolTagNode(
_In_ PPOOLTAG_CONTEXT Context,
_In_ ULONG PoolTag
)
{
POOLTAG_ROOT_NODE lookupWindowNode;
PPOOLTAG_ROOT_NODE lookupWindowNodePtr = &lookupWindowNode;
PPOOLTAG_ROOT_NODE *windowNode;
lookupWindowNode.TagUlong = PoolTag;
windowNode = (PPOOLTAG_ROOT_NODE*)PhFindEntryHashtable(
Context->NodeHashtable,
&lookupWindowNodePtr
);
if (windowNode)
return *windowNode;
else
return NULL;
}
VOID PmRemovePoolTagNode(
_In_ PPOOLTAG_CONTEXT Context,
_In_ PPOOLTAG_ROOT_NODE PoolTagNode
)
{
ULONG index = 0;
// Remove from hashtable/list and cleanup.
PhRemoveEntryHashtable(Context->NodeHashtable, &PoolTagNode);
if ((index = PhFindItemList(Context->NodeList, PoolTagNode)) != -1)
{
PhRemoveItemList(Context->NodeList, index);
}
PmDestroyPoolTagNode(PoolTagNode);
//TreeNew_NodesStructured(Context->TreeNewHandle);
}
VOID PmUpdatePoolTagNode(
_In_ PPOOLTAG_CONTEXT Context,
_In_ PPOOLTAG_ROOT_NODE PoolTagNode
)
{
memset(PoolTagNode->TextCache, 0, sizeof(PH_STRINGREF) * TREE_COLUMN_ITEM_MAXIMUM);
PhInvalidateTreeNewNode(&PoolTagNode->Node, TN_CACHE_COLOR);
//TreeNew_NodesStructured(Context->TreeNewHandle);
}
BEGIN_SORT_FUNCTION(Name)
{
sortResult = PhCompareStringZ(poolItem1->TagString, poolItem2->TagString, FALSE);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(Type)
{
sortResult = PhCompareStringWithNull(poolItem1->BinaryNameString, poolItem2->BinaryNameString, FALSE);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(Description)
{
sortResult = PhCompareStringWithNull(poolItem1->DescriptionString, poolItem2->DescriptionString, TRUE);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(PagedAlloc)
{
sortResult = uint64cmp(poolItem1->PagedAllocsDelta.Value, poolItem2->PagedAllocsDelta.Value);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(PagedFree)
{
sortResult = uint64cmp(poolItem1->PagedFreesDelta.Value, poolItem2->PagedFreesDelta.Value);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(PagedCurrent)
{
sortResult = uint64cmp(poolItem1->PagedCurrentDelta.Value, poolItem2->PagedCurrentDelta.Value);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(PagedTotal)
{
sortResult = uint64cmp(poolItem1->PagedTotalSizeDelta.Value, poolItem2->PagedTotalSizeDelta.Value);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(NonPagedAlloc)
{
sortResult = uint64cmp(poolItem1->NonPagedAllocsDelta.Value, poolItem2->NonPagedAllocsDelta.Value);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(NonPagedFree)
{
sortResult = uint64cmp(poolItem1->NonPagedFreesDelta.Value, poolItem2->NonPagedFreesDelta.Value);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(NonPagedCurrent)
{
sortResult = uint64cmp(poolItem1->NonPagedCurrentDelta.Value, poolItem2->NonPagedCurrentDelta.Value);
}
END_SORT_FUNCTION
BEGIN_SORT_FUNCTION(NonPagedTotal)
{
sortResult = uint64cmp(poolItem1->NonPagedTotalSizeDelta.Value, poolItem2->NonPagedTotalSizeDelta.Value);
}
END_SORT_FUNCTION
BOOLEAN NTAPI PmPoolTagTreeNewCallback(
_In_ HWND hwnd,
_In_ PH_TREENEW_MESSAGE Message,
_In_opt_ PVOID Parameter1,
_In_opt_ PVOID Parameter2,
_In_opt_ PVOID Context
)
{
PPOOLTAG_CONTEXT context;
PPOOLTAG_ROOT_NODE node;
context = Context;
switch (Message)
{
case TreeNewGetChildren:
{
PPH_TREENEW_GET_CHILDREN getChildren = Parameter1;
node = (PPOOLTAG_ROOT_NODE)getChildren->Node;
if (!getChildren->Node)
{
static PVOID sortFunctions[] =
{
SORT_FUNCTION(Name),
SORT_FUNCTION(Type),
SORT_FUNCTION(Description),
SORT_FUNCTION(PagedAlloc),
SORT_FUNCTION(PagedFree),
SORT_FUNCTION(PagedCurrent),
SORT_FUNCTION(PagedTotal),
SORT_FUNCTION(NonPagedAlloc),
SORT_FUNCTION(NonPagedFree),
SORT_FUNCTION(NonPagedCurrent),
SORT_FUNCTION(NonPagedTotal),
};
int (__cdecl *sortFunction)(void *, const void *, const void *);
if (context->TreeNewSortColumn < TREE_COLUMN_ITEM_MAXIMUM)
sortFunction = sortFunctions[context->TreeNewSortColumn];
else
sortFunction = NULL;
if (sortFunction)
{
qsort_s(context->NodeList->Items, context->NodeList->Count, sizeof(PVOID), sortFunction, context);
}
getChildren->Children = (PPH_TREENEW_NODE *)context->NodeList->Items;
getChildren->NumberOfChildren = context->NodeList->Count;
}
}
return TRUE;
case TreeNewIsLeaf:
{
PPH_TREENEW_IS_LEAF isLeaf = (PPH_TREENEW_IS_LEAF)Parameter1;
node = (PPOOLTAG_ROOT_NODE)isLeaf->Node;
isLeaf->IsLeaf = TRUE;
}
return TRUE;
case TreeNewGetCellText:
{
PPH_TREENEW_GET_CELL_TEXT getCellText = (PPH_TREENEW_GET_CELL_TEXT)Parameter1;
PPOOL_ITEM poolItem;
node = (PPOOLTAG_ROOT_NODE)getCellText->Node;
poolItem = node->PoolItem;
switch (getCellText->Id)
{
case TREE_COLUMN_ITEM_TAG:
PhInitializeStringRefLongHint(&getCellText->Text, poolItem->TagString);
break;
case TREE_COLUMN_ITEM_DRIVER:
PhInitializeStringRefLongHint(&getCellText->Text, PhGetStringOrEmpty(poolItem->BinaryNameString));
break;
case TREE_COLUMN_ITEM_DESCRIPTION:
PhInitializeStringRefLongHint(&getCellText->Text, PhGetStringOrEmpty(poolItem->DescriptionString));
break;
case TREE_COLUMN_ITEM_PAGEDALLOC:
{
if (poolItem->PagedAllocsDelta.Value != 0)
{
PhMoveReference(&node->PagedAllocsDeltaString, PhFormatUInt64(poolItem->PagedAllocsDelta.Value, TRUE));
getCellText->Text = node->PagedAllocsDeltaString->sr;
}
}
break;
case TREE_COLUMN_ITEM_PAGEDFREE:
{
if (poolItem->PagedFreesDelta.Value != 0)
{
PhMoveReference(&node->PagedFreesDeltaString, PhFormatUInt64(poolItem->PagedFreesDelta.Value, TRUE));
getCellText->Text = node->PagedFreesDeltaString->sr;
}
}
break;
case TREE_COLUMN_ITEM_PAGEDCURRENT:
{
if (poolItem->PagedCurrentDelta.Value != 0)
{
PhMoveReference(&node->PagedCurrentDeltaString, PhFormatUInt64(poolItem->PagedCurrentDelta.Value, TRUE));
getCellText->Text = node->PagedCurrentDeltaString->sr;
}
}
break;
case TREE_COLUMN_ITEM_PAGEDTOTAL:
{
if (poolItem->PagedTotalSizeDelta.Value != 0)
{
PhMoveReference(&node->PagedTotalSizeDeltaString, PhFormatSize(poolItem->PagedTotalSizeDelta.Value, -1));
getCellText->Text = node->PagedTotalSizeDeltaString->sr;
}
}
break;
case TREE_COLUMN_ITEM_NONPAGEDALLOC:
{
if (poolItem->NonPagedAllocsDelta.Value != 0)
{
PhMoveReference(&node->NonPagedAllocsDeltaString, PhFormatUInt64(poolItem->NonPagedAllocsDelta.Value, TRUE));
getCellText->Text = node->NonPagedAllocsDeltaString->sr;
}
}
break;
case TREE_COLUMN_ITEM_NONPAGEDFREE:
{
if (poolItem->NonPagedFreesDelta.Value != 0)
{
PhMoveReference(&node->NonPagedFreesDeltaString, PhFormatUInt64(poolItem->NonPagedFreesDelta.Value, TRUE));
getCellText->Text = node->NonPagedFreesDeltaString->sr;
}
}
break;
case TREE_COLUMN_ITEM_NONPAGEDCURRENT:
{
if (poolItem->NonPagedCurrentDelta.Value != 0)
{
PhMoveReference(&node->NonPagedCurrentDeltaString, PhFormatUInt64(poolItem->NonPagedCurrentDelta.Value, TRUE));
getCellText->Text = node->NonPagedCurrentDeltaString->sr;
}
}
break;
case TREE_COLUMN_ITEM_NONPAGEDTOTAL:
{
if (poolItem->NonPagedTotalSizeDelta.Value != 0)
{
PhMoveReference(&node->NonPagedTotalSizeDeltaString, PhFormatSize(poolItem->NonPagedTotalSizeDelta.Value, -1));
getCellText->Text = node->NonPagedTotalSizeDeltaString->sr;
}
}
break;
default:
return FALSE;
}
getCellText->Flags = TN_CACHE;
}
return TRUE;
case TreeNewGetNodeColor:
{
PPH_TREENEW_GET_NODE_COLOR getNodeColor = (PPH_TREENEW_GET_NODE_COLOR)Parameter1;
node = (PPOOLTAG_ROOT_NODE)getNodeColor->Node;
switch (node->PoolItem->Type)
{
case TPOOLTAG_TREE_ITEM_TYPE_OBJECT:
getNodeColor->BackColor = RGB(204, 255, 255);
break;
case TPOOLTAG_TREE_ITEM_TYPE_DRIVER:
getNodeColor->BackColor = RGB(170, 204, 255);
break;
}
getNodeColor->Flags = TN_CACHE | TN_AUTO_FORECOLOR;
}
return TRUE;
case TreeNewSortChanged:
{
TreeNew_GetSort(hwnd, &context->TreeNewSortColumn, &context->TreeNewSortOrder);
// Force a rebuild to sort the items.
TreeNew_NodesStructured(hwnd);
}
return TRUE;
case TreeNewContextMenu:
{
PPH_TREENEW_MOUSE_EVENT mouseEvent = (PPH_TREENEW_MOUSE_EVENT)Parameter1;
SendMessage(
context->ParentWindowHandle,
WM_COMMAND,
POOL_TABLE_SHOWCONTEXTMENU,
(LPARAM)mouseEvent
);
}
return TRUE;
case TreeNewHeaderRightClick:
{
PH_TN_COLUMN_MENU_DATA data;
data.TreeNewHandle = hwnd;
data.MouseEvent = Parameter1;
data.DefaultSortColumn = 0;
data.DefaultSortOrder = AscendingSortOrder;
PhInitializeTreeNewColumnMenu(&data);
data.Selection = PhShowEMenu(data.Menu, hwnd, PH_EMENU_SHOW_LEFTRIGHT,
PH_ALIGN_LEFT | PH_ALIGN_TOP, data.MouseEvent->ScreenLocation.x, data.MouseEvent->ScreenLocation.y);
PhHandleTreeNewColumnMenu(&data);
PhDeleteTreeNewColumnMenu(&data);
}
return TRUE;
}
return FALSE;
}
VOID PmClearPoolTagTree(
_In_ PPOOLTAG_CONTEXT Context
)
{
ULONG i;
for (i = 0; i < Context->NodeList->Count; i++)
PmDestroyPoolTagNode(Context->NodeList->Items[i]);
PhClearHashtable(Context->NodeHashtable);
PhClearList(Context->NodeList);
}
PPOOLTAG_ROOT_NODE PmGetSelectedPoolTagNode(
_In_ PPOOLTAG_CONTEXT Context
)
{
PPOOLTAG_ROOT_NODE windowNode = NULL;
ULONG i;
for (i = 0; i < Context->NodeList->Count; i++)
{
windowNode = Context->NodeList->Items[i];
if (windowNode->Node.Selected)
return windowNode;
}
return NULL;
}
VOID PmGetSelectedPoolTagNodes(
_In_ PPOOLTAG_CONTEXT Context,
_Out_ PPOOLTAG_ROOT_NODE **PoolTags,
_Out_ PULONG NumberOfPoolTags
)
{
PPH_LIST list;
ULONG i;
list = PhCreateList(2);
for (i = 0; i < Context->NodeList->Count; i++)
{
PPOOLTAG_ROOT_NODE node = (PPOOLTAG_ROOT_NODE)Context->NodeList->Items[i];
if (node->Node.Selected)
{
PhAddItemList(list, node);
}
}
*PoolTags = PhAllocateCopy(list->Items, sizeof(PVOID) * list->Count);
*NumberOfPoolTags = list->Count;
PhDereferenceObject(list);
}
VOID PmInitializePoolTagTree(
_Inout_ PPOOLTAG_CONTEXT Context
)
{
Context->NodeList = PhCreateList(100);
Context->NodeHashtable = PhCreateHashtable(
sizeof(PPOOLTAG_ROOT_NODE),
PmPoolTagNodeHashtableEqualFunction,
PmPoolTagNodeHashtableHashFunction,
100
);
PhSetControlTheme(Context->TreeNewHandle, L"explorer");
TreeNew_SetCallback(Context->TreeNewHandle, PmPoolTagTreeNewCallback, Context);
PhAddTreeNewColumn(Context->TreeNewHandle, TREE_COLUMN_ITEM_TAG, TRUE, L"Tag Name", 50, PH_ALIGN_LEFT, -2, 0);
PhAddTreeNewColumn(Context->TreeNewHandle, TREE_COLUMN_ITEM_DRIVER, TRUE, L"Driver", 82, PH_ALIGN_LEFT, 0, 0);
PhAddTreeNewColumn(Context->TreeNewHandle, TREE_COLUMN_ITEM_DESCRIPTION, TRUE, L"Description", 140, PH_ALIGN_LEFT, 1, 0);
PhAddTreeNewColumn(Context->TreeNewHandle, TREE_COLUMN_ITEM_PAGEDALLOC, TRUE, L"Paged Allocations", 80, PH_ALIGN_RIGHT, 2, DT_RIGHT);
PhAddTreeNewColumn(Context->TreeNewHandle, TREE_COLUMN_ITEM_PAGEDFREE, TRUE, L"Paged Frees", 80, PH_ALIGN_RIGHT, 3, DT_RIGHT);
PhAddTreeNewColumn(Context->TreeNewHandle, TREE_COLUMN_ITEM_PAGEDCURRENT, TRUE, L"Paged Current", 80, PH_ALIGN_RIGHT, 4, DT_RIGHT);
PhAddTreeNewColumn(Context->TreeNewHandle, TREE_COLUMN_ITEM_PAGEDTOTAL, TRUE, L"Paged Bytes Total", 80, PH_ALIGN_LEFT, 5, 0);
PhAddTreeNewColumn(Context->TreeNewHandle, TREE_COLUMN_ITEM_NONPAGEDALLOC, TRUE, L"Non-paged Allocations", 80, PH_ALIGN_RIGHT, 6, DT_RIGHT);
PhAddTreeNewColumn(Context->TreeNewHandle, TREE_COLUMN_ITEM_NONPAGEDFREE, TRUE, L"Non-paged Frees", 80, PH_ALIGN_RIGHT, 7, DT_RIGHT);
PhAddTreeNewColumn(Context->TreeNewHandle, TREE_COLUMN_ITEM_NONPAGEDCURRENT, TRUE, L"Non-paged Current", 80, PH_ALIGN_RIGHT, 8, DT_RIGHT);
PhAddTreeNewColumn(Context->TreeNewHandle, TREE_COLUMN_ITEM_NONPAGEDTOTAL, TRUE, L"Non-paged Bytes Total", 80, PH_ALIGN_LEFT, 9, 0);
TreeNew_SetTriState(Context->TreeNewHandle, TRUE);
TreeNew_SetSort(Context->TreeNewHandle, TREE_COLUMN_ITEM_NONPAGEDTOTAL, DescendingSortOrder);
PmLoadSettingsTreeList(Context);
PhInitializeTreeNewFilterSupport(&Context->FilterSupport, Context->TreeNewHandle, Context->NodeList);
}
VOID PmDeletePoolTagTree(
_In_ PPOOLTAG_CONTEXT Context
)
{
PPH_STRING settings;
settings = PhCmSaveSettings(Context->TreeNewHandle);
PhSetStringSetting2(SETTING_NAME_TREE_LIST_COLUMNS, &settings->sr);
PhDereferenceObject(settings);
for (ULONG i = 0; i < Context->NodeList->Count; i++)
{
PmDestroyPoolTagNode(Context->NodeList->Items[i]);
}
PhDereferenceObject(Context->NodeHashtable);
PhDereferenceObject(Context->NodeList);
}