零依赖的 MoonBit 文件类型识别引擎:容器结构解析、证据链判定与可扩展规则,附 CLI。
Dependencies
moon add Mr-Houjie/mbt-filetypelet detection = @Mr-Houjie/mbt-filetype.detect(bytes)
match detection.file_type {
Some(file_type) => {
println(file_type.id) // "docx"
println(file_type.mime) // "application/vnd.openxmlformats-officedocument..."
println(detection.score) // 100
for item in detection.evidence {
println("\{item.kind} +\{item.weight}: \{item.label}")
}
}
None => println("unknown")
}| 证据类型 | 权重 | 例子 |
|---|---|---|
| 强魔数 | 90 | PK\x03\x04、%PDF- |
| 弱魔数 | 20 | BM、MZ、P4 |
| 结构验证 | 40 ~ 70 | 中央目录解析成功、OLE2 目录流命中、PE 头校验通过 |
| 文本启发式 | 30 | JSON/CSV 形状 |
examples/sample.png
type: png
mime: image/png
extension: .png
category: image
confidence: certain (90/100)
evidence: PNG signature
offset: 0
bytes: 8
[1] magic +90 @0: PNG signaturelet registry = @Mr-Houjie/mbt-filetype.Registry::new()
// 声明式规则:支持固定偏移、尾部偏移、位掩码、ASCII、与/或组合
registry.add_rule(
@Mr-Houjie/mbt-filetype.Rule::new(
"myfmt", "application/x-myfmt", "myfmt", @Mr-Houjie/mbt-filetype.Other,
"MYFMT v1 魔数", 90,
@Mr-Houjie/mbt-filetype.Matcher::all([
@Mr-Houjie/mbt-filetype.Matcher::magic_at(0, b"MYFMT"),
@Mr-Houjie/mbt-filetype.Matcher::masked_at(5, b"\x01\x00", b"\xff\x00"),
]),
),
)
// 命令式检测器:规则与内置签名都没命中时兜底
registry.add_detector(fn(data) { ... })
let detection = @Mr-Houjie/mbt-filetype.detect_with(registry, bytes)# 识别单个文件
moon run cmd/main -- examples/sample.png
# 以 JSON 输出
moon run cmd/main -- --json examples/sample.json
# 显示证据链、偏移与评分
moon run cmd/main -- --explain examples/sample.png
# 断言类型:不匹配时退出码为 1
moon run cmd/main -- --expect image/png examples/sample.png
# 递归扫描目录,报告扩展名与内容不符的文件
moon run cmd/main -- --scan examples
# 列出全部已知类型
moon run cmd/main -- --list-types| 等级 | 判定方式 | 覆盖 | 定位 |
|---|---|---|---|
| A. 结构解析 | 按格式规范解析容器结构 | ZIP、OLE2、ISO-BMFF、RIFF/IFF、EBML、Ogg、tar、PE、BMP | 项目的实质贡献 |
| B. 魔数 + 字段校验 | 魔数命中后校验长度、版本或枚举字段 | 其余图像、归档、字体、数据库、科学数据 | 兼容层 |
| C. 保守启发式 | 文本结构判定,一律 probable | json / xml / html / csv / tsv / text | 显式标注不确定 |
| 容器 | 解析方式 | 可区分 |
|---|---|---|
| ZIP | EOCD → 中央目录 → 条目名/压缩方法 | docx / xlsx / pptx / epub / odt / ods / odp / xps / apk / jar / zip |
| OLE2 | 512 字节头 → DIFAT → FAT → 目录扇区 → UTF-16LE 流名 | doc / xls / ppt / ole2 |
| ISO-BMFF | box 树 → ftyp brand + moov/trak/mdia/hdlr | mp4 / mov / m4a / heic / avif / 3gp |
| RIFF / IFF | chunk 遍历 + 必需块校验 | wav / avi / webp / aiff / riff / iff |
| EBML | vint 解码 → DocType | mkv / webm |
| Ogg | 页头 + segment table → 首包 | opus / ogg |
| tar | 512 字节 header 八进制 checksum | tar |
| PE | e_lfanew → COFF → Optional Header | pe(含机器类型、EXE/DLL、节数) |
| BMP | 保留字段 + DIB header 大小 | bmp |
moon test # 标准测试(部分 Windows 环境受嵌套进程限制)
moon run cmd/verify # 独立自检入口,与 moon test 等价# 准备一个真实文件语料目录,然后:
python tools/crosscheck.py corpus/| 场景 | 行为 |
|---|---|
| deflate 条目内容 | 条目名仍从中央目录读取;内容只读 stored 条目(EPUB/ODF 规范要求 mimetype 必须 stored) |
| ZIP64 / 单条 > 2 GiB | 中央目录解析失败 → 降级为 zip,不猜具体子类型 |
| > 2 GiB 的 ISO-BMFF box | box 遍历停止 → 退回 ftyp brand 判定;64 位 largesize 仅支持高 32 位为 0 |
| 归档内部 | 不递归;--scan 只报告外层文件 |
| 文本类 | 一律 probable,不与二进制判定混用 |
| 恶意构造 / 完整性 | 不检测,本库不是安全扫描器 |
pub(all) enum Category {
Image
Document
Archive
Audio
Video
Executable
Font
Database
Scientific
Wasm
Text
Other
}pub(all) enum Confidence {
Certain
Probable
Unknown
}pub(all) struct Evidence {
kind : String
label : String
offset : Int
matched_len : Int
weight : Int
}pub struct PeInfo {
machine : Int
is_64 : Bool
is_dll : Bool
subsystem : Int
sections : Int
}pub struct ZipEntry {
name : String
compression : Int
compressed_size : Int
uncompressed_size : Int
data_offset : Int
local_offset : Int
}fn tar_checksum_ok(data : Bytes) -> BoolInstall
Download zip零依赖的 MoonBit 文件类型识别引擎:容器结构解析、证据链判定与可扩展规则,附 CLI。
Dependencies