简介:inline hook写在DLL中。在 DLL_PROCESS_ATTACH(DLL加载时) 时hook(在汇编代码层面修改原函数跳转位置),在 DLL_PROCESS_DETACH(DLL卸载时) 时UnloadHook(在汇编代码层面还原原本的跳转位置)。
个人理解:Inline Hook 其实简单来说是 DLL 内函数劫持,因为是将原函数的跳转改变,没有用到消息钩子 SetWindowsHookEx 函数。
原理:将原DLL模块中导出的函数 extern "C++"__declspec(dllexport) 的起始字节改为跳转jmp,跳转自己的一模一样的函数(从原文件中复制,在自己的cpp中构建相同的函数,记得改函数名)。
流程:
- 原进程执行到目标函数时,遇到跳转指令 jmp
- 执行自建相同函数中的自己的代码
- 不放行,return;结束
- 放行,UnloadHook(),卸载hook,防止死循环
- 使用原目标函数,记得将参数原封不动加入
- 若要继续hook,则hook()
相关函数:
- CreateProcess:创建一个新的进程和它的主线程,这个新进程运行指定的可执行文件,返回BOOL。
- memset:填充的内存块,无返回值。
- GetProcAddress:获取DLL导出的函数地址,返回值是DLL中的输出函数地址。
- GetModuleHandle:获取模块句柄(可能需要打开进程OpenProcess)返回值是模块句柄。
- ReadProcessMemory:读取进程内存,返回成功或失败。
- WriteProcessMemory:写入进程内存,返回成功或失败。
- GetCurrentProcess:返回当前进程的句柄
代码:
dllmain.cpp
#include<Windows.h>
//hook的目标函数
PROC hook_Function = NULL;
//hook的目标函数前五个字节
BYTE m_oldBytes[5];
//替代hook的目标函数的五个字节
BYTE m_newBytes[5];
BOOL Hook(LPCSTR hookModelName, LPCSTR hookFuncName, PROC myFunc);
void UnloadHook();
BOOL ReHook();
// NO2 从官方文档中复制来,在其中实现自己的代码。
//运行原理: 未劫持:程序1-> 正常的函数 -> 程序2
// 劫持后:程序1-> NO2函数 -> 程序2
BOOL WINAPI MyCreateProcessW(
_In_opt_ LPCWSTR lpApplicationName,
_Inout_opt_ LPWSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCWSTR lpCurrentDirectory,
_In_ LPSTARTUPINFOW lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation
)
{
BOOL flag = true;//true 不允许原程序继续执行,false 允许原程序继续执行
if (flag)
{
//不允许原程序继续执行
MessageBox(NULL, "lq", "lq", MB_OK);
//在此写入自己的代码
}
else
{
//允许原程序继续执行
UnloadHook();
MyCreateProcessW(lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation);
//在此写入自己的代码
ReHook();
}
return true;
}
// NO1 程序从此进入,主要作用是找到需要劫持的函数,调用NO3将跳转到正常函数的汇编代码修改成NO2函数的汇编地址
BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved )
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
//当DLL首次加载时
//这里hook的是 CreateProcessW 函数
Hook("kernel32.dll", "CreateProcessW", (PROC)MyCreateProcessW);
break;
}
case DLL_PROCESS_DETACH:
{
UnloadHook();
}
break;
}
return TRUE;
}
//NO3
BOOL Hook(LPCSTR hookModelName, LPCSTR hookFuncName,PROC myFunc)
{
//防止这两个地址内存保留原来的东西
memset(m_oldBytes,0,5);
memset(m_newBytes,0,5);
//获取模块中的目标函数地址。由于DLL加载进入了进程内部,所以不需要 OpenProcess
hook_Function = (PROC)GetProcAddress(GetModuleHandle(hookFuncName), hookFuncName);
if (hook_Function == 0)return false;
//保存 ReadProcessMemory 返回的 读取的大小
DWORD Ret = 0;
//保存目标函数的前五个字符
ReadProcessMemory(GetCurrentProcess(), hook_Function, m_oldBytes, 5, &Ret);
//构造jmp指令
// '\xE9' 为二进制码,对应jmp
m_newBytes[0] = '\xE9';
//将我们函数的地址写入后四位,注意是偏移量,用 目标函数地址-我们函数地址-指令大小。为什么要减指令大小?因为汇编语言执行完我们的指令,寄存器地址是在当前指令下方。
*(DWORD*)(m_newBytes + 1) = (DWORD)hook_Function - (DWORD)hook_Function - 5;
//将构建的指令替换原指令,执行到目标函数时跳转
WriteProcessMemory(GetCurrentProcess(), hook_Function, m_newBytes, 5, &Ret);
return true;
}
void UnloadHook()
{
//若 hook_Function == 0 则表示连目标函数都没有获取到,则没有hook住
if (hook_Function != NULL)
{
DWORD Ret = 0;
//还原目标函数,即不跳转了
WriteProcessMemory(GetCurrentProcess(), hook_Function, m_oldBytes, 5, &Ret);
}
}
BOOL ReHook()
{
//若 hook_Function == 0 则表示连目标函数都没有获取到,则没有hook住
if (hook_Function != NULL)
{
DWORD Ret = 0;
//此时已经保存了所有参数
WriteProcessMemory(GetCurrentProcess(), hook_Function, m_newBytes, 5, &Ret);
}
return true;
}