포스트

[시스템 아키텍처] 핵심 시스템 컴포넌트 - 1

[시스템 아키텍처] 핵심 시스템 컴포넌트 - 1
  • 핵심 윈도우 시스템 아키텍처와 컴포넌트에 대한 좀 더 상세하고 완전한 다이어그램

환경 서브시스템과 서브시스템 DLL

  • 환경 서브시스템의 역할은 기본 윈도우 익스큐티브 시스템 서비스의 일부분을 애플리케이션에 공개하는 것이다.
    • 각 서브시스템은 윈도우의 네이티브 서비스에 대한 다른 부분집합을 제공할 수 있다.
  • 각 실행 이미지(.exe)는 하나의 서브시스템별로 하나씩 대응한다.
    • 이미지가 실행될 때 프로세스 생성 코드는 이미지 헤더에서 서브시스템 유형 코드를 조사함으로써 새로운 프로세스에 적절한 서브시스템을 통지할 수 있다.
    • 이 유형 코드는 마이크로소프트 비주얼 스튜디오 링커의 /SUBSYSTEM 링커 옵션에 의해 지정된다.
  • 유저 애플리케이션은 윈도우 시스템 서비스를 직접 호출하지 않는다. 대신 하나 이상의 서브시스템 DLL을 통해 호출한다.
    • 이들 라이브러리는 서브시스템에 연결된 프로그램이 호출할 수 있는 문서화된 인터페이스를 노출한다.
    • 윈도우 서브시스템 DLLs(kernel32.dll, advapi32.dll, user32.dll, gdi32.dll 등)은 윈도우 API 함수를 구현한다.
    • SUA 서브시스템 DLLs(Psxdll.dll 등)은 POSIX를 지원하는 윈도우 버전에서 SUA API 함수를 구현한다.
Dependency Walker 활용해보기

Dependancy Walker 툴을 사용하면 이미지의 서브시스템 유형을 볼 수 있다.
GUI와 문자 기반 프로그램이 각기 서로 다른 두 개의 서브시스템이 존재하는 것처럼 보이지만, 사실은 하나만 있다.
GUI 프로그램은 AllocConsole을 호출하여 콘솔을 가질 수 있으며, 콘솔 프로그램은 GUI를 나타낼 수 있다.

  • notepad.exe (Console)
  • cmd.exe (GUI)

애플리케이션이 서브시스템 DLL 함수를 호출할 때

  • 함수가 전체적으로 서브시스템 DLL 내의 유저 모드에서 구현된 경우
    • 환경 서브시스템 프로세스로 아무 메시지도 전달되지 않으며, 윈도우 익스큐티브 시스템 서비스도 호출되지 않는다.
    • 함수는 유저 모드에서 수행되고 결과는 호출자에게 반환된다. 이런 함수의 예로 GetCurrentProcessGetCurrentProcessId가 있다.
  • 함수가 하나 이상의 윈도우 익스큐티브 호출을 요구하는 경우
    • ReadFileWriteFile은 각각 하부에 존재하는 내부 윈도우 I/O 시스템 서비스인 NtReadFileNtWriteFile을 호출하는 것과 연관되어 있다.
  • 함수가 환경 서브시스템 프로세스에서 수행되어야 하는 일을 요구하는 경우
    • 클라이언트/서버 요청은 어떤 동작을 수행하기 위해 서브시스템으로 보내지는 ALPC 메시지를 통해 이뤄진다.
    • 이후 서브시스템 DLL은 호출자로 복귀하기 전에 응답을 기다린다.
  • CreateProcessExitWindowsEx와 같은 함수는 위에 나열된 두 번째와 세 번째 항목의 조합으로 구성된다.

서브시스템 시작

  • 서브시스템은 세션 관리자(smss.exe) 프로세스에 의해 시작된다.
    • 서브시스템 시작 정보는 HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Subsystems 레지스트리 키 하위에 저장된다.
    • Required 값은 시스템이 부트될 때 로드되는 서브시스템을 나열하며, Windows와 Debug이다.
      • Windows 값은 윈도우 서브시스템인 csrss.exe 파일에 대한 명세를 갖는다.
      • Debug는 비어 있다. (윈도우 XP 이후로 필요 없는 필드이나 호환성을 위해 유지)
    • Optional 값은 옵션 서브시스템을 나타낸다. 하지만 SUA가 윈도우 10부터 이용 불가하므로 비어 있다.
    • 윈도우 XP였다면 Posix 항목이 Psxss.exe를 가리키는 값을 가지고 있을 것이다.
    • Kmode는 윈도우 서브시스템의 커널 모드 파일명 win32k.sys를 가진다.

윈도우 서브시스템

  • 윈도우 설계자는 기본 함수를 윈도우에 두고 디스플레이 I/O를 수행하기 위해 다른 서브시스템이 윈도우 서브시스템을 호출하도록 하였다. (코드 중복 방지)
    • 이런 설계의 결과로, 윈도우 서브시스템은 대화식 사용자가 로그인돼있지 않은 서버 시스템에서조차 윈도우 시스템의 필수 구성 요소이다.
    • 윈도우 서브시스템 프로세스는 임계 프로세스(Critical Process)로 구분된다.
윈도우 서브시스템의 주요 컴포넌트
  • 각 세션에 대한 환경 서브시스템 프로세스(csrss.exe)
    • 아래의 기능을 지원하기 위해 네 개의 DLL(basesrv.dll, winsrv.dll, sxssrv.dll, csrsrv.dll)을 로드한다.
    • 프로세스와 스레드의 생성/삭제와 관련된 다양한 관리 작업
    • ExitWindowsEx API를 통해 윈도우 애플리케이션 종료시키기
    • 하위 호환성 매핑을 위해 레지스트리 위치에 .ini 파일 포함하기
    • 특정 커널 통지 메시지(Plug&Play 관리자 등)를 윈도우 애플리케이션에 윈도우 메시지 형태(WM_DEVICECHANGE)로 보내기
    • 16비트 가상 DOS 머신 프로세스를 위한 지원의 일부(32비트 윈도우)
    • SxS(사이드-바이-사이드)/퓨전(Fusion)과 매니페스트 캐시 지원
    • 여러 자연 언어 지원 함수
  • 커널 모드 디바이스 드라이버(win32k.sys)
    • 윈도우 관리자: 윈도우 디스플레이 제어/화면 출력 관리/입력 장치로부터 입력ㄹ 수집/애플리케이션으로 유저 메시지 전달
    • 그래픽 디바이스 인터페이스(GDI): 그래픽 출력 장치를 위한 함수 라이브러리. 그림 그리기와 그래픽 조작을 위한 함수를 포함
    • 다른 커널 드라이버(dxgkrnl.sys)에 구현된 DirectX 지원 래퍼
  • 콘솔 호스트 프로세스(conhost.exe)
    • 콘솔 애플리케이션 지원 제공
  • 데스크톱 윈도우 관리자(dwm.exe)
    • CDD와 DirectX를 통해 가시 윈도우 렌더링을 단일 서피스로 구성
  • 서브시스템 DLL(kernel32.dll, advapi32.dll, user32.dll, gdi32.dll)
    • 문서화된 윈도우 API 함수를 적절하게 ntoskrnl.exewin32k.sys 내의 커널 모드 시스템 서비스 호출로 변환
  • 하드웨어 의존적인 그래픽 디스플레이 드라이버와 프린터 드라이버, 비디오 미니포트 드라이버인 그래픽 디바이스 드라이버

