
أداة للتعامل مع ملفات أرشيف PLC الخاصة بشنايدر إلكتريك
يمكن لهذه الأداة استخراج وإعادة تجميع ملفات "أرشيف" PLC، المستخدمة من قبل عدة PLCs من شنايدر إلكتريك، بما في ذلك على الأقل M580 و M340 و Quantum. ملفات الأرشيف ذات الامتداد .sta هي في الحقيقة أرشيف zip يحتوي على عدد قليل من الملفات. داخل الأرشيف، الملف المهم حقًا هو المسمى Station.apx. محتويات هذا الملف هي ما يتم نقله من/إلى PLC أثناء تنزيل/تحميل البرنامج (الكامل). بطبيعة الحال، يحتوي على الكود القابل للتنفيذ الفعلي لبرنامج PLC. ولكنه يحتوي أيضًا على جميع المعلومات اللازمة لتعديل برنامج PLC في Control Expert Classic (أو Unity Pro). تنسيق ملف .apx مملوك، ويتوفر القليل من المعلومات عنه. بعض المعلومات متاحة من Liras en la red و Team82، لكن هذا العمل يتعمق أكثر مما نشراه. باستخدام هذه الأداة، يمكن استخراج "الأقسام" المختلفة من Station.apx (وفك ضغطها إذا لزم الأمر) للفحص. يمكن للأداة أيضًا إعادة تجميع Station.apx من بيانات القسم وبيانات التعريف المستخرجة سابقًا - وربما المعدلة. وسيفتح هذا الملف المعاد تجميعه بدون أخطاء في Control Expert Classic / Unity Pro، بشرط ألا تؤدي التغييرات التي تم إجراؤها إلى كسر "منطق القسم".
لم تكن هناك أي خطة كبيرة مع هذا المشروع على الإطلاق. أستخدم PLCs من شنايدر إلكتريك بين الحين والآخر، وكنت مهتمًا بكيفية عملها (أو لا تعمل) على مستوى أساسي أكثر. اتضح أن هذا التحقيق كان معقدًا بشكل مناسب لإبقائي مستمتعًا (ربما مثل حل الكلمات المتقاطعة للأشخاص العاديين)، وانتهى بي الأمر بهذه الأداة.
تبذل الأداة قصارى جهدها لاستخراج وإعادة تجميع الملفات التي تعمل معها. ومع ذلك، يجب أن يدرك المستخدم أن الأداة تم تطويرها مع:
قد يتسبب استخدام الملفات المعاد تجميعها على PLC حقيقي في حدوث مشكلات، خاصة إذا قمت بتعديل الأقسام أو البيانات الوصفية. من الممكن أيضًا أن يؤدي ذلك إلى تعطيل PLC. لذا قم بتحميل ملفات الأرشيف المعدلة إلى PLC على مسؤوليتك الخاصة.
تُستخدم الأداة من سطر الأوامر مع مترجم بايثون:
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
سيؤدي هذا إلى تجميع Station.apx بناءً على apx_manifest.ini الموجود في دليل المحتويات، مع ضغط بيانات القسم إذا تم فك ضغطها سابقًا. سيتم إعادة حساب أحجام الأقسام ومجاميع 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
سيؤدي هذا إلى إخراج "hexdump أساسي" لملف Station.apx، مما يعني أنك ترى الإزاحات وبيانات hex وبيانات 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 بت وقيمة واحدة 16 بت "مكررة" في القسم "SD" 0x0001، لكن معناها غير معروف. آخر 11 بايت تبدو دائمًا أنها أصفار. مثال أدناه (بتنسيق 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 بايت. رقم القسم يُعطى بقيمة 16 بت عند الإزاحة بالبايت 4. بالنسبة لأنواع الأقسام 0x0000 و 0x0001، هذا كل ما أعرفه، ولا تحتوي على بيانات قسم. بالنسبة لنوع القسم 0x0002، القيمة 32 بت عند الإزاحة بالبايت 10 تحدد حجم بيانات القسم، كما هي موجودة في ملف Station.apx.
رأس RTE الذي يلي رأس القسم هو 16 بايت (على الأقل للنوع 0x02). معنى القيمة الأولى 32 بت غير معروف (ولكنها تسمى هنا "block_count"). القيمة 32 بت التي تبدأ عند الإزاحة 4 هي حجم بيانات القسم (من رأس القسم) ناقص 1، في حالة احتواء القسم على بيانات. إذا كان القسم لا يحتوي على بيانات، فإن المعنى غير معروف (والقسم 0x0011 يخالف هذه القاعدة). القيمة 32 بت التي تبدأ عند الإزاحة بالبايت 8 هي سمات/أعلام، معظمها غير معروف المعنى. بعض الأفكار مدرجة في نهاية apxutil.py. القيمة 16 بت التي تبدأ عند الإزاحة بالبايت 12 هي المجموع الاختباري CRC16/Kermit لبيانات القسم (ومرة أخرى، القسم 0x0011 يخالف هذه القاعدة). البتات الثلاث العليا من البايت 14 تحدد منطقة الذاكرة والبتات الخمس السفلى تحدد ورقة الذاكرة. آخر بايت 15 على الأرجح غير مستخدم.
بيانات القسم هي بشكل عام بيانات "خام"، قد تحتوي على كود قابل للتنفيذ، معلومات المشروع، بيانات مضغوطة، إلخ. يبدو أنه إذا تم تعيين البت 13 في سمات RTE، فإن بيانات القسم تكون مضغوطة. أنواع الضغط التي شوهدت هي zip/"pk" و zlib برأس غير قياسي و "raw deflate".
بينما لكل قسم معناه الخاص، يبدو أن القسمين 0x0001 "RT" و 0x0011 "SD" أكثر أساسية من الباقي. يبدو أنهما مرتبطان بـ RTE وتخطيط الذاكرة الفعلي لبيئة تنفيذ PLC. تم تضمين فك تشفير جزئي لهذه الأقسام في نهاية apxutil.py، ولكنه حاليًا غير مستخدم من قبل الأداة نفسها.
هناك الكثير من "الثمار المنخفضة" في حال أراد شخص ما التحقيق. بعض الأسئلة المفتوحة: