首页
社区
课程
招聘
[旧帖] 编写扫雷游戏插件 0.00雪花
发表于: 2012-3-29 22:21 3269

[旧帖] 编写扫雷游戏插件 0.00雪花

2012-3-29 22:21
3269

先说一下:本人菜鸟,做这个完全当是写作业(~~~~)
闲来无聊,下了个郁金香的外挂教程,看了第一章,想学习一下,就用扫雷开始练下手。
先上OD,检测下数据:

用PEID检测下,是c程序。由于每次布雷都不一样,猜测应该用了随机函数rand来布点;
下断点如下:

运行程序,点击扫雷笑脸图标;
断下来了:

反汇编中跟随,查找函数的调用:
rand:随机函数(记住地址 1003940)

继续查找上一步调用:
0100367A  /$  A1 AC560001   MOV EAX,DWORD PTR DS:[10056AC]//宽度
0100367F  |.  8B0D A8560001 MOV ECX,DWORD PTR DS:[10056A8]//高度
01003685  |.  53            PUSH EBX
01003686  |.  56            PUSH ESI
01003687  |.  57            PUSH EDI
01003688  |.  33FF          XOR EDI,EDI
0100368A  |.  3B05 34530001 CMP EAX,DWORD PTR DS:[1005334]//比较1005334与10056ac地址是否一致
01003690  |.  893D 64510001 MOV DWORD PTR DS:[1005164],EDI
01003696  |.  75 0C         JNZ SHORT winmine.010036A4
01003698  |.  3B0D 38530001 CMP ECX,DWORD PTR DS:[1005338]//比较1005338与10056a8地址是否一致
0100369E  |.  75 04         JNZ SHORT winmine.010036A4
010036A0  |.  6A 04         PUSH 4
010036A2  |.  EB 02         JMP SHORT winmine.010036A6
010036A4  |>  6A 06         PUSH 6
010036A6  |>  5B            POP EBX
010036A7  |.  A3 34530001   MOV DWORD PTR DS:[1005334],EAX//宽度
010036AC  |.  890D 38530001 MOV DWORD PTR DS:[1005338],ECX//高度
010036B2  |.  E8 1EF8FFFF   CALL winmine.01002ED5   //分配并编写雷区的内存数据
010036B7  |.  A1 A4560001   MOV EAX,DWORD PTR DS:[10056A4]//雷数
010036BC  |.  893D 60510001 MOV DWORD PTR DS:[1005160],EDI//5160处赋0
010036C2  |.  A3 30530001   MOV DWORD PTR DS:[1005330],EAX//雷数
010036C7  |>  FF35 34530001 PUSH DWORD PTR DS:[1005334]
010036CD  |.  E8 6E020000   CALL winmine.01003940//横坐标随机化
010036D2  |.  FF35 38530001 PUSH DWORD PTR DS:[1005338]
010036D8  |.  8BF0          MOV ESI,EAX   //将结果赋给ESI
010036DA  |.  46            INC ESI             //ESI加一
010036DB  |.  E8 60020000   CALL winmine.01003940//纵坐标随机化
010036E0  |.  40            INC EAX           //纵坐标随机结果加一;
010036E1  |.  8BC8          MOV ECX,EAX
010036E3  |.  C1E1 05       SHL ECX,5    //纵坐标结果乘以(OX20)
010036E6  |.  F68431 405300>TEST BYTE PTR DS:[ECX+ESI+1005340],80 //检测随机处的内存数是否为80
010036EE  |.^ 75 D7         JNZ SHORT winmine.010036C7      //如果为80则重新随机
010036F0  |.  C1E0 05       SHL EAX,5                                    //纵坐标结果乘以(OX20)
010036F3  |.  8D8430 405300>LEA EAX,DWORD PTR DS:[EAX+ESI+1005340] //取有效地址赋给EAX;
010036FA  |.  8008 80       OR BYTE PTR DS:[EAX],80           //将随机处内存数据与80向或;
010036FD  |.  FF0D 30530001 DEC DWORD PTR DS:[1005330]   //雷数减一
01003703  |.^ 75 C2         JNZ SHORT winmine.010036C7  //雷数为0则不调转,否则继续布雷
01003705  |.  8B0D 38530001 MOV ECX,DWORD PTR DS:[1005338]
0100370B  |.  0FAF0D 345300>IMUL ECX,DWORD PTR DS:[1005334]
01003712  |.  A1 A4560001   MOV EAX,DWORD PTR DS:[10056A4]
01003717  |.  2BC8          SUB ECX,EAX
01003719  |.  57            PUSH EDI
0100371A  |.  893D 9C570001 MOV DWORD PTR DS:[100579C],EDI
01003720  |.  A3 30530001   MOV DWORD PTR DS:[1005330],EAX
01003725  |.  A3 94510001   MOV DWORD PTR DS:[1005194],EAX
0100372A  |.  893D A4570001 MOV DWORD PTR DS:[10057A4],EDI
01003730  |.  890D A0570001 MOV DWORD PTR DS:[10057A0],ECX
01003736  |.  C705 00500001>MOV DWORD PTR DS:[1005000],1
01003740  |.  E8 25FDFFFF   CALL winmine.0100346A
01003745  |.  53            PUSH EBX                                 ; /Arg1
01003746  |.  E8 05E2FFFF   CALL winmine.01001950                    ; \winmine.01001950
0100374B  |.  5F            POP EDI
0100374C  |.  5E            POP ESI
0100374D  |.  5B            POP EBX
0100374E  \.  C3            RETN

