本合集共7部分,本篇为第五部分
如果您还没有阅读前几部分,请先阅读第一部分,第二部分,第三部分。
Why
在黑掉我的车载主机之后的某个时候,我想到了一个主意。我想要创建一个应用程序,为钥匙增加更多功能,在车辆上执行其他操作,比如启动引擎。这将使我所做的研究和工作对普通用户真正有用。
在思考最佳实现方式时,我决定弄清楚系统中的各种应用程序如何与汽车的其余部分通信。我知道这最终将引导我了解如何至少读取和写入汽车的CAN总线。如果我能访问这些数据,我可能会看到钥匙扣上的按钮按压,或者至少看到按钮按压的后果。
Starting Off
为了弄清楚应用程序最终如何将信号发送到CAN总线,我需要找到至少一个CAN总线的函数示例,这样我就可以从源头追踪回去。我决定选择ccOS的HBody库。
正如我在第三部分中提到的,ccOS是由现代和Nvidia联合开发的一个新汽车操作系统项目,它与汽车高度集成。一些ccOS的核心组件的早期工作,包括一些库在D-Audio 2V中是可用的。这些库提供了许多可以查询汽车某些状态的函数,比如检查门是否打开,或者执行某些操作,比如设置空调的温度。为了让库执行这些事情,它不可避免地需要访问CAN总线。所以我首先通过逆向工程来研究在/usr/lib/libHVehicle.so.1.0.0中发现的HVehicle库。
我选择的是HBody上的requestDoorLock()。我发现它的实现方法叫做“ccos::vehicle::general::HBody::HBodyImpl::requestDoorLock”在HVehicle库中。
Agent Ransack发现了3个包含方法名称的文件,automotivefw,HBody.h和libHVehicle.so.1.0.0。我已经知道了头文件本身和它的库,所以它一定在automotivefw中!
我搜索了方法名称,找到了HBodyStubImpl::requestRemoteControlVehicle()。在其中,我发现了多次调用HBodyStubImpl::sendPacket()方法。我猜测这就是向CAN总线发送数据的方法。
我进入那个函数,发现它调用了MicomService::sendPacket(),调用了MicomPacketRunner::sendPacket(),调用了MicomPacketRunner::rawsend(),最后使用了原生的send()函数来实际发送数据。
send()方法被用来通过套接字发送一些缓冲区。这意味着CAN总线直接或间接地可以通过系统中打开的某个套接字访问。我所要做的就是找出哪个套接字。
由于send函数需要使用socket()函数打开一个套接字,我搜索了它的使用情况。在automotivefw中只有一个对socket()的调用,MicomPacketRunner的构造函数:
当我最初尝试在第二部分中创建的backdoor时,我保存了netstat命令的输出。我查看了它,并发现多个连接到“@micom_mux”套接字。名称中的“@”意味着套接字被保存在一个不在文件系统中的抽象命名空间中。
所以现在我需要访问那个套接字,有什么比使用socat更好的方法呢?socat是一个强大的中继实用程序,内置于linux中,允许你在两个连接之间中继数据。我决定最简单的方法是将@micom_mux套接字的输出管道到我的闪存驱动器,我可以稍后复制并分析。
我“cd”进入我的闪存驱动器,然后运行命令:
socat ABSTRACT-CLIENT:micom_mux STDIO > micomOutput
等待了一会儿。大约一分钟后,我终止了进程并拔出闪存驱动器查看文件。我打开它,很快发现它完全是空的。我重试了命令,并在车上尝试了一些操作,比如打开和锁定车门,然后停止socat并运行“sync”。我查看文件,再次发现它完全是空的。
套接字可能没有自动通过它发送CAN总线数据。也许它需要某种起始数据包或秘密才能开始监听数据?
App | Magic Packet |
---|---|
app-logic-nmode |
FF8AFFF3FFFF00038ACA00 |
CANManager |
FF8AFFF3FFFF00028E00 |
RDOPacketRunner |
FF8AFFF3FFFF0003879000 |
HevService |
FF8AFFF3FFFF00028800 |
EvService |
FF8AFFF3FFFF000388C200 |
Automotivefw |
FF8AFFF1FFFF00018A |
我发现/usr/bin/中有一个名为“micomd”的应用程序,它似乎是套接字的来源。通过逆向工程,我发现应用程序遵循了这个流程:
- 连接到“/dev/tcc_ipc”。(micom数据来自哪里)
- 创建“micom_mux”抽象unix socket。
- 等待新客户端。
- 一旦客户端加入,它将开始读取数据包。
- 它将检查是否是Magic Packet,方法是查看第二个字节是否为8A,第三个字节是否为FF。
- 然后它检查第四个字节
- F1:它是AutomotiveFW数据包,它读取数据包的其余部分,并向CAN总线发送一个特殊的硬编码数据包。
- F2:它是AutomotiveFW数据包,它读取数据包的其余部分
- F3:它是不同应用程序的数据包,它读取数据包的其余部分
Hev_Packet: sendPacket:0550:send sid:88, rid:0c, type:01, Func:0x0c03, paylL:0, FuncName:HEV_RESET_GRAPH_C
Hev_Packet: sendPacket:0556:send sid:88, rid:0c, type:01, Func:0x0c03, paylL:1, FuncName:HEV_RESET_GRAPH_C
Hev_Packet: send sid:88, rid:0c, type:01, Func:0x0c03, paylL:1, FuncName:HEV_RESET_GRAPH_C
Hev_Packet: S => D micom :ff880c010c030001:74
src/VRM/Service/DiagnosticUtils/DiagMainUtil.cpp checkDiagState 00120 checkDiagState(): diag type[3], state[0]
MicomD : send_data: c => m : ff 87 03 01 03 5b 00 02 01 21
MicomD : ReceivedData[SUCCESS]: m => c : : ff 03 87 01 83 5b 00 05 01 28 46 90 58
MicomD : ReceivedData[SUCCESS]: m => c : : ff 03 87 01 83 5b 00 05 01 22 2e 00 aa
有一个RID为87,在我的RDOPacketRunner中
App | Magic Packet |
---|---|
RDOPacketRunner |
FF8AFFF3FFFF0003879000 |
printf "xFFx8AxFFxF3xFFxFFx00x02x8Ex00" | socat ABSTRACT-CLIENT:micom_mux STDIO > micomOutput
运行了大约一分钟,我关闭了socat进程,运行了sync,然后拔出了我的闪存驱动器。这一次我有了数据,现在我只需要某种方式来读取它。
//------------------------------------------------
//--- 010 Editor v12.0.1 Binary Template
//
// File: MicomPacketTemplate
// Authors: greenluigi1
// Version: 1.0
// Purpose: Decode raw data stream from micomd process on D-Audio 2V systems
// Category:
// File Mask:
// ID Bytes:
// History:
//------------------------------------------------
BigEndian();
struct
{
while(!FEof())
{
struct
{
byte FF;
byte sid;
byte rid;
byte type;
ushort func;
ushort payloadLength;
byte payload[payloadLength];
} MicomPacket;
}
} MicomDataStream;
这个我创建的模板这样做:声明文件代表一个名为MicomDataStream的结构体。MicomDataStream包含许多MicomPackets,它将继续读取MicomPackets,直到到达文件末尾(!FEof)。MicomPacket包含7个字段:FF,sid,rid,type,func,payloadLength和payload。
因为010能够解析我收到的每个数据包,我可以看到我总共收到了1070个数据包,它们都有256字节的有效载荷。
socat ABSTRACT-CLIENT:micom_mux TCP4:192.168.0.3:6999
然后我开始摆弄它。我使用我的程序发送一个带有每个可能RID的魔法数据包,这样我可以监听所有内容,然后我弄清楚了哪些RID看起来不像嘈杂的“垃圾”,然后隔离了几个有用的数据包。
- Payload: (00 if closed 01 if open) ?? (Random byte? Checksum? Timing?)
一旦我掌握了读取这些数据的技巧,我就去看看是否能够从我的钥匙扣获取任何数据,不幸的是,我不能。我无法读取钥匙扣上的按钮按压,也看不到钥匙扣是否在范围内。我只能读取如果车门锁的状态发生变化,这严重限制了我为钥匙扣添加新功能的能力。
原文始发于微信公众号(安全脉脉):HowIHackedMyCar 2021款 现代IONIQ (五)CAN Bus分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论