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

news/2025/3/10 18:04:13/文章来源:https://www.cnblogs.com/iofomo/p/18687672

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 accumulatorfilter[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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/873670.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Seq 日志: .NET 应用程序中集成与快速入门

Seq 是一个功能强大且易于使用的日志服务器,特别适合用于集中管理和可视化 .NET 应用程序的日志。它支持结构化日志记录,提供实时查询和分析功能,帮助开发者快速定位和解决问题。 安装和配置 SeqWindows 部署: 可以从 Seq 下载页面:https://datalust.co/download 获取最新…

折腾笔记[11]-使用rust进行直接法视觉里程计估计

使用rust实现了一个完整的直接法视觉里程计系统,能够通过比较两幅图像中的像素强度来估计相机的运动。它通过单层和多层的优化策略,结合图像金字塔和并行计算,提高了位姿估计的精度和效率。最终,代码输出了优化后的相机位姿变换矩阵,并可视化了投影点的位置。摘要 使用rus…

5 链路层

5 链路层 5.1 概述 节点(node):运行链路层协议的设备(主机、路由器、交换机、WiFi接入点) 链路(link):沿着通信路径连接相邻节点的通信信道 数据链路层(data link layer):将数据报封装在链路层帧(frame)中通过通信链路从一个节点传输到另一个节点 链路层提供的服务…

【动态规划】01背包专题

01背包在恰好等于的情况下求最小物品数 MELON的难题每个物品(石头)的价值w[i]就是其自己的个数,为1 体积题目已给出。 状态定义:f[i][j]表示在前i个物品中选,且体积总和恰好等于j需要的物品个数的最小值 初始化: f[i][0] = 0 , 1 <= i <= n f[0][j] = INF, 1 <=…

阿里开源语音克隆CosyVoice2 整合包

CosyVoice2 win整合包 语音克隆CosyVoice2 链接:https://pan.quark.cn/s/5e75615a5cd4 修改webui.py默认值: # 修改默认推理模式 mode_checkbox_group = gr.Radio(choices=inference_mode_list, label=选择推理模式, value=inference_mode_list[1])# 修改随机推理种子 seed =…

KeyClicker 为用户带来真实键盘打字声音体验的应用,再现机械键盘与打字机的打字感受

如果你是一名作家,或者对打字机的声音情有独钟,KeyClicker 将是你的理想选择。许多作家认为,打字机的声音能让他们更专注、更有创作灵感。虽然实体打字机的魅力独特,但它缺乏现代设备的便捷功能,例如高效的编辑与数字化操作。而使用 KeyClicker,你既能享受打字机的经典声…

gdb调试小技巧——多个窗口显示

先用tty显示需要显示的终端的序号 ┌──(root㉿kali)-[~] └─# tty /dev/pts/3然后 vim ~/.gdbinit在文件后加一行 set context-output /dev/pts/2这里数字就是tty显示的数字 设置好之后打开gdb时就可以了

道路流量监测摄像机

道路流量监测摄像机是一种结合了监控摄像技术和交通管理的先进设备,旨在通过实时监测和分析道路上车辆的行驶情况,收集交通流量数据并进行统计分析。这种摄像机在城市交通管理、道路规划、交通安全等领域有着广泛的应用前景。道路流量监测摄像机是一种结合了监控摄像技术和交…

占用消防通道监测摄像机

占用消防通道监测摄像机是一种结合了智能分析技术和监控技术的先进设备,在预防火灾事故和保障人员安全方面具有重要意义。随着社会对安全意识不断提高以及相关法规标准日益完善,相信这种先进设备将会在更多领域得到广泛应用,并为我们创造更加安全、有序的公共环境。占用消防…

安全帽佩戴识别摄像机

安全帽佩戴识别摄像机的应用不仅仅是对现有安全管理模式的一种补充,更是对安全理念的一种革新。它打破了传统安全管理中依靠人工巡检、监督的局限性,实现了安全管理的自动化、智能化。它以一种高效、精准、持续的方式,让安全管理无处不在,让每一个工作人员都时刻处于安全的…

戴头盔识别摄像机

戴头盔识别摄像机是一种结合了智能分析技术和监控技术的先进设备,在提高安全意识和减少事故风险方面具有重要意义。随着社会对安全生产和公共秩序关注度不断提高以及相关法规标准日趋完善,相信这种先进设备将会在更多领域得到广泛应用,并为我们创造更加安全、健康的生活环境…

38个!第六批产业技术基础公共服务平台名单公布

https://mp.weixin.qq.com/s?__biz=MjM5OTUwMTc2OA==&mid=2650924081&idx=1&sn=4b020757ad7a231d0391def504479dbc&chksm=bddf0a2406acfe4e2b9612d57d3de64162d337594a44dc7f061cddcebc4d3a4982a904eb621e&scene=126&sessionid=1737537299#rd