首页
论坛
课程
招聘
[原创]ring3 inline 钩子库
2010-11-27 17:49 17270

[原创]ring3 inline 钩子库

2010-11-27 17:49
17270
虽然已经有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

#include"windows.h"
#include"hooklib.h"
#include"stdio.h"
#include"ADE32.h"
////测试时要禁用自动内联函数,不然这里的测试函数太段了,可能会被优化掉……
typedef int (WINAPI *FUN)(char*);

int WINAPI My_LoadLibraryA(FUN f,char* Path)
{
  printf("监控到要加载模块!%s\n",Path);
  return f(Path);
}
typedef int (WINAPI * Message)(ULONG,wchar_t*,wchar_t *,ULONG);

int WINAPI My_MessageBoxW(Message f,ULONG hwnd,wchar_t* Mes,wchar_t * Cap,ULONG Type)
{
  wchar_t mes[]=L"哈哈!函数被HOOK!";
  return f(hwnd,mes,L"HOOK",Type);
}

int __fastcall Test1(int a)
{
  printf("Test1原始函数输出:%d\n",a);
  return 1;
}


typedef  DWORD (__fastcall *Detour_Tun)(int a);

/*
小于两个参数的话也要这样做
*/
int __stdcall Function1(Detour_Tun Fun,int a,int Edx)
{
  printf("Detour Fastcall ^-^\n");
  return Fun(a);
}

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);
  Test1(6);
  InstallInlineForFastCall((unsigned char *)Test1,(unsigned char *)Function1,NULL,1);
  Test1(6);
  system("pause");
}

[2022冬季班]《安卓高级研修班(网课)》月薪三万班招生中~

上传的附件:
收藏
点赞0
打赏
分享
最新回复 (25)
雪    币: 33
活跃值: 活跃值 (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
PEBOSS 活跃值 2010-11-27 18:30
2
0
先下载学习学习
雪    币: 71
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
mumaren 活跃值 2010-11-27 19:06
3
0
下载学习

下载学习
雪    币: 295
活跃值: 活跃值 (150)
能力值: ( LV9,RANK:610 )
在线值:
发帖
回帖
粉丝
blueapplez 活跃值 14 2010-11-27 19:19
4
0
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等。
雪    币: 226
活跃值: 活跃值 (31)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
鹿剑 活跃值 3 2010-11-27 19:38
5
0
不是没有保存,是编译器编译你的Detour函数时自动帮你保存了(每个函数里用到的寄存器编译器在使用前都会保存),没必要自己用那些汇编指令去搞了(我自己用了很长时间也没见出啥子问题),还有我这个思想和以前的那些使用裸函数是有点区别的……嘿嘿
雪    币: 373
活跃值: 活跃值 (31)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
lovesuae 活跃值 1 2010-11-27 19:40
6
0
记得好像hooklib0.5要改一个RWE的内存属性,不然debug版本会出错
雪    币: 1259
活跃值: 活跃值 (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
stu 活跃值 2010-11-28 14:21
7
0
学习学习,只会一点ASM.
雪    币: 39
活跃值: 活跃值 (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
jiangming 活跃值 2010-11-28 22:44
8
0
好东西都是要顶的
雪    币: 266
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
雪妖 活跃值 2010-11-29 10:57
9
0
mark了
雪    币: 206
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
anlinknog 活跃值 2010-11-29 11:49
10
0
MARK下……
雪    币: 85
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
狂起来 活跃值 2010-11-29 14:12
11
0
哪几句是增加了fastcall的处理,可不可以去掉,我是新菜
雪    币: 226
活跃值: 活跃值 (31)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
鹿剑 活跃值 3 2010-11-29 16:14
12
0
去掉SUPORTFASTCALL宏定义编译就不产生fastcall相关的代码了
雪    币: 560
活跃值: 活跃值 (251)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
babalove 活跃值 2010-11-30 00:40
13
0
学习了   一直不会c
雪    币: 465
活跃值: 活跃值 (24)
能力值: ( LV12,RANK:250 )
在线值:
发帖
回帖
粉丝
triones 活跃值 6 2010-11-30 10:51
14
0
寄存器保没保护暂且不说。标志寄存器可是有点关键。
试想,如HOOK以下代码:
cmp eax,edx
mov ecx,edx
mov edx,eax
jcc XXXXX
此时HOOK回调一旦影响标志寄存器,可能改变程序流程哦。呵呵。
雪    币: 226
活跃值: 活跃值 (31)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
鹿剑 活跃值 3 2010-11-30 12:14
15
0
在汇编语言里面确实自己要进行现场保护等处理,但是在C编译器里面,注意我这个库里面的函数声明是标准调用的API结构(唯一多了一个参数而已),在编译我们这个中继函数的时候,C编译器会对本函数使用到的寄存器进行自动保存,而且我这个压根就不是回调函数,用我这个库来挂钩你给的这个代码是完全没有问题的,因为在中继函数里执行开头这几句cmpeax,edx执行时这些寄存器的值完全和没有中继函数时执行是一样的,现在的API里面不可能由调用者设置标志寄存器的值,API函数使用标志寄存器,只要保证进入原始API时寄存器值不变就不会出错,我这里使用了HeadInline,只要保证了寄存器的值不变,标志寄存器在原始API里计算也不变……
雪    币: 1481
活跃值: 活跃值 (155)
能力值: ( LV15,RANK:670 )
在线值:
发帖
回帖
粉丝
cntrump 活跃值 13 2010-11-30 12:18
16
0
期待 64 位。
雪    币: 465
活跃值: 活跃值 (24)
能力值: ( LV12,RANK:250 )
在线值:
发帖
回帖
粉丝
triones 活跃值 6 2010-11-30 15:36
17
0
以下是我的实验结果。也许是对楼主的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都是自由的。编译器对它们并无明确保证。
雪    币: 226
活跃值: 活跃值 (31)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
鹿剑 活跃值 3 2010-11-30 19:26
18
0
由于编译器(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函数的,对很多情况确实没考虑那么多,如果要考虑更多,就有许多情况要处理了
雪    币: 89
活跃值: 活跃值 (52)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
swlilike 活跃值 2010-12-1 09:02
19
0
恐怕我自己的Kx 很少 了,不过想学学这个, 下之!!
雪    币: 219
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
thankme 活跃值 2010-12-1 21:40
20
0
好东西啊,下子
雪    币: 202
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
byshadows 活跃值 2010-12-3 05:20
21
0
准备下载回去围观
雪    币: 40
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
skyxskyx 活跃值 2010-12-3 10:47
22
0
谢谢分享!!!海风月影大神的挂钩库, 能否给个链接喃
雪    币: 1114
活跃值: 活跃值 (146)
能力值: ( LV13,RANK:260 )
在线值:
发帖
回帖
粉丝
ycmint 活跃值 5 2010-12-6 21:43
23
0
好帖。。。。标记。。。。
雪    币: 142
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
xiejienet 活跃值 2010-12-7 13:07
24
0
VS2010 的debug模式下,RtlCopyMemory(Buffer,Stub,StubLen);//将Stub放到内存里面,为了支持多个函数 hook会出错,直接跳到了异常处理
在release模式下可以工作
雪    币: 142
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
xiejienet 活跃值 2010-12-8 12:27
25
0
不好意思,没看后面的解释,原来Debug模式下本来就不能用
游客
登录 | 注册 方可回帖
返回