该工具可以提取并重新组装PLC的“归档”文件,这些文件被多个施耐德电气PLC使用,至少包括M580、M340和Quantum。扩展名为.sta的归档文件实际上是一个包含几个文件的ZIP压缩包。在归档中,真正重要的文件名为Station.apx。该文件的内容是在(完整)程序下载/上传过程中与PLC之间传输的数据。自然,它包含了PLC程序的实际可执行代码。但它也包含了在Control Expert Classic(或Unity Pro)中编辑PLC程序所需的所有信息。.apx文件格式是专有的,关于它的信息很少。可以从Liras en la red和Team82获得一些信息,但本工作比他们发布的内容更深入。使用此工具,可以从Station.apx中提取不同的“节”(如果需要也可以解压缩)以供检查。该工具还可以从先前提取的(可能已修改的)节数据和元数据重新组装Station.apx。只要所做的更改不破坏“节逻辑”,这个重新组装的文件就可以在Control Expert Classic / Unity Pro中无错误打开。
这个项目从未有过什么宏伟计划。我不时使用施耐德电气PLC,并对它们如何在更基础的层面上工作(或不工作)感兴趣。结果发现这项调查的复杂度刚好能让我保持乐趣(大概就像普通人解填字游戏一样),于是我最终做出了这个工具。
该工具尽最大努力提取和重新组装它所操作的文件。但是,用户应了解该工具是在以下情况下开发的:
该工具通过命令行使用Python解释器:
python apxutil.py -h
usage: apxutil.py [-h] [-f filaname-apx] [-F filaname-sta] [-e dir] [-E dir] [-a manifestpath] [-A manifestpath] [-d]
[-x] [-B] [-r]
Tool to manipulate Schneider Electric .sta and .apx files
options:
-h, --help show this help message and exit
-f, --apxfile filaname-apx
.apx file to read or write
-F, --stafile filaname-sta
.sta file to read or write
-e, --extract-apx dir
extract contents of the Station.apx file
-E, --extract-sta dir
extract contents of the .sta file
-a, --assemble-apx manifestpath
create Station.apx file based on apx_manifest.ini
-A, --assemble-sta manifestpath
create a .sta file from files in sta_manifest.ini
-d, --decompress decompress Station.apx sections that are compressed
-x, --hexdump print hexdump of Station.apx with header information
-B, --include-apd include Station.apd when creating .sta archive
-r, --restart-offsets
restart offsets at 0 for each section in hexdump print (useful for diffing)
帮助信息应该相当不言自明。通常,带大写字母的短选项用于.sta文件,小写字母用于.apx文件。下面给出一些使用示例。
要將.sta文件的内容提取到目录"extracted",你可以使用:
python apxutil.py -F archive.sta -E extracted
要提取Station.apx的内容到目录"contents",你可以使用:
python apxutil.py -f extracted/BinAppli/Station.apx -e contents -d
"-d"选项表示压缩的节将被解压缩,但如果需要(所有的)原始节数据,可以省略。
要重新组装Station.apx,你可以使用:
python apxutil.py -f extracted/BinAppli/Station.apx -a contents
这将根据contents目录中的apx_manifest.ini组装Station.apx,如果之前已解压缩,则会压缩节数据。节大小和CRC将被重新计算,而不是从apx_manifest.ini中读取。
要重新组装一个.sta文件,你可以使用:
python apxutil.py -F modified-archive.sta -A extracted
默认情况下,这将省略Station.apd文件(该文件似乎主要是对Station.apx文件头的完整性检查)。如果你希望包含它,请添加"-B"选项。但这很可能意味着Control Expert Classic将无法打开项目归档文件。
要直接“查看”Station.apx文件的内容,可以使用以下命令:
python apxutil.py -F modified-archive.sta -x
这将输出Station.apx文件的“canonical hexdump”,即你会看到偏移量、十六进制数据和ASCII数据,每次16字节,包括元数据。"-d"选项可用于解压缩压缩的节(但要注意,在这种情况下偏移量意义不大)。"-r"选项可用于将每个节的偏移量重置为0。如果你做了更改并希望能够在没有偏移量的情况下对其进行比较(diff),这很有用。
如果你想了解APX文件格式(达到我和这个工具的水平),最好的方法就是查看apxutil.py的源代码。即使编程经验不多,代码也应该不难阅读。但可能会让真正的程序员感到不适。总之,这里给出文件格式的高层概述。APX文件以一个32字节的文件头开始。文件的其余部分被划分为不同的节,每个节具有类似的结构。每个节以一个节头开始,其长度取决于节类型(在节头开头定义)。节头之后是一个RTE(头)。RTE之后是节数据(如果存在的话)。节数据之后是下一个节(头)。节头似乎更与Station.apx格式相关,而RTE更与执行环境(运行时环境或实时环境?)相关,但区别并不明确。
APX文件头长32字节,以ASCII文本"APX"开头,后跟一些元数据。也许最重要的是,它定义了所使用的RTE的类型和大小。我见过类型0x02。类型0x01可能用于16位地址空间,类型0x02用于32位地址空间,但这远不能确定。有一些未知字段可能具有重要意义。在节"SD" 0x0001中有几个32位值和一个16位值被“重复”,但其含义未知。最后11个字节似乎总是零。以下是一个示例(采用canonical hexdump格式,包含元数据):
00000000 41 50 58 00 00 01 02 01 10 01 10 60 f6 c3 30 00 |APX........`..0.| ApxFileHeader(magic=0x00585041, version_maybe=0x0100, rte_type=0x02, header_count_maybe=0x01, sdsection_total_size=0x0110, rte_size=0x10, sdsection_39_4=0x30c3f660, sdsection_35_4=0x0b926500, sesection_8_2=0x0106, zero_pad_21_11=0000000000000000000000)
00000010 65 92 0b 06 01 00 00 00 00 00 00 00 00 00 00 00 |e...............|
APX节头以一个16位值开始,定义其类型。类型0x0001 - 0x0004似乎是有效的,但我只见过类型0x0000、0x0001和0x0002。节头长度取决于类型。对于类型0x0000,长度为8字节;对于0x0001 - 0x0002,长度为16字节;对于类型0x0003 - 0x0004,应为24字节。节号由字节偏移量4处的16位值给出。对于节类型0x0000和0x0001,这就是我所知道的一切,它们没有任何节数据。对于节类型0x0002,字节偏移量10处的32位值指定了节数据的大小,与Station.apx文件中的一致。
节头后面的RTE头长度为16字节(至少对于类型0x02是这样)。第一个32位值的含义未知(但这里命名为“block_count”)。偏移量4处的32位值,在节有数据的情况下,是节数据大小(来自节头)减去1。如果节没有数据,则其含义未知(节0x0011打破了这一规则)。字节偏移量8处的32位值是属性/标志,其中大部分含义未知。一些想法列在apxutil.py的末尾。字节偏移量12处的16位值是节数据的CRC16/Kermit校验和(同样,节0x0011打破了这一规则)。字节14的高3位定义了内存区域,低5位定义了内存folio。最后一个字节15可能未使用。
节数据通常是“原始”数据,可能包含可执行代码、项目信息、压缩数据等。似乎如果RTE属性中的第13位被设置,那么节数据就是压缩的。所见的压缩类型有zip/"pk"、带有非标准头的zlib和"raw deflate"。
虽然所有节都有其自身的含义,但似乎节0x0001 "RT"和0x0011 "SD"比其他节更为基础。它们似乎与RTE和PLC执行环境的实际内存布局有关。apxutil.py末尾包含对这些节的部分解码,但目前工具本身并未使用。
如果有人想调查,还有很多“低垂的果实”。一些开放性问题: