飘云阁

 找回密码
 加入我们

QQ登录

只需一步,快速开始

查看: 5535|回复: 4

[原创] 我与软件比道行——从“科洛斯之神-游戏伴侣”反跟踪调试功能看软件破解

[复制链接]

该用户从未签到

发表于 2006-11-26 00:43:24 | 显示全部楼层 |阅读模式
前日,一位网友大概是为玩游戏急红了眼,说是“科洛斯之神--伴侣”中存在暗桩,并反编译、反调试,让我给看看。正好我也想整理一篇关于软件设置反调试代码的文章,所以就答应试试看。不料这一试,还真让我看出点新鲜东西来:主程序与.dll互相监视、大量的花指令、连续不断的跳转、定时器设置陷阱、跟踪调试软件标识符加密、反跟踪调试功能设置等等。真是用心良苦,可叹可叹!下面就来详细分析一下此软件,权当以管孔之见,博诸君一笑尔。


小知识:《科洛斯》是个MMORPG游戏,进入科洛斯,玩家会成为拯救大陆的英雄。在这个世界里,超过100名的NPC和玩家可以相互作用,跟200多种的怪物展开战斗。可供选择的职业有,战士、魔法师,还有其他网络游戏里难得见到的女剑客和野蛮人。 在游戏故事方面,按冒险旅行的形态,提供100多种故事。此时,一边进行没有规定的探险,一边了解故事主线,而且玩家的行动改变世界,并完成故事的多样性。


在哪里能找到它?
方法:用OD载入,粗跟与细根相结合。粗跟者,F8。F7,细跟也。遇到CALL,记好偏移地址。F8 过不去,那就 F7 进。
一路F8,来到这里:
004207E2 PUSH EAX
004207E3 CALL 1.004208C0 ; 过不去,F7
004207E8 MOV DWORD PTR SS:[EBP-68],EAX
004207EB PUSH EAX
到这里,得进:
004208C0 PUSH DWORD PTR SS:[ESP+10]
004208C4 PUSH DWORD PTR SS:[ESP+10]
004208C8 PUSH DWORD PTR SS:[ESP+10]
004208CC PUSH DWORD PTR SS:[ESP+10]
004208D0 CALL <JMP.&MFC42.#1576> ; 过不去,F7
004208D5 RETN 10
进来后,来到这里:
73D3B4AC >PUSH EBX
73D3B4AD PUSH ESI
73D3B4AE PUSH EDI
73D3B4AF OR EBX,FFFFFFFF
73D3B4B2 CALL MFC42.#1175
73D3B4B7 MOV ESI,EAX
73D3B4B9 CALL MFC42.#1168
73D3B4BE PUSH DWORD PTR SS:[ESP+1C]
73D3B4C2 MOV EDI,DWORD PTR DS:[EAX+4]
73D3B4C5 PUSH DWORD PTR SS:[ESP+1C]
73D3B4C9 PUSH DWORD PTR SS:[ESP+1C]
73D3B4CD PUSH DWORD PTR SS:[ESP+1C]
73D3B4D1 CALL MFC42.#1575
73D3B4D6 TEST EAX,EAX
73D3B4D8 JE SHORT MFC42.73D3B500
73D3B4DA TEST EDI,EDI
73D3B4DC JE SHORT MFC42.73D3B4EC
73D3B4DE MOV EAX,DWORD PTR DS:[EDI]
73D3B4E0 MOV ECX,EDI
73D3B4E2 CALL DWORD PTR DS:[EAX+8C]
73D3B4E8 TEST EAX,EAX
73D3B4EA JE SHORT MFC42.73D3B500
73D3B4EC MOV EAX,DWORD PTR DS:[ESI]
73D3B4EE MOV ECX,ESI
73D3B4F0 CALL DWORD PTR DS:[EAX+58] ; 1.00407790
注意这里的蹊跷,回调主程序了。不能过,跟着返回来。回来后,这里的代码形如(注意偏移地址。为便于说明,多复制了几段):
00407790 PUSH EBP ; 艰难的开始点
00407791 MOV EBP,ESP
00407793 PUSH -1
00407795 PUSH 1.00420D69
0040779A MOV EAX,DWORD PTR FS:[0]
004077A0 PUSH EAX
004077A1 MOV DWORD PTR FS:[0],ESP
004077A8 SUB ESP,3DC
004077AE PUSH EBX
004077AF PUSH ESI
004077B0 PUSH EDI
004077B1 MOV DWORD PTR SS:[EBP-88],ECX
004077B7 JMP SHORT 1.004077C9 ;开跳
004077B9 JS SHORT 1.0040782B
004077BB JB SHORT 1.0040782C
004077BD ADD AL,0
004077BF ADD BYTE PTR DS:[EAX],AL
004077C1 ADD BYTE PTR DS:[EAX],AL
004077C3 ADD BYTE PTR DS:[EAX],AL
004077C5 JS SHORT 1.00407837
004077C7 JB SHORT 1.00407838
004077C9 JMP SHORT 1.004077DB ;又跳
004077CB JS SHORT 1.0040783D
004077CD JB SHORT 1.0040783E
004077CF ADD EAX,0
004077D4 ADD BYTE PTR DS:[EAX],AL
004077D6 ADD BYTE PTR DS:[EAX+70],BH
004077D9 JB SHORT 1.0040784A
004077DB JMP SHORT 1.004077ED ;还跳
像这样的绝对跳转每轮都有几十个,在每轮跳转中间又夹杂一些系统函数调用,所以跟踪时要有心理准备,不能操之过急。而每两个绝对跳转之间夹杂的是花指令。
这里夹杂着个假动作,进去没做什么大事就回了:
004088E4 JB SHORT 1.00408955
004088E6 LEA ECX,DWORD PTR SS:[EBP-10]
004088E9 CALL <JMP.&MFC42.#540> ; 假动作,过
004088EE MOV DWORD PTR SS:[EBP-4],0
004088F5 JMP SHORT 1.00408907
以上过程很长很长,绝大部分代码省略。特别值得说明的是,中间设置了一个定时器。这个定时器设置了一个暗桩,如果你是缓慢跟踪,则可以到达一下代码处。在你不知道关键点的时候,如果你想来快点,程序却很容易跑飞。
一直跟到这里,才看见值得怀疑的代码(为减少篇幅,除第一段及主要部分外,其余跳过的部分省略):
0040914C JMP SHORT 1.0040915E
0040914E JS SHORT 1.004091C0
00409150 JB SHORT 1.004091C1
00409152 PUSH ES
00409153 ADD BYTE PTR DS:[EAX],AL
00409155 ADD BYTE PTR DS:[EAX],AL
00409157 ADD BYTE PTR DS:[EAX],AL
00409159 ADD BYTE PTR DS:[EAX+70],BH
0040915C JB SHORT 1.004091CD

