看流星社区

 找回密码
 注册账号
查看: 1851|回复: 1

ring3下的Inline hook

[复制链接]

该用户从未签到

发表于 2013-4-1 09:21:07 | 显示全部楼层 |阅读模式
inline hooking
      在IAT hook的时候,如果遇到延迟绑定,即使再IAT hook GetProcAddress,也会使得本来很简单的事情,看起来是那么的复杂。那么有没有一种更好的方法来避免DLL延迟绑定的问题,答案是肯定有的,inline hook会是一种不错的选择。
Inline Hook通过硬编码的方式修改目标函数的内存空间(通常是开始的一段字节,且一般在第一个call之前,很多时候是第一个字节,这么做是主要是为了防止堆栈混乱,如果能够处理好堆栈问题,放在哪个位置都是无所谓的),写入跳转语句,跳转到对应的函数空间里去。这样,该目标函数只要被调用,程序就会跳转到我们的函数中来,我们在自己写的函数里需要完成2个问题:
    1)处理好堆栈平衡。在程序流程中,堆栈的平衡是非常的重要,如果inline hook写入的是call语句,那么需要针对函数是否在函数里面做好了堆栈平衡做相应的处理,所以既要保证返回到目标函数中(如果写入的是jmp语句,就不需要),也要保证目标函数能在顺利执行完毕后返回到我们的函数中来。
    2)执行被覆盖的指令。我们向目标函数地址空间些如跳转指令(jmp xxxxxxxx)时,势必要覆盖原先的一些汇编指令,所以我们一定要保证这些被覆盖的指令能够顺利执行。关于这部分指令的执行,一般是将其放在我们的函数中,让我们的函数“帮助”目标函数执行完被覆盖的指令,然后再跳回目标函数中被覆盖内后后的地址继续执行剩余内容。跳回去的时候,一定要算好是跳回到什么地址,是目标函数起始地址后的第几个字节。最好的处理方法就是跳回去之前,还原之前被覆盖的汇编指令。

如下分析所示:

代码:
Before hook:
INT Func()            INT Func()
{                {
  return 10;            mov eax, 0xA
                  ret
}                }

After hook:
INT Func()            INT Func()
{                {                       
  ReplaceFunc;            jmp 0xXXXXXX;         
                  ret                  
}                }                       
                     
            
目标函数所跳转到的函数,这里只是简单的返回个信息。

代码:
VOID ReplaceFunc(VOID)
{
  ::MessageBox(NULL, "hello world!", "", MB_OK);
  HookStatus(false, g_TargetFuncAddr);
  //return fnTestDll();
}
Hook函数,获取目标函数的地址,修改函数的起始5个字节。这里使用的是相对跳转,先计算跳转的距离XXXX,得到E9 XXXX(相对地址),写入到目标函数中去。

代码:
BOOL InlineHooking
(
IN LPCTSTR pImageName,
IN LPCTSTR pTargetFuncName,
IN int pReplaceFuncAddr
)
{
  HMODULE hLib = LoadLibrary(pImageName);
  if (NULL == hLib) return FALSE;

  g_TargetFuncAddr = (int)GetProcAddress(hLib, pTargetFuncName);
  if (NULL == g_TargetFuncAddr) return FALSE;

  CopyMemory(g_OldCode,(const void *)g_TargetFuncAddr, 5);
  g_NewCode[0] = 0xE9;
  int dwJmpAddr = pReplaceFuncAddr - g_TargetFuncAddr - 5;
  CopyMemory(&g_NewCode[1], &dwJmpAddr, 4);
  g_bHooked = TRUE;
  HookStatus(true, g_TargetFuncAddr);

  return FALSE;
}
修改内存

代码:
bool HookStatus( bool blnIsHook, IN int pTargetFuncAddr )
{
  DWORD oldACC,newACC;
  if (!g_bHooked) {return false;}
  //覆盖原先的一些汇编指令
  if (blnIsHook)
  {
    VirtualProtect((LPVOID)pTargetFuncAddr, 5, PAGE_WRITECOPY, &oldACC);
    CopyMemory((void *)pTargetFuncAddr, g_NewCode, 5);
    VirtualProtect((LPVOID)pTargetFuncAddr, 5, oldACC, &newACC);
  }
  //还原之前的汇编指令,这部分代码会引发多线程的问题,后面会介绍它对应的解决办法
  else
  {
    VirtualProtect((LPVOID)pTargetFuncAddr, 5, PAGE_WRITECOPY, &oldACC);
    CopyMemory((void *)pTargetFuncAddr, g_OldCode, 5);
    VirtualProtect((LPVOID)pTargetFuncAddr, 5, oldACC, &newACC);
  }
  return true;
}
      确实,这个办法要比IAT HOOK好很多。我们不需要针对每个模块逐个HOOK了,也不担心GetProcAddress引起的问题。但是,这个方法确不能很好地在多线程环境中工作,当有多个线程执行了我们 inline Hook 的目标函数的时候,一个线程在使用修改之前的 目标函数 ,一个线程在使用修改之后的目标函数,就会导致修改之前的目标函数函数不能够实现挂钩的效果。针对这种问题我们可以给这个函数加锁或者信号量,但这样会使效率降低,只能同时一个线程使用它。有一种方法可以很好的避免这个问题,就是只要不还原之前的汇编指令,每一个线程都是使用修改之后的目标函数,我们把被覆盖的那部分指令搬到ReplaceFunc函数中去,在调回目标函数前,先执行该部分汇编指令。

    通过上面例子的详述,我们可以知道inline hook的强大,它既可以用到JUMP XXXX,也可以使用为CALL XXXX,当然也可以添加其它你想要实现的功能的汇编指令。当然在HOOK之前你必须考虑到一个问题,就是即将被HOOK的目标函数是否有足够的空间来覆盖原先的汇编代码。

该用户从未签到

发表于 2013-9-23 10:56:52 | 显示全部楼层
不错收藏了,备用啊。
点击按钮快速添加回复内容: 支持 高兴 激动 给力 加油 苦寻 生气 回帖 路过 感恩
您需要登录后才可以回帖 登录 | 注册账号

本版积分规则

小黑屋|手机版|Archiver|看流星社区 |网站地图

GMT+8, 2024-5-14 05:34

Powered by Kanliuxing X3.4

© 2010-2019 kanliuxing.com

快速回复 返回顶部 返回列表