首页 > 其他分享 >Android平台从上到下,无需ROOT/解锁/刷机,应用级拦截框架的最后一环,SVC系统调用拦截。

Android平台从上到下,无需ROOT/解锁/刷机,应用级拦截框架的最后一环,SVC系统调用拦截。

时间:2025-01-23 13:54:08浏览次数:1  
标签:__ BPF ++ JMP SVC filter SECCOMP 拦截 Android

Android平台从上到下,无需ROOT/解锁/刷机,应用级拦截框架的最后一环 —— SVC系统调用拦截。

☞ Github: https://www.github.com/iofomo/abyss ☜ 

由于我们虚拟化产品的需求,需要支持在普通的Android手机运行。我们需要搭建覆盖应用从上到下各层的应用级拦截框架,而Abyss作为系统SVC指令的调用拦截,是我们最底层的终极方案。

源码位置:https://github.com/iofomo/abyss/tree/main/svcer

01. 说明

Seccomp(Secure Computing Mode):

SeccompLinux 内核的一个安全特性,用于限制进程可以执行的系统调用。它通过过滤系统调用,防止恶意程序执行危险操作。Seccomp 通常与 BPF 结合使用,以实现更灵活的过滤规则。

BPF(Berkeley Packet Filter):

BPF 是一种内核技术,最初用于网络数据包过滤,但后来被扩展用于更广泛的用途,包括系统调用过滤。BPF 程序可以在内核中运行,用于检查和过滤系统调用。

02. 主要流程

首先,配置 BPF 规则,如下我们配置了目标系统调用号的拦截规则,不在这个名单内的就放过,这样可以实现仅拦截我们关心的系统调用(即函数),提升拦截效率和稳定性。

static void doInitSyscallNumberFilter(struct sock_filter* filter, unsigned short& i) {
    // Load syscall number into accumulator
    filter[i++] = BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr)));
    // config target syscall
    // add more syscall here ...
//    filter[i++] = BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_openat, 5, 0);
//    filter[i++] = BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_getcwd, 4, 0);
//    filter[i++] = BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_chdir, 3, 0);
//    filter[i++] = BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_execve, 2, 0);
    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);
}

然后,我们需要过滤掉一些系统库和自身库,防止写入死循环。

  • 自身实现库的过滤【必须】
  • vdso 的过滤【必须】
  • linker 的过滤【可选,提效】
  • libc 的过滤【可选,提效】

通过解析进程 maps 中对应库地址区间,配置跳过此区间的系统调用规则。

static void doInitSyscallLibFilterByAddr(struct sock_filter* filter, unsigned short& i, const uintptr_t& start, const uintptr_t& end) {
    // Load syscall lib into accumulator
#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;
}

// Unmask SIGSYS
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,
};

// set to self process
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;
}

// set seccomp to kernel
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;
}

最后,实现拦截后的处理。

static void handleSignalAction(int signo, siginfo_t* info, void* context) {
    if (!info || !context || signo != SIGSYS || info->si_code != SYS_SECCOMP) {
        LOGSVCW("signal: signo=%d, code=%d, errno=%d, call_addr=%p, arch=0x%x, syscall=0x%x,%s",
              info->si_signo, info->si_code, info->si_errno, info->si_call_addr, info->si_arch,
              info->si_syscall, SvcerDumper::index2name(info->si_syscall)
              )
        return;
    }

    ucontext_t *uc = reinterpret_cast<ucontext_t *>(context);
  
    intptr_t rc = SvcerSyscall::Call(SECCOMP_SYSCALL(uc),
                                   SECCOMP_PARM1(uc),
                                   SECCOMP_PARM2(uc),
                                   SECCOMP_PARM3(uc),
                                   SECCOMP_PARM4(uc),
                                   SECCOMP_PARM5(uc),
                                   SECCOMP_PARM6(uc)
    );
    SvcerSyscall::PutValueInUcontext(rc, uc);
}

03. 封装

为了使用方便,封装了一些基础系统调用的日志打印接口。

1)添加要拦截的系统调用号。(日常日志打印)

SvcerDumper::addDump(SVCER_SYSCALL_execve);
SvcerDumper::addDump(SVCER_SYSCALL_execveat);
SvcerDumper::addDump(SVCER_SYSCALL_open);
SvcerDumper::addDump(SVCER_SYSCALL_openat);
SvcerDumper::addAll();

SvcerHooker::init(ESvcerHookerMode_IgnoreAll, "libifmamts.so");

2)注册要拦截的系统调用回调。

// 这里注册
for (int i=SVCER_SYSCALL_None; i<SVCER_SYSCALL_Max; ++i) {
    SvcerHooker::registerCallback((TSVCER_SYSCALL_Type)i, handleSvcerHookerCallback);
}

// 这里实现

