치트 발견 - “정당한 획득 직후 값 변조”
Flutter 기반 텍스트 게임을 운영하면서, 일부 유저의 스탯 데이터에서 수상한 패턴을 발견했습니다.
예를 들어 공부 경험치 스탯을 조작한 유저의 로그를 보면, 게임 내 정당한 방식으로 경험치를 획득한 직후 값이 비정상적으로 변하는 흐름이 반복되고 있었습니다.
[12:03:01] STAT_A +5 → 105
[12:03:03] STAT_A ??? → 99999 ← 비정상
이 패턴이 의미하는 바는 명확했습니다. 유저가 정당하게 값을 획득하는 순간을 이용해 메모리에서 해당 값의 주소를 특정한 뒤, 직접 값을 덮어쓰고 있다는 것입니다. 전형적인 Cheat Engine 사용 정황이었습니다.
이런 분석이 가능했던 건, 게임 데이터 파일에 유저의 행동과 스탯 변화를 세세하게 로깅해두었기 때문입니다. 만약 로그가 부실했다면 원인을 추적하지 못했을 것입니다.
Cheat Engine은 어떻게 동작하는가

Cheat Engine은 가장 널리 알려진 메모리 스캐너/에디터 프로그램입니다. 동작 원리는 단순합니다.
- 게임 프로세스의 메모리를 스캔하여 특정 값(예: 현재 체력
100)을 가진 주소들을 찾습니다 - 게임 내에서 값을 변화시킵니다 (체력이
95로 바뀌도록 피격) - 변화된 값으로 다시 스캔하여 후보 주소를 좁힙니다
- 최종적으로 특정된 메모리 주소의 값을 원하는 값으로 덮어씁니다
핵심은 메모리에 저장된 실제 값과 게임이 표시하는 값이 동일하다는 전제에 기반한다는 점입니다. int health = 100이라면 메모리 어딘가에 100이라는 정수가 그대로 들어있고, Cheat Engine은 이 값을 검색해서 찾아냅니다.
그렇다면 방어 전략은 단순한데, 메모리에 저장되는 값과 실제 사용하는 값을 다르게 만들면 됩니다.
XOR 난독화 - 단순하지만 효과적인 방어
이 문제를 조사하던 중, 외국 게임 커뮤니티에서 흥미로운 글을 발견했습니다. 한 개발자가 간단한 비트 연산(XOR)을 통한 난독화만으로 시중 치트 엔진의 거의 대부분을 방어했다는 내용이었습니다. (출처는 다시 찾아보려 했으나 못 찾아서… 죄송함다)
아이디어는 간단합니다.
- 객체 생성 시 랜덤한
mask값을 하나 생성합니다 - 값을 저장할 때
value XOR mask로 변환하여 저장합니다 - 값을 읽을 때 다시
stored XOR mask로 복원합니다
이렇게 하면 메모리에 저장되는 값은 실제 게임 내 값과 완전히 다른 숫자가 됩니다. 예를 들어 체력이 100이고 mask가 742라면, 메모리에는 100 XOR 742 = 642가 저장됩니다. Cheat Engine으로 100을 검색해도 절대 찾을 수 없습니다. 또한 각 인스턴스마다 mask가 랜덤으로 생성되므로, 같은 값이라도 메모리에서의 표현이 매번 달라집니다.
Dart 구현 - ObfuscatedInt
아래는 Dart에 그대로 반영한 클래스입니다.
import 'dart:math';
class ObfuscatedInt {
late final int _mask;
late int _value;
ObfuscatedInt(int value) {
_mask = Random().nextInt(1000);
_value = value ^ _mask;
}
int get value => _value ^ _mask;
set value(int value) {
_value = value ^ _mask;
}
}구현은 놀라울 정도로 단순합니다.
실제 게임 코드에서의 적용도 깔끔합니다. getter/setter 패턴으로 감싸면 기존 코드를 거의 수정하지 않고 적용할 수 있습니다.
class User extends Person {
late final ObfuscatedInt _money;
...
int get money => _money.value;
set money(int value) => _money.value = value;
}
...
user.money += 500; // 사용하는 쪽에서는 차이가 없음너무 간단한거 아닌가?
맞는 말이긴 한데, 사실 대다수 일반 유저가 사용하는 치트 엔진은 단순 메모리 스캔 기반이라 이 정도면 실질적으로 충분합니다. 이 이상은 ROI가 안 맞고, 오버 엔지니어링이라고 생각합니다.
치트 엔진 사용을 적발해낸 로그 시스템이 건재한 이상, 버그 유저를 찾고, 다른 수법이 등장해도 파훼할 수 있을 것입니다. 그리고 그 새로운 수법이 충분히 방어해야할 필요성이 생겼을 때 대응해도 늦지 않습니다.