看流星社区

 找回密码
 注册账号
查看: 4680|回复: 12

最新过TesSafe反WinDbg双机调试

[复制链接]

该用户从未签到

发表于 2013-4-27 16:40:29 | 显示全部楼层 |阅读模式
貌似论坛里面有关游戏的贴子都很火,所以发篇帖子涨点人气。

正文:
在论坛搜索了下发现去年的时候有人发过一篇过TesSafe反双机调试的帖子,但是现在已经过时了,并且帖子里面也没提到怎么处理被IAT HOOK的两个函数。在这里呢,我就给大家彻底的讲明白吧。

先开ARK工具看看游戏干了什么。


从图片可以看到游戏启动的时候对ntkrnlmp.exe中的kdcom.KdSendPacket和kdcom.KdReceivePacket两个函数进行了HOOK,通过函数名称就能猜测到函数的用途,一个发包,一个收包,都是com口用来通信的。

怎样恢复这两个函数呢,方面有很多种,列举两个。

1、自己映射内核文件ntkrnlmp.exe,然后通过PE结构遍历ntkrnlmp.exe的导入表,获取导入表中KdSendPacket、KdReceivePacket两个函数的地址在ntkrnlmp.exe中的偏移,然后利用这个偏移修改系统正常运行的ntkrnlmp.exe导入表中的两个函数地址。

但是这里还有一个问题就是,导入表中KdReceivePacket这个位置有代码校验,只要被修改就会重启,所以还得绕过代码校验。所以不推荐这种方法。

2、通过图片可以看到被IATHOOK的两个函数被挂钩后的新地址分别为0xB07920E6,0xB07920F6,相差16个字节。既然不想直接去恢复导入表,那么我们可以在这两个被挂钩后的地址中再跳回正常的KdSendPacket、KdReceivePacket。或许有人又有疑问,这两个挂钩地址处没有代码校验吗,答案是也有校验,只要修改前面几个字节,同样会重启。但是游戏只检测了前面的几个字节,所以只要在靠后一点的地方修改就不会被检测到。



修改后


另外一个也是同样处理函数


这样两个被IATHOOK的函数就相当于被恢复了。

但是上面我们是用工具进行恢复的,而TP启动的时候就已经调用了KdDisableDebugger禁止了WinDbg双机调试,这时候就算恢复了上面的两个函数,同样也无法双机调试。


之所以先讲怎样处理上面的两个函数是因为在TP驱动加载的时候首先进行了IATHOOK,然后调用KdDisableDebugger,如果你先处理KdDisableDebugger,而没有恢复上面两个函数的话,系统会直接重启。



因为要赶在TP调用KdDisableDebugger之前恢复IATHOOK,那么就需要设置一个系统回调函数,然后在加载TP驱动的时候进入我们的回调函数,这时TP已经加载完成,但是还没有开始运行,所以我们可以直接定位到 挂钩地址 处 写入跳转代码到正常的函数。


代码:
//恢复两个被iat hook的函数

VOID RenewIATHook(ULONG ImageBase)      
{
  // 模块:KDCOM.dll,函数名称:KdSendPacket,函数地址:ba5a91b2,Hint:0007
  //模块:KDCOM.dll,函数名称:KdReceivePacket,函数地址:ba5a8f4c,Hint:0004

  ULONG uKdSend = 0xba5a91b2;     //KdSendPacket的地址硬编码
  ULONG uKdReceive = 0xba5a8f4c;  //KeReceivePacket地址

  //因为这两个函数不是导出的,所以我为了省事,就直接这样写了

  ULONG uJmpKdSend = ImageBase + 0x20EE;
  //KdSendPacket挂钩函数地址 = ImageBase + 0x20EE; 硬编码 :o:

  ULONG uJmpKdReceive = ImageBase + 0x20FE;

  ULONG jmpAddr1 = uKdSend - uJmpKdSend - 5;
  ULONG jmpAddr2 = uKdReceive - uJmpKdReceive - 5;

  __asm
  {  
    cli
      pushad
      mov    eax,uJmpKdSend
      mov    byte ptr [eax],0xE9
      mov    ebx,jmpAddr1
      mov    dword ptr [eax+1],ebx

      mov    eax,uJmpKdReceive
      mov    byte ptr [eax],0xE9
      mov    ebx,jmpAddr2
      mov    dword ptr [eax+1],ebx

      popad
      sti
  }
}


ULONG strEnd(PWCHAR dest,PWCHAR sub)
{
  if (dest == NULL && sub == NULL)
    return 0;
  int ulDest = wcslen(dest);
  int ulSub = wcslen(sub);

  if (ulSub == 0 || ulSub > ulDest)
    return 0;
  return !wcscmp(&dest[ulDest - ulSub],sub);
}

