逆向-内核-ebpf

搬砖

ebpf in android

1.ebpf原理

收集资料
https://www.ebpf.top/post/ebpf-overview-part-3/

我当前阶段理解的重点是: 前端,后端,加载器, 数据结构

2.ebpf学习经历

收集资料

ebpf是linux内核提供的hook api
看了很多大佬博客,前期在Android上运行bpf程序遇到问题很多,重点是内核版本,Android api变化, 编译环境方面问题很多
总结了下bpf快速上手路线,我的手机小米12,应使用kenelSu root了

  1. 多看理解早期基本用法 bpf on Android

  2. Android上体验bcc脚本

    • 环境准备
      安装magiskSSH模块,它会帮我创建好linux在安卓上的环境,还会准备好ssh, 注意这里用的frp内网穿透提供ssh登录能力,如果不需要做内网穿透,直接ssh链接内网手机就可以了。当然我是用frp内网穿透链接云手机,只需要将我自己电脑上的公钥上传到跳板机器和内网手机

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      Host jumpserver2
      User root
      HostName *****
      Port 22

      Host phone
      User root
      Port 22900
      HostName localhost
      ProxyJump jumpserver2

      发现没有,不需要额外配置 IdentityFile,直接ssh phone

      本来打算用 vscode ssh 开发的,折腾了半天vscode无法打开Android目录,因为在ssh链接时候用了bash 命令,然而Android上没有bash命令,报解析错误,后改为在手机端启动code tunnel 来远程开发

    • 体验ebpf
      没体验赶紧体验,不懂就问chatgpt,比如

      1
      2
      3
      4
      5
      BPF Maps
      BPF_PERCPU_ARRAY(ssl_data, struct probe_SSL_data_t, 1); 声明了一个 per-CPU 数组来存储追踪事件的数据。// 也就是初始化1个数据结构probe_SSL_data_t 放在 ssl_data
      BPF_PERF_OUTPUT(perf_SSL_rw); 和 BPF_PERF_OUTPUT(perf_SSL_do_handshake); 创建了用于将追踪数据发送到用户空间的性能输出数组。// 也就是创建的输出管道,内核里不断perf_submit 提交数据到管道,python前端注册好管道监听就能获取到相关数据
      BPF_HASH(start_ns, u32); 和 BPF_HASH(bufs, u32, u64); 声明了哈希表来存储事件开始的时间戳和缓冲区地址。// 这个就不用解释了,hash

  3. eCapture 编译

    • 复习下osi七层协议,重点是ssl/tls

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      应用层          (第7层) | ← 应用程序(如 Web 浏览器、电子邮件客户端)
      表示层 (第6层) | ← 数据格式化、数据加密、数据压缩
      会话层 (第5层) | ← 会话管理、身份验证
      -------------------------| ← SSL/TLS 加密层 (对tcp加密;提供了身份验证、密钥交换、加密和解密)
      传输层 (第4层) | ← 可靠的数据传输(如 TCP 协议)
      网络层 (第3层) | ← 路由、IP 数据报传输、地址解析
      数据链路层 (第2层) | ← 物理寻址、错误检测和修正
      物理层 (第1层) | ← 物理介质传输(如电缆、光纤)

      socket是抽象概念,跨越了多个层次。
      应用层协议(如 HTTP、FTP、SMTP)使用 Socket API 来实现网络通信功能。(应用层)
      Socket定义了端口和传输协议(如 TCP 或 UDP)(传输层)
      Socket本身不包含会话层和表示层的全部功能

      在 Android 系统中,绝大多数与 TLS 相关的操作都是通过位于 /apex/com.android.conscrypt/lib64/libssl.so(对于 64 位系统)这一路径下的库来执行的。这个库是一个由 BoringSSL 提供支持的 Conscrypt 库的一部分,BoringSSL 本身是 Google 维护的 OpenSSL 的分支版本。

      Conscrypt 作为 Android 平台上的一个安全提供者,为高级应用程序编程接口(API)实现了底层的加密协议。应用程序和开发者通常使用 Android SDK 中提供的更高级别的 API,例如 javax.net.ssl.SSLContext 和 HttpsURLConnection,以及流行的网络库如 OkHttp,它们在内部都沟通与 Conscrypt 提供的功能以建立和维护安全的网络连接。

      除非开发者在 Android 应用中明确地引入了自定义的 TLS 库(例如使用一个独立的 OpenSSL 版本或其他的 SSL/TLS 实现),否则默认情况下,安全通信都是通过系统提供的 Conscrypt 实现的 TLS 功能来处理的。通过 Security 类和 Provider 类的 API,Conscrypt 被集成和注册到 Java 安全框架中。这意味着不仅系统本身的网络通信依赖 Conscrypt,第三方应用程序除非另外指定,都会默认使用它来处理相关的 SSL/TLS 连接。

      因此,如果你正在处理安全连接、证书验证或其他与 TLS 相关的安全特性,只要不是使用的自定义的加密库,你可以认为所有的操作最终都是通过 Android 系统提供的 Conscrypt 实现,进而调用 /apex/com.android.conscrypt/lib64/libssl.so 库的函数来完成的。

    • 编译eCapture
      我的编译过程

      1. 使用gnirehtet将手机网络请求转发到电脑,电脑可翻墙加速下载
      2. 配置go,按照上面文章就能可以搞定,最后缺什么环境变量补什么
      3. 编译 make
    • eCapture使用

      1. 先看下eCapture项目博客的相关介绍

      2. 复习下bcc项目 只看“Network and Sockets Tools”

        • [x]tools/gethostlatency
        • [x]tools/bindsnoop
        • [x]tools/netqtop tools/netqtop.c
        • [x]tools/sofdsnoop
        • [x]tools/solisten
        • [x]tools/sslsniff “推荐 python sslsniff.py -p pid -l –handshake -g -n”
        • [x]tools/tcpaccept
        • [x]tools/tcpconnect
        • [x]tools/tcpconnlat
        • [x]tools/tcpdrop
        • [x]tools/tcplife
        • [x]tools/tcpretrans
        • [x]tools/tcprtt
        • [x]tools/tcpstates
        • [x]tools/tcpsubnet
        • [x]tools/tcpsynbl
        • [x]tools/tcptop
        • [x]tools/tcptracer
        • [x]tools/tcpcong
      3. ecapture编译
        我的手机需要引入header文件
        ANDROID=1 make nocore

      4. 使用 ./ecapture tls -h

      5. vscode remote 远程调试ecapture

        • 在手机端启动 ./code tunnel (我这里用的是vscode 隧道)
        • vscode 安装Golang插件,自动补全go代码
        • 插件装好后配置launch.json这个代码运行配置文件,重点是args 这个参数
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          {
          "version": "0.2.0",
          "configurations": [
          {
          "name": "Launch ecapture",
          "type": "go",
          "request": "launch",
          "mode": "debug",
          "program": "${workspaceFolder}", // 确保这里的路径指向正确的可执行文件或包
          "args": ["tls"], // 替换为你的实际参数
          "logOutput": "dap"
          }
          ]
          }
        • debug
        • 这时你会遇到 libpcap 没有安装问题,找到/lib/libpcap目录,./configure && make && make install
      6. 代码分析

        对比分析下

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        // 来自ecapture
        struct {
        __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
        __uint(key_size, sizeof(u32));
        __uint(value_size, sizeof(u32));
        __uint(max_entries, 1024);
        } tls_events SEC(".maps");

        // 来自bcc sslsniff.py
        struct bpf_map_def SEC("maps") ssl_data = {
        .type = BPF_MAP_TYPE_PERCPU_ARRAY,
        .key_size = sizeof(u32),
        .value_size = sizeof(struct probe_SSL_data_t),
        .max_entries = 1
        };

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        映射类型:
        tls_events 是一个 BPF_MAP_TYPE_PERF_EVENT_ARRAY 类型,用于发送事件数据到用户空间。
        ssl_data 是一个 BPF_MAP_TYPE_PERCPU_ARRAY 类型,用于每个 CPU 存储特定的数据。

        使用场景:
        tls_events 用于与用户空间通信,如监控或跟踪 SSL/TLS 事件。
        ssl_data 用于存储每个 CPU 相关数据,例如用于性能监控或者调试目的。

        映射大小:
        tls_events 可以根据系统上的 CPU 数量进行扩展,最多支持 1024 个 CPU。
        ssl_data 为每个 CPU 提供了一个数组元素。

        tls_events ssl_data 可以理解为不通作用的管道么?

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        可以这样理解,tls_events 和 ssl_data 在 eBPF 程序中的确类似于不同作用的“管道”,其中每个管道都有不同的用途和行为。这里的“管道”是指它们各自如何在内核和用户空间之间传输数据的方式。

        tls_events 映射(管道)
        tls_events 映射是一个 BPF_MAP_TYPE_PERF_EVENT_ARRAY 类型的 eBPF 映射。它的主要作用是作为一个通道将数据从内核空间(eBPF 程序)发送到用户空间(分析工具或者监听程序)。这种映射允许 eBPF 程序通过 perf event 基础设施高效地传输数据,该数据通常与监控或跟踪事件有关。

        当 eBPF 程序用 bpf_perf_event_output 函数把数据发送到 tls_events 映射时,用户空间监听程序可以从关联的 perf event 文件描述符读取这些事件数据。

        ssl_data 映射(管道)
        ssl_data 映射是一个 BPF_MAP_TYPE_PERCPU_ARRAY 类型的 eBPF 映射。这个映射像是为每个 CPU 专门创建的一个本地存储区域,使得 eBPF 程序可以避免跨 CPU 的数据共享和潜在的竞争状态。它通常用于存储每个 CPU 核心特定的上下文信息或用于临时存储计算数据。

        由于是 per-CPU 映射,ssl_data 并不直接涉及到用户空间的数据传输。它更像是内核空间内部的一个局部存储区,用于 eBPF 程序的内部数据管理。

        总结
        tls_events 和 ssl_data 虽然都是 eBPF 程序中的数据结构,但它们的“管道”作用是不同的:

        tls_events 类似于一个将数据从 eBPF 程序发送到用户空间的出口管道。
        ssl_data 类似于一个在内核中为每个 CPU 提供数据存储的内部管道。
        根据需要传输数据的方向(内核到用户空间,还是内核内部)和用途(事件传输,还是数据存储),你会选择合适的映射类型来实现你的目标。

      7. 参数ssl读取

        • ecapature/utils/ 下介绍了如何读取ssl结构体相关的参数信息
  4. 定制bcc/ebpf在android平台上实现基于dwarf的用户态栈回溯

    1. unwinddaemon

      • [x]直接ndk交叉编译 ./build.sh
        编译问题: https://github.com/SeeFlowerX/unwinddaemon/issues/2
      • [x]基于aosp编译
        编译问题: https://blog.csdn.net/Chris_1994/article/details/135017508
        1
        2
        3
        4
        5
        6
        7
        8
        1. 直接拉取对应分支
        repo init -u https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/platform/manifest -b android14-release
        repo sync -j8

        2. 交换内存不够的处理办法
        sudo dd if=/dev/zero of=/swapfile bs=1M count=20480
        sudo mkswap /swapfile
        sudo swapon /swapfile

        unwinddaemon编译问题:
        1
        2
        3
        system/extras/unwinddaemon/lib.cpp:454:85: error: unused parameter 'stack_buf' [-Werror,-Wunused-parameter]
        const char* UnwindCallChainV2(int pid, UnwindOption* opt, uint64_t* regs_buf, void* stack_buf)
        注: 对于警告问题直接注释,(void)stack_buf; // 显式声明 stack_buf 为未使用
    2. unwindbcc 主要是在注册事件前,事件回调后修改相关代码。实现跨进程通讯打印

      1. open_perf_buffer(BPF.cc)->open_all_cpu->open_on_cpu->bpf_open_perf_buffer_opts(libbpf.c)->__NR_perf_event_open
      2. perf_reader_poll(perf_reader.c)->perf_reader_event_read->parse_sw->print_frame_info
  5. stackplz
    注:go环境和ndk交叉编译, android端debian没有对于的cpu架构ndk

3.uprobe检测

https://www.cnxct.com/defeating-ebpf-uprobe-monitoring/