ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Proxmark3(Iceman Fork)MFU 二进制 Dump 格式解析:新旧两种二进制布局、Plain 格式与 JSON 格式详解

Proxmark3(Iceman Fork)MFU 二进制 Dump 格式解析:新旧两种二进制布局、Plain 格式与 JSON 格式详解 Proxmark3Iceman ForkMFU 二进制 Dump 格式解析新旧两种二进制布局、Plain 格式与 JSON 格式详解【免费下载链接】proxmark3Iceman Fork - Proxmark3项目地址: https://gitcode.com/GitHub_Trending/pr/proxmark3本篇基于 Proxmark3 仓库中的doc/mfu_binary_format_notes.md完整梳理 MIFARE Ultralight / NTAG下称 MFU标签 dump 文件的三代二进制格式New / Old / Plain以及面向外部工具的 JSON 格式。读完本文你将能够独立解析 PM3 dump 出的 MFU 二进制文件识别 56 字节新头部与各字段偏移、区分三种格式的自动检测原理BCC 校验、理解 PACK/计数器/撕裂标志在内存中的归位逻辑以及编写兼容 PM3 格式转换逻辑的工具。背景为什么 MFU dump 需要“头部 内存数据”两种结构PM3 读取 MFU 家族标签MIFARE Ultralight、Ultralight EV1、NTAG 2xx 系列等时除了可读的页内存还有一批“标签元数据”对模拟真实标签至关重要PACK标签密码。PM3 dump 时可以拿到 pwd/pack但 PACK 在标签上是不可读的内存区域。PM3 的做法是把它写回它在标签内存中的“正常位置”因此 dump 文件中的内存并非严格逐字节的真实内存而是“假如所有内存都可读它本应是什么样”What it should have looked like。计数器与撕裂标志counter / tearing byte不同厂商的标签能力不一致——例如 UL-EV1 有 3 组计数器和撕裂字节而 NTAG 只有 1 组。这正是新一代二进制格式诞生要解决的问题用统一的“最大能力”布局容纳不同厂商的差异。signatureUL-EV1 等标签返回的 32 字节签名用于模拟时的身份还原。围绕这些字段MFU dump 格式经历了 Plain → Old → New 的演进并面向 libnfc 等外部工具生态推荐了 JSON 格式。三代二进制格式在当前代码中仍然全部支持由客户端在读入 dump 时自动检测并转换。三代格式总览格式前缀长度定义位置说明New mfu format56 字节MFU_DUMP_PREFIX_LENGTHinclude/mifare.h当前格式PACK 已并入数据区Old mfu format48 字节OLD_MFU_DUMP_PREFIX_LENGTHclient/src/cmdhfmfu.h头部独立保存 tearing/pack仅用于转换Plain mfu format0 字节client/src/fileutils.c纯内存 dump无任何元数据JSONfuture不适用doc/mfu_binary_format_notes.md面向外部应用/工具的推荐格式相关全局常量定义在 client/src/cmdhfmfu.h#define MFU_BLOCK_SIZE 0x04 // MFU 页大小4 字节 #define MFU_MAX_BLOCKS 0xFF #define MFU_MAX_BYTES (MFU_MAX_BLOCKS * MFU_BLOCK_SIZE) // 1024MFU 的“块”即页page每页 4 字节data[1024]对应最多 256 页。New mfu format当前二进制格式定义见 include/mifare.h注释与文档一致长度必须对齐到 4 字节的 UL/NTAG 页// New Ultralight/NTAG dump file format // Length must be aligned to 4 bytes (UL/NTAG page) #define MFU_DUMP_PREFIX_LENGTH 56 typedef struct { uint8_t version[8]; uint8_t tbo[2]; uint8_t tbo1[1]; uint8_t pages; // max page number in dump uint8_t signature[32]; uint8_t counter_tearing[3][4]; // 3 bytes counter, 1 byte tearing flag uint8_t data[1024]; } PACKED mfu_dump_t;字段偏移逐段说明偏移长度字段说明08version标签 Version 页数据页 4 的可读部分 固定位NTAG 上对应页 3 版本信息82tboTearing Bytes0101tbo1Tearing Bytes1111pagesdump 覆盖的最大页号0 起。文件总长 56 (pages1) * 4字节1232signatureUL-EV1 签名4412counter_tearing[3][4]3 组“3 字节计数器 1 字节撕裂标志”无此能力的标签对应组为空/零56≤1024data页内存数据对齐到 4 字节PACK 位于其最后一页的末尾设计要点对应文档 New mfu format 一节PACK 被移出头部。它只是标签内存的一部分只是不可读PM3 在 dump 时若有 pwd/pack就直接写入它在内存中的正常位置末页末尾 2 字节。这样头部不再携带“特殊位置”的字段语义统一。头部尺寸由“最大能力”决定。counter_tearing[3][4]按 UL-EV1 的 3 组计数器设计NTAG 只用其中 1 组即可从而“补偿不同厂商标签功能的差异”。data声明为 1024 字节256 页但实际文件长度按pages字段裁剪m页的 dump 文件共56 (m1) * 4字节。客户端打印 dump 时也会显示该头部尺寸见 client/src/cmdhfmfu.c 中mfu_print_dump()的Header size..... 56 bytes输出。Old mfu format48 字节头部仅保留用于转换定义见 client/src/cmdhfmfu.h// Old Ultralight/NTAG dump file format // It is used only for converting #define OLD_MFU_DUMP_PREFIX_LENGTH 48 typedef struct { uint8_t version[8]; uint8_t tbo[2]; uint8_t tearing[3]; uint8_t pack[2]; uint8_t tbo1[1]; uint8_t signature[32]; //uint8_t counter[3]; uint8_t data[1024]; } PACKED old_mfu_dump_t;与 New 格式的差异字段偏移对照偏移Old长度字段在新格式中的去向08version原样拷贝82tbo原样拷贝103tearing拆散放入counter_tearing[i][3]122pack移入数据区末页末尾见下方转换代码141tbo1原样拷贝1532signature原样拷贝48≤1024data原样拷贝文档指出旧格式保存这些额外数据的目的是让 Proxmark3 能够模拟simulate真实标签注意结构体中被注释掉的counter[3]说明旧格式根本没有计数器概念转换时计数器组保持零值。旧格式 → 新格式的自动转换转换实现位于 client/src/fileutils.c 的convert_old_mfu_dump()关键动作size_t old_data_len *dumplen - OLD_MFU_DUMP_PREFIX_LENGTH; size_t new_dump_len old_data_len MFU_DUMP_PREFIX_LENGTH; ... for (int i 0; i 3; i) { mfu_dump-counter_tearing[i][3] old_mfu_dump-tearing[i]; } memcpy(mfu_dump-data, old_mfu_dump-data, sizeof(mfu_dump-data)); mfu_dump-pages old_data_len / 4 - 1; // Add PACK to last block of memory. memcpy(mfu_dump-data (mfu_dump-pages * 4 MFU_DUMP_PREFIX_LENGTH), old_mfu_dump-pack, 2);即3 个 tearing 字节分散写入三组counter_tearing[i][3]计数器本身为 0PACK 按新格式约定写入数据区最后一页的最后 2 字节pages由旧数据长度重新计算。转换后文件长度增加 8 字节56 − 48。Plain mfu format最原始的纯内存 dump文档 Plain mfu format 一节说明MFU 最早的二进制格式就是标签从第 0 页到末页的直接内存转储没有任何头部uint8_t data[1024];Plain 格式没有任何元数据无 version、signature、计数器、PACK无法完整还原标签身份。PM3 读入时会把它包装成 New 格式实现见 client/src/fileutils.c 的convert_plain_mfu_dump()memcpy(mfu-data, *dump, *dumplen); mfu-pages *dumplen / 4 - 1; *dump (uint8_t *)mfu; *dumplen MFU_DUMP_PREFIX_LENGTH;即分配一个全零的mfu_dump_t把原始数据搬进data头部其余字段留零。格式自动检测BCC 校验如何区分三种布局三种二进制格式没有魔数magic number区分布局只能靠数据本身。PM3 的检测函数是 client/src/fileutils.c 的detect_mfu_dump_format()策略是依次假设数据区起点用 MFU 页 0/1 中 UID 的 BCC 校验字节验证假设是否成立uint8_t ct 0x88; // UID BCC 常数 // detect new假设页 0 位于偏移 56 bcc0 ct ^ new-data[0] ^ new-data[1] ^ new-data[2]; bcc1 new-data[4] ^ new-data[5] ^ new-data[6] ^ new-data[7]; if (bcc0 new-data[3] bcc1 new-data[8]) { retval MFU_DF_NEWBIN; } ... // detect old假设页 0 位于偏移 48同一套 BCC 逻辑 // detect plain假设页 0 位于偏移 0同一套 BCC 逻辑原理MFU 标签第 0 页是 UID 高 3 字节 BCC第 1 页是 UID 低 3 字节 BCC。BCC 页第 0 页第 3 字节等于0x88 ^ UID[0..2]另一 BCC 等于UID[4..6]的异或。若按某一偏移解释内存时两处校验同时通过则该偏移就是数据区起点。检测顺序为新 → 旧 → Plain并返回MFU_DF_NEWBIN / MFU_DF_OLDBIN / MFU_DF_PLAINBIN枚举。代码中还保留了一个厂商特例分支NTAG I2C 1K/2K plusSAK 0x00、ATQA44 00的内存布局与常规 NTAG 不同其页 7/8/9 为00 44 00特征若命中则直接判定为新格式见 client/src/fileutils.c。统一入口convert_mfu_dump_format()client/src/fileutils.c在新格式命中时直接放行、旧/Plain 格式命中时执行对应转换无法识别时返回错误——所以第三方生成的 MFU dump 只要 UID 区完整、BCC 正确就能被 PM3 自动接受。Future mfu format面向外部工具的 JSON 格式文档 future mfu format 一节明确给出结论对于 libnfc 之类的外部应用与工具不推荐使用二进制格式而应采用 JSON因为它对新标签功能的变化更灵活。示例原文档原样继承{ Created: proxmark3, FileType: mfu, Card: { UID: 04F654CAFC388, Version: 0004030101000B0, TBO_0: 000, TBO_1: 0, Signature: BC9BFD4B550C16B2B5A5ABA10B644A027B4CB03DDB46F94D992DC0FB02E0C3F, Counter0: 00000, Tearing0: BD, Counter1: 00000, Tearing1: BD, Counter2: 00000, Tearing2: BD }, blocks: { 0: 04F6542, 1: CAFC388, 2: 8E48000, 3: E110120, 4: 0103A00, 5: 340300F, 6: 0000000, 7: 0000000, 8: 0000000, 9: 0000000, 10: 0000000, 11: 0000000, 12: 1122334, 13: 0000000, 14: 0000000, 15: 0000000, 16: 000000F, 17: 0005000, 18: 0000000, 19: 0000000 } }字段与二进制格式的对应关系Card.UID来自data的页 0–1示例中04 F6 54CA FC 3804F654CAFC38Card.Version对应version[8]TBO_0/TBO_1对应tbo/tbo1Counter0..2Tearing0..2对应counter_tearing[3][4]——示例中三组均给出体现 JSON 用“可选字段”天然消化厂商差异NTAG 标签省略 Counter1/2 即可无需像二进制格式那样固定占位blocks以页号为键、每页 4 字节十六进制为值与二进制data区一一对应。PM3 客户端在准备 JSON 文件时确实写入FileType: mfu字段见 client/src/fileutils.c 的prepareJSON()。实战解析与使用 MFU dump 文件的操作要点判断格式看文件大小。(len - 56) % 4 0且 len 56 优先按新格式(len - 48) % 4 0按旧格式两者皆否但能被 4 整除的按 Plain。严格判定则复现 BCC 检测逻辑见上文。提取纯内存新/旧格式跳过 56/48 字节前缀pages (len - 56) / 4 - 1新格式。PM3 自身读入时同理例如 client/src/cmdhfmfu.c 中pages (bytes_read - MFU_DUMP_PREFIX_LENGTH) / MFU_BLOCK_SIZE。提取/写回 PACK位于data区最后一页的最后 2 字节mem-data (bytes_read - MFU_DUMP_PREFIX_LENGTH - 2)见 client/src/cmdhfmfu.c。模拟带密码标签时必须保留这 2 字节。数据区页索引偏移在把 dump 文件直接喂给模拟器内存接口时注意页索引要平移MFU_DUMP_PREFIX_LENGTH / MFU_BLOCK_SIZE 14页即 56 字节 / 4该注释见 client/src/cmdhfmfu.c。兼容旧文件无需手工转换PM3 的pm3_load_dump()路径会经过convert_mfu_dump_format()自动把 Old/Plain 提升为 New 格式但如果你自己写解析器建议同时实现两套前缀偏移。小结**New 格式56 字节前缀mfu_dump_t**是当前标准PACK 归位内存、3 组计数器/撕裂标志容纳 UL-EV1 与 NTAG 的能力差异pages字段决定文件裁剪长度**Old 格式48 字节前缀old_mfu_dump_t**头部独立保存tearing[3]/pack[2]现仅作为转换目标保留Plain 格式是零头部的纯内存转储信息量最少JSON 格式是面向外部生态如 libnfc的推荐方案以可选字段的方式优雅处理厂商功能差异。四者并非互斥PM3 在读入时通过 UID 的 BCC 校验自动识别二进制布局并完成旧 → 新转换client/src/fileutils.c因此围绕该格式开发工具时对齐mfu_dump_t的字段偏移并保留末页 PACK即可获得与 PM3 客户端一致的兼容性。【免费下载链接】proxmark3Iceman Fork - Proxmark3项目地址: https://gitcode.com/GitHub_Trending/pr/proxmark3创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表