偶尔看到国外一些论坛讨论汽车改装(都是基于ECU的)的技术问题,于是产生了兴趣,想想如果ECU能够被改装,那么它肯定也会面临一些安全问题。所以参阅了一些讨论文章,发现他们讨论的问题基本上都是如何修改MAP表(ECU中的数据表),而并没有人基于ECU二进制代码的逆向工程,因此有了本文。本文只是做一些关于汽车动力系统(发动机)ECU逆向工程的初步探索,在这方面资料稀缺,因此把自己一些粗浅的认知和分析结果放出来,文章必定有错误之处,人外有人天外有天,还望资深人士纠正,另外发出来的目的更希望能够起到抛砖引玉的作用。
一、明确一下ECU概念
不必多说,ECU指的就是汽车电控制单元,也就是ECU的本意(Electronic Control Unit)。像防抱死制动系统、自动变速箱、SAM模块、多媒体系统、刹车辅助系统、巡航定速系统、自动空调系统、驱动系统、电控自动变速器、主动悬架系统、安全气囊等等都是包含有各自的ECU,他们分别由这些ECU控制管理并且这些ECU都通过CAN总线连接起来,形成我们通常所说的汽车网络(车载网络)。
但是汽车行业内大部分人说的ECU都特指为发动机ECU, 也称车载电脑。这种延续了传统老式的叫法,因为早期的汽车只有一个用于管理发动机控制系统的ECU,因而大家习惯说的ECU实际上指的是发动机ECU,比如ECU更新、ECU调校、ECU改装等等概念都是特指发动机ECU。包括汽车百科里也都特指ECU为行车电脑/车载电脑。但国外很多分析文章、论文中涉及ECU基本上都是广泛意义的ECU,而不特指发动机ECU。
下面我截了几张发动机ECU的PCB图,可以看出ECU的PCB似乎和普通电子设备的PCB差不多。
至于ECU控制发动机的原理,几乎所有将汽车的教材中都有,我就不多说了,否则得引出一堆东西,那就是给自己挖了一个大坑,所以还是那一句话:网上资料多,还得靠自摸^^!下图是我从PPT上截来的,应该是最直观易懂的图。
二、ECU固件获取
先做一点科普,对于大部分汽车玩得比较深的人,对汽车改装都不会陌生,现代意义的汽车改装实际上是对发动机ECU固件提取、调校、回写等。一般通过ECU调校可以让发动机输出更高的动力。ECU调校通常比较复杂,比如需要对点火时间、喷油量、喷油时间、燃油压力、增压压力等等参数的调整并且还需要结合负荷、档位、转速、温度等做出最优的调整方案,另外还得考虑安全性等等问题。因而需要对汽车系统非常熟悉并且具有丰富的调校经验的工程师才能够完成,所以ECU调校的费用也会很高。
ECU固件的读取和普通设备的固件读取类似,但是也有差别。相似之处就是都需要硬件设备支持,不同在于接口方式的不同(实际上是废话^^)。当然有的ECU需要将ECU拆解下来进行固件读取,而有的可以直接通过OBD口进行固件读取,通过OBD读取更加简单,至少不用拆机和焊线。以下是我列举的一些可以进行ECU读写的工具,当然并不全,有需要的可以自行搜索购买。
工具名称 | 简介 |
CMD Flash | 支持OBD2、BDM 和BOOT模式对ECU进行读写 |
AZN Tuning | AZN改装店专用,似乎不向外出售 |
Galletto 1260 | 实际就是一根诊断线(内含OBD 转USB芯片) |
OpenPort | 支持OBD2进行数据读写 |
MPPS V13.02 | 和Galletto 1260类似 |
当然如果你的汽车支持OBD2进行固件读写并且你已经熟悉读写ECU固件的诊断命令,那么你也可以连接OBD2接口并且向其发送诊断命令来操作,只是相对来说麻烦一些。
下图展示的以上列举的刷ECU固件的硬件工具的截图:
OpenPort
CMD Flash工具
AZN Tuning
MPPS v12
如果拆机的话,会涉及接线问题,你可以通过chip datasheet来搜索与你车型ECU相匹配的芯片手册。下面这张图是奥迪ECU的接口图,你可以根据自己ECU的类型找到其针脚定义。
目前,大部分的汽车都支持OBDII来进行ECU的操作,因此来一张OBDII关键接口的图:
如果你购买有硬件FLASH工具,那么产品相关的说明文档应该可以解决大部分问题,网上也有相当多的资料,实际上ECU固件的读写比一般的设备的固件读写简单很多,因而在这里也不细说了。
三、ECU固件分析初探
在汽车界玩得最深的估计就是做ECU调校了,但是ECU调校只是对ECU MAP表(ECU固件中的数据表,后面会有更详细的说明)进行微调并且读修改的数据做校验。但如果对ECU固件代码进行逆向工程,我们不仅可以对修改MAP表,还可以破解其校验功能(很多买得很贵的MAP校验工具就是靠逆向校验函数而编写的,这实际上并不难,因为MAP的校验算法本身就不是很复杂),并且修改固件代码流程,甚至可以给其添加功能或者是加一些恶意代码进去,就像我们玩儿PE文件一样,在保证其能够正常和安全运行的前提下,你可以自由的改造。
1. 固件信息提取
首先通过二进制数据中的字符串来看看该ECU固件的有哪些信息。
上图中我标记的第二行信息是该ECU的电子发行包,我们可以看出该ECU是采用的博世的ECU ME7.1。此后,我又发现了与发动机相关的信息,如图:
其中 06A906032JB为发动机零件号,通过零件号可以得知该车型的发动机为宝来发动机,但是ECU采用的是博世ECU,发动机排量为1600ml,变速器为AG4自动4档位等等。最后,我通过ME7Info提取出了更多的信息。比如可能的通信协议硬件号、软件号、软件版本、引擎ID等等信息。
2.指令集识别
如果要对固件进行逆向工程的话,必须知道该固件的MCU类型,以便知道该ECU采用指令集类型。因此,我首先想到了binwalk,因而我尝试了一下,binwalk识别指令花了很长时间:
Binwalk识别出来竟然是ARM,着实让人兴奋了一把,因为个人对ARM指令还是比较熟悉的,更何况IDA还可以F5。但是看到后面的说明感觉就有点不对,怎么可能只有540条有效指令。随后,我用IDA来验证了一下,反汇编指令选择ARM big endian。并且从0x12943的位置开始进行反汇编,如图:
可以明显的看出,这是不正常的代码,并且根据经验,固件开始部分应该会有一大堆中断跳转,但通过ARM Big Endian 得到的代码却是如下这样:
另外我也尝试了使用little endian方式来反汇编,同样是不正确的。
此时,第一步提取的固件信息就很有用了,至少为我们提供了搜索的线索,结合固件涉及的电子属性和发动机相关信息,我发现该ECU属于英飞凌的(Infineon,前身为西门子集团的半导体部门),而该ECU采用的是C16X系列的内核,也就是说该ECU封装的实际上C16X的MCU,这款MCU原先属于西门子,因此指令集极有可能也是西门子的。强大的IDA果然没有让我失望,其完全支持C16X家族系列。在IDA中我选择Siemens C166进行反汇编,确认后需要填写RAM和ROM地址。RAM和ROM地址目前还不清楚,因而先空着直接反汇编。
从图中我们可以看出部分跳转指令是正常的,但是有的又不正常(红色阴影的地址)。不正常的地址有一个共同点就是都是基于0x820000的地址。因而,部分于MCU相关的中断服务以及硬件管理代码可能的基地址为0x000000,这就是这部分代码可能映射在0x0000000处,而部分与ECU相关的中断服务和硬件管理代码可能的基地址为0x820000。如果你关心MCU的代码可以将固件分割后进行分段加载,我这里只是简单将整个固件映射到0x800000,因为我只关心ECU的代码,所以将该ROM的起始地址设置为0x800000,而我们的固件大小为1M,因此ROM大小设置为0x100000,加载地址和大小和ROM设置成为一样。RAM采用默认的方式不管它。
反汇编后,我们可以看到MCU相关的中断跳转表。
OK,现在我们可以正常的分析该ECU固件了。当然如果你想逆向分析该ECU固件,你得熟悉C166汇编语言,还得要十足的耐性。下表我列举了C166汇编指令及其功能(也基本上来自于网络),如果想要更新详细的信息,那就去看Infineon的手册吧。
算数指令
指令1 | 指令2 | 功能描述 |
ADD | ADDB | 两字或字节加法 |
ADDC | ADDCB | 带进位的两字或字节加法 |
SUB | SUBB | 两字或字节减法 |
SUBC | SUBCB | 带进位的两字或字节减法 |
MUL | MULU | 16位乘16位带符号或无符号乘法 |
DIV | DIVU | 16位除16位带符号或无符号除法 |
DIVL | DIVLU | 32位除16位带符号或无符号除法 |
CPL | CPLB | 一个字或字节的1的补数 |
NEG | NEGB | 一个字或字节的2的补数 |
逻辑指令
AND | ANDB | 两字或字节位与 |
OR | ORB | 两字或字节位或 |
XOR | XORB | 两字或字节位与或 |
比较指令
CMP | CMPB | 两字或字节比较 |
CMPI1 | CMPI2 | 带增长1或2的两字比较 |
CMPD1 | CMPD2 | 带增长1或2的两字比较 |
布尔位操作指令
BFLDH/BFLDL | 字的高位字节或低位字节的可屏蔽位的操作 |
BSET | 对某位置1 |
BCLR | 对某位清零 |
BMOV | 移动某一位 |
BMOVN | 反相移动某位 |
BAND | 两位相与 |
BOR | 两位相或 |
BXOR | 两位相与或 |
BCMP | 两位比较 |
移位和循环移位指令
SHR | 字右移 |
SHL | 字左移 |
ROR | 字循环右移 |
ROL | 字循环左移 |
ASHR | 带符号位右移 |
系统控制指令
SRST | 通过软件复位 |
IDLE | 进入休闲状态 |
PWRDN | 进入掉电状态 |
SRVWDT | 服务看门狗定时器 |
DISWDT | 关闭看门狗定时器 |
控制流程指令
JMPA/JMPI/JMPR | 当前程序段有条件跳转到绝对、间接或相对目标地址 |
JMPS | 在任何程序段无条件跳转到绝对目标地址 |
JB/JNB | 根据选择位的状态,在当前程短有条件跳转到相对目标地址 |
JBC/JNBS | 根据选择位的状态,用取反测试位,在当前程序段有条件跳转到相对目标地址 |
CALLA/CALLI | 在当前程序段中有条件调用绝对或间接地址的子程序 |
CALLR | 在当前程序段中无条件调用相对地址的子程序 |
CALLS | 在当前程序中无条件调用绝对地址的子程序 |
PCALL | 在当前程序段中将选择寄存器压入堆栈无条件调用绝对地址的子程序 |
TRAP | 在程序段中无条件跳转到中断或陷阱矢量跳转表 |
RET | 在当前程序段从子程序返回 |
RETS | 在任何程序段从子程序返回 |
RETP | 在当前程序段从子程序返回,外加从系统堆栈中弹出一个选择寄存器 |
RETI | 从中断服务程序中返回 |
JMPA/JMPI/JMPR | 当前程序段有条件跳转到绝对、间接或相对目标地址 |
JMPS | 在任何程序段无条件跳转到绝对目标地址 |
C166 | 汇编语言程序设计.txt |
JB/JNB | 根据选择位的状态,在当前程短有条件跳转到相对目标地址 |
JBC/JNBS | 根据选择位的状态,用取反测试位,在当前程序段有条件跳转到相对目标地址 |
CALLA/CALLI | 在当前程序段中有条件调用绝对或间接地址的子程序 |
CALLR | 在当前程序段中无条件调用相对地址的子程序 |
CALLS | 在当前程序中无条件调用绝对地址的子程序 |
这里不一一列举了,其他指令请查询C166手册,此外本文不打算介绍C166的那些寄存器以及ECU自身寄存器(这些寄存器是处在MCU外部,和MCU一起被封装在ECU中,通常都是映射在RAM中,寄存器太多了,就不做介绍了)。
- 汇编代码的二次处理
通过IDA反汇编得到毕竟只是原始的汇编代码,代码量巨大并且难以识别,分析起来也会非常吃力。如果有现成的插件或者脚本自动识别一些函数或者数据,那么可以大大减少我们逆向工程的工作量。幸运的是目前还真有这样的插件—Bosch Me7x插件,该插件针对ME7.1和7.5来进行函数和MAP表的识别以及做出相应的注释。这是一款比较老的插件,对于部分的函数和MAP表的识别有些错误。但是有总比没有好,通过该插件我们成功找到了一些关键函数和MAP表。
如下图是识别出来的操作系统自身的函数,主要是一些基本操作函数,当然这些函数我们可以不用关心。
以下是一些识别出来的lookup表(这些表构成了MAP)和以及部分操作MAP的函数。此外很多MAP查询函数都没有做明确的注释,只是被命名为LookupA-Z,因而具体的那个函数操作那个MAP需要花费大量的时间来分析。
查表函数对参数都做了注释,很人性化
另外,插件采用的是固定的函数特征和MAP表特征, 因而很多MAP表也都没有识别出来。所以我们还需要结合其他工具来分析。
在代码级别的逆向工程对固件的操作级别更高,你可以绕过某些逻辑和校验,甚至可以给ECU添加功能,或者添加一个后门(在某个传感器的值达到一个异常值时触发后门,对于目前很多将发动机ECU接入CAN网络后,你会有更多的攻击入口,此处不多说,自己体会吧),而不仅仅限制于修改MAP(大部分都是做ECU改装)。
- MAP表的进一步分析
MAP表,又称脉谱表,设计者将提前将计算好的数据采用二维、三维或多维的数据结构方式存储到ROM里面去。典型的三维MAP就是喷油MAP,一般横坐标是转速,纵坐标是节气门开度,纵横坐标交叉点就是喷油量数据。采用这种三维数据结构,能够精确的表示出每一个不同转速和不同节气门开度情况下的喷油量。 ECU通过读取传感器的工作参数得知引擎各机构当前状态(如空气流量、曲轴位置等)并且将读取的数据作为MAP表的坐标参数查出需要控制引擎的信息等(如喷油时机、喷油量)。
通过更加细致的分析,我找到了一些MAP表,比如油量表、直接转矩控制表(DTC表)、DTS表、油量表、RPM表、进气温度表等,以及操作表的函数,比如有做MAP校验的函数,查表的函数等等。但这是远远不够的,现在汽车的MAP表通常都有几十上百个MAP表。
因此,为了识别出更多MAP表,我需要一个更加专业的工具,那就是鼎鼎大名的winols,该款工具可以很好的识别出ECU固件中MAP并且能够实现这些数据的2D、3D展示,最强大的是可以实现MAP数据的编辑和校验,即便是加密处理过的数据也可以自行解密。
如图,可以看出winols可以识别出71个MAP表,每个MAP表都是以Map “Bosch II”类似的默认名称命名,这些需要自行进行识别以后进行更改,不同的ECU有不同的MAP表,也没有固定特征,需要一些经验来进行识别。
针对于每一个MAP表都可以以纯数据、2D、3D方式来进行显示。
以3D形式显示有助于工程师根据3D形状来判定该MAP的类型,也可以非常直观的看到MAP修改之后的改变。当然winols还支持修改完自动完成校验的功能。如果连接上汽车,它还可以实时进行修并且实时校验,大大方便了ECU的调试。
此外还有很多其他做MAP数据修改的工具比如3D MAP之类的,也都是非常好的工具,此处也不做介绍了。
四、总结
本文仅仅对ECU固件分析做了一个初步探索和简单的分析,后续还有很多工作要做,比如ECU MAP数据校验绕过,添加和修改ECU功能和某些逻辑,二进制代码级别的ECU CAN协议解析逆向,传感器数据解析逻辑以及指令发送逻辑等等。
原文始发于看雪社区(gjden):[原创]汽车动力系统ECU固件逆向工程初探