主要思想:
在进程启动时候,用CREATE_SUSPENDED挂起方式创建进程,或者打开进程之后把它挂起,然后申请一个内存空间,把shellcode 写到该内存中,之后修改进程eip为shellcode的地址,即可。
汇编码:
pushad
pushfd
push "dllPath"
call LoadLibary
popfd
popad
jmp Old Eip
实现:(程序省去了出错的处理)
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);
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]
*pdwAddr = (DWORD)(pProcessMem + 0x100);
ShellCode[7] = 0xe8;
pdwAddr = (DWORD *)&ShellCode[8]; // ShellCode[8/9/10/11]
*pdwAddr = (DWORD)pLoadLibraryW - (DWORD)(pProcessMem + 7) - 5;
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;
::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
汇编代码如下:
pushad
pushfd
push "dllPath"
push Addr1
push LoadLibary
retn
Addr1:
popfd
popad
push OldEip
retn
shellcode如下:(地址用0x90 填充,后面再修正)
BYTE ShellCode[32] =
{
0x60, // pushad
0x9c, // pushfd
// push DllPath 地址
0x68, 0x90,0x90,0x90,0x90,
// push Addr1:
0x68, 0x90,0x90,0x90,0x90,
// push LoadLibraryW 地址
0x68, 0x90,0x90,0x90,0x90,
0xc3, // retn 这个指令使得跳转到 LoadLibraryW,LoadLibraryW返回地址有上一个指令指出
0x9d, // popfd
0x61, // popad
// push OldEip 地址
0x68, 0x90,0x90,0x90,0x90,
0xc3, // 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的地址