Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 8 additions & 17 deletions src/30-sslsniff/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -321,24 +321,12 @@ In the eBPF ecosystem, user-space and kernel-space code often work in collaborat

In the provided code snippet, based on the setting of the `env` environment variable, the program can choose to attach to three common encryption libraries (OpenSSL, GnuTLS, and NSS). This means that we can trace calls to multiple libraries within the same tool.

To achieve this functionality, the `find_library_path` function is first used to determine the library's path. Then, depending on the library type, the corresponding `attach_` function is called to attach the eBPF program to the library function.
To achieve this functionality, the `find_library_path` function is first used to determine the library's path. Then, depending on the library type, the corresponding `attach_` function is called to attach the eBPF program to the library function. If `find_library_path` cannot locate a library (for example, because it is not installed on the system), the tool prints a warning to stderr and skips probing that provider instead of attaching against an invalid path. Each successful attachment is tracked separately so that multiple libraries can share the same eBPF program without losing link handles. Incompatible provider symbols produce a warning, and the tool exits if no probe could be attached.

```c
if (env.openssl) {
char *openssl_path = find_library_path("libssl.so");
printf("OpenSSL path: %s\n", openssl_path);
attach_openssl(obj, openssl_path);
}
if (env.gnutls) {
char *gnutls_path = find_library_path("libgnutls.so");
printf("GnuTLS path: %s\n", gnutls_path);
attach_gnutls(obj, gnutls_path);
}
if (env.nss) {
char *nss_path = find_library_path("libnspr4.so");
printf("NSS path: %s\n", nss_path);
attach_nss(obj, nss_path);
}
attach_provider(obj, env.openssl, "OpenSSL", "libssl.so", attach_openssl);
attach_provider(obj, env.gnutls, "GnuTLS", "libgnutls.so", attach_gnutls);
attach_provider(obj, env.nss, "NSS", "libnspr4.so", attach_nss);
```

