들어가며
일반적으로 게임 보안 프로세스인 안티치트(Anti-Cheat)는 크게 유저 모드(User-Mode)와 커널 모드(Kernel-Mode) 기반으로 나뉩니다. 유저 모드 안티치트는 과거부터 메모리 후킹이나 코드 인젝션 등 다양한 기법을 통해 우회되어 왔으나, 커널 모드 안티치트는 운영체제의 핵심부인 Ring 0에서 동작하기 때문에 일반적인 권한으로는 접근 자체가 불가능합니다.
하지만 BYOVD(Bring Your Own Vulnerable Driver) 기법을 활용하면 견고해 보이는 커널 레벨의 보호막도 뚫어낼 수 있습니다. 이 기법은 공격자가 '이미 정상적으로 서명되었지만, 보안 취약점이 존재하는 드라이버'를 시스템에 로드하여 커널 메모리에 대한 임의 읽기/쓰기(Arbitrary Read/Write) 권한을 획득하는 강력한 방법입니다.
이번 포스팅에서는 지난 글에서 직접 구현했던 ObRegisterCallbacks 기반의 간이 커널 안티치트인 SimpleAC를, 취약한 드라이버를 이용해 무력화하는 전체 과정을 상세히 다루어 보겠습니다.
실습환경
- OS : Windows 10 VM (취약한 드라이버 로드를 위한 환경)
- IDE : Visual Studio 2022
- Target : AssaultCube (ac_client.exe)
- Defense : SimpleAC (직접 제작한 커널 안티치트)
- Exploit : RTCore64.sys (MSI Afterburner의 취약한 드라이버)
- Tools : IDA Free, CheatEngine
타겟 분석: SimpleAC의 보호 메커니즘
직접 제작한 안티치트인 SimpleAC는 Windows 커널 API인 ObRegisterCallbacks를 활용하여 프로세스 핸들 생성을 감시하고 차단합니다
보호방식
1. PsProcessType에 PreOperation 콜백을 등록합니다.
2. 타겟 게임인 ac_client.exe에 대한 핸들 생성이 감지되면 이를 가로챕니다.
3. 요청된 권한 중 VM_READ, VM_WRITE, VM_OPERATION 권한을 강제로 제거합니다. (단, csrss, svchost 등 핵심 시스템 프로세스는 예외 처리)
이 보호 기법이 적용된 상태에서 Cheat Engine과 같은 외부 툴로 게임 프로세스에 접근을 시도하면, 권한이 거부되어 메모리 영역이 ??로 표시되며 읽기 및 쓰기가 전면 차단됩니다.
자세한 건 이전 포스팅을 참조해주시길 바랍니다.
https://imoracle.tistory.com/70
[AntiCheat] 취약 드라이버를 이용한 커널 레벨 안티치트 우회 -1-
들어가며게임 해킹과 보안의 역사는 끊임없는 '창과 방패'의 대결입니다. 과거의 게임 핵(Hack)들이 단순히 유저 모드(User-Mode, Ring 3)에서 메모리를 변조하는 수준에 머물렀다면, 오늘날의 안티치
imoracle.tistory.com
무기 준비: RTCore64.sys 취약점 분석
우리가 커널 권한을 얻기 위해 사용할 도구는 MSI Afterburner GPU 오버클럭 유틸리티에 포함되었던 RTCore64.sys 드라이버입니다. 이 드라이버는 DeviceIoControl을 통해 사용자 모드 애플리케이션이 커널 메모리를 마음대로 읽고 쓸 수 있는 치명적인 취약점(CVE-2019-16098)을 가지고 있습니다.
디바이스 심볼릭 링크: \\.\RTCore64

IDA를 통한 IOCTL 코드 추출
RTCore64.sys는 MSI Afterburner GPU 오버클럭 유틸리티에 포함된 드라이버입니다, CVE로 등록된 취약점으로 DeviceIoControl을 통해 임의 커널 메모리 읽기/쓰기가 가능합니다.
드라이버와 통신하기 위해서는 정확한 제어 코드(IOCTL)를 알아야 합니다. IDA Free를 통해 RTCore64.sys를 분석해 보았습니다.
IOCTL 코드 찾기
sub_11450 함수 내부를 살펴보면 무려 85개의 IOCTL 케이스를 처리하는 방대한 switch 문이 존재합니다. 이 중 커널 메모리 읽기와 쓰기를 담당하는 루틴을 찾아야 합니다.

switch 문의 각 케이스를 하나씩 분석해 메모리 읽기/쓰기 케이스를 찾아야 합니다.


어셈블리 코드를 추적하다 보면 메모리 읽기를 수행하는 mov eax, [rax+rcx] 구문과, 메모리 쓰기를 수행하는 mov [rcx+rdx], eax 구문을 식별할 수 있습니다. 이를 바탕으로 드라이버와 통신할 입력 구조체와 제어 코드를 구성합니다.
입력 구조체

