GammaCyber

Assembly Jumps ולולאות

Low-Level: C, Assembly ו-RE · 5–6 שעות

// סקירה

זרימת תוכנית ב-Assembly — Jumps, FLAGS ולולאות

בשיעור הקודם למדנו פקודות. עכשיו — הלב של כל תוכנית: איך CPU מקבל החלטות, קופץ, ויוצר לולאות. זה מה שמופיע בכל שאלת Dry Run ו-Reverse Engineering.

✓נושאי השיעור
  • FLAGS לעומק: ZF, SF, CF, OF — מתי דולקים ומה משתמש בהם
  • CMP ו-TEST — שתי פקודות ההחלטה
  • JMP — קפיצה ללא תנאי
  • משפחת Jcc: JE, JNE, JL, JG, JLE, JGE, JB, JA... Signed vs Unsigned
  • Patterns: if, while, for, do-while ב-Assembly
  • פקודת LOOP — ECX כמונה מובנה
  • CALL ו-RET — Stack Frame לעומק
  • NOP, INT3, LEA — פקודות מיוחדות
  • Dry Run מלא עם לולאה ותנאי — שאלות מיון
// פרק 1

FLAGS לעומק — זיכרון התוצאה

כל פקודת קפיצה מותנית מסתמכת על ביטים ב-FLAGS. ללא הבנה מוצקה של FLAGS, אי אפשר להבין קפיצות.

Flagשםמתי = 1Jcc שמשתמש
ZFZeroתוצאה = 0JE, JZ, JNE, JNZ
SFSignתוצאה < 0 (ביט גבוה=1)JL, JG, JS, JNS
CFCarryCarry/Borrow (unsigned)JB, JA, JNC, JC
OFOverflowSigned OverflowJO, JNO

טבלת עדכון FLAGS לפי פקודה

פקודהZFSFCFOFהערה
ADD✓✓✓✓משנה הכל
SUB✓✓✓✓משנה הכל
CMP✓✓✓✓כמו SUB ללא שמירת תוצאה
INC✓✓✗✓לא משנה CF — הבדל מ-ADD!
DEC✓✓✗✓לא משנה CF
MOV✗✗✗✗לא משנה FLAGS בכלל!
TEST✓✓00כמו AND ללא שמירה
AND/OR/XOR✓✓00מאפסות CF ו-OF

CMP ו-TEST — שתי פקודות ההחלטה

; CMP a, b → חישוב a-b, עדכן FLAGS, לא שומרת תוצאה mov eax, 5 cmp eax, 5 ; 5-5=0 → ZF=1, SF=0, CF=0 ; JE יקפוץ! (בודק ZF=1) cmp eax, 10 ; 5-10=-5 → ZF=0, SF=1, CF=1 ; JL יקפוץ! (בודק SF≠OF) ; ⚠️ CMP לא שינה EAX! הוא עדיין 5. ; TEST a, b → חישוב a AND b, עדכן FLAGS, לא שומרת ; שימוש 1: האם הרגיסטר הוא 0? test eax, eax ; EAX AND EAX = 0 רק אם EAX=0 jz .is_zero ; קפוץ אם EAX=0 ; שימוש 2: האם ביט ספציפי דלוק? test eax, 0x80 ; בדוק ביט 7 jnz .bit7_set ; קפוץ אם ביט 7 דלוק ; TEST EAX, EAX vs CMP EAX, 0: ; שניהם בודקים ==0, אבל TEST מהיר יותר! ; קומפיילרים תמיד מייצרים TEST לבדיקת if(x==0)
✏תרגול — FLAGS ו-CMP/TEST4 שאלות
שאלה 1.1
אחרי MOV EAX,15; MOV EBX,15; CMP EAX,EBX — מה ZF ומה EAX?
✓ תשובה: ב. CMP = SUB ללא שמירה. 15-15=0 → ZF=1. EAX לא משתנה — נשאר 15!
שאלה 1.2
מה מטרת TEST EAX, EAX?
✓ תשובה: ג. TEST = AND ללא שמירה. EAX AND EAX = EAX. אם EAX=0 → תוצאה=0 → ZF=1. EAX לא משתנה. קומפיילרים תמיד מייצרים זאת לבדיקת if(x==0).
שאלה 1.3
מה ZF ו-SF אחרי: mov eax,5; sub eax,5; dec eax; inc eax?
✓ תשובה: א. sub eax,5: 5-5=0 → ZF=1,SF=0. dec eax: 0-1=-1 → ZF=0,SF=1. inc eax: -1+1=0 → ZF=1,SF=0. מצב סופי: ZF=1, SF=0.
שאלה 1.4
מה ההבדל בין INC ל-ADD EAX,1?
✓ תשובה: ב. INC/DEC לא משנות CF. ADD/SUB משנות CF. בשאלות Dry Run שכוללות Carry — חשוב לדעת זאת!
// פרק 2