Windows 10과 win32k.sys

  • win32k.sys의 기능은 특정 시스템에 모든 모듈이 필요하지 않도록 여러 커널 모듈로 분리되어 있다.
    • 휴대폰의 win32k.sysWin32kMin.sysWin32kBase.sys를 로드한다.
    • 데스크톱 시스템의 win32k.sysWin32kBase.sysWin32kFull.sys를 로드한다.
    • IoT 시스템의 win32k.sysWin32kBase.sys만을 필요로 한다.
  • 애플리케이션은 윈도우와 버튼 같은 유저 인터페이스 컨트롤을 화면에 생성하기 위해 표준 USER 함수를 호출한다.
    • 윈도우 관리자는 이들 요청을 GDI에 전달한다.
    • GDI는 요청을 그래픽 디바이스 드라이버로 넘기고, 드라이버에서 디스플레이 디바이스에 맞게 구성된다.
    • 디바이스 드라이버는 비디오 디스플레이 지원을 완성하기 위해 비디오 미니포트 드라이버와 짝을 이룬다.
  • GDI는 애플리케이션이 디바이스에 대해 아무것도 몰라도 그래픽 디바이스와 통신할 수 있는 표준 2차원 함수들을 제공한다.
    • GDI 함수는 애플리케이션과 그래픽 디바이스 사이에서 정보를 전달한다.
    • GDI는 그래픽 출력을 위한 애플리케이션 요청을 해석하고 그래픽 디스플레이 드라이버로 요청을 전송한다. 또한 다양한 그래픽 출력 디바이스를 사용하기 위한 표준 인터페이스를 애플리케이션에 제공한다. 이 인터페이스는 애플리케이션의 코드가 하드웨어 디바이스와 그 드라이버로부터 독립적이게 한다.
    • GDI는 해당 메시지를 디바이스의 성능에 맞게 재단하며, 종종 요청을 다루기 쉬운 부분으로 나눈다.
  • 서브시스템의 많은 부분(특히 디스플레이 I/O)은 커널 모드에서 실행하므로, 소수의 윈도우 함수만이 윈도우 서브시스템 프로세스로 메시지를 보낸다.
    • 프로세스와 스레드의 생성과 종료, DOS 장치 드라이브 문자 매핑 관련 함수가 이에 해당한다.
    • 필요에 따라 새로운 마우스 커서 위치를 그리거나 키보드 입력을 처리하고 CDD를 통해 스크린을 렌더링하는 것을 제외하면 실행 중인 애플리케이션은 윈도우 서브시스템 프로세스로의 컨텍스트 전환이 많이 수반되지 않는다.
콘솔 윈도우 호스트

원래의 윈도우 서브시스템 설계에서 csrss.exe는 콘솔 윈도우를 관리하고, 각 콘솔 애플리케이션이 csrss와 통신하는 것을 책임졌다. Windows 7부터 각 시스템의 콘솔 윈도우 용도로 별개의 프로세스인 콘솔 윈도우 프로세스(conhost.exe)가 사용됐다.
Windows 8과 그 후속버전부터 conhost.exe 프로세스는 남았으나 이 프로세스는 콘솔 드라이버(condrv.sys)에 의한 콘솔 기반 프로세스로부터 시작된다.(Windows 7처럼 csrss.exe로부터 시작하지 않음) 프로세스는 읽기와 쓰기, I/O 제어 및 기타 I/O 요청 유형을 보냄으로써 콘솔 드라이버를 사용하는 conhost.exe와 통신을 한다. conhost.exe가 서버가 되고, 콘솔을 사용하는 프로세스가 클라이언트가 되는 것이다. 이런 변경 덕에 csrss.exe가 키보드 입력을 받아 win32k.sys를 통해 conhost.exe로 보낸 다음 다시 ALPC를 이용해 cmd.exe로 보낼 필요가 없어졌다. 대신 커맨드라인 애플리케이션은 읽기/쓰기 I/O를 통해 콘솔 드라이버로부터 입력을 직접 받을 수 있게 됐다.
Process Explorer에서 conhost.exe가 오픈한 핸들 목록에 \Device\ConDrv로 명명된 condrv.sys가 있음을 확인할 수 있다.
conhost.exe는 콘솔 애플리케이션의 자식 프로세스이다. conhost 생성은 콘솔 서브시스템 이미지를 로더하는 이미지 로더에 의해 시작되거나 GUI 서브시스템 이미지가 AllocConsole 윈도우 API를 호출한다면 요청에 의해 시작된다. conhost.exe의 실제 일꾼은 콘솔 드라이버와 통신하는 다수의 코드를 포함하고 있는 DLL(conhostv2.dll)이다.