[Troubleshooting Tip] IOCTL 코드 버전 불일치 문제
실습 초기, 구글링을 통해 수집한 여러 버전의 RTCore64.sys 파일이 섞이는 바람에 디버깅에 애를 먹었습니다. IDA로 분석했던 파일의 IOCTL 코드는 0x80002038 / 0x8000203C였으나, 실제 시스템에 로드하여 테스트한 파일은 0x80002048 / 0x8000204C를 사용하고 있었습니다. BYOVD를 실습할 때는 반드시 자신이 로드할 드라이버 파일의 해시값과 버전을 직접 리버싱하여 IOCTL 코드를 교차 검증해야 합니다

정확한 IOCTL 코드와 구조체를 맞춘 후, 커널 영역의 최상단인 ntoskrnl의 MZ 헤더를 성공적으로 읽어오는 것을 확인했습니다. 이제 커널 메모리를 조작할 준비가 끝났습니다.
BYOVD 공격 구현: 커널 콜백 무력화
현재 대부분의 상용 안티치트는 RTCore64.sys의 해시를 블랙리스트에 올려 로드 자체를 차단하며, Windows 11부터는 OS 차원에서 취약한 드라이버 목록(Vulnerable Driver Blocklist)을 관리하여 차단합니다. 따라서 본 실습은 서명 강제가 상대적으로 우회하기 쉬운 Windows 10 VM 환경에서 진행했습니다.
커널 심볼 파싱 및 베이스 주소 획득
먼저 유저 모드에서 EnumDeviceDrivers를 사용해 ntoskrnl.exe의 베이스 주소를 구합니다. 이후 PsInitialSystemProcess 등의 주요 커널 객체 오프셋을 계산하여 실제 커널 메모리 주소를 매핑합니다.



OB_CALLBACK_ENTRY 구조체 분석 및 디버깅
가장 까다로웠던 부분입니다. 확보한 권한으로 무작정 콜백 리스트를 수정하려다 수차례 BSOD(블루스크린)를 마주했습니다. 원인을 파악하기 위해 메모리 덤프를 떠서 OB_CALLBACK_ENTRY의 정확한 메모리 구조를 분석했습니다.

[확인된 OB_CALLBACK_ENTRY 구조]
- +0x00 : Flink
- +0x08 : Blink
- +0x10 : Operations
- +0x18 : 헤더 포인터
- +0x20 : PsProcessType 포인터
- +0x28 : PreOperation < 콜백 함수 주소
- +0x30 : PostOperation
콜백 함수 연결 끊기
구조를 완벽히 파악했으니 남은 것은 단순합니다. 순회하며 찾아낸 SimpleAC의 PreOperation 콜백 포인터 주소(entry + 0x28)에 RTCore64의 임의 쓰기(Arbitrary Write) 취약점을 이용해 0(NULL)을 덮어씌웁니다.


우회 결과 확인
콜백 포인터가 제거되자 ObRegisterCallbacks 기반의 보호 로직이 완전히 무력화되었습니다.

우회 프로그램 실행 후, ac_client.exe에 대해 PROCESS_VM_READ 권한으로 OpenProcess 및 ReadProcessMemory를 호출한 결과 정상적으로 핸들을 획득했습니다. Cheat Engine으로 다시 접근했을 때도 ??로 막히던 메모리 영역이 투명하게 들여다보이며 정상적인 변조가 가능해졌습니다.

전체 공격 흐름 요약 (Kill Chain)
- 취약한 드라이버 로드
- EnumDeviceDrivers API를 통해 ntoskrnl 베이스 주소 탐색
- 커널 익스포트 함수를 파싱하여 PsInitialSystemProcess (SYSTEM EPROCESS) 주소 획득
- PsProcessType 객체에서 CallbackList (+0xC8) 탐색
- OB_CALLBACK_ENTRY 리스트를 순회하며 SimpleAC가 등록한 PreOperation(+0x28) 포인터 발견
- 취약점을 이용해 해당 주소(entry + 0x28)에 0을 Write (콜백 해제)
- 타겟 프로세스에 대한 OpenProcess 및 메모리 접근 성공
마치며
BYOVD 기법의 가장 큰 위협은 마이크로소프트의 정상적인 서명을 받은 드라이버를 공격 무기로 역이용한다는 점입니다. 공격자는 커널에 진입하기 위해 복잡한 드라이버 서명 강제(DSE)를 우회할 필요가 없습니다.
물론, EAC(Easy Anti-Cheat)나 BattlEye 같은 최신 상용 안티치트 솔루션들은 이러한 위협에 대비해 취약한 드라이버의 해시를 서버사이드에서 지속적으로 업데이트하여 차단하고, 자가 무결성 검증 스레드를 여러 겹으로 꼬아두기 때문에 이처럼 단순한 콜백 제거만으로는 우회가 불가능에 가깝습니다.
하지만 운영체제의 방어 메커니즘을 뚫고 커널 메모리를 직접 조작해 보는 이번 실습은, 공격자의 관점에서 커널 보안의 취약점을 이해하고 더 견고한 보안 솔루션을 설계하는 데 큰 밑거름이 될 것입니다.
긴 글 읽어주셔서 감사합니다!