首页
论坛
课程
招聘
[原创]代码Hook之指令级实现
2014-1-16 22:09 7075

[原创]代码Hook之指令级实现

2014-1-16 22:09
7075
在工作中经常会使用到 Hook 技术,一般函数 Hook 会较为广泛的应用。
函数 Hook 分为很多种,修改 PE 的导入表是较为简单且常用的方法,但修改 PE 存在着实时性不高,时机不好掌握等特点,因为在 PE 文件被装载之后才能去动态地修改其导入表内容,进而达到函数跳转被截取的目的,但 PE 文件的加载可能很随意,任意一 PE 文件有导入表如果没有被 Hook 到,则就出现了漏洞,所以现在介绍一个古老但实用的技术,通过从根本上解决掉函数跳转来实现函数的 Hook。这就是今天的主角,指令级 Hook。
  函数Hook,本质就是在代码调用某个函数的时候,会先进入到自己预设的一个其它函数执行,好让我们得到一个控制的机会,那么指令级Hook,以 x86 平台为例,只要能完成跳转,即可跳转到我们自己的代码,有很多的指令可以完成如 call, jmp 系列的指令都可以做到,且 jmp 这种无条件跳转最能满足我们的需要。
  实现原理,那就很简单了,就是把目标函数的前五个字节替换成一个无条件跳转,然后保存这五个字节的内容,接着跳转到我们预设的函数里,然后我们就得到了控制权,此时你就可以无所欲为了,等您忙以后,再执行我们保存的指令,最后再跳转到目标函数的第六个字节的指令继续执行。
  下面以项目中会实际用到的代码为例,实地完成以上所说的技术。在一个大的项目中有时为了查找其它模块是否调用了 ExitProcess 导致程序退出,会对 ExitProcess 函数进行 Hook,或者查找内存申请和释放是否有泄漏,会对 malloc 和 free 函数进行 Hook,下面以实际项目中的代码为例来讲解一下。

BYTE g_btOldCode[5] = {0};
LPVOID g_pSrc;

void * _cdecl MyMallocBeforeImpl( void *pRetAddr, void *pRetAddr1, int nSize )
{
  if ( !g_bMallocWatchOn )
  {
    return 0;
  }
  if ( g_dwthreadID == 0 )
  {
    printf("Malloc size: %08d", nSize );
  }
  else if ( GetCurrentThreadId() == g_dwthreadID )
  {
    printf("Malloc size: %08d", nSize );
  }

  return 0;
}

void MyMallocAfterImpl( void *pBuff )
{
  if ( !g_bMallocWatchOn )
  {
    return;
  }

  if ( g_dwthreadID == 0 )
  {
    OspPrintf( TRUE, FALSE, TEXT("\t\taddr: 0x%08x\t\t%d\n") , pBuff, GetCurrentThreadId() );
  }
  else if ( GetCurrentThreadId() == g_dwthreadID )
  {
    OspPrintf( TRUE, FALSE, TEXT("\t\taddr: 0x%08x\t\t%d\n") , pBuff, GetCurrentThreadId() );
  }

}

__declspec(naked) void _cdecl MyMallocBefore()
{
  _asm {
    call  MyMallocBeforeImpl
    ret
  }
}

__declspec(naked) void _cdecl MyMallocAfter()
{
  _asm {
    push  eax
    push  ebx
    push  ecx
    mov    ebx, dword ptr[esp + 12]
    mov    ecx, dword ptr[esp + 16]
    mov    dword ptr[esp + 16], ebx
    mov    dword ptr[esp + 12], ecx
    pop    ecx
    pop    ebx
    call  MyMallocAfterImpl
    pop    eax
    ret
  }
}

/*==============================================================================*/
void MyFreeBeforeImpl( void *pRetAddr, void *pRetAddr1, void *pBuff )
{
  //printf( "Free: 0x%x\n", pBuff );
}

void MyFreeAfterImpl( void *pBuff )
{
}

__declspec(naked) void _cdecl MyFreeBefore()
{
  _asm {
    call MyFreeBeforeImpl
    ret
  }
}

__declspec(naked) void _cdecl MyFreeAfter()
{
  _asm {
    push  eax
    push  ebx
    push  ecx
    mov    ebx, dword ptr[esp + 12]
    mov    ecx, dword ptr[esp + 16]
    mov    dword ptr[esp + 16], ebx
    mov    dword ptr[esp + 12], ecx
    pop    ecx
    pop    ebx
    call  MyFreeAfterImpl
    pop    eax
    ret
  }
}

