Hook 技术常被叫做挂钩技术,挂钩技术其实早在DOS时代就已经存在了,该技术是Windows系统用于替代DOS中断机制的具体实现,钩子的含义就是在程序还没有调用系统函数之前,钩子捕获调用消息并获得控制权,在执行系统调用之前执行自身程序,简单来说就是函数劫持.

HOOK技术的实现方法比较多,常见的HOOK方法有 Inline Hook、IAT Hook、EAT Hook 这三种,钩子的应用范围非常广泛,比如输入监控、API拦截、消息捕获、改变程序执行流程等,杀毒软件也会HOOK钩住一些特殊的API函数,起到监控系统运行状态的目的,黑客们也会通过钩子技术截获一些有价值的数据,例如键盘消息等.

该笔记是针对32位Hook的简易封装,自己留着也没什么意思,还是分享出来吧,转载请加出处,谢谢!

Hook 实现去弹窗: 首先我们来实现一个小功能,这里有一个小程序,当我们点击弹窗时会自动的弹出一个MessageBox提示,我们的目标是通过注入DLL的方式Hook钩挂住MessageBox从而实现去除这个弹窗的目的,先来看一下Hook的思路:

1.调用 GetModuleHandle 来获取到user32.dll模块的基址

2.调用 GetProcAddress 获取到MessageBoxA弹窗的基址

3.调用 VirtualProtect 来修改MsgBox前5个字节内存属性

4.计算 Dest - MsgBox - 5 重定位跳转地址,并写入JMP跳转指令

5.计算 Dest + Offset + 5 = MsgBox +5 得到需要跳转回ret的位置

6.最后调用 VirtualProtect 来将内存属性修改为原始状态

首先我们载入带有MsgBox弹窗的程序,然后在X64DBG上按下Ctrl+G输入MessageBoxA找到我们需要Hook的地方,如下所示我们为了完成弹窗转向功能,只需要在函数开头写入jmp无条件跳转指令即可,在32位系统中JMP指令默认占用5个字节,前三条指令恰好5个字节,为了能够保持堆栈平衡,我们需要记下前三条指令,并在自己的中转函数中补齐.

759F1F70 | 8BFF                     | mov edi,edi                    | Src 替换为 jmp xxxx
759F1F72 | 55 | push ebp | 替换为 jmp xxxx
759F1F73 | 8BEC | mov ebp,esp | 替换为 jmp xxxx
759F1F75 | 6A FF | push 0xFFFFFFFF |
759F1F77 | 6A 00 | push 0x0 |
759F1F79 | FF75 14 | push dword ptr ss:[ebp+0x14] |
759F1F7C | FF75 10 | push dword ptr ss:[ebp+0x10] |
759F1F7F | FF75 0C | push dword ptr ss:[ebp+0xC] |
759F1F82 | FF75 08 | push dword ptr ss:[ebp+0x8] |
759F1F85 | E8 D6010000 | call <MessageBoxTimeoutA> |
759F1F8A | 5D | pop ebp | Dest
759F1F8B | C2 1000 | ret 0x10 |

我们还需要计算出程序的返回地址,使用759F1F8A - 772A1F70 = 1A从而得出返回地址就是基址加上1A,这里的返回地址其实就是返回到原MessageBox弹窗的ret 0x10的位置759F1F8B,从这里可以看出屏蔽弹窗的原理就是通过中转函数跳过了弹窗函数的执行,我们直接编译这段代码,并注入到弹窗程序测试,会发现弹窗被去除了.

#include <Windows.h>
#include <stdio.h> DWORD jump = 0;
// naked 关键字的作用是,不给我添加任何的汇编修饰
__declspec(naked) void Transfer(){
__asm{
mov edi, edi
push ebp
mov ebp, esp
mov ebx, jump // 取出跳转地址
jmp ebx // 无条件转向
}
} bool APIENTRY DllMain(HANDLE handle, DWORD dword, LPVOID lpvoid)
{
HMODULE hwnd = GetModuleHandle(TEXT("user32.dll"));
DWORD base = (DWORD)GetProcAddress(hwnd, "MessageBoxA");
DWORD oldProtect = 0;
// 将内存设置为可读可写可执行状态,并将原属性保存在oldProtect方便恢复
if (VirtualProtect((LPVOID)base, 5, PAGE_EXECUTE_READWRITE, &oldProtect))
{
DWORD value = (DWORD)Transfer - base - 5; // 计算出需要Hook的地址
jump = base + 0x1a; // 计算出返回地址
__asm{
mov eax, base
mov byte ptr[eax], 0xe9 // e9 = jmp 指令机器码
inc eax // 递增指针
mov ebx, value // 需要跳转到的地址
mov dword ptr[eax], ebx
}
// 恢复内存的原始属性
VirtualProtect((LPVOID)base, 5, oldProtect, &oldProtect);
}
return true;
}

