2007년 04월 12일
NProtect GameGuard 쪼물딱 거려볼까??
dump_wmimmc.sys (rev 1001, kernel driver)
dump_wmimmc.idb
native_api_inline_hook_routine.gdl
간만에 업데이트된 커널 드라이버를 봤더니 .text영역에 알 수 없는 데이터만 가득
들어있는 마치 exe가 패킹되어 있는 듯한 모습을 가지고 있었다. 드라이버가 packing
되어 있는건 생소해서 조금 더 살펴 봤더니 아래와 같은 녀석을 발견했다.
Oreans사에서 나온 Code Virtualizer가 적용이 되어 있었다. 나중에 알게된 사실이지만
rev900이후부터 적용된 듯 싶다.
Code Virtualizer를 좀 찾아보니 x86 opcode를 PE안에 내장한 virtual machine에서
돌아가도록 가상 opcode로 변환시켜준다고 한다. 그리고 가상 opcode를 분석하기가
조금 까다로운 것이 다양한 VM이 있고 이들 VM들은 모두 다른 instruction으로 돌아가기
때문에 시간이 많이 필요하다. 하지만 짱께들처럼 집단으로 분석한다면 뭐 분석 시간은
얼마 걸리지 않을 것 같다. 물론 VirtualizerStart~End 블록에 대해서만이다. 일반적으로
VM블럭 내부에 있는 코드들은 퍼포먼스가 조금 떨어지기 때문에 아주 Critical한 부분에
대해서만 Virtualization 한 것 같다. 그리고 같은 회사 제품에서 나온 TheMida가
GameGuard.des에 적용이 되어 있었다. 아래는 드라이버 코드에서 어떻게 사용하는지
알려주는 예제이다.
#include <ntddk.h>
#include "VirtualizerDDK.h" // definitions for VirtualizerStart() and VirtualizerEnd()
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
{
PDEVICE_OBJECT deviceObject;
PDEVICE_EXTENSION deviceExtension;
UNICODE_STRING ntDeviceName;
UNICODE_STRING symbolicLinkName;
NTSTATUS status;
DebugPrint(("==>DriverEntry %d\n", i));
VirtualizerStart(); // area to protect starts here!
//
// Create the device object
//
RtlInitUnicodeString( &ntDeviceName, NTDEVICE_NAME_STRING );
status = IoCreateDevice(
DriverObject, // DriverObject
sizeof( DEVICE_EXTENSION ), // DeviceExtensionSize
&ntDeviceName, // DeviceName
FILE_DEVICE_UNKNOWN, // DeviceType
FILE_DEVICE_SECURE_OPEN, // DeviceCharacteristics
FALSE, // Not Exclusive
&deviceObject // DeviceObject
);
if ( !NT_SUCCESS(status) ) {
DebugPrint(("\tIoCreateDevice returned 0x%x\n", status));
return( status );
}
VirtualizerEnd(); // area to protect finishes here!
....
}
흠.... 일단 Code Virtualizer쪽으로 일단 찔러야 대략적인 동작을 알 수 있을거 같다.
그런데 이거 디버깅 하기가 아주 더럽겠다. ㅡㅡ;;;
그리고 SSDT쪽을 살펴보니 직접 그 주소를 바꾸지는 않는다. 그래서 드라이버 로딩
전,후의 NtOpenProcess의 모습을 살펴봤더니 아래와 같았다.
8057B0DA | 68 C4000000 | PUSH C4
8057B0DF | 68 48274F80 | PUSH 804F2748
8057B0E4 | E8 9A9DF6FF | CALL 804E4E83
8057B0E9 | 33F6 | XOR ESI, ESI
8057B0EB | 8975 D4 | MOV DWORD PTR [EBP-2C], ESI
8057B0EE | 33C0 | XOR EAX, EAX
8057B0F0 | 8D7D D8 | LEA EDI, DWORD PTR [EBP-28]
8057B0F3 | AB | STOS DWORD PTR ES:[EDI]
8057B0F4 | 64:A1 24010000 | MOV EAX, DWORD PTR FS:[124]
8057B0DA | E9 73C52B38 | JMP B8837652 ; inline hook
8057B0DF | 68 48274F80 | PUSH 804F2748
8057B0E4 | E8 9A9DF6FF | CALL 804E4E83
8057B0E9 | 33F6 | XOR ESI, ESI
8057B0EB | 8975 D4 | MOV DWORD PTR [EBP-2C], ESI
8057B0EE | 33C0 | XOR EAX, EAX
8057B0F0 | 8D7D D8 | LEA EDI, DWORD PTR [EBP-28]
8057B0F3 | AB | STOS DWORD PTR ES:[EDI]
8057B0F4 | 64:A1 24010000 | MOV EAX, DWORD PTR FS:[124]
예전처럼 SSDT의 함수 엔트리 주소를 변경하지 않고, 직접 커널 함수에 대해서
inline 후킹하는 것을 알 수 있다. 정확히는 기존의 SDT를 신뢰하지 않고 처음
구동시 ntoskrnl.exe를 참조하여 주요한 SDT를 복원하고, ntoskrnl.exe에 구현된
코드에 5바이트를 inline 후킹한다. 그리고 예전에 시도했었던 int1, int3에 대한
IDT 후킹은 찾아볼 수 없었다. 하지만 int1,3에 대한 IDT 후킹이 되어 있는 경우
이를 복원하였다.
요기서 머릿돌을 잠시 굴려보면 GG가 ntoskrnl.exe를 가지고 복원할 때 ntoskrnl의
무결성까지 체크하기란 쉽지 않을 것이다. 그렇기에 변조된 ntoskrnl을 올린다면
뭔가 재미있는 일이 가능할 것이다.
dump_wmimmc.idb
native_api_inline_hook_routine.gdl
간만에 업데이트된 커널 드라이버를 봤더니 .text영역에 알 수 없는 데이터만 가득
들어있는 마치 exe가 패킹되어 있는 듯한 모습을 가지고 있었다. 드라이버가 packing
되어 있는건 생소해서 조금 더 살펴 봤더니 아래와 같은 녀석을 발견했다.
Oreans사에서 나온 Code Virtualizer가 적용이 되어 있었다. 나중에 알게된 사실이지만
rev900이후부터 적용된 듯 싶다.

