C/C++ x32 Inline Hook 代码封装
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 代码封装的更多相关文章
- x86平台inline hook原理和实现
概念 inline hook是一种通过修改机器码的方式来实现hook的技术. 原理 对于正常执行的程序,它的函数调用流程大概是这样的: 0x1000地址的call指令执行后跳转到0x3000地址处执行 ...
- Android Hook框架adbi的分析(3)---编译和inline Hook实践
本文博客地址:http://blog.csdn.net/qq1084283172/article/details/75200800 一.序言 在前面的博客中,已经分析过了Android Hook框架a ...
- Inline Hook NtQueryDirectoryFile
Inline Hook NtQueryDirectoryFile 首先声明这个是菜鸟—我的学习日记,不是什么高深文章,高手们慎看. 都总是发一些已经过时的文章真不好意思,几个月以来沉迷于游戏也是时候反 ...
- android inline hook
最近终于沉下心来对着书把hook跟注入方面的代码敲了一遍,打算写几个博客把它们记录下来. 第一次介绍一下我感觉难度最大的inline hook,实现代码参考了腾讯GAD的游戏安全入门. inline ...
- 在已有软件加壳保护 下实现 Inline hook
如写的不好请见谅,本人水平有限. 个人简历及水平:. http://www.cnblogs.com/hackdragon/p/3662599.html 正常情况: 接到一个项目实现对屏幕输出内容的获取 ...
- windows 32位以及64位的inline hook
Tips : 这篇文章的主题是x86及x64 windows系统下的inline hook实现部分. 32位inline hook 对于系统API的hook,windows 系统为了达成hotpatc ...
- Inline Hook
@author: dlive IAT Hook时如果要钩取的API不在IAT中(LoadLibrary后调用),则无法使用该技术.而Inline Hook不存在这个限制. 0x01 Inline Ho ...
- INLINE HOOK过简单驱动保护的理论知识和大概思路
这里的简单驱动保护就是简单的HOOK掉内核API的现象 找到被HOOK的函数的当前地址在此地址处先修改页面保护属性然后写入5个字节.5个字节就是一个简单的JMP指令.这里说一下JMP指令,如下: 00 ...
- Android Hook框架adbi的分析(2)--- inline Hook的实现
本文博客地址:http://blog.csdn.net/qq1084283172/article/details/74452308 一. Android Hook框架adbi源码中inline Hoo ...
- 转移指令原理和Inline Hook
目录 转移指令原理和Inline Hook 转移指令 操作符offset jmp指令 根据位移进行转移的jmp指令 插播HOOK知识 Inline Hook Inline Hook 原理 Hook代码 ...
随机推荐
- anaconda学习(未完成)
1.Anaconda安装教程(以32.7.4为例)官网地址:https://www.anaconda.com/download(如无法下载可跳转清华源下载)下载完成后点击打开即可安装点击Next选择I ...
- C++:如何将 LLVM 嵌套到你的项目中去
IDE: Clion LLVM cmake_minimum_required(VERSION 3.9) project(clang_demo) find_package(LLVM REQUIRED C ...
- Mac 配置 OpenCV C++ 版本
今天紀錄一下如何在 Mac 上安裝 OpenCV for C++ 開發環境 使用 Brew 安装,pkgconfig 检测,2023.5.17 Mac x86 ( Intel ) , Mac M1 ( ...
- ZOJ - 1610 区间修改+暴力单点查询
一.内容 题意:给定[1,8000]区间,给定n组操作,每次将一段区间修改成某种颜色(对上一次颜色进行覆盖),最后问你能看到多少种颜色,且每种颜色有多少段. 二.思路 题目给定的区间是(x, y]左开 ...
- 【每日一题】31.「土」秘法地震 (二维前缀和 / DP)
补题链接:Here 题意就是要找每一个 \(k * k\) 的小正方形里至少有一个1的数量 显然我们可以通过二维前缀和处理出(1, 1) 到 (n, m) 的数量 然后通过枚举处理出答案,具体思想是容 ...
- 【每日一题】16.Treepath (LCA + DP)
补题链接:Here 题意总结:寻找有多少条两个点之间偶数路径 看完题,很容易想到在树型中,同一层的节点必然是偶数路径到达,还有就是每隔两层的节点一样可以到达,所以我就理所应当的写了如下代码 using ...
- vivo 悟空活动中台 - 栅格布局方案
本文首发于 vivo互联网技术 微信公众号 链接: https://mp.weixin.qq.com/s/6O0CH0U_WE1YkPK75m-jDQ作者:悟空中台研发团队 一.背景 今天来给大家分享 ...
- Vue tinymce富文本编辑器整合
最近再弄一个后台管理系统,挑选了不少的编辑器,最终选择了tinymce,UI精美,功能模块多,可按需加载配置 vue cli 3 + tinymce5.0版本整合参考:https://liubing. ...
- Jackson 使用 @JsonFormat 注解进行时间格式化
本文为博主原创,未经允许不得转载: 最近帮同事定位了一个现网问题,记录一下: 项目中对所有请求的参数都进行了 Jackson 序列化,在接收请求的实体类字段上使用 @JsonFormat 注解,该注解 ...
- Angular系列教程之依赖注入详解
.markdown-body { line-height: 1.75; font-weight: 400; font-size: 16px; overflow-x: hidden; color: rg ...