مرحبًا مجددًا. في الدرس السابق تعاملت مع الثنائي والسداسي عشري بوصفهما طريقتين لكتابة القيمة نفسها، وعرفت أن البايت يتكون من 8 بتات وأن 0xFF يساوي 255 عند قراءته كعدد غير موقّع.
الآن نضيف فكرة حاسمة للتحليل العكسي: البتات المخزنة لا تحمل معها تسمية تقول إنها موجبة أو سالبة. النمط نفسه قد يعني قيمتين مختلفتين، وفقًا لطول العدد ونوع التفسير الذي يستخدمه البرنامج. ستتعلم قراءة الأعداد غير الموقعة، والأعداد الموقعة باستخدام المتمم الثنائي، ثم تطبيق ذلك على بايتات تراها داخل ملف أو نافذة ذاكرة.
البايتات أولًا، والتفسير ثانيًا
تخيّل أن الذاكرة تحتوي البايت التالي:
11111111
هذا النمط من 8 بتات هو حقيقة مادية واحدة. لكن توجد قراءتان شائعتان له:
| النمط الثنائي | Hex | التفسير غير الموقّع | التفسير الموقّع بالمتمم الثنائي |
|---|---|---|---|
11111111 | FF | 255 | -1 |
إذًا ليست المسألة أن الذاكرة غيّرت البايت من 255 إلى -1؛ بل إن البرنامج أو الأداة أعطيا المجموعة نفسها من البتات معنى مختلفًا.
في التحليل العكسي، لا تقل عادةً: “هذا البايت قيمته -1” دون سياق. الأدق أن تقول:
البايت
FFيساوي 255 إذا فُسّر كعدد غير موقّع من 8 بتات، ويساوي -1 إذا فُسّر كعدد موقّع من 8 بتات بالمتمم الثنائي.
هذا النوع من الدقة مهم عند قراءة حقول بنية بيانات، أو ثابت في Assembly، أو قيمة سجل في المصحح.
الأعداد غير الموقعة: كل البتات تمثل مقدارًا
العدد غير الموقّع (unsigned) لا يمكن أن يكون سالبًا. كل بت يساهم في القيمة الموجبة بالطريقة التي درستها سابقًا.
لعدد عرضه بت، يكون المجال:
أمثلة مهمة:
| العرض | المجال غير الموقّع |
|---|---|
| 4 بتات | 0 إلى 15 |
| 8 بتات، بايت واحد | 0 إلى 255 |
| 16 بتًا | 0 إلى 65535 |
| 32 بتًا | 0 إلى 4294967295 |
في بايت من 8 بتات، يحمل البت الأيسر الوزن 128:
128 64 32 16 8 4 2 1
1 1 0 0 1 0 0 1
لذلك:
وبالسداسي عشري:
1100 1001 = C9
إذًا:
عند القراءة غير الموقعة فقط.
{
"type": "exercise",
"id": "6f239ea6-e71c-4f8d-90a4-2792e187e31f"
}
لماذا لا تكفي فكرة “بت الإشارة” البسيطة؟
قد يبدو منطقيًا أن نقول: “إذا كان البت الأيسر 1، فالعدد سالب، وخذ البتات المتبقية كمقدار.” هذا يسمى الإشارة والمقدار (sign-magnitude).
مثلًا، في 4 بتات قد تُقرأ:
1011
بوصفها : البت الأول يشير إلى السالب، والبتات 011 تساوي 3.
لكن هذا التمثيل غير مناسب للأعداد الصحيحة المعتادة داخل المعالجات، لأنه يملك تمثيلين للصفر، 0000 و1000، كما يجعل العمليات الحسابية أكثر تعقيدًا.
الحواسيب الحديثة تستخدم عادةً المتمم الثنائي (two's complement) للأعداد الصحيحة الموقعة. فيه ما يزال البت الأيسر مفيدًا لمعرفة إن كانت القيمة سالبة، لكنه ليس مجرد علامة مستقلة عن بقية العدد.
اقرأ الآن المدخل القصير التالي لتثبيت سبب الحاجة إلى تمثيل القيم السالبة، ثم ركز على جدول المتمم الثنائي وخطوات تكوينه. لا تحتاج إلى التوسع في المتمم الأحادي أو الأنظمة البديلة في هذه المرحلة.
{"type":"reading","par_intro":"اقرأ من مقال ويكيبيديا العربي لفهم لماذا لا يمكن للذاكرة أو سجلات المعالج تخزين علامة ناقص كرمز مستقل، ولمراجعة المتمم الثنائي بوصفه التمثيل العملي الشائع.","par_directions":"في الافتتاحية، اقرأ الفقرة الأولى قبل قسم «تاريخ» التي تشرح الحاجة إلى تمثيل الأعداد السالبة داخل RAM وسجلات المعالج؛ فيها <span data-type=\"resource_reading_textrange\" data-resource-subitem-id=\"8f5f9238\" data-range-start=\"RAM\" data-range-end=\"CPU\">سبب الحاجة</span> إلى التمثيل الموقّع. ثم انتقل إلى قسم «المتمم الثنائي». ابدأ بجدول البايتات الذي يقارن القراءة الموقعة وغير الموقعة، واقرأ <span data-type=\"resource_reading_textrange\" data-resource-subitem-id=\"d6ef8ed7\" data-range-start=\"00000000\" data-range-end=\"11111111\">خريطة البايت</span> كاملة، ولاحظ خصوصًا الصفوف التي تبدأ بـ `10000000`. بعد الجدول، تابع مثال تحويل `+2` إلى قيمة سالبة، واقرأ <span data-type=\"resource_reading_textrange\" data-resource-subitem-id=\"b594a234\" data-range-start=\"00000010\" data-range-end=\"11111110\">خطوات القلب</span> التي تتكون من عكس البتات ثم إضافة واحد.","learning_duration":"8 دقائق","url":"https://ar.wikipedia.org/wiki/%D8%AA%D9%85%D8%AB%D9%8A%D9%84%D8%A7%D8%AA_%D8%A7%D9%84%D8%A3%D8%B1%D9%82%D8%A7%D9%85_%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1%D8%A9_(%D8%A7%D9%84%D9%85%D8%AD%D8%AA%D9%85%D9%84%D8%A9_%D9%84%D9%84%D8%A7%D8%B4%D8%A7%D8%B1%D8%A9)","title":"تمثيلات الأرقام المؤشرة (المحتملة للاشارة) - ويكيبيديا","isV2":true,"blockId":"c65e4336-2eaa-4825-a80b-31ce2bccc14a","lessonId":"2b392107-edbf-494b-9a0a-3a05ccb11d71"}
المتمم الثنائي: كيف يقرأ النمط نفسه كقيمة سالبة؟
في المتمم الثنائي، يوجد عددان متساويان في عدد البتات لكنهما يختلفان في المعنى:
- النصف الأول من الأنماط، حيث البت الأيسر
0، يمثل الصفر والأعداد الموجبة. - النصف الثاني، حيث البت الأيسر
1، يمثل الأعداد السالبة.
لعدد موقّع عرضه بتات، يكون المجال:
لذلك:
| العرض | المجال الموقّع بالمتمم الثنائي |
|---|---|
| 4 بتات | -8 إلى 7 |
| 8 بتات | -128 إلى 127 |
| 16 بتًا | -32768 إلى 32767 |
| 32 بتًا | -2147483648 إلى 2147483647 |
لاحظ الفرق عن المجال غير الموقّع: تخصّص الأعداد الموقعة نصف الأنماط تقريبًا للقيم السالبة.
{"type":"image","url":"https://web.stanford.edu/class/archive/cs/cs107/cs107.1224/lectures/02/IntegerRepresentations-img/img43.png","caption":"تُقارن الصورة الأنماط الثنائية نفسها عند تفسيرها كأعداد موقعة بالمتمم الثنائي في الدائرة اليسرى، وكأعداد غير موقعة في الدائرة اليمنى. مثلًا، النمط `1000` يعني -8 في اليسار و8 في اليمين، بينما `1111` يعني -1 في اليسار و15 في اليمين.","isV2":true,"blockId":"47d35822-bd98-4c71-99ae-76109f36cdc8","lessonId":"2b392107-edbf-494b-9a0a-3a05ccb11d71"}
توضح دائرة تمثيل 4 بت ثلاثة أنماط ينبغي تثبيتها:
| البتات | غير موقّع، 4 بتات | موقّع، 4 بتات |
|---|---|---|
0111 | 7 | 7 |
1000 | 8 | -8 |
1111 | 15 | -1 |
إذًا النمط 1011 الذي ذكرناه سابقًا لا يعني في المتمم الثنائي. قيمته غير الموقعة هي 11، ولأننا نقرأه ضمن 4 بتات موقعة:
إذًا:
1011 = 11 unsigned = -5 signed
وهذه نقطة خطأ شائعة: لا تقرأ البت الأيسر كعلامة ناقص ثم تحول البتات الباقية إلى مقدار. تلك طريقة الإشارة والمقدار، لا المتمم الثنائي.
طريقتان لقراءة قيمة موقعة
الطريقة الأولى: اطرح قوة العدد 2 المناسبة
هذه هي الأسرع عندما تعرف طول القيمة.
لنقرأ 0xC9 كعدد موقّع من 8 بتات:
- كعدد غير موقّع،
0xC9تساوي 201. - البت الأعلى هو
1، إذًا القيمة سالبة في المتمم الثنائي. - اطرح ، أي 256:
إذًا:
C9 = 201 unsigned = -55 signed
القاعدة العامة هي:
حيث:
- هو التفسير غير الموقّع للنمط.
- هو التفسير الموقّع.
- هو عدد البتات.
- هو البت الأعلى.
الطريقة الثانية: اعكس البتات ثم أضف 1
هذه الطريقة تكشف مقدار العدد السالب.
لنفس القيمة:
C9 = 11001001
- اعكس كل البتات:
00110110
- أضف 1:
00110111
00110111تساوي 55، إذًا القيمة الأصلية:
هذه الطريقة مفيدة أيضًا عندما تريد تمثيل قيمة سالبة بنفسك. مثلًا، لتمثيل ضمن بايت:
+55 = 00110111
عكس = 11001000
+1 = 11001001
إذًا التمثيل المخزن لـ في 8 بتات هو:
11001001 = 0xC9
{
"type": "exercise",
"id": "3ef484bf-4247-4224-8e26-e0b5b264b117"
}
أمثلة تحفظها بسرعة
| البايت | غير موقّع | موقّع بالمتمم الثنائي | سبب مهم |
|---|---|---|---|
00 | 0 | 0 | الصفر له تمثيل واحد |
01 | 1 | 1 | موجب صغير |
7F | 127 | 127 | أعلى عدد موجب موقّع ضمن بايت |
80 | 128 | -128 | أصغر عدد موقّع ضمن بايت |
C9 | 201 | -55 | مثال نموذجي لقيمة سالبة |
FE | 254 | -2 | قيمة سالبة قريبة من الصفر |
FF | 255 | -1 | نمط شائع لقيمة فشل أو عداد خاص |
يمكنك التحقق الذهني من FE بسهولة:
وكذلك:
تظهر FF كثيرًا في التحليل، لكن لا تفترض دائمًا أنها تعني . قد تكون قيمة 255 صحيحة تمامًا، مثل مكوّن لون أو طول أو علم ضمن حقل غير موقّع. السياق هو الذي يحسم.
ماذا يعني ذلك “داخل الذاكرة”؟
الذاكرة تخزن بايتات متتابعة، لا متغيرات تحمل تسميات مثل “هذا int سالب” أو “هذا unsigned int”. برنامج المصدر، أو تعريف البنية، أو دالة النظام، أو التعليمة المحيطة هو الذي يحدد طريقة التفسير.
عند رؤية C9 في محرر Hex أو في x64dbg، ابدأ بهذه الأسئلة:
- ما حجم القيمة؟ هل هي بايت واحد، أم 16 بتًا، أم 32 بتًا؟
- هل يتوقع السياق قيمة سالبة؟ مثل فرق، إزاحة، رمز حالة، أو نتيجة عملية حسابية.
- هل يوجد تعريف بنية أو توثيق API يحدد النوع؟
- هل خرج Ghidra أو اسم المتغير مجرد فرضية؟ في البداية اعتبره دليلاً، لا حقيقة نهائية.
مثلًا، النمط 32-بت التالي:
FF FF FF FF
إذا قُرئ كعدد غير موقّع من 32 بتًا، فقيمته:
أما إذا قُرئ كعدد موقّع من 32 بتًا بالمتمم الثنائي، فقيمته:
البايتات نفسها لم تتبدل؛ الذي تغير هو العدسة العددية التي نقرأها بها.
في هذا الدرس نتعامل مع قيم منفردة أو مع تسلسلها بوصفه نمطًا ثابتًا. في الدرس التالي ستتعلم كيف يؤثر ترتيب البايتات داخل القيمة متعددة البايتات في القراءة على أجهزة Windows ذات ترتيب Little-endian.
مختبر عملي: اعرض البايتات بطريقتين
خصص نحو 15 دقيقة داخل PowerShell على آلة Windows الافتراضية. لا تحتاج إلى برنامج خارجي أو عينة تدريبية؛ ستنشئ ملفًا صغيرًا من بايتات معروفة وتفسره يدويًا وبرمجيًا.
1. أنشئ ملفًا ثنائيًا صغيرًا
نفّذ الأوامر التالية في مجلد المختبر:
$raw = [byte[]](0x00, 0x01, 0x7F, 0x80, 0xC9, 0xFF)
[System.IO.File]::WriteAllBytes("$PWD\signed-demo.bin", $raw)
Format-Hex .\signed-demo.bin
ستظهر ستة بايتات، بالترتيب:
00 01 7F 80 C9 FF
المحتوى الثنائي للملف لا يسجل أن C9 تعني -55. إنه يسجل فقط البايت C9.
2. اطبع التفسيرين معًا
نفذ هذا المقطع:
foreach ($b in $raw) {
$signed = if ($b -ge 128) { [int]$b - 256 } else { [int]$b }
$bits = [Convert]::ToString([int]$b, 2).PadLeft(8, '0')
"{0:X2} bits={1} unsigned={2,3} signed={3,4}" -f $b, $bits, $b, $signed
}
ينبغي أن تلاحظ، خصوصًا، نتائج من هذا النوع:
7F bits=01111111 unsigned=127 signed= 127
80 bits=10000000 unsigned=128 signed=-128
C9 bits=11001001 unsigned=201 signed= -55
FF bits=11111111 unsigned=255 signed= -1
المعادلة الموجودة في السطر الذي يحسب $signed هي تطبيق مباشر للقاعدة:
لكنها تُطبّق فقط عندما تكون قيمة البايت غير الموقعة 128 أو أكبر.
3. وثق ثلاث قراءات بصيغة محلل
أضف إلى ملف ملاحظات المختبر صفوفًا مشابهة للتالي:
Offset 0x03: byte 80
Unsigned 8-bit: 128
Signed 8-bit two's complement: -128
Interpretation: cannot be finalized without program context.
اكتب ملاحظات مماثلة للبايتين C9 وFF. الهدف ليس حفظ أن FF تعني -1، بل بناء عادة توثيق النمط الخام، العرض، والتفسير المحتمل.
حدود العرض ولمحة عن الالتفاف
العرض جزء من القيمة، وليس تفصيلًا تجميليًا. النمط FF يعني 255 عند اعتباره بايتًا غير موقّعًا، لكن 0x000000FF هو النمط نفسه بعد توسيعه إلى 32 بتًا، وقيمته غير الموقعة أيضًا 255.
ومع ثبات العرض، لا توجد قيمة أعلى من الحد. على مستوى البتات، إذا أضفت 1 إلى FF ضمن بايت، تحصل على:
11111111
+ 1
--------
00000000
أي يعود النمط إلى 00. هذه الفكرة تسمى الالتفاف (wraparound) على مستوى تمثيل البتات. ستصبح مهمة لاحقًا عند تحليل العدادات والتحققات والحسابات منخفضة المستوى، لكن المهم الآن هو عدم نسيان عرض القيمة أثناء تفسيرها.
{
"type": "exercise",
"id": "5c2ad461-da71-4468-bb9a-8dbde21beb15"
}
الخلاصة
أصبحت تستطيع الآن قراءة نمط البتات من زاويتين أساسيتين:
- العدد غير الموقّع من بتات يمتد من 0 إلى .
- العدد الموقّع بالمتمم الثنائي من بتات يمتد من إلى .
- إذا كان البت الأعلى في قيمة موقعة يساوي 0، فالقراءة الموقعة وغير الموقعة متطابقتان.
- إذا كان يساوي 1، احسب القيمة الموقعة بطرح من القراءة غير الموقعة، أو اعكس البتات وأضف 1 لاستخراج المقدار السالب.
- الذاكرة تخزن بتاتًا فقط؛ نوع المتغير والسياق المحيط هما ما يحددان إن كان النمط موقّعًا أو غير موقّع.
FFقد يساوي 255 أو ، وC9قد يساوي 201 أو ، وفقًا للتفسير.
في الدرس التالي ستنتقل من بايت واحد إلى قيم متعددة البايتات، وستتعلم لماذا قد تظهر القيمة 0x12345678 في الذاكرة على هيئة بايتات بترتيب يبدو معكوسًا: Little-endian.
Can't find a good explanation? Sign up and we'll make it for you