【文章标题】: 星际霸主1.9j分析
【文章作者】: thomasyzh
【作者邮箱】: machinesy@gmail.com
【软件名称】: 星际霸主1.9j
【软件大小】: 5.16 MB
【下载地址】: 自己搜索下载
【加壳方式】: Themida/WinLicense V1.8.X-V1.9.X DLL
【软件介绍】: 一个星际争霸1的游戏外挂
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
这是一个破解任务,一位朋友请我帮他破解,价格是1K。因为不是为了赚这个票子,所以就直接发表出来了。在发表之前
我是在考虑这个帖子的标题写软件破解,还是软件分析。因为实际上现在有两个方法完成这个破解,1是继续按照我现在的
思路把这个东西做完了。2是我去把Virtualizer加的虚拟器代码还原掉了。实际上我选择了2,所以我就没有继续把1的路子
走完了。最后我把改vm代码还原后,一定的能够破译掉。
那么,我把1的分析过程写出来,所以我把这文章的标题就写做分析了。
一些文件啊,咋的咋的那些信息我就不说了。我直接说结论。
1这是个大量使用hook挂接星际争霸的游戏外挂。
2这个外挂是使用http验证的一个外挂。
3这个外挂的验证在scHelper.king的代码里边。
4这个外挂的数据收发在scking.exe里边,然后通过内存共享传送到scHelper.king里边。大概应该在那个sckhook.dll里边。
因为加了tmd壳。我并没有选择一开始就CreateProcess 这种方式,而是让它在内存里解密完,并且anti代码跑完的石斛attack
上去。最佳的效果是直接attack到加密验证代码那里。
于是
1.Hook一个函数,这个函数必须要是scHelper.king里边调用的一个函数。
2.在scHelper.king一开始跑的时候,用一断hook代码把整个线程拥塞起来,然后我们attack,然后恢复拥塞。太好了messagebox满足我们的需求。
于是就有了这样一段代码
;在此文档的文档工具栏项目上单击右键->参数属性
.386
.model flat, stdcall
option casemap :none
include windows.inc
include user32.inc
include kernel32.inc
includelib user32.lib
includelib kernel32.lib
inlinehookfun PROTO :DWORD,:DWORD,:DWORD
MyZwSetInformationThread PROTO
Myconnect PROTO
MyCreateThread PROTO
patchcode PROTO
ThreadProc PROTO
.data
lpszByDll db "Welcome",0
lpszErrorQuery db "query error",0
lpszErrorProtected db "protected error",0
lpszErrorProtected2 db "protected error2",0
lpszErrorGetModule db "get module handle error",0
lpszconnect db "no cmp error",0
lpZwSetInformationThread db "ZwSetInformationThread",0
lpNtDll db "NtDll.dll",0
lpWS2_32Dll db "ws2_32.dll",0
lpConnect db "connect",0
lpKernel32Dll db "kernel32.dll",0
lpCreateThread db "CreateThread",0
lpthreadmsg db "thread msg",0
lptestbreakpointer db "testbreakpointer",0
lpHideDebug db "Hide Debug",0
lphookcode db 0e9h,90h,90h,90h,90h,90h,90h,90h,90h,90h
.data?
hInstance dd ?
hinsNtDll dd ?
addrZwSetInformationThread dd ?
myaddrZwSetInformationThread dd ?
hinsWS2_32 dd ?
addrconnect dd ?
myaddrconnect dd ?
hinsKernel32 dd ?
addrCreateThread dd ?
mydddrCreateThread dd ?
.CODE
;入口.如果DLL需要加载资源,需要保存hIinstDLL这个句柄到全局变量.它才是模块句柄
;使用GetModuleHandle获得的永远是主程序的句柄
LibMain proc hInstDLL:DWORD, reason:DWORD, unused:DWORD
.if reason == DLL_PROCESS_ATTACH ;动态库被加载时调用,返回0加载失败!
mov eax,hInstDLL
mov hInstance,eax
invoke GetModuleHandle,addr lpNtDll
.if eax == NULL
invoke MessageBox,NULL,addr lpszErrorGetModule,addr lpszErrorGetModule,MB_OK
ret
.endif
mov hinsNtDll,eax
invoke GetProcAddress,hinsNtDll,addr lpZwSetInformationThread
mov addrZwSetInformationThread,eax
mov eax,MyZwSetInformationThread
mov myaddrZwSetInformationThread,eax
invoke inlinehookfun,myaddrZwSetInformationThread,addrZwSetInformationThread,0
invoke LoadLibrary,addr lpWS2_32Dll
.if eax == NULL
invoke MessageBox,NULL,addr lpszErrorGetModule,addr lpszErrorGetModule,MB_OK
ret
.endif
mov hinsWS2_32,eax
invoke GetProcAddress,hinsWS2_32,addr lpConnect
mov addrconnect,eax
mov eax,Myconnect
mov myaddrconnect,eax
invoke inlinehookfun,myaddrconnect,addrconnect,0
;invoke patchcode
invoke CreateThread,NULL,NULL,ThreadProc,NULL,NULL,NULL
invoke GetModuleHandle,addr lpKernel32Dll
mov hinsKernel32,eax
invoke GetProcAddress,hinsKernel32,addr lpCreateThread
mov addrCreateThread,eax
mov eax,MyCreateThread
mov mydddrCreateThread,eax
invoke inlinehookfun,mydddrCreateThread,addrCreateThread,0
mov eax,TRUE
ret
.elseif reason == DLL_PROCESS_DETACH
.elseif reason == DLL_THREAD_ATTACH
.elseif reason == DLL_THREAD_DETACH
;添加处理代码
.endif
ret
LibMain Endp
;供主程序调用的函数
MsgBox proc hWnd,lpszText,fStyle
invoke MessageBox,hWnd,lpszText,offset lpszByDll,fStyle
ret
MsgBox endp
inlinehookfun proc dDest,dSrc,len
LOCAL @mbi:MEMORY_BASIC_INFORMATION
LOCAL @oldpro:DWORD
invoke VirtualQuery,dSrc,addr @mbi,sizeof MEMORY_BASIC_INFORMATION
.if eax== FALSE
invoke MessageBox,NULL,addr lpszErrorQuery,addr lpszErrorQuery,MB_OK
ret
.else
invoke VirtualProtect,@mbi.BaseAddress,@mbi.RegionSize,PAGE_EXECUTE_READWRITE,addr @oldpro
.if eax == FALSE
invoke MessageBox,NULL,addr lpszErrorProtected,addr lpszErrorProtected,MB_OK
ret
.else
mov eax,dDest
mov ebx,dSrc
sub eax,ebx
sub eax,5
lea ebx,lphookcode
mov DWORD ptr [ebx+1],eax
mov eax,dSrc
mov ch,0e9h
mov BYTE ptr [eax],ch
mov ecx,DWORD ptr [ebx+1]
mov DWORD ptr [eax+1],ecx
;modify code
invoke VirtualProtect,@mbi.BaseAddress,@mbi.RegionSize,@oldpro,addr @oldpro
.if eax == FALSE
invoke MessageBox,NULL,addr lpszErrorProtected2,addr lpszErrorProtected2,MB_OK
ret
.endif
.endif
.endif
ret
inlinehookfun endp
patchcode proc
Local @mbi : MEMORY_BASIC_INFORMATION
Local @oldpro:DWORD
invoke VirtualQuery,2044747h,addr @mbi,sizeof MEMORY_BASIC_INFORMATION
invoke VirtualProtect,@mbi.BaseAddress,@mbi.RegionSize,PAGE_EXECUTE_READWRITE,addr @oldpro
mov eax,204474Eh
mov dh,0ebh
mov BYTE ptr [eax],dh
invoke VirtualProtect,@mbi.BaseAddress,@mbi.RegionSize,@oldpro,addr @oldpro
patchcode endp
MyZwSetInformationThread proc
pushad
pushfd
;invoke MessageBox,NULL,addr lptestbreakpointer,addr lptestbreakpointer,MB_OK
;ret ;2
mov eax,DWORD ptr [esp+20h+04h+08h]
cmp eax,11h
jnz CMP_NO
invoke MessageBox,NULL,addr lpHideDebug,addr lpHideDebug,MB_OK
CMP_NO:
popfd
popad
mov eax,0e5h
mov edx,addrZwSetInformationThread
add edx,5
jmp edx
ret
MyZwSetInformationThread endp
Myconnect proc
pushad
pushfd
;ret ;2
mov eax,DWORD ptr [esp+20h+04h+08h]
mov edx,DWORD ptr [eax+04h]
cmp edx,0b75ab8abh
jnz NO_CMP
invoke MessageBox,NULL,addr lpszconnect,addr lpszconnect,MB_OK
NO_CMP:
popfd
popad
mov edi,edi
push ebp
mov ebp,esp
mov eax,addrconnect
add eax,5
jmp eax
Myconnect endp
ThreadProc proc
invoke Sleep,5000
;invoke patchcode
ret
ThreadProc endp
MyCreateThread proc
pushad
pushfd
mov eax,DWORD ptr [esp+20h+04h]
sub eax, 02020000h
ja leb_2
leb_1:
jmp NO
leb_2:
mov eax,DWORD ptr [esp+20h+04h]
sub eax,026fd000h
jna leb_4
leb_3:
jmp NO
leb_4:
invoke MessageBox,NULL,addr lpthreadmsg,addr lpthreadmsg,MB_YESNO
NO:
popfd
popad
mov edi,edi
push ebp
mov ebp,esp
mov eax,addrCreateThread
add eax,5
jmp eax
MyCreateThread endp
End LibMain
实际上,在这个dll汇编代码里,我挂了几个函数,都是为了测试用的。
但是这正有用的只有一个CreateThread 看看挂接后做了什么
MyCreateThread proc
pushad
pushfd
mov eax,DWORD ptr [esp+20h+04h]
sub eax, 02020000h
ja leb_2
leb_1:
jmp NO
leb_2:
mov eax,DWORD ptr [esp+20h+04h]
sub eax,026fd000h
jna leb_4
leb_3:
jmp NO
leb_4:
invoke MessageBox,NULL,addr lpthreadmsg,addr lpthreadmsg,MB_YESNO
NO:
popfd
popad
mov edi,edi
push ebp
mov ebp,esp
mov eax,addrCreateThread
add eax,5
jmp eax
MyCreateThread endp
写了一段代码判断是不是被scHelper.king这个模块调用的CreateProcess如果是的话就MessageBox一下。范围代码判断如下
mov eax,DWORD ptr [esp+20h+04h]
sub eax, 02020000h
ja leb_2
leb_1:
jmp NO
leb_2:
mov eax,DWORD ptr [esp+20h+04h]
sub eax,026fd000h
jna leb_4
leb_3:
jmp NO
leb_4:
invoke MessageBox,NULL,addr lpthreadmsg,addr lpthreadmsg,MB_YESNO
先取一个esp的ret模块地址如果满足大于02020000h小于026fd000h 那么就messagebox.
为啥要createthread喃!~????一个dll如果不写个线程去完成很多初使化的话,那这样做是不合适的。因为会拥塞主线程的线程。
还有细节要处理下。Themida壳挂接了DbgUiRemoteBreakin这个函数,需要恢复之,然后我们就能带壳调试了。然后在messageboxa的尾巴上打上断点。
我们就可以发现线程函数就是这个了
02028964 . 55 push ebp
02028965 . 8BEC mov ebp, esp
02028967 . 81C4 58F3FFFF add esp, -0CA8
0202896D . B8 A45E2302 mov eax, 02235EA4
02028972 . 53 push ebx
02028973 . 56 push esi
02028974 . 57 push edi
02028975 . E8 D2DE0800 call 020B684C
然后就进入到这里。然后它就开始他疯狂的vm了。进过一段时间的分析,我们就找到了vm_call_fun的位置。具体如何找到vm_call_fun其实一点都不难
我是直接用od的自动跟入,跟入每一个分支,然后看到它跑到非vm的代码里边后,再用减号后退,就直接找到了vm_call_fun.
01B815C0 8B0424 mov eax, dword ptr [esp] ; vm_call_XXX
01B815C3 8DB0 44010000 lea esi, dword ptr [eax+144]
01B815C9 B9 1C000000 mov ecx, 1C
01B815CE 8B78 3C mov edi, dword ptr [eax+3C]
01B815D1 F3:A5 rep movs dword ptr es:[edi], dword p>
01B815D3 5F pop edi
01B815D4 C747 38 000000F>mov dword ptr [edi+38], F0000000
01B815DB FF77 70 push dword ptr [edi+70]
01B815DE FF77 74 push dword ptr [edi+74]
01B815E1 FFB7 84000000 push dword ptr [edi+84]
01B815E7 FFB7 8C000000 push dword ptr [edi+8C]
01B815ED FF77 7C push dword ptr [edi+7C]
01B815F0 FFB7 AC000000 push dword ptr [edi+AC]
01B815F6 FFB7 A4000000 push dword ptr [edi+A4]
01B815FC FFB7 94000000 push dword ptr [edi+94]
01B81602 FFB7 9C000000 push dword ptr [edi+9C]
01B81608 C747 28 0000000>mov dword ptr [edi+28], 0
01B8160F 61 popad
01B81610 9D popfd
01B81611 C3 retn
这里就是vm_Call_Fun.
虚拟机代码的还原部分,我会另外开篇帖子再写写,这里我就不再深入细解了。
然后我们我们在vm_call_fun看这个验证dll是调用了哪些没有被vm的代码。有可能像memcpy这样功能的函数,他没有去vm掉或者怎样怎样。
这样,我们就又找了这样一个函数。这个函数就是memcpy
020B6418 /$ 55 push ebp
020B6419 |. 8BEC mov ebp, esp
020B641B |. 56 push esi
020B641C |. 57 push edi
020B641D |. 8B7D 08 mov edi, dword ptr [ebp+8]
020B6420 |. 8BC7 mov eax, edi
020B6422 |. 8B75 0C mov esi, dword ptr [ebp+C]
020B6425 |. 8B4D 10 mov ecx, dword ptr [ebp+10]
020B6428 |. 8BD1 mov edx, ecx
020B642A |. D1E9 shr ecx, 1
020B642C |. D1E9 shr ecx, 1
020B642E |. FC cld
020B642F |. F3:A5 rep movs dword ptr es:[edi], dword p>
020B6431 |. 8BCA mov ecx, edx
020B6433 |. 83E1 03 and ecx, 3
020B6436 |. F3:A4 rep movs byte ptr es:[edi], byte ptr>
020B6438 |. 5F pop edi
020B6439 |. 5E pop esi
020B643A |. 5D pop ebp
020B643B \. C3 retn
然后我们就在这下断点看。
2
087FF0DC 020C2FBC 返回到 scHelper.020C2FBC 来自 scHelper.020B6418
087FF0E0 04512630
087FF0E4 04512614 ASCII "1234567890abcdef"""
087FF0E8 00000010
087FF0EC 0223A7F3 scHelper.0223A7F3
087FF0F0 04512566
087FF0F4 087FF130 指向下一个 SEH 记录的指针
087FF0F8 020B6893 SE处理程序
087FF0FC 02242AD0 scHelper.02242AD0
087FF100 087FF0EC
3
087FF0D8 020C2FBC 返回到 scHelper.020C2FBC 来自 scHelper.020B6418
087FF0DC 04512650
087FF0E0 04512639 ASCII "0abcdef"
087FF0E4 00000001
087FF0E8 00000001
087FF0EC 0000000A
最终,我们就找到这个函数,这个函数就是报文的组织函数
020C3448 /$ 55 push ebp ; EDX_IS_INDEX
020C3449 |. 8BEC mov ebp, esp
020C344B |. 83C4 D0 add esp, -30
020C344E |. 53 push ebx
020C344F |. 56 push esi
020C3450 |. 57 push edi
020C3451 |. 894D D0 mov dword ptr [ebp-30], ecx
020C3454 |. 8BDA mov ebx, edx
020C3456 |. 8BF8 mov edi, eax
020C3458 |. B8 342D2402 mov eax, 02242D34
020C345D |. E8 EA33FFFF call 020B684C
这里的edx,就是一个index,从随机变量到字符的转换。
我们为什么要找随机变量?因为我们要定死随机变量。定死随机变量后,我们就能够以同样的发送报文得到同样的应答报文,这样,我们也就破译了这个外挂了。
到这里,同学们如果呀破译这个外挂的话,只有再去分析完,这个edx,到底来自于内存的哪里!~~~然后去把这个edx,定死。然后再去通信一次,这个外挂就破解了。
--------------------------------------------------------------------------------
【经验总结】
咋说喃,vm还原这个东西不一句两句就说的清楚的。我自己也在学习。过两天,我去把这个外挂的vm还原放出来。有兴趣的
朋友也可以不还原vm破之。顺着我这个思路就成了。
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2011年02月01日 18:19:50
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
上传的附件: