본문 바로가기

Research/Kernel

SYSENTER 가 보이지 않는 이유








lkd> u ntdll!ntcreatefile

ntdll!ZwCreateFile:

7c93d090 b825000000   mov     eax,25h

7c93d095 ba0003fe7f    mov     edx,offset SharedUserData!SystemCallStub (7ffe0300)

7c93d09a ff12              call    dword ptr [edx]

7c93d09c c22c00         ret     2Ch

7c93d09f 90                nop






lkd> u SharedUserData!SystemCallStub

SharedUserData!SystemCallStub:

7ffe0300 f0e493          lock in al,93h

7ffe0303 7cf4            jl      SharedUserData+0x2f9 (7ffe02f9)

7ffe0305 e493            in      al,93h

7ffe0307 7c00            jl      SharedUserData!SystemCallStub+0x9 (7ffe0309)

7ffe0309 0000            add     byte ptr [eax],al

7ffe030b 0000            add     byte ptr [eax],al

7ffe030d 0000            add     byte ptr [eax],al

7ffe030f 0000            add     byte ptr [eax],al



???


lkd> u poi(SharedUserData!SystemCallStub)

ntdll!KiFastSystemCall:

7c93e4f0 8bd4                        mov     edx,esp

7c93e4f2 0f34                         sysenter

ntdll!KiFastSystemCallRet:

7c93e4f4 c3                           ret

7c93e4f5 8da42400000000         lea     esp,[esp]

7c93e4fc 8d642400                  lea     esp,[esp]

ntdll!KiIntSystemCall:

7c93e500 8d542408                 lea     edx,[esp+8]

7c93e504 cd2e                       int     2Eh

7c93e506 c3                          ret



!!!




뭐냐이게. 하고 고민했다 왜 sysenter 가 안 나오고 엉뚱한 게 나오나 ?

시스인터널을 뒤지다 보니 같은 질문을 한 사람이 있어서 갖고왔다.

왜 poi() 함수가 필요한가요 ?




SystemCall 은 NTDLL 의 stub 를 가리키는 포인터다.


※ In XP SP2 and Srv03 SP1, in the interests of reducing system attack surface, the KUSER_SHARED_DATA region was marked non-executable, and SystemCall becomes a pointer to a stub residing in NTDLL.


공격 요소를 줄이기 위해서 KUSER_SHARED_DATA 영역의 실행 권한을 없애고

대신 SystemCall 을 포인터로 만들었다. 라는 의미.


XP 이전의 방식에서 시스템콜을 호출하려면 INT 2E 명령어(SW 인터럽트)를 이용했어야 했다.

커널 모드로 들어가는 빠르고 편한 방법이었고, 32비트의 x86 프로세서와 호환이 가능한 방식이었다.

XP 시스템으로 넘어오면서 시스템콜 메커니즘이 바뀌었는데,

이 운영체제는 프로세서 타입에 따라 더 고성능의 커널 트랜지션 매커니즘을 사용했다.

팬티엄 2 이후의 프로세서들은 커널 모드로 진입하는 더 효과적인 방법인 systenter 을 사용하게 되었는데, 기존의 인터럽트 요청 방식보다 더 오버헤드가 적었다.

어떻게 문제 없이 방식을 교체했을까 ? XP SP2 와 Serv03 SP1 운영체제 이후, system service call stub 는 INT 2E처럼 명령어들을 하드코딩하는 방식을 사용하지 않았다. 대신에 KUSER_SHARED_DATA 영역으로 리다이렉텬 시키는 방법을 사용한다. 이전에는 시스템콜 영역의 코드는 고정시켜 두고 실시간으로 적절한 값을 채워주는 형식이었다.


XP sp2 와 Srv03 sp1 에서 KUSER_SHARED_DATA 영역은 실행권한이 없고, SystemCall 은 NTDLL 의 stub 를 가리키는 포인터가 되었다. (이 포인터는 적절한 system call stub 를 찾기 위해서 프로세서 타입에 따라 실시간으로 변한다. - ??)



