一、背景
最近玩PUBG遇到各种神仙和低能儿队友,让我很气,所以也就有感而发的这篇外挂编写初体验的文章咯,以前的软件漏洞分析系列还没更新,不过这个也算和那个有点联系啦,就凑个数,哈哈哈,后面会慢慢更新这个系列的啦。废话不多说,进入正题。
二、系统以及环境
1、系统:高端大气上档次的Windows 10教育版64位;
2、Cheat Engine 7.1(虽然说是7.1,但是打开之后显示的版本为6.7)这个软件可以直
接去官网下载,免费的哦。然后下载的时候可能会打不开网页,这个时候就需要点爬虫知识或者web开发的知识,直接去源码中找下载按钮点击之后转到的网址,这里我贴出来:https://duvf52y7btwne.cloudfront.net/v0z!vduhp3ro0/CheatEngine71.exe,在迅雷里自行操作下载即可,嘿嘿嘿。
3、扫雷软件,需要XP系统自带的扫雷软件,因为这个版本的扫雷更适合我这种小白,
毕竟大家都是程序员,都懂嘛,下载链接:https://pan.baidu.com/s/1_3m-04s4itTE24k3b3Kgnw 提取码:3f80,可以自行下载。
三、分析与初步实现作弊
1、打开你的扫雷与Cheat Engine 如下图一所示。
2、用CE检测扫雷程序,如下图二进行操作。
3、打开之后按照下图三红框中的选项一一进行更改,如果是英文的自行翻译或者百度找汉化方法,就这几个单词应该难不倒各位表哥。更改之后再点击红框上面的首次扫描按钮。
4、首次扫描之后在扫雷界面上点击左上角第一个方框,你可能会问我为啥要这么精确?我随便点一个不行吗?还真不是不行,只是我有点将就,表哥想一想自己写这种程序的时候会怎么存储这些雷区与非雷区呢?Bingo,数组,那么数组在内存中存储就要有一个开头咯,我们点击的左上角第一个方框按照正常思维是不是应该为数组中的第一个数据呢?So,有将就的啦。点击之后先按照下图四修改参数(将扫描类型改为“变动的数值)再点击“再次扫描”按钮。
5、最后我们在CE的左边发现了一堆1、2、3、64、65、66,这些是什么玩意儿啊?我们选中第一个“64”之后右键单击,在弹出来的菜单中选择“浏览相关内存区域”,这里你又会想为啥要选这个玩意儿啊?ASCII码出来之后发现64是@,65是A,我们仔细对比一下,你会发现按照我们点出来的空以及雷数提示和这个刚好能对上,同时猜测@就是扫雷中的空,而A代表这片未知区域有一颗雷,如下图五。
6、再看刚刚我们浏览的内存区域,你会发现还真是这样的,如下图六。在其中我用大红框框起来的其实存储的就是整个9X9的扫雷地图,在其中我发现了一点好玩的东西,按照刚刚的猜想64为空,这里却变成了40,其实这里的40不是十进制了,全称应该为0x40(十六进制),而0x40 == 64。依次类推,41、42、43就都能知道意思了,但是却出现了两个不确定的十六进制,分别为8F、0F,那么这两个是什么意思呢?仔细数一数发现8F为10个,我们都知道在9X9的扫雷地图中共有10个雷,这难道是巧合?我觉得不是,点一下不就知道了?这里不放测试8F是不是雷的步骤了,自行测试即可,经过铁头娃的测试发现8F就是个雷。所以直到此时,我们终于掌握了扫雷游戏的关键,同时也知道了雷区开始的地址为0x1005361(要牢记,这是关键),下面我们来写代码实现吧。
四、代码自动化实现
这里我选用的是QT5.12与C++的完美组合进行自动化扫雷的程序编写。直接上代码,这里只展示界面与关键代码。如下图七与代码。如果需要详细代码可以联系作者。嘻嘻嘻。
本查看
void Widget::showData() // 显示雷区功能
{
// 打开程序
HWND hWnd = ::FindWindowW(nullptr, L"Minesweeper");
if (!hWnd)
{
QMessageBox::warning(nullptr,
QStringLiteral("警告"),
QStringLiteral("游戏没有运行!"),
QMessageBox::Ok | QMessageBox::Cancel,
QMessageBox::Ok);
return;
}
DWORD dwPid;
GetWindowThreadProcessId(hWnd, &dwPid);
HANDLE hWinMine = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
if (!hWinMine)
{
QMessageBox::warning(nullptr,
QStringLiteral("警告"),
QStringLiteral("打开进程失败!"),
QMessageBox::Ok | QMessageBox::Cancel,
QMessageBox::Ok);
return;
}
// 读取内存并存储与判断
byte data[24][32] = { 0 };
if (!ReadProcessMemory(hWinMine, reinterpret_cast<LPVOID>(0x1005361), data, sizeof (data), nullptr))
{
QMessageBox::warning(nullptr,
QStringLiteral("警告"),
QStringLiteral("读取内存失败!"),
QMessageBox::Ok | QMessageBox::Cancel,
QMessageBox::Ok);
return;
}
for (int i = 0; i < Row; i++)
{
for (int j = 0; j < Column; j++)
{
char szTmp[10] = { 0 };
if (data[i][j] == 0x8f) // 如果是雷显示1,否则显示-
sprintf_s(szTmp, 10, "%s", "1");
else
sprintf_s(szTmp, 10, "%s", "-");
m_strData += szTmp;
m_strData += " ";
}
m_strData += "n";
}
ui->plainTextEdit->setPlainText(m_strData);
void Widget::killAll() // 秒杀功能
{
// 打开程序
HWND hWnd = ::FindWindowW(nullptr, L"Minesweeper");
if (!hWnd)
{
QMessageBox::warning(nullptr,
QStringLiteral("警告"),
QStringLiteral("游戏没有运行!"),
QMessageBox::Ok | QMessageBox::Cancel,
QMessageBox::Ok);
return;
}
DWORD dwPid;
GetWindowThreadProcessId(hWnd, &dwPid);
HANDLE hWinMine = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
if (!hWinMine)
{
QMessageBox::warning(nullptr,
QStringLiteral("警告"),
QStringLiteral("打开进程失败!"),
QMessageBox::Ok | QMessageBox::Cancel,
QMessageBox::Ok);
return;
}
// 读取内存并存储与判断
byte data[24][32] = { 0 };
if (!ReadProcessMemory(hWinMine, reinterpret_cast<LPVOID>(0x1005361), data, sizeof (data), nullptr))
{
QMessageBox::warning(nullptr,
QStringLiteral("警告"),
QStringLiteral("读取内存失败!"),
QMessageBox::Ok | QMessageBox::Cancel,
QMessageBox::Ok);
return;
}
for (int i = 0; i < Row + 1; i++)
{
for (int j = 0; j < 32; j++)
{
if (data[i][j] != 0x8f) // 利用Windows API模拟鼠标操作
{
int x = (j + 1) * 16 + 5; // 加5是因为左边的外框的宽为5
int y = (i + 1) * 16 + 50; // 加50是因为上边的时间笑脸雷数那一块的高为50。
::PostMessageW(hWnd, WM_LBUTTONDOWN, 0, MAKELPARAM(x, y));
::PostMessageW(hWnd, WM_LBUTTONUP, 0, MAKELPARAM(x, y));
}
}
}
void Widget::on_btnSeckill_clicked()
{ // 秒杀按钮的点击事件,其余的差不多一样。
if (ui->rbtnOwn->isChecked())
{
if (ui->spinRow->text().toInt() > 0 && ui->spinColumn->text().toInt() > 0)
{
Row = ui->spinRow->text().toInt();
Column = ui->spinColumn->text().toInt();
killAll();
}
else
QMessageBox::information(nullptr,
QStringLiteral("提示"),
QStringLiteral("请在左边的自定义难度中输入准确的行数与列数。"),
QMessageBox::Yes | QMessageBox::Cancel,
QMessageBox::Yes);
}
else if (ui->rbtnSimple->isChecked())
killAll();
else if (ui->rbtnGeneral->isChecked())
killAll();
else if (ui->rbtnHard->isChecked())
killAll();
else
QMessageBox::warning(nullptr,
QStringLiteral("警告"),
QStringLiteral("请至少选择一个难度!"),
QMessageBox::Ok | QMessageBox::Cancel,
QMessageBox::Ok);
本文始发于微信公众号(疯猫网络):杂记:SEVEN的游戏外挂初体验
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论