+بسم الله الرحمن الرحيم
الدرس الثانى : بداية عملية مع الـMASM 32
MUSiC iN BacKGRoUnD LiNKiN PaRK – 4G10 (FoRGotTeN) ;-) / NoBoDY LiSteNiNG / PLaCe 4 My HeaD / NUMB
فى هذا الدرس سوف نقوم بأنتاج برنامج ويندوز يقوم بعملية بسطية جدا وهى كالمعتاد "hello world"
يقوم النظام بعمل تهيئة كبيرة و قوية جدا للمبرمج للعمل مع بيئة تساعدة على العمل بسرعة و بتحكم كبير و أفضل ما توفة بيئة الويندوز هو الـSystem API و التى تعطيك تحكم كامل فى البرنامج و النظام بشكل كامل و قوى و هناك عدد ضخم جدا يصعب حصرة من الدوال.
وهذه الدوال تكون مخزنة فى بعض المكتبات الموحودة فى النظام على شكر ملفات فعل سبيل المثال نجد أن الملفات تالية لها بعض الدوال كالأتى :-
GDI32.dll دجد أن هذه المكتبة موجود بها معظم دوال الخاصة بالعرض والجرافيك.
Kernl32.dll نجد أن هذا الملف يحتوى على معظم الدوال الخاصة بالتعامل مع الذاكرة و المعالجة.
User32.dll نجد أن هذه المكتبة تحتوى على معظم دوال التى تخص التعامل مع المستخدم مثل وجهات البرامج و مدخلات المستخدم و هكذا.
يوجد كم هائل من الدوال و كل ما تحتاجة هو معرفة مدخلات الدالة و مخرجاتها و كيفية الأستفادة من هذه المخرجات.
كل برامج الويندوز بلا أستثناء مربوطة بهذه المكتبات دينمكياً بمعنى أنه عند أستدعاء دالة API لا يعني هذا أن الدالة سيتم أحتوائها داخل شفرة البرنامج و لكن ما يحدث مشابه لذلك وهو أن النظام يقوم بعمل نسخة من الدالة لبرنامجك و بعد الأنتهاء منها يقوم النظام بالتخلص منها و هذه النقطة هى سبب مشاكل ويندوز و هى عدم وجود نظام أدارة ذاكرة يقوم بالحفاظ على الذاكرة بدون أهدار و أظن أن الجميع يشعر بذلك عندما يعمل على الحاسب لوقت طويل تجد أن الويندوز بدأ فى البطئ و تهنيج. نعود مرة أخرى الى موضوعنا و لكن كيف سيعرف النظام أن برنامجك يحتاج أستعمال دالة معينة حتى يتمكن النظام من أحضار هذه الدالة لبرنامجك حل بسيط جدا وهو أن المترجم يقوم بناء ما يسمى جدول الأستيراد ( Import table ) هذا الجدول يقوم النظام بقرأته و تحديد الدوال التى نسخه الى الذاكرة و بالتحديد يقوم النظام بقرأة أسماء الدوال و أسماء المكتبات التى تحويها و يقوم النظام بقرأت الدوال معرفت عنوانها داخل المكتبه عن طريق جدول موجود بها و هذا الجدول أسمه Export Table أو جدول التوريد و جدول التوريد يحتوي على أسماء الدوال و عنوانها أهم ما فى الموضوع أننا نقوم بأستدعاء الدوال بعنوانها و ليس أسمها اسماء الدوال مجرد وسيلة لتسهيل اتعامل معها.
لكل داله شكلان لأستدعائها على سبيل المثال
MessageBoxA
MessageBoxW
الشكلان يؤديان نفس الغرض و لكن هناك أختلاف جوهري و مهم جدا أن كل الدوال الملحقة بحرف A دوال تتعامل مع سلسلة حرفية ASCII بمعنى أخر لا يوجد دعم للغات أخرى أى حروف الأسكي فقط و مساحة الـحرف ASCII هى 8 بت أو حرف واحد 8 bites or 1 byte و يشمل الـASCII حروف اللغة الأنجليزية + حروف اللغة الثانية الموجودة على النظام.
الشكل اللأخر من الدوال هى الدوال الملحقة بالحرف W و هذه الدوال هى الدوال التى تتعامل مع نظام الحروفUNICODE و الـUNICODE نظام يسمح بوجود 65536 حرف و لكن الـunicode مساحاتة أكبر من الأسكي حيث أن الـUNICODE يعتمد على 16 بت أو حرفان 16 bites or 2 bytes و هذا يسمح بتعدد اللغات داخل السلسلة الحرفية بالنسبة للمبرمج ليس عليه الأهتمام بهذا الجزء لانه يتم التعامل معه و تحديد الأفضل بواسطة المترجم.
الأن سوف نعرض نموذج لبرنامج فارغ لتوضيح بعض النقاط التى سوف تساعدنا حتى نهاية الدروس:-
.386
.model flat, stdcall
.data
.code
start:
end start
يبدأ تنفيذ التعليمات مع بداية الـStart Label مباشرة فى الهيكل السابق ذكرة فى الأعلى, يتم تنفيذ التعليمات تعليمه تلو الأخرى حتى يتم العثور على تعليمات التحكم أو قفز أو كما تسمى التحكم فى تتدفق البرنامج Flow Control هذه التعليمات تغير مسار تنفيذ البرنامج.
و الن لنبدأ بداية أكثر فاعلية و عملية , عندما تحتاج الى أظهار رسالة للمستخدم يجب عليك أستخدام الدالة MessageBoxA و لكن كيف سيعرف المجمع عدد مدخلات هذه الدالة كل هذا يتم عن طريق تعريف الدالة كما هو بالشكل التالى
MessageBoxA PROTO :DWORD,:DWORD,:DWORD,:DWORD
كما ذكرت منذ سطرين أن هذا السطر يسمى تعريف الدالة فائدة هذا السطر أن يقوم بتحديد مدخلات الدالة لجعل المجمع بأختبار صحة المدخلات و منع حدوث مشاكل أثناء تشغيل البرنامج , المشاكل متوقعة بنسبة 100% فى حالة عدم أستخدام التعريف الا فى حالة أدخال المدخلات بطريق صحيحة لانه عند تنفيذ الدالة سوف تقوم الدالة بسحب مدخلاتها من الرصة stack بغض التظر عن ما هو موجود فى الرصة فلذلك سوف تجد أشياء لا تريدها و غالبا تنتهى بحدوث مشكلة.
وهذا هو شكل هيكل التعريف
FunctionName PROTO [ParameterName]:DataType,[ParameterName]:DataType,...
كما نرى أن أسمى الدالة يكون فى أول التعريف ,أسم الدالة دائما يتبع بالكلمة المحجوزة PROTO و بعد ذلك تلحق بمدخلات الدالة و أنواعها و هناك موضوع مهما جدا بالنسبة للتعامل مع نتائج الدوال سيتم توضيحة لاحقا فى هذا الدرس , لاحظ أنه يتم الفصل بين كل مدخل و الأخر عن طريق الفاصلة "," و فى المثال السابق الدالة MessageBoxA تأخذ أربع متغيرات من DWORD , و لابد من معرفة أن المدخل الثانى و الثالث من نوع سلسة حرفية , طبعا هناك شئ غريب و هو كيف يمكن لمدخل حجمة 4 حروف أخذ أى عدد من الحروف كسلسلة حرفية الأمر بكل بساطة هو أننا عندما نتعامل مع السلسلة الحرفية لا نتعامل معها كما هى و لكننا نتعامل مع عنوان السلسلة الحرفية و بالنسبة لهذا الموضوع ( التعامل مع السلسلة الحرفية ) سيتم توضيحة فى هذا الدرس لاحقا, و لكن بعد كل هذا ما فائدة تعريف الدالة , فائدة تعريف الدالة هو فى طريقة أستدعاء الدالة هناك طريقتان لأستدعاء الدالة
Push arg1 ; the frist way to call the function
Push addr str1
Push addr str2
Push arg2
Call MessageBoxA
Invoke MessageBoxA,arg1, addr str1, addr str2, arg2 ; the secod way to call the function
الطريقة الأولى كما يرها البعض أنها غير أمنة نظرا لطبيعة الأنسان فى أن ذاكرته لن تسمح له بتذكل كل شئ لذلك أستخدامك لهذه الطريقة لابد أن يكون معك دليل الدوال و فى حالة نسيان أحد المدخلات أضمن لك أنك لن تحصل على النتائج المطلوبة, فى الطريقة الثانية نجدة أكثر أناقة من الأولى حيث كل المدخلات فى سطر واحد بالأضافة فى حالة أستدخدامك لوجهة RADASM سوف تجد أن الـAuto Completion يعمل معك فى الشكل الثانى و يساعدك فى فهم مدخلات الدالة, نأتى للناحية العملية للموضوع فى الحالة الأولى لن يشعر المجمع بوجود خطاء و لكن سوف يحدث الخطأ بعد تجميع البرنامج و تشغيلة و ذلك أثناء أستدعاء الدالة , و لكن فى طريقة الثانية يتحقق المجمع من تتطابق تعريف الدالة و طريقة أستدعائة و فى حالة وجود خطأ سوف يتوقف و يعلمك بوجود خطأ, الميزة الحقيقية فى الطريقة الثانية فى السهولة فى مناولة المدخلات الدالةThe ease of parameter handlingو الأن لنرى مثال جديد ( مثال بجد )
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib
.data
MsgBoxCaption db "Iczelion Tutorial No.2",0
MsgBoxText db "Win32 Assembly is Great!",0
.code
start:
invoke MessageBox, NULL, addr MsgBoxText, addr MsgBoxCaption, MB_OK
invoke ExitProcess, NULL
end start
أكيد فى ناس هتقول أن أنا ضحكت عليهم لان هيكل البرنامج ذاد سطور كتير؟؟؟ و لكن هذه السطور لتسهيل عملية التجميع و ليس مهمة بنسبة 100% لذلك لنرى أهمية هذه السطور.
و أول السطور التى زادت هذا السطر
option casemap:none
و هذا السطر معناه أنك تخبر المجمع الأخذ بعين الأعتبار حالة الحروف Case Sensitive لذلك نجد أن المتغير strTEXT يختلف تماما عن المتغير StRtext أو strText و هكذا.
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
هذه السطور فائدة للمجمع و المبرمج مهمه جدا لانها تريح كلا من الجهتين فهذه السطور معناها أحتواء ما بداخل هذه الملفات من دوال و متغيرات الى برنامجك و الا فأنك سوف تضطر الى تعريف الدوال التى تتعامل معها كما و ضحنا سابقا لذلك أنصح بأن تقوموا فتح هذه الملفات و روئية ما بداخلها ;-) .
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib
هذه الملفات تحتوى على مكتابات يحتاجها المجمع لأكمال جدول الأستيراد و أكمال linking لبرنامجك, بالأضافة الى أن هذا السطر سوف يوفر الكثير من العناء و التعب للتعامل مع المربط LINKER.
و الأن هيا للعمل Lets get our hands dirty with the code سوف نقوم بترجمة أول برنامج لنا
قم بتخزين الكود المذكور أعلاه فى ملف و قم بتسميته FiRST.asm قم بالدخول على مجلد الـMASM و أكتب الأمر التالى
ML /c /coff /cp FiRST.asm
و الأن ما معنى هذا السطر؟ هذا السطر كما نلاحظ أن هذا السطر مكون من 5 كلمات
الأولى ( ML ) هى أسمى برنامج المجمع الذي سوف يقوم بتجميع برنامجنا
الثانى ( /c ) معنى هذا الخيار أنك تخبر المجمع أن يقوم بعملية التجميع فقط و أنك لا تريد ربط البرنامج الأن.
الثالث ( /coff ) هذا الخيار معناه أنك تطلب من المترجم أن يقوم بأنتاج ملف هذف Object File من نوع Coff و الذي هو مخصص لنظام التشغيل يونكس وهذه الكلمة أختصار لـ(Common Object File Format ) ( نسق ملف هدف عام ).
الرابع ( /cp ) يقوم بحفظ حالة الحروف للمستخدم Case status
الخامس ( FiRST ) أسم الملف المراد تجميعة
الى هنا تكون قد أتممت عملية التجميع و لكنك لم تنتهي بعض من أنتاج ملف تنفيذي نحن الأن بحاجة الى ربط الرنامج Linking the object file
link /SUBSYSTEM:WINDOWS /LIBPATH:c:\masm32\lib FiRST.obj
هذا السطر سوف يقوم بهذه المهمة و لكن لابد أولا من فهم متغيرات البرنامج Parametars
الأول (/SUBSYSTEM:WINDOWS ) هذا السطر يخبر المترجم أنك سوف تنتج ملف لنظام التشغيل ويندوز
الثانى (/LIBPATH:<path to import library> ) هذا السطر يخبر المجمع بمسار مجلد ملفات المكتبات .
الثالث (FiRST.obj ) أسم ملف الهدف Object file.
بهذا يكون قد أنتهى الدرس الثانى و لكن هناك بعض النقاط و التى أود التحدث عنها و أولها هى كيفية الحصول على عنوان سلسلة حرفية, و يمكن أن يتم هذا بطريقتين و سوف أو ضحهما بمثال يمكن تطبيقة
أولا طريقة addr و التى أستخدمتها فى المثال السابق
Invoke MessageBox,0,addr strText,addr strCaption,0
و هذه الطريقة تقوم بأرجاع عنوان السلسة للدالة الطريقة الثانية و هى اطول فى الكود و لكنها تؤدي نفس الغرض
ثانيا طريقة LEA
LEA EDX, strText
LEA ECX, strCaption
Invoke MessageBox,0,EDX,ECX,0
تقوم الدالة LEA بتحميل عنوان المتغير فى مسجل و هى أختصار لـ(Load Effective Address)
و يمكن أيضا أستعداء الدالة بهذه الطريقة
LEA EDX, strText
LEA ECX, strCaption
Push 0
Push EDX
Push ECX
Push 0
Call MessageBox
النقطة الثانية التى كنت بصدد التدث عنها هى كيفية أستعمال الـRADASM فى تطبيق هذا المثال و الأن لنقوم بهذا المثال مع RADASM
المثال ملحق بالصور فى الملف الملحق

