GammaCyber

מבוא לשפות תכנות: מ-Assembly ועד Python

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

// סקירה

מבוא לשפות תכנות

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

✓נושאי השיעור
  • שרשרת קומפילציה — Preprocessing → Assembly → Object → Linking
  • Compiler לעומת Interpreter לעומת Bytecode
  • High-Level לעומת Low-Level — הספקטרום
  • Static/Dynamic Typing ו-Strong/Weak Typing
  • סקירת שפות: Assembly, C, C++, Java, C#, Python
  • קריאת קוד לא מוכר — Dry Run ו-Symbol Table
שיעור זה מסיים את יחידה 1. מהשיעור הבא מתחיל תכנות אמיתי — Python, OOP, ושפות מומצאות.
// פרק 1

שרשרת הקומפילציה: מקוד לביצוע

כשכותבים תוכנה בכל שפה, קורה תהליך שמומר לאפסים ואחדים. הבנת התהליך קריטית להבנת הבדלי הביצועים, האבטחה, ו-Reverse Engineering.

Compilation — 4 שלבים

קוד C: int x = 5 + 3; שלב 1: Preprocessing (gcc -E) מרחיב #include, #define, Macros שלב 2: Compilation (gcc -S) → Assembly Code: mov DWORD PTR [rbp-4], 8 ← Constant Folding! שלב 3: Assembling (as) Assembly → Object File (.o) = מכונה בינארי שלב 4: Linking (ld) מחבר Object Files + Libraries → Executable
🎯 דגש למבחן
ה-Linker מחבר את קבצי ה-Object עם הספריות: הוא פותר סימבולים חיצוניים (למשל printf שהוגדרה בספרייה אחרת), מקצה כתובות סופיות ובונה את קובץ ההרצה. שגיאת undefined reference היא שגיאת Linker ולא שגיאת קומפילציה. קישור סטטי מטמיע את הספרייה בתוך הקובץ; קישור דינמי טוען אותה בזמן ריצה (.so / .dll).
הרחבה Interpretation ו-Bytecode בהשוואה ›
Compilation (C/C++): Source → Assembly → Object → Executable ✓ מהיר בריצה ✓ אין Runtime ✗ ספציפי ל-OS Interpretation (Python, JS): Source → Interpreter קורא שורה-שורה → ביצוע מיידי ✓ פורטבילי ✓ דיבוג קל ✗ איטי יחסית Bytecode (Java, Python .pyc): Source → Bytecode → VM מריץ Java: .java → javac → .class → JVM Python: .py → CPython → .pyc → CPython VM

JIT (Just-In-Time): JVM מודרני גם מקמפל חלקים נפוצים לקוד מכונה בזמן ריצה. תוצאה: Java יכולה להיות מהירה כמו C++ לקטעים מסוימים.

🎯 דגש למבחן
פייתון היא לא "רק מפרשת". CPython מקמפלת את הקוד ל-Bytecode (קבצי .pyc), ורק אז מכונה וירטואלית (PVM) מריצה אותו פקודה-פקודה. כלומר יש שלב קומפילציה — אבל לא לקוד מכונה של המעבד. ב-C לעומת זאת הקומפילציה מייצרת קוד מכונה שרץ ישירות על ה-CPU, ולכן שגיאות טיפוסים נתפסות לפני ההרצה, בעוד בפייתון רובן מתגלות רק בזמן ריצה.
! Constant Folding — המהדר חכם ממך ›

int x = 5 + 3; — המהדר מחשב את 5+3 בזמן קומפילציה ומייצר MOV EBX, 8. לא מחשב בזמן ריצה כלל! ב-Reverse Engineering: קוד שראית ב-C לא בהכרח יופיע ישיר ב-Assembly.

