GammaCyber

ארכיטקטורת מחשב: איך מחשב באמת עובד

יסודות המחשוב · 4–5 שעות

// סקירה

מה נלמד בשיעור הזה

שיעור זה הוא הבסיס לכל מה שיבוא אחריו. כל נושא בקורס — Assembly, ניתוח זיכרון, Buffer Overflow, ניתוח פרוטוקולים, CTF — נשען על ההבנה שתצבור כאן. לא נניח שאתה יודע כלום מעבר לכך שמחשב קיים.

✓נושאי השיעור
  • מבנה המחשב — ארכיטקטורת Von Neumann
  • מערכות ספירה — בינארי, הקסדצימלי, ואוקטלי
  • CPU ורגיסטרים — EAX, EIP, ESP ועוד
  • מחזור Fetch-Decode-Execute
  • היררכיית זיכרון — L1/L2/L3 Cache, RAM, SSD
  • פעולות ביטוויז — AND, OR, XOR, NOT, SHL, SHR
  • Endianness — Little Endian לעומת Big Endian
  • מספרים שליליים — Two's Complement
טיפ ללימוד: ברירת המחדל היא תצוגת תקציר — קרא קודם את עמוד השדרה, ופתח "הרחבה" רק כשרוצים את הפרטים המלאים. למעבר מהיר על הכל, החלף ל‑מלא למעלה.
// פרק 1

למה בכלל קיימים מחשבים?

בשנות ה-40, המילה "computer" הייתה תיאור של תפקיד — לא מכונה. אנשים ישבו כל היום ועשו חישובים בידיים. מתמטיקאים כמו אלן טיורינג הבינו שאפשר לבנות מכונה שתעשה זאת.

💡 הרעיון הבסיסי

מחשב הוא מכונה שמבצעת רצף של הוראות פשוטות, אחת אחרי השנייה. כל תוכנה — WhatsApp, משחק וידאו, מערכת נשק — היא בסופו של דבר רצף של פעולות בסיסיות מאוד.

הרחבה אנלוגיית השף ›

דמיין שף מקצועי במטבח מסעדה. השף (CPU) מקבל פתק עם רשימת הוראות מהמלצר (תוכנה). הוא קורא הוראה אחת בכל פעם, מבצע אותה, ועובר להבאה. המצרכים (נתונים) נמצאים על השיש (RAM) או במחסן מאחורה (דיסק). הכלים שבידיו ממש עכשיו (רגיסטרים) — המהירים ביותר, אבל מוגבלים.

אנלוגיה זו תלווה אותך לאורך כל הקורס.

✏ תרגול עצמי 1 שאלה
שאלה א׳.1
מה הגדרה הנכונה ביותר למחשב?
✓ תשובה נכונה: ב — מחשב אינו חכם. הוא מבצע הוראות בסיסיות מאוד, אחת אחרי השנייה, אבל עושה זאת מהר מאוד. כל תוכנה מורכבת — WhatsApp, AI, משחק — היא בסופו של דבר רצף כזה.
// פרק 2

ארכיטקטורת Von Neumann: המפה הגדולה

ב-1945 פרסם המתמטיקאי ג'ון פון נוימן מסמך שהגדיר את מבנה המחשב שבו אנחנו משתמשים עד היום: שמור הוראות ונתונים באותו זיכרון.

ארבעת המרכיבים

מרכיבשםתפקידאנלוגיה
CPUמעבדמבצע הוראות, מחשב, מקבל החלטותהשף
RAMזיכרוןשומר נתונים והוראות זמנית בזמן ריצההשיש במטבח
Inputקלטמקלדת, עכבר, רשתספק המצרכים
Outputפלטמסך, רמקולים, קובץהצלחת ללקוח
! Von Neumann Bottleneck ›

יש בעיה בארכיטקטורה: ה-CPU וה-Memory מחוברים דרך אותו Bus. ה-CPU מהיר בהרבה מהזיכרון — ורוב הזמן הוא ממתין. כשתלמד Assembly ותראה פקודות כמו MOV, זכור: מה שנמצא ברגיסטרים — מגיע בלי המתנה. מה שמגיע מהזיכרון — גורם ל-CPU להמתין עשרות עד מאות מחזורי שעון.

הפתרון: Cache — נדון בזה בפרק 6.
✏ תרגול עצמי 2 שאלות
שאלה א׳.2
Von Neumann Bottleneck נגרם בשל:
✓ תשובה נכונה: ב — ה-CPU עובד מהר, אבל חייב לחכות ל-Bus לכל הוראה ונתון. הפתרון: Cache.
שאלה א׳.3
מה היה החידוש של ארכיטקטורת Von Neumann לעומת מחשבים קודמים?
✓ תשובה נכונה: ג — לפני Von Neumann, "תוכניות" נכתבו לחומרה — היית צריך לשנות כבלים פיזיים. Von Neumann אמר: שמור את ההוראות בזיכרון כמו נתונים רגילים.
// פרק 3