OD中输入:db 10056ac
检测下传递至EAX,ECX中的内容,测试知10056ac中数据为宽度,10056a8为高度。

在扫雷游戏里,标记一个地雷,发现该处的内存数为8E
进入010036B2  |.  E8 1EF8FFFF   CALL winmine.01002ED5
查看布雷详情:
01002ED5  /$  B8 60030000   MOV EAX,360                        //开辟0X360个内存单元
01002EDA  |>  48            /DEC EAX
01002EDB  |.  C680 40530001>|MOV BYTE PTR DS:[EAX+1005340],0F//将内存单元的数值设为0F
01002EE2  |.^ 75 F6         \JNZ SHORT winmine.01002EDA
01002EE4  |.  8B0D 34530001 MOV ECX,DWORD PTR DS:[1005334]  //横坐标赋值给ECX
01002EEA  |.  8B15 38530001 MOV EDX,DWORD PTR DS:[1005338] //~~~~~~~~~
01002EF0  |.  8D41 02       LEA EAX,DWORD PTR DS:[ECX+2]
01002EF3  |.  85C0          TEST EAX,EAX
01002EF5  |.  56            PUSH ESI
01002EF6  |.  74 19         JE SHORT winmine.01002F11
01002EF8  |.  8BF2          MOV ESI,EDX
01002EFA  |.  C1E6 05       SHL ESI,5
01002EFD  |.  8DB6 60530001 LEA ESI,DWORD PTR DS:[ESI+1005360]
01002F03  |>  48            /DEC EAX
01002F04  |.  C680 40530001>|MOV BYTE PTR DS:[EAX+1005340],10
01002F0B  |.  C60406 10     |MOV BYTE PTR DS:[ESI+EAX],10
01002F0F  |.^ 75 F2         \JNZ SHORT winmine.01002F03
01002F11  |>  8D72 02       LEA ESI,DWORD PTR DS:[EDX+2]
01002F14  |.  85F6          TEST ESI,ESI
01002F16  |.  74 21         JE SHORT winmine.01002F39
01002F18  |.  8BC6          MOV EAX,ESI
01002F1A  |.  C1E0 05       SHL EAX,5
01002F1D  |.  8D90 40530001 LEA EDX,DWORD PTR DS:[EAX+1005340]
01002F23  |.  8D8408 415300>LEA EAX,DWORD PTR DS:[EAX+ECX+1005341]
01002F2A  |>  83EA 20       /SUB EDX,20
01002F2D  |.  83E8 20       |SUB EAX,20
01002F30  |.  4E            |DEC ESI
01002F31  |.  C602 10       |MOV BYTE PTR DS:[EDX],10
01002F34  |.  C600 10       |MOV BYTE PTR DS:[EAX],10
01002F37  |.^ 75 F1         \JNZ SHORT winmine.01002F2A
01002F39  |>  5E            POP ESI
01002F3A  \.  C3            RETN

从这个里面可以看到和80或的是0F,相或结果为8F

在随机化布雷时,纵坐标数值始终是以纵坐标数乘以32进行增长(16进制为20),所以对于横纵坐标数值应为:
32高+宽;

用SPY++检测雷区坐标位置如下:







第一格和第二格:(这个和图片有点对不齐,因为图是后来截的。呵呵)
横坐标:17,纵坐标:59;
横坐标:32,纵坐标:75;
横坐标相差16,纵坐标相差16.
以横坐标为16纵坐标为60做为第一点,依次类推;
编写开雷的函数如下:

