Skip to Content
Shadow Cheat EngineDev HistoryPtrscan — 调试避坑记录

指针扫描开发避坑记录

代码状态

ptrscan 相关代码已从 master 回滚。计划在支线分支重新开发。


踩过的坑

1. UE4 全局对象在堆上,不在 .data

  • libUE4.so 的 rw- 段只有 84KB(0x15000)
  • GEngine/GWorld/GUObjectArray 等全局对象通过 .data 里的指针间接引用
  • 从 target 反向 BFS 8 层到不了 .data → 0 条有用链
  • 教训: UE4 指针链深度 > 10 层,CE 默认 depth=7 不够

2. 代码段指令被误认为指针

  • ARM64 bl 指令编码 0x94000071xxxxxxxx untag 后落在堆地址范围
  • find_module_bsearch 匹配了 r-x 代码段 → 垃圾 chain
  • 修复: 只认 rw- 段为 static base

3. base_offset 解算混乱

  • 最初存 file-relative offset → _parse_address 匹配不上 segment
  • 改成 segment-relative offset → 客户端不知道是哪个 segment
  • 改成绝对地址 → 重启后失效
  • 正确方案: 存 (module_name, segment_file_offset, offset_within_segment)
  • 或者更简单:存绝对地址 + module name 用于显示,重启后重新扫描

4. BFS 内存爆炸

  • 20M pairs × 16B × 2 (pairs + pairs_tmp) = 640MB → OOM 杀 server
  • pairs_tmp 从未使用(radix sort 没实现,用的 qsort)→ 白分配 320MB
  • BFS level 2 产生 1.7M hits 但 inner loop 不 break → 百万次无用迭代
  • 教训: server 端 malloc 总量必须 < 200MB
  • 修复: 删 pairs_tmp,inner loop break when nxt_level full

5. VMA bitmap vs 5M cap 截断

  • 4.6GB 可读内存 → 48M 候选指针 → 5M cap 只覆盖 10%
  • 提到 10M 后覆盖 ~20%,够找到 level 0 hits
  • 教训: cap 按进程内存大小动态调整

6. QTimer.singleShot 从子线程静默失败

  • worker thread 里用 QTimer.singleShot(0, lambda) 回调主线程 → 永远不执行
  • UI 永远停在 “scanning…”
  • 修复: 用 pyqtSignal.emit(线程安全的跨线程通信)

7. VMA iterator use-after-free(滑动窗口方案)

  • drop mmap_read_lock → copy_to_user → reacquire 后,VMA_ITERATOR 的 maple tree cursor 指向已释放内存
  • 修复: 放弃滑动窗口,改用 vmalloc 一整块(do_scan 模式)

8. untagged_addr 硬编码 39-bit

  • SM8750 可能用 48-bit VA
  • 硬编码 & 0x7FFFFFFFFF 会截断有效指针
  • 修复: 用内核 untagged_addr()

9. offset 顺序争议

  • a4 reviewer 说需要 reverse,a5 说不需要
  • 亲自 trace 确认:不需要 reverse
  • BFS 输出 [closest_to_target, ..., closest_to_base]
  • _resolve_pointerrange(len-1, -1, -1) 先 deref 再加 offset → 正确

实测数据

指标
FPS2 进程内存4.6GB 可读,4096 VMAs
模块数1024
libUE4.so 段数4(r-x 5C5C000 + r-x 4936000 + r— 7F5000 + rw- 15000)
10M pairs 收集~1s
qsort 10M~0.5s
BFS 8 levels~0.5s
target 到对象头 offset0x1440(寄存器 x20 验证)
访问代码libUE4.so+0x6D84C4C (LR)

三个可行方向

方案 A: 内核态 BFS(最省内存)

  • 不预收集 pair,每层直接 kmap 扫内存
  • 内存 O(4MB),时间 O(depth × 2s)
  • 最适合大游戏

方案 B: 正向追踪

  • 从 .data 段已知全局指针出发,正向遍历对象树
  • 需要 UE4 对象布局知识

方案 C: 断点辅助(已验证可行)

  • 对 target 设 read 断点 → 寄存器反推对象指针
  • x20 = 对象基址,offset = 0x1440
  • 递归断点上层对象
  • 手动但精确
Last updated on