ashishsaud2025/pcap-insight
GitHub: ashishsaud2025/pcap-insight
一个基于Python的命令行PCAP分析工具,用于将抓包文件转换为可读的安全与流量统计报告。
Stars: 0 | Forks: 0
# PCAP ANALYZER CLI
一个命令行 PCAP 分析器,可将 `.pcap` / `.pcapng` 捕获文件转换为
可读的安全与流量报告:
- **摘要**:数据包数量、捕获时长、总字节数、平均数据包大小
- **协议分布**:TCP/UDP/ICMP/ARP/DNS/TLS/HTTP 的数量和百分比
- **活跃主机(Top talkers)**:按数据包数量*和*字节量统计的前 10 个源到目标的 IP 对
- **高频端口(Top ports)**:最常用的目标端口及尽力而为的服务名称匹配
- **ASN/组织信息补充**:活跃主机行会标注其所属的组织/ASN(通过 MaxMind 免费的 GeoLite2-ASN 数据库进行离线查询;使用 `--no-enrich` 可禁用)
- **可疑模式**(仅作为标记,不进行拦截):
- 通过 HTTP 传输的明文凭据
- 大量未响应的 SYN 突发(可能是 SYN 扫描)
- 高熵/超长的 DNS 子域名(可能是隧道传输或 C2 信标)
- 在 ARP 回复中,同一个 IP 被多个 MAC 地址声明(可能是 ARP 欺骗)
- **`--export json`**:生成机器可读的输出,便于通过管道传递给其他工具
- **`--filter`**:BPF 风格的过滤器,用于将分析范围缩小到特定流量
它使用 [Scapy](https://scapy.net/) 来解析捕获文件,
使用 [Rich](https://github.com/Textualize/rich) 来渲染表格,并使用
[geoip2](https://geoip2.readthedocs.io/) 结合 MaxMind 免费的 GeoLite2-ASN
数据库进行可选的离线组织/ASN 归属查询。其余功能均由
标准库 Python(argparse、`socket.getservbyport`、`ipaddress` 等)实现。
## 安装说明
要求 **Python 3.10+**。
### 1. 克隆仓库
```
git clone https://github.com/ashishsaud2025/pcap-insight.git
cd pcap-insight
```
### 2. 创建并激活虚拟环境
虚拟环境可以将此项目的依赖项与您的系统
Python 和其他项目隔离开来。
**Windows (PowerShell):**
```
python -m venv venv
.\venv\Scripts\Activate.ps1
```
**Windows (cmd.exe):**
```
python -m venv venv
venv\Scripts\activate.bat
```
**macOS / Linux:**
```
python3 -m venv venv
source venv/bin/activate
```
激活后,您的命令行提示符将显示 `(venv)` 前缀。下面每一次 `pip install`
以及 `pcap-insight` 的运行都将在这个隔离的环境中进行。
### 3. 安装依赖项
您有两个等效的选择:
**选项 A:通过 `requirements.txt` 安装:**
```
pip install -r requirements.txt
```
**选项 B:直接安装该包(推荐):**
```
python -m pip install -e .
```
此操作将安装 `pcap-insight` 命令行入口,以及 `pyproject.toml` 中声明的 `scapy`、`rich`
和 `geoip2`。`-e`(可编辑)标志意味着
任何本地代码的更改都会立即生效,无需重新安装。
用于开发(为测试套件添加 `pytest`):
```
python -m pip install -e ".[dev]"
```
### 4. 验证安装
```
pcap-insight --version
pcap-insight --demo
```
如果这两个命令都能正常运行,说明您已正确设置。只要*激活了此虚拟环境*,`pcap-insight` 现在就可以
在任意目录下使用——每次打开新的终端会话时,请使用
上面的 `Activate.ps1` / `activate.bat` / `source ... activate` 命令重新激活它。
### 可选:组织/ASN 信息补充 (GeoLite2-ASN)
活跃主机表将为每个 IP 标注所属的组织/ASN。
该查询是**完全离线**的,并使用 MaxMind 免费的 **GeoLite2-ASN**
数据库,即在分析时不需要 API 密钥和网络访问。
要为公网 IP 启用真实的组织名称(而不是显示 `Unknown`):
1. 在 注册并下载
**GeoLite2-ASN** 数据库(`GeoLite2-ASN.mmdb`)。
2. 将其放置在以下任一位置(按顺序检查):
- `/GeoLite2-ASN.mmdb`
- `~/.pcap-insight/GeoLite2-ASN.mmdb`
- `~/.local/share/GeoLite2-ASN.mmdb`
- 或者将 `PCAP_INSIGHT_ASN_DB` 环境变量设置为其路径
私有/保留地址(RFC1918、loopback、link-local、multicast 等)会被
标记为 `Private`,并且永远不会去查询数据库。缺失或无法读取的
数据库,或未知的 IP,将被标记为 `Unknown`。结果将按 IP 缓存,有效
期为进程的生命周期,因此活跃主机信息补充只会为每个唯一的公网
IP 执行一次数据库读取。使用 `pcap-insight capture.pcap --no-enrich` 可以完全
跳过查询(适用于完全离线的环境或不需要此功能的脚本)。
程序显式处理了一种边缘情况:IPv4 映射的 IPv6 地址
(`::ffff:a.b.c.d`)在进行私有地址检查之前会被解码回其内嵌的 IPv4 地址,因为即使内嵌的
地址是公网地址,Python 也会将映射形式标记为 `reserved=True`。如果不进行解码,`::ffff:8.8.8.8` 将被
(错误地)视为私有地址,从而永远不会去查询数据库。6to4(`2002::/16`)
和 Teredo(`2001::/32`)范围会被标记为 `Private`,这是根据 Python 的标准库
分类,尽管它们可以内嵌公网 IPv4 地址——这是一个被默认接受的
标准库特性(参见 `pcap_insight/enrichment.py` 中的 `is_private()`)。
如果启用了信息补充但找不到或无法读取数据库,CLI
会向 **stderr** 打印一条警告(这样 JSON/管道输出将保持整洁),解释
公网 IP 将显示为 `Unknown`,并附带指向此部分的提示。
`--no-enrich` 会同时禁用查询和该警告。
### Windows / Npcap 注意事项
只有在您想要*嗅探*实时流量时,Scapy 才需要 **Npcap** 驱动程序。
`pcap-insight` 仅*读取捕获文件*,因此不需要管理员权限或安装 Npcap。
## 用法
```
usage: pcap-insight [-h] [--demo] [--export {json}] [--filter FILTER]
[--no-enrich] [--version]
[capture]
Analyze a .pcap/.pcapng capture: summary stats, protocol breakdown, top
talkers/ports, and heuristic suspicious-pattern flags (credentials in plaintext
HTTP, SYN scans, DNS tunneling candidates, ARP spoofing candidates).
positional arguments:
capture path to a .pcap or .pcapng file (omit with --demo)
options:
-h, --help show this help message and exit
--demo write a synthetic demo capture to './demo.pcap' and analyze it
--export {json} emit a structured JSON document instead of tables
--filter FILTER BPF-style capture filter, e.g. 'tcp port 443' or 'host 10.0.0.5'
--no-enrich disable ASN/organization enrichment for top-talker tables
(default: on, using the GeoLite2-ASN database if present)
--version show program's version number and exit
```
### 快速演示(无需真实数据包)
`--demo` 标志会生成一个小巧、完全合成的捕获文件(不产生互联网
流量),它会演练每一个分析部分,包括所有四个可疑
模式,并对其进行分析:
```
pcap-insight --demo
```
### 分析您自己的捕获文件
```
# 完整报告
pcap-insight capture.pcap
# 缩小到一个 host
pcap-insight capture.pcap --filter 'host 10.0.0.5'
# 缩小到 HTTPS 并为 jq dump JSON
pcap-insight capture.pcap --filter 'tcp port 443' --export json | jq .summary
# 跳过 org/ASN 查询(离线环境、脚本、更快的运行)
pcap-insight capture.pcap --no-enrich
```
### 示例输出
`pcap-insight --demo` 会生成(检测到 Rich,因此如果使用非 Rich 终端,输出为纯文本;此处显示的列已截断):
```
Summary
Metric Value
Total packets 27
Capture duration 13.00 s
Total bytes 1.2 KB
Average packet size 45 B
First packet (UTC) 2023-11-14 22:13:20.000000 UTC
Last packet (UTC) 2023-11-14 22:13:33.000000 UTC
Protocol breakdown
Protocol Packets Percent
TCP 18 66.7%
ARP 3 11.1%
DNS 2 7.4%
TLS 2 7.4%
HTTP 1 3.7%
ICMP 1 3.7%
Top talkers by packet count
Source Source Org Destination Dest Org Packets
10.0.0.50 Private 10.0.0.1 Private 15
10.0.0.2 Private 93.184.216.34 Unknown 2
10.0.0.2 Private 8.8.8.8 Unknown 2
10.0.0.60 Private 10.0.0.61 Private 2
93.184.216.34 Unknown 10.0.0.2 Private 1
10.0.0.61 Private 10.0.0.60 Private 1
10.0.0.2 Private 10.0.0.99 Private 1
Top talkers by byte volume
Source Source Org Destination Dest Org Bytes
10.0.0.50 Private 10.0.0.1 Private 600 B
10.0.0.2 Private 93.184.216.34 Unknown 262 B
10.0.0.2 Private 8.8.8.8 Unknown 143 B
10.0.0.60 Private 10.0.0.61 Private 80 B
93.184.216.34 Unknown 10.0.0.2 Private 63 B
10.0.0.61 Private 10.0.0.60 Private 40 B
10.0.0.2 Private 10.0.0.99 Private 28 B
Top destination ports
Port Service Packets
80 http 3
53 domain 2
443 https 1
50001 unknown 1
1024 unknown 1
1025 unknown 1
1026 unknown 1
1027 unknown 1
1028 unknown 1
1029 unknown 1
Suspicious patterns (heuristics, not ground truth)
Type Severity Summary
plaintext-credentials high 1 plaintext credential exposure(s) in HTTP traffic
syn-scan medium 1 source(s) with many unanswered SYN packets (possible port scan)
dns-tunneling-candidate medium 1 DNS quer(ies) with high-entropy or long subdomains (threshold entropy>3.5 & len>=18)
arp-spoofing-candidate medium 1 IP(s) mapped to multiple MAC addresses in ARP replies
- [packet #1] 10.0.0.2 -> 93.184.216.34: HTTP Basic Authorization header (POST /login HTTP/1.1)
- 10.0.0.50: 15 SYN(s) sent, only 0 SYN-ACK(s) observed in return
- a1b2c3d4e5f6a7b8c9d0e1f2.example.com (subdomain 'a1b2c3d4e5f6a7b8c9d0e1f2', len=24, entropy=3.92, packet #3)
- 192.168.1.1 claimed by MACs: aa:bb:cc:dd:ee:01, aa:bb:cc:dd:ee:ff
```
### JSON 导出
```
pcap-insight capture.pcap --export json > report.json
```
```
{
"summary": {
"total_packets": 27,
"duration_seconds": 13.0,
"total_bytes": 1216,
"avg_packet_size": 45.04,
"start_time": 1700000000.0,
"end_time": 1700000013.0,
"start_time_utc": "2023-11-14 22:13:20.000000+00:00",
"end_time_utc": "2023-11-14 22:13:33.000000+00:00"
},
"protocols": [
{"protocol": "TCP", "count": 18, "percent": 66.67},
{"protocol": "ARP", "count": 3, "percent": 11.11},
{"protocol": "DNS", "count": 2, "percent": 7.41},
{"protocol": "TLS", "count": 2, "percent": 7.41},
{"protocol": "HTTP", "count": 1, "percent": 3.7},
{"protocol": "ICMP", "count": 1, "percent": 3.7}
],
"top_talkers_by_packets": [
{"src": "10.0.0.50", "dst": "10.0.0.1", "src_org": "Private", "dst_org": "Private", "packets": 15}
],
"top_talkers_by_bytes": [
{"src": "10.0.0.50", "dst": "10.0.0.1", "src_org": "Private", "dst_org": "Private", "bytes": 600}
],
"top_ports": [
{"port": 80, "count": 3, "service": "http"}
],
"suspicious_findings": [
{
"type": "plaintext-credentials",
"severity": "high",
"summary": "1 plaintext credential exposure(s) in HTTP traffic",
"details": ["[packet #1] 10.0.0.2 -> 93.184.216.34: HTTP Basic Authorization header (POST /login HTTP/1.1)"]
}
]
}
```
### BPF 过滤器子集
`--filter` 接受一个实用的 tcpdump 子集,应用于我们规范化后的
数据包记录,**无需 libpcap/Npcap**:
| 原语 | 示例 |
|---|---|
| 协议 | `tcp`, `udp`, `icmp`, `arp`, `ip` |
| 主机 | `host 10.0.0.5`, `src host 10.0.0.5`, `dst host 10.0.0.5` |
| 网络 | `net 192.168.0.0/16`, `src net 10.0.0.0/8`, `dst net 172.16.0.0/12` |
| 端口 | `port 443`, `src port 53`, `dst port 22` |
| 端口范围 | `portrange 8000-8080`, `dst portrange 1024-2048` |
| 组合符 | `and`, `or`, `not`, `(`, `)` |
示例:
```
pcap-insight capture.pcap --filter 'tcp port 443'
pcap-insight capture.pcap --filter 'udp port 53 and not src host 8.8.8.8'
pcap-insight capture.pcap --filter 'src net 10.0.0.0/8 and dst portrange 1-1024'
```
不支持(会显式报错):IPv6 地址、以太网原语
(`ether host`、`ether proto`、VLAN)以及完整的 libpcap 语法。请注意,
我们的协议标签是具有传输层感知能力的:`tcp` 也会匹配标记为 HTTP 或
TLS 的数据包,因为它们承载于 TCP 之中。
## 可疑模式启发式算法的工作原理(及其误报情况)
这四个检测器是经过精心设计的、简单且具有确定性的规则。它们
旨在作为**标记启发式规则,而非绝对事实**。每个检测器都
在 `tests/` 中的合成捕获文件上进行了单元测试。
### 1. 通过 HTTP 传输的明文凭据
**规则。** 对于每个其 payload 解析为 HTTP 请求的数据包
(`METHOD SP path SP HTTP/x.y`),如果满足以下条件则将其标记:
- headers 包含以 `Basic `(base64
凭据)开头的 `Authorization:` 请求头,或
- 请求体包含匹配 `pass|pwd|password|passwd|...=`
(不区分大小写)的表单字段。
**为何容易产生误报 / 假阳性。**
- `pass` 会匹配 body 字段名中的诸如 `passphrase`、`passcode`、`bypass`、`compass` 等子串。
- 检测器无法判断 `https://` 流量是否在范围内,页面是否
为蜜罐,或者字段是否经过了客户端加密。
- 仅检查*出站请求*;检测器不解析分块传输(chunked)
的 body、gzip 或 HTTP/2(它们不是明文 HTTP/1.1 请求行)。
- 捕获工具可能会记录属于产品演示或
故意公开表单的登录表单(例如测试环境中的 `username=admin&password=admin`)。
### 2. 未响应的 SYN 突发(可能的 SYN 扫描)
**规则。** 按源 IP 统计:
- 每个设置了 `SYN` 且清除了 `ACK` 的 TCP 数据包(这是握手*或*
探测的第一步),以及
- 同一 IP 接收到的 `SYN+ACK` 回复数量。
将符合以下情况的重点源 IP 标记:SYN 数量 ≥ 10 且其中收到回复的比例不足一半。
**误报 / 注意事项。**
- 对合法但不可达的连接进行的 TCP 重传将显示为
重复的未响应 SYN,并可能触发此规则。
- 激进的负载均衡器或健康检查器一次性发起大量连接
(例如攻击者*自己的*具有短暂 `connect` 超时的 NAT 出口),如果没有响应流,其表现将与之
完全相同。
- 非对称捕获(SPAN 仅在一侧进行)会看到 SYN 但看不到
回复,从而产生虚假的“扫描”。
- 阈值(10 个 SYN,回答率 <50%)是任意的;请通过
`pcap_insight/analyzers.py` 中的 `SYN_SCAN_MIN_SYNS` 常量进行调整。
### 3. DNS 隧道传输 / C2 信标
**规则。** 对于每个带有 qname 的 DNS 查询,去除可注册域名(使用
小型的内部公共后缀列表,见注意事项),并计算子域名的 **Shannon
熵**(以位为单位)。在以下任一情况发生时标记该查询:
- 子域名熵 > **3.5 位** *且* 子域名长度 ≥ 18 个字符,或
- 子域名长度超过 18 + 20 = 38 个字符(即使是低熵也很长,
例如重复的 `aaaa...`)。
反向 IP 字面值名称(`4.4.8.8.in-addr.arpa`,`abcd.ef01.example.com`
十六进制字面值风格)被排除在外,因为它们属于常规的基础设施记录。
受管的云区域也被归入“可注册”侧:对于 AWS、
CloudFront、Azure、Google APIs、Fastly 和 Microsoft 端点,提供商区域正上方的标签是由提供商控制的
(`codewhisperer.us-east-1.amazonaws.com`,`d123abc456.cloudfront.net`,
`mobile.events.data.microsoft.com`),因此它们被视为已注册的
主机名,而不是随机的子域名。受管区域*之上*的随机标签(例如 `..amazonaws.com`)仍会被标记。
对同一 qname 的重复查询只会报告**一次**。重试和缓存
未命中是正常现象,标记每一次重试会增加噪音。此发现会统计
唯一的 qname,而不是查询数据包。
**误报 / 注意事项。**
- 熵阈值是近似值。合法的看似随机的标签,例如
CDN 主机名、跟踪查询子域名、唯一的缓存清除标识符,
以及 `crypto`/`uuid` 风格的 API 主机名经常会超过 3.5 位。
- 我们的公共后缀列表**不是**完整的
[Public Suffix List](https://publicsuffix.org/)(该非常庞大且经常
变动),因此像 `foo.example.co.uk` 这样的域名可能会被拆分为
`co.uk` 的可注册域名,从而将 `foo.example` 留作“子域名”,如果那里有一个短小的随机标签,可能会产生误报。
- 受管区域列表是一个简短的白名单。不在列表中的区域(其他 CDN、
云提供商,或您使用的云托管 SaaS)对于其看似随机但合法的
主机名仍会产生误报。请针对您的环境扩展 `pcap_insight/analyzers.py` 中的
`_MANAGED_ZONES`。
- 长的 base64/十六进制数据块是典型的隧道/C2 特征,但相同的特征
也会出现在合法的基于 DNS 的 CDN 注册以及 DoH/DoT 时代的基础设施中。
- 此检测器针对单个查询触发;真正的隧道检测还会使用
基于域名的查询量、查询大小分布和查询时间规律,
而本工具**并未**实现这些功能。
### 4. ARP 回复:一个 IP 被多个 MAC 声明
**规则。** 收集在 **ARP 回复数据包**(`op=2`)中为每个发送方 IP 声明的发送方 MAC 地址集合。标记在捕获文件中被多个不同 MAC 声明的任何 IP。
**误报 / 注意事项。**
- **合法的多 MAC 场景是最常见的误报:**
- 具有多个别名/VLAN 的服务器网卡,从
不同的物理网卡宣告相同的 IP(端口聚合、主备模式的负载均衡器);
- 脚本化的故障转移,在过渡期间新旧 MAC 地址都会
作出响应;
- 捕获文件合并:来自不同网段的两个捕获文件
自然会有共享的 IP,但具有不同的 MAC。
- ARP 请求(`op=1`)被刻意忽略:对 `who-has X` 的请求*并不*
意味着发送方声明了 `X`。
- 检测器需要在*同一个*捕获文件中包含这两个回复;如果您的捕获窗口
恰好跨越了合法的故障转移,则两个 MAC 都会出现。
## 检测器阈值一览
| 检测器 | 主要阈值(在 `analyzers.py` 中的常量) |
|---|---|
| 明文凭据 | `Basic` 认证请求头或 body 正则表达式 `(pass|pwd|password|passwd|user_pass|client_secret)=` |
| SYN 扫描 | ≥ `SYN_SCAN_MIN_SYNS` = 10 个 SYN;回答率 < 50% |
| DNS 隧道 | 子域名熵 > `DNS_ENTROPY_THRESHOLD` = 3.5 且长度 ≥ 18;或长度 > `SUBNAME_PADDING` + 18 = 38 |
| ARP 欺骗 | 在 ARP 回复中,有 ≥ 2 个不同的发送方 MAC 声明了同一个发送方 IP |
这些数值只是起点,并非经过研究支持的调优结果。请在
`pcap_insight/analyzers.py` 中调整它们并重新运行测试套件。
## 开发指南
假设您已经创建并激活了虚拟环境(参见上方的
[安装说明](#installation))。
```
python -m pip install -e ".[dev]"
python -m pytest -v
```
该测试套件(78 个测试)涵盖:
- 对照参考公式和已知字符串的 Shannon 熵正确性
- 协议计数 / 百分比 / 排序顺序
- 活跃主机和高频端口聚合、服务名回退
- 每个可疑模式检测器:命中情况、正常情况和边界情况
- BPF 过滤引擎(解析、限定符、组合符、无效过滤器)
- 端到端的 CLI 表格、JSON 导出、JSON + 过滤器以及 `--demo`
- 信息补充行为(私有 IP 短路、缓存、失败处理)
- 合成的 `tests/` 固定捕获文件的解析(在会话
fixture 中由 Scapy 生成;无外部 pcap 文件)
### 项目结构
```
pcap-insight/
pcap_insight/
__init__.py # package metadata / version
parser.py # Scapy reading -> PacketRecord, plus the BPF-subset engine
analyzers.py # stats + four heuristic detectors (pure, testable)
cli.py # argparse, rich rendering, JSON export
enrichment.py # offline GeoLite2-ASN org lookup (cache + private detection)
testing.py # synthetic capture builder (used by tests and --demo)
tests/
conftest.py # session fixtures (creates the synthetic capture)
test_analyzers.py
test_filter.py
test_cli.py
test_enrichment.py
README.md
requirements.txt
pyproject.toml # pip-installable, console script: pcap-insight
```
## 环境依赖
- Python 3.10+
- `scapy` ≥ 2.5
- `rich` ≥ 13
- `geoip2` ≥ 4(用于可选的组织/ASN 信息补充)
- `pytest` ≥ 8(仅限开发使用)
有关锁定的最低版本,请参见 `requirements.txt`。
标签:IP 地址批量处理, PCAP分析, Python, Scapy, 无后门, 网络流量分析, 逆向工具, 防御绕过