기타 서브시스템

  • 윈도우는 원래 POSIX와 OS/2 서브시스템을 지원했으나 더 이상 지원하지 않는다. 하지만 새로운 서브시스템에 대한 시스템 확장이 필요한 경우 이를 가능하게 해줄 서브시스템의 일반적인 개념은 남아 있다.

피코 공급자와 리눅스 용도의 윈도우 서브시스템

  • 전통적 서브시스템 모델은 넌윈도우 바이너리의 광범위한 사용을 어렵게 하는 기술적 단점이 있었다.
    • 서브시스템 정보는 PE 포맷 헤더로부터 추출되므로 넌윈도우 바이너리는 PE 실행 파일로 재구축하기 위해 바이너리에 대한 소스코드를 필요로 한다. 여기서 POSIX 유형의 의존성에 변경을 초래할 수 있고, psxdll.dll 라이브러리에 대한 윈도우 유형의 임포트 함수로의 시스템 호출에 대해서도 변경을 초래할 수 있다.
    • win32 서브시스템이나 NT 커널에 의해 제공되는 기능에 의해 제약이 될 수 있다. 따라서 서브시스템은 POSIX 애플리케이션이 필요로 하는 동작을 에뮬레이션하는 대신 감싼다.
  • 이런 문제를 해결하려면 다른 환경 시스템 호출과 전통적인 PE 이미지 실행에 대한 전통적인 유저 모드 래핑이 필요없는 서브시스템을 구축하기 위한 다른 접근법이 필요했고, 여기서 피코(Pico) 모델의 구현이 이뤄졌다.
    • 이 모델에서는 PsRegisterPicoProvider API를 통해 특수화된 커널 인터페이스에 대한 접근을 받는 커스텀 커널 모드 드라이버인 피코 공급자라는 개념을 도입했다.
    • 이들 인터페이스는 공급자로 하여금 자신들의 실행 컨텍스트와 세그먼트를 원하는 대로 변경하면서 피코 프로세스와 스레드를 생성할 수 있게 해주고, 이들 프로세스와 스레드의 EPROCESSETHREADS 구조체 내에 데이터를 저장할 수 있게 해준다.
    • 또한, 공급자로 하여금 프로세스나 스레드가 시스템 호출과 예외, APC, 페이지 폴트, 종료, 컨텍스트 변경, 일시 중지/재개 등과 같은 특정 시스템 동작에 관여될 때마다 다수의 통지를 받을 수 있게 해준다.
    • 윈도우 10 버전 1607에서 이런 피코 공급자가 하나 존재하는데, 바로 lxss.syslxcore.sys이다. 이는 리눅스 용도의 윈도우 서브시스템(WSL) 컴포넌트를 가리키며, 이들 드라이버는 리눅스에 대한 인터페이스를 담당하는 피코 공급자를 구성한다.
      • WSL 피코 공급자 아래서 실행하는 피코 프로세스는 일반 윈도우 프로세스와 매우 다르다. 예를 들어, 일반 프로세스에 항상 로드되는 ntdll.dll이 없나느 점이다.
      • 대신 피코 프로세스 메모리는 Linux/BSD 시스템에서만 볼 수 있는 특수한 이미지인 vDSO 같은 구조체를 포함한다.

ntdll.dll

  • ntdll은 주로 서브시스템 DLL과 네이티브 애플리케이션을 위한 특별한 시스템 지원 라이브러리이다. 두 종류의 함수를 갖는다.
    • 윈도우 익스큐티브 시스템 서비스에 대한 시스템 서비스 디스패치 스텁
    • 서브시스템과 서브시스템 DLL, 여타 네이티브 이미지에 의해 사용되는 내부 지원 함수
  • 첫 번째 그룹의 함수는 유저 모드에서 호출할 수 있는 윈도우 익스큐티브 시스템 서비스 인터페이스이며, NtCreateFile, NtSetEvent 등 이들 함수의 기능은 윈도우 API를 통해 접근할 수 있다.
  • 이들 각 함수에 대해 ntdll.dll은 같은 이름의 엔트리 포인트를 가진다.
    • 함수 코드는 시스템 서비스 디스패처를 호출하기 위해 커널 모드로의 진입을 일으키는 아키텍처 종속적인 명령어를 가지고 있다.
    • 시스템 서비스 디스패처는 일부 인자를 검증한 후 ntoskrnl.exe 내부의 실제 코드를 가진 커널 모드 시스템 서비스를 호출한다.