用C++编写代码如下:
void CSaoleiDlg::scanfun(int dire, BOOL biaolei, BOOL kailei, BOOL step)
{
        const lt=0;(左上方)
        const lb=1;(左下方)
        const rt=2;(右上方)
        const rb=3;(右下方)
//第三位为顺序位:横向为1;纵向为0;
        DWORD x = 0x10056A8;
        DWORD lparam=0x003C0010;
        DWORD y = 0x10056AC;
        DWORD addr = 0x1005361;
        HWND hwnd = ::FindWindow(NULL, "扫雷");
        DWORD hProcessId;
        GetWindowThreadProcessId(hwnd, &hProcessId);
        HANDLE Process =OpenProcess(PROCESS_ALL_ACCESS, FALSE, hProcessId);
        int b=0,s =0,nx =0,ny =0,ti=0,tj=0;
        BOOL contr=TRUE;
        ReadProcessMemory(Process, (LPCVOID)x, &nx, 1, NULL); //X为高度
        ReadProcessMemory(Process, (LPCVOID)y, &ny, 1, NULL); //Y为宽度。

if((dire&4)==4)
{
                for(int i = 0; (i < nx)&&contr ; i++)
                {
        {
                if((dire&2)==2)
                        ti=nx-i-1;
                else
                        ti=i;
        }
        for(int j = 0; (j < ny)&&contr; j++)
        {
                {
                        if((dire&1)==1)
                                tj=ny-j-1;
                        else
                                tj=j;
                }
                ::ReadProcessMemory(Process, (LPCVOID)(addr + 32*ti+tj), &b, 1, NULL);
        if (b == 0x8F&&biaolei==TRUE) //判断是否有雷
                {
                s = 0x8E; //如果有雷就画上小红旗
                ::WriteProcessMemory(Process, (LPVOID)(addr + 32*ti+tj), &s, 1, NULL);
                if(step==TRUE)
                                {
                                contr=FALSE;
//                                break;
                                }
                }
        else if(b==0x0F&&kailei==TRUE)
                {       
                        lparam=(0x003C0010+0x10000*(16*ti)+16*tj);
                        ::SendMessage(hwnd,WM_LBUTTONDOWN,0,lparam);
                        ::SendMessage(hwnd,WM_LBUTTONUP,0,lparam);
                        if(step==TRUE)
                                {
                                contr=FALSE;
//                                break;
                                }
                }
               
        }
                }
}
else
        for(int j = 0; (j < ny)&&contr; j++)
{
        {
                        if((dire&1)==1)
                                tj=ny-j-1;
                        else
                                tj=j;               
        }
        for(int i = 0; (i < nx)&&contr ; i++)
        {
                {
                        if((dire&2)==2)
                                ti=nx-i-1;
                        else
                                ti=i;                       
                }
                ::ReadProcessMemory(Process, (LPCVOID)(addr + 32*ti+tj), &b, 1, NULL);
        if (b == 0x8F&&biaolei==TRUE) //判断是否有雷
                {
                s = 0x8E; //如果有雷就画上小红旗
                ::WriteProcessMemory(Process, (LPVOID)(addr + 32*ti+tj), &s, 1, NULL);
                if(step==TRUE)
                                {
                                contr=FALSE;
//                                break;
                                }
                }
        else if(b==0x0F&&kailei==TRUE)
                {       
                        lparam=(0x003C0010+0x10000*(16*ti)+16*tj);
                        ::SendMessage(hwnd,WM_LBUTTONDOWN,0,lparam);
                        ::SendMessage(hwnd,WM_LBUTTONUP,0,lparam);
                        if(step==TRUE)
                                {
                                contr=FALSE;
//                                break;
                                }
                }
               
        }
}

        ::InvalidateRect(hwnd, NULL, TRUE);
        ::CloseHandle(Process);
}

例如:
        scanfun(4,TRUE,FALSE,TRUE);
则表明从左上方横向单补表雷;

由于本人菜鸟,写这个完全是学习,只是把他当作业~~-~~


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 6
支持
分享
最新回复 (6)
雪    币: 1015
活跃值: (235)
能力值: ( LV12,RANK:440 )
在线值:
发帖
回帖
粉丝
2
有些关键地方太简略了吧= =
2012-3-29 22:44
0
雪    币: 41
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
3
争取明天晚上补充,今天晚上忙~~~
2012-3-29 22:48
0
雪    币: 238
活跃值: (55)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
4
HWND hwnd = ::FindWindow(NULL, "扫雷");
  HANDLE Process =OpenProcess(PROCESS_ALL_ACCESS, FALSE, hProcessId);

  ::CloseHandle(Process);
菜鸟问一个小问题,为什么只close了 HANDLE Process,不用close  HWND hwnd吗,虽然我在实践中知道确实不用也不应该用,但不知道究竟为什么,不都是句柄吗?另外,也可以告诉我看什么书能知道这个,罗云彬的win32汇编行吗?
2012-3-30 09:46
0
雪    币: 41
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
5
我把进程句柄打开了,对它的内存完成了写操作,完了后肯定要关闭它嘛!
(我也是个很菜的,注册论坛开始学习C语言,到现在还在皮毛阶段!
可能我的回答帮不上忙~~)
2012-3-30 19:13
0
雪    币: 238
活跃值: (55)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
6
楼主没看清我的问题啊,但算是回答了,我有另外一个新手写的扫雷外挂,每次在结束时closehandle(hwnd)时都抛出无效句柄异常,楼主是因机缘巧合而没有画蛇添足。我一直没时间和精力看看关于句柄到底是什么的内核代码,真绕口。原来都是菜鸟,共同进步!楼主的代码写得真不错,顺便问一下楼主的郁金香在哪儿下的?
2012-3-30 22:59
0
雪    币: 39
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
思路很清晰,谢谢分享
2012-3-31 10:25
0
游客
登录 | 注册 方可回帖
返回
//