מערכות ספירה: שפת המחשב

מחשבים לא מכירים מספרים כמונו. הם עובדים רק עם שני מצבים: יש חשמל ואין חשמל. כל מחשב מודרני בנוי מטרנזיסטורים — מתגים קטנים: פתוח או סגור, 0 או 1.

3.1 — בינארי (Binary)

מערכת ספירה בבסיס 2 — רק 0 ו-1. כל ספרה בינארית נקראת bit.

הרחבה טבלת ספירה וכוחות 2 ›
דצימליבינארידצימליבינארי
0000081000
1000191001
20010101010
40100151111
7011125511111111
חזקהערךהקסשימוש נפוץ
2⁰ = 110x01ביט יחיד, Flag בסיסי
2⁴ = 16160x10ספרה הקסדצימלית
2⁷ = 1281280x80ה-MSB (ביט הסימן) ב-byte
2⁸ = 2562560x100כמות ערכים ב-byte
2³² = 4G4,294,967,2960x100000000מרחב כתובות 32 ביט

המרה בינארי → דצימלי: כפל כל ביט בכוח 2 לפי מיקומו (מ-0 בצד ימין), ואחר כך חבר.

📝 דוגמה: 10110110 → דצימלי
1×2⁷ + 0×2⁶ + 1×2⁵ + 1×2⁴ + 0×2³ + 1×2² + 1×2¹ + 0×2⁰ = 128+32+16+4+2 = 182

המרה דצימלי → בינארי: חלק ב-2 שוב ושוב, רשום שאריות, קרא מלמטה למעלה.

3.2 — הקסדצימלי (Hex)

בינארי קשה לקריאה אנושית (32 ביטים ברצף). הפתרון: הקסדצימלי — בסיס 16 עם ספרות 0–9 ו-A–F. כל ספרה הקס = בדיוק 4 ביטים. 2 ספרות הקס = בייט אחד.

💡 זיהוי מספר הקס
מספרי Hex מתחילים ב-0x (למשל: 0xFF, 0x1A3F). ב-Assembly תראה כתובות זיכרון בהקס כל הזמן.
הרחבה טבלת המרה הקס ↔ בינארי ↔ דצימלי ›
הקסבינארידצימליהקסבינארידצימלי
000000810008
A101010F111115

ערכים חשובים לשנן:

ערך הקסדצימליבינארילמה זה חשוב
0x00000000000ערך NULL, מסיים strings ב-C
0x0F1500001111mask לחצי byte תחתון
0x416501000001ASCII: 'A'
0x8012810000000MSB דלוק — שלילי ב-signed
0xFF25511111111ערך מקסימלי ב-unsigned byte
! טעויות נפוצות — זהירות! ›

טעות #1: בלבול בין 0x10 ל-10. 0x10 = 16, לא 10!

טעות #2: שכחת ש-A=10. 0xA ≠ 1. 0xA = 10.

טעות #3: קיבוץ ביטים לא נכון — כשממירים בינארי להקס, מקבצים מימין לשמאל.

✏ תרגול עצמי 5 שאלות
שאלה ב׳.1 [קל]
מה הוא הערך הדצימלי של 0b1010?
✓ תשובה נכונה: א) 10 — 1×2³ + 0×2² + 1×2¹ + 0×2⁰ = 8+0+2+0 = 10.
שאלה ב׳.2 [קל]
מה הייצוג הנכון של המספר 15 בהקסדצימלי?
✓ תשובה נכונה: ג) 0xF — F=15. 0x15=21, 0xE=14, 0x10=16.
שאלה ב׳.3 [בינוני]
מה הוא 0x2A בבינארי?
✓ תשובה נכונה: א) 0010 1010 — 2=0010, A=1010 → 00101010.
שאלה ב׳.4 [בינוני]
מה הוא המספר 182 בבינארי?
✓ תשובה נכונה: ג) 10110110 — 182 = 128+32+16+4+2 = 10110110.
שאלה ז׳.1 [גאמא-סגנון]
מה הפלט של:
x = 0b10111001
print(x >> 3)
✓ תשובה נכונה: ג) 23 — 0b10111001 = 185. 185 >> 3 = 185÷8 = 23. בינארי: 10111001 >> 3 = 00010111 = 16+4+2+1 = 23.
// פרק 4

ה-CPU: המוח של המחשב

ה-CPU עושה דבר אחד בלבד — אבל עושה אותו מיליארדי פעמים בשנייה: קורא הוראה, מבצע אותה, עובר להבאה.

הרגיסטרים — הזיכרון הכי מהיר שקיים

