游戏安全的学习(5)—— 模拟外挂学习(基于扫雷)。

前言

当时主要 PATCH 了一些指令让它能够方便自己去运行,但是还有一些更酷的操作没试过,比如说拿到雷的结构,然后鼠标模拟去点。

数据分析

首先还是先找出关键数据的位置,这个可以用CE去分析。

  • winmine.exe+56A4:雷的个数
  • winmine.exe+56A8:地图高度
  • winmine.exe+56AC:地图宽度

然后就是对雷的数据分析了,首先它应该是存了一个二维数组,一个格子应该保存这些状态:

  • 未被翻开(默认状态)
  • 插旗
  • 数字或者被翻开为空(为空可以理解为数字0,数字0默认就不显示)

此时应当又有一个数组被存当前位置是否有雷,当然两个数组可以合并起来去写,当然在不清楚它怎么写代码的情况下肯定是按分开来做。

通过对第一个方格不断反复插旗找到数据,最终得到第一个格子的数据在

winmine.exe+5361

并且看起来是一个字节大小的数组。

经过一番测试可得

  • 未翻开的值为 0F
  • 插旗为 0E
  • 问号为 0D

翻开之后再看看内存

可以看到,翻开的格子对应了 0x40,如果有数字,那么是 0x41,其实这里很明显,好像每一行之前都有一个 0x10 字节,然后把宽高改为 0xF,再把CE内存浏览拉一下,一点会有一个视觉盛宴

感觉就像是定义了一个两倍高度的二维数组,偶数下标(0开始)的存了当前的格子状态和其它一系列的状态(后面发现它其实不过是第二维度被定死是0x20)。

所以据此写一个 DLL 获取雷区。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
void WinMineModuleDlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
DWORD Base = GetBaseAddr();
DWORD Width = *(DWORD *)(WinMineModuleDlg::Width + Base);
DWORD Height = *(DWORD *)(WinMineModuleDlg::Height + Base);
BYTE* MineMapAddr = (BYTE *)(WinMineModuleDlg::MineMapAddr + Base);


if (Width * Height >= 100 * 100) {
MessageBox(NULL, L"Wrong size for minemap");
return;
}


WCHAR Msg[0x10000];
memset(Msg, 0, 0x10000);
for (int j = 0; j <= Height; j++) {
for(int i=0;i<=Width;i++){
if (i) {
MineMap[j][i - 1] = (*(MineMapAddr + j * 32 + i) == 0x8F ? 1 : 0);

}

}
}
wsprintf(Msg, L"%d*%d\n", Width, Height);
memset(Msg, 0, 0x10000);
for (int i = 0; i < Height; i++) {
for (int j = 0; j < Width; j++) {

if (MineMap[i][j]) {
StrCatW(Msg, L"0 ");
}
else {
StrCatW(Msg, L"1 ");
}

}
StrCatW(Msg, L"\r\n");
}
SetDlgItemText(IDC_EDIT1, Msg);
}

鼠标模拟

我们都知道,Windows 的窗体是通过消息机制去实现的,点了一个地方窗体会接受到对应的一个点击消息,坐标随参数在消息中,现在已经知道了所有雷的位置,那么就通过 PostMessage 这个 API 去进行一个鼠标模拟。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
void WinMineModuleDlg::OnBnClickedButton2()
{
// TODO: 在此添加控件通知处理程序代码
unsigned short pos[2] = { 0,0 };
DWORD Base = GetBaseAddr();
DWORD Width = *(DWORD *)(WinMineModuleDlg::Width + Base);
DWORD Height = *(DWORD *)(WinMineModuleDlg::Height + Base);
HWND hwnd = ::FindWindow(NULL, TEXT("扫雷"));
unsigned short xbase = 20, ybase = 60;

if (hwnd == NULL) {
MessageBox(L"扫雷未打开", L"error");
return;
}
for (int i = 0; i < Height; i++) {
for (int j = 0; j < Width; j++) {
pos[0] = xbase + j * 16;
pos[1] = ybase + i * 16;
if (WinMineModuleDlg::MineMap[i][j]==1) {//有雷
::PostMessage(hwnd, WM_RBUTTONDOWN, MK_RBUTTON, *(int *)pos);
::PostMessage(hwnd, WM_RBUTTONUP, 0, *(int *)pos);
}
else {
::PostMessage(hwnd, WM_LBUTTONDOWN, MK_LBUTTON, *(int *)pos);
::PostMessage(hwnd, WM_LBUTTONUP, 0, *(int *)pos);
}
Sleep(10);
}
}
}

效果: