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;
}
输出结果
感谢分享,收藏备用
页:
[1]