بسم الله الرحمن الرحيم
سنناقش في هذا المقال التحكم في تدفق البرامج
1. تعليمة JMP
تنفّذ تعليمة JMP قفزة غير مشروطة (Unconditional Jump). فهي تعادل وظيفيًا الكتابة فوق مؤشر التعليمات (EIP) بالعنوان المحدد في المعامل.
start:
mov eax, 0
top:
inc eax
jmp top
القائمة 3.1: استخدام تعليمة JMP لتكوين حلقة لا نهائية بسيطة
توضح القائمة 3.1 حلقة لا نهائية بسيطة:
- تُهيّئ
EAXإلى القيمة 0. - تدخل الحلقة وتزيد
EAXبمقدار 1. - تقفز عائدةً إلى عنوان التسمية
top.
ولأنه لا يوجد شرط للخروج، تتكرر الحلقة إلى ما لا نهاية حتى يتم إنهاء البرنامج.
تمرين اختراق: jumper
اكتب ونفّذ تسلسل تعليمات من أجل:
- القفز إلى العنوان
401009h.- من العنوان
401009h، القفز إلى401002h*.- دفع القيمة
1111111hإلى المكدس.- القفز إلى تعليمة الدفع من الخطوة السابقة (هذا يخلق حلقة لا نهائية).
* لتجميع تعليمة في عنوان محدد (مثل
401009h):
- ضع مؤشرك على البايت في ذلك العنوان.
- أظهر نافذة المجمّع (Assembler Dialog).
الحل (اضغط للعرض)
org 401000h
jmp 401009h ; عند 401000h
; عند 401002h:
push 1111111h ; ادفع القيمة إلى المكدس
jmp 401002h ; حلقة لا نهائية
; عند 401009h:
jmp 401002h ; عودة إلى تعليمة الدفع
2. المراجع المتقاطعة (Cross References)
في IDA Pro، تتتبع المراجع المتقاطعة (Cross-References واختصارًا xrefs) العلاقات بين الأجزاء المختلفة من الملف الثنائي. فهي تعمل كروابط تصل بين الشيفرة والبيانات داخل التفكيك، وهي بالغة الأهمية لفهم تدفق البرنامج؛ إذ تتيح لك تتبع مسار تنفيذ البرنامج، ورؤية الدوال التي تستدعي غيرها، وتحديد أين تُعالَج بيانات معينة.
هناك نوعان من المراجع المتقاطعة: مراجع شيفرة متقاطعة و مراجع بيانات متقاطعة. تربط مراجع الشيفرة تعليمة بأخرى؛ فعلى سبيل المثال، تُنشَأ مراجع الشيفرة بواسطة استدعاءات الدوال، أو القفزات غير المشروطة، أو التفريعات المشروطة.
; CODE XREF: .text:00401007↓j
; .text:loc_401009↓j
القائمة 3.2: موقع شيفرة له مرجعان متقاطعان من الشيفرة
في القائمة 3.2، تشير التعليقات التوضيحية إلى أن هذا الموقع مُشار إليه بقفزة (j) من العنوان 0x401007. ويدل السهم المتجه للأسفل (↓) على أن التعليمة المُشيرة تقع في عنوان ذاكرة أعلى.
تربط مراجع البيانات المتقاطعة قطعة من البيانات (مثل متغير أو سلسلة نصية أو ثابت) بالشيفرة التي تصل إليها أو بالبيانات التي تشير إليها.
4020A0 aKernel32Dll db 'kernel32.dll',0 ; DATA XREF: .data:0040200C↑o
القائمة 3.3: سلسلة نصية لها مرجع متقاطع واحد من البيانات
في القائمة 3.3، تُشار السلسلة النصية بواسطة إزاحة (o) في العنوان 0x40200C. ويشير السهم المتجه للأعلى (↑) إلى أن المرجع قادم من عنوان ذاكرة أقل.
للتنقل باستخدام المراجع المتقاطعة:
- انقر نقرًا مزدوجًا على تعليق المرجع المتقاطع للقفز مباشرةً إلى ذلك الموقع.
- مرّر المؤشر فوق تعليق المرجع المتقاطع لإلقاء نظرة سريعة على الموقع المُشير.
- اضغط
Xعلى اسم دالة أو متغير لفتح قائمة بجميع المواقع التي تشير إليه.
3. تعليمة CALL
تُيسّر تعليمة CALL استدعاء الدوال. فهي تنفّذ عمليتين:
- دفع عنوان التعليمة التي تلي
CALLمباشرةً إلى المكدس. هذا عنوان الإرجاع يخبر المعالج أين يستأنف التنفيذ بعد اكتمال الدالة. - نقل التنفيذ إلى الوجهة المحددة.
401000 call my_function
401005 mov ecx, eax
القائمة 3.4: تعليمة CALL تدفع عنوان إرجاع إلى المكدس
في القائمة 3.4، يستدعي البرنامج الدالة my_function. عندما تُنفَّذ CALL عند العنوان 0x401000، تدفع عنوان (0x401005) تعليمة MOV التالية إلى المكدس قبل القفز. وعندما تنتهي my_function، يسحب المعالج عنوان الإرجاع من المكدس ويستأنف التنفيذ عند 0x401005.
push 401005h
jmp my_function
mov ecx, eax
القائمة 3.5: تسلسل تعليمات يعادل وظيفيًا التسلسل في القائمة 3.4
كما يظهر في القائمة 3.5، فإن تعليمة CALL تعادل وظيفيًا تعليمة PUSH تدفع عنوان الإرجاع يليها JMP.
401000 push 20
401002 push 10
401004 call calculate
401009 mov ecx, eax
القائمة 3.6: استدعاء calculate بوسيطين صحيحين

الشكل 3.1: حالة المكدس بعد تعليمة CALL في القائمة 3.6
في القائمة 3.6، يستدعي البرنامج الدالة calculate التي تقبل وسيطين صحيحين:
- يدفع البرنامج أولًا العددين
20و10إلى المكدس كوسائط. - ثم ينفّذ البرنامج تعليمة
CALLالتي تدفع عنوان الإرجاع0x401009إلى المكدس.
يوضح الشكل 3.1 حالة المكدس مباشرةً بعد الـ CALL. تحتوي قمة المكدس على عنوان الإرجاع، تليه الوسيطان 10 و 20.
4. تعليمة RET
تنقل تعليمة RET (الإرجاع — Return) التنفيذ عائدًا إلى الإجراء الاستدعائي (Caller) عن طريق سحب قيمة من قمة المكدس ووضعها في EIP.
تقبل التعليمة معاملًا اختياريًا (RET <bytes>). يزيد هذا الصنف ESP بعدد البايتات المحدد بعد سحب عنوان الإرجاع. وهذا التعديل ينظّف مساحة المكدس التي تشغلها وسائط الدالة أثناء انتقال التنفيذ عائدًا إلى المستدعي (آلية تُعرف باسم “تنظيف المستدعي” — Callee Cleanup، وستُناقش أكثر في قسم لاحق).
5. استدعاءات الدوال (Function Calls)