Hook 实现改标题: 通常情况下,程序设置标题会调用SetWindowTextA这个API函数,我们可以拦截这个函数,并传入自定义的窗口名称,从而实现修改指定窗口的标题的目的,代码只是在上面代码的基础上稍微改一下就能实现效果.

#include <Windows.h>
#include <stdio.h> DWORD jump = 0; __declspec(naked) bool _stdcall Transfer(HWND hwnd, LPCSTR lpString){
__asm{
mov edi, edi
push ebp
mov ebp, esp
mov ebx, jump
jmp ebx
}
} bool __stdcall MySetWindowTextA(HWND hwnd, LPCSTR lpString){
char * lpText = "LyShark 破解版";
return Transfer(hwnd, lpText);
} bool APIENTRY DllMain(HANDLE handle, DWORD dword, LPVOID lpvoid)
{
HMODULE hwnd = GetModuleHandle(TEXT("user32.dll"));
DWORD base = (DWORD)GetProcAddress(hwnd, "SetWindowTextA");
DWORD oldProtect = 0; if (VirtualProtect((LPVOID)base, 5, PAGE_EXECUTE_READWRITE, &oldProtect))
{
DWORD value = (DWORD)MySetWindowTextA - base - 5;
jump = base + 5;
__asm{
mov eax, base
mov byte ptr[eax], 0xe9
inc eax
mov ebx, value
mov dword ptr[eax], ebx
}
VirtualProtect((LPVOID)base, 5, oldProtect, &oldProtect);
}
return true;
}

针对Hook代码封装: 上面代码并不具备通用性,这里我们可以使用C++将其封装成类,这样使用会更方便,通常封装类都会存在两个文件,这里我们将头文件定义为hook.h将实现文件定义为hook.cpp分别实现这两个文件代码逻辑.

#pragma once
#include <Windows.h> #ifdef __cplusplus
extern "C"{
#endif class MyHook
{
public:
PROC m_pfnOrig; // 保存函数地址
BYTE m_bOldBytes[5]; // 保存函数入口代码
BYTE m_bNewBytes[5]; // 保存Inlie Hook代码
public:
MyHook();
~MyHook(); BOOL Hook(LPSTR pszModuleName, LPSTR pszFuncName, PROC pfnHookFunc);
BOOL UnHook();
BOOL ReHook();
};
#ifdef __cplusplus
}
#endif

如下是代码实现部分MyHook()构造函数用来初始化,析构函数用来清空并恢复钩子,Hook则是具体实现挂钩的细节,在Hook()成员函数中完成了3项工作,首先是获得了被HOOK函数的函数地址,接下来是保存了被HOOK函数的前5字节,最后是用构造好的跳转指令来修改被HOOK函数的前5字节的内容.

#include "hook.h"

// 构造函数: 负责初始化
MyHook::MyHook()
{
m_pfnOrig = NULL;
ZeroMemory(m_bOldBytes, 5);
ZeroMemory(m_bNewBytes, 5);
} MyHook::~MyHook()
{
UnHook();
m_pfnOrig = NULL;
ZeroMemory(m_bOldBytes, 5);
ZeroMemory(m_bNewBytes, 5);
} // 挂钩
BOOL MyHook::Hook(LPSTR pszModuleName, LPSTR pszFuncName, PROC pfnHookFunc)
{
BOOL bRet = FALSE; // 获取指定模块中函数的地址
m_pfnOrig = (PROC)GetProcAddress(GetModuleHandle(pszModuleName),pszFuncName); if (m_pfnOrig != NULL)
{
// 保存该地址处 5 字节的内容
DWORD dwNum = 0;
ReadProcessMemory(GetCurrentProcess(),m_pfnOrig,m_bOldBytes,5,&dwNum); // 构造 JMP 指令
m_bNewBytes[0] = '\xe9'; // jmp Opcode // pfnHookFunc 是 HOOK 后的目标地址
// m_pfnOrig 是原来的地址
// 5 是指令长度
*(DWORD *)(m_bNewBytes + 1) = (DWORD)pfnHookFunc - (DWORD)m_pfnOrig - 5; // 将构造好的地址写入该地址处
WriteProcessMemory(GetCurrentProcess(),m_pfnOrig,m_bNewBytes,5,&dwNum);
bRet = TRUE;
}
return bRet;
}
// 恢复钩子
BOOL MyHook::UnHook()
{
if (m_pfnOrig != 0)
{
DWORD dwNum = 0;
WriteProcessMemory(GetCurrentProcess(),m_pfnOrig,m_bOldBytes,5,&dwNum);
}
return TRUE;
}
// 重新挂钩
BOOL MyHook::ReHook()
{
BOOL bRet = FALSE;
if (m_pfnOrig != 0)
{
DWORD dwNum = 0;
WriteProcessMemory(GetCurrentProcess(),m_pfnOrig,m_bNewBytes,5,&dwNum);
bRet = TRUE;
}
return bRet;
}

