#include <windows.h> #include <stdio.h> #include <stdlib.h> #include <conio.h> #include <math.h> int My_dump(int flags)//flags =0 标记雷区 =1 自动扫雷 =2 restart =4 { BYTE buf[10]; DWORD FindLong; INT X_num, Y_num, Total_num; int i, j, xPos, yPos; HWND hWnd = FindWindow(NULL, "扫雷"); if(hWnd == NULL) { printf("未运行扫雷程序\n"); system("pause"); return -1; } if (flags==4)//重新开始,发送F2按键消息 { PostMessage(hWnd, WM_KEYDOWN, 0x71, 0x03C0001); PostMessage(hWnd, WM_KEYUP, 0x71, 0x0C03C0001); CloseHandle(Handle); return 0; } GetWindowThreadProcessId(hWnd, &FindLong); HANDLE Handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, FindLong); ReadProcessMemory(Handle, (LPVOID)0x1005334, buf, 4, &FindLong); X_num = (buf[1]<<8) + buf[0]; ReadProcessMemory(Handle, (LPVOID)0x1005338, buf, 4, &FindLong); Y_num = (buf[1]<<8) + buf[0]; ReadProcessMemory(Handle, (LPVOID)0x1005330, buf, 4, &FindLong); Total_num = (buf[1]<<8) + buf[0]; if (flags == 3) { printf("行数:%2d ,列数:%2d ,地雷个数:%2d\n", Y_num, X_num, Total_num); } for(j=1; j<=Y_num; j++) { for(i=1; i<=X_num; i++) { ReadProcessMemory(Handle, (LPVOID)(0x1005340+(j<<5)+i), buf, 4, &FindLong); if((buf[0]&0x80) == 0x80) { //右键标记地雷 及 扫雷 if(flags == 1 || flags == 2) { xPos = i*16-8+12; yPos = j*16-8+53; PostMessage(hWnd, WM_RBUTTONDOWN, 0, (yPos<<16)+xPos); PostMessage(hWnd, WM_RBUTTONUP, 0, (yPos<<16)+xPos); } if (flags == 3) { printf("发现地雷于:%2d 行,%2d 列\n", j, i); } } else { //左键扫雷 if(flags == 2) { xPos = i*16-8+12; yPos = j*16-8+53; PostMessage(hWnd, WM_LBUTTONDOWN, 0, (yPos<<16)+xPos); PostMessage(hWnd, WM_LBUTTONUP, 0, (yPos<<16)+xPos); } } } } CloseHandle(Handle); if (flags == 3) { system("pause"); } return 0; } int main(int argc, char* argv[]) { int ch; do { system("cls"); printf("选择列表:\n" "1 标记雷区\n" "2 自动扫雷\n" "3 打印雷个数信息\n" "4 重新开始\n" "5 退出本程序\n" "请选择:" ); scanf("%d", &ch); if ( ch>0 && ch<5) { My_dump(ch); } else if (ch == 5) { exit(0); } }while(true); return 0; }
一直来都只是放着这个扫雷外挂的代码,却没写怎么写这个程序的,下面是分析扫雷程序代码的过程
在command中用命令Bp rand下断,F9运行程序,下面是断点返回到地雷生成过程的函数分析;
010036BC 893D 60510001 MOV DWORD PTR DS:[1005160],EDI 010036C2 A3 30530001 MOV DWORD PTR DS:[1005330],EAX 010036C7 FF35 34530001 PUSH DWORD PTR DS:[1005334] ; 将横坐标的值(窗口模式x*y中x的值)入栈 010036CD E8 6E020000 CALL winmine.01003940 ; 得到横坐标上的随机数== rand()%x 010036D2 FF35 38530001 PUSH DWORD PTR DS:[1005338] ; 将纵坐标的值入栈 010036D8 8BF0 MOV ESI,EAX 010036DA 46 INC ESI ; rand()%y+1 010036DB E8 60020000 CALL winmine.01003940 ; 得到纵坐标上的随机值== rand()%A 010036E0 40 INC EAX ; rand()%x+1 010036E1 8BC8 MOV ECX,EAX 010036E3 C1E1 05 SHL ECX,5 ; (rand()%x+1)<<5 010036E6 F68431 40530001>TEST BYTE PTR DS:[ECX+ESI+1005340],80 ; 判断是否已经是地雷了 010036EE ^ 75 D7 JNZ SHORT winmine.010036C7 ; 如果是,那么重新取值 010036F0 C1E0 05 SHL EAX,5 ; (rand()%x+1)<<5 010036F3 8D8430 40530001 LEA EAX,DWORD PTR DS:[EAX+ESI+1005340] ; (rand()%x+1)<<5 + rand()%y+1 + 1005340 010036FA 8008 80 OR BYTE PTR DS:[EAX],80 ; 标记成地雷 8F = 80|0F 010036FD FF0D 30530001 DEC DWORD PTR DS:[1005330] ; 要设置的地雷数减1 01003703 ^ 75 C2 JNZ SHORT winmine.010036C7 ; 若还需要生成地雷,往回跳 01003705 8B0D 38530001 MOV ECX,DWORD PTR DS:[1005338] ; 宽y 0100370B 0FAF0D 34530001 IMUL ECX,DWORD PTR DS:[1005334] ; x*y 01003712 A1 A4560001 MOV EAX,DWORD PTR DS:[10056A4] ; 雷数 [10056A4] 01003717 2BC8 SUB ECX,EAX ; 得到非地雷数 x*y-0x42 01003719 57 PUSH EDI ; EDI=0 0100371A 893D 9C570001 MOV DWORD PTR DS:[100579C],EDI 01003720 A3 30530001 MOV DWORD PTR DS:[1005330],EAX ; 地雷数目存在 地址 1005330 01003725 A3 94510001 MOV DWORD PTR DS:[1005194],EAX ; 未标记的地雷数目存在 地址 1005194 0100372A 893D A4570001 MOV DWORD PTR DS:[10057A4],EDI 01003730 890D A0570001 MOV DWORD PTR DS:[10057A0],ECX ; 总的小块数 x*y 01003736 C705 00500001 0>MOV DWORD PTR DS:[1005000],1 01003740 E8 25FDFFFF CALL winmine.0100346A 01003745 53 PUSH EBX 01003746 E8 05E2FFFF CALL winmine.01001950 0100374B 5F POP EDI 0100374C 5E POP ESI 0100374D 5B POP EBX 0100374E C3 RETN
以上代码用C语言写就是
//差不多这意思,不是按上面的反汇编代码来写的 int x; // 模式x值 内存地址 1005334 int y; // 模式Y值 内存地址 1005338 int Mine[(x<<5)*y]; // 存储地雷地址 &Mine[0] = 1005340 int TotalMine = 10;//地雷总数 地址1005330 void SetMine() { int i = TotalMine; while ( i > 0) { int tmp_x = rand() % x + 1;//随机生成x坐标点位置 int tmp_y = rand() % y + 1;//Y坐标点位置 if ( (Mine[(tmp_x<<5) + tmp_y]&0x80) == 0 )//判断是不是已经是地雷了 { Mine[(tmp_x<<5) + tmp_y] |= 0x80; //标记地雷 i-- ; } } }
从上面代码可以得到以下信息:
地雷个数地址 : 1005330
地雷窗口宽度x值 : 1005334
地雷窗口高度y值 : 1005338
扫雷模式 :10056A0
=0 初级
=1 中级
=2 高级
=3 自定义
地雷标记:
无雷:0x0F
有雷:0x8F