מכונות וירטואליות באתגרי הנדסה הפוכה, כשהתוכנה בונה מעבד משלה¶
יש דרגת קושי בהנדסה לאחור שמרגישה כמו קפיצת מדרגה - לא עוד "לקרוא אסמבלי רגיל", אלא לגלות שהתוכנה שאתם מנתחים בכלל לא מריצה קוד רגיל. במקום זה, היא מריצה שפה משלה, על מעבד וירטואלי שהמפתחים בנו במיוחד כדי להקשות עליכם. זה נקרא VM obfuscation, והוא אחד הכלים החזקים ביותר בעולם ההסוואה.
מה זה בעצם VM obfuscation¶
הרעיון הבסיסי הוא כזה - במקום לכתוב פונקציה רגישה כקוד מכונה רגיל (שאסמבלר רגיל וכלים כמו Ghidra יודעים לקרוא היטב), המפתחים ממירים אותה ל"שפת בייטקוד" מומצאת משלהם - סדרה של מספרים שמייצגים פקודות בשפה שקיימת רק בתוכנית הזאת. לצד זה, הם כותבים "מפרש" (interpreter) - פונקציה אחת גדולה שיודעת לקרוא את הבייטקוד הזה, פקודה אחר פקודה, ולבצע את הפעולה המתאימה.
מבחינת חוקר שמסתכל על הקוד המקומפל, כל מה שהוא רואה זה לולאה גדולה אחת שקוראת בייט, קופצת לפי הערך שלו לאחד מכמה מקרים אפשריים (switch-case ענקי), ומבצעת פעולה כלשהי - חיבור, השוואה, קפיצה בתוך הבייטקוד עצמו. הלוגיקה האמיתית של התוכנית לא נמצאת בקוד המכונה כלל, היא נמצאת בתוך אותו בלוק בייטקוד, שנראה כמו נתונים חסרי משמעות.
למה זה כל כך יעיל כהסוואה¶
כלי הנדסה לאחור רגילים מצטיינים בניתוח קוד מכונה של ארכיטקטורות מוכרות כמו x86 או ARM. אבל כשהלוגיקה האמיתית "מתחבאת" בתוך שפת בייטקוד מומצאת, אין לכלים האלה שום ידע מובנה על מה כל בייט אומר. כדי להבין מה התוכנית באמת עושה, החוקר צריך קודם להבין את שפת הבייטקוד עצמה - מה כל ערך אופקוד (opcode) עושה, איך נראים הארגומנטים שלו, ואיפה בייטקוד מתחיל ונגמר.
מנגנוני VM obfuscation מסחריים, כמו VMProtect, לוקחים את זה עוד רחוק יותר - הם משנים את מבנה הבייטקוד ואת המפרש בכל קומפילציה, כך שאי אפשר פשוט לכתוב פענוח אחד שעובד על כל תוכנית שהוגנה בכלי הזה.
איך ניגשים לפתור אתגר כזה¶
הצעד הראשון הוא לאתר את המפרש עצמו - הלולאה הגדולה עם ה-switch-case הענקי, ולהבין את מבנה הבייטקוד הכללי - כמה בייטים מוקדשים לכל אופקוד, איך ארגומנטים מקודדים, ואיפה נמצא "מצביע ההוראה" הווירטואלי שמתקדם על פני הבייטקוד. משם, בונים בהדרגה טבלת תרגום - אופקוד 0x01 אולי שווה חיבור, אופקוד 0x02 אולי שווה קפיצה מותנית, וכן הלאה, על ידי מעקב זהיר בזמן ריצה תחת debugger וצפייה בהשפעה של כל אופקוד.
ברגע שיש לכם מספיק אופקודים מפוענחים, אפשר לכתוב סקריפט משלכם - "דה-אסמבלר" זעיר לשפת הבייטקוד הספציפית הזאת - שהופך את הבייטקוד לצורה קריאה, ולפעמים אפילו מתרגם אותו בחזרה לפסאודו-קוד שדומה לשפה רגילה.
למה זה נחשב אחד האתגרים היוקרתיים ביותר¶
פתרון אתגר VM דורש שילוב נדיר של סבלנות, יכולת דיבוג מדויקת, וחשיבה אלגוריתמית - אתם בעצם בונים כלי הנדסה לאחור חדש תוך כדי פתרון האתגר עצמו. זו הסיבה שאתגרי VM נחשבים לרוב לאתגרי ה-Reverse הכי קשים ומכובדים בתחרויות CTF ברמה בינלאומית.
איך מתחילים ללמוד את זה¶
כדאי להתחיל דווקא בכיוון ההפוך - לבנות VM פשוט משלכם, עם כמה אופקודים בסיסיים, ולראות איך זה נראה "מבפנים". זה בונה אינטואיציה חזקה בהרבה מלנסות לפרק VM זר כצעד ראשון.
בקורס מחקר חולשות אנחנו מגיעים לאתגרי VM רק אחרי שביססנו את יסודות ההנדסה לאחור וקריאת אסמבלי בביטחון מלא, כי זה תחום שדורש בסיס חזק לפני שהוא הופך למהנה במקום מתסכל.
מפרקים VM אתגר ותקועים על אופקוד שלא ברור מה הוא עושה? זה בדיוק סוג האתגר שכיף לפתור ביחד בדיסקורד שלנו.
לסיכום¶
VM obfuscation לוקח הנדסה לאחור לשלב נוסף - במקום לנתח קוד מכונה מוכר, אתם צריכים קודם לפענח שפה שלמה שהומצאה במיוחד כדי להקשות עליכם. זה תחום מאתגר ומתגמל, ומי שמתמצא בו נחשב בצדק לחוקר הנדסה לאחור ברמה גבוהה.