הנדסה לאחור
למה להשתמש ב-NotHoly להנדסה הפוכה
השאירו את Ghidra, radare2 או IDA. הדביקו את פלט ה-decompiler והמודל מסביר אותו וכותב את הסקריפט, בלי לסרב. triage סטטי למבט ראשון.
מה קומפילציה מאבדת, במה דיסאסמבלר שונה מדקומפיילר, והמגבלות המשפטיות שכדאי להכיר לפני ניתוח של בינארי.
בהנדסה לאחור מבינים איך משהו נבנה מתוך בחינת הדבר המוגמר. בתוכנה, הדבר המוגמר הוא בדרך כלל תוכנית מקומפלת: קובץ הרצה, ספרייה משותפת או תמונת קושחה. קוד המקור חסר או שמעולם לא פורסם, ולכן מתחילים מקוד מכונה ועובדים לאחור אל מה שהתוכנית עושה ולמה.
אנשים עושים את זה מסיבות רבות, ורובן רגילות. מהנדס אבטחה בודק בינארי של ספק לפני שסומך עליו. מפתחת מנסה לגרום לפורמט קובץ סגור לעבוד עם כלי פתוח. סטודנט רוצה לראות מה קומפיילר באמת מייצר. המאמר הזה מכסה מה קומפילציה מאבדת, את ההבדל בין ניתוח סטטי לדינמי, דיסאסמבלרים מול דקומפיילרים, הסיבות הלגיטימיות לעבודה הזאת והמגבלות המשפטיות שכדאי להכיר לפני שמתחילים. המאמר הבא בסדרה מריץ ניתוח אמיתי ראשון.
קומפיילר הופך קוד מקור קריא לאדם להוראות שמעבד יכול להריץ. הפלט נכון עבור המכונה ואומר מעט על הכוונה. כמה דברים נעלמים בדרך:
for הופכת להשוואה ולקפיצה. if הופך לקפיצה מותנית למקום אחר בקובץ.כל אובדן הופך את השלב הבא לפחות ודאי. מי שעושה הנדסה לאחור בונה מחדש חלק מהפרטים האלה מתוך רמזים: המחרוזות שהתוכנית משתמשת בהן, הפונקציות שהיא מייבאת ממערכת ההפעלה והדרך שבה נתונים עוברים דרך אוגרים.
אפשר לראות גרסה קטנה של זה עם Python 3.12. הדוגמה למטה היא פונקציה של שלוש שורות עם הערה. bytecode של Python הוא לא קוד מכונה native, ולכן זו אנלוגיה ולא מודל של פלט C. עדיין, היא ממחישה את הנקודה.
# Teaching constant, not real access control.
def check_pin(user_pin):
training_pin = 2468 # the comment never reaches the bytecode
return user_pin == training_pinמקמפלים ועושים דיסאסמבלי עם הפקודות האלה:
python -c "import py_compile; py_compile.compile('check_pin.py', cfile='check_pin.pyc', doraise=True)"
python -c "import marshal, dis; data = open('check_pin.pyc','rb').read()[16:]; code = marshal.loads(data); fn = [c for c in code.co_consts if hasattr(c,'co_code')][0]; print('name:', fn.co_name, '| constants:', fn.co_consts, '| locals:', fn.co_varnames); dis.dis(fn)"על המחשב שבו נבדק המאמר, הפלט היה:
name: check_pin | constants: (None, 2468) | locals: ('user_pin', 'training_pin')
2 0 RESUME 0
3 2 LOAD_CONST 1 (2468)
4 STORE_FAST 1 (training_pin)
4 6 LOAD_FAST 0 (user_pin)
8 LOAD_FAST 1 (training_pin)
10 COMPARE_OP 40 (==)
14 RETURN_VALUEשני דברים בולטים. ההערה נעלמה: חיפוש של הטקסט שלה בקובץ המקומפל לא מוצא כלום. השמות נשמרו, כי Python מחזיק אותם באובייקט הקוד. תוכנית C מקומפלת לרוב לא שומרת אף אחד מהם, ולכן אותו תרגיל על בינארי native קשה יותר. זה מודל חשיבה, ולא הבטחה לגבי כל שפה.
שיטות הנדסה לאחור מתחלקות לשתי קבוצות רחבות.
ניתוח סטטי בוחן את הקובץ בלי להריץ אותו. קוראים את ההוראות, מונים ייבואים ומחרוזות, ממפים פונקציות והפניות צולבות ובונים מודל של התוכנית. הקובץ אף פעם לא רץ, אבל הכלים עדיין מפרסרים אותו, ולכן שומרים אותם מעודכנים ועובדים על מחשב מבודד כשהדוגמה עלולה להיות עוינת. עבודה סטטית איטית יותר, אבל היא ניתנת לשחזור, ורוב האנשים מתחילים ממנה.
ניתוח דינמי מריץ את התוכנית תחת מעקב. דיבאגר עוצר אותה בנקודות עצירה כדי לקרוא אוגרים וזיכרון. tracer רושם קריאות מערכת, גישה לקבצים או חיבורי רשת. ארגז חול (sandbox) רושם מה משתנה בדיסק. עבודה דינמית מראה התנהגות אמיתית, כולל ערכים שקיימים רק בזמן ריצה, אבל היא מחייבת להריץ קוד שאולי אין סיבה לסמוך עליו. עושים אותה במכונה וירטואלית חד־פעמית בלי חשבונות אמיתיים, בלי תיקיות משותפות ובלי נתונים רגישים, ובאופן אידיאלי בלי גישה לרשת.
רוב העבודה האמיתית משלבת את השתיים. מעבר סטטי מוצא את הפונקציה שבודקת סיסמה. הרצה בדיבאגר מראה מה באמת הקלט והפלט.
דיסאסמבלר ממיר בתים להוראות assembly של ארכיטקטורת מעבד מסוימת. התרגום מכני: תבנית בתים הופכת למנמוניק כמו mov, cmp או jne, עם האופרנדים שלו. התוצאה תואמת למכונה בדיוק, אבל נשארת ברמה נמוכה. קריאה של אלפי הוראות כדי להבין יכולת אחת לוקחת זמן רב.
דקומפיילר הולך רחוק יותר. הוא מנסה להפיק מאותם בתים פסאודו־קוד ברמה גבוהה יותר, בדרך כלל דמוי C. הוא משחזר גבולות פונקציות, זרימת בקרה כמו if או while, וטיפוסי נתונים בקירוב. הפלט שלו הוא פרשנות, ולא עותק של קוד המקור. הוא יכול לטעות בטיפוסים, למזג דברים שהיו נפרדים או להמציא מבנה שמעולם לא היה קיים. הרגל טוב הוא לקרוא את פלט הדקומפיילר ליד הדיסאסמבלי ולסמוך על ה-assembly כשהם לא מסכימים.
Ghidra הוא אחד הכלים החינמיים הנפוצים ביותר שעושים את שניהם. זו מסגרת להנדסה לאחור של תוכנה ש-Research Directorate של U.S. National Security Agency יצר ומתחזק, ברישיון Apache-2.0, והיא רצה על Windows, macOS ו-Linux. היא תומכת בארכיטקטורות מעבד ובפורמטי קבצים רבים, ואפשר להרחיב אותה ב-Java או ב-Python. ה-README שלה דורש JDK 25 (64-bit) כדי להריץ גרסה רשמית. קיימים כלים אחרים, מסחריים ופתוחים, והרעיונות במאמר הזה חלים על כולם.
להנדסה לאחור יש כמה שימושים לגיטימיים.
יכולת פעולה הדדית (interoperability). התוכנה שלך צריכה להחליף נתונים עם תוכנית או פורמט סגורים, והספק לא מפרסם את הפרטים. האיחוד האירופי מטפל במקרה הזה ישירות, כפי שהסעיף המשפטי מסביר.
מחקר חולשות. בוחנים תוכנה שיש לך הרשאה לבדוק, מוצאים פגמים ומדווחים עליהם כדי שהספק יתקן. זה בדרך כלל נעשה בגילוי מתואם: דוח פרטי, חלון זמן לתיקון, ואז פרטים פומביים.
ניתוח נוזקות. מגינים מפרקים קובץ חשוד כדי ללמוד מה הוא עושה, באילו מערכות הוא נוגע ואיך לזהות אותו. חלק גדול מהידע הציבורי על איומים מגיע מהעבודה הזאת.
למידה. קריאת תוכנית מקומפלת היא אחת הדרכים הטובות ביותר להבין איך מחשבים מריצים קוד: פריסת זיכרון, מוסכמות קריאה והפער בין מה שכותבים למה שרץ.
בדיקת טענות. לפעמים רוצים לאמת מה תוכנית שולחת ברשת או שומרת מקומית, במיוחד כשפרטיות על הפרק. גם זה נוגע באותן שאלות משפטיות כמו כל ניתוח אחר.
זו סקירה כללית ולא ייעוץ משפטי. חוקים שונים ממדינה למדינה, וחוזים יכולים להוסיף תנאים שחלים בכל מקום. שלוש שכבות חשובות במיוחד.
1. תנאי רישיון. רוב רישיונות התוכנה המסחריים אוסרים הנדסה לאחור אלא במקום שהחוק מתיר. רישיונות קוד פתוח עובדים אחרת ולעיתים קרובות מתירים קריאה ושינוי של קוד, אבל גם להם יש תנאים. קריאת מקור שקיבלת שונה מהנדסה לאחור של בינארי, ושניהם שונים מפריצה לשירות. לפני ניתוח של מוצר שלא כתבת, קוראים את הרישיון שלו.
2. האיחוד האירופי. Directive 2009/24/EC בדבר ההגנה המשפטית על תוכנות מחשב כולל חריג של יכולת פעולה הדדית ב-Article 6. בקצרה, משתמש חוקי, או מי שפועל מטעמו, רשאי לבצע דקומפילציה לתוכנית כשזה הכרחי להשגת המידע הדרוש כדי שתוכנית שנוצרה באופן עצמאי תעבוד עם אחרות. חלים תנאים. המידע לא יכול להיות זמין כבר בקלות, העבודה מוגבלת לחלקי התוכנית שנחוצים ליכולת פעולה הדדית, והמידע לא ישמש למטרות אחרות ולא יועבר מעבר למה שיכולת פעולה הדדית דורשת. אסור להשתמש בו לפיתוח תוכנית דומה במהותה, והחריג לא יכול לפגוע באופן בלתי סביר באינטרסים הלגיטימיים של בעל הזכויות. Article 6 עוסק ביכולת פעולה הדדית ולא בסקרנות כללית.
3. ארצות הברית. Section 1201 של Digital Millennium Copyright Act אוסר לעקוף אמצעים טכנולוגיים ששולטים בגישה ליצירות מוגנות בזכויות יוצרים. האיסור הזה נפרד מהפרת זכויות יוצרים, כך שאדם יכול לעבור על הכלל בלי להעתיק דבר. החוק כולל חריגים, ביניהם section 1201(j), שמכסה בדיקות אבטחה מסוימות בתנאים שנקבעו. ה-Copyright Office גם מקיים הליך קביעת תקנות כל שלוש שנים שיכול להוסיף חריגים זמניים. הדף שלו על section 1201, שמקושר במקורות, מתאר את התהליך. בזמן הבדיקה ההליך העשירי כבר החל, וחריגים שחודשו בו תקפים לשלוש שנים, מאוקטובר 2027 עד אוקטובר 2030. לפני הסתמכות על חריג כלשהו קוראים את החוק ואת התקנות הנוכחיות. הטקסט הוא שקובע, לא סיכום.
למדינות אחרות יש כללים משלהן. שלוש השכבות שלמעלה הן דוגמאות לאופן שבו השאלות מנוסחות, ולא מפה של תחום השיפוט שלך.
מכאן נובעות שלוש נקודות מעשיות. מנתחים תוכנה בבעלותך, builds משלך או חומר שיש לך הרשאה לבדוק. עבודת בדיקה נשארת בתוך מערכות שבשליטתך או שיש לך עליהן הרשאה בכתב. אם יש בפרויקט סיכון משפטי, שואלים עורך דין לפני שמתחילים, ולא אחרי שמפרסמים.
הדרך הבטוחה ביותר ללמוד היא על הקוד שלך. כותבים תוכנית קטנה, מקמפלים אותה ומנתחים את התוצאה. המקור ידוע, כך שאפשר לבדוק כל ניחוש מול האמת. כך גם נמנעים מהשאלות המשפטיות שמגיעות עם תוכנה של צד שלישי ומהסיכון של הרצת דוגמה לא מוכרת.
שני כללים שומרים על תרגול נקי. לא מריצים בינאריים לא מוכרים על המחשב היומיומי. לא מכוונים כלי ניתוח או סריקות למערכות שאינן בבעלותך ואין לך הרשאה בכתב לבדוק, כולל אתרים שנראים תמימים. המאמר הבא, ניתוח ראשון ב-Ghidra עם בינארי שקימפלת בעצמך, מלווה פרויקט ראשון על תוכנית משלך.
לומדים את המעבד שבודקים, בדרך כלל x86-64 או ARM64 במחשבים מודרניים, ואיך מערכת ההפעלה מעבירה ארגומנטים לפונקציות. מקמפלים את אותו קובץ C קטן פעמיים עם קומפיילר C מהימן, פעם עם מידע דיבוג ופעם אחרי strip, ומשווים מה נשאר. קוראים את התיעוד של Ghidra ואת ה-README של הפרויקט. אחר כך ממשיכים למאמר המעשי שבא אחרי זה.
הנדסה לאחור
השאירו את Ghidra, radare2 או IDA. הדביקו את פלט ה-decompiler והמודל מסביר אותו וכותב את הסקריפט, בלי לסרב. triage סטטי למבט ראשון.
הנדסה לאחור
לקמפל בדיקת רישיון לדוגמה ב-C בשתי דרכים, ואז למצוא ב-Ghidra את המחרוזת, לעקוב אחרי ההפניות ולהשוות את הדקומפיילר לקוד המקור.