본문 바로가기

01. 커널 오브젝트 上

by 식 2009. 8. 18.

1. 커널(Kernel)
 -  커널 
 원래의 뜻은 곧식의 낟알, 알맹이이라는 뜻으로, 그만큼 운영체제(OS)의 가장 기본적이고 핵심이 되는 부분이기 때문에 코어(Core)라고 불리기도 한다.
 운영체제는 커널기반 위에 외부와의 통신을 가능하게 하는 입출력 프로그램, 기본 사용자 인터페이스, 하드웨어 드라이버등이 덧붙여서 완성된다.

- 역할
 ① 자원관리 : 한정된 시스템 자원을 효율적으로 관리하여 프로그램의 실행을 원할하도록 돕는다. 프로세스에 자원을 분배하는 것을 스케줄링이라고 한다.
 ② 보안 : 하드웨어와 프로세스간의 보안을 책임진다.
 ③ 추상화 : 운영체제의 내부를 감추고 일관성 있는 하드웨어를 제공하기 위해 하드웨어 추상화(같은 종류의 장비에 대한 공통 명령어의 집합)들로 구현된다.



2. 커널 오브젝트(Kernel Object)
- 커널 오브젝트
 커널(Kernel)에 의해 할당된 단순한 메모리 블록이다. 이 메모리 블록은 생성한 커널에 의해서만 접근이 가능한 구조체로 구성되어 있으며, 애플리케이션이 구조체를 옮기거나 내용을 변경할 수 없다.


 - 종류
Event Object, File Object, File-Mapping Object, Mutex Object, Process Object, Semaphore Object, Thread Object.. 등이 있고 운영체제에서는 이러한 커널오브젝트를 생성하고 조작한다.


- 데이터 구조체
 커널 오브젝트에 대한 정보를 저장하는 구조체이다. 커널 오브젝트에 대한 세부 정보들을 저장하고 있다.
커널 오브젝트간에는 공통적으로 존재하는 값(Security Descriptor, Usage Count 등)도 있지만 대부분의 값들은 각 오브젝트별로 독특하다.  예를 들어 Process Object는 Process ID, Base Priority, Exit Code를 가지지만 File Object는 Byte Offset, Sharing Mode, Open Mode를 가진다.
 커널에 의해서만 접근이 가능하기 때문에 애플리케이션에서 데이터 구조체가 저장되어 있는 메모리 위치를 직접 접근하여 그 내용을 변경하는 것은 불가능하다. 마이크로소프트는 커널 오브젝트의 구조체가 가능한 한 일관되게 유지될 수 있도록 하기 위해 이러한 제약사양을 의도적으로 만들어두었다. 이렇게 구조체에 대한 직접적인 접근을 제한함으로서 마이크로소프트는 이미 개발되어 있는 애플리케이션에 영향을 미치지 않고도 구조체에 내용을 임의로 추가, 삭제, 변경할 수 있다.


- 커널 오브젝트의 조작
커널 오브젝트 구조체의 변경을 위해 MS는 정제된 방법을 통해 구조체의 내용에 접근할 수 있도록 일련의 함수 집합을 제공 하고 있는데 이를 통해 커널 오브젝트의 내부적인 값에 접근할 수 있다.

 ① 커널 오브젝트를 생성하는 함수를 호출 시에 생성된 오브젝트에 대한 핸들을 반환한다.
 ② 핸들은 프로세스 내의 모든 스레드에 의해 사용 가능한 값이지만 특별한 의미를 가지고 있지는 않다. 
 ③ 운영체제는 매개변수로 전달된 핸들 값들을 통해 어떤 커널 오브젝트를 조작하고자 하는지 구분할 수 있다.

※ 핸들 값은 프로세스별로 독립된 프로세스 핸들 테이블이 존재한다. 따라서 동일한 핸들 값이라도 전혀 다른 커널 오브젝트를 참조할 수 있기 때문에 이 핸들 값을 다른 프로세스에 전달하여 사용하는것은 불가능하다.


