image

hunter的这个overlay system检测不是必要的,一开始推测检测原理就是在native 拿 statfs和fstatfs函数查看/system返回的f_type

image

10进制的2035054128对应的16进制是 0x794C7630 也就是overlayFS的魔数(magic number 这个专业术语我也不知道是什么)

5eb90ccb0bd2:/data/local/tmp # stat -f -c %T /system
0x794c7630

strace一下 再拿ebpf处理一下就ojbk了

image

牛头也检测,但是方法和hunter有些区别

root@orangepi5pro:~/ebpf# cat /sys/kernel/debug/tracing/available_events | grep statfs
syscalls:sys_exit_fstatfs
syscalls:sys_enter_fstatfs
syscalls:sys_exit_statfs
syscalls:sys_enter_statfs
root@orangepi5pro:~/ebpf# cat /sys/kernel/debug/tracing/events/syscalls/sys_enter_statfs/format
name: sys_enter_statfs
ID: 735
format:
        field:unsigned short common_type;       offset:0;       size:2; signed:0;
        field:unsigned char common_flags;       offset:2;       size:1; signed:0;
        field:unsigned char common_preempt_count;       offset:3;       size:1; signed:0;
        field:int common_pid;   offset:4;       size:4; signed:1;

        field:int __syscall_nr; offset:8;       size:4; signed:1;
        field:const char * pathname;    offset:16;      size:8; signed:0;
        field:struct statfs * buf;      offset:24;      size:8; signed:0;

print fmt: "pathname: 0x%08lx, buf: 0x%08lx", ((unsigned long)(REC->pathname)), ((unsigned long)(REC->buf))

image

光这一个点位还不够 fstats 也需要操作,ctx->arg[1] 是一个statfs的指针结构体,这个偷不了懒了,涉及到的syscall操作比较多,需要先用enter_openat判断路径存入hash_map,再去和enter_fstats和exit_fstats配合,exit的时候在hash_map里取出地址修改buf里的type,statfs的结构在这里能看到,最后在exit_fstats清理掉map

https://elixir.bootlin.com/linux/v5.10/source/arch/mips/include/uapi/asm/statfs.h#L23

cat /sys/kernel/debug/tracing/events/syscalls/sys_exit_fstatfs/format                                                  <
name: sys_exit_fstatfs
ID: 732
format:
        field:unsigned short common_type;       offset:0;       size:2; signed:0;
        field:unsigned char common_flags;       offset:2;       size:1; signed:0;
        field:unsigned char common_preempt_count;       offset:3;       size:1; signed:0;
        field:int common_pid;   offset:4;       size:4; signed:1;

        field:int __syscall_nr; offset:8;       size:4; signed:1;
        field:long ret; offset:16;      size:8; signed:1;

print fmt: "0x%lx", REC->ret

cat /sys/kernel/debug/tracing/events/syscalls/sys_enter_fstatfs/format                                                 <
name: sys_enter_fstatfs
ID: 733
format:
        field:unsigned short common_type;       offset:0;       size:2; signed:0;
        field:unsigned char common_flags;       offset:2;       size:1; signed:0;
        field:unsigned char common_preempt_count;       offset:3;       size:1; signed:0;
        field:int common_pid;   offset:4;       size:4; signed:1;

        field:int __syscall_nr; offset:8;       size:4; signed:1;
        field:unsigned int fd;  offset:16;      size:8; signed:0;
        field:struct statfs * buf;      offset:24;      size:8; signed:0;

print fmt: "fd: 0x%08lx, buf: 0x%08lx", ((unsigned long)(REC->fd)), ((unsigned long)(REC->buf))
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h>

// 简化版 只做可行性验证

struct {
    __uint(type, BPF_MAP_TYPE_HASH);
    __uint(max_entries, 128);
    __type(key, unsigned int);
    __type(value, u64);
} fstatfs_map SEC(".maps");

SEC("tracepoint/syscalls/sys_enter_fstatfs")
int tracepoint__syscalls__sys_enter_fstatfs(struct trace_event_raw_sys_enter *ctx)
{
    struct task_struct *task = (struct task_struct *)bpf_get_current_task();
    unsigned int pid_ns = get_namespace_pid(task);
    
    if(pid_ns != filter_ns){
        return 0;
    }

    u32 pid = bpf_get_current_pid_tgid() >> 32;

    u64 statfs_addr = (u64)ctx->args[1];
    bpf_map_update_elem(&fstatfs_map, &pid, &statfs_addr, BPF_ANY);

    return 0;
}

SEC("tracepoint/syscalls/sys_exit_fstatfs")
int tracepoint__syscalls__sys_exit_fstat(struct trace_event_raw_sys_exit *ctx)
{
    struct task_struct *task = (struct task_struct *)bpf_get_current_task();
    unsigned int pid_ns = get_namespace_pid(task);
    
    if(pid_ns != filter_ns){
        return 0;
    }

    u32 pid = bpf_get_current_pid_tgid() >> 32;

    u64 *statfs_addr = bpf_map_lookup_elem(&fstatfs_map, &pid);
    if(!statfs_addr){
        return 0;
    }

    struct statfs statfs;
    int ret = bpf_probe_read_user(&statfs, sizeof(statfs), (const void *)*statfs_addr);

    if(statfs.f_type == 2035054128){
        statfs.f_type = 0xef53;
        bpf_probe_write_user((void *)*statfs_addr, &statfs, sizeof(statfs));
    }
    
    bpf_map_delete_elem(&fstatfs_map, &pid);
    return 0;
}

char LICENSE[] SEC("license") = "GPL";

最后效果 又换成vendor了 实际测试下来上面的没有效果 但是代码对以后的修改结构体变量有帮助

imageimage


补上之后,好消息是消失了,但是又蹦出来了个新的东西 (Permission 权限检测问题)

image