reverse-skills-max: initial public release (restored, redacted, hardened)

This commit is contained in:
baradok609hecl1pq6qi
2026-10-20 02:17:15 +03:00
commit da24c2bd20
2227 changed files with 443931 additions and 0 deletions

View File

@@ -0,0 +1,120 @@
---
name: re-frida-script-author
type: atomic
description: >
Frida 脚本生成方法论:目标特征 → 模板选择 → 改写 → 验证。独立于执行插桩(re-frida)。
触发词:生成Frida脚本、写hook脚本、frida脚本怎么写、写个hook、脚本生成。
capabilities: [frida-instrumentation]
---
# Frida 脚本生成
## 何时使用 / 何时不用
- 用:需要新脚本时——拦截(加密/网络/文件)、绕过(检测/固定)、追踪(JNI/方法调用)
- 用:现成模板没有的变体——按 [[re-frida/frida-scripts]] 模板改写出目标专用脚本
- 不用:执行现成脚本 → 转 [[re-frida]](本技能只产出脚本,运行与进程管理在 re-frida)
- 不用:反检测对抗面整体分析 → [[re-analyze/anti-dynamic-workflow]]
- 不用:纯静态可解的问题——先走 [[re-apk]] / [[re-ghidra]] 静态路径,动态是最后手段
- 不用:目标不可达(无设备/无 root/无越狱、加固拦注入)——先解决环境([[re-frida]] 工具准备),不硬写脚本
## 工具准备
### frida-tools(脚本编写与运行验证)
- 推荐(upstream 路径): `python -m pip install -U frida-tools`,建议用 venv 或 pipx 隔离环境
- 第三方渠道: `brew install frida` 等非 upstream 渠道,安装前核验版本与维护状态,不与 pip 路径等同看待
- 验证: `frida --version`、`frida-ps -U`(连设备后)
- 注意: `frida`(运行时)与 `frida-tools`(CLI)版本需配套;跨大版本升级(16 → 17)有 API 移除,写法差异见 [[gotchas]] 版本组
### python3(配合脚本调试)
- 各平台同 [[re-python]] 工具准备
### 反编译辅助(侦察用)
- jadx([[re-apk]])静态出类/方法清单;Ghidra/IDA([[re-ghidra]] / [[re-ida]])native 侧符号与调用点定位
- 验证: 能按包名列出目标类与方法签名
### 目标设备/模拟器
- Android 真机/模拟器 + frida-server(至少与主机 frida 同 major,安装见 [[re-frida]] 工具准备);桌面目标直接本机
- iOS 越狱环境 frida-server 见 [[re-ios-jb]]
## 操作步骤
按「探 → 选 → 改 → 验」四步,先探后写,不猜。每步产物(特征清单/脚本/验证输出)记录路径 + sha256(见 [[re-triage]]),供报告引用。
1. **目标侦察**:
- 静态:目标包名/类名/关键 API([[re-apk]] jadx 输出)、加固商特征([[re-mobile-pack]])、Flutter/RN 混合结构([[re-hybrid-app]])
- 动态基线:原样跑一次抓崩溃与日志(崩溃特征 → 保护机制对照,见 [[re-analyze/anti-dynamic-workflow]])
- 产出:目标特征清单——检测点/目标 API 全限定名/输入输出形态/方法 overload 数
- 不确定的类名/方法名先小脚本枚举(`Java.enumerateLoadedClasses` / `Java.use(...).overloads`),不猜名字
- 侦察结论记入 [[re-analyze/analysis-contract]] 契约字段(包名/类名/API 清单),供脚本与报告复用
2. **模板选择**:按特征清单对照 [[re-frida/frida-scripts]] 模板表:
| 目标特征 | 模板 |
|---|---|
| HTTPS 抓包被 TLS 加密(BoringSSL) | TLS 密钥日志(SSLKEYLOGFILE) |
| 证书固定挡抓包 | SSL 固定绕过(TrustManager/CertificatePinner) |
| 加密算法/密钥要提取 | 加密拦截(Cipher/SecretKeySpec 全 overload) |
| 加固/运行时解密 | DEX dump(类加载点)/ SO dump |
| 双向 TLS 客户端证书 | keystore p12 导出 |
| JNI 动态注册要还原 | RegisterNatives + 汇聚点双 hook |
| 反调试/检测拦截 | 检测绕过表(root/属性/文件/命令) |
- 特征不匹配任何模板 → 组合改写(多个模板拼装)而非从零写
- 选完模板先读模板内已知边界注释([[re-frida/frida-scripts]]),避免重复踩坑
3. **改写**:
- 替换占位符:包名/类名/方法名(精确匹配,Java 全限定名)
- overload 精确匹配:先 `overloads` 枚举再逐个定义或按参数类型选(见坑 1)
- 结构规范:Java 操作包在 `Java.perform`;保存 original 引用、带原 `this` 调用;`Interceptor.attach` 里 onEnter 用 `this.context` 读寄存器,**改返回值只能在 onLeave 用参数 `retval.replace(...)`**——`this` 上只有 returnAddress/context/errno/lastError/threadId/depth,没有 `this.returnValue`;且 retval 跨调用复用,需留存时先 `ptr(retval.toString())` 复制
- 输出统一 JSON(可打印 ASCII + hex 双格式)经 `send()` 传出;每个 hook 主体 `try/catch`,错误发消息不静默
- 同一类多 hook 合并进一个 `.implementation`(缓存静默覆盖,见坑 2)
- 骨架参考(Java + native 双面最小结构,按目标裁剪):
```js
Java.perform(function () {
var Cls = Java.use("com.target.Cls");
Cls.method.overload("java.lang.String").implementation = function (s) {
try { send({ type: "call", arg: s ? s.toString() : null }); }
catch (e) { send({ type: "error", msg: String(e) }); }
return this.method(s); // 调原实现(保留原 this)
};
});
Interceptor.attach(
Process.getModuleByName("libtarget.so").getExportByName("func"),
{ onEnter: function (args) { send({ type: "native", arg0: args[0].toInt32() }); } }
);
```
- native 侧:导出符号优先(`Process.getModuleByName(...).getExportByName`;旧写法 `Module.getExportByName` 见 [[gotchas]] 版本组);非导出函数用调用点 hook 或内存特征定位
4. **验证**:
```sh
frida -U -f <pkg> -l script.js --pause # spawn + 停在早期代码前
frida -U -f <pkg> -l script.js -o out.json # 输出存文件(大流量/批量时)
```
- 验证清单:hook 挂载成功(无报错输出)→ 触发目标路径(操作 app 或复现调用)→ 输出与静态分析一致 → 重复触发输出稳定
- 输出 JSON 可解析、目标行为符合预期(拦截到目标调用/绕过生效)
- 失败 → 回步骤 2 重选模板或细化特征;崩溃 → 按 [[re-analyze/anti-dynamic-workflow]] 崩溃对照表定位;输出为空 → 见 [[decision-tree]] 排查分支
## 跨域联合
- [[re-frida]]:脚本执行(本技能产出 → re-frida 运行)
- [[re-mobile]] / [[re-android-native]]:移动目标场景衔接
- [[re-analyze/anti-dynamic-workflow]]:检测面与崩溃迭代法
- [[re-frida/frida-scripts]]:模板素材库(re-frida references)
- [[re-analyze/analysis-contract]]:脚本输出按数据契约消费(证据存档)
- [[re-ios-jb]]:iOS 越狱环境执行侧
## 常见坑与陷阱
- **overload 不匹配静默失效**:现象——hook 无输出;原因——目标方法 overload 签名与定义不符;对策——先 `Java.use(...).<method>.overloads` 列出全部重载再选
- **Java.use 缓存覆盖**:现象——多个 hook 只生效最后一个;原因——同类多次 `.implementation` 赋值静默覆盖;对策——合并进一个 hook
- **参数索引版本相关**:现象——native 参数读错;原因——目标版本字段/参数位次变化;对策——对照目标符号核实,不照搬经验值
- **不侦察就写脚本**:现象——脚本对不上目标;原因——跳过步骤 1;对策——先探后写
- **绕过类脚本只观察不持久化**:现象——测试后目标状态被改;原因——脚本含写操作;对策——脚本只做读取/日志,绕过仅用于观察(红线)
- **类未加载时 hook 不生效**:现象——类存在但无输出;原因——懒加载/加固延迟加载;对策——`Java.choose` 或对类加载点下 hook([[re-analyze/anti-dynamic-workflow]])
- **版本相关 API 报错**:现象——`Module.getExportByName is not a function`;原因——Frida 17 移除静态 Module 查找 API;对策——按 [[gotchas]] 版本组迁移写法
- 模板选择分支与验证排查树见 [[decision-tree]];版本差异与边界见 [[gotchas]];全部在沙箱内执行([[re-analyze/platform-tips]] 最高原则),脚本输出按 [[re-analyze/analysis-contract]] 契约存档

