本文探索使用 BPF
改变运行中的程序的函数参数,挖掘 BPF
的黑魔法。
这是我们的示例程序,打印第一个命令行参数:
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
修改对应地址的内存内容来达成修改函数参数的目的。你可以在这里找到本文的全部代码。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。