在此前的一篇,我们已经介绍过了注入Dll 阻止任务管理器结束进程 -- Win 10/11。本篇利用 hook NtQuerySystemInformation 并进行断链的方法实现进程隐身,实测支持 taskmgr.exe 的任意多进程隐身。
代码:
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include <detours/detours.h>
#include <winternl.h>
#include <string>
#include <iostream>
#include <stdio.h>
#include <vector>
#include <shared_mutex>#pragma comment(lib, "detours.lib")
#pragma comment(lib, "user32.lib")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; // 进程IDULONG 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;// 定义一个指针函数类型
typedef NTSTATUS(WINAPI* __NtQuerySystemInformation)(SYSTEM_INFORMATION_CLASS SystemInformationClass,PVOID SystemInformation,ULONG SystemInformationLength,PULONG ReturnLength);// 定义一个存放原函数的指针
PVOID fpNtQuerySystemInformation = NULL;
// 读写锁
std::shared_mutex ppNameListMutex;
// 受保护进程名列表
std::vector<std::wstring> ppNameList;// 声明函数
extern "C" {__declspec(dllexport)void StartHookingFunction();__declspec(dllexport) void UnmappHookedFunction();__declspec(dllexport) bool SetProtectedProcessListFromBuffer(const wchar_t* buffer, size_t length);
}NTSTATUS WINAPI HookedNtQuerySystemInformation(SYSTEM_INFORMATION_CLASS SystemInformationClass,PVOID SystemInformation,ULONG SystemInformationLength,PULONG ReturnLength
);void OpenDebugConsole()
{AllocConsole();FILE* fDummy;freopen_s(&fDummy, "CONOUT$", "w", stdout);freopen_s(&fDummy, "CONOUT$", "w", stderr);freopen_s(&fDummy, "CONIN$", "r", stdin);std::wcout << L"Debug console opened.\n";
}BOOL APIENTRY DllMain(HMODULE hModule,DWORD ul_reason_for_call,LPVOID lpReserved)
{// 禁用 DLL 模块的通知DisableThreadLibraryCalls(hModule);switch (ul_reason_for_call){case DLL_PROCESS_ATTACH:{OpenDebugConsole(); // 打开控制台进行调试std::wcout << L"DLL injected, setting up hooks.\n";// 设置受保护进程列表,此函数也可以从远程进程注入线程来调用const WCHAR ppName[] = L"cmd.exe;conhost.exe";if (SetProtectedProcessListFromBuffer(ppName, wcslen(ppName) + 1)){std::wcout << L"Protected process list set successfully.\n";}else{std::wcout << L"Failed to set protected process list.\n";}// 启用 HOOKStartHookingFunction();std::wcout << L"Hooking started.\n";}break;case DLL_THREAD_ATTACH:case DLL_THREAD_DETACH:break;case DLL_PROCESS_DETACH:UnmappHookedFunction();std::wcout << L"Hooking detached.\n";break;}return TRUE;
}extern "C"
__declspec(dllexport)
void StartHookingFunction()
{//开始事务DetourTransactionBegin();//更新线程信息 DetourUpdateThread(GetCurrentThread());fpNtQuerySystemInformation =DetourFindFunction("ntdll.dll","NtQuerySystemInformation");//将拦截的函数附加到原函数的地址上,这里可以拦截多个函数。DetourAttach(&(PVOID&)fpNtQuerySystemInformation,HookedNtQuerySystemInformation);//结束事务DetourTransactionCommit();
}extern "C"
__declspec(dllexport)
void UnmappHookedFunction()
{//开始事务DetourTransactionBegin();//更新线程信息 DetourUpdateThread(GetCurrentThread());//将拦截的函数从原函数的地址上解除,这里可以解除多个函数。DetourDetach(&(PVOID&)fpNtQuerySystemInformation,HookedNtQuerySystemInformation);//结束事务DetourTransactionCommit();
}// 从缓冲区解析多个进程名的函数
extern "C"
__declspec(dllexport)
bool SetProtectedProcessListFromBuffer(const wchar_t* buffer, size_t length) {if (buffer == nullptr || length == 0) {return false; // 返回错误状态}std::unique_lock lock(ppNameListMutex); // 写锁ppNameList.clear(); // 清空原列表std::wstring tempName;for (size_t i = 0; i < length; ++i) {if (buffer[i] == L';' || i == length - 1) {// 遇到分号或者到达缓冲区末尾,表示一个进程名结束if (i == length - 1 && buffer[i] != L';' && buffer[i] != L'\0') {tempName += buffer[i]; // 处理最后一个字符不是分号的情况}if (!tempName.empty()) {std::wcout << L"Parsed process name: " << tempName << L"\n"; // 输出调试信息std::wcout << L"Length: " << tempName.size() << L"\n";ppNameList.push_back(tempName); // 将进程名存入列表tempName.clear(); // 清空临时字符串以解析下一个进程名}}else {// 继续读取进程名字符tempName += buffer[i];}}std::wcout << L"Total protected processes: " << ppNameList.size() << L"\n"; // 输出调试信息return !ppNameList.empty(); // 返回成功标志,如果解析后列表为空则返回false
}// 检查进程是否在受保护列表中的函数(带读锁)
static bool IsProcessProtected(const std::wstring& processName) {std::vector<std::wstring> localPpNameList;{std::shared_lock lock(ppNameListMutex);localPpNameList = ppNameList; // 将受保护列表复制到局部变量}// 在局部变量中进行比较return std::find(localPpNameList.begin(), localPpNameList.end(), processName) != localPpNameList.end();
}static bool IsHandleValidate(const LPVOID lpAddress)
{MEMORY_BASIC_INFORMATION Buffer{};VirtualQuery(lpAddress, &Buffer, 0x30u);//std::wcout << L"HandleValidate Buffer.Protect: " << Buffer.Protect << L"\n";return Buffer.State == MEM_COMMIT && Buffer.Protect != PAGE_NOACCESS;
}static BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) {DWORD processId;GetWindowThreadProcessId(hwnd, &processId);// 检查窗口是否属于当前进程if (processId == GetCurrentProcessId()) {// 检索指向窗口句柄向量的指针std::vector<HWND>* pWindowHandles = reinterpret_cast<std::vector<HWND>*>(lParam);// 检查指针是否有效if (pWindowHandles && IsHandleValidate(pWindowHandles)) {pWindowHandles->push_back(hwnd);}else {// (可选)记录错误或处理无效指针情况std::cerr << "Invalid pointer passed to EnumWindowsProc." << std::endl;}}return TRUE; // 继续枚举
}static void FlushProcessWindows() {std::vector<HWND> windowHandles;// 枚举所有顶级窗口,通过lParam将指针传递给EnumWindowsProcEnumWindows(EnumWindowsProc, reinterpret_cast<LPARAM>(&windowHandles));// 向每一个窗口发送 F5 来刷新窗口for (HWND hwnd : windowHandles) {// 模拟 F5PostMessage(hwnd, WM_KEYDOWN, VK_F5, 0);Sleep(10);PostMessage(hwnd, WM_KEYUP, VK_F5, 0);}
}// NtQuerySystemInformation的Hook函数,用于隐藏受保护的进程
NTSTATUS WINAPI HookedNtQuerySystemInformation(SYSTEM_INFORMATION_CLASS SystemInformationClass,PVOID SystemInformation,ULONG SystemInformationLength,PULONG ReturnLength
) {//std::wcout << L"NtQuerySystemInformation hook called.\n";static bool isNotFirstHook;const size_t nodeSize = sizeof(MY_SYSTEM_PROCESS_INFORMATION);// 先调用原始的 NtQuerySystemInformationNTSTATUS status = ((__NtQuerySystemInformation)fpNtQuerySystemInformation)(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength);//std::wcout << L"Original NtQuerySystemInformation returned: " << status << L"\n";// 只处理 SystemProcessInformation 类型的信息if (SystemInformationClass == SystemProcessInformation && NT_SUCCESS(status)) {//std::wcout << L"Processing SystemProcessInformation.\n";PMY_SYSTEM_PROCESS_INFORMATION pCurrentNode = (PMY_SYSTEM_PROCESS_INFORMATION)SystemInformation;PMY_SYSTEM_PROCESS_INFORMATION pPreviousNode = nullptr;bool isFirstNode = true;while (pCurrentNode != nullptr && IsHandleValidate(pCurrentNode)) {if (pCurrentNode->NextEntryOffset == 0) { // 到达末尾break;}if (pCurrentNode->ImageName.Buffer == nullptr || pCurrentNode->ImageName.Length == 0) {// 跳过无效的进程名//std::wcout << L"Skipping invalid process name.\n";pCurrentNode = (PMY_SYSTEM_PROCESS_INFORMATION)((PUCHAR)pCurrentNode + pCurrentNode->NextEntryOffset);continue;}// 获取当前进程名std::wstring processName(pCurrentNode->ImageName.Buffer, pCurrentNode->ImageName.Length / sizeof(WCHAR));//std::wcout << L"Processing process: " << processName << L"\n";// 检查该进程名是否在受保护列表中if (IsProcessProtected(processName)) {std::wcout << L"Process is protected: " << processName << L"\n";// 如果在受保护列表中,则将该进程从链表中移除if (pPreviousNode) {pPreviousNode->NextEntryOffset += pCurrentNode->NextEntryOffset;std::wcout << L"Process removed from list: " << processName << L"\n";}else if (isFirstNode) { // 第一个节点是受保护进程,// 替换为下一个节点的数据if (pCurrentNode->NextEntryOffset == 0) {// 如果没有下一个节点,表示列表中只有一个受保护// 将进程信息列表清空memset(pCurrentNode, 0, SystemInformationLength);std::wcout << L"Only one protected process, clearing list.\n";break;}else {PMY_SYSTEM_PROCESS_INFORMATION pNextNode =(PMY_SYSTEM_PROCESS_INFORMATION)((PUCHAR)pCurrentNode + pCurrentNode->NextEntryOffset);if(!IsHandleValidate(pNextNode)) {std::wcout << L"HandleValidate failed.\n";break;}// 将下一个节点的数据拷贝到当前节点memcpy(pCurrentNode, pNextNode, sizeof(MY_SYSTEM_PROCESS_INFORMATION));pCurrentNode->NextEntryOffset = pNextNode->NextEntryOffset;std::wcout << L"First process was protected, replaced with next process.\n";continue; // 保持 pPreviousNode 不变,重新检查当前节点}}}else {// 如果没有被保护,移动到下一个节点pPreviousNode = pCurrentNode;}// 如果下一个节点超出缓冲区范围,停止处理if (((PUCHAR)pCurrentNode + pCurrentNode->NextEntryOffset + nodeSize) >(PUCHAR)SystemInformation + SystemInformationLength) {std::wcout << L"Reached end of buffer.\n";break;}// 继续下一个进程信息节点pCurrentNode = (PMY_SYSTEM_PROCESS_INFORMATION)((PUCHAR)pCurrentNode + pCurrentNode->NextEntryOffset);}}// 只在第一次调用 hook 函数后强制刷新窗口if (!isNotFirstHook) {isNotFirstHook = true;FlushProcessWindows();}return status;
}
可以删除 dllmain 里面的hook 函数以及所有输出字符串。从外部进程通过注入远程线程的方式来实现动态调整隐身策略。主要利用下面三个函数:
执行效果:
本文出处链接:[https://blog.csdn.net/qq_59075481/article/details/142676712]。
本文发布于:2024.10.02。