돌아가도록 가상 opcode로 변환시켜준다고 한다. 그리고 가상 opcode를 분석하기가
조금 까다로운 것이 다양한 VM이 있고 이들 VM들은 모두 다른 instruction으로 돌아가기
때문에 시간이 많이 필요하다. 하지만 짱께들처럼 집단으로 분석한다면 뭐 분석 시간은
얼마 걸리지 않을 것 같다. 물론 VirtualizerStart~End 블록에 대해서만이다. 일반적으로
VM블럭 내부에 있는 코드들은 퍼포먼스가 조금 떨어지기 때문에 아주 Critical한 부분에
대해서만 Virtualization 한 것 같다. 그리고 같은 회사 제품에서 나온 TheMida가
GameGuard.des에 적용이 되어 있었다. 아래는 드라이버 코드에서 어떻게 사용하는지
알려주는 예제이다.
#include <ntddk.h>
#include "VirtualizerDDK.h" // definitions for VirtualizerStart() and VirtualizerEnd()
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
{
PDEVICE_OBJECT deviceObject;
PDEVICE_EXTENSION deviceExtension;
UNICODE_STRING ntDeviceName;
UNICODE_STRING symbolicLinkName;
NTSTATUS status;
DebugPrint(("==>DriverEntry %d\n", i));
VirtualizerStart(); // area to protect starts here!
//
// Create the device object
//
RtlInitUnicodeString( &ntDeviceName, NTDEVICE_NAME_STRING );
status = IoCreateDevice(
DriverObject, // DriverObject
sizeof( DEVICE_EXTENSION ), // DeviceExtensionSize
&ntDeviceName, // DeviceName
FILE_DEVICE_UNKNOWN, // DeviceType
FILE_DEVICE_SECURE_OPEN, // DeviceCharacteristics
FALSE, // Not Exclusive
&deviceObject // DeviceObject
);
if ( !NT_SUCCESS(status) ) {
DebugPrint(("\tIoCreateDevice returned 0x%x\n", status));
return( status );
}
VirtualizerEnd(); // area to protect finishes here!
....
}
흠.... 일단 Code Virtualizer쪽으로 일단 찔러야 대략적인 동작을 알 수 있을거 같다.
그런데 이거 디버깅 하기가 아주 더럽겠다. ㅡㅡ;;;
그리고 SSDT쪽을 살펴보니 직접 그 주소를 바꾸지는 않는다. 그래서 드라이버 로딩
전,후의 NtOpenProcess의 모습을 살펴봤더니 아래와 같았다.
8057B0DA | 68 C4000000 | PUSH C4
8057B0DF | 68 48274F80 | PUSH 804F2748
8057B0E4 | E8 9A9DF6FF | CALL 804E4E83
8057B0E9 | 33F6 | XOR ESI, ESI
8057B0EB | 8975 D4 | MOV DWORD PTR [EBP-2C], ESI
8057B0EE | 33C0 | XOR EAX, EAX
8057B0F0 | 8D7D D8 | LEA EDI, DWORD PTR [EBP-28]
8057B0F3 | AB | STOS DWORD PTR ES:[EDI]
8057B0F4 | 64:A1 24010000 | MOV EAX, DWORD PTR FS:[124]
8057B0DA | E9 73C52B38 | JMP B8837652 ; inline hook
8057B0DF | 68 48274F80 | PUSH 804F2748
8057B0E4 | E8 9A9DF6FF | CALL 804E4E83
8057B0E9 | 33F6 | XOR ESI, ESI
8057B0EB | 8975 D4 | MOV DWORD PTR [EBP-2C], ESI
8057B0EE | 33C0 | XOR EAX, EAX
8057B0F0 | 8D7D D8 | LEA EDI, DWORD PTR [EBP-28]
8057B0F3 | AB | STOS DWORD PTR ES:[EDI]
8057B0F4 | 64:A1 24010000 | MOV EAX, DWORD PTR FS:[124]
예전처럼 SSDT의 함수 엔트리 주소를 변경하지 않고, 직접 커널 함수에 대해서
inline 후킹하는 것을 알 수 있다. 정확히는 기존의 SDT를 신뢰하지 않고 처음
구동시 ntoskrnl.exe를 참조하여 주요한 SDT를 복원하고, ntoskrnl.exe에 구현된
코드에 5바이트를 inline 후킹한다. 그리고 예전에 시도했었던 int1, int3에 대한
IDT 후킹은 찾아볼 수 없었다. 하지만 int1,3에 대한 IDT 후킹이 되어 있는 경우
이를 복원하였다.
요기서 머릿돌을 잠시 굴려보면 GG가 ntoskrnl.exe를 가지고 복원할 때 ntoskrnl의
무결성까지 체크하기란 쉽지 않을 것이다. 그렇기에 변조된 ntoskrnl을 올린다면
뭔가 재미있는 일이 가능할 것이다.
# by | 2007/04/12 15:06 | TrashCan | 트랙백 | 핑백(1) | 덧글(11)
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
... nProtect에 무슨 일이 생겼는지... 꽤나 궁금했었는데...오늘 rss를 읽다가 우연히 한 분이 분석한걸 보게 되었다.http://nerd.egloos.com/3109902 SDT 쪽에 별 문제가 없길래 설마 설마 했었는데...kernel 함수를 직접 손볼줄이야.. @0@)/~!!방식이야 유저레벨의 DLL 조작하는 ... more
이런걸 생각할 수 있다니 ㅋㅋ
지난번에 Code Virtualizer 문서 잠깐 봤었는데 쉽지 않더군요. -_-;;
UCE에 올라가는 드라이버에 Code Virtualizer를 적용하고 CE 실행파일과 DLL들을 패커로 중무장해서
어떻게든 패턴매칭을 피해가려고 하더군요. 흐미 앞으로 이런식으로 나아가면 점점 더 Anti Cheating이
힘들어지겠군요. ㅠㅠ;;
nprotect를 bypassing하는걸 재미로 만들어서 쓰고있다가..
어느날 갑자기 전부 바뀌어있길래 저렇게 바꿔놨었군요..
dump_wmimmc.sys를 덤프하는걸 몰라서
대충 SSDT쪽 보고 bypassing 했는데 앞으로 점점 재미있어지겠내요~
안그래도 어떻게 된건지 궁금했는데... 설마 설마 하는 부분이 고쳐졌군요...
방금 직접 확인했습니다.
약간 더 신경을 써야 겠군요... 흠...
좋은 실마리를 찾아쥐고 갑니다.
감사합니다.
저파일 받아가면 되는건가요..?
system32 drivers에 세파일 다 넣었는데.. 다시 덤프 뜨더군요... 그후.... 보니까.. sys 파일만 없던데.ㅠ.ㅠ
근데 한번들어갔을때는... 안뜨던데.... 들어갔다 나왓다 한뒤 다시 들어갔다 나오니까.. 덤프 뜨네요..... [이파일 안 받았을땐... 한번에 덤프 떳었죠.ㅠ.ㅠ.
자꾸 게임가드만 실행시키면 지워짐 도와주세요 ㅠ