扫雷辅助程序

扫雷程序的外挂貌似网上很多人都做过.但我相信肯定还是有很多和我一样的初学者还没有做出来或者正在努力中.所以我希望以下的文章能对大家有帮助.高手就自行飘过啦~文中肯定会有很多的不足的地方,错误也在所难免.因为我是第一次做这样的东西.以前只做过星际争霸的单机一键修改器.那个相对简单很多,因为直接用金山游侠搜索改变的数值就能找到对应地址.但是扫雷却不能这样.因为没有特定的数值拿给我们作搜索参考. 

★顺便给新建的群打个广告:不管你是菜鸟还是高手,只要喜欢编程.痴迷编程.都欢迎加入.群号:36260125

下面我就把我制作的全过程和心得给大家分享分享吧~~ 

首先我们需要了解一个API函数: 
BOOL BitBlt( 
HDC hdcDest,                   //指向目标设备环境的句柄 
int nXDest,                       //指定目标矩形区域左上角的X轴逻辑坐标。 
int nYDest,                       //指定目标矩形区域左上角的Y轴逻辑坐标。 
int nWidth,                       //指定源和目标矩形区域的逻辑宽度。 
int nHeight,                      //指定源和目标矩形区域的逻辑高度。 
HDC hdcSrc,                     //指向源设备环境的句柄。 
int nXSrc,                         //指定源矩形区域左上角的X轴逻辑坐标。 
int nYSrc,                        //指定源矩形区域左上角的Y轴逻辑坐标。  
DWORD dwRop             //指定光栅操作代码。这些代码将定义源矩形区域的颜色数据,如何  
//与目标矩形区域的颜色数据组合以完成最后的颜色。通常使用SRCCOPY 
); 

  大家可能都知道,那些比较老或者是比较简单的游戏都是采用BitBlt函数利用双缓冲技术来画图的.双缓冲技术说白了就是先创建一张白纸,然后用BitBlt在白纸上做画.最后再用一次BitBlt把画了画的纸打到屏幕上.这样能有效避免屏幕的闪烁.所以我的思路就是拦截BitBlt函数.看它都做了什么. 
    
   了解了以上知识以后就可以开工了~ 
1.先用PEID查看程序是否有壳.结果显示:Microsoft Visual C++ 7.0 Method2 [Debug].没有加壳 

