לדלג לתוכן

7.4 כלי Ghidra הרצאה

בהרצאה הזו נלמד על Ghidra - כלי הנדסה הפוכה חינמי וקוד פתוח שפותח על ידי ה-NSA.
גידרה היא אחד הכלים החזקים ביותר בתחום, והיא כוללת disassembler ו-decompiler מלאים - כלומר, היא לא רק מראה לנו אסמבלי, אלא גם ממירה את האסמבלי בחזרה לקוד דמוי C שקריא בהרבה.

למה Ghidra?

עד עכשיו השתמשנו ב-objdump וב-GDB כדי לנתח קוד. הכלים האלו מצוינים, אבל כשמנתחים תוכנה גדולה עם מאות או אלפי פונקציות, צריך כלי שנותן תמונה רחבה יותר.

גידרה נותנת לנו:
- תצוגת disassembly מסודרת עם שמות, תיוגים, והפניות צולבות
- decompiler שממיר אסמבלי בחזרה לקוד C קריא - זו התכונה החזקה ביותר!
- ניווט קל בין פונקציות, מחרוזות, ייבואים ויצואים
- יכולת לשנות שמות, להוסיף הערות, ולהגדיר טיפוסים
- תצוגה גרפית של זרימת בקרה

התקנה והגדרה

גידרה דורשת Java (גרסה 17 ומעלה). התקינו JDK:

sudo apt install openjdk-17-jdk -y

הורידו את Ghidra מהאתר הרשמי:

https://ghidra-sre.org/

חלצו את הקובץ והריצו:

unzip ghidra_*.zip
cd ghidra_*
./ghidraRun

בפתיחה הראשונה Ghidra תבקש מכם להגדיר את ה-JDK path. בדרך כלל היא מוצאת אותו אוטומטית.


יצירת פרויקט וייבוא קובץ בינארי

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

יצירת פרויקט חדש:

  1. פתחו את Ghidra
  2. בחרו File -> New Project
  3. בחרו Non-Shared Project (לעבודה מקומית)
  4. תנו שם לפרויקט ובחרו תיקייה

