Create your own
Lesson illustration

فهم ترتيب البايتات الصغير عند قراءة القيم متعددة البايتات

مرحبًا مجددًا. في الدرس السابق فرّقنا بين البايتات الخام وبين تفسيرها كأعداد موقعة أو غير موقعة. الآن ننتقل من بايت واحد إلى قيمة مؤلفة من عدة بايتات، وهي لحظة ستتكرر كثيرًا عند فحص الذاكرة في x64dbg، أو قراءة حقول ملف PE، أو تحليل بيانات برنامج.

ستتعلم كيف تقرأ القيم متعددة البايتات وفق ترتيب Little-endian المستخدم عادةً على أجهزة Windows ذات معالجات x86 وx64. الفكرة المركزية بسيطة: في الذاكرة، يُخزَّن البايت الأقل أهمية أولًا عند العنوان الأصغر.


القيمة ليست ترتيبها في الذاكرة

خذ العدد ذي 32 بتًا التالي:

0x12345678

يمكن تقسيمه إلى أربعة بايتات:

البايتموقعه داخل القيمة المكتوبة
12البايت الأعلى أهمية، MSB
34البايت التالي
56البايت التالي
78البايت الأدنى أهمية، LSB

نحن نكتب العدد عادةً من الأكثر أهمية إلى الأقل أهمية، تمامًا كما نكتب العدد العشري 512 بحيث تمثل 5 مئات و2 آحادًا. لكن الذاكرة لا تخزن عددًا كاملًا في خانة واحدة؛ بل تخزن بايتات متجاورة لكل منها عنوان مستقل.

إذا بدأ هذا العدد عند العنوان الافتراضي 0x00401000، فهناك طريقتان أساسيتان لترتيب البايتات:

العنوانLittle-endianBig-endian
0x004010007812
0x004010015634
0x004010023456
0x004010031278

إذن، عندما تُخزّن قيمة 0x12345678 في ذاكرة جهاز Windows المعتاد، سترى غالبًا:

78 56 34 12

لكن هذه البايتات لا تمثل العدد 0x78563412 إذا كانت الأداة تقرؤها بوصفها قيمة 32 بت Little-endian؛ إنها ما تزال تمثل:

0x12345678

الخطأ الشائع هو النظر إلى صف البايتات وقراءته من اليسار إلى اليمين كأنه رقم Hex مكتوب للبشر. صف الذاكرة يعرض العناوين بترتيب تصاعدي، لا ترتيب أهمية البايتات داخل العدد.


ما المقصود بـ “الأقل أهمية” و“الأعلى أهمية”؟

في العدد:

0x12345678

كل زوج من رموز Hex هو بايت واحد. قوة كل بايت تتحدد بموقعه:

لذلك:

  • 0x12 هو Most Significant Byte أو البايت الأعلى أهمية، لأن تغييره يؤثر كثيرًا في القيمة الكلية.
  • 0x78 هو Least Significant Byte أو البايت الأدنى أهمية، لأن قيمته تمثل خانة الآحاد على مستوى البايتات.

في ترتيب Little-endian، يبدأ التخزين بالبايت ذي الوزن الأصغر، 0x78. ثم تأتي البايتات الأعلى أهمية بالتدريج.

Little-endian لا يعكس البتات داخل كل بايت.
البايت 0x78 يبقى 0x78، ولا يصبح 0x1E. الذي يتغير فقط هو ترتيب البايتات الكاملة داخل القيمة متعددة البايتات.

شاهد هذا المقطع القصير من فيديو Endianness Explained لقناة Aaron Yoo. يعرض التمييز بين القيمة المكتوبة وتمثيلها في الذاكرة، وهو أساس تجنب سوء القراءة في المحللات والمصححات.

Endianness Explained

شاهد المقطع لفهم معنى البايت الأعلى والأدنى أهمية، ولماذا لا تكفي البايتات الخام وحدها لمعرفة القيمة من دون معرفة ترتيبها.