الشكل 3.2: تصور لدورة حياة استدعاء دالة بين الدالة المستدعية (Caller) والدالة المستدعاة (Callee)
يصوّر الشكل 3.2 دورة حياة استدعاء دالة، من الدالة المستدعية (الدالة التي تُجري الاستدعاء) إلى الدالة المستدعاة (الدالة الهدف) ثم العودة مجددًا:
- دفع الوسائط: إذا كانت الدالة المستدعاة تتوقع أي وسائط، تدفعها الدالة المستدعية إلى المكدس.
- نقل التنفيذ: تدفع تعليمة
CALLعنوان الإرجاع إلى المكدس، ثم تعدّلEIPلتقفز إلى بداية الدالة المستدعاة. - تنفيذ الدالة المستدعاة: عند الاكتمال، تنفّذ الدالة المستدعاة تعليمة
RET، التي تسحب عنوان الإرجاع المخزن من المكدس إلىEIPلنقل التنفيذ عائدًا إلى الدالة المستدعية. - تنظيف المكدس: يجب تحرير أي مساحة من المكدس شغلتها وسائط الدالة. وتحديد المسؤول عن التنظيف — هل هو المستدعي أم المستدعى — يعتمد على اصطلاح الاستدعاء (Calling Convention) الذي سُيناقش في القسم التالي.
6. تمرين اختراق: sleepy
لجميع تمارين الاختراق المتبقية: استخدم
io_blank.exe(io_blank.i64) الذي يستورد جميع وظائف المكتبة الضرورية للتمارين المتبقية.عند تنقيح تعليمات تتضمن استدعاءات دوال، لديك خياران للخطو الخطوة الواحدة:
- الخطوة فوق (Step Over) —
F8في IDA Pro:
- خطُّ تعليمة واحدة مع معاملة الاستدعاء كتعليمة واحدة؛ لا يدخل إلى داخل الدالة.
- استخدم “الخطوة فوق” لاستدعاءات وظائف المكتبة لتجنب تنقيح شيفرة موثّقة وخارج نطاق جلسة التنقيح.
- الخطوة إلى (Step Into) —
F7في IDA Pro:
- خطُّ تعليمة واحدة؛ لا معاملة خاصة لتعليمات
CALL، ويدخل إلى داخل الدوال.جمّع استدعاء دالة مستوردة بالصيغة:
call dword ptr [_<import>]
- مثال:
call dword ptr [_Sleep].اكتب ونفّذ تسلسل تعليمات من أجل:
- السكون (Sleep) لمدة ثانية واحدة:
Sleep(DWORD Milliseconds).- إنهاء العملية برمز إرجاع 0:
ExitProcess(DWORD ReturnCode).
الحل (اضغط للعرض)
push 1000 ; 1000 مللي ثانية = ثانية واحدة
call dword ptr [_Sleep]
push 0 ; رمز الإرجاع 0
call dword ptr [_ExitProcess]
7. السلاسل النصية (Strings)
ستتطلب بعض تمارين الاختراق التعامل مع السلاسل النصية، لذا نستعرض نوعي السلاسل الشائعين في برامج ويندوز: السلاسل الضيقة و السلاسل العريضة.
السلاسل الضيقة (Narrow Strings) تستخدم ترميزًا بحجم 8-بت (يُشار إليه غالبًا باسم ANSI أو ASCII) حيث يشغل كل محرف بايتًا واحدًا. وتنتهي هذه السلاسل ببايت NULL واحد (0x00). فيما يلي مثال للسلسلة Hello World! كسلسلة ضيقة:
48 65 6c 6c 6f 20 57 6f 72 6c 64 21 00 |Hello World!.|
السلاسل العريضة (Wide Strings) تستخدم ترميز Unicode (بنمط UTF-16LE) حيث يشغل كل محرف بايتين. يدعم هذا التنسيق محارف من كل لغات العالم تقريبًا، وهو تنسيق السلاسل الأصلي لنواة ويندوز. وتنتهي هذه السلاسل بـ NULL بحجم 16-بت (0x00 ، 0x00). فيما يلي مثال للسلسلة Hello World! كسلسلة عريضة:
48 00 65 00 6c 00 6c 00 6f 00 20 00 57 00 6f 00 |H.e.l.l.o. .W.o.|
72 00 6c 00 64 00 21 00 00 00 |r.l.d.!...|
تشير البرامج إلى السلاسل النصية عبر عنوان الذاكرة الذي تبدأ عنده بايتات السلسلة.
401000 push 401040h
401005 call printf
...
401040 48 65 6c 6c 6f 20 57 6f |Hello Wo|
401048 72 6c 64 21 00 00 00 00 |rld!....|
القائمة 3.7: استدعاء printf بوسيط سلسلة نصية
توضح القائمة 3.7 تمرير سلسلة نصية إلى printf. لطباعة السلسلة Hello World، يدفع البرنامج عنوان الذاكرة 0x401040 إلى المكدس. وعندما تُنفَّذ printf، تقرأ الذاكرة بدءًا من 0x401040 وتستمر في طباعة المحارف حتى تصادف محرف الإنهاء NULL.
تمرين اختراق: too_sleepy
لاستخدام
printf: يجب أن يكون وسيط السلسلة النصية موجودًا في الذاكرة.
- حرّر الذاكرة (
F2) لإنشاء سلسلة نصية، وادفع عنوان ذاكرتها إلىprintf.- بدلًا من ذلك، يمكنك استخدام سلسلة موجودة، مثل الموجودة في العنوان
402118h.اكتب ونفّذ تسلسل تعليمات من أجل:
- عند العنوان
401010h، أنشئ دالة تقوم بما يلي:
- طباعة رسالة إلى الشاشة باستخدام
printf.- إضافة 4 إلى سجل
ESP.- السكون لمدة ثانية واحدة.
- الإرجاع.
- عند العنوان
401000h، أنشئ شيفرة تقوم بما يلي:
- استدعاء الدالة عند
401010h.- استدعاء
ExitProcessبرمز إرجاع 0.
الحل (اضغط للعرض)
; عند 401000h:
call 401010h
push 0
call dword ptr [_ExitProcess]
; عند 401010h:
push 402118h ; عنوان السلسلة النصية
call dword ptr [_printf]
add esp, 4 ; تنظيف وسيط واحد (cdecl)
push 1000 ; ثانية واحدة
call dword ptr [_Sleep]
ret
خلاصة المقال
| المفهوم | النقاط الرئيسية |
|---|---|
JMP | قفزة غير مشروطة؛ تكافئ الكتابة فوق EIP بالعنوان الهدف |
| المراجع المتقاطعة (xrefs) | روابط بين الشيفرة والبيانات؛ اضغط X لعرض كل المراجع لموقع ما |
CALL | تدفع عنوان الإرجاع ثم تقفز للدالة الهدف |
RET | تسحب عنوان الإرجاع من المكدس إلى EIP؛ RET n تنظف الوسائط أيضاً |
| دورة استدعاء الدالة | دفع الوسائط ← CALL ← تنفيذ الدالة ← RET ← تنظيف المكدس |
| السلاسل النصية | ضيقة (ASCII، بايت لكل محرف، تنتهي بـ 0x00) وعريضة (UTF-16LE، بايتان، تنتهي بـ 00 00) |
| التنقيح | F8 للخطوة فوق الاستدعاءات، F7 للخطوة إليها |
المصدر الأصلي: Malware Analysis Crash Course — FLARE Learning Hub