상시 동작하는 하나의 거대한 모듈 대신, 실행 감지·핸들 보호·사용자 상태 표시를 분리해 필요한 시점에 연결하는 구조를 실험했습니다.
구성요소를 나눈 이유
핸들 콜백을 포함한 핵심 보안 모듈을 게임이 꺼진 동안에도 계속 활성화할 필요가 있는지 고민했습니다. 그래서 가벼운 시작 감시 드라이버가 게임 실행을 관찰하고, 게임이 실행되는 동안에만 핸들 보호 드라이버를 로드하는 구조를 설계했습니다.
구성요소 | 역할 |
ExternalAnticheatStart | Palworld 프로세스 생성·종료 감시, 핵심 드라이버 로드·언로드 |
ExternalAnticheat | 대상 PID 탐색, 프로세스 핸들 사전 콜백과 권한 정책 |
PalGuard | 사용자 모드 상태 확인, C 드라이브의 NT 디바이스 경로 전달 |
실행 흐름
프로세스 시작·종료 감지
시작 드라이버는
PsSetCreateProcessNotifyRoutineEx로 콜백을 등록합니다. 생성 알림의 이미지 경로가 Palworld 실행 파일과 일치하면 ZwLoadDriver로 핵심 드라이버를 로드하고, 같은 PID가 종료되면 ZwUnloadDriver를 호출합니다.역할을 분리한 덕분에 “게임 존재 여부 확인”과 “핸들 보안 정책”을 별도 모듈로 생각할 수 있었습니다. 다만 게임 경로와 서비스 레지스트리 경로가 하드코딩되어 있어 다른 설치 위치에는 바로 적용되지 않습니다.
IOCTL로 사용자 모드와 연결
두 드라이버는 장치 객체와 심볼릭 링크를 만들고, PalGuard는
CreateFileW와 DeviceIoControl로 통신합니다.- 시작 드라이버: 게임과 핵심 모듈의 시작 상태 반환
- 핵심 드라이버: 사용자 모드에서 전달한 C 드라이브의 NT 디바이스 이름 수신
- PalGuard: 논리 드라이브를 조사해
C:에 대응하는\Device\HarddiskVolume...값을 전달
이 설계는 고정된
HarddiskVolume3 값에 대한 의존을 줄이려는 시도였습니다.지금 다시 코드를 보며 발견한 문제
잘못된 상태 조회 제어 코드
WaitforExternalAnticheatSTART 함수는 핵심 드라이버 상태용 상수를 정의했지만 실제 호출에서는 게임 시작 상태와 같은 IOCTL 코드를 사용합니다. 의도한 두 상태를 구분하려면 IOCTL_RECEIVE_STARTEXTERNAL을 사용해야 합니다.오류 이후에도 유효하지 않은 핸들을 사용할 수 있음
CreateFileW 실패를 출력만 하고 즉시 반환하지 않는 경로가 있습니다. 이후 DeviceIoControl을 호출하므로 오류 처리를 분리해야 합니다. 성공 경로의 CloseHandle도 반환문 뒤에 있어 실행되지 않는 부분이 있습니다.전달값과 커널 정책 연결이 불완전함
핵심 드라이버에는 볼륨 경로를 받아 System32 기준 경로를 구성하려는 코드가 있지만, 해당 값을 실제 정책에 반영하는 흐름과 버퍼 검증이 완성되어 있지 않습니다. 단순 문자열 신뢰 자체도 보안 경계로 충분하지 않습니다.
프로세스 경로 하드코딩
Steam 기본 설치 경로 하나만 비교합니다. 실제 배포를 고려한다면 설치 경로 탐색, 구성 서명, 서비스 권한, 재시작·중복 로드 상태를 함께 다뤄야 합니다.
“수면 모드” 표현을 다시 정의했다
당시 최종보고서에는 탐지 오버헤드를 줄이기 위한 “수면 모드”라고 적었습니다. 지금 코드를 다시 보면, 더 정확한 표현은 게임 실행 이벤트에 따라 핵심 드라이버를 활성화·비활성화하는 두 드라이버 구조입니다.
CPU나 메모리 사용량을 정량 비교한 자료가 없기 때문에 “오버헤드를 얼마나 줄였다”고 주장하지 않습니다. 대신 상시 활성화 범위를 줄이려는 설계 의도와 구현 흐름을 성과로 남깁니다.
다음 단계라면
- 하드코딩 경로를 서명된 설정과 안전한 검색 방식으로 교체
- IOCTL별 입력·출력 크기, 호출자 권한과 상태 전이를 엄격하게 검증
- Driver Verifier와 정적 분석으로 IRQL·메모리·언로드 경로 검증
- 드라이버 로드 실패, 게임 재시작, 중복 PID에 대한 상태 머신 설계
- 유휴·게임 실행·공격 테스트별 CPU/메모리/지연 측정
- 이벤트 로그와 재현 가능한 테스트 매트릭스 작성
결론
이 단계에서 얻은 핵심은 IOCTL 호출 자체가 아니라 커널 모듈의 역할, 수명 주기, 사용자 모드 제어면을 분리해 본 경험입니다. 동시에 커널 프로젝트에서 “작동한다”와 “안전하게 운영할 수 있다” 사이에는 입력 검증, 상태 관리, 서명과 측정이 더 필요하다는 점을 확인했습니다.
당시 원본 기록
아래 하위 문서는 2024년 당시 제가 작성한 개인보고서 원문입니다. 사진·코드·시행착오는 그대로 보존했고, 지금 다시 보며 바로잡은 내용은 위 본문에 따로 적었습니다.