# WireOwl
一款受 Wireshark 启发、完全使用 Python 从头编写的网络数据包分析器。只需将其指向一个十六进制转储(hex dump),它就会沿着协议栈逐层解析字节 —— Ethernet 帧、接着是 IPv4 头、然后是 TCP 段、最后是 HTTP 负载 —— 将每一层渲染为可读的字段,绘制出会话流图,并将整个分析结果导出为 PDF。
每个协议解码器都是纯手工编写的:**没有使用 scapy,也没有使用 dpkt**。该项目的初衷是为了阅读 RFC 并手动切片字节,因此解析器完全基于原始偏移量和位掩码进行工作。
## 快速开始
你需要安装带有 tkinter 的 **Python 3.11+**。除此之外无需其他依赖 —— 两个运行时依赖会自动安装。
```
git clone https://github.com/Tinshea/WireOwl.git
cd WireOwl
```
**Linux / macOS**
```
chmod +x run.sh
./run.sh
```
**Windows (PowerShell)**
```
.\run.ps1
```
或者,如果你有 `make`:
```
make setup && make run
```
以上三种方式都会创建一个 `.venv`,安装相关依赖并启动应用程序。按下 **Start**,从 `sample/` 中挑选任意文件,你就可以开始使用了。
## 界面展示
加载捕获文件后,每一帧都会被总结为一行:源地址和目标地址、端口、TCP 标志、序列号和确认号以及窗口大小。属于同一会话的帧会共享同一种颜色,HTTP 帧会以洋红色突出显示,点击其中一帧即可在下方的面板中查看其解码细节 —— 如下图所示的一个 SYN 的 TCP 层。
左侧的五个图标用于切换详情面板显示的层级:原始帧、Ethernet、IPv4、TCP 或重组后的 HTTP 负载。
### 过滤
输入一个表达式,帧列表就会自动筛选出匹配的帧。当表达式能够被成功解析时,输入框会变为**青色**;若解析失败则变为**红色**,这样拼写错误就会一目了然。过滤机制的实现方式是:将解析后的帧加载到内存中的 SQLite schema 里,并将表达式作为 `WHERE` 子句进行执行 —— 如下图所示,从一个 2.3 MB 的捕获文件中提取出了 477 个 TCP 帧。
| 字段 | 匹配目标 | 示例 |
|---|---|---|
| `protocol` | IP 协议**编号**(见下表) | `protocol == 6` |
| `ip.src` / `ip.addr` | IPv4 源 / 目标地址 | `ip.src == 192.168.1.102` |
| `tcp.src` / `tcp.addr` | TCP 源 / 目标端口 | `tcp.addr == 80` |
| `eth.src` / `eth.addr` | MAC 源 / 目标地址 | `eth.src == f0189859ae32` |
| `http.version` | HTTP 版本字符串 | `http.version == 1.1` |
支持的运算符为 `==` 和 `!=`,并可以通过 `and` / `&&` / `or` / `||` 进行组合:
```
protocol == 6 and ip.src == 192.168.31.69
ip.src == 192.168.1.102 or ip.addr == 192.168.1.117
```
关于该语法有两点需要注意:
- `protocol` 存储的是数字形式的 IANA 值,而不是名称 —— 应使用 `protocol == 6`,而不是 `protocol == TCP`。后者的语法本身没有问题,但不会匹配到任何结果。
- **复合表达式必须限制在同一字段族(family)内。** 每个字段族都存在于独立的 SQLite 表中(`protocol` 和 `ip.*` 同属一个表,此外还有 `tcp.*`、`eth.*`、`http.version`),生成的查询会针对由*第一个*条件所指定的表进行检索。`protocol == 6 and ip.src == …` 是有效的;而 `protocol == 6 and tcp.addr == 80` 则会被拒绝。
### 流图
流图将主机布局为列,将帧布局为行,将每一次交互绘制为带有端口注释的箭头 —— 让你能够一目了然地看清会话的全貌。
### PDF 报告
只需点击一下,即可将一份完整的报告写入 `PDF/` 目录:包含带链接的目录、流图,随后是每一帧的独立详情页,其中展示了所有解码后的字段。
## 工作原理
`traitement()` 负责读取转储文件,将偏移量为 `0000` 的行视为一个新帧的起始,并剔除偏移量列和 ASCII 侧边栏,最终为每一帧留下一个十六进制字符串。这些字符串随后会流经四个解析器,每个解析器会消耗(解析)它所能识别的头部,并将剩余部分传递给下一个解析器:
```
hex text ──▶ trame.tramecalcul MAC addresses, EtherType
──▶ paquetip.paquetipv4 IPv4 header: TTL, flags, protocol, addresses
──▶ tcp.tcpaquet ports, sequence numbers, flags, window
──▶ httpmodule.req_or_rep request/response, method, status, headers
```
每个阶段都会将其人类可读的输出追加到 `Trames/TrameN.txt` 中,并填充以帧编号为键的模块级字典(如 `Source_Address`、`Port_Source`、`flagdic` 等)。GUI、过滤引擎和 PDF 导出器读取的都是这些字典中的数据 —— 解析过程只需进行一次,之后的所有操作都只是纯粹的数据查询。
### 支持的协议
| 层级 | 解码内容 |
|---|---|
| **Ethernet** | 源和目标 MAC 地址,EtherType |
| **IPv4** | 头长度、TOS、总长度、标识符、DF/MF 标志、分片偏移、TTL、协议、头部校验和、地址 |
| **TCP** | 端口、序列号和确认号、数据偏移、全部六个标志位、窗口大小、校验和、紧急指针、选项 |
| **HTTP** | 请求与响应、方法、版本、状态码、头部 |
IP 协议号通过 `dico_protocol` 进行解析:`1` 为 ICMP,`2` 为 IGMP,`6` 为 TCP,`8` 为 EGP,`9` 为 IGP,`17` 为 UDP,`36` 为 XTP,`46` 为 RSVP。
**目前仅解析 IPv4。** 携带其他协议(如 IPv6、ARP)的帧会列出其类型,并被标记为 `n'est pas pris en charge par le programme`,而不会被解码。同样地,只有 TCP 会进行传输层解码;UDP 帧会被计数并支持过滤,但不会被详细拆解。
## 示例捕获文件
`sample/` 目录同时也是测试套件 —— 其中有几个文件是专门为了测试错误处理路径而存在的。
| 文件 | 内容 |
|---|---|
| `Trace.txt`, `TCP_1..3.txt` | 干净的 TCP 握手 —— 最佳入门起点 |
| `HTTP_2.txt`, `HTTP_TCP.txt` | 基于 TCP 的 HTTP 请求/响应 |
| `ARP_UDP_ICMP_IGMP.txt` | 63 个混合帧;适合用于测试过滤器 |
| `test-3f.txt` | 大小为 2.3 MB,包含约 500 个帧 —— 用于压力测试 |
| `HTTP_1.txt` | 单个 IPv6 帧,即触发不支持协议路径的场景 |
| `Corrupted.txt`, `Empty.txt`, `Text.txt` | 格式错误的输入;程序应发出警告,而不是崩溃 |
## 项目结构
```
WireOwl/
├── run.sh / run.ps1 # set up the venv and launch
├── Makefile # setup / run / clean
├── core/
│ ├── main.py # entry point
│ ├── source.py # tkinter GUI, dump parsing, all screens
│ ├── trame.py # Ethernet layer
│ ├── paquetip.py # IPv4 layer
│ ├── tcp.py # TCP layer
│ ├── httpmodule.py # HTTP layer
│ ├── filtrage.py # SQLite-backed filter engine
│ └── pdf.py # report + flow graph export
├── affichage/ # icons, backgrounds, logo
├── sample/ # hex dump captures (see above)
├── Tools/requirements.txt
├── Trames/ # per-frame decode output (generated)
└── PDF/ # generated reports
```
`main.py` 必须在仓库的根目录作为工作目录时才能运行:`source.py` 加载图片时使用的是类似 `./affichage/bg.png` 的相对路径。启动脚本已经为你处理好了这些设置。
## 注意事项
- **`tkmacosx` 是可选的。** 它的存在仅仅是为了在 macOS 上提供一个支持自定义 `bg`(背景色)的按钮控件;在所有其他平台上,`source.py` 都会回退使用 `tkinter.Button`,因此该库仅在 macOS 上才会被安装。
- **在许多 Linux 发行版上,需要单独安装 tkinter** —— 例如在 Debian/Ubuntu 上执行 `sudo apt install python3-tk`。`run.sh` 会自动检查并给予提示。
- **每次加载时都会清空 `Trames/` 目录。** 该目录用于保存逐帧解码的转储数据和过滤器数据库,每次打开捕获文件时,这些内容都会从头重新构建。
- UI 界面和解码输出均使用**法语**,这与该项目最初的课程作业要求保持一致。
## 致谢
- **Malek Bouzarkouna** — [@Tinshea](https://github.com/Tinshea)
- **Sevag Baboyan** — [@SesevagB](https://github.com/SesevagB)
Sorbonne Université — 网络课程。
未指定许可证。