首页
论坛
课程
招聘
[原创]一个hooklib源码 for ring0 ring3 x86 amd64
2012-4-19 20:31 19243

[原创]一个hooklib源码 for ring0 ring3 x86 amd64

2012-4-19 20:31
19243
自己折腾的一个hooklib,使用distorm解析指令
支持 ring0 & ring3 以及 x86 & amd64
先贴点代码体现以下这个lib的使用特点

先看,这部分是经典的inline hook的代码,这风格觉得算经典吧
//去msdn或xxx.h抄一个来定义原型
typedef VOID (WINAPI *T_OutputDebugStringW)( LPCWSTR lpOutputString );
//申明一个变量直接调用原函数
T_OutputDebugStringW old_OutputDebugStringW;

//申明自己的handler
int WINAPI my_OutputDebugStringW( LPCWSTR lpOutputString )
{
	//调用原函数功能
	return old_OutputDebugStringW(lpOutputString);
}

//安装hook
old_OutputDebugStringW = OutputDebugStringW;
install_hook(&old_OutputDebugStringW, my_OutputDebugStringW);


当一个工程写上很多个hook的时候,你就体会到好烦了吧。特别是那个typedef 很烦有木有???

/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////

这里用了一些宏来改善经典hook代码风格,变成这样

//去msdn或xxx.h抄一个原型
//改装申明为自己的handler
MyDetourProc(no_Return,
			 VOID, WINAPI, OutputDebugStringW, ( LPCWSTR lpOutputString )
			 )
{
	//调用原函数功能
	CallTrampoline(OutputDebugStringW)(lpOutputString);
}

//安装hook
ENABLE_HOOK(OutputDebugStringW);


简洁很多吧,不用申明那个old_xxx函数指针变量了,还不用老写那个恶心的typedef啦~~~~
个人用起来觉得这样好舒服……爽歪歪…………

以上只是这个hooklib的使用特点
至于一个hooklib是否强大稳定,首先从hook的基本步骤看:
1,指令流长度解析是必需的!
2,分析要hook的目标地址的指令内容,判断是否容得下自己的跳转代码
3,保存原指令到一块新的内存,还可能要修复一些指令的rva
4,目标地址写入跳转

是否强大稳定主要是2、3点的处理逻辑吧
下面是折腾这个hooklib的感触:
32位下:
    第2点还是比较省心的,一个jmp xxx 就5字节,判断目标函数头前几字节,没有一些逻辑短跳啊就可以满足了
    第3点原指令拷贝到新的内存地址后,可能要修复一些相对地址的问题,比如目标函数头指令就已经是jmp xxxx 或call xxxxx,

64位下:
    第2点就比较蛋疼了,因为64位我用的跳转方法是 ff 25 00 00 00 00 11 22 33 44 55 66 77 88 ,要14字节,
在判断目标函数前十几字节的情况经常容易碰到已有的一些32位级别的jmp啊call啊
    第3点,保存原指令到新内存的时候常常要修复偏移啦,而且新内存和源目标的地址差常常大于4G,不单单是修复偏移,而是得改指令了

        所以64位下真蛋疼,备份下来的原指令代码还得逐一解析,像一些jmp(e9 xxxxxx)啊call(e8 xxxxxx)啊该修复的修复,改装的要改装。像jmp的e9 改装为 ff 25 xxxxxx ,call的e8改装为ff 15 xxxxxx。
        只要一字节的疏忽啊,就蓝屏啊蓝屏,又重开虚拟机啊等啊……又挂windbg啊,调试起来还特费劲

        还有64位不能嵌入__asm,ring0下一些关中断啊修改cr寄存器啦,还好可以通过#include <intrin.h>、#pragma intrinsic解决

具体细节可以参考源码中的inlinehook.c和inlinehook.h,其他都是distorm的源码。
写inlinehook.h中的那些宏代码时也把我折腾的……还有那些定义的TRAMPOLINE被编译器优化的问题也折腾惨………………
自己看吧
#if defined (_MSC_VER) && (_MSC_VER <= 1200)
#define __debugbreak() { __asm int 3};
#endif