This section primarily covers the attachment logic for the OpenSSL, GnuTLS, and NSS libraries. NSS is a set of security libraries designed for organizations, supporting the creation of secure client and server applications. Originally developed by Netscape, they are now maintained by Mozilla. The other two libraries have been introduced earlier and are not reiterated here.
Expand All @@ -352,8 +340,11 @@ The specific `attach` functions are as follows:
do { \
LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts, .func_name = #sym_name, \
.retprobe = is_retprobe); \
skel->links.prog_name = bpf_program__attach_uprobe_opts( \
struct bpf_link *link = bpf_program__attach_uprobe_opts( \
skel->progs.prog_name, env.pid, binary_path, 0, &uprobe_opts); \
int attach_err = track_attached_link(link, #prog_name); \
if (attach_err) \
return attach_err; \
} while (false)

int attach_openssl(struct sslsniff_bpf *skel, const char *lib) {
Expand Down
25 changes: 8 additions & 17 deletions src/30-sslsniff/README.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -309,24 +309,12 @@ int BPF_URETPROBE(probe_SSL_do_handshake_exit) {

上述代码片段中,根据环境变量 `env` 的设定,程序可以选择针对三种常见的加密库(OpenSSL、GnuTLS 和 NSS)进行挂载。这意味着我们可以在同一个工具中对多种库的调用进行追踪。

为了实现这一功能,首先利用 `find_library_path` 函数确定库的路径。然后,根据库的类型,调用对应的 `attach_` 函数来将 eBPF 程序挂载到库函数上。
为了实现这一功能,首先利用 `find_library_path` 函数确定库的路径。然后,根据库的类型,调用对应的 `attach_` 函数来将 eBPF 程序挂载到库函数上。如果 `find_library_path` 找不到某个库(例如系统中没有安装该库),工具会向 stderr 打印一条警告并跳过该库的探测,而不是挂载到无效的路径上。每次成功挂载都会单独保存 link 句柄,因此多个库可以复用同一个 eBPF 程序而不会丢失句柄;库符号不兼容时会给出警告,如果一个探针都未能挂载,工具会退出。

```c
if (env.openssl) {
char *openssl_path = find_library_path("libssl.so");
printf("OpenSSL path: %s\n", openssl_path);
attach_openssl(obj, openssl_path);
}
if (env.gnutls) {
char *gnutls_path = find_library_path("libgnutls.so");
printf("GnuTLS path: %s\n", gnutls_path);
attach_gnutls(obj, gnutls_path);
}
if (env.nss) {
char *nss_path = find_library_path("libnspr4.so");
printf("NSS path: %s\n", nss_path);
attach_nss(obj, nss_path);
}
attach_provider(obj, env.openssl, "OpenSSL", "libssl.so", attach_openssl);
attach_provider(obj, env.gnutls, "GnuTLS", "libgnutls.so", attach_gnutls);
attach_provider(obj, env.nss, "NSS", "libnspr4.so", attach_nss);
```

这里主要包含 OpenSSL、GnuTLS 和 NSS 三个库的挂载逻辑。NSS 是为组织设计的一套安全库,支持创建安全的客户端和服务器应用程序。它们最初是由 Netscape 开发的,现在由 Mozilla 维护。其他两个库前面已经介绍过了,这里不再赘述。
Expand All @@ -340,8 +328,11 @@ int BPF_URETPROBE(probe_SSL_do_handshake_exit) {
do { \
LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts, .func_name = #sym_name, \
.retprobe = is_retprobe); \
skel->links.prog_name = bpf_program__attach_uprobe_opts( \
struct bpf_link *link = bpf_program__attach_uprobe_opts( \
skel->progs.prog_name, env.pid, binary_path, 0, &uprobe_opts); \
int attach_err = track_attached_link(link, #prog_name); \
if (attach_err) \
return attach_err; \
} while (false)

int attach_openssl(struct sslsniff_bpf *skel, const char *lib) {
Expand Down
93 changes: 63 additions & 30 deletions src/30-sslsniff/sslsniff.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,28 @@
#define INVALID_UID -1
#define INVALID_PID -1
#define DEFAULT_BUFFER_SIZE 8192
#define MAX_ATTACH_LINKS 32

static struct bpf_link *attached_links[MAX_ATTACH_LINKS];
static size_t attached_link_count;
static int track_attached_link(struct bpf_link *link, const char *program_name);
static char *find_library_path(const char *libname);

#define __ATTACH_UPROBE(skel, binary_path, sym_name, prog_name, is_retprobe) \
do { \
LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts, .func_name = #sym_name, \
.retprobe = is_retprobe); \
skel->links.prog_name = bpf_program__attach_uprobe_opts( \
struct bpf_link *link = bpf_program__attach_uprobe_opts( \
skel->progs.prog_name, env.pid, binary_path, 0, &uprobe_opts); \
} while (false)

#define __CHECK_PROGRAM(skel, prog_name) \
do { \
if (!skel->links.prog_name) { \
perror("no program attached for " #prog_name); \
return -errno; \
} \
int attach_err = track_attached_link(link, #prog_name); \
if (attach_err) \
return attach_err; \
} while (false)

#define __ATTACH_UPROBE_CHECKED(skel, binary_path, sym_name, prog_name, \
is_retprobe) \
do { \
__ATTACH_UPROBE(skel, binary_path, sym_name, prog_name, is_retprobe); \
__CHECK_PROGRAM(skel, prog_name); \
} while (false)

#define ATTACH_UPROBE_CHECKED(skel, binary_path, sym_name, prog_name) \
Expand All @@ -59,7 +59,7 @@ const char argp_program_doc[] =
"USAGE: sslsniff [OPTIONS]\n"
"\n"
"EXAMPLES:\n"
" ./sslsniff # sniff OpenSSL and GnuTLS functions\n"
" ./sslsniff # sniff OpenSSL, GnuTLS, and NSS functions\n"
" ./sslsniff -p 181 # sniff PID 181 only\n"
" ./sslsniff -u 1000 # sniff only UID 1000\n"
" ./sslsniff -c curl # sniff curl command only\n"
Expand Down Expand Up @@ -90,8 +90,8 @@ struct env {
.uid = INVALID_UID,
.pid = INVALID_PID,
.openssl = true,
.gnutls = false,
.nss = false,
.gnutls = true,
.nss = true,
.comm = NULL,
};

Expand Down Expand Up @@ -167,6 +167,27 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) {
#define PERF_POLL_TIMEOUT_MS 100
#define warn(...) fprintf(stderr, __VA_ARGS__)

static int track_attached_link(struct bpf_link *link, const char *program_name) {
long err = libbpf_get_error(link);

if (err) {
warn("failed to attach %s: %s\n", program_name, strerror(-err));
return (int)err;
}
if (attached_link_count == MAX_ATTACH_LINKS) {
bpf_link__destroy(link);
warn("too many SSL probes requested\n");
return -E2BIG;
}
attached_links[attached_link_count++] = link;
return 0;
}

static void destroy_attached_links(void) {
while (attached_link_count > 0)
bpf_link__destroy(attached_links[--attached_link_count]);
}

static struct argp argp = {
opts,
parse_arg,
Expand Down Expand Up @@ -232,16 +253,33 @@ int attach_nss(struct sslsniff_bpf *skel, const char *lib) {
return 0;
}

static void attach_provider(struct sslsniff_bpf *obj, bool enabled,
const char *name, const char *libname,
int (*attach)(struct sslsniff_bpf *, const char *)) {
if (!enabled)
return;
char *path = find_library_path(libname);

if (!path) {
warn("%s not found; skipping %s probing\n", libname, name);
return;
}
printf("%s path: %s\n", name, path);
if (attach(obj, path))
warn("%s probing is incomplete\n", name);
}

/*
* Find the path of a library using ldconfig.
*/
char *find_library_path(const char *libname) {
static char *find_library_path(const char *libname) {
char cmd[128];
static char path[512];
FILE *fp;

// Construct the ldconfig command with grep
snprintf(cmd, sizeof(cmd), "ldconfig -p | grep %s", libname);
// Match the SONAME prefix literally; dots in library names must not be
// interpreted as regular-expression wildcards.
snprintf(cmd, sizeof(cmd), "ldconfig -p | grep -F -- '%s'", libname);

// Execute the command and read the output
fp = popen(cmd, "r");
Expand Down Expand Up @@ -397,21 +435,15 @@ int main(int argc, char **argv) {
goto cleanup;
}

if (env.openssl) {
char *openssl_path = find_library_path("libssl.so");
printf("OpenSSL path: %s\n", openssl_path);
attach_openssl(obj, openssl_path);
}
if (env.gnutls) {
char *gnutls_path = find_library_path("libgnutls.so");
printf("GnuTLS path: %s\n", gnutls_path);
attach_gnutls(obj, gnutls_path);
}
if (env.nss) {
char *nss_path = find_library_path("libnspr4.so");
printf("NSS path: %s\n", nss_path);
attach_nss(obj, nss_path);
attach_provider(obj, env.openssl, "OpenSSL", "libssl.so", attach_openssl);
attach_provider(obj, env.gnutls, "GnuTLS", "libgnutls.so", attach_gnutls);
attach_provider(obj, env.nss, "NSS", "libnspr4.so", attach_nss);
if (attached_link_count == 0) {
warn("no SSL provider probes attached\n");
err = -ENOENT;
goto cleanup;
}
err = 0;

pb = perf_buffer__new(bpf_map__fd(obj->maps.perf_SSL_events),
PERF_BUFFER_PAGES, handle_event, handle_lost_events,
Expand Down Expand Up @@ -450,6 +482,7 @@ int main(int argc, char **argv) {

cleanup:
perf_buffer__free(pb);
destroy_attached_links();
sslsniff_bpf__destroy(obj);
return err != 0;
}