硬盘固件程序嵌入代码,泄密是迟早的事情,为什么?数据恢复专家告诉你,原理是利用硬盘的一些智能机制,在某个位置嵌入一些信息(比如:登录信息),然后操作系统验证用户登陆时,会不自主地读取黑客预留下的用户名和密码。
简介
硬盘:如果你在看这篇文章,我肯定你起码用过一两个硬盘。硬盘很简单,基本就是一些512字节的扇区,由递增的数字标明地址,称之为 LBA,也就是“逻辑块寻址”。电脑可以向连接的硬盘的扇区中读写数据。通常会有个文件系统把这些扇区抽象成文件或文件夹。
如果你从这个幼稚的角度看硬盘,你会认为硬件应该也很简单:你需要的就是个能连接SATA接口的东西,然后可以定位读写头,从盘片上读写数据。但是可能不止这么简单:硬盘不是还有处理坏块、S.M.A.R.T.属性的功能么?不是还有什么缓存需要管理的么?
以上这些意味着硬盘中有些智能的东西,有智能就意味着可以黑掉它。我就喜欢可以黑的东西,于是我决定要看看硬盘是如何在非机械层面上工作的。这种研究以前在很多硬件上做过:从笔记本的PCI扩展模块到嵌入式控制器,甚至是苹果的键盘。通常这些研究都是为了证明这些硬盘可以被破解,导致其受到软件的影响,于是我决定达到同样的目标:我要在这次破解中让硬盘绕过软件安全机构。
PCB上的部件
要想知道硬盘是否可以被破解,我需要更了解它们。如你们大多数一样,我也有一摞或坏或旧的硬盘来一看究竟:
当然了,我们都知道硬盘的机械结构应该是好用的,我对那些部分也不感兴趣。我的兴趣在于大多数硬盘背面都有的那一小块PCB板子,上面有SATA接口和电源接口。这种PCB看起来是这样的:
可以看见PCB上有四块芯片。接下来说说这些芯片:
这是一块DRAM(动态随机存储器)。这块很好处理,芯片手册很好找。这些芯片的容量一般在8MB到64MB之间,对应的就是硬盘标称的缓存容量。
这个是电机控制器。这不是个标准器件,数据手册不好找,但是这些控制器一般都有容易找的差不多的同系列产品。ST Smooth控制器大概是最常用的一种了;除了驱动电机,它还能进行电源整流,还带一些A/D变换通道。
这是一块串行闪存。这个也好处理,容量一般在64KB到256KB之间。看起来这个是用来存储硬盘控制器的启动程序。有些硬盘没有这个芯片,而是在控制器芯片内部有闪存来存储程序。
这些小东西不是芯片,而是压电震动传感器。当硬盘受到撞击时,它们可以把磁头移到安全的地方,但是更有可能它在某个地方标记一个值,表示你的保修无效,因为是你自己摔的硬盘。
这里才是奇迹将要发生的地方:硬盘控制器。多是由Marvell、ST或者其他的LSI公司制造。有些硬盘厂商自己做控制器:我见过三星和西数就有自己的控制芯片。因为其他的部分都很好处理,这一块才是我的兴趣所在。
不幸的是,这些芯片都没有文档。话说这些制造控制器的厂商不公开文档有些不厚道真是说轻了:他们甚至在自己的网站上都不提这些芯片!更不幸的是,整个互联网也帮不了我:搜这些芯片手册只能找到没有手册的手册网站,和卖芯片的中国厂商……
那么,没有最重要的芯片手册,就意味着我们的计划搁浅了么?
连接JTAG
幸运的是,总有些办法找到除了芯片手册以外的有用信息。我就搜到这么一个。
我找的是HDDGuru论坛上一个叫Dejan的人做的连接线。Dejan不知怎么把他硬盘控制器的内部闪存废掉了,然后想知道有没有办法,要么从外部闪存启动控制器,要么重写一下内部闪存。过了五天,没人回应他,但是这哥们很有创造力:他又发了个帖子说他找到了JTAG口的管脚。这真是个重大发现:JTAG接口可以用来控制控制器。你可以用它启动控制器、重启、修改内存、设置断点等等。然后Dejan发现了如何关掉控制器的启动ROM,找到了硬盘一个串口,然后试图恢复他的闪存ROM。后来他又提了一些关于更新闪存的过程,最后消失在茫茫人海中了。
这些都是有用的信息:至少我知道了西部数据的控制器是ARM内核的,有JTAG接口。这些硬盘通常有串口,虽然没有使用但是可以用来调试程序。有了这些,我应该有足够的信息可以开始破解了。
嗯,这些是我的准备工作:
那个红色的是一块FT2232H的小板,大概30欧元,很便宜,可以用来进行JTAG调试,串口,还有SPI通信。把它连到硬盘的JTAG口,还有串口上。硬盘直接连到我电脑主板的SATA口上,还有外部ATX电源。我用OpenOCD来驱动JTAG接口。
现在的问题是:这玩意真能工作么?Dejan用的是88i6745控制器的2.5” 250G硬盘,他检测到的是ARM9内核。我找的是88i6745控制器的3.5” 2TB硬盘,有不同的格式因素,而且有点新。幸运的是,OpenOCD可以自动检测JTAG连接的设备。如下所示:
这我就有点搞不懂了……我本来估计会有一个tap,就是单独的ARM内核……可这里竟然有三个tap……难道这个片子有三个ARM内核?
一番研究后,我发现这个芯片真的是有三个内核。两个是Feroceon的内核,是比较牛逼的类似ARM9的内核,还有一个是Crotex-M3内核,比较小,相比更像微控制器的核心。鼓捣了一阵(以及后来的研究)发现这些控制器各自有不同的功能:
现在从哪个核心开始破解呢?我的目标是通过使用修改的硬盘固件来影响系统的安全。最简单的方法,同时也可能是最难检测的方法就是直接修改数据。这种方法不需要修改磁盘上的数据,固件可以使自己隐身不可见。为此,我需要找到一个合适的核心来进行监听:我需要一个能在从硬盘到SATA线的传输过程中接触到数据的核心,同时可以被操纵在磁盘和SATA线缆之间修改数据。
现在,数据是如何从硬盘盘片上送到SATA借口上的呢?凭黑客的直觉我推测:
如果处理器工作在150MHz,使用标准的内存复制,它们就只能达到150*23/2=2.4Gbp的速率,而实际情况要比这个少很多。硬盘的速度是6Gbps,所以肯定有些加速硬件参与其中。最可能的加速硬件应该就是使用DMA(直接访问内存)。那就意味着数据直接从磁头读回来放进内存,没有处理器的参与。SATA口也是一样:处理器只指明数据在哪里,DMA会直接从内存中读数据。
如果是这样的话,DMA引擎指向的内存会在哪呢?硬盘的缓存是个好地方:数据从磁盘读出来总是要放进缓存的,所以当读取磁盘的时候马上去那里复制也就说的通了。我之前发现第二个Feroceon负责管理缓存;于是它就成了我的首选目标。
就这样,我推断数据通过DMA来读写,不需要任何CPU动作。现在的问题是:既然CPU不会在正常操作中接触数据,那么CPU能不能(非正常地)接触到数据呢?为了解答这个问题,我首先使用JTAG连接,用了一些反汇编,来看看Feroceon2号的内存。
如你所见,内存图有些零碎。RAM中有一些小块散落着,还有一些IO空间和IRQ空间,以及一块内部启动的ROM。还有一块64MB的数据段,我猜这个是用作缓存的DRAM。一起来看看是不是这样。首先,我把硬盘加载到我的电脑上,在硬盘上的一个文件里写入「Hello world」。现在看看我是否能从64MB的内存中找到这个字符串。
没错,找到了!看起来Feroceon2号可以读取缓存,并对这块64MB的DRAM进行了地址映射。
注入代码
当然了,如果我想要在缓存里修改数据,我可不能每次都完全扫描整个64MB的缓存:我需要知道缓存是如何工作的。为此,我需要进行反汇编并理解硬盘的固件,至少要明白缓存的函数。
对固件进行反汇编,可不是个简单的活。首先,代码混合了ARM和Thumb指令,如果你没有自动切换两种指令的反汇编器就很令人抓狂了。而且,没有那些能使反汇编更简单的信息了:一般程序都被写好了,当有东西出错总会弹出类似「Couldn’t open logfile!」的信息。这些信息对于了解代码功能有很大帮助。而这个固件,一条信息都没有:你得自己看代码来知道代码在做什么。代码库好像有点老,而且有些时候反汇编的感觉就像给代码加了很多特性,把所有事情都搞得更复杂。
当然,也有几件事使得反汇编相对简单些。首先呢,西部数据没有故意混淆代码:没有在指令中间用些跳转的招数。还有,因为JTAG接口的存在,你可以干预代码的执行,设置断点,或者直接修改,让你非常容易地知道程序在做什么。
我看了很久代码,试着去理解,有时候用调试器验证我猜的对不对,最后我找到了缓存系统的核心代码:在RAM中的一个表,我称之为「缓存描述符表」。
缓存描述表的每一项描述了缓存中的一个块。它包含了可能在缓存中的磁盘扇区的起始LBA、缓存中存有多少硬盘数据、一些标明了缓存项的状态标志符,还有一个标明了缓存数据在内存中未知的数。
现在,缓存描述符表的秘密还没有被揭开,我能否在数据送出SATA口之前截断磁盘读取码?为此,我需要在磁盘控制器上执行我自己的代码。不仅如此,我还需要确定代码能否在正确的时间运行:如果它修改缓存太早,数据还没进去;如果太晚的话,数据已经送到PC了。
我的方法是绑定在一个已存在的任务上。我破解的是Feroceon2号,这个CPU负责所有的SATA传送,所以肯定有个服务是负责设置SATA硬件去缓存中读取数据。如果我找到这个服务,我就可能在它之前运行我的代码。
在看了很多代码,设置了很多断点,修改了很多次之后,我最终找到了某个符合条件的服务。我通过连接让这个服务在执行前先运行我的代码。这是原来的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
000167BE ; r0 - slot in sata_req 000167BE sub_0_167BE: 000167BE PUSH {R4-R7,LR} 000167C0 MOVS R7, R0 000167C2 LSLS R1, R0, # 4 000167C4 LDR R0, =sata_req 000167C6 SUB SP, SP, # 0x14 000167C8 ADDS R6, R1, R0 000167CA LDRB R1, [R6,# 0xD ] 000167CC LDR R2, =stru_0_40028DC
|