لغة Assembly 

من انا؟

يوسف بشاوري, طالب مهتم في مجال الامن السيبراني وخصوصا مجال الهندسة العكسية وتحليل البرمجيات الخبيثة وكل ما يخص الــ Blue Team.



ماهي لغة اسمبلي؟


 لغة اسمبلي هي لغة low-level يمكن للبشر قراءتها وفهمها وأيضا يمكن للمعالج ان يترجم هذه الاسطر البرمجية المكتوبة بلغة اسمبلي الى machine code بحيث يستطيع فهمها وتنفيذها، أيضا لغة اسمبلي تسمح بالوصول المباشر الى الهاردوير وإمكانية التعديل المباشر على الذاكرة العشوائية والـregisters.


 تتنوع لغة اسمبلي وتختلف بحسب معمارية الكومبيوتر (instruction set architecture) , وتنقسم المعمارية الى 4 أنواع رئيسية، وهي:

1- RISC (Reduced Instruction-Set Computer)

DSP (Digital Signal Processor) -2

CISC: Complex Instruction Set Computer -3

VLIW: Very Long Instruction Word -4


الشرح الموجود هو لــــCISC على x64



ماهي أنواع البيانات في لغة اسمبلي؟


Byte : يتكون من 8 بت (البت(bit) هو 0 أو 1) ودائما القيمة موجبة

  • اصغر قيمة هي 0 .᥏ᤆ

  • اكبر قيمة هي 255.

  • الحرف الواحد عبارة عن 1 بايت, مثلا    A’=1 Byte’.


    أيضا يوجد لدينا الـSByte وهو عبارة عن 8 بت لكن قد تكون سالبة او موجبة.

    • صغر قيمة للــSByte هي 128-.

    • اكبر قيمة للـSByte هي 127.


WORD : وهو عبارة عن 16 بت وبالتي تساوي 2 بايت وايضا تكون موجبة.

  • اكبر قيمة للــWORD هي 65535.

  • ايضا يوجد لدينا Signed WORD وتختصر SWORD وتكون 16 بت وايضا تكون سالبة او موجبة .

  • أصغر قيمة سالبة للــSWORD هي 32768- واكبر قيمة هي 32767+



DWORD : وهي اختصار للــDouble WORD وتكون بمساحة 32 بت اي 4 byte, ويتراوح مداها من 0 الى 4,294,967,295



QWORD : وهي اختصار لـQuad WORD وتكون بمساحة 64 بت اي ما يعــادل  Bytes 8 ويتراوح المدى للـunsigned الى 18,446,744,073,703,709,551,615




ماهي الريجسترات (Registers)؟

᥏ᤆهي وحدات تخزينية توجد في الـCPU وتكون مساحتها صغيرة جدا وتخزن bits, وتعتمد مساحتها التخزينية على معمارية المعالج سواء 32bit او 64bit , بحيث انه في 64bit تكون مساحة الريجستر الواحد 64 bits , تمتاز الريجسترات بأنها داخل المعالج, فيسهل الوصول لها بسرعة فائقة على عكس الذاكرة العشوائية RAM. 

انواع الريجسترات:

1- ريجسترات الاستخدام العام (general purpose registers) :

عددها 16 وتستخدم عادة لأغلب الأغراض بحيث تعتمد على حسب المبرمج او المطور, لكن بعض هذه الريجسترات لها استخدامات متعارف عليها.

الريجسترات التي تكون 64 بت , تبدأ بحرف ال”r” مثل RAX والريجسترات التي تكون 32 بت, تبدأ بالحرف “e” مثل EAX. والريجستر RAX و EAX ليست ريجسترين مختلفة , بل هي نفس الريجستر ولكن EAX لها علاقة بالــ lower 32 bits

rax (Accumulator Register) : يستخدم للعمليات الحسابية و تخزين قيمة الــreturn للفنكشن.


rbx(Base Register) : ويسنخدم كمؤشر (pointer) على بيانات معينة.


rcx(Counter Register) : يستخدم في الـloops كــCounter.


rdx(Data Register) : يستعمل في العمليات الحسابية وعمليات الادخال والاخراج (I/O).


 rbp(Base Pointer Register) : يستعمل كمؤشر (pointer) على بداية الستاك (Stack).


