使用strace抓取uds数据包

目前很多公司的服务架构都采用了mesh作为服务治理,那么就会引入一个问题,就需要所有的流量都多经历一跳,大部分为了优化性能都会采用UDS作为进程间通信,那么如何抓取uds数据包呢?tcpdump没办法使用,专业点就是使用 bcc ,但是依赖太重了,所以采用 strace 抓取系统调用, 进行实现抓包,就可以完美解决了!

uds 服务端代码

uds 是 unix domain socket,其实就是不实用tcp/ip进行传输,降低一些性能开销,是比较广泛的进程间通信的解决方案!

  1. 代码
go 复制代码
package main

import (
	"flag"
	"fmt"
	"net"
	"net/http"
)

func main() {
	socket := flag.String("socket", "", "unix domain socket")
	flag.Parse()
	if socket == nil || *socket == "" {
		panic("not found socket")
	}
	fmt.Println("listen addr: ", *socket)
	listen, err := net.Listen("unix", *socket)
	if err != nil {
		panic(err)
	}
	if err := http.Serve(listen, http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
		if _, err := writer.Write([]byte(`hello world`)); err != nil {
			panic(err)
		}
	})); err != nil {
		panic(err)
	}
}
  1. 启动服务
shell 复制代码
go run example/uds/server.go -socket /tmp/tmp.9dd2RLqRnc/uds.socket

curl 测试请求

  • --unix-socket <socket> 用于指定socket地址
  • http:/xx.xx.xxx/api/v1 格式 <scheme>:/<host><path> , 例如这里 scheme是http, host是xx.xx.xx , path是/api/v1