ابدأ من أهمية البايتات لترسيخ الفرق بين موضع البايت داخل العدد وموضعه في الذاكرة. ثم شاهد القيمة والترميز؛ ركز على أن تسلسل البايتات نفسه قد يُقرأ كقيمتين مختلفتين إذا لم يكن ترتيب البايتات محددًا.


قاعدة Little-endian العملية

عند قراءة بايتات من الذاكرة في Windows، اتبع هذه القاعدة:

أصغر عنوان يحتوي البايت الأدنى أهمية.

إذا قرأت أربعة بايتات متجاورة كما يلي:

العنوان          البايت
0x00401000       78
0x00401001       56
0x00401002       34
0x00401003       12

فإن القراءة الصحيحة بوصفها عددًا 32 بت Little-endian هي:

0x12345678

لإعادة تركيب العدد يدويًا، اقرأ البايتات الثنائية بترتيب معاكس لعرض الذاكرة:

Memory bytes:  78 56 34 12
Value:         12 34 56 78

والصيغة العامة، إذا كانت البايتات في الذاكرة هي ، هي:

في مثالنا:

لا تحتاج عادةً إلى إجراء هذه العملية الحسابية كاملة يدويًا؛ يكفي أن تتعلم تجميع البايتات من الأخير إلى الأول عند قراءة قيمة Little-endian. لكن فهم الصيغة يجعلك تعرف لماذا تعمل القاعدة.

الصورة تقارن تخزين القيمة `0A0B0C0D` في الذاكرة: في Little-endian يظهر البايت `0D` أولًا عند العنوان الأصغر، بينما يظهر `0A` أولًا في Big-endian.

في الصورة، لا يتغير العدد المنطقي 0A0B0C0D. الذي يتغير هو مكان كل بايت في الذاكرة. على Windows، الشكل الموجود في جهة Little-endian هو الذي ستصادفه غالبًا عند فحص int أو عنوان أو ثابت عددي متعدد البايتات.


مثالان صغيران: 16 بت و32 بت

قيمة من 16 بت

لنفترض أن نافذة الذاكرة تعرض:

34 12

إذا فُسرت كبايتين Little-endian، فالقيمة هي:

0x1234

لأن:

  • 34 في العنوان الأصغر، وهو البايت الأدنى أهمية.
  • 12 في العنوان التالي، وهو البايت الأعلى أهمية.

أما لو كان التنسيق Big-endian، فكان التسلسل نفسه يعني:

0x3412

قيمة من 32 بت

إذا ظهرت البايتات التالية:

EF BE AD DE

فقراءة Little-endian تعطي:

0xDEADBEEF

وهذا نمط شائع في الأمثلة التعليمية والتحليل منخفض المستوى. لا تقرأه كـ 0xEFBEADDE لمجرد أن هذه هي البايتات الظاهرة من اليسار إلى اليمين.

قاعدة تحقق سريعة:

بايتات متجاورة في الذاكرةالقراءة الصحيحة كـ Little-endian
01 000x0001
FF 000x00FF
34 120x1234
78 56 34 120x12345678
EF BE AD DE0xDEADBEEF

Little-endian لا يحدد إن كانت القيمة موقعة

ترتيب البايتات يجيب عن سؤال:

ما ترتيب تجميع البايتات في قيمة واحدة؟

أما التوقيع فيجيب عن سؤال مختلف:

هل نفس نمط البتات يمثل عددًا موجبًا فقط، أم عددًا موقّعًا بالمتمم الثنائي؟

خذ البايتات التالية:

00 80

عند قراءتها كقيمة 16 بت Little-endian، نعيد ترتيب البايتات منطقيًا:

0x8000

ثم نحدد نوع التفسير:

التفسير بعد تطبيق Little-endianالقيمة
غير موقعة uint1632768
موقعة int16 بالمتمم الثنائي-32768

إذًا الترتيب المنهجي هو:

  1. حدد حجم القيمة: بايتان، أربعة بايتات، أو ثمانية بايتات.
  2. حدد ترتيب البايتات وفق المعمارية أو مواصفة التنسيق.
  3. اجمع البايتات للحصول على النمط العددي.
  4. قرر هل يُفسر النمط كموقّع أو غير موقّع بناءً على نوع الحقل والسياق.

لا تقفز مباشرة من 00 80 إلى “هذه قيمة سالبة”. هي سالبة فقط إن كان البرنامج يعاملها كعدد موقّع من 16 بت.


لماذا يهم ذلك في التحليل العكسي؟

عند استخدام محرر Hex أو مصحح أخطاء، ترى الذاكرة غالبًا على هيئة صفوف بايتات ذات عناوين تصاعدية. لذلك قد يبدو عنوان أو عداد أو ثابت عددي “معكوسًا” في البداية.

مثلًا، إذا رأيت في ذاكرة عملية Windows:

A0 40 00 00 00 00 00 00

فهذا قد يمثل قيمة 64 بت Little-endian:

0x00000000000040A0

والصفران في النهاية المرئية ليسا “نهاية” القيمة؛ إنهما البايتات الأعلى أهمية المخزنة عند عناوين أعلى.

هذا مهم لاحقًا عند:

  • قراءة قيمة متغير من نافذة الذاكرة في x64dbg.
  • فهم المعاملات أو العناوين المخزنة على المكدس.
  • تفسير حقول رقمية في ترويسات ملفات PE.
  • تحليل كود آلة يحتوي على ثوابت فورية متعددة البايتات.
  • مقارنة بايتات ملف مع قيمة يعرضها Ghidra أو المصحح.

لكن لا تفترض أن كل شيء في بيئة Windows هو Little-endian. ترتيب البايتات خاص بالتنسيق أو البروتوكول أيضًا، وليس فقط بالمعالج:

  • الذاكرة المعتادة لمعالجات x86 وx64 تستخدم Little-endian.
  • البايت المفرد لا يملك مشكلة ترتيب أصلًا.
  • النص ASCII العادي سلسلة بايتات منفصلة، فلا نعكس أحرفه.
  • بعض تنسيقات الشبكات تستخدم Network Byte Order، وهو Big-endian؛ يجب اتباع مواصفة البروتوكول لا افتراض ترتيب الذاكرة المحلي.

بما أن لديك معرفة أولية بالشبكات والبروتوكولات، احتفظ بالتمييز التالي: قد يستقبل برنامج Windows حقلًا شبكيًا بترتيب Big-endian، ثم يحوله داخليًا إلى القيمة المناسبة قبل استخدامها. لا يكفي أن تقول “Windows Little-endian” عند تحليل حزمة شبكة؛ مواصفة الحقل هي المرجع.


مختبر عملي: قراءة قيم Little-endian على Windows

خصص نحو 15 إلى 20 دقيقة لهذا المختبر داخل الآلة الافتراضية. لا تحتاج إلى ملف مشبوه أو برنامج خارجي؛ سننشئ بايتات معروفة ثم نطلب من Windows تفسيرها.

1. أنشئ بايتات قيمة 32 بت

افتح PowerShell ونفذ:

$le = [byte[]](0x78, 0x56, 0x34, 0x12)
[System.IO.File]::WriteAllBytes("$PWD\endian-demo.bin", $le)

Format-Hex .\endian-demo.bin

ينبغي أن ترى:

78 56 34 12

هذا هو التخزين Little-endian للقيمة:

0x12345678

2. اطلب من Windows قراءة البايتات كعدد 32 بت

نفذ:

$value = [BitConverter]::ToInt32($le, 0)

"Decimal: $value"
"Hex:     0x{0:X8}" -f $value

النتيجة المتوقعة:

Decimal: 305419896
Hex:     0x12345678

تستخدم BitConverter ترتيب البايتات الخاص بالجهاز الحالي. على Windows x64 المعتاد، سيقرأ هذه البايتات بوصفها Little-endian.

3. بدّل ترتيب البايتات واختبر النتيجة

نفذ الآن:

$opposite = [byte[]](0x12, 0x34, 0x56, 0x78)
$wrongForLittleEndian = [BitConverter]::ToInt32($opposite, 0)

