שיטות ערפול של Unity והגנה נגד פריצה

סוף סוף הוצאתם את משחק שעבדתם עליו כל כך קשה, ואולי אפילו הוספת leaderboard כדי להוסיף אתגר למשחק. אבל ימים חולפים ואתה מבחין בכמה שחקנים שצצים על גבי לוח התוצאות עם ציונים גבוהים באופן לא מציאותי. המחשבה הראשונה שלך היא כמובן שהם פורצים, אבל איך הם עושים את זה?

התשובה היא שככל הנראה הם משתמשים בתוכנית כדי להחדיר ערכים משלהם לזיכרון, כשהתוכנות הפופולריות ביותר הן Cheat Engine. עכשיו, במשחקים לשחקן יחיד, האקינג לא באמת משנה כל כך, אבל זה הופך לבעיה כאשר מדובר במשחק מרובה משתתפים שבו מעורבים השחקנים האחרים.

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

הערה: מאמר זה מכסה רק בקצרה את ההתקפות הנפוצות ביותר ואת ההגנה הבסיסית מפניהן. אם אתה צריך עוד פתרון מחוץ לקופסה, אתה מוזמן לבדוק את Asset Store החבילה הזו.

כשזה מגיע לפריצה עם Cheat Engine ישנן 2 התקפות נפוצות ביותר: Speed ​​​​Hacking וסריקת ערך.

גרזן מהירות

בהיותו הקל ביותר לביצוע (דורש רק 2 קליקים), ה-Speed ​​Hack הוא בדרך כלל הבחירה הראשונה עבור משתמשים מתחילים.

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

למרבה המזל, יש דרך לזהות את הפריצה הזו ב-Unity. בדוק את התסריט למטה:

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

SC_SpeedhackDetector.cs

using UnityEngine;
using System;

public class SC_SpeedhackDetector : MonoBehaviour
{
    //Speed hack protection
    public int timeDiff = 0; 
    int previousTime = 0;
    int realTime = 0;
    float gameTime = 0;
    bool detected = false;

    // Use this for initialization
    void Start()
    {
        previousTime = DateTime.Now.Second;
        gameTime = 1;
    }

    // Update is called once per frame 
    void FixedUpdate()
    {
        if (previousTime != DateTime.Now.Second)
        {
            realTime++;
            previousTime = DateTime.Now.Second;

            timeDiff = (int)gameTime - realTime;
            if (timeDiff > 7)
            {
                if (!detected)
                {
                    detected = true;
                    SpeedhackDetected();
                }
            }
            else
            {
                detected = false;
            }
        }
        gameTime += Time.deltaTime;
    }

    void SpeedhackDetected()
    {
        //Speedhack was detected, do something here (kick player from the game etc.)
        print("Speedhack detected.");
    }
}

התסריט שלמעלה משווה את הזמן במשחק עם הזמן של המחשב (המערכת). בדרך כלל שני הזמנים מתעדכנים באותו קצב (בהנחה ש-Time.timeScale מוגדר ל-1), אך כאשר ה-SpeedHack מופעל, הוא מאיץ את תדירות העדכון במשחק, מה שגורם לזמן המשחק להצטבר. מהיר יותר.

ברגע שההבדל בין שני הזמנים הופך להיות גדול מדי (במקרה זה 7 שניות, אבל אתה יכול לבחור כל ערך, רק וודא שהוא לא קטן מדי כדי למנוע תוצאות חיוביות שגויות) הסקריפט קורא לשיטת SpeedhackDetected() שמסמנת את נוכחותו של SpeedHack.

כדי להשתמש בסקריפט ודא שהוא מצורף לכל אובייקט בסצנה.

סריקת ערך

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

