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

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

  1. [android]不解锁刷机

    本人因为误操作进入andriod recovery模式,显示failed to boot 2,致手机无法恢复出厂值, 当时那叫一个郁闷.上论坛搜寻无数,唉让刷底包的无数(在此不解释),万恶的刷底包. ...

  2. Windows下将ImageMagick移植到Android平台

    Windows下将ImageMagick移植到Android平台 原文链接  http://www.pedant.cn/2014/06/18/imagemagick-ported-android/ I ...

  3. UDP局域网通信的Java实现及Android平台尝试

    局域网通信已经很少被他人所提及了,我曾经还尝试过通过蓝牙构建通信网络,这次有机会尝试UDP局域网通信,在这里把一些基本过程和在Android平台上的问题记录一下. 1. UDP基础知识 1.1 什么是 ...

  4. 图铭Android平台银行卡号识别系统

    随着智能终端(智能手机及平板电脑)及移动通信(3G)的发展,原来运行在PC上的信息系统(如邮件系统.即时通信.网页浏览.协同办公.网络购物.社交网站.博客等)逐渐转移到智能终端设备上.可以预见未来几年 ...

  5. Cocos2d-x v3.0正式版尝鲜体验【2】 Android平台移植

    今天没事又尝试了下3.0正式版关于Android平台的移植,把新建的项目移植了下.过程仅用了十分钟左右,什么概念?! 好吧,事实上我想说,这个版本号真的移植非常轻松啊,只是还没加上其它东西,只是就眼下 ...

  6. 小米手机Root 刷机

    需要备份的资料: miui系统资料:电话.短信.便签,有小米云账号wifi下自己会备份好的. 应用数据:微信.qq 聊天记录: UC浏览器收藏夹. root后, rootexplorer,选择某个文件 ...

  7. (转)Android刷机的一些知识整理

    刷机概述刷机原因刷机可以升级和破解固件(在Android上:即可以升级系统,更改系统,获取Root权限):破解系统的原因①安装第三方软件不需要签名,不受证书的束缚:②修改系统的文件,达到系统的瘦身,以 ...

  8. Nexus Root Toolkit教程——刷机

    Nexus Root Toolkit是Nexus系列设备专属解锁.root.刷机.修复工具.本教程以Nexus7二代刷安卓5.0 Lollipop系统为实例演示刷机过程. 标签: 安卓5.0刷机教程 ...

  9. Android平台免Root无侵入AOP框架Dexposed使用详解

    Dexposed是基于久负盛名的开源Xposed框架实现的一个Android平台上功能强大的无侵入式运行时AOP框架. Dexposed的AOP实现是完全非侵入式的,没有使用任何注解处理器,编织器或者 ...

  10. Android平台下渗透测试工具大集合

    Android平台下渗透测试工具大集合 分享一个google的项目,各种Android下的渗透测试工具. Ad Network Detector (1.2): http://market.androi ...

随机推荐

  1. 给网站免费升级https协议

    给网站免费升级HTTPS协议,可以通过申请并部署免费的SSL证书来实现.以下是一个详细的步骤指南: 一.申请免费SSL证书 选择证书颁发机构: 可以选择像JoySSL这样的公益项目,它提供免费.自动化 ...

  2. 全网最适合入门的面向对象编程教程:59 Python并行与并发-并行与并发和线程与进程

    全网最适合入门的面向对象编程教程:59 Python 并行与并发-并行与并发和线程与进程 摘要: 在 Python 中,"并行"(parallelism)与"并发&quo ...

  3. PythonDay6Advance

    PythonDay6Advance 模块.类与对象 模块 内置模块 time, random, os, json 第三方模块 requests, pandas, numpy,.... 自定义模块 xx ...

  4. 网站刚上线,就被 DDoS 攻击炸了!

    今天是一个值得纪念的日子,你打开一罐可乐,看着自己刚刚上线的小网站,洋洋得意. 这是你第一次做的网站,上线之后,网站访问量突飞猛进:没过多久,你就拿到了千万的风投,迎娶了女神,走上了人生巅峰... 害 ...

  5. 基于 C# 编写的 Visual Studio 文件编码显示与修改扩展插件

    前言 在软件开发过程中,尤其是在处理跨平台或来自不同来源的项目时,文件的编码格式往往会成为一个不可忽视的问题.不同的操作系统.编程语言和编辑器可能对文件编码有不同的支持和默认设置,这可能导致在打开一个 ...

  6. WinForm 通用权限框架,简单实用支持二次开发

    前言 开发一个安全.灵活且易于维护的应用程序是至关重要的.特别是在企业级应用中,权限管理不仅涉及到用户访问控制,还关系到数据的安全性和系统的稳定性. 推荐一款 WinForm 通用.完整的权限架构开发 ...

  7. ASP.NET Core EventStream (SSE) 使用以及 WebSocket 比较

    在开发环境中,对于实时数据流的需求非常常见,最常用的技术包括 Server-Sent Events (SSE) 和 WebSocket. 什么是 Server-Sent Events (SSE)? S ...

  8. vue总是报错:Trailing spaces not allowed

    翻译: Trailing spaces not allowed:不允许尾随空格 1-报错: 2-解决: 你的某些行的空格多了,删掉就行了 以我的截图为例  代码12行出错   选中12行(点击前面的1 ...

  9. 零基础入门:基于开源WebRTC,从0到1实现实时音视频聊天功能

    本文由微医云技术团队前端工程师张宇航分享,原题"从0到1打造一个 WebRTC 应用",有修订和改动. 1.引言 去年初,突如其来的新冠肺炎疫情让线下就医渠道几乎被切断,在此背景下 ...

  10. Omnivore 替代品 Readeck 安装与使用教程

    前段时间 Omnivore 宣布服务关停,作为一个长期使用 Omnivore 的用户,我需要寻找替代方案. 我对替代品的核心需求是: 浏览器插件:支持一键剪藏当前网页. RSS 支持:能够输入 RSS ...