לדלג לתוכן

7.4 כלי Ghidra פתרון

פתרון תרגיל 1 - פתיחת בינארי בגידרה

  1. גידרה תזהה פונקציות רבות - גם פונקציות פנימיות של libc וגם פונקציות של התוכנית. המספר המדויק תלוי בגרסת gcc ובמערכת, אבל בדרך כלל תראו כמה עשרות פונקציות (רובן פונקציות עזר של הריצה כמו _start, __libc_csu_init וכו').

  2. מציאת main: כיוון שעשינו strip, אין סימבולים. הדרך למצוא main:

  3. מצאו את entry (נקודת הכניסה של ה-ELF)
  4. ב-entry, מחפשים את הקריאה ל-__libc_start_main
  5. הפרמטר הראשון (שנמצא ב-rdi לפני הקריאה) הוא הכתובת של main
  6. לחצו כפול על הכתובת הזו - זו main

פתרון תרגיל 2 - קריאת הDecompiler

  1. פלט ה-Decompiler של main ייראה בערך כך:

    undefined8 FUN_00401136(void) {
        FUN_00401119("Student");
        FUN_00401119("World");
        return 0;
    }
    

    גידרה מראה שmain קוראת לאותה פונקציה פעמיים עם מחרוזות שונות.

  2. לשינוי שם: לחצו על FUN_00401119 ואז L, הקלידו greet.

  3. בתוך greet, ה-Decompiler מראה:

    void greet(char *param_1) {
        printf("Hello, %s!\n", param_1);
    }
    

    לחצו על param_1, לחצו L, הקלידו name.

  4. להוספת הערה: לחצו ימני על שם הפונקציה -> Comment -> Set Plate Comment. כתבו: "Prints a greeting message with the given name".


פתרון תרגיל 3 - מציאת מחרוזות

  1. אחרי פתיחה בגידרה, הולכים ל-Search -> For Strings.

  2. ברשימת המחרוזות נראה:

  3. "Enter password: "
  4. "s3cretP@ss"
  5. "Access granted!"
  6. "Access denied!"
  7. "%63s"

  8. הסיסמה היא s3cretP@ss. מצאנו אותה פשוט על ידי חיפוש מחרוזות - היא מופיעה בטקסט גלוי בתוך הבינארי כי strcmp משווה ישירות למחרוזת.

  9. לחיצה כפולה על "Access granted!" מביאה אותנו לכתובת בsection של הdata. שם נראה XREF שמצביע לפונקציה check_password (או FUN_XXXXX אם אין סימבולים). לחצו כפול על ה-XREF כדי להגיע לפונקציה שמשתמשת במחרוזת.


פתרון תרגיל 4 - הבנת פונקציה

  1. מוצאים את main כמו שלמדנו (דרך entry ו-__libc_start_main).

  2. מ-main נראה קריאה לפונקציה עם שני ארגומנטים: 7 ו-5.

  3. פלט ה-Decompiler של mystery ייראה בערך כך:

    int FUN_00401136(int param_1, int param_2) {
        int local_c = 0;
        while (0 < param_2) {
            if ((param_2 & 1) != 0) {
                local_c = local_c + param_1;
            }
            param_1 = param_1 << 1;
            param_2 = param_2 >> 1;
        }
        return local_c;
    }
    

    הפונקציה מבצעת כפל באמצעות אלגוריתם Russian Peasant Multiplication. היא בודקת כל ביט של param_2, ואם הוא דלוק - מוסיפה את param_1 לתוצאה. בכל איטרציה param_1 מוכפל ב-2 (shift שמאלה) ו-param_2 מחולק ב-2 (shift ימינה).

עבור 7 כפול 5:
- ביט 0 של 5 דלוק: result += 7 (result = 7)
- ביט 1 של 5 כבוי: לא מוסיפים
- ביט 2 של 5 דלוק: result += 28 (result = 35)
- התוצאה: 35 = 7 * 5

  1. שמות משמעותיים:
  2. הפונקציה: multiply
  3. param_1 -> a
  4. param_2 -> b
  5. local_c -> result

פתרון תרגיל 5 - ניתוח מלא

  1. פותחים את הבינארי בגידרה ומוצאים את main.

  2. ב-main נראה קריאות לשלוש פונקציות printf ולשתי פונקציות נוספות (encode ו-decode).

  3. ה-Decompiler של encode יראה בערך:

    void FUN_00401136(char *param_1, int param_2) {
        int local_c;
        for (local_c = 0; param_1[local_c] != '\0'; local_c++) {
            param_1[local_c] = param_1[local_c] ^ param_2;
        }
    }
    

    הפונקציה עוברת על כל תו במחרוזת ומבצעת XOR עם המפתח (param_2).

  4. encode ו-decode הן אותו דבר כי XOR היא פעולה הפיכה:

  5. אם a ^ key = b
  6. אז b ^ key = a
  7. כלומר XOR פעמיים עם אותו מפתח מחזיר את הערך המקורי

בגידרה תראו ש-decode פשוט קוראת ל-encode - הן באמת אותה פונקציה.

  1. שמות מוצעים:
  2. הפונקציה הראשונה: xor_encode או xor_cipher
  3. הפרמטר הראשון: str
  4. הפרמטר השני: key
  5. המשתנה המקומי: i (האינדקס בלולאה)
  6. הפונקציה השנייה: xor_decode (ולציין בהערה שהיא פשוט קוראת ל-encode)
  7. ב-main: secret למערך התווים, key למפתח ה-XOR