안티치트

DLL 인젝션·후킹 공격이란 — 게임 프로세스 침투와 Native 탐지

"외부 프로그램이 없는데 치트가 돌아간다?" — 은밀하게 침투하는 위협

벽 너머의 적을 정확히 조준하는 에임봇(Aimbot)이나, 쿨타임 없이 스킬을 난사하는 플레이어를 만나본 적 있으신가요? 로그를 뒤져보고 클라이언트 환경을 조사해 보아도 의심스러운 외부 불법 프로그램이 전혀 실행되지 않은 것처럼 보일 때가 있습니다. 외부 프로그램이 없는데도 고도화된 치트가 작동한다면, 공격자는 이미 게임 프로세스의 '내부'에 자리를 잡았을 가능성이 매우 큽니다.

스피드핵이나 단순 메모리 변조를 넘어선 현대의 고급 치트 도구들은 단순히 게임 외부에서 메모리 데이터를 조작하는 방식에 머무르지 않습니다. 게임 프로세스 내부에 악의적인 코드를 직접 심어 넣어, 게임 시스템 스스로가 치트를 실행하도록 조작합니다.

이것이 바로 DLL 인젝션(DLL Injection)함수 후킹(Function Hooking) 의 핵심입니다. 외부가 아닌 게임 내부에서 코드가 실행되기 때문에, 단순한 백그라운드 프로그램 검사나 C# 스크립트 수준의 탐지 로직으로는 포착하기가 매우 까다롭습니다. 이번 글에서는 인젝션과 후킹이 실제로 어떻게 작동하는지, 그리고 이를 시스템 레벨에서 효과적으로 억제하는 방법은 무엇인지 살펴봅니다.

DLL 인젝션의 동작 원리

DLL 인젝션은 공격자가 작성한 코드(치트 로직)가 담긴 .dll 파일을, 실행 중인 타깃 프로세스(게임)의 메모리 공간 안으로 강제로 로드시키는 기법입니다. 일단 주입이 성공하면 해당 악성 코드는 게임과 완전히 동일한 메모리 공간과 실행 권한을 부여받게 됩니다.

공격자 DLL 준비 ──> 게임 프로세스에 강제 로드 및 실행
                         └──> 게임 메모리 직접 읽기/쓰기 권한 획득
                         └──> 게임 내부 함수 직접 호출 및 후킹 가능

스레드 하이재킹(Thread Hijacking), CreateRemoteThread 등 주입을 달성하는 기술적 방법은 다양하지만, 결과는 같습니다. 바로 공격자의 코드가 게임의 일부가 되어버린다는 점입니다. 운영체제(OS) 관점에서 이 코드는 게임 클라이언트가 정상적으로 실행하는 스레드처럼 보이기 때문에, 겉핥기식의 프로세스 모니터링은 무력화됩니다.

주입 이후 — 함수 후킹(Hooking)으로 게임 로직 조작

DLL이 성공적으로 주입된 이후, 공격자가 게임의 흐름을 통제하기 위해 가장 자주 사용하는 기법이 함수 후킹(Function Hooking)입니다. 이는 게임이 특정 핵심 함수를 호출할 때 그 진입점을 가로채어, 원래의 로직 대신 공격자의 코드를 먼저 실행하도록 방향을 트는 기술입니다.

가장 대표적인 인라인 후킹(Inline Hook)의 전형적인 어셈블리 흐름은 다음과 같습니다.

[원본 함수 진입점]                   [후킹 후 진입점]
push rbp                 →        jmp <공격자 치트 코드>   ← 첫 명령어가 교체됨
mov rbp, rsp                      ...
...                                (공격자 코드 실행 후 원본으로 복귀하거나 강제 반환)

예를 들어, 보안을 위해 작성한 CheckSpeedHack() 함수의 첫 몇 바이트를 위와 같이 공격자 코드로 점프(jmp)하는 명령어로 덮어쓰면 어떻게 될까요? 게임 시스템이 이 탐지 함수를 호출할 때마다, 실제 검사 로직은 무시된 채 "아무 이상 없음"을 반환하는 악성 코드가 대신 실행됩니다.

Cheat Engine의 고급 기능이나 Frida(프리다)와 같은 동적 계측 도구들이 바로 이러한 원리를 기반으로 동작하며, 이를 통해 에임봇, 스킬 쿨타임 해제, 결제 검증 우회 등 치명적인 조작이 이루어집니다.

C# 계층 탐지가 갖는 구조적 한계

많은 유니티 개발자들이 치트 탐지 및 방어 로직을 익숙한 C# 스크립트 환경에 작성합니다. 그러나 DLL 인젝션이 발생한 상황에서 이 접근법은 근본적인 취약점을 안고 있습니다.