그래서 NTDLL 함수를 조회했을 때 나왔던 디스어셈블 코드 중 SharedUserData!SystemCallStub (7ffe0300)


7FFE0300 은 그 자체가 아니라 KUSER_SHARED_DATA 영역을 조회했을 때 +300 위치에 있는 포인터 값을 가리킨다.


lkd> dt ntdll!_KUSER_SHARED_DATA

   +0x000 TickCountLow     : Uint4B

   +0x004 TickCountMultiplier : Uint4B

   +0x008 InterruptTime    : _KSYSTEM_TIME

   +0x014 SystemTime       : _KSYSTEM_TIME

   +0x020 TimeZoneBias     : _KSYSTEM_TIME

   +0x02c ImageNumberLow   : Uint2B

   +0x02e ImageNumberHigh  : Uint2B

   +0x030 NtSystemRoot     : [260] Uint2B

   +0x238 MaxStackTraceDepth : Uint4B

   +0x23c CryptoExponent   : Uint4B

   +0x240 TimeZoneId       : Uint4B

   +0x244 Reserved2        : [8] Uint4B

   +0x264 NtProductType    : _NT_PRODUCT_TYPE

   +0x268 ProductTypeIsValid : UChar

   +0x26c NtMajorVersion   : Uint4B

   +0x270 NtMinorVersion   : Uint4B

   +0x274 ProcessorFeatures : [64] UChar

   +0x2b4 Reserved1        : Uint4B

   +0x2b8 Reserved3        : Uint4B

   +0x2bc TimeSlip         : Uint4B

   +0x2c0 AlternativeArchitecture : _ALTERNATIVE_ARCHITECTURE_TYPE

   +0x2c8 SystemExpirationDate : _LARGE_INTEGER

   +0x2d0 SuiteMask        : Uint4B

   +0x2d4 KdDebuggerEnabled : UChar

   +0x2d5 NXSupportPolicy  : UChar

   +0x2d8 ActiveConsoleId  : Uint4B

   +0x2dc DismountCount    : Uint4B

   +0x2e0 ComPlusPackage   : Uint4B

   +0x2e4 LastSystemRITEventTickCount : Uint4B

   +0x2e8 NumberOfPhysicalPages : Uint4B

   +0x2ec SafeBootMode     : UChar

   +0x2f0 TraceLogging     : Uint4B

   +0x2f8 TestRetInstruction : Uint8B

   +0x300 SystemCall       : Uint4B

   +0x304 SystemCallReturn : Uint4B

   +0x308 SystemCallPad    : [3] Uint8B

   +0x320 TickCount        : _KSYSTEM_TIME

   +0x320 TickCountQuad    : Uint8B

   +0x330 Cookie           : Uint4B




그래서 값을 찾아보면

lkd> dd 7ffe0300

7ffe0300  7c93e4f0 7c93e4f4 00000000 00000000

7ffe0310  00000000 00000000 00000000 00000000

7ffe0320  00000000 00000000 00000000 00000000

7ffe0330  70589fcd 00000000 00000000 00000000

7ffe0340  00000000 00000000 00000000 00000000

7ffe0350  00000000 00000000 00000000 00000000

7ffe0360  00000000 00000000 00000000 00000000

7ffe0370  00000000 00000000 00000000 00000000


7C93E4F0 이 있고, 이 부분을 u 로 디스어셈블 해야 SYSENTER 가 보인다.


lkd> u 7c93e4f0

ntdll!KiFastSystemCall:

7c93e4f0 8bd4            mov     edx,esp

7c93e4f2 0f34            sysenter

ntdll!KiFastSystemCallRet:

7c93e4f4 c3              ret

7c93e4f5 8da42400000000  lea     esp,[esp]

7c93e4fc 8d642400        lea     esp,[esp]

ntdll!KiIntSystemCall:

7c93e500 8d542408        lea     edx,[esp+8]

7c93e504 cd2e            int     2Eh

7c93e506 c3              ret




www.nynaeve.net/?p=48

"The system call dispatcher on x86"




06년 문서를 이제서야 찾아보다니 할일이 태산이다..