הרגיסטרים הם כמות מוגבלת מאוד של תאי זיכרון שנמצאים בתוך ה-CPU עצמו. הגישה אליהם היא מיידית — אפס המתנה.

שם (32 ביט)שם (64 ביט)תפקיד
EAXRAXAccumulator — תוצאות חישובים, ערכי החזרה
EBXRBXBase — מצביע לבסיס מידע
ECXRCXCounter — מונה לולאות
EDXRDXData — נתונים לפעולות I/O
ESPRSPStack Pointer — ראש ה-Stack
EBPRBPBase Pointer — בסיס Stack Frame
EIPRIPInstruction Pointer — כתובת ההוראה הבאה
!EIP הוא הכי חשוב מכולם
  • EIP מחזיק את כתובת ההוראה הבאה שה-CPU יריץ
  • מי ששולט ב-EIP — שולט בתוכנית
  • זו השורה התחתונה של מאות חולשות סייבר (Buffer Overflow)
הרחבה חלקי רגיסטר — EAX, AX, AH, AL ›

רגיסטרי x86 מאורגנים בצורה היסטורית חשובה לראות ב-Assembly ובכלים כמו Ghidra:

EAX = רגיסטר מלא של 32 ביט AX = 16 הביטים הנמוכים של EAX AH = 8 ביטים גבוהים של AX (ביטים 8–15) AL = 8 ביטים נמוכים של AX (ביטים 0–7) לדוגמה: EAX = 0x12345678 EAX = 0x12345678 (32 ביט) AX = 0x5678 (16 ביט תחתון) AH = 0x56 (byte שני) AL = 0x78 (byte ראשון)
✏ תרגול עצמי 3 שאלות
שאלה ב׳.4 [קל]
איזה רגיסטר מחזיק את כתובת ההוראה הבאה לביצוע?
✓ תשובה נכונה: ב) EIP — EIP = Instruction Pointer. EAX=Accumulator, ESP=Stack Pointer, EBP=Base Pointer.
שאלה א׳.3 [קל]
מה שמו ותפקידו של הרגיסטר שמחזיק את כתובת ההוראה הבאה שה-CPU יריץ?
✓ תשובה נכונה: ד) EIP — בארכיטקטורה 64 ביט הוא נקרא RIP. בכל מחזור, ה-CPU שולח את כתובת ה-EIP לזיכרון כדי לשלוף הוראה.
שאלה ד׳1 — Dry Run (בסיסי)
מצב התחלתי: EAX = 5, EBX = 3. מה ערך EAX אחרי הוראות אלו?
ADD EAX, EBX ; EAX = EAX + EBX MOV ECX, EAX ; ECX = EAX SUB ECX, 2 ; ECX = ECX - 2 IMUL EAX, ECX ; EAX = EAX × ECX
✓ תשובה נכונה: ג) 48 — ADD: EAX=8. MOV: ECX=8. SUB: ECX=6. IMUL: EAX=8×6=48.
// פרק 5

מחזור Fetch-Decode-Execute

כל מה שמחשב עושה מבוצע דרך אותו מחזור שחוזר על עצמו שוב ושוב: Fetch → Decode → Execute → עדכון EIP → Fetch → ...

שלב 1 — Fetch (שליפה)

ה-CPU שולף את ההוראה הבאה מהזיכרון לפי הכתובת ב-EIP.

שלב 2 — Decode (פענוח)

ה-Control Unit מפענח: מה הפקודה? מה הנתונים שהיא צריכה?

שלב 3 — Execute (ביצוע)

ה-ALU מבצע את הפעולה. התוצאה נשמרת ברגיסטר היעד.

שלב 4 — עדכון EIP

EIP מתעדכן לכתובת ההוראה הבאה (EIP = EIP + גודל ההוראה). אם הייתה קפיצה, EIP מקבל את כתובת הקפיצה.

הרחבה דוגמה מלאה של מחזור אחד ›
📝 הוראה: ADD EAX, 5 | מצב: EAX=3, EIP=0x00401010

FETCH: CPU שולח 0x00401010 לזיכרון → מקבל: 83 C0 05

DECODE: 83=ADD עם נתון מיידי, C0=EAX, 05=הנתון 5

EXECUTE: ALU: 3+5=8 → שמור 8 ב-EAX

עדכון: EIP = 0x00401010 + 3 = 0x00401013

מצב סופי: EAX=8, EIP=0x00401013 ✓

✓לזכור
  • מה שנמצא ברגיסטרים — מגיע בלי המתנה
  • מה שמגיע מהזיכרון — גורם ל-CPU להמתין
  • שינוי EIP = שינוי זרימת הביצוע
✏ תרגול עצמי 2 שאלות
שאלה א׳.4 [קל]
מה שלושת שלבי מחזור הריצה של ה-CPU (בסדר נכון)?
✓ תשובה נכונה: ב) Fetch → Decode → Execute — שלב אחרי שלב, שוב ושוב, מיליארדי פעמים בשנייה.
שאלה ד׳2 — עם קפיצה מותנית
מצב: EAX = 10, EBX = 10. האם מתבצעת קפיצה ל-0x2020?
CMP EAX, EBX ; משווה EAX ו-EBX JE 0x2020 ; קופץ אם שווים MOV EAX, 0 ADD EAX, 1 ... 0x2020: ADD EAX, 100
✓ תשובה נכונה: א) — CMP EAX,EBX: EAX=EBX=10 → Zero Flag=1. JE קופצת ל-0x2020. ADD EAX,100: EAX=10+100=110.
// פרק 6

היררכיית הזיכרון

אין "זיכרון מושלם". הפיזיקה מכתיבה tradeoff ברור: מהיר = קטן ויקר, איטי = גדול וזול. הפתרון: פירמידה של זיכרונות.

רמהמהירותגודל טיפוסיאנלוגיה למטבח
רגיסטרים~0.3 ns< 1 KBהידיים
L1 Cache~1 ns32–64 KBפינת הכיריים
L2 Cache~3-10 ns256 KB–1 MBהשיש
L3 Cache~10-40 ns4–32 MBהמדף מעל השיש
RAM~50-100 ns4–64 GBהשולחן הגדול
SSD~0.1 ms256 GB–4 TBהמדפים
HDD~10 ms1–16 TBהמחסן
הרחבה Cache Hit vs Cache Miss ›

כש-CPU צריך נתון, הוא בודק תחילה ב-L1. אם מצא — Cache Hit. אם לא — בודק ב-L2, L3, RAM. Cache Miss (לL3 ו-RAM) הוא יקר מאוד — ה-CPU ממתין מאות מחזורי שעון.

עיקרון המיקום (Locality Principle):

  • מיקום זמני: אם גשת לנתון עכשיו — סביר שתגיע אליו שוב בקרוב
  • מיקום מרחבי: אם גשת לכתובת X — סביר שתגש ל-X+1, X+2 בקרוב
✏ תרגול עצמי 3 שאלות
שאלה ב׳.5 [קל]
מי מהבאים הוא הזיכרון המהיר ביותר?
✓ תשובה נכונה: א) L1 Cache — L1≈1ns, RAM≈50-100ns, L3≈10-40ns, SSD≈0.1ms. המיון: Registers > L1 > L2 > L3 > RAM > SSD.
שאלה א׳.5 [בינוני]
מה ההבדל בין Cache ל-RAM?
✓ תשובה נכונה: א) — Cache קיים כי ה-RAM איטי מדי. ה-CPU מנסה לשמור נתונים שישמשו בקרוב ב-Cache. שניהם נדיפים (מתאפסים בכיבוי).
שאלה ב׳.12 [בינוני]
מדוע תוכנית שעוברת על מערך גדול איטית יותר מאותה תוכנית על מערך קטן?
✓ תשובה נכונה: ב) — Cache Miss = המתנה ל-RAM (50-100ns לעומת 1ns ל-L1). מערך קטן → נכנס ל-L1/L2 → Cache Hits. מערך גדול → Cache Misses × מיליוני גישות = פגיעה ענקית.
// פרק 7

פעולות ביטוויז (Bitwise Operations)

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

AND — כיבוי ביטים

תוצאה 1 רק אם שני הביטים הם 1. שימוש: Masking, בדיקת ביטים.

📝 דוגמה: 0b11110000 AND 0b10101010
  11110000
& 10101010
──────────
  10100000  → ה-AND "כיבה" את הביטים שלא רצינו

OR — הדלקת ביטים

תוצאה 1 אם לפחות אחד מהביטים הוא 1. שימוש: הדלקת ביטים, שילוב דגלים.

XOR — הפיכה ↔ הצפנה

תוצאה 1 אם הביטים שונים. תכונה מיוחדת: A XOR K XOR K = A — ניתן לבטל על ידי XOR שוב.

📝 הצפנת XOR
הצפן  = data XOR key
פיצוח = cipher XOR key = data ← XOR הפיכה!

NOT — היפוך

הופך כל ביט: 1→0 ו-0→1. שים לב: A + NOT(A) = 255 תמיד (לבייט אחד).

Shift — הזזה

<< (SHL): מזיז שמאלה = כפל ב-2ⁿ. >> (SHR): מזיז ימינה = חלוקה ב-2ⁿ.

✓סיכום Bitwise
  • AND → כיבוי ביטים, בדיקת ביטים ספציפיים
  • OR → הדלקת ביטים, שילוב דגלים
  • XOR → שינוי ביטים, הצפנה בסיסית, swap חכם
  • NOT → היפוך, Two's complement
  • SHL → כפל מהיר ב-2ⁿ
  • SHR → חלוקה מהירה ב-2ⁿ
