Android
平台从上到下,无需ROOT/解锁/刷机,应用级拦截框架的最后一环 —— SVC
系统调用拦截。
☞ Github: https://www.github.com/iofomo/abyss ☜
由于我们虚拟化产品的需求,需要支持在普通的Android
手机运行。我们需要搭建覆盖应用从上到下各层的应用级拦截框架,而Abyss
作为系统SVC
指令的调用拦截,是我们最底层的终极方案。
源码位置:270K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6A6L8$3k6G2L8h3!0Q4x3V1k6S2j5Y4W2K6M7#2)9J5c8Y4c8J5k6h3g2Q4x3V1k6E0j5h3W2F1i4K6u0r3M7%4k6U0k6i4t1`.
Seccomp(Secure Computing Mode):
Seccomp
是 Linux
内核的一个安全特性,用于限制进程可以执行的系统调用。它通过过滤系统调用,防止恶意程序执行危险操作。Seccomp
通常与 BPF
结合使用,以实现更灵活的过滤规则。
BPF(Berkeley Packet Filter):
BPF
是一种内核技术,最初用于网络数据包过滤,但后来被扩展用于更广泛的用途,包括系统调用过滤。BPF
程序可以在内核中运行,用于检查和过滤系统调用。
首先,配置 BPF
规则,如下我们配置了目标系统调用号的拦截规则,不在这个名单内的就放过,这样可以实现仅拦截我们关心的系统调用(即函数),提升拦截效率和稳定性。
然后,我们需要过滤掉一些系统库和自身库,防止写入死循环。
通过解析进程 maps
中对应库地址区间,配置跳过此区间的系统调用规则。
其次,应用以上配置。
最后,实现拦截后的处理。
为了使用方便,封装了一些基础系统调用的日志打印接口。
1)添加要拦截的系统调用号。(日常日志打印)
2)注册要拦截的系统调用回调。
3)初始化
额外模块:
本框架实现了最基本的检测仿真,如通过 __NR_rt_sigaction
和 __NR_prctl
获取配置时,会对返回值进行还原。
参考项目:
b93K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6H3M7X3!0G2N6q4)9J5k6r3#2W2i4K6u0r3M7s2u0G2L8%4b7`.
d96K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6@1k6i4u0E0N6i4S2Q4x3V1k6H3M7X3!0G2N6l9`.`.
static
void
doInitSyscallNumberFilter(
struct
sock_filter* filter, unsigned
short
& i) {
filter[i++] = BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(
struct
seccomp_data, nr)));
filter[i++] = BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_openat, 1, 0);
filter[i++] = BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW);
}
static
void
doInitSyscallNumberFilter(
struct
sock_filter* filter, unsigned
short
& i) {
filter[i++] = BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(
struct
seccomp_data, nr)));
filter[i++] = BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_openat, 1, 0);
filter[i++] = BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW);
}
static
void
doInitSyscallLibFilterByAddr(
struct
sock_filter* filter, unsigned
short
& i,
const
uintptr_t
& start,
const
uintptr_t
& end) {
#if defined(__arm__)
filter[i++] = BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(
struct
seccomp_data, instruction_pointer));
filter[i++] = BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, start, 0, 2);
filter[i++] = BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, end, 1, 0);
filter[i++] = BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW);
#else // __aarch64__
filter[i++] = BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(
struct
seccomp_data, instruction_pointer) + 4));
filter[i++] = BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, (uint32_t)(start >> 32), 0, 4);
filter[i++] = BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(
struct
seccomp_data, instruction_pointer)));
filter[i++] = BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, (uint32_t)start, 0, 2);
filter[i++] = BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, (uint32_t)end, 1, 0);
filter[i++] = BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW);
#endif
}
static
void
doInitSyscallLibFilterByAddr(
struct
sock_filter* filter, unsigned
short
& i,
const
uintptr_t
& start,
const
uintptr_t
& end) {
#if defined(__arm__)
filter[i++] = BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(
struct
seccomp_data, instruction_pointer));
filter[i++] = BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, start, 0, 2);
filter[i++] = BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, end, 1, 0);
filter[i++] = BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW);
#else // __aarch64__
filter[i++] = BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(
struct
seccomp_data, instruction_pointer) + 4));
filter[i++] = BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, (uint32_t)(start >> 32), 0, 4);
filter[i++] = BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(
struct
seccomp_data, instruction_pointer)));
filter[i++] = BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, (uint32_t)start, 0, 2);
filter[i++] = BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, (uint32_t)end, 1, 0);
filter[i++] = BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW);
#endif
}
struct
sigaction act = { 0 };
act.sa_flags = SA_SIGINFO | SA_NODEFER;
act.sa_sigaction = handleSignalAction;
struct
sigaction old_sa = {};
ret = sigaction(SIGSYS, &act, &old_sa);
if
(0 != ret) {
LOGSVCE(
"sigaction: %d, %d, %s"
, ret,
errno
,
strerror
(
errno
))
::
free
(filter);
__ASSERT(0)
return
-11;
}
sigset_t mask;
if
(sigemptyset(&mask) || sigaddset(&mask, SIGSYS) ||
sigprocmask(SIG_UNBLOCK, &mask, nullptr)
) {
LOGSVCE(
"sigprocmask: %d, %d, %s"
, ret,
errno
,
strerror
(
errno
))
::
free
(filter);
__ASSERT(0)
return
-12;
}
struct
sock_fprog prog = {
.len = filterCount,
.filter = filter,
};
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
if
(0 != ret) {
LOGSVCE(
"PR_SET_NO_NEW_PRIVS: %d, %d, %s"
, ret,
errno
,
strerror
(
errno
))
::
free
(filter);
__ASSERT(0)
return
-13;
}
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
if
(0 != ret) {
LOGSVCE(
"PR_SET_SECCOMP: %d, %d, %s"
, ret,
errno
,
strerror
(
errno
))
::
free
(filter);
__ASSERT(0)
return
-14;
}
struct
sigaction act = { 0 };
act.sa_flags = SA_SIGINFO | SA_NODEFER;
act.sa_sigaction = handleSignalAction;
struct
sigaction old_sa = {};
ret = sigaction(SIGSYS, &act, &old_sa);
if
(0 != ret) {
LOGSVCE(
"sigaction: %d, %d, %s"
, ret,
errno
,
strerror
(
errno
))
::
free
(filter);
__ASSERT(0)
return
-11;
}
sigset_t mask;
if
(sigemptyset(&mask) || sigaddset(&mask, SIGSYS) ||
sigprocmask(SIG_UNBLOCK, &mask, nullptr)
) {
LOGSVCE(
"sigprocmask: %d, %d, %s"
, ret,
errno
,
strerror
(
errno
))
::
free
(filter);
__ASSERT(0)
return
-12;
}
struct
sock_fprog prog = {
.len = filterCount,
.filter = filter,
};
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
if
(0 != ret) {
LOGSVCE(
"PR_SET_NO_NEW_PRIVS: %d, %d, %s"
, ret,
errno
,
strerror
(
errno
))