- 사용 카운트(Usage Count) 
 모든 커널 오브젝트 타입이 가지고 있는 공통적인 값이다. 커널 오브젝트는 프로세스(Process)가 아니라 커널에 의해 소유되기 때문에 프로세스가 특정함수를 통해 커널 오브젝트를 생성한 후 종료된다고 하더라도 반드시 생성된 커널 오브젝트가 프로세스와 함께 삭제되는 것은 아니다.

사용 카운트가 변경되는 경우
 ① 커널 오브젝트가 최초로 생성되면 사용카운트는 1로 설정
 ② 다른 프로세스가 생성된 커널 오브젝트에 대한 접근 권한을 획득하면 사용카운트가 증가된다.
 ③ CloseHandle()함수 호출시 사용카운트가 감소된다.
 ④ 프로세스가 종료시에 커널 오브젝트가 아직 열려 있는 모든 커널오브젝트에 대하여 자동적으로 사용 카운트를 감소시킨다.
 ⑤ 사용 카운트가 0까지 내려가면 이 오브젝트는 커널에 의해 삭제된다.


- 보안 해설자(Security Descriptor)
커널 오브젝트는 보안 해설자로 보호될 수 있다. 주로 서버 애플리케이션을 개발할 때 많이 사용된다.
대부분의 커널 오브젝트 생성 함수들은 SECURITY_ATTRIBUTES 구조체에 대한 포인터를 인자로 받아들인다. 
 일반적으로 커널 오브젝트를 기본 보안 설정으로 생성하기 위해 NULL값을 파라미터로 설정하여 함수를 호출한다. 기본 보안 설정은 관리자 그룹의 모든 구성원과 커널 오브젝트를 생성한 프로세스가 이 커널 오브젝트에 대한 모든 접근 권한을 갖도록 한다.

보안해설자가 가지고 있는 정보
 ① 커널 오브젝트의 소유자
 ② 커널 오브젝트를 사용하고 있는 그룹과 사용자
 ③ 접근이 제한된 그룹과 사용자

typedef struct _SECURITY_ATTRIBUTES { // sa 
    DWORD  nLength;					// 버전확인을 위한 정보
    LPVOID lpSecurityDescriptor;	// 초기화된 SD 주소
    BOOL   bInheritHandle;			// 상속을 위한 멤버 변수
} SECURITY_ATTRIBUTES; 

 ※ 커널 오브젝트 이외에도 유저 오브젝트(Menu, Window, Cursor, Icon, Brush, Font..), GDI(Graphic Device Interface) 오브젝트가 존재한다. 커널오브젝트인지의 여부를 결정하는 간단한 방법은 오브젝트를 생성하는 함수에서 보안 특성을 지정하는 매개변수의 존재여부를 확인하는 것이다.

3. 핸들 테이블(Handle Table)
 프로세스가 초기화되면 운영체제는 프로세스를 위해 커널 오브젝트 핸들 테이블을 할당한다. 이러한 핸들 테이블은 사용자 오브젝트나 GDI 오브젝트에 의해서는 사용되지 않고 유일하게 커널 오브젝트에 의해서만 사용된다.

 인덱스 커널 오브젝트의 메모리 블록을 가리키는 포인터  액세스 마스크(각 비트별 플래그 값을 가지는 DWORD)  플래그 
   0x????????  0x????????  0x???????? 
 2  0x????????  0x????????   0x????????
 ..  ...  ...   ... 


- 커널 오브젝트 생성
 ① 프로세스가 최초로 초기화되면 프로세스의 핸들 테이블은 비어 있다.
 ② 프로세스 내의 스레드가 커널 오브젝트를 생성하는 함수를 호출하면 커널은 커널 오브젝트를 위한 메모리 블록을 할당하고 초기화한다.
 ③ 커널은 프로세스의 핸들 테이블을 조사하여 비어 있는 공간을 찾아낸다.
 ④ 핸들 테이블의 빈 공간에 초기화를 수행한다.
 ⑤ 커널 오브젝트를 생성하는 함수는 고유한 핸들 값을 반환한다.