View File

@@ -0,0 +1,73 @@
# Frida 脚本生成决策树与证据分级
## 场景决策树(入口 → 分支)
### 1. 目标类型分支(第一级)
```
目标对象
├─ Java/ART 层(类/方法级逻辑)
│ └─ 模板族:Java hook(Java.use + implementation)
├─ Native 层(JNI / so 内逻辑)
│ ├─ 导出函数 → Interceptor.attach 锚定导出符号
│ └─ 非导出/静态函数 → 调用点 hook 或内存特征扫描
├─ Flutter / React Native 混合([[re-hybrid-app]])
│ └─ 先按混合结构定位 JS/Dart 侧逻辑,再决定 hook 面
└─ 横切能力(网络/加密/存储)→ 直接对照模板表(步骤 2)
```
### 2. 目标行为分支(第二级)
```
要做什么
├─ 拦截数据(参数/返回值/明文)
│ ├─ TLS 加密 → SSLKEYLOGFILE 模板
│ ├─ 证书固定挡抓包 → SSL 固定绕过模板
│ └─ 加密算法/密钥 → Cipher 拦截模板(全 overload)
├─ 绕过检测/限制
│ ├─ root/调试器/模拟器检测 → 检测绕过表
│ └─ 证书/签名校验 → 校验绕过(只观察不持久化,红线)
├─ 还原逻辑
│ ├─ JNI 动态注册 → RegisterNatives + 汇聚点双 hook
│ └─ 运行时解密内容 → DEX/SO dump 模板
└─ 追踪调用序列 → 日志型 hook(不中断不改值)
```
### 3. 验证失败排查分支(无输出时)
```
hook 无输出
├─ 脚本控制台有异常 → 修语法/API 版本问题([[gotchas]] 版本组)
├─ 无报错但无输出
│ ├─ 类未加载 → Java.choose / 类加载点 hook([[re-analyze/anti-dynamic-workflow]])
│ ├─ overload 不匹配 → 枚举 overloads 重选
│ ├─ 目标路径未触发 → 操作 app 触发后再观察
│ └─ 多 hook 互相覆盖 → 合并进一个 implementation
└─ 进程未注入/连接失败 → 查设备连接与 frida-server 版本([[re-frida]])
```
## 证据分级表
| 级别 | 证据形态 | 示例 | 用途 |
|---|---|---|---|
| A 强 | 动态拦截 + 静态交叉闭合 | 拦截到目标调用,参数/返回值与 jadx 反编译一致 | 可下结论(密钥、调用链、明文格式) |
| B 中 | 动态输出可解析但无静态交叉 | 只有 hook 输出,静态侧对不上或缺失 | 可下「疑似」,标注缺静态侧 |
| C 弱 | 静态推断 | 仅凭反编译推断调用存在,未动态确认 | 只支持「待验证」 |
| 反证 | 良性解释 | 检测到调用但确认属正常功能路径 | 记录并存档 |
- 脚本输出按 [[re-analyze/analysis-contract]] 契约存档:时间戳、进程、hook 点、参数/返回值、原始 hex
- 绕过类结论(检测绕过成功/固定绕过成功)单独标注,与数据类结论分开陈述
## 实现教训(内化)
- 先探后写:类名/方法名/overload 数先枚举再写死,脚本里不猜目标符号
- Java 操作包在 `Java.perform`;回调线程场景注意线程切换(`Java.scheduleOnMainThread`)
- 输出结构化 JSON(ASCII + hex)比打印字符串可消费性好;`try/catch` 包住每个 hook 主体,错误发消息不静默
- hook 最小化:只拦需要的方法——全类 hook 引入性能与稳定性噪声,验证时先小范围
- 版本敏感 API 先查当前 frida 版本:Frida 17 起静态 Module 查找 API 移除([[gotchas]])
- 可复现性:占位符模板 + 目标特征清单一起存档,脚本可重放
## 使用注意
- 动态执行在沙箱内([[re-sandbox]] / [[re-analyze/platform-tips]] 最高原则)
- 绕过类脚本只观察不持久化(红线);输出按 [[re-analyze/analysis-contract]] 契约存档

