首页
社区
课程
招聘
[原创]稳定多线程中的inline hook
发表于: 2019-6-19 07:56 15222

[原创]稳定多线程中的inline hook

2019-6-19 07:56
15222

我所说的是windows下的inline hook,总所周知inline hook是windows平台下很灵活好用的一种hook方式, 但inline hook是非线程稳定的,也就是说在多线程的程序中,频繁的进行inline hook有可能会导致程序崩溃,不稳定。

之前在为了解决这个问题,只找到了Hook七个字节的方法,而这两天空余时间研究了下inline hook中线程同步的方法,下面我就分享下,如有不对之处,欢迎大佬指出批评。

在inline hook中实现线程同步,下面我介绍自己所知的3种方法:

首先进行一下普通inline hook观察效果。注入程序我就不介绍了,之后附上源码。
我这里hook的是MessageBoxW对话框函数,每当被hook的程序调用了MessageBoxW函数,弹出的对话框里面的字符串就会被我修改,下图是hook前和hook后弹框的对比图。

下面附上inline hook的源代码,核心点是安装钩子的函数,之后的原子操作,信号量,修改7字节都是在此代码的基础上稍加修改:

简单形容一下就是:一个线程中的原子操作在更改一个变量时,其它所有线程相关的操作都处于暂停状态,这就是所谓的互斥。
在inline hook中写入5个字节的地方我们使用原子操作,这就可以杜绝其它线程的访问,从而使程序稳定的运行。
在这里使用到一个宏,他会以原子方式把新值赋给旧值。:

那么在inline hook中写入数据的时候我们改为原子操作就能达到优化的效果,主要就是安装钩子函数,更改如下图:

信号量是一个能跨进程使用的好东西!
简而言之,假设创建了一个信号量,设置它的信号数为1,使用WaitForSingleObject()时该信号数减1,也就说这个信号数此时为0了,此时其他线程中再有代码使用WaitForSingleObject()时那个线程就处于阻塞状态,必须等到信号数非0了,他才能继续运行下去。使用ReleaseSemaphore()函数可把信号数加1。

利用信号量的这个特性貌似也起到优化inline hook的效果,前提条件是在其他任意程序中创建一个信号量,因为它是跨进程的:

然后HOOK代码如下:


信号量的作用不止于此,在内联hook中,之前我在15PB的老师"WM"发掘出用信号量传递进程PID,当真是好用之极,让我大开眼界。

修改7字节 inline hook这种技术我在《逆向工程核心原理》一书中看到作者称之为"热补丁",那我也就称它就hook 热补丁吧。为了了解他先做一些准备工作。
首先使用OD观察普通API函数(大多数API都一样)的汇编代码,第一行指令是mov edi,edi 这条2个字节的命令显然没有任何作用完全可以忽略。

再观察普通的inline hook后MessageBoxW函数地址的汇编代码(注意这里要选运行程序注入后,使用OD附加调试)。它直接跳转到我们自己的那个函数中去。jmp(0xE9)后面是4个字节,所以破坏了原函数的可调用性,如果之占据mov edi,edi 这条2个字节的就是实现跳转的话那么从此以后就不必再频繁修改该函数地址上的数据了。注意观察大多数函数地址上面都有5个或以上的无用字节

头两个字节改为0xEB,0xF9,实现了跳转到此地址之上的5个字节的位置,在这5个字节中在构造跳转到函数的代码,那么就完美解决了hook时多线程不稳定的问题。修改7字节时如下:

修改7字节的热补丁技术,主要就是安装钩子和实现自己的函数值得注意:

最后把附上源码,里面还有远程线程注入代码。

 
 
 
 
#include "stdafx.h"

// 原函数的地址数据
BYTE g_OldData[5];
// 替换后的数据
BYTE g_NewData[5] = { 0xE9 };

// 声明自己的函数
int
WINAPI
MyMsg(_In_opt_ HWND hWnd,
    _In_opt_ LPCWSTR lpText,
    _In_opt_ LPCWSTR lpCaption,
    _In_ UINT uType);

// 安装钩子
void InHook()
{
    // 保存原函数地址
    memcpy(g_OldData, MessageBoxW, 5);

    // 计算出要跳转的偏移(自己的函数地址 - 原函数地址 - 5)       
    DWORD Offset = (DWORD)MyMsg - (DWORD)MessageBoxW - 5;
    *(DWORD*)(g_NewData + 1) = Offset;

    // 修改内存属性
    DWORD Protect;
    VirtualProtect(MessageBoxW, 5, PAGE_EXECUTE_READWRITE, &Protect);

    // 替换函数地址        
    memcpy(MessageBoxW, g_NewData, 5);

    // 还原内存属性
    VirtualProtect(MessageBoxW, 5, Protect, &Protect);
}

// 卸载钩子
void UnHook()
{
    DWORD Protect;
    VirtualProtect(MessageBoxW, 5, PAGE_EXECUTE_READWRITE, &Protect);
    memcpy(MessageBoxW, g_OldData, 5);
    VirtualProtect(MessageBoxW, 5, Protect, &Protect);
}

// 自己的Hook函数
int
WINAPI
MyMsg(
    _In_opt_ HWND hWnd,
    _In_opt_ LPCWSTR lpText,
    _In_opt_ LPCWSTR lpCaption,
    _In_ UINT uType)
{
    // 替换字符串
    lpText = L"九阳道人内联Hook成功!";
    lpCaption = L"九阳道人";

    // 先卸载钩子
    UnHook();
    // 在调用真正的MessageBoxW函数
    int nRet = MessageBoxW(hWnd, lpText, lpCaption, uType);
    // 最后在把钩子装上
    InHook();
    return nRet;
}

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 4
支持
分享
最新回复 (22)
雪    币: 3496
活跃值: (749)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
不错,讲的很好,这个能远程注入dll吗?比如有的易语言写的dll注入工具,是这个原理吗
2019-6-19 09:42
0
雪    币: 3496
活跃值: (749)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3