rsp(Stack Pointer Register) : ويستخدم كمؤشر لقمة الستاك.


rdi (Destination Index Register) : يستعمل كمؤشر على مواقع في الـData Section في الذاكرة , وايضا يستعمل لحفظ اول argument عند استدعاء دالة.


rsi (Source index register) : يعمل نفس ما يفعله الـrdi لكن على source وليس destination , وايضا يستعمل لحفظ ثاني argument عند استدعاء دالة.


r8,r9,r10,r11,r12,r13,r14,r15 : هذه الريجسترات تستعمل لأغراض عدة, مثل حفظ الـarguments او تستعمل كمؤشر على البيانات او حفظ قيم تستخدم لوقت طويل اثناء الـfunction calls.


هذه الاستعمالات هي استعمالات متعارف عليها ويمكن للمبرمج تغيير وظيفة كل ريجستر حسب الحاجة. فهي في الاخير ريجسترات للاستعمال العام.


يمكن تقسيم الريجستر للوصول الى جزء معين فيه, بدون تغيير الاجزاء الباقية ( تغيير الـ lower 8 bits فقط) ولكن سوف تختلف تسمية الريجستر بهذا الشكل:


2- segment registers

هي ريجسترات تستعمل كمؤشر (pointer) على مناطق في الذاكرة العشوائية مثل data. او code. او الـStack وعددها 6 :

  • SS(Stack Segment) : يعمل كمؤشر على الستاك, وقد يحتوي على عنوان البداية للدالة, ويستعمل ايضا لتخزين return address للــprocedures.

  • CS (Code Segment) : يعمل كمؤشر يحتوي على الاوامر التي سوف تنفذ , يحتوي الـCS  ايضا على عنوان بداية الـCode section في الذاكرة.

  • DS(Data Segments) : يحتوي على عنوان البداية للـData Section التي بدورها تحتوي على المتغيرات والثوابت وبعض المعلومات مثل global variables. 

  • GS, FS, ES : يستعملون كمؤشرات على المزيد من من البيانات , وسبب وجودهم هو تمكين البرامج الى الوصول الى اكبر عدد من البيانات في الذاكرة .


3-RFLAGS Register:

وهو ريجستر واحد طوله 64 bits , لكن ما يميز هذا الريجستر هو ان كل bit فيه مخصص لغرض معين ويتكون من 32 بت محجوزة + EFLAGS

وكلbit᜻ فيه يسمى flag وتنقسم الـflags الى ثلاثة اقسام :