✏תרגול עצמי3 שאלות
שאלה ב׳.1 [קל]
איזו שפה היא Compiled לקוד מכונה ישיר (לא Bytecode)?
✓ תשובה: ד) C — gcc מקמפל C ישירות לקוד מכונה. Java ← Bytecode/JVM. Python ← Interpreted/CPython. JS ← Interpreted/V8.
שאלה ב׳.2 [קל]
מה התוסף של קובץ Python Bytecode?
✓ תשובה: ד) .pyc — Python Compiled. לא ניתן-לקריאה ישירות, אבל dis module יכול לפרק אותו. שמור ב-__pycache__/.
שאלה ב׳.8 [בינוני]
gcc -S filename.c — מה עושה?
✓ תשובה: ב) — -E=Preprocessing, -S=Assembly, -c=Object, ללא דגל=הכל. -o output_name = שם הפלט.
// פרק 2

הספקטרום: High-Level עד Low-Level

High-Level = רחוק מהחומרה, מוגן, נוח. Low-Level = קרוב לחומרה, מהיר, שולט. כל שכבת הפשטה מסתירה פרטים — אבל עולה בביצועים.

מדדPythonJava/C#C/C++Assembly
ניהול זיכרוןאוטומטי (GC)אוטומטי (GC)ידני (malloc/free)ידני מוחלט
מהירות ריצה~50x איטי מC~2-5x איטי מCמהירהכי מהיר
פורטביליותכל OSכל OS עם JVMקומפל לOS ספציפיCPU ספציפי בלבד
בטיחותגבוההגבוההנמוכה (Buffer Overflow!)אפסית
הרחבה אותה משימה ב-4 שפות ›

משימה: מצא סכום המספרים הזוגיים ברשימה

// C — 6 שורות, ניהול ידני int sum_evens(int *arr, int len) { int sum = 0; for (int i = 0; i < len; i++) if (arr[i] % 2 == 0) sum += arr[i]; return sum; } // Java — Streams int sumEvens(List<Integer> nums) { return nums.stream().filter(n -> n % 2 == 0).mapToInt(Integer::intValue).sum(); } # Python — שורה אחת! sum_evens = lambda nums: sum(x for x in nums if x % 2 == 0) ; Assembly — 30+ שורות לאותה פעולה test rax, 1 ; האם זוגי? jnz .skip ; אם לא — דלג add rcx, rax ; הוסף לסכום
🎯 דגש למבחן
מוסכמת הקריאה (Calling Convention) ב-x86-64 Linux (System V): ששת הארגומנטים הראשונים עוברים ברגיסטרים לפי הסדר RDI, RSI, RDX, RCX, R8, R9, וערך ההחזרה חוזר ב-RAX. בקריאת מערכת (syscall) מספר הקריאה נמצא ב-RAX והארגומנטים ב-RDI, RSI, RDX.... שינון הסדר הזה הוא חובה לניתוח קוד Assembly ולשאלות Reverse Engineering.
✏תרגול עצמי3 שאלות
שאלה ב׳.7 [בינוני]
C# vs Java — מה המשותף ביותר?
✓ תשובה: ג) — Java: JVM + Bytecode. C#: CLR + IL Bytecode. שניהם Static Typing, OOP חזק, GC. C# = Java של Microsoft בהרבה מובנים.
שאלה ב׳.12 [בינוני]
לאיזו שפה ממליצים ב-Gamma לסדנה המעשית?
✓ תשובה: ג) C# או Python — תרגיל ה-OOP בסדנה מיועד ל-C# או Python. רוב המדריכים מכוונים לC#. חשוב להכיר את שתיהן.
שאלה ב׳.15 [בינוני]
Assembly — מה נכון לגבי Types?
✓ תשובה: ב) — Assembly לא מכיר "int" או "string". יש רק בייטים, מילים, ורגיסטרים. ה"טיפוס" הוא פרשנות — הכל ביטים.
// פרק 3

מערכות Types: שני צירים עצמאיים

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

ציר 1 — Static vs Dynamic

