挂起进程修改eip进行dll注入

挂起进程修改eip进行dll注入

(该文件最早发于:http://blog.sina.com.cn/s/blog_6f50984a01013dyx.html,现整理后重发)

启动进程的时候,使用CREATE_SUSPENDED 挂起创建方式创建进程(或者打开存在进程后将其挂起),然后在该进程内申请一块空间,将汇编码写入该空间内,汇编码内容为“加载目标dll,加载完成后跳回原来的进程运行点”。

以创建进程时候进行注入为例:

STARTUPINFOW si = { 0 };
PROCESS_INFORMATION pi = { 0 };
si.cb = sizeof(STARTUPINFOW);

::CreateProcessW(pProcessPath, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);
CONTEXT ct = { 0 };
ct.ContextFlags = CONTEXT_ALL;
GetThreadContext(pi.hThread, &ct);
DWORD dwSize = sizeof(WCHAR) * 1024;
BYTE *pProcessMem = (BYTE *)::VirtualAllocEx(pi.hProcess, NULL, dwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
// 将dll 写入申请的内存的偏移100的位置初,留出100个字节大小内存写入汇编码
DWORD dwWrited = 0;
::WriteProcessMemory(pi.hProcess, (pProcessMem + 0x100), pDllPath, (wcslen(pDllPath) + 1) * sizeof(WCHAR), &dwWrited);

FARPROC pLoadLibraryW = (FARPROC)::GetProcAddress(::GetModuleHandle(L"Kernel32"), "LoadLibraryW");

BYTE ShellCode[32] = { 0 };
DWORD *pdwAddr = NULL;

// 先保持下寄存器信息
ShellCode[0] = 0x60; // pushad
ShellCode[1] = 0x9c; // pushfd
ShellCode[2] = 0x68; // push
pdwAddr = (DWORD *)&ShellCode[3]; // ShellCode[3/4/5/6],也就是一个DWORD 的大小
*pdwAddr = (DWORD)(pProcessMem + 0x100); // 指向申请的内存的偏移100处,也就是刚刚写入的dll 路径所在的内存
// 上面三行,等价于 push dll 的地址,为call LoadLibrary 做压参,行数调用做准备
ShellCode[7] = 0xe8;
pdwAddr = (DWORD *)&ShellCode[8]; // ShellCode[8/9/10/11]
*pdwAddr = (DWORD)pLoadLibraryW - (DWORD)(pProcessMem + 7) - 5;
// 上面三行 等价于call LoadLibraryW
// 上面六行汇编码,实际上就是 LoadLibraryW(dllPath) 了

ShellCode[12] = 0x9d; // popfd
ShellCode[13] = 0x61; // popad

ShellCode[14] = 0xe9; // jmp
pdwAddr = (DWORD *)&ShellCode[15]; // ShellCode[15/16/17/18]
*pdwAddr = ct.Eip - (DWORD)(pProcessMem + 14) - 5;
// 跳回原来的地方

::WriteProcessMemory(pi.hProcess, pProcessMem, ShellCode, sizeof(ShellCode), &dwWrited);
ct.Eip = (DWORD)pProcessMem; // 将eip 位置指向shellCode 汇编码
::SetThreadContext(pi.hThread, &ct);

::ResumeThread(pi.hThread);
::CloseHandle(pi.hProcess);
::CloseHandle(pi.hThread);

上面的
*pdwAddr = (DWORD)pLoadLibraryW – (DWORD)(pProcessMem + 7) – 5;
*pdwAddr = ct.Eip – (DWORD)(pProcessMem + 14) – 5;
两句的说明,
因为0xe8 (call) 和0xe9(jmp)跳指令,eip改变格式如下:
eip + 5 + x = newEip,其中5为一个字节的指令长度加上四个字节的地址长度
所以得
x = newEip – eip – 5
故得上面两句。

如果不想计算偏移,这段shellCode可以变形一下,就不用计算偏移地址了。
主要做法是用push addr 加上retn 来代替 call。

shellcode如下:(地址用0x90 填充,后面再修正)

BYTE ShellCode[32] =
{

0x60, // [0] pushad
0x9c, // [1] pushfd

// [2 - 6] push XXXX, 这里后四子节待填充为DllPath 地址,等价于传入DllPath 参数
0x68, 0x90,0x90,0x90,0x90,

// [7 - 11] push XXXX, 这里后四子节待填充为调用LoadLibraryW后,需要回到的位置
0x68, 0x90,0x90,0x90,0x90,

// [12 - 16] push XXXX, 待填充为 LoadLibraryW 地址
0x68, 0x90,0x90,0x90,0x90,

0xc3, // [17] retn 这个指令使得跳转到 LoadLibraryW,LoadLibraryW返回地址由上一个指令指出

0x9d, // [18] popfd
0x61, // [19] popad

// [20 - 25]push OldEip 地址
0x68, 0x90,0x90,0x90,0x90,

0xc3, // [25] retn
0x00
};
*((DWORD *)&ShellCode[3]) = (DWORD)(pProcessMem + 0x100); // ShellCode[3/4/5/6] LoadLibraryW 的参数
*((DWORD *)&ShellCode[8]) = (DWORD)pProcessMem+0x12; // ShellCode[8/9/10/11] LoadLibraryW 返回的地址
*((DWORD *)&ShellCode[13]) = (DWORD)pLoadLibraryW; // ShellCode[13/14/15/16] LoadLibraryW的地址
*((DWORD *)&ShellCode[21]) = (DWORD)ct.Eip; // ShellCode[21/22/23/24] ct.Eip的地址

发表评论

电子邮件地址不会被公开。 必填项已用*标注