طرق تشويش الوحدة والحماية ضد الاختراق

لقد قمت أخيرًا بإصدار game الذي كنت تعمل بجد عليه، وربما أضفت leaderboard لإضافة التحدي إلى اللعبة. ولكن تمر الأيام وتلاحظ ظهور بعض اللاعبين على رأس لوحة النتائج بنتائج عالية بشكل غير واقعي. أول ما يتبادر إلى ذهنك هو بالطبع أنهم يقومون بالقرصنة، لكن كيف يفعلون ذلك؟

الجواب هو أنهم على الأرجح يستخدمون برنامجًا لحقن قيمهم الخاصة في الذاكرة، وأكثر هذه البرامج شيوعًا هو Cheat Engine. الآن، في الألعاب الفردية، لا يهم القرصنة كثيرًا، ولكنها تصبح مشكلة عندما تكون لعبة متعددة اللاعبين يشارك فيها اللاعبون الآخرون.

في هذا المنشور، سأعرض كيفية جعل لعبتك أكثر أمانًا ضد مثل هذه الهجمات، الأمر الذي سيؤدي بدوره إلى تحسين تجربة اللاعبين غير المخترقين.

ملاحظة: تتناول هذه المقالة بإيجاز الهجمات الأكثر شيوعًا والحماية الأساسية منها. إذا كنت بحاجة إلى المزيد من الحلول الجاهزة فلا تتردد في التحقق من هذه الحزمة Asset Store Package.

عندما يتعلق الأمر بالقرصنة باستخدام Cheat Engine، هناك نوعان من الهجمات الأكثر شيوعًا: القرصنة السريعة وفحص القيمة.

سرعة الإختراق

نظرًا لكونه الأسهل في التنفيذ (يتطلب نقرتين فقط)، فإن 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.");
    }
}

يقارن البرنامج النصي أعلاه الوقت داخل اللعبة مع وقت الكمبيوتر (النظام). عادةً يتم تحديث المرتين بنفس المعدل (بافتراض ضبط TimeScale على 1)، ولكن عند تنشيط SpeedHack، فإنه يعمل على تسريع وتيرة التحديث داخل اللعبة، مما يؤدي إلى تراكم الوقت داخل اللعبة أسرع.

بمجرد أن يصبح الفارق بين المرتين كبيرًا جدًا (في هذه الحالة 7 ثوانٍ، ولكن يمكنك اختيار أي قيمة، فقط تأكد من أنها ليست صغيرة جدًا لتجنب النتائج الإيجابية الخاطئة) يستدعي البرنامج النصي طريقة SpeedhackDetected() التي تشير إلى وجود SpeedHack.

لاستخدام البرنامج النصي تأكد من أنه مرفق بأي كائن في المشهد.

مسح القيمة

مسح القيمة هو عملية للعثور على القيم ذات الصلة في الذاكرة المخصصة للعبة واستبدالها بقيم مختلفة. تُستخدم بشكل شائع لزيادة صحة اللاعب أو ذخيرة السلاح أو أي قيمة من شأنها أن تمنح المتسلل ميزة غير عادلة في اللعبة.

من الناحية الفنية، يمكن استبدال/تغيير كل قيمة في اللعبة، ولكن هل يعني ذلك أن جميعها بحاجة إلى الحماية؟ ليس بالضرورة. بشكل عام، يستهدف المتسللون المبتدئون فقط القيم التي يتم عرضها على الشاشة والمعروفة فيما يتم استخدامها (على سبيل المثال، صحة اللاعب، والذخيرة، وما إلى ذلك). لذلك في معظم الأوقات تحتاج فقط إلى حماية قيم "exposed".

لقطة شاشة للعبة Unity FPS

على سبيل المثال، في لقطة الشاشة أعلاه، تعد كل قيمة على الشاشة هدفًا محتملاً للاختراق.

لذا فإن السؤال هو، كيف نحمي القيم المهمة من هجوم فحص القيمة؟ الجواب هو التعتيم.

التعتيم هو إجراء جعل شيء غامضًا أو غير واضح أو غير مفهوم.

هناك العديد من الطرق لتعتيم متغير، ولكنني سأستخدم طريقة أسميها 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;
    }
}

سيتم استخدام البرنامج النصي أعلاه لإنشاء رقم عشوائي وطريقتين بسيطتين لتعتيم القيم وإزالة التشويش منها.

  • لننتقل الآن إلى مثال عادي للبرنامج النصي دون أي تشويش:
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");
    }
}

يحتوي البرنامج النصي أعلاه على متغيرين بسيطين: الصحة (العائمة) والذخيرة (كثافة العمليات). يتم عرض كلا المتغيرين على الشاشة:

تعتبر هذه الطريقة للقيام بالأشياء بسيطة ومريحة من حيث الصيانة، ولكن سيتمكن المتسللون بسهولة من مسح القيم والكتابة فوقها باستخدام 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 Package.

المقالات المقترحة
مقدمة إلى لغة البرمجة النصية Unity C#
إنشاء محاكي حركة المرور في الوحدة
تنفيذ معدل التحديث المخصص في الوحدة
دليل لتحميل المشهد في الوحدة
الأساليب في بداية وقت التشغيل التي تقوم بتهيئة القيم في الوحدة
مقارنة LateUpdate وFixedUpdate في الوحدة
الوحدة تنفذ أصوات الخطوات