JMP ומשפחת Jcc — קפיצות

JMP = goto ב-Assembly. Jcc = קפיצה מותנית לפי FLAGS. הקומפיילר מתרגם כל if ולולאה ל-CMP+Jcc.

פקודהFlag שנבדקשקולה ל-CSigned/Unsigned
JE / JZZF=1==שניהם
JNE / JNZZF=0!=שניהם
JL / JNGESF≠OF<Signed
JLE / JNGZF=1 OR SF≠OF<=Signed
JG / JNLEZF=0 AND SF=OF>Signed
JGE / JNLSF=OF>=Signed
JB / JNAECF=1<Unsigned
JA / JNBECF=0 AND ZF=0>Unsigned
⚠️ Signed vs Unsigned — ההבדל הקריטי
int a = -1; int b = 5; if (a < b) → JL (כי -1 < 5 כ-Signed)
unsigned a = 0xFFFFFFFF; → 4,294,967,295 כ-Unsigned — גדול מ-5! לא JB
קומפיילר: int → JL/JG. unsigned int → JB/JA. בRE: JB/JA רומז unsigned.

Patterns: if ו-if-else

; ══ תבנית 1: if בלי else ══════════════════════ ; C: if (a == b) { do_something(); } cmp eax, ebx jne .skip ; אם a ≠ b → דלג על הגוף call do_something .skip: ; ⚠️ הקפיצה היא ה-OPPOSITE של התנאי! ; if (a==b) → JNE (קפוץ כשלא שווים → עוקף הגוף) ; ══ תבנית 2: if-else ══════════════════════════ ; C: if (a > b) { ecx=1; } else { ecx=-1; } cmp eax, ebx jle .else_part ; אם a ≤ b → קפוץ ל-else mov ecx, 1 ; then jmp .end_if ; דלג על else .else_part: mov ecx, -1 ; else .end_if: ; 🔍 Pattern לזיהוי if-else: ; CMP → Jcc(.else) → [then] → JMP(.end) → .else: [else] → .end:
✏תרגול — קפיצות ו-Patterns4 שאלות
שאלה 2.1
נתון: MOV EAX,1; MOV EBX,2; CMP EAX,EBX; JG label_1; JMP label_2; label_1: ADD EAX,10; label_2: ADD EAX,5 — מה EAX הסופי?
✓ תשובה: א) 6. CMP EAX,EBX: 1<2 → JG לא קופץ (1 לא גדול מ-2). JMP label_2 → מדלג על ADD EAX,10. ב-label_2: ADD EAX,5 → EAX=1+5=6.
שאלה 2.2
מדוע קריטי להבחין בין JG ל-JA?
✓ תשובה: ג. JG בודק SF ו-OF (Signed). JA בודק CF ו-ZF (Unsigned). קוד שמשתמש ב-JA על signed int — תוקף יכול לשלוח מספר שלילי שעוקף בדיקות (Integer Overflow Vulnerability).
שאלה 2.3 — Crackme
קוד: CALL check_password; CMP EAX,0x1337; JNE Access_Denied; MOV EBX,1. מה תנאי לגישת מנהל?
✓ תשובה: ב. CMP EAX,0x1337: אם שווה → ZF=1 → JNE לא קופץ → MOV EBX,1. בדיבאגר: שנה EAX ל-0x1337 לפני CMP, או שנה ZF ל-1 אחרי CMP.
שאלה 2.4
כיצד לממש: if (x != 5) { y=10; } else { y=20; } — x ב-EAX, y ב-EBX?
✓ תשובה: ב. התנאי הוא x!=5. הקפיצה לעקוף if-body = כשהתנאי שקרי = כשx==5 → JE else_b. אם x!=5 → לא קופץ → MOV EBX,10. אם x==5 → JE → MOV EBX,20.
// פרק 3

לולאות ב-Assembly — while, for, do-while

לולאות הן שילוב של Label, CMP/TEST, Jcc ו-JMP. כלל: קפיצה אחורה = לולאה. קפיצה קדימה = if.

while, for, do-while

; ══ while: בדיקה בראש ══════════════════════════ ; C: while (ecx > 0) { eax += 2; ecx--; } .while_start: cmp ecx, 0 jle .while_end ; ecx <= 0 → יצא add eax, 2 dec ecx jmp .while_start .while_end: ; ══ for: init + while + increment ══════════════ ; C: for(int i=0; i<10; i++) { sum += i; } mov ecx, 0 ; i=0 (init) mov eax, 0 ; sum=0 .for_start: cmp ecx, 10 jge .for_end ; i>=10 → יצא add eax, ecx ; sum += i inc ecx ; i++ (increment) jmp .for_start .for_end: ; EAX = 45 ; ══ do-while: גוף לפני בדיקה ══════════════════ ; C: do { eax *= 2; } while (eax < 100); .do_start: imul eax, 2 ; גוף (רץ לפחות פעם!) cmp eax, 100 jl .do_start ; ← קפיצה אחורה בסוף!

פקודת LOOP — ECX כמונה

; LOOP target → ECX--; אם ECX != 0 → קפוץ ; ⚠️ LOOP לא משנה FLAGS! mov ecx, 4 ; מונה mov eax, 1 factorial_loop: imul eax, ecx ; eax *= ecx loop factorial_loop ; ECX--; אם != 0 → חזור ; EAX = 1*4*3*2*1 = 24, ECX = 0
// PATTERN RECOGNITION — CHEAT SHEET
while: [label]: CMP → Jcc(exit) → [body] → JMP(label)
for: [init] → [label]: CMP → Jcc(exit) → [body] → [incr] → JMP(label)
do-while: [label]: [body] → CMP → Jcc(label) ← קפיצה אחורה בסוף!
LOOP: [label]: [body] → LOOP(label) ← ECX כמונה

כלל: קפיצה אחורה = לולאה | קפיצה קדימה = if
✏תרגול — לולאות4 שאלות
שאלה 3.1 — Dry Run
MOV EAX,1; MOV ECX,4; my_loop: ADD EAX,EAX; SUB ECX,1; JNZ my_loop — מה EAX הסופי?
✓ תשובה: ג) 16. כל סיבוב: ADD EAX,EAX → כפול ב-2. ECX: 4→3→2→1→0. סיבוב 1: EAX=2. סיבוב 2: 4. סיבוב 3: 8. סיבוב 4: 16, ECX=0 → JNZ לא קופץ. EAX=16=2^4.
שאלה 3.2 — While לאסמבלי
איזו פקודה חסרה? loop_start: CMP EAX,0; _____ loop_end; DEC EAX; JMP loop_start; loop_end: — ממש while(EAX>0)
✓ תשובה: ב) JLE. הלולאה ממשיכה כל עוד EAX>0. הקפיצה לעקוף את הגוף (ולצאת) = כש-EAX ≤ 0. CMP EAX,0; JLE loop_end.
שאלה 3.3 — Dry Run מלא
ECX=5: mov eax,0; .loop: test ecx,1; jz .even; add eax,ecx; .even: dec ecx; test ecx,ecx; jnz .loop — מה EAX?
✓ תשובה: ב) 9. TEST ECX,1 בודק אי-זוגי. ECX=5(אי-זוגי)→EAX+=5=5. ECX=4(זוגי)→skip. ECX=3(אי-זוגי)→EAX+=3=8. ECX=2(זוגי)→skip. ECX=1(אי-זוגי)→EAX+=1=9. ECX=0→יצאנו. סכום אי-זוגיים: 1+3+5=9.
שאלה 3.4 — LOOP ועצרת
MOV EAX,1; MOV ECX,4; loop: IMUL EAX,ECX; LOOP loop — מה EAX?
✓ תשובה: ג) 24. LOOP = ECX-- + JNZ. סיבוב 1: EAX=1×4=4, ECX=3. סיבוב 2: EAX=4×3=12, ECX=2. סיבוב 3: EAX=12×2=24, ECX=1. סיבוב 4: EAX=24×1=24, ECX=0 → LOOP עוצר. EAX=24 = 4!.
// פרק 4

CALL, RET ופקודות מיוחדות

CALL = PUSH כתובת חזרה + JMP. RET = POP + JMP. NOP ו-INT3 נפוצים ב-Exploit Development ו-Debugging.