最后把附上源码,里面还有远程线程注入代码

附件里 远程注入的代码是 那个啊?
2019-6-19 09:50
0
雪    币: 5293
活跃值: (1735)
能力值: ( LV2,RANK:150 )
在线值:
发帖
回帖
粉丝
4
第一个工程就是注入的代码
2019-6-19 10:45
0
雪    币: 914
活跃值: (2768)
能力值: ( LV5,RANK:68 )
在线值:
发帖
回帖
粉丝
5
你是卖豆浆机的那个?
2019-6-19 14:15
0
雪    币: 61
活跃值: (1046)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
6
暂停其他线程,你想怎么hook怎么hook。
2019-6-19 18:11
0
雪    币: 10313
活跃值: (3167)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
真正稳定是第三种,用信号量和互斥量不够完美,影响性能。
2019-6-19 22:33
0
雪    币: 908
活跃值: (169)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
8
你这个  只能自己多线程安全  可以看看  调试器原理  是怎么下断点 怎么执行断点......
2019-6-20 08:46
0
雪    币: 3496
活跃值: (749)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
第一个就是帖子里的源代码吧,就是编译出来的hook.dll,那个注入其他进程的exe文件好像没有,我咋用你这个dll呢?
上传的附件:
2019-6-20 11:11
0
雪    币: 5293
活跃值: (1735)
能力值: ( LV2,RANK:150 )
在线值:
发帖
回帖
粉丝
10
楼上那哥们,原代码都发了,全部在里面,稍微翻一下就能找到注入的原代码,还有中文注释,作为一个学计算机的,你不要这么懒好么!
2019-6-20 12:37
0
雪    币: 13508
活跃值: (6924)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
11
楼主,传统的inline hook有线程隐患主要是unhook()没执行完吧,如果是修改了函数参数后立即无条件跳转到FunctionAddress+5呢,还会存在这种问题么?
2019-6-21 08:47
0
雪    币: 1555
活跃值: (4700)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
直接用CMPXCHG8B 这个汇编,一次替换8字节,怎么会有线程安全问题,用得着这么繁琐么
2019-6-21 18:46
0
雪    币: 5293
活跃值: (1735)
能力值: ( LV2,RANK:150 )
在线值:
发帖
回帖
粉丝
13
11楼,跳到 目标地址+5 之后会发生错误,因为内存没有开辟新的栈帧
2019-6-21 19:17
0
雪    币: 144
活跃值: (718)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
一直用的微软得inline hook
2019-6-22 11:19
0
雪    币: 13508
活跃值: (6924)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
15
九阳道人 11楼,跳到 目标地址+5 之后会发生错误,因为内存没有开辟新的栈帧
我的理解是按你上面那个hook message的例子,自己构造的函数本身就写有push ebp mov esp, ebp 如果只是对参数进行判断修改没有产生任何新的栈帧,立刻跳转回去问题也不大啊,只不过构造的函数不ret而已
2019-6-22 14:52
0
雪    币: 243
活跃值: (80)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
九阳道人 楼上那哥们,原代码都发了,全部在里面,稍微翻一下就能找到注入的原代码,还有中文注释,作为一个学计算机的,你不要这么懒好么!
顶学长!
2019-6-26 15:29
0
雪    币: 216
活跃值: (250)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17

三个方法,前两个具有本质性的错误.

1. interlocked 只能"锁"你自己. 对于inline hook修改指令导致的不稳定性没有任何帮助.
2. 同上. 信号量同样只能锁你自己.

如八楼所说, 这两个方法, 只是让你自己多个线程同时对同一个点进行HOOK的时候变安全. 对于修改指令导致的不稳定性没有任何帮助.

2019-6-29 13:21
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
帖子写的非常棒,前2个方法并不行,第三个是正解!
2020-2-19 04:58
0
雪    币: 7824
活跃值: (4693)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
前两个方法对于程序里的线程来说没有作用,其他线程不会因为你的原子和信号量在那一直等待,这两种方法只适合自己写的程序,第三种方法是对的,还有我认为普通的inlinehook对于多线程不安全是因为大部分时候hook头几个字节的时候破坏了接下来的几条指令,如果此时刚好有其他线程的eip在这几条被破坏的指令里,就会在线程切换的时候发生崩溃
2020-2-20 08:05
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
20
用了第三个是正解!谢谢,对我有帮助~~
2020-3-5 15:16
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
21
收藏一下先
2020-4-4 20:24
0
雪    币: 5766
活跃值: (2437)
能力值: ( LV4,RANK:42 )
在线值:
发帖
回帖
粉丝
22
min hook 实现的更加巧妙 05fK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6f1M7%4g2V1j5f1E0S2k6$3g2&6N6g2)9J5c8X3#2A6L8X3S2G2L8$3D9`.
2020-10-16 11:04
0
雪    币: 13508
活跃值: (6924)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
23
蜜蜂啊 min hook 实现的更加巧妙 88fK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6f1M7%4g2V1j5f1E0S2k6$3g2&6N6g2)9J5c8X3#2A6L8X3S2G2L8$3D9`.
Trampoline我也很喜欢用,凡是热补丁解决不了的特殊函数头它都能搞定,通用库这种玩意说白了就是个收集各类函数头的体力活
2020-10-18 11:58
0
游客
登录 | 注册 方可回帖
返回