P01NT
0x17 متقدم 18/19

الحقول البتية والاتحادات والتعداد

Bit Fields وUnions وEnums — ثلاث أدوات يومية في قراءة الـ binaries وأنماطها الواضحة في الـ assembly.

· 4 دقائق قراءة · 725 كلمة
المحتويات

بسم الله الرحمن الرحيم.

في هذا المقال سنتحدث عن: Bit Fields (الحقول البتية)، Unions (الاتحادات)، و Enums (التعدادات).

1. الحقول البتية (Bit Fields)

تعريفنا في المقال السابق أن الـ struct يوزع بايتات — لكن ماذا لو أردنا تقسيم بايت واحد إلى بتات؟ هنا يأتي الـ Bit Field: نحدد عدد البتات لكل عضو.

typedef struct {
    u8 a : 4;   // 4 بتات
    u8 b : 2;   // 2 بتات
    u8 c : 1;   // 1 بت
} student;      // المجموع 7 بتات ← داخل بايت واحد
+---+---+---+---+---+---+---+---+
| a (4 بت)  | b (2) | c | (فارغ) |
+---+---+---+---+---+---+---+---+

ملاحظة: المترجم يحزم الـ bit fields داخل البايت، لكن لا يضمن سلوكاً واحداً عبر جميع المترجمات (implementation-defined). عند التحليل، قراءة الـ bit fields في الـ assembly تتضمن عمليات and/shift.

2. الاتحادات (Unions)

الـ Union تعني أن جميع الأعضاء يتقاطعون في نفس عنوان الذاكرة — كل الأعضاء يبدأون من نفس الموقع، فيتشابه شكلها مع الـ struct لكن بسلوك مختلف.

عناوين عالية
+-----------------+
|                 |
+-----------------+
|   u32 z         |
|   + u16 y       |
|   +   u8 x      |  ← كلهم من نفس العنوان
+-----------------+
عناوين منخفضة

سلوك التداخل

  • تغيير الـ u8 يغير الـ u16 والـ u32 (كلهم فوق بعض).
  • تغيير الـ u16 لا يغير بالضرورة الـ u8 (قد يكون خارجه)، لكنه يغير الـ u32.

Syntax

union unionName {
    member1;
    member2;
};

حجم الـ Union

typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;

union myUnion {
    u8 x;
    u16 y;
    u32 z;
};

printf("the size of the myUnion is: %zu bytes\n", sizeof(myUnion));

الناتج: 4 بايت — لأن الـ union يأخذ حجم أكبر عضو فيه (وليس مجموعهم).

للمقارنة: لو كانت struct، الحجم = 7 بايت (packed) أو 8 بايت (مع padding). أما الـ union فاكتفى بأكبر عضو لأنه يشترك بالعنوان.

مثال عملي: سجل Register

typedef union {
    struct {
        u8 B0 : 1;   // البت الأول
        u8 B1 : 1;   // البت الثاني
    } Bits;          // struct من bit fields = 1 بايت
    u8 Byte;         // البايت كاملاً
} Register;

الحجم = 1 بايت — لأن الـ struct (1 بايت) والـ Byte (1 بايت).

الوصول

1. الوصول للبايت كاملاً:

Register x;
x.Byte = 30;   // تعيين القيمة كاملة

2. الوصول بتاً بتاً:

x.Bits.B0 = 1; // تعيين البت الأول فقط

تغيير B0 يغيّر قيمة الـ Byte — لأنهما في نفس العنوان.

3. التعداد (Enums)

التعداد وسيلة لإعطاء أسماء لقيم عددية — يجعل الكود أكثر تعبيراً (representative). بدلاً من أن يرى المستخدم/المبرمج 7، يرى apple.

enum fruit {
    mango,  // 0 (افتراضياً)
    apple   // 1
};

يمكن تحديد القيم يدوياً:

enum fruit {
    mango = 2,
    apple = 7
};

مثال متدرج

enum weekdays {
    sunday = 1,  // إخبار المترجم أن البداية من 1
    monday,      // 2
    tuesday      // 3
};

enum weekdays w;
w = monday;
printf("the value of w is: %d\n", w); // 2

ملاحظة RE: في C، الـ enum مجرد عدد صحيح (خلافاً لبعض اللغات) — يظهر في الـ assembly كقيمة ثابتة (immediate).

ربط الـ RE — لماذا هذا المقال كنز تحليلي

1. Bit Fields في الـ Assembly = and + shift

قراءة أي bit field تتحول إلى نمط واضح:

mov al, [ebp-4]   ; قراءة البايت كاملاً
and al, 0x0F      ; استخراج البتات (مثل B0)
shr al, 2         ; إزاحة إلى الموضع الصحيح

عند التحليل، رؤية and + shr متتالية تكشف وجود bit fields — مثل أعلام TCP/IP، حقول الـ PE headers، وأعلام السجلات.

2. Union = نفس العنوان بأحجام مختلفة

في الـ disassembly، الـ union هو نفس العنوان يُفسَّر بعروض مختلفة:

mov eax, [esi]   ; يقرأ u32
mov al, [esi]    ; يقرأ u8 — من نفس العنوان!

هذا يهم في فك بروتوكولات الشبكة و packet headers — نفس البايت يُقرأ كعلم أو كقيمة كاملة.

3. Enum = ثوابت مدمجة (Immediates)

سترى cmp eax, 7 بدل cmp eax, apple — والمحلل يسمي هذه القيم بأسماء الـ enum في IDA/Ghidra لجعل الكود مقروءاً. القيم تظهر كـ immediate في الـ cmp/switch.

4. مثال Register = حقيقي في الـ RE

في التحليل العملي، السجلات وتنسيقات الأجهزة تُقرأ عبر مثل هذه الـ unions (بايت/بت من سجل) — وسترى النمط في كود السائقين (drivers) وتهيئة الأجهزة.

خلاصة

  • Bit Field: تقسيم البايت لبتات — يظهر في الـ asm كـ and/shift.
  • Union: أعضاء تشترك بنفس العنوان — يظهر كتفسيرات مختلفة لنفس الموقع.
  • Enum: أسماء لقيم عددية — يظهر كقيم مدمجة في الـ cmp/switch.

الثلاثة أدوات يومية في قراءة الـ binaries — أتقنها وستتسارع قراءتك لأي كود تحلله.