void PlaceHolder()
{
}


/*
 * 指令中的 _emit 为占位符,会被填充为目标函数中被替换掉的指令
 */
__declspec(naked) void Adapter1( void )
{
  _asm {
    call  PlaceHolder
    push  PlaceHolder
    push  eax
    push  ebx
    mov    eax, dword ptr[esp + 12]
    mov    ebx, dword ptr[esp + 16]
    mov    dword ptr[esp + 16], eax
    mov    dword ptr[esp + 12], ebx
    pop    ebx
    pop    eax
    _emit  0x00
    _emit  0x00
    _emit  0x00
    _emit  0x00
    _emit  0x00
    jmp    PlaceHolder
  }
}

__declspec(naked) void Adapter2( void )
{
  _asm {
    call  PlaceHolder
    push  PlaceHolder
    push  eax
    push  ebx
    mov    eax, dword ptr[esp + 12]
    mov    ebx, dword ptr[esp + 16]
    mov    dword ptr[esp + 16], eax
    mov    dword ptr[esp + 12], ebx
    pop    ebx
    pop    eax
    _emit  0x00
    _emit  0x00
    _emit  0x00
    _emit  0x00
    _emit  0x00
    jmp    PlaceHolder
  }
}

void HookDebugFun1( LPVOID pSrc, LPVOID pDstBefore, LPVOID pDstAfter, LPVOID pAdapter )
{
  g_pSrc = pSrc;

  /* 在源函数中构造跳转 */
  BYTE btCode[] = { 0xE9, 0x00, 0x00, 0x00, 0x00 };
  DWORD dwOffset = (DWORD)pAdapter - ((DWORD)pSrc + 5);
  memcpy( btCode + 1, &dwOffset, 4 );

  DWORD dwWriteenBytes;
  DWORD dwReadBytes;
//   LPVOID pAdapterRealAddr = (LPBYTE)pAdapter + 5 + *(LPDWORD)((LPBYTE)pAdapter + 1);            // Debug 下本地函数地址有跳转语句
  LPVOID pAdapterRealAddr = (LPBYTE)pAdapter;
  LPVOID pOldCodeAddr = (LPBYTE)pAdapterRealAddr + 5 + 5 + 20;
  LPVOID pHolder1Addr = (LPBYTE)pAdapterRealAddr + 1;
  LPVOID pHolder2Addr = (LPBYTE)pAdapterRealAddr + 5 + 1;
  LPVOID pHolder3Addr = (LPBYTE)pAdapterRealAddr + 5 + 20 + 5 + 5 + 1;

  ReadProcessMemory( GetCurrentProcess(), pSrc, g_btOldCode, 5, &dwReadBytes );
  /* 在源函数中写入跳转 */
  WriteProcessMemory( GetCurrentProcess(), (LPDWORD)((LPBYTE)pSrc), btCode, 5, &dwWriteenBytes );

  /* 将原函数中被替换的代码写入适配程序中 */
  WriteProcessMemory( GetCurrentProcess(), pOldCodeAddr, g_btOldCode, 5, &dwWriteenBytes );

  /* 将过滤函数写入适配函数中 PlaceHolder1*/
  DWORD dwFilterOffset = (DWORD)pDstBefore - ((DWORD)pHolder1Addr + 4 );
  WriteProcessMemory( GetCurrentProcess(), pHolder1Addr, &dwFilterOffset, 4, &dwWriteenBytes );

  /* 将过滤函数写入适配函数中 PlaceHolder2*/
  //DWORD dwFilterOffset = (DWORD)pDstBefore - ((DWORD)pHolder1Addr + 4 );
  WriteProcessMemory( GetCurrentProcess(), pHolder2Addr, &pDstAfter, 4, &dwWriteenBytes );

  /* 将适配函数中的跳转地址写入被 Hook 的函数的跳转指令之后的地址 PlaceHolder3 */
  dwFilterOffset = ((DWORD)pSrc + 5) - ((DWORD)pHolder3Addr + 4);
  WriteProcessMemory( GetCurrentProcess(), pHolder3Addr, &dwFilterOffset, 4, &dwWriteenBytes );
}
// #endif

// 申请内存打印开
API void mallocwatchon( int threadID )
{
  g_dwthreadID = threadID;
  static BOOL32 bStart = FALSE;
  if ( !bStart )
  {
    HookDebugFun1( malloc, MyMallocBefore, MyMallocAfter, Adapter1 );
    HookDebugFun1( free, MyFreeBefore, MyFreeAfter, Adapter2 );
    bStart = TRUE;
  }
  g_bMallocWatchOn = TRUE;
}

// 申请内存打印关
API void mallocwatchoff()
{
  g_bMallocWatchOn = FALSE;
}


代码中有详细的注释,jmp 计算偏移是一个炒剩饭的技术了,如果有疑问可 google 或讨论一下。 在附件代码中,由于是从项目中截取的代码版本,但实现较为完整,只需要加上适当的头文件和 main 函数,经过修改即可运行实验了。

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

上传的附件:
收藏
点赞0
打赏
分享
最新回复 (21)
雪    币: 184
活跃值: 活跃值 (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
whnet 活跃值 2014-1-16 22:26
2
0
嗯。经典的 5 字节了。 不过很容易检测出来。
雪    币: 31
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
lovelyday 活跃值 2014-1-16 22:31
3
0
不就是inline hook么,标题起得太深奥
雪    币: 459
活跃值: 活跃值 (34)
能力值: ( LV13,RANK:400 )
在线值:
发帖
回帖
粉丝
tishion 活跃值 9 2014-1-16 22:34
4
0
我以为你要讲OPCode了……
雪    币: 26
活跃值: 活跃值 (11)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
Henzox 活跃值 2014-1-16 22:45
5
0
起名字一直是我的弱项,朝花儿拾,今天又看到微软的 hotfix 实现让我想起了这个古老的技术。
雪    币: 26
活跃值: 活跃值 (11)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
Henzox 活跃值 2014-1-16 22:50
6
0
那五个字节确实经典,不过微软经典的不是这5个字节,应该是 mov edi, edi 哈哈。
雪    币: 95
活跃值: 活跃值 (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
JingSao 活跃值 2014-1-16 22:53
7
0
是呀,名字起得好玄呀,,,,,,
雪    币: 255
活跃值: 活跃值 (18)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
AioliaSky 活跃值 1 2014-1-16 22:53
8
0
看标题还以为是指令拦截,没想到是inline  hook
雪    币: 26
活跃值: 活跃值 (11)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
Henzox 活跃值 2014-1-16 22:53
9
0
不好意思,让你失望了,古老但实用的技术拿出来 mark 一下而已,
雪    币: 26
活跃值: 活跃值 (11)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
Henzox 活跃值 2014-1-16 22:55
10
0
可以改名字吗,我已经后悔了,呵呵
雪    币: 26
活跃值: 活跃值 (11)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
Henzox 活跃值 2014-1-16 23:02
11
0
inline hook 这个词没怎么听过,对一些专业名词不是很敏感。
雪    币: 728
活跃值: 活跃值 (131)
能力值: ( LV9,RANK:200 )
在线值:
发帖
回帖
粉丝
房有亮 活跃值 3 2014-1-16 23:03
12
0
多谢楼主,好神奇
雪    币: 5
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
rqqeq 活跃值 2014-1-16 23:22
13
0
还以为是vt呢……
雪    币: 184
活跃值: 活跃值 (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
whnet 活跃值 2014-1-17 00:26
14
0
windows 内核 安全防护   这里面就用过这个词。 你out了!
雪    币: 470
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
cnxxm 活跃值 2014-1-17 01:08
15
0
同4楼,哈哈
雪    币: 882
活跃值: 活跃值 (345)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zquchs 活跃值 2014-1-17 02:02
16
0
虽然看不明白,好像很厉害那样
雪    币: 26
活跃值: 活跃值 (11)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
Henzox 活跃值 2014-1-17 08:41
17
0
我一般关注的重点都在内核的实现原理及算法和性能,对安全防护方面确实不太关注!
雪    币: 79
活跃值: 活跃值 (21)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
cqzj70 活跃值 2014-1-17 09:16
18
0
MARK
雪    币: 80
活跃值: 活跃值 (18)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
grusirna 活跃值 1 2014-1-17 13:40
19
0
inline hook ,鉴定完毕,结贴............
雪    币: 1178
活跃值: 活跃值 (470)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
小调调 活跃值 2014-1-17 13:59
20
0
我以为是 HOOK 一字节的指令呢~
雪    币: 218
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
誓言剑 活跃值 2014-1-19 23:26
21
0
跪求楼主修改标题和正文为inline hook吧
雪    币: 218
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
誓言剑 活跃值 2014-1-21 01:11
22
0
靠,又被标题骗进来了
能不能改改啊
游客
登录 | 注册 方可回帖
返回