因为现代汽车
2022 年 7 月下旬的某个时候,现代汽车删除了所有 DAudio2 固件下载的链接。只留下一条消息说更新将于 9 月 1 日返回。如果我不得不猜测的话,现代/摩比斯的某人读了我的博客,他们想解决他们的问题。考虑到这一点,我开始准备破解最新的固件。
9 月 1 日过去了,页面没有任何变化,也没有可用的新固件更新。近一周后发布了新的更新,但只是将日期推迟到 9 月 26 日。
然后,为了更好地衡量,他们又做了一次。这次他们将日期推迟到“~2022 年 10 月”。
但最终他们在 2022 年 10 月 24 日发布了一组新的固件更新。
Looking Inside
我快速下载了新固件 zip 并解压了文件。
其中有两个文件,而不是普通的单一 encsystempackage_{version}.zip 文件:
一个的预期名称为“encsystempackage134.100.220927.zip”,另一个的名称为“encd2vsystempackage134.100.220927.zip”。
使用之前找到的 zip 密码和加密密钥,我能够解压缩并解密 encsystempackage_134.100.220927.zip 文件,但它们对其他文件不起作用。
现代换了钥匙。
同样的游戏,新一轮
好吧,如果我想要最新最好的固件,我就必须重新破解它。但我怎么能这么做呢?
我的第一直觉是收集一些信息。提供的两个 zip 似乎都是完整的系统更新,因此我决定找出两者之间的区别。
但由于现代改变了邮政编码密码,我不得不破解它。使用 bkcrack(我在第 1 部分中学到的工具),我能够使用普通 zip 中的文件成功解密“d2v”zip。
与往常一样,更新中的大多数文件都已加密,但 system.img 文件并未加密。因此,我对两个 system.img 文件进行了比较。结果发现它们是相同的。
system.img 文件包含 IVI 通常从中启动的整个正常系统映像。这意味着除了使用的密钥之外,系统更新基本相同。
我想他们需要能够使用最新的更新来更新任何车辆的主机。由于有许多主机仍在运行旧固件,因此它们需要包含使用旧加密密钥的更新包,并且它们包含第二个文件以更新先前已更新的主机以使用新密钥。
如果这是真的,新的 zip 密码和加密密钥仍将位于我可以读取的“旧”zip 更新中。如果我能弄清楚这些,我就可以看看是否可以解密新的 d2v 更新,并可能像上次那样对其进行修改。
寻找秘钥
从我之前的经历中,我知道更新逻辑位于恢复映像 (updateboot.img) 的 /usr/bin/updateagent 可执行文件中。提取 updateagent 后,我开始对其进行反编译。
利用我之前对 updateagent 的了解,我很快就找出了新的 zip 密码。
我无法在 Windows 上使用“$09#$모비스98@!OTA$$”密码提取 d2v 更新 zip,但在 Linux 中使用 unzip 命令可以完美地工作。
现在找到加密密钥/iv,以及用于验证固件更新签名的 RSA 公钥。
快速计算
更新代理二进制文件与以前相比看起来有点不同。现代汽车肯定做出了一些改变。
至少其中一项变化是他们如何在 calcAES128() 函数中“存储”加密密钥和 IV。
我正在寻找的值是第 114/115 行的“iv”和“key”。所以我开始对代码进行逆向工程来找到它们。
该代码首先创建我所说的“userIV”。该值是一些传入值和 unk_86510 中找到的数据的组合。
然后,代码将 unk_8651C 中的数据与传入的相同值结合起来,形成我所说的“userKey”。
之后,AESDecryptWithKey() 函数被调用两次。第一次调用使用之前创建的“userIV”作为密钥和 IV 解密“byte_86528”中的值,然后将其存储到“iv”中。
第二次调用使用“userKey”作为Key和IV解密“byte_86538”中的值并将其存储到“key”中。
这确实有点令人困惑,我可以使用更好的变量名称,但至少我现在有了用于计算出真正的加密密钥和 IV 的逻辑链。唯一未知的是传入的什么值有助于生成“userIV”和“userKey”?
我查看了对 calcAES128() 函数的引用,看看传入了什么值。看起来它是“variantDataStruct”值的前 6 个字节。
根据我之前的研究,我知道变体结构是一种数据结构,用于存储有关车辆的各种信息,例如:型号、后视摄像头的类型,或者是否支持 HD Radio。
为了弄清楚到底是设置variantDataStruct,我查找了对它的引用。只有 1 个其他函数使用了它,receivevariantdata()。
现在这个函数还没有被最好地反编译,但据我所知,它接受了某种类型的缓冲区值(名为 buf),并将其中的 30 个字节移动到variantDataStruct。
所以我追寻了 buf 的值,这将我引向了函数 receive_pd()。在其中我看到了一个日志,涉及从微机读取数据。
CAN
根据我之前对车辆与 CAN 总线交互的研究,我知道微型计算机是 IVI 和车辆 CAN 总线之间的桥梁。每当 IVI 想要与车辆的其余部分进行交互时,它都必须经过它。
我还知道它是通过以这种格式读取和写入数据包来工作的:
始终为 FF(字节)
SID(字节)- 发送者 ID
RID(字节)- 接收器 ID
类型:(字节)
函数(Int16,大端)
有效负载长度(Int16,大端)
有效负载({有效负载长度}字节数组)
因此,updateagent 必须向 micom 发送一个数据包,请求变体数据,而 micom 则以最终在variantDataStruct 中结束的数据进行响应。
通过向上移动调用链,我来到了 requestvariantinfo() 函数。其中,它将一些数据保存到 v4 变量中,该变量看起来是一个数据包。(第 11-12 行)
由于字节序的奇怪性,数据包的两个部分是相反的。所以数据包是:FF8A010101170001
拆开它可以看到以下结构:
数据包开始:FF
发件人 ID (SID):8A
接收者 ID (RID):01
类型:01
功能:0117
有效负载长度:0001
有效负载:98(校验和字节)
因此,如果我可以将此数据包发送到 micom 并接收响应,我可以获取它的前 6 个字节并用它来解密加密密钥/IV!
由于在第 4 部分中我制作了一个可以用来与 micom 交互的应用程序,因此我曾经发送上述数据包。由于我是通过 micommux 套接字发送它,而不是像更新代理那样直接发送到 /dev/tccipc,因此我没有添加校验和字节,因为 micomd 进程会为我执行此操作。
我发送“FF8A010101170001”并收到以下内容:“FF018A0181171642???????????????????????????????????? ??????...”(审查有效负载)成功的响应数据包,解码为:
数据包开始:FF
发件人 ID (SID):01
接收器 ID (RID):8A
类型:01
功能:8117
有效负载长度:1642
有效负载:{0x1642 字节}
我现在有了variantDataStruct 的值。
我回到 calcAES128() 来计算出 userIV 和 userKey 的值。
看起来很简单,但我不相信自己,所以我只是复制了伪 C 并将其转换为 C#。我用获取变体数据包的有效负载填充了variantData并运行它:
它给了我一个密钥和 IV,我立即用它们来解密 d2v zip 中的文件。
“解密失败”?:(
我出了点问题。我想了想,每个车型的变体数据应该是相对唯一的,因为每个车型都有不同的能力。如果汽车之间的变体数据不同,它们将具有不同的计算密钥/ivs,因此具有不同的加密文件。
为了检查这是否属实,我下载了 Elantra 的相同更新版本,并将更新中的一些加密文件与 Ioniq 的加密文件进行了比较。
令我惊讶的是,我比较的所有文件都是相同的。这意味着变量数据结构的前 6 个字节在模型之间必须相同。
此后,我尝试了很多事情,例如仔细检查我的代码、重新获取变体数据包,甚至尝试暴力破解丢失的 6 个字节。一切都没有成功。最终,我意识到variantDataStruct不仅包含数据包的有效负载,还包含数据包的标头。
解决这个问题的关键就在我面前。响应数据包标头的开头是我要查找的 6 个字节。
通过将变体数据设置为“FF018A0181171642”并运行代码,它给了我以下 Key 和 IV:
密钥:F4DA05A5E848309EE8377464CF4254A3
四:763AC2AFA9E8EE379788B6CC08612B0
它成功了!
这对于 IVI 的安全来说是非常必要的,而且一点也不复杂。
66.6% 完成
我现在可以解密整个系统更新。这样,我终于验证了两个更新文件(正常的和 d2v 的)实际上是相同的。
我还花时间寻找用于验证固件签名的 RSA 公钥。
我最终找到了一个名为decryptPublicKey()的函数。这个函数称为 calcAES128(),就像加密密钥一样,但它通过函数的不同分支。第二个分支创建用作密钥和 IV 的字节,以将“/usr/share/pub_key/updatePublic.info”文件解密为实际的公钥。
我再次复制反编译的代码,将其转换为C#,运行它,它给了我密钥/iv:FF018A018117D0AD830AD5A7DABEDA72
很好,虽然我不知道为什么他们一开始就费心去尝试加密公钥(毕竟它是公钥,如果它是公开的就好了。)。
然后我使用找到的 key/iv 来解密公钥:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApTCnVgqDkrE4VrnuEI5G
slnmP6GmO03Pg0RuW1wzWsP9BybsNX4GuULSCqr3MsZWOquSO3ftEM9c59LSuTli
9LE57zsfohoieeeFaE1mFW5fLio+AQhwpeEVIeOLzt/gX/bcq0c+JKcwEawpOxM0
GhMenNdA+jaODjlhK1cqjrTdBQUPcQNBguAbNU6VVufjmAsNTwT7hUVEvdZIllIo
RE8c3lQqRy7eV1nQxxQXiGSK/YkcAfjgmaSuLamdgBUcbc8s8wMs6xoXbJqxFXW1
2zDMXPD98g/mysGZgzfQPfr2w1r4b5q7wvC0kbhrcZFuJ9mmf1cufzB2NgMvzZJ6
qQIDAQAB
-----END PUBLIC KEY-----
我还用谷歌搜索了该密钥,但它似乎是唯一生成的,与上次不同。
0% 已完成?
RSA 公钥实际上是唯一的。这意味着我不知道私钥是什么,并且无法签署自定义固件更新。
MOBIS
这一切都表明,现代/摩比斯在这里实行的默默无闻的安全措施是完全没有必要的。如果您有适当的安全设置,隐藏密钥并不重要。即使 zip 密码、加密密钥/iv 和 RSA 公钥被公开发布,它们的固件更新仍然是安全的,因为它们使用唯一生成的 RSA 公钥/私钥对。所有这些都会使原始代码更难以阅读和维护。它甚至可能更容易错过一些安全方面的内容,因为它更难阅读。想想程序员吧!
让我们破解它
不管怎样,现代/摩比斯在安全方面做得很好……通过对 updateAgent 的反编译,我发现了一些非常有趣的东西。
固件更新不必加密。
在我称为“unzipUpdate”的函数中,有两个逻辑分支:
一个分支使用我找到的密码将 zip 解压到 USB 驱动器上,而另一个分支则在没有密码的情况下将其解压。以下是 updateAgent 如何处理固件更新的一般逻辑:
对于未经训练的人来说,这可能看起来不错,但对于我经过专业训练的 Security Eye™️ 来说,我可以看到潜在的漏洞。
其中一个分支机构没有任何安全检查。
如果我能让系统采用未加密的更新文件(system_update.zip),我就可以根据需要对其进行修改。
95%?
刷新未加密固件更新的第一步当然是进行未加密固件更新。
从一开始,我就决定制作一个 C# 实用程序来帮助自动创建未加密的固件更新过程。
我创造性地将其称为“HyundaiFirmwareDecrypter”。源码下载
我将其设计为具有离散的“步骤”。每一步都是制作解密更新 zip 的过程中的一步。
可用的步骤是:
提取 - 将 d2v 格式的 zip 提取到临时目录。
解密 - 如有必要,解密所有提取的文件。(还重命名以“enc_”开头的文件夹,使其不包含该文件夹)
安装 ADB Backdoor - 启用 Android 调试桥 TCP 服务器,可用于访问 root shell。(需要以 root/sudo 身份运行)
计算哈希值 - 计算更新中每个文件的 SHA512 哈希值并将其编译到 update.list 文件中
创建 Zip - 压缩所有文件,创建 system_update.zip 更新文件。
该实用程序仅运行指定的步骤。这使得您可以轻松进行自定义修改,因为您可以运行前两个步骤;提取和解密。然后对系统文件进行所需的更改,然后运行计算哈希和创建 Zip 步骤,将您的更改编译到固件更新文件中。
或者,如果您想要更多“一键式解决方案”,请运行“安装 ADB 后门”(-i) 步骤,这将在固件更新中启用 ADB TCP 服务器。这意味着您可以简单地连接到主机的 Wi-Fi 或使用以太网连接和 adb 工具来获取 root shell。有关更多信息,请参阅我如何破解我的汽车指南:创建自定义固件
还有一个易于使用的“-a”参数,该参数贯穿每个步骤,以使用 ADB 后门制作未加密的固件文件。
我将编译好的 x64 Linux 二进制文件放入 Kali VM 上的一个新文件夹中。然后,我将 IVI 的最新固件更新 zip (encd2vsystempackage_134.100.220927.zip) 传输到同一文件夹中。
然后我运行以下命令来创建后门固件更新:
sudo ./HyundaiFirmwareDecrypter enc_d2vsystem_package_134.100.220927.zip -a
经过一分钟的等待,我手头有了一个有后门、未加密的固件更新。我所要做的就是弄清楚如何闪烁它。
我必须做的一切
好吧,好吧,我这里的副标题有点戏剧性。在我的车上刷新更新实际上非常简单。
我的主机运行版本 098.152.211130,这是现代修复我之前发现的问题之前的最后一次更新。
因为这是在他们的安全审核之前,所以对于可用于刷新未经授权的更新的功能更加宽松。
在工程模式中有一个名为“USB Image”的选项
“更新全部”按钮将使 IVI 重新启动进入恢复模式,并按照我上面制作的漂亮流程图从闪存驱动器刷新 system_update.zip 文件。
按下一个按钮并稍等片刻后,我的 IVI 就闪烁了最新、最好的更新,同时保持了 root 访问权限。(我绝对没有多次搞乱散列或 zip 格式,从而导致无数的调试会话以及在汽车和我的工作站之间来回旅行,哈哈,当然不是。)
后门?
这对我来说很棒,我的主机仍然有后门,并且它正在运行最新、最好的固件。但其他人呢?
我决定研究一下最新的固件,看看它是否可以被黑客入侵,别担心,我找到了另一种方法。
通过我对主机如何工作的努力研究,我已经反编译了无数(约 47)个应用程序中的无数功能。
工程模式应用程序是反编译最多的应用程序之一。也有充分的理由,它具有整个系统中一些最有趣的功能。
在整个代码中,我看到应用程序的变体编码部分多次提到“强制更新”弹出窗口。根据我反编译的代码,很清楚它做了什么。它会尝试在 USB 驱动器中查找任何看起来像系统更新的 zip 文件,然后重新启动到恢复模式,并将其作为选定的更新文件。
我什至看到它有某种密码屏幕可以访问它。(根据 MD5 哈希,密码为 3600)
经过一次小小的冒险/切题试图找出安装应用程序的所有秘密后,我回到工程模式应用程序并决定弄清楚如何到达此屏幕。
根据我在二进制文件中找到的许多字符串,强制更新屏幕以某种方式链接到变体编码屏幕。
因为在变体编码屏幕上看不到与系统更新相关的条目,所以我知道存在可能性:一种是该功能在生产 IVI 上被禁用,或者有一些秘密方法可以进入屏幕,例如工程模式或经销商如何进入模式。
我看得更深了。我没有找到提到构建变体编码 UI 的屏幕,因此它不仅仅是仅用于开发单元的菜单选项。我也没有太多运气找到隐藏的按钮,比如用于进入工程模式的按钮。因此,我开始寻找使用按键的功能,例如经销商模式。
要进入经销商模式,您必须:
将音量设置为7,按下调谐旋钮,
将音量设置为 3,按下调谐旋钮,
将音量设置为1,按下调谐旋钮
最后一次按下调谐旋钮时,会显示密码屏幕。输入密码 (2400) 后,经销商模式应用程序就会启动。
有趣的是,在查看与 VariantCodingDisplay 相关的函数时,我发现了一个在按下调谐旋钮时触发的函数。
这个函数恰好很容易阅读。它似乎遵循经销商模式使用的相同模式。
这次的组合是 7, 5, 3,而不是 7, 3, 1。
因为此事件直接在 VariantCodingDisplay 上,所以我猜测如果我在 Variant Coding 屏幕上使用音量和调谐器旋钮输入该代码,则会显示强制更新密码输入。
我去了我的车。我进入工程模式却发现他们更新了密码(并且密码长度增加了一倍!)。幸运的是,密码列表可以在工程模式应用程序中轻松找到,并且在网上的许多地方也列出了。
对我有用的工程模式密码是 26031236。
变体编码有自己的密码条目,并且还有一个闪亮的、新的、更长的密码。使用我上面链接的相同列表,我发现代码是 23060663。
当我进入“变体编码”菜单时,我拨入了秘密卷代码。
7、调谐旋钮、5、调谐旋钮、3、调谐旋钮…
瞧:
输入之前找到的密码“3600”后,它快速扫描我的闪存驱动器并建议刷新我的未加密更新。
选择“是”后,主机重新启动并成功重新安装我的更新文件。这也意味着它没有进行任何版本检查,因为版本是相同的。这意味着如果需要,此“强制更新”功能可用于降级主机。
结果
基于 D-Audio 2V 的主机现在再次开放进行修补和修改。
但是等等,这一切的发生难道只是为了它最终的结局吗?
给现代/mobis的一点信息
我知道我对贵公司的业务决策几乎没有影响力,而且我知道您已经对 ccOS 制定了宏伟的计划,但如果您仍然愿意倾听,我将不胜感激。
我认为你们开发的这个平台非常酷并且有很大的潜力。但我认为锁定这个平台是一个错误。
将来的某个时候,也许不是今天,但不久之后,您将停止支持这些主机。无论是只是不添加新功能(我认为我们已经过去了),不修复错误,还是只是总体上不支持该平台,总有一天这些简洁的小工具将不再受到支持。
主机是完成远程启动、远程气候控制和远程监控的地方。更不用说导航和音乐播放发生的地方了。
如果 BlueLink 有一天停止工作,如果 Android Auto、Apple CarPlay 或简单的音乐播放开始出现故障,或者出现像 2038 年问题这样的错误。我们车辆的价值、寿命和实用性将严重下降。
但与车辆的大多数其他部件不同,我们可以在损坏时更换它们,但我们无法对主机进行此操作。不要误会我的意思,我知道售后主机单元是存在的,但由于它们的专有性质,实际上没有任何东西能够在其功能方面取代原始主机单元,尤其是紧密的车辆集成。
这导致了我的建议。如果你要开放这个平台,车主甚至其他企业都可以从你离开的地方继续并支持它更长的时间。
我所说的“开放平台”是指使系统支持外部开发的应用程序和/或外部开发的系统更新。您不需要开源所有代码。虽然那很好,但我理解保护公司(或其他公司)知识产权的愿望或需要。
我也明白一切都不是非黑即白的,我知道还有减轻法律责任之类的考虑。但话又说回来,任何白痴都可以将平板电脑用胶带粘在他们的汽车上,插入 OBD2 适配器,并且同样危险(如果不是更危险的话)。如果平台开放,ccOS 和其他可用的 API 实际上可以成为防止任何对车辆产生负面影响的安全措施。与尝试直接使用 CAN 总线相比,为主机开发应用程序是更安全的选择。由于所有这些以及更多原因,我建议开放该平台。
以下是我认为要使平台“开放”需要做的一些布局:
提供系统头文件,以便开发应用程序。
这可以很简单,就像再次在系统更新中包含头文件一样,就像您以前所做的那样。
提供安装应用程序/固件更新的方法。
最简单的选择是在工程模式下重新启用“更新全部”按钮以允许未加密的更新。(或保留当前的强制更新功能。)
我还应该说,这不需要现代/摩比斯的明确许可。就像需要现代/摩比斯的特定钥匙来“解锁”主机一样。因为这仍然需要您维护此功能,并且它不会帮助那些在您停止分发密钥后需要解锁它的人。
还有很多事情可以做,这些事情对开放平台很有帮助,但无论如何都不是必需的。
使应用程序列表动态化,并允许从主屏幕添加和启动新应用程序。
通过从 .json 文件加载可用应用程序可以轻松完成此操作。
公开 ccOS 文档,以帮助开发 ccOS 集成应用程序。
继续为现有车辆开发 ccOS 和 ccOS API。
有关如何为 D-Audio 2V 应用程序设置开发环境的公开文档。
将内置应用程序的某些/部分开源以作为示例。
制作某种安装程序系统以使添加或更新应用程序变得更容易。
无论如何,将来可能有必要进行开发以允许实时 OTA 更新。
现代/摩比斯运行的自定义应用程序市场,包括其他开发人员制作的经批准的应用程序(如应用程序商店)
在 BlueLink 或其他方式中拥有一个等级,只需支付 SIM 数据费用(+ 小额附加费?)。它将允许开发使用互联网的应用程序,并且即使在 BlueLink 关闭之后,也可以为您提供少量但可靠的持续收入来源。
在公司锁定平台并减少功能的时代,您可能会成为打破常规的公司。现代汽车有机会成为这里的领导者,引导整个行业更加开放,让所有参与者受益。
免责声明:
本公众号所有文章均为用于技术沟通交流,请勿用于其他用途,否则后果自负。
第二十七条:任何个人和组织不得从事非法侵入他人网络、干扰他人网络正常功能、窃取网络数据等危害网络安全的活动;不得提供专门用于从事侵入网络、干扰网络正常功能及防护措施、窃取网络数据等危害网络安全活动的程序和工具;明知他人从事危害网络安全的活动,不得为其提供技术支持、广告推广、支付结算等帮助