주입된 악성 DLL은 C# 가상머신(Mono 또는 IL2CPP)보다 더 깊은 계층, 즉 OS와 맞닿은 네이티브(Native) 레벨에서 동작할 수 있습니다. C# 코드가 실행되기도 전에 이미 프로세스 내부의 주도권을 쥐고 있으며, 앞서 설명한 인라인 후킹을 통해 C# 스크립트로 작성된 '파수꾼(탐지 함수)'의 눈을 쉽게 가릴 수 있습니다.

다시 말해, C# 기반의 스크립트 계층에 탐지 로직을 세워두더라도, 더 낮은 레벨에 침투한 공격자가 그 탐지 로직 자체를 최우선으로 찾아내어 무력화할 위험이 큽니다. 공격자에게 분석되기 쉬운 위치에 방어 코드를 두는 것 자체가 구조적 맹점이 되는 것입니다.

Native 계층에서 인젝션과 후킹을 억제하는 방법

인젝션과 후킹 공격을 실효성 있게 탐지하고 억제하려면, 탐지 로직 자체를 공격자의 분석 도구가 쉽게 닿기 어려운 깊은 계층에 배치해야 합니다.

(1) 로드된 모듈 목록 스캔 게임 프로세스 메모리에 로드된 DLL 모듈 목록을 런타임에 주기적으로 점검합니다. 정상적인 배포 빌드에서 로드되어야 할 화이트리스트 모듈과 실제 로드된 목록을 대조하여, 출처가 불분명하거나 허가되지 않은 모듈의 삽입을 식별해 냅니다.

(2) 함수 프리앰블(Preamble) 무결성 검증 게임의 핵심 로직과 보안 함수들의 진입부(첫 몇 바이트) 상태를 배포 시점에 기록해 두고, 런타임에 이를 다시 읽어 기준값과 비교합니다. 인라인 후킹은 필연적으로 이 진입부 메모리를 덮어써야만 동작하므로, 정밀한 무결성 검증을 통해 단 몇 바이트의 조작 시도라도 효과적으로 포착할 수 있습니다.

(3) 디버거 및 후킹 도구 실행 환경 탐지 Cheat Engine, Frida, x64dbg 등 널리 알려진 분석 및 후킹 도구들이 시스템에서 작동 중일 때 남기는 특유의 흔적(디버거 연결 상태, 특정 드라이버, 메모리 핸들 등)을 점검합니다. 게임 로직이 변조되기 전, 치트 도구의 존재 자체를 선제적인 탐지 신호로 활용하는 방식입니다.

(4) 검증 시스템 자체의 Native 격리 가장 중요한 것은 앞선 탐지 기능들을 C# 계층이 아닌, 난독화가 적용된 Native C++ 계층으로 격리하는 것입니다. 감시자의 위치와 동작 방식을 IL2CPP 및 C# 스크립트로부터 분리함으로써, 공격자가 방어 로직을 파악하고 우회하는 데 드는 시간과 비용을 기하급수적으로 끌어올릴 수 있습니다.

OZero Security는 비정상 인젝션 모듈 탐지, 함수 무결성 검증, 그리고 후킹 도구 환경 탐지 기능을 Native C++ 계층에서 통합적으로 수행합니다. 모든 탐지 로직은 C# 레이어와 철저히 분리된 난독화 바이너리 내부에서 안전하게 동작합니다. 또한, Plus Add-on의 앱별 Native Variant 기능을 통해 빌드마다 보안 바이너리의 형태가 무작위로 변경되어, 특정 게임에서 분석된 해킹 튜토리얼이 다른 게임에 악용되는 것을 방지합니다. 탐지된 위협은 Pro Add-on의 텔레메트리 기능을 통해 서버로 실시간 전송되어, 즉각적이고 유연한 라이브 서비스 대응을 가능하게 합니다.

요약 및 정리

  • DLL 인젝션은 공격자의 코드를 게임 프로세스 내부에 직접 심는 기법으로, 외부 프로그램 검사만으로는 포착하기 어렵습니다.
  • 침투한 코드는 게임과 동일한 권한을 가지며, 함수 진입점을 덮어쓰는 후킹(Hooking)을 통해 탐지 로직을 무력화하고 에임봇 등 고도화된 치트를 실행합니다.
  • C# 계층에 작성된 방어 코드는 Native 레벨의 인젝션 및 후킹 공격에 구조적으로 취약합니다. 감시자 역할을 하는 코드가 먼저 변조될 위험이 크기 때문입니다.
  • 이를 막기 위해서는 비정상 모듈 스캔, 함수 무결성 검증, 분석 환경 탐지를 결합하고, 이 모든 보안 로직을 Native 계층으로 안전하게 격리하는 다계층 접근이 필수적입니다.

현대의 치트는 게임 시스템의 바깥에서 문을 두드리지 않습니다. 이미 프로세스라는 집 안에 들어와 있을 때, 그 미세한 변화를 알아채는 정교한 센서가 필요합니다.

OZero Security의 Native 기반 인젝션·후킹 탐지로 프로세스 내부 침투를 선제적으로 차단하세요.

함께 읽으면 좋은 글