CALL ו-RET — מה קורה ב-Stack

; CALL target → 1. PUSH EIP+N (כתובת חזרה) ; 2. JMP target ; RET → 1. POP EIP (שחזר כתובת חזרה) ; 2. JMP EIP main: push 7 ; arg2 push 3 ; arg1 (הפוך!) call my_add ; PUSH [כתובת חזרה]; JMP my_add add esp, 8 ; נקה 2 args * 4B (cdecl!) ; EAX = 10 (return value) my_add: push ebp mov ebp, esp ; הגדר Stack Frame mov eax, [ebp+8] ; arg1=3 [EBP+8] add eax, [ebp+12] ; arg2=7 [EBP+12] pop ebp ret ; POP כתובת חזרה → JMP
כתובת ב-Stackתוכןהסבר
[EBP+0]EBP ישןPUSH EBP שמר אותו
[EBP+4]כתובת חזרהCALL שמר אותה
[EBP+8]arg1ארגומנט ראשון
[EBP+12]arg2ארגומנט שני
[EBP-4]local var 1משתנה מקומי ראשון

NOP, INT3 ו-LEA

; NOP (0x90): No Operation — EIP += 1, שום דבר אחר ; שימושים: Alignment, NOP Sled לExploits, Patching ; 90 90 90 90 [SHELLCODE] = NOP Sled ; INT 3 (0xCC): Breakpoint Exception ; Debugger מחליף byte ב-0xCC → CPU מגיע → עצירה ; כשמגדירים Breakpoint ב-IDA/GDB — זה מה שקורה ; LEA dst, [expr] → dst = כתובת (לא תוכן!) mov eax, [ebx + 4] ; EAX = memory[EBX+4] ← קורא מזיכרון lea eax, [ebx + 4] ; EAX = EBX+4 ← חישוב כתובת! ; LEA לחישוב מהיר: lea eax, [ebx + ebx*4] ; EAX = EBX*5 (מהיר מ-IMUL!)
✏תרגול — CALL/RET ופקודות מיוחדות3 שאלות
שאלה 4.1
מה מכיל [EBP+4] בתוך פונקציה שנקראה עם CALL?
✓ תשובה: ג. מבנה Stack: [EBP+0]=EBP ישן, [EBP+4]=כתובת חזרה, [EBP+8]=arg1, [EBP+12]=arg2. Buffer Overflow = שכתוב כתובת החזרה ב-[EBP+4] לשינוי זרימת תוכנית!
שאלה 4.2
מה ההבדל בין mov eax, [ebx+4] ל-lea eax, [ebx+4]?
✓ תשובה: ג. MOV [ebx+4] = קרא ערך מ-RAM[EBX+4]. LEA [ebx+4] = שים את הכתובת EBX+4 ב-EAX — לא נוגע ב-RAM. שימושי לחישוב כתובות ולמכפלות מהירות.
שאלה 4.3
מה עושה קוד Opcode 0xCC שמוצא בזיכרון?
✓ תשובה: ג. 0xCC = INT 3. Debugger מחליף byte בכתובת Breakpoint ב-0xCC. כש-CPU מגיע → Exception → Debugger מתעורר. 0x90 = NOP.
// פרק 5

Reverse Engineering — זיהוי Patterns

ברגע שמזהים "זה if", "זה while", "זה factorial" — קריאת Assembly הופכת לתרגום של C. המטרה: לזהות מה פונקציה עושה מבלי קוד מקור.

ניתוח קטע: מה הפונקציה עושה?

