使用 BPF 改变运行中的程序的函数参数

本文探索使用 BPF 改变运行中的程序的函数参数,挖掘 BPF 的黑魔法。

实验环境

  • Ubuntu 20.04.2 LTS
  • BCC

测试程序

这是我们的示例程序,打印第一个命令行参数:

package main

import (
	"fmt"
	"os"
	"time"
)

//go:noinline
func greet(s string) {
	fmt.Println(s)
}

func main() {
	for {
		greet(os.Args[1])
		time.Sleep(time.Second)
	}
}

注意到我们使用 //go:noinline 修饰了 main.greet 函数,防止被编译器内联,方便进行测试验证。

这是我们的 BPF 程序,尝试修改函数参数为字符串 You are hacked!:

#include <uapi/linux/ptrace.h>

int hack(struct pt_regs *ctx) {
    char text[] = "You are hacked!";
    // read string address
    u64 addr = 0;
    u64* sp = (u64*)ctx->sp;
    bpf_probe_read(&addr, sizeof(addr), sp + 1);
    // overwrite string content
    bpf_probe_write_user((u64*)addr, text, sizeof(text));
    return 0;
}

使用 bpf_probe_write_user 修改用户内存空间的内容,此操作存在风险,因此每当带有此函数的 BPF 程序被加载时,从 dmesg 中都可以看到如下日志:

tracer[609901] is installing a program with bpf_probe_write_user helper that may corrupt user memory!

实验结果

在第一个终端先启动示例程序,每隔一秒打印字符串 hello world!:

$ ./tracee 'hello world!'
hello world!
hello world!
...

在第二个终端再启动 BPF 程序:

$ sudo ./tracer /path/to/tracee 'main.greet'

此时再看看示例程序的输出:

$ ./tracee 'hello world!'
hello world!
hello world!
...
You are hack
You are hack
You are hack
...

修改成功!

结论

本文探索使用 BPF 修改执行中的 Go 程序的函数参数, 由于 Golang 的 ABI 是使用栈来传递函数参数,通过读取栈上的指针地址,使用 bpf_probe_write_user 修改对应地址的内存内容来达成修改函数参数的目的。你可以在这里找到本文的全部代码。

参考

本站文章资源均来源自网络,除非特别声明,否则均不代表站方观点,并仅供查阅,不作为任何参考依据!
如有侵权请及时跟我们联系,本站将及时删除!
如遇版权问题,请查看 本站版权声明
THE END
分享
二维码
海报
使用 BPF 改变运行中的程序的函数参数
注意到我们使用 //go:noinline 修饰了 main.greet 函数,防止被编译器内联,方便进行测试验证。
<<上一篇
下一篇>>