الفريق العربي للهندسة العكسية

نسخة كاملة : ما سبب صعوبة الرجوع للغة الأساسية C أو ++C بدلا من Assembly أثناء الهندسة العكسية؟
أنت حالياً تتصفح نسخة خفيفة من المنتدى . مشاهدة نسخة كاملة مع جميع الأشكال الجمالية .
السلام عليكم ورحمة الله وبركاته

لو سمحتم أنا عندي إستفسار ؟

لماذا يصعب الرجوع للغة الأساسية التي كتبت بها البرنامج مثل C بدل من ال Assembly ؟
و عليكم السلام ورحمة الله تعالى وبركاته
الحديث هنا عن ما يسمى بالــ Decompilation و هي محاولة كتابة اوامر قريبة و تحاكي ما تم كتابته قبل عمل Compilation
اللغات التي تعتمد على Virtual machine لتفسير و تنفيذ ما تم كتابته من اوامر تنجح فيها بنسبة كبيرة جدا عملية استرجاع الأمر الأصلية، مثل الجافا و الدوت نت و الباثيون و الأوتوايت و غيرها...

اما لغات الناتيف التي تعتمد على كومبايليشن لبناء ملفات تنفيذية مثل السي و السي بلص بلص و دلفي و غيرها، نسبة استرجاع الأوامر الأصلية تتضاءل كلما كانت الأوامر الأصلية معقدة... لكن يمكن الحصول على اوامر قريبة تحاكي الأصلية و تتيح فهم ما تقوم به...

عدو الــ Decompilation هو التشفير و التشويش، الملفات الناتجة تفشل فيها عملية استرجاح الأوامر الأصلية...
سؤال جميل.
بشكل عام، هناك نوعان من الملفات القابلة للتنفيذ على حاسوبك : الاول ما يعرف بالـnative code وهي تكون جاهزة للتشغيل مباشرة حيث تقوم وحدة المعالجة المركزية (CPU) ببساطة بتحميلها الى الذاكرة وتبدأ في قراءة التعليمات البرمجية في الملف التنفيذي. هذه الملفات التنفيذية تتحول من كود برمجي الى لغة الاله، ولغات الـnative كثيرة أبرزها هي لغات سي وسي++ وتهدف  للعمل على معالج معين، اما النوع الثاني هو intermediate code او لغة وسيطة، ولا يتم تشغيله مباشرة على وحدة المعالجة المركزية. يتم تحميلها بواسطة مفسر (interpreter) أو virtual machine الذي يحول التعليمات البرمجية الوسيطة إلى تعليمات قابلة للتنفيذ الفعلي. هذه اللغات تقوم بتحويل الكود البرمجي الى لغة وسيطة لكي تعمل على اي معالج او نظام تشغيل يحوي الـvirtual machine ، بمعنى كود برمجي -> لغة وسيطة -> لغة الالة. مثال على ذلك هما Microsoft .NET Framework و Java.
بالنسبة للغات الـnative فهي عند تحويلها لغة الاله تحذف العديد من المعلومات عن الكود المصدري لانها لا تحتاجها فهي هدفها ان تعمل لمعالج معين وعلى نظام معين لم تحتاج كل هذه العبء بحفظ معلومات لا تحتاجها  بعكس لغات مثل جافا ولغات الدوت نت والتي تستخدم virtual machine بحيث الكود يتحول الى Byte-code وليس لغة الاله وهي تقوم بحفظ الكثير من المعلومات لانها تحتاجها في مرحلة JIT (the just-in-time compiler)  لان هذه المرحلة تحتاجها بتحويل الـByte-code الى لغة الاله للجهاز الهدف ( يعني سورس جافا --> Byte-code --> لغة اله لمعالج X على نظام لينكس ويندوز ماك، اي جهاز يشغل virtual machine الخاص بجافا سيشغل برنامجك)