到此为止整个Inline Hook的封装已经完成了,在后面的代码中,可以很容易地实现对函数的HOOK功能,这里我再多说一句,如果我们需要在自己实现的MessageBox函数中要调用原始的API函数,则需要恢复Inline Hook,否则程序将会崩溃.

#include <Windows.h>
#include "hook.h" MyHook MsgHook; int WINAPI MyMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
// 先来恢复Hook 之所以要恢复是因为我们需要调用原始的MsgBox弹窗
MsgHook.UnHook(); MessageBoxA(hWnd, "hook inject", lpCaption, uType); // 弹窗完成后重新Hook
MsgHook.ReHook();
return 0;
} int main(int argc, char * argv[])
{
// 开始Hook
MsgHook.Hook("user32.dll", "MessageBoxA", (PROC)MyMessageBoxA);
MessageBoxA(NULL, "hello lyshark", "Msg", MB_OK);
// 结束Hook
MsgHook.UnHook();
return 0;
}

第二种封装方式: 该封装方式直接将定义与实现写到hook.h头文件中,使用时直接包含一个文件即可.

#pragma once
#include <Windows.h> #ifdef __cplusplus
extern "C"{
#endif #pragma once
class MyHook
{
public:
static DWORD Hook(LPCWSTR lpModule, LPCSTR lpFuncName, PROC lpFunction)
{
DWORD dwAddr = (DWORD)GetProcAddress(GetModuleHandle(lpModule), lpFuncName);
BYTE jmp[] =
{
0xe9, // jmp
0x00, 0x00, 0x00, 0x00, // address
0xc3 // retn
}; ReadProcessMemory(GetCurrentProcess(), (LPVOID)dwAddr, MemoryAddress(), 6, 0);
DWORD dwCalc = ((DWORD)lpFunction - dwAddr - 5);
memcpy(&jmp[1], &dwCalc, 4);
WriteProcessMemory(GetCurrentProcess(), (LPVOID)dwAddr, jmp, 6, 0); return dwAddr;
} static BOOL UnHook(LPCWSTR lpModule, LPCSTR lpFuncName)
{
DWORD dwAddr = (DWORD)GetProcAddress(GetModuleHandle(lpModule), lpFuncName);
if (WriteProcessMemory(GetCurrentProcess(), (LPVOID)dwAddr, MemoryAddress(), 6, 0))
return TRUE;
return FALSE;
} static BYTE* MemoryAddress()
{
static BYTE backup[6];
return backup;
}
};
#ifdef __cplusplus
}
#endif
#include "hook.h"

MyHook MsgHook;

int WINAPI MyMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
// 先来恢复Hook 之所以要恢复是因为我们需要调用原始的MsgBox弹窗
MsgHook.UnHook(L"user32.dll","MessageBoxA"); MessageBoxA(hWnd, "hook inject", lpCaption, uType); // 弹窗完成后重新Hook
MsgHook.Hook(L"user32.dll", "MessageBoxW", (PROC)MyMessageBoxA);
return 0;
} int main(int argc, char * argv[])
{
// 开始Hook
MsgHook.Hook(L"user32.dll", "MessageBoxA", (PROC)MyMessageBoxA); MessageBoxA(NULL, "hello lyshark", "Msg", MB_OK); // 结束Hook
MsgHook.UnHook(L"user32.dll", "MessageBoxA");
return 0;
}