static void handleKonkerSvcerHookerCallback(int sn, SvcerHookerArgument* arg/*Not NULL*/) {
    switch (sn) {
    case __NR_statfs:// int statfs(const char* path, struct statfs* result);
    case __NR_truncate:// typedef int truncate(const char *filename, off_t len);
    case __NR_chdir:// int chdir(const char *path);
    {
        const char* pathname = (const char*)arg->getArgument1();
        char memString[512];
        if (memString == KonkerFixer::fixDataPath(pathname, memString)) {
            LOGSVCI("fixer, %s: %s", SvcerDumper::index2name(sn), __PRINTSTR(pathname))
            arg->setArgument1((intptr_t)memString);
        }
        arg->doSyscall();
        return;
    }
    default:
        LOGSVCI("ignore, %s", SvcerDumper::index2name(sn))
        break;
    }
    arg->doSyscall();
}

3)初始化

// 设置要过滤的库和当前自身库名称
SvcerHooker::init(ESvcerHookerMode_IgnoreVdso|ESvcerHookerMode_IgnoreLibc|ESvcerHookerMode_IgnoreLinker, "libdemo.so");

04. 附

额外模块:

本框架实现了最基本的检测仿真,如通过 __NR_rt_sigaction__NR_prctl 获取配置时,会对返回值进行还原。

参考项目:

https://github.com/proot-me/proot

https://github.com/termux/proot

标签:__,BPF,++,JMP,SVC,filter,SECCOMP,拦截,Android
From: https://www.cnblogs.com/iofomo/p/18687672

相关文章

  • 新浪安卓(Android)开发面试题及参考答案(68道题,9道手撕题)
     链表判环,找入口思路:判断是否有环:使用快慢指针,快指针每次走两步,慢指针每次走一步,如果它们相遇,说明有环。找出环入口:当判断出有环后,将慢指针重新指向头节点,然后快慢指针同时以相同速度移动,再次相遇的节点就是环的入口。以下是判断链表是否有环以及找出环入口的Java代码......
  • 基于 Android 的校园闲置物品交易平台设计与实现
    标题:基于Android的校园闲置物品交易平台设计与实现内容:1.摘要随着移动互联网的快速发展,智能手机已经成为人们生活中不可或缺的一部分。在大学校园里,学生们拥有大量的闲置物品,如书籍、衣物、电子产品等。这些物品占用了学生们的空间,同时也造成了资源的浪费。因此,设计一个......
  • 可达鸭J3题目 拦截导弹
    题目描述某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所......
  • Android图形层垂直同步虚拟VSYNC机制
    简介某次调图形性能的时候(启动后台录屏,下(或)称case)发现AndroidSurfaceFlingerVsync机制并没有以前想的这么简单粗糙,特别是这次调图形性能发现一些跟Vsync有关联,因此做个总结详解。跟不上旋律节奏的VSYNC一份追踪报告,发现Vsync信号非常不规律,于是从这里入手分析、总结Vsync。......
  • 记录---当window.open被ios安全机制拦截,我掏出3种方案,终于跳转成功!
    ......
  • Android 12.0 系统添加自定义屏保并设置为默认屏保功能实现
    1.前言在12.0的系统rom定制化开发中,在进行相关项目开发的过程中,由于需要在系统锁屏休眠的时候,需要显示相关的背景,就是自定屏保功能,所以就需要添加自定义的屏保,然后在上一篇已经实现在进行锁屏休眠的时候进入屏保的功能,这里就介绍下自定义屏保和设置默认屏保功能就可以了2.......
  • Fart:Android 自动化脱壳技术全解析
    目录Fart:Android自动化脱壳技术全解析一、引言二、Fart简介三、安装准备(一)环境要求(二)获取Fart代码四、Fart工作原理(一)ART运行时基础(二)脱壳原理核心五、使用步骤(一)配置项目(二)选择目标应用(三)执行脱壳(四)获取脱壳结果六、代码示例(一)DexFile结构体解析代码示......
  • 实现Android键盘自适应
    unitUnit13;interfaceusesSystem.SysUtils,System.Types,System.UITypes,System.Classes,System.Variants,FMX.Types,FMX.Controls,FMX.Forms,FMX.Graphics,FMX.Dialogs,FMX.Controls.Presentation,FMX.Edit,Androidapi.Helpers,Androidapi.JNI.Graph......
  • Android Systrace 基础知识 - Triple Buffer 解读
    怎么定义掉帧?Systrace中可以看到应用的掉帧情况,我们经常看到说主线程超过16.6ms就会掉帧,其实不然,这和我们这一篇文章讲到的TripleBuffer和一定的关系,一般来说,Systrace中我们从App端和SurfaceFlinger端一起来判断掉帧情况App端判断掉帧如果之前没有看过Systrace......
  • Android studio开发实战之碎片Fragment
        一、碎片化的概念        碎片化(Fragment)是Android应用开发中的一个重要概念,它的设计初衷是增强界面模块化,便于开发者灵活构建和管理复杂的界面。什么是模块化?将应用界面拆分成多个可复用的小模块(Fragment),每个模块可以独立定义自己的布局、逻辑和交互......