前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用 BPF 进行 HTTPS 抓包 —— 以 Golang 和 cURL 为例

使用 BPF 进行 HTTPS 抓包 —— 以 Golang 和 cURL 为例

原创
作者头像
ritchiechen
发布2021-02-25 20:21:55
2.9K1
发布2021-02-25 20:21:55
举报
文章被收录于专栏:Serverless+

背景

分析某些业务进程的 HTTPS 请求时,类似 tcpdump 和 Fiddler 等工具无法获得请求明文,不方便进行分析。本文探讨使用 BPF 解决 HTTPS 的可观测性问题。

实验环境

  • 内核版本 4.1 及以上的 Linux 机器
  • 未删除符号表的目标二进制

操作步骤

  • 阅读源码,找到要插入探针的函数
  • 编写代码,捕获 HTTPS 请求并打印

Golang 示例

目标程序

一个简单的示例,向第一个命令行参数指定的 URL 发起 HTTP GET 请求,目标进程代码如下:

代码语言:txt
复制
package main

import (
	"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 标准库的代码就知道,我们要插入的探针的代码位点在:

代码语言:txt
复制
crypto/tls.(*Conn).writeRecordLocked(typ recordType, data []byte)

第二个参数就是我们所需要的,知道了这个我们就可以插入探针输出这个参数了

探针代码如下:

代码语言:txt
复制
#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
bpf-https-golang.png

cURL 示例

目标程序

以 curl 7.68.0 为例。通常情况下,各 Linux 发行版上实用程序的符号表可能被 strip 掉了,所以需要获取代码重新编译,此过程略去不表。

探针代码

稍稍阅读一下 cURL 的源代码就知道,我们要插入的探针的代码位点在:

代码语言:txt
复制
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 的定义如下:

代码语言:txt
复制
struct Curl_send_buffer {
  char *buffer;
  size_t size_max;
  size_t size_used;
};

探针代码如下:

代码语言:txt
复制
#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
bpf-https-curl.png

总结

本文通过两个简单的例子,演示了如何使用 BPF 解决 HTTPS 的可观测性问题。大多数使用 Golang 编写的应用程序都会使用标准库,故上文的 Golang 示例具有通用性,我们可以很方便地追踪分析所有 Golang 程序的 HTTPS 调用

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • 实验环境
  • 操作步骤
  • Golang 示例
    • 目标程序
      • 探针代码
        • 实验结果
        • cURL 示例
          • 目标程序
            • 探针代码
              • 实验结果
              • 总结
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档