Status Flags (a باللون الاخضر

Control Flags (b باللون الرمادي

System Flags (c باللون الاصفر

ما يلي بعض الـflags واستخداماتها:

CF(Carry Flag) : تكون قيمته 1 اذا حصل هنالك استلاف اثناء عملية الطرح.


PF(Parity Flag) : تكون قيمته 1 في حال كان عدد 1 في الـ lower 8 bits عددا زوجيا .

مثل:  0101001010010101


ZF(Zero Flag) : يكون 1 اذا كانت نتيجة العملية تساوي 0 , وتكون قيمته 0 عدا ذلك .


SF(Sign Flag) : تكون قيمته 0 اذا كان العدد موجبا و 1 اذا كان العدد سالبا.


OF(Overflow Flag) : تكون قيمته 1 اذا حصل هنالك overflow اي ان القيمة المدخلة اكبر من المساحة التخزينية المخصصة لها.


4- المؤشر على الأوامر (Instruction Pointer Register):

هو ريجستر يحتوي على عنوان الامر قيد التنفيذ (Instruction Pointer)RIP,مثلا:

ملاحظة: يسمى RIP في معمارية 64 بت (x86_64) ويدعى EIP في معمارية 32 بت (x86)



ما هو الستاك وما هي أوامره؟

هو في الاساس احد تراكيب البيانات (طريقة لتخزين وعرض البيانات) وتتبع طريقة FILO وهي اختصار لــfirst in last out أي ان اول من يدخل الستاك هو اخر من تتم ازالته , يمكنك ان تتخيل الستاك على انها مجموعة صحون متراكمة فوق بعضها البعض ولا يمكنك ازالة الصحن الاول الا بعد ان تتم ازالة جميع الصحون التي فوقه.

لكن في حالتنا , الستاك هي منطقة في الذاكرة تتبع هذه الطريقة في تخزين البيانات وتخزنها تخزينا مؤقتا.

عملية الاضافة الى الستاك تسمى : push 

وعملية الازالة من الستاك تسمى: pop

مثال: PUSH 4

على ماذا يحتوي الستاك؟

يحتوي الستاك غالبا على هذه الخمس :

1- Arguments

2- المتغيرات الموجودة في scope معين (Local Variables)

3- استدعاء الدوال 

4- الدوال المعرفة 

5- عنوان الرجوع (return address) ويستخدم للعودة الى المكان الذي تم استدعاء الدالة منه.


ما هو الـ Stack Prologue:

افترض ان الكود البرمجي يبدأ التنفيذ من الدالة ()main وحصل هنالك استدعاء لدالة أخرى على سبيل المثال تدعى ()sum , كيف ستتم تهيئة الستاك لاستقبال هذه الدالة؟ . هنا يأتي دور الـstack prologue وهي عبارة عن أوامر بلغة الاسمبلي:

push rbp

mov rbp,rsp

sub rsp,0x60(الرقم للتوضيح فقط)

في البداية قبل الـstack prologue تتم عملية الـpush للـrip بحيث انه عند الرجوع الى الـmain تتم معرفة المكان الذي توقفت عنده دالة main

بعد ان تتم العملية السابقة, يتبقى علينا فقط الطرح من قيمة الـrsp بحيث نزيد من حجم الستاك (الستاك يكبر من high memory address الى low memory address) وقد تختلف القيمة عن 0x60 بحسب حجم الـstack frame المراد انشاؤه.

عكس عملية الــstack prologue هي عملية الـstack epilogue فعلى النقيض تماما , هي مجموعة اوامر مخصصة عند الانتهاء من الـstack frame والعودة الى دالة main على حسب المثال الذي تم ذكره. ويتكون الـstack epilogue من هذه الاوامر:

leave

  ret

امر ret يقوم بعملية pop لقيمة الـrip ويقوم بجعل القيمة الموجودة في الستاك, تحفظ في الـrip register بحيث ان يعود تنفيذ البرنامج الى دالة main.



اوامر اسمبلي الاكثر شيوعا :

اغلب الاوامر في اسمبلي تأتي بهذا السياق:

  <القيمة>  , <المكان المراد الحفظ فيه>       <العلمية>


فعلى سبيل المثال:     mov  eax, 5

هذا الامر سيقوم بحفظ 5 في الريجستر eax.



  • عملية ADD

    هذه العملية مخصصة للقيام بعملية الجمع , فلو اردنا جمع عددين يمكننا القيام بهذه الاوامر :



    على سبيل المثال (4+5=9)

     mov  eax , 5

    mov   ebx , 4

 add   eax , ebx

 

هذا الامر سيقوم بجمع القيمتين ووضع الناتج في eax register.

  • عملية SUB

    وهي عملية مخصصة للقيام بطرح قيمتين من بعضهما البعض, فلو اردنا القيام بعملية 2=3-5 سوف نقوم بالاوامر التالية:

     mov  r8 , 5

     sub   r8 , 3

 

 

هذا الامر سيقوم بطرح 3 من 5 ووضع قيمة الناتج في r8

  • عملية MUL

    تستعمل هذه العملية لضرب عددين unsigned اي لا يوجد عدد سالب ولا نتيجة سالبة, عملية mul تعتبر غريبة بعض الشي بسبب انها تأخذ معامل واحد فقط ولكنها تقوم بعملية الضرب على معامل اخر وتحفظ قيمة الضرب فيه:


    مثال: 30=5*6

                                          mov   ax , 6

                                          mov   cx , 5

                                               mul   cx

هذا ما حصل بطريقة مبسطة:

                                                  ax = 6

                                                  cx = 5

                                          ax = ax * cx

                                                ax = 30


وهذا شرح بالتفصيل لأمر mul :

https://stackoverflow.com/questions/40893026/mul-function-in-assembly

  • عملية DIV

    هي عملية القسمة في لغة اسمبلي وتقوم على نفس مبدأ mul بحيث انه اذا اردنا ان نحصل على ناتج العملية التالية 10/5 فعلينا القيام بالتالي:


                              mov   eax , 10

                               mov   ecx , 5

                                      div   ecx

    لكن في القسمة يوجد لدينا ناتج عملية القسمة بالاضافة الى باقي عملية القسمة, ويتم الاحتفاظ بالناتج في eax وباقي القسمة يحفظ في الريجستر edx.

                             edx = 0   &   eax = 2



التحكم في سير البرنامج :

في البداية عملية jump هي القفز من جزء في الكود الى جزء اخر , أي من مكان الى اخر.


يتم التحكم في سير البرنامج عن طريق الامر jmp في اسمبلي, لكن يختلف نواع الـjmp فقد يكونا كشروطا بشرط معين او لا, وهذا ياخذنا الى نوعين من انواع الـjmp وهي المشروط (conditional) وغير مشروط (unconditional).


  • Conditional Jump:

    ومن هذه العمليات المشروطة :


    je(Jump if equal) : تقفز الى جزء معين في الكود في حال تساوت القيمتين اي ان الـzero flag يساوي 1 .


    jne(Jump if not equal) : هذه العملية ستقوم بالقفز الى المكان المراد من الكود اذا لم تتساوى القيمتين, اي ان الـzero flag مساوي لـ0.


    jg(Jump if Greater) : تتحقق عملية القفز الى الجزء المراد من الكود اذا كانت القيمة اكبر من القيمة المطلوبة.


    jl(Jump if Less) : تتحقق عملية القفز الى الجزء  المراد من الكود اذا كانت القيمة اصغر من القيمة المطلوبة.


  • Unconditional Jump

    وهي بكل اختصار عملية القفز والذهاب الى الجزء المراد في الكود من غير شرط.


    jmp(Jump) : تغير سير البرنامج الى اسم الدالة او عنوان الذاكرة المحدد.



عمليات الـBitwise:

هي عمليات يتم تنفيذها على مستوى الـbits وتختص في تعديل قيمة المتغير او العدد عن طريق تحريك الـbits او عمل عمليات رياضية على مستوى الـbit.


  • shr (shift to the right) : هي عملية تحريك الـbits الى اليمين بعدد خانات معين , فإذا اردنا تحريك القيمة الموجودة داخل ebx بمقدار 5 خانات الى اليمين فسنقوم بالامر التالي:

shr   ebx , 5

  • shl (Shift to the left) : وهي عملية تحريك الـbits الى اليسار بعدد خانات معين , فإذا اردنا تحريك القيمة الموجودة داخل eax بثلاثة خانات الى اليسار فسنقوم بالامر التالي: 

shl   eax , 3

  • ror (rotate to the right) : هي عملية تحريك الـbits بعدد خانات معين الى اليمين , لكن اذا وصل الـbit الى النهاية (نهاية الطرف اليمين) فإنه يعود مرة اخرى الى البداية  (بداية الطرف من اليسار)


    هذا ما سيحصل في حال  تمت عملية تحريك الخانات الى اليمين بمقدار 1 خانة:

  • rol (rotate to the left) : وهي عملية تحريك الـbits الى اليسار , لكن اذا وصل الـbit الى النهاية ( اقصى اليسار) فإنه يعود الى (الطرف الايمن) :


    مثال : تم تحريك العدد 01001110 الى اليسار 3 خانات:



العمليات المنطقية logical operations :

هي عمليات تتم فيها مقارنة كل bit مع bit اخر (او الذي يقابله) وتتم المقارنة حسب العملية المحددة:

  • عملية AND

    هي عملية يكون ناتجها true اذا كان كل الطرفين true. عدا ذلك فالنتيجة false.

وللتوضيح, هكذا تتم عملية AND على المعاملات:


  • عملية OR

    هي عملية ناتجها true اذا كان احد الشرطين true او كلاهما.


  • عملية XOR

    هي عملية يكون ناتجها true اذا كان المعاملين مختلفين , اي على سبيل المثال : true XOR false او false XOR true


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

The best way to learn is by doing. The only way to build a strong work ethic is getting your hands dirty.

Alex Spanos