✏ תרגול עצמי 6 שאלות
שאלה ב׳.8 [בינוני]
מה תוצאת 0b11001100 AND 0b10101010?
✓ תשובה נכונה: ב) 0b10001000 — AND ביט-ביט: 11001100 AND 10101010 = 10001000. תוצאה 1 רק כשגם א וגם ב = 1.
שאלה א׳.7 [בינוני]
מה תוצאת הפעולה: 0b10101010 XOR 0b10101010? נמק.
✓ תשובה נכונה: ב) 0 — A XOR A = 0 תמיד. זו תכונה מרכזית שמשמשת בהצפנה: XOR פעמיים עם אותו מפתח = מחזיר למקור.
שאלה ב׳.11 [בינוני]
מדוע XOR שימושית להצפנה פשוטה?
✓ תשובה נכונה: ד) — XOR הפיכה: הצפן = data XOR key, פיצוח = cipher XOR key = data. א' נכון אבל לא הסיבה לשימוש בהצפנה.
שאלה ב׳.13 [בינוני]
מה תוצאת הפעולה 0b00000101 << 3?
✓ תשובה נכונה: ג) 0b00101000 — 5 << 3 = 5×2³ = 40 = 0b00101000. Shift Left ב-n = כפול ב-2ⁿ.
שאלה ז׳.2 [גאמא-סגנון]
מה הפלט של:
a = 0x1A
b = 0x0F
print(a & b)
✓ תשובה נכונה: ד) 10 — 0x1A = 0001 1010. 0x0F = 0000 1111. AND: 0000 1010 = 10.
שאלה ז׳.4 [גאמא-סגנון]
מה הפלט של:
x = 0b11110000
x = x ^ 0b10101010
print(bin(x))
✓ תשובה נכונה: ד) 0b01011010 — 11110000 XOR 10101010: ביט-ביט: 0,1,0,1,1,0,1,0 = 01011010.
🎯 דגש למבחן
AND עם מסכה = בידוד ביטים, OR = הדלקה, XOR = היפוך (וגם השוואה: a^a=0), הזזה שמאלה ב-n = כפל ב-2 בחזקת n. שאלות דגלים (Flags) במבחן כמעט תמיד נפתרות עם AND למסכה.
// פרק 8

Big Endian ו-Little Endian

כשמאחסנים מספר שגדול מבייט אחד בזיכרון, עולה שאלה: באיזה סדר שומרים את הבייטים? התשובה תלויה בארכיטקטורה.

לדוגמה, המספר 0x12345678 הוא 4 בייטים בכתובות 0x1000–0x1003:

שיטה0x10000x10010x10020x1003נפוץ ב
Big Endian0x120x340x560x78פרוטוקולי רשת TCP/IP
Little Endian0x780x560x340x12Intel x86, ARM, AMD
✓כלל אצבע לזכור
  • Intel x86 = Little Endian (LSB בכתובת הנמוכה)
  • פרוטוקולי רשת = Big Endian ("Network Byte Order")
  • כשמנתחים dump זיכרון ב-GDB — הערכים נראים "הפוך"
הרחבה Little Endian בפועל — GDB דוגמה ›
📝 int = 0x12345678 בכתובת 0xFFFF1000
(gdb) x/4xb 0xFFFF1000
0xFFFF1000: 0x78  0x56  0x34  0x12

נראה "הפוך" — אבל זה תקין! זה Little Endian בפעולה.

✏ תרגול עצמי 2 שאלות
שאלה ב׳.9 [בינוני]
מחשב Intel x86. המספר 0x12345678 נשמר בכתובת 0x1000. מה הבייט בכתובת 0x1000?
✓ תשובה נכונה: ג) 0x78 — Little Endian = LSB קודם. 0x12345678: LSB=0x78. תשובה א' = Big Endian (שגוי לIntel).
שאלה ו׳5 — Hex Dump
כתובת 0x1010 מכילה: 41 41 41 41 (0x41='A' ב-ASCII). מה ניתן להסיק בהקשר של Buffer Overflow?
✓ תשובה נכונה: א) — 0x41414141 ב-EIP = pattern קלאסי של ניסיון ניצול. כשרואים EIP=0x41414141 ב-crash — זהו ניסיון Buffer Overflow!
🎯 דגש למבחן
x86 הוא Little Endian: הבית הנמוך נשמר ראשון. הערך 0x12345678 יופיע בזיכרון כ-78 56 34 12. בפרוטוקולי רשת ההיפך (Big Endian, Network Order) — ומכאן פונקציות htons/htonl.
// פרק 9

מספרים שליליים: Two's Complement

כיצד מייצגים מספר שלילי ב-0 ו-1? הפתרון: Two's Complement — NOT(המספר) + 1.

