启动代码貌似只是分布在不同的内存地址,但没有什么简单的办法能够将它们全部导出,所以我决定从内存的0×00000000开始导出区块然后所有的都来引用这个区块的外部地址(建立一个基本的代码地图)。
虽然硬盘模型之间的确其地址不同,但是我的布局可以给你一个好的思路。
在附近看了一会之后,我发现它会从一些地方读取代码到内存中,所以我猜这里是闪存。
闪存是从一个跨越0到0×120的表开始(起始的第一个块),每个表项32字节并且使用相同的格式(参考镜像)。每个区块都有1到n的id,第一个块是个例外值总为0x5A。
0x5A处的区块是引导程序,它从剩下的闪存中读取并解压代码,我不确定,但是却反证了一个问题。 我想在入口点断下引导程序,听起来很容易,但并非如此。
1.在引导进程的一些点上我的硬件断点没有生效,有可能是处理器重写了寄存器,或者禁用了它们;但我还是没有找到问题。这意味着我无法在引导程序入口点处下断点(0×19000)。
2.复杂加载和执行引导程序的代码在ROM中,所以我没有办法下软件断点。
3.观测点好像也没有生效,甚至在断点停止工作之前我没有办法让代码段在任何内存上。
4.大部分不在ROM中的代码都是用分页技术,当他们需要的时候,会在内存中读取特定区域的代码,然后在他们没有时进行替换,防止使用软件断点。
总之情况比我想象的困难的多所以我决定买一个扩展到EEPROM芯片驱动,来替代在MCU中重新构建。 通过这种方法我可以用EEPROM程序往闪存芯片里面写入软件断点。
硬件和固件使得这个磁盘相当统一,除了固件现在存储在一个256k的SPI闪存芯片中,来替代存在MCU内存中。我做的第一件事是用万用表来查看PCB的顶端是否有地方连接到闪存芯片(所以我们有从磁盘里把PCB弄下来)。
WP#和HOLD#都存在VCC中,所以这不是什么问题,剩下需要的pin点需要提出到E11周围的测试点。 我尝试连接我的TUMPA的SPI接口到测试点上并使用通信软件,但是我没有办法检测到芯片。 我不确定是不是我的TUMPA没有电路编程的能力,或者数据线上有其他填充物。
如果其他方法都无效的话,我会试着找一个SOIC芯片,一个好的EEPROM编程器和焊台。同时我会尝试找到是什么原因使我的断点停止工作并修复它。
尝试才会有新的发现
这个周末我有了大的突破,这使我否定了我之前大部分的研究。我知道其实“不要重复造轮子”并不总是最好的选择,特别是涉及到信任其他人的研究结论时。
一个主要的目标是在能够物理访问的情况下,找到快速简单的方法对硬盘进行重新编程。在spritesmods博客上,他将闪存芯片从PCB上弄下来并将它附在一些veroboard上,所以他能够在硬盘和闪存编程器之间进行数据交换。显然这对我不是特别有用,因为我不得不一直断开和重连闪存芯片,对于快速感染磁盘特别不切实际。
我之前就知道,在有管理员或root账号的情况下从操作系统内部拷出WD硬盘固件是有可能的。硬盘必须运行以便能够接收SATA命令,并且必须重启来加载新的固件。如果新的固件有错误磁盘将无法启动,因此固件不能被修复(通常这样硬盘就变砖了)。事实上我是想破解固件,所以我只能用其他的方式来拷贝。
在线电路编程
当电路一直连接时,在电路中编程(ICP)方法能够在闪存芯片和其他组件中进行编程。在上一篇文章中我已经能够找到连接到闪存芯片的测试端口了,但没有办法进行编程。然而,在周末做了一些实验以后我已经能够使用ICP了。
问题
ICP最大的问题在于为了写入闪存芯片你需要打开它的电源,但是因为它与其他电路连接,所以最终会给其他芯片供电,这会在不同的总线上发送数据干扰你跟闪存之间的通信尝试。
为了防止SPI总线上的干扰,通常只需要按住复位按钮或者将复位信号接地即可,这会导致系统被重置并且无法启动(让你能够不间断的访问闪存总线)。
下面是我做的尝试:
1.直接用3.3v的电源供电
2.按住系统重置按钮的时候直接用3.3v的电源供电
3.通过插入HD接口直接给整个系统供电
4.按住系统重置按钮的时候插入HD接口直接给整个系统供电
由于所有东西都没有工作,我几乎都快放弃了(我的SPI编程器甚至都没法检测到闪存芯片),但当我在周六决定做最后一次尝试时,它就马上工作了(某种程度上)。
SPI编程器能够检测到闪存芯片,但是读出来的大部分数据都是错的。检查之后我发现VCC(电源)线从测试点松开挂在桌子上了。所以如果VCC线都没有连接并且系统都没有接进来,怎么会读取到闪存芯片呢?
通过更多的实验我发现电路是以这样一种方式设置的,一些被发送到MISO引脚的电压(大概它的1.2v)以某种方式反馈到VCC轨道然后启动芯片。我不是电路专家,这让我非常困惑,但是这告诉我在电路里读取芯片是可行的,我只想需要学习一些诀窍。
解决方法
通过大量的实验和一些失败之后,我发现按住系统的重置按钮(大部分ICP问题的解决方案)是唯一的问题。我已经让JTAG连接到系统这样会拉低重置引脚(保持重置),所以我就不需要自己手动保持重置了。在断开JTAG的SRST引脚之后,只要磁盘没有插入一切都工作的很好然后我直接给闪存芯片上电。看来这个系统是专门为ICP做的,不要一直重置,否则会出现一些问题。
这是上一篇文章图的修改版本
如果你有一个支持SPI编程的USB设备并且它是由“flashrom”支持的,就像我做的那样。你可以将它连接到电脑上,使用flashrom来读写和修改闪存。TUMPA是很好的单板,因为它有两个信道,所以你不需要为了使用SPI而断开JTAG。
不要重新造轮子
在我开始决定破解之前我就决定看一些别人的研究来找点灵感。很机智,对吗?然而事实证明我找到大部分研究资料都是错的或者不适合我的硬盘。
当连接到JTAG然后出现“reset halt”命令的时刻我记得很清楚,在代码运行之前系统暂停住了,引导程序从闪存启动执行的时候,我能够单步跟踪引导程序。
通过在闪存里写入我的代码折腾一番之后,我意识到在系统停下来之前它已经被读到内存并执行很久了。事实上0xFFFF0000处的代码只是调试控制台,只有下达停止命令的时候才会跳转到这,或者从闪存读取或执行代码失败的时候。此时,JTAG已经能够连接并且出现命令行了,整个内核都已经完成加载和初始化,这就是为什么我没有办法在引导程序上下任何断点的原因。
我可以写断点到闪存来产生一个异常让系统加载失败,然后删掉断点然后手动跳转到引导程序,但不知道什么原因第二次系统没有正确引导。这不是什么大问题,因为我能够一直调试引导程序直到某一个点(我写的代码只要在引导的早期执行,我就能够调试),但是后来我找到为什么第二次失败,我发现在启动阶段后期和内核初始化前期(我无法调试)有一个时间窗口。
我的主要目的是通过在目标电脑上执行程序和物理访问硬盘驱动的时候向固件插入代码,我已经实现了。现在来看看我能做什么。
如果你意识到原来攻击者那么容易就能够感染你的硬盘驱动固件,这里有两个快速的方法你可以做的。
1. 从主机通过SATA接口发送固件更新命令(需要root或者管理员权限)
2. 通过按下硬盘底部的测试点创建一个可以擦除固件的便携式SPI编程器。(只需要5秒)
就在5分钟之后,我发现了最后一点困惑:第二个CPU核心。当我意识到它有一个单一的硬编码定义tap(测试存取端口)的时候我去浏览OpenOCD配置,我决定将它注释掉并且让OpenOCD尝试自动发现tap。
自动探测发现相同的id有两个TAP,所以我假设有两个不同的核心,因此我需要修改我的配置。
下面是让两个核心都工作的配置设置:
经过几次小的调整,剩下需要做的就是运行OpenOCD,然后看看新的内核里有什么秘密。
当我通过IDA连接到JTAG,一切都清晰了。我可以看到第二个核心停在我写到闪存芯片的断点上了,这是负责加载和执行引导程序的核心,我之前看到的核心只是在一个循环中等待。 很明显引导代码肯定跟核心2的不同,因为另一个引导程序只是循环到稍微后面一点点时间。
总结:
所以正如你所见,只需要使用很少的经验我就能够JTAG一个WD硬盘,导出固件,甚至发现如何使用ICP读写闪存芯片。我肯定会多花一些时间来研究固件来看看它是怎么工作的,但是这些超出了这篇文章的范围,为了避免让大家觉得无聊,这是这个系列的最后一篇文章。如果我发现任何有趣的东西,我将会把我的发现发布到白皮书并上传一些演示视频。
我想继续发布硬件和软件方面的文章(以及一些教程),如果你有什么建议可以给我发邮件admin@malwaretech.com。
现在已经很清楚了,核心1引导只是调试/管理代码,同时核心2有一个完全独立的代码区域映射到相同的地址。核心2不单单从闪存加载引导代码,同时也负责大部分有趣的操作比如操作SATA请求和写入缓存描述符。