[ref - SSDT_Hooking.pdf - Written by 백구]
Native API의 호출과정은 위의 그림과 같다.
1. INT 0x2E나 SYSENTER 같은 시스템 콜이 호출된다. (XP 이후는 SYSENTER를 사용)
- http://luckey.tistory.com/86 <- INT 0x2E와 SYSENTER의 대해서 참고.
2. 시스템 콜이 호출되면 커널에서 KiSystemService(시스템 서비스 디스패처)를 호출한다.
- EAX에서 시스템 콜 번호를 읽어 SSDT의 인덱스로 사용
- EDX는 인자를 유저모드 스택에서 커털 모드 스택으로 복사한 곳을 가리킴
3. EAX에서 받은 시스템 콜 번호에 맞게 KeServiceDescriptorTable(SSDT)를 참조하여 Native API를 호출한다.
- [SSDT+EAX*4]와 같이 참조
4. 시스템 콜 종료하고 유저 모드로 복귀한다.
- INT 0x2E의 경우 iretd(인터럽트 복귀 명령) 사용.
- SYSENTER의 경우 SYSEXIT 사용.
[ 시스템 콜 & KeServiceDescriptorTable 구조]
위의 호출 과정을 따라서 좀더 상세하게 분석해보자.
1. 먼저 INT 0x2E와 SYSENTER의 경우에 (KiSystemService)시스템 서비스 디스패처를 호출하는데 약간의 차이가 있다.
kd> !idt 2e
Dumping IDT:
2e: 8053f481 nt!KiSystemService
kd> rdmsr 176 //SYSENTER_EIP_MSR : 0x176
msr[176] = 00000000`8053f540
kd> ln 00000000`8053f540
(8053f540) nt!KiFastCallEntry | (8053f649) nt!KiServiceExit
Exact matches:
nt!KiFastCallEntry = <no type information>
INT 0x2E의 경우에는 KiSystemService를 바로 호출하지만 SYSENTER의 경우에는 KiFastCallEntry를 먼저 호출한 후에 다시 KiSystemService를 호출한다.
- INT 0x2E -> KiSystemService
- SYSENTER -> KIFastCallEntry -> KiSystemService
[ref - http://luckey.tistory.com/86]
2. KeServiceDescriptorTable에 대해서 자세히 살펴보겠다. KeServiceDescriptorTable는 커널에서 제공하는 테이블로 SDE 구조체를 묶어놓은 SDT 구조체이다. SDE 구조체에 SSDT, SSPT가 들어있으며 아래와 같은 구조를 가진다.
typedef struct ServiceDescriptorEntry
{
unsigned int *ServiceTableBase;
unsigned int *ServiceCounterTableBase;
unsigned int NumberOfServices;
unsigned char *ParamTableBase;
} SERVICE_DESCRIPTOR_ENTRY, *PSSERVICE_DESCRIPTOR_ENTRY;
1. SSDT 포인터
2. 함수의 호출 개수를 저장하는 테이블 포인터(Windows의 Checked 빌드버전에서만 사용)
3. 이 서비스 테이블에 저장되어 있는 서비스의 개수
4. SSPT 포인터
[*] SSDT, SSPT
SSDT(System Service Dispatch Table) : 시스템 콜을 처리하기 위한 함수를 찾을 때 사용.
SSPT(System Service Parameter Table) : 각 서비스에서 사용되는 인자의 크기가 몇 바이트인지 알려줌.
[*] windbg로 KeServiceDescriptorTable 내용확인
kd> dd KeServiceDescriptorTable
80554fa0 80503b8c 00000000 0000011c 80504000
80554fb0 00000000 00000000 00000000 00000000
80554fc0 00000000 00000000 00000000 00000000
kd> d 80503b8c
80503b8c 8059b948 805e8db6 805ec5fc 805e8de8
80503b9c 805ec636 805e8e1e 805ec67a 805ec6be
80503bac 8060ddfe 8060eb50 805e41b4 805e3e0c
kd> u 8059b948
nt!NtAcceptConnectPort:
8059b948 689c000000 push 9Ch
8059b94d 6838b14d80 push offset nt!_real+0x128 (804db138)
kd> u 805e8db6
nt!NtAccessCheck:
805e8db6 8bff mov edi,edi
805e8db8 55 push ebp
805e8db9 8bec mov ebp,esp
...
[ SSDT에 쓰기 권한 주기 ]
WIndows XP 이상에서 SSDT가 Read Only로 되어 있는데 SSDT 후킹을 하기 위해서는 쓰기 권한이 필요하다.
이를 우회하여 쓰기 권한을 주는 두가지 방법은 아래와 같다.
1. CR0 레지스터의 WP(Write Protect) bit
- 0이면 메모리 보호기능 해제
- 1이면 메모리 보호 활성화
--------- CODE ---------
// 메모리 보호 기능 OFF
__asm
{
push eax
mov eax, CR0
and eax, 0FFFEFFFFh
mov CR0, eax
pop eax
}
// 메모리 보호 기능 ON
__asm
{
push eax
mov eax, CR0
or eax, NOT 0FFFEFFFFh
mov CR0, eax
pop eax
}
[ SSDT 후킹 ]
간단히 Native API함수들의 주소가 들어 있는 SSDT에 후킹할 함수의 주소가 있는 index 번째에 새롭게 만든 함수 주소를 덮어버리면 SSDT 후킹이 이루어진다.
[ZwQuerySystemInformation 후킹을 통한 Process Hiding.]
'Programming > WDK' 카테고리의 다른 글
가상주소-선형주소-물리주소 변환 (0) | 2014.01.25 |
---|---|
메모리 접근 (0) | 2014.01.19 |
WDK빌드 환경 ( + Visual Studio ) (0) | 2014.01.13 |