유저모드에서 커널 모드로 접근하는 메커니즘에 대해 알아 보겠다.
커널 모드로 가는 방법은 크게 두가지 있다.
두가지로 나누는 가장 큰 이유는 그래픽 관련한 일을 분리하기 위해서이다.
그래픽 관련 작업은 반응이 빨라야 하기 때문에 모든 프로세스가 공통으로 사용하는 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
'사이버보안 > OS(윈도우즈)' 카테고리의 다른 글
[Windows Tool] 윈도우 터미널 (Windows Terminal) (0) | 2024.09.07 |
---|---|
[윈도우 구조 #7] Sysinternals Suite : 윈도우즈 관리 도구 (0) | 2023.09.09 |
[윈도우 구조 #5] 맥에서 윈도우 커널 디버깅 (Windows Kernel Debugging in Mac) (0) | 2022.06.08 |
[윈도우 구조 #4] WinDbg 윈도우 디버거 (0) | 2022.06.06 |
[윈도우 구조 #3] 윈도우 전체 구조 이해 (0) | 2022.06.05 |