不仅要在所有地方hook
,还要在那里可编程,eBPF
让一切成为现实,而bpftrace
让这一过程变的简单。
内置变量 & 函数
- pid - Process ID (kernel tgid)
- tid - Thread ID (kernel pid)
- cgroup - Cgroup ID of the current process
- uid - User ID
- gid - Group ID
- nsecs - Nanosecond timestamp
- cpu - Processor ID
- comm - Process name
- stack - Kernel stack trace
- ustack - User stack trace
- arg0, arg1, … etc. - Arguments to the function being traced
- retval - Return value from function being traced
- func - Name of the function currently being traced
- probe - Full name of the probe
- curtask - Current task_struct as a u64
- rand - Random number of type u32
- hist(int n) - Produce a log2 histogram of values of n
- lhist(int n, int min, int max, int step) - Produce a linear histogram of values of n
- count() - Count the number of times this function is called
- sum(int n) - Sum this value
- min(int n) - Record the minimum value seen
- max(int n) - Record the maximum value seen
- avg(int n) - Average this value
- stats(int n) - Return the count, average, and total for this value
- delete(@x) - Delete the map element passed in as an argument
- str(char *s) - Returns the string pointed to by s
- printf(char *fmt, …) - Print formatted to stdout
- print(@x[, int top [, int div]]) - Print a map, with optional top entry count and divisor
- clear(@x) - Delete all key/values from a map
- sym(void *p) - Resolve kernel address
- usym(void *p) - Resolve user space address
- kaddr(char *name) - Resolve kernel symbol name
- uaddr(char *name) - Resolve user space symbol name
- reg(char *name) - Returns the value stored in the named register
- join(char *arr[]) - Prints the string array
- time(char *fmt) - Print the current time
- system(char *fmt) - Execute shell command
- exit() - Quit bpftrace
常用
网络-外发连接
hook: kprobe
查看内核函数参数及返回值。通过tcp_sendmsg
查看tcp外发包情况,tcpsend.bt:
#!/usr/bin/bpftrace
#include <net/sock.h>
k:tcp_sendmsg
{
@sk[tid] = arg0;
@size[tid] = arg2;
}
kr:tcp_sendmsg
/@sk[tid]/
{
$sk = (struct sock *)@sk[tid];
$size = @size[tid];
$af = $sk->__sk_common.skc_family;
if ($af == AF_INET) {
$daddr = ntop($af, $sk->__sk_common.skc_daddr);
$saddr = ntop($af, $sk->__sk_common.skc_rcv_saddr);
$lport = $sk->__sk_common.skc_num;
$dport = $sk->__sk_common.skc_dport;
$dport = ($dport >> 8) | (($dport << 8) & 0xff00);
printf("kr: %-15s %-5d -> %-15s %-5d: %d bytes, retval %d\n",
$saddr, $lport, $daddr, $dport, $size, retval);
} else {
printf("IPv6...\n");
}
delete(@sk[tid]);
delete(@size[tid]);
}
打印tcp IP地址、端口信息:
kr: 192.168.1.2 22 -> 192.168.1.3 62419: 128 bytes, retval 128
用户态-查看用户态函数参数及返回值
类型:kprobe
sum.c:
#include <stdio.h>
int sum(int a, int b) {
return a+b;
}
int main(void)
{
printf("sum 2 + 3 = %d\n", sum(2, 3));
printf("sum 3 + 2 = %d\n", sum(3, 2));
return 0;
}
uprobe, sum.bt:
#!/usr/bin/env bpftrace
uprobe:./sum:"sum"
{
printf("args: %d + %d\n", arg0, arg1);
}
uretprobe:./sum:"sum"
{
printf("result: %d\n", reg("ax"));
}
./sum.bt
attach uprobe之后,再运行./sum
.
Attaching 2 probes...
args: 2 + 3
result: 5
args: 3 + 2
result: 5
用户态审计-bash - history
bash-readline.bt:
#!/usr/bin/env bpftrace
uretprobe:bash:"readline_internal_teardown"
{
printf(": %s\n", str(reg("ax")));
}
可以抓到shell
的所有输入:
Attaching 1 probe...
: whoami
: ps
: cat /etc/passwd >/tmp/log
: cat tmp/log
: cat /tmp/log
获取进程打开文件
bpftrace -e 'tracepoint:syscalls:sys_enter_openat { printf("%s %s\n", comm, str(args->filename)); }'
输出内容很多:
vim /usr/share/vim/vim82/scripts.vim
vim /etc/vim/vimrc.local
vim .
vim /home/ubuntu/.vimrc
阻断(enforce)
override
Kernel 4.16
#!/usr/bin/bpftrace --unsafe
kprobe:__x64_sys_openat
/comm == "vim"/ {
printf("comm: %s\n", (comm));
override(-1);
}
vim
会被阻断:
vim /tmp/a
vim: error while loading shared libraries: libm.so.6: cannot open shared object file: Operation not permitted
signal
Kernel 5.3
#!/usr/bin/bpftrace --unsafe
kprobe:__x64_sys_execve
/comm == "bash"/ {
signal(5);
}
被attatch
的进程会被杀掉:
ls
Trace/breakpoint trap (core dumped)
参考资料
文档信息
- 本文作者:seamaner
- 本文链接:https://seamaner.github.io/2024/10/31/bpftrace/
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)