出发
为了弄清楚应用程序最终如何将信号发送到 CAN 总线,我需要至少找到一个需要 CAN 总线的函数的好示例,以便我可以对其进行追溯。我决定使用 ccOS 的 HBody 库。
正如我在第 3 部分中提到的,ccOS 是现代汽车和 Nvidia 的联合项目,旨在打造高度集成到车辆中的新型汽车操作系统。D-Audio 2V 中提供了 ccOS 核心组件的一些早期工作,包括几个库。这些库提供了许多功能,可以查询汽车的某些状态,例如检查车门是否打开,或执行某些操作,例如设置空调温度。库要做这些事情,不可避免地需要访问 CAN 总线。因此,我首先对 /usr/lib/libHVehicle.so.1.0.0 中找到的 HVehicle 库进行逆向工程。
我选择研究的方法是 HBody 上的 requestDoorLock()。我在HVehicle库中找到了它的实现方法“ccos::vehicle::general::HBody::HBodyImpl::requestDoorLock”。
该方法调用了另一个名为“requestRemoteControlVehicle”的函数,该函数最终调用了 HBodyGDBus 上的同名方法。
GD总线
好的,什么是“GDBus”?
我在 Google 上搜索了这个术语,看起来“GDBus”是“D-Bus 的特定实现”。
很好,可以把事情弄清楚。
后来又谷歌了一下,我了解到 D-Bus 是“Desktop Bus”的缩写,是一种基于消息的通信机制,允许计算机上的不同进程相互通信。整洁的。
因此,看起来某些其他进程必须接收此“requestRemoteControlVehicle”D-Bus 方法调用,并且该进程与 CAN 总线进行通信。
我拿出我最喜欢的工具之一,Agent Ransack(一款超酷且快速的 Windows 文件搜索工具),并在我的主机固件中搜索字符串“requestRemoteControlVehicle”。
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 总线。我所要做的就是找出哪个插座。
由于发送函数需要使用 socket() 函数打开套接字,因此我搜索了它的用法。在automotivefw(MicomPacketRunner 的构造函数)中只调用了一次socket():
socket() 方法有 3 个参数:域、类型和协议。域设置为 1,表示它是 AFUNIX/Unix 域套接字。根据 _socketmakesockaddrun() 调用中的字符串“micommux”,我猜测我正在寻找的套接字是一个名为“micommux”的 Unix 域套接字。
当我第一次尝试在第 2 部分中创建的后门时,我保存了 netstat 命令的输出。我仔细查看了它,发现了多个到“@micom_mux”套接字的连接。名称中的“@”表示套接字保存在不在文件系统中的抽象命名空间中。
所以现在我需要访问该套接字,有什么比使用 socat 更好的方法呢?socat 是 Linux 中内置的一个强大的中继实用程序,它允许您在两个连接之间中继数据。我决定获取数据的最简单方法是将 @micom_mux 套接字的输出通过管道传输到我的闪存驱动器中,我可以稍后复制和分析。
我“cd”到我的闪存驱动器并运行命令:
socat ABSTRACT-CLIENT:micom_mux STDIO > micomOutput
等了一会儿。大约一分钟后,我终止了该进程并取出闪存驱动器来查看该文件。我打开它,很快发现它是空的。我重试了该命令,并在车内尝试了一些操作,例如打开和锁定车门,然后停止 socat 并运行“sync”。我查看了该文件,再次发现它完全是空的。
套接字不得自动通过它发送 CAN 总线数据。也许它需要某种启动数据包或秘密才能开始监听数据?
我开始通过查看与“micommux”套接字通信的每个应用程序来搜索这个神奇的数据包。我发现每个应用程序连接到 micommux 套接字后都会发送特定的数据包。
我发现至少有 6 个应用程序/库使用了 micom_mux:
app-logic-nmode:
应用程序逻辑 nmode:
Used for N Mode which is an app which allow users to monitor and tune certain advanced settings like the Launch RPM or traction control in Hyundai N vehicles. 用于 N 模式,该应用程序允许用户监控和调整某些高级设置,例如现代 N 车辆中的启动转速或牵引力控制。
CANManager:
CAN管理器:
Used by BlueLink to talk to the CAN bus. 由 BlueLink 用于与 CAN 总线通信。
RDOPacketRunner:
RDOPacketRunner:
Used by the radio app. 由广播应用程序使用。
HevService:
混合动力汽车服务:
Used to monitor hybrid vehicle stats. 用于监控混合动力车辆的统计数据。
EvService:
电动汽车服务:
Used to monitor electric vehicle stats. 用于监控电动汽车统计数据。
Automotivefw:
汽车fw:
The core framework that most apps talk to to control the vehicle. 大多数应用程序用来控制车辆的核心框架。
Each of these apps had different Magic packets: 这些应用程序中的每一个都有不同的 Magic 数据包:
现在我只需要弄清楚这些意味着什么。通过比较这些数据包,我确实注意到了一些模式:
They all started with FF8AFF 他们都是从FF8AFF开始的
Which is followed by either a F1 or F3 后面跟着 F1 或 F3
Then FFFF 然后FFFF
A length (In this case 0001, 0002, or 0003) 长度(在本例中为 0001、0002 或 0003)
n amount of bytes of the length previously, most end with 00 前面长度的n个字节,大部分以00结尾
micomd
我在 /usr/bin/ 中发现了一个名为“micomd”的应用程序,它似乎是套接字的源。经过逆向工程后,我发现该应用程序遵循以下流程:
Connects to “/dev/tccipc”. (Where the micom data comes from) 连接到“/dev/tccipc”。(micom数据来自哪里)
Creates the “micommux” abstract unix socket. 创建“micommux”抽象 unix 套接字。
Waits for a new client. 等待新客户。
Once a client joins it will start reading a packet.
一旦客户端加入,它将开始读取数据包。
It will check if it is a Magic Packet by seeing if the 2nd byte is 8A and the 3rd byte is FF. 它将通过查看第 2 个字节是否为 8A、第 3 个字节是否为 FF 来检查它是否为 Magic Packet。
Then it checks the 4th byte
然后它检查第 4 个字节
F1: It is an AutomotiveFW packet, it reads the rest of the packet and sends out a special hardcoded packet to the CAN bus. F1:这是一个 AutomotiveFW 数据包,它读取数据包的其余部分并向 CAN 总线发送一个特殊的硬编码数据包。
F2: It is an AutomotiveFW packet, it reads the rest of the packet F2:这是一个AutomotiveFW数据包,它读取数据包的其余部分
F3: It is a different app’s packet, it reads the rest of the packet F3:这是另一个应用程序的数据包,它读取数据包的其余部分
这解释了一些事情,但我仍然不知道整个数据包的含义,尤其是可变长度部分。但是,在查看 micomd 时,我发现它已被大量记录,所以我决定检查一下。
在查看我以前的日志转储中的 micom 相关日志时,我发现了这一点:
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
嗯,看起来这里的最后一个日志条目是一个 micom 数据包,上面的日志将其与名称分开。
这可能就是我所需要的。看起来数据包格式是:
Always FF (Byte) 始终为 FF(字节)
SID (Byte) SID(字节)
RID (Byte) RID(字节)
Type: (Byte) 类型:(字节)
Function (Int16, Big Endian) 函数(Int16,大端)
Payload Length (Int16, Big Endian) 有效负载长度(Int16,大端)
Payload (Array of {Payload Length} Bytes) 有效负载({有效负载长度}字节数组)
我还发现了一些与 micom 相关的日志:
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
看起来,一个名为 checkDiagState() 的函数发出了一个 SID 为 87、RID 为 03 的数据包,并收到了一个 SID 和 RID 颠倒的数据包。我猜测 SID 是发送者 ID,RID 是接收者 ID。
在查看所有日志时,我注意到我看到的每个 RID 都在我之前发现的魔法数据包的有效负载中:
数据包示例:
MicomD : ReceivedData[SUCCESS]: m => c : : ff 03 87 01 83 5b 00 05 01 22 2e 00 aa
RID 为 87,位于 RDOPacketRunner 的有效负载中
这可能意味着魔术数据包用于订阅来自具有特定 RID 设置的 micom 的数据包。
好吧,因为我可能知道魔术包的作用,所以我现在可以使用 socat 尝试读取一些真实数据。我使用以下命令使用 socat 打开 micom 套接字,从 CANManager (FF8AFFF3FFFF00028E00) 发送魔术数据包,然后将其收到的所有内容写入我的闪存驱动器上的文件中:
printf "\xFF\x8A\xFF\xF3\xFF\xFF\x00\x02\x8E\x00" | socat ABSTRACT-CLIENT:micom_mux STDIO > micomOutput
让它运行一分钟后,我关闭了 socat 进程,运行同步,并拔出了我的闪存驱动器。这次我有一些数据,现在我只需以某种方式读取它。
读取数据
为了获得数据的基本布局,我使用十六进制编辑器 010 来查看数据。我捕获的第一个数据包是:
Packet Start: FF 数据包开始:FF
SID: 0B 安全识别码:0B
RID: 8E 行驶里程:8E
Type: 01 类型:01
Function: 8BC7 功能:8BC7
Payload Length: 0100 (256) 有效负载长度:0100 (256)
我想我捕获了一些大数据包,这可能会导致很难看到并突出显示一个数据包的结束位置和另一个数据包的开始位置。但幸运的是,010 有一个超级方便的工具帮助我解决了我的问题:模板。
模板是一个超级酷的功能,它允许您编写类似布局的 C/C++ 结构,并让 010 将其转换为更易读/可解析的数据。
//------------------------------------------------
//--- 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 字节的有效负载。
Mason数字,它们意味着什么
我现在可以分辨出一个数据包在哪里结束,另一个数据包从哪里开始,但我仍然不知道其中的数据。
我本质上是一名程序员,所以我决定编写一个程序,该程序接收这些数据包的数据流,然后实时记录/解码它们。
我最终创建了一个 C# 程序,该程序在 TCP 端口上侦听该数据流并对其进行解析。
我使用以下命令将流量从 @micom_mux 中继到在我的笔记本电脑上运行的数据包解码器服务器:
socat ABSTRACT-CLIENT:micom_mux TCP4:192.168.0.3:6999
然后我开始摆弄它。我使用我的程序发送一个包含所有可能的 RID 的魔术数据包,这样我就可以监听所有内容,然后我找出了看起来不像嘈杂“垃圾”的 RIDS,然后隔离了几个有用的数据包。
我发现的数据包之一是指示驾驶员车门是否打开或关闭的数据包:
Packet Start: FF 数据包开始:FF
SID: 44 安全号:44
RID: C1 行驶路线:C1
Type: 00 类型:00
Function: C403 功能:C403
PayloadLength: 02 有效负载长度:02
Payload: (00 if closed 01 if open) ?? (Random byte? Checksum? Timing?) 有效负载:(如果关闭则为 00,如果打开则为 01)?(随机字节?校验和?计时?)
我一边继续转储微电脑数据,一边尝试执行各种操作。慢慢地,但我确实又找到了一些数据包。
我什至制作了一个小 UI 来显示门的状态:
一旦我有机会清理它并向其中添加更多数据包类型,我可能会在将来的某个时候发布该应用程序。
一旦我掌握了读取这些数据的窍门,我就去看看是否可以从我的钥匙扣中获取任何数据,不幸的是我不能。我无法读取遥控钥匙上的按钮按下情况,也无法查看遥控钥匙是否在范围内。我唯一能读到的是门锁的状态(如果门锁发生变化)。这严重限制了我向密钥添加新功能的能力。
免责声明:
本公众号所有文章均为用于技术沟通交流,请勿用于其他用途,否则后果自负。
第二十七条:任何个人和组织不得从事非法侵入他人网络、干扰他人网络正常功能、窃取网络数据等危害网络安全的活动;不得提供专门用于从事侵入网络、干扰网络正常功能及防护措施、窃取网络数据等危害网络安全活动的程序和工具;明知他人从事危害网络安全的活动,不得为其提供技术支持、广告推广、支付结算等帮助
第十二条:国家保护公民、法人和其他组织依法使用网络的权利,促进网络接入普及,提升网络服务水平,为社会提供安全、便利的网络服务,保障网络信息依法有序自由流动。
任何个人和组织使用网络应当遵守宪法法律,遵守公共秩序,尊重社会公德,不得危害网络安全,不得利用网络从事危害国家安全、荣誉和利益,煽动颠覆国家政权、推翻社会主义制度,煽动分裂国家、破坏国家统一,宣扬恐怖主义、极端主义,宣扬民族仇恨、民族歧视,传播暴力、淫秽色情信息,编造、传播虚假信息扰乱经济秩序和社会秩序,以及侵害他人名誉、隐私、知识产权和其他合法权益等活动。