舒默哦 发表于 2020-5-12 12:34

inlinehook框架

本帖最后由 舒默哦 于 2020-5-12 12:52 编辑

https://static.52pojie.cn/static/image/hrline/4.gif

步骤:
1.构造shellcode
2.寻找主程序的空白区域,插入shellcode
3.在被hook的地址跳转到这个空白区域,执行shellcode


https://static.52pojie.cn/static/image/hrline/3.gif

运用:
把inlinehook.h添加到test_dll.dll这个工程里面


inlinehook的接口函数是SetHook() ,函数有四个参数,
参数一:进程句柄
参数二:要hook的地址
参数三:总共要hook多少个字节
参数四:hook之后要跳转的处理函数
注意:如果不用参数三,想要自动计算hook的字节数,需要用到反汇编引擎。


编译器用的vs2017,设置的是多字符集


编译完成后,用注入工具注入到"测试.exe"这个进程里面。
点击按钮就会触发这个inlinehook。

inlinehook.h
#include "CString.h"
#include <TlHelp32.h>

BYTE m_HookByte;//存放跳转的字节
BYTE m_OrigByte;//存放原始的字节
HANDLE m_hProcess;//存放进程句柄
DWORD m_nLength;//存放hook的长度
DWORD m_Addr;//存放要hook的地址
DWORD m_JmpFuncTion;//要跳转的函数

DWORD m_Jmp_Orig;//跳回到原来的地方继续执行

DWORD m_Blank_Area;//空白区域


//函数声明
void SetHook(HANDLE hProcess, DWORD Addr,DWORD nLength, DWORD CallBackFun);
void InjectShellCode();
void UnHook();

//寻找空白区域
DWORD Find_Blank();

/*
push xxxxxxxx
ret
*/
/*
参数一:进程句柄
参数二:要hook的地址
参数三:总共要hook多少个字节
参数四:hook之后要跳转的处理函数
*/
void SetHook(HANDLE hProcess, DWORD Addr, DWORD nLength, DWORD JmpFunTion)
{
    if (hProcess == NULL)
    {
      MessageBox(NULL, "句柄不能为空", "提示", MB_OK);
      return;
    }
    if (Addr == 0)
    {
      MessageBox(NULL, "要hook的地址不能为0", "提示", MB_OK);
      return;
    }

    if (nLength < 6)
    {
      MessageBox(NULL, "hook失败\r\n要hook的位置字节不足", "提示", MB_OK);
      return;
    }

    //初始化
    memset(m_HookByte, 0x90, sizeof(m_HookByte));
    memset(m_OrigByte, 0x90, sizeof(m_OrigByte));

    //1.1 保存句柄 地址 长度(卸载的时候要用到)
    m_hProcess = hProcess;
    m_Addr = Addr;
    m_nLength = nLength;
    m_JmpFuncTion = JmpFunTion;
    m_Jmp_Orig = Addr + nLength;

    if (Find_Blank()==0)
    {
      MessageBox(NULL, "寻找空白区域失败,注入不成功!", "提示", MB_OK);
      return;
    };

    m_HookByte = 0x68;
    m_HookByte = 0xc3;
    *(DWORD*)(m_HookByte + 1) = (DWORD)m_Blank_Area;

    //保存要hook的地方
    BOOL isRead = ReadProcessMemory(hProcess, (LPVOID)Addr, m_OrigByte, nLength, 0);

    if (isRead == 0)
    {
      MessageBox(NULL, "原始字节保存失败!", "提示", MB_OK);
    }

    DWORD written = 0;
    DWORD dwold;
    VirtualProtectEx(hProcess, (LPVOID)Addr, nLength, PAGE_EXECUTE_READWRITE, &dwold);

    WriteProcessMemory(hProcess, (LPVOID)Addr, m_HookByte, nLength, &written);

    VirtualProtectEx(hProcess, (LPVOID)Addr, nLength, dwold, &dwold);

    InjectShellCode();

   
}