이러한 핸들 값을 4로 나누면(윈도우가 내부적으로 사용하고 있는 하위 2비트를 무시하기 위해 오른쪽으로 2를 시프트) 커널 오브젝트에 대한 정보를 저장하고 있는 프로세스 핸들 테이블의 인덱스 값을 얻을 수 있다. 유효한 커널 오브젝트 핸들 값은 4부터 시작된다.

커널 오브젝트 핸들을 인자로 취하는 함수를 호출할 때에는 항상 Create* 류의 함수 중 하나를 호출하여 반환된 핸들 값을 전달해야 한다. 내부적으로 이러한 함수들은 프로세스 핸들 테이블로부터 사용하고자 하는 커널 오브젝트의 실제 주소를 얻어낸 후 잘 정의된 방식으로 커널 오브젝트의 자료 구조를 변경한다.

 ※ 유효하지 않은 핸들을 사용시 함수는 실패하고 GetLastError 호출 결과로 6(ERROR_INVALID_HANDLE)을 반환한다. 
 ※ 커널 오브젝트를 생성하는 함수가 실패하면 반환되는 핸들 값은 보통 NULL(0)이 된다. 시스템의 가용 메모리가 매우 작거나 보안 문제로 인해서 함수가 실패하는 경우 몇몇 함수들은 INVALID_HANDLE_VALUE(-1) 값을 반환 한다. 따라서 커널 오브젝트를 생성하는 함수의 반환 값을 확인할때 주의해야 한다.


- 커널 오브젝트 삭제
프로젝트를 어떻게 생성했는지와 상관없이 CloseHandle 함수를 호출하여 더 이상 커널 오브젝트를 사용하지 않을 것임을 시스템에게 알려줄 수 있다.
BOOL CloseHandle(HANDLE hObject);

CloseHandle함수를 호출하지 않더라도 프로세스 종료시 운영체제는 사용 카운트를 감소시키기고 프로세스가 사용하던 모든 자원을 반환하여 누수가 발생하지 않지만 프로세스가 실행중인 경우에는 자원의 낭비가 발생할 수 있다.

CloseHandle 함수 호출시
 ① 프로세스의 핸들 테이블을 검사하여 전달받은 핸들 값을 통해 실제 커널 오브젝트에 접근 가능한지를 확인한다. 
 ② 핸들이 유효한 값이고 시스템이 커널 오브젝트의 자료 구조를 획득하게 되면, 구조체 내의 사용 카운트를 감소시킨다.
 ③ 사용 카운트가 0이 되면 이 커널 오브젝트는 메모리에서 삭제된다.

프로세스 종료시
 ① 자동으로 프로세스 핸들 테이블을 검색한다.
 ② 프로세스 종료 이전에 닫지 않은 커널 오브젝트의 핸들 값이 운영체제에 의해 자동으로 닫혀진다.
 ③ 사용 카운트가 0이 되면 이 커널 오브젝트는 메모리에서 삭제된다.

 ※ 유효하지 않은 핸들로 CloseHandle함수 호출시 프로세스는 정상적으로 수행되고 FALSE를 반환한다. GetLastError호출시 ERROR_INVALID_HANDLE을 반환한다. 디버깅시에는 에러를 디버깅할 수 있도록 0xC0000008("An invalid handle was specified")예외가 발생한다.

 ※ CloseHandle을 호출한 이후에는 핸들 값 자체도 NULL로 초기화하는 것이 좋다. 만약 초기화하지 않는다면 에러를 반환할수도 있지만 나쁜 경우로 핸들테이블에 다른 커널 오브젝트를 가리키는 경우에는 적절하지 않은 커널 타입을 전달했다는 에러를 유발하거나 새로 생성된 커널 오브젝트가 삭제했던 오브젝트와 동일한 타입일 경우 충돌을 일으킬 수가 있다.



참고 : 위키피디아, Windows VIA C/C++,

반응형

'' 카테고리의 다른 글

03. 프로세스  (0) 2009.08.21
02. 커널 오브젝트 下  (0) 2009.08.18
한 줄에 여러개의 숫자를 입력받아 배열에 할당  (0) 2009.07.13
문자열 거꾸로 출력하기  (0) 2009.07.13
2. 메시지  (0) 2009.07.12