📝 ייצוג -3 ב-8 ביטים
+3 = 0000 0011
NOT  = 1111 1100
+1   = 1111 1101  ← זהו -3!

בדיקה: 3 + (-3) = 0?
0000 0011 + 1111 1101 = 1 0000 0000 → 0 ✓
בינאריUnsigned (0–255)Signed (-128–127)
0000 000000
0111 1111127127
1000 0000128-128
1111 1111255-1
! Integer Overflow — חולשת אבטחה קריטית ›
unsigned char x = 255; x = x + 1; // x = 0! (Overflow) signed char x = 127; x = x + 1; // x = -128! (Overflow לשלילי)

Integer Overflow הוא מקור לבאגי אבטחה קריטיים. תוכנית שמחשבת "גודל buffer = a+b" ול-a+b יש overflow → buffer קטן מדי → Buffer Overflow.

✏ תרגול עצמי 4 שאלות
שאלה ב׳.10 [בינוני]
מה ייצוג Two's Complement של -1 ב-8 ביטים?
✓ תשובה נכונה: א) 11111111 — NOT(00000001)+1 = 11111110+1 = 11111111 = 0xFF. בדיקה: -1+1=0 ✓
שאלה ב׳.15 [בינוני]
מה ערכו של 0x80 כ-signed byte?
✓ תשובה נכונה: ב) -128 — 0x80 = 10000000₂. MSB=1 → שלילי. -128 הוא הערך השלילי המינימלי ב-byte. תשובה א' = unsigned.
שאלה א׳.10 [בינוני]
מה ההבדל בין signed ל-unsigned integer? מה ערך 0xFF כ-signed byte?
✓ תשובה נכונה: ב) — Signed: MSB=1 → שלילי. 0xFF = 11111111: unsigned=255, signed=-1 (Two's Complement).
שאלה ז׳.10 [גאמא-סגנון]
תוכנית C מגדירה: unsigned char x = 255; x = x + 1; באיזה ערך שמור x?
✓ תשובה נכונה: א) 0 — 255+1=256=0x100. unsigned char הוא 8 ביטים. 0x100 → carry נופל. x=0. זהו Unsigned Integer Overflow — קריטי לסייבר!
🎯 דגש למבחן
מספר שלילי מיוצג כהיפוך כל הביטים ועוד 1. הביט העליון הוא ביט הסימן: ב-8 ביט הטווח הוא 128- עד 127. חיסור במעבד מתבצע כחיבור של המשלים ל-2 — ולכן אין פקודת חיסור נפרדת ברמת החומרה. במבחן: זהו 0xFF כ-1- בטיפוס signed char, ו-255 בטיפוס unsigned.
// תרגול נוסף

תרגול נוסף — בינארי, Hex ו-Endianness

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

✏תרגול נוסף3 שאלות
שאלה ט׳.1 [קל]
מה הערך העשרוני של 0xAB?
✓ תשובה נכונה: א — 0xAB: A=10, B=11. הערך = 10×16 + 11 = 160 + 11 = 171. קיצור: 0xAB = 0xA0 + 0xB = 160 + 11.
שאלה ט׳.2 [בינוני]
המספר 0x0102 מאוחסן ב-Little Endian. מה סדר הבייטים בזיכרון?
✓ תשובה נכונה: ג — Little Endian = הבייט הפחות משמעותי ראשון. 0x0102: LSB=0x02, MSB=0x01, ובזיכרון: 02 01. רשתות TCP/IP משתמשות ב-Big Endian (01 02).
שאלה ט׳.3 [בינוני]
מה תוצאת 0xFF AND 0x0F?
✓ תשובה נכונה: א — 11111111 AND 00001111 = 00001111 = 0x0F. שימוש נפוץ: Masking — חילוץ ה-nibble הנמוך של בית.
// אתגר מעשי

אתגר מעשי — ארכיטקטורה וייצוג בינארי

שיא השיעור: לא לזהות תשובה — לבנות ולתקן. פתרו את משימת החובה, ואז את האתגר שמעליה. הגשה נבדקת אוטומטית מול מקרי-קצה נסתרים.