"Decimal: $wrongForLittleEndian"
"Hex:     0x{0:X8}" -f $wrongForLittleEndian

ستحصل على:

Hex:     0x78563412

هذا يثبت أن BitConverter لم “يتعرف” على نيتك الأصلية بأن القيمة هي 0x12345678. هو قرأ البايتات الخام وفق ترتيب Little-endian الخاص بالنظام.

4. افحص الصورة الذهنية في محرر Hex

إذا كان لديك HxD، افتح endian-demo.bin. ستظهر البايتات حرفيًا:

78 56 34 12

ثم اختر أول بايت وافتح Data Inspector. غيّر تفسير القيمة إلى عدد صحيح بحجم 32 بت، وتأكد من تفعيل خيار Little endian.

تُظهر نافذة Data Inspector في HxD كيف يمكن تفسير البايت المحدد بأحجام وأنواع مختلفة، مع خيار صريح للاختيار بين Little-endian وBig-endian؛ التفسير يتغير بينما البايتات الخام لا تتغير.

المبدأ الذي يجب ملاحظته في HxD هو أن نفس موضع الملف أو الذاكرة يمكن عرضه كـ:

  • UInt16 أو Int16
  • UInt32 أو Int32
  • UInt64 أو Int64
  • قيمة Little-endian أو Big-endian

هذه الخيارات لا تغير الملف وحدها؛ إنها تغير طريقة العرض والتفسير. لا تحفظ فوق ملف إلا عندما تكون متأكدًا تمامًا من البايتات التي تريد تعديلها.


قائمة فحص قبل تفسير أي قيمة

عند رؤية تسلسل بايتات في x64dbg أو HxD أو Ghidra، استخدم قائمة الفحص التالية:

  1. أين تبدأ القيمة؟ دوّن عنوان أول بايت أو إزاحته في الملف.
  2. ما طولها؟ هل هي 2 أو 4 أو 8 بايتات؟
  3. ما مصدرها؟ ذاكرة عملية Windows، حقل ملف، أم حزمة شبكة؟
  4. ما ترتيب البايتات الذي يحدده هذا المصدر؟ لا تفترضه بلا دليل.
  5. ما نوعها المحتمل؟ عدد موقّع، غير موقّع، عنوان، علم، أو جزء من بنية.
  6. هل النتيجة منطقية في السياق؟ قيمة مثل 0x78563412 قد تعني أن الحجم أو الترتيب المختار خطأ.

صياغة ملاحظة تحليل جيدة تكون مثل:

Address: 0x00401000
Raw bytes: 78 56 34 12
Width: 32 bits
Byte order: Little-endian
Unsigned interpretation: 0x12345678 (305419896)
Signed interpretation: 305419896
Confidence: high; value read from Windows process memory.

هذه العادة تمنع خلط البايتات الخام بالقيمة المستنتجة منها، وتسمح لك بمراجعة استنتاجك لاحقًا.


الخلاصة

أهم ما ينبغي تثبيته من هذا الدرس:

  • ترتيب البايتات يهم فقط للقيم التي تتكون من أكثر من بايت.
  • في Little-endian، يُخزن البايت الأدنى أهمية عند العنوان الأصغر.
  • القيمة 0x12345678 تظهر في ذاكرة Windows عادةً كبايتات 78 56 34 12.
  • لا يجري عكس البتات ولا رموز Hex المنفردة؛ بل يُعاد ترتيب مجموعات البايتات فقط عند تجميع القيمة.
  • Little-endian مستقل عن كون العدد موقّعًا أو غير موقّع؛ حدد الترتيب أولًا، ثم نوع التفسير.
  • ذاكرة Windows المعتادة Little-endian، لكن مواصفات الملفات والبروتوكولات قد تستخدم ترتيبًا آخر.

في الدرس التالي ستنتقل من الأعداد إلى المحتوى النصي: كيف تميّز نصوص ASCII وUnicode عن البيانات الخام عند النظر إلى ملف أو ذاكرة داخل محرر Hex.

Can't find a good explanation? Sign up and we'll make it for you

Sign up