/*
定义一个TRAMPOLINE
----
一堆的__debugbreak();是用来扩充函数代码长度,这些代码空间会用来备份hook前的指令等等

其中volatile int i = 0; memset(0, 0, 0); 是故意用来干扰编译器某些优化行为

例如,1)不加volatile的时候,定义了xxxA和xxxW两个函数的trampoline,
      结果编译后,发现两个trampoline的函数地址居然是同一个(因为编译器觉得两个函数代码一样的就优化为一份了)
	  函数体内加个volatile发现可以避免这种优化

	  2)函数体内加个其他函数的调用,这里选用memset来干扰,避免调用时传参数的优化
	  那么像这样连续两次调用
	  trampoline_xxxA(arg1);
	  trampoline_xxxA(arg1); 的时候,
	  汇编应该是:
	  lea rcx, arg1  x64设置第一个参数
	  call trampoline_xxxA
	  lea rcx, arg1  再次调用是需要要再次设置的
	  call trampoline_xxxA
	  //如果不加memset来干扰,编译trampoline的代码时编译器认为该函数没任何“有意义”代码完全不会修改任何寄存器,会被优化为
	  lea rcx, arg1
	  call trampoline_xxxA
	  call trampoline_xxxA
	  上面第二次调用的时候编译器觉得不必要再次设置rcx了
-----------------
*/

#define DEFINE_TRAMPOLINE(name, ret)\
	name {\
	volatile int i = 0;\
	memset(0, 0, 0);\
	__debugbreak();__debugbreak();__debugbreak();__debugbreak();\
	__debugbreak();__debugbreak();__debugbreak();__debugbreak();\
	__debugbreak();__debugbreak();__debugbreak();__debugbreak();\
	__debugbreak();__debugbreak();__debugbreak();__debugbreak();\
	__debugbreak();__debugbreak();__debugbreak();__debugbreak();\
	__debugbreak();__debugbreak();__debugbreak();__debugbreak();\
	__debugbreak();__debugbreak();__debugbreak();__debugbreak();\
	__debugbreak();__debugbreak();__debugbreak();__debugbreak();\
	__debugbreak();__debugbreak();__debugbreak();__debugbreak();\
	..............................
	..............................
	##ret;\
}



#define DETOUR_METHOD(rettype, calltype, method) rettype calltype method
#define MyDetourHook(rettype, calltype, method, args) DETOUR_METHOD(rettype, calltype, hook_##method)##args
#define MyDetourTrampoline(rettype, calltype, method, args) DETOUR_METHOD(rettype, calltype, Trampoline_##method)##args

#define MyDetourProc(returnVal,rettype, calltype, method, args)\
	DEFINE_TRAMPOLINE(MyDetourTrampoline(rettype, calltype, method, args), returnVal);\
	MyDetourHook(rettype, calltype, method, args)

#define CallTrampoline(method) Trampoline_##method

#define ENABLE_HOOK(fn) hook_set((unsigned char*)fn, (unsigned char*)Trampoline_##fn, hook_##fn)
#define DISABLE_HOOK(fn) hook_remove((unsigned char*)Trampoline_##fn, hook_##fn)
#define ENABLE_HOOK2(fn, addr) hook_set((unsigned char*)addr, (unsigned char*)Trampoline_##fn, (unsigned char*)hook_##fn)


inlinehook.rar

第五届安全开发者峰会(SDC 2021)10月23日上海召开!限时2.5折门票(含自助午餐1份)

