【文章标题】: 菜鸟逆向一个老的双升扑克游戏,并完成外挂
【文章作者】: cdanlover
【软件名称】: 古今大战80分
【加壳方式】: 无
【保护方式】: Name/Serial
【编写语言】: Delphi
【使用工具】: peid、DeDeDark、RadASM、MASM32、OD、WINDOWS计算器
【操作平台】: WINXP
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
【首发连接】:http://bbs.pediy.com/showthread.php?t=129281
--------------------------------------------------------------------------------
【详细过程】
好久没有逆向过程序了,手都有点生了,今天有空就找了一个简单的来练练手,原来写过这个游戏的注册机,算法挺简单,估计软件设计也不会太复杂,于是就有了这篇笔记。
这个游戏是用Delphi编程的,于是找来DeDeDark对其反了一下,很容易找到菜单的调用过程地址:
Caption = '开局(K)'
OnClick = MIShuffleClick 00464A58
Caption = '摸牌(M)'
OnClick = MISettleClick 00464A60
Caption = '扣牌(D)'
OnClick = MIDiscardClick 00464A70
Caption = '出牌(C)'
OnClick = MIShowClick 00464A78
Caption = '作弊模式'
OnClick = MIShowCardClick 004649FC
Caption = '察看'
Caption = '底牌'
OnClick = N14Click 00466CAC
首先是对“开局”进行跟踪的,发现在发牌的过程中,在寄存器中反复出现同一个地址,我就想到有可能是程序申请到的一块内存的开始地址,用来存放牌面信息,在我的办公电
脑上是00B53250,后来在另一台电脑上跟踪时发现这个地址变了,于是就想找出是什么时间开始取的这块内存,发现在程序一开始就进行了申请:
004678E4 > $ 55 push ebp
004678E5 . 8BEC mov ebp,esp
004678E7 . 83C4 F4 add esp,-0C
004678EA . B8 1C774600 mov eax,0046771C
004678EF . E8 B8DEF9FF call 004057AC ; 申请内存
004678F4 . A1 408E4600 mov eax,dword ptr ds:[468E40]
004678F9 . 8B00 mov eax,dword ptr ds:[eax]
004678FB . E8 746AFCFF call 0042E374
00467900 . 8B0D 348D4600 mov ecx,dword ptr ds:[468D34] ; 古今大战.00469980
00467906 . A1 408E4600 mov eax,dword ptr ds:[468E40]
0046790B . 8B00 mov eax,dword ptr ds:[eax]
0046790D . 8B15 74614500 mov edx,dword ptr ds:[456174] ; 古今大战.004561B4
00467913 . E8 746AFCFF call 0042E38C ; 在call之前,内存已申请好,在call过后,地址指针等已经分配好了,开始出界面
00467918 > . A1 408E4600 mov eax,dword ptr ds:[468E40]
0046791D . 8B00 mov eax,dword ptr ds:[eax]
0046791F . E8 F46AFCFF call 0042E418 ; 已完全运行
就开始找这个00B53250,最早出现的地方,是在
mov ecx,dword ptr ds:[468D34]
[468D34] 保存一个地址:00469980,程序正常运行后,在[00469980]保存了基址:------>00B53250
在00467918处下条件记录断点,可直接定位到分配的内存基址。
在后来的分析中,发现以这个基址有很多可能关注的地方:
+F34 [00B54184] 代表本局中出的第几张牌。
+4E4 [00B53734] 比较是否为庄家,4为庄家,1为最后出牌(分析结果:1代表左边的,2代表对家,3代表右边的,4代表玩家自己)
一开始没有找到存放牌信息的地方,后来就对“扣牌”过程进行分析,其中有段:
004641E8 |. B8 14000000 mov eax,14 ; 20个元素
004641ED |. 8D55 B0 lea edx,[local.20] ; 初始化一个数组,用于保存出的牌是手中的第几张牌
004641F0 |> 33C9 /xor ecx,ecx
004641F2 |. 890A |mov dword ptr ds:[edx],ecx
004641F4 |. 83C2 04 |add edx,4
004641F7 |. 48 |dec eax
004641F8 |.^ 75 F6 \jnz short 004641F0
004641FA |. B8 01000000 mov eax,1
004641FF |> 8B9483 4C0400>/mov edx,dword ptr ds:[ebx+eax*4+44C]
00464206 |. 8B52 34 |mov edx,dword ptr ds:[edx+34]
00464209 |. 8B8B 18030000 |mov ecx,dword ptr ds:[ebx+318]
0046420F |. 83E9 0A |sub ecx,0A
00464212 |. 3BD1 |cmp edx,ecx
00464214 |. 75 05 |jnz short 0046421B
00464216 |. 46 |inc esi
00464217 |. 8944B5 AC |mov dword ptr ss:[ebp+esi*4-54],eax ; 一个数组,记录出的第几张牌
0046421B |> 40 |inc eax
0046421C |. 83F8 1A |cmp eax,1A ; 与26比较,每个玩家手中25张牌
0046421F |.^ 75 DE \jnz short 004641FF
发现在mov edx,dword ptr ds:[ebx+eax*4+44C]这个地方计算是哪张牌,查看这块内存发现挺有规律的,有很多4SD开始的,继续向上翻,找到第一个4SD的单元,重新跟踪,对其
下内存写入断点,就找到了这块保存牌面信息的地方,当eax=1时,即可以算出玩家手中第一张牌保存的地方了,每个双字保存一个地址指针,指向每一张牌:
左手边玩家的:
00B53574 94 99 B5 00 70 BA B5 00 50 BC B5 00 48 BE B5 00 ....p...P...H...
00B53584 C8 9A B5 00 F4 9C B5 00 C8 9E B5 00 C0 A0 B5 00 ................
00B53594 B8 A2 B5 00 24 A5 B5 00 F8 A6 B5 00 F0 A8 B5 00 ....$...........
00B535A4 88 05 B6 00 80 07 B6 00 78 09 B6 00 A4 0C B6 00 ........x.......
00B535B4 9C 0E B6 00 94 10 B6 00 0C 14 B6 00 04 16 B6 00 ................
00B535C4 FC 17 B6 00 F4 19 B6 00 EC 1B B6 00 E4 1D B6 00 ................
00B535D4 DC 1F B6 00 ....
对家的:
00B535D8 6C 4E B5 00 64 50 B5 00 5C 52 B5 00 54 54 B5 00 lN..dP..\R..TT..
00B535E8 4C 56 B5 00 44 58 B5 00 3C 5A B5 00 E0 C0 B5 00 LV..DX..<Z......
00B535F8 D8 C2 B5 00 D0 C4 B5 00 C8 C6 B5 00 C0 C8 B5 00 ................
00B53608 D4 21 B6 00 CC 23 B6 00 C4 25 B6 00 BC 27 B6 00 .!...#...%...'..
00B53618 B4 29 B6 00 AC 2B B6 00 24 2F B6 00 1C 31 B6 00 .)...+..$/...1..
00B53628 14 33 B6 00 0C 35 B6 00 04 37 B6 00 FC 38 B6 00 .3...5...7...8..
00B53638 D0 3C B6 00 .<..
右手边玩家的:
00B5363C 64 CD B5 00 5C CF B5 00 48 D2 B5 00 40 D4 B5 00 d...\...H...@...
00B5364C 38 D6 B5 00 30 D8 B5 00 28 DA B5 00 20 DC B5 00 8...0...(... ...
00B5365C 18 DE B5 00 10 E0 B5 00 08 E2 B5 00 00 E4 B5 00 ................
00B5366C C8 3E B6 00 80 F2 B5 00 70 0B B6 00 F0 3F B6 00 .>......p....?..
00B5367C E8 41 B6 00 E0 43 B6 00 D8 45 B6 00 D0 47 B6 00 .A...C...E...G..
00B5368C C8 49 B6 00 C0 4B B6 00 B8 4D B6 00 B0 4F B6 00 .I...K...M...O..
00B5369C 84 53 B6 00 .S..
保存的是玩家手中的牌信息:
00B536A0 F0 E7 B5 00 E8 E9 B5 00 D4 EC B5 00 CC EE B5 00 ................
00B536B0 C4 F0 B5 00 F0 F3 B5 00 E8 F5 B5 00 E0 F7 B5 00 ................
00B536C0 D8 F9 B5 00 D0 FB B5 00 C8 FD B5 00 18 D1 B5 00 ................
00B536D0 7C 55 B6 00 74 57 B6 00 6C 59 B6 00 64 5B B6 00 |U..tW..lY..d[..
00B536E0 5C 5D B6 00 54 5F B6 00 4C 61 B6 00 44 63 B6 00 \]..T_..La..Dc..
00B536F0 3C 65 B6 00 84 69 B6 00 7C 6B B6 00 74 6D B6 00 <e...i..|k..tm..
00B53700 6C 6F B6 00 lo..
扣下去的底牌信息:8张
00B53704 98 4C B5 00 6C CB B5 00 F8 E5 B5 00 00 00 B6 00 .L..l...........
00B53714 98 01 B6 00 90 03 B6 00 64 71 B6 00 5C 73 B6 00 ........dq..\s..
每张牌代表的点数尚没有分析出来。对程序查找字符串参考,就可以找到一段代码,:
0044581B . BA 2C5C4400 mov edx,00445C2C ; ASCII "KINGL"
00445820 . B8 01000000 mov eax,1
00445825 . E8 DEFEFFFF call 00445708
0044582A . BA 345C4400 mov edx,00445C34 ; ASCII "KINGS"
0044582F . B8 02000000 mov eax,2
00445834 . E8 CFFEFFFF call 00445708
00445839 . BA 3C5C4400 mov edx,00445C3C ; ASCII "HACE"
0044583E . B8 03000000 mov eax,3
00445843 . E8 C0FEFFFF call 00445708
00445848 . BA 445C4400 mov edx,00445C44 ; ASCII "HKING"
0044584D . B8 04000000 mov eax,4
00445852 . E8 B1FEFFFF call 00445708
00445857 . BA 4C5C4400 mov edx,00445C4C ; ASCII "HQUEEN"
0044585C . B8 05000000 mov eax,5
00445861 . E8 A2FEFFFF call 00445708
00445866 . BA 545C4400 mov edx,00445C54 ; ASCII "HJACK"
0044586B . B8 06000000 mov eax,6
00445870 . E8 93FEFFFF call 00445708
00445875 . BA 5C5C4400 mov edx,00445C5C ; ASCII "HTEN"
0044587A . B8 07000000 mov eax,7
0044587F . E8 84FEFFFF call 00445708
00445884 . BA 645C4400 mov edx,00445C64 ; ASCII "HNINE"
00445889 . B8 08000000 mov eax,8
0044588E . E8 75FEFFFF call 00445708
00445893 . BA 6C5C4400 mov edx,00445C6C ; ASCII "HEIGHT"
00445898 . B8 09000000 mov eax,9
0044589D . E8 66FEFFFF call 00445708
004458A2 . BA 745C4400 mov edx,00445C74 ; ASCII "HSEVEN"
004458A7 . B8 0A000000 mov eax,0A
004458AC . E8 57FEFFFF call 00445708
004458B1 . BA 7C5C4400 mov edx,00445C7C ; ASCII "HSIX"
004458B6 . B8 0B000000 mov eax,0B
004458BB . E8 48FEFFFF call 00445708
并利用资源查看工具,可以找到一个对应关系(数字为十六进制):
KINGL 1 大王
KINGS 2 小王
红桃
HACE 3
HKING 4
HQUEEN 5
HJACK 6
HTEN 7
HNINE 8
HEIGHT 9
HSEVEN 0A
HSIX 0B
HFIVE 0C
HFOUR 0D
HTHREE 0E
HTWO 0F
梅花
CACE 10
CKING 11
CQUEEN 12
CJACK 13
CTEN 14
CNINE 15
CEIGHT 16
CSEVEN 17
CSIX 18
CFIVE 19
CFOUR 1A
CTHREE 1B
CTWO 1C
方块
DACE 1D
DKING 1E
DQUEEN 1F
DJACK 20
DTEN 21
DNINE 22
DEIGHT 23
DSEVEN 24
DSIX 25
DFIVE 26
DFOUR 27
DTHREE 28
DTWO 29
黑桃
SACE 2A
SKING 2B
SQUEEN 2C
SJACK 2D
STEN 2E
SNINE 2F
SEIGHT 30
SSEVEN 31
SSIX 32
SFIVE 33
SFOUR 34
STHREE 35
STWO 36
继续对“出牌”的过程进行跟踪,发现在每张牌的开始,即4SD处,加上偏移C8H存的为牌的点数,在开始处加上偏移34H 代表这张牌的一个状态,出过之后变为17C,
00B59994 34 53 44 00 00 00 00 00 00 00 00 00 00 00 00 00 4SD.............
00B599A4 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00B599B4 00 01 00 00 50 32 B5 00 70 F0 41 00 94 99 B5 00 ....P2..p.A.....
00B599C4 0A 00 00 00 00 00 00 00 45 00 00 00 5E 00 00 00 ........E...^...
00B599D4 EA 00 00 00 01 01 01 01 00 00 00 00 00 00 00 00 ................
00B599E4 2C 84 B5 00 0F 00 00 80 00 00 F4 FF 00 00 00 00 ,...............
00B599F4 00 00 00 00 F1 FF FF FF 1F 01 01 00 00 00 00 00 ................
00B59A04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00B59A14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00B59A24 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00B59A34 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00B59A44 00 00 00 00 D0 B9 B5 00 34 00 00 00 34 96 B5 00 ........4...4...
00B59A54 38 BA B5 00 00 00 00 00 35 00 00 00 99 00 00 00 8.......5.......
00B59A64 00 01 00 00 0F 00 00 80 00 00 00 00 1A 00 00 00 ................
00B59A74 44 38 41 00 8C 60 41 00 4C BF B5 00 3C 4A B5 00 D8A..`A.L...<J..
00B59A84 80 BF B5 00 22 00 00 00 50 3B 41 00 00 00 00 00 ...."...P;A.....
00B59A94 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00B59AA4 00 00 00 00 1E 00 00 00 01 00 00 00 0D 00 00 00 ................
00B59AB4 4E 4F 54 20 49 4D 50 4F 52 54 41 4E 54 00 00 00 NOT IMPORTANT...
00B59AC4 E2 00 00 00 ....
对其它信息还没有分析清楚。
有了以上这些信息,已经可以写出看牌的外挂了,比较初级,直接粘贴源码:
main.asm:
================================================================
.386
.model flat, stdcall ;32 bit memory model
option casemap :none ;case sensitive
include LookPai.inc
.code
FindPai proc
LOCAL @painum
LOCAL @paiadd
LOCAL @LineFlag
;清空几个文本框
invoke SetDlgItemText,hDlg,IDC_LEFT,NULL
invoke SetDlgItemText,hDlg,IDC_DUIJIA,NULL
invoke SetDlgItemText,hDlg,IDC_RIGHT,NULL
invoke SetDlgItemText,hDlg,IDC_DIPAI,NULL
invoke FindWindow,NULL,addr szGameCaption ;查找目标进程窗口
mov hwndGame,eax
invoke GetWindowThreadProcessId,hwndGame,addr dwProcessID ;取得目标进程ID
.if dwProcessID
invoke OpenProcess,PROCESS_VM_READ,FALSE, dwProcessID
.if eax
mov hGameHandle,eax
invoke ReadProcessMemory,hGameHandle,dwAdd,addr dwMemAdd,4,NULL
.if eax
invoke ReadProcessMemory,hGameHandle,dwMemAdd,addr dwMemAdd,4,NULL
.if eax
mov ebx,dword ptr dwMemAdd
add ebx,324h
invoke ReadProcessMemory,hGameHandle,ebx,addr dwPaiAdd,432,NULL
.if eax
;已得到每张牌的存放地址,循环处理这108个地址
;在每个地址加上偏移C8H,即可以得到每张牌的点数
mov @paiadd,offset dwPaiAdd
;处理左手玩家的牌
mov @painum,1
mov @LineFlag,0
.while @painum<26
mov ebx, @paiadd
mov ebx,[ebx]
add ebx,34h
invoke ReadProcessMemory,hGameHandle,ebx,addr dwState,4,NULL
.if dwState==017ch
jmp @F
.endif
mov ebx, @paiadd
mov ebx,[ebx]
add ebx,0C8h
invoke ReadProcessMemory,hGameHandle,ebx,addr dwPaiDian,1,NULL
movzx eax,dwPaiDian
dec eax
mov esi,4
imul eax,esi
mov ebx,offset szPai
add ebx,eax
;在文本框中显示,取出原有内容,加上新显示的内容
invoke GetDlgItemText,hDlg,IDC_LEFT,addr szTemp,512
inc @LineFlag
.if @LineFlag==6
mov @LineFlag,0
invoke lstrcat,addr szTemp,addr szCrLf
.else
invoke lstrcat,addr szTemp,addr szBlank
.endif
invoke lstrcat,addr szTemp,[ebx]
invoke SetDlgItemText,hDlg,IDC_LEFT,addr szTemp
@@:
add @paiadd,4
inc @painum
.endw
;处理对家的牌
mov @painum,1
mov @LineFlag,0
.while @painum<26
mov ebx, @paiadd
mov ebx,[ebx]
add ebx,34h
invoke ReadProcessMemory,hGameHandle,ebx,addr dwState,4,NULL
.if dwState==017ch
jmp @F
.endif
mov ebx, @paiadd
mov ebx,[ebx]
add ebx,0C8h
invoke ReadProcessMemory,hGameHandle,ebx,addr dwPaiDian,1,NULL
movzx eax,dwPaiDian
dec eax
mov esi,4
imul eax,esi
mov ebx,offset szPai
add ebx,eax
invoke GetDlgItemText,hDlg,IDC_DUIJIA,addr szTemp,512
inc @LineFlag
.if @LineFlag==6
mov @LineFlag,0
invoke lstrcat,addr szTemp,addr szCrLf
.else
invoke lstrcat,addr szTemp,addr szBlank
.endif
invoke lstrcat,addr szTemp,[ebx]
invoke SetDlgItemText,hDlg,IDC_DUIJIA,addr szTemp
;invoke MessageBox,NULL,[ebx],addr szCaption,MB_OK
@@:
add @paiadd,4
inc @painum
.endw
;处理右手玩家的牌
mov @LineFlag,0
mov @painum,1
.while @painum<26
mov ebx, @paiadd
mov ebx,[ebx]
add ebx,34h
invoke ReadProcessMemory,hGameHandle,ebx,addr dwState,4,NULL
.if dwState==017ch
jmp @F
.endif
mov ebx, @paiadd
mov ebx,[ebx]
add ebx,0C8h
invoke ReadProcessMemory,hGameHandle,ebx,addr dwPaiDian,1,NULL
movzx eax,dwPaiDian
dec eax
mov esi,4
imul eax,esi
mov ebx,offset szPai
add ebx,eax
invoke GetDlgItemText,hDlg,IDC_RIGHT,addr szTemp,512
inc @LineFlag
.if @LineFlag==6
mov @LineFlag,0
invoke lstrcat,addr szTemp,addr szCrLf
.else
invoke lstrcat,addr szTemp,addr szBlank
.endif
invoke lstrcat,addr szTemp,[ebx]
invoke SetDlgItemText,hDlg,IDC_RIGHT,addr szTemp
@@:
add @paiadd,4
inc @painum
.endw
;跳过自己手中的牌,累加相关的变量
mov @painum,1
.while @painum<26
add @paiadd,4
inc @painum
.endw
;处理扣下去的底牌
mov @painum,1
mov @LineFlag,0
.while @painum<9
mov ebx, @paiadd
mov ebx,[ebx]
add ebx,0C8h
invoke ReadProcessMemory,hGameHandle,ebx,addr dwPaiDian,1,NULL
movzx eax,dwPaiDian
dec eax
mov esi,4
imul eax,esi
mov ebx,offset szPai
add ebx,eax
invoke GetDlgItemText,hDlg,IDC_DIPAI,addr szTemp,512
inc @LineFlag
.if @LineFlag==6
mov @LineFlag,0
invoke lstrcat,addr szTemp,addr szCrLf
.else
invoke lstrcat,addr szTemp,addr szBlank
.endif
invoke lstrcat,addr szTemp,[ebx]
invoke SetDlgItemText,hDlg,IDC_DIPAI,addr szTemp
add @paiadd,4
inc @painum
.endw
.endif ;//
.endif ;//
.endif ;//
.endif
.else
;游戏没有运行
.endif
ret
FindPai endp
start:
invoke GetModuleHandle,NULL
mov hInstance,eax
invoke InitCommonControls
invoke DialogBoxParam,hInstance,IDD_DIALOG1,NULL,addr DlgProc,NULL
invoke ExitProcess,0
;########################################################################
DlgProc proc hWin:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
mov eax,uMsg
.if eax==WM_INITDIALOG
;可以设置图标
mov eax,hWin
mov hDlg,eax
invoke LoadIcon,hInstance,ICO_MAIN
invoke SendMessage,hDlg,WM_SETICON,ICON_BIG,eax
invoke SetWindowPos,hDlg,HWND_TOPMOST,0,0,0,0,SWP_NOMOVE or SWP_NOSIZE
.elseif eax==WM_COMMAND
mov eax,wParam
.if eax==IDC_LOOK
;点了看牌按钮
invoke FindPai
.endif
.elseif eax==WM_CLOSE
invoke EndDialog,hWin,0
.else
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
DlgProc endp
end start
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)