解密跟踪调试软件标识符
注意这里:
0040915E PUSH 1
00409160 PUSH 1.004292D0 ; ASCII "xetn"
00409165 CALL 1.00407750 ;解密函数,代码见后
上下翻滚着看看,发现每一次都调用同一函数。调用时各自压入不同的字符串,出来后这些字符分别变成了调试软件的标题字符,所以值得进去看看。
0040916A JMP SHORT 1.00409181
004091B7 PUSH 1
004091B9 PUSH 1.004292F0 ; ASCII "tpgujdf"
004091BE CALL 1.00407750
004091C3 JMP SHORT 1.004091DA
00409210 PUSH 1
00409212 PUSH 1.00429310 ; ASCII "usx"
00409217 CALL 1.00407750
0040921C JMP SHORT 1.00409233
00409269 PUSH 1
0040926B PUSH 1.00429330 ; ASCII "efcvh"
00409270 CALL 1.00407750
00409275 JMP SHORT 1.0040928C
004092C2 PUSH 1
004092C4 PUSH 1.00429350 ; ASCII "tpvsdfs"
004092C9 CALL 1.00407750
004092CE JMP SHORT 1.004092E5
0040931B PUSH 1
0040931D PUSH 1.00429370 ; ASCII "gjmfnpo"
00409322 CALL 1.00407750
00409327 JMP SHORT 1.0040933E
00409374 PUSH 1
00409376 PUSH 1.00429390 ; ASCII "tqz"
0040937B CALL 1.00407750
00409380 JMP SHORT 1.00409397
004093CD PUSH 1
004093CF PUSH 1.004293B0 ; ASCII "jeb"
004093D4 CALL 1.00407750
004093D9 ADD ESP,40
004093DC JMP SHORT 1.004093F3
00409429 PUSH 1
0040942B PUSH 1.004293D0 ; ASCII "pmmzech"
00409430 CALL 1.00407750
00409435 JMP SHORT 1.0040944C
00409482 PUSH 1
00409484 PUSH 1.004293F0 ; ASCII "qfcspxtf"
00409489 CALL 1.00407750
0040948E JMP SHORT 1.004094A5
004094DB PUSH 1
004094DD PUSH 1.00429410 ; ASCII "ejtbttfn"
004094E2 CALL 1.00407750
004094E7 JMP SHORT 1.004094FE
00409534 PUSH 1
00409536 PUSH 1.00429430 ; ASCII "hpcvh"
0040953B CALL 1.00407750
00409540 JMP SHORT 1.00409557
0040958D PUSH 1
0040958F PUSH 1.00429450 ; ASCII "uxy"
00409594 CALL 1.00407750
00409599 JMP SHORT 1.004095B0