// Java — Static: סוג נקבע בזמן קומפילציה int x = 5; x = "hello"; // ← ERROR בזמן קומפילציה! # Python — Dynamic: סוג נקבע בזמן ריצה x = 5 x = 'hello' # ← OK! x עכשיו String x = [1,2,3] # ← OK! x עכשיו List

ציר 2 — Strong vs Weak

# Python — Strong: לא מבצע המרות אוטומטיות '5' + 3 # → TypeError: מחרוזת + מספר // JavaScript — Weak: המרות אוטומטיות מפתיעות '5' + 3 → '53' // String concat '5' - 3 → 2 // Number arithmetic null >= 0 → true // WTF!
! Dynamic ≠ Weak! שני צירים עצמאיים ›
שפהStatic/DynamicStrong/Weak
PythonDynamicStrong
JavaStaticStrong
CStaticWeak (implicit casting)
JavaScriptDynamicWeak
🎯 דגש למבחן
גם C היא טיפוסיות חלשה. היא Static (הסוג נקבע בקומפילציה) אך Weak: המרות מרומזות מתבצעות בשקט — char→int, int→float, ו-void* ניתן להמרה לכל מצביע. מכאן באגי אבטחה קלאסיים: Integer Overflow, בלבול signed/unsigned וחיתוך ערכים בהמרה.
🎯 דגש למבחן
Duck Typing — "אם זה מדבר כמו ברווז, זה ברווז". בפייתון לא נבדק מהו הטיפוס של האובייקט אלא אילו מתודות יש לו: כל אובייקט עם .read() יתאים לפונקציה שקוראת ממנו, בלי ירושה ובלי ממשק מוצהר. זו תוצאה ישירה של Dynamic Typing.
✏תרגול עצמי4 שאלות
שאלה ב׳.3 [קל]
Python — Static או Dynamic Typing?
✓ תשובה: ד) Dynamic — x=5; x='hello' — Python בסדר גמור. Type נקבע בזמן ריצה, לא בזמן קומפילציה.
שאלה ב׳.4 [קל]
JavaScript — Strong או Weak Typing?
✓ תשובה: א) Weak — '5'+3='53'. JavaScript מבצע המרות אוטומטיות מפתיעות. זה מקור לבאגים רבים.
שאלה ב׳.6 [בינוני]
Python הוא Dynamic ו-?
✓ תשובה: ב) Strong — Python Dynamic (Type נקבע בזמן ריצה) + Strong (לא עושה המרות אוטומטיות: '5'+3 = TypeError). שני צירים עצמאיים!
שאלה ב׳.18 [קשה]
Python Type Hints (def f(x: int) -> str) — מה קורה אם נשלח float?
✓ תשובה: ג) — Type Hints הם הערות בלבד! Python נשאר Dynamic. mypy/pyright בודקים אותם, אבל לא Python Runtime עצמו.
// פרק 4

סקירת שפות: מה, למה, ומה תראה בגאמא

כל שפה פותרת בעיה שונה. איש סייבר חייב לקרוא ולהבין כולן — כי מערכות אמיתיות כתובות בשפות שונות, וכדי לתקוף/להגן חייבים להבין את הכל.

Assembly (x86)

תרגום כמעט-ישיר לפקודות CPU. כל שורה = פקודה אחת. אין לולאות, אין פונקציות, אין Types — רק הוראות ורגיסטרים.

; הדפסת 'Hello' ב-Linux x86-64 NASM section .text global _start _start: mov rax, 1 ; syscall write mov rdi, 1 ; fd=stdout mov rsi, msg ; buffer mov rdx, 5 ; length syscall mov rax, 60 ; syscall exit xor rdi, rdi ; exit code 0 syscall
🎯 Assembly בגאמא
מבחן ידע Assembly, Black Box CTF, Shellcode. כל CTF Pwn דורש הבנת Assembly.

שפת C

Low-Level עם syntax נוח יחסית. הומצאה ב-1972 ע"י Dennis Ritchie לפיתוח Unix. שולטת בכל מה שקרוב לחומרה: OS Kernels, Embedded, Drivers.