上传的附件:
收藏
点赞0
打赏
分享
最新回复 (18)
雪    币: 142
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
xiejienet 活跃值 2012-4-19 23:45
2
0
sf,这个感脚好流逼啊
雪    币: 310
活跃值: 活跃值 (18)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
muyen 活跃值 2012-4-19 23:57
3
0
这年头板凳没人占,我来占了,详细,感谢,收藏了
雪    币: 65
活跃值: 活跃值 (99)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
jordonA 活跃值 2012-4-20 08:50
4
0
多谢, 我也写了一个, 但是64位的跳转方法我这里一直有问题没有处理掉。
雪    币: 240
活跃值: 活跃值 (113)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
xicao 活跃值 2012-4-20 09:11
5
0
不错,有用,收藏了。64位将是主战场
雪    币: 117
活跃值: 活跃值 (11)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
cherryEx 活跃值 1 2012-6-9 23:18
6
0
两个月前的帖子居然被通知变精华了,
实在是非常的鼓舞啊~~
终于射&精了
雪    币: 728
活跃值: 活跃值 (131)
能力值: ( LV9,RANK:200 )
在线值:
发帖
回帖
粉丝
房有亮 活跃值 3 2012-6-10 10:20
7
0
雪    币: 73
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
kingcomer 活跃值 2012-6-12 18:53
8
0
for ring0 ring3 x86 amd64,这一句吓了我一跳。
雪    币: 564
活跃值: 活跃值 (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Hoiker 活跃值 2012-9-20 01:31
9
0
正求个通用的hook类库呢、
雪    币: 40
活跃值: 活跃值 (16)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
游戏神通 活跃值 2012-9-20 09:00
10
0
sounds great
雪    币: 218
活跃值: 活跃值 (14)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
bolide 活跃值 2012-11-28 09:07
11
0
不错,支持。。。。。
雪    币: 346
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
YwdxY 活跃值 2012-11-28 09:25
12
0
感谢分享~
看看
雪    币: 199
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
一个穷光蛋 活跃值 2013-3-16 11:25
13
0
这么好的东西难得找到。
雪    币: 213
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
xhlovewzx 活跃值 2013-3-21 19:56
15
0
正准备学习HOOK技术,谢谢。
雪    币: 397
活跃值: 活跃值 (709)
能力值: ( LV3,RANK:25 )
在线值:
发帖
回帖
粉丝
KooJiSung 活跃值 2013-3-21 20:41
16
0
不错.....
雪    币: 215
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
冰糖 活跃值 2013-3-22 15:49
17
0
看懂是啥的顺便留个言
雪    币: 6696
活跃值: 活跃值 (448)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
b23526 活跃值 2013-11-4 02:03
18
0
好东西正需要了,收藏了
雪    币: 479
活跃值: 活跃值 (659)
能力值: ( LV12,RANK:1010 )
在线值:
发帖
回帖
粉丝
sudami 活跃值 25 2013-11-22 18:33
19
0
Hook库在x64下备份指令做解析的时候有问题,导致解析x64下的JMP时候必然出错导致挂掉:

比如原始指令形如:
notepad!WinMainCRTStartup:
00000000`ff233570 4883ec28 sub rsp,28h
00000000`ff233574 e807feffff call notepad!_security_init_cookie (00000000`ff233380)
00000000`ff233579 4883c428 add rsp,28h
00000000`ff23357d eb11 jmp notepad!DisplayNonGenuineDlgWorker+0x14c (00000000`ff233590)


使用楼主的HOOK库后,中转跳板的指令是错误的:

sudami64!Trampoline_myfunc:
00000001`80001e80 4883ec28 sub rsp,28h
00000001`80001e84ff1543000000 call qword ptr [sudami64!Trampoline_myfunc+0x4d (00000001`80001ecd)]
// 这个跳转解析错误,一执行就会崩溃

00000001`80001e8a 4883c428 add rsp,28h
00000001`80001e8e eb11 jmp sudami64!Trampoline_myfunc+0x21 (00000001`80001ea1)
// 这个短跳楼主干脆没有解析,于是跳错了


看来对x64的支持不行啊。
雪    币: 11
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
stonly 活跃值 2014-3-16 17:35
20
0
x64下确实不行。
游客
登录 | 注册 方可回帖
返回