示例代码如下。我使用python-BCC库,编写了一个简单的BPF函数,并尝试在echo-bash内置函数上附加uprobe。
from bcc import BPF
prog = """
#include<linux/sched.h>
int echo_catch(struct pt_regs *ctx){
char command[64]={};
bpf_probe_read_user_str(command, sizeof(command), (char *) PT_REGS_PARM1(ctx));
bpf_trace_printk("%s", command);
return 0;
}
"""
b = BPF(text=prog)
b.attach_uprobe(name="/bin/bash", sym="echo_builtin", fn_name="echo_catch")
b.trace_print()
但它总是什么都不打印:
b' bash [001] 51239.033139: bpf_trace_printk:'
我做错什么了吗?如何获取用户模式程序的参数?
我认为这里的部分问题是对echo_builtin
函数的参数进行假设。如果您查看bash源代码,echo_builtin
的定义如下:
int
echo_builtin (list)
WORD_LIST *list;
也就是说,echo_builtin
的自变量不是要打印的单词;有一个参数,它是指向WORD_LIST
结构的指针(如果使用gdb
运行bash
并在echo_bulitin
上设置断点,则可以对此进行详细研究(。
更新1
这是一个可怕的黑客攻击,因为我不知道自己在做什么,但它展示了我试图传达的想法:
# Inspired partially by https://github.com/iovisor/bcc/blob/master/tools/bashreadline.py
from bcc import BPF
prog = """
#include<linux/sched.h>
typedef struct word_desc {
char *word;
int flags;
} WORD_DESC;
typedef struct word_list {
struct word_list *next;
WORD_DESC *word;
} WORD_LIST;
struct event_item {
char str[80];
};
BPF_PERF_OUTPUT(events);
int echo_catch(struct pt_regs *ctx){
WORD_LIST head, *cur;
WORD_DESC data;
int i;
cur = (void *)PT_REGS_PARM1(ctx);
for (i=0; i<10; i++) {
char *word;
struct event_item item = {"hello"};
if (! cur) break;
bpf_probe_read_user(&head, sizeof(head),
(void *)cur);
bpf_probe_read_user(&data, sizeof(data),
(void *)head.word);
bpf_probe_read_user_str(&item.str, sizeof(item.str), (void *)data.word);
events.perf_submit(ctx,&item,sizeof(item));
cur = head.next;
}
return 0;
}
"""
b = BPF(text=prog)
b.attach_uprobe(name="/bin/bash", sym="echo_builtin", fn_name="echo_catch")
def print_event(cpu, data, size):
event = b["events"].event(data)
print(event.str.decode('utf-8', 'replace'))
b["events"].open_perf_buffer(print_event)
while 1:
try:
b.perf_buffer_poll()
except KeyboardInterrupt:
break
如果您运行此程序并在bash终端中输入:
echo hello world this is a test
然后,您将看到bcc程序运行的位置:
hello
world
this
is
a
test
更新2
我以这个问题为借口了解了一些关于bpftrace的知识。我们可以使用以下bpftrace
脚本实现相同的解决方案,我认为它比早期的解决方案干净一点:
#!/usr/bin/bptrace
struct word_desc {
char *word;
int flags;
};
struct word_list {
struct word_list *next;
struct word_desc *word;
};
uprobe:/bin/bash:echo_builtin
{
$head = (struct word_list *)arg0;
$marker = $head;
$count = 10;
printf("%d: ", pid);
while ($count) {
printf("%s ", str($marker->word->word));
$marker = $marker->next;
if ($marker == 0) {
break;
}
$count--;
}
printf("n");
}
上述程序的输出将显示为<pid>: <arguments to echo command>
,例如:
76534: hello world
76534: this is a test