ייבוא קובץ בינארי:

  1. גררו קובץ בינארי לתוך חלון הפרויקט, או בחרו File -> Import File
  2. גידרה תזהה אוטומטית את סוג הקובץ (ELF, PE, Mach-O וכו')
  3. לחצו OK ואז לחצו כפול על הקובץ כדי לפתוח אותו ב-CodeBrowser
  4. גידרה תשאל אם לנתח את הקובץ (Analyze) - אישרו. הניתוח האוטומטי מזהה פונקציות, מחרוזות, הפניות צולבות ועוד.

החלונות הראשיים - main windows

כשפותחים קובץ בינארי ב-CodeBrowser, מופיעים כמה חלונות חשובים:

חלון הListing - תצוגת disassembly

זהו החלון המרכזי. הוא מציג את קוד האסמבלי של התוכנית, עם:
- כתובות זיכרון בצד שמאל
- שמות פונקציות ותיוגים (labels)
- הפניות צולבות (cross-references) - מי קורא לפונקציה הזו? לאן היא קופצת?
- הערות אוטומטיות שגידרה מוסיפה

זה דומה לפלט של objdump, אבל הרבה יותר מסודר ואינטראקטיבי.

חלון הDecompiler

כאן קורה הקסם. כשאתם לוחצים על פונקציה ב-Listing, ה-Decompiler מציג את קוד C מתורגם של אותה פונקציה.

למשל, אם באסמבלי יש לנו:

push   rbp
mov    rbp, rsp
sub    rsp, 0x10
mov    dword ptr [rbp-0x4], edi
mov    eax, dword ptr [rbp-0x4]
imul   eax, eax
leave
ret

ה-Decompiler יציג משהו כזה:

int FUN_00401000(int param_1) {
    return param_1 * param_1;
}

הרבה יותר קריא! אבל שימו לב - השמות אוטומטיים (FUN_00401000, param_1). חלק מהעבודה שלנו כחוקרים הוא לשנות את השמות למשהו משמעותי.

עץ הסמלים - Symbol Tree

חלון שמראה את כל הסמלים בתוכנית, מאורגנים לפי קטגוריות:
- פונקציות - Functions: כל הפונקציות שגידרה זיהתה
- תיוגים - Labels: כתובות עם שמות
- ייבואים - Imports: פונקציות מספריות חיצוניות (כמו printf, malloc מ-libc)
- יצואים - Exports: פונקציות שהתוכנית חושפת החוצה

מנהל טיפוסי הנתונים - Data Type Manager

כאן מגדירים ומנהלים struct-ים, enum-ים, typedef-ים וטיפוסים אחרים. כשאנחנו מבינים שפוינטר מצביע על struct מסוים, אנחנו מגדירים אותו כאן ומחילים אותו - וה-Decompiler מתעדכן בהתאם.

גרף הפונקציה - Function Graph

תצוגה גרפית של זרימת הבקרה בפונקציה. כל בלוק (basic block) מוצג כריבוע, וחיצים מראים את הקפיצות המותנות ולולאות. זה עוזר מאוד להבין לוגיקה מורכבת - if-ים מסועפים, לולאות מקוננות, וכו'.

אפשר לעבור לתצוגה הגרפית דרך Window -> Function Graph.


ניווט בגידרה - navigation

לחיצה כפולה

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

מקש G - מעבר לכתובת - Go to address

לחצו G ותוכלו להקליד כתובת זיכרון ולקפוץ ישירות אליה.

מקש L - שינוי שם - Label

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

מקש T - שינוי טיפוס - reTtype

לחצו T על משתנה כדי לשנות את הטיפוס שלו. למשל, אם גידרה חושבת שמשהו הוא int אבל אתם יודעים שזה char * - שנו אותו.

חזרה אחורה וקדימה

כמו בדפדפן - הכפתורים Back/Forward (או Alt+Left/Right) מחזירים אתכם למקום הקודם.


הDecompiler - הבנת הפלט

ה-Decompiler הוא הכלי החזק ביותר בגידרה. הוא ממיר אסמבלי לקוד דמוי C. אבל חשוב להבין כמה דברים:

הוא לא מושלם

הפלט הוא קירוב של הקוד המקורי, לא עותק מדויק. שמות משתנים הם אוטומטיים:
- local_10 - משתנה מקומי ב-offset 0x10 מ-rbp
- param_1 - הפרמטר הראשון לפונקציה
- iVar1 - משתנה int שגידרה יצרה
- uVar2 - משתנה unsigned שגידרה יצרה

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

לפעמים הטיפוסים שגויים

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

למשל, אם גידרה מציגה:

*(int *)(local_10 + 4) = 0x41;

ואתם יודעים ש-local_10 הוא מצביע ל-struct, תוכלו להגדיר את ה-struct ב-Data Type Manager, לשנות את הטיפוס של local_10, ופתאום הקוד יראה ככה:

my_struct->value = 0x41;

שחזור struct-ים

תהליך חשוב: כשרואים גישה לoffset-ים קבועים מתוך מצביע, סביר שמדובר ב-struct.
1. פתחו את Data Type Manager
2. צרו struct חדש
3. הגדירו את השדות לפי ה-offset-ים שראיתם
4. חזרו לDecompiler ושנו את הטיפוס של המצביע ל-struct החדש


הפניות צולבות - Cross-References (XREF)

זו אחת התכונות החזקות ביותר להבנת זרימת הקוד. הפניות צולבות עונות על השאלות:
- מאיפה קוראים לפונקציה הזו? - מי משתמש בה?
- איפה משתמשים במחרוזת הזו? - איזו פונקציה מדפיסה את ההודעה הזו?
- לאן קופצים מהכתובת הזו? - מה קורה אחרי הif?

איך למצוא הפניות:

  • לחצו ימני על פונקציה/משתנה/מחרוזת -> References -> Find references to
  • או פשוט הסתכלו על ההערות בצד - גידרה מציגה XREF אוטומטית ליד כל פונקציה

למשל, אם רואים:

XREF[2]: main:00401050(c), check_input:00401120(c)

זה אומר שהפונקציה נקראת משני מקומות - מ-main ומ-check_input.

הפניות צולבות הן הדרך הכי יעילה לעקוב אחרי זרימת הקוד בתוכנית גדולה.


שינוי שמות והוספת הערות - annotating

ככל שאתם מנתחים יותר, הקוד צריך להפוך ליותר קריא. הנה הכלים:

שינוי שם פונקציה

לחצו על שם הפונקציה ולחצו L. שנו מ-FUN_00401000 ל-check_password כשאתם מבינים מה היא עושה.

שינוי שם משתנה בDecompiler

לחצו על משתנה ב-Decompiler ולחצו L. שנו מ-local_10 ל-user_input.

הוספת הערות

לחצו ; (נקודה פסיק) או לחצו ימני -> Comment. אפשר להוסיף הערות ב:
- EOL Comment - הערה בסוף שורה
- Pre Comment - הערה לפני שורה
- Post Comment - הערה אחרי שורה
- Plate Comment - הערה גדולה בראש פונקציה

שינוי טיפוסים

לחצו T על משתנה כדי לשנות את הטיפוס שלו. זה משפיע ישירות על איכות הפלט של ה-Decompiler.


חיפוש - searching

חיפוש מחרוזות - Search for Strings

בחרו Search -> For Strings. גידרה תמצא את כל המחרוזות בקובץ הבינארי.

זה מאוד שימושי! מחרוזות כמו הודעות שגיאה, URL-ים, שמות קבצים, וכו' נותנות רמזים חשובים על מה התוכנית עושה. מ-Search -> For Strings אפשר ללחוץ כפול על מחרוזת, ואז להשתמש בXREF כדי למצוא איזו פונקציה משתמשת בה.

חיפוש בזיכרון - Search for bytes

בחרו Search -> Memory. מאפשר לחפש רצף בתים ספציפי בכל הקובץ.

חיפוש פונקציות

השתמשו ב-Symbol Tree או ב-Search -> For Address Tables.


סקריפטים בגידרה - Ghidra scripting

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

הכניסה לסקריפטים דרך Window -> Script Manager. לא ניכנס לעומק של הנושא הזה כרגע, אבל דעו שהאפשרות קיימת.


עבודה מעשית - walkthrough

בואו נעבור על תהליך עבודה טיפוסי עם גידרה:

שלב 1 - ייבוא וניתוח

  1. פתחו פרויקט חדש
  2. ייבאו את הבינארי
  3. תנו לגידרה לנתח אותו (Auto Analysis)

שלב 2 - סקירה ראשונית

  1. בדקו את רשימת הפונקציות ב-Symbol Tree
  2. חפשו מחרוזות מעניינות (Search -> For Strings)
  3. מצאו את main (או את נקודת הכניסה) ולחצו עליה

שלב 3 - ניתוח הDecompiler

  1. קראו את הקוד בDecompiler
  2. התחילו לשנות שמות של פונקציות ומשתנים
  3. שנו טיפוסים כשצריך

שלב 4 - מעקב אחר זרימת הקוד

  1. השתמשו ב-XREF כדי לעקוב אחרי קריאות לפונקציות
  2. לחצו כפול כדי לנווט
  3. הוסיפו הערות במקומות חשובים

שלב 5 - תיעוד

  1. וודאו שכל הפונקציות החשובות קיבלו שמות משמעותיים
  2. הוסיפו Plate Comments לפונקציות מרכזיות
  3. שמרו את הפרויקט

השוואה - Ghidra מול כלים אחרים

תכונה Ghidra IDA Pro radare2/Cutter
מחיר חינם (קוד פתוח) מסחרי (אלפי דולרים) חינם (קוד פתוח)
ממשק גרפי (Java) גרפי (Qt) שורת פקודה / גרפי
דיקומפיילר כלול תוסף נפרד (Hex-Rays) כלול (r2ghidra)
ארכיטקטורות המון המון המון
סקריפטים Python / Java Python / IDC Python / r2pipe
קהילה גדלה במהירות ותיקה ומבוססת פעילה

גידרה מצוינת כנקודת התחלה - היא חינמית, כוללת decompiler, ויש לה קהילה גדלה. IDA Pro נחשבת לסטנדרט בתעשייה אבל עולה הרבה כסף. radare2 (ו-Cutter הממשק הגרפי שלה) היא אלטרנטיבה חינמית שעובדת גם משורת הפקודה - שימושית מאוד לאוטומציה.

לצורך הקורס שלנו, נתמקד בגידרה.


סיכום

גידרה היא כלי חזק שמשלב disassembler, decompiler, וסביבת ניתוח מלאה. הדבר הכי חשוב לזכור:
- ה-Decompiler ממיר אסמבלי לקוד C קריא - זה חוסך המון זמן
- הפניות צולבות (XREF) הן הדרך לעקוב אחרי זרימת הקוד
- שינוי שמות הוא חלק מהותי מתהליך הניתוח - ככל שאתם מבינים יותר, אתם משנים שמות ומשפרים את הקריאות
- חיפוש מחרוזות הוא בדרך כלל הצעד הראשון בניתוח - מחרוזות נותנות רמזים חשובים

בשילוב עם GDB שלמדנו בנושאים הקודמים, יש לנו כלים חזקים מאוד לניתוח סטטי (Ghidra) ודינמי (GDB) של תוכנות.