看流星社区

 找回密码
 注册账号
查看: 2019|回复: 0

过TesSafe反WinDbg双机调试

[复制链接]

该用户从未签到

发表于 2017-6-1 17:25:23 | 显示全部楼层 |阅读模式
标 题: 【原创】过TesSafe反WinDbg双机调试
作 者: 易始
时 间: 2013-04-24,12:54:49
链 接: http://bbs.pediy.com/showthread.php?t=170342

貌似论坛里面有关游戏的贴子都很火,所以发篇帖子涨点人气。

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

先开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已经加载完成,但是还没有开始运行,所以我们可以直接定位到挂钩地址处写入跳转代码到正常的函数。


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

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

ULONGuKdSend=0xba5a91b2;//KdSendPacket的地址硬编码
ULONGuKdReceive=0xba5a8f4c;//KeReceivePacket地址

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

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

ULONGuJmpKdReceive=ImageBase+0x20FE;

ULONGjmpAddr1=uKdSend-uJmpKdSend-5;
ULONGjmpAddr2=uKdReceive-uJmpKdReceive-5;

__asm
{
cli
pushad
moveax,uJmpKdSend
movbyteptr[eax],0xE9
movebx,jmpAddr1
movdwordptr[eax+1],ebx

moveax,uJmpKdReceive
movbyteptr[eax],0xE9
movebx,jmpAddr2
movdwordptr[eax+1],ebx

popad
sti
}
}


