
게임 치팅과의 전쟁, 그 기술적 최전선
혹시 온라인 게임을 하다가 핵 유저를 만나본 적 있으신가요? 에임봇, 월핵, 스피드핵… 게임 회사 입장에서 치터(cheater)는 정말 골치 아픈 존재예요. 유저들이 떠나거니까요. 그래서 AAA급 대형 게임 스튜디오들은 자신들의 게임 바이너리를 난독화(obfuscation)하는 데 엄청난 공을 들이고 있는데요, 최근 이 분야에서 흥미로운 기법이 공유되어서 한번 깊이 들여다보려고 해요.
난독화가 뭐냐면, 쉽게 말해서 컴파일된 프로그램(바이너리)을 리버스 엔지니어링하기 어렵게 만드는 기술이에요. 소스 코드를 볼 수 없는 상태에서 프로그램의 동작을 분석하려면 디스어셈블러나 디컴파일러 같은 도구를 쓰는데, 난독화는 이런 도구들이 제대로 동작하지 못하게 방해하는 거죠. 마치 책의 내용은 그대로인데, 글자를 뒤섞고 페이지 순서를 바꿔서 읽기 어렵게 만드는 것과 비슷해요.
핵심 문제: 난독화 vs 컴파일러 최적화
여기서 중요한 딜레마가 하나 있어요. 바로 LTO(Link-Time Optimization)와의 충돌이에요. LTO가 뭐냐면, 컴파일러가 프로그램 전체를 링크하는 시점에 한꺼번에 최적화를 수행하는 기법이에요. 보통 컴파일은 파일 단위로 하잖아요? 그런데 LTO를 쓰면 모든 파일을 합쳐놓고 "아, 이 함수는 여기서만 쓰이니까 인라인 처리하자", "이 변수는 사실 필요 없으니까 제거하자" 같은 전역 최적화를 할 수 있거든요. 성능이 10~20% 이상 좋아지는 경우도 흔해요.
문제는 전통적인 난독화 기법들이 이 LTO를 망가뜨린다는 거예요. 왜 그러냐면, 대부분의 난독화는 컴파일러가 만든 중간 표현(IR)이나 최종 바이너리를 직접 조작하는 방식으로 동작하거든요. 그런데 LTO는 링크 단계에서 IR을 다시 최적화하면서 난독화가 삽입한 코드를 "불필요한 코드"로 판단하고 제거해버리는 거죠. 열심히 문을 잠갔는데 컴파일러가 "이 문 필요 없네?" 하고 치워버리는 셈이에요.
AAA 게임들이 쓰는 접근법
이 블로그 글에서 소개하는 접근법은 컴파일러 패스(compiler pass) 수준에서 난독화를 삽입하되, LTO 파이프라인과 공존할 수 있게 설계하는 것이에요. 구체적으로 몇 가지 기법을 살펴볼게요.
첫 번째는 제어 흐름 평탄화(Control Flow Flattening)예요. 이게 뭐냐면, 프로그램의 if-else나 switch 같은 분기 구조를 하나의 거대한 switch문 안에 넣어서 "어디서 어디로 점프하는지" 알기 어렵게 만드는 거예요. 원래 A→B→C 순서로 실행되는 코드가 있다면, 이걸 switch(state) 안에 넣고 state 변수를 통해 간접적으로 흐름을 제어하는 거죠. 디컴파일러가 보면 모든 블록이 같은 레벨에 있으니 원래의 논리 구조를 파악하기 훨씬 어려워져요.
두 번째는 불투명 술어(Opaque Predicates)예요. 쉽게 말하면, 항상 참(또는 거짓)인 조건문을 삽입하는 건데, 컴파일러가 그걸 "항상 참이네, 제거하자"라고 판단하지 못하게 만드는 기법이에요. 예를 들어 수학적으로는 항상 참이지만, 정적 분석으로는 증명하기 어려운 조건을 사용하는 거죠. 이렇게 하면 가짜 분기가 바이너리에 남아서 분석을 어렵게 만들어요.
세 번째는 문자열 암호화예요. 게임 바이너리 안에는 에러 메시지, 함수 이름 힌트 같은 문자열이 들어있는데, 이것들이 리버서에게 엄청난 단서가 되거든요. 그래서 빌드 시점에 문자열을 암호화하고, 런타임에 필요할 때만 복호화하는 방식을 써요.
핵심은 이 모든 변환이 LLVM의 패스 매니저 안에서 LTO와 조화롭게 동작하도록 패스 순서(pass ordering)를 정교하게 조절한다는 점이에요. 난독화 패스가 LTO 최적화 패스 "이후"에 실행되도록 하거나, 난독화된 코드가 후속 최적화에 의해 제거되지 않도록 특별한 마킹을 하는 방식이죠.
업계에서는 어떤 도구들을 쓰고 있을까?
상용 난독화 도구로는 Denuvo가 가장 유명해요. PC 게임 DRM 솔루션으로 많이 쓰이는데, 성능 오버헤드 때문에 유저들 사이에서 호불호가 극명하게 갈리죠. VMProtect이나 Themida 같은 도구들도 게임 업계에서 많이 쓰이는데, 이들은 가상 머신 기반 난독화라서 성능 비용이 상당해요.
오픈소스 쪽에서는 LLVM 기반의 obfuscator-llvm(O-LLVM)이 유명했는데, 현재는 유지보수가 활발하지 않아요. 최근에는 Hikari나 Pluto 같은 프로젝트들이 그 빈자리를 채우고 있고요. 이번에 소개된 접근법은 이런 도구들이 갖고 있던 "LTO와의 호환성" 문제를 정면으로 다루고 있다는 점에서 의미가 있어요.
언리얼 엔진이나 유니티 같은 상용 엔진을 쓰는 스튜디오들은 엔진 레벨에서 자체 난독화 레이어를 추가하기도 하고, 안티치트 솔루션(EasyAntiCheat, BattlEye 등)과 조합해서 다층 방어를 구축해요.
한국 개발자에게 주는 시사점
한국은 온라인 게임 강국이잖아요. 넥슨, 엔씨, 크래프톤 같은 대형 게임사들도 치팅 방지에 막대한 리소스를 투입하고 있어요. 특히 배틀그라운드나 메이플스토리 같은 글로벌 서비스 게임은 난독화가 필수적이에요.
게임 개발자가 아니더라도, 이 기법들은 보안이 중요한 소프트웨어를 만드는 모든 분야에 적용돼요. 금융 앱의 암호화 로직 보호, DRM이 필요한 미디어 플레이어, 라이선스 검증 로직 보호 등이 대표적이죠. LLVM 패스를 직접 작성하는 건 진입 장벽이 높지만, 컴파일러 인프라에 대한 이해를 넓히는 좋은 학습 주제이기도 해요.
또 하나 흥미로운 점은, 이런 공격-방어의 군비 경쟁이 결국 컴파일러 기술의 발전을 이끈다는 거예요. 난독화를 뚫으려는 시도가 정적 분석 도구를 발전시키고, 그게 다시 더 강력한 난독화 기법을 낳는 선순환이 있거든요.
마무리
한줄 정리: AAA 게임의 바이너리 난독화는 단순히 코드를 복잡하게 만드는 게 아니라, 컴파일러 최적화 파이프라인과 공존하면서도 리버싱을 어렵게 만드는 정교한 엔지니어링이에요.
여러분은 소프트웨어 보호에 대해 어떻게 생각하시나요? "어차피 다 뚫리는데 의미가 있나"라는 회의적 시각도 있고, "시간을 벌어주는 것만으로도 가치가 있다"는 현실적 시각도 있는데요. 여러분의 생각이 궁금해요!
🔗 출처: Hacker News
TTJ 코딩클래스 정규반
월급 외 수입,
코딩으로 만들 수 있습니다
17가지 수익 모델을 직접 실습하고, 1,300만원 상당의 자동화 도구와 소스코드를 받아가세요.
"비전공 직장인인데 반년 만에 수익 파이프라인을 여러 개 만들었습니다"
실제 수강생 후기- 비전공자도 6개월이면 첫 수익
- 20년 경력 개발자 직강
- 자동화 프로그램 + 소스코드 제공