Unity 난독화 방법 및 해킹 방지 보호

열심히 노력한 게임을 마침내 출시했으며, 게임에 도전 과제를 추가하기 위해 리더보드를 추가할 수도 있습니다. 하지만 며칠이 지나고 일부 플레이어가 비현실적으로 높은 점수를 받아 점수판 상단에 나타나는 것을 발견했습니다. 당신의 첫 번째 생각은 물론 그들이 해킹하고 있다는 것입니다. 그러나 그들은 어떻게 그렇게 할 수 있습니까?

대답은 그들은 자신의 값을 메모리에 주입하기 위해 프로그램을 사용할 가능성이 가장 높으며 그러한 프로그램 중 가장 인기 있는 프로그램은 치트 엔진입니다. 이제 싱글 플레이어 게임에서는 해킹이 그다지 중요하지 않지만, 다른 플레이어가 참여하는 멀티 플레이어 게임에서는 문제가 됩니다.

이 게시물에서는 이러한 공격으로부터 게임을 더욱 안전하게 만드는 방법을 보여드리겠습니다. 이를 통해 해킹을 하지 않는 플레이어의 경험도 향상될 것입니다.

참고: 이 문서에서는 가장 일반적인 공격과 이에 대한 기본적인 보호에 대해서만 간략하게 설명합니다. 더 많은 기본 솔루션이 필요한 경우 이 Asset Store 패키지를 확인하세요.

치트 엔진을 사용한 해킹에는 가장 일반적인 2가지 공격이 있습니다: Speed ​​Hacking과 Value Scanning.

스피드 해킹

실행하기 가장 쉬운(2번의 클릭만 필요) 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초이지만 임의의 값을 선택할 수 있습니다. 거짓 긍정을 피하기 위해 너무 작지 않은지 확인하세요) 스크립트는 SpeedHack의 존재를 알리는 SpeedhackDetected() 메서드를 호출합니다.

스크립트를 사용하려면 장면의 모든 개체에 연결되어 있는지 확인하세요.

가치 스캐닝

값 스캐닝은 게임에 할당된 메모리에서 관련 값을 찾아 다른 값으로 덮어쓰는 과정입니다. 플레이어 체력, 무기 탄약 또는 해커에게 게임에서 불공평한 이점을 제공하는 모든 값을 늘리는 데 가장 일반적으로 사용됩니다.

기술적으로 말하면, 게임의 모든 값은 덮어쓰거나 변경할 수 있지만, 이것이 모두 보호되어야 한다는 뜻인가요? 반드시 그런 것은 아닙니다. 일반적으로 초보 해커는 화면에 표시되는 값만 목표로 삼고 그 값이 무엇에 사용되는지 알고 있습니다(예: 플레이어 체력, 탄약 등). 따라서 대부분의 경우 "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");
    }
}

위의 스크립트에는 health(float) 및 ammo(int)라는 2개의 간단한 변수가 포함되어 있습니다. 두 변수가 모두 화면에 표시됩니다.

이러한 방식은 유지 관리 측면에서 간단하고 편리하지만 해커는 치트 엔진이나 유사한 소프트웨어를 사용하여 쉽게 값을 스캔하고 덮어쓸 수 있습니다.

  • 다음은 동일한 스크립트이지만 '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(값)).

그런 다음 값을 표시할 때 SC_Obf.Deobfuscate(value)을 호출하여 즉시 난독 해제하여 실제 값을 표시합니다.

해커는 10030을 검색하려고 시도하지만 실제 값이 완전히 다르기 때문에 찾을 수 없습니다.

난독화된 값을 조작하려면(예: 체력 빼기) 먼저 값을 난독화한 다음 필요한 값을 뺀 다음 최종 결과를 다시 난독화합니다.

더 발전된 솔루션을 원하시면 이 Asset Store 패키지를 확인해보세요.