C/C++ x32 Inline Hook 代码封装的更多相关文章

  1. x86平台inline hook原理和实现

    概念 inline hook是一种通过修改机器码的方式来实现hook的技术. 原理 对于正常执行的程序,它的函数调用流程大概是这样的: 0x1000地址的call指令执行后跳转到0x3000地址处执行 ...

  2. Android Hook框架adbi的分析(3)---编译和inline Hook实践

    本文博客地址:http://blog.csdn.net/qq1084283172/article/details/75200800 一.序言 在前面的博客中,已经分析过了Android Hook框架a ...

  3. Inline Hook NtQueryDirectoryFile

    Inline Hook NtQueryDirectoryFile 首先声明这个是菜鸟—我的学习日记,不是什么高深文章,高手们慎看. 都总是发一些已经过时的文章真不好意思,几个月以来沉迷于游戏也是时候反 ...

  4. android inline hook

    最近终于沉下心来对着书把hook跟注入方面的代码敲了一遍,打算写几个博客把它们记录下来. 第一次介绍一下我感觉难度最大的inline hook,实现代码参考了腾讯GAD的游戏安全入门. inline ...

  5. 在已有软件加壳保护 下实现 Inline hook

    如写的不好请见谅,本人水平有限. 个人简历及水平:. http://www.cnblogs.com/hackdragon/p/3662599.html 正常情况: 接到一个项目实现对屏幕输出内容的获取 ...

  6. windows 32位以及64位的inline hook

    Tips : 这篇文章的主题是x86及x64 windows系统下的inline hook实现部分. 32位inline hook 对于系统API的hook,windows 系统为了达成hotpatc ...

  7. Inline Hook

    @author: dlive IAT Hook时如果要钩取的API不在IAT中(LoadLibrary后调用),则无法使用该技术.而Inline Hook不存在这个限制. 0x01 Inline Ho ...

  8. INLINE HOOK过简单驱动保护的理论知识和大概思路

    这里的简单驱动保护就是简单的HOOK掉内核API的现象 找到被HOOK的函数的当前地址在此地址处先修改页面保护属性然后写入5个字节.5个字节就是一个简单的JMP指令.这里说一下JMP指令,如下: 00 ...

  9. Android Hook框架adbi的分析(2)--- inline Hook的实现

    本文博客地址:http://blog.csdn.net/qq1084283172/article/details/74452308 一. Android Hook框架adbi源码中inline Hoo ...

  10. 转移指令原理和Inline Hook

    目录 转移指令原理和Inline Hook 转移指令 操作符offset jmp指令 根据位移进行转移的jmp指令 插播HOOK知识 Inline Hook Inline Hook 原理 Hook代码 ...

随机推荐

  1. OOALV 不同ALV调用相同屏幕

    一.一次创建,多次调用 当使用OOALV开发平台,存在点击不同位置,跳转到一个ALV,除ALV内容不一致以外,其他都一样. 常规方法,即跳转到多少个ALV,就创建多少个SCREEN.而屏幕对应的输入输 ...

  2. 2020年第十一届蓝桥杯省赛 第一场(7月5日)B组个人题解

    PDF文件下载: https://files.cnblogs.com/files/Angel-Demon/CB.zip 试题 A: 跑步训练 [问题描述] 小明要做一个跑步训练. 初始时,小明充满体力 ...

  3. vue子页面给App.vue传值

    前端工程现在变成了单页面富文本的模式,整体布局定下来后,跳转只在<router-view>中展示,外层的布局不容易改变.最近发现有如下这个方法可以直接传值给App.vue,经过实践确实可以 ...

  4. Invalid options object. Sass Loader has been initialized using an options obj

    https://blog.csdn.net/liwan09/article/details/106981239

  5. mongodb导入本地json文件

  6. freeswitch on centos dockerfile模式

    概述 freeswitch是一款简单好用的VOIP开源软交换平台. centos7 docker上编译安装fs的流程记录,本文使用dockerfile模式. 环境 docker engine:Vers ...

  7. python之数学函数应用

    一.abs(x) 1.作用: 函数返回 x(数字)的绝对值,如果参数是一个复数,则返回它的大小(模) 2.举例说明: #1.abs() a = abs(-15) print(a) b = abs(1+ ...

  8. Kubernetes 网络:Pod 和 container 的那点猫腻

    1. Kubernetes 网络模型 在 Kubernetes 的网络模型中,最小的网络单位是 Pod.Pod 的网络设计原则是 IP-per-Pod,即 Pod 中 container 共享同一套网 ...

  9. mybatis plus 中增删改查及Wrapper的使用

    本文为博主原创,未经允许不得转载: mybatis plus 通过封装  baseMapper 以及 ServiceImpl ,实现对数据库的增删改查操作,baseMapper 是我们通常所说的 da ...

  10. wireshark 显示过滤表达式

    转载请注明出处: 1.根据协议过滤: 在显示过滤表达式的输入框中直接输入对应的协议类型即可:http   tcp  udp 2.根据 IP 过滤: 根据源IP地址过滤:如源地址IP为:127.0.0. ...