시스템 서비스 디스패처 코드 살펴보기
  • WinDbg로 notepad.exe를 실행하면 최초 브레이크포인트 지점에 멈춘다. 이 작업은 프로세스 시작의 매우 이른 시점에 이뤄진다.
  • 호출 스택 출력 명령인 k를 입력해보면 이미지 로더를 나타내는 Ldr로 시작하는 일부 함수를 볼 수 있다.
    1
    2
    3
    4
    5
    6
    7
    
    0:000> k
     # Child-SP          RetAddr               Call Site
    00 000000d3`d97ef0f0 00007ff9`ba072e87     ntdll!LdrpDoDebuggerBreak+0x35
    01 000000d3`d97ef130 00007ff9`ba0710f8     ntdll!LdrpInitializeProcess+0x1a93
    02 000000d3`d97ef510 00007ff9`ba09ea23     ntdll!LdrpInitialize+0x16c
    03 000000d3`d97ef590 00007ff9`ba07340e     ntdll!LdrpInitializeInternal+0xd7
    04 000000d3`d97ef5e0 00000000`00000000     ntdll!LdrInitializeThunk+0xe
    
    • notepad의 메인 함수는 아직 실행되지 않았으므로 notepad의 윈도우도 보이지 않는다.
  • ntdll.dll 내의 NtCreateFile에 브레이크포인트를 설정한다. bp ntdll!NtCreateFile
  • g를 입력하여 계속하면 디버거는 즉시 멈추며 다음과 같은 출력을 보인다.
    1
    2
    3
    
    Breakpoint 0 hit
    ntdll!NtCreateFile:
    00007ff9`ba0e0280 4c8bd1          mov     r10,rcx
    
  • u를 입력하거나 Disassembly 창을 보면 다음과 같은 명령어를 볼 수 있다.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    0:000> u
    ntdll!NtCreateFile:
    00007ff9`ba0e0280 4c8bd1           mov     r10,rcx
    00007ff9`ba0e0283 b855000000       mov     eax,55h
    00007ff9`ba0e0288 f604250803fe7f01 test    byte ptr [SharedUserData+0x308 (00000000`7ffe0308)],1
    00007ff9`ba0e0290 7503             jne     ntdll!NtCreateFile+0x15 (00007ff9`ba0e0295)
    00007ff9`ba0e0292 0f05             syscall
    00007ff9`ba0e0294 c3               ret
    00007ff9`ba0e0295 cd2e             int     2Eh
    00007ff9`ba0e0297 c3               ret
    
    • EAX 레지스터는 시스템 서비스 번호(이 경우 16진수 55)로 설정되어 있다. 이 값은 해당 OS에서의 시스템 서비스 번호이다.
    • syscall 명령어는 프로세서를 커널 모드로 전환하게끔 하는 명령어로서 여기서 EAXNtCreateFile 익스큐티브 서비스를 선택하는 데 사용된다.
    • SharedUserData 내의 오프셋 0x308에서 플래그를 검사한다. 이 플래그가 설정되어 있다면 int 2Eh 명령어를 사용해 실행하는 다른 경로로 이루어진다. Credential Guard VBS 기능을 활성화한다면 이 플래그가 설정된다. 하이퍼바이저는 syscall 명령어보다 효율적인 방식으로 int 명령어에 반응하고, 이런 동작이 Credential Guard에 이롭기 때문이다.
  • ntdll은 이미지 로더(Ldr로 시작하는 함수들)와 힙 관리자, 윈도우 서브시스템 프로세스 통신 함수(Csr로 시작하는 함수들)와 같은 많은 지원 함수를 가지고 있으며, 일반적인 런타임 라이브러리 루틴(Rtl로 시작하는 함수들)과 유저 모드 디버깅을 위한 지원(DbgUi로 시작하는 함수들), 윈도우 이벤트 트레이싱(Etw로 시작하는 함수들), 유저 모드 비동기 프로시저(APC) 호출 디스패처, 예외 디스패처를 가지고 있다.
  • 문자열과 표준 라이브러리(memcpy, strcpy, sprintf 등)의 일부로 한정되는 C 런타임의 일부도 가지고 있다.

네이티브 이미지

  • 일부 실행 파일은 어떠한 서브시스템에도 속하지 않는다. 즉, 이들은 윈도우 서브시스템의 kernel32.dll과 같은 서브시스템 DLL과 링크되지 않는다. 대신 서브시스템을 확장하는 최소의 공통 부분인 ntdll.dll과 링크만 될 뿐이다.
  • ntdll.dll에 의해 노출된 네이티브 API는 거의 문서화가 되어 있지 않기 때문에 이런 종류의 이미지는 일반적으로 마이크로소프트에 의해서만 만들어진다.
    • 세션 관리자 프로세스(smss.exe)가 한 예시이다.
      • 이는 커널에 의해 직접 생성되는 최초의 유저 모드 프로세스다. csrss.exe가 아직 시작되지 않았으므로 윈도우 서브시스템에 종속적일 수 없다.
      • 실제로 smss.execsrss.exe의 시작을 책임진다.
      • Dependency Walker로 봤을 때 오직 ntdll.dll에서만 의존성을 보여주며, 서브시스템이 Native임에 주목하자.
    • 디스크 검사를 위해 종종 시스템 시작 시점에 실행하는 Autochk 유틸리티도 다른 한 예다.
      • 이 유틸리티는 smss.exe에 의해 시작되며, 부트 과정에서 상대적으로 이른 시점에 실행하므로 어떠한 서브시스템에도 의존적일 수 없다.

익스큐티브

  • 윈도우 익스큐티브는 ntoskrnl.exe의 상위 계층이다(커널은 하위 계층). 익스큐티브는 다음과 같은 유형의 함수를 내장한다.
익스포트되어 있고 유저 모드로부터 호출 가능한 함수
  • 시스템 서비스로 불리며, ntdll.dll을 통해 익스포트된다. (NtCreateFile)
  • 대부분의 서비스는 윈도우 API나 여타 환경 서브시스템 API를 통해 접근이 가능하지만, 일부 서비스는 문서화된 서브시스템 함수를 통해서는 이용할 수 없다. 예를 들어 ALPC와 NtQueryInformationProcess 같은 다양한 쿼리 함수나, NtCreatePagingFile 등의 특수 함수가 있다.
DeviceIoControl 함수를 통해 호출되는 디바이스 드라이버 함수
  • 이 함수는 읽기나 쓰기와 관계없는 디바이스 드라이버 내의 함수를 호출하기 위한 유저 모드에서 커널 모드로의 일반화된 인터페이스를 제공한다.
WDK에 문서화되어 있고 커널 모드에서만 호출할 수 있는 익스포트된 함수
  • I/O 관리자(Io로 시작하는)와 일반 익스큐티브 함수(Ex) 등과 같은 다양한 지원 루틴을 포함한다.
WDK에 문서화되지 않았고 커널 모드에서만 호출할 수 있는 익스포트된 함수
  • Inbv로 시작하는 비디오 드라이버에 의해 호출되는 함수
전역 심볼에는 정의되어 있지만 익스포트되지 않은 함수
  • 이들 함수는 내부 I/O 관리자 지원 함수(lo로 시작)나 내부 메모리 관리 지원 함수(Mi)와 같은 ntoskrnl.dll 내부에서 호출되는 내부 지원 함수를 포함한다.
모듈 내부에 있지만 전역 심볼에 정의되어 있지 않은 함수
  • 익스큐티브와 커널에 의해서만 사용된다.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.