log_pcap() 拼包函数
📦 r0capture 的精华之一:把 SSL hook 出来的明文,手工拼成一个合法的 pcap 数据包。
📎 源码在线查看:
r0capture.py#L147-L255(log_pcap)
为什么要手工拼
r0capture 抓的是 SSL hook 出来的已解密明文,不是网卡上的原始加密包。明文不在任何真实网络数据包里,所以必须手工把明文 data 包装成「假的 IPv4+TCP 包」,再按 pcap 文件格式逐字节写进去。
pcap 文件结构
整个文件 = 1个全局头(24字节, 在 ssl_log 里写) + N条记录
每条记录 = 记录头(16字节) + 一个完整的 IPv4 包
IPv4 包 = IPv4头(20字节) + TCP头(20字节) + 载荷(明文 data)
每个包总长 = 16 + 20 + 20 + len(data) = 40 + len(data) ← 40 = IP头+TCP头函数签名
python
def log_pcap(pcap_file, ssl_session_id, function, src_addr, src_port,
dst_addr, dst_port, data):seq/ack 模拟
真实 TCP 用序列号保证顺序。这里造的是假包,得自己编一套自洽的 seq/ack:
python
if ssl_session_id not in ssl_sessions:
ssl_sessions[ssl_session_id] = (random.randint(0, 0xFFFFFFFF),
random.randint(0, 0xFFFFFFFF))
client_sent, server_sent = ssl_sessions[ssl_session_id]
if function == "SSL_read":
seq, ack = (server_sent, client_sent) # 服务端发
else:
seq, ack = (client_sent, server_sent) # 客户端发- 首次见该会话:随机生成两个起始 seq(client_sent / server_sent)
SSL_write(客户端发):seq 用客户端已发量,ack 用服务端已发量SSL_read(服务端发):seq/ack 对调- 写完一个包后,对应方向的「已发字节数」累加
len(data),下次同会话的包 seq 接着往后排
声明式写头
用「元组列表 + 循环」把协议字段按顺序列成表,比逐行写清晰:
python
for writes in (
# --- pcap 记录头 16字节, 本机字节序 ---
("=I", int(t)), # Timestamp seconds
("=I", int((t * 1000000) % 1000000)), # Timestamp microseconds
("=I", 40 + len(data)), # Number of octets saved
("=i", 40 + len(data)), # Actual length
# --- IPv4 头 20字节, 大端序 ---
(">B", 0x45), # Version+IHL: 0100 0101
(">B", 0), # ToS/DSCP
(">H", 40 + len(data)), # Total Length
(">H", 0), # Identification
(">H", 0x4000), # Flags=DF + Fragment Offset
(">B", 0xFF), # TTL=255
(">B", 6), # Protocol=TCP
(">H", 0), # Header Checksum=0
(">I", src_addr), # Source Address
(">I", dst_addr), # Destination Address
# --- TCP 头 20字节, 大端序 ---
(">H", src_port), # Source Port
(">H", dst_port), # Destination Port
(">I", seq), # Sequence Number
(">I", ack), # Acknowledgment Number
(">H", 0x5018), # Data Offset(5)+Flags(ACK+PSH)
(">H", 0xFFFF), # Window Size
(">H", 0), # Checksum=0
(">H", 0)): # Urgent Pointer
pcap_file.write(struct.pack(writes[0], writes[1]))
pcap_file.write(data) # 最后写载荷明文字节序
| 区域 | 字节序 | struct 首字符 | 原因 |
|---|---|---|---|
| pcap 记录头 | 本机序(小端) | = | 全局头 magic 声明了 0xa1b2c3d4(本机序) |
| IPv4 头 | 大端(网络序) | > | 网络协议规定 |
| TCP 头 | 大端(网络序) | > | 网络协议规定 |
关键字段含义
IPv4
0x45= Version(4) + IHL(5,即 5×4=20 字节头)0x4000= DF(Don't Fragment)位置 16= Protocol = TCP(1=ICMP, 17=UDP)- Checksum 填 0:Wireshark 不校验造的包也能解析
TCP
0x5018:高 4 位5= Data Offset(5×4=20 字节头),低 12 位0x018= ACK+PSH 标志0xFFFFWindow:填满避免 Wireshark 报窗口警告