几年前我就开始 做 BIOS rootkits 方面的东西(在 UEFI 成为主流之前)。我知道在初始化启动过程的后期阶段,大多数硬件都有一个 BIOS 类型设置 , 我的主要关注 GPU 和硬盘。 本文 我 所做的是我曾经 在 spritesmods 上看到的 一些 东西。
我发现一个老版本的西部数码硬盘驱动器很适合现在的研究,所以我把控制器卸下来。
S pritesmods 上的那个家伙把固件的闪存芯片弄下来然后把里面的内容全部拷贝出来了,这里唯一的问题是红圈中闪存芯片这个地方。
红圈中是 Marvell 88i8846-TFJ2 ARM 处理器,有内部的闪存。我不想为了访问内存而手工把整个CPU弄下来,所以我决定使用JTAG方法。
JTAG 的头是很知名的,虽然可以被颠倒(在我的案例中头部的第一个 pin 点用 ’ 1 ’ 标识)。 Pins 6 到 11 是我们需要的 JTAG 金属圈。
正如你可以看到的,我们决定不焊接 pins 点。主要原因是其有点生锈并且离得太近,所以很容易把单板弄坏掉。作为替代我决定使用测试点,这可以用万用表的“连续”模式实现。
通过设置万用表的这个模式能够得到两点之间的电阻,‘ 1 ’代表完全绝缘(这意味着这两点是不通电的),‘ 0.01 ’代表互通性很好。当电阻很低的时候,仪表会发出声音(难以忍受的噪音),所以我们只需要通过声音来判断两点之间是否连接即可。
将硬盘驱动器断开,只要把你要测的两点用万用表测一下即可。然后移动其中一段到其他点上,直到听到蜂鸣声为止。在我的单板上你会发现从头部的 pin 点到测试点有可见的数据行,这给了你很好的入口(前提是你的视力很好)。
我不想将硬盘插入到我的电脑电源上以免出现一些错误,并且我的电脑在房间的对面,所以我也不想搞个 10 米长的 SATA 数据线。下面是我的解决方案:
如果你有备用电源,在没有连接到主板的情况下,你可以将 ATX 的第三个和第四个 pin 点短路来将它开启。我的备用电源很老并且少一个风扇,所以我很惊讶居然没有短路和起火(我的房子的所有设备都在一个断路器上,所以短路的话我得花一个小时重启所有设备)。
我使用 5 美元的 SATA 来连接 USB ,这是一个很好的方法。右边的红色单板使用了一个 30 美元的 TIAO USB 多协议适配器,基于 FT2232H 同时以可以用 SPI 和 JTAG 。
在这里我有一个愚蠢复杂的设置,由于我的 windows 机器放在房间的另一边, iMac 为 JTAG 软件运行了一个 linux 虚拟机( FTDI 驱动和 OpenOCD )因为这在 windows 和 OSX 上安装很麻烦。 W indows 系统运行 IDA 来逆向和调试(当我开始动态调试时我打算在本地网络上用 IDA 连到 OpenOCD 的 GDB 服务器上去)。
现在所有要连接的东西都已经准备好了,给硬盘插上电源然后输入下面的命令:
openocd -f interface/<your interface here>.cfg -f target/test.cfg
需要先为你的硬盘控制 CPU 配置 test.cfg 文件,对于大部分 marvell 的 CPU 这个配置都有效。我不知道 khz 适配器行不行,所以我将 mine 设置成 100 (由于这个值低于它正常的工作值)。
如果一切正常,你会看到这些信息。现在你可以 telnet 到 4444 端口然后发送命令了。
硬盘控制器用的 Marvell 芯片没有公开的文档,所以如果你想了解相关信息比如内存布局,你需要在 NDA 上注册然后付费。作为替代,我想要做的是尽可能在固件上和通过探测电路找到更多的信息。
如果你们没有办法焊下闪存并导出信息要得到固件并不容易,所以我们将会在 boot 进程下工作。 大部分 ARM CPU 的起始执行地址是 0xFFFF0000 ,这是复位向量。如果我们从这个地址导出 65536 字节,我们将会找到启动代码,这是一个很好的入口点。
为了导出内存,我们首先要停止 CPU ,可以使用命令 ” reset halt ” (这是必要的因为我们没有办法在任何一个阶段都能停下来)。如果这个命令没有效果可能是 JTAG 的 RST pin 点没有连接,你需要断开并重连硬盘的电源然后快速的修复这个 JTAG 问题。内存导出用以下命令
“dump_image <file name> <address> <size>”
当我们将导出的镜像文件反汇编时,它是一个很小的只有 4KB 的启动代码,这段代码能够引导剩下的固件。
我发现 JTAG 的重置点不是真的系统重置( SRST )只是一个 TAP 重置( TRST ),这对调试不是很有用。这里是对系统重置信号的添加(这样允许重置停止命令在重置向量上中断下来,在任何指令执行之前)。
在我的例子中没有 SRST 行的测试点,但是在序列标签下面有暴露出非常小的镀铜位,这个可以用来连接 CPU 的 SRST pin 点。
红迪网上的 Ceriand 指出 JTAG 头跟 MICTOR 连接器很像(通常是 38 或 40 个 pin 点)。所以如果你不想做任何焊接工作你可以找一个 MICTOR 连接器和数据线。
同时我发现老版本的 PSU 硬件无法在低电压下使用(内部的组件可能会爆炸),所以我推荐为 Molex 电源适配器买一个好的 AC (不要买便宜的,一天就坏掉了)。
最后,由于 JTAG 头有一个 RTCK 连接器,你需要将 opencd 的 adapter_khz 配置为 0. 这样 JTAG 能够使用自适应时钟,这样你能够防止所有超时错误。
通过一些逆向我确定第二部分的引导代码无法在正常的启动中使用。执行的时候在一个端口上等待一些数据(最有可能是串行端口),然后采取相关的行动。如果没有数据到来,这段代码将进入一个死循环驱动将永远不会启动。
PCB 周围的端口和测试点映射到 CPU 的 0x1C00A000-0x1C00AFFF 范围上。现在,由于我没有钱买示波器或者像样的逻辑分析仪。我不得不通过这些端口映射,这样会使事情变得更容易。
所有的这些代码都是为了基于一些值读取进入系统特殊模式的开关:
4- 不确定,当它为了一些端口的值进行无限循环。我猜这些值是允许开发者读取 / 写入 / 擦除处理器内部的闪存。
3- 跳转到 R4 的地址(我的例子中这个值为 0 ,但可能是设计的原因)
6- 串行端口的串行控制台可以用 ASCII 字节( r , w , j , h ),可以让开发者发送读取, 写入,跳转和停止命令。
我对端口如何映射到内存不太熟悉, 0x1C00A030 总是为 0 , 0x1C00A03A 总是为 0xFFFF (我认为这一个是低电压常数一个是高电压常数)。
有趣的是在“ cmp R1 , #R3 ”上设置断点将 R3 设置成 3 ,代码会跳到地址 0 然后正常启动(这就是我认为为什么 0 不代表未初始化)。让我们看看地址 0 有什么。
地址 0 通常是 RAM ,但已经有一些有效的代码了,所以很可能是在启动过程中 CPU 临时映射地址 0 到一些内部只读存储器。这是标准的 ARM IVT ,你可以在任何 ARM 设备的启动地址看到;使我想到 CPU 检测到 JTAG 附加进来时 0xFFFF0000 上的启动只是被执行了。在我买到逻辑分析仪并找到哪个端口允许我控制启动模式跳转之前,唯一通过 JTAG 正常启动硬件的办法是在校验之前执行“重置”然后将 R1 设置成 3.
在这个例子中启动代码是节与节之间差距最大的地方,后续有机会我会将它进行映射,导出,并逆向。
*参考来源: malwaretech.com ,FB小编老王隔壁的白帽子翻译,转载请注明来自FreeBuf黑客与极客(FreeBuf.COM)