BPFHTTPS
实验环境
- 内核版本 4.1 及以上的 Linux 机器
- 未删除符号表的目标二进制
- 阅读源码,找到要插入探针的函数
- 编写代码,捕获 HTTPS 请求并打印
目标程序
一个简单的示例,向第一个命令行参数指定的 URL 发起 HTTP GET 请求,目标进程代码如下:
package mainimport ( "fmt" "io/ioutil" "net/http" "os")func main() { resp, err := http.Get(os.Args[1]) if err != nil { panic(err) } defer resp.Body.Close() data, err := ioutil.ReadAll(resp.Body) if err != nil { panic(err) } fmt.Println(string(data))}
探针代码
稍稍阅读一下 Go 标准库的代码就知道,我们要插入的探针的代码位点在:
crypto/tls.(*Conn).writeRecordLocked(typ recordType, data []byte)
第二个参数就是我们所需要的,知道了这个我们就可以插入探针输出这个参数了
探针代码如下:
#include <uapi/linux/ptrace.h>BPF_PERF_OUTPUT(trace);inline int crack_https(struct pt_regs *ctx) { u8 buf[256] = {0}; u64* addr = (u64*)ctx->sp; u64 val = 0; bpf_probe_read(&val, sizeof(val), addr + 2); if (val != 23) { return 0; } val = 0; bpf_probe_read(&val, sizeof(val), addr + 3); addr = (u64*)val; bpf_probe_read(buf, sizeof(buf), addr); trace.perf_submit(ctx, &buf, sizeof(buf)); return 0;}
实验结果
bpf-https-golang.png
cURL 示例目标程序
以 curl 7.68.0 为例。通常情况下,各 Linux 发行版上实用程序的符号表可能被 strip 掉了,所以需要获取代码重新编译,此过程略去不表。
探针代码
稍稍阅读一下 cURL 的源代码就知道,我们要插入的探针的代码位点在:
CURLcode Curl_add_buffer_send(Curl_send_buffer **inp, struct connectdata *conn, /* add the number of sent bytes to this counter */ curl_off_t *bytes_written, /* how much of the buffer contains body data */ size_t included_body_bytes, int socketindex)
Curl_send_buffer
struct Curl_send_buffer { char *buffer; size_t size_max; size_t size_used;};
探针代码如下:
#include <uapi/linux/ptrace.h>BPF_PERF_OUTPUT(trace);inline int crack_https(struct pt_regs *ctx) { u8 buf[256] = {0}; u64 val = 0; // Curl_send_buffer** u64* addr = (u64*)ctx->di; // Curl_send_buffer* bpf_probe_read(&val, sizeof(val), addr); addr = (u64*)val; // Curl_send_buffer bpf_probe_read(&val, sizeof(val), addr); addr = (u64*)val; // Curl_send_buffer.buffer bpf_probe_read(buf, sizeof(buf), addr); trace.perf_submit(ctx, &buf, sizeof(buf)); return 0;}
实验结果
bpf-https-curl.png
总结HTTPS