自己搞网站做外贸,wordpress主题上的字怎么移动,软件开发项目经理,百度搜索引擎官网前言
在之前几篇文章已经学习了解了几种钩取的方法
浅谈调试模式钩取浅谈热补丁浅谈内联钩取原理与实现导入地址表钩取技术
这篇文章就利用钩取方式完成进程隐藏的效果。
进程遍历方法
在实现进程隐藏时#xff0c;首先需要明确遍历进程的方法。
CreateToolhelp32Snapsh…前言
在之前几篇文章已经学习了解了几种钩取的方法
浅谈调试模式钩取浅谈热补丁浅谈内联钩取原理与实现导入地址表钩取技术
这篇文章就利用钩取方式完成进程隐藏的效果。
进程遍历方法
在实现进程隐藏时首先需要明确遍历进程的方法。
CreateToolhelp32Snapshot
CreateToolhelp32Snapshot函数用于创建进程的镜像当第二个参数为0时则是创建所有进程的镜像那么就可以达到遍历所有进程的效果。
#include iostream
#include Windows.h
#include TlHelp32.hint main()
{//设置编码便于后面能够输出中文setlocale(LC_ALL, zh_CN.UTF-8);//创建进程镜像参数0代表创建所有进程的镜像HANDLE hSnapshot CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);if (hSnapshot INVALID_HANDLE_VALUE){std::cout Create Error std::endl;exit(-1);}/** typedef struct tagPROCESSENTRY32 { * DWORD dwSize; 进程信息结构体大小,首次调用之前必须初始化* DWORD cntUsage; 引用进程的次数,引用次数为0时,则进程结束* DWORD th32ProcessID; 进程的ID* ULONG_PTR th32DefaultHeapID; 进程默认堆的标识符,除工具使用对我们没用* DWORD th32ModuleID; 进程模块的标识符* DWORD cntThreads; 进程启动的执行线程数* DWORD th32ParentProcessID; 父进程ID* LONG pcPriClassBase; 进程线程的基本优先级* DWORD dwFlags; 保留* TCHAR szExeFile[MAX_PATH]; 进程的路径* } PROCESSENTRY32; * typedef PROCESSENTRY32 *PPROCESSENTRY32; */PROCESSENTRY32 pi;pi.dwSize sizeof(PROCESSENTRY32);//取出第一个进程BOOL bRet Process32First(hSnapshot, pi);while (bRet){wprintf(L进程路径:%s\t进程号:%d\n, pi.szExeFile, pi.th32ProcessID);//取出下一个进程bRet Process32Next(hSnapshot, pi);}
}
EnumProcesses
EnumProcesses用于将所有进程号的收集。
#include iostream
#include Windows.h
#include Psapi.hint main()
{setlocale(LC_ALL, zh_CN.UTF-8);DWORD processes[1024], dwResult, size;unsigned int i;//收集所有进程的进程号if (!EnumProcesses(processes, sizeof(processes), dwResult)){std::cout Enum Error std::endl;}//进程数量size dwResult / sizeof(DWORD);for (i 0; i size; i){//判断进程号是否为0if (processes[i] ! 0){//用于存储进程路径TCHAR szProcessName[MAX_PATH] { 0 };//使用查询权限打开进程HANDLE hProcess OpenProcess(PROCESS_QUERY_INFORMATION |PROCESS_VM_READ,FALSE,processes[i]);if (hProcess ! NULL){HMODULE hMod;DWORD dwNeeded;//收集该进程的所有模块句柄第一个句柄则为文件路径if (EnumProcessModules(hProcess, hMod, sizeof(hMod),dwNeeded)){//根据句柄获取文件路径GetModuleBaseName(hProcess, hMod, szProcessName,sizeof(szProcessName) / sizeof(TCHAR));}wprintf(L进程路径:%s\t进程号:%d\n, szProcessName, processes[i]);}} }
}ZwQuerySystemInfomation
ZwQuerySystemInfomation函数是CreateToolhelp32Snapshot函数与EnumProcesses函数底层调用的函数也用于遍历进程信息。代码参考https://cloud.tencent.com/developer/article/1454933
#include iostream
#include Windows.h
#include ntstatus.h
#include winternl.h
#pragma comment(lib, ntdll.lib) //定义函数指针
typedef NTSTATUS(WINAPI* NTQUERYSYSTEMINFORMATION)(IN SYSTEM_INFORMATION_CLASS SystemInformationClass,IN OUT PVOID SystemInformation,IN ULONG SystemInformationLength,OUT PULONG ReturnLength);int main()
{//设置编码setlocale(LC_ALL, zh_CN.UTF-8);//获取模块地址HINSTANCE ntdll_dll GetModuleHandle(Lntdll.dll);if (ntdll_dll NULL) {std::cout Get Module Error std::endl;exit(-1);}NTQUERYSYSTEMINFORMATION ZwQuerySystemInformation NULL;//获取函数地址ZwQuerySystemInformation (NTQUERYSYSTEMINFORMATION)GetProcAddress(ntdll_dll, ZwQuerySystemInformation);if (ZwQuerySystemInformation ! NULL){SYSTEM_BASIC_INFORMATION sbi { 0 };//查询系统基本信息NTSTATUS status ZwQuerySystemInformation(SystemBasicInformation, (PVOID)sbi, sizeof(sbi), NULL);if (status STATUS_SUCCESS){wprintf(L处理器个数:%d\r\n, sbi.NumberOfProcessors);}else{wprintf(LZwQuerySystemInfomation Error\n);}DWORD dwNeedSize 0;BYTE* pBuffer NULL;wprintf(L\t----所有进程信息----\t\n);PSYSTEM_PROCESS_INFORMATION psp NULL;//查询进程数量status ZwQuerySystemInformation(SystemProcessInformation, NULL, 0, dwNeedSize);if (status STATUS_INFO_LENGTH_MISMATCH){pBuffer new BYTE[dwNeedSize];//查询进程信息status ZwQuerySystemInformation(SystemProcessInformation, (PVOID)pBuffer, dwNeedSize, NULL);if (status STATUS_SUCCESS){psp (PSYSTEM_PROCESS_INFORMATION)pBuffer;wprintf(L\tPID\t线程数\t工作集大小\t进程名\n);do {//获取进程号wprintf(L\t%d, psp-UniqueProcessId);//获取线程数量wprintf(L\t%d, psp-NumberOfThreads);//获取工作集大小wprintf(L\t%d, psp-WorkingSetSize / 1024);//获取路径wprintf(L\t%s\n, psp-ImageName.Buffer);//移动psp (PSYSTEM_PROCESS_INFORMATION)((PBYTE)psp psp-NextEntryOffset);} while (psp-NextEntryOffset ! 0);delete[]pBuffer;pBuffer NULL;}else if (status STATUS_UNSUCCESSFUL) {wprintf(L\n STATUS_UNSUCCESSFUL);}else if (status STATUS_NOT_IMPLEMENTED) {wprintf(L\n STATUS_NOT_IMPLEMENTED);}else if (status STATUS_INVALID_INFO_CLASS) {wprintf(L\n STATUS_INVALID_INFO_CLASS);}else if (status STATUS_INFO_LENGTH_MISMATCH) {wprintf(L\n STATUS_INFO_LENGTH_MISMATCH);}}}
}进程隐藏
通过上述分析可以知道遍历进程的方式有三种分别是利用CreateToolhelp32Snapshot、EnumProcesses以及ZwQuerySystemInfomation函数
但是CreateToolhelp32Snapshot与EnumProcesses函数底层都是调用了ZwQuerySystemInfomation函数因此我们只需要钩取该函数即可。
由于测试环境是Win11因此需要判断在Win11情况下底层是否还是调用了ZwQuerySystemInfomation函数。
可以看到在Win11下还是会调用ZwQuerySystemInfomation函数在用户态下该函数的名称为NtQuerySystemInformation函数。 这里采用内联钩取的方式对ZwQuerySystemInfomation进行钩取处理具体怎么钩取在浅谈内联钩取原理与实现已经介绍过了这里就不详细说明了。这里对自定义的ZwQuerySystemInfomation函数进行说明。
首先第一步需要进行脱钩处理因为后续需要用到初始的ZwQuerySystemInfomation函数紧接着获取待钩取函数的地址即可。
...//脱钩UnHook(ntdll.dll, ZwQuerySystemInformation, g_pOrgBytes);HMODULE hModule GetModuleHandleA(ntdll.dll);//获取待钩取函数的地址PROC pfnOld GetProcAddress(hModule, ZwQuerySystemInformation);//调用原始的ZwQuerySystemInfomation函数NTSTATUS status ((NTQUERYSYSTEMINFORMATION)pfnOld)(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength);
...为了隐藏指定进程我们需要遍历进程信息找到目标进程并且删除该进程信息实现隐藏的效果。这里需要知道的是进程信息都存储在SYSTEM_PROCESS_INFORMATION结构体中该结构体是通过单链表对进程信息进行链接。因此我们通过匹配进程名称找到对应的SYSTEM_PROCESS_INFORMATION结构体然后进行删除即可效果如下图。 通过单链表中删除节点的操作取出目标进程的结构体。代码如下
...pCur (PSYSTEM_PROCESS_INFORMATION)(SystemInformation);while (true){if (!lstrcmpi(pCur-ImageName.Buffer, Ltest.exe)){//需要隐藏的进程是最后一个节点if (pCur-NextEntryOffset 0)pPrev-NextEntryOffset 0;//不是最后一个节点则将该节点取出elsepPrev-NextEntryOffset pCur-NextEntryOffset;}//不是需要隐藏的节点则继续遍历elsepPrev pCur;//链表遍历完毕if (pCur-NextEntryOffset 0)break;pCur (PSYSTEM_PROCESS_INFORMATION)((PBYTE)pCur pCur-NextEntryOffset);}
...完整代码https://github.com/h0pe-ay/HookTechnology/blob/main/ProcessHidden/inlineHook.c
但是采用内联钩取的方法去钩取任务管理器就会出现一个问题这里将断点取消利用内联钩取的方式去隐藏进程。
首先利用bl命令查看断点 紧着利用 bc [ID]删除断点 在注入之后任务管理器会在拷贝的时候发生异常 在经过一番调试后发现由于多线程共同执行导致原本需要可写权限的段被修改为只读权限
在windbg可以用使用!vprot address查看指定地址的权限可以看到由于程序往只读权限的地址进行拷贝处理所以导致了异常。 但是在执行拷贝阶段是先修改了该地址为可写权限那么导致该原因的情况就是其他线程执行了权限恢复后切换到该线程中进行写所以导致了这个问题。 因此内联钩取是存在多线程安全的问题此时可以使用微软自己构建的钩取库Detours可以在钩取过程中确保线程安全。
帮助网安学习全套资料S信免费领取 ① 网安学习成长路径思维导图 ② 60网安经典常用工具包 ③ 100SRC分析报告 ④ 150网安攻防实战技术电子书 ⑤ 最权威CISSP 认证考试指南题库 ⑥ 超1800页CTF实战技巧手册 ⑦ 最新网安大厂面试题合集含答案 ⑧ APP客户端安全检测指南安卓IOS
Detours
项目地址https://github.com/microsoft/Detours
环境配置
参考https://www.cnblogs.com/linxmouse/p/14168712.html
使用vcpkg下载
vcpkg.exe install detours:x86-windows
vcpkg.exe install detours:x64-windows
vcpkg.exe integrate install实例
挂钩
利用Detours挂钩非常简单只需要根据下列顺序并且将自定义函数的地址与被挂钩的地址即可完成挂钩处理。
...//用于确保在 DLL 注入或加载时恢复被 Detours 修改的进程镜像,保持稳定性DetourRestoreAfterWith();//开始一个新的事务来附加或分离DetourTransactionBegin();//进行线程上下文的更新DetourUpdateThread(GetCurrentThread());//挂钩DetourAttach((PVOID)TrueZwQuerySystemInformation, ZwQuerySystemInformationEx);//提交事务error DetourTransactionCommit();
...脱钩
然后根据顺序完成脱钩即可。
...//开始一个新的事务来附加或分离DetourTransactionBegin();//进行线程上下文的更新DetourUpdateThread(GetCurrentThread());//脱钩DetourDetach((PVOID)TrueZwQuerySystemInformation, ZwQuerySystemInformationEx);//提交事务error DetourTransactionCommit();
...挂钩的原理
从上述可以看到Detours是通过事务确保了在DLL加载与卸载时后的原子性但是如何确保多线程安全呢后续通过调试去发现。
可以利用x ntdl!ZwQuerySystemInformation查看函数地址可以看到函数的未被挂钩前的情况如下图。 挂钩之后原始的指令被修改为一个跳转指令把前八个字节覆盖掉剩余的3字节用垃圾指令填充。 该地址里面又是一个jmp指令并且完成间接寻址的跳转。 该地址是自定义函数ZwQuerySystemInformationEx因此该间接跳转是跳转到的自定义函数内部。 跳转到TrueZwQuerySystemInformation内部发现ZwQuerySystemInformation函数内部的八字节指令被移动到该函数内部。紧接着又完成一个跳转。 该跳转到ZwQuerySystemInformation函数内部紧接着完成ZwQuerySystemInformation函数的调用。 综上所述整体流程如下图。实际上Detours实际上使用的是热补丁的思路但是Detours并不是直接在原始的函数空间中进行补丁而是开辟了一段临时空间将指令存储在里面。因此在挂钩后不需要进行脱钩处理就可以调用原始函数。因此就不存在多线程中挂钩与脱钩的冲突。 完整代码https://github.com/h0pe-ay/HookTechnology/blob/main/ProcessHidden/detoursHook.c