能力值:
(RANK:1130 )
|
-
-
3 楼
riijj放了一个DIY的,我也放个DIY的,这篇文章是NE365电子书里的,外面好像没见过
对winxp sp1下的扫雷的pediy,伪造鼠标消息,自动扫雷的实现
【破解作者】 海风月影[NE365][DFCG]
【作者邮箱】 zjhangtian@sohu.com
【使用工具】 od 1.10d ,Peid
【破解平台】 WinXP(win9x下不能用)
【软件名称】 winmine.exe
【软件简介】 对winxp sp1下的扫雷的pediy,伪造消息,自动扫雷的实现
【破解声明】 我是一只小菜鸟,偶得一点心得,愿与大家分享:)
--------------------------------------------------------------------------------
【破解内容】
一、分析程序
以winxp sp1下的扫雷为例(windows版本不同,扫雷不太一样的),用OllyDbg载入winmine.exe(windows的程序一般是不加壳的)。第一步我们要找到相关处理的代码,即单击鼠标后判断是否有雷,有雷就炸,没有雷就翻开。找代码各有各的高招,我说说我的笨办法,但一定能找到。
扫雷是用visual c++ 写的,visual c++写的程序有个WinMain函数,函数原型如下
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
在od里的代码为如下单步跟踪不久就会出现如下代码:
01003F89 > \50 push eax ; /Arg4
01003F8A . 56 push esi ; |Arg3
01003F8B . 53 push ebx ; |Arg2
01003F8C . 53 push ebx ; |/0
01003F8D . FFD7 call edi ; |\GetModuleHandleA
01003F8F . 50 push eax ; |Arg1
01003F90 . E8 5BE2FFFF call winmine.010021F0 ; \winmine.010021F0
从01003F90跟进,运行到RegisterClass这里
0100228B |. 50 push eax ; /pWndClass
0100228C |. 897D D4 mov [local.11],edi ; |
0100228F |. 8975 D8 mov [local.10],esi ; |
01002292 |. FF15 CC100001 call dword ptr ds:[<&USER32.Reg>; \RegisterClassW
这里是注册窗口类,熟悉c++编程的都知道,参数pWndClass指向窗口类信息,其中第二项是lpfnWndProc这是我们所需要的,是窗口回调函数的地址,是处理消息用的,我们看堆栈
0006FEC0 0006FED0 \pWndClass = 0006FED0
0006FEC4 77E5AD86 kernel32.GetModuleHandleA
0006FEC8 00091F01
0006FECC 00000000
地址是0006FED0,因此[0006FED4]里就是窗口回调函数的地址了,内存如下
0006FED0 00 00 00 00 C9 1B 00 01 00 00 00 00 00 00 00 00 ....?.........
0006FEE0 00 00 00 01 B6 02 A3 00 11 00 01 00 14 00 90 01 ...??...?
因此,对1001BC9下断点,运行,立刻就断下来了,代码如下
01001BC9 . 55 push ebp
01001BCA . 8BEC mov ebp,esp
01001BCC . 83EC 40 sub esp,40
01001BCF . 8B55 0C mov edx,dword ptr ss:[ebp+C]
★ edx 为消息参数
01001BD2 . 8B4D 14 mov ecx,dword ptr ss:[ebp+14]
★ ecx 为其它参数,当 edx 为鼠标方面的消息参数时,ecx 为坐标
01001BD5 . 53 push ebx
01001BD6 . 56 push esi
01001BD7 . 33DB xor ebx,ebx
01001BD9 . 57 push edi
01001BDA . BE 00020000 mov esi,200
01001BDF . 43 inc ebx
01001BE0 . 33FF xor edi,edi
01001BE2 . 3BD6 cmp edx,esi
窗口回调函数的原型是 LRESULT WINAPI WndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
单步到01001BCC此时看堆栈,关联到ebp
EBP ==> >/0006F7F0 原来的ebp
EBP+4 >|77D13A50 返回到 USER32.77D13A50
EBP+8 >|002502D6 HWND hWnd
EBP+C >|00000024 UINT msg
EBP+10 >|00000000 WPARAM wParam
EBP+14 >|0006F8F0 LPARAM lParam
根据窗口回调函数的原型很清楚能看到,[ebp+C]里是msg,也就是消息参数,传给了 edx,下面开始对不同的msg分别做不同的处理,在window.h头文件中定义了
201为WM_LBUTTONDOWN
202为WM_LBUTTONUP
204为WM_RBUTTONDOWN
205为WM_RBUTTONUP
207为WM_MBUTTONDOWN
208为WM_MBUTTONUP
可以对01001BD2下条件断点,命令为bp 1001bd2 ,edx==201,然后运行,出现了扫雷窗口,点击左键,促发断点,一步步跟踪到
01001FA6 > \393D 48510001 cmp dword ptr ds:[1005148],edi ; Case 201 (WM_LBUTTONDOWN) of switch 01001F5F
01001FAC .^ 75 D6 jnz short winmine.01001F84
01001FAE . FF75 14 push dword ptr ss:[ebp+14] ; /Arg1
01001FB1 . E8 56F4FFFF call winmine.0100140C ; \winmine.0100140C
01001FB6 . 85C0 test eax,eax
01001FB8 .^ 0F85 A0FCFFFF jnz winmine.01001C5E
01001FBE . 841D 00500001 test byte ptr ds:[1005000],bl
01001FC4 . 0F84 DF010000 je winmine.010021A9
01001FCA . 8B45 10 mov eax,dword ptr ss:[ebp+10]
01001FCD . 24 06 and al,6
01001FCF . F6D8 neg al
01001FD1 . 1BC0 sbb eax,eax
01001FD3 . F7D8 neg eax
01001FD5 . A3 44510001 mov dword ptr ds:[1005144],eax
01001FDA . E9 80000000 jmp winmine.0100205F ; 跳到到下一段
。。。
0100205F > \FF75 08 push dword ptr ss:[ebp+8] ; /hWnd
01002062 . FF15 E4100001 call dword ptr ds:[<&USER32.SetCapture>] ; \SetCapture
01002068 . 830D 18510001 FF or dword ptr ds:[1005118],FFFFFFFF
0100206F . 830D 1C510001 FF or dword ptr ds:[100511C],FFFFFFFF
01002076 . 53 push ebx
01002077 . 891D 40510001 mov dword ptr ds:[1005140],ebx
0100207D . E8 91080000 call winmine.01002913
01002082 . 8B4D 14 mov ecx,dword ptr ss:[ebp+14]
01002085 > 393D 40510001 cmp dword ptr ds:[1005140],edi
0100208B . 74 34 je short winmine.010020C1
0100208D . 841D 00500001 test byte ptr ds:[1005000],bl
01002093 .^ 0F84 54FFFFFF je winmine.01001FED
01002099 . 8B45 14 mov eax,dword ptr ss:[ebp+14] ; 这里开始处理坐标,
★下面几行代码我们后面处理还要用到
0100209C . C1E8 10 shr eax,10
0100209F . 83E8 27 sub eax,27
010020A2 . C1F8 04 sar eax,4
010020A5 . 50 push eax ; /第几行
010020A6 . 0FB745 14 movzx eax,word ptr ss:[ebp+14] ; |
010020AA . 83C0 04 add eax,4 ; |
010020AD . C1F8 04 sar eax,4 ; |
010020B0 . 50 push eax ; |第几列
010020B1 > E8 1E110000 call winmine.010031D4 ; \处理过程
跟进上面的处理过程
010031D4 /$ 55 push ebp
010031D5 |. 8BEC mov ebp,esp
010031D7 |. 83EC 20 sub esp,20
010031DA |. 8B55 08 mov edx,[arg.1] ; edx为第几列
010031DD |. A1 18510001 mov eax,dword ptr ds:[1005118]
010031E2 |. 3BD0 cmp edx,eax
010031E4 |. 8B0D 1C510001 mov ecx,dword ptr ds:[100511C]
010031EA |. 57 push edi
010031EB |. 8B7D 0C mov edi,[arg.2] ; edi为第几行
中间大段代码省略。。。
01003397 |> \85DB test ebx,ebx ; 跟到这里
01003399 |. 7E 34 jle short winmine.010033CF ; 如果ebx和esi不是参数则判断edx和edi是不是参数
0100339B |. 85F6 test esi,esi
0100339D |. 7E 30 jle short winmine.010033CF
0100339F |. 3B1D 34530001 cmp ebx,dword ptr ds:[1005334] ; 行数是否越界
010033A5 |. 7F 28 jg short winmine.010033CF
010033A7 |. 3B35 38530001 cmp esi,dword ptr ds:[1005338] ; 列数是否越界
010033AD |. 7F 20 jg short winmine.010033CF
010033AF |. 8BC6 mov eax,esi
010033B1 |. C1E0 05 shl eax,5 ; 都没有就判断是否点击过
010033B4 |. F68418 40530001 >test byte ptr ds:[eax+ebx+1005340],40 ; 以1005340为基址的二维数组
010033BC |. 75 11 jnz short winmine.010033CF
010033BE |. 56 push esi
010033BF |. 53 push ebx
010033C0 |. E8 DBFDFFFF call winmine.010031A0 ; 把没有点击的变成点击的
010033C5 |. 56 push esi
010033C6 |. 53 push ebx
010033C7 |. E8 7AF2FFFF call winmine.01002646
010033CC |. 8B55 08 mov edx,[arg.1]
010033CF |> 85D2 test edx,edx ; 第几列
010033D1 |. 7E 42 jle short winmine.01003415
010033D3 |. 85FF test edi,edi ; 第几行
010033D5 |. 7E 3E jle short winmine.01003415
010033D7 |. 3B15 34530001 cmp edx,dword ptr ds:[1005334] ; 行数是否越界
010033DD |. 7F 36 jg short winmine.01003415
010033DF |. 3B3D 38530001 cmp edi,dword ptr ds:[1005338] ; 列数是否越界
010033E5 |. 7F 2E jg short winmine.01003415
010033E7 |. C1E7 05 shl edi,5 ; 都没有就判断是否点击过
010033EA |. 8A8417 40530001 mov al,byte ptr ds:[edi+edx+1005340] ; 以1005340为基址的二维数组
010033F1 |. A8 40 test al,40 ; 是否点击过?
010033F3 |. 75 20 jnz short winmine.01003415 ; 不是就出去了
010033F5 |. 24 1F and al,1F
010033F7 |. 3C 0E cmp al,0E
010033F9 |. 74 1A je short winmine.01003415
010033FB |. 8B3D 1C510001 mov edi,dword ptr ds:[100511C]
01003401 |. 8B35 18510001 mov esi,dword ptr ds:[1005118]
01003407 |. 57 push edi
01003408 |. 56 push esi
01003409 |. E8 5DFDFFFF call winmine.0100316B ; 把没有点击的变成点击的
0100340E |. 57 push edi
0100340F |. 56 push esi
01003410 |. E8 31F2FFFF call winmine.01002646 ; 显示出来
01003415 |> 5E pop esi ; 这里就出去了
这个是左键单击按下的处理过程,再分析一下左键弹起、右键单击的过程,多试验几次会知道大概规则,以1005340为起始地址有个二维数组表,里面是是否有雷的信息,以一个字节为一格,规则如下
xF 表示还没点击
xE 、xD 表示标上了雷的标志和打了问号
x0 表示点击过了
4x 表示点击过了,而且没有雷,4x中的x表示周围几个雷
8x 表示有雷
0x 表示还没有点击过,且没有雷
大概的规则了解了,下面就可以做些手脚了:-)
二、如何作弊
分析好了,下面看看如何作弊,当单击到雷的时候,就暴了,如果想不死,那就在单击的时候,先判断一下是否有雷,没有雷就单击一下,有雷就不单击,修改单击的处理过程不太现实,有两处,要改就要一起改,所以想到在窗口回调函数一开始,先让它判断一下单击的位置是否有雷,有雷就不单击(改为右击)。这样左键点击就永远死不了了,但是一个一个点也太麻烦了,也太累了,这还不够,如果让电脑自动帮我们点多好啊,一个一个顺着点下去,这样就立即扫完雷了。说做就做,开始动手
三、分析文件
这步很重要,你要在文件里添加代码,就必须先确定添加在哪,文件剩余空间够不够等等,用nbw友情提供的pe剩余空间查看器分析一下
名称 RVA OA 尺寸D 可写否
.text 00004a56 00003e56 426 否 选这个节
.data 00005b98 00004b98 -2456 可
.rsrc 0001f160 0001d360 160 否
有效剩余空间(字节D)为: 586
我们要写的代码也就100多字节吧,所以我们就选 .text 00004a56 00003e56 426 否 这个节,要注意,不能写入,没关系,用LoadPE改一下这个节的属性就行了,加个可写属性就行了
实在不放心就添加一个节,大小是1000,名字随便起,不过我喜欢完美,尽量不增加文件长度(想起了经典的cih病毒^_^),nbw的工具就可以增加节,要点上加载这个节,还可以用topo等工具,这里就不介绍了。但要注意一点,这个是windows的程序,就这样加节是会出错的,因为里面有BoundImport数据,用loadpe去除这些数据就可以加了
四、编写作弊代码
添加作弊代码的方法很多,nbw是先写汇编代码然后编译,再写进去,这个方法我不太会,所以不用。也可以写个dll让winmine调用,这个方法比较烦,这样做还不如写个内存作弊器,直接把雷找出来。我的做法是直接在ollydbg里写代码,虽然比较累,但是可以随时调试。(应该这么说,其他方法我都不会,只会这种,丢脸了。。。)
通过上面对WndProc回调函数的分析我们可以在下面这个点修改
01001BC9 . 55 push ebp
01001BCA . 8BEC mov ebp,esp
01001BCC . 83EC 40 sub esp,40
01001BCF . 8B55 0C mov edx,dword ptr ss:[ebp+C]
01001BD2 > . 8B4D 14 mov ecx,dword ptr ss:[ebp+14]
01001BD5 . 53 push ebx
01001BD6 . 56 push esi
01001BD7 . 33DB xor ebx,ebx
01001BD9 . 57 push edi
★把代码插在这两句中间
01001BDA . BE 00020000 mov esi,200
01001BDF . 43 inc ebx
01001BE0 . 33FF xor edi,edi
01001BE2 . 3BD6 cmp edx,esi
为什么选则这里呢?因为这里刚保存了 ebx,esi,edi 三个寄存器,还没有开始用这时我们来用,就免去了保护寄存器的工作,这样我们就有4个寄存器可以使用了(还有一个是eax,后面代码可以发现eax在这里的值没有作用),从1001BD5开始5个字节改成jmp xxxxxxxx,其中xxxxxxxx就是我们写的代码的地址。我这里是跳到了这个节的尾部104A56,在104A56开始写代码
01004A56 > \8B55 0C mov edx,dword ptr ss:[ebp+C]
01004A59 . 8B4D 14 mov ecx,dword ptr ss:[ebp+14] ; 这两行重写一遍也无所谓,跳到下一行也没关系
01004A5C . 53 push ebx
01004A5D . 56 push esi
01004A5E . 57 push edi ; 这三行保存寄存器
01004A5F . EB 07 jmp short winmine.01004A68 ; 跳到我们的作弊代码
01004A61 > 33DB xor ebx,ebx ; 这里是出口,跳回去,清空ebx(原来的代码)
01004A63 .^ E9 72D1FFFF jmp winmine.01001BDA ; 跳回去
这几行写的是基本的一些代码,保存寄存器,恢复寄存器的值,为什么把出口写在这而不写在代码最后,是因为这样有个明确的出口,否则出口不明确代码不好编写(直接写代码的坏处,用汇编写然后编译就没有这个问题),下面开始判断
01004A68 > \81FA 01020000 cmp edx,201 ; 左键按下?
01004A6E . 74 12 je short winmine.01004A82 ; 是就继续
01004A70 . 81FA 02020000 cmp edx,202 ; 左键弹起?
01004A76 .^ 75 E9 jnz short winmine.01004A61 ; 也不是就跳回去,不是我们要处理的消息
01004A78 . 33FF xor edi,edi
01004A7A . 393D 40510001 cmp dword ptr ds:[1005140],edi
01004A80 .^ 74 DF je short winmine.01004A61 ;
★这三句是判断左键弹起的,程序里是这样判断的,保险起见,我们也这样判断
01004A82 > 8B45 14 mov eax,dword ptr ss:[ebp+14] ; 这里开始是抄写源程序分解坐标的方法
01004A85 . C1E8 10 shr eax,10
01004A88 . 83E8 27 sub eax,27
01004A8B . C1F8 04 sar eax,4 ; 用了栈传递参数
01004A8E . 50 push eax ; 行
01004A8F . 0FB745 14 movzx eax,word ptr ss:[ebp+14]
01004A93 . 83C0 04 add eax,4
01004A96 . C1F8 04 sar eax,4
01004A99 . 50 push eax ; 列
01004A9A . 5E pop esi ; 取出列数
01004A9B . 5B pop ebx ; 取出行数
01004A9C . 85DB test ebx,ebx
01004A9E .^ 7E C1 jle short winmine.01004A61
01004AA0 . 85F6 test esi,esi
01004AA2 .^ 7E BD jle short winmine.01004A61
01004AA4 . 3B1D 38530001 cmp ebx,dword ptr ds:[1005338]
01004AAA .^ 7F B5 jg short winmine.01004A61
01004AAC . 3B35 34530001 cmp esi,dword ptr ds:[1005334]
01004AB2 .^ 7F AD jg short winmine.01004A61
01004AB4 C1E3 05 shl ebx,5
01004AB7 > 8A8433 40530001 mov al,byte ptr ds:[ebx+esi+1005340] ; 到这里,判断方法和程序是一样的
01004ABE . 3C 80 cmp al,80 ; 判断是否有雷
★不管雷是什么状态,前面是8就有雷
01004AC0 .^ 72 9F jb short winmine.01004A61 ; 小于即没有雷
★这里不管是什么状态,只要没有雷就行
01004AC2 . BA 04020000 mov edx,204 ; 有雷的情况把消息改成右击
01004AC7 . C745 10 02000000 mov dword ptr ss:[ebp+10],2 ; 判断右击的标志
★(右击时wParam=2,程序里是要判断的,所以要改一下标志)
01004ACE .^ EB 91 jmp short winmine.01004A61 ; 跳回去
这样修改的程序就永远死不了了,左键单击太麻烦了,我们还要继续改一下,让电脑自动帮我们点击,因此,在这行修改跳转
01004AB4 C1E3 05 shl ebx,5
改成跳转
01004AB4 EB 2D jmp short winmine.01004AE3
01004AB6 90 nop
先把我写的代码贴上,然后解释
01004AD0 . 00 db 00 ; 这里是是否为我们伪造消息的状态,1为是,0为否
01004AD1 00 db 00
01004AD2 . 00000000 dd 00000000
01004AD6 . 00000000 dd 00000000 ; 当前的列数
01004ADA . 00000000 dd 00000000 ; 当前的行数
01004ADE > C1E3 05 shl ebx,5
01004AE1 .^ EB D4 jmp short winmine.01004AB7
01004AE3 > 803D D04A0001 00 cmp byte ptr ds:[1004AD0],0 ; 跳到这里,先判断是否为我们伪造消息状态
01004AEA .^ 75 F2 jnz short winmine.01004ADE ; 是就跳走,不处理下面的
01004AEC . C605 D04A0001 01 mov byte ptr ds:[1004AD0],1 ; 标志为伪造消息状态
01004AF3 . 3E:8B5D 08 mov ebx,dword ptr ds:[ebp+8]
01004AF7 . 891D D24A0001 mov dword ptr ds:[1004AD2],ebx
01004AFD >/ 8B35 D64A0001 mov esi,dword ptr ds:[1004AD6] ; 取出列数
01004B03 .| 46 inc esi ; +1
01004B04 .| 8935 D64A0001 mov dword ptr ds:[1004AD6],esi ; 写回去
01004B0A .| 3B35 34530001 cmp esi,dword ptr ds:[1005334] ; 比较是否出界
01004B10 .| 7F 68 jg short winmine.01004B7A ; 出界代表结束了,跳走
01004B12 .| C1E6 04 shl esi,4 ; 计算坐标的逆运算
01004B15 .| 83EE 03 sub esi,3
01004B18 >|/8B1D DA4A0001 mov ebx,dword ptr ds:[1004ADA] ; 取出行数
01004B1E .||43 inc ebx ; +1
01004B1F .||891D DA4A0001 mov dword ptr ds:[1004ADA],ebx ; 写回去
01004B25 .||3B1D 38530001 cmp ebx,dword ptr ds:[1005338] ; 比较是否出界
01004B2B .||7F 41 jg short winmine.01004B6E ; 出界就跳走
01004B2D .||33FF xor edi,edi ; edi为参数lParam
01004B2F .||03FB add edi,ebx ; 坐标参数的逆运算
01004B31 .||C1E7 04 shl edi,4
01004B34 .||83C7 28 add edi,28
01004B37 .||C1E7 10 shl edi,10
01004B3A .||03FE add edi,esi
01004B3C .||57 push edi ; /lParam
01004B3D .||6A 01 push 1 ; |wParam
01004B3F .||68 01020000 push 201 ; |msg=左键按下
01004B44 .||FF35 D24A0001 push dword ptr ds:[1004AD2] ; |hWnd
01004B4A .||E8 7AD0FFFF call winmine.01001BC9 ; \call WndProc
01004B4F .||C705 40510001 01000000 mov dword ptr ds:[1005140],1 ; 左键弹起标志
01004B59 .||57 push edi ; /lParam
01004B5A .||6A 00 push 0 ; |wParam
01004B5C .||68 02020000 push 202 ; |msg=左键弹起
01004B61 .||FF35 D24A0001 push dword ptr ds:[1004AD2] ; |hWnd
01004B67 .||E8 5DD0FFFF call winmine.01001BC9 ; \call WndProc
01004B6C .|\EB AA jmp short winmine.01004B18 ; 行数小循环
01004B6E >| C705 DA4A0001 00000000 mov dword ptr ds:[1004ADA],0 ; 行数从0开始
01004B78 .\ EB 83 jmp short winmine.01004AFD ; 列数大循环
01004B7A > C705 D64A0001 00000000 mov dword ptr ds:[1004AD6],0 ; 列数从0开始
01004B84 . C605 D04A0001 00 mov byte ptr ds:[1004AD0],0 ; 恢复伪造消息标志
01004B8B .^ E9 D1FEFFFF jmp winmine.01004A61 ; 出门咯^_^
这里为了让计算机帮我们单击,我们伪造了鼠标单击的消息
01004B3C .||57 push edi ; /lParam
01004B3D .||6A 01 push 1 ; |wParam
01004B3F .||68 01020000 push 201 ; |msg=左键按下
01004B44 .||FF35 D24A0001 push dword ptr ds:[1004AD2] ; |hWnd
01004B4A .||E8 7AD0FFFF call winmine.01001BC9 ; \call WndProc
01004B4F .||C705 40510001 01000000 mov dword ptr ds:[1005140],1 ; 左键弹起标志
01004B59 .||57 push edi ; /lParam
01004B5A .||6A 00 push 0 ; |wParam
01004B5C .||68 02020000 push 202 ; |msg=左键弹起
01004B61 .||FF35 D24A0001 push dword ptr ds:[1004AD2] ; |hWnd
01004B67 .||E8 5DD0FFFF call winmine.01001BC9 ; \call WndProc
然后用了两个循环,分别遍历行数和列数,做到每个格子都让鼠标点一遍,但是,我们的这个处理是在消息过程里分出来的,所以要有个标志,就是消息是否是我们发出的,如果是的话就不执行上面这段伪造代码,所以在开始有个标志位
01004AE3 > 803D D04A0001 00 cmp byte ptr ds:[1004AD0],0 ; 跳到这里,先判断是否为我们伪造消息状态
01004AEA .^ 75 F2 jnz short winmine.01004ADE ; 是就跳走,不处理下面的伪造消息
还有一个就是坐标的逆运算,原来的运算是
01004A82 > 8B45 14 mov eax,dword ptr ss:[ebp+14] ; 这里开始是抄写源程序分解坐标的方法
01004A85 . C1E8 10 shr eax,10 ; 取高位
01004A88 . 83E8 27 sub eax,27 ; -39(边界)
01004A8B . C1F8 04 sar eax,4 ; ÷16
01004A8E . 50 push eax ; 结果为行
01004A8F . 0FB745 14 movzx eax,word ptr ss:[ebp+14] ; 低位
01004A93 . 83C0 04 add eax,4 ; +4
01004A96 . C1F8 04 sar eax,4 ; ÷16
01004A99 . 50 push eax ; 列
高位-39,再除以16结果为行,低位+4再除以16,结果为列。所以我们可以逆运算一下
行数×16,再加上40(为的是将鼠标点在格子里,所以要小于39,小一点就可以了,否则点不到的)
列数×16,再减取3(道理和上面一样)
然后再组装起来,行数放在高位(前两个字节),列数放在低位(后两个字节)
00 00 00 00
行数 列数
最后保存一下就可以了,在代码上点右键,复制到可执行文件-》全部修正,就行了
整理一下自己添上去的代码
01004A56 > \8B55 0C mov edx,dword ptr ss:[ebp+C]
01004A59 . 8B4D 14 mov ecx,dword ptr ss:[ebp+14] ; 这两行重写一遍也无所谓,跳到下一行也没关系
01004A5C . 53 push ebx
01004A5D . 56 push esi
01004A5E . 57 push edi ; 这三行保存寄存器
01004A5F . EB 07 jmp short winmine.01004A68 ; 跳到我们的作弊代码
01004A61 > 33DB xor ebx,ebx ; 这里是出口,跳回去,清空ebx(原来的代码)
01004A63 .^ E9 72D1FFFF jmp winmine.01001BDA ; 跳回去
01004A68 > 81FA 01020000 cmp edx,201 ; 左键按下?
01004A6E . 74 12 je short winmine.01004A82 ; 是就继续
01004A70 . 81FA 02020000 cmp edx,202 ; 左键弹起?
01004A76 .^ 75 E9 jnz short winmine.01004A61 ; 也不是就跳回去,不是我们要处理的消息
01004A78 . 33FF xor edi,edi
01004A7A . 393D 40510001 cmp dword ptr ds:[1005140],edi
01004A80 .^ 74 DF je short winmine.01004A61 ;
这三句是判断左键弹起的,程序里是这样判断的,保险起见,我们也这样判断
01004A82 > 8B45 14 mov eax,dword ptr ss:[ebp+14] ; 这里开始是抄写源程序分解坐标的方法
01004A85 . C1E8 10 shr eax,10
01004A88 . 83E8 27 sub eax,27
01004A8B . C1F8 04 sar eax,4 ; 用了栈传递参数
01004A8E . 50 push eax ; 行
01004A8F . 0FB745 14 movzx eax,word ptr ss:[ebp+14]
01004A93 . 83C0 04 add eax,4
01004A96 . C1F8 04 sar eax,4
01004A99 . 50 push eax ; 列
01004A9A . 5E pop esi ; 取出列数
01004A9B . 5B pop ebx ; 取出行数
01004A9C . 85DB test ebx,ebx
01004A9E .^ 7E C1 jle short winmine.01004A61
01004AA0 . 85F6 test esi,esi
01004AA2 .^ 7E BD jle short winmine.01004A61
01004AA4 . 3B1D 38530001 cmp ebx,dword ptr ds:[1005338] ; 行数比较是否出界
01004AAA .^ 7F B5 jg short winmine.01004A61
01004AAC . 3B35 34530001 cmp esi,dword ptr ds:[1005334]
01004AB2 .^ 7F AD jg short winmine.01004A61
01004AB4 . EB 2D jmp short winmine.01004AE3
01004AB6 90 nop
01004AB7 > 8A8433 40530001 mov al,byte ptr ds:[ebx+esi+1005340] ; 到这里,判断方法和程序是一样的
01004ABE . 3C 80 cmp al,80 ; 判断是否有雷
01004AC0 .^ 72 9F jb short winmine.01004A61 ; 小于即没有雷
01004AC2 . BA 04020000 mov edx,204 ; 有雷的情况把消息改成右击
01004AC7 . C745 10 02000000 mov dword ptr ss:[ebp+10],2 ; 判断右击的标志
(右击时wParam=2,程序里是要判断的,所以要改一下标志)
01004ACE .^ EB 91 jmp short winmine.01004A61 ; 跳回去
01004AD0 . 00 db 00 ; 这里是是否为我们伪造消息的状态,1为是,0为否
01004AD1 00 db 00
01004AD2 . 00000000 dd 00000000
01004AD6 . 00000000 dd 00000000 ; 当前的列数
01004ADA . 00000000 dd 00000000 ; 当前的行数
01004ADE > C1E3 05 shl ebx,5
01004AE1 .^ EB D4 jmp short winmine.01004AB7
01004AE3 > 803D D04A0001 00 cmp byte ptr ds:[1004AD0],0 ; 跳到这里,先判断是否为我们伪造消息状态
01004AEA .^ 75 F2 jnz short winmine.01004ADE ; 是就跳走,不处理下面的
01004AEC . C605 D04A0001 01 mov byte ptr ds:[1004AD0],1 ; 标志为伪造消息状态
01004AF3 . 3E:8B5D 08 mov ebx,dword ptr ds:[ebp+8]
01004AF7 . 891D D24A0001 mov dword ptr ds:[1004AD2],ebx
01004AFD >/ 8B35 D64A0001 mov esi,dword ptr ds:[1004AD6] ; 取出列数
01004B03 .| 46 inc esi ; +1
01004B04 .| 8935 D64A0001 mov dword ptr ds:[1004AD6],esi ; 写回去
01004B0A .| 3B35 34530001 cmp esi,dword ptr ds:[1005334] ; 比较是否出界
01004B10 .| 7F 68 jg short winmine.01004B7A ; 出界代表结束了,跳走
01004B12 .| C1E6 04 shl esi,4 ; 计算坐标的逆运算
01004B15 .| 83EE 03 sub esi,3
01004B18 >|/8B1D DA4A0001 mov ebx,dword ptr ds:[1004ADA] ; 取出行数
01004B1E .||43 inc ebx ; +1
01004B1F .||891D DA4A0001 mov dword ptr ds:[1004ADA],ebx ; 写回去
01004B25 .||3B1D 38530001 cmp ebx,dword ptr ds:[1005338] ; 比较是否出界
01004B2B .||7F 41 jg short winmine.01004B6E ; 出界就跳走
01004B2D .||33FF xor edi,edi ; edi为参数lParam
01004B2F .||03FB add edi,ebx ; 坐标参数的逆运算
01004B31 .||C1E7 04 shl edi,4
01004B34 .||83C7 28 add edi,28
01004B37 .||C1E7 10 shl edi,10
01004B3A .||03FE add edi,esi
01004B3C .||57 push edi ; /lParam
01004B3D .||6A 01 push 1 ; |wParam
01004B3F .||68 01020000 push 201 ; |msg=左键按下
01004B44 .||FF35 D24A0001 push dword ptr ds:[1004AD2] ; |hWnd
01004B4A .||E8 7AD0FFFF call winmine.01001BC9 ; \call WndProc
01004B4F .||C705 40510001 01000000 mov dword ptr ds:[1005140],1 ; 左键弹起标志
01004B59 .||57 push edi ; /lParam
01004B5A .||6A 00 push 0 ; |wParam
01004B5C .||68 02020000 push 202 ; |msg=左键弹起
01004B61 .||FF35 D24A0001 push dword ptr ds:[1004AD2] ; |hWnd
01004B67 .||E8 5DD0FFFF call winmine.01001BC9 ; \call WndProc
01004B6C .|\EB AA jmp short winmine.01004B18 ; 行数小循环
01004B6E >| C705 DA4A0001 00000000 mov dword ptr ds:[1004ADA],0 ; 行数从0开始
01004B78 .\ EB 83 jmp short winmine.01004AFD ; 列数大循环
01004B7A > C705 D64A0001 00000000 mov dword ptr ds:[1004AD6],0 ; 列数从0开始
01004B84 . C605 D04A0001 00 mov byte ptr ds:[1004AD0],0 ; 恢复伪造消息标志
01004B8B .^ E9 D1FEFFFF jmp winmine.01004A61 ; 出门咯^_^
然后运行一下,随便点,填上你的大名,呵呵,1秒扫雷记录^_^
五、后记
很懒,去年10月份就搞定了,一直没写文章,主要是没法上网,不想写了。现在ne365要出杂志,所以就贡献一篇pediy的文章吧,感谢ne365里的所有死党,也感谢看雪论坛对我的帮助,感谢所有密界的朋友!
|