USER32.DLL 에서의 sysenter
NTDLL 이 제공하는 API 는,
eax 에 Service Table 의 Index 를 설정하고 sysenter 를 호출합니다.
하지만, USER32.DLL 은 eax 에 설정되는 값의 모양이 약간 다릅니다.
SendMessageA 함수의 호출 구조를 살펴보면서, 다른 점을 찾아보겠습니다.
SendMessageA 가 호출하는 함수들을 확인하면 다음과 같습니다.
Flow analysis was incomplete, some code may be missing
USER32!SendMessageA (77d4e2ae)
USER32!SendMessageA+0x2a (77d4e2db):
call to USER32!ValidateHwnd (77d48490)
USER32!SendMessageA+0x41 (77d4e2f2):
call to USER32!SendMessageWorker (77d4b67b)
USER32!SendMessageA+0x48 (77d6da92):
call to USER32!GetDesktopWindow (77d4d7bb)
함수들의 용도를 볼때, 메인 함수는 SendMessageWorker 일 것으로 생각됩니다.
다시 이 함수가 호출하는 함수를 확인하면,
Flow analysis was incomplete, some code may be missing
USER32!SendMessageWorker (77d4b67b)
USER32!SendMessageWorker+0x31 (77d4b6ac):
call to USER32!PtiCurrent (77d48625)
USER32!SendMessageWorker+0x26f (77d4b70e):
call to USER32!IsInsideUserApiHook (77d4860c)
USER32!SendMessageWorker+0x4a0 (77d4b73e):
call to USER32!UserCallWinProcCheckWow (77d48734)
USER32!SendMessageWorker+0x4d2 (77d4c0d9):
call to USER32!NtUserMessageCall (77d494d7)
USER32!SendMessageWorker+0x4e4 (77d4de67):
unresolvable call: call dword ptr USER32!gapfnScSendMessage (77d418e8)[eax*4]
USER32!SendMessageWorker+0x149 (77d70edd):
call to USER32!UserSetLastError (77d4f41f)
USER32!SendMessageWorker+0x180 (77d70ef7):
call to USER32!RtlMBMessageWParamCharToWCS (77d4da9f)
USER32!SendMessageWorker+0x1c1 (77d70f38):
call to kernel32!IsDBCSLeadByteEx (7c87a17a)
USER32!SendMessageWorker+0x1e1 (77d70f58):
call to USER32!RtlWCSMessageWParamCharToMB (77d4c4ef)
NtUserMessageCall 이라는 함수가 보입니다.
( 정확히 하자면, 이 함수가 메인인지 확인해야 하지만, sysenter 에서의 system service 확인이 목적이므로 생략합니다. )
USER32!NtUserMessageCall:
77d494d7 b8cc110000 mov eax,11CCh
77d494dc ba0003fe7f mov edx,offset SharedUserData!SystemCallStub (7ffe0300)
77d494e1 ff12 call dword ptr [edx]
77d494e3 c21c00 ret 1Ch
NTDLL.DLL 에서 어플리케이션 레벨의 마지막 호출과 마찬가지로
ntdll!KiFastSystemCall 이 호출되는 것을 알 수 있습니다.
7ffe0300 7c90eb8b ntdll!KiFastSystemCall
이제 실제 NtUserMessageCall 에 브레이크 포인트를 걸고
PROCESS 820c7020 SessionId: 0 Cid: 07b8 Peb: 7ffd4000 ParentCid: 058c
kd> g
Breakpoint 13 hit
USER32!NtUserMessageCall:
001b:77d494d7 b8cc110000 mov eax,11CCh
sysenter 직전의 레지스터를 확인합니다.
eax=000011cc ebx=00000000 ecx=0012fad8 edx=0012faac esi=00521770 edi=00000202
eip=7c90eb8d esp=0012faac ebp=0012fae4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!KiFastSystemCall+0x2:
001b:7c90eb8d 0f34 sysenter
sysenter 의 동작 방식에 따라, 브레이크포인트를 걸고
kd> g
Breakpoint 1 hit
nt!KiFastCallEntry:
8053c710 b923000000 mov ecx,23h
eax=000011cc ebx=00000000 ecx=0012fad8 edx=0012faac esi=00521770 edi=00000202
eip=8053c710 esp=f8ac6000 ebp=0012fae4 iopl=0 nv up di pl zr na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000046
nt!KiFastCallEntry:
8053c710 b923000000 mov ecx,23h
kd> bd 0-1
커널에서의 KiFastCallEntry 를 분석하면,
8053c710 b923000000 mov ecx,23h
8053c715 6a30 push 30h
8053c717 0fa1 pop fs
8053c719 8ed9 mov ds,cx
8053c71b 8ec1 mov es,cx
8053c71d 8b0d40f0dfff mov ecx,dword ptr ds:[0FFDFF040h]
8053c723 8b6104 mov esp,dword ptr [ecx+4]
8053c726 6a23 push 23h
8053c728 52 push edx
8053c729 9c pushfd
8053c72a 6a02 push 2
8053c72c 83c208 add edx,8
...
8053c79d 8bf8 mov edi,eax -> edi=000011cc
8053c79f c1ef08 shr edi,8 -> edi=00000011
8053c7a2 83e730 and edi,30h -> edi=00000010
8053c7a5 8bcf mov ecx,edi -> ecx=edi=00000010
8053c7a7 03bee0000000 add edi,dword ptr [esi+0E0h] ds:0023:820dfc10={nt!KeServiceDescriptorTableShadow (80552140)}
8053c7ad 8bd8 mov ebx,eax
8053c7af 25ff0f0000 and eax,0FFFh -> eax=000001cc
8053c7b4 3b4708 cmp eax,dword ptr [edi+8]
8053c7b7 0f8345fdffff jae nt!KiBBTUnexpectedRange (8053c502)
8053c7bd 83f910 cmp ecx,10h
8053c7c0 751a jne nt!KiFastCallEntry+0xcc (8053c7dc)
8053c7c2 8b0d18f0dfff mov ecx,dword ptr ds:[0FFDFF018h]
8053c7c8 33db xor ebx,ebx
8053c7ca 0b99700f0000 or ebx,dword ptr [ecx+0F70h]
8053c7d0 740a je nt!KiFastCallEntry+0xcc (8053c7dc)
8053c7d2 52 push edx
8053c7d3 50 push eax
8053c7d4 ff15c4215580 call dword ptr [nt!KeGdiFlushUserBatch (805521c4)]
8053c7da 58 pop eax
8053c7db 5a pop edx
8053c7dc ff0538f6dfff inc dword ptr ds:[0FFDFF638h]
8053c7e2 8bf2 mov esi,edx
8053c7e4 8b5f0c mov ebx,dword ptr [edi+0Ch]
8053c7e7 33c9 xor ecx,ecx
8053c7e9 8a0c18 mov cl,byte ptr [eax+ebx]
8053c7ec 8b3f mov edi,dword ptr [edi] ds:0023:80552150={win32k!W32pServiceTable (bf997600)}
8053c7ee 8b1c87 mov ebx,dword ptr [edi+eax*4] ds:0023:bf997d30={win32k!NtUserMessageCall (bf80f615)}
8053c7f1 2be1 sub esp,ecx
8053c7f3 c1e902 shr ecx,2
8053c7f6 8bfc mov edi,esp
8053c7f8 3b35b47b5580 cmp esi,dword ptr [nt!MmUserProbeAddress (80557bb4)]
8053c7fe 0f83a8010000 jae nt!KiSystemCallExit2+0x9f (8053c9ac)
8053c804 f3a5 rep movs dword ptr es:[edi],dword ptr [esi]
8053c806 ffd3 call ebx {win32k!NtUserMessageCall (bf80f615)}
8053c808 8be5 mov esp,ebp
...
NTDLL.DLL 에서 제공되는 API 는 SystemServiceIndex 를 eax 에 설정하였지만,
USER32.DLL 에서 제공되는 API 는 eax 에 설정된 값에 0x0FFF 를 AND 연산하여 호출하는 것을 확인할 수 있습니다.
또한, NTDLL.DLL 은 KeServiceDescriptorTableShadow[0]을 참조하는데
USER32.DLL 은 KeServiceDescriptorTableShadow[1]을 참조하고 있습니다.
메모리에서 확인하면 다음과 같습니다.
80552140 80501030 00000000 0000011c 805014a4 ; KeServiceDescriptorTableShadow[0]
80552150 bf997600 00000000 0000029b bf998310 ; KeServiceDescriptorTableShadow[1]
80552160 00000000 00000000 00000000 00000000
80552170 00000000 00000000 00000000 00000000
80552180 80501030 00000000 0000011c 805014a4
80552190 00000000 00000000 00000000 00000000
805521a0 00000000 00000000 00000000 00000000
805521b0 00000000 00000000 00000000 00000000
이 때, KeServiceDescriptorTableShadow[1] 은 win32k!W32pServiceTable 입니다.
bf997600 bf934ffe win32k!NtGdiAbortDoc
bf997604 bf946a92 win32k!NtGdiAbortPath
bf997608 bf8bf295 win32k!NtGdiAddFontResourceW
bf99760c bf93e718 win32k!NtGdiAddRemoteFontToDC
bf997610 bf9480a9 win32k!NtGdiAddFontMemResourceEx
bf997614 bf935262 win32k!NtGdiRemoveMergeFont
bf997618 bf935307 win32k!NtGdiAddRemoteMMInstanceToDC
bf99761c bf839cb5 win32k!NtGdiAlphaBlend
sysenter 호출시의 eax 값이 0x11cc 이고,
앞의 디어셈 결과에 따라 service index 는 0x1cc 이므로,
USER32.DLL 이 제공하는 API 의 System Service 는,
다음과 같이 확인할 수 있습니다.
bf997d30 bf80f615 win32k!NtUserMessageCall
하지만...
대충 풀어본거라서, 정답인지는 잘 모르겠네요 ㅎㅎ;;