P01NT
0x3 03/10

التحكم الأساسي في التدفق (Basic Flow Control)

JMP والمراجع المتقاطعة xrefs وCALL وRET ودورة استدعاء الدالة، وأنواع السلاسل النصية الضيقة والعريضة.

· 7 دقائق قراءة · 1441 كلمة
المحتويات

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

سنناقش في هذا المقال التحكم في تدفق البرامج


1. تعليمة JMP

تنفّذ تعليمة JMP قفزة غير مشروطة (Unconditional Jump). فهي تعادل وظيفيًا الكتابة فوق مؤشر التعليمات (EIP) بالعنوان المحدد في المعامل.

start:
  mov eax, 0
top:
  inc eax
  jmp top

القائمة 3.1: استخدام تعليمة JMP لتكوين حلقة لا نهائية بسيطة

توضح القائمة 3.1 حلقة لا نهائية بسيطة:

  1. تُهيّئ EAX إلى القيمة 0.
  2. تدخل الحلقة وتزيد EAX بمقدار 1.
  3. تقفز عائدةً إلى عنوان التسمية 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 استدعاء الدوال. فهي تنفّذ عمليتين:

  1. دفع عنوان التعليمة التي تلي CALL مباشرةً إلى المكدس. هذا عنوان الإرجاع يخبر المعالج أين يستأنف التنفيذ بعد اكتمال الدالة.
  2. نقل التنفيذ إلى الوجهة المحددة.
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.1: حالة المكدس بعد تعليمة CALL في القائمة 3.6

في القائمة 3.6، يستدعي البرنامج الدالة calculate التي تقبل وسيطين صحيحين:

  1. يدفع البرنامج أولًا العددين 20 و 10 إلى المكدس كوسائط.
  2. ثم ينفّذ البرنامج تعليمة CALL التي تدفع عنوان الإرجاع 0x401009 إلى المكدس.

يوضح الشكل 3.1 حالة المكدس مباشرةً بعد الـ CALL. تحتوي قمة المكدس على عنوان الإرجاع، تليه الوسيطان 10 و 20.


4. تعليمة RET

تنقل تعليمة RET (الإرجاع — Return) التنفيذ عائدًا إلى الإجراء الاستدعائي (Caller) عن طريق سحب قيمة من قمة المكدس ووضعها في EIP.

تقبل التعليمة معاملًا اختياريًا (RET <bytes>). يزيد هذا الصنف ESP بعدد البايتات المحدد بعد سحب عنوان الإرجاع. وهذا التعديل ينظّف مساحة المكدس التي تشغلها وسائط الدالة أثناء انتقال التنفيذ عائدًا إلى المستدعي (آلية تُعرف باسم “تنظيف المستدعي” — Callee Cleanup، وستُناقش أكثر في قسم لاحق).


5. استدعاءات الدوال (Function Calls)

الشكل 3.2: تصور لدورة حياة استدعاء دالة بين دالة المستدعي ودالتها المستدعاة

الشكل 3.2: تصور لدورة حياة استدعاء دالة بين الدالة المستدعية (Caller) والدالة المستدعاة (Callee)

يصوّر الشكل 3.2 دورة حياة استدعاء دالة، من الدالة المستدعية (الدالة التي تُجري الاستدعاء) إلى الدالة المستدعاة (الدالة الهدف) ثم العودة مجددًا:

  1. دفع الوسائط: إذا كانت الدالة المستدعاة تتوقع أي وسائط، تدفعها الدالة المستدعية إلى المكدس.
  2. نقل التنفيذ: تدفع تعليمة CALL عنوان الإرجاع إلى المكدس، ثم تعدّل EIP لتقفز إلى بداية الدالة المستدعاة.
  3. تنفيذ الدالة المستدعاة: عند الاكتمال، تنفّذ الدالة المستدعاة تعليمة RET، التي تسحب عنوان الإرجاع المخزن من المكدس إلى EIP لنقل التنفيذ عائدًا إلى الدالة المستدعية.
  4. تنظيف المكدس: يجب تحرير أي مساحة من المكدس شغلتها وسائط الدالة. وتحديد المسؤول عن التنظيف — هل هو المستدعي أم المستدعى — يعتمد على اصطلاح الاستدعاء (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