2.用OD打开扫雷程序.程序停留在此处 
01003E21 > $  6A 70         PUSH 70 
01003E23   .  68 90130001   PUSH winmine.01001390 
01003E28   .  E8 DF010000   CALL winmine.0100400C 
01003E2D   .  33DB          XOR EBX,EBX 
01003E2F   .  53            PUSH EBX                                 ; /pModule => NULL 
01003E30   .  8B3D 8C100001 MOV EDI,DWORD PTR DS:[<&KERNEL32.GetModu>; |kernel32.GetModuleHandleA 
01003E36   .  FFD7          CALL EDI                                 ; \GetModuleHandleA 
(以下地址省略)                           

  此时程序还没有运行起来,还没有进行画图.我们先来看看到底它调用了哪些API函数,在反汇编窗口点击右键-查看-所有模块间的调用.通过出现的调用信息发现它确实调用了Bitblt函数.真是太好啦!!和我们预想的一样.

按照我们的原计划拦截BitBlt.在"所有模块间的调用"窗口中找到BitBlt(方法是输入首字母)然后按F2下断点..F9运行程序.扫雷被运行了起来.可能你会觉得奇怪.按照我们的理论拦截了BitBlt函数后它就不该画出图呀.为什么能够运行呢.而且扫雷也被画了出来??先别急.现在用鼠标随便点击一个小方格.是不是断下了呢?既然我们的理论是双缓冲那附近肯定还有一个BliBlt函数.结果确实被我们找到了.另一个BitBlt函数就在下面一点点. 
这里是第一个BitBlt 

01002646  /$  56            PUSH ESI 
01002647  |.  FF35 245B0001 PUSH DWORD PTR DS:[1005B24]              ; /hWnd = NULL 
0100264D  |.  FF15 2C110001 CALL DWORD PTR DS:[<&USER32.GetDC>]      ; \GetDC 
01002653  |.  8B4C24 0C     MOV ECX,DWORD PTR SS:[ESP+C] 
01002657  |.  68 2000CC00   PUSH 0CC0020                             ; /ROP = SRCCOPY 
0100265C  |.  8BF0          MOV ESI,EAX                              ; | 
0100265E  |.  8B4424 0C     MOV EAX,DWORD PTR SS:[ESP+C]             ; | 
01002662  |.  8BD1          MOV EDX,ECX                              ; | 
01002664  |.  6A 00         PUSH 0                                   ; |YSrc = 0 
01002666  |.  C1E2 05       SHL EDX,5                                ; | 
01002669  |.  0FBE9402 4053>MOVSX EDX,BYTE PTR DS:[EDX+EAX+1005340]  ; | 
01002671  |.  6A 00         PUSH 0                                   ; |XSrc = 0 

1002673  |.  83E2 1F       AND EDX,1F                               ; | 
01002676  |.  FF3495 205A00>PUSH DWORD PTR DS:[EDX*4+1005A20]        ; |hSrcDC 
0100267D  |.  C1E1 04       SHL ECX,4                                ; | 
01002680  |.  6A 10         PUSH 10                                  ; |Height = 10 (16.) 
01002682  |.  6A 10         PUSH 10                                  ; |Width = 10 (16.) 
01002684  |.  83C1 27       ADD ECX,27                               ; | 
01002687  |.  C1E0 04       SHL EAX,4                                ; | 
0100268A  |.  51            PUSH ECX                                 ; |YDest 
0100268B  |.  83E8 04       SUB EAX,4                                ; | 
0100268E  |.  50            PUSH EAX                                 ; |XDest 
0100268F  |.  56            PUSH ESI                                 ; |hDestDC 
01002690  |.  FF15 5C100001 CALL DWORD PTR DS:[<&GDI32.BitBlt>]      ; \BitBlt 
01002696  |.  56            PUSH ESI                                 ; /hDC 
01002697  |.  FF35 245B0001 PUSH DWORD PTR DS:[1005B24]              ; |hWnd = NULL 
0100269D  |.  FF15 28110001 CALL DWORD PTR DS:[<&USER32.ReleaseDC>]  ; \ReleaseDC 

通过大致分析代码发现这个BitBlt貌似并没做什么有意义的事情,先不管它.
往下面翻一点点就会看见另一个BitBlt
通过对这个BitBlt的分析发现里面大有文章.为什么这么说呢?打眼一看就发现里面有很多的判断分支.我已经将大致的注解标在了后面.现在我们一句一句的看.

010026A7  /$  55            PUSH EBP                                 ;  保存EBP
010026A8  |.  8BEC          MOV EBP,ESP                              ;  指向栈顶

这两句就不说了.调用函数的常规格式

010026AA  |.  83EC 0C       SUB ESP,0C                               ;  申请3个局部变量每个4字节
这句注意了.程序申请了三个变量.我们依次命名为变量1变量2变量3每个4字节

010026AD  |.  33C0          XOR EAX,EAX                              ;  清零EAX=0
010026AF  |.  40            INC EAX                                  ;  EAX+1=1

不知道什么作用.暂时不管它

010026B0  |.  3905 38530001 CMP DWORD PTR DS:[1005338],EAX           ;  将EAX与[1005338]处进行比较

进行比较,为后面的判断(10026C0)做准备.我们查一下1005338中的值是9.记下来.

010026B6  |.  C745 F8 37000>MOV DWORD PTR SS:[EBP-8],37              ;  保存变量1=37
010026BD  |.  8945 F4       MOV DWORD PTR SS:[EBP-C],EAX             ;  保存变量2=EAX=1

这两句就进行了变量的赋值!变量1=37.变量2=1

010026C0  |. /7C 68         JL SHORT winmine.0100272A                ;  如EAX<[1005338]则跳(结束)

这里就进行了上面说的判断.即如果EAX<9则跳转...通过查看跳转的地址我们发现那实际是结束地址.

010026C4  |.  BB 60530001   MOV EBX,winmine.01005360                 ;  将地址1005360中的数据传给EBX

我们可以查到1005360地址处的值是10.记下

010026C9  |> /33F6          /XOR ESI,ESI                             ;  ESI=0
010026CB  |. |46            |INC ESI                                 ;  ESI+1=1


我们可以看见010026C9地址处有个箭头表明是可以从其他地方跳转上来. 即(1002726)处的条件为真则跳上来.


010026CC  |.  3935 34530001 |CMP DWORD PTR DS:[1005334],ESI          ;  比较[1005334]和ESI的大小
010026D2  |.  C745 FC 0C000>|MOV DWORD PTR SS:[EBP-4],0C             ;  保存变量3=0C

通过查看发现1005334的值也是9..至此我们开始申请的3个变量已经全部都有值了
变量1=37
变量2=1
变量3=0C

010026D9  |. /7C 38         |JL SHORT winmine.01002713               ;  如果[1005334]<ESI则跳(否则画图)

如果ESI>9则调用BitBlt画图.否则跳到1002713处..我们可以看到1002713处是的代码作用是增加变量2的值.

01002706  |.  8345 FC 10    ||ADD DWORD PTR SS:[EBP-4],10            ;  画完后变量3加10
0100270A  |.  46            ||INC ESI                                ;  ESI+1
0100270B  |.  3B35 34530001 ||CMP ESI,DWORD PTR DS:[1005334]         ;  比较ESI和[1005334]大小
01002711  |.^ 7E C8         |\JLE SHORT winmine.010026DB             ;  ESI<=[1005334]则跳(继续画)


我们可以清楚的看到其实这是一个循环.为什么我这么肯定呢.因为在1002711处程序又把ESI和9进行了比较如果还是没有9大的话又继续画每画一次就让ESI+1直到它大于9为止
类似于一个for循环

for(ESI=1;ESI<=9;ESI++)
{
BitBlt(......);
}

我们现在分析一下BitBlt函数的参数


010026DB  |> /68 2000CC00   |/PUSH 0CC0020                           ; /ROP = SRCCOPY
010026E0  |. |33C0          ||XOR EAX,EAX                            ; |
010026E2  |. |8A0433        ||MOV AL,BYTE PTR DS:[EBX+ESI]           ; |
010026E5  |. |6A 00         ||PUSH 0                                 ; |YSrc = 0
010026E7  |. |6A 00         ||PUSH 0                                 ; |XSrc = 0
010026E9  |. |83E0 1F       ||AND EAX,1F                             ; |
010026EC  |. |FF3485 205A00>||PUSH DWORD PTR DS:[EAX*4+1005A20]      ; |hSrcDC
010026F3  |. |6A 10         ||PUSH 10                                ; |Height = 10 (16.)
010026F5  |. |6A 10         ||PUSH 10                                ; |Width = 10 (16.)
010026F7  |. |FF75 F8       ||PUSH DWORD PTR SS:[EBP-8]              ; |YDest
010026FA  |. |FF75 FC       ||PUSH DWORD PTR SS:[EBP-4]              ; |XDest
010026FD  |. |FF75 08       ||PUSH DWORD PTR SS:[EBP+8]              ; |hDestDC
01002700     |FF15 5C100001 ||CALL DWORD PTR DS:[<&GDI32.BitBlt>]    ; \BitBlt

我们还原成伪代码

BOOL BitBlt(
HDC hdcDest=hDestDC,                              //指向目标设备环境的句柄
int nXDest=XDest=第3个变量,                       //指定目标矩形区域左上角的X轴逻辑坐标。
int nYDest=YDest=第1个变量,                       //指定目标矩形区域左上角的Y轴逻辑坐标。
int nWidth=Width = 10 ,                             //指定源和目标矩形区域的逻辑宽度。
int nHeight=Height = 10 ,                           //指定源和目标矩形区域的逻辑高度。
HDC hdcSrc=DS:[EAX*4+1005A20],            //指向源设备环境的句柄。
int nXSrc=XSrc = 0,                                     //指定源矩形区域左上角的X轴逻辑坐标。
int nYSrc=YSrc = 0,                                     //指定源矩形区域左上角的Y轴逻辑坐标。 
DWORD dwRop=SRCCOPY                          //指定光栅操作代码。这些代码将定义源矩形区域的颜色数据,如何 
//与目标矩形区域的颜色数据组合以完成最后的颜色。通常使用SRCCOPY
);

到目前位置我们已经了解了下列信息
1.变量3是X坐标...变量1是Y坐标
2.[1005338]=9,[1005360]=10,[1005334]=9
3.一个for循环在管理BitBlt的执行
4.X坐标一次是增加的10


01002713  |> \FF45 F4       |INC DWORD PTR SS:[EBP-C]                ;  第二个变量加1
01002716  |.  8B45 F4       |MOV EAX,DWORD PTR SS:[EBP-C]            ;  把第二个变量送回EAX
01002719  |.  8345 F8 10    |ADD DWORD PTR SS:[EBP-8],10             ;  把第一个变量加10
0100271D  |.  83C3 20       |ADD EBX,20                              ;  EBX+20
01002720  |.  3B05 38530001 |CMP EAX,DWORD PTR DS:[1005338]          ;  把EAX与[1005338]比较
01002726  |.^ 7E A1         \JLE SHORT winmine.010026C9              ;  EAX<=[1005338]则跳否则结束

for循环执行完后执行上面这几句代码

我们可以清楚的看到.这是在给Y加10..所以很容易得出结论原来是一个而重for循环.外层管理Y坐标的增加内层管理X坐标的增加.每次增加10.结束条件都是执行到了9次就结束.为什么是9次呢?原来是我们选定是初级.是9X9的格子....

好了.这个函数分析完了.下面我们该找找看雷区在内存中对应的位置了...
我们找到1005334查看附近内存中的数据发现

01005330  0A 00 00 00 09 00 00 00 09 00 00 00 00 00 00 00  ................
    01005340  10 10 10 10 10 10 10 10 10 10 10 0F 0F 0F 0F 0F  
    01005350  0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F  
    01005360  10 8F 8F 0F 0F 0F 0F 0F 0F 0F 10 0F 0F 0F 0F 0F  弿
    01005370  0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F  
    01005380  10 0F 0F 0F 0F 0F 0F 0F 0F 0F 10 0F 0F 0F 0F 0F  
    01005390  0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F  
    010053A0  10 0F 0F 0F 8F 0F 0F 0F 0F 0F 10 0F 0F 0F 0F 0F  ?
    010053B0  0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F  
    010053C0  10 0F 0F 0F 0F 0F 8F 0F 0F 0F 10 0F 0F 0F 0F 0F  ?
    010053D0  0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F  
    010053E0  10 8F 0F 8F 8F 0F 0F 0F 0F 0F 10 0F 0F 0F 0F 0F  ?弿
    010053F0  0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F  
    01005400  10 0F 8F 0F 0F 0F 0F 0F 0F 0F 10 0F 0F 0F 0F 0F  ?
    01005410  0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F  
    01005420  10 0F 0F 0F 0F 0F 0F 0F 0F 0F 10 0F 0F 0F 0F 0F  
    01005430  0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F  
    01005440  10 0F 0F 8F 0F 0F 0F 0F 0F 0F 10 0F 0F 0F 0F 0F  ?

注意观察...你会发现这个内存块其实是用10围成的一个9*9的格子.(其实每行中间还多了一行全是OF的还不清楚这是为什么不过对我们分析没影响)
现在我们通过随便点击一个格子再来观察发现有一个数据变了.继续...经过多次实验发现这个内存区域就是我们苦苦寻找的雷区了....我们只用知道8F是雷0F是安全的就行啦~~~接下来就是工具的编写了.这个相对就很轻松了....
我用的是VS2008.MFC编写的...
其实只用几个函数就搞定了.最重要的还是上面的分析过程.
下面是需要用到的函数.很简单的.....依顺序给出...
FindWindow
GetWindowThreadProcessId
OpenProcess
ReadProcessMemory



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值