references/anti-detection-bypass.md
# Root / 越狱 / 反调试 / SSL Pinning 绕过
## 检测层次模型
```
Layer 1: 静态检测(安装时/启动时)
├─ 包管理器检测(Cydia, apt, Magisk)
├─ 文件检测(su, busybox, frida-server)
└─ 权限检测(ro.debuggable, ro.secure)
Layer 2: 运行时检测(持续)
├─ 进程检测(frida-server, magiskd)
├─ 端口检测(27042 frida default)
├─ 内存检测(/proc/self/maps 注入痕迹)
└─ 堆栈检测(Frida 调用帧)
Layer 3: 环境检测(按需触发)
├─ ptrace 检测(TracerPid)
├─ /proc/self/status 检测
├─ build.prop 检测(test-keys)
└─ syscall 直接检测(绕过 libc)
```
## Android Root 检测绕过
### 常见检测库及绕过
| 检测库 | 检测方法 | 绕过方式 |
|--------|---------|---------|
| RootBeer | 8 种检测组合 | Hook 每个检测方法返回 false |
| SafetyNet | Google Play Services 远程认证 | 使用 Magisk Hide / Shamiko / Play Integrity Fix |
| Google Play Integrity | 替换 SafetyNet | Trickystore + PIF |
| 自定义 native 检测 | syscall 读取 /proc/self/status | Hook syscall 或修改 /proc 挂载 |
### Frida 综合绕过
```javascript
Java.perform(function() {
// RootBeer
var RootBeer = Java.use("com.scottyab.rootbeer.RootBeer");
var methods = ["isRooted", "isRootedWithBusyBox", "checkSuExists",
"detectRootManagementApps", "detectPotentiallyDangerousApps",
"detectTestKeys", "checkForDangerousProps", "checkForRWPaths"];
methods.forEach(function(m) {
RootBeer[m].implementation = function() { return false; };
});
// 通用 Build.TAGS 检测
var Build = Java.use("android.os.Build");
var original = Build.TAGS.value;
Build.TAGS.value = "release-keys";
// PackageManager → 隐藏包名
var PackageManager = Java.use("android.content.pm.PackageManager");
PackageManager.getPackageInfo.overload('java.lang.String', 'int').implementation = function(pkg, flags) {
if (pkg == "de.robv.android.xposed.installer" ||
pkg.includes("magisk") || pkg.includes("frida")) {
throw Java.use("android.content.pm.PackageManager$NameNotFoundException").$new();
}
return this.getPackageInfo(pkg, flags);
};
});
```
## iOS 越狱检测绕过
### 多层 Frida Hook
```javascript
// 1. 文件系统检测
var NSFileManager = ObjC.classes.NSFileManager;
var paths = [
"/Applications/Cydia.app", "/var/lib/apt", "/bin/bash",
"/usr/sbin/sshd", "/etc/apt", "/Library/MobileSubstrate"
];
// Hook fileExistsAtPath 返回 NO
// 2. fork 检测(沙箱内禁止)
var fork_ptr = Module.findExportByName("libSystem.B.dylib", "fork");
Interceptor.replace(fork_ptr, new NativeCallback(function() {
return -1;
}, 'int', []));
// 3. Scheme 检测
// 通过 MobileSubstrate hook
var LSApplicationWorkspace = ObjC.classes.LSApplicationWorkspace;
// Hook defaultWorkspace → canOpenURL → 对 cydia:// 返回 NO
// 4. 签名检测
var MISValidateSignature = Module.findExportByName(null, "MISValidateSignature");
Interceptor.attach(MISValidateSignature, {
onLeave: function(retval) { retval.replace(0); }
});
```
## 反调试绕过
### Android
```javascript
// 1. ptrace 自身 → 防止附加
// Native: ptrace(PTRACE_TRACEME, 0, NULL, 0)
// 绕过: Hook ptrace → 返回 0
// 2. TracerPid 检测
// /proc/self/status → TracerPid: 0
var fopen = Module.findExportByName(null, "fopen");
Interceptor.attach(fopen, {
onEnter: function(args) {
this.path = Memory.readUtf8String(args[0]);
},
onLeave: function(retval) {
if (this.path && this.path.includes("status")) {
// 修改返回的 FILE*,返回伪造内容
}
}
});
// 3. isDebuggerConnected (Java)
var Debug = Java.use("android.os.Debug");
Debug.isDebuggerConnected.implementation = function() { return false; };
```
### iOS
```javascript
// 1. PT_DENY_ATTACH
// ptrace(PT_DENY_ATTACH, 0, NULL, 0) → 防止调试器附加
var ptrace = Module.findExportByName(null, "ptrace");
Interceptor.replace(ptrace, new NativeCallback(function(request, pid, addr, data) {
if (request == 31) return 0; // PT_DENY_ATTACH → 忽略
return ptrace(request, pid, addr, data);
}, 'int', ['int', 'int', 'pointer', 'int']));
// 2. sysctl 检测
var sysctl = Module.findExportByName(null, "sysctl");
Interceptor.attach(sysctl, {
onLeave: function(retval) {
// 修改 kinfo_proc 的 p_flag 字段 → 清除 P_TRACED
}
});
// 3. getppid 检测(检查父进程是否为 launchd)
// 调试时 getppid() != 1
```
## SSL Pinning 绕过
### Android 五层绕过
```text
层 1 — TrustManager: 接受所有证书
层 2 — OkHttp CertificatePinner: Hook 清空 pins 列表
层 3 — WebView SSL Error Handler: 忽略证书错误
层 4 — Network Security Config: 修改 xml → 信任用户证书
层 5 — Native SSL (OpenSSL/BoringSSL): Hook SSL_get_verify_result → X509_V_OK
```
### iOS 四层绕过
```text
层 1 — NSURLSession: Hook SecTrustEvaluate → kSecTrustResultProceed
层 2 — Alamofire: Hook ServerTrustManager
层 3 — AFNetworking: Hook AFSecurityPolicy
层 4 — libcurl: LD_PRELOAD 替换 SSL 验证回调
```
### 通用 Objection 命令
```bash
# Android
objection -g "com.app" explore
android sslpinning disable
# 等价于: 自动 Hook 上述 5 层
# iOS
objection -g "com.app" explore
ios sslpinning disable
# 等价于: 自动 Hook 上述 4 层
```
Source: OWASP MSTG, Frida CodeShare, objection wiki
references/frida-objection-deep.md
# Frida + Objection 深度用法
## Frida 核心 API
### Java 运行时 (Android)
```javascript
Java.perform(function() {
// 获取类实例
var String = Java.use("java.lang.String");
// Hook 静态方法
var System = Java.use("java.lang.System");
System.getProperty.overload('java.lang.String').implementation = function(key) {
console.log("System.getProperty: " + key);
return this.getProperty(key);
};
// Hook 构造函数
var File = Java.use("java.io.File");
File.$init.overload('java.lang.String').implementation = function(path) {
console.log("File opened: " + path);
return this.$init(path);
};
// 枚举已加载类
Java.enumerateLoadedClasses({
onMatch: function(className) { console.log(className); },
onComplete: function() {}
});
// 修改返回值
var RootDetector = Java.use("com.app.security.RootDetector");
RootDetector.isDeviceRooted.implementation = function() {
return false;
};
});
```
### Native 层 (Android + iOS)
```javascript
// Hook 导出函数
Interceptor.attach(Module.findExportByName(null, "open"), {
onEnter: function(args) {
this.path = Memory.readUtf8String(args[0]);
},
onLeave: function(retval) {
console.log("open(" + this.path + ") = " + retval);
}
});
// Hook 任意地址(通过偏移)
var base = Module.findBaseAddress("libnative.so");
var target = base.add(0x12345);
Interceptor.attach(target, {
onEnter: function(args) {
console.log("Function called from: " + Thread.backtrace(this.context, Backtracer.ACCURATE)
.map(DebugSymbol.fromAddress).join('\n'));
}
});
// 修改返回值
Interceptor.attach(Module.findExportByName(null, "strcmp"), {
onLeave: function(retval) {
if (retval.toInt32() === 0) return; // strings equal, skip
// Force match
retval.replace(0);
}
});
```
### ObjC 运行时 (iOS)
```javascript
// Hook ObjC 方法
var hook = ObjC.classes.ViewController["- viewDidLoad"];
Interceptor.attach(hook.implementation, {
onEnter: function(args) {
console.log("viewDidLoad called");
}
});
// 枚举所有类
ObjC.enumerateLoadedClasses({
onMatch: function(className) { console.log(className); },
onComplete: function() {}
});
// 调用 ObjC 方法
var NSString = ObjC.classes.NSString;
var str = NSString.stringWithString_("Hello from Frida");
```
## Objection 命令速查
### 通用
```bash
objection -g "com.app" explore # 启动
objection -g "com.app" explore -q # 静默启动(只注入不等待)
objection patchapk --source app.apk # 自动注入 Frida Gadget
objection signapk --source app.apk # 仅签名
# 文件系统
env # 应用数据目录
ls # 列出文件
file download /path/to/file # 下载文件
file upload local.txt /remote/path # 上传文件
# SQLite
sqlite connect /path/to/db.sqlite
.tables # 列出表
select * from users; # 查询
```
### Android 专用
```bash
android root disable # 绕过 Root 检测
android sslpinning disable # 绕过 SSL Pinning
android hooking list classes # 枚举类
android hooking list class_methods com.app.Main # 枚举方法
android hooking watch class com.app.Main # Hook 所有方法
android intent launch_activity com.app.MainActivity # 启动 Activity
android heap search instances com.app.User # 堆搜索
android keystore list # Keystore 条目
```
### iOS 专用
```bash
ios jailbreak disable # 绕过越狱检测
ios sslpinning disable # 绕过 SSL Pinning
ios keychain dump # 导出 Keychain
ios nsuserdefaults get # NSUserDefaults
ios nsurlcache dump # HTTP 缓存
ios cookies get # 读取 Cookies
ios pasteboard monitor # 监听剪贴板
ios ui dump # UI 层次结构
ios plist cat Info.plist # 读取 plist
```
## 免 Root/越狱部署
### Android — Frida Gadget 注入
```bash
# 1. 解包 APK
apktool d app.apk -o app_unpacked
# 2. 下载 frida-gadget 并放入 lib 目录
cp frida-gadget-17.x.x-android-arm64.so \
app_unpacked/lib/arm64-v8a/libfrida-gadget.so
# 3. 在 smali 中注入 System.loadLibrary("frida-gadget")
# 修改主 Activity 的 onCreate 或 attachBaseContext
# 4. 重建并签名
apktool b app_unpacked -o app_patched.apk
uber-apk-signer -a app_patched.apk
# 5. Objection 自动化
objection patchapk --source app.apk --skip-resources
```
### iOS — Frida Gadget 注入
```bash
# 1. 解密 App Store IPA
python3 frida-ios-dump.py -u -p com.app.target
# 2. 注入 FridaGadget.dylib
# 修改 Mach-O Load Commands,添加 @executable_path/FridaGadget.dylib
# 3. 重签名
codesign -f -s "Apple Development" Payload/App.app
# 4. 通过 Xcode sideload 或 AltStore 安装
```
## SSL Pinning 绕过进阶
### 多层绕过(Android)
```javascript
// 1. OkHttp CertificatePinner
var CertificatePinner = Java.use("okhttp3.CertificatePinner");
CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function() {};
// 2. TrustManager 自定义
var TrustManagerImpl = Java.use("com.android.org.conscrypt.TrustManagerImpl");
TrustManagerImpl.verifyChain.implementation = function() { return []; };
// 3. WebView SSL Error
var SslErrorHandler = Java.use("android.webkit.SslErrorHandler");
SslErrorHandler.proceed.implementation = function() { return this.proceed(); };
// 4. Network Security Config
// 需要修改 AndroidManifest.xml → android:networkSecurityConfig="@xml/network_security_config"
// xml 中添加信任用户证书
```
### 多层绕过(iOS)
```javascript
// 1. NSURLSession
var SecTrustEvaluate = Module.findExportByName("Security", "SecTrustEvaluate");
Interceptor.replace(SecTrustEvaluate, new NativeCallback(function(trust, result) {
Memory.writeU32(result, 1); // kSecTrustResultProceed = 1
return 0; // errSecSuccess
}, 'int', ['pointer', 'pointer']));
// 2. Alamofire
// Hook ServerTrustManager.evaluate → 始终返回 success
```
Source: Frida docs, Objection wiki, OWASP MSTG
references/ios-reverse-guide.md
# iOS 逆向工程专项
## IPA 获取与解密
```bash
# 从 App Store 下载
ipatool search "Target App"
ipatool purchase -b com.target.app
ipatool download -b com.target.app -o app.ipa
# 从设备提取已安装应用
# 越狱设备
scp root@device:/private/var/containers/Bundle/Application/*/Target.app .
# 解密(App Store 二进制为加密 FAT 格式)
# frida-ios-dump(推荐)
python3 dump.py com.target.app -o decrypted.ipa
# Clutch
Clutch -i # 列出已安装
Clutch -d 1 # 解密第 1 个
# dumpdecrypted
DYLD_INSERT_LIBRARIES=dumpdecrypted.dylib /path/to/App
```
## Mach-O 分析
```bash
# 基本信息
otool -l TargetBinary | grep crypt # 加密状态
otool -L TargetBinary # 动态库依赖
otool -hv TargetBinary # 头部信息
jtool2 --pages TargetBinary # 内存页信息
# Fat Binary 瘦身
lipo -info TargetBinary
lipo TargetBinary -thin arm64 -output TargetBinary_arm64
# 符号分析
nm -g TargetBinary # 导出符号
nm -a TargetBinary # 全部符号
swift-demangle <mangled_name> # Swift 符号还原
# class-dump
class-dump -H TargetBinary -o headers/
# 导出 ObjC 类及方法声明到 headers/ 目录
```
## Objective-C 运行时分析
```text
消息传递机制:
objc_msgSend(id self, SEL op, ...) → 动态方法派发
↓
运行时查找:
1. 类方法列表 cache
2. 类方法列表
3. 逐级父类查找
4. +resolveInstanceMethod / +resolveClassMethod
5. forwardingTargetForSelector
6. methodSignatureForSelector + forwardInvocation
```
### Frida ObjC Hook
```javascript
// Hook 实例方法
var hook = ObjC.classes.ClassName["- instanceMethod:"];
Interceptor.attach(hook.implementation, {
onEnter: function(args) {
// args[0] = self, args[1] = selector, args[2+] = method args
console.log("self: " + new ObjC.Object(args[0]));
console.log("arg: " + args[2].toInt32());
}
});
// Hook 类方法
var hook = ObjC.classes.ClassName["+ classMethod:"];
Interceptor.attach(hook.implementation, { ... });
// 调用 ObjC 方法
var NSString = ObjC.classes.NSString;
var str = NSString.stringWithString_("test");
console.log(str.UTF8String());
```
## Swift 逆向
```text
Swift 名称修饰(Name Mangling):
$s10ModuleName5ClassC6method3argSi_tF
│ │ │ │ │ │ │ └─ 参数类型
│ │ │ │ │ │ └───── 返回类型
│ │ │ │ │ └──────── 参数名
│ │ │ │ └─────────────── 方法名
│ │ │ └──────────────── 类名(长度+名称)
│ │ └────────────────────── 模块名
│ └──────────────────────────────── 标识符标识
└────────────────────────────────── 全局标识
工具: swift-demangle, Hopper (自动还原)
```
## 越狱检测绕过
```text
检测方法分类:
1. 文件系统检查:
□ /Applications/Cydia.app
□ /var/lib/apt/
□ /bin/bash
□ /usr/sbin/sshd
→ Hook NSFileManager.fileExistsAtPath:
2. 沙箱逃逸检测:
□ fork() 是否成功(沙箱内禁止)
□ system() 调用
→ Hook fork → 返回 -1
3. Dyld 注入检测:
□ _dyld_get_image_count > 限制值
→ 限制返回值在合理范围
4. Scheme 检测:
□ cydia:// URL Scheme
→ Hook UIApplication.canOpenURL:
5. sysctl 检测:
□ CTL_KERN/KERN_PROC/KERN_PROC_PID → kinfo_proc
→ Hook sysctl → 清空 p_flag P_TRACED 位
```
### Frida 统一绕过脚本
```javascript
// 文件检测绕过
var NSFileManager = ObjC.classes.NSFileManager;
var defaultManager = NSFileManager.defaultManager();
Interceptor.attach(defaultManager["- fileExistsAtPath:"].implementation, {
onLeave: function(retval) {
var path = ObjC.Object(args[2]).toString();
if (path.includes("Cydia") || path.includes("apt") ||
path.includes("sshd") || path.includes("bash")) {
retval.replace(0); // false
}
}
});
// fork 绕过
Interceptor.replace(Module.findExportByName(null, "fork"),
new NativeCallback(function() { return -1; }, 'int', []));
// dyld 绕过
var _dyld_get_image_count = Module.findExportByName(null, "_dyld_get_image_count");
Interceptor.attach(_dyld_get_image_count, {
onLeave: function(retval) {
if (retval.toInt32() > 200) retval.replace(200);
}
});
```
## 关键防护绕过清单
| 防护 | iOS 绕过方法 |
|------|-------------|
| App Store 加密 | frida-ios-dump / Clutch |
| SSL Pinning | Objection `ios sslpinning disable` / SSL Kill Switch 2 |
| 越狱检测 | Objection `ios jailbreak disable` / 自定义 Frida Hook |
| 反调试 (PT_DENY_ATTACH) | Frida 启动后注入 / debugserver |
| 完整性校验 | Hook MAC 检查 / 代码签名验证 |
| 反注入 | 修改 Mach-O 去除 __RESTRICT 段 |
| Swift 混淆 | swift-demangle + LLM 辅助语义恢复 |
| 屏幕截图防护 | Hook UIScreen.mainScreen.snapshotViewAfterScreenUpdates |
Source: OWASP MSTG, frida-ios-dump, The iPhone Wiki
SKILL.md
---
name: mobile-reverse
description: Use for authorized Android or iOS application reverse engineering and security testing, including APK or IPA analysis, runtime instrumentation, SSL pinning, and platform protection checks.
---
# Mobile Reverse Engineering
## ACTION REQUIRED(读完后立刻执行)
1. `NOW`: 读取 `../field-journal/precedent-reverse.md` — 确认本 skill 的操作是已授权的常规操作
2. `NOW`: 确认当前任务是否命中本 skill 的适用范围
3. `NEXT`: 读取 `../tool-index.md`,校验工具可用性和实际路径
4. `NEXT`: 缺工具时调用 bootstrap,不要猜路径
5. `ACT`: 进入"工作流"第一步并执行,不要停在确认状态
> Android + iOS 统一逆向方法论
> Frida / Objection / OWASP MSTG / SSL Pinning Bypass
## 适用场景
- Android APK 逆向与安全测试
- iOS IPA 逆向与安全测试
- 移动应用运行时动态插桩
- SSL Pinning / Root 检测 / 越狱检测绕过
- 移动端加密算法提取(AES/RSA/HMAC 密钥)
- 移动应用渗透测试(OWASP MASTG)
- 非 Root/越狱环境下的应用测试
## 四阶段工作流
### Phase 1: 信息收集
```text
Android:
□ APK 获取(Google Play / APKMirror / adb pull)
□ Manifest 分析: 权限、导出组件、Intent Filter、backup 标志
□ androguard: androguard analyze APK → 组件/权限/签名
□ APKLeaks: 硬编码 API Key / Token / Secret 扫描
□ 加固检测: 是否加壳(360/腾讯/梆梆/爱加密)
iOS:
□ IPA 获取(App Store / ipatool / Apple Configurator)
□ 解密 App Store 二进制: frida-ios-dump / Clutch
□ Info.plist 分析: ATS 配置、URL Scheme、Queries Schemes
□ class-dump: 导出 ObjC 类结构
□ 加固检测: 是否使用 Swift/ObjC 混淆
```
### Phase 2: 静态分析
```text
跨平台:
□ JADX-GUI: APK → Java 源码(Android)
□ Ghidra / Hopper: .so / Mach-O 反编译
□ radare2 / Cutter: CLI 快速侦察
Android 专项:
□ apktool d app.apk → smali 代码 + 资源
□ dex2jar: DEX → JAR → JD-GUI
□ smali/baksmali: Dalvik 字节码修改
iOS 专项:
□ class-dump: 导出 ObjC 头文件
□ Swift 符号恢复: swift-demangle
□ dsymutil: 调试符号提取
□ otool -L: 查看动态库依赖
□ jtool2: Mach-O 分析
```
### Phase 3: 动态分析
```text
Frida — 通用动态插桩:
□ frida-ps -U: 列出设备进程
□ frida-trace -U -i "open*" com.app: 追踪函数调用
□ 自定义 Hook 脚本: 修改参数/返回值、调用私有方法
Objection — Frida 增强层(无需写脚本):
□ objection -g "com.app" explore
□ android root disable / ios jailbreak disable
□ android sslpinning disable / ios sslpinning disable
□ android keystore list / ios keychain dump
□ env / ls / sqlite connect
Frida Gadget(免 Root/越狱):
□ 注入 frida-gadget.so / FridaGadget.dylib 到 APK/IPA
□ 重新签名 → 安装 → 无需设备权限即可 Hook
□ objection patchapk --source app.apk(全自动)
```
### Phase 4: 网络分析
```text
□ Burp Suite: 拦截 HTTP/HTTPS,修改请求/响应
□ mitmproxy: 脚本化代理(Python API)
□ Wireshark: PCAP 抓包分析
□ 证书安装: Android 用户证书 → 系统证书(Magisk + MoveCert)
□ SSL Pinning 绕过: Frida/Objection/Xposed/SSL Kill Switch 2
□ WebSocket / gRPC 流量分析
```
## 常见绕过速查
### SSL Pinning
```bash
# Objection(最简)
objection -g "com.app" explore
android sslpinning disable
# Frida 通用脚本
frida -U -l ssl_pinning_bypass.js -f com.app
# Xposed(Android)
TrustMeAlready 模块 → 全局禁用证书校验
```
### Root / 越狱检测
```bash
# Objection
android root disable
ios jailbreak disable
# Frida 自定义(多层检测)
Java.perform(function() {
var RootBeer = Java.use("com.scottyab.rootbeer.RootBeer");
RootBeer.isRooted.implementation = function() { return false; };
// 额外绕过: Magisk su 检测、frida-server 检测、/proc/self/maps 检测
});
```
### 反调试
```bash
# Android
frida -U -l anti_debug_bypass.js -f com.app
# 绕过: ptrace(TracerPid)、/proc/self/status、isDebuggerConnected()
# iOS
# 绕过: PT_DENY_ATTACH、sysctl CTL_KERN/KERN_PROC/KERN_PROC_PID
frida -U -l ios_anti_debug.js -f com.app
```
## 移动端加密提取
```javascript
// Android — Hook Cipher.getInstance 获取密钥+算法
Java.perform(function() {
var Cipher = Java.use("javax.crypto.Cipher");
Cipher.getInstance.overload('java.lang.String').implementation = function(algo) {
console.log("[Cipher] Algorithm: " + algo);
return this.getInstance(algo);
};
Cipher.init.overload('int', 'java.security.Key').implementation = function(mode, key) {
console.log("[Cipher] Key: " + bytesToHex(key.getEncoded()));
return this.init(mode, key);
};
});
// iOS — Hook CCCrypt
Interceptor.attach(Module.findExportByName("libcommonCrypto.dylib", "CCCrypt"), {
onEnter: function(args) {
console.log("CCCrypt op: " + args[0] + " alg: " + args[1]);
console.log("Key: " + hexdump(args[3], { length: args[4].toInt32() }));
}
});
```
## 工具链
| 工具 | 平台 | 用途 |
|------|:--:|------|
| JADX-GUI | A | Java 反编译 |
| apktool | A | APK 解包/重建 |
| Ghidra | A+I | 多架构反编译 |
| Hopper | I | iOS 专用反汇编 |
| Frida | A+I | 动态插桩 |
| Objection | A+I | Frida REPL 增强 |
| MobSF | A+I | 自动化 SAST+DAST |
| class-dump | I | ObjC 类导出 |
| frida-ios-dump | I | IPA 解密 |
| jtool2 | I | Mach-O 分析 |
| Burp Suite | A+I | HTTP 拦截 |
| mitmproxy | A+I | 脚本化代理 |
> A=Android, I=iOS
## 参考
- `references/frida-objection-deep.md` — Frida + Objection 深度用法
- `references/ios-reverse-guide.md` — iOS 逆向专项
- `references/anti-detection-bypass.md` — Root/越狱/反调试/SSL Pinning 绕过
## 任务完成自检(声称完成前 MUST 通过)
- [ ] 我是否执行了工作流中的每一步(而不是只阅读)?
- [ ] 我是否基于 `tool-index` 使用了真实工具路径?
- [ ] 我是否产出了可复现证据(命令/脚本/截图/报告)?
- [ ] 我是否完成并回写了 RULES 要求的 Checklist 项?