- واجهة برمجة التطبيقات على الويندوز Windows 32 API :
نظام الويندوز لديه مجموعة ضخمة من الدوال أو الوظائف Functions و بًنى البيانات Data Structures و والمتغيرات Variables والتي تدعى معاً بواجهة برمجة التطبيقات للويندوز Windows APIs. هذه الواجهة تُستخدم في التحكم بشكل وتصرف كل نافذة في نظام الويندوز (من شكل سطح المكتب إلى التحكم في ذاكرة أي عملية جديدة). كل حركة تتم عن طريق استخدام واحدة أو أكثر من الدوال أو الأوامر المتوفرة في هذه الواجهة وقد مررنا في المثال السابق على الدالة التي تقوم باستدعاء نافذة مكتوب عليها نص معين. تمت تسميتها بالواجهة Interface لأن الأوامر أو الدوال التي تشتمل عليها قد صممت لتعمل كواجهة لعناصر البرنامج المختلفة كي تتواصل مع بعضها البعض. الهدف من واجهة الـ Windows API هو السماح للمبرمجين بتطوير برامج مختلفة تتماشى مع نظام تشغيل الويندوز. فبدلاً من كتابة أكواد طويلة لإنشاء مكونات أو عناصر البرنامج على نظام تشغيل الويندوز مثل الأزرار والقوائم فإن المستخدم يمكنه فقط استدعاء الأمر أو الدالة Function من أوامر واجهة برمجة التطبيقات Windows API المختلفة وترك نظام التشغيل يتكفل بالباقي. كانت هذه الواجهة بكل عناصرها تسمى في السابق بـ Win32 API، ثم وبعد إطلاق معالجات الـ x64 وأنظمة التشغيل التي تعمل على هذه المعالجات قامت مايكروسوفت بإنشاء نسخة من هذه الواجهة لتعمل في تلك النظم وسميت Win64، ولكن في النهاية تم إثبات الاسم في كل النسخ على Windows API
الاختصار (API) يعني بالترجمة العربية “واجهات برمجة التطبيقات Application Programming Interface” فهو نظام برمجي لنظم التشغيل وليس بالأخص لشركة مايكروسوفت. تقوم البرنامج في أي نظام تشغيل باستخدام الدوال والتعليمات التي توفرها هذه الواجهات للتمكن من تنفيذ وظائف إضافية داخل نظام التشغيل. كما أن استخدام هذه الواجهات يوفر المساحة البرمجية ويوفر الوقت على المبرمج، فبدلاً من أن يقوم المبرمج بكتابة الكود الذي يقوم بتنفيذ تعليمة معينة فإنه يتركها على الدوال التي يوفرها نظام التشغيل. طبعاً أغلب (إن لم يكن كل) نظم التشغيل تمتلك مثل هذه الدوال ولذلك لتسهيل كتابة برامجها.
بمكن تصنيف واجهات برمجة التطبيقات بشكل عام إلى نوعين: النوع الأول يتمثل بالواجهات التي تأتي مع لغة برمجة معينة ويمكن استخدام دوالها وأوامرها مع تلك اللغة فقط. النوع الثاني من الواجهات APIs يمكن استخدامه مع أي لغة برمجة.الواجهات الخاصة بالويندوز Windows APIs تأتي مع نظام التشغيل ويمكن لأي شخص استخدامها في أغلب لغات البرمجة ولذلك فهي تقع ضمن الصنف الثاني. هنالك خيارين تقوم الشركات بأخذهما بعين الاعتبار عند نشر واجهة برمجة تطبيقات معينة API :
- الخيار الأول هو حماية معلومات الواجهة API من عوام الناس. فعلى سبيل المثال تقوم شركة سوني Sony بتوفير المعلومات الضرورية عن واجهات API الخاصة بجهاز الـ PlayStation 2 للمطورين الرسميين لهذا الجهاز فقط. قيامهم بها سمح للشركة بالتحكم بمبرمجي ألعاب PlayStation 2 وبالتالي بالتحكم بالربح الناتج عن الجهاز.
- الخيار الثاني هو جعل معلومات الواجهات APIs متوفرة لعموم الناس. وهذا ما تقوم به شركة مايكروسوف في أنظمة تشغيل الويندوز، وكذلك شركة آبل Apple والهدف من توفير هذه المعلومات هو إعطاء القدرة لجميع المبرمجين ليتمكنوا من كتابة برامجهم على الأنظمة التي توفرها هذه الشركات.
من الأمثلة على واجهات برمجة التطبيقات API ما يلي:
Single UNIX Specification (SUS)
Microsoft Win32 API
ASPI for SCSI device interfacing
Carbon and Cocoa for the Macintosh OS
DirectX for Microsoft Windows
Simple DirectMedia Layer (SDL)
Universal Home API
LDAP Application Program Interface
Svgalib for Linux and FreeBSD
Google Maps API
Wikipedia API
بالنسبة لواجهات الـ API في نظام الويندوز فإنها تقع في ملفات الـ DLL والتي تُعد ملف ذاتي التشغيل Portable Executable حيث أنها تتبع الصيغة التي شرحناها سابقاً. من الأمثلة على ملفات الـ DLL التي تحتوي على بعض تعليمات ووظائف الـ API الملفات التالية:
User32.dll, GDI32.dll, Shell32.dll
والتي تقع في ملف نظام الويندوز. وقد تم تصميمها للعمل مع لغات ++C\C وهي أكثر طريقة مباشرة للتعامل مع نظام الويندوز في تصميم البرامج. كما أن مايركروسوف توفر حزمة تطويرية تدعى Software Development Kit (SDK) تحتوي على المستندات والأدوات اللازمة لمساعدة المطورين والمبرمجين على إنشاء برامج باستخدام دوال الـ Windows API .
مكاتب الربط الالكترونية Dynamic-link library (DLL) تُمثل التطبيق العملي لمفهوم “مشاركة المكتبات Shared library” في نظام تشغيل الويندوز. كل ملف DLL يوفر دالة Function أو أكثر يقوم البرنامج التنفيذي على الجهاز باستدعائها عن طريق إنشاء رابط ديناميكي Dynamic link أو ستاتيكي Static link مع ملف الـ DLL المطلوب. الرابط الإستاتيكي يبقى ثابتاً طوال فترة عمل البرنامج بينما الرابط الديناميكي يتم إنشائه متى ما احتاجه البرنامج. من الأشياء المهم ذكرها هو أنه ليس بالضرورة لملفات الربط الديناميكي DLL أن تنتهي بالامتداد .dll (كما هو الحال مع جميع الأمثلة التي سنتعامل معها). هذه الملفات تُعد أيضاً ملفات ذاتية التشغيل PE وبالتالي يمكن أن تنتهي بالامتدادات exe. (كما كان الحال مع العديد من إصدارات الويندوز القديمة) أو .dll أو .drv اعتماداً على كيفية استخدامها.
أي ملف DLL يُمكن أن يُستخدم في أكثر من برنامج تطبيقي بنفس الوقت. بعض ملفات الـ DLL التي تستخدمها التطبيقات على الجهاز تأتي مع نظام التشغيل ولكن بعضها الآخر يأتي مع البرنامج ويكون قد كُتب خصيصاً للعمل معه. الهدف الأصلي لاستخدام ملفات الـ DLL هو لتوفير المساحة على القرص الصلب وفي الذاكرة. في الأنظمة التي لا تستخدم خاصية مشاركة المكتبات Nonshared library فإنه يتم إضافة كود آخر مع الكود التنفيذي للبرنامج ليحل محل دوال الـ Windows DLL .بالتالي إن كان هناك برنامجان يحتاجان لاستخدام نفس العملية أو الدالة فإن الكود نفسه سيكون موجود مع كلاهما، أي أننا سنستخدم ضعف المساحة في الذاكرة. لكن في حال الأنظمة التي تملك خاصية مشاركة المكتبات فإن أكثر من برنامج يمكنه أن يشترك نفس ملف الـDLL والذي يتواجد كملف وحيد على القرص الصلب وسيعمل مرة واحدة في الذاكرة.ما يسمح للأوامر والدوال التي توفرها واجهات برمجة التطبيقات بتوفير المساحة على القرص هو أن هذه الواجهات موجودة في كل أنظمة تشغيل الويندوز على جميع أجهزة الويندوز. أي أنه إذا ما قام المستخدم بإستخدام برنامجه الذي قام بكتابته في جهازه فإنه سيعمل بلا مشاكل في أي جهاز آخر يحمل نظام الويندوز لأن الجهاز الآخر يمتلك نفس الملفات الخاصة بواجهات برمجة التطبيقات.
واجهات الـWindows API كانت دائماً ما تشكل جزء كبير من البنية التحتية للعديد من أنظمة تشغيل الويندوز وهو ما أعطى العديد من مبرمجي الويندوز المرونة والقدرة على التعامل مع برامجهم. ولكنه في نفس الوقت أعطى التطبيقات المختلفة على الويندوز القدرة على التعامل مع مختلف العمليات المتعلقة بالتعامل مع كرت الشاشة والواجهات الصورية Graphical User Interface. على مر السنوات السابقة تم إجراء العديد من التغيرات والتعديلات على نظام الويندوز ومنها تم تعديل واجهات الـ Windows API في كثير من الأحيان ليعكس هذه التغيرات.فواجهات الـ API في نظام Windows 1.0 الذي تم إصدارة عام 1985 كانت تدعم أقل من 450 وظيفة، بينما في الإصدارات الحديثة من الويندوز هناك الآلف من الدوال والوظائف.
من المفاهيم المهمة والتي يجب معرفتها عند التعامل مع واجهات الـWindows API هو ما يمكن ترجمته للعربية إلى “المقبض handle” للاختصار فإنه بالإنجليزية يدعى hWnd وهو رقم مميز يقوم نظام الويندوز بتعينه لكل نافذة يتم إنشائها فيه. والمقصود بكل نافذة هو كل شئ من أزرار أو نوافذ أو قوائم يمكن أن تظهر على الشاشة. المقبض hWnd يُستخدم في دوال الـ API كطريقة لتمييز النوافذ المختلفة. كما أنه عبارة عن رقم يشغل مساحة 4 بايت. عندما يقوم البرنامج بالعمل فإن يتحكم بكل نوافذه عن طريق معرفته لأرقام المقابض handlers .
من الأسئلة التي طالما يتم طرحها هو كيفية استخدام الأوامر أو الدوال التي يوفرها الويندوز تحت ظل واجهة برمجة التطبيقات فيه، طر يقة التطبيق العملي تختلف من لغة لأخرى ويجب على المبرمج أن يعرج على موقع مايكروسوفت للحصول على تفاصيل استخدام أمر معين https://docs.microsoft.com/en-us/welcome-to-docs
- قسم البيانات الصادرة Export data:
ذكرنا أن للويندوز مجموعة من الدوال Functions والمتغيرات التي يستطيع تصديرها إلى التطبيقات المختلفة. وأن هذه الدوال والمتغيرات موجودة في مكتبات الربط الديناميكي DLL في نظام الويندوز والتي لها تركيب مماثل للملفات ذاتية التشغيل PE. تجدر الإشارة هنا إلى أن أي برنامج .exe يمكنه امتلاك قسم للبيانات الصادرة يقوم منه بتحديد دوال أو نصوص أو متغيرات معينة يسمح للبرنامج الأخرى أن تستوردها منه. لكن عندما نتحدث عن واجهة برمجة التطبيقات فإننا نشير إلى المجموعة الضخمة من الأوامر التي يوفرها نظام التشغيل نفسه للبرامج المختلفة. ويقوم نظام التشغيل بتخزين هذه المجموعة الضخمة في ملفات الربط الديناميكي DLL في مجلدات النظام نفسه. فعد الذهاب إلى المسار C:\Windows\System32 في جهازك سترى آلاف الملفات ذات الصيغة .dll وهي التي تحتوي على أومر واجهة برمجة التطبيقات. لكي يقوم أي من هذه الملفات بتصدير أمر من الأوامر فإنه يجب أن يملك صيغة معينة أو ترتيب معين للكود الخاص به. هذا ليس غريباً فأي ملف ذاتي التشغيل PE يجب أن يملك صيغة معينة لكي يعمل وقد مررنا على ذلك من بداية الكتاب. لفهم كيفية قيام ملف ذاتي التشغيل PE بتصدير دوال معينة لغيره من التطبيقات فإننا سنتخذ من الملف user32.dll مثال لنا ومنه سنفهم العملية بالكامل. هذا الملف موجود في المسار C:\Windows\System32 ولكن من المهم جداً ألا تقوم بتغير أي شئ في ذلك المجلد لكي لا تضر بنظام الجهاز. فكما قلنا جميع التطبيقات التي تعمل عليها حالياً تقوم باستخدام الدوال والأوامر الموجودة في ملفات الربط الديناميكي في ذلك المسار، وأي تغير لملفات المسار سيؤثر على بقية التطبيقات. ولهذا من الأفضل القيام بنسخ الملف user32.dll إلى مسار آخر ثم العمل عليه (ستجد رابط لتحميل الملف هنا ).
عند فتح الملف user32.dl ببرنامج LordPE والذهاب لرؤية أقسام الملف سترى أن له أربع أقسام:
فالسؤال هنا هو أين يقم قسم البيانات الصادرة؟ الإجابة بكل بساطة أنه عادةً ما يتم كتابة صيغة تصدير أوامر الملف (أو بنية صورة دليل الصادرات والتي سيأتي شرحها IMAGE_EXPORT_DIRECTORY) في قسم البيانات الصادرة، لكن هذا الأمر ليس ضرورياً حيث أن الهدف من وجود الملف user32.dll هو فقط تصدير دوال معينة لغيره من الملفات. لكن في التطبيقات التنفيذية الأخرى فإننا نرى قسم خاص للبيانات الصادرة كما في برنامج crackme.exe الذي تعاملنا معه سابقاً:
إن لم يكن هناك قسم خاص بالبيانات الصادرة فكيف سنصل إلى بينة صورة دليل الصادرات IMAGE_EXPORT_DIRECTORY والتي تحتوي على المعلومات الخاصة بعدد الدوال التي يصدرها البرنامج وأسمائها؟وكيف يصل إليها نظام التشغيل بنفسه؟ الجواب هنا يرجعنا إلى قسم دليل البيانات DataDirectory الذي تحدثنا عنه بإيجاز سابقاً. كل ما يحتويه دليل البيانات هو عناوين ظاهرية نسبية لأقسام أو بنى كثيرة ومختلفة، أولها هو جدول الصادرات والذي يحتوي على العنوان الظاهري النسبي (العنوان في الذاكرة) لبنية صورة دليل الصادرات IMAGE_EXPORT_DIRECTORY وكذلك حجمها. ويمكن الوصول لدليل البيانات من للملف user32.dll من خلال برنامج LordPE :
العنوان الظاهري النسبية للبنية التي نحاول الوصول إليها هو 00016904h. إذا عدنا للعناوين الظاهرية النسبية لأقسام الملف user32.dll (صورة 79) سنرى أن البنية التي نبحث عنها توجد في أول قسم .text وللوصول إليها فإننا نوجد الفرق بين عنوانها الظاهري النسبي وعنوان أول قسم الظاهري النسبي: 16904h – 1000h = 15904h وبالتالي نصل إلى عنوان البنية المطلوبة على القرص الصلب بجمع الرقم الناتج مع عنوان أول قسم على القرص الصلب: 15904h + 400h = 15D04h.
قبل الذهاب إلى العنوان 15D04h يجب أن نعرف ما الذي ينتظرنا هناك، أي يجب أن نعرف تركيب بنية صورة دليل الصادرات IMAGE_EXPORT_DIRECTORY وهي كالآتي:
الخانات التي تهمنا من هذه البنية هي ما يلي:
- الاسم nName :هذه الخانة والتي تأتي بعد 24 بايت من بداية البنية تحتوي على عنوان ظاهري نسبي لأسم الملف الذي يقوم بتصدير الدوال، وهذا يعني أننا نتوقع من هذه الخانة في المثال الذي سنتعامل معه أن تشير إلى الاسم user32.dll. السبب في وجود مثل هذه الخانة هو أنه يمكن للمستخدم أن يغير اسم الملف وعندها سيكون على مُحمل البرامج أن يستعمل هذا الاسم ليستدل على الملف في حال ما قامت برامج أخرى باستيراد دوال منه (وهو ما سنراه في الجزئية التالية من الكتاب).
- القاعدة nBase :تحتوي هذه الخانة على أول رقم في ترتيب الدوال. يُمكن لملف الربط الديناميكي DLL تصدير الدوال لغيره من التطبيقات بطريقتين: الأولى هي باستخدام اسم الدالة والثانية هي بترتيب الدالة كما سنرى لاحقاً. المقصود بترتيب الدالة هو أن ملف الربط الديناميكي DLL يقوم بترتيب جميع الدوال التي يصدرها لغيره من التطبيقات بالنظام السداسي عشر وهكذا. تحتوي الخانة nBase غالباً على أول رقم في هذا الترتيب وهو ما يكون عادةً الرقم 1. كل رقم ترتيبي من هذه الأرقام يتشكل من 16 بت (بحجم كلمة WORD) ويقوم بالإشارة أو الدلالة على دالة معينة. وتجدر الاشارة إلى أنه ليس من الضروري أن تحتوي هذه الخانة على الرقم 1 بل يمكن أن يبداً الترتيب بأي رقم.
- عدد الدوال NumberOfFunctions: هذه الخانة تحتوي على الرقم الكلي للدوال (والتي يشار إليها أيضاً بمصطلح الرموز symbols) التي يتم تصديرها من قبل هذا الملف.
- عدد الأسماء NumberOfNames: هذه الخانة تحتوي على عدد الدوال أو الرموز التي يتم تصديرها بإستخدام الطريقة الأولى وهي التصدير باستخدام اسم الدالة. أي أن هذه الخانة لا تحتوي على عدد كل الدوال في ملف الربط الديناميكي بل الدالة التي تسبقها هي من تحتوي على ذلك. في حال ما اذا كانت هذه الخانة تحتوي على الرقم 0 فإن هذا يعني أن كل دوال الملف يتم تصديرها بالطريقة الثانية أي بترتيب الدالة فقط.
- عنوان الدوال AddressOfFunctions: هذه من الخانات المهمة لأنها تحتوي العنوان الظاهري النسبي لجدول من العناوين الظاهرية النسبية للدوال التي يصدرها الملف. أي أن الرقم الموجود في هذه الخانة والذي يشكل 8 بايت هو عنوان ظاهري نسبي عند الذهاب إليه سنكون قد انتقلنا إلى جدول جديد. يحتوي هذا الجدول على عناوين ظاهرية نسبية عددها تحدده خانة عدد الدوال NumberOfFunctions. كل عنوان في هذا الجدول يتشكل من 8 بايت ويُسمى هذا الجدول بجدول العناوين الصادرة Export Address Table (EAT). كل عنوان في هذا الجدول يخص دالة معينة من الدوال التي يصدرها الملف وعند الذهاب إلى هذا العنوان سنكون قد انتقلنا إلى بداية الكود التنفيذي الذي يقوم بتنفيذ هذه الدالة. وهذا شيء متوقع حيث أن الملف الذي يقوم يتصدير دوال معينة لبقية البرامج يجب أن يحتوي على الكود التنفيذي أو البرمجي الذي يقوم بتنفيذ هذه الدالة.
- عناوين الأسماء AddressOfNames: هذه الخانة هي الأخرى تحتوي على عنوان ظاهري نسبي لجدول من عناوين أسماء الدوال التي يصدرها الملف. أي أننا عند الذهاب إلى العنوان الذي تشير إليه هذه الخانة فإننا سنكون قد انتقلنا إلى جدول من العناوين الظاهرية النسبية يقودنا كل عنوان منها إلى اسم احد الدوال بالاسكي، يفصل كل اسم عن الأخر الرقم صفر الذي يشغل بايت واحد ويُسمى هذا الجدول بجدول الأسماء الصادرة Export Name Table (ENT) حيث أن الاسم المشار إليه بأول عنوان في هذا الجدول يقابل أول عنوان في جدول العناوين الصادرة وثاني اسم من جدول الأسماء الصادرة يقابله ثاني عنوان من جدول العناوين الصادرة وهكذا.
- عنوان ترتيبة الأسماء AddressOfNamesOrdinals: هذه الخانة تحتوي عنوان ظاهري نسبي لجدول من أرقام ترتيبية من 2 إلى عدد الدوال التي يصدرها الملف 2,3,4,5,6,7,8,9A,B,C,D وهكذا. كل رقم يحتل مساحة 16بت ويسمى هذا الجدول بجدول ترتيب الصادرات Export Ordinal Table (EOT) والهدف من الأرقام بهذا لجدول القيام بالربط بين اسم كل دالة من جدول الاسماء الصادرة وما يوازيه من جدول العناوين الصادرة.
شرح معاني الخانات المختلفة لا يكفي دون توضيحها في ملف user32.dll كأحد الأمثلة. كنا قد حددنا سابقاً أن عنوان بنية صورة دليل الصادرات IMAGE_EXPORT_DIRECTORY التي تحتوي على العناصر السابقة موجودة في العنوان 15D04 على القرص الصلب. وعند الوصول إليها بمحرر الهيكس سنجدها كما يأتي:
الصورة توضح بنية صورة دليل الصادرات مع أهم العناصر التي تم ذكرها من قبل، أول العناصر الموضحة في الصورة هو الأسم nName والذي يجب أن يحتوي على عنوان ظاهري يرشدنا إلى اسم ملف الربط الديناميكي. كنا قد ذكرنا من قبل أن أول قسم في هذا الملف يأتي تحت العنوان الظاهري النسبي 1000 والعنوان الخام (على القرص الصلب) 400. وبالتالي للوصول إلى اسم الملف نقوم بتحول عنوان الذاكرة الموجود في خانة الاسم nName إلى العنوان الموازي له على القرص الصلب كما يلي:
18C1Ch – 1000h = 17C1Ch
17C1Ch + 400h = 1801Ch
وعند الذهاب إلى هذا العنوان في محرر الهيكس:
أما بالنسبة لخانة القاعدة nBase فإنها كما قلنا تحتوي على أول رقم لترتيب الدوال والذي هو 000005DCh وتجدر الإشارة إلى أن الأرقام في جدول ترتيب الصادرات لا تتوالى تتابعاً بعد هذا الرقم. سنرى كيف يتعلق هذا الرقم بأسماء دوال ملف الربط الديناميكي لاحقاً. الخانة الثالثة لدينا هي خانة عدد الدوال وتحتوي على الرقم 000003EBh بالنظام السداسي عشر والذي يماثل الرقم 1003 بالنظام العشري. وهذا يعني أن ملف الربط الديناميكي user32.dll يقوم بتصدير 1003 دالة لغيره من التطبيقات. كذلك هذا الرقم يعني أن ترتيب الدوال الذي يبدأ من الرقم 5DCh سينتهي عند الرقم 5DCh + 3EBh – 1 = 9C6h . والسبب في طرح الرقم واحد هو أنه عند تحديد عدد الدوال يتم الحساب من الرقم صفر ثم واحد ثم اثنان وهكذا، وليس من الرقم واحد أولاً. الخانة التالية هي عدد الأسماء وتحتوي على الرقم 00000336h وكونه أقل من عدد الدوال يعني أن بعض الدوال يتم تصديره بإستخدام الطريقة الثانية وهي ترتيب الدالة، وسيأتي شرح الطريقتين لاحقاً. الخانة التالية هي عنوان الدوال والتي ستأخذنا إلى جدول من العناوين على القرص الصلب:
0001692C – 1000 = 1592C
1592C + 400 = 15D2Ch
نلاحظ من العنوان أنه يأتي مباشرةً بعد بنية صورة دليل الصادرات:
كل مربع في الصورة يحمل عنوان للكود التنفيذي الخاص بأحد الدوال التي يصدرها الملف مع العلم أن الصورة لا توضح العدد الكلي للعناوين، لكن لمعرفة كل عنوان يخص أي دالة فإننا ننتقل إلى خانة عنوان الأسماء والتي تحمل العنوان 000178D8h. للوصول إلى هذا العنوان على القرص الصلب نقوم بالآتي:
000178D8h – 1000h = 168D8h
168D8h + 400h = 16CD8h
وباستخدام محرر الهيكس نرى في هذا العنوان ما يلي:
كل مربع في الصورة يحتوي على عنوان لأسم أحد الدوال التي يصدرها الملف، فالمربع الأول يحتوي على العنوان 00018C27h والذي هو في القرص الصلب:
18C27h – 1000h + 400h = 18027h
حيث نجد في هذا العنوان الدالة ActivateKeyboardLayout
وكذلك المثل في العنوان الثاني حيث سنجد الدالة AddClipboardFormatListener وهكذا. الخانة التالية هي عنوان ترتيبة الأسماء والتي تحتوي على العنوان 581B0h والذي يشير إلى الجدول التالي على القرص الصلب:
وهو كما ذكرنا جدول يحتوي على الأرقام بالتتابع، قد يستغرب القارئ من أن هذه الأرقام لا تتابع من بعد الرقم المُحدد بخانة القاعدة nbase وسيأتي تفسير ذلك لاحقاً. وهكذا نرى أن بنية صورة دليل الصادرات تحتوي على ثلاث جداول من العناوين وجدول من نصوص الآسكي. أهم هذه الجداول هو جدول العناوين الصادرة EAT. أما الجدولين الآخرين واللذان هما جدول الأسماء الصادرة EAT وجدول ترتيب الصادرات EOT فيعملان بشكل مواز Parallel تصاعدي (الأسماء مرتبة من أول حرف A وحتى آخر حرف Z والأرقام مربتة ترتيب تصاعدي) تبعاً لأسم الدالة بحيث أن أي بحث عن اسم دالة في جدول يوصلنا إلى ترتيبها من الجدول الآخر. كما أن أرقام ترتيب الدوال ما هو إلا مؤشر إلى عنوان الدالة فهو رابط بين الاسم والعنوان لا أكثر ولا يمكن لجدول ترتيب الصادرات أن يحتوي على عناصر أكثر من جدول الأسماء الصادرة. كل اسم يمكنه أن يرتبط بعنوان واحد فقط لا غير ولكن العكس غير صحيح حيث يمكن للعنوان أن يمتلك أكثر من اسم مرتبط به. وإن كان هنالك دوال بأسماء مختلفة ولكن تقوم بنفس الوظيفة (تشير إلى نفس العنوان) Aliases عندها سيحتوي جدول الأسماء على عناصر أكثر من جدول ترتيب الصادرات.
كمثال على ذلك، إن كان ملف الربط الديناميكي DLL يصدر 40 دالة فيجب أن يكون هناك 40 عنصر في جدول عنوان الدوال ويجب على الخانة “عدد الدوال NumberOfFunctions أن تحتوي على الرقم 40. لإيجاد عنوان الدالة من اسمها يقوم نظام التشغيل أولاً بقراءة خانة “عدد الدوال NumberOfFunctions” وخانة “عدد الأسماء NumberOfNames” في دليل الصادرات. بعد ذلك يذهب يقوم بالمرور على جدول أسماء الصادرات ENT وجدول ترتيب الصادرات EOT بشكل متواز بحثاً عن اسم الدالة. إذا تمكن من إيجاد الاسم في جدول أسماء الصادرات فإن القيمة الموجودة في جدول ترتيب الصادرات EOT يتم استخراجها واستعمالها كمؤشر لايجاد العنوان من جدول العناوين الصادرة EAT.
في مثال الملف الذي يحتوي على 40 دالة، إن كنا نبحث عن الدالة “ص” ووجدنا أنه العنصر رقم 39 في جدول أسماء الصادرات ، ثم ظرنا إلى العنصر 39 في جدول ترتيب الصادرات ووجدنا أنه الرقم 5 (بافتراض أن أول قيم الجدول هو الرقم صفر). فإننا عندها ننظر إلى العنصر الخامس من جدول عناوين الصادرات لنجد عنوان الدالة ص. هذه الطريقة في البحث هي البحث باستخدام الاسم. لكن ان كان نظام التشغيل يعرف رقم ترتيب الدالة (وهذا يعتمد على كيفية استدعاء الدالة من قبل البرنامج وسنرى ذلك في الجزئية التالية من الكتاب) فإنه يذهب إلى الدالة مباشرةً في جدول عناوين الصادرات وهذه هي الطريقة الثانية في البحث عن طريق ترتيب الدالة. بالرغم من أن الطريقة الثانية أسرع من الأولى لكن عيبها هو صعوبة الحفاظ على توافقها مع البرامج. فإن تم تحديث upgrade/update ملف الربط الديناميكي DLL من خلال تحديثات الجهاز فإن ترتيب الدوال يمكن أن يتغير وبالتالي فإن البرامج الاخرى التي تعتمد على استدعاء الدالة بمعرفة ترتيبها سوف تفشل في فعل ذلك.
كنا قد ذكرنا من قبل أن الرقم في خانة عدد الأسماء NumberOfNames يمكن أن يكون أقل من عدد الدوال NumberOfFunctions عندما يكون هناك دوال يمكن استدعائهم عن طريق رقم ترتيبهم فقط. وهذا يعني أن هذه الدوال ليس لها أي قيمة في جدول أسماء الصادرات وجدول ترتيب الصادرات (أي أنه ليس لها اسم من الأساس). على سبيل المثال إن كان هناك 70 دالة لكن 40 عنصر فقط في جدول أسماء الصادرات، أي أن هناك 30 دالة في البرنامج يتم استدعائهم (أو تصديرهم) برقم ترتيبهم فقط. عندها كيف يمكن لنا أن نعرف أو نصل إلى هذه الدوال؟ سيكون علينا أن نجد العناصر في جدول عناوين الصادرات التي ليس لها أي مؤشر في جدول ترتيب الصادرات.
كنا قد ذكرنا من قبل أنه يمكن لترتيب الدوال أن يبدأ بأي رقم غير الواحد كما كان الحال مع الملف user32.dll. لنقل أن الملف يبدأ في العنوان 200 أي أن الخانة “القاعدة nBase” تحتوي على الرقم 200، لكن جدول ترتيب الصادرات يشير إلى الرقم 2 كأول رقم. بالطبع لن يقوم محمل البرامج بترك 200 مساحة فارغة حتى يصل إلى الرقم 200 ثم يبدأ بموازاته مع جدول العناوين! ولكنه يقوم بجمع الرقم في جدول ترتيب الصادرات مع الرقم في خانة القاعدة ليحدد مكان أول رقم ترتيبي الموازي لأول عنوان في جدول العناوين. أي أنه في مثالنا هذا سيكون أول رقم ترتيبي هو 2+200 = 202 وهو الرقم الترتيبي لأول دالة يصدرها الملف. في حالة ملف الربط الديناميكي user32.dll فإن أول رقم ترتيبي سيكون 5DCh + 2 = 5DEh.
في بعض الأحيان قد يبدو أن دالة معينة قد تم تصديرها من ملف ربط ديناميكي معين DLL بينما هذه الدالة في الحقيقة تقع في ملف DLL مختلف نهائياً. هذه الطريقة في تصدير الدوال تدعى التصدير بالتمرير Export Forwarding. التصدير بالتمرير هي احدى التقنيات التي تستخدمها مايكروسوفت في واجهات Windows API لإخفاء الفروق بين واجهات الأنظمة Windows NT و Windows 9x. هناك المزيد في هذا الموضوع لكن لن يتم مناقشته أكثر.
عندما يتم تصدير الدالة باسمها فإن ملفات الربط الديناميكي أو الملفات التنفيذية الاخرى تقوم باستدعاء Call تلك الدالة باستخدام الامر GetProcAddress والذي سيُعطي العنوان الظاهري النسبي للدالة المرادة. طريقة استخدام هذا الامر والمدخلات التي يحتاجها مشروحة في موقع مايكروسوف للتطوير وهو ليس موضوعنا الآن.
أخيراً الوصول لمعلومات دليل الصادرات باستخدام برنامج LordPE :
قسم البيانات الواردة Import data:
بعد التحدث عن قسم البيانات الصادرة فإننا نستكمل الموضوع لنرى كيف تقوم البرامج بالتواصل مع مكتبات الربط الديناميكي DLL التي تحمل دوال وأوامر واجهة الـ API على نظام الويندوز. التعمق في هذا الموضوع يشتمل على دراسة قسم البيانات الواردة Import data والذي يدعى .idata ويتواجد مع بقية الأقسام الأخرى في نهاية ملف البرنامج التنفيذي PE. هذا القسم يحتوي على المعلومات الخاصة بكل الدوال functions التي يقوم البرنامج التنفيذي باستيرادها من ملفات الربط الديناميكي DLL، أي أنه لكي يتمكن أي تطبيق على الجهاز من استخدام دالة أو أمر معين من واجهة برمجة التطبيقات فلا بد للملف الذي يقوم بتصدير هذه الدوال أن يمتلك بنية معينة وهي التي شرحناها سابقاً ولابد للتطبيق الذي يقوم باستيراد واستعمال هذه الدوال أن يتملك هو الاخر بنية معينة سنقوم بشرحها في هذه الجزئية من الكتاب. لتوضيح الفكرة سيتم استخدام مثال الآلة الحاسبة مرة أخرى.
كما كان الوضع مع دليل الصادرات، فإن الأرقام السداسية عشر في قسم البيانات الواردة .idata مُرتبة ومبنية بطريقة معينة تُمكن البرنامج من فهمها والتعامل معها. كما أنه ليس هناك داع لوجود قسم البيانات الصادرة لكي يتمكن البرنامج من اصدار الدوال لغيره من التطبيقات، كذلك ليس هنالك داع لوجود قسم البيانات الواردة ليتمكن البرنامج من استيراد الدوال من غيره من البرامج. ولكي نتمكن من تحديد موقع البنية المسئولة عن استيراد الدوال للبرنامج فإننا نلجأ إلى دليل البيانات. من دليل البيانات نجد في جدول الموارد Import table عنوان البنية التي نبحث عنها والتي تسمى دليل الوارد The Import Directory. ويمكن إيجادها لبرنامج الآلة الحاسبة:
يتكون دليل الموارد بعد الذهاب إليه من أقسام أصغر منه تسمى كل منها ببنية وصف صورة الوارد IMAGE_IMPORT_DESCRIPTOR structures. قد تكون الترجمة العربية غير دقيقة والأفضل البقاء مع المصطلحات الإنجليزية. كل واحدة من هذه البُنى الصغيرة تحتوي على معلومات خاصة بملف ربط ديناميكي معين DLL. أي أنه إذا كان ملف الـPE يستورد أو يستخدم دوال من 3 ملفات ربط ديناميكي DLL مختلفة فإنه سيحتوي على 3 بُنى وصف صورة الوارد IMAGE_IMPORT_DESCRIPTOR structures مختلفة. كل بنية منها تشكل 20 بايت وتحتوي على معلومات خاصة بملف ربط ديناميكي واحد. لا يوجد هناك خانة معينة تدل أو تشير إلى عدد البنى الموجودة في الملف، فبدلاً من ذلك آخر بنية من هذه البنى تحتوي على أصفار بالكامل. أي أنه إن أردنا أن نعرف عدد البنى في ملف تنفيذي PE معين فإننا سنعد كل 20 بايت من بداية قسم البيانات الواردة حتى نصل إلى 20 بايت تحتوي على أصفار فقط.
للوصول إلى قسم البيانات الواردة في برنامج الآلة الحاسبة بعد فتحه باستخدام محرر الهيكس HexWorkshop فإننا يمكننا من العنوان الظاهري النسبي الموضح في الصورة 90 بعد حساب موقعه على القرص الصلب أو مباشرةً من خلال خانة “مؤشر البيانات الخام” الموجودة في جدول الأقسام:
إذاً عنوان قسم البيانات الواردة على القرص الصلب (في محرر الهيكس) هو 002AC00h. عند الذهاب إلى هذا العنوان فإننا سنكون في بداية قسم البيانات الواردة، أي أننا سنكون في أول بُنية من بنى وصف صورة الوارد IMAGE_IMPORT_DESCRIPTOR structures . هذه البُنية الأولى وكل من البُنى الأًخرى التي تليها والتي تتكون كل منها من 20 بايت تتكون من الخانات التالية:
أول 4 بايت في كل بينة من بُنى وصف صورة الوارد قد تحتوي إما على خواص البيانات Characteristics أو ما يسمى الصوت الأول الأصلي Original First Thunk والذي يحتوي على العنوان الظاهري النسبي لبنية أخرى تُسمى صورة صوت البيانات IMAGE_THUNK_DATA والتي سيتم شرحها لاحقاً.
العنصر “الاسم 1” يحتوي على العنوان الظاهري النسبي لاسم ملف الربط الديناميكي DLL. أما أخر عنصر “الصوت الأول” فإنه هو الاخر يحتوي على العنوان الظاهري النسبي لبنية صورة صوت البيانات IMAGE_THUNK_DATA والتي يشير إليها العنصر الأول “الصوت الأول الأصلي Original First Thunk” وهذه البنية هي نسخة طبق الاصل عن البنية التي يشير اليها العنصر الاول. قد يكون من الغريب أن يكون وجود خانتان يشيران إلى بنيتا في موقعين مختلفين لكنها طبق الاصل عن بعضهما البعض وسيتم شرح ذلك بعد رؤية المثال على بنى وصف صورة الموارد في الالة الحاسبة.
الصورة تبين كل بنى IMAGE_IMPORT_DESCRIPTOR structures الموجودة في برنامج الآلة الحاسبة من بداية قسم البيانات الواردة من العنوان 002AC00h وحتى الانتهاء ببنية من الأصفار فقط (وهي البينية رقم 9) والتي تشير إلى انتهاء أول جزء من أجزاء قسم البيانات الواردة والذي هو دليل الموارد The Import Directory . طالما أننا وجدنا 8 بُنى (البنية الأخيرة رقم 9 لا تحتسب لأنها تشير فقط إلى نهاية دليل الموارد لا أكثر) فهذا يعني أن برنامج الآلة الحاسبة يستعير دواله من 8 ملفات ربط ديناميكي DLL مختلفة. يُمكننا طبعاً معرفة أسماء هذه الملفات بالذهاب إلى العنوان الذي تشير إليه خانة “الاسم 1 Name” في كل بنية. لكن تلك الخانة تحمل العنوان الظاهري النسبي لاسم الملف، أي عنوان الاسم بعد انتقال البرنامج من القرص الصلب إلى ذاكرة الكمبيوتر. هناك طريقتين للوصول للإسم الأولى هي باستخدام برنامج Ollydbug والذي يسمح لنا برؤية معلومات البرنامج في الذاكرة. أما الطريقة الثانية هي بحساب موقع أسماء ملفات الربط الديناميكي في القرص الصلب مع خلال معرفتنا لعنوانها في الذاكرة. سنستخدم الطريقة الثانية لمعرفة كل الأسماء، طبعاً عرفنا مسبقاً من خلال برنامج LoardPE
عرفنا سابقاً أن قسم البيانات الواردة يبدأ في الذاكرة عند العنوان 002D000h والذي يوازي العنوان 0002AC00h في القرص الصلب. الفرق بين الاثنين هو 2400h = 002D000h – 0002AC00h. وهذا يعني أننا نستطيع معرفة عنوان أسماء ملفات الربط الديناميكي على القرص الصلب بجمع الرقم 2400h مع عناوينهم في الذاكرة (تذكر أن الكمبيوتر يخزن العناوين بالترتيب العكسي للأرقام وقد تم تصحيحهم في الجدول ):
جدول رقم 1
بعد الذهاب إلى عناوين الاسم 1 في القرص الصلب من 1 إلى 8 المشار إليها في إليها في الجدول السابق وُجدت أسماء ملفات الربط الديناميكي التالية:
جدول رقم 2
الآن وبعد معرفة كل بنية من بنى IMAGE_IMPORT_DESCRIPTOR structures وكيف أنها تشير أو ترتبط بملف ربط ديناميكي معين لننتقل إلى البنيتان المتماثلتان اللتان يشيران إليها أول وآخر عنصران في بنية صورة وصف صورة الوارد والتي تتكون كل واحدة منها من بنية تسمى صورة صوت البيانات IMAGE_THUNK_DATA وتتكون من العناصر التالية:
كل بنية صورة صوت البيانات تحتوي على قيمة واحدة فقط وتحتل مساحة 4 بايت. على القرص الصلب فإن هذه القيمة تحتوي على ترتيب الدالة التي يتم استدعائها من الملف الربط الديناميكي DLL المُشار إليه بخانة الاسم في بنية وصف صورة المورد. إن لم تحتوي على ترتيب الدالة فإنها ستحتوي على العنوان الظاهري النسبي لبنية أخرى تُسمى بنية صورة الوارد بالاسم IMAGE_IMPORT_BY_NAME. كثرة البنى المختلفة تزيد من صعوبة تتبع وفهم الملف التنفيذي ولكن الامر سيتوضح أكثر بعض هذا الشرح. ذكرنا القيمتين اللتين يُحتمل أن تحتويهما هذه البنية على القرص الصلب. أما عند نقل البرنامج للذاكرة فإن هذه البنية بأكملها، بنية صورة صوت البيانات IMAGE_THUNK_DATA، التي يُشير إليها العنصر الأخير من عناصر بنية وصف صورة المورد IMAGE_IMPORT_DESCRIPTOR والذي يدعى “الصوت الأول FirstThunk” فإنه يتم إعادة كتابتها لتحتوي على العنوان الظاهري النسبي للدالة التي يتم استيرادها إلى الملف. أما البنية التي يتم الاشارة إليها بأول عنصر من عناصر بنية وصف صورة المورد والذي يدعى “الصوت الأول الأصلي OrignialFirstThunk” فإنها تبقى كما هي في حال احتاج إليها مُحمل البرامج مرة اخرى.
سنلخص الموضوع كما يلي: يبدأ قسم البيانات الواردة ببنية كبيرة تسمى دليل الوارد The Import Directory والتي تتكون من أقسام أصغر منها كل قسم يدعى بنية وصف صورة المورد IMAGE_IMPORT_DESCRIPTOR. كل قسم يشير إلى ملف ربط ديناميكي مختلف وبالتالي نستطيع أن نحدد عدد الملفات التي يقوم البرنامج بإستيراد الدوال منها. أول عنصر في كل بنية والذي يدعى “الصوت الأول الاصلي OrignialFirstThunk” يشير إلى بنية اخرى تسمى صورة صوت البيانات IMAGE_THUNK_DATA“. كل بنية صورة صوت البيانات تخص دالة واحدة يقوم الملف باستيرادها. أي انه إن كان الملف يستورد 5 دوال من الملف user32.dll فهذا يعني أن هناك بنية وصف صورة المورد خاصة بهذا بالملف user32.dll. أول عنصر في هذه البنية يشير إلى أول بنية صورة صوت البيانات الخاصة بأول دالة يستوردها البرنامج من الملف user32.dll. ما ان تنتهي هذه البنية حتى تبدأ بنية صورة صوت البيانات التي تليها والتي تخص الدالة الثانية من الدوال التي يستوردها البرنامج من الملف user32.dll وهكذا حتى نصل إلى 4 بايت تحوي على أصفار فقط وتدل على نهاية الدوال التي يستوردهم البرنامج من الملف user32.dll. مجموعة البنى التي يشير إليها العنصر الأول في بنية وصف صورة المورد تسمى جدول أسماء الموارد Import Name Table. كما أن العنصر الأخير في بنية وصف صورة الوارد والذي يدعى “الصوت الأول FirstThunk” يشير إلى نسخة من بنى صورة صوت البيانات مطابقة للتي يشير إليها العنصر الأول في بنية وصف صورة الوارد. هذه النسخة المتطابقة من بنى صورة صوت البيانات تسمى جدول عناوين الموارد Import Address Table (IAT). وبالتالي في مثال الملف user32.dll سيكون لدينا 5 بنيى صورة صوت البيانات تسمى جدول أسماء الموارد ونسخ مطابقة منهم تسمى جدول عناوين الموارد. كل بنيتان متماثلتان من جدول أسماء الموارد وجدول عناوين الموارد تشيران إلى بنية واحدة تسمى صورة الموارد بالاسم IMAGE_IMPORT_BY_NAME والتي تتكون من العناصر التالية:
الصورة رقم 87 تلخص كل ما تم ذكره عن كيفية قراءة الدوال الواردة لملف الـ PE. بنية صورة الموارد بالاسم تحتوي فقط على الترتيب المحتمل للدالة التي يرغب البرنامج باستيرادها لمساعدة محمل البرامج بإيجادها (ارجع إلى دليل الصادرات) أما “الاسم” فإنه يحتوي على اسم الدالة التي يتم استيرادها. عندما يقوم محمل البرامج بجلب الدالة المطلوبة فإنه يقوم بإعادة كتابة كل محتوى في جدول عناوين الموارد ليحتوي على العنوان الظاهري الفعلي للدالة التي يتم استيرادها. ولفهم كيف اعمق لكيفية حصول ذلك فإن القارئ يُنصح بقراءة مقالة روسيل اوستيرلاند في هذا الموضوع: http://msdn.microsoft.com/en-us/magazine/cc301808.aspx
الخاتمة:-
دراسة الملفات التنفيذية موضوع مهم وطويل حيث أنه يتفرع ويشتمل على دراسة الطرق المختلفة لتشفير البرامج لمنع المخترقين من كسرها. ويشتمل على دراسة وتتبع تأثير الفيروسات في عمل البرامج وماهية الطرق المثلى للإزالة الفيروسات ومسحها من الجهاز. كما أنه يعد مقدمة للتعرف على طرق معالجة النظم المختلفة System Debuging.
يفتقد هذا الكتاب إلى الكثير من المعلومات التي تعد ضرورية للاكمال في هذا المجال. منها التعرف على طرق تحزيم البرامج Packing وتشفيرها. كذلك كيفية التعديل على تعليماتها وتغيير طريقة عملها أو إضافة امر اضافي ووظيفة إضافية لها. كذلك تعلم كيفية إضافة موارد اخرى إلى الملف التنفيذي. ويحتاج الباحث في هذا المجال أن يتعرف بشكل تفصيلي على ماهية نظام التشغيل وكيفية عمله وتوافقه مع البرامج المختلفة.
الهدف من هذا الكتاب هو اثارة انتباه القارئ لهذه المواضيع وحثه على البحث عنها. كل ما ذكر هنا ما هو إلا مقدمة لا أكثر ويُترك الباقي على القارئ ليبحث ويتعمق أكثر. جميع المعلومات التي ذُكرت هنا قد تم تجميعها وتلخيصها من مصادر مختلفة وقد بذلت جهدي في عرضها بأفضل صورة. ولكن أنا لا أدعي الكمال فالكمال لله وحده. وإن كنت قد وفقت فمن الله وحده وإن أخطأت فمن نفسي ومن الشيطان. وكل ما أرجوه في النهاية هو الدعاء الصالح.