看了第六期精华中的 xdkui <<蜘蛛纸牌分析与简单DIY>>
及hearmecryle 写的<<蜘蛛纸牌底牌数据结构图及辅助代码利用>>
(链 接: http://bbs.pediy.com/showthread.php?t=129478)
对蜘蛛纸牌进行了一次DIY实践。
通过分析,程序一开始就保存了一个内存的基址:
0100FE5F . B9 08200101 mov ecx,01012008 ; 设置基址的地方
0100FE64 . E8 6D58FFFF call 010056D6
0100FE69 . 68 7AFE0001 push 0100FE7A
0100FE6E . E8 9B94FFFF call 0100930E
0100FE73 . 59 pop ecx
0100FE74 . C3 retn
01012008是一个基址
里面保存的是游戏窗口的句柄,很多地方要使用的。
+4的地方保存一个地址,指向难度值。
+8的地方保存一个地址,指向一个有10个元素的数组,每个元素代表每一列的一个双向链表的指针地址,指针中保存的是链表的第一张牌的位置。
+14h 与+18h这两个地方保存的是两列牌之间的空隙的宽度值,在sendmessage时,计算坐标要用到的。
+58h 的地方保存发牌的次数
[01012008+4]+0Ch 指向每张牌的信息
以一次游戏过程中的第三列数据为例:
每张牌的信息:
利用本程序消息循环的特征指令,可以在程序中定位到处理菜单消息的地方:
movzx eax,word ptr ss:[ebp+10] ; Case 111 (WM_COMMAND) of switch 01007BF5
利用reshack增加一个菜单:
101 MENU
LANGUAGE LANG_CHINESE, 0x2
{
POPUP "游戏(&G)"
{
MENUITEM "开局(&N)\tF2", 40005
MENUITEM "重新开局(&R)", 40006, GRAYED
MENUITEM SEPARATOR
MENUITEM "撤销(&U)\tCtrl+Z", 40010, GRAYED
MENUITEM "新一轮发牌(&D)\tD", 40007, GRAYED
MENUITEM "显示可行的操作(&M)\tM", 40013, GRAYED
MENUITEM SEPARATOR
MENUITEM "难易级别(&I)...\tF3", 40017
MENUITEM "战况(&T)...\tF4", 40014
MENUITEM "选项(&P)...\tF5", 40015
MENUITEM SEPARATOR
MENUITEM "保存本次游戏(&S)\tCtrl+S", 40011, GRAYED
MENUITEM "打开上次保存的游戏(&O)\tCtrl+O", 40012
MENUITEM SEPARATOR
MENUITEM "退出(&X)", 40004
}
MENUITEM "发牌(&D)", 40016, GRAYED
POPUP "帮助(&H)"
{
MENUITEM "目录(&C)\tF1", 40003
MENUITEM SEPARATOR
MENUITEM "关于蜘蛛(&A)...", 40002
}
MENUITEM "秒杀", 40020
}
原程序中移动牌的地方:
01004C98 |> \FF75 FC PUSH [LOCAL.1] ;目的列的第几张
01004C9B |. 8B4E 08 MOV ECX,DWORD PTR DS:[ESI+8];指向listarray
01004C9E |. FF75 10 PUSH [ARG.3] ;目的列
01004CA1 |. FF75 0C PUSH [ARG.2]
01004CA4 |. 57 PUSH EDI ;
01004CA5 |. E8 50320000 CALL 01007EFA ; 移动牌的函数
考虑到代码要多次修改,并且增加的比较多,因此采用了增加一个DLL导入函数的方法对程序进行补丁,利用loadPE增加,并记下其RVA地址。
原来的程序,主窗口菜单消息过程,判断是什么菜单
01006C3F |. 0FB745 10 movzx eax,word ptr ss:[ebp+10]
01006C43 |. 05 BE63FFFF add eax,FFFF63BE ; Switch (cases 9C42..9C51)
01006C48 |. 83F8 0F cmp eax,0F
对原程序进行修改:
01006C43 /E9 08A00000 jmp 01010C50
并在后面的空白处写入新加菜单的处理代码:
01010C50 60 pushad
01010C51 3D 549C0000 cmp eax,9C54
01010C56 75 06 jnz short 01010C5E
01010C58 FF15 1D700801 call dword ptr ds:[108701D] ; 调用新加的功能函数
01010C5E 61 popad
01010C5F 05 BE63FFFF add eax,FFFF63BE
01010C64 E9 DF5FFFFF jmp 01006C48
新加函数的主要功能:
1.调用发牌函数,把所有的牌全部发完;
2.把后面两列的牌调整到前面8列,使每列均为13张;
3.修改每列的双向链表中的牌的序号;
4.修改保存每张牌详细的数组,使每列牌按K--A的顺序排列;
5.修改每列没有翻开的张数;
6.发送鼠标消息,完成自动移牌。
完工的蜘蛛纸牌如下:
dll代码如下:
.386
.model flat, stdcall ; 32 bit memory model
option casemap :none ; case sensitive
include windows.inc
include masm32.inc
include user32.inc
includelib masm32.lib
includelib user32.lib
DllEntry proto hInstance:DWORD, dwFunction:DWORD, lpReserve:DWORD
crackspider proto
.data?
dwEasyLevel dd ? ;保存难度等级,初级为1,中级为2,高级为4,数值正好为牌的花色数量
dwListArray dd ? ;保存每列链表的指针数组的起始地址
dwCardArray dd ? ;保存牌的信息数组的起始地址
dwCardNumArray dd ? ;保存每列牌的张数的数组地址
dwUnShowNumArray dd ? ;保存每列没有翻开的张数的数组地址
;//////////////////////////////////////////////////////////////////
.const
lpMoveCard equ 01007EFAh ;移动牌的函数入口
lpSendCard equ 010069B2h ;发牌的函数入口
dwBaseMemAdd equ 01012008h ;内存基址,并在这里保存了主窗口的句柄
;//////////////////////////////////////////////////////////////////
.code
align 4
DllEntry proc hInstance:DWORD, dwFunction:DWORD, lpReserve:DWORD
xor eax, eax
inc eax
ret
DllEntry endp
;//////////////////////////////////////////////////////////////////
AdjustTen2Eight proc dwOut,dwIn1,dwIn2,dwIn3,dwIn4 ;把dwOut列分到后面的4个列中,前两个分两个元素,后两个分三个元素
mov ecx,dwListArray
mov ebx,lpMoveCard
push 0ah ;目的列的现有张数
push dwIn1 ;目的列,列是从0计算的
push 8 ;源列的第几张牌开始移动,从0计算的
push dwOut ;源列,列是从0计算的
call ebx
mov ecx,dwListArray
mov ebx,lpMoveCard
push 0ah ;目的列的现有张数
push dwIn2 ;目的列,列是从0计算的
push 6 ;源列的第几张牌开始移动,从0计算的
push dwOut ;源列,列是从0计算的
call ebx
mov ecx,dwListArray
mov ebx,lpMoveCard
push 9 ;目的列的现有张数
push dwIn3 ;目的列,列是从0计算的
push 3 ;源列的第几张牌开始移动,从0计算的
push dwOut ;源列,列是从0计算的
call ebx
mov ecx,dwListArray
mov ebx,lpMoveCard
push 9 ;目的列的现有张数
push dwIn4 ;目的列,列是从0计算的
push 0 ;源列的第几张牌开始移动,从0计算的
push dwOut ;源列,列是从0计算的
call ebx
ret
AdjustTen2Eight endp
MoveCardCol proc dwCol
LOCAL @hWnd
LOCAL @width
;取得游戏的窗口句柄,其值是保存在dwBaseMemAdd中
mov eax,dword ptr ds:[dwBaseMemAdd]
mov @hWnd,eax
;取得列与列之间的间隙宽度
mov eax,dword ptr ds:[dwBaseMemAdd+14h]
mov @width,eax
mov eax,47h
add eax,@width
mov ecx,dwCol
imul eax,ecx
add eax,@width
add eax,23h
mov ebx,1bh
shl ebx,16
add eax,ebx
invoke PostMessage,@hWnd,WM_LBUTTONDOWN,1,eax
mov eax,47h
add eax,@width
mov ecx,8
imul ecx
add eax,@width
mov ecx,eax
mov eax,1bh
shl eax,16
add eax,ecx
push eax
invoke PostMessage,@hWnd,WM_MOUSEMOVE,1,eax
pop eax
invoke PostMessage,@hWnd,WM_LBUTTONUP,0,eax
ret
MoveCardCol endp
crackspider proc
LOCAL @index
LOCAL @dwNum
LOCAL @dwTemp
LOCAL @dwCardValue
;取得每列牌的链表指针数组地址
mov edx,dword ptr ds:[dwBaseMemAdd+8]
mov dwListArray,edx
;取得每列没有翻开的牌的张数的数组地址
add edx,28h
mov dwCardNumArray,edx
add edx,28h
mov dwUnShowNumArray,edx
;取得保存张牌信息的数组地址及难度级别
mov edx,dword ptr ds:[dwBaseMemAdd+4]
mov esi,[edx]
mov dwEasyLevel,esi
add edx,0Ch
mov esi,[edx]
mov dwCardArray,esi
;;;;;;;;;;;发完所有的牌;;;;;;
mov eax,dword ptr ds:[dwBaseMemAdd+58h] ;该地址保存已发牌的次数
mov @index,eax
.while @index<5
mov ecx,dwBaseMemAdd ;过程需要此参数
mov eax,lpSendCard ;发牌的过程入口
call eax
inc @index
.endw
;;;;;;;;;;;;;;;;;;;;;
.if eax
;发牌出错时,中止后面的工作
ret
.endif
;;设置每张牌为翻开的状态
mov @index,0
mov esi,dwCardArray
.while @index<68h
mov eax,@index
mov ebx,0Ch
imul eax,ebx
mov dword ptr ds:[esi+eax+8],1
inc @index
.endw
;;设置每列没有翻开的牌的张数为0
;;游戏中是以这个没有翻开的张数来设置每张牌露在外面的高度
mov @index,0
mov esi,dwUnShowNumArray
.while @index<0Ah
mov dword ptr ds:[esi],0
add esi,4
inc @index
.endw
;;;只保留8列在窗口中,
;把最后两列分到前面几列中,使每列有13张牌
invoke AdjustTen2Eight,8,0,1,4,5
invoke AdjustTen2Eight,9,2,3,6,7
;;;修改每一列牌的顺序
mov @index,0
mov @dwNum,0
.while @index<8
mov esi,dwListArray
mov eax,@index
mov ebx,4
imul eax,ebx
mov esi,dword ptr ds:[esi+eax]
mov eax,dword ptr ds:[esi] ;eax指向第一张牌了
.while eax
mov ebx,@dwNum
mov dword ptr ds:[eax],ebx
mov eax,dword ptr ds:[eax+8]
inc @dwNum
.endw
inc @index
.endw
;;;对牌的数组进行重写,从K到A,在内存中重写
push dwEasyLevel
pop @index ;牌的花色
xor edx,edx
mov eax,104
mov ecx,13
div ecx
xor edx,edx
mov ecx,@index
div ecx
mov edi,eax
mov esi,dwCardArray
.while @index>0
mov edx,4
sub edx,@index ;牌的花色
mov @dwNum,edi
.while @dwNum>0
mov @dwCardValue,0Ch ;牌的点数
mov @dwTemp,0
.while @dwTemp<13
mov dword ptr ds:[esi],edx ;牌的花色
mov ecx,@dwCardValue
mov dword ptr ds:[esi+4],ecx ;牌的点数
add esi,12
inc @dwTemp
dec @dwCardValue
.endw
dec @dwNum
.endw
dec @index
.endw
;取得游戏的窗口句柄,其值是保存在dwBaseMemAdd中
mov eax,dword ptr ds:[dwBaseMemAdd]
;重绘窗口内容
invoke InvalidateRect,eax,NULL,TRUE
;移动第1-8列到第9列
mov @dwNum,0
.while @dwNum<8
invoke MoveCardCol,@dwNum
inc @dwNum
.endw
ret
crackspider endp
;///////////////////////////////////////////////////////////////////
end DllEntry
补丁及DLL如下:
patch_dll.rar
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课