T1 · מתחיל 100 נק' ~8 דק' algorithmic
משקל האמינג — ספירת ביטים דלוקים
כל מספר חי בזיכרון כרצף ביטים. נתון מספר שלם אי-שלילי n. החזר את מספר הביטים הדלוקים (1) בייצוגו הבינארי — 'משקל האמינג'. פתרון יעיל אינו סורק את כל 64 הסיביות אלא מדלג ישירות מביט דלוק אחד לבא אחריו.
דוגמאות
count_set_bits(0) → 0
count_set_bits(7) → 3
count_set_bits(8) → 1
הפתרון נשמר מקומית ומשותף עם עמוד „כתיבת קוד”.
פתרון-ייחוס משקל האמינג — ספירת ביטים דלוקים ›
def count_set_bits(n: int) -> int: c = 0 while n: n &= n - 1 # מכבה את הביט הדלוק הנמוך ביותר c += 1 return c
T2 · בסיסי 200 נק' ~20 דק' algorithmic
מחבר בינארי Ripple-Carry (O(n))
בנה את פעולת החיבור של ה-ALU במו ידיך. נתונות שתי מחרוזות בינאריות a ו-b (תווי '0'/'1' בלבד, אורכים שרירותיים ואולי שונים, אפסים מובילים אפשריים). החזר את סכומן כמחרוזת בינארית ללא אפסים מובילים (פרט ל-"0" עצמו). חובה O(n): קלט של מיליון ביטים חייב לרוץ בזמן. בנייה תמימה שמשרשרת תו בראש המחרוזת בכל צעד היא O(n²) ותיפסל ב-timeout.
דוגמאות
add_binary('1','1') → '10'
add_binary('101','11') → '1000'
add_binary('0','0') → '0'
הפתרון נשמר מקומית ומשותף עם עמוד „כתיבת קוד”.
פתרון-ייחוס מחבר בינארי Ripple-Carry (O(n)) ›
def add_binary(a: str, b: str) -> str: i, j, carry, out = len(a) - 1, len(b) - 1, 0, [] while i >= 0 or j >= 0 or carry: s = carry if i >= 0: s += a[i] == '1'; i -= 1 if j >= 0: s += b[j] == '1'; j -= 1 out.append('1' if (s & 1) else '0') carry = s >> 1 out.reverse() # append+reverse = O(n) (לא שרשור מלפנים!) res = ''.join(out).lstrip('0') return res if res else '0'
// סיכום

כל מה שלמדנו: כיצד מופיע בגאמא?

הגענו לסוף השיעור. בואו נסגור מעגל ונראה איך כל נושא מתחבר ישירות לשלבי המיון.

נושאשלב מיוןכיצד מופיע
בינארי ↔ הקסשאלון + כנס'כמה הוא 0x7F?', 'כמה ביטים צריך ל-1000?'
רגיסטרי CPUמבחן Assemblyכל שאלת Assembly: EAX, EIP, ESP, EBP
Fetch-Decode-ExecuteAssembly + Black Boxהבנת זרימת תוכנית, מה EIP מחזיק
היררכיית זיכרוןמבחן C + AssemblyStack/Heap, עלות גישה לזיכרון, Buffer Overflow
BitwiseAssembly + פרוטוקול + CTFTCP flags, הצפנות XOR, masks ב-Assembly
Little EndianAssembly + Wireshark + CTFקריאת dump זיכרון, ניתוח packets
Two's ComplementAssembly + מבחן Csigned vs unsigned, overflow bugs, -1 = 0xFFFFFFFF
✓מה למדנו בשיעור 1
  • מחשב = מכונה לביצוע רצף הוראות. ארכיטקטורת Von Neumann = CPU + זיכרון + I/O
  • בינארי = בסיס 2. הקסדצימלי = בסיס 16, 4 ביטים לספרה
  • CPU = Control Unit + ALU + Registers. EIP = כתובת ההוראה הבאה
  • Fetch → Decode → Execute → עדכון EIP → חזור
  • היררכיה: Registers > L1 > L2 > L3 > RAM > SSD > HDD
  • Bitwise: AND=כיבוי, OR=הדלקה, XOR=הפיכה/הצפנה, SHL/SHR=כפל/חלוקה
  • Little Endian (Intel): LSB בכתובת הנמוכה. Big Endian (רשת): MSB קודם
  • Two's Complement: -1 = 0xFF, -128 = 0x80 (בבייט)
