介绍
这是尝试修改 2010 年大众高尔夫车电子动力转向(EPS)ECU 上运行的固件的系列文章的第四部分,也是最后一部分。目标是修改固件,以消除车道保持操作的 6 分钟锁定,并允许在低速时使用车道保持。
在前面的部分中,我确定了我想要对固件进行的更改。在这一部分中,我将专注于将补丁刷入实际模块。首先,我将使用 Can 校准协议(CCP)从模块中提取引导加载程序。然后,我将对引导加载程序进行逆向工程,以找出刷写过程的工作原理。最后,我将将补丁应用到实际汽车上,并验证它们的工作情况。
提取引导程序
在较早的部分,我使用更新文件提取应用程序固件,并用其来识别我想要进行的补丁。然而,该更新文件仅包含高于 0xa000
的地址,而引导加载程序可能位于其之前。当进入“编程”诊断模式时,应用程序将重新启动并跳转到闪存开头的引导加载程序代码。有趣的是,您无需输入任何密码即可进入此编程模式。在尝试在实际模块上进行此操作时,我观察到常规的 CAN 消息停止输出,并且我只能发送有限数量的 KWP2000 请求,表明确实运行的是不同的应用程序。
为了准确了解flashing的工作原理,我需要从实际的 ECU 中转储引导加载程序。通常一旦你有了应用固件,要找到任意读取并转储剩余代码并不太困难。我见过一些完整的基于文本的 shell,UDS 例程控制允许请求内存内容,或者保留 CCP/XCP 启用。
在这种情况下,我们将会浏览前一部分中发现的 CAN 解析函数,我们很快看到类似于地址 1746 和 1747(发送/接收)上的 CCP 的东西,带有一个处理函数 0x00011152
。它允许使用站点 ID 0x0
和 0x815
进行连接。与原始内存交互的必需函数( DNLOAD
, UPLOAD
和 SET_MTA
)已经实现,并且没有任何边界检查。CCP 标准的关键/种子部分都没有实现,因此也不需要进行身份验证。
连接 CCP 处理程序功能的一部分。请注意,站点 ID 0x0 和 0x815 是有效的站点 ID。
使用 panda 中的 CCP 库很容易编写一个脚本来转储完整的内存内容。这也让我能够验证在 ECU 上运行的代码是否与我一直在逆向工程的更新文件固件匹配。转储整个闪存后,我们可以通过“文件”→“添加到程序”将引导加载程序区域( 0x0
- 0x9fff
)替换为刚刚转储的文件。之后,我重新运行了自动分析。
Flashing 程序
将引导加载到 Ghidra 后,我需要在引导加载程序中识别 KWP2000 处理程序。我采用相同的方法,寻找 KWP 错误代码。在这种情况下,我们期望找到闪烁代码,因此我搜索了 return0x41
(improperDownloadType),并在 0x00008c64
找到了处理程序。
刷写 ECU 通常遵循相同的一般步骤,我预计在反向工程这个固件时会找到类似的内容:
(optional) Log in with a key/seed using
securityAccess
使用securityAccess
键/种子登录Entering the bootloader using
diagnosticSessionControl
使用diagnosticSessionControl
进入引导加载程序(optional) Log in again with a key/seed using
securityAccess
再次使用securityAccess
键/种子登录Start the flashing using
requestDownload
. Sometimes called after erasing 使用requestDownload
开始闪烁。有时称为擦除后。Erase memory using a
routineControl
call (0xFF00
for UDS) 使用routineControl
调用擦除内存 (0xFF00
用于 UDS)(optional) set a decryption key if the firmware is sent encrypted to the ECU using
writeDataById
(可选)如果固件使用writeDataById
加密发送到 ECU,则设置解密密钥Send the data in chunks using
transferData
使用transferData
将数据分块发送Indicate all data has been sent using
requestTransferExit
指示所有数据已使用requestTransferExit
发送Make the ECU verify the checksum and mark the firmware as bootable using a
routineControl
(0xFF01
for UDS) 使电控单元验证校验和,并使用routineControl
(UDS 为0xFF01
)标记固件为可引导Reboot the ECU using
stopCommunication
orecuReset
使用stopCommunication
或ecuReset
重新启动 ECU
我将在下面详细阐述每个步骤。您也可以跟着我写的flashing脚本一起进行,该脚本实现了所有这些步骤。
进入引导程序
在这种情况下,我们不需要在应用固件中经过任何 securityAccess
,可以通过请求诊断会话 0x85
直接重启到引导加载程序。一旦进入引导加载程序,我们确实需要经过 securityAccess
,但种子始终是 0x12345678
,实际上不会检查响应。您确实需要按照请求种子和发送密钥的步骤进行操作,否则闪存状态机将无法进入下一步。
请求下载
实际的闪存是使用 requestDownload
开始的。这包括起始地址、大小、压缩类型(未压缩 - 0x0
)和加密类型(未加密 - 0x0
)。数据包布局为 0x34( requestDownload
)- 起始地址(3 字节)- 压缩和加密(1 字节)- 字节数(3 字节)。
擦除内存
使用 routineControlByLocalIdentifier
和例行标识符 0xc4
擦除闪存。同时传入起始地址和结束地址(包括在内)。然后引导加载程序检查给定地址是否与上一步发送的起始地址和大小匹配。如果匹配,则开始擦除闪存。擦除操作可能需要一段时间,ECU 在此期间不会响应。因此,我们不断尝试打开新的 TP 2.0 通道,直到 ECU 再次响应。
重新建立连接后,我们需要检查擦除是否成功。这是通过使用相同的例行标识符( 0xc4
)进行的 requestRoutineResultsByLocalIdentifier
。如果擦除成功,将返回 0x0
。
传输数据
擦除后,是时候发送新固件了。这是分块进行的。最大分块大小由 requestDownload
调用返回,在本例中为 0xf1
(241)字节每块。
所有块都已发送后,使用 requestTransferExit
完成传输。这还会检查根据先前的范围请求是否已发送了正确数量的数据。
计算校验和
刷写完成后,数据的校验和必须作为一种合理性检查发送,以确保在传输过程中没有数据损坏。就像擦除一样,这是使用标识符 0xc5
进行的 routineControlByLocalIdentifier
。然后我们需要使用 requestRoutineResultsByLocalIdentifier
来检查是否成功。校验和本身只是所有传输字节的总和,截断为两个字节。
一些 ECU 在这一点上还会验证固件是否具有有效的签名,但对于这个 EPS 似乎并非如此。在测试其他潜在的补丁时,我对桌上的 EPS 进行了小的代码更改,它正常启动了。根据我的观察,这似乎在德国品牌车辆中更为常见,而在日本车辆上则较少见。请务必查看 Brian Ledbetter 和 JinGen Lim 在这个主题上的有趣帖子!
停止沟通
所有的闪烁完成后(可以重复这些步骤多次以针对不同的块),现在是通过发送 stopCommunication
重新启动 ECU 的时候了。如果所有的闪烁步骤都成功,这将在闪存的末尾写入一个特殊标记 0x65646e45
("Ende",德语表示结束),这向引导加载程序表明应用程序是有效的并且可以启动。如果由于某种原因闪烁失败,它不会尝试启动可能已损坏的应用程序,而是会保持在引导加载程序中。当闪烁回使用 CCP 获得的转储时,重要的是要删除这个标记,这样我们就不会在闪烁可能失败时意外地将闪存标记为有效。
结论
我们现在拥有所有移动部件,可以实际重新刷新 ECU 以进行所需的更改。我创建了一个 GitHub 存储库,其中包含必要的脚本和实现 TP2.0 和 KWP2000 的辅助库:https://github.com/I-CAN-hack/pq-flasher。
只有在您知道自己在做什么的情况下才在实际汽车上尝试此操作。尽管只对模型特定校准值进行了两个非常小的修补,但更改您的 EPS 可能会产生意想不到的后果。您可能会失去动力转向,或者转向电机可能会突然对车轮施加大量扭矩。在使用前确保校准值的修补是安全的。
flashing 程序大致包括三个步骤。
使用 CCP 转储现有固件+校准。技术上可以使用更新文件跳过此步骤,但这样可以确保刷写回完全相同的固件。这需要通过直接连接到 EPS 来完成,并且不能通过 OBD-II 端口完成,因为存在阻止 CCP 地址的网关。例如,可以使用 J533 线束来完成此步骤。此步骤大约需要 15 分钟。
应用补丁。打补丁脚本将更改最小速度和 HCA 计时器。它根据版本字符串验证是否正在打补丁正确的固件版本,并在更改之前检查现有值。
回刷。您可以选择回刷整个固件,但不建议这样做,因为这需要大约 10 分钟,并且如果应用错误的补丁,可能会导致 ECU 变砖。默认情况下,刷写脚本只会覆盖包含我们实际更改的值的校准区域。
希望这一系列的博客文章能成为刚开始进行汽车黑客攻击的人们有用的参考。不同汽车的 ECU 固件之间有很多相似之处,这些文章应该让人们能够从我的经验(和错误)中学习。
免责声明
由于传播、利用本公众号渗透安全团队所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,公众号渗透安全团队及作者不为此承担任何责任,一旦造成后果请自行承担!如有侵权烦请告知,我们会立即删除并致歉。谢谢