רגע! לפני שהולכים... 👋
אל תפספסו! מסלולי לימוד נפתחים בקרוב - מקומות מוגבלים
| מסלול Machine Learning | 03/05 |
| מסלול Computer Vision | 03/05 |
| מסלול RT Embedded Linux | 06/05 |
| מסלול Cyber | 06/05 |
| מסלול Full Stack | 12/05 |
✓ ייעוץ אישי ללא התחייבות | תשובה תוך 24 שעות

עודכן לאחרונה: 19 אפריל, 2026
בעבר, מערכות משובצות מחשב (Embedded Systems) נחשבו ל"איים מבודדים". הן לא היו מחוברות לאינטרנט, הן הריצו קוד סגור על חומרה ייעודית, וההנחה הייתה שמי שאין לו גישה פיזית למכשיר – לא יכול להזיק לו. היום, המציאות הזו השתנתה לחלוטין.
בעידן ה-IoT והמערכות המקושרות, המקרר הביתי, בקר המשאבה במפעל והמחשב ברכב מחוברים כולם לרשת. חיבור זה הופך כל שורת קוד ב-C או ב-++C למטרה פוטנציאלית עבור תוקפים.
בעוד שבתוכנת Desktop "באג" יכול להסתכם בקריסת האפליקציה, במערכות Embedded ההשלכות הן אחרות לגמרי:
שפות C ו-++C הן הבחירה הטבעית ל-Embedded בגלל היעילות והשליטה הישירה בחומרה. אך השליטה הזו מגיעה עם מחיר: השפה לא "שומרת" על המפתח. טעויות קטנות בניהול מצביעים (Pointers), חישוב שגוי של גודל מערך או שימוש לא נכון בטיפוסי נתונים (Integer Promotion) הם המקומות שבהם פרצות אבטחה נולדות.
הידעת? למעלה מ-70% מפרצות האבטחה הקריטיות בעשורים האחרונים נבעו מניהול זיכרון לא תקין – נושא שנמצא בליבת הקורס שלנו.
דף תמיכה זה, והקורס כולו, נועדו להקנות לך את ה-"Security Mindset". אנחנו לא רק לומדים לכתוב קוד שעובד (Functional Code), אלא קוד שהוא חסין (Robust) ומאובטח (Secure) לפי התקנים המחמירים ביותר בעולם (MISRA, CERT).
בהמשך ריכוז של השאלות הטכניות הנפוצות ביותר (Technical FAQ) עבור דף התמיכה. שאלות אלו נועדו לתת מענה למפתחים שרוצים להבין את העומק המקצועי של הקורס ואת הרלוונטיות שלו לעבודה היומיומית שלהם.
כדי להצליח בקורס ולצלול לעומק עולם האבטחה ב-Embedded, חשוב להגיע עם בסיס טכני איתן. הקורס אינו מיועד למתכנתים מתחילים, אלא למפתחים שכבר "מרגישים בבית" מול החומרה.
במהלך הקורס נשתמש בכלים המקובלים ביותר בתעשייה, כדי שהידע שתרכשו יהיה ישים מיד ביום העבודה הבא שלכם:
כדי לכתוב קוד מאובטח, צריך קודם כל להכיר את הדרכים שבהן הוא נשבר. הנה חמש מהטעויות הנפוצות ביותר במערכות Embedded והדרך הנכונה להתמודד איתן:
המוקש: שימוש בפונקציות כמו strcpy או sprintf ללא בדיקת גבולות המערך. תוקף יכול להזין מחרוזת ארוכה מהמתוכנן ולדרוס את ה-Stack, מה שמאפשר הרצת קוד זדוני.
הפתרון: מעבר לפונקציות בטוחות (כמו strncpy או snprintf) ושימוש ב-Stack Canaries – מנגנון שמזהה דריסת זיכרון לפני שהפונקציה חוזרת.
המוקש: גישה למצביע (Pointer) שמתייחס לכתובת זיכרון שכבר שוחררה (free). במערכות מורכבות, הכתובת הזו עלולה להיות מוקצית מחדש למשימה אחרת, מה שמוביל לשיבוש נתונים קריטי.
הפתרון: אימוץ אסטרטגיית Static Allocation במידת האפשר, או שימוש ב-Smart Pointers (ב-C++) שמנהלים את מחזור החיים של האובייקט באופן אוטומטי.
המוקש: חישובים אריתמטיים פשוטים בין טיפוסים שונים (למשל uint8_t מול int) עלולים להוביל לתוצאות לא צפויות בגלל חוקי השפה, או לגלישה (Overflow) שמשבשת לוגיקה של בדיקות אבטחה.
הפתרון: שימוש בטיפוסים מוגדרים היטב (stdint.h), בדיקת חריגות לפני ביצוע הפעולה, והפעלת דגלי קומפיילר כמו -Wconversion.
המוקש: פונקציית פסיקה (ISR) שמעדכנת משתנה גלובלי בזמן שתוכנית ה-Main קוראת אותו. ללא הגנה, התוכנית עלולה לקרוא ערך חלקי או שגוי.
הפתרון: הגדרת משתנים כ-volatile, שימוש ב-Atomic Operations או חסימת פסיקות ב"קטעים קריטיים" (Critical Sections).
המוקש: פונקציות מערכת רבות מחזירות סטטוס שגיאה, אך מפתחים נוטים להתעלם מהן ("לי זה לא יקרה"). תוקף יכול לגרום לפונקציה להיכשל במכוון כדי להעביר את המערכת למצב לא מוגדר.
הפתרון: אכיפת בדיקת ערכי חזרה לכל פונקציה קריטית ושימוש ב-Static Analysis כדי לוודא שאין התעלמות משגיאות.
כאשר אני פוגש מהנדסים שמגיעים לקורס, רובם כבר “יודעים C”. חלקם עובדים שנים על מערכות Embedded, חלקם מגיעים מעולמות RTOS, דרייברים או Firmware. ובכל זאת, כמעט בכל קוד שאני בודק — גם של מפתחים מנוסים — יש דפוסים חוזרים של טעויות אבטחה, או ליתר דיוק: הנחות לא נכונות לגבי התנהגות המערכת.
הנקודה המרכזית שאני מדגיש כבר בשיעור הראשון היא זו:
במערכות Embedded, אבטחת קוד אינה שכבה נוספת — היא חלק מהדיזיין הבסיסי של המערכת. כל החלטה על זיכרון, תזמון, API או קומפיילר משפיעה ישירות על רמת הסיכון.
במאמר הזה אני מרכז את התשתית המחשבתית והטכנית שאני מצפה מהסטודנטים להבין. לא כסילבוס, אלא כבסיס מקצועי שמגדיר מה זה “קוד בטוח” במערכות משובצות.
הקורס מבוסס על גישת "Learn by Breaking" – אנחנו לומדים איך לתקוף ולהפיל מערכת, כדי להבין איך לבנות אותה חסינה באמת. כל מודול מסתיים במעבדה מעשית (Lab) שבה תתנסו במקרים אמיתיים מהשטח.
במעבדה זו תקבלו קוד קיים למערכת בקרה פשוטה המכילה פגימויות זיכרון קלאסיות. המטרה: להריץ התקפת Buffer Overflow שתגרום למערכת לשנות את זרימת התוכנית (Jump to address).
דוגמה למה שנלמד לתקן:
C
void process_data(char *input) {
char buffer[16];
// סכנה: אין בדיקה של גודל הקלט!
strcpy(buffer, input);
}
C
#define SAFE_BUFFER_SIZE 16
void process_data(const char *input) {
char buffer[SAFE_BUFFER_SIZE];
// שימוש בפונקציה בטוחה עם הגבלת גודל וסיומת Null
if (strncpy(buffer, input, SAFE_BUFFER_SIZE - 1) != NULL) {
buffer[SAFE_BUFFER_SIZE - 1] = ' ';
// לוגיקה נוספת לטיפול בשגיאה
}
}
התמודדות עם באגים חמקמקים שקורים רק ב-Runtime. נשתמש בסימולטור RTOS כדי לזהות מצבי מרוץ (Race Conditions) בין פסיקות (ISRs) למשימות רקע.
מה עושים בפועל?
קבלת פרויקט "מלוכלך" והעברתו דרך כלי אנליזה סטטיים (Static Analysis). המשימה היא להביא את הקוד למצב של אפס חריגות מתקן MISRA-C/C++.
בפרויקט המסכם, תדרשו לתכנן מאפס שכבת Driver או API למערכת תקשורת. הפרויקט חייב לכלול:
הנה דוגמה נוספת למעבדה שמתמקדת במעבר ל-C++ מאובטחת. זהו אחד הנושאים הכי מבוקשים, כי מפתחים רבים חוששים מה"תקורה" (Overhead) של C++ במערכות Embedded, והמעבדה הזו מראה איך להשתמש בה כדי להגביר את האבטחה בלי להקריב ביצועים.
במעבדה זו נלמד כיצד למנוע את אחת הבעיות הנפוצות והמסוכנות ביותר: זליגת זיכרון (Memory Leak) וגישה לזיכרון משוחרר. נראה איך מנגנוני השפה המודרניים מחליפים את הניהול הידני המסוכן.
דוגמה לשינוי התפיסתי (Refactoring):
C++
void processing_task() {
DeviceHandler* handler = new DeviceHandler(); // הקצאה ידנית
if (!handler->initialize()) {
return; // אופס! הזיכרון דלף (Memory Leak)
}
handler->run();
delete handler; // שחרור ידני - קל לשכוח או לבצע פעמיים
}
C++
void processing_task() {
// שימוש ב-Unique Pointer - אפס תקורה בביצועים וביטחון מלא
auto handler = std::make_unique<DeviceHandler>();
if (!handler->initialize()) {
return; // הזיכרון משוחרר אוטומטית כאן!
}
handler->run();
// אין צורך ב-delete ידני
}
ת: בעוד ששני התקנים שואפים לקוד איכותי יותר, המיקוד שלהם שונה:
ת: במערכות זמן אמת, שימוש ב-Heap מעלה שלוש בעיות מרכזיות:
ת: ממש לא, אם יודעים באילו כלים להשתמש. מצד אחד, פיצ'רים כמו Exception Handling או Dynamic Cast עלולים להיות בעייתיים במערכות Embedded מסוימות. מצד שני, מנגנונים כמו RAII, Smart Pointers ו-Templates מאפשרים למנוע זליגות זיכרון ושגיאות לוגיות כבר בזמן הקומפילציה – מה שהופך את הקוד למאובטח הרבה יותר מקוד C מסורתי.
ת: זהו מצב שבו תקן השפה לא מגדיר מה אמור לקרות (למשל: חריגה מגבולות מערך או Integer Overflow). במקרה כזה, הקומפילר יכול לבצע אופטימיזציה ש"תשבור" את הלוגיקה של המפתח. תוקפים מנצלים בדיוק את המקומות האלו כדי לשנות את זרימת התוכנית. בקורס נלמד לזהות מצבים אלו בעזרת כלי אנליזה סטטיים.
ת: הקורס סוקר דפוסי עבודה מאובטחים מול RTOS, בדגש על Concurrency. נלמד איך למנוע Race Conditions ו-Priority Inversion שיכולים לשבש את מנגנוני האבטחה של המערכת, ואיך להשתמש נכון ב-Atomic Operations כדי להגן על משאבים משותפים.
הפרק הבא הוא "מדריך מהיר: חמשת המוקשים הנפוצים בקוד C/C++". המטרה של הפרק הזה בדף התמיכה היא לתת "ערך מוסף" מיידי – להראות לקורא דוגמאות קונקרטיות לבעיות שהוא עלול לפגוש ביומיום ואיך הקורס מלמד לפתור אותן.
במערכות IT, כשל תוכנה לרוב מתבטא באובדן נתונים, פגיעה בפרטיות או השבתת שירות. במערכות Embedded, הקוד מחובר ישירות לעולם הפיזי.
משמעות של Buffer Overflow בדרייבר של בלמים אינה קריסת אפליקציה — אלא פעולה פיזית לא צפויה.
משמעות של Race Condition במערכת רפואית יכולה להיות מינון שגוי.
משמעות של Undefined Behavior במערכת תעשייתית יכולה להיות נזק לציוד יקר.
ההבדל אינו רק ברמת הסיכון, אלא גם באופי הסביבה:
כל אלו הופכים את שפת C — עם החופש שהיא נותנת — לכלי מסוכן אם לא משתמשים בו בצורה ממושמעת.
התשובה הקצרה: בגלל הפער בין מה שהקוד “נראה” שעושה לבין מה שהקומפיילר והחומרה באמת עושים.
התשובה המלאה מורכבת משלושה גורמים:
רבים מתייחסים ל-Undefined Behavior כאל משהו נדיר. בפועל, הוא מופיע בדפוסים יומיומיים:
הבעיה אינה רק שהקוד שגוי — אלא שהתוצאה אינה מוגדרת כלל. כלומר, אותו קוד יכול להתנהג אחרת בין קומפיילרים, בין אופטימיזציות, או בין גרסאות.
Stack, Heap, Static, Registers — כולם מתנהגים אחרת.
כאשר מוסיפים Interrupts או RTOS, התמונה מסתבכת עוד יותר.
הקומפיילר מניח שהקוד חוקי. אם הוא מזהה Undefined Behavior, הוא רשאי לבצע אופטימיזציות שמפרקות את ההיגיון של המפתח.
Buffer Overflow הוא אחת הבעיות הבסיסיות והנפוצות ביותר.
דוגמה קלאסית:
#include <string.h>
void process_input(const char *input) {
char buffer[16];
strcpy(buffer, input);
}
הבעיה כאן ברורה: אין בדיקה של אורך הקלט. אם input ארוך מ-16 בתים, נכתב מעבר לגבולות ה-buffer.
לא מספיק להחליף פונקציה — צריך לשנות גישה.
#include <string.h>
void process_input(const char *input) {
char buffer[16];
size_t len = strnlen(input, sizeof(buffer) - 1);
memcpy(buffer, input, len);
buffer[len] = ' ';
}
בנוסף, בסביבות מתקדמות יותר:
Static Analysis הוא תהליך של ניתוח קוד ללא הרצה.
הוא מזהה:
כי:
תהליך נכון נראה כך:
כלים נפוצים:
להריץ Static Analysis רק “לפני שחרור”.
בפועל, צריך לשלב אותו כחלק מהפיתוח השוטף.
שני הסטנדרטים עוסקים בכתיבת קוד בטוח, אך הם שונים בגישה.
לדוגמה:
בפועל, בהרבה ארגונים משלבים בין השניים.
זו שאלה שחוזרת בכל מחזור.
התשובה: כן — אבל רק אם משתמשים בו נכון.
כל אלו יכולים לפגוע בדטרמיניזם ובביצועים.
גישה נפוצה היא Safe Subset של C++:
דוגמה:
#include <array>
class SafeBuffer {
public:
explicit SafeBuffer(size_t size) : size_(size) {}
bool write(size_t index, char value) {
if (index >= size_) return false;
data_[index] = value;
return true;
}
private:
std::array<char, 32> data_;
size_t size_;
};
הגישה כאן:
גישה לזיכרון ששוחרר.
במערכות ללא הגנות — זה עלול לגרום להתנהגות אקראית לחלוטין.
במיוחד עם טיפוסים קטנים (uint8_t, uint16_t).
עלול לגרום לחישובים שגויים או לבעיות לוגיות.
במערכות RTOS או עם interrupts.
לדוגמה:
עדיין אחת הסיבות הנפוצות לקריסות.
משתנים לא מאותחלים → ערכים אקראיים
אבטחה אינה פונקציה אחת — היא תכונה של המערכת כולה.
לא מוסיפים אבטחה בסוף — מתכננים מראש:
קוד חייב להיות צפוי:
פחות קוד = פחות שטח תקיפה
יכולת לראות מה קורה:
במעבדות הקורס אנחנו עובדים על קוד “מלוכלך”.
התהליך כולל:
המטרה אינה רק “לתקן באג” — אלא לשנות את הארכיטקטורה.
בניגוד ל-Static Analysis, כאן הקוד רץ.
כלים כמו:
מאפשרים לזהות:
מזהים בעיות שלא תמיד נראות בניתוח סטטי
דורש הרצה — לא תמיד אפשרי על target אמיתי
לכן משלבים:
כתיבת קוד מאובטח למערכות Embedded היא לא רק המלצה; היא דרישה לעמידה בתקני איכות ורגולציה מחמירים. הקורס נועד ליישר קו עם הסטנדרטים המובילים בתעשייה, כדי שהפרויקטים שלכם יהיו מוכנים לאישור (Certification).
MISRA-C:2012 / MISRA C++:2023: תקנים אלו מגדירים תת-קבוצה (Subset) בטוחה של שפות C/C++, על ידי פסילת מבני קוד מסוכנים. בקורס נלמד את ה-Rationale מאחורי החוקים הקריטיים וכיצד להשתמש בכלי אנליזה סטטיים כדי לוודא עמידה בהם (Static Analysis Rule Enforcement).
חשיבות לתעשייה: עמידה בתקן MISRA היא חובה עבור ספקי תוכנה לתעשיות הרכב (ISO 26262), הרפואה (IEC 62304) והתעופה.
CERT (Computer Emergency Response Team): בעוד ש-MISRA מתמקד בבטיחות ואמינות, תקן CERT C מזהה דפוסי קוד שניתן לנצל להתקפות סייבר. בקורס נלמד כיצד לזהות ולתקן פרצות המוגדרות ב-CERT C Top 10, כגון Buffer Overflow, Format String ומצבי מרוץ.
הקורס מספק את התשתית הטכנית לעמידה ברגולציות אבטחת סייבר חדשות, כגון ה-Cyber Resilience Act האירופי, המחייב יצרני מכשירים מחוברים (IoT) להוכיח כי התוכנה שלהם מאובטחת לאורך כל מחזור החיים שלה.
אנחנו מבינים שלמידת חומר טכני מורכב דורשת יותר מהרצאות פרונטליות. לכן, הקורס מציע מעטפת תמיכה מקיפה:
במהלך המעבדות (Labs), מדריכים מנוסים יהיו זמינים לתמיכה בזמן אמת, כדי לעזור לכם לפתור בעיות, להריץ את הבדיקות ולבצע את ה-Refactoring בהצלחה.
כל חומרי הלימוד – כולל מצגות, דוגמאות קוד, וחוברות המעבדה – יהיו זמינים לכם דרך פורטל הלמידה.
במידה ותפספסו שיעור, או שתרצו לחזור על נושא מורכב, תוכלו לצפות בהקלטות השיעורים בזמנכם החופשי.
אינכם בטוחים אם הרקע הטכני שלכם מתאים? צוות היועצים הטכניים שלנו ישמח לשוחח איתכם, להבין את הניסיון שלכם ולהמליץ על מסלול הלימודים המתאים ביותר עבורכם.