我正在我的屋子里寻找下一个逆向的潜在目标,很高兴地发现了我的XBox One控制器手柄:
我并没有真正玩过XBox,所以我认为拆掉该控制器手柄并查看可以从中提取什么样的信息可能很有趣。
0x01 分析目标
在评估嵌入式平台时,你可以做或尝试完成许多事情,在这篇文章中,奇热我想测试以下内容:
1. 可以从目标中提取固件吗?
2. 目标是否可以通过调试或检测的方式使我们能够了解其内部操作的更多信息?
3. 是否可以通过软件开发或硬件修改来修改或更改固件?
回答其中一些问题的第一步将是硬件拆解。
0x02 硬件拆解
打开外壳可以看到以下PCB:
请注意,由于主芯片上涂有环氧树脂,因此这里实际上看不到太多东西。对我们来说幸运的是,许多测试板都被标记了,但是被标记的板似乎是各种按钮按下的测试点,因此没有什么令人兴奋的地方。
电路板底部有一个标记为AK4961的IC,但这是音频编解码器芯片。数据表可以在这里找到。该芯片是有麦克风,耳机和扬声器放大器的低功耗24位立体声编解码器。
https://www.digikey.com/product-detail/en/akm-semiconductor-inc/AK4951EN/974-1064-1-ND/5180415
但是,如果我们仔细看,可以看到上面带有一些丝网印刷标签:
于是我们看到3V3,A13,A14,RES标记在丝网印刷。如果你已经阅读了我以前的有关路由器拆除和发现UART的文章,那么你可能已经对如何进行此操作有了一些想法。我们首先用万用表测量每个引脚的电压:
RES,A14或A13上没有波动或调制,因此肯定是其他原因,但那又是什么呢?假设其中一个标签是RES(可能表示系统重置),那么很有可能存在JTAG或SWD标头。
我们可以通过RES使用10k电阻将其拉低来测试该引脚是否真的将目标复位。如果你不熟悉这些类型的接头连接器或系统复位引脚的典型工作方式,它们通常为低电平有效,这意味着它们以高电平空闲并且必须拉低才能激活。因此,如果我们监控输出dmesg -w并使用10k电阻将该线切换为低电平,我们会看到什么?
[ 2108.588884] usb 1-6.4: new full-speed USB device number 10 using xhci_hcd[ 2108.691108] usb 1-6.4: New USB device found, idVendor=0e6f, idProduct=02a2, bcdDevice= 1.0f[ 2108.691113] usb 1-6.4: New USB device strings: Mfr=1, Product=2, SerialNumber=3[ 2108.691116] usb 1-6.4: Product: PDP Wired Controller for Xbox One - Crimson Red[ 2108.691119] usb 1-6.4: Manufacturer: Performance Designed Products[ 2108.691122] usb 1-6.4: SerialNumber: 0000AE38D7650465[ 2108.698675] input: Generic X-Box pad as /devices/pci0000:00/0000:00:14.0/usb1/1-6/1-6.4/1-6.4:1.0/input/input25[ 2131.403862] usb 1-6.4: USB disconnect, device number 10[ 2133.420350] usb 1-6.4: new full-speed USB device number 11 using xhci_hcd[ 2133.522469] usb 1-6.4: New USB device found, idVendor=0e6f, idProduct=02a2, bcdDevice= 1.0f[ 2133.522474] usb 1-6.4: New USB device strings: Mfr=1, Product=2, SerialNumber=3[ 2133.522478] usb 1-6.4: Product: PDP Wired Controller for Xbox One - Crimson Red[ 2133.522480] usb 1-6.4: Manufacturer: Performance Designed Products[ 2133.522483] usb 1-6.4: SerialNumber: 0000AE38D7650465[ 2133.530103] input: Generic X-Box pad as /devices/pci0000:00/0000:00:14.0/usb1/1-6/1-6.4/1-6.4:1.0/input/input26
这样做会使控制器复位,降低了1针,还剩下2针。
当查看这样的调试头时,通常的假设是它用于JTAG或某种其他形式的硬件级调试。然而,JTAG规格要求有至少4个引脚,TDO,TDI,TMS和TCK。我们的目标上只有两个,因此很有可能这是一个单线调试(SWD)端口。
0x03 SWD协议
SWD是用于ARM Cortex目标的通用调试接口。顾名思义,SWD仅需要一条数据线和一条时钟线,但是我们如何确定哪一条呢?在分析之前,我们应该多了解一些SWD的工作原理以及可以使用哪些工具与之交互。
首先,SWD与称为“调试访问端口”(DAP)的接口连接。DAP代理访问各种“访问端口”(AP),这些端口提供的功能包括典型的硬件调试,旧式JTAG内核以及其他高性能内存总线。从本文档中提取的下图是DAP和AP的结构的直观表示。
https://stm32duinoforum.com/forum/files/pdf/Serial_Wire_Debug.pdf
这些AP均由64个32位寄存器组成,其中一个寄存器用于标识AP的类型。AP的功能和特性决定了如何访问和利用这些寄存器。你可以在此处找到有关某些标准AP的有关这些交易的所有信息。ARM接口规范默认情况下定义了两个AP,它们分别是JTAG-AP和MEM-AP,MEM-AP还包括用于与其连接的组件的发现机制。
https://static.docs.arm.com/ihi0031/c/IHI0031C_debug_interface_as.pdf
如前所述,SWD是JTAG的替代品。使用SWD时,引脚数从4个减少到2个,并且提供了许多与JTAG相同的功能。但是,SWD的一个缺点是无法将设备链接在一起,这正是JTAG所允许的。SWD中使用的两个引脚如下:
SWD利用基于数据包的协议来读取和写入DAP / AP中的寄存器,它们包括以下阶段:
1. 主机到目标数据包请求;
2. bus转换;
3. 为目标主机确认响应;
4. 数据传输阶段。
数据包的结构可以在下图中看到,我也细分了表中的各个字段。
在停放位(从主机到目标)之后,有一个周转期,这基本上意味着目标现在将在同一条线上做出响应。
从极高的层次来看,SWD端口使用这些数据包与DAP交互,从而允许访问MEM-AP,后者提供对调试以及内存读取/写入功能的访问。出于本文的目的,我们将使用一个名为OpenOCD的工具来执行这些事务。接下来,我们将回顾如何编译和使用OpenOCD。
0x04 OpenOCD
安装依赖项:
sudo apt-get install build-essential libusb-1.0-0-dev automake libtool gdb-multiarch
安装存储库,配置和编译!
wrongbaud@115201:~/blog$ git clone https://git.code.sf.net/p/openocd/code openocd-codecd openocd-code./bootstrap./configuremake -j$(nproc)
使用OpenOCD编译后,我们可以尝试通过SWD调试此控制器。为了做到这一点,我们需要至少做两件事:
· 使用什么调试器
· 我们调试的目标是什么
为了进行调试,我们将使用上一篇文章中使用的FT2232H 转储SPI闪存。通过此接口,我们可以使用OpenOCD通过SWD查询有关目标的信息,这很重要,因为在逆向过程的此阶段,我们甚至都不知道目标CPU是什么!
https://wrongbaud.github.io/Holiday-Teardown/
下表是确定FT2232H上的哪些引脚需要连接到SWD目标的表格:
最后,为了将FT2232H用作SWD适配器,必须在FT2232H上的AD1/ AD2之间放置一个470 OHM电阻。
一旦我们将FT2232H上的引脚连接到目标,就可以使用以下脚本查询DAP控制器上的DPIDR寄存器:
# We are using an FT2232H so specify this hereinterface ftdi# Provide the VID/PID of the FT2232Hftdi_vid_pid 0x0403 0x6010# There are two channels, this is the defaultftdi_channel 0# To the best of my knowledge, this is used to properly set and confiture the state of the lines we are usingftdi_layout_init 0x0018 0x05fb# Enable SWD for the lines that we are using, and the portftdi_layout_signal SWD_EN -data 0# This is used to specify the sRST pin, in our case we're using ftdi_layout_signal nSRST -data 0x0010# Here we are selecting SWD as opposed to another transport layer such as JTAGtransport select swd# Set the speed of the adapter, this will vary based on what your hardware supportsadapter_khz 100# Create a new dap, (TAP for JTAG terms) with name chip and role CPU, -enable let's OpenOCD to know to add it to the scanswd newdap chip cpu -enable# Create the DAP instance, this must be explicitly created according to the OpenOCD docsdap create chip.dap -chain-position chip.cpu
我们可以使用openocd如图所示运行此脚本,并显示以下输出(请注意,第一次运行该脚本时,没有输出,在交换SWD/ SCLK行之后,将输出以下输出)。参见下表,了解从FT2232到控制器的连接
我们已经找到了一个芯片ID0x2ba01477,如果我们通过谷歌搜索该ID,就会看到各种Cortex M / STM32组件的点击率很高,这很有意义,因为该处理器系列支持SWD。现在我们可以与DAP进行通信,我们应该看看是否可以确定正在使用的处理器,如果该处理器已为其编写了配置文件,则我们将能够转储闪存组并从中获取其他辅助信息。有了这些额外的信息,我们可以告诉OpenOCD使用具有Cortex M定义的芯片来创建目标,这将使我们能够在弄清楚DAP的更多优点并获得一些更通用的功能确切地定位的是哪个CPU:
# Set up the GDB target for the CPU, cortex_m is the CPU type, target create chip.cpu cortex_m -dap chip.dap# init reads out all of the necessary information from the DAP, kicks off the debugging session, etcinit# Read out the information from the DAP, including the ROM tabledap info
当使用此配置文件运行openocd时,将看到以下结果:
wrongbaud@wubuntu:~/blog/stm32-xbox$ sudo openocd -f openocd.cfg Open On-Chip Debugger 0.10.0+dev-01040-ge7e681ac (2020-01-27-18:55)Licensed under GNU GPL v2For bug reports, readhttp://openocd.org/doc/doxygen/bugs.htmlInfo : FTDI SWD mode enabledInfo : clock speed 100 kHzInfo : SWD DPIDR 0x2ba01477Info : chip.cpu: hardware has 6 breakpoints, 4 watchpointsInfo : chip.cpu: external reset detectedInfo : Listening on port 3333 for gdb connectionsAP ID register 0x24770011Type is MEM-AP AHB3MEM-AP BASE 0xe00ff003Valid ROM table presentComponent base address 0xe00ff000Peripheral ID 0x00000a0411Designer is 0x0a0, STMicroelectronicsPart is 0x411, Unrecognized Component class is 0x1, ROM tableMEMTYPE system memory present on busROMTABLE[0x0] = 0xfff0f003Component base address 0xe000e000Peripheral ID 0x04000bb00cDesigner is 0x4bb, ARM Ltd.Part is 0xc, Cortex-M4 SCS (System Control Space)Component class is 0xe, Generic IP componentROMTABLE[0x4] = 0xfff02003Component base address 0xe0001000Peripheral ID 0x04003bb002Designer is 0x4bb, ARM Ltd.Part is 0x2, Cortex-M3 DWT (Data Watchpoint and Trace)Component class is 0xe, Generic IP componentROMTABLE[0x8] = 0xfff03003Component base address 0xe0002000Peripheral ID 0x04002bb003Designer is 0x4bb, ARM Ltd.Part is 0x3, Cortex-M3 FPB (Flash Patch and Breakpoint)Component class is 0xe, Generic IP componentROMTABLE[0xc] = 0xfff01003Component base address 0xe0000000Peripheral ID 0x04003bb001Designer is 0x4bb, ARM Ltd.Part is 0x1, Cortex-M3 ITM (Instrumentation Trace Module)Component class is 0xe, Generic IP componentROMTABLE[0x10] = 0xfff41003Component base address 0xe0040000Peripheral ID 0x04000bb9a1Designer is 0x4bb, ARM Ltd.Part is 0x9a1, Cortex-M4 TPIU (Trace Port Interface Unit)Component class is 0x9, CoreSight componentType is 0x11, Trace Sink, PortROMTABLE[0x14] = 0xfff42003Component base address 0xe0041000Peripheral ID 0x04000bb925Designer is 0x4bb, ARM Ltd.Part is 0x925, Cortex-M4 ETM (Embedded Trace)Component class is 0x9, CoreSight componentType is 0x13, Trace Source, ProcessorROMTABLE[0x18] = 0x0End of ROM tableInfo : Listening on port 6666 for tcl connectionsInfo : Listening on port 4444 for telnet connections
有了这些新的更改,我们不仅可以与DAP和MEM-AP进行交互,还可以通过GDB调试目标。由于MEM-AP条目中的0x411部件号,我们还可以确定目标CPU是STM32F2X系列:
MEM-AP BASE 0xe00ff003Valid ROM table presentComponent base address 0xe00ff000Peripheral ID 0x00000a0411Designer is 0x0a0, STMicroelectronicsPart is 0x411, Unrecognized Component class is 0x1, ROM table
如果我们无法访问DAP并想通过内存读写确定我们的目标是什么呢?为了弄清楚这一点,在STM32 CPU中有一些常见的存储区域用于存储ID和闪存信息。利用此信息,我们可以修改OpenOCD脚本以读取这些区域并查找相关的ID信息!
https://github.com/antongus/stm32tpl/blob/master/stm32.h
下表具有ID信息的必要偏移量:
mdw 0x1FFFF7AC 3mdw 0x1FFFF7E8 3mdw 0x1FFF7A10 3mdw 0x1FF0F420 3mdw 0x1FF80050 3mdw 0x1FF800D0 3
当我们运行更新的OpenOCd脚本和以上命令时,我们看到以下结果:
> mdw 0x1FFFF7AC 30x1ffff7ac: ffffffff ffffffff ffffffff > mdw 0x1FFFF7E8 30x1ffff7e8: ffffffff ffffffff ffffffff > mdw 0x1FFF7A10 30x1fff7a10: 006c0028 31385114 30373639 > mdw 0x1FF0F420 3SWD DPIDR 0x2ba01477Failed to read memory at 0x1ff0f424> mdw 0x1FF80050 3SWD DPIDR 0x2ba01477Failed to read memory at 0x1ff80054> mdw 0x1FF800D0 3SWD DPIDR 0x2ba01477Failed to read memory at 0x1ff800d4>
我们可以使用以下命令,使用该芯片数据手册中的闪存地址或上面链接的存储库,获得闪存大小:
> mdh 0x1FFF7A220x1fff7a22: 0100 https://github.com/antongus/stm32tpl/blob/master/stm32.h
现在我们知道确切的目标,可以删除swd和dap的目标,在命令行-f /usr/local/share/openocd/scripts/target/stm32f2x.cfg,将正确枚举目标CPU,现在我们也知道了该STM32F2系列芯片具有0x100 1kb页的闪存。
wrongbaud@wubuntu:~/blog/stm32-xbox$ sudo openocd -f openocd.cfg -f /usr/local/share/openocd/scripts/target/stm32f2x.cfg [sudo] password for wrongbaud: Open On-Chip Debugger 0.10.0+dev-01040-ge7e681ac (2020-01-27-18:55)Licensed under GNU GPL v2For bug reports, readhttp://openocd.org/doc/doxygen/bugs.htmlInfo : FTDI SWD mode enabledadapter speed: 100 kHzInfo : Listening on port 6666 for tcl connectionsInfo : Listening on port 4444 for telnet connectionsInfo : clock speed 1000 kHzInfo : SWD DPIDR 0x2ba01477Info : stm32f2x.cpu: hardware has 6 breakpoints, 4 watchpointsInfo : Listening on port 3333 for gdb connections
现在可以正常工作了,我们可以使用以下命令转储内部闪存:
> flash list{name stm32f2x base 0 size 0 bus_width 0 chip_width 0} {name stm32f2x base 536836096 size 0 bus_width 0 chip_width 0}> flash read_bank 0 bank0.bindevice id = 0x00016423flash size = 256 kbyteswrote 262144 bytes to file bank0.bin from flash bank 0 at offset 0x00000000 in 3.690861s (69.361 KiB/s)> flash read_bank 1 bank1.binflash size = 512 byteswrote 512 bytes to file bank1.bin from flash bank 1 at offset 0x00000000 in 0.007852s (63.678 KiB/s)
我们还可以使用以下命令使用gdb调试控制器:
wrongbaud@wubuntu:~/blog/stm32-xbox$ gdb-multiarch GNU gdb (Ubuntu 8.1-0ubuntu3.2) 8.1.0.20180409-gitCopyright (C) 2018 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law. Type "show copying"and "show warranty" for details.This GDB was configured as "x86_64-linux-gnu".Type "show configuration" for configuration details.For bug reporting instructions, please see:.Find the GDB manual and other documentation resources online at:.For help, type "help".Type "apropos word" to search for commands related to "word".(gdb) set architecture armThe target architecture is assumed to be arm(gdb) target remote localhost:3333Remote debugging using localhost:3333warning: No executable has been specified and target does not supportdetermining executable automatically. Try using the "file" command.0x0800307e in ?? ()(gdb) x/10x 0x1FFF7A100x1fff7a10: 0x006c0028 0x31385114 0x30373639 0xc000fcc00x1fff7a20: 0x0100c000 0x67ff47d2 0x05dcf000 0x04a803b30x1fff7a30: 0x451744b1 0xffffffff(gdb)
因此,在这一点上,我们将闪存转储了,可以调试并单步执行固件,但是……可以重新刷新MCU吗?
如果我们可以在固件映像中找到USB描述符字符串并进行修补,则可以将其用作确定是否可以修补固件的可见方法。让我们在GHIDRA中加载固件,看看是否可以找到它们,固件映像可以加载到address 0x8000000。我们知道固件是0x8000000基于数据表加载的,但是如果我们没有数据表,则可以通过发出reset halt命令并单步执行第一条指令从OpenOCD确定。幸运的是,此固件映像很小,Ghidra可以快速对其进行处理。在下面的截图中可以看到在dmesg输出中看到的字符串:
让我们用字符串做一个简单的patch,将其更改为“ Testing Firmware Patches”。可以在OpenOCD telnet控制台中使用以下命令覆盖闪存:
wrongbaud@wubuntu:~/blog/stm32-xbox$ telnet localhost 4444Trying 127.0.0.1...Connected to localhost.Escape character is '^]'.Open On-Chip Debugger> flash read 0 bank0-orig.bin> flash read_bank 1 bank1-orig.bin flash size = 512 byteswrote 512 bytes to file bank1-orig.bin from flash bank 1 at offset 0x00000000 in 0.007867s (63.557 KiB/s)> stm32f2x unlock 0Target not haltedstm32f2x failed to unlock device> halttarget halted due to debug-request, current mode: Handler External Interrupt(67)xPSR: 0x61000053 pc: 0x0800839c msp: 0x2000ff48> stm32f2x unlock 0stm32f2x unlocked.INFO: a reset or power cycle is required for the new settings to take effect.> reset halt target halted due to debug-request, current mode: Thread xPSR: 0x01000000 pc: 0x080002a4 msp: 0x20010000> stm32f2x mass_erase 0stm32x mass erase complete> flash write_bank 0 bank0-patch.binwrote 262144 bytes from file bank0-patch.bin to flash bank 0 at offset 0x00000000 in 3.744948s (68.359 KiB/s)> reset >
这里有一些步骤可能没有意义,所以我想解释一下:
1. 在尝试重新刷新之前,请始终始终备份所有闪存映像。
2. STM32闪存控制器具有一个锁定位,可以防止不必要的写入。这在STM32的“选项字节”中设置
· 对我们来说幸运的是,我们能够解锁锁定位,有时候这不是一个很好的选择!
· 对于STM32上的内部闪存,我们需要先执行擦除操作,然后再对其进行写入
· 我要在这里补充一点,如果你的目标昂贵或很重要,这绝不是你要做的事情,除非你百分百确定可以将其恢复为原始状态
· 我们编写修补后的固件映像,然后重新启动CPU,并提示以下 dmesg内容
[54691.886194] usb 1-6.4: new full-speed USB device number 14 using xhci_hcd[54691.992411] usb 1-6.4: New USB device found, idVendor=0e6f, idProduct=02a2, bcdDevice= 1.0f[54691.992417] usb 1-6.4: New USB device strings: Mfr=1, Product=2, SerialNumber=3[54691.992420] usb 1-6.4: Product: Testing Firmware Patches[54691.992423] usb 1-6.4: Manufacturer: Performance Designed Products[54691.992426] usb 1-6.4: SerialNumber: 0000AE38D7650465[54691.998102] input: Generic X-Box pad as /devices/pci0000:00/0000:00:14.0/usb1/1-6/1-6.4/1-6.4:1.0/input/input28
现在我们已经完全提取了固件并将其加载到ghidra中,并且能够按我们认为合适的方式对其进行修改,现在是时候编写一些宏文件了。
0x05 分析总结
在分析嵌入式系统时,你通常希望枚举和探索与目标交互的所有可能的接口和方法。无论你的最终目标是寻找漏洞,修改设备的正常操作还是只是了解其工作原理,硬件调试都是极其重要的有用。通过利用硬件调试,我们能够从该目标中提取固件,设置实时调试器并修改固件。通过本练习,我们还介绍了单线调试的工作方式,以及如何使用硬件调试工具识别,枚举和调试未知的CPU。OpenOCD还与基于FT2232H的界面一起使用,以提取固件映像并将新固件重新刷写到目标上。