
シュナイダーエレクトリックPLCのアーカイブファイルを操作するツール
このツールは、Schneider Electricの複数のPLC(少なくともM580、M340、Quantumを含む)で使用されているPLC「アーカイブ」ファイルを抽出および再構築できます。拡張子が.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でエラーなく開くことができます。
このプロジェクトには、壮大な計画はまったくありませんでした。私は時々Schneider ElectricのPLCを使用しており、それらがより基本的なレベルでどのように動作する(あるいはしない)かに興味がありました。この調査は、私を楽しませるのに十分な複雑さ(おそらく普通の人にとってクロスワードパズルを解くようなもの)であることが判明し、このツールができあがりました。
このツールは、扱うファイルを最善を尽くして抽出および再構築します。ただし、ユーザーはこのツールが以下を前提として開発されたことに注意する必要があります:
実際のPLCで再構築されたファイルを使用すると、特にセクションやメタデータを変更した場合に問題が発生する可能性があります。PLCが文鎮化する可能性さえあります。したがって、変更されたアーカイブファイルを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ファイルの「正規の16進ダンプ」が出力されます。つまり、オフセット、16進データ、ASCIIデータが16バイトずつ、メタデータとともに表示されます。「-d」オプションを使用すると、圧縮されたセクションを解凍できます(ただし、その場合オフセットはあまり意味をなさないことに注意)。「-r」オプションを使用すると、各セクションのオフセットを0から再開できます。これは、変更を加えてオフセットなしで差分を確認したい場合に便利です。
APXファイル形式を(私自身とこのツールのレベルで)理解したい場合、最善の方法はapxutil.pyのソースコードを見ることです。プログラミング経験が少なくても、コードはそれほど難しくないはずです。ただし、本物のプログラマーは吐き気を催すかもしれません。いずれにせよ、ファイル形式の高レベルの概要をここに示します。APXファイルは32バイトのファイルヘッダーで始まります。ファイルの残りの部分は、それぞれ類似した構造を持つさまざまなセクションに分割されています。各セクションはセクションヘッダーで始まり、その長さはセクションタイプ(ヘッダーの先頭で定義)によって異なります。セクションヘッダーの後にRTE(ヘッダー)が続きます。RTEの後にはセクションデータ(存在する場合)が続きます。そしてセクションデータの後に次のセクション(ヘッダー)が始まります。セクションヘッダーはStation.apx形式に、RTEは実行環境(ランタイム環境またはリアルタイム環境?)に関連しているように見えますが、明確な区分ではありません。
APXファイルヘッダーは32バイトで、ASCIIテキスト「APX」で始まり、その後にいくつかのメタデータが続きます。おそらく最も重要なのは、使用されるRTEのタイプとサイズを定義することです。タイプ0x02は私が見たものです。タイプ0x01は16ビットアドレス空間用、タイプ0x02は32ビットアドレス空間用である可能性が高いですが、これは確かではありません。おそらく重要ないくつかの未知の値があります。いくつかの32ビット値と1つの16ビット値がセクション「SD」0x0001で「繰り返されています」が、それらの意味は不明です。最後の11バイトは常にゼロのようです。以下に例を示します(正規の16進ダンプ形式、メタデータ付き):
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ビットはメモリフォリオを定義します。最後のバイト15はおそらく未使用です。
セクションデータは一般に「生の」データであり、実行可能コード、プロジェクト情報、圧縮データなどが含まれる場合があります。RTE属性のビット13が設定されている場合、セクションデータは圧縮されているようです。見られる圧縮タイプは、zip/「pk」、非標準ヘッダーを持つzlib、および「raw deflate」です。
すべてのセクションには独自の意味がありますが、セクション0x0001「RT」と0x0011「SD」は他のものよりも基本的であるように見えます。これらはRTEおよびPLC実行環境の実際のメモリレイアウトに関連しているようです。これらのセクションの部分的なデコードがapxutil.pyの最後の方に含まれていますが、現在ツール自体では使用されていません。
誰かが調査したい場合、たくさんの「すぐに取れる」果実があります。いくつかの未解決の質問: