לדלג לתוכן

מה זה malloc, ואיך מנהל הזיכרון עובד מבפנים

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

מה בדיוק malloc מחזיר

כשקוראים ל-malloc(size), המנהל (allocator, בגלינוקס לרוב glibc) לא פשוט "חותך" חתיכה מזיכרון פנוי ומחזיר אותה. הוא מקצה יחידה שנקראת chunk - מבנה שמכיל את הזיכרון שביקשתם, ולפניו מטא-דאטה קטן שמתאר את ה-chunk עצמו. המטא-דאטה הזה כולל שדה גודל (size), וכן, אם ה-chunk פנוי, גם מצביעים שמקשרים אותו ל-chunks פנויים אחרים.

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

מה קורה כשקוראים ל-free

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

כמה בינים חשובים שכדאי להכיר:

  • Tcache. מבנה מהיר יחסית חדש (מגרסת glibc 2.26 ואילך) שמייעל שחרור והקצאה חוזרת של chunks קטנים, ומאוד רלוונטי בעולם ניצול ה-heap המודרני.
  • Fastbin. בינים ל-chunks קטנים במיוחד, מהירים לשימוש חוזר.
  • Unsorted bin. מקום זמני שבו chunks משוחררים "יושבים" לפני שהם ממוינים לבין המתאים להם.
  • Smallbin ו-largebin. בינים מסודרים לפי גודל מדויק, לשימוש ב-chunks בגדלים בינוניים וגדולים.

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

למה זה מורכב יותר מזיכרון על הסטאק

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

למה מתכנתים כמעט אף פעם לא צריכים לדעת את זה

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

איך לומדים את זה נכון

הדרך הכי טובה להבין את malloc היא לא רק לקרוא עליו, אלא לצפות בזיכרון בפועל תחת debugger כמו gdb עם התוסף pwndbg, ולראות איך chunks נראים בזיכרון אחרי הקצאות ושחרורים שונים. זה הופך מושג מופשט למשהו מוחשי לגמרי.

בקורס מחקר חולשות אנחנו מקדישים זמן משמעותי להבנת מבנה ה-chunk ומנגנון הבינים לפני שעוברים לחולשות ספציפיות כמו double free או tcache poisoning, כי בלי הבסיס הזה קשה מאוד להבין למה טכניקות ניצול heap בכלל עובדות.

מתבלבלים בין fastbin ל-tcache ולכל שאר הבינים? זה קורה לכולם בהתחלה, ובדיסקורד שלנו אפשר לשאול ולקבל תשובה ברורה.

הצטרפו לקהילה בדיסקורד

לסיכום

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