函数介绍
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_READWRITE);
if (NULL == lpDestBaseAddr)
{
ShowError("VirtualAllocEx");
return FALSE;
}
// 写入替换的数据
bRet = ::WriteProcessMemory(pi.hProcess, lpDestBaseAddr, pReplaceData, dwReplaceDataSize, NULL);
if (FALSE == bRet)
{
ShowError("WriteProcessError");
return FALSE;
}
// 获取线程上下文
// 注意此处标志,一定要写!!!
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;
}
效果图:
原文链接: https://www.cnblogs.com/csnd/p/15613455.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;
也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/359894
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!