目录
挂起进程其实就是在创建进程的时候不让其先执行.然后获取它的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的地址
下面主要针对这几点讲解.
首先挂起进程注入. 你通过CONTEXT 这个结构可以获取 目标进程EIP.但是你要想办法执行完ShellCode之后跳转回去. 所以这里我使用了一个简单的方法
push Address
ret
这个方式是利用堆栈.以及ret指令来进行返回的. 当我们保存了原始地址.然后ret的时候.ret会把我们push的地址当做返回地址来执行.
我们正常来说.调用LoadLibraryA/W的时候.都会进行参数压栈进而进行调用. 如:
push xxxx地址 (地址里面是个DLL路径)
call LoadLibraryA; 调用LoadLibrary
而为了方便我直接代码重定位.直接将ShellCode尾部写入我们的DLL路径.
call $0:
pop ebx
使用这两句可以得到ebx当前指令指定的EIP的地址. 直接当前 EIP + xxx偏移(偏移是你写DLL路径的位置的偏移) 就是我们的参数地址.
当你直接使用 Call的方式调用LoadLibrary的时候.你还需要计算偏移.各种等等. 但是这种不需要的.为啥. 因为Windows在启动后 kernel32的基址已经固定了.任何程序启动都会默认加载 kernel32的.所以直接使用LoadLibrary当地址即可. 但是你使用Call的方式 (call LoadLibrary) 你还需要计算你的ShellCode 与LoadLibrary的偏移.所以我们直接使用寄存器来做.
mov reg,LoadLibrary
call reg
reg代表任意通用寄存器. 此时我们的ShellCode就可以正常运行了.通过以上步骤就可以开始运行了.
#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;
}