某企业加固加载Frida流程
本教程流程由AI全程完成,教程由AI生成。
如有侵权,联系删除。
1. 环境
设备 ROOT真机
系统 Android 10
架构 arm64-v8a
加固 某企业加固(libjiagu_***_**.so / libjgdtc.so / assets/.jgapp)
工具链 frida 16.7.19
adb
为什么必须真机:雷电 / MuMu 上跑的是 houdini(ARM→x86 翻译层),Process.arch 报 ia32,
native 层 hook 与 Frida 的 Java bridge 都会失配。改 so 字符串、改翻译库名、关 root、伪装 cpuinfo
全部无效——判定依据是"原生桥环境本身",绕不过去。
目标:把 Frida 装进目标进程
2.1 现象:attach 不上
frida -H 127.0.0.1:27099 -n com.***.app
→ Failed to attach: unable to access process with pid XXXX
而 ps 里进程活得好好的。查 /proc/<pid>/status:
TracerPid: 15086 ← 被别人 trace 着
Linux 规定一个进程同时只能有一个 tracer,所以 Frida 挂不上去。
2.2 守护进程的真实启动顺序(实测纠正)
最初的判断是"主进程与守护进程互相 ATTACH",方向对、机制错。
用 fork 钩子抓到真实顺序:
① 主进程 fork() 出守护进程
② 守护进程调用 ptrace(PTRACE_TRACEME)
→ 内核把「父进程」设为它的 tracer
→ 于是 守护.TracerPid = 主进程
③ 守护进程再调用 ptrace(PTRACE_ATTACH, 主进程)
→ 于是 主.TracerPid = 守护的某个线程
推论很关键:ATTACH 是守护进程发出的。所以只注入主进程去 hook ptrace,
永远看不到那次 ATTACH——这就是早期"伪造 EXITKILL 从没被调用"的真正原因。
时序上,这 4 次 fork 与 42 次 TRACEME 自检全部发生在 StubApp.attachBaseContext 阶段,
即 ***Application.onCreate 之前,调用方都在 libjiagu_***_**.so 的代码段。
2.3 五条失败路径(都实测过)
| 尝试 |
结果 |
原因 |
拦掉所有 fork |
主进程 TracerPid=0、Java 可用,但 5–8 秒后 SIGSEGV |
壳看门狗拿不到守护握手,判死自杀 |
只拦前 4 次 fork |
第 5 次成功 → 守护建立 → 主进程被杀 |
fork 次数不固定 |
| 按调用栈只拦"壳发起"的 fork |
仍放行了 1 个 → 被杀 |
栈回溯判断不准 |
| 放行 fork 但立刻 kill 子进程 |
主进程同时死 |
PTRACE_O_EXITKILL 绑死 |
把 TRACEME 返回值改成 0 |
2 秒内被杀 |
壳拿它与 TracerPid 交叉验证,改了就是自曝 |
结论:ptrace 的 TRACEME 一个字都不能碰,fork 也不能拦。
唯一可行的是——让守护进程正常存在,但让它挂不上主进程。
2.4 方案:把 ATTACH 伪造成"成功"
2.4.1 可行性判定
先读 libc.ptrace 入口的机器码,确认能不能做 inline patch:
sub sp, sp, #0x30 ← 标准函数序言
str x19, [sp, #0x10]
stp x20, x21, [sp, #0x20]
stp x29, x30, [sp, #0x30]
四条指令都不涉及 PC 相对寻址,覆盖 16 字节是安全的,不需要指令重定位。
2.4.2 stub 设计
核心约束:这个补丁必须在 fork 之前装好,好让守护进程继承。
而 fork 出的子进程没有 JS 线程,所以 stub 绝对不能回调 JS——
只能用纯 ARM64 机器码。
逻辑:
if (request == PTRACE_ATTACH // 16
|| request == PTRACE_SEIZE // 0x4206
|| request == PTRACE_SETOPTIONS) // 0x4204
return 0; // 假装成功
else
执行原序言 4 条 → 跳回 ptrace+16 // 其余请求原样透传
TRACEME(0) 故意不在列表里——它是壳的交叉验证锚点。
布局(72 字节):
+0 cmp x0, #0x10
+4 beq +64
+8 movz w4, #0x4206
+12 cmp x0, x4
+16 beq +64
+20 movz w4, #0x4204
+24 cmp x0, x4
+28 beq +64
+32 <原 prologue 4 条>
+48 ldr x16, #8
+52 br x16
+56 .quad ptrace+16
+64 mov x0, #0
+68 ret
入口改写为 16 字节跳板:
ldr x16, #8
br x16
.quad <stub>
2.4.3 安装三步
// ① 申请 RWX 页(关键:不能用 Memory.alloc)
var mmap = new NativeFunction(Module.findExportByName('libc.so', 'mmap'),
'pointer', ['pointer','size_t','int','int','int','int64']);
var page = mmap(ptr(0), 4096, 7 /*RWX*/, 0x22 /*MAP_PRIVATE|MAP_ANON*/, -1, 0);
// ② 写 stub(此刻 ptrace 还没被改,可以直接调用自检)
...
// ③ 给入口打跳板
Memory.patchCode(pt, 16, function (code) {
code.add(0).writeU32(0x58000050); // ldr x16, #8
code.add(4).writeU32(0xD61F0200); // br x16
code.add(8).writePointer(page);
});
2.4.4 自检与实机结果
stub 写好后先直接调用(此时 ptrace 还没打补丁),再打完补丁经入口调用,两次都要对:
SELFTEST attach=0 detach=-1 ← 直接调 stub
LIVE attach=0 detach=-1 ← 经 ptrace 入口
实机效果:
[ 337ms] attached pid=7195
[ 350ms] ptrace patched
[ 4327ms] procs=[(7195, 619), (7267, 7195)] tracer=0
JAVA_READY ver=10
MainActivity → SplashActivity → MainTabActivity
[42533ms] t+30s tracer=0 OK
守护进程 7267 正常存在,主进程 TracerPid=0,Java bridge 可用,30 秒以上稳定,可随时 attach/detach,无需重启。
2.5 三个隐蔽的坑
① Memory.patchCode 的 code 指针不会自动偏移。
两次 writeU32 都落在 offset 0,把入口前 8 字节写成了指针值。反汇编出来是 ldr s0; udf #0x76,
现象是"一调用就 SIGILL"。必须显式 code.add(0) / add(4) / add(8)。
② 用 Memory.alloc + patchCode 写 stub 会 SIGILL。
patchCode 用完会恢复该页原本的保护属性——堆页是 RW,恢复后就没有执行权限了。
必须 mmap 一块 RWX。
③ ARM64 指令缓存不保证一致。
裸写内存后 CPU 可能执行到旧指令。用 Memory.patchCode 写入(它负责 flush i-cache),
不要用 ptr.writeU32 直接怼。
2.6 标准加载流程
方式 A:冷启动 + 抢注(进程还没起来时)
adb shell "nohup /data/local/tmp/aaanew -P -l 127.0.0.1:27099 >/dev/null 2>&1 &"
adb forward tcp:27099 tcp:27099
python analysis/***946/frida_946_stable.py 300
内部顺序:am force-stop → am start → 轮询 pidof(约 +330ms 抢到)→ attach
→ 灌 frida_agent39_ptrace.js 打补丁 → 等 MainTabActivity 稳定 → 灌业务探针。
注意:aaanew 是重命名后的 frida-server。
方式 B:attach 已在运行的进程(推荐,不打断用户操作)
先看 TracerPid:
P=$(adb shell pidof com.******.app | tr -d '\r')
adb shell "grep TracerPid /proc/$P/status"
TracerPid: 0 → 直接 attach,无需补丁
TracerPid: 非0 → 说明守护已挂上,需要走方式 A 冷启动重来(或对当前进程补打补丁)
python analysis/***946/load_on_pid.py auto 你的脚本.js 60