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. 用户 IP,里面藏了多少秘密?

    大家都知道,要邮寄一封信给正确的收件人,需要提供准确而精细的地址,这个地址需要从国家和城市精确到邮政编码,街道和门牌号码.只有这样,邮局的工作人员才能知道将信送到那里. Internet 上也是如此, ...

  2. DNS--主从

    操作系统:centos7.8 DNS-master:192.168.198.128 DNS-slave:192.168.198.129 一 主从同步过程 master修改完成重启后 将传送notify ...

  3. 【HZERO】定时任务

  4. 【SpringBoot】 启动后会调用执行的方法的 (五种方式)

    在 SpringBoot 工程 启动后, 会调用执行方法的五种方式: 亲自测试, 按照执行顺序如下: 第一种: @Component public class SpringContext1 { @Po ...

  5. celery与django的结合以及定时任务配置

    一.conda创建新的开发环境 C:\Users\yc>conda create --name celery_django python=3.8 C:\Users\yc>conda inf ...

  6. freeswitch通过limit限制cps

    概述 freeswitch在业务开发中有极大的便利性,因为fs内部实现了很多小功能,这些小功能组合在一起,通过拨号计划就可以实现很多常见的业务功能. 在voip云平台的开发中,我们经常会碰到资源的限制 ...

  7. 线性代数 · 矩阵 · Matlab | 满秩分解代码实现

    背景 - 矩阵的满秩分解: 若 A 为 m×n 矩阵,rank(A) = r,则存在 F m×r.G r×n,使得 A = FG. 其中,F 列满秩,G 行满秩. 求满秩分解的方法: 得到 A 的行最 ...

  8. (已解决)pulse secure 连接功能变灰禁用 连接面板找不到

    今天打开 pulse secure 时,发现窗口变成了这样: 连接功能是灰色的,被禁用了: 解决方案: 运行 PulseSecureService 服务. 然后就正常了!

  9. 前端开发环境搭建踩坑笔记——npm install node-sass安装失败的解决方案

    .markdown-body { line-height: 1.75; font-weight: 400; font-size: 16px; overflow-x: hidden; color: rg ...

  10. ORA-01017: 用户名/密码无效;登录被拒绝

    总结 出现此错误的原因有多种: 您的用户名或密码实际上不正确 数据库配置不正确(tnanames.ora. $ORACLE_SID 参数) 现在,我们来看看这个错误的解决方案. ORA-01017 解 ...