ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Arm9门铃黑盒逆向:从UART定位到bootloader提取主镜像

2026/9/9 1:39:03 拓冰建站 浏览量
Arm9门铃黑盒逆向:从UART定位到bootloader提取主镜像 朋友拿过来一台二手的网络可视门铃机器能通电但屏幕一直停在启动画面厂家的支持早就停了网上搜不到任何原理图和SDK。唯一能确认身份的就是主控芯片丝印上的“Arm9”字样。没有串口定义、没有JTAG资料、没有原厂固件包想把它救活只能从黑盒逆向开始。我的思路很直接不管业务镜像多复杂先把引导bootloader的行为摸清楚它管着硬件初始化和固件装载通常也留着传输、内存读写这类调试命令只要撬开它主镜像提取的路就通了一半。这篇就完整复盘我从拆机、找UART、进bootloader到导出2MB主镜像的全过程。整个操作基于合法持有的设备全程以读取为主不做任何破坏性修改。1. 拆壳与识片黑盒逆向的第一步不是接J-Link1.1 清丝印、认主控从ARM926EJ-S到具体SoC拆开外壳之后第一眼看到的是两块PCB叠放一块负责摄像头和补光灯一块是主控板。主控芯片上贴着一层标签下面还有残留的散热垫用洗板水慢慢擦掉后丝印才露出来。这颗SoC用的是ARM926EJ-S内核也就是大家常说的Arm9主频一般在200~500MHz之间在门铃这类网络音视频设备里非常常见。需要说明的是Arm9是一个内核家族不是一颗具体芯片。同样是ARM926EJ-S可能是AT91SAM9系列也可能是i.MX28、GK7102这类安防SoC。丝印如果看不清光看主控很难确定具体型号。我当时的做法是反过来推断先看配套芯片再确定SoC的大致范围。旁边有一颗SDRAM两颗NAND Flash一颗以太网PHY还有一些电源管理芯片。这些信息拼起来基本能说明这板子走的是“Arm9 SoC SDRAM NAND启动 有线网络”的经典路线。主控型号对逆向有用吗有但要分清优先级。如果只是提取主镜像型号不确定影响不大我更多的是要找到flash控制器、NAND地址以及bootloader的加载习惯。如果后面想深入反汇编业务代码那确实需要确认是ARM926EJ-S还是别的ARM核。ARM926EJ-S支持ARMv5TE指令集反汇编时用支持ARMv5的IDA或者Ghidra配置就行。1.2 存储介质决定提取策略SPI NOR、NAND与SDRAM拆完丝印紧接着要确认存储介质。这一步直接决定后面用什么方案提镜像越早判断越少走弯路。我在板子上看到两颗明显的存储芯片一颗是8脚的小封装丝印以25开头典型的SPI NOR Flash另外还有一颗48脚封装的芯片丝印是K9F1G08U0C这是三星的128MB SLC NAND。存在两类存储往往意味着不同的作用。SPI NOR里装的可能是主控最底层的启动代码NAND里装的才是业务主镜像和数据区。很多Arm9方案就是这种组合SoC内部的ROM先加载SPI NOR里的bootloader再由bootloader去NAND里把真正的业务镜像读出来。这里有个很重要的判断如果用SPI NOR做第一级引导那么NOR的数据量通常很小256KB到2MB就够NAND才是主镜像的大本营。标题里提到的“主镜像提取”指的就是从NAND这128MB空间里把2MB的App镜像完整抠出来。SPI NOR和NAND在后续操作上的差异很大SPI NOR可以直接用编程器读几乎没有坏块管理也不需要ECC校验NAND则完全不是这么回事它有坏块、有OOB区、有ECC纠错脱离系统直接读出来的裸数据往往不能直接用。所以对NAND这种介质让bootloader本身来读比用编程器硬读可靠得多。至于SDRAM它只是运行时内存不需要导出。但知道SDRAM的地址和大小很重要因为bootloader读NAND时会把数据放到SDRAM的某个地址上。地址不对后面的一系列内存转储操作都无从谈起。1.3 排查调试接口为什么UART的优先级高于JTAG很多人一听说逆向就想着找JTAG接上J-Link一顿操作。但在量产设备上JTAG往往不是最优选择。我检查了这块主控板PCB上确实有疑似JTAG的测试点阵列但顺着走线量了一圈发现TCK、TMS这些信号都连着SoC的GPIO。这说明量产固件大概率已经把JTAG引脚复用成了普通GPIO或者需要触发特定的启动模式才会启用JTAG在没有完整资料的情况下想靠JTAG打开突破口并不现实。UART优先级更高原因有三个UART调试口在嵌入式产品里几乎不会被禁用厂商也要靠它产测。UART只要三根线TX、RX、GND就能工作而JTAG至少要四根还不是每块板子都引出来。UART能看到bootloader的打印信息这些信息本身就是巨大的线索来源。还有一点门铃设备不像路由器那样普遍提供固件升级界面它的串口往往被当作隐藏维护口保留。只要找到UART引脚读出一条启动日志后面的事情就顺了。所以我的顺序是先UART后JTAG最后再考虑编程器硬读。2. 从串口日志剥出bootloader的真实身份2.1 万用表接示波器定位UART引脚找UART引脚这一步骤很多刚入门的同学会卡住。其实思路并不复杂UART发送脚在空闲时是高电平启动时会有大量数据流输出只要把示波器探头挨个扫测试点很快就能找到那个“启动时疯狂翻转”的焊盘。我当时没有用示波器而是用逻辑分析仪采样率设成20MHz。给板上电一瞬间逻辑分析仪自动抓了一整段波形然后把UART协议解码打开波特率先猜115200结果直接解出了完整日志。整个过程从接好线到出日志不到十分钟。如果手里只有万用表也有笨办法把万用表打到直流电压档去量测试点正常空闲的高电平会在3.3V附近上电启动时如果看到电压不断跳动就说明这个引脚大概率就是TX。拿到TX之后还要找到RX和GND。GND通常和外壳、屏蔽罩、地平面相连用万用表蜂鸣档打一下就能确认。RX一般就在TX附近是相邻几个点中间的那个。接USB-TTL模块时模块的RX接板子的TX模块的TX接板子的RX如果接反了只会收到乱码调换两根线就行。注意电平匹配大部分Arm9板子是3.3V TTLUSB-TTL模块也设成3.3V别用5V去怼板子上的芯片不一定能扛。2.2 一份自带引导日志的逐行解读接好串口上电终端里刷出一片日志。原始日志稍微做了脱敏整理大概长这样[BOOT] 2023-05-26 build [WDT] reset reason: 0x1 [CLK] CPU532MHz, AHB133MHz [DRAM] size64MB [FLASH] probe SPI-NOR id0xEF4018 size8MB [NAND] probe K9F1G08U0C size128MB [BOOT] boot_sel: normal [ENV] load env 0x20000 ok [NAND] read 0x40000 0x200000 - 0x23000000 [CRC] check ok [JUMP] to 0x23000000这段日志信息量很大。逐行看[WDT] reset reason: 0x1这次启动是看门狗复位还是上电复位。如果是看门狗复位说明上一次启动可能因为固件问题导致系统没跑起来。[CLK] CPU532MHz, AHB133MHzCPU频率532MHz这在ARM926EJ-S里偏高一点说明SoC可能做了倍频配置。[DRAM] size64MBSDRAM是64MB结合K9F1G08的128MB NAND这是个很典型的容量组合。64MB的SDRAM意味着主镜像运行时会被放进32MB以下的地址空间通常SDRAM的起始地址是0x20000000、0x21000000或0x23000000。[FLASH] probe SPI-NOR id0xEF4018 size8MBSPI NOR是8MBID是0xEF4018对应Winbond的W25Q64。注意这里的用途还没写。[NAND] probe K9F1G08U0C size128MBNAND是128MB。[BOOT] boot_sel: normal说明有个硬件引脚或拨码开关在决定启动模式normal对应正常启动。[NAND] read 0x40000 0x200000 - 0x23000000这是整份日志里最关键的一行。它说明主镜像在NAND的偏移0x40000位置读取长度为0x2000002MB被加载到SDRAM地址0x23000000。这意味着主镜像本身是2MB大小的裸机固件。[JUMP] to 0x23000000最后跳转到0x23000000执行不做MMU映射直接运行物理地址。这很裸机——没有Linux那样的虚拟地址转换直接物理地址跑。这份日志直接告诉我bootloader是个自定义的裸机loader不是U-Boot。它不打印U-Boot的版本信息也不提供Linux启动参数功能很单一。但“单一”不等于“没用”只要它保留着内存读写和数据传输命令就足够完成镜像提取。2.3 bootloader读NAND的地址与长度主镜像存放位置的第一个证据日志里已经给出了两个关键参数NAND偏移0x40000读取长度0x200000。这两组数据就成了后续所有操作的地图。顺着这个思路可以推断出整个NAND的初步分区布局起始偏移结束偏移大小用途推测0x0000000x01FFFF128KBbootloader代码区0x0200000x03FFFF128KB环境变量/配置区0x0400000x23FFFF2MB主业务镜像0x2400000x??剩余参数区/升级备区注意bootloader实际读NAND时是把自己知道的分区结构写死在代码里的日志里的0x40000和0x200000就是这种硬编码。黑盒逆向最怕没有地图而bootloader的日志天然就是地图。只要这个地址是真的那我后续做nand read 0x23000000 0x40000 0x200000再把内存数据捞出来就一定能拿到主镜像的原始二进制。这里也顺带解释了为什么“黑盒分析bootloader”是整个逆向的前置步骤。如果没有bootloader的日志和信息我连主镜像放哪、多大、加载到哪个地址都不知道还得靠binwalk去NAND裸转储里盲目扫描效率低得多。3. 黑盒撬开bootloader命令枚举与恢复模式3.1 中断启动流程的按键时机看完日志发现bootloader在跳转前没有打印“Press any key to stop autoboot”这类提示说明默认的CLI入口不是靠按键。但很多自研loader会隐藏一个菜单在[BOOT] boot_sel: normal之后、[NAND] read之前的那一小段窗口里如果串口收到特定字符就进入命令模式而不是正常启动。我试了几次按住空格、回车、字母m都没有反应。后来重新翻日志注意到引脚电位会影响boot_sel。板子上有两个测试点丝印分别是TP_BOOT0和TP_BOOT1。我用镊子把TP_BOOT1短接到地再上电日志就变了[BOOT] boot_sel: recovery Recovery CLI (type help for commands) 原来门铃的recovery模式入口不是按键而是上电时拉低某个引脚的电平。这是很多IPC、门铃产品会留的“研发后门”目的就是产线烧录和售后恢复。知道这个规律之后所有能枚举的输入手段串口字符、GPIO电平、按键组合都值得试一遍这比盲目猜测命令高效得多。3.2 读懂命令帮助读命令优先写命令一律慎用进入CLI后第一件事不是乱敲命令而是先输入help看支持哪些功能。界面不完整列举但有代表性的命令大概是这些help reset nand read dst off size nand write src off size md addr len mw addr data go addr loady dst loadb dst tftp dst path flinfo看到这组命令心里的石头落了地。这个bootloader虽然没挂U-Boot的名字但功能明显是从U-Boot那套指令风格简化来的。nand read、md、mw、go、loady、tftp这些命令基本上能覆盖完整的内存查看和镜像传输流程。这里必须强调一个基本原则在没有任何完整备份的情况下写命令一律慎用。nand write、mw、erase这类命令只要敲错一个地址可能直接把NAND里的环境变量甚至bootloader擦掉整机变砖。我当时给自己定的规矩就是先用md和nand read把所有能读的都读出来确认方案再做任何写操作。实际提取过程也确实没有执行过写命令。3.3 隐藏的恢复入口与GPIO组合TP_BOOT1拉低触发recovery模式这招是从日志里猜出来的。还有一个更暴力的隐藏恢复入口就是在SPI NOR上做文章。排查到SPI NOR里有第一级启动代码后我当时没有动它但我知道一个常见坑如果固件升级失败导致NAND数据全乱厂商会设计一个“从SPI NOR启动后通过UART重新烧写NAND”的恢复流程。这类流程往往由某个GPIO或拨码开关触发而且会打印类似“Wait for image via UART...”的提示。黑盒状态下这些隐藏入口是可遇不可求的。我的经验是先把明显标记的测试点全部摸一遍再看boot_sel相关的电位最后才考虑去操作物理存储芯片。门铃的按键也可能参与长按门铃按键再上电、按住Reset再插网线这些组合都可能改变启动分支。记录每一次尝试的日志变化比瞎试有价值得多。比如我当时就发现长按光感按钮再上电启动日志里会多出来一行[BOOT] factory default虽然没进CLI但也验证了“按键影响启动模式”的判断。3.4 Bootloader与常见MCU引导流程的对照顺带聊聊AB分区分析这个Arm9门铃的bootloader时我一直拿它和更常见的MCU bootloader做对比。STM32F1这种芯片出厂自带ROM bootloader可以走USART、USB或者CAN下载程序用户bootloader通常放在Flash起始位置启动后跳到App区。MCU的启动逻辑简单直接因为MCU不需要初始化SDRAM。Arm9 SoC则完全不同。它的bootloader要负责SDRAM初始化、时钟树配置、NAND控制器初始化然后才能把主镜像搬到SDRAM里跳转执行。这也是为什么Arm9设备的bootloader代码量比STM32大得多而且普遍会提供DDR init、NAND read这类底层命令。对比一下常见的启动流程阶段MCUSTM32F1Arm9本门铃上电芯片ROM直接读FlashSoC ROM读SPI NOR/NAND里的bootloader硬件初始化配置时钟、外设配置时钟、SDRAM、NAND控制器镜像加载从Flash复制到RAM或原地执行从NAND读到SDRAM指定地址跳转设置SP和PC设置SP、PC跳到SDRAM物理地址搜索里有人提到“bootloader双分区ab分区”这在MCU OTA方案里很常见分区A存当前版本分区B存新版本失败就回滚到A。Arm9门铃也有类似思想但从日志里看它没有做完整的双A/B分量只是在NAND尾部留了一块“升级备区”。这说明产品当时OTA策略比较简单要么在线升级主镜像要么靠recovery模式回滚。分析的时候不要想当然认为一定有A/B分区拿到日志后再判断。4. 主镜像提取内存转储、串口传输与网络通道4.1 方案A用内存显示命令逐段抄写最直接的办法是先把主镜像从NAND读到SDRAM再用mdmemory display命令把SDRAM里的内容一段段打印出来用脚本解析成二进制。这个方案最稳因为它只用到了最基本的内存读取命令任何一个带脚本的人都能做。操作流程是nand read 0x23000000 0x40000 0x200000 md 0x23000000 0x100md 0x23000000 0x100会从0x23000000开始打印256字节的内存内容。打印出来的格式通常是每行16字节的十六进制我把串口捕获下来的输出存成文本再用脚本过滤每行的hex段拼成bin文件。这个方案的代价很直白2MB的镜像每一行才16字节总共需要打印13万行。即使脚本处理没问题串口115200波特率下光打印就可能要半小时以上而且解析过程中只要有一行漏字节文件就废了。所以这个方案它是个保底方案不是首选方案。但它有个巨大优势——不依赖bootloader是否支持loady或tftp只要md存在就能干。实际操作中我会先转储前256字节也就是ARM向量表所在的位置立刻用010 Editor看一眼。如果出现EA000000这类ARM分支指令或者E59FF018这类PC加载指令说明转储方向和地址都没问题再继续全量转储。4.2 方案B把NAND分区装载到SDRAM后全量导出单纯用md打印太笨更聪明的做法是把NAND分区先完整读到SDRAM然后用bootloader的传输命令把内存内容拉出来。这本质上还是“装载到内存然后导出内存”但比逐行抄写高效得多。具体命令nand read 0x23000000 0x40000 0x200000把2MB主镜像读到0x23000000紧接着就可以用md按4KB一块去查看开头和结尾确认数据不是全FF也不是全00。里面有正常的代码和字符串后再考虑用接下来的协议传输方案。需要确认一个细节Bootloader里的nand read是否已经处理了ECC和坏块映射。绝大多数情况下bootloader读NAND是经过完整底层驱动的读出来的就是真实业务数据不需要再关心ECC问题。这一点和直接用编程器抓NAND裸镜像完全不同也是为什么“bootloader辅助读取”比“编程器硬读NAND”更可靠。4.3 方案C利用YMODEM批量接收省去手抄为了把2MB数据从板子传到电脑最顺手的命令是loady。这个命令会在串口启动YMODEM接收协议等待电脑端发送文件再把收到的数据写入指定的内存地址。配合脚本流程loady 0x23000000电脑端打开minicom或者Tera Term按CtrlA进入发送菜单选择ZModem或YModem选中本地保存的app_blank.bin这类占位文件发送。为啥要传一个占位文件因为loady只是负责把数据收进内存它并不会直接保存成文件发送哪个文件的内容只决定内存里的数据长什么样。更常规的做法是先在电脑端准备好一个大小和主镜像一致的空白文件用loady把它传进去然后电脑端再用md把这2MB内存内容通过脚本读出来。这个方案依然需要md转储但它让转储流程可控避免了逐行手打时不知道当前到哪个地址的问题。如果bootloader支持loadb二进制模式那区别也不大协议从YMODEM换成XMODEM/Binary速度更慢但兼容性更广。实测下来YMODEM在115200波特率下传输2MB大约要3分钟是可以接受的时间。这类命令非常常见自定义bootloader只要带升级功能几乎都会留loady或loadx作为接收通道。4.4 方案DTFTP/网络导出的先决条件与使用时机如果门铃带以太网口而bootloader又集成了网络功能那TFTP就是最理想的导出通道。这个门铃有网口bootloader的CLI里也确实有tftp命令。我先用网线把门铃和电脑连到同一个交换机电脑上跑一个TFTP服务端然后执行tftp 0x23000000 app.bin这里有一个坑必须提醒bootloader的tftp通常是从tftp服务器下载文件到指定内存而不是把内存传回服务器。所以这个命令真正的用途是往门铃内存里灌数据而不是导出。如果我需要的是“把2MB主镜像从门铃里传出来”用tftp的默认方向并不合适。但换个思路就通了我其实不需要主镜像从门铃端主动上传我可以用md继续把2MB内存打印出来恢复脚本解析或者更聪明一点在电脑上把主镜像的占位文件先tftp到门铃内存再用它和md做差分。更直接的用法是先通过nand read把NAND数据读进内存电脑端再用YMODEM的下行方向接收。然而很多自定义bootloader并没有“下行方向”导出命令。所以我的判断是TFTP在这个项目里是补充通道真正适合大批量导出的是“内存打印脚本解析”以及“YMODEM/串口抓包”。4.5 分片转储的合并、对齐与CRC校验最后一步是把多个分片合成一个完整镜像。我按前面推断的分区表分别读取了几个区域0x000000~0x01FFFFbootloader区128KB0x020000~0x03FFFF环境变量区128KB0x040000~0x23FFFF主镜像区2MB0x240000~结束参数区若干合并时要特别注意对齐。NAND的页大小如果是2KB那么转储的每个分片尺寸最好也是2KB的整数倍。如果某个分片长度不是页对齐截图到bin文件后很容易出现中间多出几个字节、导致后面所有偏移错位的问题。合并完成后再用binwalk扫一遍关键字符串比如“APP”“VERSION”“CRC”确认没有明显的分区错位。确认完毕后我还做了一次冗余校验把完整镜像的SHA256存下来之后所有逆向分析基于这个文件做避免每次重新提取出来不同的结果。这一步虽然不起眼但对后续反复反汇编、和厂商升级包做对比非常重要。5. 镜像落地后的第一轮逆向裸机启动流程与结构识别5.1 从向量表读出加载地址拿到2MB主镜像后我第一个打开的文件就是偏移0x000000处。ARM926EJ-S的向量表固定在前32字节复位、未定义指令、SWI、预取中止、数据中止、IRQ、FIQ都在里面。打开十六进制编辑器的反汇编视图会看到这样典型的指令序列00000000 e59ff018 ldr pc, [pc, #24] 00000004 e59ff018 ldr pc, [pc, #24] 00000008 e59ff018 ldr pc, [pc, #24] ... 00000018 deadbeef 23000100 ...第一条ldr pc, [pc, #24]会从向量表后面的“跳转目标表”里取一个绝对地址作为PC的新值。后面的deadbeef只是占位再往下四个字节就能看到真正的复位入口地址。我当时在文件里看到了23000100也就是说编译器链接时把代码的加载地址定在了0x23000000复位入口在0x23000100。这个结果和bootloader日志里的[JUMP] to 0x23000000完全吻合间接验证了提取镜像的完整性。很多入门者看到ldr pc就迷茫其实记住一句话就行ARM向量表本质是一张“跳转方法表”前8条指令每条都对应一个异常入口不管它是用B指令做相对跳转还是用LDR PC做绝对跳转最终都会把控制流送到异常处理函数。看懂这张表加载地址就出来了镜像的链接基址也就确定了。这是所有裸机固件逆向的地基。5.2 裸机、RTOS还是Linux熵与字符串给出答案从标题就能猜到这是裸机门铃但分析镜像时要给出证据不能靠猜。我的判断顺序是从宏观到微观先看有没有Linux特征如果镜像里有vmlinux、uncompressing...、Booting Linux这类字符串同时在入口附近有MMU和页表配置那基本就是Linux内核。再看有没有RTOS特征搜FreeRTOS、TaskCreate、tx_thread。很多轻量设备会直接带FreeRTOS或ThreadX。最后才是裸机判断没有任务调度器的初始化痕迹没有OS抽象层main函数之后直接进超级循环。我当时用Ghidra把镜像进到0x23000000基址然后在字符串窗口里搜了FreeRTOS、linux、vTask一个都没有。倒是搜到了大量网络协议栈相关的字符串比如lwip、pbuf、tcp_in还有一些音频编码相关的字符串。这说明它用的是裸机环境下移植的lwIP协议栈而不是Linux或RTOS里的协议栈。5.3 提取后的固件里能看到什么lwIP、PID、音频与视频任务裸机lwIP移植是很多嵌入式项目里的经典问题它和“lwip裸机移植模型”这个词条完全对应。主镜像里网卡能够正常发包收包是因为业务代码在裸机环境下直接跑lwIP的tcpip线程再调用网卡驱动的轮询或中断接口。门铃业务里常见的RTSP流媒体协议、P2P穿透、对讲信令基本都能在tcp_connect、udp_send这些lwIP函数往上追。搜索字符串时我还看到了一组疑似PID控制器的调试信息pid_err、kp、ki、kd。乍一看在门铃里做PID有点奇怪但仔细想门铃的自动白平衡、补光灯亮度、麦克风增益都在用PID或类似控制环。门铃这类设备不是简单触发式需要对环境光、声音做连续调节pwm周期和采样周期之间做反馈控制和“裸机pid控制”的场景高度吻合。镜像里同时还存在另一个有意思的点音频和视频任务的分工。虽然整个镜像只有2MB代码里却能看到两个明确的任务上下文一个负责采集编码一个负责网络传输。任务调度是自己实现的调度器没有商业RTOS的壳。所以这个门铃不是纯“死循环裸机”而是没有内核的超级循环调度器裸机。5.4 反向还原启动序列bootloader如何把主镜像交给业务代码结合bootloader日志和镜像反汇编我把启动序列完整还原了SoC上电内部ROM先检查SPI NOR把第一级bootloader加载到内部SRAM运行。第一级bootloader初始化SDRAM和NAND控制器从NAND的0x40000读2MB主镜像到SDRAM 0x23000000。跳转到0x23000000主镜像入口先设置栈指针初始SP值来自向量表前4字节再禁用中断、切换CPU模式到SVC。主镜像复制data段、清零bss段初始化外设时钟、GPIO、串口、以太网PHY。进入lwIP初始化创建网络任务和音视频任务进入超级循环调度。这个还原过程对后续修改固件非常重要。比如我想让门铃连接自己的服务器不需要动bootloader只需要改主镜像里的服务器地址变量和密钥就行。想改主镜像就必须先知道它启动后哪些外设已经被bootloader初始化过、哪些要自己重新初始化。从反汇编上看主镜像入口的序列很规范没有依赖bootloader留下的临时环境这也是这个裸机方案做得比较稳的地方。6. 复盘黑盒逆向最容易翻车的几个环节6.1 没有全量备份前绝不执行写入类命令这次实操我一直在强调一个原则读取命令随便用写入命令一律不给权限。黑盒逆向最怕的是“我敲了个nand write然后整台机器就没反应了”。别以为这种错误很初级实际操作时人很容易毛躁特别是一遍遍尝试按键进入recovery模式都没成功的时候就想着用写命令去碰运气。那不是逆向那是破坏。正确的顺序是先把所有分区的读取都做完存到电脑上。bootloader区、环境变量区、主镜像区、参数区能读多少读多少。这些数据不仅仅是用来分析也是后面如果误操作把设备刷死之后的“安全网”。在没有任何备份的情况下任何写操作都是在赌命。6.2 NAND的坏块与ECC读出来的数据不一定是明文固件NAND和SPI NOR不一样它有OOB区每页多出的额外空间、有坏块标记、有ECC校验字节。bootloader里读取NAND时底层驱动会自动跳过坏块、校验ECC返回给上层的是经过处理的“逻辑数据”所以我们用nand read读出来的主镜像通常是干净的。但如果你哪天决定不用bootloader直接把NAND芯片焊下来放编程器里读读出来的就不一定能用了。比如某个页面里的数据可能因为物理坏块根本没存或者每一页末尾多出了一些ECC校验字节。这时候就需要根据具体NAND型号的page size、oob layout手动做一次“去ECC化”过程非常繁琐。所以我建议能用bootloader读就别急着用编程器硬读。6.3 示波器、逻辑分析仪与胶带工具清单这次逆向用到的工具都是很基础的东西列出来给准备入门的同学参考USB转TTL模块3.3V电平配杜邦线就够。逻辑分析仪8通道、24MHz采样率足够解码UART和SPI还能抓NAND读写时序。万用表量通断、量电平必不可少。镊子短接测试点、调整电阻。热风枪和洗板水拆散热片、清丝印。编程器备用方案万一bootloader锁死就直接读SPI NOR。这里多提一句网上关于“jlink正版 bootloader sn”的讨论很多场景是拿J-Link去给单片机刷bootloader时发现SN被锁或克隆固件不稳定。J-Link本身是正经调试器但如果要处理Arm9这类复杂SoC的调试口又遇到JTAG被复用成GPIOJ-Link能发挥的作用就很有限。拿J-Link之前先确认目标的JTAG到底通不通不然光接线就能耗掉半天。6.4 如果Bootloader完全锁死怎么办如果遇到的设备连recovery模式都进不去串口也没有CLI那就只能考虑最后这几招。第一招用编程器把SPI NOR整个备份下来再从SPI NOR里的第一级bootloader分析启动逻辑看看有没有强制进入下载模式的代码路径。第二招查SoC的Boot Mode引脚。很多Arm9 SoC支持直接从UART或USB启动ROM loader类似于MCU的ISP模式。把Boot引脚拨到对应模式上电后SoC内部的BROM会通过串口接收固件。第三招如果系统的固件没有签名校验还可以自己构造一个最小镜像通过串口灌到SDRAM里执行然后借助这个自制的“临时bootloader”去读NAND或Flash。自制临时bootloader是我个人最推荐的一招但对动手能力要求高。你需要会写ARM926EJ-S的启动代码至少能把串口、SDRAM、NAND驱动起来然后利用自编代码把Flash内容通过串口送出来。这相当于在逆向过程中自己当了一把SoC厂商的工程师。做完这一步你对该平台启动流程的理解会比读十篇文档都深。最后再分享一个小技巧整个黑盒逆向过程中本子比电脑还重要。每次切换启动模式、改GPIO电平、敲命令我都会在纸上记录当时的日志特征和结果。表面看很多尝试是“瞎试”但其实每次记录都是在排除一个变量。等记录足够多你会发现bootloader的启动逻辑完全可以从这些离散的现象里逆推出来。黑盒逆向拼的不是运气而是这种一点一点把不确定性消掉的过程。