任务管理器通过 NtQuerySystemInformation 获取进程信息,通过 TerminateProcess 以及 EndTask 等函数终止进程,我们可以通过挂钩途径中的多个R3函数实现在任务管理器中保护进程。网上有很多通过挂钩 TerminalProcess 来拦截终止功能的教程。这里从查找进程信息阶段拦截实现保护/隐身目标进程。
2024.05.24:近期有读者指出该代码不能够做到拦截的效果,我将抽空核实是否能够拦截,并研究解决方法。
这里需要注意的是,通过挂钩 SHParseDisplayName 可以进一步阻止右键菜单中的打开文件路径操作。
NtQuerySystemInformation 的定义如下:
NTSTATUS WINAPI NtQuerySystemInformation(
_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass,
_In_ _Out_ PVOID SystemInformation,
_In_ ULONG SystemInformationLength,
_Out_opt_ PULONG ReturnLength
);
其中,第一个参数是一个结构体,包含进程的信息,结构体是根据 ProcessHacker 网站的资料获得的:
typedef struct _SYSTEM_PROCESS_INFORMATION
{
ULONG NextEntryOffset; // 指向下一个结构体的指针
ULONG ThreadCount; // 本进程的总线程数
ULONG Reserved1[6]; // 保留
LARGE_INTEGER CreateTime; // 进程的创建时间
LARGE_INTEGER UserTime; // 在用户层的使用时间
LARGE_INTEGER KernelTime; // 在内核层的使用时间
UNICODE_STRING ImageName; // 进程名
KPRIORITY BasePriority; //
ULONG ProcessId; // 进程ID
ULONG InheritedFromProcessId;
ULONG HandleCount; // 进程的句柄总数
ULONG Reserved2[2]; // 保留
VM_COUNTERS VmCounters;
IO_COUNTERS IoCounters;
SYSTEM_THREAD_INFORMATION Threads[5]; // 子线程信息数组
}SYSTEM_PROCESS_INFORMATION, * PMY_SYSTEM_PROCESS_INFORMATION;
可以看出该接口返回的结构体包含链表结构,我们可以通过断链方法隐藏进程,或者通过记录查找到的进程信息,在联合挂钩 NtOpenProcess 等函数来保护进程。
// 一下记录最近20条的模式其实可以用栈来实现
DWORD pid[20] = { 0 };// 只记录最近的20个目标进程
int cnt = 0;// 计数
NTSTATUS WINAPI HookedNtQuerySystemInformation(
_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass,
_In_ _Out_ PVOID SystemInformation,
_In_ ULONG SystemInformationLength,
_Out_opt_ PULONG ReturnLength
) {
bool flag = false;
// 先正常调用原函数,获取返回值
NTSTATUS status = ((__NtQuerySystemInformation)fpNtQuerySystemInformation)(SystemInformationClass,
SystemInformation,
SystemInformationLength,
ReturnLength);
// 判断是否是进程信息和调用是否成功
if (SystemInformationClass == SystemProcessInformation && NT_SUCCESS(status)) {
//MY_SYSTEM_PROCESS_INFORMATION pCurrent = NULL;
PMY_SYSTEM_PROCESS_INFORMATION pNext = (PMY_SYSTEM_PROCESS_INFORMATION)SystemInformation;
// 单链表循环
do
{
pNext = (PMY_SYSTEM_PROCESS_INFORMATION)((PUCHAR)pNext + pNext->NextEntryOffset);
if (!wcsncmp(pNext->ImageName.Buffer, L"Notepad.exe", pNext->ImageName.Length))
{
flag = false;
for (int i = 1; i <= cnt; i++)
{
if (pid[i] == (DWORD)pNext->InheritedFromProcessId)
flag = true;
}
if (flag == false && cnt < 19)
{
pid[++cnt] = (DWORD)pNext->InheritedFromProcessId;
pid[0] = cnt;
}
else if (flag == false && cnt >= 19)// 记录满了就清零
{
cnt = 0;
pid[++cnt] = (DWORD)pNext->InheritedFromProcessId;
pid[0] = cnt;
}
}
} while (pNext->NextEntryOffset != 0);
}
// 正常返回
return status;
}
其他部分不在详细叙述,给出完整代码,需要注意的是,该挂钩只适用于 Taskmgr 不适用于procexp 等程序。
对于窗口程序,需要挂钩 SendMessageTimeout 函数,过滤其发出的 SC_CLOSE 消息,挂钩 NtOpenProcess 和 NtOpenThread 可以阻止 ZwTerminateProcess 等函数,因为我们返回无效句柄。
完整代码:
#include "pch.h"
#include "detours.h"
#include <winternl.h>
#include <ShlObj.h>
#include <TlHelp32.h>
#pragma comment(lib, "detours.lib")
void StartHookingFunction();
void UnmappHookedFunction();
typedef struct _VM_COUNTERS
{
SIZE_T PeakVirtualSize;
SIZE_T VirtualSize;
ULONG PageFaultCount;
SIZE_T PeakWorkingSetSize;
SIZE_T WorkingSetSize;
SIZE_T QuotaPeakPagedPoolUsage;
SIZE_T QuotaPagedPoolUsage;
SIZE_T QuotaPeakNonPagedPoolUsage;
SIZE_T QuotaNonPagedPoolUsage;
SIZE_T PagefileUsage;
SIZE_T PeakPagefileUsage;
} VM_COUNTERS;
// 线程信息结构体
typedef struct _MY_SYSTEM_THREAD_INFORMATION
{
LARGE_INTEGER KernelTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER CreateTime;
ULONG WaitTime;
PVOID StartAddress;
CLIENT_ID ClientId;
KPRIORITY Priority;
KPRIORITY BasePriority;
ULONG ContextSwitchCount;
LONG State;// 状态,是THREAD_STATE枚举类型中的一个值
LONG WaitReason;//等待原因, KWAIT_REASON中的一个值
} MY_SYSTEM_THREAD_INFORMATION, * PMY_SYSTEM_THREAD_INFORMATION;
typedef struct _MY_UNICODE_STRING
{
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} MY_UNICODE_STRING, * PMY_UNICODE_STRING;
typedef struct _MY_SYSTEM_PROCESS_INFORMATION
{
ULONG NextEntryOffset; // 指向下一个结构体的指针
ULONG ThreadCount; // 本进程的总线程数
ULONG Reserved1[6]; // 保留
LARGE_INTEGER CreateTime; // 进程的创建时间
LARGE_INTEGER UserTime; // 在用户层的使用时间
LARGE_INTEGER KernelTime; // 在内核层的使用时间
MY_UNICODE_STRING ImageName; // 进程名
KPRIORITY BasePriority; //
ULONG ProcessId; // 进程ID
ULONG InheritedFromProcessId;
ULONG HandleCount; // 进程的句柄总数
ULONG Reserved2[2]; // 保留
VM_COUNTERS VmCounters;
IO_COUNTERS IoCounters;
SYSTEM_THREAD_INFORMATION Threads[5]; // 子线程信息数组
}MY_SYSTEM_PROCESS_INFORMATION, * PMY_SYSTEM_PROCESS_INFORMATION;
#define STDAPICALLTYPE __stdcall
// 定义一个指针函数类型
typedef NTSTATUS(WINAPI* __NtQuerySystemInformation)(
_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass,
_In_ _Out_ PVOID SystemInformation,
_In_ ULONG SystemInformationLength,
_Out_opt_ PULONG ReturnLength
);
typedef HANDLE (WINAPI* __OpenProcess)(
_In_ DWORD dwDesiredAccess,
_In_ BOOL bInheritHandle,
_In_ DWORD dwProcessId
);
typedef HANDLE (WINAPI* __OpenThread)(
_In_ DWORD dwDesiredAccess,
_In_ BOOL bInheritHandle,
_In_ DWORD dwThreadId
);
typedef LRESULT (WINAPI* __SendMessageW)(
_In_ HWND hWnd,
_In_ UINT Msg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
typedef LRESULT(WINAPI* __SendMessageTimeoutW)(
_In_ HWND hWnd,
_In_ UINT Msg,
_In_ WPARAM wParam,
_In_ LPARAM lParam,
_In_ UINT fuFlags,
_In_ UINT uTimeout,
_Out_opt_ PDWORD_PTR lpdwResult
);
typedef HRESULT (STDAPICALLTYPE* __SHParseDisplayName)(
_In_ PCWSTR pszName,
_In_opt_ IBindCtx* pbc,
_Out_ PIDLIST_ABSOLUTE* ppidl,
_In_ SFGAOF sfgaoIn,
_Out_opt_ SFGAOF* psfgaoOut
);
typedef BOOL (WINAPI* __EndTask)(
_In_ HWND hWnd,
_In_ BOOL fShutDown,
_In_ BOOL fForce
);
// 定义一个存放原函数的指针
PVOID fpNtQuerySystemInformation = NULL;
PVOID fpOpenProcess = NULL;
PVOID fpOpenThread = NULL;
PVOID fpSendMessageTimeout = NULL;
PVOID fpSendMessage = NULL;
PVOID fpSHParseDisplayName = NULL;
PVOID fpEndTask = NULL;
// 全局变量
DWORD pid[20]{};
int cnt = 0;
NTSTATUS WINAPI HookedNtQuerySystemInformation(
_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass,
_In_ _Out_ PVOID SystemInformation,
_In_ ULONG SystemInformationLength,
_Out_opt_ PULONG ReturnLength
);
HANDLE WINAPI HookedOpenProcess(
_In_ DWORD dwDesiredAccess,
_In_ BOOL bInheritHandle,
_In_ DWORD dwProcessId
);
HANDLE WINAPI HookedOpenThread(
_In_ DWORD dwDesiredAccess,
_In_ BOOL bInheritHandle,
_In_ DWORD dwThreadId
);
LRESULT WINAPI HookedSendMessageW(
_In_ HWND hWnd,
_In_ UINT Msg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
LRESULT WINAPI HookedSendMessageTimeout(
_In_ HWND hWnd,
_In_ UINT Msg,
_In_ WPARAM wParam,
_In_ LPARAM lParam,
_In_ UINT fuFlags,
_In_ UINT uTimeout,
_Out_opt_ PDWORD_PTR lpdwResult
);
HRESULT STDAPICALLTYPE HookedSHParseDisplayName(
_In_ PCWSTR pszName,
_In_opt_ IBindCtx* pbc,
_Out_ PIDLIST_ABSOLUTE* ppidl,
_In_ SFGAOF sfgaoIn,
_Out_opt_ SFGAOF* psfgaoOut
);
BOOL WINAPI HookedEndTask(
_In_ HWND hWnd,
_In_ BOOL fShutDown,
_In_ BOOL fForce
);
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
DisableThreadLibraryCalls(hModule);
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
StartHookingFunction();
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
UnmappHookedFunction();
break;
}
return TRUE;
}
void StartHookingFunction()
{
//开始事务
DetourTransactionBegin();
//更新线程信息
DetourUpdateThread(GetCurrentThread());
fpNtQuerySystemInformation =
DetourFindFunction(
"ntdll.dll",
"NtQuerySystemInformation");
fpOpenProcess =
DetourFindFunction(
"kernel32.dll",
"OpenProcess");
fpOpenThread =
DetourFindFunction(
"kernel32.dll",
"OpenThread");
fpSendMessage =
DetourFindFunction(
"user32.dll",
"SendMessageW");
fpSendMessageTimeout =
DetourFindFunction(
"user32.dll",
"SendMessageTimeoutW");
fpSHParseDisplayName =
DetourFindFunction(
"shell32.dll",
"SHParseDisplayName");
fpEndTask =
DetourFindFunction(
"user32.dll",
"EndTask");
//将拦截的函数附加到原函数的地址上,这里可以拦截多个函数。
DetourAttach(&(PVOID&)fpNtQuerySystemInformation,
HookedNtQuerySystemInformation);
DetourAttach(&(PVOID&)fpOpenProcess,
HookedOpenProcess);
DetourAttach(&(PVOID&)fpOpenThread,
HookedOpenThread);
DetourAttach(&(PVOID&)fpSendMessage,
HookedSendMessageW);
DetourAttach(&(PVOID&)fpSendMessageTimeout,
HookedSendMessageTimeout);
DetourAttach(&(PVOID&)fpSHParseDisplayName,
HookedSHParseDisplayName);
DetourAttach(&(PVOID&)fpSHParseDisplayName,
HookedSHParseDisplayName);
DetourAttach(&(PVOID&)fpEndTask,
HookedEndTask);
//结束事务
DetourTransactionCommit();
}
void UnmappHookedFunction()
{
//开始事务
DetourTransactionBegin();
//更新线程信息
DetourUpdateThread(GetCurrentThread());
//将拦截的函数从原函数的地址上解除,这里可以解除多个函数。
DetourDetach(&(PVOID&)fpNtQuerySystemInformation,
HookedNtQuerySystemInformation);
DetourDetach(&(PVOID&)fpOpenProcess,
HookedOpenProcess);
DetourDetach(&(PVOID&)fpOpenThread,
HookedOpenThread);
DetourDetach(&(PVOID&)fpSendMessage,
HookedSendMessageW);
DetourDetach(&(PVOID&)fpSendMessageTimeout,
HookedSendMessageTimeout);
DetourDetach(&(PVOID&)fpSHParseDisplayName,
HookedSHParseDisplayName);
DetourDetach(&(PVOID&)fpEndTask,
HookedEndTask);
//结束事务
DetourTransactionCommit();
}
// 定义一个Hook函数,隐藏指定进程名程序
NTSTATUS WINAPI HookedNtQuerySystemInformation(
_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass,
_In_ _Out_ PVOID SystemInformation,
_In_ ULONG SystemInformationLength,
_Out_opt_ PULONG ReturnLength
) {
bool flag = false;
// 先正常调用原函数,获取返回值
NTSTATUS status = ((__NtQuerySystemInformation)fpNtQuerySystemInformation)(SystemInformationClass,
SystemInformation,
SystemInformationLength,
ReturnLength);
// 判断是否是进程信息和调用是否成功
if (SystemInformationClass == SystemProcessInformation && NT_SUCCESS(status)) {
//MY_SYSTEM_PROCESS_INFORMATION pCurrent = NULL;
PMY_SYSTEM_PROCESS_INFORMATION pNext = (PMY_SYSTEM_PROCESS_INFORMATION)SystemInformation;
// 单链表循环
do
{
//pCurrent = pNext;
pNext = (PMY_SYSTEM_PROCESS_INFORMATION)((PUCHAR)pNext + pNext->NextEntryOffset);
if (!wcsncmp(pNext->ImageName.Buffer, L"Notepad.exe", pNext->ImageName.Length))
{
flag = false;
for (int i = 1; i <= cnt; i++)
{
if (pid[i] == (DWORD)pNext->InheritedFromProcessId)
flag = true;
//pid = (DWORD)pNext->UniqueProcessId;
}
if (flag == false && cnt < 19)
{
pid[++cnt] = (DWORD)pNext->InheritedFromProcessId;
pid[0] = cnt;
//ListProcessThreads((DWORD)pNext->UniqueProcessId);
}
else if (flag == false && cnt >= 19)
{
cnt = 0;
pid[++cnt] = (DWORD)pNext->InheritedFromProcessId;
pid[0] = cnt;
//ListProcessThreads((DWORD)pNext->UniqueProcessId);
}
}
} while (pNext->NextEntryOffset != 0);
}
// 正常返回
return status;
}
HANDLE WINAPI HookedOpenProcess(
_In_ DWORD dwDesiredAccess,
_In_ BOOL bInheritHandle,
_In_ DWORD dwProcessId
)
{
if ((int)pid[0] > 1) {
for (int i = (int)pid[0]; i >= 1; i--) {
if (dwProcessId == pid[i])
{
SetLastError(5);
return NULL;
}
}
return ((__OpenProcess)fpOpenProcess)(dwDesiredAccess, bInheritHandle, dwProcessId);
}
else if ((int)pid[0] == 1) {
if (dwProcessId == pid[1])
{
SetLastError(5);
return NULL;
}
else
{
return ((__OpenProcess)fpOpenProcess)(dwDesiredAccess, bInheritHandle, dwProcessId);
}
}
else
{
return ((__OpenProcess)fpOpenProcess)(dwDesiredAccess, bInheritHandle, dwProcessId);
}
}
HANDLE WINAPI HookedOpenThread(
_In_ DWORD dwDesiredAccess,
_In_ BOOL bInheritHandle,
_In_ DWORD dwThreadId
)
{
DWORD dwProtectedTid = 0;
HWND hTargetWnd = FindWindowW(L"Notepad", NULL);
if (hTargetWnd != NULL)
{
dwProtectedTid = GetWindowThreadProcessId(hTargetWnd, NULL);
}
if (dwProtectedTid != 0 && dwThreadId == dwProtectedTid)
{
SetLastError(5);
return NULL;
}
return ((__OpenThread)fpOpenThread)(dwDesiredAccess, bInheritHandle, dwThreadId);
}
LRESULT WINAPI HookedSendMessageW(
_In_ HWND hWnd,
_In_ UINT Msg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
//DWORD curprocid = 0;
if (hWnd == 0)
return E_FAIL;
if ((hWnd == HWND_BROADCAST ) && (Msg == WM_DESTROY || Msg == WM_QUIT || Msg == WM_CLOSE))
{
SetLastError(5);
return E_FAIL;
}
if (hWnd != NULL)
{
int Target = NULL;
WCHAR ClassName[500]{};
Target = GetClassNameW(hWnd, ClassName, sizeof(ClassName) / sizeof(WCHAR));
if (!wcscmp(ClassName, L"Notepad"))
{
SetLastError(5);
return E_FAIL;
}
else return ((__SendMessageW)fpSendMessage)(hWnd, Msg, wParam, lParam);
}
return ((__SendMessageW)fpSendMessage)(hWnd, Msg, wParam, lParam);
}
LRESULT WINAPI HookedSendMessageTimeout(
_In_ HWND hWnd,
_In_ UINT Msg,
_In_ WPARAM wParam,
_In_ LPARAM lParam,
_In_ UINT fuFlags,
_In_ UINT uTimeout,
_Out_opt_ PDWORD_PTR lpdwResult
)
{
if (hWnd != NULL)
{
int Target = NULL;
WCHAR ClassName[500]{};
Target = GetClassNameW(hWnd, ClassName, sizeof(ClassName) / sizeof(WCHAR));
if (!wcscmp(ClassName, L"Notepad") && Msg == WM_SYSCOMMAND && wParam == SC_CLOSE)
{
return S_FALSE;
}
}
return ((__SendMessageTimeoutW)fpSendMessageTimeout)
(hWnd, Msg, wParam, lParam, fuFlags, uTimeout, lpdwResult);
}
HRESULT STDAPICALLTYPE HookedSHParseDisplayName(
_In_ PCWSTR pszName,
_In_opt_ IBindCtx* pbc,
_Out_ PIDLIST_ABSOLUTE* ppidl,
_In_ SFGAOF sfgaoIn,
_Out_opt_ SFGAOF* psfgaoOut
)
{
if (wcsstr(pszName, L"Notepad.exe"))
return ((__SHParseDisplayName)fpSHParseDisplayName)
(L"C:\\Windows\\explorer.exe", pbc, ppidl, sfgaoIn, psfgaoOut);
else
return ((__SHParseDisplayName)fpSHParseDisplayName)
(pszName, pbc, ppidl, sfgaoIn, psfgaoOut);
}
BOOL WINAPI HookedEndTask(
_In_ HWND hWnd,
_In_ BOOL fShutDown,
_In_ BOOL fForce
)
{
int Target = NULL;
WCHAR ClassName[500]{};
Target = GetClassNameW(hWnd, ClassName, sizeof(ClassName) / sizeof(WCHAR));
if (Target != NULL && !wcscmp(ClassName, L"Notepad"))
{
SetLastError(5);
return FALSE;
}
else return ((__EndTask)fpEndTask)(hWnd, fShutDown, fForce);
}
程序运行效果如下图所示:
转载请注明出处:Detours挂钩实现进程保护(任务管理器)
发布于:2023.10.05,更新于:2023.10.14, 2024.05.24.