🛫 系列文章导航
- 【Frida】 00_简单介绍和使用 https://blog.csdn.net/kinghzking/article/details/123225580
- 【Frida】 01_食用指南 https://blog.csdn.net/kinghzking/article/details/126849567
- 【Frida】02_常见API示例及功能函数封装(snippets)https://blog.csdn.net/kinghzking/article/details/136903974
- 【Frida】 03_初识frida-node https://blog.csdn.net/kinghzking/article/details/136685316
- 【Frida】 04_Frida中使用TypeScript脚本(采坑) https://blog.csdn.net/kinghzking/article/details/136772475
- 【Frida】 05_读取扫雷游戏的数据 https://blog.csdn.net/kinghzking/article/details/136781623
- 【Frida】 06_分析扫雷游戏的数据,显示地雷位置 https://blog.csdn.net/kinghzking/article/details/136685316
- 【Frida】 07_让系统重新绘制指定窗口 https://blog.csdn.net/kinghzking/article/details/136829854
- 【Frida】 08_将目标窗口切换到前台 https://blog.csdn.net/kinghzking/article/details/136837275
- 【Frida】 09_获取软件窗口位置,设置鼠标指针位置 https://blog.csdn.net/kinghzking/article/details/136854052
- 【Frida】10_用鼠标自动标记棋盘上的雷区(一键过关) https://blog.csdn.net/kinghzking/article/details/136854020
- 【frida-实战】“一行”代码教你获取WeGame平台中所有的lua脚本 https://blog.csdn.net/kinghzking/article/details/125590584
- 【Frida-实战】EA游戏平台的文件监控(PsExec.exe提权) https://blog.csdn.net/kinghzking/article/details/130512479
▒ 目录 ▒
- 🛫 系列文章导航
- 🛫 导读
- 开发环境
- 1️⃣ 需求分析
- 获取游戏地图区域位置(软件窗口固定偏移)
- 模拟点击:mouse_click
- 2️⃣ 代码编写测试
- 🛬 文章小结
🛫 导读
开发环境
版本号 | 描述 | |
---|---|---|
文章日期 | 2024-03-17 | |
操作系统 | Win11 - 22H2 | 22621.2715 |
node -v | v20.10.0 | |
npm -v | 10.2.3 | |
yarn -v | 3.1.1 | |
frida-compile | 10.2.1 | 高版本各种异常 |
扫雷程序下载地址 | https://download.csdn.net/download/kinghzking/88979919 | |
课程源码 | https://gitcode.net/kinghzking/MyOpen | 所在目录:/course/frida |
1️⃣ 需求分析
流程:
- 将目标窗口切换到前台(参考历史文章)
- 获取游戏地图区域位置(软件窗口固定偏移)
- 遍历棋盘,按行遍历
- 如果是地雷(0x8F),标记为地雷(右键点击)
- 如果是无雷区(0x0F),左键点击。
- 重绘窗口区域(参考历史文章)
获取游戏地图区域位置(软件窗口固定偏移)
- 我们先看下从基址拿到的内存数据,地址
0x01005340
对应的是(0,0)元素,该元素值为0x10
,表示边界,不需要点击。真正需要点击的是0x01005361
,该元素才是界面上的第一个元素。
有了上面的介绍,我们可以假设一个虚拟的
(0,0)
点,如下图所示,该点相对于窗口的坐标为(6,88)
,所以,起始点的计算如下(lpRect为窗口在屏幕中的左上角位置):
- this.start_x = lpRect.readU32() + 6;
- this.start_y = lpRect.add(4).readU32() + 88;
完整的代码如下:
class L07 {
// 设置鼠标位置_自动点击鼠标
private start_x = 0;
private start_y = 0;
private step = 16;
获取软件窗口位置_设置鼠标指针位置() {
let lpRect = Memory.alloc(4 * 4);
User32.GetWindowRect(this.hWnd, lpRect);
this.start_x = lpRect.readU32() + 6;
this.start_y = lpRect.add(4).readU32() + 88;
console.log("start_x", this.start_x);
console.log("start_y", this.start_y);
}
模拟点击:mouse_click
模拟鼠标操作,我们需要使用Windows API函数
MouseEvent
。该函数是一个Windows API函数,用于模拟鼠标的各种操作,例如移动鼠标、点击鼠标按键等。但需要注意的是,由于其在新版本的Windows中已经被标记为过时,推荐使用更现代的方法,如SendInput
函数来模拟输入事件。
函数原型:void MouseEvent( DWORD dwFlags, // 指定鼠标动作的标志,可以是以下值的组合: // MOUSEEVENTF_ABSOLUTE:指定x和y参数是绝对坐标 // MOUSEEVENTF_LEFTDOWN:模拟鼠标左键按下 // MOUSEEVENTF_LEFTUP:模拟鼠标左键释放 // MOUSEEVENTF_RIGHTDOWN:模拟鼠标右键按下 // MOUSEEVENTF_RIGHTUP:模拟鼠标右键释放 // MOUSEEVENTF_MIDDLEDOWN:模拟鼠标中键按下 // MOUSEEVENTF_MIDDLEUP:模拟鼠标中键释放 // MOUSEEVENTF_WHEEL:模拟鼠标滚轮滚动 // MOUSEEVENTF_XDOWN:模拟鼠标X按钮按下 // MOUSEEVENTF_XUP:模拟鼠标X按钮释放 DWORD dx, // x坐标变化量或绝对坐标(取决于dwFlags) DWORD dy, // y坐标变化量或绝对坐标(取决于dwFlags) DWORD dwData, // 滚轮滚动量 ULONG_PTR dwExtraInfo // 额外信息,一般设为0 );
mouse_click实现逻辑:
User32.SetCursorPos
移动到指定位置- 当为左键时,模拟鼠标左键按下和弹起。
- 当为右键时,模拟鼠标右键按下和弹起。
mouse_click(x: number, y: number, left_click: boolean = true) { User32.SetCursorPos(this.start_x + this.step * x, this.start_y + this.step * y); if (left_click) { User32.MouseEvent(User32.Const.MOUSEEVENTF_LEFTDOWN, 0, 0, 0, User32.GetMessageExtraInfo()); User32.MouseEvent(User32.Const.MOUSEEVENTF_LEFTUP, 0, 0, 0, User32.GetMessageExtraInfo()); } else { User32.MouseEvent(User32.Const.MOUSEEVENTF_RIGHTDOWN, 0, 0, 0, User32.GetMessageExtraInfo()); User32.MouseEvent(User32.Const.MOUSEEVENTF_RIGHTUP, 0, 0, 0, User32.GetMessageExtraInfo()); } }
2️⃣ 代码编写测试
核心的函数都已经封装完毕,最后我们遍历地雷数据,执行点击事件,这里需要注意几点内容:
- for循环中的长宽范围需要
+2
,因为有边界0x10
byte_data
为head的偏移j + 0x20 * i
,这是因为每行数据是固定的0x20(这也是为什么扫雷最大宽为30的原因)- 程序只有初始的时候有两种状态:
0x8F
、0x0F
,如果已经开始游戏的情况,会出现未处理的按钮。
具体代码如下:
class L07 {
run() {
this.将目标窗口切换到前台()
this.获取软件窗口位置_设置鼠标指针位置()
//遍历棋盘,按行遍历
for (let i = 0; i < this.height + 2; i++) {
//按列遍历
let data = [];
for (let j = 0; j < this.width + 2; j++) {
let byte_data = this.head.add(j + 0x20 * i).readU8();
data.push(byte_data.toString(16).padStart(2, "0"));
// 标记地雷
if (byte_data == 0x8F) {
this.mouse_click(j, i, false);
}
// 点击无雷区
if (byte_data == 0x0F) {
this.mouse_click(j, i);
}
}
console.log(data.join(" "));
}
// 重绘窗口区域
this.board_repaint()
}
}
let l07 = new L07();
l07.run();
代码已上传gitcode:https://gitcode.net/kinghzking/MyOpen 。 所在目录为:
/course/frida/11_用鼠标自动标记棋盘上的雷区/index.ts
。
配置脚本如下:
执行
npm run watch11
,以监视模式编译脚本。
执行npm run runx
,运行编译后的脚步,最终1秒过关。
🛬 文章小结
到此为止,我们通过TypeScript方式进行开发,通过扫雷程序,实战了Frida的基本使用。
当然,Frida的路途还很遥远,期待下次再见。
ps: 文章中内容仅用于技术交流,请勿用于违规违法行为。