; ניתחו: מה קטע ה-Assembly הזה עושה? mov eax, 1 mov ecx, 2 .start: cmp ecx, [ebp+8] ; [ebp+8] = ארגומנט n jg .done imul eax, ecx inc ecx jmp .start .done: ; EAX = תוצאה סופית
✓ פתרון הניתוח
EAX מתחיל ב-1, ECX מתחיל ב-2. הלולאה: כל עוד ECX ≤ n → EAX *= ECX; ECX++.
EAX = 1 × 2 × 3 × ... × n = n! (עצרת)
ב-C: int factorial(int n) { int r=1; for(int i=2; i<=n; i++) r*=i; return r; }
Pattern ב-Assemblyמה זה ב-Cסימן לזיהוי
CMP + Jcc(skip) + bodyif (cond) { body }קפיצה קדימה, לא JMP אחרי body
CMP + Jcc(else) + body + JMP(end) + elseif-elseשני labels, JMP בין גופים
label + CMP + Jcc(exit) + body + JMP(label)whileJMP אחורה בסוף
init + label + CMP + body + inc + JMPforinit לפני, inc בסוף
label + body + CMP + Jcc(label)do-whileהבדיקה אחרי הגוף
CMP + JA + JMP [table + reg*4]switch-caseJump Table
PUSH args (הפוך) + CALL + ADD ESP,Nfunction callPUSH לפני, ADD אחרי
✏תרגול — Reverse Engineering2 שאלות
שאלה 5.1 — Dry Run מלא
EAX=10, ECX=0: .lp: add ecx,eax; sub eax,3; cmp eax,0; jg .lp — מה ECX?
✓ תשובה: א) 22. סיבוב 1: ECX=10, EAX=7 (7>0 → חזור). סיבוב 2: ECX=17, EAX=4. סיבוב 3: ECX=21, EAX=1. סיבוב 4: ECX=22, EAX=-2 (לא >0 → יצאנו). ECX=10+7+4+1=22.
שאלה 5.2 — כתיבת Assembly
כתבו Assembly ל-max(a,b) — a ב-EAX, b ב-EBX, תוצאה ב-ECX. מה הנכון?
✓ תשובה: א. CMP EAX,EBX: אם EAX>EBX → JG .a_big → ECX=EAX. אחרת → ECX=EBX. JMP .end עוקף את ה-else. Pattern קלאסי של if-else.

סיכום — שיעור 27

✓מה למדנו בשיעור 27
  • FLAGS: ZF (=0), SF (שלילי), CF (Unsigned), OF (Signed Overflow)
  • MOV לא משנה FLAGS. INC/DEC לא משנות CF!
  • CMP = SUB ללא שמירה. TEST = AND ללא שמירה. TEST EAX,EAX = בדיקת ==0
  • Jcc: JE/JNE, JL/JG/JLE/JGE (Signed), JB/JA (Unsigned)
  • Pattern if: CMP + Jcc(הפוך) + body — קפיצה OPPOSITE של התנאי
  • Pattern while: label + CMP + Jcc(exit) + body + JMP(label)
  • Pattern for: init + label + CMP + body + increment + JMP
  • Pattern do-while: label + body + CMP + Jcc(label) — קפיצה אחורה!
  • LOOP: ECX-- + JNZ בפקודה אחת. ECX=מונה מובנה
  • CALL = PUSH כתובת חזרה + JMP. RET = POP + JMP
  • [EBP+4]=ret addr, [EBP+8]=arg1, [EBP+12]=arg2, [EBP-4]=local1
  • NOP=0x90 (NOP Sled לExploits), INT3=0xCC (Breakpoint)
  • LEA: מחשב כתובת (לא קורא זיכרון). Switch → Jump Table
לפני שיעור 28 (Stack Frames):
1. Dry Run עם if בתוך לולאה — כמו בשאלות מיון
2. כתבו Assembly ל-fibonacci(n)
3. חשבו: CALL → PUSH כתובת חזרה. מה קורה ב-Buffer Overflow?
4. שיעור 28: Stack Frames מלאים, cdecl vs stdcall
// תרגול נוסף

תרגול נוסף — Assembly: לולאות ותנאים

שאלות העשרה נוספות לחיזוק החומר של השיעור — באותו פורמט בחירה של הקורס.

✏תרגול נוסף2 שאלות
שאלה 6.1 [בינוני]
מה ערך EAX אחרי הרצת הקוד?
mov eax, 0
mov ecx, 5
.l: add eax, ecx
    dec ecx
    jnz .l
✓ תשובה נכונה: ב — add בכל סיבוב: EAX += ECX. ECX יורד 5→4→3→2→1, ואז 0 (jnz עוצר). סכום=5+4+3+2+1=15. jnz = 'קפוץ אם ZF=0'.
שאלה 6.2 [בינוני]
מה קוד ה-C המקביל?
cmp eax, 10
jge .skip
add eax, 5
.skip:
✓ תשובה נכונה: א — cmp+jge: 'אם x>=10 דלג'. הגוף (add 5) רץ רק כש-x<10. jge בודק את דגלי cmp. תרגום קפיצות מותנות ל-if הוא מיומנות RE ליבה.
// אתגר מעשי

אתגר מעשי — Reverse Engineering — Crackme ו-XOR