🎯 C בגאמא
Buffer Overflow, Heap Exploits, OS Internals. מבחן ידע C — Pointers, שאלות Overflow.

Java ו-C#

Static OOP, Garbage Collection, Bytecode על JVM/CLR. חשוב לגאמא: C# היא השפה המומלצת לסדנה המעשית!

Python

Dynamic, Interpreted, Simple. "כל מה שתצטרך כבר מובנה". כלי הackers — pwntools, scapy, requests. כל שיעורי הקורס.

תרחיש סייברשפות נדרשות
ניתוח MalwareAssembly, C/C++, Python
Exploit DevelopmentC, Python/pwntools, Assembly (shellcode)
CTFPython, C, Java/C# (RE)
גאמא סדנהC# או Python ← הכי חשוב!
✏תרגול — זהה את השפה4 שאלות
זיהוי שפה א׳
public class Main { public static void main(String[] args) { ArrayList<Integer> nums = new ArrayList<>(); nums.add(42); System.out.println(nums.get(0)); } }מה השפה? מה הסימנים?
✓ תשובה: ד) Java — public class (הכל בClass), ArrayList<Integer> (Generics), System.out.println (Java print style).
זיהוי שפה ב׳
def fibonacci(n): if n <= 1: return n return fibonacci(n-1) + fibonacci(n-2) print([fibonacci(i) for i in range(10)])מה השפה?
✓ תשובה: ג) Python — def + indent (ללא סוגריים מסולסלים), list comprehension ([f(i) for i in range(10)]), : בסוף שורת def.
זיהוי שפה ג׳
#include <stdio.h> int factorial(int n) { if (n == 0) return 1; return n * factorial(n - 1); } int main() { printf("%d\n", factorial(5)); }מה השפה?
✓ תשובה: ד) C — #include <stdio.h>, int main(), printf("%d\n"), return 0. C קלאסי.
זיהוי שפה ד׳
section .text global _start _start: mov eax, 4 mov ebx, 1 int 0x80מה השפה?
✓ תשובה: א) Assembly x86 Linux — section .text, global _start, mov eax/ebx, int 0x80 (Linux syscall 32-bit).
// פרק 5

קריאת קוד לא מוכר: Dry Run ו-Symbol Table

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

✓מה לחפש ראשון בשפה לא מוכרת
  • השמת ערכים: x = 5 / SET x TO 5 / x := 5
  • תנאים: if / IF / cond
  • לולאות: for / while / LOOP / REPEAT
  • פונקציות: def / func / PROC
  • השוואה: == / != / EQ / EQUALS
הרחבה דוגמת Dry Run — LangX מלאה ›
; שפה מומצאת LangX: SET a TO 4 ; a=4 SET b TO 7 ; b=7 ADD a BY b ; a=4+7=11 MUL a BY 2 ; a=11×2=22 IF a GREATER 20 SET b TO 100 PRINT b ; ← מדפיס 100 ENDIF LOOP i FROM 1 TO 3 MUL b BY i ; i=1: b=100. i=2: b=200. i=3: b=600 ENDLOOP PRINT a ; ← מדפיס 22 PRINT b ; ← מדפיס 600

פלט: 100, 22, 600 — עקוב עם Symbol Table: {a, b, i} ורשום ערכים בכל שלב.