解密函数:
00407750 PUSH ESI
00407751 MOV ESI,DWORD PTR SS:[ESP+8] ; 指向字符串
00407755 PUSH EDI
00407756 MOV EDI,ESI
00407758 OR ECX,FFFFFFFF
0040775B XOR EAX,EAX
0040775D REPNE SCAS BYTE PTR ES:[EDI] ; 扫描字符串
0040775F NOT ECX
00407761 DEC ECX ; 计算字符串长度
00407762 TEST CX,CX
00407765 JBE SHORT 1.00407784
00407767 MOV DL,BYTE PTR SS:[ESP+10] ; 取出基数
通过跟踪发现,这里取出的基数都为 1。
0040776B PUSH EBX
0040776C MOV EAX,ESI ; 指向字符串
0040776E AND ECX,0FFFF
00407774 MOV BL,BYTE PTR DS:[EAX] ; 取出一位
00407776 SUB BL,DL ; 减去基数
00407778 MOV BYTE PTR DS:[EAX],BL ; 替换原字符
0040777A INC EAX ; 指向下一位
0040777B DEC ECX
0040777C JNZ SHORT 1.00407774 ; 未完继续
0040777E POP EBX
0040777F MOV EAX,ESI
00407781 POP EDI
00407782 POP ESI
00407783 RETN
这个函数所作的工作非常简单:将每个字符的ASCII值减1,再存回去,作为后面判断是否被调试的参照。

防调试软件清单
解密前:
004292D0 78 65 74 6E 00 00 00 00 xetn
004292F0 74 70 67 75 6A 64 66 00 tpgujdf
00429310 75 73 78 00 00 00 00 00 usx
00429330 65 66 63 76 68 00 00 00 efcvh
00429350 74 70 76 73 64 66 73 00 tpvsdfs
00429370 67 6A 6D 66 6E 70 6F 00 gjmfnpo
00429390 74 71 7A 00 00 00 00 00 tqz
004293B0 6A 65 62 00 00 00 00 00 jeb
004293D0 70 6D 6D 7A 65 63 68 00 pmmzech
004293F0 71 66 63 73 70 78 74 66 qfcspxtf
00429410 65 6A 74 62 74 74 66 6E ejtbttfn
00429430 68 70 63 76 68 00 00 00 hpcvh
00429450 75 78 79 00 00 00 00 00 uxy

解密后:
004292D0 77 64 73 6D 00 00 00 00 wdsm
004292F0 73 6F 66 74 69 63 65 00 softice
00429310 74 72 77 00 00 00 00 00 trw
00429330 64 65 62 75 67 00 00 00 debug
00429350 73 6F 75 72 63 65 72 00 sourcer
00429370 66 69 6C 65 6D 6F 6E 00 filemon
00429390 73 70 79 00 00 00 00 00 spy
004293B0 69 64 61 00 00 00 00 00 ida
004293D0 6F 6C 6C 79 64 62 67 00 ollydbg
004293F0 70 65 62 72 6F 77 73 65 pebrowse
00429410 64 69 73 61 73 73 65 6D disassem
00429430 67 6F 62 75 67 00 00 00 gobug
00429450 74 77 78 00 00 00 00 00 twx

到这里,我们可以大概测算一下有些什么办法搞定它?
1.改变加密的字符串,是参照失效。例如,改xetn为1etn,则解密出的是0dsm,则防调试代码部分不会理会wdsm。
2.改变解密算法,使其解出错误的字符串。例如,将代码:
00407767 MOV DL,BYTE PTR SS:[ESP+10]
改为:
00407767 MOV DL,0
则取消了解密,后面用未解密的字符串与跟踪调试软件的标识符比较,自然的不出正确的结果。如果上面不做任何修改,在准备好参照字符串后,会来到这里:

