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. LLVM 参考链接

    https://www.llvm.org/ LLVM Essentials(Paperback) LLVM 编译器 https://www.cs.cmu.edu/afs/cs.cmu.edu/acad ...

  2. 【QT】tr()的作用

    函数 tr() 全名是 QObject::tr() ,被它处理的 字符串可以 使用工具提取出来翻译成其他语言, 也就是做国际化使用. 只要记住,Qt 的最佳实践:如果你想让你的程序国际化的话,那么,所 ...

  3. [啊哈!算法] 零基础彻底弄懂"并查集"

    今天是算法数据结构专题的第5篇文章,我们一起来学习一下「并查集」. 并查集被很多ACMer认为是最简洁而优雅的数据结构之一,主要用于解决一些元素分组的问题.并支持两种操作: 合并(Union):把两个 ...

  4. Codeforces Round #704 (Div. 2) A~E

    比赛链接:https://codeforces.com/contest/1492 1492A.Three swimmers 题意: 有三名游泳的人,他们分别需要 \(a,b,c\) 分钟才能在一个游泳 ...

  5. 打破监控壁垒,棉花厂3D可视化建设让生产加工更加智能化

    前言 现在的棉花加工行业还停留在传统的反应式维护模式当中,当棉花加下厂的设备突然出现故障时,控制程序需要更换.这种情况下,首先需要客户向设备生产厂家请求派出技术人员进行维护,然后生产厂家才能根据情况再 ...

  6. java中除法结果不对。

    今天遇一个非常简单地计算,计算结果居然是不对0,查了一些前辈们的资料动手实验了一下,实验结果和代码分享给大家.需要计算的公式:(7/10)*0.8 结果居然不是0.56 而是 0,最后找到原因(7/1 ...

  7. 浅谈 Docker 网络:单节点多容器

    1.同网段多容器访问 这一节将对 Docker 多容器网络进行讨论,构建容器网络示意图如下:

  8. 在Chrome中安装扩展程序

    场景:在Chrome中安装NetBeans Connector插件,将下载好的crx文件拖到扩展程序页面时,发现该插件并没有安装成功. 分析:浏览器默认禁用了拖入安装 .crx 扩展的功能,导致crx ...

  9. Linux telnet安装及端口测试联通性

    安装步骤: 可使用该文中的步骤进行安装,已经过本人验证,是可以安装成功的: https://blog.csdn.net/doubleqinyan/article/details/80492421 安装 ...

  10. 数字IC设计全流程介绍

    数字IC设计全流程设计 掌握数字集成电路设计的流程 数字设计流程中每个阶段主要做哪些工作? 数字设计流程中每个阶段使用的主要EDA工具? 数字电路常用软件公司Mentor(questasim),Syn ...