首页
社区
课程
招聘
[原创]ring3 inline 钩子库
发表于: 2010-11-27 17:49 18825

[原创]ring3 inline 钩子库

2010-11-27 17:49
18825

虽然已经有Detour等一批优秀的钩子实现库了,本着学习的原则还是弄了一下,参考了海风月影大神的挂钩库,汇编就学了个皮毛,大侠飘过,提供给需要的人参考参考……

这个库的优点:原始函数地址会作为第一个参数传递给Detour函数,Detour函数的后面参数和原始函数一致,所以很方便实现调用前过滤和调用后过滤,反正就是使用很简单,也不需要自己记录原始函数地址了。为了方便移植到内核里使用,专门增加了fastcall的处理……先上一个例子,原始代码见附件了,看了例子就知道使用有多么简单了……
说一下那个Stub为啥不需要pushad之类的现场保护指令
        __asm
        {
                pop eax
                push OLD_CODE
                push eax///如果使用其它的寄存器可能会失败,一般是在eax里面返回数据,所以这个可以随便用
                _emit 0xE9这个E9指令就是JMP指令,JMP过去的那个函数自己破坏的现场编译器自己会很好的进行保存,那个函数给上层函数暴露的相当于一个标准的API函数,也就是说现场不会因为中途调用了这个函数而被破坏。而原始函数暴露给这个中继函数的也是一个标准的函数,和API无异,所以Detour直接调用不会有什么问题。当然这个库只能用于Release版本,道理很明显,在Debug里面,给出函数名最后编译得到的不是函数的开头地址,而是一个中继地址
                                多个nop
OLD_CODE:
                        _emit 0xCC
                        nop


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 7
支持
分享
最新回复 (25)
雪    币: 33
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
先下载学习学习
2010-11-27 18:30
0
雪    币: 71
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
下载学习

下载学习
2010-11-27 19:06
0
雪    币: 458
活跃值: (421)
能力值: ( LV9,RANK:610 )
在线值:
发帖
回帖
粉丝
4
int GetFunctionLength(BYTE* Address)
{
int Length=0,Temp;
struct disasm_struct data={0};
BYTE* Pointer=Address;
while((Temp=disasm(Pointer,&data))>0)
{
Length+=Temp;
if(data.disasm_opcode==0xc2)
break;
if(data.disasm_opcode==0xc3)
break;
Pointer+=Temp;
}
return Length;

}

这个问题太大了。
ps   没有发现保存现场的 pushad popad  pushf  popf等。
2010-11-27 19:19
0
雪    币: 284
活跃值: (106)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
5
不是没有保存,是编译器编译你的Detour函数时自动帮你保存了(每个函数里用到的寄存器编译器在使用前都会保存),没必要自己用那些汇编指令去搞了(我自己用了很长时间也没见出啥子问题),还有我这个思想和以前的那些使用裸函数是有点区别的……嘿嘿
2010-11-27 19:38
0
雪    币: 386
活跃值: (46)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
6
记得好像hooklib0.5要改一个RWE的内存属性,不然debug版本会出错
2010-11-27 19:40
0
雪    币: 1259
活跃值: (38)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
stu
7
学习学习,只会一点ASM.
2010-11-28 14:21
0
雪    币: 57
活跃值: (55)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
8
好东西都是要顶的
2010-11-28 22:44
0
雪    币: 266
活跃值: (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
mark了
2010-11-29 10:57
0
雪    币: 206
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
MARK下……
2010-11-29 11:49
0
雪    币: 85
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
哪几句是增加了fastcall的处理,可不可以去掉,我是新菜
2010-11-29 14:12
0
雪    币: 284
活跃值: (106)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
12
去掉SUPORTFASTCALL宏定义编译就不产生fastcall相关的代码了
2010-11-29 16:14
0
雪    币: 560
活跃值: (939)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
学习了   一直不会c
2010-11-30 00:40
0
雪    币: 517
活跃值: (84)
能力值: ( LV12,RANK:250 )
在线值:
发帖
回帖
粉丝
14
寄存器保没保护暂且不说。标志寄存器可是有点关键。
试想,如HOOK以下代码:
cmp eax,edx
mov ecx,edx
mov edx,eax
jcc XXXXX
此时HOOK回调一旦影响标志寄存器,可能改变程序流程哦。呵呵。
2010-11-30 10:51
0
雪    币: 284
活跃值: (106)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
15
在汇编语言里面确实自己要进行现场保护等处理,但是在C编译器里面,注意我这个库里面的函数声明是标准调用的API结构(唯一多了一个参数而已),在编译我们这个中继函数的时候,C编译器会对本函数使用到的寄存器进行自动保存,而且我这个压根就不是回调函数,用我这个库来挂钩你给的这个代码是完全没有问题的,因为在中继函数里执行开头这几句cmpeax,edx执行时这些寄存器的值完全和没有中继函数时执行是一样的,现在的API里面不可能由调用者设置标志寄存器的值,API函数使用标志寄存器,只要保证进入原始API时寄存器值不变就不会出错,我这里使用了HeadInline,只要保证了寄存器的值不变,标志寄存器在原始API里计算也不变……
2010-11-30 12:14
0
雪    币: 1708
活跃值: (586)
能力值: ( LV15,RANK:670 )
在线值:
发帖
回帖
粉丝
16
期待 64 位。
2010-11-30 12:18
0
雪    币: 517
活跃值: (84)
能力值: ( LV12,RANK:250 )
在线值:
发帖
回帖
粉丝
17
以下是我的实验结果。也许是对楼主的LIB理解上有点问题。HOOK效果不尽人意。
实验代码:

void	thistest()
	{
	cout<<"in this test..."<<endl;
	}
void	mytest()
	{
	cout<<"in my test"<<endl;
	}


int _tmain(int argc, _TCHAR* argv[])
	{
	thistest();
	HOOK_INFO Info={0};
	InstallInlineByAddress((void*)thistest,mytest,&Info);
	thistest();
	}

情景分析:
thistest被HOOK之前:
00403BD0                           /$  56                       push    esi
00403BD1                           |.  57                       push    edi
00403BD2                           |.  68 DCF54100              push    封装测试.0041F5DC                                    ;  in this test...
00403BD7                           |.  68 F0414200              push    封装测试.004241F0

HOOK之后:
00403BD0                           /$- E9 2BC4FBFF              jmp     003C0000

003C0000                             58                         pop     eax
003C0001                             68 0D003C00                push    3C000D                                           ; ASCII "VWh荃A"
003C0006                             50                         push    eax
003C0007                           - E9 A41E0400                jmp     封装测试.00401EB0
003C000C                             90                         nop
003C000D                             56                         push    esi
003C000E                             57                         push    edi
003C000F                             68 DCF54100                push    41F5DC                                           ; ASCII "in this test..."
003C0014                           - E9 BE3B0400                jmp     封装测试.00403BD7

我想,原来HOOK_BUF的意图可能是要先执行钩子函数,再回来执行原来的函数体。
但是事实情况是没有。而且堆栈还因为多了一个push造成不平衡
我想,楼主把push RETS与push eax的位置放反了吧??
InstallInlineForFastCall的情况类似。
作为一个hooklib,当HOOK一段内存代码X时,应该让用户自由选择这段X先于钩子函数执行或后于钩子函数执行。

另外我再提一下我之前说过的问题,标志位。
如果这时我的钩子函数如下:
0100739D notepad.<ModuleEntryPoint>    8B45 08                  mov     eax, dword ptr [ebp+8]                           ;  notepad.<ModuleEntryPoint>
010073A0                               40                       inc     eax
010073A1                               40                       inc     eax
010073A2                               C3                       retn

且不管我提供的HOOK函数有多诡异,它改变了寄存器EAX,用户提供多么诡异的钩子HOOKLIB都得接受,不是吗,呵呵。
而HOOK的原始地址代码片断如下:
010073A4                               85D2                     test    edx, edx
010073A6                               8B4D 08                  mov     ecx, dword ptr [ebp+8]
010073A9                               74 10                    je      short notepad.010073BB

再假设这时线程环境,edx==0,eax==0
这时候,按照你生成的jmp,成功jmp进入钩子函数。
进入前Z标志位被置位,接下来的程序流程应该进入010073BB的。
但是由于钩子函数内部改变了Z标志。
所以,当钩子回来以后,程序流程变了。

编译器不会像你所说的,保护所有环境,至少,eax,ecx,edx都是自由的。编译器对它们并无明确保证。
2010-11-30 15:36
0
雪    币: 284
活跃值: (106)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
18
由于编译器(VS系列)调试版本都函数名处理有问题,得到的不是函数开头地址,而是一个中间地址,对debug版本使用这个挂钩库肯定会错误
发布版本中,我这个库主要要用于系统API函数挂钩的,你的这个例子中函数太短了,很多标准帧函数头会被优化得不见踪影了的,还有我这个使用的原理和导入表挂钩有点类似,真正的用法如下,2008下发布版本没问题……
void  thistest()
{
        printf("in this test:\n");
}

void  mytest(DWORD Fun)
{       
        __asm call Fun//过滤前拦截
        printf("in my test\n ");
        __asm call Fun//过滤后拦截
}在这个函数里如果不使用参数,会被优化成参数为空的情况,导致不含有ret指令,失去清理堆栈的机会,返回后崩溃,但是有必要进行过滤的函数不可能有这么囧的情况

void main()
{
        //HOOK_INFO Info={0};
        /*MessageBoxA(NULL,"This is a Test String!","TEST",MB_OK);
        InstallInlineByName("Kernel32.dll","LoadLibraryA",My_LoadLibraryA,&Info);
        InstallInlineByName("user32.dll","MessageBoxW",My_MessageBoxW,NULL);
        MessageBoxW(NULL,L"This is a Test String!",L"TEST",MB_OK);
        LoadLibraryA("Test.dll");
        UnInstallInline(&Info);*/

        thistest();
        InstallInlineByAddress(thistest,mytest,NULL);
        thistest();
        //LoadLibraryA("Test.dll");
        /*__asm int 3;
        Test1(6);
        InstallInlineForFastCall((unsigned char *)Test1,(unsigned char *)Function1,NULL,1);
        Test1(6);*/
        system("pause");
}
当然了一开始设计这个的目的就是用来挂钩常见的一些API函数的,对很多情况确实没考虑那么多,如果要考虑更多,就有许多情况要处理了
2010-11-30 19:26
0
雪    币: 89
活跃值: (53)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
恐怕我自己的Kx 很少 了,不过想学学这个, 下之!!
2010-12-1 09:02
0
雪    币: 219
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
好东西啊,下子
2010-12-1 21:40
0
雪    币: 202
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
准备下载回去围观
2010-12-3 05:20
0
雪    币: 40
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
谢谢分享!!!海风月影大神的挂钩库, 能否给个链接喃
2010-12-3 10:47
0
雪    币: 1149
活跃值: (888)
能力值: ( LV13,RANK:260 )
在线值:
发帖
回帖
粉丝
23
好帖。。。。标记。。。。
2010-12-6 21:43
0
雪    币: 142
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
VS2010 的debug模式下,RtlCopyMemory(Buffer,Stub,StubLen);//将Stub放到内存里面,为了支持多个函数 hook会出错,直接跳到了异常处理
在release模式下可以工作
2010-12-7 13:07
0
雪    币: 142
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
不好意思,没看后面的解释,原来Debug模式下本来就不能用
2010-12-8 12:27
0
游客
登录 | 注册 方可回帖
返回
//