שיא השיעור: לרדת לרמת-המכונה — C, אסמבלי, היפוך. ההגשה נבדקת אוטומטית.

T4 · מתקדם550 נק'~40 דק'reverse
Crackme — מצא את ה-Serial
מסופק בינארי crackme. הרץ אותו — הוא מבקש serial. הפוך את הבינארי (objdump/Ghidra/gdb), הבן את בדיקת ה-check, ומצא את ה-serial (6 תווים) שגורם לו להדפיס 'Correct!' ולחשוף את ה-flag. המקור אינו מסופק.
דוגמאות
הרצת הבינארי + serial שגוי → Wrong.
הפתרון נשמר מקומית ומשותף עם עמוד „כתיבת קוד”.
פתרון-ייחוס Crackme — מצא את ה-Serial ›
# התנאי: (s[i] + (7*i+3)) & 0xFF == target[i] -> s[i] = (target[i] - (7*i+3)) & 0xFF target = [0x4a,0x3e,0x7e,0x85,0x53,0x7e] serial = ''.join(chr((target[i] - (7*i+3)) & 0xFF) for i in range(6)) print(serial) # G4mm4X -> flag{gamma_re_G4mm4X}
T3 · בינוני300 נק'~18 דק'analysis
פענוח XOR (מפתח יחיד)
מחרוזת הוצפנה ב-XOR עם מפתח בית-יחיד. ה-ciphertext (hex): 4c 0b 4b 4d 51 12 1a 45 04 5e 04 5a. ידוע שהטקסט הפתוח מתחיל ב-'flag{'. שחזר את המפתח ופענח — הגש את הטקסט הפתוח המלא.
דוגמאות
cipher[0] ^ 'f' → = המפתח
הפתרון נשמר מקומית ומשותף עם עמוד „כתיבת קוד”.
פתרון-ייחוס פענוח XOR (מפתח יחיד) ›
key = 0x4c ^ ord('f') = 0x2a. XOR כל בית ב-0x2a → flag{x0r_ez}
שאלות תרגול ותשובות (10)

שאלה 1 · מה קורה לדגלים ולרגיסטרים אחרי הקוד?

mov eax, 15
mov ebx, 15
cmp eax, ebx
  • א. ZF=1 ו-EAX נשאר 15 — CMP מחסרת רק כדי לעדכן דגלים
  • ב. ZF=1 ו-EAX מתאפס
  • ג. ZF=0 ו-EAX נשאר 15
  • ד. EAX נהיה 30
תשובה: א

CMP מבצעת חיסור "וירטואלי": התוצאה נזרקת ורק הדגלים מתעדכנים. שני ערכים שווים ⇒ תוצאה 0 ⇒ ZF=1.

שאלה 2 · למה משמשת הפקודה הבאה, ובמה היא שונה מ-CMP?

test eax, eax
  • א. היא מבצעת AND לוגי ובודקת אם EAX שווה לאפס — הדרך המהירה למימוש if (x == 0)
  • ב. היא משווה בין EAX לערך קבוע 1
  • ג. היא מאפסת את EAX
  • ד. היא זהה לחלוטין ל-CMP EAX, EAX
תשובה: א

🎯 דגש למבחן — TEST מול CMP: CMP מבוססת חיסור ומיועדת להשוואה בין שני ערכים (גדול/קטן/שווה). TEST מבוססת AND ומיועדת לבדיקת ביטים; test eax,eax נותנת 0 רק אם EAX אפס, כלומר ZF=1 ⇒ x==0. היא קצרה ומהירה יותר מ-cmp eax,0 ולכן זה האידיום שתראו בכל דיסאסמבלי.

שאלה 3 · מדוע חשוב להבחין בין JG ל-JA?

  • א. JG לערכים מסומנים (בודקת SF/OF) ו-JA לערכים לא-מסומנים (בודקת CF) — טעות תהפוך ‎-1 למספר ענק
  • ב. JG מהירה יותר
  • ג. JA עובדת רק בלולאות
  • ד. אין הבדל, רק שמות נרדפים
תשובה: א

0xFFFFFFFF הוא ‎-1 כמסומן, אבל 4,294,967,295 כלא-מסומן. בחירה שגויה בקפיצה היא באג אבטחה קלאסי בבדיקות אורך.

שאלה 4 · ניתוח Crackme — מתי תתקבל גישת מנהל?

