先说一下:本人菜鸟,做这个完全当是写作业(~~~~)
闲来无聊,下了个郁金香的外挂教程,看了第一章,想学习一下,就用扫雷开始练下手。
先上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);
则表明从左上方横向单补表雷;
由于本人菜鸟,写这个完全是学习,只是把他当作业~~-~~
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!