




السمعة:
- إنضم6 مارس 2024
- المشاركات 115
- الحلول 4
- مستوى التفاعل 221
- النقاط 43



دلوقتي بعد ما فهمنا معماريه الكمبيوتر والمعالج بشكل عام محتاجين نفهم شوية عناصر أساسية في التجميع قبل ما نبدأ نتعلمها زي Registers, Memory Addresses, Address Endianness, and Data Types كل عنصر من دول مهم وفهمهم صح هيساعدنا نتجنب مشاكل وساعات من تصحيح الأخطاء وhحنا بنكتب كود التجميع ونعدل عليه
Registers
زي ما قلنا قبل كده كل نواة في المعالج بتكون فيها مجموعة من السجلات والسجلات دي أسرع مكونات في أي كمبيوتر لأنها متصنعة جوا نواة المعالج لكن حجمها محدود جداً ومش قادرة تخزن غير شوية بايتات من البيانات في وقت واحد فيه كتير من السجلات في بنية x86 لكن هنركز بس على السجلات الضرورية لتعلم أساسيات التجميع واللي هتفيدنا في استغلال الثغرات في المستقبلفيه نوعين رئيسيين من السجلات اللي هنركز عليهم: Data Registers and Pointer Registers
Data Registers | Pointer Registers |
rax | rbp |
rbx | rsp |
rcx | rip |
rdx | |
r8 | |
r9 | |
r10 |
سجلات البيانات (Data Registers) :
- بتستخدم عاده لتخزين الأوامر (instructions) أو معطيات الدوال (syscall arguments)
- أهم سجلات البيانات هي: rax, rbx, rcx, rdx.
- كمان في rdi و rsi اللي بيتم استخدامهم عاده كمصدر ومقصد للأوامر - (destination)
- لو السجلات السابقة كلها مشغولة ممكن نستخدم سجلات البيانات الثانوية: r8, r9, r10
- بتستخدم لتخزين عناوين مهمة محددة
- rbp (Base Stack Pointer): يشير لبداية المكدس (stack)
- rsp (Current Stack Pointer): يشير للمكان الحالي داخل المكدس (أعلى المكدس)
- rip (Instruction Pointer): يحمل عنوان الأمر التالي
Sub-Registers
كل سجل 64-بت ممكن يتقسم لأجزاء أصغر تحتوي على بتات أقل ممكن تكون بايت واحد 8-بت أو بايتين 16-بت أو أربع بايتات 32-بت كل جزء من السجل ممكن نستخدمه وندخله على حدة يعني مش محتاجين نستخدم كل 64-بت لو البيانات اللي عندنا أصغر من كده

ممكن نaccess الsub-registers بالشكل التالي
Size in bits | Size in bytes | Name | Example |
16-bit | 2 bytes | base name | ax |
8-bit | 1 bytes | base name and/or ends with L | al |
32-bit | 4 bytes | base name + starts with the e prefix | eax |
64-bit | 8 bytes | base name + starts with the r prefix | rax |
مثلا بالنسبة ل register bx ال 16-bit هي bx و ال 8-bit هي bl و ال 32-bit هي ebx و ال 64-bit هي rbx و كمان بالنسبة ل register bp ال 16-bit هي bp و ال 8-bit هي bpl و ال 32-bit هي ebp و ال 64-bit هي rbp
Memory Addresses
زي ما قولنا قبل كده البروسيسور x86 64-bit عنده عناوين بحجم 64-bit اللي بتتراوح من 0x0 لحد 0xffffffffffffffff يعني المفروض العناوين تكون في الرينج ده لكن الـRAM مقسمة لمناطق زي الـStack والـheap وأجزاء تانية بتبقى خاصة بالبرامج أو الكيرنل وكل منطقة في الذاكرة بيبقى ليها صلاحيات معينة زي القراءة والكتابة والتنفيذ اللي بتحدد إذا كنا نقدر نقرا منها أو نكتب فيها أو ننفذ عنوان فيها ولما الـInstruction تدخل في الـInstruction Cycle عشان تتنفذ أول خطوة بتكون إننا بنجيب الـInstruction من العنوان اللي هي موجودة فيه زي ما قولنا قبل كده وفي كذا نوع لـAddress fetching أو اللي بنسميه Addressing modes في الـx86 architectureزي ما قولنا في الجدول اللي فوق كل ما القيمة تكون أقل مباشرةً كل ما كانت أبطأ في الوصول ولما بنجي نتعلم الأساسيات في الـAssembly ممكن ما يكونش السرعة هي أولويتنا لكن مهم نفهم إزاي وفين بيتحدد كل عنوان عشان الفهم ده هيساعدنا في حاجات زي استغلال الثغرات اللي بتعتمد على Buffer Overflow مثلاً والفهم ده هيبقى ليه تأثير أكبر في استغلال الثغرات المتقدمة زي ROP أو استغلال الـHeap
Address Endianness
عنوان الـEndianness هو ترتيب البايتات لما بتتخزن أو بتتسحب من الذاكرة فيه نوعين من الـEndianness اللي هما Little-Endian وBig-Endian في الـLittle-Endian الماكينة بتخزن البايت الصغير الأول من اليمين للشمال وفي الـBig-Endian البايت الكبير بيتخزن الأول من الشمال لليمينمثال لو عندنا العنوان 0x0011223344556677 وعايزين نخزنه في الذاكرة في الـLittle-Endian الماكينة هتحط البايت 0x00 على اليمين وبعدين تحط البايت 0x11 بعده فتبقى 0x1100 وبعدين تحط البايت 0x22 فتبقى 0x221100 وهكذا لحد ما يخلصوا البايتات في الآخر بيبقى العنوان معكوس 0x7766554433221100 ولما تسحب القيمة تاني الماكينة هتجيبها بنفس الـLittle-Endian عشان ترجع لنفس القيمة الأصلية
لو شوفنا مثال تاني في الباينري مثلاً لو عندنا عدد مكون من 2 بايت اللي هو 426 بالشكل الثنائي 00000001 10101010 طريقة تخزين البايتين دول ممكن تغير قيمته تخيل لو خزناه بالعكس 10101010 00000001 قيمته هتبقى 43521
الـBig-Endian بيخزن البايتات بالشكل الطبيعي من الشمال لليمين 00000001 10101010 لكن الـLittle-Endian بيخزنها بالعكس 10101010 00000001 اليمين للشمال ولما تيجي تسحب القيمة لازم تستخدم نفس الترتيب اللي اتخزنت بيه عشان تجيب القيمة الصح
اللي محتاجين نركز عليه إن البايتات بتتخزن في الذاكرة من اليمين للشمال يعني لو عايزين ندخل عنوان أو نص بلغة الـAssembly بنحتاج ندخله بالعكس لو عايزين نخزن كلمة Hello هتدخّلها كـo بعدين l بعدين l بعدين e وأخيراً H
وده ممكن يكون غريب شوية لأننا متعودين نقرا من الشمال لليمين بس ليه فوايد في معالجة البيانات زي إنك تقدر تجيب Sub-register من غير ما تمر على الريجيستر كله أو إنك تعمل عمليات حسابية بالترتيب المظبوط من اليمين للشمال
Data Types
أخيراً بنية x86 بتدعم أنواع كتير من أحجام البيانات اللي بنستخدمها مع تعليمات مختلفة الأنواع دي هي اللي هنستخدمها غالباً مع التعليماتلما بنستخدم متغير بنوع بيانات معين أو بنستخدم نوع بيانات مع تعليمة لازم الاتنين يكونوا بنفس الحجم
مثال على كده لو عندنا متغير متعرف إنه byte ماينفعش نستخدمه مع rax لأن rax حجمه 8 bytes في الحالة دي لازم نستخدم al اللي حجمه نفس حجم المتغير وهو 1 byte
التعديل الأخير بواسطة المشرف: