首页
论坛
课程
招聘
[原创]inline hook 入门教程
2014-12-12 23:28 11897

[原创]inline hook 入门教程

2014-12-12 23:28
11897
一、 什么是 inline hook

inline hook 就是在运行的流程中插入跳转指令(call/jmp)来抢夺程序运行流程的一个方法。

好了那么问题就来了:

1. 插入怎么样的跳转指令
    一般为 相对JMP(0xE9) + 4字节地址,方便计算 
        公式:源地址 + 相对偏移 = 目的地址
        公式:目的地址 - 源地址 = 相对偏移

2. 被跳转指令覆盖的原始指令应该被保存起来,找适当的时机再执行

3. 需要对应的 jmpIn 和 jmpOut 跳转。

4. inline hook 的一般流程:
    源程序流 -> jmpIn -> 保存寄存器 -> 具体处理 -> 恢复寄存器 -> jmpOut -> 源程序流
        被跳转指令覆盖的代码可以在 jmpIn之后 或者 jmpOut之前

二、 被 inline hook 的位置

1. 指令或者指令之和大于等于5,因为我们要塞进去5个字节(0xE9 + Address)。只要你处理跳转指令覆盖的指令,理论上可以 inline hook 函数中的任意位置。

2. 判断被 inline hook 位置的指令可以使用反汇编引擎判断指令长度。

3. 很多系统调用的开头都是,能很方便的 inline hook
    mov    edi, edi
    push    ebp
    mov    ebp, esp


三、 可以写 hookProxy 函数,保存和恢复寄存器,统一调用界面。

例子:
// inline_hook.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"


__declspec(naked) int add(int a, int b)
{
  __asm 
  {
    mov    edi, edi
    push    ebp
    mov    ebp, esp

    sub    esp, 8
    mov    eax, [ebp + 0x08]
    mov    [ebp - 4], eax
    mov    eax, [ebp + 0x0C]
    mov    [ebp - 8], eax

    mov    eax, [ebp - 4]
    add    eax, [ebp - 8]

    mov    esp, ebp
    pop    ebp
    ret
  }
}


__declspec(naked) void hookProxy()
{
  // moved bytes
  __asm 
  {
    nop
    nop
    nop
    nop
    nop
  }

  // save regs
  //   __asm
  //   {
  //     pushad
  //     pushfd
  //   }


  // do something (or call specific function.)
  __asm
  {
    add [esp + 0x08], 1
    add [esp + 0x0C], 1
  }

  // store old regs
  //   __asm
  //   {
  //     popfd
  //     popad
  //   }

  // jump back code
  __asm 
  {
    nop
    nop
    nop
    nop
    nop
  }
}


void inlineHook()
{
  char* pMovedBytes = &((char*)hookProxy)[0];
  char* pJmpBackCode = &((char*)hookProxy)[0xF];

  // move old bytes
  memcpy((char*)hookProxy, (char*)add, 5);
  
  // fill jump in code
  char jmpInBuffer[5];
  jmpInBuffer[0] = 0xE9;

  int* pJmpInOffset = (int*)&jmpInBuffer[1];
  *pJmpInOffset = (char*)hookProxy - ((char*)add + 5);

  memcpy((char*)add, jmpInBuffer, 5);

  // fill jump out code
  char jmpOutBuffer[5];
  jmpOutBuffer[0] = 0xE9;

  int* pJmpOutOffset = (int*)&jmpOutBuffer[1];
  *pJmpOutOffset = ((char*)add + 5) - ((char*)pJmpBackCode + 5);
  memcpy(pJmpBackCode, jmpOutBuffer, 5);
}


int _tmain(int argc, _TCHAR* argv[])
{
  _asm int 3;

  DWORD oldProtect;
  if( VirtualProtect((LPVOID)add, 4096, PAGE_EXECUTE_READWRITE, &oldProtect) )
  {
    inlineHook();
  }

  printf("result = %d \n", add(1, 2));

  system("pause");
  return 0;
}


四、解析例子

1. 把Add函数的前5个字节搬到了 hookProxy的前5个字节。

2. 然后再 Add函数的前5个字节填充跳转到 hookProxy 的跳转指令

3. 在hookProxy的末尾5个字节填充跳转回 Add函数 + 5 的跳转指令

4. 原本 Add 函数的结构是3,被 hook 之后会变成 5

五、调试过程

在main函数中的,printf F11进去就能看到被 inline hook 改写的程序流程

0:000> t
inline_hook!add:
01001000 e91b000000      jmp     inline_hook!hookProxy (01001020)

0:000> p
inline_hook!hookProxy:
01001020 8bff            mov     edi,edi

0:000> p
inline_hook!hookProxy+0x2:
01001022 55              push    ebp

0:000> p
inline_hook!hookProxy+0x3:
01001023 8bec            mov     ebp,esp

0:000> p
inline_hook!hookProxy+0x5:
01001025 8044240801      add     byte ptr [esp+8],1         ss:0023:002ef794=01

0:000> p
inline_hook!hookProxy+0xa:
0100102a 8044240c01      add     byte ptr [esp+0Ch],1       ss:0023:002ef798=02

0:000> p
inline_hook!hookProxy+0xf:
0100102f e9d1ffffff      jmp     inline_hook!add+0x5 (01001005)

六、工程附件(VS2010 - Release)
inline_hook.rar

2021 KCTF 秋季赛 防守篇-征题倒计时(11月14日截止)!

上传的附件:
收藏
点赞1
打赏
分享
最新回复 (5)
雪    币: 9420
活跃值: 活跃值 (1102)
能力值: ( LV12,RANK:340 )
在线值:
发帖
回帖
粉丝
bxc 活跃值 6 2014-12-13 00:16
2
0
vc编译器可以使用/hotpatch和/functionpadmin选项生成带热补丁的函数头,即5个nop(x86)在函数入口前,一个mov edi,edi在入口处~
雪    币: 4109
活跃值: 活跃值 (684)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
option 活跃值 2014-12-13 08:10
3
0
受教育了,继续学习呀
雪    币: 2
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
唱哥 活跃值 2014-12-13 08:29
4
0
感谢, 请教一个问题. 如果hook的地址开头是 eb xx, 而这个跳转偏移5个字节, 但是我hook了7个字节, 这时应该是有两个备份吗, 一个原始的字节, 一个将eb xx 改成 e9 后的数据. 当跳回原本的入口点时用 e9这个, 还原时用原始的. 可对?

0x00100000:     //备份的布局
//4 bytes hook的字节大小n
//n bytes hook的目标的原始字节  这里如果eb 就改 e9
//5 bytes jmp (原本入口点+ n)
//hook的目标的原始字节

test:
   jmp   myHook
   ...

void __stdcall* myHook()
{

   __asm
   {
     leave;
     jmp  (0x00100000 + 4)
   }
}
雪    币: 21
活跃值: 活跃值 (42)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
AntiPsycho 活跃值 2014-12-13 09:08
5
0
我研究研究
雪    币: 21
活跃值: 活跃值 (42)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
AntiPsycho 活跃值 2014-12-13 09:16
6
0
不要用 jmpIn 去覆盖 源程序流的相对跳转指令。。那样很难处理

比如你 jmpIn 覆盖了两条指令,第一条是 4字节,第二条是3字节。你就移动 7 字节的数据。5字节的 jmpIn 会剩余两字节。。
游客
登录 | 注册 方可回帖
返回