180 lines
15 KiB
Markdown
180 lines
15 KiB
Markdown
---
|
||
name: re-automotive
|
||
description: >
|
||
汽车逆向:CAN 总线、ECU 固件、AUTOSAR Classic(RTE/runnable/BSW)与 Adaptive(执行管理/清单)。
|
||
触发词:汽车、CAN、ECU、车载、OBD、UDS、AUTOSAR、RTE、runnable、SWC、BSW、E_OS_ACCESS、
|
||
Adaptive、ARA、Execution Management、Function Group、Machine State、Execution Manifest
|
||
capabilities: [automotive-analysis]
|
||
---
|
||
|
||
# 汽车逆向(CAN 总线 / ECU / AUTOSAR Classic)
|
||
|
||
## 何时使用 / 何时不用
|
||
|
||
- 前置:拿到 ARXML 或车载软件包但不确定是 Classic 还是 Adaptive → [[re-analyze/system-fingerprints]] 的车载段
|
||
- 用:拿到车辆 CAN 总线流量(candump 日志 / .asc / .blf)需要解析报文与信号
|
||
- 用:车载设备(车机、T-Box、IVI、ECU 升级包)逆向
|
||
- 用:OBD-II / UDS 诊断会话分析(读 DTC、安全访问、标定读写)
|
||
- 用:ECU 固件提取与后续解包(配合 [[re-firmware]])
|
||
- 用:**AUTOSAR Classic 软件**(SWC/runnable/RTE/BSW)——执行图由配置与代码生成决定,不能用普通调用图理解([[autosar-classic]])
|
||
- 不用:只有 ECU 固件文件、没有总线访问(直接 [[re-fw-extract]])
|
||
- 不用:需要跑固件观察行为([[re-fw-emulate]])
|
||
- 不用:需要 JTAG/SWD 读 flash([[re-hardware-io]])
|
||
- 红线:只对自有/获授权车辆与测试台架测试(见步骤 5)
|
||
|
||
## 工具准备
|
||
|
||
所有工具先验证再使用。实车/实总线测试默认物理隔离——[[re-analyze/platform-tips]] 最高原则:仅自有或获授权测试车辆/台架可接入;实验室起步用 vcan 虚拟总线,不接真车。
|
||
|
||
### can-utils —— candump/cansend/cangen/canplayer 主力
|
||
|
||
- Linux: `apt install can-utils`(Debian/Ubuntu)/ `dnf install can-utils`(Fedora)/ Arch: 无官方包 → AUR(can-utils)或源码构建(`git clone https://github.com/linux-can/can-utils && cd can-utils && ./autogen.sh && ./configure && make`)
|
||
- macOS/Windows: 无官方包(SocketCAN 为 Linux 内核特性)→ WSL/虚拟机内 Linux 版
|
||
- 验证: `candump --help`;`cansend --help`
|
||
|
||
### socketcan —— Linux 内核 CAN 栈(vcan 虚拟总线 / can-isotp)
|
||
|
||
- Linux: 内核自带模块(5.10+ 内核含 can-isotp):`modprobe can vcan can-isotp`;`ip link` 来自 iproute2(基础系统自带)
|
||
- macOS/Windows: 不支持 → WSL2/虚拟机(需 Linux 5.10+)
|
||
- 验证: `modprobe vcan && ip link add dev vcan0 type vcan && ip link set up vcan0 && ip -details link show vcan0`
|
||
|
||
### USB-CAN 适配器 —— gs_usb 系(canable/candlelight)即插即用
|
||
|
||
- Linux: 内核自带 gs_usb 驱动(drivers/net/can/usb/gs_usb.c);插入后 `dmesg` 应注册 can0(个别发行版 CAN 驱动在 modules-extra 包,`modprobe gs_usb` 报错时装对应 extra 包)
|
||
- macOS/Windows: 用厂商驱动 + 厂商软件,或经虚拟机透传
|
||
- 验证: `modprobe gs_usb`;`ip -details link show can0`
|
||
|
||
### wireshark / tshark —— CAN/ISO-TP/UDS 解析
|
||
|
||
- 安装同 [[re-netcap]] 工具准备(apt/dnf/pacman/brew/choco);CAN(socketcan / Vector ASCII / BSLogger)、ISO-TP、UDS(ISO 14229)dissector 内置
|
||
- 验证: `tshark --version`;candump `-l` 日志可直接 File → Open 打开
|
||
|
||
### python-can —— 脚本化解析(跨平台)
|
||
|
||
- 全平台: `pip install python-can`
|
||
- Linux: `apt install python3-can`(Debian/Ubuntu)/ `dnf install python3-can`(Fedora)/ `pacman -S python-can`(Arch extra)
|
||
- 验证: `python3 -c "import can; print(can.__version__)"`
|
||
|
||
### OBD-II / UDS 诊断库
|
||
|
||
- python-obd(ELM327 适配器,OBD-II 标准服务): 无官方包 → `pip install obd`;验证 `python3 -c "import obd"`
|
||
- udsoncan(UDS ISO 14229 客户端): 无官方包 → `pip install udsoncan isotp`;验证 `python3 -c "import udsoncan, isotp"`
|
||
- 注意:udsoncan 用 IsoTPSocketConnection 需要 Linux can-isotp 内核模块(5.10+)或 socketcan;无内核模块时用 PythonIsoTpConnection(走 python-can,跨平台但时序略差)
|
||
|
||
### scapy —— 汽车协议层(automotive 包,可选)
|
||
|
||
- 全平台: `pip install scapy`(含 `scapy.contrib.automotive` 与 `scapy.layers.can`)
|
||
- 验证: `python3 -c "from scapy.contrib.automotive.uds import UDS; from scapy.contrib.automotive.doip import DoIP; from scapy.layers.can import CAN; print('ok')"`
|
||
|
||
## 操作步骤
|
||
|
||
按顺序执行,每步记下结果。
|
||
|
||
1. **CAN 接入(USB-CAN / socketcan)**:
|
||
- 无硬件起步(vcan 虚拟总线):
|
||
```sh
|
||
sudo modprobe can vcan can-isotp
|
||
sudo ip link add dev vcan0 type vcan
|
||
sudo ip link set up vcan0
|
||
```
|
||
- USB-CAN(gs_usb 系):
|
||
```sh
|
||
sudo modprobe gs_usb
|
||
sudo ip link set can0 up type can bitrate 500000 # 500 kbit/s 经典波特率
|
||
ip -details link show can0 # 确认 bitrate / state UP
|
||
```
|
||
- 波特率必须与总线一致(错误波特率 = 错误帧刷屏,见坑 3)
|
||
|
||
2. **candump 抓包**:
|
||
```sh
|
||
candump -t a can0 # 实时查看(绝对时间戳)
|
||
candump -l can0 # 落盘日志(默认文件名带时间戳;无轮转——需要轮转交给 logrotate/外部脚本;Wireshark 可直接打开)
|
||
candump can0 -n 100 # 只抓 100 帧
|
||
candump can0,7E0:7FF # ID 过滤:接口名后接 can_id:mask(逗号分隔);掩码 7FF = 精确匹配 7E0,区间过滤用 7F8(匹配 7E0~7E7);CAN 无地址(见坑 1)
|
||
candump -f can.log can0 # 注意 -f 是"日志写入文件",不是过滤参数
|
||
```
|
||
|
||
3. **报文 ID / 周期 / 信号解析**:
|
||
```sh
|
||
cansniffer can0 # 按 ID 聚类、周期与变化字节高亮
|
||
candump can0 | awk '{print $2}' | sort | uniq -c # 按 ID 统计帧数(默认输出第 2 列是 ID;注意 -c 是颜色模式,不是计数)
|
||
canbusload can0@500000 # 总线负载率 %
|
||
```
|
||
- 周期消息(固定间隔 10/50/100ms 常见)→ 转速/车速等连续信号;事件消息(无规律)→ 开关/按钮
|
||
- 信号解包:先定字节序(Intel 小端 vs Motorola 大端,见坑 2)→ 逐字节变化观察(cansniffer 高亮变化字节)→ 用 cantools 按 DBC 定义解包(`pip install cantools`)
|
||
- 产出 ID 表:ID / 周期 / 长度 / 疑似信号 / 字节序
|
||
|
||
4. **UDS / OBD 诊断**:
|
||
- OBD-II 快速体检(标准诊断对 0x7E0/0x7E8,ELM327 适配器):
|
||
```python
|
||
import obd
|
||
conn = obd.OBD() # 自动探测适配器串口
|
||
conn.query(obd.commands.SPEED) # 车速
|
||
conn.query(obd.commands.GET_DTC) # 故障码
|
||
```
|
||
- UDS(ISO 14229,厂商诊断 ID 不固定,先抓包确认):
|
||
```python
|
||
import udsoncan, isotp
|
||
from udsoncan.connections import IsoTPSocketConnection
|
||
from udsoncan.client import Client, ClientConfig
|
||
from udsoncan.services import DiagnosticSessionControl, DataIdentifier
|
||
|
||
# 关键:DID 的 payload 怎么编解码,由 config['data_identifiers'] 决定——
|
||
# 该 DID 不在映射里时,udsoncan 在发送请求**之前**就抛 ConfigError
|
||
#("contains no definition for data identifier 0xf190");映射里有但 codec 不是
|
||
# DidCodec 实例则抛 ValueError("is not a valid DidCodec")。
|
||
# 配置只需给出要覆盖的键:Client 会用 udsoncan.configs.default_client_config 补齐其余项,
|
||
# 所以 data_identifiers 之外不必逐项照抄默认值。
|
||
config = ClientConfig()
|
||
config['data_identifiers'] = {
|
||
0xF190: ..., # VIN 的标准 DID;codec = 17 字节 ASCII(用库自带的类型,或自定义 DidCodec 子类)
|
||
}
|
||
|
||
conn = IsoTPSocketConnection('can0', isotp.Address(isotp.AddressingMode.Normal_11bits, rxid=0x7E8, txid=0x7E0))
|
||
with Client(conn, request_timeout=2, config=config) as client:
|
||
client.change_session(DiagnosticSessionControl.Session.extendedDiagnosticSession)
|
||
vin = client.read_data_by_identifier(DataIdentifier.VIN)
|
||
```
|
||
- **读/写 DID 必须先配 codec**:`read_data_by_identifier` / `write_data_by_identifier` 依赖 client 配置里的 `data_identifiers`(DID → codec 映射)——**响应只是字节串,缺了 codec 就无从解码**,udsoncan 会以配置错误终止而不是"返回原始字节"。官方示例一律写成 `Client(..., config=MyCar.config)` 并注明 VIN 的 codec 配在 client 配置里。只想看原始载荷就别走 DID 解码路径,用不做解码的低层接口
|
||
- 服务速查:0x10 会话控制 / 0x22 读数据标识符 / 0x27 安全访问(seed-key)/ 0x2E 写数据 / 0x31 例程控制 / 0x3E 保持活动 / 0x11 ECU 复位 / 0x34-0x36-0x37 固件下载流程
|
||
- Wireshark 打开 candump 日志 → ISO-TP/UDS dissector 自动解码诊断会话
|
||
|
||
5. **ECU 固件**:
|
||
- UDS 下载流程:标准传输核心是 `0x34 RequestDownload → 0x36 TransferData × N → 0x37 RequestTransferExit`;实际 ECU 编程通常先按 OEM 要求进 `0x10` 编程会话,服务受安全级别保护时执行 `0x27`(安全访问),擦除/完整性校验多经 OEM 定义的 `0x31`(例程控制),过程可能需 `0x3E` 保活、结束可能 `0x11` 复位——具体序列从 ECU 行为/ODX/固件恢复,不假定固定顺序。或 `0x23` 读内存获取固件 → 转 [[re-fw-extract]] 解包(ECU/车机固件与 IoT 固件同流程)
|
||
- 直接拿到的升级包文件 → [[re-fw-extract]];固件 ELF → [[re-binary-core]];需硬件 flash 读取 → [[re-hardware-io]]
|
||
|
||
## IVI / T-Box / V2X 路径
|
||
|
||
按目标形态分流(判定规则):
|
||
|
||
- **IVI(车载娱乐系统)**:本质是 Android/Linux 系统——应用层走 [[re-apk]],系统镜像走 [[re-firmware]];关注 OEM 定制层(启动器、诊断接口、ADB/调试口)
|
||
- **T-Box(远程信息处理箱)**:蜂窝模组(AT 指令接口)、MCU 固件([[re-fw-extract]] 提取分析)、远程控制协议(车控指令——门锁/空调/启动,走 [[re-protocol]])
|
||
- **V2X(车联网通信)**:DSRC / C-V2X 帧结构 → [[re-protocol]] / [[re-ics]] 路径(协议状态机重建、PC5/Uu 接口区分);物理层信号采集走 [[re-sdr]]
|
||
- 判定规则:应用层 → 移动/系统路径([[re-apk]] / [[re-firmware]]);通信层 → 协议路径([[re-protocol]] / [[re-ics]]);固件层 → 固件路径([[re-fw-extract]] / [[re-fw-rootfs]])
|
||
|
||
## 分支(references)
|
||
|
||
- [[autosar-classic]] —— **配置驱动 + 代码生成**的执行图:RunnableEntity 由 **RTEEvent** 激活、经 RTE 胶水映射到 **OS Task 或 ISR2**(**RTE 没有独立调度能力**),**无 caller 的 runnable 不是死代码**;**隐式访问**(`Rte_IRead_*`/`Rte_IWrite_*`)在 runnable 边界做快照与写回("消费者看到旧值"可能是语义);`E_OS_ACCESS` 来自 **OS-Application 所有权/保护**(默认拒绝,授权来自配置),不是参数错误;并发异常先查 OS 配置(Task 映射、优先级、抢占)
|
||
- [[autosar-adaptive]] —— **清单驱动的生命周期**:POSIX 进程 + ARA 功能簇;**Modelled Process ≠ OS 进程**(不要 1:1 比较);**"bin 存在但进程不存在"先查 Function Group State / Machine State**(EM 把状态翻译成进程集合);Execution Manifest 可按状态给同一可执行文件不同配置;EM 切换时先终止再按依赖启动
|
||
|
||
## 跨域联合
|
||
|
||
- [[re-firmware]]:本技能是其汽车分支(ECU 固件提取后 → 解包 → rootfs → 仿真)
|
||
- [[re-fw-extract]] / [[re-fw-rootfs]] / [[re-fw-emulate]]:ECU/车机固件解包、文件系统分析、仿真
|
||
- [[re-hardware-io]]:OBD 口抓包(CAN/UART)、JTAG/SWD 读 flash
|
||
- [[re-protocol]]:DoIP(UDS over Ethernet,13400 端口)等车载以太网协议重建;CAN 私有协议转 [[re-proto-rev]]
|
||
- [[re-sandbox]]:虚拟 CAN / DoIP 测试床隔离;固件仿真默认网络隔离([[re-analyze/platform-tips]] 最高原则)
|
||
- [[re-binary-core]]:固件内 ELF 深度静态分析
|
||
- [[re-crypto-id]] / [[re-crypto-decrypt]]:seed-key 算法、CAN 加密变体
|
||
|
||
## 常见坑与陷阱
|
||
|
||
- **CAN 无地址概念 → 过滤/归因错**:现象——想"按目标 ECU 过滤"却要么漏帧要么全抓;原因——CAN 帧无源目地址,只有 11/29 位仲裁 ID,ID 兼具优先级与"身份"(ID 越小优先级越高);对策——先全量抓(candump)再按 ID 聚类归类(cansniffer,或对 candump 输出管道 awk/sort/uniq 统计),用 `candump can0,ID:mask` 过滤,不预设主机概念
|
||
- **信号位打包错**:现象——信号值乱(转速负值/巨大、多字节字段错位);原因——信号跨字节跨位打包,Intel(小端)/Motorola(大端)序混用,且信号不必对齐字节边界;对策——先定字节序再解包,用 DBC + cantools 定义位布局,与实车读数/已知量程交叉验证
|
||
- **波特率不符与总线负载**:现象——`candump` 错误帧刷屏(error frame),或高负载下周期抖动、抓到的周期不可信;原因——波特率与总线不一致;负载高(>60-70%)时仲裁延迟与丢帧;对策——接入前确认波特率(500 kbit/s 最常见,canbusload 看负载率),周期性分析排除高负载时段,需要精确时序用硬件时间戳
|
||
- **seed-key 安全访问被拒**:现象——UDS 读内存/写标定回 0x7F 27 33(SecurityAccessDenied);原因——0x27 需先解锁:服务端发 seed(0x27 01/03),客户端回 key(0x27 02/04),算法厂商私有(XOR/CRC/AES 常见);对策——有合法诊断仪/工具时先抓一次正常解锁流程,算法还原走 [[re-crypto-id]] / [[re-crypto-decrypt]];只在自有测试设备上做,不盲目爆破
|
||
- **固件校验/加密层**:现象——提取的固件在 [[re-fw-extract]] 解出垃圾,或烧回不工作;原因——ECU 固件带签名/CRC 校验、bootloader 验签或整体加密;对策——提取时按 0x34/0x36 会话完整传输并校验;分析时先区分加密/签名层再解包,解不开转 [[re-crypto-id]] / [[re-crypto-decrypt]];仅在授权测试床/设备上操作
|
||
- **把 AUTOSAR runnable 当死代码**:现象——某业务函数没有任何调用者,判为残留/无用代码;原因——runnable 由 RTEEvent 经生成的 RTE 胶水在某 OS Task 内被调用,**调用关系在配置里而不是在源码里**;对策——找 `Rte_*` 包装与 RTEEvent 映射、确认它被映射到哪个 OS Task([[autosar-classic]])
|
||
- **把隐式访问的"旧值"当一致性问题**:现象——生产者在运行中改了数据,消费者 runnable 执行期间一直读到旧值,怀疑 cache 或内存损坏;原因——隐式访问在 runnable 边界做快照与写回;对策——区分 `Rte_Read_*`(显式)与 `Rte_IRead_*`(隐式),按配置判定通信形式([[autosar-classic]])
|
||
- **把 `E_OS_ACCESS` 当参数错误**:现象——`ActivateTask` 一类服务返回访问错误,怀疑 task ID 写错或 OS 损坏;原因——OS-Application 的所有权/保护状态:未在配置时授予访问权,或对象属于另一个不可访问的应用;对策——查对象所属 OS-Application、应用状态、信任级别与当前调用上下文([[autosar-classic]])
|