首页
社区
课程
招聘
[原创]Inline hook中继函数通用汇编宏
发表于: 2013-10-4 13:53 13925

[原创]Inline hook中继函数通用汇编宏

2013-10-4 13:53
13925

标 题:【原创】Inline hook中继函数通用汇编宏
作 者: Kern

大神们都在讲解如何hook,我就不讲hook了,分享一个hook中经常见到,却又被大家忽视的东西:中继函数。

大家都知道在hook的时候需要一个中继函数,通常是一个naked的嵌入汇编函数,目的是为了作为一个桥梁,执行真正的hook函数,
而大家都有感触,内容都差不多,但是又不得不为每一个hook都重复的写,有时候不小心参数压错了,还会造成函数无法返回或者堆栈异常。

工欲善其事,必先利其器。

我们就先来打造一个宏,让他帮助我们自动生成中继函数,而我们就只用关心hook函数的实现即可。

先看一下宏的参数:
#define DECLARE_HOOKPROXY(proxyFunc, hookFunc, argc, shellcode)

proxyFunc:就是中继函数,当然,这里就是填入你要的中继函数的名字
hookFunc:就是你真正实现功能的hook函数咯,这里要求必须是stdcall(当然你可以改)
argc:被hook的函数参数个数,中继函数压参数以及返回的时候平衡堆栈需要这个信息
shellcode:事先构造好的一段机器码,保存了被hook掉的机器码,以及返回到原函数的一个长跳转(注意内存要有执行属性)

看具体实现前需要先注意该中继函数的适用范围:
1、Hook函数头:如果需要hook任意合适位置,需要稍微改一下宏的内容
2、Hook跳转表:我们知道有的函数call进去之后是一个jmp xxx,其实也是hook函数头
3、Hook Call xxx:把跳转地址xxx改成中继函数的地址

先用嵌入汇编的函数来看一下实现的功能:

void __declspec(naked) proxyFunc()
{
    mov     edi, edi        /* 让我们的函数看起来跟WIN API更像一点 */
    push    ebp
    mov     esp, ebp
    push    ebx
    mov     eax, argc        /* 取参数个数 */
    add     eax, 1           /* 加1是为了跳过返回地址,如果hook的不是上边提到的位置 */
                             /* 那就需要自己去计算应该跳过多少堆栈数据,修改这里 */
    jmp     l2
l1:
    lea     ebx, [ebp + eax * 4]  /* 这里取的是参数的地址,目的是能够修改参数 */
    push    ebx                   /* 例如让MessageBox改成显示"Hook..." */
    sub     eax, 1
l2:
    cmp     eax, 1
    jne     l1
    call    hookFunc              /* 参数压好了,该干嘛干嘛去吧 */
    pop     ebx
    mov     esp, ebp
    pop     ebp                   /* 恢复堆栈,注意不需要平衡esp */
    cmp     eax, 0x4E52454B       /* MAGIC: 如果hook函数返回的值不是这个,*/
                                  /* 就将作为该API调用的返回值,我叫这个为GOON */
    je      l3                    /* 如果是MAGIC,就跳到shellcode继续执行原函数 */
    ret     argc * 4              /* 标准WIN API是stdcall的,需要我们平衡堆栈 */
l3:
    jmp     offset shellcode      /* 注意这里是取offset,千万不要在宏里边用高级语言的符号 */
}

差不多OK了,来看一下宏汇编,大体上差不多,只是一些跳转需要硬编码:

#define DECLARE_HOOKPROXY(proxyFunc, hookFunc, argc, shellcode) \
void __declspec(naked) proxyFunc() \
{ \
    _asm mov    edi, edi \
    _asm push   ebp \
    _asm mov    ebp, esp \
    _asm push   ebx \
    _asm mov    eax, argc \
    _asm add    eax, 1 \
    _asm _emit  0xEB \
    _asm _emit  0x08 \
    _asm lea    ebx, [ebp + eax * 4] \
    _asm push   ebx \
    _asm sub    eax, 1 \
    _asm cmp    eax, 1 \
    _asm _emit  0x75 \
    _asm _emit  0xF3 \
    _asm call   hookFunc \
    _asm pop    ebx \
    _asm mov    esp, ebp \
    _asm pop    ebp \
    _asm cmp    eax, 0x4E52454B \
    _asm _emit  0x74 \
    _asm _emit  0x03 \
    _asm ret    argc * 4 \
    _asm jmp    offset shellcode \
}

举个例子,中继函数proxyMessageBox就直接声明一下就OK了,是不是佷方便?

#define HOOKAPI     unsigned int __stdcall
#define GOON        0x4E52454B

HOOKAPI hookMessageBox(
  HWND *hWnd,          // handle to owner window
  LPCTSTR *lpText,     // text in message box
  LPCTSTR *lpCaption,  // message box title
  UINT *uType          // message box style
)
{
    *lpText = "Hook";
    return GOON;
}
RET_SHELLCODE scMessageBox;
DECLARE_HOOKPROXY(proxyMessageBox, hookMessageBox, 5, scMessageBox);

void main()
{
    /* 这个函数作用当然就是保存函数头,构造shellcode,然后hook掉 */
    HookApiHeader(MessageBox, proxyMessageBox, 5, &scMessageBox);
    MessageBox(NULL, "TEST", "TEST", MB_OK);
}


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

