计划执行与检查节点
executing-plans
按既定实施计划完成工作,保持过程检查和反馈。
re-frida【效率与协作】Frida 动态插桩(桌面+移动统一)。 触发词:frida、hook、插桩、绕过、spawn
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
动态分析按 [[re-analyze/platform-tips]] 最高原则:移动端在受控设备 / 模拟器快照内执行,桌面端插桩前确认沙箱环境。所有工具先验证再使用。
pip install frida-tools(Python 3.8+;建议 venv: python3 -m venv venv && venv/bin/pip install frida-tools)frida --version(输出 frida 版本号);frida-ps -U / frida-trace -h 可用https://github.com/frida/frida/releases,选 frida-server-<版本>-android-<架构>(arm64 选 -arm64,32 位选 -arm,模拟器 x86_64 选 -x86_64)frida --version);为避免功能/API 差异,推荐使用完全相同且最新的版本。架构与设备匹配,否则连接报协议错误(见坑 1)adb push frida-server-xxx /data/local/tmp/frida-server
adb shell "chmod 755 /data/local/tmp/frida-server"
adb shell "su -c /data/local/tmp/frida-server" & # 或 adb root 后直接运行
https://build.frida.re 安装 frida deb,或 ssh root@<设备IP> 后安装frida-ps -U 能列出设备进程(-U = USB 设备,-R = 远程 ip:port)>=3.7 下限不同,需单独满足): python -m pip install -U objection,建议另建 3.10+ 的 venv 或 pipx 隔离objection --versionobjection -n <应用或 Bundle ID> start,内置 android hooking / ios hooking 子命令(如 android hooking list activities、ios sslpinning disable)-g / explore 已弃用(在 CLI 中隐藏,仅出弃用告警,旧写法仍可跑);新写作用 -n + start按顺序执行,每步记下结果。
spawn vs attach 选择:
frida -U -f com.target.app # spawn:从零启动应用,能抓启动早期逻辑(解密 / 初始化)
frida -U com.target.app # attach:附加到已运行进程(不重启)
frida-trace -U -f com.target.app -i "Java!*" # 启动即跟踪 Java 方法调用
规则:抓启动逻辑 / 绕过早期检测用 spawn;只是观察现状用 attach。attach 晚于启动,可能错过已执行完的早期逻辑(见坑 2)。
JS hook 编写(拦截 / 改参 / 返回值):
// hook.js —— 拦截、改参数、改返回值(Android Java 层)
Java.perform(function () {
var cls = Java.use("com.example.Target");
cls.doLogin.implementation = function (user, pass) {
console.log("doLogin(" + user + ", " + pass + ")");
return this.doLogin("hacked", "pass123"); // 改参
};
cls.isLicensed.implementation = function () {
return true; // 改返回值
};
});
frida -U -f com.target.app -l hook.js
原生函数用 Interceptor.attach(Process.getModuleByName("libfoo.so").findExportByName("func"), { onEnter: ..., onLeave: ... }) 拦截(onEnter 改参数、onLeave 用 retval.replace() 改返回值)。桌面端同样本:frida -p <pid> -l hook.js。
枚举与调用(enumerateModules / Java.perform):
Java.perform(function () {
Java.enumerateLoadedClasses({
onMatch: function (c) { if (c.indexOf("target") >= 0) console.log(c); },
onComplete: function () {}
});
});
// 模块与导出枚举(原生层)
Process.enumerateModules().forEach(function (m) { console.log(m.name + " " + m.base); });
Process.getModuleByName("libfoo.so").enumerateExports().forEach(function (e) { console.log(e.name); });
运行: frida -U -f com.target.app -l enum.js。定位到目标后直接主动调用:Java.use("com.x").method(...) / 原生导出函数。
绕过证书校验 / 检测(常见模板):
// Android SSL 绕过模板(配合抓包工具)
Java.perform(function () {
var X509TrustManager = Java.use("javax.net.ssl.X509TrustManager");
var TrustAll = Java.registerClass({
name: "com.bypass.TrustAll",
implements: [X509TrustManager],
methods: { checkClientTrusted: function () {}, checkServerTrusted: function () {},
getAcceptedIssuers: function () { return []; } }
});
var SSLContext = Java.use("javax.net.ssl.SSLContext");
SSLContext.init.implementation = function (km, tm, sr) {
this.init(km, [TrustAll.$new()], sr);
};
});
// iOS 证书绕过:hook SecTrustEvaluateWithError 返回值
var SecTrust = Module.findGlobalExportByName("SecTrustEvaluateWithError"); // Frida 17+:全局符号静态查找
Interceptor.attach(SecTrust, { onLeave: function (r) { this.context.x0 = 0; } }); // arm64 返回寄存器 x0;x86_64 用 rdi 场景先验证
现成命令:objection -n com.target.app start → android sslpinning disable / ios sslpinning disable。
反检测对抗(隐藏 frida-server、改名):
/data/local/tmp/frida-server 路径、gum-js-loop / gmain 线程名、/proc/self/maps 中的 frida 特征、frida 字符串# 1) 二进制改名后启动(避开路径检测)
cp frida-server-xxx /data/local/tmp/fridad
adb shell "su -c 'chmod 755 /data/local/tmp/fridad && /data/local/tmp/fridad' &"
# 2) 换端口 + adb 端口转发,主机用 -H 连接
adb shell "su -c '/data/local/tmp/fridad -l 0.0.0.0:27142' &"
adb forward tcp:27142 tcp:27142
frida -H 127.0.0.1:27142 -f com.target.app
Java.use("com.target.rootcheck").isRooted.implementation = function () { return false; };脚本模板与对抗方法论:常用脚本骨架见 [[frida-scripts]](TLS keylog / DEX/SO dump / JNI 注册还原 / 加密拦截 / 检测绕过表);崩溃迭代法与检测面对照表见 [[re-analyze/anti-dynamic-workflow]]——先基线跑看裸崩,再定点 hook,不预置绕过全家桶。
[[re-address-space]]:运行时基址换算(Process.getModuleByName(...).base 与链接地址对齐)
[[re-mobile]]:工作流第 3 步动态插桩固定调用本技能
[[re-apk]] / [[re-ios]]:静态分析后需要运行时行为(解密 / hook / 绕过 / 脱壳执行)时调用本技能
系统调用级跟踪互补 → [[re-tracing]];运行时内存提取 → [[re-memdump]]
本技能被 [[re-analyze]] 的 triage「移动 App 分析」路径调用(re-mobile → re-frida)
脚本生成:目标特征 → 模板选择 → 改写验证 → [[re-frida-script-author]](模板素材 [[frida-scripts]])
frida-ps -U 报 unable to communicate with the frida server / 协议错误;原因——客户端与 frida-server major 不一致,或所用的功能在目标版本上不存在;对策——先查 major 是否匹配(frida --version 对照 frida-server 版本,见工具准备),再确认该功能是否被对应版本支持;升级侧用 pip install -U frida-toolsTypeError: Module.findExportByName is not a function(或 getExportByName / enumerateExports / findBaseAddress 同类);原因——17.0.0 起静态查找与枚举 API 全部移除,静态只余 Module.load / Module.findGlobalExportByName / Module.getGlobalExportByName,其余改为模块实例方法;对策——先取实例再查:Process.getModuleByName("libfoo.so").findExportByName("func")(find 返 null,get 抛异常)、基址用 .base、枚举用 .enumerateExports();全局符号(原 null 模块参数)改用 Module.findGlobalExportByName(...);注意 Process.getModuleByName 在模块未加载时抛异常(旧 Module.findExportByName 返 null)——不确定时先用 Process.findModuleByName 判空再查;动笔前 frida --version 确认版本再选写法(迁移清单见 [[re-frida-script-author]] 版本相关组)-f spawn 模式起步即插桩;仍错过则 hook dlopen / ClassLoader.loadClass 这类更早的执行点frida 线程名或 maps 特征;对策——步骤 5 改名 + 换端口;仍检测用 frida-gadget 注入(隐藏于进程内)Java.perform 外调用 Java API、模块名大小写不符;对策——用步骤 3 的枚举先核对名称,脚本内 console.log 打桩定位IsDebuggerPresent/NtQueryInformationProcess 等导出,反调试照样触发、进程退出;原因——加壳/加固目标不走导入表:自实现 GetProcAddress、API 名存哈希,或直接 syscall(内联 Nt* 直调),hook 点根本没经过;对策——hook 更深一层(ntdll 的 syscall 包装点)、跟踪 GetProcAddress/哈希解析处反推真实调用点,反调试与反 VM(RegOpenKeyExA/GetSystemFirmwareTable)API 一并 hook(Arkana 项目实战模板),先用 Process.enumerateModules()/enumerateExports 确认实际调用目标再插桩/proc/self/fd 的 memfd 名称、JIT 缓存池(pool-frida)、frida_agent.so/frida_rpc 等内存字符串与导出符号、管道/linjector 名称,甚至非标准端口也会被扫(Promon 式扫描);对策——用 undetected-frida 补丁集(字符串/符号/线程/协议/memfd/JIT 池全量混淆,Magisk/KSU 模块形态),或 frida-gadget + 自编译隐藏版,缩小指纹面/proc/self/maps+/proc/self/status 进程监控 + 非标准端口扫描,整体防线而非单点;对策——先处理完整性校验与进程监控(hook 校验函数返回、patch 监控点)再过反调试,hook 落到自定义导入解析处而非导出 API,必要时结合 [[re-apk]] 静态改 smali + Native 层插桩配合SocketOutputStream 无结果,抓包却看到流量;原因——应用多进程(发流量的逻辑在子进程)或使用 Netty 的 SocketChannelImpl(不走 OutputStream 路径);对策——排查顺序:hook 最外层出口 → 无果 ps -e 查子进程分别 hook → 再试 SocketChannelImpl 等替代实现;hook 成功后打印堆栈(Netty 的 MessageToByteEncoder 链)定位组装/加密代码;从"编解码器链"逐层向上追明文对象与加密产物bArr 类)拿当前密钥,再向上追踪密钥来源与传输路径,配合抓包对照(首包固定头如 89 04 01 01 可作协议锚点)Process.arch==x64 且找不到目标原生 so),运行时从未执行你的字节;部分目标在模拟器上校验宽松,「模拟器能跑」可能是降级假象;对策——按阶段选环境:静态 patch 与渲染回归可用任意模拟器(含 x86 翻译层),动态插桩必须原生 ARM,交付验证必须与目标匹配的真机;翻译层假象可证伪:把已知必需函数首指令 patch 成裸 ret,UI 照常渲染即运行时没执行你的字节,立即停掉该环境的 live 工作(盘上静态 patch 下次启动重新翻译仍可用);真机采集前 svc power stayon true 并唤醒,防睡眠屏造成「空白」假截图
(来源:reverse-skills(inliver233),MIT)/dev/binder,不能用 --device 或 binderfs)、新内核无 ashmem 时未加 androidboot.use_memfd=1、设了 ro.secure=1 ro.debuggable=0 杀掉 root adb、停掉的容器 binder 已消失无法 docker restart、adbd 高负载下崩溃;对策——binder 用 bind-mount、新内核强制 use_memfd、保留 root adb(防篡改用保持签名有效而非藏 root)、重建容器后重注入 adb 公钥(echo $PUBKEY > /data/misc/adb/adb_keys)、大文件用 docker cp 而非 adb push、动态分析用 spawn 模式(frida -U -f,attach 在防篡改下常 0 命中);定位认知——它是数据采集工具而非迭代环境,采完动态数据回稳定环境做静态 patch 回归
(来源:reverse-skills(inliver233),MIT)findModuleByName 的 waitForLibapp 包装器,起步优先 spawn 模式;破 cid 墙:目标函数 onEnter 记 lr(returnAddress)+ Thread.backtrace(Backtracer.ACCURATE) 前 20 帧,一次回溯定位分派点;guard 定位三件套:Stalker.follow transform 里对 ldr 类指令放 putCallout(首个命中 PC = guard)、MemoryAccessMonitor.enable 等重建触发(arm64 支持不一需回退)、候选 hook 扫射;运行时翻转验证前置:live writePointer 翻转候选门字段 → 触发重建 → 组件消失即确认门字段,确认后再投静态 patch,省掉反复重打包;frida 17 起 Java bridge 不再内置于底层 GumJS runtime,但 frida CLI/REPL 与 frida-trace 仍自带三个 bridge,普通 frida -l 脚本无需改动;仅自建 agent / 走 bindings 注入脚本时需显式安装 frida-java-bridge(必要时用 frida-compile 打包)
(来源:reverse-skills(inliver233),MIT)