btrace:binder_transaction+eBPF+Golang实现通用的Android APP动态行为追踪工具
一、简介:
在进行Android恶意APP检测时,需要进行自动化的行为分析,一般至少包括行为采集和行为分析两个模块。其中,行为分析有基于规则、基于机器学习、基于深度学习甚至基于大模型的方案,各有各的优缺点,不是本文关注的重点,本文主要关注Android APP的动态行为采集。在做Android APP逆向分析时经常需要通过hook系统调用观察APP的行为,也需要一个动态行为追踪工具。
btrace(GitHub - null-luo/btrace: btrace:binder_transaction+eBPF+Golang实现通用的Android APP动态行为追踪工具)就是一个开源的针对Android APP的动态行为采集/追踪工具。目标是通用、可靠、简单。如果类比到Linux tracing systems的话,我们的工具也可以分成三部分:data sources我们的方案是kprobe/binder_transaction;way to extract data我们采用eBPF;frontends我们使用Golang。
接下来分别介绍这三个部分的方案。
二、data sources:kprobe/binder_transaction
binder是Android IPC的核心机制,Android APP在访问系统服务的时候,实际上就是在进行跨进程通信,因此,监控binder就可以获取到APP调用系统服务的行为。
这里就不再重复说明了,我们重点看一下在kernel层的哪个函数做监控比较好。我们的目标是要获取:APP的包名、调用服务

首先想到的是内核已经定义的tracepoint:
可惜大部分tracepoint都没有带上binder核心数据的指针,也就是没有办法获取到目标服务名和函数参数:
只有binder_ioctl这个tracepoint里面的arg指向的是struct binder_write_read:

但问题是struct binder_write_read相当的原始,解析起来比较复杂:
这是因为binder_ioctl是链路上kernel层的第一个函数,传进来的数据还没有经过处理。那么,我们能不能找一找binder_ioctl后面的函数,尽可能让系统对数据进行解析和处理之后我们直接拿到想要的字段呢?
我们把binder_ioctl->binder_ioctl_write_read->binder_thread_write->binder_transaction这条调用链分析了一下,发现binder_transaction是一个比较合适的点,在它之前的函数已经对用户层传入的数据进行了很多解析和过滤,这里拿到的数据是struct binder_transaction_data,相对比较简单了:
其实,仔细看binder_transaction函数的代码可以发现,本来通过binder_debug和trace_binder_transaction这两个地方直接拿到数据是最方便的,可惜的是binder_debug没有输出code(调用函数的编号),trace_binder_transaction又没有输出调用服务名和参数的数据指针。导致没有办法直接使用这两个点。
尤其是trace_binder_transaction,如果往后一点放到内存拷贝(user->kernel)完成之后,再将数据指针输出的话就非常完美了。
所以,最后我们还是回到对binder_transaction这个内核函数进行监控,解析参数struct binder_transaction_data来拿到数据的方案。
三、way to extract data:eBPF
eBPF是一个运行在Linux内核里面的虚拟机组件,它可以在无需改变内核代码或者加载内核模块的情况下,安全而又高效地拓展内核的功能。是一种非侵入性的内核函数hook方法。
并且,Google 为了解决 Android 碎片化提出了GKI(通用内核镜像),要求Android 12以上版本的设备出厂必须使用GKI内核,而且GKI内核的编译选项把eBPF相关的功能都是打开的。
所以eBPF特别适合用于对Android设备中Linux内核函数的监控。
binder_transaction函数总共5个参数,我们可以根据第4个参数来过滤掉回应的transaction,只关注请求的transaction:
我们的目标是要获取:APP的包名、调用服务名、调用函数名、调用参数这几个字段:
-
APP的包名可以通过当前UID来获取(因为binder_transaction函数是在client的进程内);
-
调用函数名可以通过binder_transaction_data->code来获取;
-
调用服务名和调用参数可以通过binder_transaction_data->data.ptr.buffer来获取;