call check_password
cmp  eax, 0x1337
jne  Access_Denied
mov  ebx, 1
  • א. כאשר הפונקציה מחזירה ב-EAX בדיוק את הערך 0x1337
  • ב. כאשר EAX שונה מ-0x1337
  • ג. תמיד — הקפיצה לא מתבצעת
  • ד. רק אם EBX כבר שווה 1
תשובה: א

JNE קופצת החוצה כשהערכים שונים; רק שוויון מדויק ממשיך לשורה הבאה. בפריצה טיפוסית משנים JNE ל-NOP או ל-JE כדי לעקוף את הבדיקה.

שאלה 5 · Dry Run — מה יהיה ערך EAX בסוף?

mov eax, 1
mov ecx, 4
.loop:
  add eax, eax
  sub ecx, 1
  jnz .loop
  • א. 16
  • ב. 8
  • ג. 4
  • ד. 32
תשובה: א

כל סיבוב מכפיל את EAX ב-2, וארבעה סיבובים נותנים 1·2⁴ = 16. SUB מעדכנת את ZF, ולכן JNZ ממשיכה עד ש-ECX מתאפס.

שאלה 6 · איזו פקודה חסרה כדי לקבל לולאת while (eax != 0)?

.loop_start:
  cmp eax, 0
  ____ .loop_end
  dec eax
  jmp .loop_start
.loop_end:
  • א. je
  • ב. jne
  • ג. jg
  • ד. jmp
תשובה: א

תנאי הכניסה ללולאה הוא "שונה מאפס", ולכן היציאה מתבצעת כשהוא כן שווה לאפס — JE. שימו לב שהתנאי בקוד מכונה תמיד הפוך מהתנאי בשפת C.

שאלה 7 · מה מסוכן בקוד הבא כאשר ECX מתחיל ב-0?

mov ecx, 0
.lp:
  ; ...
  loop .lp
  • א. LOOP מקטינה תחילה את ECX — 0 יגלוש ל-0xFFFFFFFF והלולאה תרוץ כ-4 מיליארד פעם
  • ב. הקוד לא יתקמפל
  • ג. הלולאה לא תרוץ אפילו פעם אחת
  • ד. ECX יישאר 0 והלולאה תעצור מיד
תשובה: א

🎯 דגש למבחן — LOOP ו-Off-by-One: הפקודה מבצעת DEC ECX ואז קופצת אם ECX≠0. משמעות הדבר: ECX=n נותן n חזרות, אבל ECX=0 גורם ל-Underflow ולולאה כמעט אינסופית. לכן בקוד אמיתי בודקים ECX לפני, או משתמשים ב-JECXZ.

שאלה 8 · מה מכיל [ebp+4] בתוך פונקציה שנקראה עם CALL?

  • א. כתובת החזרה שנדחפה למחסנית
  • ב. הפרמטר הראשון
  • ג. המשתנה המקומי הראשון
  • ד. ה-EBP של הפונקציה הקוראת
תשובה: א

‎[ebp] מחזיק את ה-EBP השמור, ‎[ebp+4] את כתובת החזרה ו-‎[ebp+8] את הפרמטר הראשון. דריסת ‎[ebp+4] היא בדיוק מה שקורה ב-Buffer Overflow.

שאלה 9 · איזה קוד C מקביל לקטע הבא?

cmp eax, 10
jge .skip
add eax, 5
.skip:
  • א. if (eax < 10) eax += 5;
  • ב. if (eax >= 10) eax += 5;
  • ג. while (eax < 10) eax += 5;
  • ד. eax = 10;
תשובה: א

הקפיצה מדלגת על הגוף כשהתנאי ההפוך מתקיים, ולכן הגוף רץ רק כאשר EAX קטן מ-10 — זהו if ללא else.

שאלה 10 · Dry Run — מה ערך ECX בסוף?

mov eax, 10
mov ecx, 0
.lp:
  add ecx, eax
  sub eax, 3
  cmp eax, 0
  jg  .lp
  • א. 22
  • ב. 30
  • ג. 25
  • ד. 10
תשובה: א

הסיבובים: EAX=10 (ECX=10) → 7 (17) → 4 (21) → 1 (22) → ‎-2 ואז JG לא קופצת. סה"כ 10+7+4+1 = 22. טיפ ל-Dry Run: נהלו טבלה של EAX/ECX והדגלים, ומחקו בקו במקום למחוק — כך אפשר לחזור אחורה ולאתר טעות.