겸손한 개발을 위한 자양분

Intel 의 32bit CPU / 최근의 OS 에서는
시스템 프로시져 호출을 위해 SYSENTER 명령을 사용합니다.
http://sysenter.tistory.com/entry/SYSENTER


AMD 에서는 SYSCALL 명령을 사용하구요...

그런데, 64비트에서는 x64 명령어셋에 대해 AMD 가 주도하다 보니,
SYSCALL 으로 통일되었나봅니다. 아닌가? ㅡㅡㅋ

어쨋든~
64bit VISTA 를 분석하다보니,
SYSENTER 가 아닌, SYSCALL 을 호출합니다.
AMD Processor 도 INTEL Processor 도 마찬가지입니다.


AMD 의 SYSCALL and SYSRET Instruction Specification 을 보면

The EIP register is copied into the ECX register. Bits [31–0] of the 64-bit
SYSCALL/SYSRET Target Address Register (STAR) are copied into the EIP register.
(The STAR register is Model-Specific Register C000_0081h.

대충 보면, Instructuon Point 가 MSR C000_0081 에 지정되는것 같습니다.
아... 이것 때문에 삽질 겁나 했습니다.
인터넷 아무리 뒤져봐도, SYSCALL 은 MSR C000_0081h 로 나오는데...
잘은 모르지만, 이건 32Bit OS 일 때, 이야기인것 같습니다.

AMD BIOS and Kernel Developer’s Guide (BKDG) 를 확인하니,

MSRC000_0082 Long Mode SYSCALL Target Address Register (STAR64)
Reset: X.
Bits Description
63:0 LSTAR: long mode target address. Read-write. Target address for 64-bit mode calling programs.
The address stored in this register must be in canonical form (if not canonical, a #GP fault occurs).

64Bit 모드에서는 MSR C000_0082 에 타겟 주소가 지정된다고 합니다.
하아... C000_0081 때문에, 하루를 삽질했는데...


뭐쨋든 확인해보면,

0: kd> rdmsr C0000082h
msr[c0000082] = fffff800`018aec00

0: kd> u fffff800`018aec00
nt!KiSystemCall64:
fffff800`018aec00 0f01f8          swapgs
fffff800`018aec03 654889242510000000 mov   qword ptr gs:[10h],rsp
fffff800`018aec0c 65488b2425a8010000 mov   rsp,qword ptr gs:[1A8h]
fffff800`018aec15 6a2b            push    2Bh
fffff800`018aec17 65ff342510000000 push    qword ptr gs:[10h]
fffff800`018aec1f 4153            push    r11
fffff800`018aec21 6a33            push    33h
fffff800`018aec23 51              push    rcx

KiSystemCall64 의 주소가 지정되어있는 것을 알 수 있습니다.

ㅡㅡa
NT kernel 에서 KiFastCallEntry 가 안나오길래 의아했었는데,
64Bit 는 저 함수가 처리하나봅니다.

아, 32Bit Intel OS 에서는
ntdll 의 KiFastSystemCall 이라는 루틴을 통해서 커널모드로 들어가는데,
64Bit OS 에서는 이 루틴을 통하지 않습니다.

OpenProcess 를 통해 동작을 비교해보면,



32Bit Intel OS

Application Level

kernel32!OpenProcess

ntdll!ZwOpenProcess

ntdll!KiFastSystemCall
nt!KiFastCallEntry

nt!NtOpenProcess

Kernel Level



64Bit OS

Application Level

kernel32!OpenProcess

ntdll!ZwOpenProcess
nt!KiSystemCall64

nt!NtOpenProcess

Kernel Level


위와 같죠.


재밌는건..
64Bit 에서도 유저모드의 kernel32 모듈 이름이 동일합니다.
은근히... kernel64, user64 를 기대하기도 했는데 말이죠..


KiSystemCall64 의 심볼을 보면
KiSystemServiceStart 와 같은 이름을 볼 수 있는데,
구글링해보면, 이 함수가 시스템 서비스를 호출하는 본체로 설명되어있습니다.

정확히 본다면,

SDT 를 참조하는 부분은
nt!KiSystemServiceRepeat 이고,
실제 호출하는 부분은
nt!KiSystemServiceCopyEnd 입니다.

흠.. 위의 두 심벌은 함수는 아닌것 같습니다. 스택 정리 관련 명령이 내부에 없어서...
GOTO 문에 사용된 심벌인가? ㅡㅡa 잘 모릅니다 저런거...



SDT 는...

1: kd> dqs nt!KeServiceDescriptorTable
fffff800`01a31980  fffff800`01860d00 nt!KiServiceTable
fffff800`01a31988  00000000`00000000
fffff800`01a31990  00000000`00000187
fffff800`01a31998  fffff800`0186193c nt!KiArgumentTable
fffff800`01a319a0  00000000`00000000
fffff800`01a319a8  00000000`00000000
fffff800`01a319b0  00000000`00000000
fffff800`01a319b8  00000000`00000000
fffff800`01a319c0  fffff800`01860d00 nt!KiServiceTable
fffff800`01a319c8  00000000`00000000
fffff800`01a319d0  00000000`00000187
fffff800`01a319d8  fffff800`0186193c nt!KiArgumentTable
fffff800`01a319e0  fffff960`00103100 win32k!W32pServiceTable
fffff800`01a319e8  00000000`00000000
fffff800`01a319f0  00000000`00000306
fffff800`01a319f8  fffff960`00104c3c win32k!W32pArgumentTable

32bit 에서 구했던 것 처럼 쉽게 확인 가능합니다.
ㅡㅡa 그런데, win32k!W32pServiceTable 이 바로 나오네요. 32도 그랬나? '.'?

서비스 테이블은
nt 함수들과 1:1 매칭이 되지 않습니다.
아마 패치가드인가 그것 때문인것 같은데
이게, 전체 모양이 다 안나오는게 아니라

1: kd> dqs nt!KiServiceTable L187
fffff800`018b2d00  02780a00`03969f00
fffff800`018b2d08  02668005`fff69d00
fffff800`018b2d10  0289e905`029b2206
fffff800`018b2d18  02728700`026aeb01
fffff800`018b2d20  023a5e00`023a5c40

.....

fffff800`018b3908  fffff800`01c210c0 nt!NtWaitForDebugEvent
fffff800`018b3910  fffff800`01ad7658 nt!NtWaitForKeyedEvent
fffff800`018b3918  fffff800`018caabc nt!NtWaitForWorkViaWorkerFactory
fffff800`018b3920  fffff800`01c16a40 nt!NtWaitHighEventPair
fffff800`018b3928  fffff800`01c16ad0 nt!NtWaitLowEventPair
fffff800`018b3930  fffff800`018ed34c nt!NtWorkerFactoryWorkerReady


한, 반정도는 안나오고 나머지 반정도는 나오네요.

패치가드때문에, SDT 훅이 안된다고 하던데,
된다하더라도 저 숨은 Quad Word 의 비밀을 풀어야할 것 같습니다.

그나저나, SDT 훅이 안되니...
-_ㅡa 뭔 수로 후킹을 하지...

MSR 값 변조로 KiSystemCall 후킹, 아니면 CALL r10 후킹... 되려나...