其中要注意的是,binder_transaction_data->data.ptr.buffer指向的数据目前还在用户空间,还没有完成向内核空间的拷贝,所以需要使用bpf_probe_read_user函数。(这就是我上节说的如果把trace_binder_transaction往后移到内存拷贝之后,并且把内核空间的数据地址输出,那就完美了,可惜!):
四、frontend:Golang
eBPF的核心程序一般是使用C语言编写,clang进行编译后,需要将其加载到内核中。目前有多个项目对eBPF的编写调试运行的流程进行了封装和优化,比如bcc、libbpf等,我们选择的是cilium/ebpf。
它封装了BPF系统调用,与内核提供的libbpf类似,区别在于这个库是Go语言的,更加方便进行用户态程序的开发,而且外部依赖少,与此同时其还提供了bpf2go工具,可用来将eBPF程序编译成Go语言中的一部分,使得交付更加方便。也就是说很容易将项目编译为一个独立可运行的ELF文件。
我们的开发环境是Ubuntu arm64的虚拟机(主机是Mac):
cilium/ebpf使用起来非常方便,整个框架分为三个部分:
- 运行在内核态用C写eBPF代码,llvm编译为eBPF字节码;
- 用户态使用Golang编写,cilium/ebpf纯go类库,做eBPF字节码的内核加载,kprobe HOOK对应函数;
- 用户态使用Golang做事件读取、解码、处理。
我们在内核态程序里将需要的数据放到ringbuf里传递给用户态:


用户态程序收到数据后做处理:
1、APP的包名
知道UID后执行命令"pm list packages -U"去查一下就能知道:
2、调用函数名、调用服务名、调用参数
Android进程间通信基于Proxy与Stub的设计模式,AIDL是Android接口定义语言,在写完AIDL文件后,编译器自动生成一个同名的.java文件,里面包含Stub和Proxy两个类,Stub类是服务端抽象层的体现。Proxy的接口供客户端程序调用,然后它内部会把信息包装好,通过binder传递给Stub,而后者通过对应的接口作用于服务端系统,从而完成了“远程调用”。
先来看看Proxy的代码,红色对应的就是要调用函数的编号,也就是binder_transaction_data->code。蓝色就是要调用的服务的接口名,绿色部分则是要调用函数的参数,可以看出来这两部分被打包到一个Parcel里面去,对应的就是binder_transaction_data->data.ptr。最后通过transact函数将以上三部分内容往binder传递。

writeInterfaceToken函数在写入接口名之前,还写了12字节(4+4+4)的其他数据:
我们在解析的时候先跳过头部12字节,接下来的4字节代表接口名字符串的长度,接着的数据即是接口名字符串:
最后,看一下binder_transaction_data->code如何转换成函数名,仔细分析了binder流程代码,函数名在编译.aidl文件的时候就已经转换成code了,之后一直传递的都是code,直到服务端的onTransact函数里才根据code去选择函数:

所以在整个binder数据传输的过程中都找不到合适的hook点,后来偶然发现.aidl文件自动生成的Stub类里面有getTransactionName、getDefaultTransactionName这么两个函数可以根据函数编码获取到函数名,那么我们就可以使用反射来获取函数名 
顺手在Android代码里搜索了一下这两个函数,发现有一个类已经将对getDefaultTransactionName的调用包装好了:
但是在golang里面不太好调用Android的API,所以换了一个思路,写了一个Android APP,利用反射把系统服务下所有的transactionCode和methodName的映射关系记录下来,输出给btrace在运行时候查询:
有几个注意点:
- 系统所有的服务名可以通过service list获取:

- 需要打开策略开关才能访问hide的API:adb shell settings put global hidden_api_policy 1。
-
每一个服务内transaction函数一般是从1开始逐个编号的,而且每个函数对应一个field,所以我们获取服务类的field数目就知道此类最多有多少个transaction函数需要我们尝试去获取名字:

