尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

固件黑盒解包与打包实战:从零构建嵌入式固件分析工具链

发布时间:2026/9/27 10:26:55

资讯中心
01
ARTICLE

固件黑盒解包与打包实战:从零构建嵌入式固件分析工具链

固件黑盒解包与打包实战:从零构建嵌入式固件分析工具链
接手这份固件的时候我整个人是懵的。文件倒是完整一个几百 MB 的 bin 镜像但没人能说清楚它内部怎么分区、用什么文件系统、启动流程走到哪一步、改动后怎么打包回去。前任离职时交接文档只有一句话“这个是整包固件可以刷。”我拿着这句话对着一个黑盒硬着头皮把解包、分析、修改、打包的整套流程趟了一遍。最终做出来一个小工具集能把“没人讲得清的固件”变成一份带结构说明、带校验逻辑、带可回放步骤的工程资料。这篇文章就是把整个思路和实操过程记录下来。主要内容包括先怎么判断固件类型、工具链怎么设计、解包和打包的核心步骤、刷机验证时的坑以及我踩过之后总结出的排查方法。对经常接触嵌入式设备、路由器、IoT 产品固件或者正在被无文档固件折磨的工程师应该会有直接参考价值。1. 项目背景与问题拆解1.1 固件为什么容易变成“没人讲得清”的状态固件就是设备上电后运行的那一套完整软件通常包含引导程序、操作系统内核、根文件系统、应用配置分区这些部分。平时用户感知不到它但设备启动、联网、执行功能全都要靠它。对做设备维护或二次开发的人来说固件就是一个需要不断拆解的谜题。正常情况下一份固件应该有对应的源码、编译脚本、分区表配置、刷机说明。可现实里这些材料经常是残缺的。我遇到过的情况无非这么几种产品迭代快固件打包脚本散落在离职同事的个人电脑里硬件方案供应商只给了编译好的镜像内部结构不解释项目经过外包转手关键文档在验收时就没交付时间一长连当初谁打的包都查不到了。结果就是你接手了一个设备想改个配置、加个功能、或者排查一个问题发现手上只有这份光秃秃的固件文件。这个文件内部是什么格式、是否压缩、有没有签名、改动后怎么算校验没有任何人说得清楚。所以这篇文章提到的“做了个工具”其实做的是针对这种黑盒固件的系统化分析流程。不是某个单一功能而是把固件识别、解包、文件系统展开、校验分析、再打包整合成一套半自动化的工具链让之后每次拿到类似的固件都能快速上手。1.2 拿到固件后我做的第一件事不是解包而是勘察很多人拿到固件第一反应是直接上 binwalk 一把梭。我最初也这么干过但后来发现在完全不了解目标设备的情况下直接解包容易漏掉关键区域也容易误判结构。我的第一轮动作是先把固件当成一份“未知格式的二进制文件”来对待做的事情包括用 file 命令看文件类型用 hexdump 看文件头部和尾部用 strings 检索里面的可读字符串用 binwalk 做一次整体扫描看有没有嵌入的文件系统和内核镜像。接着会再看一遍文件大小判断Flash容量和分区边界是否合理。这一步很关键。它能帮我确认固件是完整的整包镜像还是只有升级用的部分分区平台是大端还是小端引导程序是什么类型文件系统大概用的哪一种。这些东西决定了后面所有步骤怎么做。我习惯把勘察结果记下来写成一份简单的笔记包括文件大小、检测到的文件系统偏移、可疑的头部魔数、字符串里暴露的版本号。这些信息在后面对比不同版本固件、定位问题时特别有用。2. 工具选型与整体设计2.1 为什么没有只用一个通用工具硬扛网上能直接用的固件分析工具其实不少binwalk、firmware-mod-kit、binutils、各种十六进制编辑器还有各厂商私有格式的解包脚本。这些工具最大的价值是可以快速识别常见文件系统和压缩算法。但遇到“没人讲得清的固件”它们往往只能撑住前半段。问题在于一份固件如果经过厂商自定义封装口头上讲的“固件”可能包含了自定义头部、签名块、分区交错排列甚至部分加密数据通用工具没有对应知识扫出来的结果是一堆碎片没法直接定位到真正的文件系统起始位置。就算找到了 rootfs也不知道它是 SquashFS、UBIFS 还是别的格式就算知道格式也不知道打包时用什么参数对齐。这个时候真正的解决方案不是迷信某一个工具而是围绕这份固件建立一套自己的分析流程先看结构再解析头部然后按分区解包最后按原格式打包。通用工具在其中充当基础组件但核心逻辑需要写在自定义脚本里。所以我做工具集时定了一个原则不追求用一个命令解决所有问题而是把每个环节拆成小模块每个模块处理一件事模块之间通过标准文件接口传递数据。这个设计让我在后期调整分析逻辑时不用每次从头跑可以只重跑某一个环节。2.2 工具集分成四个模块识别、解包、定位、打包最终形成的工具集大约长这样识别模块读取固件头部信息、检测魔数、扫描分区表输出一份固件结构描述JSON 格式。解包模块根据结构描述把固件切割成多个分区镜像识别并解出文件系统。定位模块在解包出来的文件系统里搜索关键词、检查版本、对比文件差异。打包模块把修改后的文件系统重新压缩按原分区偏移和填充规则拼回完整固件重算校验值。每个模块相互独立都可以单独跑。比如我只想看看某个分区里有没有某个文件就不用解整个包我只想改一个文件再打回去也不用重新跑识别逻辑。在实际使用中这种模块化的做法帮我省了很多时间。原本一个固件从拿到手到解包成功可能要折腾一整天有了这套流程之后大部分重复劳动变成脚本自动执行我只需要人工确认关键点比如文件系统类型判断、校验算法识别、签名处理方式。2.3 技术路线Python 为主外部工具为辅工具主体用 Python 3 写因为处理二进制数据、结构化输出、文本检索都方便。具体用到的东西很常规struct 模块用来解析二进制头部zlib/lzma 用来处理常见压缩数据subprocess 调用 binwalk、unsquashfs、mksquashfs 这些现成命令行工具hashlib 用来算校验和。我没有用太复杂的新框架。原因很简单固件分析工具不需要常驻服务也不是高并发系统它的运行时间瓶颈在文件 I/O 和解压算法Python 足够应对。写起来快可维护性也好还能快速根据新遇到的固件格式调整脚本。这里有个经验可以分享外部工具调用时尽量不要在内存里传大文件而是让工具读写磁盘上的临时文件。否则几百 MB 的固件在 Python 和 C 工具之间反复复制容易把内存撑爆。3. 核心实现固件分析工具链的实操细节3.1 解析固件头部与分区表固件解析的第一步是读取文件头部。大多数嵌入式固件在开头会有一个魔数比如常见的 uImage 头以 27 05 19 56 开头有些厂商会在自定义头部里写固件版本、平台号、镜像类型、长度、校验值。不了解这些字段含义的时候最好的办法就是对照十六进制文本一个个试。我会在脚本里读取前 256 或 512 字节打印出十六进制和对应的 ASCII 字符再把可能的魔数、长度字段、版本字符串标出来。然后根据固件大小和 Flash 的擦除块大小常见 64KB、128KB、256KB逐步对齐搜索分区表。分区表的位置不固定。有的在固件最开头有的在正式数据前一段固定偏移有的干脆藏在固件尾部。搜索方式一般是这么做的先找文件系统魔数比如 SquashFS 的 sqsh/hsqs或者 UBI 的 UBI#再找内核镜像特征比如 ARM 平台常见的 zImage 起始数据每个候选位置记录下来再结合长度字段反推。这个阶段输出最关键的东西是一个“分区描述列表”包含每个分区的名称、起始偏移、长度、类型。后续所有操作都依赖这个列表所以我在脚本里加了人工确认步骤识别出的分区信息不会直接用于解包而是先生成一个可检查的报告确认无误后再继续。3.2 文件系统解包与文件识别确认分区结构后下一步是解包。常见嵌入式文件系统主要是 SquashFS、UBIFS、JFFS2、CramFS、ext4 这些。SquashFS 最改起来最顺手因为它是只读压缩文件系统解包后修改再重新压缩即可UBIFS 相对麻烦需要在 nandsim 环境或使用 ubi 工具处理。解包第一步是用 binwalk 检查文件系统在固件中的偏移也可以用宿主机自带的 file 命令直接识别。得到偏移后用 dd 把文件系统部分单独切出来dd iffirmware.bin ofrootfs.squashfs bs1 skip0x2A0000 count0x3C00000 statusprogress切出来之后用 unsquashfs 解包unsquashfs rootfs.squashfs解完会得到一个 rootfs 目录里面就是完整的根文件系统。这时候我能看到 /etc、/usr、/bin 这些目录也能找到 init 脚本、服务配置、web 页面文件。需要特别注意的是文件权限和特殊文件。嵌入式文件系统里经常有设备节点、符号链接、setuid 程序解包和修改后如果这些信息丢失跑起来就会出各种诡异问题。我的工具里加了一步解包完成后自动对比关键目录的权限、属主和符号链接信息生成一份差异报告。3.3 校验与签名逻辑改动前必须弄清楚的事情很多固件在头部带有校验信息刷机时设备会先验证再决定是否更新。常见的校验方式有明文的 MD5、SHA256 校验值也有更严格的 RSA 签名和 AES 整体加密。这部分决定了你能不能顺利把修改后的固件刷回去。拿到一个固件我先看头部有没有长度字段和校验字段。长度字段好找通常出现在头部偏移 8 到 32 字节之间与文件实际大小相等或相近。校验字段则要通过对比原文件和一个字节都不改但重算摘要的版本才能确认。处理方法分两种对于无签名、只有普通校验值的固件改完后用同算法重算回填到头部字段即可对于有 RSA 签名的固件如果私钥不在手里靠重算普通摘要过不了验证。这时候不要去考虑绕签名而是先确认设备是不是强制校验签名有些设备只校验哈希不校验签名有些设备跳过签名验证的条件很宽松要先做分析再决定。这里有个很实际的判断技巧看固件内容的熵值。如果一个区域数据高度随机、熵值接近 8那很可能是加密过如果熵值中等多半只是压缩。LZMA 压缩的数据熵值也偏高但通常还有结构特征可寻可以尝试用 binwalk 和 uncompress 识别。3.4 打包回写把修改后的固件拼回去解包、修改都做完了最后一步是把整个固件重新拼装成可刷写的镜像。这个过程的核心不是压缩文件系统而是精确还原分区布局和填充规则。我会先把修改后的目录重新打成文件系统镜像。SquashFS 的打包命令大致是mksquashfs rootfs.new rootfs.squashfs -comp xz -b 131072 -processors 4注意这里的块大小和压缩算法要跟原固件保持一致否则设备解压或挂载时可能出问题。如何知道原来的参数看 unsquashfs 解包时的输出它会显示原始压缩方式、块大小信息。然后按原固件分区表用 dd 或一个简单的 Python 拼接脚本把头部、内核、文件系统、配置分区按原始偏移写入新镜像。import struct import hashlib with open(firmware_new.bin, wb) as out: for part in partition_table: # part: (name, offset, size, source_file) with open(part.source_file, rb) as src: out.seek(part.offset) out.write(src.read()) out.seek(0, 2) total_size out.tell() # 更新头部校验字段示例 digest hashlib.sha256(open(firmware_new.bin, rb).read()).digest()最后要做一次完整的校验重新解包新镜像确认所有修改都在计算整包哈希确认长度和校验字段正确再模拟一次分区偏移对齐检查防止刷入后出现坏块错位。4. 实操过程从分析到产出一个可复用的固件包4.1 第一轮勘察到底看到了什么用一个典型场景来还原整个过程。假设设备是一个基于 ARM 平台的小型网关固件名字就叫 upgrade.bin大小约 64MB。用 file 看结果只是简单的“data”用 hexdump 看头部前几个字节是厂商自定义的魔数后面跟着一串 ASCII 版本号再往后是若干个 4 字节长度字段用 binwalk 扫描检测出在偏移 0x2A0000 附近有 SquashFS 文件系统标记另外在 0x1F0000 附近可能有一个 Linux 内核镜像。第一轮勘察结论大概是这样的这是一个整包固件头部区域 0x0 到 0x1F0000 是引导和内核区0x2A0000 开始是根文件系统。有了这个粗判断再结合固件大小看分区边界基本清晰。我把它记录为一份结构草图然后进入解包环节。这个过程看起来简单但后面很多问题的根源都在第一轮遗漏了细节比如根文件系统前是否存在一段空白区域或者内核镜像前后有没有填充字节。我的工具在识别阶段会把所有可疑偏移都列出来宁可多列也不要漏掉。4.2 定位到需要修改的功能接下来假设我要做的一件事是修改设备的默认网络配置。解包 rootfs 后进入 /etc 目录查看网络相关的配置文件比如 network 脚本或者某个 JSON 配置文件用 grep 在解包目录里搜索关键字段比如“192.168.1.1”或者某个默认网关地址。搜索结果通常很明确配置内容就在文本文件里直接修改保存即可。需要注意的是有些配置文件只在出厂时使用第一次启动后会被生成的配置覆盖所以光改一个静态文件不够还要找到生成逻辑。这种情况就要在启动脚本里去追认到底是哪个程序写出来的配置追踪 chain 直到源头。我在这个阶段会很依赖文件系统的时间戳和改动痕迹。解包目录里每个文件的时间戳能反映原始打包时间用 ls -l 能看到哪些文件在近一次打包前被改动过这些往往是实际运行时依赖的文件。4.3 重新打包与校验回填修改完成后进入打包环节。将 rootfs 目录重新打成 SquashFS 镜像然后用脚本按原偏移拼装整包。拼装后用一个新的 Python 脚本重算头部校验值写到对应偏移。这里特别强调一下重算校验之前先确认原头部校验字段的覆盖范围。有些固件的校验值只覆盖头部之后的正文有些覆盖整个文件如果搞错了就算计算正确设备仍然会拒绝升级。我的做法是先用原固件做一次“零修改重打包”实验把原固件解包后不做任何改动直接重新打包看重新生成的文件是否和原文件一致。如果一致说明打包参数和校验逻辑完全掌握如果不一致再对比差异定位是格式对齐、填充数据、还是时间戳的问题。零修改重打包是检验工具链是否可靠的好方法省去之后所有“我明明改了但刷不进”的折腾。4.4 刷机验证的风险控制打包完成后接下来是刷机验证。刷机方式很多串口烧写、U 盘升级、Web 管理页上传、SD 卡启动都有。我通常先选择风险最小的升级方式比如 Web 上传因为这种方式如果失败设备一般会自动回退到旧版本或保持等待状态。刷机前无论如何都要先备份原始固件。我的经验是把原固件和修改后的固件都存到外部存储里并记录它们的 SHA256 值防止后面需要对比时找不到原版。刷完后检查设备是否正常启动、功能是否生效。如果一切正常不代表结束我还会再导出一份完整的分区状态确认运行时的序列号和配置没有被冲掉。很多设备刷完整包会同时重置配置分区如果只改了系统文件不想动配置刷机后就要马上备份配置再恢复。另外如果设备支持双系统分区或者启动回退机制我会优先利用这个特性。刷机过程中异常断电是最容易变砖的场景所以不要在有断电风险的环境下验证。5. 常见问题与排查技巧实录5.1 解包后文件系统挂载失败解包 rootfs 后发现挂载失败最常见原因是偏移没对齐。嵌入式文件系统对分区起始偏移有严格的对齐要求通常是 Flash 擦除块大小的整数倍。如果偏移差一个字节挂载器就会报错。排查时可以尝试把偏移往前或往后调整到最近的擦除块边界。比如检测到文件系统标记在 0x2A0010那真正的起点很可能是 0x2A0000 或 0x2A0000 减去某个填充长度。用 binwalk 的 -A 参数或者手动调整 dd 的 skip 值逐一对齐通常能解决问题。还有一种情况是真端点序不对。ARM 平台一般小端但某些 MIPS 平台是大端。文件系统解包报错时把端序反过来再试一次不损失什么时间。5.2 重打包后校验值一直不对这个问题我遇到过很多次几乎都是因为忽略了头部长度字段或填充字段。固件头部除了校验值之外通常还包含一个长度字段表示“有效数据长度”或“总长度”。如果你只更新了校验值没有同步更新长度字段设备可能在校验阶段直接返回失败。解决方法是先把头部每个字段都映射清楚。用 Python 解析头部把所有 4 字节字段的值和含义列出来比如可能是魔数、版本、平台 ID、镜像长度、校验值、偏移。这步虽然繁琐但非常必要。零修改重打包实验在这里是救命的它能让你确定哪些字段是必须同步更新的。5.3 固件区域熵值过高疑似加密当某一区域怎么都找不到可读字符串、binwalk 也扫不出结构同时十六进制里看到的数据分布非常均匀、没有明显规律这可能是加密数据。先用熵值工具统计验证如果熵值接近 8基本可以断定不是普通压缩文件。压缩数据也能出现高熵但压缩数据通常有可识别头部和尾部或者解压后能看出文件系统结构。遇到高熵区域时我一般分几步排查先看是不是 LZMA 压缩看头部有没有 5D 00 00 等常见 LZMA 属性字节再看是否带 zlib 头和尾再尝试用 binwalk 自动识别。如果确认是加密且没有密钥这一块内容基本没法通过常规手段恢复。那就先跳过它把其他未加密分区处理好再决定后续方案。不要抱着“一定要解出来”的心态死磕很多时候外围数据已经足够定位问题了。5.4 刷进去之后启动卡死启动卡死有两种典型表现一种是在引导阶段就停住一种是在内核或应用启动阶段反复重启。前者通常是内核镜像加载地址不对或者引导参数与内核不匹配后者通常是根文件系统内容错误、权限不对、或者缺少运行依赖。排查时最关键的是拿到串口日志。很多嵌入式设备板上留有 UART 调试口用 USB 转 TTL 接上启动时就能看到完整日志。系统起不来的时候日志会明确告诉你停在哪一行。内核起来但文件系统挂不上会报 VFS 错误应用起不来会报找不到动态库或脚本语法错误。如果手头没有调试口另一个笨办法是回退到“最小修改”原则先把改动限制在一个文件系统分区里用原版固件仅做一次零修改回包确认能不能正常启动。如果零修改回包都起不来说明打包参数本身有问题跟功能改动无关。6. 复盘与后续扩展6.1 这套工具还能往哪些方向扩展我当初做这套工具只是为了解决手头那一个固件但在使用中意识到固件分析流程是高度可复用的。做成工具集之后以后拿到同类设备的新固件只需要跑一遍勘察流程生成结构描述就能定位到所有分区信息节省了大量重复劳动。后续还可以扩展的方向包括多个固件版本的自动 diff能快速看出厂商改了哪些文件文件系统的批量识别把更多私有格式加进识别库固件内嵌文件的自动提取与语法检查以及把分析流程接入 CI让每次拿到新固件都能自动生成一份固件报告。这些都能在原有工具基础上逐步加进去。6.2 我踩过最深的坑只改内容忘了校验的是“整包”有一次改完固件刷进去之后设备没有采用新配置而是反复回到旧状态。排查到后面发现设备并不是只校验升级包的哈希它还会在启动时对整个系统分区做完整性检查一旦发现异常就自动回滚到备份分区。这让我意识到固件校验不一定只在刷机阶段发生运行时校验同样存在。所以分析固件时不能只看刷机校验那一步还要关注系统启动流程中有没有 checksum 检查脚本。这个坑让我调整了工具的设计解包后自动扫描 rootfs 里的脚本搜索 check、verify、hash 相关字样的可执行配置提前知道设备会在哪些环节做完整性校验避免改完文件之后被系统悄悄拉回原样。6.3 最后分享一点个人实操心得回过头看接管这份没人讲得清的固件真正有价值的不是最终产出的新固件而是那套让你有能力反复分析、反复实验的工具链。固件本身只是一堆二进制的累积工具链才是你对它建立“理解”的过程。如果你正在被一份来路不明的固件折磨我的建议是不要急着去搜“万能解包工具”先花时间把它的结构摸清楚把你自己的解包流程做成脚本哪怕一开始很简陋都没关系。只要骨架搭对了后面每遇到一种新格式都只是往工具里加一个识别规则的问题。最后再补一个实用小技巧把每个中间产物的 SHA256、当时的命令行、当时的偏移参数都顺便写进日志文件里。一次两次可能觉得啰嗦一旦哪天需要回退或复盘这些日志的价值比你自己记忆中的“好像是这样”靠谱得多。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。