VOID NotifyRoutine(IN PUNICODE_STRING  FullImageName,
  IN HANDLE  ProcessId, // where image is mapped
  IN PIMAGE_INFO  ImageInfo)
{
              //这个strEnd是一个判断字符串是不是以***结束
         //我记得wdk中有一个函数可以判断的,但是记不起来了,知道的回复下啊:eek:
  if (strEnd(FullImageName->Buffer,L"TesSafe.sys"))
  {
                                 //判断加载的驱动是不是TP
    KdPrint(("TesSafe.sys:ImageBase:%08x \t size: %06x\n",
ImageInfo->ImageBase,ImageInfo->ImageSize));
    RenewIATHook((ULONG)ImageInfo->ImageBase);  
  }
}


#pragma alloc_text("INIT")
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegPath){
  NTSTATUS status;
  UNREFERENCED_PARAMETER(RegPath);

  DriverObject->DriverUnload = DriverUnload;
  for (int i=0;i<IRP_MJ_MAXIMUM_FUNCTION;i++)
    DriverObject->MajorFunction = DriverDispatch;


  KdPrint(("Init\n"));
  PsSetLoadImageNotifyRoutine(NotifyRoutine);

  return STATUS_SUCCESS;
}

通过上面的代码就可以对IATHOOK的两个函数进行恢复

编译上面的代码,然后重启虚拟机,加载编译好的驱动运行。

下面就来处理TP调用KdDisableDebugger函数来禁止调试

WinDbg中输入bp KdDisableDebugger

启动DNF登陆器,然后WinDbg断下,



直接修改首行代码 ret 返回即可。

eb 804f8886 0xC3

单步走一下,返回上层函数。


代码:
b094dfd9 57              push    edi
b094dfda 8b782c          mov     edi,dword ptr [eax+2Ch]
b094dfdd 33f1            xor     esi,ecx
b094dfdf 33f9            xor     edi,ecx
b094dfe1 eb4b            jmp     TesSafe+0x702e (b094e02e)
b094dfe3 803d6da295b000  cmp     byte ptr [TesSafe+0x1326d (b095a26d)],0
b094dfea 7516            jne     TesSafe+0x7002 (b094e002)
b094dfec 688a4d6e43      push    436E4D8Ah
b094dff1 6876426e57      push    576E4276h
b094dff6 e873caffff      call    TesSafe+0x3a6e (b094aa6e)
b094dffb c6056da295b001  mov     byte ptr [TesSafe+0x1326d (b095a26d)],1
b094e002 85ff            test    edi,edi
b094e004 7404            je      TesSafe+0x700a (b094e00a)
b094e006 ffd7            call    edi           //call KdDisableDebugger
b094e008 eb24            jmp     TesSafe+0x702e (b094e02e)
b094e00a 803d6ea295b000  cmp     byte ptr [TesSafe+0x1326e (b095a26e)],0
b094e011 751b            jne     TesSafe+0x702e (b094e02e)
b094e013 6812010000      push    112h
b094e018 68e64d6e43      push    436E4DE6h
b094e01d 6873426e57      push    576E4273h
b094e022 e865caffff      call    TesSafe+0x3a8c (b094aa8c)
b094e027 c6056ea295b001  mov     byte ptr [TesSafe+0x1326e (b095a26e)],1
b094e02e 803e00          cmp     byte ptr [esi],0
b094e031 75b0            jne     TesSafe+0x6fe3 (b094dfe3)

//跳回去,继续call KdDisableDebugger  ,应该是个while循环,所以把这里nop掉
//ew b094e031 0x9090

b094e033 5f              pop     edi
b094e034 5e              pop     esi
b094e035 c3              ret
输入g,继续运行,WinDbg再次断下。

查看调用堆栈:TesSafe+0x7124


代码:
b094e112 a16ceb95b0      mov     eax,dword ptr [TesSafe+0x17b6c (b095eb6c)]
b094e117 8b402c          mov     eax,dword ptr [eax+2Ch]
b094e11a 330568eb95b0    xor     eax,dword ptr [TesSafe+0x17b68 (b095eb68)]
b094e120 7404            je      TesSafe+0x7126 (b094e126)
b094e122 ffd0            call    eax         //call KdDisableDebugger
b094e124 eb24            jmp     TesSafe+0x714a (b094e14a)
b094e126 803d72a295b000  cmp     byte ptr [TesSafe+0x13272 (b095a272)],0
b094e12d 751b            jne     TesSafe+0x714a (b094e14a)
b094e12f 6882010000      push    182h
b094e134 68e64d6e43      push    436E4DE6h
b094e139 6873426e57      push    576E4273h
b094e13e e849c9ffff      call    TesSafe+0x3a8c (b094aa8c)
b094e143 c60572a295b001  mov     byte ptr [TesSafe+0x13272 (b095a272)],1
b094e14a 8b0d64a295b0    mov     ecx,dword ptr [TesSafe+0x13264 (b095a264)]
//跳到这里,dd b095a264的值是8055d664
//kd> u 8055d664
//nt!KiDebugRoutine:            

b094e150 85c9            test    ecx,ecx
b094e152 740f            je      TesSafe+0x7163 (b094e163)
b094e154 a168a295b0      mov     eax,dword ptr [TesSafe+0x13268 (b095a268)]
//这里eax = nt!KdpStub:

b094e159 85c0            test    eax,eax
b094e15b 7406            je      TesSafe+0x7163 (b094e163)
b094e15d 3901            cmp     dword ptr [ecx],eax
b094e15f 7402            je      TesSafe+0x7163 (b094e163)
b094e161 8901            mov     dword ptr [ecx],eax
//根据上面的ecx,跟eax的值来看 应该是设置debug函数之类的。
//测试后,发现这条代码执行后,windbg失去通讯。所以直接nop掉


b094e163 c3              ret
b094e164 cc              int     3
b094e165 cc              int     3
输入g,继续运行,这时WinDbg输出
Shutdown occurred at (Wed Apr 24 11:59:41.193 2013 (UTC + 8:00))...unloading all symbol tables.
Waiting to reconnect...

看样子好像是重启了? 在切换到虚拟机发现并没有重启。

这个地方经过测试发现如果没有恢复上面的两个IATHOOK,系统就直接死掉了,如果恢复了,虽然windbg输出了上面的信息,但切换到虚拟机之后发现游戏已经到了 输入账号密码 界面。


到了这一步,仍然没完,将虚拟机断下来,再次下bp KdDisableDebugger断点。

发现系统再次断下,因为我们已经在KdDisableDebugger的第一行代码ret了。所以不用担心WinDbg会断开,输入g,再次运行,再次断下。

发现这里应该有一个定时器在不断的调用KdDisableDebugger。




代码:
b0965112 a16c5b97b0      mov     eax,dword ptr ds:[B0975B6Ch]
b0965117 8b402c          mov     eax,dword ptr [eax+2Ch]
b096511a 3305685b97b0    xor     eax,dword ptr ds:[0B0975B68h]
b0965120 7404            je      b0965126
b0965122 ffd0            call    eax           //call KdDisableDebugger
b0965124 eb24            jmp     b096514a
b0965126 803d721297b000  cmp     byte ptr ds:[0B0971272h],0
b096512d 751b            jne     b096514a
b096512f 6882010000      push    182h
b0965134 68e64d6e43      push    436E4DE6h
b0965139 6873426e57      push    576E4273h
b096513e e849c9ffff      call    b0961a8c
b0965143 c605721297b001  mov     byte ptr ds:[0B0971272h],1
b096514a 8b0d641297b0    mov     ecx,dword ptr ds:[0B0971264h]
b0965150 85c9            test    ecx,ecx
b0965152 740f            je      b0965163
b0965154 a1681297b0      mov     eax,dword ptr ds:[B0971268h]
b0965159 85c0            test    eax,eax
b096515b 7406            je      b0965163
b096515d 3901            cmp     dword ptr [ecx],eax
b096515f 7402            je      b0965163
b0965161 90              nop
b0965162 90              nop
b0965163 c3              ret


这里怎么处理,直接把call eax nop掉吗,很不幸,哪条代码也有校验,一改就重启了。不过哪个位置不能改,我们可以改别的位置。在这个函数头部b0965112 的位置直接 jmp b0965124 跳过call eax即可。

这样就可以完全的解决了TP的反双机调试。

以前的那篇文章里面提到在登陆游戏过程中还会调用KdDisableDebugger,但我测试之后发现直到进入游戏里面也没有再断下来过。。。

然后,可以去邪恶了。。。。

源码也贴上吧,都是用硬编码写的,自己修改修改就可以了。

游客,如果您要查看本帖隐藏内容请回复

该用户从未签到

发表于 2013-4-27 22:37:46 | 显示全部楼层
很喜欢转载别人的东西啊,兄弟

该用户从未签到

发表于 2013-4-27 22:39:48 | 显示全部楼层
欢迎分享,不过请注明转载,尊重作者

该用户从未签到

发表于 2013-5-3 10:19:12 | 显示全部楼层
好文章!!!!

该用户从未签到

发表于 2013-5-5 19:34:26 | 显示全部楼层
好东西支持

该用户从未签到

发表于 2013-5-13 17:50:41 | 显示全部楼层
好东西支持

该用户从未签到

发表于 2013-5-16 14:28:27 | 显示全部楼层
转载也把图片弄来啊。

该用户从未签到

发表于 2013-7-1 00:07:39 | 显示全部楼层
好动西啊,给力
  • TA的每日心情
    无聊
    2021-6-4 08:07
  • 发表于 2013-9-25 09:56:45 | 显示全部楼层
    好动西啊,给力

    该用户从未签到

    发表于 2013-10-3 00:17:15 | 显示全部楼层
    呵呵,不错,谢谢了
    点击按钮快速添加回复内容: 支持 高兴 激动 给力 加油 苦寻 生气 回帖 路过 感恩
    您需要登录后才可以回帖 登录 | 注册账号

    本版积分规则

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

    GMT+8, 2024-3-28 16:27

    Powered by Kanliuxing X3.4

    © 2010-2019 kanliuxing.com

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