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?
buffer db 100 dup(?)
.CODE
START:
invoke GetCommandLine
invoke MessageBox,NULL,eax,offset mytitleA,MB_OK
invoke ExitProcess,0
end START
编译并重命名成网游的核心文件名,放于其目录下。正常步骤运行之。
选择任意一个服务器测试,记下那个服务器所对应的参数(此处为1rag21)。
等待IDA分析的过程中顺便查了import table,看一看有什么可疑的函数……
首先会找FindWindow
之类…没错!
再看下去,发现引入了 CreateMutexA
和 FindWindowA
不管怎样,这两个不可不防。
改之! 找到FindWindowA
函数的xrefs(嘻嘻,只有一处):
..... call FindWindowA
.text:0064CFCF test eax, eax
.text:0064CFD1 jz short loc_64CFDC
如果没有FindWindow
到,就注册个窗体类,再建立一个窗口。
改成 xor eax,eax
(只改动了一个字节的机器码,不能改长度)。
然后处理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
填充……
保存修改完的结果。
由于那个核心文件不是.exe为后缀的,为了绕过Updater还要依赖另一个程序:
.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?
buffer db 100 dup(?)
.CODE
START:
invoke StdIn,offset buffer,100
invoke lstrlen,offset buffer
dec eax
mov BYTE PTR buffer[eax],0
dec eax
mov BYTE PTR buffer[eax],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动态调试。大可在很后面的地方按下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
。完工!
由于Updater频繁更新,还需写个程序打打每次自动补丁。共须Patch四字节。
CreateMutex
以及FindWindow
来保证程序只运行一个实例。破解过程非常简单。