android ebpf初体验实现文件重定位

发布时间 2023-10-20 14:58:01作者: 怎么可以吃突突

之前通过修改内核插桩并编写内核模块的方式hookdo_sys_open函数(这种方式有点像tracepoint,都属于静态探测),这种方式优点是可以hook内核中的任意函数,但是需要编译内核和驱动模块较为麻烦。

eBPF相当于在内核中定义了一个虚拟机,能够加载eBPF字节码并依赖kprobe,uprobe,tracepoint实现内核层以及用户层函数的hook。每次更新hook代码不需要重新编译内核,只需要编写新的eBPF程序生成新的eBPF字节码。缺点是eBPF程序有很多限制,包括堆栈大小限制,内核函数调用限制(只能通过BPF Helper函数调用部分内核函数)等。同时在android平台上的开发编译和部署也是不太成熟,eBPF程序开发和部署相关参考https://github.com/eunomia-bpf/bpf-developer-tutorial/blob/main/src/0-introduce/README.md

目前最常用的的eBPF开发就是使用BCCBCC全称为BPF Compiler Collection,该项目是一个python库,包含了完整的编写、编译、和加载BPF程序的工具链。可以使用c编写内核部分代码并使用python加载和解析输出,环境搭建参考https://bbs.kanxue.com/thread-275176.htm#msg_header_h1_5https://cloud.tencent.com/developer/article/2312646

BCC官方开发教程https://github.com/iovisor/bcc/blob/master/docs/reference_guide.md#2-attach_kretprobehttps://github.com/iovisor/bcc/blob/master/docs/tutorial_bcc_python_developer.md,官方也提供了很多小工具作为例子。

参考官方文档编写一个文件重定位的BCC脚本,使用的pixel6内核是Linux 5.10,查看内核代码中openopenatopenat2系统调用最后的实现都是do_sys_openat2。对do_sys_openat2函数进行hook并修改参数即可实现文件重定位。

BCC脚本如下:

import argparse
from bcc import BPF

# define BPF program
prog = """
#include <uapi/linux/ptrace.h>
#include <linux/sched.h>
struct openat_entry_info {
    u32 pid;
    u64 time;
    char filename[NAME_MAX];
    char comm[TASK_COMM_LEN];
};

struct openat_filter_info {
    int fd;
    u32 pid;
    u64 time;
    char filename[NAME_MAX];
    char comm[TASK_COMM_LEN];
};
//key : pid_tid
BPF_HASH(entry_info, u64, struct openat_entry_info);

int pre_openat(struct pt_regs *ctx, int dfd, const char __user *filename, struct open_how *how) {
    struct openat_entry_info _info = {};
    u64 pid_tid = bpf_get_current_pid_tgid();
    _info.pid = pid_tid >> 32;
    _info.time = bpf_ktime_get_ns();

    if(bpf_get_current_comm(&_info.comm, sizeof(_info.comm)) != 0){
        return 0;
    }

    if(bpf_probe_read(&_info.filename, sizeof(_info.filename), (void*)PT_REGS_PARM2(ctx)) < 0){
        return 0;
    }
    entry_info.update(&pid_tid, &_info);

    size_t filename_len;
    for (filename_len = 0; filename_len < NAME_MAX; filename_len++) {
        if (_info.filename[filename_len] == '\\0') {
            break;
        }
    }   
    
    size_t num = 0;
    char hide_file[] = "HIDE_FILE_NAME";
    if(strlen(hide_file) == filename_len){
        for(int i = 0; i < filename_len; i++){
            if(hide_file[i] == _info.filename[i]){
                num++;
            }
            else{
                break;
            }
        }

        if(num == strlen(hide_file)){
            char relocate_file[] = "RELOCATE_FILE_NAME";
            bpf_probe_write_user((void*)filename, relocate_file, sizeof(relocate_file));
            bpf_trace_printk("Hello, World : %s!\\n", relocate_file);
        }
    }
    return 0;
}

BPF_PERF_OUTPUT(openat_filter_events);
int post_openat(struct pt_regs *ctx) {
    struct openat_filter_info filter_info = {};
    u64 pid_tid = bpf_get_current_pid_tgid();
    struct openat_entry_info* p_info;
    
    p_info = entry_info.lookup(&pid_tid);
    if(p_info == 0){
        return 0;
    }
    
    filter_info.fd = PT_REGS_RC(ctx);
    filter_info.pid = p_info->pid;
    filter_info.time = p_info->time;
    bpf_probe_read_kernel(&filter_info.comm, sizeof(filter_info.comm), p_info->comm);
    bpf_probe_read_kernel(&filter_info.filename, sizeof(filter_info.filename), p_info->filename);
    openat_filter_events.perf_submit(ctx, &filter_info, sizeof(filter_info));
    entry_info.delete(&pid_tid);
    return 0;
}
"""

# Get args
parser = argparse.ArgumentParser(description="file relocate by bcc")
parser.add_argument("--hide", type=str)
parser.add_argument("--relocate", type=str)
args = parser.parse_args()
prog = prog.replace("HIDE_FILE_NAME", args.hide)
prog = prog.replace("RELOCATE_FILE_NAME", args.relocate)

# load BPF program
b = BPF(text=prog)
b.attach_kprobe(event="do_sys_openat2", fn_name="pre_openat")
b.attach_kretprobe(event="do_sys_openat2", fn_name="post_openat")

# read events
def print_openat_filter(cpu, data, size):
    event = b["openat_filter_events"].event(data)
    print("%-10d %-32s %-6d %-32s" % (event.pid, event.comm, event.fd, event.filename))

b["openat_filter_events"].open_perf_buffer(print_openat_filter)
while True:
    try:
        b.perf_buffer_poll()
    except KeyboardInterrupt:
        exit()

在手机的BCC环境中运行脚本python relocate_file.py --hide /data/local/tmp/as --relocate /tmp/234234234,将/data/local/tmp/as文件重定位到一个不存在的文件/tmp/234234234