ULONGstrEnd(PWCHARdest,PWCHARsub)
{
if(dest==NULL&&sub==NULL)
return0;
intulDest=wcslen(dest);
intulSub=wcslen(sub);

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

VOIDNotifyRoutine(INPUNICODE_STRINGFullImageName,
INHANDLEProcessId,//whereimageismapped
INPIMAGE_INFOImageInfo)
{
//这个strEnd是一个判断字符串是不是以***结束
//我记得wdk中有一个函数可以判断的,但是记不起来了,知道的回复下啊:eek:
if(strEnd(FullImageName->Buffer,L"TesSafe.sys"))
{
//判断加载的驱动是不是TP
KdPrint(("TesSafe.sys:ImageBase:%08x\tsize:%06x\n",
ImageInfo->ImageBase,ImageInfo->ImageSize));
RenewIATHook((ULONG)ImageInfo->ImageBase);
}
}


#pragmaalloc_text("INIT")
NTSTATUSDriverEntry(PDRIVER_OBJECTDriverObject,PUNICODE_STRINGRegPath){
NTSTATUSstatus;
UNREFERENCED_PARAMETER(RegPath);

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


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

returnSTATUS_SUCCESS;
}
[/code]


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

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

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

WinDbg中输入bpKdDisableDebugger

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



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

eb804f88860xC3

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


代码:
b094dfd957pushedi
b094dfda8b782cmovedi,dwordptr[eax+2Ch]
b094dfdd33f1xoresi,ecx
b094dfdf33f9xoredi,ecx
b094dfe1eb4bjmpTesSafe+0x702e(b094e02e)
b094dfe3803d6da295b000cmpbyteptr[TesSafe+0x1326d(b095a26d)],0
b094dfea7516jneTesSafe+0x7002(b094e002)
b094dfec688a4d6e43push436E4D8Ah
b094dff16876426e57push576E4276h
b094dff6e873caffffcallTesSafe+0x3a6e(b094aa6e)
b094dffbc6056da295b001movbyteptr[TesSafe+0x1326d(b095a26d)],1
b094e00285fftestedi,edi
b094e0047404jeTesSafe+0x700a(b094e00a)
b094e006ffd7calledi//callKdDisableDebugger
b094e008eb24jmpTesSafe+0x702e(b094e02e)
b094e00a803d6ea295b000cmpbyteptr[TesSafe+0x1326e(b095a26e)],0
b094e011751bjneTesSafe+0x702e(b094e02e)
b094e0136812010000push112h
b094e01868e64d6e43push436E4DE6h
b094e01d6873426e57push576E4273h
b094e022e865caffffcallTesSafe+0x3a8c(b094aa8c)
b094e027c6056ea295b001movbyteptr[TesSafe+0x1326e(b095a26e)],1
b094e02e803e00cmpbyteptr[esi],0
b094e03175b0jneTesSafe+0x6fe3(b094dfe3)

//跳回去,继续callKdDisableDebugger,应该是个while循环,所以把这里nop掉
//ewb094e0310x9090

b094e0335fpopedi
b094e0345epopesi
b094e035c3ret
[/code]

输入g,继续运行,WinDbg再次断下。

查看调用堆栈:TesSafe+0x7124


代码:
b094e112a16ceb95b0moveax,dwordptr[TesSafe+0x17b6c(b095eb6c)]
b094e1178b402cmoveax,dwordptr[eax+2Ch]
b094e11a330568eb95b0xoreax,dwordptr[TesSafe+0x17b68(b095eb68)]
b094e1207404jeTesSafe+0x7126(b094e126)
b094e122ffd0calleax//callKdDisableDebugger
b094e124eb24jmpTesSafe+0x714a(b094e14a)
b094e126803d72a295b000cmpbyteptr[TesSafe+0x13272(b095a272)],0
b094e12d751bjneTesSafe+0x714a(b094e14a)
b094e12f6882010000push182h
b094e13468e64d6e43push436E4DE6h
b094e1396873426e57push576E4273h
b094e13ee849c9ffffcallTesSafe+0x3a8c(b094aa8c)
b094e143c60572a295b001movbyteptr[TesSafe+0x13272(b095a272)],1
b094e14a8b0d64a295b0movecx,dwordptr[TesSafe+0x13264(b095a264)]
//跳到这里,ddb095a264的&#20540;是8055d664
//kd>u8055d664
//nt!KiDebugRoutine:

b094e15085c9testecx,ecx
b094e152740fjeTesSafe+0x7163(b094e163)
b094e154a168a295b0moveax,dwordptr[TesSafe+0x13268(b095a268)]
//这里eax=nt!KdpStub:

b094e15985c0testeax,eax
b094e15b7406jeTesSafe+0x7163(b094e163)
b094e15d3901cmpdwordptr[ecx],eax
b094e15f7402jeTesSafe+0x7163(b094e163)
b094e1618901movdwordptr[ecx],eax
//根据上面的ecx,跟eax的&#20540;来看应该是设置debug函数之类的。
//测试后,发现这条代码执行后,windbg失去通讯。所以直接nop掉


b094e163c3ret
b094e164ccint3
b094e165ccint3
[/code]

输入g,继续运行,这时WinDbg输出
Shutdownoccurredat(WedApr2411:59:41.1932013(UTC+8:00))...unloadingallsymboltables.
Waitingtoreconnect...

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

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


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

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

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




代码:
b0965112a16c5b97b0moveax,dwordptrds:[B0975B6Ch]
b09651178b402cmoveax,dwordptr[eax+2Ch]
b096511a3305685b97b0xoreax,dwordptrds:[0B0975B68h]
b09651207404jeb0965126
b0965122ffd0calleax//callKdDisableDebugger
b0965124eb24jmpb096514a
b0965126803d721297b000cmpbyteptrds:[0B0971272h],0
b096512d751bjneb096514a
b096512f6882010000push182h
b096513468e64d6e43push436E4DE6h
b09651396873426e57push576E4273h
b096513ee849c9ffffcallb0961a8c
b0965143c605721297b001movbyteptrds:[0B0971272h],1
b096514a8b0d641297b0movecx,dwordptrds:[0B0971264h]
b096515085c9testecx,ecx
b0965152740fjeb0965163
b0965154a1681297b0moveax,dwordptrds:[B0971268h]
b096515985c0testeax,eax
b096515b7406jeb0965163
b096515d3901cmpdwordptr[ecx],eax
b096515f7402jeb0965163
b096516190nop
b096516290nop
b0965163c3ret
[/code]



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

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

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



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


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

过双机调试.rar.*转载请注明来自看雪论坛@PEdiy.com
点击按钮快速添加回复内容: 支持 高兴 激动 给力 加油 苦寻 生气 回帖 路过 感恩
您需要登录后才可以回帖 登录 | 注册账号

本版积分规则

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

GMT+8, 2024-3-29 15:20

Powered by Kanliuxing X3.4

© 2010-2019 kanliuxing.com

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