커널 오브젝트 보는 방법
Searching for Process with Cid == 140
Cid Handle table at e1003000 with 256 Entries in use
PROCESS 81f32be0 SessionId: none Cid: 0140 Peb: 7ffd9000 ParentCid: 0004
DirBase: 037d0000 ObjectTable: e100f658 HandleCount: 18.
Image: smss.exe
kd> dt nt!_HANDLE_TABLE e100f658
nt!_HANDLE_TABLE
+0x000 TableCode : 0xe1293000
+0x004 QuotaProcess : 0x81f32be0 _EPROCESS
+0x008 UniqueProcessId : 0x00000140
+0x00c HandleTableLock : [4] _EX_PUSH_LOCK
+0x01c HandleTableList : _LIST_ENTRY [ 0xe1374a4c - 0xe1000d84 ]
+0x024 HandleContentionEvent : _EX_PUSH_LOCK
+0x028 DebugInfo : (null)
+0x02c ExtraInfoPages : 0
+0x030 FirstFree : 0x50
+0x034 LastFree : 0
+0x038 NextHandleNeedingPool : 0x800
+0x03c HandleCount : 18
+0x040 Flags : 0
+0x040 StrictFIFO : 0y0
kd> dd 0xe1293000
e1293000 00000000 fffffffe e1008591 000f0003
e1293010 81ecdd53 00100020 e1350639 001f0001
e1293020 e14a7bb1 001f0001 e1009e89 000f000f
e1293030 e1387dc1 000f000f 81ed2691 00100001
e1293040 e1004fc9 000f0001 e137c9f9 000f000f
e1293050 81efa4f1 001f0003 e13c5459 00020006
e1293060 81f827e9 001f0003 81f8c009 001f0fff
e1293070 81f8c009 00000400 e13fd539 001f0001
kd> dt nt!_HANDLE_TABLE_ENTRY
+0x000 Object : Ptr32 Void
+0x000 ObAttributes : Uint4B
+0x000 InfoTable : Ptr32 _HANDLE_TABLE_ENTRY_INFO
+0x000 Value : Uint4B
+0x004 GrantedAccess : Uint4B
+0x004 GrantedAccessIndex : Uint2B
+0x006 CreatorBackTraceIndex : Uint2B
+0x004 NextFreeTableEntry : Int4B
kd> dt nt!_OBJECT_HEADER e1008591&0xfffffffc
+0x000 PointerCount : 17
+0x004 HandleCount : 16
+0x004 NextToFree : 0x00000010
+0x008 Type : 0x81fb5040 _OBJECT_TYPE
+0x00c NameInfoOffset : 0x10 ''
+0x00d HandleInfoOffset : 0 ''
+0x00e QuotaInfoOffset : 0 ''
+0x00f Flags : 0x32 '2'
+0x010 ObjectCreateInfo : 0x00000001 _OBJECT_CREATE_INFORMATION
+0x010 QuotaBlockCharged : 0x00000001
+0x014 SecurityDescriptor : 0xe100a77a
+0x018 Body : _QUAD
kd> dt nt!_OBJECT_TYPE 0x81fb5040
+0x000 Mutex : _ERESOURCE
+0x038 TypeList : _LIST_ENTRY [ 0x81fb5078 - 0x81fb5078 ]
+0x040 Name : _UNICODE_STRING "KeyedEvent"
+0x048 DefaultObject : 0x80561b40
+0x04c Index : 0x10
+0x050 TotalNumberOfObjects : 1
+0x054 TotalNumberOfHandles : 0x10
+0x058 HighWaterNumberOfObjects : 1
+0x05c HighWaterNumberOfHandles : 0x11
+0x060 TypeInfo : _OBJECT_TYPE_INITIALIZER
+0x0ac Key : 0x6579654b
+0x0b0 ObjectLocks : [4] _ERESOURCE
W32pServiceTable 원복하기 ( Win32K Hooking 무력화 )
KeServiceDescriptorTableShadow를 통해, 보면,
80552140 80501030 nt!KiServiceTable
80552144 00000000
80552148 0000011c
8055214c 805014a4 nt!KiArgumentTable
80552150 bf997600 win32k!W32pServiceTable
80552154 00000000
80552158 0000029b
8055215c bf998310 win32k!W32pArgumentTable
win32k!W32pServiceTable 이 보이고~ 내용을 확인해봅니다.
bf997600 ????????
bf997604 ????????
bf997608 ????????
bf99760c ????????
orz;;; 젠장.. 안그래도 요새 되는일도 없는데, 내용까지 없네요...
페이징 문제인건가...
일단 GDI 관련된 프로세스를 만들어서 붙혀봅니다.
kd> !process 0 0
**** NT ACTIVE PROCESS DUMP ****
...
PROCESS 81d45bd0 SessionId: 0 Cid: 0430 Peb: 7ffdb000 ParentCid: 05a8
DirBase: 072c0260 ObjectTable: e10e1d18 HandleCount: 13.
Image: S****ge2.exe
kd> .process /i 81d45bd0
You need to continue execution (press 'g' <enter>) for the context
to be switched. When the debugger breaks in again, you will be in
the new process context.
kd> g
Break instruction exception - code 80000003 (first chance)
nt!RtlpBreakWithStatusInstruction:
80526da8 cc int 3
다시 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
bf997620 bf9479d0 win32k!NtGdiAngleArc
bf997624 bf933a9d win32k!NtGdiAnyLinkedFonts
bf997628 bf947fc8 win32k!NtGdiFontIsLinked
bf99762c bf90e7e0 win32k!NtGdiArcInternal
bf997630 bf88e5fe win32k!NtGdiBeginPath
나옵니다. 낄낄...
좀전에 걸려있던 쓰레드는, GDI 를 안쓰나 봅니다. 확인은 귀찮아서 패스하고...
nt 모듈에서 그랬던 것처럼, W32pServiceTable 이라는 변수를 파일에서 확인해보면,
.data:BF999B80 ; DATA XREF: DriverEntry(x,x)+F6o
.data:BF999B80 ; NtGdiAbortDoc(x)
.data:BF999B84 dd offset _NtGdiAbortPath@4 ; NtGdiAbortPath(x)
.data:BF999B88 dd offset _NtGdiAddFontResourceW@24 ; NtGdiAddFontResourceW(x,x,x,x,x,x)
.data:BF999B8C dd offset _NtGdiAddRemoteFontToDC@16 ; NtGdiAddRemoteFontToDC(x,x,x,x)
.data:BF999B90 dd offset _NtGdiAddFontMemResourceEx@20 ; NtGdiAddFontMemResourceEx(x,x,x,x,x)
.data:BF999B94 dd offset _NtGdiRemoveMergeFont@8 ; NtGdiRemoveMergeFont(x,x)
.data:BF999B98 dd offset _NtGdiAddRemoteMMInstanceToDC@12 ; NtGdiAddRemoteMMInstanceToDC(x,x,x)
.data:BF999B9C dd offset _NtGdiAlphaBlend@48 ; NtGdiAlphaBlend(x,x,x,x,x,x,x,x,x,x,x,x)
.data:BF999BA0 dd offset _NtGdiAngleArc@24 ; NtGdiAngleArc(x,x,x,x,x,x)
.data:BF999BA4 dd offset _NtGdiAnyLinkedFonts@0 ; NtGdiAnyLinkedFonts()
.data:BF999BA8 dd offset _NtGdiFontIsLinked@4 ; NtGdiFontIsLinked(x)
.data:BF999BAC dd offset _NtGdiArcInternal@40 ; NtGdiArcInternal(x,x,x,x,x,x,x,x,x,x)
.data:BF999BB0 dd offset _NtGdiBeginPath@4 ; NtGdiBeginPath(x)
.data:BF999BB4 dd offset _NtGdiBitBlt@44 ; NtGdiBitBlt(x,x,x,x,x,x,x,x,x,x,x)
.data:BF999BB8 dd offset _NtGdiCancelDC@4 ; NtGdiCancelDC(x)
.data:BF999BBC dd offset _NtGdiCheckBitmapBits@32 ; NtGdiCheckBitmapBits(x,x,x,x,x,x,x,x)
.data:BF999BC0 dd offset _NtGdiCloseFigure@4 ; NtGdiCloseFigure(x)
.data:BF999BC4 dd offset _NtGdiClearBitmapAttributes@8 ; NtGdiClearBitmapAttributes(x,x)
.data:BF999BC8 dd offset _NtGdiClearBrushAttributes@8 ; NtGdiClearBrushAttributes(x,x)
우왕ㅋ 굿~
DriverEntry 에서 셋팅하나봅니다.
INIT:BF9AFD0F _DriverEntry@8 proc near ; DATA XREF: DriverEntry(x,x)+1CEo
INIT:BF9AFD0F
....
INIT:BF9AFDF2 push edi
INIT:BF9AFDF3 push offset _W32pArgumentTable
INIT:BF9AFDF8 push _W32pServiceLimit
INIT:BF9AFDFE mov _countTable, esi
INIT:BF9AFE04 push esi
INIT:BF9AFE05 push offset _W32pServiceTable
INIT:BF9AFE0A call ds:__imp__KeAddSystemServiceTable@20 ; KeAddSystemServiceTable(x,x,x,x,x)
코드를 보니, W32pServiceTable 변수를 인자로,
KeAddSystemServiceTable 함수를 호출하여, SST를 등록하는 것 같습니다.
뭐... 끝 났네요...
win32k.sys 의 DriverEntry 로부터,
KeAddSystemServiceTable 의 주소를 호출하는 CALL 문을 찾는,
소스 코드 작성하면 되겠군요.
orz;;;
SSDT 원복하기 ( SDT Hooking 무력화 )
SDT Hooking 은, Service Descriptor Table 에 등록된 각 함수들의 Addr 을 변경하여
... 하.. 설명 귀찮...
어쨋든,
시스템의 SSDT 주소는 KeServiceDescriptorTable 이라는 커널 변수로 export 되어있습니다.
이 커널 변수가 가진 내용을 보면,
80552180 80501030 nt!KiServiceTable
80552184 00000000
80552188 0000011c
8055218c 805014a4 nt!KiArgumentTable
위와 같이 nt 모듈의 KiServiceTable 을 보고 있습니다.
80501030 8059849a nt!NtAcceptConnectPort
80501034 805e5666 nt!NtAccessCheck
80501038 805e8ec4 nt!NtAccessCheckAndAuditAlarm
8050103c 805e5698 nt!NtAccessCheckByType
80501040 805e8efe nt!NtAccessCheckByTypeAndAuditAlarm
80501044 805e56ce nt!NtAccessCheckByTypeResultList
80501048 805e8f42 nt!NtAccessCheckByTypeResultListAndAuditAlarm
실제 테이블이 위치한 nt 모듈의 섹션을 확인하기 위해, PE 정보와 모듈의 베이스를 연산해보면
+0x000 Signature : 0x4550
+0x004 FileHeader : _IMAGE_FILE_HEADER
+0x000 Machine : 0x14c
+0x002 NumberOfSections : 0x19
+0x004 TimeDateStamp : 0x41107b0c
+0x008 PointerToSymbolTable : 0
+0x00c NumberOfSymbols : 0
+0x010 SizeOfOptionalHeader : 0xe0
+0x012 Characteristics : 0x12e
+0x018 OptionalHeader : _IMAGE_OPTIONAL_HEADER
+0x000 Magic : 0x10b
+0x002 MajorLinkerVersion : 0x7 ''
+0x003 MinorLinkerVersion : 0xa ''
+0x004 SizeOfCode : 0x19cd00
+0x008 SizeOfInitializedData : 0x58f80
+0x00c SizeOfUninitializedData : 0
+0x010 AddressOfEntryPoint : 0x1b66dc
+0x014 BaseOfCode : 0x600
+0x018 BaseOfData : 0x6da00
start end module name
7c900000 7c9b0000 ntdll (pdb symbols) c:\symbols\ntdll.pdb\
804d7000 806cd280 nt (pdb symbols) c:\symbols\ntkrnlpa.pdb\
nt 모듈의 코드 영역에 들어있는 테이블임을 알 수 있고.
코드 영역에 들어있는 테이블 이므로, 파일에서도 같은 테이블을 갖고 있다고 예상할 수 있습니다.
다음은 IDA 로 nt 파일을 열어, 찾은 KiServiceTable 입니다.
.text:0040D8A0 ; DATA XREF: KiInitSystem()+116o
.text:0040D8A0 ; NtAcceptConnectPort(x,x,x,x,x,x)
.text:0040D8A4 dd offset _NtAccessCheck@32 ; NtAccessCheck(x,x,x,x,x,x,x,x)
.text:0040D8A8 dd offset _NtAccessCheckAndAuditAlarm@44 ; NtAccessCheckAndAuditAlarm(x,x,x,x,x,x,x,x,x,x,x)
.text:0040D8AC dd offset _NtAccessCheckByType@44 ; NtAccessCheckByType(x,x,x,x,x,x,x,x,x,x,x)
.text:0040D8B0 dd offset _NtAccessCheckByTypeAndAuditAlarm@64 ; NtAccessCheckByTypeAndAuditAlarm(x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x)
.text:0040D8B4 dd offset _NtAccessCheckByTypeResultList@44 ; NtAccessCheckByTypeResultList(x,x,x,x,x,x,x,x,x,x,x)
.text:0040D8B8 dd offset _NtAccessCheckByTypeResultListAndAuditAlarm@64 ; NtAccessCheckByTypeResultListAndAuditAlarm(x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x)
.text:0040D8BC dd offset _NtAccessCheckByTypeResultListAndAuditAlarmByHandle@68 ; NtAccessCheckByTypeResultListAndAuditAlarmByHandle(x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x)
.text:0040D8C0 dd offset _NtAddAtom@12 ; NtAddAtom(x,x,x)
.text:0040D8C4 dd offset _NtEnumerateBootEntries@8 ; NtEnumerateBootEntries(x,x)
.text:0040D8C8 dd offset _NtAdjustGroupsToken@24 ; NtAdjustGroupsToken(x,x,x,x,x,x)
.text:0040D8CC dd offset _NtAdjustPrivilegesToken@24 ; NtAdjustPrivilegesToken(x,x,x,x,x,x)
.text:0040D8D0 dd offset _NtAlertResumeThread@8 ; NtAlertResumeThread(x,x)
.text:0040D8D4 dd offset _NtAlertThread@4 ; NtAlertThread(x)
.text:0040D8D8 dd offset _NtAllocateLocallyUniqueId@4 ; NtAllocateLocallyUniqueId(x)
.text:0040D8DC dd offset _NtAllocateUserPhysicalPages@12 ; NtAllocateUserPhysicalPages(x,x,x)
.text:0040D8E0 dd offset _NtAllocateUuids@16 ; NtAllocateUuids(x,x,x,x)
.text:0040D8E4 dd offset _NtAllocateVirtualMemory@24 ; NtAllocateVirtualMemory(x,x,x,x,x,x)
KiServiceTable 을 KiInitSystem() 이라는 함수에서 사용하고 있습니다.
이 함수의 내용을 확인해보면,
INIT:005EF1DE _KiInitSystem@0 proc near ; CODE XREF: KiInitializeKernel(x,x,x,x,x,x)+230p
INIT:005EF1DE push 20h
INIT:005EF1E0 mov eax, offset _KiDispatcherReadyListHead
...
INIT:005EF2E0 mov ds:byte_48B46C, 1
INIT:005EF2E7 mov ds:byte_48B46E, 4
INIT:005EF2EE mov ds:dword_48B470, esi
INIT:005EF2F4 mov ds:_KeServiceDescriptorTable, offset _KiServiceTable
INIT:005EF2FE mov ds:dword_48B524, esi
INIT:005EF304 mov ds:dword_48B52C, offset _KiArgumentTable
...
INIT:005EF32A pop esi
INIT:005EF32B retn
INIT:005EF32B _KiInitSystem@0 endp
KeServiceDescriptorTable 커널 변수를 설정하는 본체 함수임을 알 수 있습니다.
위의 내용을 종합했을 때,
nt 파일을 열어, KiServiceTable 의 Offset 을 알아내면, 실제 SystemService 들의 Original RVA 를 알 수 있을것 같습니다.
다음은 이러한 내용을 바탕으로 SDT 를 복원하는 방법입니다.
단, 이 소스는 파일을 직접 열어 OFFSET 연산을 한 것은 아니고,
LoadLibraryEx API 를 사용하여, 파일을 메모리에 올리는 것으로 되어있습니다.
( http://rootkit.com/newsread.php?newsid=176 )
코드의 주 내용은, KiInitSystem() 함수 내부에서
부분을 찾아 _KiServiceTable 의 offset 을 알아내는 것 입니다.
실제 사용된 코드는 다음과 같습니다.
// check for mov [mem32],imm32. we are trying to find
// "mov ds:_KeServiceDescriptorTable.Base, offset _KiServiceTable"
// from the KiInitSystem.
if (*(PWORD)((DWORD)hModule+dwPointerRva-2)==0x05c7) {
// should check for a reloc presence on KiServiceTable here
// but forget it
dwKiServiceTable=*(PDWORD)((DWORD)hModule+dwPointerRva+4)-poh->ImageBase;
return dwKiServiceTable;
}
}
export 된, KeServiceDescriptorTable 의 주소는 이미 알고 있으므로,
해당 주소에 특정값을 대입하는 Asm OPCODE 를 검색하고
그 명령줄에서 KiServiceTable(dwKiServiceTable) 의 offset 을 반환합니다.
위의 offset 에서 알아낸 RVA를 연산하여 확인한다면,
SDT Hooking 을 풀 수... 있을지는 사실 미지수입니다.
국내외 보안 제품들의 경우, SDT Hooking 을 풀어버리면, 다시 걸고 걸고 반복하면서 BSOD 를 내는 경우가 많은듯합니다.
... orz;;; 좌절~
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
하지만...
대충 풀어본거라서, 정답인지는 잘 모르겠네요 ㅎㅎ;;