🎯 השיעור הבא
שיעור 2 — מערכות הפעלה: Kernel, תהליכים ומרחבי זיכרון
✏ תרגול סיום — שאלות אינטגרציה 3 שאלות
שאלה ב׳.18 [קשה]
פקודת ADD EAX, 5 מבוצעת. EAX = 0xFFFFFFFB לפני. מה ערך EAX אחרי?
✓ תשובה נכונה: א) 0x00000000 — 0xFFFFFFFB = -5 (signed). -5+5=0. ב-32 ביטים: carry נופל מחוץ לטווח. תוצאה = 0.
שאלה ב׳.16 [קשה]
מהי תוצאת השרשרת: ((0xFF AND 0x0F) << 2) OR 0b10000000?
✓ תשובה נכונה: א) 0b10111100 — שלב 1: 0xFF AND 0x0F = 0x0F = 0b00001111. שלב 2: <<2 = 0b00111100. שלב 3: OR 0b10000000 = 0b10111100.
שאלה ח׳1 — ניתוח עמוק
מדוע שינוי ה-EIP בזמן ריצה מהווה חולשת אבטחה קריטית?
✓ תשובה נכונה: ג) — Buffer Overflow דורס את ה-Return Address על ה-Stack. כשהפונקציה מסיימת, ה-CPU "חוזר" לכתובת שבחר התוקף — ומריץ את קודו.
✓חוקי ברזל של השיעור
  • בית = 8 ביט. שני תווי Hex = בית אחד בדיוק
  • המעבד לא "מבין" משמעות — הפרשנות (signed/unsigned) היא של המתכנת
  • 0xFF = 255 ב-unsigned, ו-1- ב-signed (משלים ל-2)
  • x86 = Little Endian; רשת = Big Endian. תמיד לוודא מי הצד
  • XOR עם עצמו מאפס — כלי הבסיס של הצפנה פשוטה ושל ניקוי רגיסטר
  • הזזה שמאלה/ימינה = כפל/חילוק בחזקות 2, וזול בהרבה מפעולת כפל
  • מחזור Fetch-Decode-Execute הוא הבסיס לכל ניתוח Assembly בהמשך
שאלות תרגול ותשובות (7)

שאלה 1 · לפי ארכיטקטורת Von Neumann, היכן נשמרות הפקודות של התוכנית?

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

החידוש של Von Neumann: קוד ונתונים חיים באותו זיכרון. מכאן גם החולשה — אם תוקף מזריק נתונים למקום שממנו המעבד קורא פקודות, הנתונים יורצו כקוד. זהו הבסיס ל-Buffer Overflow.

שאלה 2 · מהו סדר השלבים במחזור הפעולה של המעבד?

  • א. Execute → Fetch → Decode
  • ב. Fetch → Decode → Execute
  • ג. Decode → Execute → Fetch
  • ד. Fetch → Execute → Decode
תשובה: ב

שליפה (Fetch) של הפקודה מהכתובת שב-PC, פענוח (Decode) לזיהוי הפעולה והאופרנדים, ולבסוף ביצוע (Execute). ה-PC מתקדם אוטומטית — וקפיצה היא בסך הכול כתיבה ל-PC.

שאלה 3 · מה מייצג הרגיסטר PC (Program Counter)?

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

ה-PC (או RIP ב-64 ביט) מחזיק את כתובת הפקודה הבאה. שליטה ב-PC = שליטה בזרימת התוכנית, ולכן כל מטרת ניצול חולשה קלאסי היא לדרוס אותו.

שאלה 4 · מדוע קיימת היררכיית זיכרון (רגיסטרים → Cache → RAM → דיסק)?

  • א. כדי לאזן בין מהירות לעלות ולנפח
  • ב. כדי להצפין את הנתונים בשכבות
  • ג. כדי לאפשר ריצת כמה מערכות הפעלה
  • ד. כדי למנוע גישה של תוכניות לזיכרון
תשובה: א

ככל שהזיכרון מהיר יותר הוא יקר וקטן יותר. רגיסטר נגיש תוך מחזור שעון בודד, RAM עשרות עד מאות מחזורים, ודיסק פי אלפים. ההיררכיה מאפשרת מהירות ממוצעת גבוהה בעלות סבירה.

שאלה 5 · מה מבצעת הפעולה x & 0x0F?

  • א. מאפסת את הערך כולו
  • ב. משאירה רק את ארבעת הביטים התחתונים
  • ג. הופכת את כל הביטים
  • ד. מכפילה את הערך בשש עשרה
תשובה: ב

המסכה 0x0F היא 00001111. פעולת AND מאפסת כל ביט שמולו 0 ומשאירה את מה שמולו 1 — ולכן זו הדרך הסטנדרטית לבודד שדה או דגל מתוך בית.

שאלה 6 · הערך 0x12345678 נכתב לזיכרון במערכת Little Endian. מה סדר הבתים בפועל?

  • א. 12 34 56 78
  • ב. 78 56 34 12
  • ג. 34 12 78 56
  • ד. 56 78 12 34
תשובה: ב

ב-Little Endian (x86) הבית הנמוך ביותר נשמר בכתובת הנמוכה ביותר, ולכן הסדר מתהפך. בפרוטוקולי רשת משתמשים ב-Big Endian — ולכן צריך המרה עם htons/htonl.

שאלה 7 · מהו הערך העשרוני של 0xFF בטיפוס signed char בן 8 ביט?

  • א. 255
  • ב. 1-
  • ג. 127
  • ד. 128-
תשובה: ב

ב-signed char הביט העליון הוא ביט הסימן. 0xFF = 11111111, ולפי משלים ל-2 זהו 1-. אותם בתים עצמם ב-unsigned char שווים 255 — ההבדל הוא בפרשנות בלבד.