前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ring3 x32挂起进程注入原理.

ring3 x32挂起进程注入原理.

作者头像
IBinary
发布2019-12-11 11:25:24
8400
发布2019-12-11 11:25:24
举报
文章被收录于专栏:逆向技术

目录

一丶挂起进程注入简介与前言

挂起进程其实就是在创建进程的时候不让其先执行.然后获取它的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如下:

代码语言:javascript
复制
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的地址

下面主要针对这几点讲解.

二丶ShellCode核心讲解.

2.1 保存Contex.EIP

首先挂起进程注入. 你通过CONTEXT 这个结构可以获取 目标进程EIP.但是你要想办法执行完ShellCode之后跳转回去. 所以这里我使用了一个简单的方法

代码语言:javascript
复制
push Address
ret

这个方式是利用堆栈.以及ret指令来进行返回的. 当我们保存了原始地址.然后ret的时候.ret会把我们push的地址当做返回地址来执行.

2.2 DLL路径重定位

我们正常来说.调用LoadLibraryA/W的时候.都会进行参数压栈进而进行调用. 如:

代码语言:javascript
复制
push xxxx地址 (地址里面是个DLL路径)
call LoadLibraryA; 调用LoadLibrary

而为了方便我直接代码重定位.直接将ShellCode尾部写入我们的DLL路径.

代码语言:javascript
复制
call $0:
pop ebx

使用这两句可以得到ebx当前指令指定的EIP的地址. 直接当前 EIP + xxx偏移(偏移是你写DLL路径的位置的偏移) 就是我们的参数地址.

2.3 LoadLibrary的重定位

当你直接使用 Call的方式调用LoadLibrary的时候.你还需要计算偏移.各种等等. 但是这种不需要的.为啥. 因为Windows在启动后 kernel32的基址已经固定了.任何程序启动都会默认加载 kernel32的.所以直接使用LoadLibrary当地址即可. 但是你使用Call的方式 (call LoadLibrary) 你还需要计算你的ShellCode 与LoadLibrary的偏移.所以我们直接使用寄存器来做.

代码语言:javascript
复制
mov reg,LoadLibrary
call reg

reg代表任意通用寄存器. 此时我们的ShellCode就可以正常运行了.通过以上步骤就可以开始运行了.

三丶 全部C++代码.拷贝即可使用.

代码语言:javascript
复制
#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;
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-12-03 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一丶挂起进程注入简介与前言
  • 二丶ShellCode核心讲解.
    • 2.1 保存Contex.EIP
      • 2.2 DLL路径重定位
        • 2.3 LoadLibrary的重定位
        • 三丶 全部C++代码.拷贝即可使用.
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档