黑进Harley的调谐器

  • A+

原文地址

https://therealunicornsecurity.github.io/Powervision-1/
正文


黑进Harley的调谐器

找到我的方法,著名的Harley调谐器的固件.
注意:所有加密密钥和密码都是伪造的,用于撰写此帖子。

第 1 部分:什么是调谐器

调谐器是一种应该插在摩托车上的小装置。它的目的是配置车载计算机,以优化燃料空气比和其他参数。众所周知,它被用来取消发动机功率限制,或优化燃油消耗。不过,我敢肯定,一个真正的摩托车手会用更好的词语来描述它是如何被使用的。

image.png
这里研究的模型是戴诺杰(Dynojet)为哈雷·戴维森(Harley Davidson)设计的Power Vision。硬件是由德鲁技术公司(Drew Technologies)推出的山猫D版(PCB上有一个打字错误,实际上写的是德鲁技术公司)。

image.png

第 2 部分:获取固件

2.1:分析工具

调谐器应在连接到计算机时进行配置。它在CAN总线旁边有一个迷你USB接口(这个接口应该连接到自行车的车载计算机上)。用来配置它的工具可以在Dynojet的网站上免费下载。

image.png
已安装的Windows工具包含以下二进制文件:
* WinPV.exe:带有图形用户界面的主软件。
* PVUpdateClient.exe:软件更新程序,它的工作是下载新的固件并通过USB链接复制它们。
* RecoveryTool.exe:由PVUpdateClient专门调用,用于更新固件的恢复部分。
* PVLink.dll:负责串口通信,非常重要

image.png
现在我们的目标是得到固件,这样我们就可以开始逆向分析了。在YouTube教程和TheWaybackMachine上查看,我们可以看到固件曾经可以直接在Dynojet的网站上,在固件部分,现在是空的。我们通过同时运行PVUpdateCLient.exe、Wireshark发现了一条有趣的线索。

image.png
Wireshark捕获显示纯文本HTTP将dynojetpowervision.com网站以及检查可用的固件文件。这里没有真正的保护,只是您应该使用的用户代理是“PVUpdateClient”,否则文件仍然是隐藏的。
使用curl命令,我们可以得到我们要查找的文件名:
```
curl -v -A PVUpdateClient http://dynojetpowervision.com/downloads/PowerVisionVersions.xml
* Trying 52.183.62.164:80...
* TCP_NODELAY set
* Connected to dynojetpowervision.com (52.183.62.164) port 80 (#0)

GET /downloads/PowerVisionVersions.xml HTTP/1.1
Host: dynojetpowervision.com
User-Agent: PVUpdateClient
Accept: /

  • Mark bundle as not supporting multiuse
    < HTTP/1.1 200 OK
    < Content-Type: text/xml
    < Last-Modified: Mon, 09 Nov 2020 18:35:22 GMT
    < Accept-Ranges: bytes
    ...



    ...
    我们开始关注固件和SYSTEM_UPGRADE文件,因为它们最有可能包含我们感兴趣的内容。我们使用wget下载它们,麻烦从这里开始:
    [email protected]:~/powervision$ unzip PV_SYSTEMUPGRADE-1.0.1-631.pvr

    Archive: PV_SYSTEMUPGRADE-1.0.1-631.pvr

    [PV_SYSTEMUPGRADE-1.0.1-631.pvr] PVRecoveryInfo.xml password:

    [email protected]:~/powervision$ unzip PV_FIRMWARE-2.0.47-1613.pvu

    Archive: PV_FIRMWARE-2.0.47-1613.pvu

    extracting: PVU_TYPE

    extracting: PVU_CERT

    inflating: PVU_FILE

    [email protected]:~/powervision$ file PVU_FILE

    PVU_FILE: openssl enc'd data with salted password

    ```
    SYSTEM_RECOVERY,也就是我们所说的 "PVR文件 "是一个受密码保护的存档,而名为 "PVU_FILE "的FIRMWARE文件实际上是使用Openssl加密的。另外,PVU_CERT文件表示可能会对PVU_FILE进行完整性检查。
    我将跳过细节,但由于PVR文件是以明文形式写在设备上的,很明显,其中一个工具(在本例中是RecoveryTool.exe)在某个地方有密码。稍后通过逆向工程,我们得到一个密码 "POWERVISION_RECOVER_3456789Z"。虽然在这里我不会解释整个事情的来龙去脉,但是这个密码其实是有一定的隐蔽性的。它并没有硬编码,而是通过一些循环去整数值生成密码的结尾模式,然后再给它连接一个大写字母。有明显的意图是为了隐藏这一点,防止有不良意图的用户,这通常是我们走对了路的标志。

恢复文件内容

nandflash_bobcat.bin: data
PVRecoveryInfo.xml: exported SGML document, ASCII text, with CRLF line terminators
u-boot.bin: data
uImage: u-boot legacy uImage, Bobcat-577, Linux/ARM, OS Kernel Image (Not compressed), 828996 bytes, Thu Feb 3 14:44:52 2011, Load Address: 0x20008000, Entry Point: 0x20008040, Header CRC: 0x5EDBBE36, Data CRC: 0xD026D2D5

恢复文件是一个u-boot镜像与Kernel镜像。文件的熵表明其中有一部分是加密的。另外,at91bootstrap文件的存在表明我们是存在一个SAM AT 91板,它可以使用安全启动。该死的。不过我们可以从这些文件中得到一个信息:处理器类型是SAM926X。
浏览互联网,我们还可以找到以下论坛帖子,其中有一位Drew Technoligies的员工询问关于这个相同系列的处理器(特别是SAM9260-EK)的信息:https://lists.denx.de/pipermail/u-boot/2011-June/093651.html

更新文件

由于更新文件是加密的,我们可以提出两个假设:
* 固件是加密存储的,在运行时解密。这可能会很慢,但由于板子支持安全启动,这是一个可行的假设。
* 固件是不加密存储的,只有更新是加密的。更新过程会解密PVU_FILE,并替换运行中的固件,这样就好了,不是吗?

2.2: 物理构造

快刀斩乱麻的胜法总是拆开内存芯片来获取固件。但这样的话,就比较复杂了。整个PCB是用塑料保护成型的,可能是为了密封防潮。

image.png

image.png
我必须把它切开才能看到实际的PCB:

image.png
内存芯片似乎被焊接在PCB的另一侧。这是一个相当糟糕的消息,因为它在屏幕下方,如果试图从物理上得到这个,可能会破坏设备。另外,在这个阶段,我们也不知道拿到芯片是否能帮助我们。它可能只是包含一个Openssl加密文件,在启动过程中会被解密。所以我们从其他地方寻找。
通过仔细观察,我们可以发现有4个引脚上面写着DEBUG!

image.png
所以我们用一个UART转USB的适配器连接到它,然后启动minicom。
ROMBoot
Welcome to bobcat
bocat login:

问题是这个shell是有密码保护的,即使经过几天的强制执行(使用这个工具),也没有找到密码。另外,U-Boot被设置为静音,从这个shell没有办法与启动序列进行交互。
有一条轨迹仍然没有被探索出来:RECOVERY MODE。

恢复模式

摆弄一下RecoveryTool.exe,我们发现有一个设备的恢复模式。它是通过按电源按钮,同时插入USB链接激活.

image.png
现在,这个模式的有趣之处在于,它切换了USB Link端口的通信模式。事实上,在名义上的工作模式下,这个端口使用了一个专有的协议,限制了很多操作(我将在专门的部分详细介绍),而在恢复模式下,这个端口会暴露出一个U-Boot shell!
```
U-Boot> printenv
bootargs=console=ttyS0,115200 ubi.mtd=linux root=31:4 lpj=598016 quiet
bootcmd=nboot kernel;bootm;
bootdelay=0
baudrate=115200
mtdids=nand0=atmel_nand
mtdparts=mtdparts=atmel_nand:[email protected](at91bootstrap)ro,1m(u-boot)ro,2m(kernel),-(linux)
silent=yes
ver=U-BootVersion:1.0.1
stdout=usbser
stdin=usbser
stderr=usbser

Environment size: 316/131068 bytes
我们可以修改引导参数,以绕过内部UART调试端口的认证:
U-Boot> setenv bootargs "console=ttyS0,115200 ubi.mtd=linux root=31:4 lpj=598016 single"
U-Boot> setenv silent no
U-Boot> setenv bootdelay 3
U-Boot> printenv
bootcmd=nboot kernel;bootm;
baudrate=115200
mtdids=nand0=atmel_nand
mtdparts=mtdparts=atmel_nand:[email protected](at91bootstrap)ro,1m(u-boot)ro,2m(kernel),-(linux)
ver=U-BootVersion:1.0.1
stdout=usbser
stdin=usbser
stderr=usbser
bootargs="console=ttyS0,115200 ubi.mtd=linux root=31:4 lpj=598016 single"
silent=no
bootdelay=3

Environment size: 319/131068 bytes
```
我们用single代替quiet,以便停用认证,添加一个延迟,以便我们有足够的时间进入UART shell,并将silent设置为 "no",以确保我们在UART shell上有一个启动跟踪。

要做到这一点,我们需要同时连接到我们配置新参数的USB链接和内部UART调试端口,在那里shell应该弹出。

image.png
设置好所有参数后,用U-Boot在USB Link上运行启动命令,将触发单用户恢复模式启动:
```
U-Boot> boot
Loading from nand0, offset 0x120000
Image Name: Bobcat-577
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 828996 Bytes = 809.6 KiB
Load Address: 20008000
Entry Point: 20008040

Booting kernel from Legacy Image at 20008000 ...

Image Name: Bobcat-577
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 828996 Bytes = 809.6 KiB
Load Address: 20008000
Entry Point: 20008040
Verifying Checksum ... OK
XIP Kernel Image ... OK
OK
同时,在UART调试连接上:
[ 0.000000] Linux version 2.6.30 ([email protected]) (gcc version 4.3.3 (GCC) ) #32 PREEMPT Thu Feb 3 09:43:13 E1
[ 0.000000] CPU: ARM926EJ-S [41069265] revision 5 (ARMv5TEJ), cr=00053177
[ 0.000000] CPU: VIVT data cache, VIVT instruction cache
[ 0.000000] Machine: Dynojet Power Vision
[ 0.000000] Memory policy: ECC disabled, Data cache writeback
[ 0.000000] Clocks: CPU 192 MHz, master 96 MHz, main 16.000 MHz
[ 0.000000] Built 1 zonelists in Zone order, mobility grouping on. Total pages: 16002
[ 0.000000] Kernel command line: console=ttyS0,115200 ubi.mtd=linux root=31:4 lpj=598016 single
[ 0.000000] NR_IRQS:192

[ 0.000000] AT91: 96 gpio irqs in 3 banks

...
[ 0.930000] UBI: background thread "ubi_bgt0d" started, PID 97
[ 0.950000] VFS: Mounted root (squashfs filesystem) readonly on device 31:4.
[ 0.960000] Freeing init memory: 64K
/

id

uid=0(root) gid=0(root)

ggwp

```
左边是U-Boot shell,右边是显示启动顺序的UART shell

2.3 恢复Mode Shell

