wailun42/canon-g3010-chmp-scanner
GitHub: wailun42/canon-g3010-chmp-scanner
通过逆向工程 Canon 未公开的 CHMP 网络协议,让缺乏官方 Linux 扫描驱动的 PIXMA G3010 打印机实现网络扫描功能。
Stars: 0 | Forks: 0
# Canon G3010 CHMP 扫描仪 — 逆向工程网络扫描协议
这是一个适用于 Canon PIXMA G3010(以及可能相关的 Canon G 系列喷墨打印机)的可用 Linux 扫描客户端。它通过 HTTP 直接与打印机未公开的 **CHMP** 协议进行通信 —— 无需 Windows 主机,无需厂商扫描实用程序,也无需 SANE backend。
## 问题所在
Canon 为**打印**提供了正规的 Linux 驱动路径(CUPS/IPP 运行良好),但对于包括 G 系列在内的许多消费级喷墨多功能一体机,却没有提供**扫描**支持。唯一受支持的扫描方式是使用 Canon PRINT 移动应用程序或 Windows/Mac 桌面实用程序 —— 它没有 SANE backend,并且打印机无法完美兼容任何标准的网络扫描协议:
| 尝试的协议 | 结果 |
|---|---|
| eSCL(现代的“即插即用”标准,尝试了 20 多种路径变体) | 全部返回 404 —— 跨子网无法解析 mDNS 广播的 `rp=` 路径 |
| BJNP(Canon 较早的 UDP 发现/扫描协议,端口 8612) | 无响应 |
| WSD(WS-Discovery/WS-Scan,UDP 3702 + HTTP) | 无响应 / 404 |
| SNMP(端口 161) | 无响应 |
| 除了端口 80 / 443 / 631 / 9100 之外的任何端口 | 已关闭 |
这就只剩下了一个选择:对 Canon PRINT 应用程序实际使用的协议进行逆向工程。
## 方法
在取得突破之前,我们编写了 27 个探测脚本,针对打印机的 HTTP endpoint(`/canon/ij/command2/port3`)逐步测试各种假设 —— 包括会话建立、TLS 行为、命令组帧等。这些探测没有一个是浪费的:每一个都排除了死胡同,并验证了传输层的一部分(例如 endpoint、会话握手的结构、端口 443 上必须使用 TLS 1.2 等)。
真正的突破来自于 [`pixma-rs`](https://github.com/pdrgds/pixma-rs),该项目通过抓取 Canon PRINT 应用程序的数据包,对一台相关的 Canon 打印机协议进行了逆向工程。将其与我们之前的尝试进行对比,揭示了确切的协议版本不匹配问题:
| 我们之前尝试的方式 | 应用程序实际的执行方式 |
|---|---|
| `X-CHMP-Version: 1.1.0` | `X-CHMP-Version: 1.4.0` |
| 在 StartJob *之后* 进行 ModeShift | 在 StartJob **之前** 发送空任务的 ModeShift |
| 没有功能查询 | 在 StartSession **之前和之后** 发送 `0xf320` 功能查询 |
| StartSession flags = `0x0000` | StartSession flags = `0x0001` |
| 带有参数和校验和的 ReadImage | 在 header 的第 12-13 字节处带有块大小的 ReadImage,**没有**参数/校验和 |
| Status3 总是返回 `0x1515` | Status3 正常工作 —— 通过 header 的第 14-15 字节请求 8 字节的响应 |
`0x1515` 看起来就像是 `ReadImage`/`Status3` 收到的永久性 NAK —— 但实际上它只是协议版本错误时报告的“未实现”。
## 协议:基于 HTTP 的 Canon CHMP 1.4.0
**Endpoint:** `POST /canon/ij/command2/port3`,紧接着是 `GET /canon/ij/command2/port3`(每个命令对应一个请求/响应对 —— POST 负责发送命令,GET 负责获取响应)。
**扫描序列:**
```
1. Ping handshake (0x00 byte) ×2
2. Blank-job ModeShift (XML) — BEFORE StartJob
3. Capability query (0xf320)
4. StartJob (XML)
5. ModeShift, mode=1 (XML)
6. StartSession (0xdb20, flags=0x0001) → expect 0x0606
7. Capability query (0xf320) — again, after StartSession
8. ScanParam3 (0xd820, params + checksum) → expect 0x0606
9. ScanStart3 (0xd920, flags=0x0001) — triggers the physical scan
10. Status3 (0xda20) polling loop until status byte == ready
11. GetDimensions (0xdc20)
12. ReadImage (0xd420) loop — block size in header bytes 12-13, no params/checksum
13. AbortSession (0xef20) + EndJob (XML) + cleanup handshake
```
**命令码:**
```
CMD_CAPABILITY = 0xf320
CMD_START_SESSION = 0xdb20
CMD_SCAN_PARAM3 = 0xd820
CMD_SCAN_START3 = 0xd920
CMD_STATUS3 = 0xda20 # needs rlen=8 in the request header to get a real answer
CMD_READ_IMAGE = 0xd420 # block size at header bytes 12-13, no checksum
CMD_ABORT_SESSION = 0xef20
CMD_GET_DIMENSIONS= 0xdc20
```
**响应状态码:**
- `0x0606` —— ACK / OK
- `0x1515` —— NAK(在错误的协议版本下,`ReadImage`/`Status3` 会永久返回此状态)
- `0x1414` —— 忙碌
**关于 HTTPS(端口 443)的注意事项:** 具有相同的 CHMP 语义,但需要强制将 `TLSv1.2` 作为*最高*版本 —— 打印机的嵌入式服务器不支持 1.3,且自签名证书仅对打印机的局域网 IP 有效。
## 仓库结构
```
src/
chmp_scan.py single-file client, read top to bottom for the full protocol implementation
```
`chmp_scan.py` 内部解析:
| 部分 | 功能 |
|---|---|
| `log`, `recv_until`, `parse_headers`, `read_chunked`, `read_response` | 在裸 socket 上进行原始 HTTP + 分块传输解析 —— 不使用 `requests`/`urllib`,因为 CHMP 需要手动控制“先 POST 后 GET”的组帧逻辑 |
| `class Chmp` | Session wrapper:开启 socket/TLS 连接,将每个命令作为“POST + GET”对来驱动 |
| `build_command`, `checksum_params` | 二进制命令组帧 —— header 字节、参数 payload、校验和 |
| `cmd_start_session`, `cmd_abort_session`, `cmd_capability_query`, `cmd_get_dimensions`, `cmd_scan_start_3`, `cmd_status_3`, `cmd_read_image` | 每个 CHMP 命令码对应一个构建器(参见上方的命令表) |
| `a4_params` | 为给定的 DPI / 颜色模式构建 ScanParam3 payload |
| `status_code`, `require_ok`, `parse_image_block` | 响应解码 —— 状态字节、ACK/NAK 检查、从 `ReadImage` 提取 JPEG 块 |
| `main` | CLI 入口 —— 参数解析,端到端运行 13 步扫描序列,写入输出文件 |
## 用法
```
python src/chmp_scan.py --host --dpi 300 --out scan.jpg
python src/chmp_scan.py --host --dpi 600 --gray --out scan-gray.jpg
```
要求打印机在网络中已经可达(同一局域网,或通过 VPN/Tailscale 路由)。无需 SANE,无需驱动程序,无需厂商应用程序。
## 最终部署在哪里
在生产环境中,这被作为一个 agent skill 集成到了 Discord 机器人(`/printer scan`、`/printer print`、`/printer status`)中,与基于 CUPS 的打印功能并列 —— 因此可以通过聊天进行打印/扫描,而无需接触打印机自带的(仅限 Canon 应用程序的)UI。
## 潜力 / 未来工作
CHMP 协议族极有可能也适用于其他 Canon G 系列消费级喷墨打印机(G1010/G2010/G4010 等),这些机型都面临着完全相同的“无 Linux 扫描路径”问题。本仓库目前专门针对 G3010 进行了记录和解决;如果将 `chmp_scan.py` 泛化为一个能够自动检测不同型号间功能差异的轻量级 SANE-backend 风格工具,就能将其从“针对单一打印机的修复方案”转变为更广泛的 Canon-on-Linux 社区真正能够使用的东西 —— 如果大家有需求,这将是顺理成章的下一步计划。
## 致谢
基于 [`pixma-rs`](https://github.com/pdrgds/pixma-rs) 的协议基础工作构建。
标签:SANE, 云存储安全, 云资产清单, 协议分析, 权限提升, 网络扫描, 逆向工具, 逆向工程