◆杨柳鹏
(河北大学 河北 071002)
简谈MC7字节码
◆杨柳鹏
(河北大学 河北 071002)
本文简单介绍MC7字节码和已知的MC7文件格式,并且说明对MC7文件进行修饰的过程,阐述基于SNAP7库的PLC注入程序,最后给出一个成功的范例来进一步佐证MC7字节码对PLC的影响。
MC7字节码;西门子PLC;注入
Stuxnet是一个具有划时代意义的病毒。作为世界上首个以关键工业设施为目标的破坏性病毒,它的出现标志着工业级黑客登上历史舞台。直到今天,Stuxnet的感染方式仍旧是不少针对工业控制系统的病毒的模仿对象。其在西门子PLC中执行的恶意代码由MC7字节码构成。
西门子PLC的编程语言主要有五种:LAD、STL、FBD、SCL、GRAPH。其中,LAD和STL是常用的两种基本语言。然而无论采用哪种语言编程,编码完成后源码都将自动地转化为STL。最后编程软件将由MC7字节码构建产生MC7文件。简单来说就是把代码包装成二进制文件。MC7字节码是西门子PLC的CPU可以理解并执行的原生代码。
这是FC100块:
其STL如下:
其中有意义的字节和其对应位置的作用:
0707 :(第1-2字节)MC7文件开始标志
01:(第3字节)块标识,01表示用户块、03表示系统块
01:(第4字节)块类型,01为常规类型、03为配置文件、05为背景数据
01:(第5字节)程序语言种类,STL对应01、LAD对应02、FBD对应03、SCL对应04、GRAPH对应06、05和07表示编写DB块或SDB块(即用数据类型、地址、大小描述)
0C:(第6字节)块种类,OB块对应08、DB块对应0A、SDB块对应0B、FC块对应0C、SFC块对应0D、FB块对应0E、SFB块对应0F
0064 :(第7-8字节)块号
0000 0074 :(第9-12字节)文件总长度。将74化为十进制116,对应此MC7文件字节总数(116个字节)
0000 :(第13-14字节)数据类型。0800表示系统配置文件,0000表示用户数据文件
0000 :(第15-16字节)块是否处于保护状态。0003表示块被保护,0000表示块不被保护
02ab 2735 2d03:(第17-22字节)最后修改时间。其中02ab 2735(第17-20字节)表示毫秒(44771125毫秒),2d03(第21-22字节)表示天数(11523天)。基准时间为1984年1月1日。此段代码表示该MC7文件最后修改时间为2015年7月20日12:26:11.125
03a1 6383 21a7:(第23-28字节)最后创建时间。计算方法等同于最后修改时间
001 c:(第29-30字节)内部块数据表长度
0006 :(第31-32字节)跳转指令内存
0014 :(第33-34字节)本地数据长度
000 a:(第35-36字节)执行代码长度
c000:(第37-38字节)对应语句A I0.0
c100:(第39-40字节)对应语句A I0.1。对于A I指令,字节码占用两个字节,以c开头,用紧跟c后的半个字节表示位地址(即I0.1的.1),用一个字节表示字节地址(即I0.1的0)
ca00:(第41-42字节)对应语句O I0.2。对于O I指令,字节码占用两个字节,以c开头,用紧跟c后的半个字节-8的值表示位地址(即I0.2的.2),用一个字节表示字节地址(即I0.2的0)
d880:(第43-44字节)对应语句= Q0.0.对于= Q指令,字节码占用两个字节,以d开头,用紧跟c后的半个字节-8的值表示位地址(即Q0.0的.0),用后一个字节-80的值表示字节地址(即Q0.0的0)
6500 :(第45-46字节)对应语句BE。中止在当前块中的程序扫描,跳转到调用当前块的程序块
0100 :(第47-48字节)执行代码区结束标志。
MC7字节码可以用于设计西门子PLC的仿真程序,或脱离西门子PLC官方编译器S7kafapx.exe实现对程序的修改(Stuxnet核心功能)。
3.1.注入原理
3.1.1 西门子S7Comm协议
S7Comm协议是S7通讯协议簇中的一种,用以传输块。
3.1.2 SNAP7库函数
SNAP7是一个直接和西门子S7系列PLC对接的开源的多平台的以太网通信软件。我们主要用两个函数:
Cli_FullUpload(),从PLC的CPU上传一个完整的块
Cli_Download(),从我们的PC向PLC的CPU下载MC7文件
具体使用方法请查询《SNAP7-refman》。
3.2 修饰
这里的修饰指对自由循环组织块OB1的修饰。在注入前,参照上文给出的文件格式用十六进制编辑器手动写出完成含有攻击代码的MC7文件(在这里我把它称作Payload),以“被调用块名_被调用块号.mc7”格式保存。注入时可注入MC7文件所在的整个文件夹,但被调用块只能唯一。由于PLC在设计时考虑了生产线的连续工作问题,所以接收一般程序时不会重启,我们便在OB1的原始执行代码前插入一个用来调用块的函数调用写好的Payload。这样在下一个刷新周期,CPU便会执行我们的攻击代码。
这里需要注意,CALL函数调用块时需要添加程序块参数和背景数据块,所以我采用UC函数,即无条件调用。
构建注入器时采用如下代码定义调用FC块代码:
uint8_tuc[] = {
0xFB, 0x70, 0x00, 0x00,
0x70 , 0x0B, 0x00, 0x02,
0xFE, 0x03, 0x00, 0x00, 0x00, 0x00,
0xFE, 0x0B, 0x00, 0x00, 0x00, 0x00,
0x30 , 0x03, 0x00, 0x00,
0x30 , 0x03, 0x00, 0x00,
0x70 , 0x07
};
除了插入调用块函数,我们还应按照插入代码大小修改OB1的原始MC7文件的“执行代码长度”和“文件的总长度”区域。
3.3 情景假设
假设我们已获得一个由许多PLC、监控计算机、传感器、HMI和其它一些设备组成的ICS结构中的网络访问权限。在这种情形下,我们可获得目的PLC地址,进而篡改逻辑。
范例采用PLCSIM仿真器模拟被注入的PLC。使用NetToPLCSIM软件建立物理网络接口和仿真PLC的连接,模拟和仿真PLC的通信。
采用上文例子“FC_100.mc7”。
注入完成后打开Step7主面板选择“在线”,此时会发现OB1块后新增FC100块。观察OB1块会发现调用函数“UCFC100”已插入其中。由于存在块调用PLCSIM增加了逻辑:
I0.0和I0.1同时置1则Q0.0置1;I0.2置1则Q0.0置1。
[1]Dillon Beresford. Black hat. In Siemens Simatic S7 PLC Exploitation[M].NSS Labs, 2011.
[2]DavideNardella.SNAP7[EB/OL]. http://snap7.sourceforg e.net/.