//获取进程主模块的基址和大小
void GetModuleInf(DWORD* BaseAddr, DWORD* BaseSize)
{
    //1 获取进程主模块的基址和大小
    HANDLE      hModuleSnap = INVALID_HANDLE_VALUE;
    MODULEENTRY32 me32 = { sizeof(MODULEENTRY32) };
    //1. 创建一个模块相关的快照句柄
    hModuleSnap = CreateToolhelp32Snapshot(
      TH32CS_SNAPMODULE,// 指定快照的类型
      GetCurrentProcessId());            // 指定进程
    if (hModuleSnap == INVALID_HANDLE_VALUE)
      return;
    //2. 通过模块快照句柄获取第一个模块信息
    if (!Module32First(hModuleSnap, &me32)) {
      CloseHandle(hModuleSnap);
      return;
    }
    //3. 循环获取模块信息
    do {
      /*printf("模块基址:%d,模块大小:%d,模块名称:%s\n",
            me32.modBaseAddr, me32.modBaseSize, me32.szModule);*/
      CString str = me32.szModule;
      if (strcmp(str.CutFromRight(3).MakeLower().GetString(), "exe") == 0)
      {
            *BaseAddr = (DWORD)me32.modBaseAddr;
            *BaseSize = me32.modBaseSize;
            break;
      }

    } while (Module32Next(hModuleSnap, &me32));
    //4. 关闭句柄并退出函数
    CloseHandle(hModuleSnap);
}



DWORD Find_Blank()
{
    DWORD BaseAddr = 0;//主模块的基址
    DWORD BaseSize = 0;//模块的大小
    //DWORD BaseCode = 0;//代码基址

    //获取主模块基址和大小
    GetModuleInf(&BaseAddr, &BaseSize);

    MEMORY_BASIC_INFORMATION Mem_Info;
    if (VirtualQueryEx(GetCurrentProcess(), (LPCVOID)(0x401000), &Mem_Info, 28) == 0)
    {
      MessageBox(NULL, "模块信息获取失败!", "", MB_ICONINFORMATION);
      return 0;
    }
    char* strbuff = new char;
    if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID)(0x401000), strbuff, Mem_Info.RegionSize, 0) == 0)
    {
      MessageBox(NULL, "分析时读取内存失败!", "", MB_ICONINFORMATION);
      return 0;
    }

    char temp_k[] = { //易语言的0xcc较多,如果是c语言写的程序用0x00来找
      0xCC,0xCC, 0xCC,0xCC, 0xCC,0xCC, 0xCC,0xCC, 0xCC,0xCC,
      0xCC,0xCC, 0xCC,0xCC, 0xCC,0xCC, 0xCC,0xCC, 0xCC,0xCC,
      0xCC,0xCC, 0xCC,0xCC, 0xCC,0xCC, 0xCC,0xCC, 0xCC,0xCC,
      0xCC,0xCC, 0xCC,0xCC, 0xCC,0xCC, 0xCC,0xCC, 0xCC,0xCC,
      0xCC,0xCC, 0xCC,0xCC, 0xCC,0xCC, 0xCC,0xCC, 0xCC,0xCC
    };

    CString strTotal = "找空白区域";
    //寻找二进制,我在CString类封装了一个方法Find_Memory_Bytes,代码贴在后面
    DWORD Blank = strTotal.Find_Memory_Bytes(strbuff, temp_k, BaseSize- sizeof(temp_k),sizeof(temp_k));

    if (Blank==0)
    {
      MessageBox(NULL, "空白区域寻找失败", "提示", MB_OK);
      return 0;
    }

    Blank = Blank - (DWORD)strbuff + 0x401000;
    m_Blank_Area = Blank;
    //CString ss = Blank;
    //MessageBox(NULL, ss.GetString(), "找到空白区域了", MB_OK);

    return Blank;
}