🎯 אסטרטגיה למחשבון קרב
שלב 1: קרא מפרט — זהה דפוסים מוכרים. שלב 2: מצא דוגמת קוד פשוטה ו"פרסר" אותה ידנית. שלב 3: בנה Symbol Table — רשום כל משתנה וערכו. שלב 4: הרץ Dry Run שורה-שורה. שלב 5: אל תבזבז יותר מ-3 דקות על שאלה קשה — עבור הלאה.
✏תרגול Dry Run3 שאלות
תרגיל ג3 — Recursion
FUNC mystery(n): IF n EQUALS 0 RETURN 1 ENDIF RETURN n MUL mystery(n SUB 1) ENDFUNC PRINT mystery(4)מה הפלט? מה האלגוריתם?
✓ תשובה: א) 24 — פקטוריאל — mystery(4)=4×mystery(3)=4×3×mystery(2)=4×3×2×mystery(1)=4×3×2×1×mystery(0)=4×3×2×1×1=24.
שאלה ב׳.16 [קשה]
x = [1,2,3] y = x y.append(4) print(x)מה הפלט?
✓ תשובה: ג) [1,2,3,4] — y = x לא מעתיק! שניהם מצביעים לאותה list. y.append(4) משנה אותה in-place. x גם רואה שינוי.
שאלה ב׳.20 [קשה]
gcc hello.c -o hello — איזה שלבים מתרחשים?
✓ תשובה: א) — ללא דגל מיוחד, gcc מריץ את כל 4 השלבים: Preprocessing(-E), Compilation(-S), Assembling(-c), Linking → hello
// אתגר מעשי

אתגר מעשי — מ-Assembly לקוד: מכונות ומפרשים

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

T1 · מתחיל 100 נק' ~10 דק' debug
תיקון באג — אינטרפרטר צובר (Accumulator VM)
מכונה זעירה מריצה תוכנית של שורות טקסט על אוגר צובר acc (מתחיל ב-0): "SET n" קובע acc=n · "ADD n" מוסיף · "SUB n" מחסיר · "MUL n" מכפיל · "NEG" הופך סימן. הפונקציה אמורה להחזיר את acc הסופי — אך יש בה באג: הפקודה SUB מתנהגת כמו ADD. מצא ותקן. החזר את הגרסה המתוקנת.
// הקוד השבור לתיקון
def run(program: list[str]) -> int: acc = 0 for line in program: parts = line.split() op = parts[0] if op == 'SET': acc = int(parts[1]) elif op == 'ADD': acc += int(parts[1]) elif op == 'SUB': acc += int(parts[1]) # באג כאן elif op == 'MUL': acc *= int(parts[1]) elif op == 'NEG': acc = -acc return acc
דוגמאות
run(['SET 10', 'SUB 4']) → 6
run(['SET 3', 'MUL 4', 'NEG']) → -12
הפתרון נשמר מקומית ומשותף עם עמוד „כתיבת קוד”.
פתרון-ייחוס תיקון באג — אינטרפרטר צובר (Accumulator VM) ›
def run(program: list[str]) -> int: acc = 0 for line in program: parts = line.split() op = parts[0] if op == 'SET': acc = int(parts[1]) elif op == 'ADD': acc += int(parts[1]) elif op == 'SUB': acc -= int(parts[1]) # התיקון: -= (היה +=) elif op == 'MUL': acc *= int(parts[1]) elif op == 'NEG': acc = -acc return acc
T2 · בסיסי 200 נק' ~15 דק' algorithmic
מכונת-מחסנית — מחשבון RPN
לפני שכותבים מהדר — כותבים מכונה. ממש מחשבון בנוטציית פוסטפיקס (Reverse Polish). נתונה רשימת אסימונים tokens: כל אסימון הוא מספר שלם (ייתכן שלילי, כמחרוזת) או אחד מהאופרטורים + - * /. אופרטור מוציא שני ערכים מהמחסנית ודוחף את התוצאה. חילוק / הוא חילוק שלם עם קיטום לכיוון אפס (כמו ב-C): 7/2=3 וגם (−7)/2=−3. הקלט תקין ומסתיים בערך יחיד. החזר את התוצאה.
דוגמאות
eval_rpn(['2','3','+']) → 5
eval_rpn(['4','5','*','6','-']) → 14
eval_rpn(['-7','2','/']) → -3
הפתרון נשמר מקומית ומשותף עם עמוד „כתיבת קוד”.
פתרון-ייחוס מכונת-מחסנית — מחשבון RPN ›
def eval_rpn(tokens: list[str]) -> int: st = [] for t in tokens: if t in ('+', '-', '*', '/'): b = st.pop(); a = st.pop() if t == '+': st.append(a + b) elif t == '-': st.append(a - b) elif t == '*': st.append(a * b) else: # קיטום לכיוון אפס (כמו C) q = abs(a) // abs(b) st.append(q if (a < 0) == (b < 0) else -q) else: st.append(int(t)) return st[-1]
// סיכום

