Kernel Object / Object Handle (펌)
2015. 8. 18. 13:13ㆍIT-개발/winapi 및 MFC
반응형
Kernel Object에 대해서 두리뭉실하게 알고 있던걸 아주 상세하게는 아니지만 조금은 덜 두리뭉실하게 알게 해주는~ ^^
어쨌든 Kernel Object에 대해서 아는데 도움을 많~이~ 주는 글을 보게 됐다.
1. 커널 오브젝트란?
우선 한 문장 정의부터 정리하면 아래와 같다고 할 수 있다.
"커널에서 관리하는 중요한 정보를 담아둔 데이터 블록"
예를 들어, 프로세스를 하나 생성했다고 치자.
OS에서는 이에 대한 요청이 오면 프로세스(커널 리소스)를 생성하고, 이에 관리하기 위한 정보들을 커널 오브젝트에 저장한다.
프로세스의 경우 프로세스 ID (PID), 기본 우선 순위, 종료 코드 등과 같은 정보를 가지고 있다.
반면 파일 커널 오브젝트의 경우 byte offset, sharing mode, open mode 등과 같은 정보를 담고 있다.
윈도우의 경우 커널 오브젝트에 대한 접근이 엄격하게 통제된다.
내용을 까 볼수도 없고, 직접적 엑세스 역시 불가하다.
커널 오브젝트는 프로세스뿐 아니라 상당히 다양한 종류가 있는데,
이들의 성격과 필요 정보에 따라 커널 오브젝트의 내용과 형식 또한 모두 다르다.
프로세스에 관련된 커널 오브젝트에 파일에 관련된 정보가 필요하진 않을테니 말이다.
다음 MSDN 링크를 보면 Kernel object에 대한 간략한 설명과 그 종류들에 대해 테이블로 정리되어 있다.
2. 오브젝트 핸들?
위에서 커널 오브젝트는 OS의 엄격한 통제로 인해 프로그래머가 직접 조작할 수 없다고 하였다.
이에 OS는 이를 OS가 제공하는 API들을 통해 커널 오브젝트를 간접 조작할 수 있도록,
커널 오브젝트를 가리키고 있는 오브젝트 핸들이라는 녀석을 제공한다.
커널 오브젝트 핸들은 프로세스별로 독립적으로 유지되며, 이는 운영체제를 더욱 견고하게 하기 위함이다.
만약, 글로벌하게 커널 오브젝트 핸들이 종료한다면, 멜웨어 프로세스가 정상 프로세스의 커널 사용을 중도에 불가시킬 수도 있다.
자세한 내용은 아래에서 다시 설명하겠다.
3. 오브젝트 핸들 - 커널 오브젝트 - 커널 리소스
아래 그림과 같이 핸들 - 오브젝트 - 리소스가 관계지어진다.
이에 대한 자세한 내용은 아래 챕터에서 꾸준히 설명해 갈 것이다.
4. 커널 오브젝트와 오브젝트 핸들의 종속성
여기서 얘기하는 종속성이란, 이 녀석들이 누구에 의해 관리 되는지로 해석하면 된다.
1) 커널 오브젝트
커널 오브젝트는 OS에 종속적이다. 정확히 윈도우 운영체제에 종속적이다.
커널 오브젝트의 소멸 시점은 프로세스 단위가 아닌 운영체제에 의해서 결정된다.
게다가, 여러 개의 프로세스에서 동시 접근(공유)이 가능하다.
따라서, 프로세스 종속이 아닌 운영체제 종속적이란 표현을 쓰는 것이다.
보통 커널 오브젝트에 대응하는 오브젝트 핸들을 얻은 후 이에 대해 반환 요청을 할 때 다음 함수를 사용한다.
BOOL WINAPI CloseHandle(HANDLE hObject);
CloseHandle 함수는 커널 오브젝트의 usage count를 감소시키는 녀석이지,
커널 오브젝트를 바로 반환하는 함수는 아니다.
위의 내용을 다음과 같이 정리할 수 있다.
1) 커널 오브젝트는 OS에서 생성, 소멸 등 관리를 전적으로 책임진다.
2) 커널 오브젝트는 Usage Count라는 Reference Count 개념에 의해 소멸된다.
: 생성시 UsageCount는 1이 되겠지...
3) CloseHandle() 함수는 커널 오브젝트를 소멸시키는 것이 아니라, "자신이 다 썼음"을 알리는 녀석일 뿐이다.
또 하나의 예는 프로세스 내 프로세스 생성이다.
음... 바탕 화면 (Explorer.exe)의 제어판(ControlPanel.exe)을 실행하는 경우를 생각하면 쉬울 듯 하다.
제어판 프로세스가 생성되는 순간 Usage Count는 얼마인가? 2 다.
바탕화면 프로세스가 제어판 프로세스를 생성시키는 순간 바탕화면 역시 제어판을 참조할 수 있기 때문이다.
사용자가 제어판을 닫으면, 제어판 스스로 닫힐 때 UC가 1이 감소되고,
바탕화면에서 이를 감지하여 CloseHandle() 을 호출하여야 비로소 제어판 프로세스 커널 오브젝트가 소멸되겠지~
사용자가 제어판을 닫으면, 제어판 스스로 닫힐 때 UC가 1이 감소되고,
바탕화면에서 이를 감지하여 CloseHandle() 을 호출하여야 비로소 제어판 프로세스 커널 오브젝트가 소멸되겠지~
또한, CloseHandle 함수는 반환되기 직전에 프로세스의 커널 오브젝트 핸들 테이블에서 해당 항목을 삭제한다.
이럼으로써, 해당 프로세스 내에서는 더 이상 닫은 핸들로 커널 오브젝트에 접근이 불가능하게 된다.
2) 오브젝트 핸들
오브젝트 핸들은 커널 오브젝트와 다르게 해당 프로세스 종속적이다.
즉, 오브젝트 핸들은 각 프로세스마다 독립적으로 관리됨을 의미한다.
위에서 설명한대로 오브젝트 핸들은 해당 프로세스 내에서 커널 오브젝트에 접근하기 위한 인덱스 개념에 가깝다.
즉, 동일 커널 오브젝트라 하더라도 프로세스 별로 그 핸들값은 상이할 수 있다.
이러한 특성으로 오브젝트 핸들과 커널 오브젝트간 연결고리에 대해 제대로 설명하려면 오브젝트 핸들 테이블을 설명해야 한다.
우선, 커널 리소스의 생성 과정에서 커널 오브젝트가 생성되면, 생성된 커널 오브젝트를 가리키기 위해 사용되는 오브젝트 핸들이 반환되는 것은 위에서 설명하였다.
헌데, 커널 오브젝트 핸들만 가지고 어떻게 커널 오브젝트를 찾아가지? 라는 생각에 다다르면 이해가 쉽게 되지 않는다.
임시 발급된 1004 핸들을 가지고 0x1234 번지에 할당된 커널 오브젝트를 찾아가려면, 1004 핸들이 0x1234 위치를 가리키고 있는 정보가 필요하게 된다
이것이 바로 커널 오브젝트 핸들 테이블(핸들 정보를 저장하고 있는 테이블)이 존재하는 이유이다.
위 그림과 같이 커널 오브젝트에 대한 오브젝트 핸들을 던져줄 때 핸들값과 위치 정보등을 같이 넘겨주고, 프로세스에서는 이를 오브젝트 핸들 테이블로써 관리하기에 오브젝트 핸들을 통한 커널 오브젝트에 대한 간접 접근이 가능한 것이다.
프로세스가 초기화되면, 운영체제는 프로세스를 위해 커널 오브젝트 핸들 테이블을 할당한다.
따라서, 당연히 커널 오브젝트 핸들 테이블은 프로세스별로 따로 관리되며, 프로세스의 경우 상속이 되기도 한다.
부모 프로세스가 자식 프로세스를 생성할 때 옵션으로 상속 여부를 결정할 수 있으며, 상속이 되면 자식 프로세스는 부모 프로세스의 핸들 테이블 중 상속이 허용된 항목들에 대해 테이블 복사가 발생한다.
이 경우 복사된 오브젝트 핸들들에 대한 커널 오브젝트의 Usage Count는 모두 증가한다.
즉, 커널 오브젝트의 Usage Count는 참조하는 프로세스의 수만큼 잡힌다고 표현하지만,
프로세스 핸들 테이블에 포함된 수 만큼 값이 설정된다고 표현하는 것이 더욱 더 정확한 표현일 것이다.
커널 오브젝트 핸들 테이블은 단순한 데이터 구조체의 배열로 이루어져 있으며, 정확한 구성은 아래와 같다.
- Index
- 커널 오브젝트의 메모리 블록을 가리키는 포인터
- AccessMask
- Flag (HANDLE_FLAG_INHERIT=0x0000001, HANDLE_FLAG_PROTECT_FROM_CLOSE=0x00000002)
5. 커널 오브젝트의 생성과 실패시 주의 사항
프로세스가 최초로 초기회되면 프로세스의 핸들 테이블은 비어있다.
프로세스 내 쓰레드가 커널 오브젝트를 생성하는 Create* 함수를 호출하면, 커널은 커널 오브젝트를 위한 메모리 블록을 할당하고 초기화한다.
이후 커널은 프로세스의 핸들 테이블을 조사하여 비어 있는 공간을 찾아내어, 해당 포인터를 업데이트 시킨다.
커널 오브젝트를 생성하는 함수들, 즉 Create* 함수들은 반드시 SECURITY_ATTRIBUTES의 포인터를 인자로 가진다.
이를 통해 보안 등급에 대한 정보와 상속 여부에 대한 정보를 설정할 수 있다.
커널 오브젝트를 생성하는 함수가 실패하면 반환되는 핸들값은 보통 NULL이 된다.
하지만, 모든 커널 오브젝트가 실패시 NULL만 리턴하는 것은 아니다.
INVALID_HANDLE_VALUE를 반환하는 커널 오브젝트들도 있다.
따라서, 해당 커널 오브젝트 생성 실패시 NULL을 반환하는지, INVALID_HANDLE_VALUE를 반환하는지 유념해서 예외 처리를 해야 한다.
- HANDLE hMutex = CreateMutex(...);
- if (INVALID_HANDLE_VALUE == hMutex)
- {
- // CreateMutex는 실패시 NULL을 반환하므로,
- // 이 조건문 블럭은 절대로 수행되지 않는다.
- }
- HANDLE hFile = CreateFile(...);
- if (NULL == hFile)
- {
- // CreateFile는 실패시 INVALID_HANDLE_VALUE을 반환하므로,
- // 이 조건문 블럭은 절대로 수행되지 않는다.
- }
6. 커널 오브젝트의 상태
커널 오브젝트는 다음 두 가지 상태를 가진다.
1) Signaled
2) NON - Signaled
커널 오브젝트는 커널 리소스의 타입에 따라 다양하게 구성되어 있다고 얘기하였다.
커널 오브젝트의 상태 역시 타입에 따라 Signaled <-> NON - Signaled 상태 전이의 시점이 다르다.
이 상태를 감시할 수 있는 함수가 바로 WaifForSingleObject / WaitForMultipleObject 이다.
위 함수들은 커널 오브젝트가 NON - Signaled 상태에선 Blocking 되어 있다가, Signaled 상태가 되면 함수를 빠져 나온다.
프로세스 커널 오브젝트의 경우엔 Signaled <-> NON - Signaled 상태 전이가 다음과 같이 이루어진다.
1) 프로세스 실행 중 : NON - Signaled
2) 프로세스 종료시 : Signaled
WaitForSingleObject MSDN : http://msdn.microsoft.com/en-us/library/ms687032(VS.85).aspx
참고로, 쓰레드 커널 오브젝트 역시 프로세스 커널 오브젝트와 동일하게 상태 전이가 발생한다.
참고로, 쓰레드 커널 오브젝트 역시 프로세스 커널 오브젝트와 동일하게 상태 전이가 발생한다.
7. 커널 오브젝트의 공유
커널 오브젝트를 프로세스간 공유하는 방법엔 크게 세 가지가 있다.
- 커널 오브젝트 핸들의 상속 (자식 프로세스를 생성할 때에만 가능. 이미 만들어진 자식에게 추가 상속은 불가능)
- Named 커널 오브젝트 (Mutex, Semaphore, Event, WaitableTimer, FileMapping, JobObject)
- DuplicateHandle (프로세스간 핸들 복사, http://msdn.microsoft.com/en-us/library/ms724251)
위 세 가지 방법 중, 커널 오브젝트 핸들의 상속과 DuplicateHandle의 경우 핸들을 넘기는 것은 문제가 되지 않으나,
어떠한 녀석이 넘어갔는지 반드시 프로세스간 통신을 통해 알려주어야 하는 문제가 있다.
=================================================================================================
CreateProcess 사용하고도 Process Handle 전혀 관리하지 않는 사람은 꼭 닫으시길~ ㅋ 나부터 닫자
'IT-개발 > winapi 및 MFC' 카테고리의 다른 글
CListCtrl 포커스 해제시 selection color 유지하기 (0) | 2015.08.21 |
---|---|
잘못된 인수가 발견되었습니다(An invalid argument encountered) (0) | 2015.08.18 |
CreateEvent 분석...(펌) (0) | 2015.08.11 |
Microsoft.Windows.Common-Controls 6.0 Style (0) | 2015.07.23 |
메모리누수 - 대응법(2) (0) | 2015.07.02 |