收藏
免费 5
支持
分享
最新回复 (11)
雪    币: 129
活跃值: (2763)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
居然没人观摩 占楼出租
2013-10-4 20:03
0
雪    币: 200
活跃值: (38)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
其实mov edi, edi是“官方”的hook点位

先把这个两字节的指令改成short jmp,jmp到函数之前的nop字节处,在那里放入near jmp,转移到hook函数。
2013-10-5 00:39
0
雪    币: 75
活跃值: (723)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
4
参数压栈有问题吧
2013-10-5 09:40
0
雪    币: 1392
活跃值: (5177)
能力值: ( LV13,RANK:240 )
在线值:
发帖
回帖
粉丝
5
有这么一说???
JMP 5字节 刚好是 mov edi,edi push ebp emov ebp,esp
2013-10-5 10:32
0
雪    币: 200
活跃值: (38)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
6
不是这个意思,先修改mov edi, edi为2字节的jmp,jmp到函数之前的nop处,那里放入5字节的jmp。

君不见很多api函数前面都有5个0x90,显然是为了5字节的jmp。

mov edi, edi执行时任何作用都没有,只是为了方便hook
2013-10-5 11:23
0
雪    币: 81
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
7
如果是hook我提到的几个位置,参数压栈是没有问题的

例如在函数头:
call过来的时候返回地址压栈,而在我代码本身有:push    ebp,
所以此时堆栈的信息:
ebp + 0:保存的ebp
ebp + 4:返回地址

假如函数带一个参数:
add     eax, 1                 =>  eax为2
lea     ebx, [ebp + eax * 4]   =>  ebp + 8即是第一个参数
注意前边有说明是stdcall,两个以上参数压栈的顺序从右到左,所以eax递减

其他任意合适的位置的话,需要修改一下,理论上任何位置都可以hook,只要注意hook点指令的完整性即可
2013-10-6 09:43
0
雪    币: 81
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
8
是有Hot packing这么一说,不过对于我们来说,函数头的hook是最脆弱的,还是隐蔽点好
2013-10-6 09:50
0
雪    币: 75
活跃值: (723)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
9
你这里 为什么要把参数的地址作为参数压进去,而不是参数本身,就是
lea ebx ,[ebp+eax*4]
push ebx
是不是应该改为 push [ebp+eax*4]
2013-10-6 12:33
0
雪    币: 81
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
10
[QUOTE=skypismire;1228094]你这里 为什么要把参数的地址作为参数压进去,而不是参数本身,就是
lea ebx ,[ebp+eax*4]
push ebx
是不是应该改为 push [ebp+eax*4][/QUOTE]

故意为之,其实在注释里有解释,Messagebox的例子也是这么写的:

    lea     ebx, [ebp + eax * 4]  /* 这里取的是参数的地址,目的是能够修改参数 */
    push    ebx                   /* 例如让MessageBox改成显示"Hook..." */

目的佷简单,我们可以把上层传过来的参数改掉,给函数传递一个错误的信息
2013-10-6 20:46
0
雪    币: 75
活跃值: (723)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
11
ok。z z z z z z
2013-10-7 11:57
0
雪    币: 157
活跃值: (471)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
12
来几个函数.看得懂的.拿去用吧

acallb proc a_addr:dword,b_addr:dword
local OldProtect
        pushad
        invoke IsBadWritePtr,a_addr,8
        .if eax!=0
                invoke VirtualProtect,a_addr,8,PAGE_EXECUTE_READWRITE,addr OldProtect
        .endif
        mov eax,a_addr
        mov edx,b_addr
        sub edx,eax
        sub edx,5
        mov byte ptr[eax],0e8h
        xchg [eax+1],edx
        popad
        ret
acallb endp
       
ajmpb proc a_addr:dword,b_addr:dword
local OldProtect
        pushad
        invoke IsBadWritePtr,a_addr,8
        .if eax!=0
                invoke VirtualProtect,a_addr,8,PAGE_EXECUTE_READWRITE,addr OldProtect
        .endif
        mov eax,a_addr
        mov edx,b_addr
        sub edx,eax
        sub edx,5
        mov byte ptr[eax],0e9h
        xchg [eax+1],edx
        popad
        ret
ajmpb endp

xcopy proc src:dword,des:dword,slen:dword
        pushad
        mov edi,src
        mov esi,des
        mov ecx,slen
xcopyloop:
        mov al,byte ptr[edi]
        mov byte ptr[esi],al
        inc edi
        inc esi
        loop xcopyloop
        popad
        ret
xcopy endp

hook_addr_jump proc lpaddr,myapi,jump
LOCAL vaddr
local OldProtect

        pushad
        invoke VirtualAlloc,0,1000h,MEM_RESERVE+MEM_COMMIT,PAGE_READWRITE       
        mov vaddr,eax
        invoke acallb,vaddr,myapi
        mov ecx,lpaddr
        mov ebx,vaddr
        add ebx,5
        invoke xcopy,ecx,ebx,jump
       
        mov ebx,vaddr
        add ebx,5
        add ebx,jump
        mov ecx,lpaddr
        add ecx,jump
        invoke ajmpb,ebx,ecx
       
        invoke ajmpb,lpaddr,vaddr
        invoke VirtualProtect,vaddr,1000h,PAGE_EXECUTE_READWRITE,addr OldProtect
        popad
        ret
hook_addr_jump endp
2013-10-28 09:43
0
游客
登录 | 注册 方可回帖
返回
//