btrace:binder_transaction+eBPF+Golang实现通用的Android APP动态行为追踪工具

本文主要是介绍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动态行为追踪工具的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1063598

相关文章

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

如何通过Python实现一个消息队列

《如何通过Python实现一个消息队列》这篇文章主要为大家详细介绍了如何通过Python实现一个简单的消息队列,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录如何通过 python 实现消息队列如何把 http 请求放在队列中执行1. 使用 queue.Queue 和 reque

Python如何实现PDF隐私信息检测

《Python如何实现PDF隐私信息检测》随着越来越多的个人信息以电子形式存储和传输,确保这些信息的安全至关重要,本文将介绍如何使用Python检测PDF文件中的隐私信息,需要的可以参考下... 目录项目背景技术栈代码解析功能说明运行结php果在当今,数据隐私保护变得尤为重要。随着越来越多的个人信息以电子形

使用 sql-research-assistant进行 SQL 数据库研究的实战指南(代码实现演示)

《使用sql-research-assistant进行SQL数据库研究的实战指南(代码实现演示)》本文介绍了sql-research-assistant工具,该工具基于LangChain框架,集... 目录技术背景介绍核心原理解析代码实现演示安装和配置项目集成LangSmith 配置(可选)启动服务应用场景

使用Python快速实现链接转word文档

《使用Python快速实现链接转word文档》这篇文章主要为大家详细介绍了如何使用Python快速实现链接转word文档功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 演示代码展示from newspaper import Articlefrom docx import