الان للجواب المطول:
المترجم اوcompiler يقوم بخطوات كثيرة ومعقدة (لن اقوم بالتعمق لان برمجة الـcompiler امر صعب وقراءة الكورست عن هذا الامر اصعب ولكني انصحك بالاطلاع على كورس على يوتيوب) انظر هنا:
[صورة مرفقة: compiler_phases.jpg]
Lexical analyzer تعمل المرحلة الأولى كماسح نصي text scanner. تقوم هذه المرحلة بمسح الكود المصدري في صورة دفق من الأحرف وتحويله إلى معجمات ذات معنى. يمثل Lexical analyzer هذه المعجمات في شكل tokens على النحو التالي:
<token-name, attribute-value>
الـ keywords و  constants و  identifiers و  strings و  numbers و  operators و punctuations symbols كلها تعتبر tokens
Syntax Analysis - تحليل بناء الجملة المرحلة التالية تسمى syntax analysis او parsing. يأخذ الـ tokens الناتج عن lexical analysis كمدخلات ويولد شجرة تحليل- parse tree (أو شجرة بناء جملة-syntax tree). في هذه المرحلة، يتم التحقق من الترتيبات الرمزية مقابل قواعد الكود المصدري، أي أن المحلل اللغوي يتحقق مما إذا كانت التعابير-expression الذي أدلت بها الرموز صحيحًا.
Semantic Analysis - تحليل دلالات الألفاظ يتحقق تحليل دلالات الألفاظ ما إذا كانت شجرة التحليل-parse tree التي تم بنائها تتبع قواعد اللغة. على سبيل المثال ، يكون تعيين values بين أنواع البيانات المتوافقة، وإضافة string إلى integer. أيضا، يقوم تحليل دلالات الألفاظ بتتبع المعرفات وأنواعها وتعابيرها، ما إذا كان يتم الإعلان عن المعرفات قبل الاستخدام أم لا.... إلخ. ينتج محلل حليل دلالات الألفاظ شannotated syntax tree كمخرجات.
Intermediate Code Generation - توليد الشفرة الوسيطة بعد تحليل دلالات الألفاظ، يقوم المترجم بإنشاء كود وسيط من الكود المصدري للمعالج المستهدف. وهو يمثل لغة مجردة. اي إنه بين اللغة عالية المستوى ولغة الآلة ويجب إنشاء هذا الكود الوسيط بطريقة تجعل من السهل ترجمته إلى معالج الجهاز الهدف.
[صورة مرفقة: intermediate_code.jpg]
هنا الـIntermediate Representation اما يكون خاص بلغة معينة مثل Byte Code الخاص بـ Java او مستقل عن  اللغة ويسمى three-address code
Code Optimization يمكن افتراض لبـOptimization على أنه شيء يزيل سطور التعليمات البرمجية غير الضرورية، ويرتب تسلسل البيانات من أجل تسريع تنفيذ البرنامج دون إضاعة موارد وحدة المعالجة المركزية والذاكرة.
Code Generation - توليد الكود في هذه المرحلة، يأخذ code generator الـoptimized representation للـintermediate code ويعينها إلى لغة الآلة المستهدفة. يقوم code generator بترجمة الكود الوسيط إلى لغة الاله.

لنأتي لخطوة Intermediate Code Generation التي يقوم بها المترجم وفي هذه الخطوة يقوم  بتحويل السورس كود الذي كتبة المبرمج الى intermediate representation

[صورة مرفقة: irgraphexample.gif]
وفي العادة هذه العملية لا تحتوي على semantic information  اي لا تحتوي على التعليقات او المسافات الفارغة اسماء الدوال المتغيرات المحلية والكثير الكثير من المعلومات. وبالتالي ابتعدنا خطوة من ارجاع الكود البرمجي الاصلي.
كما ان عملية الـ optimizations  ايظا ستأخذ نصيبها من تقليل الكود المنتج لكي يكون اقل واسرع ولا يخلوا مترجم محترم من خوارزميات جيدة تقوم بالـoptimizations  وبالتالي سنفقد معلومات اكثر (حتى جافا وسي شارب وفيجوال بيسك تفقد القليل من المعلومات بسبب الـoptimizations وبالتالي لا يمكنك الحصول على الكود الاصلي الذي كتبة المبرمج)

بالنسبة للغات virtual machine (جافا - سي شارب وغيرها)  فهي تحوي ما يسمى بـ metadata هذه الـmetadata  تصف لك الكود البرمجي وبسببها يمكنك ارجاع الكود الذي كتبة المبرمج الى كود شبه مطابق، ولكن لماذا تحوي metadata  الجواب لانها تحتاج ان تعمل على عدة انظمة وربما لمعالجات مختلفة فلغات الـnative  تحول الكود الى 1:1 اي الكود الى ما يقابله من تعليمات لذلك المعالج لذلك النظام (ويندوز ومعالجات انتل على سبيل المثال) وبالتالي لا تحتاج ان تحتفظ بمعلومات تجعل البرنامج ابطأ واكبر وانت اصلا لا تحتاج ان تعمل على معالج او نظام اخر( اذا برمجت برامج لويندوز لا تحتاجة ان يحوي معلومات تخص لينكس أليس كذلك)
وبالتالي الـ Byte-code سيحوي معلومات تمكن والـvirtual machine من تشغيل برنامج جافا على سبيل المثال على لينكس وويندوز وماك وغيرها، بينما لغات الـnative صممت لكي تعمل على نظام مستهدف واحد.
إقتباس :لماذا يصعب الرجوع للغة الأساسية التي كتبت بها البرنامج مثل C بدل من ال Assembly ؟
بالمناسبة لا يتم ارجاع الكود الى لغة Assembly  بل لغة Assembly  هي نفسها لغة الاله تقريباً (عدى ان المعالج لا يفهم الاسمبلي بل الـ0 والـ1)، صممت لغة الاسمبلي كتعليمات يسهل حفظها من قبل المبرمج بسبب صعوبة حفظ 0100010010010100 لذلك احرف يكون اسهل.
المعالج يفهم  الـ0 والـ1 والملف التنفيذي يجب ان يحوي على بيانات مكونة من الـ0 والـ1 على شكل بايتات هيكس
بمعنى لو اردت ان تقوم بنقل 1 الى مسجل معين مثلاً mov 1 الى مسجل  eax فعليك كتابة  0xb801000000  على شكل بايتات هيكس
الـ disassembler سيحول الـ0xb801000000  الى  mov eax ,1 (بلغة الالة تحول او ترمز الى 0xb801000000 ) وبالتالي التحويل الى الاسمبلي ليس التحويل من لغة الالة الى لغة اخرى بل هي تقريباً نفسها

----------
الإنسان معرض للخطأ فأي خطأ تجدونه او أي تصحيح مرحب به Heart
شكرا جزيلا أخي الكريم ...
أفدتني بشكل كبير  Heart