【Frida】07_让系统重新绘制指定窗口

🛫 系列文章导航

🛫 导读

开发环境

版本号描述
文章日期2024-03-17
操作系统Win11 - 22H222621.2715
node -vv20.10.0
npm -v10.2.3
yarn -v3.1.1
frida-compile10.2.1高版本各种异常
扫雷程序下载地址https://download.csdn.net/download/kinghzking/88979919
课程源码https://gitcode.net/kinghzking/MyOpen所在目录:/course/frida

1️⃣ 分析思路

获取窗口句柄

方案一:FindWindow
最顶层符合条件的窗口,通过SpyXX.exe,我们可以传参扫雷获取我们想要的结果,但是当有多个窗口时,只能获得第一个。
在这里插入图片描述

方案二:内存特征方案
除了FindWindow,我们还可以找到程序内存放该句柄的地址,然后从中取出来。

  • 通过SpyXX,我们可以拿到窗口句柄00000000000A1D86
    在这里插入图片描述
  • 然后,通过CE,我们查找该数值(32位应用,4字节的类型),定位到一个基址00005B24
    在这里插入图片描述
    最后,我们重启应用,并重复上述步骤,验证几次,确保基址00005B24可用。

将窗口客户区域重新绘制

InvalidateRect需要用到两个参数:

  • HWND : 固定偏移00005B24中取值。
  • RECT : 需要刷新的区域,窗口的客户区域(也就是除了标题、菜单栏的区域);通过GetClientRect函数获取

函数原型如下:

   BOOL InvalidateRect(
       [in] HWND       hWnd,
       [in] const RECT * lpRect,
       [in] BOOL       bErase
   );
  
   BOOL GetClientRect(
       [in]  HWND   hWnd,
       [out] LPRECT lpRect
   );
  

2️⃣ 代码编码

获取窗口句柄

  • 声明变量,类型为NativePointer。(也可以是number,因为对于windows应用层,句柄就是一个整数值)
  • 初始化:这个跟其它变量初始化一样的,只是最后调用的是readPointer()方法,返回一个指针对象。

class L07 {
  // 声明指针
  private hWnd: NativePointer = ptr(0);

  constructor() {
    // 初始化游戏相关数据
    this.hWnd = this.module_winmine.base.add(0x5B24).readPointer();
  }


编写API接口

为了方便使用,也为了模块化编程,我们将windows API封装到一个类中,并将该类独立到一个文件中。

  • 声明类User32,并默认导出
  • 声明静态变量address_GetClientRect,类型为指针。
  • 通过Module.findExportByName获取接口
  • 根据地址及声明类型,返回NativeFunction对象,用于后期调用本地(C++)函数

GetClientRect为例,代码如下:

// 声明类User32,并默认导出
export default class User32 {
  // 声明静态变量address_GetClientRect,类型为指针。
  private static address_GetClientRect: NativePointerValue | null;
  static GetClientRect(hWnd: NativePointerValue, lpRect: NativePointerValue): number {
    // 若address_GetClientRect为null,则通过Module.findExportByName获取接口
    if (this.address_GetClientRect == null) {
      this.address_GetClientRect = Module.findExportByName("User32.dll", "GetClientRect");
    }
    // 根据地址及声明类型,返回NativeFunction对象,用于后期调用本地(C++)函数
    return new NativeFunction(this.address_GetClientRect!, "bool", ["pointer", "pointer"])(hWnd, lpRect);
  }

}

ps: TS 中的感叹号,称作『非空断言』操作符,Non-null assertion operator
中文理解:
x! 将从 x 值对应的类型集合中排除 null 和 undefined 的类型。比如 x 可能是 string | undefind,则 x! 类型缩窄为 string。

将窗口客户区域重新绘制

  • 引入User32:import User32 from '../winapi/user32'
  • 创建内存对象lpRectMemory.alloc(4 * 4)
  • 获取客户区域:User32.GetClientRect
  • 重新绘制:User32.InvalidateRect

代码如下:

  board_repaint() {
    const lpRect = Memory.alloc(4 * 4);
    User32.GetClientRect(this.hWnd, lpRect);
     
    User32.InvalidateRect(this.hWnd, lpRect, 1);
  }

3️⃣ 测试验证

添加编译和运行脚本

  • frida-compile: 编译代码
  • frida.exe: 运行测试

配置如下所示:

{
  "name": "04_frida_with_typescript",
  "main": "src/index.ts",
  "scripts": {
    "watch07": "frida-compile ./07_让系统重新绘制指定窗口/index.ts -o ./build/07.js -w",
    "runx": "D:/Python/Python371/Scripts/frida.exe -n winmine.exe -l ./build/07.js -q"
  }
}

执行npm run runx运行编译后的脚步./build/07.js,这时候就直接重绘了窗口了,验证成功。
在这里插入图片描述

ps: 文章中内容仅用于技术交流,请勿用于违规违法行为。

  • 21
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夜猫逐梦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值