shell 复制代码
~ curl --unix-socket /tmp/tmp.9dd2RLqRnc/uds.socket -X GET http:/xx.xx.xxx/api/v1 -v
Note: Unnecessary use of -X or --request, GET is already inferred.
*   Trying /tmp/tmp.9dd2RLqRnc/uds.socket...
* Connected to xx.xx.xxx (/tmp/tmp.9dd2RLqRnc/uds.socket) port 80 (#0)
> GET /api/v1 HTTP/1.1
> Host: xx.xx.xxx
> User-Agent: curl/7.64.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Thu, 18 Apr 2024 05:28:58 GMT
< Content-Length: 11
< Content-Type: text/plain; charset=utf-8
<
* Connection #0 to host xx.xx.xxx left intact
hello world

使用 strace 抓包

  1. 抓取系统调用 sudo strace -p 3604685 -tt -f -y -s 65535
  • -f 参数主要是适用于多线程/多进程
  • -tt 会附加时间
  • -s <size> 是打印字符串的最大长度,默认很短貌似16个?,这里我设置的是64k
  • -p <pid> 标识抓取的进程ID
  • -y 表示打印出相关文件的路径,对于 open, connect 等函数,它将显示打开或连接的文件或 socket 的路径
  • -o <output> 输出到文件
  • -x: print non-ascii strings in hex
  • -xx:print all strings in hex,比较适用于抓取二进制数据包,后续做处理!
shell 复制代码
strace: Process 3604685 attached with 6 threads
[pid 3608084] 14:09:45.676154 epoll_pwait(5<anon_inode:[eventpoll]>,  <unfinished ...>
[pid 3604689] 14:09:45.676221 futex(0xc000080148, FUTEX_WAIT_PRIVATE, 0, NULL <unfinished ...>
[pid 3604687] 14:09:45.676266 futex(0xc000050548, FUTEX_WAIT_PRIVATE, 0, NULL <unfinished ...>
[pid 3604686] 14:09:45.676286 restart_syscall(<... resuming interrupted futex ...> <unfinished ...>
[pid 3604685] 14:09:45.676320 futex(0x85c368, FUTEX_WAIT_PRIVATE, 0, NULL <unfinished ...>
[pid 3604688] 14:09:45.676340 futex(0xc000050948, FUTEX_WAIT_PRIVATE, 0, NULL
 <unfinished ...>
[pid 3608084] 14:09:47.595066 <... epoll_pwait resumed> [{EPOLLIN, {u32=1924628344, u64=139897599393656}}], 128, -1, NULL, 0) = 1
[pid 3608084] 14:09:47.595186 futex(0x85c720, FUTEX_WAKE_PRIVATE, 1) = 1
[pid 3604686] 14:09:47.595242 <... restart_syscall resumed> ) = 0
[pid 3608084] 14:09:47.595255 accept4(3<socket:[1187928144]>,  <unfinished ...>
[pid 3604686] 14:09:47.595330 epoll_pwait(5<anon_inode:[eventpoll]>,  <unfinished ...>
[pid 3608084] 14:09:47.595371 <... accept4 resumed> {sa_family=AF_UNIX}, [112->2], SOCK_CLOEXEC|SOCK_NONBLOCK) = 4<socket:[1188331398]>
[pid 3604686] 14:09:47.595399 <... epoll_pwait resumed> [], 128, 0, NULL, 0) = 0
[pid 3608084] 14:09:47.595462 epoll_ctl(5<anon_inode:[eventpoll]>, EPOLL_CTL_ADD, 4<socket:[1188331398]>, {EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLET, {u32=1924628104, u64=139897599393416}} <unfinished ...>
[pid 3604686] 14:09:47.595597 nanosleep({tv_sec=0, tv_nsec=20000},  <unfinished ...>
[pid 3608084] 14:09:47.595621 <... epoll_ctl resumed> ) = 0
[pid 3608084] 14:09:47.595655 getsockname(4<socket:[1188331398]>, {sa_family=AF_UNIX, sun_path="/tmp/tmp.9dd2RLqRnc/uds.socket"}, [112->33]) = 0
[pid 3604686] 14:09:47.595700 <... nanosleep resumed> NULL) = 0
[pid 3604686] 14:09:47.595721 nanosleep({tv_sec=0, tv_nsec=20000},  <unfinished ...>
[pid 3608084] 14:09:47.595773 futex(0x85c368, FUTEX_WAKE_PRIVATE, 1) = 1
[pid 3604685] 14:09:47.595863 <... futex resumed> ) = 0
[pid 3608084] 14:09:47.595876 accept4(3<socket:[1187928144]>,  <unfinished ...>
[pid 3604686] 14:09:47.595901 <... nanosleep resumed> NULL) = 0
[pid 3604685] 14:09:47.595914 epoll_pwait(5<anon_inode:[eventpoll]>,  <unfinished ...>
[pid 3608084] 14:09:47.595939 <... accept4 resumed> 0xc000104b28, [112], SOCK_CLOEXEC|SOCK_NONBLOCK) = -1 EAGAIN (Resource temporarily unavailable)
[pid 3604686] 14:09:47.596022 nanosleep({tv_sec=0, tv_nsec=20000},  <unfinished ...>
[pid 3604685] 14:09:47.596041 <... epoll_pwait resumed> [{EPOLLIN|EPOLLOUT, {u32=1924628104, u64=139897599393416}}], 128, 0, NULL, 0) = 1
[pid 3604685] 14:09:47.596071 epoll_pwait(5<anon_inode:[eventpoll]>,  <unfinished ...>
[pid 3608084] 14:09:47.596109 read(4<socket:[1188331398]>,  <unfinished ...>
[pid 3604686] 14:09:47.596135 <... nanosleep resumed> NULL) = 0
[pid 3608084] 14:09:47.596155 <... read resumed> "GET /api/v1 HTTP/1.1\r\nHost: xx.xx.xxx\r\nUser-Agent: curl/7.64.0\r\nAccept: */*\r\n\r\n", 4096) = 79
[pid 3604686] 14:09:47.596179 nanosleep({tv_sec=0, tv_nsec=20000},  <unfinished ...>
[pid 3608084] 14:09:47.596258 futex(0xc000080148, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
[pid 3604686] 14:09:47.596284 <... nanosleep resumed> NULL) = 0
[pid 3608084] 14:09:47.596297 <... futex resumed> ) = 1
[pid 3604689] 14:09:47.596309 <... futex resumed> ) = 0
[pid 3604686] 14:09:47.596320 nanosleep({tv_sec=0, tv_nsec=20000},  <unfinished ...>
[pid 3608084] 14:09:47.596406 write(4<socket:[1188331398]>, "HTTP/1.1 200 OK\r\nDate: Thu, 18 Apr 2024 06:09:47 GMT\r\nContent-Length: 11\r\nContent-Type: text/plain; charset=utf-8\r\n\r\nhello world", 128 <unfinished ...>
[pid 3604689] 14:09:47.596487 nanosleep({tv_sec=0, tv_nsec=3000},  <unfinished ...>
[pid 3608084] 14:09:47.596567 <... write resumed> ) = 128
[pid 3604686] 14:09:47.596582 <... nanosleep resumed> NULL) = 0
[pid 3604685] 14:09:47.596600 <... epoll_pwait resumed> [{EPOLLOUT, {u32=1924628104, u64=139897599393416}}], 128, -1, NULL, 0) = 1
[pid 3608084] 14:09:47.596620 read(4<socket:[1188331398]>,  <unfinished ...>
[pid 3604689] 14:09:47.596634 <... nanosleep resumed> NULL) = 0
[pid 3604686] 14:09:47.596647 nanosleep({tv_sec=0, tv_nsec=20000},  <unfinished ...>
[pid 3608084] 14:09:47.596659 <... read resumed> 0xc0001b0000, 4096) = -1 EAGAIN (Resource temporarily unavailable)
[pid 3604689] 14:09:47.596678 epoll_pwait(5<anon_inode:[eventpoll]>,  <unfinished ...>
[pid 3608084] 14:09:47.596740 futex(0xc000080548, FUTEX_WAIT_PRIVATE, 0, NULL <unfinished ...>
[pid 3604689] 14:09:47.596774 <... epoll_pwait resumed> [{EPOLLIN|EPOLLOUT|EPOLLHUP|EPOLLRDHUP, {u32=1924628104, u64=139897599393416}}], 128, -1, NULL, 0) = 1
[pid 3604686] 14:09:47.596807 <... nanosleep resumed> NULL) = 0
[pid 3604685] 14:09:47.596816 epoll_pwait(5<anon_inode:[eventpoll]>,  <unfinished ...>
[pid 3604689] 14:09:47.596907 read(4<socket:[1188331398]>,  <unfinished ...>
[pid 3604686] 14:09:47.596975 nanosleep({tv_sec=0, tv_nsec=20000},  <unfinished ...>
[pid 3604689] 14:09:47.597008 <... read resumed> "", 4096) = 0
[pid 3604685] 14:09:47.597022 <... epoll_pwait resumed> [], 128, 0, NULL, 0) = 0
[pid 3604689] 14:09:47.597063 epoll_ctl(5<anon_inode:[eventpoll]>, EPOLL_CTL_DEL, 4<socket:[1188331398]>, 0xc00010595c <unfinished ...>
[pid 3604686] 14:09:47.597126 <... nanosleep resumed> NULL) = 0
[pid 3604689] 14:09:47.597152 <... epoll_ctl resumed> ) = 0
[pid 3604686] 14:09:47.597171 nanosleep({tv_sec=0, tv_nsec=20000},  <unfinished ...>
[pid 3604689] 14:09:47.597190 close(4<socket:[1188331398]> <unfinished ...>
[pid 3604685] 14:09:47.597211 epoll_pwait(5<anon_inode:[eventpoll]>,  <unfinished ...>
[pid 3604689] 14:09:47.597245 <... close resumed> ) = 0
[pid 3604689] 14:09:47.597284 futex(0xc000080148, FUTEX_WAIT_PRIVATE, 0, NULL <unfinished ...>
[pid 3604686] 14:09:47.597309 <... nanosleep resumed> NULL) = 0
[pid 3604686] 14:09:47.597335 futex(0x85c720, FUTEX_WAIT_PRIVATE, 0, {tv_sec=60, tv_nsec=0}

这里我们就可以看到请求/响应的内容了!

  1. 内容太杂了,我们可能仅关注我们需要关注的系统调用,比如 readwriteclose 等系统调用,可以通过参数

-e read,write,close:这个选项告诉 strace 只跟踪指定的系统调用。在这个例子中,只有 readwriteclose 这三个系统调用被跟踪。

我们使用 sudo strace -p 3604685 -tt -f -y -s 65535 -e read,write,close 再抓取一次包呢?

shell 复制代码
strace: Process 3604685 attached with 6 threads
[pid 3604685] 14:16:15.263803 read(4<socket:[1188372268]>, "GET /api/v1 HTTP/1.1\r\nHost: xx.xx.xxx\r\nUser-Agent: curl/7.64.0\r\nAccept: */*\r\n\r\n", 4096) = 79
[pid 3604685] 14:16:15.264236 write(4<socket:[1188372268]>, "HTTP/1.1 200 OK\r\nDate: Thu, 18 Apr 2024 06:16:15 GMT\r\nContent-Length: 11\r\nContent-Type: text/plain; charset=utf-8\r\n\r\nhello world", 128) = 128
[pid 3608084] 14:16:15.264523 read(4<socket:[1188372268]>, "", 4096) = 0
[pid 3608084] 14:16:15.264702 close(4<socket:[1188372268]>) = 0

这么就清晰许多了!解释一下含义

shell 复制代码
# 内容
read(4<socket:[1188372268]>, "GET /api/v1 HTTP/1.1\r\nHost: xx.xx.xxx\r\nUser-Agent: curl/7.64.0\r\nAccept: */*\r\n\r\n", 4096) = 79

# read 系统调用,头文件 #include <unistd.h>
# extern ssize_t read (int __fd, void *__buf, size_t __nbytes) __wur;

# 4<socket:[1188372268]> 表示 fd=4
# "GET..." 表示__buf内容
# 4096 表示 __nbytes
# 79 表示读取了多少字节

备注

对于 hex 的解析,这里有个简单例子,大家可以看一下!逻辑很简单!

go 复制代码
package main

import (
	"encoding/hex"
	"fmt"
	"regexp"
	"strings"
)

var input = `[pid 3604687] 13:44:54.848366 write(4<\x73\x6f\x63\x6b\x65\x74\x3a\x5b\x31\x31\x38\x38\x30\x38\x37\x30\x33\x38\x5d>, 4<\x6f\x6f\x63\x6b\x65\x74\x3a\x5b\x31\x31\x38\x38\x30\x38\x37\x30\x33\x38\x5d>, "\x48\x54\x54\x50\x2f\x31\x2e\x31\x20\x32\x30\x30\x20\x4f\x4b\x0d\x0a\x44\x61\x74\x65\x3a\x20\x54\x68\x75\x2c\x20\x31\x38\x20\x41\x70\x72\x20\x32\x30\x32\x34\x20\x30\x35\x3a\x34\x34\x3a\x35\x34\x20\x47\x4d\x54\x0d\x0a\x43\x6f\x6e\x74\x65\x6e\x74\x2d\x4c\x65\x6e\x67\x74\x68\x3a\x20\x31\x31\x0d\x0a\x43\x6f\x6e\x74\x65\x6e\x74\x2d\x54\x79\x70\x65\x3a\x20\x74\x65\x78\x74\x2f\x70\x6c\x61\x69\x6e\x3b\x20\x63\x68\x61\x72\x73\x65\x74\x3d\x75\x74\x66\x2d\x38\x0d\x0a\x0d\x0a\x68\x65\x6c\x6c\x6f\x20\x77\x6f\x72\x6c\x64", 128 <unfinished ...>`

func hexToString(input string) (string, error) {
	input = strings.ReplaceAll(input, "\\x", "")
	decodeString, err := hex.DecodeString(input)
	if err != nil {
		return "", err
	}
	return string(decodeString), nil
}

func handleSubMatch(input string, subs [][]string) string {
	for _, sub := range subs {
		if len(sub) != 2 {
			continue
		}
		originStr := sub[1]
		if toStr, err := hexToString(originStr); err == nil {
			input = strings.ReplaceAll(input, originStr, toStr)
		}
	}
	return input
}

func main() {
	match1 := regexp.MustCompile(`"([\\x0-9a-f]+)"`)
	match2 := regexp.MustCompile(`\d<([\\x0-9a-f]+)>`)
	input = handleSubMatch(input, match1.FindAllStringSubmatch(input, -1))
	input = handleSubMatch(input, match2.FindAllStringSubmatch(input, -1))
	fmt.Println(input)
}
相关推荐
鸭梨山大。3 分钟前
ubuntu安全配置基线
linux·安全·ubuntu
带电的小王32 分钟前
Android Studio:Linux环境下安装与配置
android·linux·android studio
WeeJot嵌入式38 分钟前
【Linux】进程间通信IPC
linux·运维·算法
啊晚1 小时前
ASP.NET Core - 依赖注入(四)
后端·asp.net
云端 架构师1 小时前
PL/SQL语言的文件操作
开发语言·后端·golang
小蒜学长1 小时前
疾病防控综合系统设计与实现(代码+数据库+LW)
前端·数据库·vue.js·spring boot·后端·oracle
MoFe12 小时前
【.net core】【sqlsugar】时间查询示例
linux·前端·.netcore
camellias_2 小时前
Springboot(五十八)SpringBoot3使用Redisson实现接口的限流功能
java·spring boot·后端
C++小厨神3 小时前
Java语言的软件工程
开发语言·后端·golang
代码讲故事3 小时前
Linux安装docker,安装配置xrdp远程桌面
linux·docker·远程连接·远程桌面·rdp·图形化·xrdp