//插入ShellCode
void InjectShellCode()
{
    char ShellCode[] = {
      0x60,0x9C,0xB8,
      m_JmpFuncTion & 0xFF,(m_JmpFuncTion & 0xFF00) >> 8,(m_JmpFuncTion & 0xFF0000) >> 16,m_JmpFuncTion >> 24,
      0xFF,0xD0,
         0x9D,0x61,
      m_OrigByte,m_OrigByte,m_OrigByte,m_OrigByte,m_OrigByte,m_OrigByte,m_OrigByte,m_OrigByte,
      m_OrigByte,m_OrigByte,m_OrigByte,m_OrigByte,m_OrigByte,m_OrigByte,m_OrigByte,m_OrigByte,
      m_OrigByte,m_OrigByte,m_OrigByte,m_OrigByte,
      0x68,
      m_Jmp_Orig & 0xFF,(m_Jmp_Orig & 0xFF00) >> 8,(m_Jmp_Orig & 0xFF0000) >> 16,m_Jmp_Orig >> 24,
      0xC3
    };

    DWORD written = 0;
    DWORD dwold;
    VirtualProtectEx(m_hProcess,(LPVOID)m_Blank_Area, sizeof(ShellCode), PAGE_EXECUTE_READWRITE, &dwold);

    WriteProcessMemory(m_hProcess, (LPVOID)m_Blank_Area, ShellCode, sizeof(ShellCode), &written);
   

    VirtualProtectEx(m_hProcess,(LPVOID)m_Blank_Area, sizeof(ShellCode), dwold, &dwold);
}

//卸载hook
void UnHook()
{
    DWORD written = 0;
    DWORD dwold;
    VirtualProtectEx(m_hProcess, (LPVOID)m_Addr, m_nLength, PAGE_EXECUTE_READWRITE, &dwold);

    WriteProcessMemory(m_hProcess, (LPVOID)m_Addr, m_OrigByte, m_nLength, &written);

    VirtualProtectEx(m_hProcess, (LPVOID)m_Addr, m_nLength, dwold, &dwold);
}

test_dll.dll
#include "InlineHook.h"

void CallBack()
{
    //MessageBox(NULL, "测试成功", "提示", MB_OK);
    //定义创建进程需要用的结构体                                                                        
    STARTUPINFO si = { 0 };
    PROCESS_INFORMATION pi;
    si.cb = sizeof(si);

    //创建子进程                                                                        
    BOOL res = CreateProcess(
      (LPSTR)"网络流量监控.exe"
      ,NULL,
      NULL,
      NULL,
      TRUE,                                 //TRUE的时候,说明子进程可以继承父进程的句柄表                              
      CREATE_NEW_CONSOLE,
      NULL,
      NULL, &si, &pi);
//这儿必定会失败,因为目录不对
    if (res==0)
    {
      MessageBox(NULL, "子进程创建失败!", "", MB_OK);
    }
    //UnHook();
}

//线程回调函数
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{

    SetHook(GetCurrentProcess(), 0x00401154, 6, (DWORD)CallBack);
    return 0;
}

BOOL APIENTRY DllMain(HMODULE hModule,
    DWORDul_reason_for_call,
    LPVOID lpReserved
)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    {
      MessageBox(NULL, "注入成功", "恭喜", MB_OK);
      //创建一个新线程
      HANDLE hTread = ::CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
      ::CloseHandle(hTread);

    }

    case DLL_THREAD_ATTACH:
      //MessageBox(NULL, "DLL_THREAD_ATTACH", "", MB_OK);
    case DLL_THREAD_DETACH:
      //MessageBox(NULL, "DLL_THREAD_DETACH", "", MB_OK);
    case DLL_PROCESS_DETACH:
      //MessageBox(NULL, "DLL_PROCESS_DETACH", "", MB_OK);
      break;
    }
    return TRUE;
}

寻找二进制的方法
/*
在指定内存查找目标二进制

@strRes[]:要查找的内存
@strTar[]:目标二进制
@nLenRes[]:要查找的内存的长度
@nLenTar[]:目标二进制的长度

成功返回:匹配到的内存地址
失败返回:0

目标二进制的书写格式: char strTar[]={0xE8,0x80,0x65,0x67,0x89};

注意:可以支持模糊匹配,通配符是'*',比如0xE8后面的地址是变化的,
      则目标二进制可以这样写char strTar[]={0xE8,'*','*','*','*',0x80,0x65}。

*/

DWORD CString::Find_Memory_Bytes(char * strRes, char * strTar, int nLenRes, int nLenTar)
{
    int calc = 0;

    for (int i = 0; i < nLenRes; ++i)
    {
      if (strRes == strTar || strTar == '*')
      {
            ++calc;
      }
      else
      {
            calc = 0;
      }

      if (calc == nLenTar)
      {
            return(DWORD)strRes + i - nLenTar + 1 ;
      }
    }
    return 0;
}



输出结果

hszt 发表于 2020-7-15 08:18

感谢分享,收藏备用
页: [1]
查看完整版本: inlinehook框架