x64에서 커널진입이후, 동작을 알아보기 위해 ntdll!ZwCreateFile의 경우를 알아보자.
ntdll!ZwCreateFile내부를 보자.
ntdll!ZwCreateFile: 0033:00000000`77b91860 4c8bd1 mov r10,rcx ; 첫번째 인수를 r10에 백업시킨다. 이는 syscall이후 반환주소가 rcx로 전달되기 때문 0033:00000000`77b91863 b852000000 mov eax,52h ; ServiceNumber를 eax로 전달 0033:00000000`77b91868 0f05 syscall ; 커널진입 0033:00000000`77b9186a c3 ret |
Syscall명령어를 통해 커널에 진입하게 되는데 eax를 통해 ServiceNumber를 전달하는것은 동일하다. 다른 점이 있다면 r10에 첫번째 인수인 rcx를 저장하는데 이는 커널진입 이후에 실행될 복귀주소인 77b9186a 이 전달되기 때문이다. 또한 x86때는 sysenter를 통하여 커널에 진입한 반면 x64는 syscall명령어를 쓰는데 이를 알아보기 위하여 intel문서를 참조하였다.
위 설명이 부족하여 자세한 설명을 보면 이렇다.
SYSCALL invokes an OS system-call handler at privilege level 0. It does so by loading RIP from the IA32_LSTAR MSR(0xc0000082) by Intel Doc |
말인 즉슨 커널 진입시 가장 먼저 실행되는 코드의 주소인 rip를 IA32_LSTAR MSR(0xc0000082)로부터 가져온다는 말이다. (x86에서는 0x176읽었었었드렜는데… ) 그래서 해당 주소를 확인해보았다.
해당 주소는 nt!KiSystemCall64였다. 해서 nt!KiSystemCall에 bp를 걸고 진행해보았다. 근데…..계속 멈춘다….. 검색해보니 욕시 앞서 삽질하실분들이 있군...그렇다면 시키는대로 그래서 첫 명령어인 swapgs의 몇줄밑에 bp를 걸고 진행해보니 잘되는 것을 알 수 있었다. 이제 차분히 nt!KiSystemCall64를 분석해본다.
Nt!KiSystemCall64:
fffff800`02e8c640 0f01f8 swapgs ; GS 레지스터의 값을 MSR address 0xC0000102H주소의 값으로 바꾼다.
fffff800`02e8c643 654889242510000000 mov qword ptr gs:[10h],rsp ; KPCR.UserRsp = gs:[0x10]에 User모드 rsp백업
fffff800`02e8c64c 65488b2425a8010000 mov rsp,qword ptr gs:[1A8h] ; KPCR.KPRCB.RspBase = gs:[1A8h]를 rsp로 저장
fffff800`02e8c655 6a2b push 2Bh
fffff800`02e8c657 65ff342510000000 push qword ptr gs:[10h] ; PUSH KPCR.UserRsp
fffff800`02e8c65f 4153 push r11
fffff800`02e8c661 6a33 push 33h
fffff800`02e8c663 51 push rcx
fffff800`02e8c664 498bca mov rcx,r10
fffff800`02e8c667 4883ec08 sub rsp,8
fffff800`02e8c66b 55 push rbp
fffff800`02e8c66c 4881ec58010000 sub rsp,158h
fffff800`02e8c673 488dac2480000000 lea rbp,[rsp+80h]
fffff800`02e8c67b 48899dc0000000 mov qword ptr [rbp+0C0h],rbx
fffff800`02e8c682 4889bdc8000000 mov qword ptr [rbp+0C8h],rdi
fffff800`02e8c689 4889b5d0000000 mov qword ptr [rbp+0D0h],rsi
fffff800`02e8c690 c645ab02 mov byte ptr [rbp-55h],2
fffff800`02e8c694 65488b1c2588010000 mov rbx,qword ptr gs:[188h] ; nt!KPCR._KPCB.CurrentThread
fffff800`02e8c69d 0f0d8bd8010000 prefetchw [rbx+1D8h] ; Store nt!_KTHREAD._KTRAP_FRAME in the Cache
fffff800`02e8c6a4 0fae5dac stmxcsr dword ptr [rbp-54h] ; Store mxcsr
fffff800`02e8c6a8 650fae142580010000 ldmxcsr dword ptr gs:[180h] ; load nt!_KPCB.MxCsr into MxCsr
fffff800`02e8c6b1 807b0300 cmp byte ptr [rbx+3],0 ; nt!KPCR._KPCB.CurrentThread.
fffff800`02e8c6b5 66c785800000000000 mov word ptr [rbp+80h],0 ; nt!_ETHREAD._KTHREAD._DISPATCHER_HEADER.DebugActive
fffff800`02e8c6be 0f848c000000 je nt!KiSystemCall64+0x110 (fffff800`02e8c750)
========================================================================================
fffff800`02e8c750 fb sti
fffff800`02e8c751 48898be0010000 mov qword ptr [rbx+1E0h],rcx ; store parameter1 in the nt!KTHREAD.FirstArgument
fffff800`02e8c758 8983f8010000 mov dword ptr [rbx+1F8h],eax ; store Service Number in the nt!KTHREAD.systemcallNumber nt!KiSystemServiceStart:
fffff800`02e8c75e 4889a3d8010000 mov qword ptr [rbx+1D8h],rsp ; nt!_KTHREAD._KTRAP_FRAME
fffff800`02e8c765 8bf8 mov edi,eax
fffff800`02e8c767 c1ef07 shr edi,7
fffff800`02e8c76a 83e720 and edi,20h
fffff800`02e8c76d 25ff0f0000 and eax,0FFFh ; Service Table Index
nt!KiSystemServiceRepeat:
fffff800`02e8c772 4c8d15c7202300 lea r10,[nt!KeServiceDescriptorTable (fffff800`030be840)]
fffff800`02e8c779 4c8d1d00212300 lea r11,[nt!KeServiceDescriptorTableShadow (fffff800`030be880)]
fffff800`02e8c780 f7830001000080000000 test dword ptr [rbx+100h],80h ; Check GUI Thread
fffff800`02e8c78a 4d0f45d3 cmovne r10,r11 ; Check Service Table(cmpare SSDT or ShadowSSDT)
fffff800`02e8c78e 423b441710 cmp eax,dword ptr [rdi+r10+10h] ; ServiceNumber > ServiceTable Limit?
fffff800`02e8c793 0f83e9020000 jae nt!KiSystemServiceExit+0x1a7 (fffff800`02e8ca82)
fffff800`02e8c799 4e8b1417 mov r10,qword ptr [rdi+r10] ; Get nt!KiServiceTable
fffff800`02e8c79d 4d631c82 movsxd r11,dword ptr [r10+rax*4] ; Get Service Offset X86이랑 다르게 DWORD로 저장되어 있음
fffff800`02e8c7a1 498bc3 mov rax,r11
fffff800`02e8c7a4 49c1fb04 sar r11,4 ; Service Offset >>4
fffff800`02e8c7a8 4d03d3 add r10,r11 ; Get Service VA
fffff800`02e8c7ab 83ff20 cmp edi,20h
fffff800`02e8c7ae 7550 jne nt!KiSystemServiceGdiTebAccess+0x49 (fffff800`02e8c800)
fffff800`02e8c7b0 4c8b9bb8000000 mov r11,qword ptr [rbx+0B8h]
nt!KiSystemServiceGdiTebAccess:
==============================================================================================
fffff800`02e8c800 83e00f and eax,0Fh
fffff800`02e8c803 0f84b7000000 je nt!KiSystemServiceCopyEnd (fffff800`02e8c8c0)
fffff800`02e8c809 c1e003 shl eax,3
fffff800`02e8c80c 488d642490 lea rsp,[rsp-70h]
fffff800`02e8c811 488d7c2418 lea rdi,[rsp+18h]
fffff800`02e8c816 488bb500010000 mov rsi,qword ptr [rbp+100h]
fffff800`02e8c81d 488d7620 lea rsi,[rsi+20h]
fffff800`02e8c821 f685f000000001 test byte ptr [rbp+0F0h],1
fffff800`02e8c828 7416 je nt!KiSystemServiceGdiTebAccess+0x89 (fffff800`02e8c840)
fffff800`02e8c82a 483b35cf172300 cmp rsi,qword ptr [nt!MmUserProbeAddress (fffff800`030be000)]
fffff800`02e8c831 480f4335c7172300 cmovae rsi,qword ptr [nt!MmUserProbeAddress (fffff800`030be000)]
fffff800`02e8c839 0f1f8000000000 nop dword ptr [rax]
fffff800`02e8c840 4c8d1d79000000 lea r11,[nt!KiSystemServiceCopyEnd (fffff800`02e8c8c0)]
fffff800`02e8c847 4c2bd8 sub r11,rax
fffff800`02e8c84a 41ffe3 jmp r11 ; Call Service |
x86과 다른부분은 x86의 경우 KiServiceTable에 VA값을 가지고 있다 하지만~~x64의 경우, DWORD 값을 가지고 있고 별도의 계산과정을 통해 함수 주소를 가져온다. 계산의 원활함을 위해 테이블이 가지고 값을 Serviceoffset이라고 겠다.
간단히 Service주소를 가져오는 것을 보겠다.
Service주소 계산 : KiServiceTable + ( Serviceoffset>>4 )
위 산식을 통해 서비스주소를 가져오고 ffff800`02e8c84a
41ffe3에서 서비스를 호출한다.
'Security > Windows System' 카테고리의 다른 글
NT / Zw Native API 차이 (0) | 2019.04.05 |
---|---|
I/O Control Code를 정의하는 CTL_CODE (0) | 2018.07.31 |
x64 Calling Convention(Windows) (0) | 2018.04.14 |
64 Windows커널 진입 차이 : 유저모드편 (0) | 2018.04.12 |
kernel32 vs kernelbase (0) | 2018.04.05 |
댓글