admin 发表于 2010-9-28 23:57:18

一个大型网游多开实例

找到一个没加壳的半免费网游(网游的名字就不说了),分析其双开方式。
大概“某人们”早就对它动过手脚了,只是没看到有人公布。
经过几天的试用,至今没有发现这种破法有问题。(由于是几天以前破的,现在回忆起来可能会有点出入,还是希望读者以实际为准,灵活应变^_^)
本文仅仅作为研究用,不可作其他用途……
///////////////////////////////////

经过简单的观察,猜测执行桌面快捷方式后打开的是一个自动Update的程序,该程序每天更新其核心。
继续观察,发现Update程序完成后直接用CreateProcess之类的方法运行那个核心文件。核心文件通过分析命令行参数来指定登陆服务器(官服,不是私的)。

于是开始下手:

首先自己写一个程序 ShowCmdLine.asm :

.386
.model flat, stdcall
option casemap :none

include windows.inc
include user32.inc
include kernel32.inc
include masm32.inc

includelib user32.lib
includelib kernel32.lib
includelib masm32.lib
include macro.asm
.data
mytitleA db 'GetCmdLine()',0
.data?
bufferdb 100 dup(?)

.CODE
START:

invoke GetCommandLine
invoke MessageBox,NULL,eax,offset mytitleA,MB_OK
invoke ExitProcess,0

end START

编译它,重命名成网游的核心文件名,放到网游目录下。正常步骤运行之…………
Oh,Ja!事实证明了猜想。选择任意一个服务器测试,记下那个服务器所对应的参数(我玩的服务器参数是1rag21,这个字眼熟悉吧~应该有许多人知道这是什么网游了,我也不能多说,不知道的自己google一下吧)。

打开IDA,分析原来的核心(文件满大的~要有耐心)。在等待IDA分析的时候多看看import table,找找看有什么可疑的函数……
大多人首先会找FindWindow之类的……没错!
从IDA的Imports中看到

CreateMutexA 和 FindWindowA

不管怎样,这两个不可不防。
改之!
在FindWindowA上按下'X',得到FindWindowA函数的xrefs(嘻嘻,只有一处):

..........
.....       call FindWindowA
.text:0064CFCF               test    eax, eax
.text:0064CFD1               jz      short loc_64CFDC
.......

如果没有FindWindow到,就注册个窗体类,再建立一个窗口。
没话说,改成 xor eax,eax (只改动了一个字节的机器码,不能改长度)。

然后处理CreateMutexA:
同样按下'x',得到xrefs
这里的CreateMutexA的调用大概有5处左右。
每处的代码类似:

.text:006517F2               call    ebx ; CreateMutexA
.text:006517F4               mov   esi, eax
.text:006517F6               test    esi, esi
.text:006517F8               jnz   .....


jnz到的每个地方(只有两个实例,其他都是重复的)一开始都是GetLastError,如:

.text:00651843               call    ds:GetLastError
.text:00651849               cmp   eax, ERROR_ALREADY_EXISTS

这里有个技巧,把 cmp eax, ERROR_ALREADY_EXISTS 改成比如 cmp eax,00000001 之类的其他错误代码,建议直接修改机器码,否则要注意不能改变文件长度,或者应该用nop填充……

保存修改完的结果。

为了绕过Updater,迅速启动游戏,由于那个核心文件不是.exe为后缀的。写个程序来调用它:
.386
.model flat, stdcall
option casemap :none

include windows.inc
include user32.inc
include kernel32.inc
include masm32.inc

includelib user32.lib
includelib kernel32.lib
includelib masm32.lib
include macro.asm
.data
ErrorTitle db 'Error',0
ErrorMsg db '不能运行',0
.data?
bufferdb 100 dup(?)
.CODE
START:
invoke StdIn,offset buffer,100
invoke lstrlen,offset buffer
dec eax
mov BYTE PTR buffer,0
dec eax
mov BYTE PTR buffer,0
invoke WinExec,offset buffer,SW_SHOW
cmp eax,31D
jg @f
invoke MessageBox,NULL,offset ErrorMsg,offset ErrorMsg,MB_OK
@@:invoke ExitProcess,0
end START

link的时候加上 /subsystem:console。

通过这个程序间接调用游戏核心程序(其实应该说成是主程序),别忘了加上参数指定服务器(否则……)。

…………
………………
……………………
……………………………
…………………………………
…………………………………………
………………汗~~还是不能双开。

别着急,来动态调试它。
我用OllyDebug,主要因为它和Tc的快捷键基本相同,以前TC用了相当长的时间……
我用的计算机速度相当慢(P3-667,15G硬盘)。
有经验的无产阶级们不必等着OllyDbg分析完整个程序,在OD进度调缓慢移动的时候按下空格,阻止OD分析。
(虽然本菜鸟没有经验,但是由于计算机配置的限制,也只能加载完不等分析把它停了。)如果实在对动态加载显示的Call来Call去摸不着头脑,可以对照着刚才IDA的分析结果。但是相信只要稍微熟悉VC6的rtl就不会有大问题。

大可在很后面的地方按下F4,因为前面的障碍在静态分析的时候都已经扫除了!

跳跃着动态F4在其中一个可疑的地方停下(似乎应该是这里,记得不是很清楚了。不过,既然本菜鸟都可以一眼看出来,相信大家……)

.text:0064E1E9               call    ds:CreateMutexA
.text:0064E1EF               push    eax             ; hHandle
.text:0064E1F0               call    ds:WaitForSingleObject
.text:0064E1F6               test    eax, eax         ;!!!!!!!!!!!
.text:0064E1F8               jz      short loc_64E208

大概是刚才分析CreateMutexA的时候漏了这里,把这里也补成 xor eax,eax
再运行一遍……Gut!成功了。
顺便针对玩家(不是为了研究的那种)说一下,该文件每天更新,如果用旧版本则无法进入游戏,写个程序打打补丁吧。

总共Patch了4个字节,应该算是改动比较少的,其他几组核心程序改法也类似。

总结:
这个程序利用了普遍应用的CreateMutex以及FindWindow来保证程序只运行一个实例。该破解过程属于简单类型。

!!!再次声明,本文仅为讨论技术,作者不承担任何责任。

农妇果园 发表于 2012-3-4 14:56:04

顶你一下吧











static/image/common/sigline.gif
北京商标注册代理
页: [1]
查看完整版本: 一个大型网游多开实例