본문 바로가기
개발/Windows

장치 드라이버와 커널 오브젝트 (Device Driver & Kernel Object)

by lucidmaj7 2019. 12. 30.
728x90
반응형

Kernel Object(커널 오브젝트)

Windows OS가 구동되는데 많은 요소들이 존재한다. 프로세스가 실행되고, 레지스트리의 값을 읽고, 쓰고, 쓰레드가 생성되고, 파일에 접근한다. 이 모든 리소스들은 Kernel에서는 Kernel Object로 관리된다. Windows의 Kernel Object는 크게 25가지 이상의 유형으로 나뉜다.
https://docs.microsoft.com/ko-kr/windows-hardware/drivers/kernel/windows-kernel-mode-object-manager

Kernel Object Manager

Windows NT Kernel 내부에는 Kernel Object를 관리하는 역할을 하는 것이 있는데 이것을 Kernel Object Manager라 부른다. Kernel Object Manager는 Windows NT 아키텍쳐의 한 부분으로 논리적으로 정의된 Kernel Object를 관리한다. 이때 각각 객체들은 정해진 Namespace로 이름이 정해지고 관리된다. 그 현황을 볼 수 있는 툴은 Sysinternals WinObj.
Kernel Object Manager는 자원들의 Reference count 기준으로 생성과 소멸을 관리한다. 또 한 Handle과 Kernel Object들의 이름을 맵핑하여 User Mode에서 접근 가능 하도록 한다.

WinObj - Sysinternals

Kernel Object에 접근?

Windows(NT)는 User Mode와 Kernel Mode가 분리된 OS이다. 때문에 User Mode에서 Kernel Mode의 요소에 함부로 접근할 수 없다. OS의 허락이 있어야 접근 가능하다. 파일, 프로세스, 쓰레드, 레지스트리 등 각각의 Kernel Object를 User Mode Application이 접근하려면 Kernel Object에 할당된 이름을 가지고 Kernel Object Manager에게 API를 통해 요청하여 Handle을 할당 받을 수 있다.
대표적으로 파일 접근을 예로 들 수 있다.
c:\a.txt에 위치한 파일을 접근하려면 CreateFile 함수를 통해 C:\a.txt라는 경로의 파일 핸들을 요청하게 된다. 이때 커널은 C:\a.txt에 해당하는 Kernel Object를 생성하고 그에 해당하는 Handle을 반환한다.

User Mode에서 접근 할 수 없는 Device Object

Device Object는 Windows가 정의한 Kernel Object Namespace 규칙에 따라 \Device\ 아래에 놓이게 된다.
https://docs.microsoft.com/ko-kr/windows-hardware/drivers/kernel/object-directories
하지만 큰 문제가 있는데 \Device\ 밑에 있는 Device Object는 User Mode에서 접근이 불가능 하다. 대신 다른 방법으로 접근이 가능한데 이때 사용하는 방법이 Symbolic Link이다. User Mode Application은 Kernel의 Device Object에 접근하려면 Symbolic link를 통해서 접근이 가능하다. 우리가 Device Driver를 개발할 때 DriverEntry에서 SymbolicLink를 생성하는 이유가 바로 여기에 있다. 아래 코드는 Device Driver에서 Device Object와 SymbolicLink를 연결하여 생성해 주는 예시 코드 이다.
https://docs.microsoft.com/ko-kr/windows-hardware/drivers/kernel/introduction-to-ms-dos-device-names

UNICODE_STRING DeviceName;
UNICODE_STRING DosDeviceName;
NTSTATUS status;

RtlInitUnicodeString(&DeviceName, L"\\Device\\DeviceName");
RtlInitUnicodeString(&DosDeviceName, L"\\DosDevices\\DosDeviceName");
status = IoCreateSymbolicLink(&DosDeviceName, &DeviceName);
if (!NT_SUCCESS(status)) {
  /* Symbolic link creation failed.  Handle error appropriately. */
}

위 코드와 같이 Device Object와 User Mode에서 접근 가능한 DosDeviceName의 Symbolic Link를 생성하고 나면 User Application에서는 아래와 같이 접근이 가능하다.

file = CreateFileW(L"\\\\.\\DosDeviceName",
  GENERIC READ | GENERIC WRITE,
    0,
    NULL,
    OPEN_EXISTING,
    0,
    NULL);

왜 DosDevice라 명명할까?

우리는 이제 Windows NT의 드라이버를 주로 개발 할 것이다. 그런데 왜 갑자기 DosDevice라는 Symbolic Link를 만들까? DOS는 우리가 아는 그것 아닌가?

MS-DOS

MSDN의 문서에 보면 \Device\로 명명되는 Object이름을 _NT Device Name_이라 부르고 있다.

A named device object has a name of the form \Device\DeviceName. This is known as the NT device name of the device object.

여기서 부터 추측이다.
과거 NT계열 이전의 OS(95, 98, me... )들은 MS-DOS기반 운영체제 였다. 따로 User Mode와 Kernel Mode의 분리가 없었고 바로 Device에 접근하였을 것 이다. Windows API의 하위 호환성은 유명하 듯 Windows 10에 와서도 여전히 어느정도 유지된다. 때문에 장치에 접근하는 소스 코드들은 DosDevice 이름을 이용하여 접근하고 있었을 것이다. 때문에 NT로 넘어와서도 이와 같은 호환성을 지켜주기 위해 NT Device Name을 DosDevice Name으로 Symblic Link를 통해 접근 가능하게 한게 아닌가 생각된다.
두번째 추측으로 예전의 User Mode와 Kernel Mode가 분리되지 않던 시절 DosDevice Name을 User Mode에서도 썼기 때문에 이것을 User Mode에서 접근 가능한 Namespace로 정의하고, NT Device Name은 커널모드에서만 접근가능하다는 의미로 정의한 것이 아닌가 싶다.
아무튼 결론은 User Mode에서 접근 할 때에는 DosDevice Name을 이용하여 접근 하여야 한다.

728x90
반응형

댓글