Вызов системных сервисов в winxp x64. Кое-какие наброски
Все начинается в ntdll.
public NtRegisterThreadTerminatePort
NtRegisterThreadTerminatePort proc near
mov r10, rcx ; NtRegisterThreadTerminatePort
mov eax, 0E3h
syscall
retn
NtRegisterThreadTerminatePort endp
Значит, куда передается управление. Syscall в своей работе использует MSR lstar [для прог в 64-битном режиме] и cstar в compatibility mode (т.е. режиме совместимости)
lstar – C0000082h (для 64-битного софта) – по этому адресу KiSystemCall64
cstar – C0000083h (в режиме совместимости) – по этому адресу KiSystemCall32
Еще уточнение, что есть long mode, а 64-битный режим и режим совместимости (compatibility mode) являются его частью.
Команда rdmsr,кстати к вопросу чтения из msr, работает в 64-битном режиме так же, как и в 32-битном. EDX:EAX содержат соответственно после того, как команда отработала, старшую и младшую часть адреса. Старшие 32 бита rdx и rax не юзаются
Вернемся к нашей KiSystemCall64.
.text:0000000000440F40 KiSystemCall64 proc near ; DATA XREF: SepAdtInitializePrivilegeAuditing-1ED1Co
.text:0000000000440F40
.text:0000000000440F40 var_110 = qword ptr -110h
.text:0000000000440F40
.text:0000000000440F40 swapgs
.text:0000000000440F43 mov gs:1A0h, rsp
.text:0000000000440F4C mov rsp, gs:1A8h
.text:0000000000440F55 push 2Bh ; usermode ds/es/gs/ss
.text:0000000000440F57 push qword ptr gs:1A0h
.text:0000000000440F5F push r11
.text:0000000000440F61 push 33h ; usermode cs
.text:0000000000440F63 push rcx
.text:0000000000440F64 mov rcx, r10
.text:0000000000440F67 sub rsp, 8
.text:0000000000440F6B push rbp
.text:0000000000440F6C sub rsp, 158h
.text:0000000000440F73 lea rbp, [rsp+190h+var_110]
.text:0000000000440F7B mov [rbp+0C0h], rbx
.text:0000000000440F82 mov [rbp+0C8h], rdi
.text:0000000000440F89 mov [rbp+0D0h], rsi
.text:0000000000440F90 mov byte ptr [rbp-55h], 2
.text:0000000000440F94 mov rbx, gs:188h ; KTHREAD
.text:0000000000440F9D prefetchw byte ptr [rbx+1C8h]
.text:0000000000440FA4 stmxcsr dword ptr [rbp-54h]
.text:0000000000440FA8 ldmxcsr dword ptr gs:180h
.text:0000000000440FB1 test byte ptr [rbx+3], 1
.text:0000000000440FB5 mov word ptr [rbp+80h], 0
.text:0000000000440FBE jz short loc_440FEF
.text:0000000000440FC0 mov [rbp-50h], rax
.text:0000000000440FC4 mov [rbp-48h], rcx
.text:0000000000440FC8 mov [rbp-40h], rdx
.text:0000000000440FCC mov [rbp-38h], r8
.text:0000000000440FD0 mov [rbp-30h], r9
.text:0000000000440FD4 call KiSaveDebugRegisterState
.text:0000000000440FD9 mov rax, [rbp-50h]
.text:0000000000440FDD mov rcx, [rbp-48h]
.text:0000000000440FE1 mov rdx, [rbp-40h]
.text:0000000000440FE5 mov r8, [rbp-38h]
.text:0000000000440FE9 mov r9, [rbp-30h]
.text:0000000000440FED db 66h
.text:0000000000440FED nop
.text:0000000000440FEF
.text:0000000000440FEF loc_440FEF: ; CODE XREF: KiSystemCall64+7Ej
.text:0000000000440FEF sti
.text:0000000000440FEF KiSystemCall64 endp
.text:0000000000440FEF
.text:0000000000440FF0
.text:0000000000440FF0 ; ————— S U B R O U T I N E —————————————
.text:0000000000440FF0
.text:0000000000440FF0
.text:0000000000440FF0 KiSystemServiceStart proc near ; DATA XREF: KiServiceInternal+5Ao
.text:0000000000440FF0
.text:0000000000440FF0 var_70 = qword ptr -70h
.text:0000000000440FF0 arg_10 = qword ptr 18h
.text:0000000000440FF0
.text:0000000000440FF0 mov [rbx+1C8h], rsp
.text:0000000000440FF7 mov edi, eax ; eax – номер системного сервиса, переданный из юзермода
.text:0000000000440FF9 shr edi, 7
.text:0000000000440FFC and edi, 20h ; edi – индекс сервисной таблицы
.text:0000000000440FFF and eax, 0FFFh ; номер сервиса в таблице
.text:0000000000441004
.text:0000000000441004 KiSystemServiceRepeat: ; CODE XREF: KiSystemServiceExit+187j
.text:0000000000441004 lea r10, KeServiceDescriptorTable ; отсюда можно утянуть адреса таблиц шадоу и ntos
.text:000000000044100B lea r11, KeServiceDescriptorTableShadow ; достаточно получить адрес KiSystemCall64
.text:0000000000441012 test dword ptr [rbx+0F4h], 4 ; а дальше дело техники )
Для тех, кто не знает – инструкция swapgs доступна только в 64-битном режиме. Она обменивает текущее значение gs со значением, содержащиеся в регистре MSR_KERNELGSBase (0xC000_0102). Доступна при CPL=0. Инструкция stmxcsr – сохраняет значение регистра mxcsr, ldmxcsr соответственно загружает из памяти. mxcsr – это 32-битный регистр, содержащий ряд флагов, связанных с SSE.
Теперь у нас KTHREAD лежит по адресу gs:188h (см ниже, откуда это очевидно)
.text:00000000004C8D80 ; PKTHREAD KeGetCurrentThread(void)
.text:00000000004C8D80 public KeGetCurrentThread
.text:00000000004C8D80 KeGetCurrentThread proc near
.text:00000000004C8D80 mov rax, gs:188h ; KeGetCurrentThread
.text:00000000004C8D89 retn
.text:00000000004C8D89 KeGetCurrentThread endp
Если необходимо, то тред конвертируется в гуи
.text:0000000000441298 mov [rbp-80h], eax
.text:000000000044129B mov [rbp-78h], rcx
.text:000000000044129F mov [rbp-70h], rdx
.text:00000000004412A3 mov [rbp-68h], r8
.text:00000000004412A7 mov [rbp-60h], r9
.text:00000000004412AB call KiConvertToGuiThread
.text:00000000004412B0 or eax, eax
.text:00000000004412B2 mov eax, [rbp-80h]
.text:00000000004412B5 mov rcx, [rbp-78h]
.text:00000000004412B9 mov rdx, [rbp-70h]
.text:00000000004412BD mov r8, [rbp-68h]
.text:00000000004412C1 mov r9, [rbp-60h]
.text:00000000004412C5 mov [rbx+1C8h], rsp
.text:00000000004412CC jz KiSystemServiceRepeat
KiConvertToGuiThread вызывает PsConvertToGuiThread, та в свою очередь PspW32Thread[Process]Callout [о работе callouts я уже здесь писала].
В ntoskrnl вызов сервисов выглядит так
.text:000000000043D850 ZwPowerInformation proc near ; CODE XREF: PoInitHiberServices+2Bp
.text:000000000043D850 ; NtPowerInformation+6E0p …
.text:000000000043D850 mov rax, rsp
.text:000000000043D853 cli
.text:000000000043D854 sub rsp, 10h
.text:000000000043D858 push rax
.text:000000000043D859 pushf
.text:000000000043D85A push 10h
.text:000000000043D85C lea rax, KiServiceLinkage
.text:000000000043D863 push rax
.text:000000000043D864 mov eax, 5Ch
.text:000000000043D869 jmp KiServiceInternal
.text:000000000043D869 ZwPowerInformation endp
Т.е. по ret из сервиса мы отправимся по адресу KiServiceLinkage, по которому кроме ret ничего не лежит.
Далее смотрим KiServiceInternal, она просто устанавливает PreviousMode и передает управление в KiSystemCall64
text:0000000000440EC0 KiServiceInternal proc near ; CODE XREF: ZwMapUserPhysicalPagesScatter+19j
.text:0000000000440EC0 ; ZwWaitForSingleObject+19j …
.text:0000000000440EC0
.text:0000000000440EC0 var_E8 = qword ptr -0E8h
.text:0000000000440EC0
.text:0000000000440EC0 sub rsp, 8
.text:0000000000440EC4 push rbp
.text:0000000000440EC5 sub rsp, 158h
.text:0000000000440ECC lea rbp, [rsp+168h+var_E8]
.text:0000000000440ED4 mov [rbp+0C0h], rbx
.text:0000000000440EDB mov [rbp+0C8h], rdi
.text:0000000000440EE2 mov [rbp+0D0h], rsi
.text:0000000000440EE9 sti
.text:0000000000440EEA mov rbx, gs:188h ; KTHREAD
.text:0000000000440EF3 prefetchw byte ptr [rbx+1C8h]
.text:0000000000440EFA movzx edi, byte ptr [rbx+153h]
.text:0000000000440F01 mov [rbp-58h], dil ; dil – младший байт регистра edi
.text:0000000000440F05 mov byte ptr [rbx+153h], 0 ;PreviousMode
.text:0000000000440F0C mov r10, [rbx+1C8h] ;*TrapFrame
.text:0000000000440F13 mov [rbp+0B8h], r10
.text:0000000000440F1A lea r11, KiSystemServiceStart ; прыгаем в середину KiSystemCall64
.text:0000000000440F21 jmp r11
.text:0000000000440F21 KiServiceInternal endp
В зависимости от того, откуда пришел вызов, из юзермода или сервис был вызван из ядра с помощью KiServiceInternal выполняем выход
.text:0000000000441219 goto_usermode: ; CODE XREF: KiSystemServiceExit+C7j
.text:0000000000441219 mov r8, [rbp+100h]
.text:0000000000441220 mov r9, [rbp+0D8h]
.text:0000000000441227 xor edx, edx
.text:0000000000441229 xor r10, r10
.text:000000000044122C pxor xmm0, xmm0
.text:0000000000441230 pxor xmm1, xmm1
.text:0000000000441234 pxor xmm2, xmm2
.text:0000000000441238 pxor xmm3, xmm3
.text:000000000044123C pxor xmm4, xmm4
.text:0000000000441240 pxor xmm5, xmm5
.text:0000000000441244 mov rcx, [rbp+0E8h]
.text:000000000044124B mov r11, [rbp+0F8h]
.text:0000000000441252 mov rbp, r9
.text:0000000000441255 mov rsp, r8
.text:0000000000441258 swapgs ; меняем gs
.text:000000000044125B sysret
.text:000000000044125E
.text:000000000044125E if_fromkernelmode: ; CODE XREF: KiSystemServiceExit+25j
.text:000000000044125E mov rcx, gs:188h
.text:0000000000441267 mov rdx, [rbp+0B8h]
.text:000000000044126E mov [rcx+1C8h], rdx ;*TrapFrame
.text:0000000000441275 mov dl, [rbp-58h]
.text:0000000000441278 mov [rcx+153h], dl ; PreviousMode
.text:000000000044127E cli
.text:000000000044127F mov rsp, rbp
.text:0000000000441282 mov rbp, [rbp+0D8h]
.text:0000000000441289 mov rsp, [rsp+arg_F8]
.text:0000000000441291 sti
.text:0000000000441292 retn ;->KiServiceLinkage
В заключение хочу сказать пару слов об KiSystemCall32.
.text:0000000000440CC0 KiSystemCall32: ; DATA XREF: SepAdtInitializePrivilegeAuditing-1ED31o
.text:0000000000440CC0 swapgs
.text:0000000000440CC3 mov gs:1A0h, rsp
.text:0000000000440CCC mov rsp, gs:1A8h
.text:0000000000440CD5 push 2Bh
.text:0000000000440CD7 push qword ptr gs:1A0h
.text:0000000000440CDF push r11
.text:0000000000440CE1 push 23h
.text:0000000000440CE3 push rcx
.text:0000000000440CE4 swapgs
.text:0000000000440CE7 sub rsp, 8
.text:0000000000440CEB push rbp
.text:0000000000440CEC sub rsp, 158h
.text:0000000000440CF3 lea rbp, [rsp+2F8h+var_278]
.text:0000000000440CFB mov byte ptr [rbp-55h], 1
.text:0000000000440CFF mov [rbp-50h], rax
.text:0000000000440D03 mov [rbp-48h], rcx
.text:0000000000440D07 mov [rbp-40h], rdx
.text:0000000000440D0B mov [rbp-38h], r8
.text:0000000000440D0F mov [rbp-30h], r9
.text:0000000000440D13 mov [rbp-28h], r10
.text:0000000000440D17 mov [rbp-20h], r11
.text:0000000000440D1B test byte ptr [rbp+0F0h], 1
.text:0000000000440D22 jz short loc_440D45
.text:0000000000440D24 swapgs
.text:0000000000440D27 mov r10, gs:188h
.text:0000000000440D30 test byte ptr [r10+3], 1
.text:0000000000440D35 mov word ptr [rbp+80h], 0
.text:0000000000440D3E jz short loc_440D45
.text:0000000000440D40 call KiSaveDebugRegisterState
.text:0000000000440D45
.text:0000000000440D45 loc_440D45: ; CODE XREF: KiDebugServiceTrap+122j
.text:0000000000440D45 ; KiDebugServiceTrap+13Ej
.text:0000000000440D45 cld
.text:0000000000440D46 stmxcsr dword ptr [rbp-54h]
.text:0000000000440D4A ldmxcsr dword ptr gs:180h
.text:0000000000440D53 movdqa oword ptr [rbp-10h], xmm0
.text:0000000000440D58 movdqa oword ptr [rbp+0], xmm1
.text:0000000000440D5D movdqa oword ptr [rbp+10h], xmm2
.text:0000000000440D62 movdqa oword ptr [rbp+20h], xmm3
.text:0000000000440D67 movdqa oword ptr [rbp+30h], xmm4
.text:0000000000440D6C movdqa oword ptr [rbp+40h], xmm5
.text:0000000000440D71 test qword ptr [rbp+0F8h], 200h ; rflags
.text:0000000000440D7C jz short loc_440D7F
.text:0000000000440D7E sti
.text:0000000000440D7F
.text:0000000000440D7F loc_440D7F: ; CODE XREF: KiDebugServiceTrap+17Cj
.text:0000000000440D7F mov ecx, 0C000001Dh
.text:0000000000440D84 xor edx, edx
.text:0000000000440D86 mov r8, [rbp+0E8h]
.text:0000000000440D8D call KiExceptionDispatch
Реально, как мы видим этот обработчик только вызывает STATUS_ILLEGAL_INSTRUCTION. Если попытаться заюзать syscall из 32-битной программы она грохнется с этим исключением.
Следующая запись в блоге – KTHREAD 64-бит.