WinObjEx64/Source/WinObjAdv/propProcess.c

617 lines
14 KiB
C

/*******************************************************************************
*
* (C) COPYRIGHT AUTHORS, 2015
*
* TITLE: PROPPROCESS.C
*
* VERSION: 1.00
*
* DATE: 19 Feb 2015
*
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
* PARTICULAR PURPOSE.
*
*******************************************************************************/
#include "global.h"
#include "propDlg.h"
#include "propProcess.h"
//page imagelist
HIMAGELIST ProcessImageList = NULL;
//page listview
HWND ProcessList = NULL;
//column to sort
static LONG ProcessListSortColumn = 0;
//sort direction
BOOL bProcessListSortInverse = FALSE;
/*
* ProcessListCompareFunc
*
* Purpose:
*
* Process page listview comparer function.
*
*/
INT CALLBACK ProcessListCompareFunc(
_In_ LPARAM lParam1,
_In_ LPARAM lParam2,
_In_ LPARAM lParamSort
)
{
LPWSTR lpItem1, lpItem2;
INT nResult, k;
SIZE_T sz1, sz2;
ULONG_PTR Value1, Value2;
sz1 = 0;
lpItem1 = supGetItemText(ProcessList, (INT)lParam1, (INT)lParamSort, &sz1);
if (lpItem1 == NULL)
return 0;
sz2 = 0;
lpItem2 = supGetItemText(ProcessList, (INT)lParam2, (INT)lParamSort, &sz2);
if (lpItem2 == NULL)
return 0;
switch (lParamSort) {
case 0: //name column
if (bProcessListSortInverse)
nResult = _strcmpi(lpItem2, lpItem1);
else
nResult = _strcmpi(lpItem1, lpItem2);
break;
case 1: // id column
Value1 = strtou64(lpItem1);
Value2 = strtou64(lpItem2);
if (bProcessListSortInverse)
nResult = Value2 > Value1;
else
nResult = Value1 > Value2;
break;
//anything else is hex
default:
k = 0;
if ((sz1 > 1) && (sz2 > 1)) {
if (lpItem1[1] == L'x')
k = 2;
}
Value1 = hextou64(&lpItem1[k]);
Value2 = hextou64(&lpItem2[k]);
if (bProcessListSortInverse)
nResult = Value2 > Value1;
else
nResult = Value1 > Value2;
break;
}
HeapFree(GetProcessHeap(), 0, lpItem1);
HeapFree(GetProcessHeap(), 0, lpItem2);
return nResult;
}
/*
* ProcessListHandleNotify
*
* Purpose:
*
* WM_NOTIFY processing for Process page listview.
*
*/
VOID ProcessListHandleNotify(
LPNMLISTVIEW nhdr
)
{
LVCOLUMNW col;
INT c;
if (nhdr == NULL)
return;
if (nhdr->hdr.idFrom != ID_PROCESSLIST)
return;
switch (nhdr->hdr.code) {
case LVN_COLUMNCLICK:
bProcessListSortInverse = !bProcessListSortInverse;
ProcessListSortColumn = ((NMLISTVIEW *)nhdr)->iSubItem;
ListView_SortItemsEx(ProcessList, &ProcessListCompareFunc, ProcessListSortColumn);
RtlSecureZeroMemory(&col, sizeof(col));
col.mask = LVCF_IMAGE;
col.iImage = -1;
for (c = 0; c < 4; c++)
ListView_SetColumn(ProcessList, c, &col);
if (bProcessListSortInverse)
col.iImage = 1;
else
col.iImage = 2;
ListView_SetColumn(ProcessList, ((NMLISTVIEW *)nhdr)->iSubItem, &col);
break;
default:
break;
}
}
/*
* ProcessQueryInfo
*
* Purpose:
*
* Extracts icon resource from given process for use in listview and determines process WOW64 status
*
*/
BOOL ProcessQueryInfo(
_In_ DWORD ProcessId,
_Out_ HICON *pProcessIcon,
_Out_ BOOL *pbIs32
)
{
BOOL bResult = FALSE, bIconFound, bWow64State;
ULONG bytesNeeded;
HANDLE hProcess;
NTSTATUS status;
PVOID Buffer;
PUNICODE_STRING dynUstr;
OBJECT_ATTRIBUTES obja;
CLIENT_ID cid;
PROCESS_EXTENDED_BASIC_INFORMATION pebi;
if ((pProcessIcon == NULL) || (pbIs32 == NULL)) {
return bResult;
}
*pProcessIcon = NULL;
*pbIs32 = FALSE;
bWow64State = FALSE;
bIconFound = FALSE;
__try {
cid.UniqueProcess = (HANDLE)ProcessId;
cid.UniqueThread = NULL;
InitializeObjectAttributes(&obja, NULL, 0, NULL, NULL);
status = NtOpenProcess(&hProcess, PROCESS_QUERY_LIMITED_INFORMATION, &obja, &cid);
if (!NT_SUCCESS(status)) {
return bResult;
}
//query process icon, first query win32 imagefilename then parse image resources
bytesNeeded = 0;
NtQueryInformationProcess(hProcess, ProcessImageFileNameWin32, NULL, 0, &bytesNeeded);
if (bytesNeeded) {
Buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bytesNeeded);
if (Buffer) {
status = NtQueryInformationProcess(hProcess, ProcessImageFileNameWin32, Buffer, bytesNeeded, &bytesNeeded);
if (NT_SUCCESS(status)) {
dynUstr = (PUNICODE_STRING)Buffer;
if (dynUstr->Buffer && dynUstr->Length) {
*pProcessIcon = supGetMainIcon(dynUstr->Buffer, 16, 16);
bIconFound = TRUE;
}
}
HeapFree(GetProcessHeap(), 0, Buffer);
}
}
//query if this is wow64 process
RtlSecureZeroMemory(&pebi, sizeof(pebi));
pebi.Size = sizeof(PROCESS_EXTENDED_BASIC_INFORMATION);
status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pebi, sizeof(pebi), NULL);
if (NT_SUCCESS(status)) {
*pbIs32 = (pebi.IsWow64Process);
bWow64State = TRUE;
}
NtClose(hProcess);
}
__except (exceptFilter(GetExceptionCode(), GetExceptionInformation())) {
return FALSE;
}
bResult = (bIconFound && bWow64State);
return bResult;
}
/*
* ProcessListAddItem
*
* Purpose:
*
* Adds an item to the listview.
*
*/
VOID ProcessListAddItem(
_In_ PVOID ProcessesList,
_In_ PSYSTEM_HANDLE_TABLE_ENTRY_INFO phti
)
{
BOOL bIsWow64;
INT nIndex, iImage;
LVITEMW lvitem;
HICON hIcon;
WCHAR szBuffer[MAX_PATH * 2];
if ((phti == NULL) || (ProcessesList == NULL)) {
return;
}
//default image index
iImage = 0;
//set default process name as Unknown
RtlSecureZeroMemory(&szBuffer, sizeof(szBuffer));
_strcpyW(szBuffer, T_Unknown);
if (supQueryProcessName(phti->UniqueProcessId,
ProcessesList, szBuffer, MAX_PATH)) {
//id exists, extract icon
//skip idle, system
if (phti->UniqueProcessId <= 4) {
iImage = 0;
}
else {
hIcon = NULL;
bIsWow64 = FALSE;
if (ProcessQueryInfo(phti->UniqueProcessId, &hIcon, &bIsWow64)) {
if (hIcon) {
iImage = ImageList_ReplaceIcon(ProcessImageList, -1, hIcon);
DestroyIcon(hIcon);
}
if (bIsWow64) {
_strcatW(szBuffer, L"*32");
}
} //ProcessQueryInfo
} //else
}
//Name
RtlSecureZeroMemory(&lvitem, sizeof(lvitem));
lvitem.mask = LVIF_TEXT | LVIF_IMAGE;
lvitem.iImage = iImage;
lvitem.iSubItem = 0;
lvitem.pszText = szBuffer;
lvitem.iItem = MAXINT;
nIndex = ListView_InsertItem(ProcessList, &lvitem);
//ID
RtlSecureZeroMemory(&szBuffer, sizeof(szBuffer));
ultostr(phti->UniqueProcessId, szBuffer);
lvitem.mask = LVIF_TEXT;
lvitem.iSubItem = 1;
lvitem.pszText = szBuffer;
lvitem.iItem = nIndex;
ListView_SetItem(ProcessList, &lvitem);
//Value
RtlSecureZeroMemory(&szBuffer, sizeof(szBuffer));
_strcpyW(szBuffer, L"0x");
ultohex(phti->HandleValue, _strendW(szBuffer));
lvitem.mask = LVIF_TEXT;
lvitem.iSubItem = 2;
lvitem.pszText = szBuffer;
lvitem.iItem = nIndex;
ListView_SetItem(ProcessList, &lvitem);
//GrantedAccess
RtlSecureZeroMemory(&szBuffer, sizeof(szBuffer));
_strcpyW(szBuffer, L"0x");
ultohex(phti->GrantedAccess, _strendW(szBuffer));
lvitem.mask = LVIF_TEXT;
lvitem.iSubItem = 3;
lvitem.pszText = szBuffer;
lvitem.iItem = nIndex;
ListView_SetItem(ProcessList, &lvitem);
}
/*
* ProcessListSetInfo
*
* Purpose:
*
* Query information and fill listview.
* Called each time when page became visible.
*
*/
VOID ProcessListSetInfo(
PROP_OBJECT_INFO *Context,
_In_ HWND hwndDlg
)
{
BOOL cond = FALSE;
UCHAR ObjectTypeIndex;
ULONG i;
DWORD CurrentProcessId = GetCurrentProcessId();
ULONG_PTR ObjectAddress;
HICON hIcon;
ACCESS_MASK DesiredAccess;
PVOID ProcessesList;
HANDLE hObject, tmpb;
PSYSTEM_HANDLE_INFORMATION pHandles;
if (Context == NULL) {
return;
}
hObject = NULL;
pHandles = NULL;
ProcessesList = NULL;
ObjectAddress = 0;
ObjectTypeIndex = 0;
//empty process list images
ImageList_RemoveAll(ProcessImageList);
//empty process list
ListView_DeleteAllItems(GetDlgItem(hwndDlg, ID_PROCESSLIST));
//set default app icon
hIcon = LoadIcon(NULL, IDI_APPLICATION);
if (hIcon) {
ImageList_ReplaceIcon(ProcessImageList, -1, hIcon);
DestroyIcon(hIcon);
}
//sort images
tmpb = LoadImage(g_hInstance, MAKEINTRESOURCE(IDI_ICON_SORTUP), IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR);
if (tmpb) {
ImageList_ReplaceIcon(ProcessImageList, -1, tmpb);
DestroyIcon(tmpb);
}
tmpb = LoadImage(g_hInstance, MAKEINTRESOURCE(IDI_ICON_SORTDOWN), IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR);
if (tmpb) {
ImageList_ReplaceIcon(ProcessImageList, -1, tmpb);
DestroyIcon(tmpb);
}
//check if additional info available
if (Context->ObjectInfo.ObjectAddress != 0) {
ObjectAddress = Context->ObjectInfo.ObjectAddress;
ObjectTypeIndex = Context->ObjectInfo.ObjectHeader.TypeIndex;
}
do {
//object info not present
if (ObjectAddress == 0) {
switch (Context->TypeIndex) {
case TYPE_DIRECTORY:
DesiredAccess = DIRECTORY_QUERY;
break;
case TYPE_EVENT:
DesiredAccess = EVENT_QUERY_STATE;
break;
case TYPE_MUTANT:
DesiredAccess = MUTANT_QUERY_STATE;
break;
case TYPE_SEMAPHORE:
DesiredAccess = SEMAPHORE_QUERY_STATE;
break;
case TYPE_SECTION:
DesiredAccess = SECTION_QUERY;
break;
case TYPE_SYMLINK:
DesiredAccess = SYMBOLIC_LINK_QUERY;
break;
case TYPE_TIMER:
DesiredAccess = TIMER_QUERY_STATE;
break;
case TYPE_JOB:
DesiredAccess = JOB_OBJECT_QUERY;
break;
case TYPE_WINSTATION:
DesiredAccess = WINSTA_READATTRIBUTES;
break;
default:
DesiredAccess = MAXIMUM_ALLOWED;
break;
}
//open temporary object handle to query object address
if (!propOpenCurrentObject(Context, &hObject, DesiredAccess)) {
break;
}
}
pHandles = (PSYSTEM_HANDLE_INFORMATION)supGetSystemInfo(SystemHandleInformation);
if (pHandles == NULL) {
break;
}
ProcessesList = supGetSystemInfo(SystemProcessInformation);
if (ProcessesList == NULL) {
break;
}
//no additional info available which mean we must query object address by yourself
if (ObjectAddress == 0) {
//find our handle object by handle value
for (i = 0; i < pHandles->NumberOfHandles; i++)
if (pHandles->Handles[i].UniqueProcessId == CurrentProcessId)
if (pHandles->Handles[i].HandleValue == (USHORT)(ULONG_PTR)hObject) {
ObjectAddress = (ULONG_PTR)pHandles->Handles[i].Object;
ObjectTypeIndex = pHandles->Handles[i].ObjectTypeIndex;
break;
}
}
//object no longer needed
if (hObject) {
NtClose(hObject);
hObject = NULL;
}
//nothing to compare
if (ObjectAddress == 0) {
break;
}
//retake snapshot
HeapFree(GetProcessHeap(), 0, pHandles);
pHandles = (PSYSTEM_HANDLE_INFORMATION)supGetSystemInfo(SystemHandleInformation);
if (pHandles == NULL) {
break;
}
//find any handles with the same object address and object type
for (i = 0; i < pHandles->NumberOfHandles; i++)
if (pHandles->Handles[i].ObjectTypeIndex == ObjectTypeIndex) {
if ((ULONG_PTR)pHandles->Handles[i].Object == ObjectAddress) {
//decode and add information to the list
ProcessListAddItem(ProcessesList, &pHandles->Handles[i]);
}
}
} while (cond);
//cleanup
if (pHandles) {
HeapFree(GetProcessHeap(), 0, pHandles);
}
if (ProcessList) {
HeapFree(GetProcessHeap(), 0, ProcessesList);
}
if (Context->TypeIndex == TYPE_WINSTATION && hObject) {
CloseWindowStation(hObject);
hObject = NULL;
}
if (hObject) {
NtClose(hObject);
}
//show/hide notification text
ShowWindow(GetDlgItem(hwndDlg, ID_PROCESSLISTNOALL), (ObjectAddress == 0) ? SW_SHOW : SW_HIDE);
}
/*
* ProcessListCreate
*
* Purpose:
*
* Initialize listview for process list.
* Called once.
*
*/
VOID ProcessListCreate(
_In_ HWND hwndDlg
)
{
LVCOLUMNW col;
ProcessList = GetDlgItem(hwndDlg, ID_PROCESSLIST);
if (ProcessList == NULL)
return;
ProcessImageList = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 32, 8);
if (ProcessImageList) {
ListView_SetImageList(ProcessList, ProcessImageList, LVSIL_SMALL);
}
ListView_SetExtendedListViewStyle(ProcessList, LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER | LVS_EX_GRIDLINES | LVS_EX_LABELTIP);
RtlSecureZeroMemory(&col, sizeof(col));
col.mask = LVCF_TEXT | LVCF_SUBITEM | LVCF_FMT | LVCF_WIDTH | LVCF_ORDER | LVCF_IMAGE;
col.iSubItem = 1;
col.pszText = L"Process";
col.fmt = LVCFMT_LEFT | LVCFMT_BITMAP_ON_RIGHT;
col.iOrder = 0;
col.iImage = 2;
col.cx = 160;
ListView_InsertColumn(ProcessList, 1, &col);
col.iSubItem = 2;
col.pszText = L"ID";
col.iOrder = 1;
col.iImage = -1;
col.cx = 60;
ListView_InsertColumn(ProcessList, 2, &col);
col.iSubItem = 3;
col.pszText = L"Handle";
col.iOrder = 2;
col.iImage = -1;
col.cx = 80;
ListView_InsertColumn(ProcessList, 3, &col);
col.iSubItem = 4;
col.pszText = L"Access";
col.iOrder = 3;
col.iImage = -1;
col.cx = 80;
ListView_InsertColumn(ProcessList, 4, &col);
}
/*
* ProcessListDialogProc
*
* Purpose:
*
* Process list page for various object types.
*
* WM_INITDIALOG - Initialize listview, set window prop with context.
* WM_NOTIFY - Handle list view notifications.
* WM_SHOWWINDOW - Collect processes info and fill list.
* WM_DESTROY - Free image list and remove window prop.
*
*/
INT_PTR CALLBACK ProcessListDialogProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
LPNMLISTVIEW nhdr;
PROPSHEETPAGE *pSheet = NULL;
PROP_OBJECT_INFO *Context = NULL;
switch (uMsg) {
case WM_SHOWWINDOW:
if (wParam) {
Context = GetProp(hwndDlg, T_PROPCONTEXT);
ProcessListSetInfo(Context, hwndDlg);
if (ProcessList) {
ListView_SortItemsEx(ProcessList, &ProcessListCompareFunc, ProcessListSortColumn);
}
return 1;
}
break;
case WM_NOTIFY:
nhdr = (LPNMLISTVIEW)lParam;
ProcessListHandleNotify(nhdr);
return 1;
break;
case WM_DESTROY:
if (ProcessImageList) {
ImageList_Destroy(ProcessImageList);
}
RemoveProp(hwndDlg, T_PROPCONTEXT);
break;
case WM_INITDIALOG:
pSheet = (PROPSHEETPAGE *)lParam;
if (pSheet) {
SetProp(hwndDlg, T_PROPCONTEXT, (HANDLE)pSheet->lParam);
}
ProcessListCreate(hwndDlg);
return 1;
break;
}
return 0;
}