סיכום שיעור 4 — סוף יחידה 1

✓מה למדנו
  • שפות תכנות = הפשטות. כל שכבה מסתירה פרטים — אבל עולה בביצועים
  • Compiler: Source → Assembly → Object → Executable (gcc -E -S -c -o)
  • Interpreter: קורא שורה-שורה ומבצע מיידית (Python, JavaScript)
  • Bytecode: פשרה — מקמפל לשפת ביניים, JVM/CPython מריץ
  • Static/Dynamic Typing: C/Java/C# = Static. Python/JS = Dynamic
  • Strong/Weak: Python/Java = Strong. JS/C = Weak. שני צירים עצמאיים!
  • Assembly = CPU בלבד, Reverse Engineering, Exploits
  • C = Buffer Overflow, OS, Embedded
  • Java/C# = Static OOP, GC, גאמא סדנה
  • Python = Dynamic, Scripting, CTF, כל שיעורי הקורס
  • קוד לא מוכר: חפש דפוסים (if/for/=), בנה Symbol Table, הרץ Dry Run
🎯 השיעור הבא
שיעור 5 — Python מרוכז: יסודות אינטנסיביים (יחידה 2)
משתנים, מחרוזות, תנאים, לולאות, Lists, Dicts, Mutability, Comprehensions
שאלות תרגול ותשובות (5)

שאלה 1 · מה תפקיד ה-Linker בשרשרת הבנייה של תוכנית C?

  • א. מרחיב פקודות #include ו-#define
  • ב. מתרגם קוד C לקוד Assembly
  • ג. מחבר קבצי Object וספריות לקובץ הרצה ופותר סימבולים חיצוניים
  • ד. מריץ את התוכנית בזמן ריצה
תשובה: ג

ה-Linker מאחד Object Files + Libraries, פותר סימבולים חיצוניים ומקצה כתובות. לכן undefined reference היא שגיאת Linker.

שאלה 2 · כיצד CPython מריצה קוד פייתון?

  • א. מקמפלת ישירות לקוד מכונה של המעבד
  • ב. מקמפלת ל-Bytecode ומכונה וירטואלית מריצה אותו
  • ג. קוראת את קובץ המקור בלי שום שלב קומפילציה
  • ד. ממירה את הקוד ל-C ואז מהדרת ב-gcc
תשובה: ב

יש שלב קומפילציה ל-Bytecode (.pyc), אך לא לקוד מכונה — ה-PVM מריצה אותו פקודה-פקודה.

שאלה 3 · איך מסווגים את שפת C בשני צירי הטיפוסיות?

  • א. Dynamic ו-Strong
  • ב. Static ו-Strong
  • ג. Static ו-Weak
  • ד. Dynamic ו-Weak
תשובה: ג

הסוג נקבע בקומפילציה (Static), אך מתבצעות המרות מרומזות בשקט (Weak) — מקור לבאגי Integer Overflow ו-signed/unsigned.

שאלה 4 · ב-x86-64 Linux (System V) — באילו רגיסטרים עוברים שלושת הארגומנטים הראשונים לפונקציה?

  • א. RAX, RBX, RCX
  • ב. RDI, RSI, RDX
  • ג. RSP, RBP, RIP
  • ד. R8, R9, R10
תשובה: ב

הסדר: RDI, RSI, RDX, RCX, R8, R9. ערך ההחזרה ב-RAX.

שאלה 5 · מהו Duck Typing?

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

אם לאובייקט יש את המתודה הדרושה (למשל .read()) — הוא מתאים, בלי ירושה או ממשק מוצהר.