1. Windows Object와 Handle이란?
Windows 운영체제 환경에서 실행되는 모든 응용 프로그램들은 시스템 자원에 대한 직접적인 접근이 불가능하다. 그렇기 때문에 Windows 환경에서 실행되는 응용 프로그램에서 시스템 자원(Object)에 접근하기 위해서는 Windows 운영체제에서 제공하는 핸들(Handle)을 사용하여 시스템의 자원에 접근해야 한다. 많이 악성코드들이 응용프로그램을 삭제, 종료 하거나 특정코드에 대한 삽입이 가능한 이유가 오브젝트 핸들을 사용하기 때문이다.
2. Object Pointer Table
Object 들은 응용프로그램 내에서 관리되지 않고 Windows 운영체제가 관리한다. Windows 운영체제는 시스템 내부적으로 오브젝트에 직접 접근할수 있는 테이블 만들어 관리하며 테이블에 존재하는 인덱스 값이 오브젝트에 접근할수 있는 핸들값을 의미한다. 핸들 값은 32Bit Pointer 이거나 정수 형태의 자료형 이며 이러한 핸들 값들은 오브젝트의 종류에 따라 관리되어 지기 때문에 절대로 중복되지 않는 구조이다.
<그림1. Object Pointer Table>
3. Object의 종류
Object는 User Object, GDI Object, Kernel Object 로 분류된다. User Object는 Windows 창, 커서 등을 의미하여 Windows 운영체제에 의해서 전역적으로 관리되는 오브젝트 이다. 응용 프로그램이 User Object를 생성하고 정상적인 핸들값을 반환할 경우 해당값은 시스템 전체에서 유일한 값을 의미한다. FindWindow() 함수로 오브젝트 핸들을 얻어온후 SendMessage(), PostMessage() 함수로 메시지를 전송하는 방식이 User Object를 사용하는 예이다.
두 번째로 GDI Object는 Windows 환경에서 윈도우를 화면에 표현하기 위해서 사용되는 오브젝트로 Device Context, 펜, 브러쉬, 폰트 비트맵 등이 여기에 해당된다. GDI Object의 경우는 Object Pointer Table 뿐만 아니라 GDI Object 자체도 응용 프로그램 내에서 소유하고 관리되므로 프로세스에 지역적이다. Windows 운영체제는 단지 Win32API 를 통해서 GDI 오브젝트를 생성할 뿐이지 관리하고 소유하지는 않는다. 즉 GDI 오브젝트는 어떠한 방법으로도 다른 응용프로그램에 속해있는 GDI 오브젝트를 접근에서 제어할 수 없다.
마지막으로 프로세스 내부에 존재하는 Kernel Object는 프로세스, 스레드, 파일, 동기화 등을 의미하는 오브젝트로 다른 오브젝트 들과는 달리 프로세스에 한정적인 특징을 가지고 있다. Kernel Object 자체는 User Object 처럼 Windows 운영체제가 소유하지만, Kernel Object의 Object Pointer Table은 각각의 프로세스 내에서 소유하고 관리한다. 그렇기 때문에 Kernel Object는 서로다른 응용프로그램 사이에서 공유되어 사용하고 제어할 수 있다.
4. Kernel Object
Windows 운영체제에서 사용하는 3가지 오브젝트 중 보안적인 측면에서 반드시 알고 넘어 가야할 중요한 개념이 Kernel Object 이다. Kernel Object는 다른 프로세스와 공유가 가능하기 때문에 해당 기능을 악용하게 되면 정상적인 응용 프로그램들을 제어하는 많은 악성코드들이 발생할 가능성이 있다. Kernel Object의 세부적인 종류를 살펴보면 액세스 토큰 오브젝트(Access Token object), Event (이벤트 오브젝트), File (파일 오브젝트), File mapping (파일 매핑 오브젝트), Job (잡 오브젝트), Mailslot (메일슬롯 오브젝트), Mutex (뮤텍스 오브젝트), Pipe (파이프 오브젝트), Waitable timer (대기 타이머 오브젝트), Access Token (액세스 토큰 오브젝트) 등이 있다. Kernel Object가 할당된 시스템의 메모리 블록은 Kernel에 의해서만 접근이 가능한 구조체로 구성되어 있으며 Kernel Object의 세부적인 정보들이 저장되어 있다. 해당 영역은 Windows 운영체제가 소유하고 있으며 응용프로그램이 특정함수를 통해 Kernel Object를 생성한후 종료되었다 하더라도 생성된 Kernel Object가 프로세스와 함께 사라지지는 않는다. 대부분의 경우는 프로세스와 함께 사라질수 있겠지만 다른 응용프로그램이 동일한 커널 오브젝트를 Open하여 사용하고 있을 경우에는 해당 프로세스가 종료되는 순간까지 Kernel Object는 삭제되지 않고 남아있게 되는것이다. 즉 Kernel Object를 생성하고 종료하지 않을 경우 자신을 생성했던 프로세스보다 더 오랫동안 메모리에서 삭제되지 않고 남아 있거나 메모리 누수가 발생할 가능성이 있다.
5. Kernel Object의 보안
Kernel Object의 보안을 구체적으로 설명하기 위해서 아래와 같이 Windows 운영체제에서 제공하는 Kernel Object관련 함수들을 참고하여야 한다.
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes,
BOOL bManualReset,
BOOL bInitialState,
LPCTSTR lpName
);
Kernel Object 중 하나인 Event 객체를 생성하는 함수를 예로들면 SECURITY_ ATTRIBUTES 구조체의 포인터를 인자로 받는 첫번째 매개변수가 존재한다. 해당 값은 Kernel Object 를 생성하는 모든 함수들에 존재하는 설정 값으로 아래와 같이 Windows의 보안식별자(SecurityDescriptor) 에 대한 정보가 담겨져 있다.
typedef struct _SECURITY_ATTRIBUTES
{
DWORD nLength;
LPVOID lpSecurityDescriptor;
BOOL bInheritHandle;
} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES;
어떤 프로세스가 Kernel Object 를 소유하고 있는지, 어떤 사용자와 그룹들이 Kernel Object에 대한 접근권한이 있는지 여부를 판단하기 위해 보안식별자 값을 전달받는다. 대부분의 개발자들은 Kernel Object 생성시 해당 매개변수에 NULL을 넣는다. 해당 매개변수에 NULL을 넣게되면 현재 로그인한 계정의 권한에 해당하는 프로세스 보안토큰 설정값을 보안 디스크립터로 사용하게 된다. Kernel Object 생성시 Kernel Obejct에 대한 접근을 제한하고자 한다면 보안 식별자를 메모리에 블록을 할당하고 접근제어 리스트(ACL)를 가공하여 Kernel Obejct 생성함수의 매개변수에 넣어주어야 한다. 위와 같이 보안식별자 값을 전달하여 생성한 Kernel Object를(이미 생성된) 특정 프로세스가 접근하고자 한다면 아래와 같이 Kernel Object를 오픈하는 함수를 통해 접근해야 한다.
HANDLE OpenEvent(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTR lpName
);
Kernel Object 를 오픈하는 함수들도 접근 권한을 설정할수 있는 dwDesiredAccess 매개변수가 존재한다. 만약 Win32 API에서 정의된 FILE_MAP_READ 값을 전달할 경우 해당 Kernel Obejct에 대한 읽기 동작만 수행한다. OpenEvent() 함수는 유효한 핸들값을 반환하기 이전에 먼저 보안권한을 확인하고, Open한 프로세스가 해당 Kernel Object에 접근할수 있는 권한이라면 OpenEvent() 함수는 유효한 핸들값을 반환하고, 그렇지 않을 경우에는 NULL을 반환하고 GetLastError() 호출시 5(ERROR_ACCESS_DENIED)가 반환되므로 보안 설정관련 매개변수 값에 유의하여 생각을 해야한다.
6. 결론
이번 보안 컬럼의 주제였던 “Windows Obejct” 관련 내용들은 개발적인 지식이 없는 상태에서 모든 내용들을 이해하는데 어려움이 있을 것이다. 그러나 이번 컬럼의 내용들을 개발적인 부분에 초점을 맞쳐서 이해하기 보다는 Windows 운영체제가 시스템의 여러 가지 자원들을 관리하기 위해 Object Handle을 사용하고 있고, Winodws의 Kernel Object에 대한 보안적인 관리가 이루어 지지 않으면 많은 악성코드들이 발생할 가능성이 있다는 부분에 대해서 이해하는 것이 무엇보다도 중요할 것이다.
7. 참고문헌
- 제프리 리처의 Windows Via C/C++ - 원리에서 활용까지 윈도우 MFC 프로그래밍
'C & C++ > C & C++' 카테고리의 다른 글
하위 경로까지의 모든 파일 목록 얻기 (0) | 2011.10.26 |
---|---|
프로세스 찾아 죽이기 (0) | 2011.10.26 |
CFileFind 를 이용하여 디렉토리 안의 파일 찾기 (0) | 2011.10.25 |
파일에서 버전 정보 얻어오기 (0) | 2011.10.25 |
유니코드 <--> 멀티 바이트 변환 (0) | 2011.10.25 |
댓글