进程标识检查
下面是检查各种破解软件的代码,值得借鉴:
0040D003 LEA EDI,DWORD PTR SS:[EBP-20C]
0040D009 OR ECX,FFFFFFFF
0040D00C XOR EAX,EAX
0040D00E LEA EDX,DWORD PTR SS:[EBP-108]
0040D014 REPNE SCAS BYTE PTR ES:[EDI] 扫描活动进程标识符长度
0040D016 NOT ECX
0040D018 SUB EDI,ECX
0040D01A MOV EAX,ECX
0040D01C MOV ESI,EDI
0040D01E MOV EDI,EDX
0040D020 SHR ECX,2
0040D023 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS> ;转移标识符
0040D025 MOV ECX,EAX
0040D027 AND ECX,3
0040D02A REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[> ;转移标识符
0040D02C LEA ECX,DWORD PTR SS:[EBP-108]
0040D032 PUSH ECX
0040D033 CALL DWORD PTR DS:[<&MSVCRT._strlwr>] ; MSVCRT._strlwr
0040D039 ADD ESP,4
0040D03C TEST EAX,EAX
0040D03E JE 1.0040D195
0040D044 LEA EDX,DWORD PTR SS:[EBP-108]
0040D04A PUSH 1.004292D0 ; ASCII "wdsm"
0040D04F PUSH EDX
0040D050 CALL EBX ;检查是否是 wdsm
……
0040D16B JE SHORT 1.0040D195 ;如果都不是则跳过
0040D16D MOV EAX,DWORD PTR SS:[EBP-228] ;
如果上面的跳转跳向了这里,则调试软件会被关闭
0040D173 PUSH 0
0040D175 PUSH EAX
0040D176 PUSH 0
0040D178 PUSH 1F0FFF
0040D17D CALL DWORD PTR DS:[<&KERNEL32.OpenProces>; kernel32.OpenProcess
0040D183 PUSH EAX
0040D184 CALL DWORD PTR DS:[<&KERNEL32.TerminateP>; 终止调试进程
0040D18A TEST EAX,EAX
0040D18C JNZ SHORT 1.0040D195
0040D18E PUSH EAX
0040D18F CALL DWORD PTR DS:[<&KERNEL32.ExitProces>; kernel32.ExitProcess
0040D195 MOV EDX,DWORD PTR SS:[EBP-4]
0040D198 LEA ECX,DWORD PTR SS:[EBP-230]
0040D19E PUSH ECX
0040D19F PUSH EDX
0040D1A0 CALL <JMP.&KERNEL32.Process32Next> ;检查下一个进程
0040D1A5 TEST EAX,EAX
0040D1A7 JNZ 1.0040D003
程序只要查出与内定的标识符相同的软件,不论自己是否被调试,一律关闭这个软件,阻止自身被调试跟踪的可能。

可以怎么对付它?
1.修改所有代码:JNZ SHORT 1.0040D16D 为 nop,修改 0040D16B JE SHORT 1.0040D195 为:0040D16B JMP SHORT 1.0040D195,让程序瞎忙乎一回。
2.不让程序执行这段代码,使其刚要执行这段代码时立即返回。要做到这一点,只需找到这段子程序的入口,将其修改成返回指令即可。例如修改以下代码:
0040CFA0 55 PUSH EBP
0040CFA1 8BEC MOV EBP,ESP
0040CFA3 81EC 30020000 SUB ESP,230
0040CFA9 53 PUSH EBX
改为:
0040CFA0 C3 RETN
0040CFA1 8BEC MOV EBP,ESP
0040CFA3 81EC 30020000 SUB ESP,230
0040CFA9 53 PUSH EBX
这样就可以达到不执行这段代码的目的了。

目前很多软件添加了反调试代码,但手法各种各样,与软件制作者方法及能力有极大关系。有的宜修改字符串,有的宜修改代码,实际应用中应相机行事,不可生搬硬套。尤其是一些主程序与.dll互相监督的情况下,尽量使用修改字符串的方法进行处理。本文所采用的例子就属后者,应为.dll监视着主程序的运行情况的。只要你修改了主程序,.dll也同样可以关闭调试软件。
PYG19周年生日快乐!
  • TA的每日心情
    无聊
    2020-4-10 17:02
  • 签到天数: 5 天

    [LV.2]偶尔看看I

    发表于 2006-12-8 16:31:46 | 显示全部楼层
    哇~~~老家伙的文章被弄这里来了?
    PYG19周年生日快乐!

    该用户从未签到

    发表于 2006-12-8 16:50:00 | 显示全部楼层
    是原创????/:?
    PYG19周年生日快乐!

    该用户从未签到

    发表于 2006-12-9 00:07:49 | 显示全部楼层
    深奥,有点不明白,接着学吧.
    PYG19周年生日快乐!

    该用户从未签到

    发表于 2008-9-19 11:19:56 | 显示全部楼层
    深奥,有点不明白,接着学吧.
    PYG19周年生日快乐!
    您需要登录后才可以回帖 登录 | 加入我们

    本版积分规则

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