暂时只支持ARM64,ARM32的逻辑也是一样的,有兴趣的大佬可以自行更改哈。
在这里感谢珍惜大佬介绍的seccomp机制,推荐一波珍惜大佬的课程能学到很多有趣的骚操作。
seccomp沙箱机制介绍文章
seccomp 是 Linux 内核提供的一种应用程序沙箱机制,主要通过限制进程的系统调用来完成部分沙箱隔离功能。seccomp-bpf 是 seccomp 的一个扩展,它可以通过配置来允许应用程序调用其他的系统调用。
seccomp的具体用法可以参考「什么是seccomp」中的seccomp介绍文章。当返回规则设置为「SECCOMP_RET_TRAP」,目标系统调用时seccomp会产生一个SIGSYS系统信号并软中断,这时就可以通过捕获这个SIGSYS信号获得svc调用和打印具体参数。
这里使用Frida的API「CModule」,CModule提供强大的动态编译功能可以让你在JS中写C,
frida文档中的示例
使用Frida的API「Process.setExceptionHandler」即可捕获异常并在自己写的回调中进行数据处理。
数据处理的逻辑解释写在注释里啦。
根据Frida文档介绍「setExceptionHandler」捕获异常后只需要让回调返回true就会resume原本的线程,但是其只是跳过了svc指令继续执行,实际上并不会执行svc,这时候如果不执行syscall轻则导致APP数据异常,重则导致APP直接崩溃。所以在异常的回调中需要手动调用了syscall并赋值给x0。
但这时候会发生个新的问题,因为在主线程开启seccomp后,主线程和其后创建出来的线程都会被seccomp规则约束,在异常处理函数直接调用syscall同样会被seccomp约束再次抛出异常,就形成了”死锁“了。
可以注意到上面“死锁”部分描述,那我们在主线程被约束前,提前创建一个线程,这个线程就是不被约束的,同时受到线程池启发,我们让这个syscall线程循环接受任务,就能完成在一个没有约束的线程里进行syscall调用。
直接使用Frida的API「Thread.backtrace」很容易导致崩溃,原因可能是seccomp规则或者「prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)」导致的权限收紧和Frida实现堆栈回溯功能冲突。
手动实现堆栈回溯,原理是Arm64中每个函数都会在函数头部位置对x29、x30寄存器存入栈中,所以可以对x29不断读取往上回溯,最后得到完整的堆栈信息。
实现
疑似同坑2的原因
在CModule中手动实现了通过地址查soinfo信息「base, size, soname」等
同坑2的原因
直接改用syscall线程使用「__android_print_log」打印信息
在调用线程syscall前后可以更改传参、返回值、地址等更改,达到HOOK的效果
求star
https://github.com/Abbbbbi/Frida-Seccomp
log信息可以在logcat过滤“seccomp”查看
同时也自动保存到了「包名_pid_时间戳」文件夹内(支持多进程)
这是一个bpf规则:
struct sock_filter
filter
[]
=
{
BPF_STMT(BPF_LD
+
BPF_W
+
BPF_ABS,
(offsetof(struct seccomp_data, nr))),
BPF_JUMP(BPF_JMP
+
BPF_JEQ
+
BPF_K, nr,
0
,
1
),
BPF_STMT(BPF_RET
+
BPF_K, SECCOMP_RET_TRAP),
BPF_STMT(BPF_RET
+
BPF_K, SECCOMP_RET_ALLOW),
};
这是一个bpf规则:
struct sock_filter
filter
[]
=
{
BPF_STMT(BPF_LD
+
BPF_W
+
BPF_ABS,
(offsetof(struct seccomp_data, nr))),
BPF_JUMP(BPF_JMP
+
BPF_JEQ
+
BPF_K, nr,
0
,
1
),
BPF_STMT(BPF_RET
+
BPF_K, SECCOMP_RET_TRAP),
BPF_STMT(BPF_RET
+
BPF_K, SECCOMP_RET_ALLOW),
};
const cm
=
new CModule(`
void hello(void) {
printf(
"Hello World from CModule\\n"
);
}
`);
const hello
=
new NativeFunction(cm.hello,
'void'
, []);
hello();
const cm
=
new CModule(`
void hello(void) {
printf(
"Hello World from CModule\\n"
);
}
`);
const hello
=
new NativeFunction(cm.hello,
'void'
, []);
hello();
/
/
异常处理
Process.setExceptionHandler(function (details) {
const current_off
=
details.context.pc
-
4
;
/
/
判断是否是seccomp导致的异常 读取opcode
010000d4
=
=
svc
0
if
(details.message
=
=
"system error"
&& details.
type
=
=
"system"
&&
hex
(ptr(current_off).readByteArray(
4
))
=
=
"010000d4"
) {
/
/
上锁避免多线程问题
lock(syscall_thread_ptr)
/
/
获取x8寄存器中的调用号
const nr
=
details.context.x8.toString(
10
);
let loginfo
=
"\n=================="
loginfo
+
=
`\nSVC[${syscalls[nr][
1
]}|${nr}]
=
=
> PC:${addrToString(current_off)} P${Process.
id
}
-
T${Process.getCurrentThreadId()}`
/
/
构造线程syscall调用参数
const args
=
Memory.alloc(
7
*
8
)
args.writePointer(details.context.x8)
let args_reg_arr
=
{}
for
(let index
=
0
; index <
6
; index
+
+
) {
eval
(`args.add(
8
*
(index
+
1
)).writePointer(details.context.x${index})`)
eval
(`args_reg_arr[
"arg${index}"
]
=
details.context.x${index}`)
}
/
/
获取手动堆栈信息
loginfo
+
=
"\n"
+
stacktrace(ptr(current_off), details.context.fp, details.context.sp).
map
(addrToString).join(
'\n'
)
/
/
打印传参
loginfo
+
=
"\nargs = "
+
JSON.stringify(args_reg_arr)
/
/
调用线程syscall 赋值x0寄存器
details.context.x0
=
call_task(syscall_thread_ptr, args,
0
)
loginfo
+
=
"\nret = "
+
details.context.x0.toString()
/
/
打印信息
call_thread_log(loginfo)
/
/
解锁
unlock(syscall_thread_ptr)
return
true;
}
return
false;
})
/
/
异常处理
Process.setExceptionHandler(function (details) {
const current_off
=
details.context.pc
-
4
;
/
/
判断是否是seccomp导致的异常 读取opcode
010000d4
=
=
svc
0
if
(details.message
=
=
"system error"
&& details.
type
=
=
"system"
&&
hex
(ptr(current_off).readByteArray(
4
))
=
=
"010000d4"
) {
/
/
上锁避免多线程问题
lock(syscall_thread_ptr)
/
/
获取x8寄存器中的调用号
const nr
=
details.context.x8.toString(
10
);
let loginfo
=
"\n=================="
loginfo
+
=
`\nSVC[${syscalls[nr][
1
]}|${nr}]
=
=
> PC:${addrToString(current_off)} P${Process.
id
}
-
T${Process.getCurrentThreadId()}`
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2022-3-12 22:53
被阿碧编辑
,原因: