본문 바로가기
사이버보안/OS(윈도우즈)

[윈도우 구조 #6] 유저모드에서 커널모드로 접근

by 홍주은 2022. 6. 11.
반응형
 

 

유저모드에서 커널 모드로 접근하는 메커니즘에 대해 알아 보겠다.

커널 모드로 가는 방법은 크게 두가지 있다.

두가지로 나누는 가장 큰 이유는 그래픽 관련한 일을 분리하기 위해서이다.

그래픽 관련 작업은 반응이 빨라야 하기 때문에 모든 프로세스가 공통으로 사용하는 ntdll.dll 을 사용하지 않고 별도의 함수를 만들었다.

시스템에 일이 많으면 그래픽 일을 처리하지 못해 화면이 멈출수 있기 때문에 이런 구조를 택한 것이다.

 

이제부터는 필요할 때마다 WinDbg와 병행하며 설명을 하겠다.

기본적으로 알아야 할 것은 커널 디버깅이기 때문에 ctrl+break를 하면 항상 커널모드로 접근한다.

유저모드로 접근하기 위해서는 다음과 같이 하면 된다.

> !process 0 0  ; 접근하기 원하는 프로세스의 정보를 획득
PROCESS fffffa8005b6b060
    SessionId: 1  Cid: 068c    Peb: 7fffffd3000  ParentCid: 047c
    DirBase: 7d31a000  ObjectTable: fffff8a0025b95e0  HandleCount: 277.
    Image: notepad.exe

> .process /i fffffa8005b6b060  ; 원하는 프로세스의 EPROCESS 로 설정

> g ; 게스트에 권한을 주면 바로 설정한 프로세스로 전환됨

> .reload /f ; 강제로 유저 라이브러리를 로드 시킴. 이후 분석을 하면 됨
 

1. ntdll.dll 을 통하는 방법

일반적으로 알려진 흐름이다.

ntdll.dll 내에 구현된 Native API를 통해 ntoskrnl.exe 내에 동일한 함수명으로 구현된 Native API를 호출한다.

 

ntdll.dll 내의 함수는 단지 정의된 Native API 번호와 파라미터 값을 유저 모드 메모리에서 커널 모드 메모리로 전달한다. 유저와 커널에서 접근할 수 있는 메모리 영역이 다르기 때문에 각각의 영역에 복사가 필요하다.

 

이때, 사용하는 벙법이 software interrupt 이다. 예전에는 int 0x2e 였는데 syscall 로 바뀌었다.

쉽게 말하면 윈도우가 설정한 인터럽트를 이용하여 커널로 흐름을 변화시킨다.

 

KiSystemService 함수는 유저 모드에서 전달된 Native API와 파라미터 값을 이용하여 ntdll.dll 과 동일한 함수를 커널에서 호출한다.

 

(1) ntdll.dll 의 Native API : 함수번호와 파라미터 값을 저장하고 syscall 을 호출한다.

ntdll!ZwCreateFile:
00000000`76ef1860 4c8bd1          mov     r10,rcx
00000000`76ef1863 b852000000      mov     eax,52h
00000000`76ef1868 0f05            syscall
00000000`76ef186a c3              ret
00000000`76ef186b 0f1f440000      nop     dword ptr [rax+rax]
 

 

(2) ntoskrnl.exe 의 Native API : 파라미터 값을 복사한 후 일을 수행한다.

nt!NtCreateFile:
fffff800`02fee400 4c8bdc          mov     r11,rsp
fffff800`02fee403 4881ec88000000  sub     rsp,88h
fffff800`02fee40a 33c0            xor     eax,eax
fffff800`02fee40c 498943f0        mov     qword ptr [r11-10h],rax
fffff800`02fee410 c744247020000000 mov     dword ptr [rsp+70h],20h
fffff800`02fee418 89442468        mov     dword ptr [rsp+68h],eax
fffff800`02fee41c 498943d8        mov     qword ptr [r11-28h],rax
fffff800`02fee420 89442458        mov     dword ptr [rsp+58h],eax
fffff800`02fee424 8b8424e0000000  mov     eax,dword ptr [rsp+0E0h]
fffff800`02fee42b 89442450        mov     dword ptr [rsp+50h],eax
fffff800`02fee42f 488b8424d8000000 mov     rax,qword ptr [rsp+0D8h]
fffff800`02fee437 498943c0        mov     qword ptr [r11-40h],rax
fffff800`02fee43b 8b8424d0000000  mov     eax,dword ptr [rsp+0D0h]
fffff800`02fee442 89442440        mov     dword ptr [rsp+40h],eax
...
 

 

2. gdi32.dll, user32.dll 을 통하는 방법

gdi32.dll 은 그래픽 관련 일을, user32.dll 은 윈도우의 여러 창과 관련된 일을 수행한다.

그래서 이들에게 가장 중요한 것은 윈도우 사용자가 보는데 불편함을 느끼면 안된다.

즉 마우스로 조작하는 일에 대한 빠른 응답을 사용자에게 주어야 한다.

 

이런 이유로 윈도우는 시스템 관련 일은 ntdll.dll 을 통해사용자 인터페이스(UI) 관련해서는 gdi32.dll 과 user32.dll 을 통해 일을 수행한다.

 

유저모드의 gdi32.dll 과 user32.dll의 Native API 는 커널모드의 win32k.sys의 Native API를 호출한다.

 

(1) gdi32.dll 의 Native API : 함수번호와 파라미터 값을 저장하고 syscall 을 호출한다.

GDI32!NtUserMessageCall:
000007fe`fe9d5900 4c8bd1          mov     r10,rcx
000007fe`fe9d5903 b807100000      mov     eax,1007h
000007fe`fe9d5908 0f05            syscall
000007fe`fe9d590a c3              ret
000007fe`fe9d590b 90              nop
000007fe`fe9d590c 90              nop
000007fe`fe9d590d 90              nop
000007fe`fe9d590e 90              nop
000007fe`fe9d590f 90              nop
 

(2) win32k.sys 의 Native API : 파라미터 값을 복사한 후 일을 수행한다.

win32k!NtUserMessageCall:
fffff960`0015ffb0 48895c2408      mov     qword ptr [rsp+8],rbx
fffff960`0015ffb5 48896c2410      mov     qword ptr [rsp+10h],rbp
fffff960`0015ffba 4889742418      mov     qword ptr [rsp+18h],rsi
fffff960`0015ffbf 57              push    rdi
fffff960`0015ffc0 4154            push    r12
fffff960`0015ffc2 4155            push    r13
fffff960`0015ffc4 4883ec60        sub     rsp,60h
fffff960`0015ffc8 488bd9          mov     rbx,rcx
 

 

반응형