View File

@@ -0,0 +1,36 @@
# Frida 脚本方法论坑与边界
## 版本差异组(最容易踩)
- **Frida 17 移除静态 Module 查找 API**:`Module.getExportByName()` / `Module.findExportByName()` / `Module.enumerateExports()` 等在 Frida 17 起不再是函数——脚本运行报 `TypeError: not a function`;迁移写法:全局符号用 `Module.getGlobalExportByName("open")` / `Module.findGlobalExportByName("open")`,指定模块用 `Process.getModuleByName("libc.so").getExportByName("open")`;动笔前先 `frida --version` 确认版本再选写法
- **frida / frida-tools / frida-server 版本需配套**:设备端 frida-server 与主机端 frida 大版本不一致会连接失败或行为异常;升级任一后先 `frida-ps -U` 验证
- **gadget 与 server 模式差异**:无 root 场景用 frida-gadget(集成进 app),hook 时机比 server 模式早(app 启动早期);同一脚本两种模式行为可能不同,按运行模式分别验证
- **行为以当前版本为准**:`Interceptor` 回调细节、`Java.perform` 使用时机、`Java.choose` 语义在 16/17 间有变化——异常先查对应版本官方文档,不照旧教程
## Java 层坑组
- **overload 不匹配静默失效**:`.implementation` 的形参数量/类型必须与目标 overload 一致,否则 hook 挂上但不生效——先 `overloads` 枚举再写
- **同类多 hook 缓存覆盖**:同一类多次 `.implementation` 赋值只有最后一次生效——合并进一个 hook 内分支
- **类未加载**:目标类懒加载/加固延迟加载时,脚本启动即 `Java.use` 抛 ClassNotFoundException——用 `Java.choose`(已实例对象)或延迟到类加载后;加固场景先处理脱壳([[re-mobile-pack]])
- **混淆后类名漂移**:ProGuard/R8 混淆的 release 包类名/方法名与 debug 包不同——以目标包实际字符串为准(jadx 输出),不照抄其他版本经验值
- **回调线程**:在非 Java 线程直接调 Java 方法可能异常——主线程操作用 `Java.scheduleOnMainThread` 调度
## Native 层坑组
- **导出符号找不到**:目标函数未导出(`static`/strip)时 `getExportByName` 返回 null——改用调用点 hook(从 Java 层 JNI 调用进入)或 `Memory.scan` 内存特征定位
- **多 ABI / 多 so 同名**:同一函数名在多个 so 中存在(多 ABI 或版本化库)——hook 前确认目标模块实例,按模块名锚定
- **字符串编码**:native 返回值常为 UTF-8(Java 侧是 UTF-16)——按调用点语义选解码方式,别统一按一种编码解析
- **参数寄存器按平台**:ARM64 前 8 参在 x0-x7;x86_64 是 rdi/rsi/rdx/rcx/r8/r9——onEnter 里 `this.context` 按目标架构读取,对照目标核实不照搬
## 反例与边界组
- **无输出 ≠ 未 hook**:目标路径未触发(需操作/定时/特定输入)、权限弹窗拦截、进程已崩溃——先确认触发条件再判「hook 失败」
- **绕过成功 ≠ 目标无防护**:绕过只对本次运行有效,重启/更新后可能失效——结论标注版本范围与运行模式
- **只观察不持久化**:绕过类脚本若含写操作会留下测试痕迹——脚本只读/日志,写操作单独评估(红线)
- **全类 hook 的反例**:高频方法全类 hook 会让 app 变慢或崩溃——hook 面最小化,先小范围验证再扩大
- **模板是起点不是终点**:[[re-frida/frida-scripts]] 模板按目标版本改写,直接套旧模板可能因 API 差异失效
## 使用注意
- 动态执行在沙箱内([[re-analyze/platform-tips]] 最高原则);脚本与输出按 [[re-analyze/analysis-contract]] 契约存档
- 结论按 [[decision-tree]] 证据分级标注(A/B/C/反证),绕过类与数据类结论分开陈述