最后效果(参数的解析暂不支持):
五、总结:
我们使用binder_transaction+eBPF+Golang来实现一个针对Android APP的动态行为追踪工具,目标是通用、可靠、简单。
-
通用:基于binder底层内核函数,可以监控到所有API调用,覆盖系统版本广泛;
-
可靠:基于eBPF,对内核无侵入,并且有验证器的验证,安全可靠;
-
简单:基于Golang作为frontend,灵活高效,逻辑简单清晰,外部依赖少,单一ELF可独立运行。
相关文章:
btrace:binder_transaction+eBPF+Golang实现通用的Android APP动态行为追踪工具
一、简介: 在进行Android恶意APP检测时,需要进行自动化的行为分析,一般至少包括行为采集和行为分析两个模块。其中,行为分析有基于规则、基于机器学习、基于深度学习甚至基于大模型的方案,各有各的优缺点,不…...
C# OCCT Winform 界面搭建
目录 1.创建一个WInform项目 2.代码总览 代码解析 3.添加模型到场景 4.鼠标交互 1.创建一个WInform项目 2.代码总览 using Macad.Occt.Helper; using Macad.Occt; using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Remoting.Co…...
System.Dynamic.ExpandoObject的使用说明
官方文档 ExpandoObject 类 (System.Dynamic) | Microsoft Learn https://learn.microsoft.com/zh-cn/dotnet/api/system.dynamic.expandoobject?viewnet-8.0 System.Dynamic.ExpandoObject 类 - .NET | Microsoft Learn https://learn.microsoft.com/zh-cn/dotnet/fundame…...
adb之ps命令用法
目录 前言一、命令参数二、输出结果含义 前言 在adb shell终端,输入 ps,可查看手机当前所有的进程状态,其中ps的英文全称是Process Status。 ps命令对于分析系统异常情况时都是必备的技能,需要通过这个简单命令来查看系统真实的状…...
Ubuntu-24.04-live-server-amd64安装界面中文版
系列文章目录 Ubuntu安装qemu-guest-agent Ubuntu-24.04-live-server-amd64启用ssh Ubuntu乌班图安装VIM文本编辑器工具 文章目录 系列文章目录前言一、准备工作二、开始安装三、测试效果总结 前言 Centos结束,转战Ubuntu。我之所以写这篇文章,是因为我…...
Git的3个主要区域
一般来说,日常使用只要记住下图6个命令,就可以了。但是熟练使用,恐怕要记住60~100个命令。 下面是我整理的常用 Git 命令清单。几个专用名词的译名如下。 Workspace:工作区 Index / Stage:暂存区 Reposito…...
【操作系统】操作系统实验02-生产者消费者程序改进
1. 说明文档中原有程序实现的功能、实现方法。(用语言、程序流程图、为原有程序添加注释等方式均可) 1.//const.h 2.//定义宏变量 3.#ifndef CONST_H 4.#define CONST_H 5. 6.#define TRUE 1 7.#define FALSE 0 8.#define ERROR 0 9.#define OVERFLOW -…...
TCP协议是安全的吗?
不安全 虽然 TCP 提供了一种可靠且高效的数据传输方式,但它不提供任何加密或身份验证机制来保护数据。因此,传输的数据可能会被未经授权的用户拦截和读取,而且其真实性无法验证。 因此,为了确保 TCP 通信的安全,必须…...
c语言回顾-结构体(2)
前言 前面讲了结构体的概念,定义,赋值,访问等知识,本节内容小编将讲解结构体的内存大小的计算以及通过结构体实现位段,话不多说,直接上干货!!! 1.结构体内存对齐 说到计…...
Prometheus常见exporter安装部署
Prometheus常见exporter安装部署 在稳定性环境的监控当中需要收集各种各样的数据,这样的数据收集是通过各种exporter进行的,在这里我们进行最常用稳定性数据的收集exporter安装部署介绍。 node_exporter安装部署 node_exporter主要监控服务器本身的一…...
DGit的使用
将Remix连接到远程Git仓库 1.指定克隆的分支和深度 2.清理,如果您不在工作区上工作,请将其删除或推送至 GitHub 或 IPFS 以确保安全。 为了进行推送和拉取,你需要一个 PAT — 个人访问令牌 当使用 dGIT 插件在 GitHub 上推送、拉取、访问私…...
ElasticSearch学习篇13_《检索技术核心20讲》进阶篇之LSM树
背景 学习极客实践课程《检索技术核心20讲》https://time.geekbang.org/column/article/215243,文档形式记录笔记。 内容 磁盘和内存数据读取特点 工业界中数据量往往很庞大,比如数据无法全部加载进内存,无法支持索引的高效实时更新&…...
简单好用的C++日志库spdlog使用示例
文章目录 前言一、spdlog的日志风格fmt风格printf风格 二、日志格式pattern三、sink,多端写入四、异步写入五、注意事项六、自己封装了的代码usespdlog.h封装代码解释使用示例 前言 C日志库有很多,glog,log4cpp,easylogging, eas…...
python 方法运行计时装饰模式实现
在代码开发过程中,需要记录方法的执行时间,每个方法都硬代码也可以实现,但是不是最好的方式,考虑到设计模式和模版代码,通过装饰模式实现方法运行计时 在Python中,装饰器可以接受参数,这样可以…...
【权威出版/投稿优惠】2024年水利水电与能源环境科学国际会议(WRHEES 2024)
2024 International Conference on Water Resources, Hydropower, Energy and Environmental Science 2024年水利水电与能源环境科学国际会议 【会议信息】 会议简称:WRHEES 2024 大会时间:点击查看 截稿时间:点击查看 大会地点:…...
阿赵UE引擎C++编程学习笔记——场景加载和切换
大家好,我是阿赵。 继续学习UE引擎,这次来学习一下切换和加载场景的各种做法。 一、 蓝图实现 1、 切换关卡 所谓切换关卡,就是从当前关卡进入到一个新的关卡, 旧关卡的数据将会被放弃。进入新的关卡后,将会执行…...
【LLM之RAG】RAFT论文阅读笔记
研究背景 论文针对的主要问题是如何将预训练的大型语言模型(LLMs)适应特定领域的检索增强生成(RAG)。这些模型通常在广泛的文本数据上进行预训练,已经表现出在广义知识推理任务上的优越性能。然而,在特定领…...
【Android】使用Binder(AIDL)实现利用自定义Bean进行的进程间通信(二)
项目前置 这是我之前写的关于Binder的一些知识点和使用基本数据类型在通信的文章,感兴趣的可以看一下: Binder(一)Binder的介绍和AIDL使用Binder的实例 项目目标 在两个APP之间进行数据传递,使用Android推荐的Binder通讯&#…...
HTTP中get与post的区别?在传输数据类型上有什么区别?【面试】
HTTP中的GET和POST是两种最常见的请求方法,它们在数据传输和使用场景上有一些关键的区别: GET请求: 数据传输方式:GET请求将数据附加在URL之后,形成查询字符串(namevalue的形式),数…...
「51媒体-年中大促」天津有哪些媒体资源-媒体宣传服务公司
传媒如春雨,润物细无声,大家好,我是51媒体网胡老师。 天津的媒体资源相当丰富,涵盖了报纸、电视、广播、新闻门户网站、央媒驻天津机构、视频媒体以及全国媒体资源等多个方面。以下是详细的媒体资源分类和具体信息: 一…...
铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...
Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...
JDK 17 新特性
#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持,不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的ÿ…...
ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...
html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码
目录 一、👨🎓网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站效果 五、🪓 代码实现 🧱HTML 六、🥇 如何让学习不再盲目 七、🎁更多干货 一、👨…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...
排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...
基于鸿蒙(HarmonyOS5)的打车小程序
1. 开发环境准备 安装DevEco Studio (鸿蒙官方IDE)配置HarmonyOS SDK申请开发者账号和必要的API密钥 2. 项目结构设计 ├── entry │ ├── src │ │ ├── main │ │ │ ├── ets │ │ │ │ ├── pages │ │ │ │ │ ├── H…...
如何在Windows本机安装Python并确保与Python.NET兼容
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...
【阅读笔记】MemOS: 大语言模型内存增强生成操作系统
核心速览 研究背景 研究问题:这篇文章要解决的问题是当前大型语言模型(LLMs)在处理内存方面的局限性。LLMs虽然在语言感知和生成方面表现出色,但缺乏统一的、结构化的内存架构。现有的方法如检索增强生成(RA…...
