Windows Kernel(윈도우즈 커널) |
|
C/C++, MFC | 2008/05/04 (일) 11:13 |
| |||
커널 오브젝트(Kernel Objects)
커널 오브젝트는 시스템의 가장 핵심적인 기능인 프로세스, 스레드, 동기화 객체, 디스크 드라이버 사용, 메모리 관리 등의 기능을 제공한다.
사용자 인터페이스 오브젝트(User Interface Objects)
사용자 인터페이스 오브젝트는 윈도우를 만들 수 있는 기능을 제공하는데, 윈도우, 메뉴, 아이콘, 커서, 도구모음, 리바, 상태바 등이 이에 해당한다.
GDI 오브젝트(Graphic Device Interface Objects)
Windows 커널
커널의 일반적인 기능
1. 여러 프로그램의 동시 경쟁적 커널 서비스 요청을 처리하는 인터럽트 처리
2. 여러 프로그램에 대하여 커널 처리시간 할당을 위한 순서를 결정하는 스케줄러
3. 여러 프로그램에게 각종 자원의 사용 권한을 부여하는 관리자
4. 메모리, 디스크 내에서 운영체제의 주소공간을 관리하고 고르게 할당하는 메모리 관리자
커널 오브젝트 특성
커널 오브젝트가 가지는 공통적인 특징
1. 커널 내부 구조에 접근할 수 없다.
커널 내부로의 접근은 원칙적으로 제한되어 있다. 그 이유는 커널이라는 부분은 시스템에 현재 실행되고 있는 프로그램, 즉 모든 프로세스가 공유해서 사용하기 때문에 어느 하나의 프로세스가 실수로 커널을 잘못 사용할 경우 모든 프로세스가 중지되는 사태를 예방하기 위한 것이다. 이에 따라 커널 오브젝트를 사용하기 위한 API를 제공한다. WIN32 프로젝트에서는 windows.h, MFC 프로젝트에서는 afxwin.h를 삽입하여 각종 API를 사용할 수 있다.
2. 핸들(HANDLE): 커널 오브젝트에게 부여된 고유값
프로세스의 요구에 의해 생성된 커널 오브젝트에는 32비트 값의 고유 번호가 붙여진다. 이것을 핸들(handle)이라고 한다. Windows가 내부적으로 정해 놓은 HANDLE이라는 데이터 타입 형태로 프로세스에게 생성된 커널 오브젝트에 대한 고유값을 넘겨 준다.
3. 시그널(signaled), NonSignaled 중 한가지 상태를 가진다.
4. 보안속성: 커널 오브젝트 접근에 대한 통제가 가능하다.
디렉토리에 사용자별로 사용권한을 설정할 수 있는 것과 같이 커널 오브젝트도 유사한 보안 속성을 가진다. 즉, 커널 오브젝트를 생성한 프로세스가 해당 오브젝트의 소유자가 된다. 이 정보는 SECURITY_ATTRIBUTES 구조체를 사용하여 설정할 수 있다. 이 구조체는 커널 오브젝트에 대한 접근 및 사용 방법 등을 제한하고 있으며, 여러 커널 오브젝트의 생성 함수 인자에 사용된다.
이 구조체는 32bit api에서 지원하게 되는 커널 오브젝트 접근에 대한 제한을 설정하기 위하여 사용된다. SECURITY_ATTRIBUTES 구조체는 주로 서버측 프로그램에 설정된다. 그 이유는 이 구조체를 사용하는 API 함수들이 Windows 95/98/Me 에서는 자동적으로 접근을 최대한 허용하도록 설정되기 때문에 무시된다. 반면 Windows NT/2000 의 경우는 값을 설정하거나, NULL 값을 전달해야 한다. 따라서 클라이언트측 프로그램을 작성할 경우에는 신경 쓰지 않아도 된다. 서버측 프로그램일 경우에도 일반적으로는 쉽게 사용하기 위하여 특별한 설정 없이 NULL 값을 대입하는 경우가 많다.
5. 문자열 형태의 고유 이름(Name Object)을 가질 수 있다.
핸들을 고유값이라고 하였지만, 자신의 프로세스 안에서만 고유할 뿐이고, 다른 프로그램에서는 같은 값이 전혀 다른 오브젝트를 가리킬 수 있다. 시스템 전체에서 유일하도록 하려면 문자열 형태의 이름을 붙여야 한다. 이 값은 다른 프로그램에서도 사용이 가능하다. 즉, 동일한 이름을 가진 오브젝트를 시스템 내에서는 하나 밖에 생성할 수 없으며, 또한 여러 프로그램이 하나의 커널 오브젝트를 공유하여 사용할 수도 있다.
HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, “rain’s Event”);
위의 “rain’s Event”인 이벤트 커널 오브젝트를 생성한 것이다. 따라서 시스템 어디에서도 “rain’s Event”라는 이벤트 오브젝트는 생성할 수 없다.
6. 사용 계수(Usage Count)에 의해 소멸 여부가 결정된다.
커널 오브젝트는 한 개 이상의 프로세스에서 동시에 접근 및 사용이 가능하다. 따라서 운영체제는 얼마나 많은 프로그램이 해당 커널 오브젝트를 사용하고 있는지를 판단할 수 있어야 한다. 이때 사용하는 것이 사용 계수(Usage Count)라고 하며, 이 값이 0이면, 해당 오브젝트를 아무도 사용하지 않는다는 뜻이므로, 커널은 오브젝트를 물리적 메모리에서 제거한다.
Windows 3.x 운영체제에서는 시스템 내의 모든 프로세스에서 생성된 커널 오브젝트에 대한 핸들이 고유한 값이었다. 즉, A라는 프로세스에서 생성한 오브젝트가 12라는 핸들을 가지고 있었다면, B라는 프로세스에서 12라는 동일한 핸들로 접근이 가능했다는 것이다. 하지만, 각각의 프로세스가 독립적인 메모리 영역으로 분리가 되면서부터, 핸들은 각각의 프로그램 내에서만 유일한 값이 되었다. 이제는 A와 B프로세스에서 12는 서로 다른 오브젝트를 가리키거나, 유효하지 않은 핸들일 수도 있다는 것이다. Windows는 하나의 특정 프로세스에서 생성된 오브젝트를 다른 프로세스에서도 사용할 수 있도록 하는 몇 가지 기법을 마련하였는데, 부모 프로세스로의 상속, DuplicateHandle, 문자열 형태의 오브젝트 이름이 그것이다.
커널 오브젝트 핸들 테이블
하나의 프로세스는 실행 중 한 개의 커널 오브젝트만을 사용하지 않는다. Windows는 생성된 커널 오브젝트들을 핸들이 나열된 테이블(Table)을 이용하여 관리하는데, 이를 커널 오브젝트 핸들 테이블(Kernel Object Handle Table)이라고 한다.
핸들 테이블은 커널 오브젝트에 대한 정보를 가지고 있는 구조체의 단순한 배열 구조이다. 이 구조체에는 커널 오브젝트에 실제 포인터와 기타 정보가 들어 있다.
커널 오브젝트의 생성과 사용
각종 커널 오브젝트를 생성하기 위해서 API 함수는 일반적으로 “Create + 오브젝트명” 으로 정의되어 있다.
HANDLE CreateFile(…)
BOOL CreateProcess(…); 핸들은 인자로 전달됨
HANDLE CreateThread()
HANDLE HeapCreate()
HANDLE CreateFileMapping()
HANDLE CreateEvent(0
HANDLE CreateMutex()
HANDLE CreateSemaphore()
HANDLE CreateNamedPipe()
HANDLE CreateIoCompletionPort()
HMODULE LoadLibrary()
SOCKET socket() 소켓 핸들 SOCKET은 HANDLER과 같은 데이터 타입
HRESULT CCreateDevice()
위 함수들의 공통점은 리턴형이 HANDLE이며, 성공적으로 수행되면 생성된 오브젝트에 대한 핸들을 리턴한다. CreateProcess와 같이 리턴형이 HANDLE 값이 아닌 예외의 경우도 있다.
프로그램이 실행되면 시스템은 프로그램이 커널 오브젝트를 사용할 수 있도록 핸들 테이블을 메모리에 할당하고 초기화 한다. 이때 위와 같은 API를 사용하여 Windows의 운영체제에게 커널 오브젝트에 대한 생성을 요청하면, 해당 커널 오브젝트가 메모리에 생성된 후 고유번호(핸들)를 리턴 하게 된다. 프로그램은 이 값을 핸들 테이블에 저장하게 된다.
핸들을 가지고 커널 오브젝트를 사용할 때, 잘못된 핸들로 API를 호출하면 에러를 리턴한다. 따라서 커널 오브젝트를 생성할 때 사용하는 Create 계통(Create + 오브젝트)의 API함수의 리턴값, 즉 핸들이 유효한지를 반드시 확인해야 한다.
커널 오브젝트 소멸
프로세스가 더 이상 특정 커널 오브젝트를 사용할 필요가 없을 때, CloseHandle() API 를 호출하여 운영체제에게 커널 오브젝트의 사용이 끝났음을 통보한다.
BOOL CloseHandle(HANDLE hObject);
커널 오브젝트 핸들 상속성
프로세스 내부(Parent Process)에서 또 다른 프로세스(Child Process)를 생성하는 경우에 부모 프로세스가 생성한 커널 오브젝트의 핸들을 자식 프로세스가 사용할 수 있도록 할 수 있다. 즉, 부모 프로세스가 생성한 파일 오브젝트를 자식 프로세스가 사용할 수 있다는 뜻이다.
부모 프로세스에서 생성된 오브젝트에 대한 권한을 자식 프로세스에게까지 확장하려면 오브젝트 생성 API의 인자 SECURITY_ATTRIBUTES 구조체의 3번째 멤버인 bInheritHandle을 TRUE로 설정해야 한다.
typedef struct _SECURITY_ATTRIBUTES
{ DWORD nLength; // 본 구조체의 크기
LPVOID lpSecurityDescriptor; // 보안 기술자
BOOL bInheritHandle; // 핸들의 상속여부
} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES;
다음은 세마포어 오브젝트를 생성할 때, 후에 생겨날지 모르는 자식 프로세스에게 상송할 수 있는지에 대한 성질을 부여하는 예이다.
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;// 상속성 여부를 TRUE로 설정: TRUE이면 자식 프로세스가 핸들을 상속 받을 수 있다.
HANDLE hSemaphore = CreateSemaphore(&sa,0,3,”MySemaphore”);
if(hSemaphore == NULL){ … }
상속성을 TRUE로 설정한 후, CreateSemaphore()에 의해 핸들값이 32인 세마포어가 생성되었다면, 나중에 자식 프로세스가 생성되었더라도 32라는 동일한 값으로 커널 오브젝트를 접근할 수 있다는 뜻
중복 핸들(Duplicate Handle)
부모 프로세스와 자식 프로세스간에는 핸들을 같이 사용할 수가 있다. 하지만 부모, 자식 관계가 아닌 프로세스 간에도 동일한 오브젝트를 사용할 수 있다.
오브젝트가 생성된 프로세스의 핸들과 사용할 프로세스에 대한 정보를 알고 있다면 사용이 가능하다. 다음은 프로세스간에 핸들 공유 기능을 하는 API 원형이다.
BOOL DuplicateHandle(
HANDLE hSourceProcessHandle, // 오브젝트를 소유한 프로세스 핸들
HANDLE hSourceHandle, // 사용할 오브젝트 핸들 원본
HANDLE hTargetProcessHandle, // 복사본을 사용한 프로세스 핸들
LPHANDLE lpTargetHandle, // 오브젝트 원본핸들을 저장할 복사본 핸들주소
DWORD dwDesiredAccess, // 복사본 사용권한, 일반적으로 NULL
BOOL bInheritHandle, // 복사본 상속성 여부
DWORD dwOptions // 핸들 플래그 값의 설정 옵션
};
이 함수는 자신의 오브젝트 핸들 테이블을 상대편 프로세스의 핸들 테이블에 복사하여 준다. 상대편 프로세스가 핸들을 알고 있다면 동일한 커널 오브젝트를 사용할 수 있다.
A와 B라는 프로세스가 있다고 가정할 때, A프로세스에서 생성한 커널 오브젝트를 B에서 사용하려고 한다. A에서는 이벤트 오브젝트와 쓰레드 오브젝트를 사용하고 있다. 이 상황에서 DuplicateHandle()을 호출하면 A프로세스에 있는 핸들 테이블이 B프로세스로 복사가 된다. 즉, B프로세스의 핸들 테이블 안에는 A프로세스에서 생성된 커널 오브젝트에 대한 정보가 포함되게 된다. 이때 PostMessage 또는 SendMessage 등의 메시지 전달 함수로 [IPC] B프로세스에게 핸들을 넘겨준다. B 프로세스가 넘겨받은 핸들을 사용하면, 자신의 핸들 테이블에 저장된 핸들을 통해서 A프로세스가 생성한 커널 오브젝트를 사용할 수 있게 된다.
//--------------------------------
HANDLE hMutex = CreateMutex(NULL, FALSE, NULL);
// 이미 생성되어 있는 B의 프로세스 커널 오브젝트를 열고 핸들값을 얻어온다.
// 마지막 인자 dwProcessID_B는 이미 알고 있는 값이라고 가정
HANDLE hProcess_B=OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwProcessId_B);
HANDLE hMutex_B;
// 자신의 핸들 테이블을 B프로세스에게 복사하여 준다.
DuplicateHandle(GetCurrentProcess(),hMutex,hProcess_B,&hMutex_B,0,FALSE,DUPLICATE_SAME_ACCESS);
// hMutex_B를 IPC를 이용하여 B프로세스에게 넘겨준다.
SendMessage(..hMutex_B..);
// 사용이 끝났으면 닫는다.
CloseHandle(hMutex_B);
CloseHandle(hMutex);
//----------------------------------
GetCurrentProcess(): 현재의 프로세스 핸들을 리턴
OpenProcess(): 이미 생성되어 있는 프로세스의 핸들을 리턴
이를 이용하여 DuplicateHandle()을 호출하면 두 프로세스가 하나의 커널 오브젝트를 동시에 사용할 수 있다. 핸들 테이블이 복사된 이후 구체적인 핸들 값만 전달해 주면 된다.
출처: Visual C++.Net Programming Bible (삼양미디어)
|
'커널, 드라이버' 카테고리의 다른 글
디바이스 드라이버에서 3가지 버퍼 전달 방식 (0) | 2009.11.01 |
---|---|
운영체제의 메모리 관리 (1) | 2009.10.31 |
프로그래밍단에서 커널단 접근 및 프로세스 숨김 (0) | 2009.06.24 |
여러가지 훜 기법들과 탐지법들.... (2) | 2008.09.03 |
장치 드라이버 기초와 프로그래밍 Study (1) | 2008.08.01 |
SCM(services.exe) (0) | 2008.07.28 |