【文章标题】: 快乐传说脱机 破解分析
【文章作者】: goodcode
【软件名称】: 快乐传说脱机 1.39
【下载地址】: 自己搜索下载
【加壳方式】: ASPack 2.12 -> Alexey Solodovnikov
【保护方式】: 网络验证
【编写语言】: Delphi
【使用工具】: OD+DEDE+IDA
【操作平台】: Win32
【软件介绍】: 一款游戏的脱机外挂
【作者声明】: 外挂已经停止更新,并且不能正常连接验证服务器.
--------------------------------------------------------------------------------
【详细过程】
打开程序通过"快乐传说脱机.exe"打开"gjmain.dat", 确定外挂主程序为"gjmain.dat".
将"gjmain.dat"改名为"gjmain.exe", peid查壳为"ASPack 2.12 -> Alexey Solodovnikov",通
过脱壳机自动脱壳.
启动"gjmain.exe", 输入帐号密码日志窗口提示如下,请稍等)......
7:27:55 连接验证服务器失败
7:28:01 登陆成功
可以进入游戏,但是挂机功能不能正常使用(不能寻怪)
通过dede分析"gjmain.exe", 然后创建项目文件.
打开"main.dfm", 游览窗口上的组件发现ClientSocket1很有可能用于验证, 内容如下
object ClientSocket1: TClientSocket
Active = False
ClientType = ctNonBlocking
Host = 'www.csjpwg.com'
Port = 80
OnConnect = ClientSocket1Connect
OnRead = ClientSocket1Read
OnError = ClientSocket1Error
Left = 177
Top = 417
end
打开"main.pas", 查找上面组件相关的事件代码
procedure TGjForm.ClientSocket1Connect(Sender : TObject);
begin
(*
004EDB28 53 push ebx
004EDB29 8BD8 mov ebx, eax
004EDB2B A154234F00 mov eax, dword ptr [$004F2354]
004EDB30 833800 cmp dword ptr [eax], +$00
004EDB33 7423 jz 004EDB58
004EDB35 8B1554234F00 mov edx, [$004F2354]
004EDB3B 8B12 mov edx, [edx]
* Reference to control ClientSocket1 : N.A.
|
004EDB3D 8B8378040000 mov eax, [ebx+$0478]
004EDB43 8B8090000000 mov eax, [eax+$0090]
* Reference to: ScktComp.TCustomWinSocket.SendText(TCustomWinSocket;AnsiString):Integer;
|
004EDB49 E8BA8EFAFF call 00496A08
004EDB4E A154234F00 mov eax, dword ptr [$004F2354]
* Reference to: System.@LStrClr(void;void);
|
004EDB53 E8B066F1FF call 00404208
004EDB58 5B pop ebx
004EDB59 C3 ret
*)
end;
procedure TGjForm.ClientSocket1Error(Sender : TObject);
begin
(*
004EDB5C 55 push ebp
004EDB5D 8BEC mov ebp, esp
* Possible String Reference to: '连接验证服务器失败'
|
004EDB5F B980DB4E00 mov ecx, $004EDB80
004EDB64 BA00800000 mov edx, $00008000
004EDB69 A160464F00 mov eax, dword ptr [$004F4660]
|
004EDB6E E8C1D8FFFF call 004EB434
004EDB73 5D pop ebp
004EDB74 C20800 ret $0008
*)
end;
通过ClientSocket1Error确认这个组件与网络验证密切相关.
查看ClientSocket1的属性Host与Port得知验证是用web方式进行的,现在"www.csjpwg.com"已经不能打开,
所以我们通过winhex修改"www.csjpwg.com"属性为"www.baidu.com"多余字节以00填充.
输入帐号密码进入游戏,点击自动战斗发现依旧是不能寻怪.
我们下面的重点放在ClientSocket1Read事件处理过程.
因为已经不能连接原始的验证服务器,所以也不能跟踪出正确的验证路线,所以大部分也就只能猜测了-_-...
用ida展开exe文件, 来到ClientSocket1Read过程
CODE:004EDB94 ClientSocket1Read proc near
CODE:004EDB94
CODE:004EDB94 var_20 = dword ptr -20h
CODE:004EDB94 struseredit2 = dword ptr -1Ch
CODE:004EDB94 var_18 = dword ptr -18h
CODE:004EDB94 var_14 = dword ptr -14h
CODE:004EDB94 strUseredit = dword ptr -10h
CODE:004EDB94 var_C = dword ptr -0Ch
CODE:004EDB94 RecvText = dword ptr -8
CODE:004EDB94 CustomWinSocket = dword ptr -4
CODE:004EDB94
CODE:004EDB94 push ebp
CODE:004EDB95 mov ebp, esp
CODE:004EDB97 push 0
CODE:004EDB99 push 0
CODE:004EDB9B push 0
CODE:004EDB9D push 0
CODE:004EDB9F push 0
CODE:004EDBA1 push 0
CODE:004EDBA3 push 0
CODE:004EDBA5 push 0
CODE:004EDBA7 push ebx
CODE:004EDBA8 push esi
CODE:004EDBA9 push edi
CODE:004EDBAA mov [ebp+CustomWinSocket], ecx
CODE:004EDBAD mov ebx, eax ; 保存self指针
CODE:004EDBAF xor eax, eax
CODE:004EDBB1 push ebp
CODE:004EDBB2 push offset loc_4EDCD9
CODE:004EDBB7 push dword ptr fs:[eax]
CODE:004EDBBA mov fs:[eax], esp
CODE:004EDBBD lea edx, [ebp+RecvText]
CODE:004EDBC0 mov eax, [ebp+CustomWinSocket]
CODE:004EDBC3 call @Scktcomp@TCustomWinSocket@ReceiveText$qqrv ; 返回数据
CODE:004EDBC8 mov eax, [ebx+478h]
CODE:004EDBCE cmp dword ptr [eax+34h], 50h
CODE:004EDBD2 jz short loc_4EDC14 ; 客户端执行时跳转
CODE:004EDBD4 lea eax, [ebp+var_C]
CODE:004EDBD7 mov edx, [ebp+RecvText]
CODE:004EDBDA call @System@@LStrLAsg$qqrv ; System::__linkproc__ LStrLAsg(void)
CODE:004EDBDF lea edx, [ebp+strUseredit]
CODE:004EDBE2 mov eax, [ebx+324h] ; useredit控件
CODE:004EDBE8 call @TControl@GetText$qqrv ; TControl::GetText(void)
CODE:004EDBED mov eax, [ebp+strUseredit]
CODE:004EDBF0 push eax
CODE:004EDBF1 lea edx, [ebp+var_14]
CODE:004EDBF4 mov eax, ebx
CODE:004EDBF6 call sub_4EB218
CODE:004EDBFB mov eax, [ebp+var_14]
CODE:004EDBFE mov ecx, [ebp+var_C]
CODE:004EDC01 pop edx
CODE:004EDC02 call sub_4E3C58
CODE:004EDC07 mov eax, [ebp+CustomWinSocket]
CODE:004EDC0A call @Scktcomp@TCustomWinSocket@Close$qqrv ; Scktcomp::TCustomWinSocket::Close(void)
CODE:004EDC0F jmp loc_4EDC99
CODE:004EDC14 ; ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
CODE:004EDC14
CODE:004EDC14 loc_4EDC14: ; CODE XREF: ClientSocket1Read+3Ej
CODE:004EDC14 mov edx, [ebp+RecvText]
CODE:004EDC17 mov eax, offset dword_4EDCF0 ; 字符串 REG: 将此字符串前3字符串修改为ver(因为www.baidu.com返回的数据中
CODE:004EDC17 ; 会包含这些字符串)
CODE:004EDC1C call @System@@LStrPos$qqrv ; System::__linkproc__ LStrPos(void)
CODE:004EDC21 mov esi, eax ; 在返回字符串中查找
CODE:004EDC23 test esi, esi
CODE:004EDC25 jle short loc_4EDC99 ; 如果发现字符串不跳转
CODE:004EDC27 lea eax, [ebp+var_C]
CODE:004EDC2A call @System@@LStrClr$qqrr17System@AnsiString ; System::__linkproc__ LStrClr(System::AnsiString &)
CODE:004EDC2F add esi, 4 ; 上面查找返回的pos+4
CODE:004EDC32 mov eax, [ebp+RecvText]
CODE:004EDC35 call @System@_16823 ; System::_16823
CODE:004EDC3A mov edi, eax ; 取字符串内某成员
CODE:004EDC3C sub edi, esi ; 计算出一个长度
CODE:004EDC3E jl short loc_4EDC69
CODE:004EDC40 inc edi ; 循环 取数据然后连接吧
CODE:004EDC41
CODE:004EDC41 loc_4EDC41: ; CODE XREF: ClientSocket1Read+D3j
CODE:004EDC41 mov eax, [ebp+RecvText]
CODE:004EDC44 cmp byte ptr [eax+esi-1], 0Dh ; 判断当前pos是否指向0dh内容的字节
CODE:004EDC49 jz short loc_4EDC69 ; 指向0dh跳转
CODE:004EDC4B lea eax, [ebp+var_18]
CODE:004EDC4E mov edx, [ebp+RecvText]
CODE:004EDC51 mov dl, [edx+esi-1]
CODE:004EDC55 call unknown_libname_13 ; Borland Visual Component Library & Packages
CODE:004EDC5A mov edx, [ebp+var_18]
CODE:004EDC5D lea eax, [ebp+var_C] ; 接到这个字符串后
CODE:004EDC60 call @System@@LStrCat$qqrv ; System::__linkproc__ LStrCat(void)
CODE:004EDC65 inc esi ; 接字符串
CODE:004EDC66 dec edi
CODE:004EDC67 jnz short loc_4EDC41
CODE:004EDC69
CODE:004EDC69 loc_4EDC69: ; CODE XREF: ClientSocket1Read+AAj
CODE:004EDC69 ; ClientSocket1Read+B5j
CODE:004EDC69 lea edx, [ebp+struseredit2]
CODE:004EDC6C mov eax, [ebx+324h]
CODE:004EDC72 call @TControl@GetText$qqrv ; TControl::GetText(void)
CODE:004EDC77 mov eax, [ebp+struseredit2] ; 从某控件取字符串(Useredit)
CODE:004EDC7A push eax
CODE:004EDC7B lea edx, [ebp+var_20] ; 此变量还未初始化过
CODE:004EDC7E mov eax, ebx ; self指针
CODE:004EDC80 call sub_4EB218 ; 功能未知 返回服务器登陆字符串的某一部分吧
CODE:004EDC85 mov eax, [ebp+var_20]
CODE:004EDC88 mov ecx, [ebp+var_C]
CODE:004EDC8B pop edx
CODE:004EDC8C call sub_4E3C58 ; 外挂验证检测函数
CODE:004EDC91 mov eax, [ebp+CustomWinSocket]
CODE:004EDC94 call @Scktcomp@TCustomWinSocket@Close$qqrv ; Scktcomp::TCustomWinSocket::Close(void)
CODE:004EDC99
CODE:004EDC99 loc_4EDC99: ; CODE XREF: ClientSocket1Read+7Bj
CODE:004EDC99 ; ClientSocket1Read+91j
CODE:004EDC99 xor eax, eax
CODE:004EDC9B pop edx
CODE:004EDC9C pop ecx
CODE:004EDC9D pop ecx
CODE:004EDC9E mov fs:[eax], edx
CODE:004EDCA1 push offset loc_4EDCE0
CODE:004EDCA6
CODE:004EDCA6 loc_4EDCA6: ; CODE XREF: ClientSocket1Read+14Aj
CODE:004EDCA6 lea eax, [ebp+var_20]
CODE:004EDCA9 call @System@@LStrClr$qqrr17System@AnsiString ; System::__linkproc__ LStrClr(System::AnsiString &)
CODE:004EDCAE lea eax, [ebp+struseredit2]
CODE:004EDCB1 call @System@@LStrClr$qqrr17System@AnsiString ; System::__linkproc__ LStrClr(System::AnsiString &)
CODE:004EDCB6 lea eax, [ebp+var_18]
CODE:004EDCB9 mov edx, 2
CODE:004EDCBE call @System@@LStrArrayClr$qqrv ; System::__linkproc__ LStrArrayClr(void)
CODE:004EDCC3 lea eax, [ebp+strUseredit]
CODE:004EDCC6 call @System@@LStrClr$qqrr17System@AnsiString ; System::__linkproc__ LStrClr(System::AnsiString &)
CODE:004EDCCB lea eax, [ebp+var_C]
CODE:004EDCCE mov edx, 2
CODE:004EDCD3 call @System@@LStrArrayClr$qqrv ; System::__linkproc__ LStrArrayClr(void)
CODE:004EDCD8 retn
CODE:004EDCD9 ; ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
CODE:004EDCD9
CODE:004EDCD9 loc_4EDCD9: ; DATA XREF: ClientSocket1Read+1Eo
CODE:004EDCD9 jmp @System@@HandleFinally$qqrv ; System::__linkproc__ HandleFinally(void)
CODE:004EDCDE ; ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
CODE:004EDCDE jmp short loc_4EDCA6
CODE:004EDCE0 ; ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
CODE:004EDCE0
CODE:004EDCE0 loc_4EDCE0: ; DATA XREF: ClientSocket1Read+10Do
CODE:004EDCE0 pop edi
CODE:004EDCE1 pop esi
CODE:004EDCE2 pop ebx
CODE:004EDCE3 mov esp, ebp
CODE:004EDCE5 pop ebp
CODE:004EDCE6 retn
CODE:004EDCE6 ClientSocket1Read endp
来到验证函数内
CODE:004E3C58 sub_4E3C58 proc near ; CODE XREF: sub_4E26DC+4Ap
CODE:004E3C58 ; ClientSocket1Read+6Ep ...
CODE:004E3C58
CODE:004E3C58 var_18 = dword ptr -18h
CODE:004E3C58 var_14 = dword ptr -14h
CODE:004E3C58 var_10 = dword ptr -10h
CODE:004E3C58 var_C = dword ptr -0Ch
CODE:004E3C58 var_8 = dword ptr -8
CODE:004E3C58 var_4 = dword ptr -4
CODE:004E3C58
CODE:004E3C58 push ebp
CODE:004E3C59 mov ebp, esp
CODE:004E3C5B add esp, 0FFFFFFE8h
CODE:004E3C5E push ebx
CODE:004E3C5F push esi
CODE:004E3C60 push edi
CODE:004E3C61 xor ebx, ebx
CODE:004E3C63 mov [ebp+var_14], ebx
CODE:004E3C66 mov [ebp+var_18], ebx
CODE:004E3C69 mov [ebp+var_10], ebx
CODE:004E3C6C mov [ebp+var_C], ecx
CODE:004E3C6F mov [ebp+var_8], edx
CODE:004E3C72 mov [ebp+var_4], eax
CODE:004E3C75 mov eax, [ebp+var_4]
CODE:004E3C78 call @System@@LStrAddRef$qqrv ; System::__linkproc__ LStrAddRef(void)
CODE:004E3C7D mov eax, [ebp+var_8]
CODE:004E3C80 call @System@@LStrAddRef$qqrv ; System::__linkproc__ LStrAddRef(void)
CODE:004E3C85 mov eax, [ebp+var_C]
CODE:004E3C88 call @System@@LStrAddRef$qqrv ; System::__linkproc__ LStrAddRef(void)
CODE:004E3C8D xor eax, eax
CODE:004E3C8F push ebp
CODE:004E3C90 push offset sub_4E3D73
CODE:004E3C95 push dword ptr fs:[eax]
CODE:004E3C98 mov fs:[eax], esp
CODE:004E3C9B push [ebp+var_4]
CODE:004E3C9E push [ebp+var_8]
CODE:004E3CA1 lea edx, [ebp+var_18]
CODE:004E3CA4 mov eax, ds:off_4F2404
CODE:004E3CA9 mov eax, [eax] ; 这是堆栈上能看到登陆帐号,服务器名称等信息
CODE:004E3CAB call @TControl@GetText$qqrv ; TControl::GetText(void)
CODE:004E3CB0 push [ebp+var_18]
CODE:004E3CB3 lea eax, [ebp+var_14]
CODE:004E3CB6 mov edx, 3
CODE:004E3CBB call sub_404588 ; LStrCat
CODE:004E3CC0 mov eax, [ebp+var_14]
CODE:004E3CC3 call sub_48F58C ; TIdTCPClient._PROC_0048F58C()
CODE:004E3CC8 imul ebx, eax, 3039h
CODE:004E3CCE mov edx, ds:dword_4F4604
CODE:004E3CD4 mov eax, ebx
CODE:004E3CD6 call sub_48F5F0 ; * Reference to : TIdTCPClient._PROC_0048F5F0()
CODE:004E3CDB imul eax, 3039h
CODE:004E3CE1 mov ebx, eax
CODE:004E3CE3 lea ecx, [ebp+var_10]
CODE:004E3CE6 mov edx, [ebp+var_C]
CODE:004E3CE9 mov eax, ebx ; 执行到这里会异常 也许是上面的字符串有问题吧
CODE:004E3CEB call @Dbclient@TClientDataSet@CreateDSCursor$qqr44System@_DelphiInterface$t16Dsintf@IDSCursor_ ; * Reference to : TIdTCPClient._PROC_0048F694() ;nop掉这里
CODE:004E3CF0 mov ds:dword_4F4608, 0FFFFFFFFh ; 一处验证标志 >0就算通过验证吧
CODE:004E3CFA xor eax, eax
CODE:004E3CFC push ebp
CODE:004E3CFD push offset loc_4E3D1F
CODE:004E3D02 push dword ptr fs:[eax]
CODE:004E3D05 mov fs:[eax], esp
CODE:004E3D08 mov eax, [ebp+var_10] ; 这里装的应该是含有数字的字符串
CODE:004E3D0B call @Sysutils@StrToInt$qqrx17System@AnsiString ; 修改为mov eax, 1 替换掉这个call
CODE:004E3D10 mov ds:dword_4F4608, eax ; 一处验证标志 >0就算通过验证吧
CODE:004E3D15 xor eax, eax
CODE:004E3D17 pop edx
CODE:004E3D18 pop ecx
CODE:004E3D19 pop ecx
CODE:004E3D1A mov fs:[eax], edx
CODE:004E3D1D jmp short loc_4E3D30
CODE:004E3D1F ; ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
CODE:004E3D1F
CODE:004E3D1F loc_4E3D1F: ; DATA XREF: sub_4E3C58+A5o
CODE:004E3D1F jmp sub_4038D4
CODE:004E3D24 ; ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
CODE:004E3D24 call @@DoneExcept$qqrv ; __linkproc__ DoneExcept(void)
CODE:004E3D29 jmp short loc_4E3D50
CODE:004E3D2B ; ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
CODE:004E3D2B call @@DoneExcept$qqrv ; __linkproc__ DoneExcept(void)
CODE:004E3D30
CODE:004E3D30 loc_4E3D30: ; CODE XREF: sub_4E3C58+C5j
CODE:004E3D30 cmp ds:dword_4F4608, 0 ; 一处验证标志 >0就算通过验证吧
CODE:004E3D37 jl short loc_4E3D50 ; >0
CODE:004E3D39 mov eax, offset dword_4F4610
CODE:004E3D3E mov edx, [ebp+var_C]
CODE:004E3D41 call @System@@LStrAsg$qqrv ; System::__linkproc__ LStrAsg(void)
CODE:004E3D46 mov ds:dword_4F460C, 31589h
CODE:004E3D50
CODE:004E3D50 loc_4E3D50: ; CODE XREF: sub_4E3C58+D1j
CODE:004E3D50 ; sub_4E3C58+DFj
CODE:004E3D50 xor eax, eax
CODE:004E3D52 pop edx
CODE:004E3D53 pop ecx
CODE:004E3D54 pop ecx
CODE:004E3D55 mov fs:[eax], edx
CODE:004E3D58 push offset loc_4E3D7A
CODE:004E3D5D
CODE:004E3D5D loc_4E3D5D: ; CODE XREF: CODE:004E3D78j
CODE:004E3D5D lea eax, [ebp+var_18]
CODE:004E3D60 call @System@@LStrClr$qqrr17System@AnsiString ; System::__linkproc__ LStrClr(System::AnsiString &)
CODE:004E3D65 lea eax, [ebp+var_14]
CODE:004E3D68 mov edx, 5
CODE:004E3D6D call @System@@LStrArrayClr$qqrv ; System::__linkproc__ LStrArrayClr(void)
CODE:004E3D72 retn
CODE:004E3D72 sub_4E3C58 endp ; sp = -38h
修改过以上3处后继续使用外挂登陆, 进入游戏点自动战斗.
已经可以正常战斗了,到此破解完成.
--------------------------------------------------------------------------------
【经验总结】
外挂是一次验证, 判断标志也非常少, 使用压缩壳加壳, 字符串明码出现这些对破解来说带来很多方便.
不知道会不会有烂人看完文章自己写个loader在加一个广告拿出去忽悠...
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2007年01月02日 7:49:59