60秒学会用eBPF-BCC hook系统调用
2022-11-11 18:29:6 Author: mp.weixin.qq.com(查看原文) 阅读量:34 收藏


本文为看雪论坛优秀文章

看雪论坛作者ID:爱吃菠菜

备注1: 前面是为了格式工整, 建议直接从11 hello world开始看。
备注2: 60秒指的是在Linux上, 如果是Android可能要在基础再上加点。
Linux内核中运行的虚拟机,
可以在外部向其注入代码执行。
理解成BFP PLUS++
BPF虚拟机只运行BPF指令, 直接敲BPF指令比较恶心。
BCC可以理解成辅助写BPF指令的工具包,
用python和c语言间接生成EBPF指令。
指的是开源项目&&开发者社区,
BCC是IOVisor项目下的编译器工具集。


[email protected]:~/Desktop/bcc/build$ uname -aLinux ubuntu 5.15.0-52-generic #58~20.04.1-Ubuntu SMP Thu Oct 13 13:09:46 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 4052245BD4284CDDecho "deb https://repo.iovisor.org/apt/$(lsb_release -cs) $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/iovisor.listsudo apt-get updatesudo apt-get install bcc-tools libbcc-examples linux-headers-$(uname -r)
echo "deb [trusted=yes] https://repo.iovisor.org/apt/xenial xenial-nightly main" | sudo tee /etc/apt/sources.list.d/iovisor.listsudo apt-get updatesudo apt-get install bcc-tools libbcc-examples linux-headers-$(uname -r)
lsb_release -aNo LSB modules are available.Distributor ID:    UbuntuDescription:    Ubuntu 20.04.5 LTSRelease:    20.04Codename:    focal
# For Focal (20.04.1 LTS)sudo apt install -y bison build-essential cmake flex git libedit-dev \libllvm12 llvm-12-dev libclang-12-dev python zlib1g-dev libelf-dev libfl-dev python3-distutils
git clone https://github.com/iovisor/bcc.gitmkdir bcc/build; cd bcc/buildcmake ..makesudo make installcmake -DPYTHON_CMD=python3 .. # build python3 bindingpushd src/python/makesudo make installpopd

运行hello_fields.py
这个脚本是一样的逻辑, 不过输出格式对齐了。
进入bcc/tools/目录,运行opensoop.py脚本。
然后自己开clion编一个demo,
调用open触发eBFP的callback。

我打开那个脚本看了一下, blabla一大堆, 基本都在处理兼容和格式,

我把不关心的东西都删了, 留下核心的代码, 写好注释放这里了。
如何任意的hook syscall?
只关心4点:
(1)怎么写before?
(2)怎么写after?
(3)怎么注册hook?
(4)怎么输出日志?
(跟xposed差不多的叙事结构)
#!/usr/bin/env python# 该代码在ubuntu 20环境里运行通过from __future__ import print_functionfrom bcc import ArgString, BPFfrom bcc.containers import filter_by_containersfrom bcc.utils import printbimport argparsefrom collections import defaultdictfrom datetime import datetime, timedeltaimport os # 注入到eBPF虚拟机的代码bpf_text = '''#include <uapi/linux/ptrace.h>#include <uapi/linux/limits.h>#include <linux/sched.h> // hook到参数和返回值,放在这两个结构里struct val_t {  u64 id;  char comm[TASK_COMM_LEN];  const char *fname;}; // 同上struct data_t {  u64 id;  int ret;  char comm[TASK_COMM_LEN];  char name[NAME_MAX];};  // 创建一个events (hook到东西后就用它通知python那个callback输出)BPF_PERF_OUTPUT(events);  // 这个api是在创建一个map变量,变量名为infotmp// 因为你不能在eBPF里用std::map, 只能用它提供的这种东西.BPF_HASH(infotmp, u64, struct val_t);  // after函数int after_openat(struct pt_regs *ctx) {  u64 id = bpf_get_current_pid_tgid(); // 获取tid  struct val_t *valp;  struct data_t data = {};  valp = infotmp.lookup(&id); // 在map中查询id  if (valp == 0) {    return 0;  }  // 从map中读取至局部变量  bpf_probe_read_kernel(&data.comm, sizeof(data.comm), valp->comm);  bpf_probe_read_user_str(&data.name, sizeof(data.name), (void *)valp->fname);  data.id = valp->id;  data.ret = PT_REGS_RC(ctx); // before里读取了参数,此时在after里补充返回值  events.perf_submit(ctx, &data, sizeof(data)); // 提交perf poll事件来让perf输出(作用就是,调用它会通知python中那个callback输出日志)  infotmp.delete(&id); // 从map中删除id  return 0;}  int syscall__before_openat(struct pt_regs *ctx, int dfd,                                const char __user *filename, int flags) {  struct val_t val = {};  u64 id = bpf_get_current_pid_tgid();  u32 pid = id >> 32;  // 获取当前进程名  if (bpf_get_current_comm(&val.comm, sizeof(val.comm)) == 0) {    val.id = id;    val.fname = filename;    infotmp.update(&id, &val); // id插入map  }  return 0;};''' # 注册hookb = BPF(text=bpf_text)b.attach_kprobe(event="__x64_sys_openat", fn_name="syscall__before_openat")b.attach_kretprobe(event="__x64_sys_openat", fn_name="after_openat") # 回调函数def my_callback(cpu, data, size):    temp = b["events"].event(data)    if temp.id is not None:        print("[pid]",temp.id & 0xffffffff, end=" ")    if temp.name is not None:        print("[path]",temp.name, end=" ")    if temp.ret is not None:        print("[ret]",temp.ret, end=" ")    if temp.comm is not None:        print("[comm]",temp.comm, end=" ")    print("") b["events"].open_perf_buffer(my_callback, page_cnt=64)while True:    try:        # 等待数据, 触发open_perf_buffer指定的回调函数        b.perf_buffer_poll()    except KeyboardInterrupt:        exit()pass

看雪ID:爱吃菠菜

https://bbs.pediy.com/user-home-760871.htm

*本文由看雪论坛 爱吃菠菜 原创,转载请注明来自看雪社区

看雪CTF官网:https://ctf.pediy.com/

# 往期推荐

1.CVE-2022-21882提权漏洞学习笔记

2.wibu证书 - 初探

3.win10 1909逆向之APIC中断和实验

4.EMET下EAF机制分析以及模拟实现

5.sql注入学习分享

6.V8 Array.prototype.concat函数出现过的issues和他们的POC们

球分享

球点赞

球在看

点击“阅读原文”,了解更多!


文章来源: https://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458483404&idx=1&sn=a75fd608eb28013525fc031631389488&chksm=b18e4a4686f9c350fa76ae2f3b56b65cd9bee86bfa3dcd793add37fd6d9e2f5fc5f179cf01da#rd
如有侵权请联系:admin#unsafe.sh