我们得到的shell是在一个特定的模式下设置的,其中只有部分固件被安装。我们现在需要完整的固件。一种方法是找到Openssl加密密码,并解密PVU_FILE。但我们先从另一个方法开始。
在2.1的时候,我们怀疑固件可能是未加密存储的,只有更新文件是加密存储的。这是正确的答案。
[ 0.410000] UBI: attaching mtd3 to ubi0
[ 0.410000] UBI: physical eraseblock size: 131072 bytes (128 KiB)
[ 0.420000] UBI: logical eraseblock size: 129024 bytes
[ 0.430000] UBI: smallest flash I/O unit: 2048
[ 0.430000] UBI: sub-page size: 512
[ 0.430000] UBI: VID header offset: 512 (aligned 512)
[ 0.440000] UBI: data offset: 2048
[ 0.870000] UBI: attached mtd3 to ubi0
[ 0.880000] UBI: MTD device name: "linux"
[ 0.880000] UBI: MTD device size: 252 MiB
[ 0.890000] UBI: number of good PEBs: 2023
[ 0.890000] UBI: number of bad PEBs: 0
[ 0.900000] UBI: max. allowed volumes: 128
[ 0.900000] UBI: wear-leveling threshold: 4096
[ 0.910000] UBI: number of internal volumes: 1
[ 0.910000] UBI: number of user volumes: 2
[ 0.910000] UBI: available PEBs: 0
[ 0.920000] UBI: total number of reserved PEBs: 2023
[ 0.920000] UBI: number of PEBs reserved for bad PEB handling: 20
[ 0.930000] UBI: max/mean erase counter: 110/68
[ 0.930000] UBI: background thread "ubi_bgt0d" started, PID 97

在启动序列中,我们可以看到从MTD设备中挂载了一个UBI文件系统。使用我们现在拥有的root shell,我们发现2个有趣的设备。UBI0_0和UBI0_1. 为了直接从它们那里读取数据,我们使用dd和uencode:
dd if=/dev/ubi0X of=stdout bs=SIZE count=COUNT 2&>/dev/null |uuencode -m ubi00
而我们从minicom日志中提取base64编码的数据。我们从PVU_FILE中知道固件的大小(约11MB),从恢复文件中的u-boot数据知道内存芯片的大小(128MB)
```
$ binwalk ubi00

DECIMAL HEXADECIMAL DESCRIPTION

0 0x0 Squashfs filesystem, little endian, version 4.0, compression:gzip, size: 10473396 bytes, 455 inodes, blocksize: 131072 bytes, created: 2019-09-04 20:48:04

$ binwalk ubi01

DECIMAL HEXADECIMAL DESCRIPTION

0 0x0 UBIFS filesystem superblock node, CRC: 0x8254BB9D, flags: 0x0, min I/O unit size: 2048, erase block size: 129024, erase block count: 1917, max erase blocks: 2039, format version: 4, compression type: lzo
129024 0x1F800 UBIFS filesystem master node, CRC: 0xBA73BA7B, highest inode: 2248, commit number: 132173
131072 0x20000 UBIFS filesystem master node, CRC: 0xF9347A80, highest inode: 2248, commit number: 132174
133120 0x20800 UBIFS filesystem master node, CRC: 0xB9DAE7BF, highest inode: 2248, commit number: 132175
258048 0x3F000 UBIFS filesystem master node, CRC: 0xB6434F66, highest inode: 2248, commit number: 132173
260096 0x3F800 UBIFS filesystem master node, CRC: 0xF5048F9D, highest inode: 2248, commit number: 132174
262144 0x40000 UBIFS filesystem master node, CRC: 0xB5EA12A2, highest inode: 2248, commit number: 132175
5062704 0x4D4030 Zip archive data, at least v1.0 to extract, compressed size: 11, uncompressed size: 11, name: PVU_TYPE
5062781 0x4D407D Zip archive data, at least v1.0 to extract, compressed size: 128, uncompressed size: 128, name: PVU_CERT
5062975 0x4D413F Zip archive data, at least v2.0 to extract, compressed size: 11295533, uncompressed size: 11293808, name: PVU_FILE
...
```
好了,整个固件都在这里了:

  • UBI0_0: 固件的只读部分,也就是二进制文件,布局和设备的一切必需品。
  • UBI0_1: 读/写的部分,所以许可证,用户文件,新的更新等
    好了,我们可以逆向分析了

参考文献

这个帖子让我想到了在启动参数中切换到单用户模式
https://blog.senr.io/blog/jtag-explained

在下一集里

固件逆向工程和仿真(着重于USB链路专有协议)
Loot(密码、加密密钥…)
缓冲区溢出
我代表Unicorn Security祝大家新年快乐!请继续关注下一部分。

写于2021年1月1日