Anti-Cheat Toolkit / 유니티 메모리 변조 방지

에셋 스토어 링크

https://assetstore.unity.com/packages/tools/utilities/anti-cheat-toolkit-2021-202695?aid=1101l7zGS 

 

Anti-Cheat Toolkit 2021 | 유틸리티 도구 | Unity Asset Store

Use the Anti-Cheat Toolkit 2021 from Code Stage on your next project. Find this utility tool & more on the Unity Asset Store.

assetstore.unity.com

 

가격

80달러

 

어떤 에셋인가요?

유니티로 만든 게임에서 메모리 후킹을 통한 데이터 변조 치팅방지가 주 기능이다.

고전게임에서는 치트오메틱등으로 돈이나 공격력 등을 변조하는데,

모바일게임에서도 비슷한 툴이 존재하기 때문에 문제가 될수 있다.

이를 위해 Obscured 형식의 메모리 탐지로부터 '조금 더 안전한' 데이터 형식을 지원한다.

(클라인 이상 완전한 방어는 없다)

추가로 스피드 핵 방지 / 시간대 조작 치팅 방지 등을 지원함.

 

개인 평가

방범창을 달아놔도 마음먹은 도둑은 공구로 창살 떼고 들어오겠지만,

시간이 없어나 공구가 없는 도둑은 못들어오겠지.

 

주의할 점?

데이터 형식이 아직은 LINQ를 지원하지않는다고 한다.

 

해당 에셋으로는 메모리 변조만 가능하니, 코드쪽 보안은 취약하다.

때문에 에셋 제작자가 직접 코드 난독화 에셋인 Obfuscator를 같이 사용할 것을 권장하고있다.

 

https://assetstore.unity.com/packages/tools/utilities/obfuscator-48919?aid=1101l7zGS

 

Obfuscator | 유틸리티 | Unity Asset Store

Get the Obfuscator package from Beebyte and speed up your game development process. Find this & other 유틸리티 options on the Unity Asset Store.

assetstore.unity.com

 

 

 

 

 

 

 

 

Obscured 형식

딱히 특별하게 사용하는건 아니고,

그냥 데이터형식을 ObscuredInt / ObscuredFloat 등으로 넣고 사용하면된다. 

 

using CodeStage.AntiCheat.ObscuredTypes;

void Example()
{
      ObscuredInt localSecure = Random.Range(regular, secure);
      localSecure = localSecure - 2;
      int testValue = localSecure;
}

 

래핑 클래스인지 그냥 ObscuredInt = int 로 암시적 대입도되고

웬만한 계산식도 전부 가능하다.

 

 

메모리 치팅하는 유저 탐지

ObscuredCheatingDetector 라는 기능을 통해 메모리를 변조하는 유저를 감지하고,

콜백이벤트를 발생시킬 수 있다.

 

말 그대로 덫을 놓는건데, 일부러 골드랑 동일한 값을 가지는 int 변수를 두고,

그 값을 바꾸는 순간 계정을 정지시키는 악랄한 짓도 가능.. 

 

감지되는순간 부정사용이 확인됨 팝업을 띄우고, 계정정지 시키는게 보통인듯.

 

 

알수 없는 값 찾기로 뚫린다?

사실 치트 엔진들은 unknown Value 찾기 기능이 있어서, 암호화 되어있는 값을 찾아낼 수 있다.

 

 

개발사의 답변은 'RandomizeCryptoKey API를 섞어서 저항하라'라고 한다.

해당 부분에 대한 API 문서를 찾아보니,

 

https://codestage.net/uas_files/actk/api/struct_code_stage_1_1_anti_cheat_1_1_obscured_types_1_1_obscured_int.html

 

Anti-Cheat Toolkit: ObscuredInt Struct Reference

Inherits IObscuredType, IFormattable, IEquatable< ObscuredInt >, IComparable< ObscuredInt >, IComparable< int >, and IComparable. Use it instead of regular int for any cheating-sensitive variables. Regular type is faster and memory wiser comparing to the o

codestage.net

 

Allows to change current crypto key to the new random value and re-encrypt variable using it. Use it for extra protection against 'unknown value' search. Just call it sometimes when your variable doesn't change to fool the cheater.

현재 암호화 키를 새로운 임의의 값으로 변경하고 이를 사용하여 변수를 다시 암호화 할 수 있습니다. '알 수없는 값'검색에 대한 추가 보호를 위해 사용하십시오. 변수가 사기꾼을 속이는 것으로 바뀌지 않을 때 가끔 호출하십시오.

 

 -> 해석하자면 '이 메소드를 호출하면 변수의 현재 암호화 키가 임의의 값으로 변경' 된다는 것.

 

 

 

꽤 성능이슈가 발생할것같음.... 해당 부분 실험하신 분이 있어서 링크.

 

https://ajh322.tistory.com/224

 

 

Anti-Cheat 데이터 저장/로드하기

저장할 때는 기본형으로 저장하고, 로드하면서 암호화된 형식으로 바꾸는게 일반적일 것 같긴한데,

저장할 때도 암호화가 가능하다고 함.

 

All simple Obscured types have public static methods GetEnctypted() and SetEncrypted() to let you work with encrypted value from obscured instance. May be helpful if you’re going to use some custom saves engine

 

대략 가려진 상태로 저장할때는 GetEncrypted()가 유용하고,

가려진 상태로 로드할때는 SetEncrypted()가 유용하다는 설명

 

using CodeStage.AntiCheat.ObscuredTypes;
 
// you may edit this variable in
// inspector, just like regular int
public ObscuredInt secure = 1000;
 
// ...
 
public void DummyExample()
{
    int regular = 100;
     
    // common operations are supported
    secure++;
    secure += regular;
     
    // you can seamlessly mix obscured and regular types
    regular += 10 + secure / 2;
 
    ObscuredInt localSecure = Random.Range(regular, secure);
 
    // crypto key will be saved to this variable, use it to decrypt value back
    int encryptionKey;
     
    // you may get raw encrypted value of any obscured var
    int rawValue = localSecure.GetEncrypted(out encryptionKey);
     
    // or encrypt clean values (1234 - value, 80085 - key)
    int encrypted = ObscuredInt.Encrypt(1234, 80085);
}

 


You may generate random crypto keys for the Encrypt(value, key) methods using GenerateKey() API. Do not forget to store those keys in order to Decrypt() your value later.

 

혹은 GenerateKey()로 새로운 암호화 Key를 만들어서, Encrypt로 암호화시켜 저장하고, (키도 따로 저장해놔야됨)

저장된 암호화데이터와 키를 사용해서 Decrypt 할 수 있다는 듯.

 

댓글

Designed by JB FACTORY