מבחינה טכנית, ניתן להחליף/לשנות כל ערך במשחק, אבל האם זה אומר שצריך להגן על כולם? לא בהכרח. בדרך כלל, האקרים מתחילים מכוונים רק לערכים המוצגים על המסך וידועים למה הם משמשים (לדוגמה בריאות השחקן, תחמושת וכו'). אז רוב הזמן רק ערכי "exposed" צריכים להיות מוגנים.

צילום מסך של משחק Unity FPS

לדוגמה בצילום המסך למעלה, כל ערך על המסך הוא יעד פוטנציאלי לפריצה.

אז השאלה היא איך להגן על הערכים החשובים מפני מתקפת Value Scanning? התשובה היא בלבול.

ערפול היא הפעולה של הפיכת משהו לא ברור, לא ברור או לא מובן.

ישנן דרכים רבות לטשטש משתנה, אבל אני אשתמש בשיטה שאני קורא לה Randomizer. הערך האקראי נוצר בהתחלה, לאחר מכן הערך האמיתי מופחת ממנו (בהמשך מסתיר אותו), ולאחר מכן בעת ​​הצורך, הערך הנסתר מופחת מערך אקראי שנוצר, כשההפרש הוא המספר המקורי. המפתח הוא שיהיה ערך שמוצג על המסך בעל ערך שונה לחלוטין מהמשתנה, מה שמוביל האקרים בצורה שגויה לחלוטין בעת ​​הסריקה.

  • צור סקריפט חדש, קרא לו 'SC_Obf' והדבק בתוכו את הקוד למטה:

SC_Obf.cs

using UnityEngine;

public class SC_Obf : MonoBehaviour
{
    static float random = -1;

    public static void Initialize()
    {
        if(random == -1)
        {
            random = Random.Range(10000, 99999);
        }
    }

    public static float Obfuscate(float originalValue)
    {
        return random - originalValue;
    }

    public static float Deobfuscate(float obfuscatedValue)
    {
        return random - obfuscatedValue;
    }
}

הסקריפט שלמעלה ישמש ליצירת מספר אקראי ו-2 שיטות פשוטות לטשטש ולטשטוש הערכים.

  • כעת נעבור לדוגמה רגילה של סקריפט ללא כל ערפול:
using UnityEngine;

public class SC_Test : MonoBehaviour
{
    public float health = 100;
    public int ammo = 30;

    public void Damage(float points)
    {
        health -= points;
    }

    void OnGUI()
    {
        GUI.Label(new Rect(5, 5, 150, 25), health + " HP");
        GUI.Label(new Rect(5, 30, 150, 25), ammo + " Ammo");
    }
}

התסריט למעלה מכיל 2 משתנים פשוטים: בריאות (float) ותחמושת (int). שני המשתנים מוצגים על המסך:

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

  • הנה אותו סקריפט, אבל תוך שימוש בשיטות ערפול מה-'SC_Obf.cs':
using UnityEngine;

public class SC_Test : MonoBehaviour
{
    public float health;
    public int ammo;

    void Awake()
    {
        SC_Obf.Initialize();
        health = SC_Obf.Obfuscate(100);
        ammo = (int)SC_Obf.Obfuscate(30);
    }

    public void Damage(float points)
    {
        health = SC_Obf.Obfuscate(SC_Obf.Deobfuscate(health) - points);
    }

    void OnGUI()
    {
        GUI.Label(new Rect(5, 5, 150, 25), SC_Obf.Deobfuscate(health) + " HP");
        GUI.Label(new Rect(5, 30, 150, 25), SC_Obf.Deobfuscate(ammo) + " Ammo");
    }
}

במקום לאתחל משתני בריאות ותחמושת ישירות, אנו מאתחלים אותם בהתחלה ב-void Awake() (הקפד לקרוא SC_Obf.Initialize() לפני הקצאת הערכים באמצעות SC_Obf.Obfuscate(value)).

לאחר מכן, בעת הצגת הערכים, אנו מסלקים אותם תוך כדי תנועה על ידי קריאה ל-SC_Obf.Deobfuscate(value) ובכך מציגים את הערכים האמיתיים.

ההאקר ינסה לחפש 100 ו-30 אך לא יצליח למצוא אותם כי הערכים האמיתיים שונים לחלוטין.

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

לפתרון מתקדם יותר, אל תהסס לבדוק את Asset Store חבילה זו.