- 4- واجهة ملف الـ PE:
“واجهة ملف الـ PE” هو الاسم العام لما يطلق عليه تقنياً “صورة واجهة الـ (IMAGE_NT_HEADERS (NT. هذه الجزئية من الملف تحتوي على المعلومات الضرورية التي تُمكن مُحمل البرامج من أداء عمله. هذه الجزئية تنقسم إلى ثلاث أقسام رئيسية:
التوقيع يًكون أول أربع بايتات DWORD وقد تم توضيحه مسبقاً. ودائماً ما يحتوي على الحرفين PE متبوعان بصفريين. يلي التوقيع واجهة الملف FileHeader ويتكون من 20 بايت، تحتوي واجهة الملف هذه على معلومات عن التخطيط الفيزيائي العام وخواص الملف مثل عدد الأقسام الموجودة فيه. الواجهة الاختيارية OptionalHeader دائماً موجودة وتشكل الـ 224 بايت بعد واجهة الملف. تحتوي الواجهة الاختيارية معلومات التخطيط المنطقي للملف مثل عنوان نقطة الدخول في الذاكرة (سيتم توضيحها لاحقاً).
واجهة الملف FileHeader تحتوي على العناصر التالية:
أغلب هذه العناصر ليست ذات أهمية كبيرة، ما يهمنا الآن هو “عدد الأقسام” لأننا سنقوم بتغيره في حال قمنا بإضافة أو حذف أي قسم من أقسام ملف الـ PE. العنصر الأخير في الصورة هو” الخواص” و يحتوي على معلومات تُحدد ما إذا كان هذا الملف ملف تنفيذي “exe.” أو ملف ربط ديناميكي. فلنعد إلى برنامج الآلة الحاسبة الذي كنا قد فتحناه بمحرر الهيكس، يمكنك أن تصل إلى “عدد الأقسام” بتجاوز الـ DWORD الخاصة بالتوقيع ثم الـ WORD (2 بايت) الخاصة بعنصر “الآلة” ثم ستصل إلى عدد أقسام الملف والمتمثل بـ 2 بايت:
الصورة تبين أن عدد الأقسام هو 8 (تذكر أن الجهاز يخزن الأرقام بترتيب معكوس). الأقسام الثمانية هذه هي الموجودة في آخر الملف كما هو موضح في الصورة رقم 20. كما يمكنك التأكد من ذلك باستخدام أي برنامج من برامج تحليل ملفات الـ PE مثل برنامج PEBrowsePro الذي يمكن تحميله من هنا:
أو برنامج LordPE الذي يمكن تحميله من هنا:
وأيضاً برنامج PEID الذي يمكن تحميله من هنا:
هناك أمر مهم يجب ذكره قبل الاستمرار، نحن الآن ندرس التركيب الأساسي لأي برنامج يستطيع العمل على الجهاز. لكن غالباً ما يقوم مبرمج أي برنامج بعمل تحزيم Packing لبرنامجه، تحزيم ملف الـ PE هو ضغط البرنامج تماماً كما يفعل أي برنامج للضغط مثل WinRar، البرنامج الذي يقوم بالتحزيم يقوم أيضاً بإضافة كود في الملف لكي يقوم الملف بفك تحزيم أو فك تشفير نفسه قبل أن يبدأ عمله المعتاد. تحزيم الملف Packing له فوائد عديدة منها تصغير حجم الملف وتصعيب مهمة المهندسين العكسيين من فهم الملف واستخراج أسراره. تعلم وفهم عملية التحزيم مهم جداً في هذا المجال. برنامج PEID يعتبر الأداة الأولى في مجال فك تحزيم الملفات unpacking. يمكنك أن تجد رابط تحميل للبرامج التي تم استخدامها حتى الآن في آخر الملف.
العنصر الأول في واجهة الملف “الآلة” Machine يشير إلى نوع المعالج الذي يستطيع تشغيل الملف. فالرقم Ch014 يشير إلى المعالج Intel 386 وما هو أفضل منه. والرقم Dh014 يشير إلى المعالج Intel 486 وما هو أفضل منه. والرقم Eh014 يشير إلى المعالج Intel 586 والمعروف أيضاً بـ Pentium وما هو أفضل منه. والرقم h0200 يشير إلى Intel 64-bit .
ننتقل الآن إلى الواجهة الاختيارية وهي الجزء الأخير وربما الأكثر أهمية في واجهة ملف الـ PE. تشكل العناصر في الواجهة الاختيارية 224 بايت من حجم الملف، هذه العناصر تنقسم إلى قسمين كما هو موضح في الصورة رقم 24، أول 96 بايت يمثلون بالعناصر الموضحة بالصورة، وآخر 128 بايت يدخلون في نطاق جزء آخر من الملف يُطلق عليه دليل البيانات DataDirectory. أهم العناصر في الجزء الأول من الملف هي التالي:
- عنوان نقطة الدخول AddressOfEntryPoint، قبل البدء بهذا العنصر هناك أمر يجب أن تعرفه. هناك ثلاث أنواع من العناوين نشير إليها عند التحدث عن ملفات الـ PE، أول نوع هو ما كنا نستخدمه منذ بداية حديثنا في هذا المستند وهو عنوان أي عنصر بالنسبة لبداية الملف. وكنا قد رمزنا إليه بالـ Offset وهو يشير لعنوان أي عنصر بالنسبة لبداية الملف الموجود على القرص الصلب (وهو الذي نتعامل معه من خلال برنامج محرر الهيكس). فعنوان العصنر lfanew مثلاً الموضح في الصورة رقم 16 هو 60 (أو Ch3)، أي أنك إذا بدأت من بداية الملف وتجاوزت 60 بايت فإنك ستصل إلى هذا العنصر. النوعان الآخران يتعاملان مع الملف بعد نقله إلى الذاكرة، فهناك العنوان الظاهري النسبي (Relative Virtual Address RVA) وهو مثل الـ Offset تماماً لكن بعد نقل الملف إلى الذاكرة (أي انه عنوان اي عنصر من عناصر الملف في الذاكرة بالنسبة لبداية الملف). وهناك العنوان الظاهري Virtual Address بدون كلمة نسبي Relative. أعتقد أنك تعرف لماذا يسمى عنوان ظاهري، وذلك لأننا لا نرى الذاكرة الحقيقية بل الظاهرية وكما قلنا سابقاً فإن هذا غير مهم، فقط تجاهله وتعامل معها كأنها ذاكرة حقيقية. ما يهمنا حقاً هو كلمة نسبي Relative. أنت تعرف الآن أن أجزاء الملف المختلفة لن تكون بنفس القرب من بعضها البعض عند تحميلها إلى الذاكرة، لكنها ستكون بنفس الترتيب. فلنقل أن مُحمل البرنامج قرر تحميل الملف من بدايته إلى عنوان الذاكرة 4000000 ولكنه قام بتحميل أحد الأقسام (ولنقل قسم الكود البرمجي CODE) إلى العنوان 4001000. عندها فإن عنوان الذاكرة الظاهرية Virtual Address هو 4001000 وعنوان الذاكرة الظاهرية النسبي هو 1000. عنوان نقطة الدخول AddressOfEntryPoint الموضح في الصورة رقم 24 يشير إلى عنوان أول تعليمة سيتم تنفيذها بعد نقل الملف إلى الذاكرة. إن وصلنا إلى قيمة هذا العنصر في برنامج الآلة الحاسبة سترى أنه:
قيمة العنصر (نقطة الدخول) هي 0002ADB4 (تذكر الترتيب العكسي للأرقام)، هذا هو العنوان الظاهري النسبي AddressOfEntryPoint لأول تعليمة سيتم تنفيذها بعد تشغيل الملف في الذاكرة، و ليست الموجودة في الملف على القرص الصلب والتي تظهر في الصورة التالية:
فنحن حتى الآن (أي ان هنالك طريقة سيتم شرحها لاحقاً) لا نعرف أين سيتم وضع الأقسام المختلفة في الذاكرة، فالتعليمة ىالتي تشغل هذا الموقع في الذاكرة ستكون أول ما سيقوم المعالج بتنفيذه. أعلم أن هذا الموضوع معقد قليلاً لكنه سيتوضح ما أن نقوم بدراسة الأقسام المختلفة فيما بعد. العنصر التالي المهم في الواجهة الاختيارية هو قاعدة الصورة ImgeBase والمقصور فيه العنوان الذي يُفضل وضع الملف فيه في الذاكرة ، فإذا كانت قيمة هذا العنصر 400000h فإن مُحمل البرامج Windows Loader سيحاول وضع ملف الـ PE في هذا العنوان في الذاكرة كما تم توضيحه في مثال سابق. وكلمة “مفضل” يُقصد فيها أن مُحمل البرامج قد لا يقوم بوضع الملف في ذلك العنوان في حال كان هنالك ملف آخر يشغل ذلك الموقع من الذاكرة، في 99% من البرامج تكون قيمة هذا العنصر 400000h.
- العنصر التالي هو إزاحة الأقسام Sections Alignment وهو يشير إلى إزاحة الأقسام في الذاكرة. فمثلاً، إذا كانت قيمة هذا العنصر هي 1000h (4096 بايت) فإن كل قسم من أقسام الملف (الأقسام الموجودة في آخر الملف وليست الأجزاء التي يتكون منها ملف الـ PE) سيبدأ عند قيمة تكون أحد مضاريب الرقم 1000h أي (1000h، 2000h ، 3000h…). فإذا بدأ القسم الأول بعنوان الذاكرة الظاهرية 401000h وكان حجمه 10h فقط، فإن القسم التالي سيبدأ بعنوان 402000h بغض النظر عن المساحة الغير مستخدمة بين القسمين 401000h و 402000h. كما ان هذا العنصر هو ما يحدد حجم الصفحة page في الذاكرة كما تحدثنا سابقاً.
- إزاحة الملف FileAlignment يشير إلى إزاحة الأقسام على القرص الصلب (وهو ما نتعامل معه من خلال برنامج محرر الهيكس). فمثلاً إن كانت قيمة هذا العنصر 200h أي (512) بايت، فإن كل قسم سيبدأ برقم يكون من مضاريب الرقم 200h. فإن كان القسم الأول يحمل العنوانOffset 200h وحجمه 10 بايت فقط، فإن القسم الثاني سيبدأ عند العنوان 004h بغض النظر عن المساحة غير المستخدمة بينهما، وحتى إن كان حجم القسم الأول أكبر من 200h ولنقل أن حجمه 500h فإن القسم الثاني سيبدأ عند العنوان 600h (لأنه أحد مضاريب الرقم 200h).
- العنصر التالي هو حجم صورة الملف في الذاكرة SizeOfImage وهو يمثل حجم الملف الكلي في الذاكرة، وهو مجموع كل الواجهات والأقسام والمساحات بين الأقسام الناتجة عن عنصر إزاحة الأقسام Sections Alignment.
- حجم الواجهات SizeOfHeaders هو حجم كل الواجهات من أول الملف + جدول الأقسام Section Table. أي أن هذه القيمة تساوي حجم الملف بالكامل ناقص مجموع أحجام كل الأقسام الموجودة في نهاية الملف. كما يمكنك أن تستخدم قيمة هذا العنصر كعنوان لأول قسم من أقسام الملف.
والآن ننتقل إلى القسم الثاني من الواجهة الاختيارية وهو دليل البيانات DataDirectory. دليل البيانات عبارة عن بنية ككل ما سبقها وتشتمل على 16 عنصر كل منها يشير إلى بينة اخرى أساسية في الملف مثل جدول العناوين الواردة. الصورة التالية توضح الصورة الكلية لملف الـ PE بإستخدام محرر الهيكس:
قبل الانتقال للحديث عن دليل البيانات، هنالك شيء أريدك أن تُلاحظه، أنظر إلى واجهة تنصيب الدوس Dos Stup التالية:
ترى في الصورة أن هنالك بيانات لا معنى لها توجد بين واجهة تنصيب الدوس Dos Stup وواجهة ملف الـ PE.هذه البيانات تجدها في البرامج التي تمت ترجمتها من لغة عالية المستوى إلى لغة منخفضة المستوى باستخدام أحد برامج مايكروسوفت Micro$oft. لا نرى هذه البيانات في برنامج الآلة الحاسبة لأنه قد تمت ترجمته باستخدام Borland Delphi (يمكن أن تعرف ذلك باستخدام برنامج PEID). هذه البيانات محتويةً على كلمة Rich التي تشمل الأربع بايت القبل الأخيرة توجد في كل البرامج التي تم وصلها (وصل Linking الملف هو أحد مراحل ترجمته من لغة عالية المستوى إلى ملف PE) باستخدام M$Link.exe من الإصدار v5.12.8078 وحتى الآن. هذه البيانات تشتمل على كود مشفر يقوم بتحديد العوامل التي أُستخدمت في ترجمت هذا الملف. ويبدوا أن الهدف من هذه البيانات هو تحديد مبرمج الملف (في حال ما إذا كان هذا ملف فيروس تمت ترجمته بأحد برامج مايكروسوفت) وإثبات أن هذا الفيروس تم صنعه على كمبيوتر هذا المبرمج. يمكنك العودة إلى مجلة CodeBreakers لمقالة Goppit لتعرف المزيد عن هذه البيانات وطريقة فك تشفيرها.
- دليل البيانات DataDirectory:
للتذكير فقط، دليل البيانات DataDirectory يمثل آخر 128 بايت من الواجهة الاختيارية OptionalHeader، كما أنه آخر جزء من واجهة ملف الـ PE. يحتوي دليل البيانات على 16 بنية Sruct. كل منها تتكون من 8 بايت، وكل بنية تتكون من عنصرين كلٌ منهم 4 بايت (4x2x16 = 128).
أول عنصر في كل بنية يمثل عنوان الذاكرة الظاهري النسبي لبنية البيانات تلك (سنرى لاحقاً) والعنصر الثاني يدعى isize ويمثل حجم بنية البيانات التي يشير إليها العنوان. يمكنك أن ترى كل بنية في دليل البيانات باستخدام محرر الهيكس:
كل مربع في الصورة يحتوي على 8 بايت، أول أربع بايت تمثل عنوان الذاكرة الظاهري وثاني أربع تمثل الحجم. يمكنك أن ترى الأمر بصورة تفصيلية وأوضح باستخدام برنامج LoardPE :
الخانات المظللة في الصورة رقم 30 والصورة رقم 31 هي الخانات المستخدمة فقط في برنامج الآلة الحاسبة، والبقية أصفار. أنت تعرف الآن أن موقع دليل البيانات ثابت دائماً في كل ملفات الـ PE، فجدول الموارد Import Directory في الصورة رقم 30 سيمثل دائماً الـ 4 بايت بعد أول 80 بايت من بداية واجهة ملف الـ PE. وبالتالي يستطيع مُحمل البرامج أن يعرف أين تقع بنية جدول الموارد Import Directory من خلال دليل البيانات لاحظ أن برنامج LoardPE يعرض الأرقام بترتيبها الصحيح بعكس برنامج الهيكس الذي يستخدم الترتيب العكسي. على الأغلب ما زلت لا ترى فائدة واضحة لدليل البيانات، ولكنه يعتبر من أهم مكونات ملف الـ PE وسنعود إليه لاحقاً لتوضيح أهميته بالتفصيل.
- جدول الأقسام Section Table :
هذا هو القسم التالي مباشرةً بعد واجهة ملف الـ PE. يتكون هذا القسم من مصفوفة من عدة أقسام، كلٌ منها يحتوي على عناصر معينة. أي أنه كما رأينا أن دليل البيانات يتكون من عدة أجزاء، وكل جزء يحتوي على عنصرين الأول يمثل عنوان ذاكرة والثاني يمثل حجم بنية. نفس الأمر هنا، جدول الأقسام يحتوي على عدة أجزاء هو الآخر، عدد هذه الأجزاء (كل جزء يسمى بنية) يساوي عدد الأقسام الموجودة في الملف (تذكر أننا عرفنا عدد الأقسام في الملف من العنصر الثاني في واجهة الملف FileHeader الموجود في واجهة ملف الـ PE، أي 6 بايت من بداية واجهة ملف الـ PE ومنه عرفنا أن برنامج الالة الحاسبة يحتوي على 8 أقسام)، وبالتالي فإن جدول الأقسام يتكون من 8 أجزاء، كل جزء يحتوي معلومات مهمة عن قسم من الأقسام. البيانات الموجودة في كل قسم معرفة كالتالي:
الصورة 32 تبين معنى كل عنصر من عناصر أجزاء جدول البيانات الثمانية. طبعاً بعض العناصر تكون مهمة فقط والبقية لا حاجة لنا بمعرفتها. تذكر أن تلك المعلومات مكررة بحسب عدد أقسام الملف (8 أقسام في الآلة الحاسبة) وكل مجموعة منها تعطي معلومات عن قسم من أقسام الملف.
- العنصر الأول “اسم القسم” Name1 وهو فقط اسم للدلالة على القسم الذي نتعامل معه الآن، لكن كما قلنا في بداية المستند، هذا الاسم لاستخدام المبرمج فقط ولا يحتاج إليه نظام التشغيل وبالتالي يمكن تركه فارغاً.
- حجم القسم VirtualSize (أو أحياناً يكون معناها عنوان القسم PhysicalAddress) يشير إلى الحجم الحقيقي لبيانات القسم على القرص الصلب بالبايت دون حساب المساحة الزائدة نتيجة إزاحة الملف، قيمة هذا العنصر غالباً ما تكون أقل من حجم البيانات الخام SizeOfRawData والتي تشمل بيانات القسم بالإضافة إلى المساحة الناتجة عن إزاحة الملف.
- عنوان القسم Virtual Address وهو عنوان الذاكرة الظاهري النسببي Relative Virtual Address RVA لهذا القسم في ذاكرة الكمبيوتر، مُحمل البرامج يستخدم هذه القيمة ليعرف أين يضع البرنامج في الذاكرة، فإذا كانت القيمة في هذه الخانة 1000h ووضع الملف من أوله في العنوان 400000h فإن عنوان هذا القسم في الذاكرة سيكون 401000h . لكن انتبه، هذا ليس عنوان أول تعليمة سيتم تنفيذها، فأول تعليمة توجد في خانة “عنوان نقطة الدخول” AddressOfEntryPoint الذي تحدثنا عنه سابقاً.
- حجم البيانات الخام SizeOfRawData يشير إلى حجم بيانات القسم على القرص الصلب مقربة للمضروب التالي من إزاحة الملف.
- مؤشر إلى البيانات الخام PoiterToRawData (العنوان الخام Raw Offset) يشير إلى عنوان القسم على القرص الصلب من بداية الملف Offset. مُحمل البرامج يستخدم قيمة هذه الخانة ليجد مكان البيانات الموجودة في القسم داخل الملف.
- العنصر “خواص” Characteristics يحتوي على بيانات تبين ما إذا كان هذا القسم يحتوي على كود تنفيذي أو معلومات بحتة، وما إذا كان يمكن الكتابة أو القراءة منه (صلاحية القسم).
عند البحث عن قسم معين يمكنك تجاوز واجهة ملف الـ PE بالكامل والبدء بالبحث عن اسم القسم في جدول الأقسام من خلال شاشة الأسكي على يمين محرر الهيكس. الصورة التالية توضح جدول الأقسام للأقسام الثمانية في ملف الآلة الحاسبة:
المعلومات التي تم تظليلها بالصورة هي التي تهمنا والتي يمكن عن طريقها أن نجد كل قسم ونقوم بتعديل بياناته أو ما شابه. فعنوان الذاكرة الظاهرية المظلل باللون الوردي يشير إلى العنوان النسبي الذي سيقوم محمل البرامج بتحميل هذا القسم إليه في الذاكرة، فنوان الذاكرة الظاهرية النسبي في القسم الأول (وهو قسم الكود التنفيذي) هو 00001000h، وبالتالي فإن القسم الأول سيوضع في العنوان 00401000h في الذاكرة (في حال ما إذا قام محمل البرامج بالموافقة على استخدام الرقم 0040000 الموجود في خانة قاعدة الصورة Imagebase في الواجهة الاختيارية). وعنوان القسم الثاني سيكون 0042B000h (طالما أنه يقبل القسمة على 200h فهو يحقق إزاحة الملف التي تحدثنا عنها سابقاً). ما الذي يفيدنا في معرفة عنوان الأقسام في الذاكرة؟ بكل بساطة فإن هذا يفيدنا في معرفة من أين سيبدأ البرنامج بالعمل. بمعنى أنه إذا عدنا إلى خانة “عنوان نقطة الدخول AddressOfEntryPoint” في الواجهة الاختيارية سترى أنها تحمل القيمة 0002ADB4h في برنامج الآلة الحاسبة (لا تنسى الترتيب العكسي للأرقام)، نقطة الدخول في ذاكرة الحاسوب ستكون 0042ADB4h (بعد إضافة قيمة قاعدة الصورة)، ستلاحظ أن هذه القيمة الجديدة والتي تشير إلى أول ما سينفذه البرنامج عند بدء تشغيله تقع بين عنوان القسم الثاني (0042B000h) وعنوان القسم الأول (00401000h) وهذا يعني أنها موجودة في القسم الأول (دائماً ما تكون أول تعليمة موجود في قسم الكود التنفيذي فهذا هو القسم الوحيد الذي يحتوي على المعلومات التي يجب على المعالج أن ينفذها بعكس بقية الأقسام كما سنرى لاحقاً). الآن لكي نجد تلك التعليمة في الملف الموجود على القرص الصلب علينا فعل التالي:
- العنصر “مؤشر إلى البيانات الخام PointerToRawData” يحتوي على عنوان بداية القسم أثناء وجوده في القرص الصلب، كما ترى فإن العنوان هو (00000400h) ويحتوي على التالي:
هذا هو بالفعل عنوان بداية القسم فكما ترى لا يوجد أي بيانات قبل العنوان 400h.
- الآن نحن نعرف أن هذا العنوان المظلل في الصورة ستكون قيمته 00401000h في الذاكرة والفرق بين عنوان الذاكرة والعنوان في الملف هو C00h (3072 بالنظام العشري)، وبالتالي فإن عنوان أول تعليمة سيتم تنفيذها في الملف التنفيذي على القرص الصلب سيكون
2A1B0h = 0042ADB4h – 00400000 – C00h وهو الموضح في الصورة:
الآن أعتقد أن السؤال الأكبر في ذهنك هو : ما معنى وكيف لمجرد رقم من الأرقام ان يكون هو أول تعليمة أو أول أمر سيقوم الكمبيوتر بتنفيذه؟؟ سنتحدث عن هذا الموضوع المهم بالتفصيل في جزئية أخرى من الكتاب لاحقاً، لكن الآن دعنا ننهي شرح أقسام ملف الـ PE المختلفة.
عند تحميل ملف الـ PE إلى الذاكرة فإن الأقسام دائماً ما تبدأ في حدود صفحة جديدة في الذاكرة، أي أن أول بايت من كل قسم يمثل صفحة في الذاكرة. في عائلة معالجات x86 كل صفحة تبدأ في حدود 4 كيلوبايت (4096) بينما في معالجات IA-64 فإن كل صفحة تبدأ في حدود 8 كيلوبايت. طبعاً هذه القيمة التي تحدد حدود البرنامج في الذاكرة مخزنة في خانة “إزاحة الأقسام SectionAlignment” في الواجهة الاختيارية.
- أقسام ملف الـ PE:
الأقسام هي المحتوى الرئيسي لملف الـ PE والتي تتضمن الكود التنفيذي، البيانات أو معلومات الملف البحتة، المصادر، ومعلومات أخرى يحتاجها الملف لكي يُتم عمله. لكل قسم هناك واجهة و جسد (محتوى القسم). واجهة القسم هي الموجودة في “جدول الأقسام Section table” والذي تعرفنا عليه وعلى محتواه سابقاً. أما بالنسبة لجسد القسم فإنه يفتقد إلى ترتيب أو بناء معين، فهو يُنظم بأي طريقة يرغب بها رابط أو واصل البرامجThe Linker (عملية ربط البرنامج هي إحدى مراحل انتقاله من لغة عالية المستوى إلى ملف PE). أي برنامج في عائلة Windows NT يمكن أن يحتوي على 9 أقسام معرفة وهي .text و .bss و .rdata و .data و .rsrc و .edata و .idata و .pdata و .debug بعض البرامج لا تحتاج إلى كل هذه الأقسام بينما البعض الآخر قد يحتوي على أقسام أكثر من ذلك.
- قسم الكود البرمجي
كل الكود البرمجي في Windows NT يقع في قسم واحد يدعى text. أو CODE. فحيث أن نظام الـ Windows NT يستخدم الذاكرة الظاهرية ومبدأ صفحات الذاكرة، فإدارة قسم واحد كبير خاص بالكود التنفيذي أسهل لنظام التشغيل ومطور البرنامج. هذا القسم كما رأينا سابقاً يحتوي على نقطة دخول البرنامج، وهي أول تعليمة سيقوم البرنامج بتنفيذها وسيتم توضيح كيفية ذلك في الجزئية التالية من الكتاب.
– قسم البيانات أو المعلومات البحتة Data ويمثل بثلاث أقسام:
القسم المدعو bss. يحتوي على بيانات غير معرفة للبرنامج والتي تتضمن المتغيرات الثابتة التي تم تعريفها للدوال المختلفة.
أما القسم rdata. فهو يحتوي على بيانات للقراءة فقط Read-Only Data مثل قيم الثوابت Constants.
بقية المتغيرات تُخزن في قسم data.
– قسم المصارد Resources
القسم المدعو rsrc. يحتوي على المصادر مثل الصور التي يظهرها البرنامج. بيانات هذا القسم مرتبة بشكل هرمي ولكي تستطيع مشاهدتها بشكل أفضل عليك باستخدام برنامج محرر للمصادر Resources editor مثل ResHacker:
هذه الأداة القوية لها القدرة على ترجمة البيانات الرقمية الموجودة في قسم المصادر إلى ما يقابلها مما تشاهده عند فتحك للبرنامج. وتستخدم أيضاً في كسر البرامج Cracking.
– قسم البيانات الصادرة Export:
قسم edata. يحتوي على معلومات مختلفة تخص الدوال التي يصدرها البرنامج وسيتم تخصيص جزئية من الكتاب للتحدث عنها بالتفصيل.
– قسم البيانات الواردة Import data :
قسم idata. يحتوي على معلومات مختلفة تخص الدوال يجلبها البرنامج من الخارج لأسباب وطرق سنعرفها بالتفصيل في جزية خاصة لاحقاً.
– قسم معلومات متابعة عمل البرنامج Debug Information:
معلومات متابعة عمل البرنامج توضع في قسم debug. كما أن ملف الـ PE يدعم ملفات منفصلة عنه لمتابعة معلومات عمل البرنامج (يكون امتدادها DBG.) كوسيلة لجمع المعلومات الضرورية في مكان موحد.
– مساحة تخزين المسار Thread Local Storage:
عند فتح أي برنامج فنسخته الموجودة في الذاكرة والتي تناقشنا مطولاً كيف سيكون شكلها تسمى Module، أما كل المساحة التي ستأخذها من الذاكرة والتي يوجد بها الكود ونشاطه الحالي فإنها تسمى العملية Process. يُمكنك رؤية كل العمليات التي تعمل في ذاكرة حاسوبك من خلال مدير العمليات Task Manager:
كل عملية Process تحتوي على عمليات فرعية أصغر في داخلها، كلُ منها يسمى المسار Thread (ركز على المصطلحات الإنجليزية لعدم دقة الترجمة العربية). لسرعة الكمبيوتر تشعر أنك تقوم بأكثر من عملية في وقد واحد، ولكن الحقيقة أن المعالج (في الكمبيوترات التي تحتوي على معالج واحد) يقوم بتنفيذ كل حركة وكل إجراء على حدة. فنظام التشغيل يعطي دوراً لكل عملية لتعمل لفترة معينة، ثم يجعلها تنتظر إلى أن ينتهي من عملية أخرى قبل أن يعود إليها من جديد. عندما يقوم البرنامج بإرسال طلب إلى الشاشة لإظهار صورة أو إلى الطابعة أو لوحة المفاتيح (سيتم توضيح طريقة قيامه بذلك في القسم التالي) فإن البرنامج يقوم بإنشاء مسار Thread لتلك العملية الجديدة يحتوي على بيانات تلك العملية حتى يستطيع نظام التشغيل أن يعود إليها لاستكمال تنفيذها. أغلب نظم التشغيل توفر خاصية تعدد المهام Multitasking وتعدد المسارات Multithreading. كل المسارات Threads الموجودة في عملة ما تستطيع أن تشترك نفس المعلومات الموجودة في مساحة الذاكرة المخصصة لتلك العملية. لكن العمليات المختلة Processes لا تشترك الذاكرة مع بعضها البعض إلا من خلال إجراء معقد قليلاً. الصورة التالية توضح كيف أن المعالج يقوم بتنفيذ مسار معين خلال العملية الواحدة في كل لحظة، ثم يتركه لينتظر ويقوم بتنفيذ مسار آخر قبل أن يعود للأول:
وقد تم تشبيه هذا الأمر في بعض الكتب وكأن هنالك أكثر من طباخ يقومون باستخدام نفس الكتاب لإعداد وصفتهم ولكن ليس من الضروري أن يكونوا يقرؤون من الصفحة. الكتاب هو العملية والطباخون هم المسارات. طبعاً نظام الويندوز من الأنظمة التي تضم خاصية تعدد المسارات Multitasking، ولكل مسار هنالك مساحة خاصة به تسمى “مساحة تخزين المسار Thread Local Storage“وتختصر TLS. ويقوم كل مسار باستخدام هذه المساحة لتخزين البيانات الخاصة بالعملية الصغيرة التي قد أُنشئ المسار للقيام بها، وتشمل هذه البيانات مؤشرات إلى البيانات والمصادر التي سيستخدمها هذا المسار. ويحتوي القسم tls. في ملف الـ PE على تصميم ومخطط لمساحة تخرين المسار TLS التي قد يحتاجها البرنامج وأي ملف DLL يشير إليها. في كل مرة تقوم العملية Process بإنشاء مسار جديد Thread يحصل هذا المسار على مساحة تخزين TLS خاصة به باستخدام قسم الـ tls. كنموذج لذلك.
قسم إعادة موقع القاعدة Base Relocations :
عندما يتم إنشاء أي برنامج فإن البرنامج الواصل Linker يقوم بتخمين الموقع الذي سيتم تحميل البرنامج إليه في الذاكرة (تذكر خانة قاعدة الصورة ImageBase سابقاً). إذا حصل في أي حال من الأحوال وتم تحميل البرنامج في موقع آخر في الذاكرة الظاهرية فإن تلك العنوان الافتراضية التي وضعها البرنامج الواصل Linker في ملف الـ PE تكون غير صحيحة. القسم reloc. يسمح لمحمل البرامج بتصحيح هذه العناوين في نسخة البرنامج الموجودة في الذاكرة بحيث يكونوا مسجلين بشكل صحيح. في حال ما تمكن محمل البرامج من تحميل البرنامج على العنوان الذي افترضه البرنامج فإن القسم reloc. يتم تجاهله. محتوى القسم reloc. يسمى “إعادة موقع القاعدة Base Relocation” وقد سُمي بذلك لأنه يعتمد على العنوان الموجود في خانة “قاعدة الصورة ImageBase“. هذا القسم يحتوي على لائحة بكل المواقع الموجودة في صورة البرنامج في الذاكرة والتي يمكن أن تحوي معلومات خاطئة لكي يتم تصحيحها.
- نظرة في قلب البرامج:
في هذه الجزئية من الكتاب سيتم توضيح الكثير من الأمور التي تم تأجيلها والتي تُعد صلب موضوعنا. نبدأ باستخدام كل المعلومات التي جمعناها حتى الآن لكي نقوم ببناء صورة لأحد البرامج في الذاكرة. البرنامج الذي سنستخدمه يدعى Crackme ويمكنك تحميله من الرابط:
http://www.woodmann.com/crackz/Archives/Crackmes.zip
كلمة Crackme تشير إلى أنه قد تمت برمجته لكي يحاول الآخرون كسره وهذا النوع من البرامج منتشر بكثرة في الإنترنت. تم اختيار هذا البرنامج لصغر حجمه مما يسهل علينا شرحه. في هذه الجزئية من الكتاب سنقوم بمتابعة عمل هذا البرنامج خطوة بخطوة وفهم كل ما يحدث أمامنا مما سيقودنا لمعرفة كيفية كسر البرنامج أو إيجاد الكلمة السرية الصحيحة. هذا البرامج يُعد مثال فقط لا أكثر ولا أقل فهو لا يختلف عن أي برنامج في جهازك. عند فتح البرنامج سترى الآتي:
وعند الضغط على كلمة ok !! بعد كتابة اسم وكلمة السر:
سيخبرك البرنامج أن ما أدخلته خاطئ:
الذاكرة الموجودة في جهاز تتكون عملياً من عدد كبير جداً من وحدات صغيرة جداً كلً منها يستطيع تخزين بت واحد. كل 8 بت يطلق عليها بايت. كل 1024 بايت يطلق عليها كيلوبايت (الكيلو في علوم الكمبيوتر يختلف عن الكيلو الفيزيائي الذي يعادل 1000 فقط). إذا كان حجم ذاكرة الرام لديك 2 جيجابايت مثلا فأول موقع سيكون 0000000 وآخر موقع سيكون 2097151، أو بالنظام السداسي عشر من 000000h إلى 1FFFFFh. عند فتح البرنامج Crackme فإن مُحمل البرامج سيقوم بقراءة العنصر ImageBase ليرى أين سيضع البرنامج في الذاكرة. تذكر أن مُحمل البرامج هو الآخر برنامج يعمل في مكان ما في الذاكرة، وكذلك الحال مع كل العمليات التي تعمل في نافذة مدير العمليات Task Manger الموضحة في الصورة رقم 37. وتذكر موضوع الذاكرة الظاهرية الذي تحدثنا عنه سابقاً. فإن وجد مُحمل البرامج القيمة 400000h في العنصر ImageBase فإنه سيقوم بإنشاء عملية جديدة لهذا البرنامج يكون أول موقع فيها هو 400000h (إلا إن قام مُحمل البرامج بتحميل البرنامج في موقع آخر لانشغال هذا الموقع). هذا الموقع لن يكون في الذاكرة الفيزيائية الحقيقية الموجودة في الجهاز، بل في الذاكرة الظاهرية التي أنشئها نظام التشغيل للبرنامج (والتي توجد أيضاً في الذاكرة الحقيقية ولكن ليس بالضرورة في الموقع 400000h). فالبرنامج سيرى أنه قد تم وضعه في الموقع 400000h ولن يرى الموقع الفيزيائي الحقيقي الذي وُضع فيه. لماذا يهتم البرنامج بالموقع الذي سيوضع فيه؟ لأن هناك الكثير من تعليمات القفز أو الانتقال إلى موقع جديد أو تعليمة جديدة في حال تحقق شرط ما أو في حال عدم تحققه وهو ما سنراه عملياً بعد أن نكمل صورة البرنامج في الذاكرة. الذي سيوضع في الموقع 400000h تحديداً هو واجهة الـ Dos وواجهة تحميل الدوس Dos Stup وواجهة ملف الـ PE وكذلك جدول الأقسام Section Table، أي كل أجزاء الملف ما عدا الأقسام الأخيرة. إن رأيت هذه الأقسام باستخدام محرر الهيكس ستجد أن نهاية آخر عنصر في جدول الأقسام يوجد في العنوان 2E7h (743 بالنظام العشري) من أول الملف. أي أن الملف سيوضع في الذاكرة من بدايته في العنوان 400000h بنفس الترتيب وستكون نهاية آخر عنصر في جدول الأقسام في العنوان 4002E7h. وبعد ذلك سيبدأ البرنامج برفع الأقسام في الذاكرة. يقوم مُحمل البرامج بقراءة العنصر عنوان الذاكرة الظاهرية Virtual Address الموجود في جدول الأقسام لكل قسم على حدة ومنه يعرف أين سيضع هذا القسم في الذاكرة. فالقسم الأول في برنامجنا “CODE” يحمل الـقيمة 1000h في العنصر Virtual Address مما يعني أنه سيوضع في العنوان 401000h في الذاكرة. أما بالنسبة للمساحة بين نهاية آخر عنصر من عناصر جدول الأقسام 4002E7h وبداية أول قسم 401000h فإنها ستملأ بأصفار أي ستكون فارغة. القسم الثاني سيوضع في العنوان 402000h وقد عرفنا ذلك نفس طريقة القسم الأول. كما أن القسم الثاني لا يخالف شرط إزاحة الأقسام الموجود في خانة “إزاحة الأقسام Section Alignment” الموجود في الواجهة الاختيارية.فقيمة هذا العنصر هي 1000hمما يعني أن القسم الثاني سيبعد مسافة تكون من أحد مضاريب الرقم 1000h عن القسم الأول. وكذلك الحال مع القسم الثالث الذي سيوضع في العنوان 3000h والقسم الرابع الذي سيوضع في العنوان 4000h والقسم الخامس الذي سيوضع في العنوان 5000h . الآن بعد رفع البرنامج للذاكرة سيبدأ مُحمل البرامج بتنفيذه، في البداية يتم التأكد من توقيع واجهة الدوس Dos “الحرفان MZ“، وجودهما يدل على صحة هذا الواجهة ولكن طالما أنك قٌمت بتشغيل البرنامج بإستخدام تحت نظام الويندوز وليس عن طريق الدوس فإن مُحمل البرامج سينظر إلى آخر أربع بايتات ليبحث عن عنوان واجهة ملف الـ PE وسيتجاهل واجهة تحميل الدوس DOS. بعد ذلك يتأكد من توقيع هذه الواجهة والذي يجب أن يكون “PE”، يأتي بعد ذلك العنصر Machine والذي يدل على نوع المعالج الذي تم إنتاج الملف فيه، فإن كانت القيمة المسجلة هنا لإصدار أحدث من الإصدار الذي يعمل عليه جهازك (أي أن معالجك لا يعرف كل التعليمات التي يمكن للملف أن يقوم بتنفيذها والتي سنراها بعد قليل) فإنه لن يقوم بتشغيل الملف. العنصر التالي هو عدد أقسام الملف Section Number ويقوم محمل البرامج بمقارنته مع الأقسام الموجودة في جدول الأقسام. يليه عنصر يدعى ختم الوقت والتاريخ TimeDateStamp ويحتوي على تاريخ إنشاء الملف… . وهكذا يمر مُحمل البرامج على كل العناصر للتأكد من صلاحيتها وأن الملف كامل وقادر على العمل. بعد ذلك يقوم بإعطاء التحكم للملف نفسه وذلك بجعل الملف يقوم بتنفيذ أول تعليمة له والتي يشير إليها العنصر “عنوان نقطة الدخول AddressOfEnteryPoint“. لرؤية شكل البرامج أثناء تواجده في الذاكرة وكذلك لمتابعة عمله وفهمه بالكامل سنستخدم برنامج Ollydbg. هذا البرامج يُعد الأشهر في مجال الهندسة العكسية للبرامج وذلك لقوته وتعدد إمكاناته، كما أنه مجاني ويمكنك تحميله من الإنترنت /http://www.ollydbg.de.
البرنامج لا يحتاج إلى تسطيب على الجهاز، كل ما عليك فعله هو تحميل الملف وفك الضغط عنه. بعد فتحه أختر من القائمة File > Open ثم اختر برنامج Crackme الذي سنقوم بدراسته. ستظهر لك الشاشة التالية:
هنالك الكثير من النوافذ والأمور التي عليك معرفتها في هذا البرنامج وسنمر عليها بالتفصيل. أول شاشة من شاشات البرنامج والتي توضحها الصورة السابقة تنقسم إلى أربع أجزاء. الجزء الأول بالأزرق يبين موقعنا في الذاكرة، فهو يحدد العنوان الذي نحن موجودون فيه في الذاكرة وهو ما قمنا ببنائه والتحدث عنه في الصفحة السابقة. فكما ترى أول عنون في العمود الخاص بالعناوين في الصورة هو 401000h. لماذا بدأنا عند هذا العنوان؟ لأنه عنوان نقطة الدخول AddressOfEntryPoint الذي تحدثنا عنه كثيراً في السابق. أي إن أي برنامج بعد أن يتم بنائه في الذاكرة فإن أول ما سيقوم بتنفيذه هو التعليمة المحددة في هذا العنوان. نلاحظ أيضاً أن هذا العنوان 401000h هو أيضاً عنوان قسم الكود البرمجي CODE، ولكن ليس بالضرورة أن يكون دائماً مساوياً لعنوان نقطة الدخول. العمود الثاني يبين التعليمات بالنظام السداسي عشر التي سيقوم البرنامج بتنفيذها. ويمكن رؤيتها باستخدام محرر الهيكس على القرص الصلب للتأكد من أنها هي نفس التعليمات:
لترى البرنامج من بدايته في الذاكرة، اضغط بالزر اليمين على النافذة التي نحن فيها وأختر Go to >> Expression ثم اكتب العنوان الذي ترغب الذهاب إليه:
وستظهر لك أقسام الملف الأولى التي تحدثنا عنها مطولاً:
أما إذا حاولت الذهاب إلى عنوان خارجي لا صلة له بالموضوع مثل 301234h فسيخبرك البرنامج أنه ليس هنالك ذاكرة في العنوان الذي تريده. العمود الثالث الملون بالأخضر في الصورة 43 يحتوي على ترجمة للتعليمات المكتوبة بالنظام السداسي عشر والتي يفهمها الجهاز إلى لغة الأسمبلي البسيطة التي يمكننا أن نفهمها نحن. البرامج التي تقوم بعملية ترجمة لغة الآلة إلى لغة الأسمبلي تسمى Assembler، برنامج Ollydbg يحتوي في داخله على Assembler خاص به. ما يمكن ترجمته إلى هذه اللغة هي التعليمات التي سيقوم البرنامج بتنفيذها والموجودة في قسم الكود البرمجي، أما ترجمة بقية أجزاء الملف مثل واجهة الدوس Dos Header فلا معنى له لأن واجهة الدوس ليست تعليمات لتُنفذ بل معلومات مُخزنة عن الملف. أما عن كيفية ترجمة هذه التعليمات فالأمر بسيط، كل بايت في الملف له معنى يفهمه المعالج. فالمعالج في أقرب صورة له يقوم بأخذ تعليمات رقمية من أصفار وآحاد (نتعامل معها بالنظام السداسي عشر لنسهل فهمها علينا) ويقوم بتنفيذها مباشرةُ. هذه أقرب وأوضح صورة يمكننا النظر بها للمعالج:
وعند النظر إلى التعليمات المكتوبة في البرنامج:
ستجد أن لكل تعليمة أو أمر هنالك كود معين (Opcode). فالرقم A6 يدل على الأمر push الذي يقوم بإدخال القيمة التالية إلى ذاكرة الـمكدس Stack والتي كما ذَكرتُ في أول الكتاب أنني أفترض أن القارئ يعرف ما معناها وما الغرض منها. كل رقم من الأرقام السداسي عشر يشير إلى أمر على المعالج القيام به، الرقم 74 يدل على أمر القفزة المشروطة JE والرقم C705 هو الأمر النقل mov. طبعاً قد يكون هناك أكثر من رقم لنفس الأمر مثل 68 و 50 و A6 للأمر push وذلك لاختلاف المدخلات التي تلحق كل منها. المهم أن الفكرة واضحة، هذه الأوامر هي نفسها في كل معالجات Intel في جميع حواسب الكمبيوتر في العالم، وهذا أمر بديهي إن كنا نريد لنفس البرنامج المكتوب بنفس اللغة (أي يحوي على نفس التعليمات السداسية عشر التي شرحناها من البداية) أن يعمل على جميع حواسب العالم. يمكنك رؤية كل أوامر معالجات انتل من عائلة x86 في الموقع التالي: http://ref.x86asm.net
الأمر التالي الذي يجب أن تعرفه هو تركيب معالجات Intel، بالإضافة إلى ذاكرة الـ Stack ووحدة التحكم فإن معالجات انتيل تحتوي على وحدات تخزين صغيرة ومؤقتة تسمى المسجلات Registers. من هذه المسجلات هنالك المسجلات العامة التي يستخدمها البرنامج لأغراضه المختلفة دون تحديد وتشمل: EAX و EBX و ECX و EDX و ESI و EDI و EBP و ESP.
يقوم البرنامج بتخزين أي بيانات يحتاجها أو يريد استعمالها في تلك المسجلات. كل من المسجلات المذكورة تتسع لـ 32 بت من البيانات ولكن يمكن للبرنامج أي يخزن ما يريده في أول 8 بت أو أول 16 بت أو اخر 8 بت أو أخر 16 بت. الصورة التالية توضح تقسيم المسجل الأول EAX إلى أحجامه المختلفة:
فإذا أراد البرنامج أن يقوم بتخزين رقم في أول 8 بت فإن الأمر سيكون
mov AL, 15
فإن كان المسجل EAX فارغاً في البداية فإنه سيصبح بعد تنفيذ التعليمة 00000015 لكن الأمر mov AL, 100 غير صحيح لأنك تحاول وضع قيمة في خانة أكبر مما تتسع له وبالتالي يجب عليك استخدام أول 16 بت بدلاً من أول 8 بت فقط : mov AX, 100 وهكذا. هذه المسجلات ستراها موضح على يمين الشاشة في برنامج Olly فهو يقوم بقراءتها من المعالج عن طريق نظام التشغيل:
بعض هذه المسجلات له استخدام خاص، فالمسجل ESP يقوم تلقائياً بالنقصان بمقدار رقم واحد لكل بايت جديد يدخل إلى ذاكرة المكدس. للتذكير فقط، ذاكرة المكدس هي مجرد ذاكرة عادية في الكمبيوتر تعمل بنظام يدعى LIFO (Last input First output) أي أخر بمعلومة تدخل هذه الذاكرة ستكون أول ما يخرج منها. يمكنك تخيل هذه الذاكرة كعلبة اسطوانية تحتفظ فيها بأقراصك ولكي تتمكن من إخراج احد الأقراص التي بالأسفل فإن عليك أن تخرج التي بالأعلى أولاً والصورة التالية مجرد توضيح للفكرة.
المسجل ESP يقل بمقدار رقم واحد لكل قرص يدخل إلى الاسطوانة، أو لكل بايت يدخل إلى المكدس. الأمر المستخدم لإدخال أي رقم إلى المكدس هو push. وقد رأينا أن هذا هو أول أمر موجود في برنامج crack الذي نقوم بدراسته. ذاكرة المكدس موجودة على الطرف الأيمن في أسفل الشاشة:
كما توضح الصورة هنالك معلومات مخزنة من البداية في ذاكرة المكدس والعنوان الموجود في أعلى ذاكرة المكدس والمظلل بالون الأسود هو 0012FF8C وهو نفس الرقم الموجود في المسجل ESP . الآن لنقم بتنفيذ أول أمر في برنامجنا بالضغط على زر F8 من لوحة المفاتيح (والذي يقوم بتنفيذ أمر واحد فقط في كل مرة تغط عليه). بعد تنفيذ أول أمر والذي هو push 0 (أي ادخل الرقم صفر إلى المكدس) نرى في ذاكرة المكدس أن رقم جديد قد جاء في أول الذاكرة وهو الرقم صفر وحصل على العنوان 0012FF88 أي ان العنوان الجديد أقل من العنوان القديم بمقدار أربع
( 0012FF8C – 0012FF88 = 4) وهذا لأن الرقم الذي تم إدخاله هو 00000000 (أي اربع بايتات (أو 32 بت) من الرقم صفر). لاحظ أن الصفر في الأمر push 0 تشكل 4 بايت وليس بالضرورة بايت واحد. كذلك الرقم الموجود في المسجل ESP أصبح يحتوي على القيمة الجديدة له.
المسجل EIP يقوم بتخزين قيمة التعليمة التي نقف عليها حالياً، فلو لاحظت قيمته في الصورة رقم 50 سترى أنه 00410000h وهي أول تعليمة في البرنامج. ولكن بعد تنفيذ أول أمر أصبحت قيمته 00401002h أي أننا قد انتهينا من التعليمة الأولى في البرنامج push 0 والتي تتكون من بايتين بالنظام السداسي عشر. فالرقم المخزن في هذا المسجل هو نفسه عنوان الذاكرة الذي نقف عنده حالياً.
التعليمة التالية في البرنامج هي Call وتقوم باستدعاء (بالذهاب) إلى جزء آخر من البرنامج (أو خارج البرنامج) ثم تعود مرة أخرى إذا لاقت الأمر Ret. المقصود بأنها تذهب إلى خارج البرنامج أي إلى أحد ملفات البرنامج الفرعية أو ملفات الكمبيوتر والتي تكون على الأغلب إحدى ملفات مكتبات الوصل الديناميكية (Dynamic Link library) DLL. وسوف يتم شرح كيفية انتقال البرنامج من العمل في الكود الخاص به إلى كود آخر خارج البرنامج لاحقاً في هذا الكتاب. الآن وبعد تنفيذ الأمر الأول لنقم بالضغط على زر F7 من لوحة المفاتيح. الإختلاف بين هذا الزر و F8 هو أن F8 يقوم بتنفيذ الأمر والإنتقال إلى الذي يليه بغض النص عن طريقة تنفيذ الكمبيوتر لهذا الأمر، فإذا قمت بالضغط على F8 فسترى أن البرنامج قام وانتهى بالفعل من تنفيذ الأمر Call ولكن إذا قمت بالضغط على F7 فسيدخل البرنامج داخل هذا الأمر ويريك كيف يتم تنفيذه. الآن بعد الضغط على F7 سترى أن البرنامج قد انتقل إلى مكان أخر بعيد كل البعد عن موقعه الأصلي:
ها هو البرنامج قد انتقل إلى العنوان 00401506h أي إلى اخر البرنامج وتوقف عند الأمر JMP. الأمر JMP يقوم بنفس وظيفة الأمر Call ولكن الفرق أنه لن ينتظر للأمر RETN ليعود مرة اخرى. وهذا يعني أننا إذا قابلنا الأمر RETN في طريقنا فإننا سنعود إلى ثاني أمر في البرنامج وليس إلى هنا. كما هو الحال مع الأمر CALL فإن الأمر JMP قد ينقلنا إلى خارج البرنامج كما هو الحال مع الأمر الذي توقفنا عنده الآن. يمكنك معرفة ذلك من خلال النص المجاور إلى الأمر JMP والذي يشير إلى الموقع الذي سينتقل إليه البرنامج بعد تنفيذ الأمر. ولكي ترى ذلك اضغط على F8 ثم انظر الى الموقع الذي سيذهب إليه البرنامج :
ترى أن البرنامج انتقل إلى ملف الـ kernel 32 الموجود في نظام التشغيل. سبب حاجة البرنامج إلى الانتقال إلى ملفات نظام التشغيل هو أن أي برنامج على نظام الويندوز ليس ليده بالتأكيد أي صلاحية للدخول أو التواصل مع كرت الشاشة لإظهار الصور أو المعلومات الخاصة به. لذلك فإنه مجبور بأن يستخدم دوال معينة يوفرها نظام الويندوز لإظهار أي شاشة بأي حجم، وتلك الدوال هي التي تستطيع التواصل مع كرت الشاشة من خلال نظام التشغيل. سنرى طريقة عمل هذا بالتفصيل مع الأمثلة لاحقاً. الآن قم بالضغط على الزر المشار إليه بالصورة لكي يبدأ البرنامج من جديد:
آخر الأمور التي سنشرحها قبل الإنتقال إلى كسر البرنامج فعلياً هو احدى طرق عنونة الذاكرة (أي الوصول إلى أي موقع في الذاكرة للكتابة فيها أو القراءة منها). طبعاً ما نعنيه بالذاكرة هنا هو قسم المعلومات والذي يبدأ بالعنوان 00402000h وهو بعد قسم الكود البرمجي. الشاشة الموجودة على اليسار في أسفل الشاشة ترينا المعلومات التي ستخزن في هذا القسم:
الأمر الثالث في برنامجا هو mov DWORD PTR DS:[4020CA], EAX والذي يعني أن ننقل محتوى المسجل EAX إلى عنوان الذاكرة المشار إليه بـ DS:[4020CA] مع أخذ الاعتبار أن يتم أخذ 32 بت (DWORD) منه. طريقة كتابة عنوان الذاكرة بالطريقة السابقة هي طريقة تستخدمها معالجات انتل منذ إنتاجهم للمعالج 80286 وتتمثل الطريق بإستخدام مسجلين لكتابة عنوان الذاكرة احدهم يدعى (الجزء segment) والأخر يدعي (الفرع offset) وطريقة كتابة العنوان تكون: Segment:offset . أي أن في حالتنا هذه فإن المسجل الذي يأخذ مكان الجزء هو DS والفرع يتمثل بالرقم [4020CA]. وعنوان الذاكرة النهائي يكون عبارة عن ناتج جمع الجزء مع الفرع. لن يتم الخوض في هذه النقطة كثيراً ولكن العنوان النهائي في حالتنا سيكون هو نفسه الرقم بين القوسين 4020CA وإذا قمت بالذهاب إلى هذا الموقع في الذاكرة ثم تنفيذ الأمر فسترى أنه أصبح يحمل نفس القيمة التي كانت موجودة في المسجل EAX :
نلاحظ أن برنامج Ollydbg يزودنا بشرح وافي على يمين شاشة الكود، هذا الشرح يوفر علينا الكثير من الوقت والجهد وربما تكمن أهميته الكبرى بأننا لن نحتاج إلى الذهاب في كل مرة إلى الذاكرة لنرى ما الذي يقوم بتخزينه أي أمر هناك، بل سنجد القيمة التي يتم تخزينها أو التي يتم نقلها بين الذاكرة والمسجلات دائماً مذكورة في الشرح الذي يوفره البرنامج. الآن ارجع للصورة رقم 41 والتي يخبرنا فيها البرنامج أننا لم يحالفنا الحظ في كتابة الكلمة السرية الصحيحة “No luck there, mate”، هذه الجملة هي مفتاحنا لكسر البرنامج. الطريق الطويل هو أن نقوم بالبحث في ذاكرة البرنامج عن هذه الجملة لكي نعرف عنوان الذاكرة الذي يصل إليها، ومن ثم نقوم بالبحث في كامل البرنامج عن أي كود يحتوي على عنوان الجملة. ولكن بفضل الشرح الذي يوفره البرنامج لنا فيمكننا فقط أن نمر سريعاً على شرح البرنامج لعلنا نجد فيه ما نبحث عنه.
عند المرور على قسم الملاحظات في برنامج Ollydbug سترى ان جملة الخطأ التي ظهرت سابقاً موجودة هناك. الصورة التالية توضح ذلك من خلال النسخة المعدلة للبرنامج:
أجل!، إنها الجملة نفسها ومكررة مرتين، ليس هذا فحسب بل يمكننا أن نرى الجملة الصحيحة التي تدل على نجاحنا أيضاً. طبعاً يمكنك أن ترى أن الكود المقابل لكل من هذه الجمل هو مجموعة من أوامر إدخال البيانات إلى المكدس push ثم يليهم أمر الاستدعاء CALL الذي يذهب إلى خارج البرنامج. هذا الترتيب في الأوامر هو الذي تعمل عليه دوال الويندوز الخاصة بإظهار النوافذ والنصوص على الشاشة وغيرها. فأنت تقوم بإدخال نوع الشاشة التي تريد إظهارها (بزر واحد، زرين أحدهما هو ok والأخر هو cancel وغيره، أو ربما ترغب بشاشة من غير أزرار) إلى المكدس، ثم تدخل عنوان الذاكرة الذي يوجد فيه النص الذي تريد إظهاره في منتصف الشاشة ومن ثم تقوم بإستدعاء الدالة المناسبة للقيام بالوظيفة والتي تدعى في المثال السابق Message Box A. هذه الدالة التي تم استدعائها من ملف خارجي تقوم بقراءة المعلومات التي قُمت بإدخالها في المكدس ومنها تعمل على بناء وإظهار الشاشة التي تريدها. ما يهمنا الآن في الكود السابق هو الأمر RETN والذي يدل على أن هنالك أمر CALL كان قد أمر البرنامج من مكان ما بالمجيئ إلى هذه النقطة لإظهار شاشة الخطأ. ولكن قبل تفحصه فلنتأكد أولاً أي من رسالتي الخطأ هي التي تظهر لنا على الشاشة. يمكننا فعل ذلك بوضع “نقطة توقف” قبل كل رسالة خطأ ومن ثم تشغيل البرنامج وإدخال البيانات الخاطئة. نقطة التوقف هي احدى الميزات التي يوفرها لنا برنامج Ollydbg وهي تسمح لنا بأن نجعل البرنامج يعمل من بدايته إلى نقطة معينة لا يمكنه أن يتخطاها “وهي نقطة التوقف”. لوضع نقطة توقف عند تعليمة معينة في البرنامج فإننا نقوم بالضغط على الكود الرقمي (السداسي عشر) لتلك التعليمة مرتين وسنرى أن عنوان تلك التعليمة قد تظلل بالون الأحمر:
والآن قم بتشغيل البرنامج بالضغط على الزر الموجود في الزاوية العليا على يسار البرنامج. وعندما يبدأ البرنامج قم بإدخال أي كلمة سر واسم مستخدم من Help >> Register. في هذا المثال لقد اخترت اسم المستخدم “Mohammed” وكلمة المرور “123456” كما هو موضح في الصورة:
بعد الضغط على موافق “ok” سترى أن البرنامج لم يُظهر رسالة الخطأ بل توقف عند العنوان 00401369h وهذا يعني أن رسالة الخطأ الأولى هي التي كانت تظهر لنا. وربما تكون رسالة الخطأ الأولى في حال ما كان اسم المستخدم هو الخاطئ والثانية لكلمة السر. هذا يحتاج إلى قراءة إضافية في كود البرنامج وهو متروك للمستخدم.
الآن لنقم بإكمال تنفيذ الكود وذلك بالضغط على زر F8 تدريجياً، إن قمت بذلك سترى أن رسالة الخطأ ستظهر بعد تنفيذ الأمر Call الذي يقوم بإستدعاء نافذة الخطأ:
اضغط على موافق “ok” ومن ثم نفذ الأمر RETN. بعد تنفيذه سيذهب البرنامج إلى عنوان مختلف نهائياً وهو يأتي تماماً بعد أمر Call الذي تم منه الذهاب إلى الكود الخاص باستدعاء رسالة الخطأ:
لاحظ أن قبل أمر الإستدعاء Call هناك قفزة شرطية JE وهي تقوم بالقفز إلى عنوان جديد في حال ما تحقق شرط معين. في حال الأمر JE فإنها إختصار لـ (Jumb if equal) فهذه العملية تقوم بالقفز إلى العنوان المذكور بجانبها (والذي هو في هذا المثال 0040124C) في حال ما كان محتوى المسجل Z هو 1. هذا المسجل بالإضافة إلى مسجلات تخزن بت واحد فقط (اما 0 أو 1) ويمكنك رؤيتها أسفل المسجلات العامة التي تحدثنا عنها سابقاً:
والذي يحدد قيمة هذه المسجلات هي العمليات المنطقية التي يقوم بها البرنامج، فقبل القفزة المشروطة ترى هنالك عملية مشروطة وهي CMP :
والتي تقوم بالمقارنة بين قيمة المسجل EAX والمسجل EBX فإن كانت قيمتهم متساوية فإن المسجل Z يصبح 1 وإن كانت مختلفة فإن المسجل Z سيحتوي على 0. بمعنى أخر، هذه المقارنة بين المسجلين هي ما تحدد ما إذا كانت البرنامج سيقوم بإظهار شاشة الخطأ (الأولى على الأقل) أو لا. يمكننا أن نعود قليلاً للوراء وندرس كيفية حساب البرنامج للقيم الموجودة في هذين المسجلين ومنها نستطيع الحصول على الرقم التسجيلي الحقيقي (serial number). يمكننا أيضاً أن نغير شيئاً في الكود التنفيذي بحيث نجبره أن يذهب إلى الرسالة الصحيحة دائماً. الخيار الأول سيحتاج شرحه بالتفصيل إلى مساحة أكبر من هذا الكتاب لذلك فلنقم بالخيار الثاني. لكن قبل ذلك قم بحفظ عنوان القفزة الشرطية والذي هو 0040124Ah ومن ثم قم بإزالة نقطة التوقف التي وضعتها سابقاً عن طريق الضغط مرتين على بزر الفأرة الأيسر على الكود التنفيذي المكتوب بالأرقام السداسية عشر للكود الذي وضعنا عنده نقظة التوقف سابقاً. الآن قم بإعادة تحميل البرنامج من جديد عن طريق الضغط على بعد ذلك اذهب إلى القفزة الشرطية مرة اخرى والتي كنا قد سجلنا عنوانها سابقاً، لكي نغير القفزة الشرطية إلى قفزة دائمة بحيث نجعل البرنامج يقفز دائماً عن رسالة الخطاً ويتجنبها فعلينا الضغط بالزر الأيمن على تعليمة القفزة الشرطية واختيار Assemble :
ومن ثم نقوم بتغير كلمة JE إلى كلمة JMP ونضغط على Assemble :
بعد إجراء هذا التغير قم بتشغيل البرنامج وإدخال نفس البيانات مرة أخرى:
وهكذا قد نجحنا في كسر البرنامج وهو يطلب منا الانتقال إلى كسر واحد جديد. لحفظ البرنامج الذي تم كسره اضغط على الزر الأيمن واختر
Copy to executable >> All modifications
ومن ثم اضغط الزر الأيمن للفأرة في الشاشة الجديدة واختر حفظ الملف Save as
كنا قد تحدثنا في السابق عن أجزاء الملفات ذاتية التشغيل وذكرنا أنها تنتهي بعدة أقسام تعد هي أساس هذه الملفات. من تلك الأقسام وجدنا قسم الكود التنفيذي والذي يمثل التعليمات التي يقوم البرنامج على أساسها بأداء وظيفته. وتم توضيح كيفية ذلك في الجزئية السابقة من الكتاب. ومن تلك الأقسام أيضاً كان هناك قسم المعلومات والذي يحتوي على الجمل التي يظهرها البرنامج على شاشته، كما أنه يُستخدم لتخزين ما يُدخله المستخدم من كلمات سر وغيره. من أقسام البرنامج أيضاً قسم البيانات الواردة الذي تحدثنا عنه بشكل موجز في السابق. قي الجزئية السابقة رأينا كيف أن البرنامج يستعين بدوال من ملفات خارجية باستخدام الأمر Call لأداء وظيفته، ماهية هذه الدوال وطريقة تواصل البرنامج معها هي أهم ما يجب شرحه قبل رؤيتة تفاصيل قسم البيانات الواردة.