글 작성자: Sowhat_93

커널 오브젝트는 무엇인가?

Windows는 Object의 형태로 OS내의 자원을 관리한다.

Object는 메모리 블럭의 형태를 띄고있다.

C/C++ 의 구조체를 생각하면 된다.

https://docs.microsoft.com/en-us/windows/win32/sysinfo/object-categories

 

Object Categories - Win32 apps

The system provides three categories of objects: user, graphics device interface (GDI), and kernel.

docs.microsoft.com

 

이중에서, 그 중요도가 높아 Windows Kernel에 의해서 관리되는 자원을 Kernel Object로 분리해두었다.

 

 

Windows Application은 Process, Thread, File 등과 같은 리소스등에 접근할때

커널 오브젝트를 사용해야만 한다.

 

WinObj를 통해 어떤 Kernel Object가 사용중인지 볼 수 있다.

커널 오브젝트는 운영체제에 의해서 관리된다.

또한 커널 오브젝트는 운영체제가 관리하는 커널메모리에 위치하며,

App은 이 메모리에 직접 접근할수 없도록 설계되어 있다.

 

App이 OS에게 사용하고 싶은 기능이 있으니 커널 메모리내에 Kernel Object를 생성해달라는 요청을 하면, OS는 이를 수락할 수도 있고, 기각할수도 있다.

 

일반적으로 Kernel Object 생성요청 함수는 Create~ 와 같은 형태를 띄는데,

많은 개발자들이 이를 프로세스 메모리내의 어딘가에 동적으로 할당했다고 생각하지만,

Kernel Object는 어디까지나 OS에 의해 커널 메모리 내에 할당되고,

App으로부터의 요청이 왔을때 구분하기 위해 HANDLE값을 매겨짐을 기억해야한다.  

 

App에서 Kernel Object 를 생성하는 함수를 호출하면, 

Windows OS가 이를 수락할시, HANDLE(핸들)이라는 형태로 반환한다.  

 

App은 HANDLE을 매개변수로 하는 많은 함수들로,

OS에게 Kernel Object에 대한 다양한 작업에 대한 요청을 전달한다.

참고로 이 HANDLE값은, 같은 Kernel Object를 사용한다고 하더라도 프로세스마다 다르다.

프로세스 별로 독립적인 핸들테이블이 존재하기 때문이다.

 

때문에 예를들어

어떠한 Process A 가 4126 핸들을 가지고 있다고 하고,

또 어떠한 Process B가 4126 핸들을 가지고 있다고 하면,

 

이 두개의 프로세스가 각각 가지고 있는 HANDLE은 같은 Kernel Object를 가리키지 않고 있을

확률이 매우 높다는 것이다.

앞서 언급된 대로, 프로세스 별로 독립적인 핸들테이블이 존재한다.

절대로 같은 OS 내부에서 같은 핸들값이 같은 커널 오브젝트를 의미하는 것이 아니다.

 

커널 오브젝트는 사용 카운트를 가진다.

Application 개발자들에게는 Reference Count로 이미 잘 알려진 개념이다.

커널 메모리에 위치한 Kernel Object 의 내부에 사용카운트가 있으며, OS가 이를 관리한다.

 

사용 카운트의 감소조건은 다음과 같다.

 

Application이 Kernel Object 해제 관련함수(CloseHandle 등. HANDLE을 인자로 받는)를 호출해

Kernel Object 에 대한 사용이 끝났음을 알리면,

OS는 Process의 핸들테이블을 참조해서 어떤 Kernel Object을 의미하는지 알아내고,

해당 Kernel Object의 사용카운트를 감소시킨다.

 

process가 종료되면, OS는 process의 핸들테이블을 참조해 해당 process가사용중이던 Kernel Object에 대해 사용카운트를 감소시킨다.

 

이를 통해 Kernel Object의 사용카운트가 0이되면, OS가 커널 메모리상의 Kernel Object를 해제한다.

 

만일 두개이상의 process가 이를 참조중이고,

하나의 process가 HANDLE에 대해 해제함수를 호출하거나 종료되어도,

커널오브젝트 내부적으로 사용카운트가 있어 커널오브젝트는 해제되지 않는다.

 

CloseHandle은 해당 Kernel Object에 대한 사용 카운트를 감소시킴을 의미한다.

 

커널 오브젝트에 대해 보안 수준이 설정될 수 있다.

커널 오브젝트는 Security Descripter를 통해 보호 될수 있는데,

Security Descripter는

Windows의 그룹과 사용자 정보에 대해 그들이 각기 어떠한 권한을

소유하고 있는지 등에 대한 정보를 담고있다.

 

 

Kernel Object에 대한 HANDLE을 반환하는 Create~계열 함수들은

SECURITY_ATTRIBUTES 타입 구조체 에 대한 Address를 Param으로 받는 경우가 많다.

 

대부분의 Windows Application 개발자들의 경우 이 인자에 nullptr를 넣게되는데,

이는 현재 프로세스의 보안 토큰을 근간으로 하는 프로세스의 기본 보안 디스크립터를 사용한다는 뜻이 된다.

 

만일 오브젝트 생성시 Kernel Object에 대한 접근 권한을 제한하고자 한다면,

함수호출시 nullptr를 전달하면 안되고,  SECURITY_ATTRIBUTES 타입 구조체를 원하는대로 초기화시켜야 한다.

이렇게 생성된 커널 오브젝트에 대해 접근시(OpenFileMapping 등) 접근권한이 없는 사용자나

process가 접근이 거부 될 경우, GetLastError를 호출하면 ERROR_ACCESS_DENIED를 얻게 된다.