본문 바로가기

High Level Technique/Window System

핸들 테이블과 오브젝트 핸들의 상속

핸들 테이블과 오브젝트 핸들의 상속


지금까지 핸들 값을 받아오고 종료하는 것에 대해서 공부했습니다. Mailslot을 만들 때 CreateMailslot()을 통해서 메일 슬롯을 만들었는데 이 메일 슬롯 또한 커널 오브젝트가 존재합니다.

앞서 지금까지 설명한 내용을 바탕으로 핸들이라는 것이 해당 커널 오브젝트를 지정하고자 정수 값으로 나타낸 것이라고 했습니다.


그렇다면 생성된 Mailslot의 주소가 0x1000이라고 하고 핸들 값을 123이라고 하면 해당 핸들을 가지고 CloseHandle()을 통해서 소멸시킬 수 있었습니다.

그러면 단순히 Handle 값만 알고 어떻게 0x1000에 있는 커널 오브젝트를 찾아가 소멸시킬 수 있었을까요?


이러한 이유는 핸들 테이블이라는 것이 존재하기 때문입니다.


핸들 테이블에 해당 핸들 값과 커널 오브젝트의 값을 저장해두고 사용하기 때문에 핸들 값 만으로 해당 커널 오브젝트를 알 수 있는 것입니다.




핸들의 상속


부모 프로세스와 자식 프로세스간에 핸들 테이블을 상속할 수 있습니다. 이를 지정하는 건 CreateProcess()의 5번째 인자가 결정합니다.

이 값을 TRUE로 할 경우 상속이되고, FALSE로 할 경우 상속이 되지 않습니다.


그렇다면 커널 오브젝트를 관리하는데 Usage Count를 이용한다고 했는데, 해당 커널 오브젝트가 자기 자신을 누가 참조하고 있는지 판단하기 위해서는 어떤 조건이 필요할까요?


프로세스가 커널 오브젝트를 참조할 경우 그 수 만큼 Usage Count가 증가한다고 했습니다. 핸들을 얻은 프로세스가 커널 오브젝트를 참조하는 프로세스가 된다고 했죠. 

따라서 핸들 테이블이 존재한다고 했는데 이 핸들 테이블이 갱신되어 추가가 될 경우 커널 오브젝트에서 Usage Count가 증가하게 되는 것입니다.



CreateMailslot의 인자 중 LPSECURITY_ATTRIBUTES lpSecurityAttributes 멤버가 존재하는데 이 멤버는 구조체로 이루어져 있습니다.

이 값을 적절히 초기화 하여 사용하라는 것인데 NULL일 경우 자식 프로세스에 상속되지 않습니다.


LPSECURITY_ATTRIBUTES의 구조체를 보면 세번째 멤버 변수가 BOOL bInheritHandle로 되어있습니다. 이 값을 TRUE로 설정하면 상속이 되는거죠.



Receiver.cpp 동일



MailSender2_1.cpp


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
 
#define SLOT_NAME _T("\\\\.\\mailslot\\mailbox")
 
int main(void)
{
    HANDLE hMailSlot;
    TCHAR message[50];
    DWORD bytesWritten;
 
    SECURITY_ATTRIBUTES sa;
    sa.nLength = sizeof(sa);
    sa.lpSecurityDescriptor = NULL;
    sa.bInheritHandle = TRUE; //상속
 
    hMailSlot = CreateFile(SLOT_NAME, GENERIC_WRITE, FILE_SHARE_READ, &sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
 
    printf("Ingeritable Handle : %d \n", hMailSlot);
 
    FILE* file = fopen("InheritableHandle.txt""wt");
    fprintf(file, "%d", hMailSlot);
    fclose(file);
 
    STARTUPINFO si = { 0, };
    PROCESS_INFORMATION pi;
    si.cb = sizeof(si);
 
    TCHAR command[] = _T("MailSender2_2.exe");
 
    CreateProcess(NULL, command, NULLNULL, TRUE, CREATE_NEW_CONSOLE, NULLNULL&si, &pi);
 
    while (1)
    {
        printf("Sender1 CMD > ");
        _fgetts(message, sizeof(message) / sizeof(TCHAR), stdin);
 
        if (!WriteFile(hMailSlot, message, _tcslen(message) * sizeof(TCHAR), &bytesWritten, NULL))
        {
            printf("Unable to write!\n");
            CloseHandle(hMailSlot);
            return 1;
        }
 
        if (!_tcscmp(message, _T("exit")))
        {
            printf("Good Bye!\n");
            break;
        }
    }
 
    CloseHandle(hMailSlot);
 
    return 0;
}
cs


13라인을 보면 SECURITY_ATTRIBUTES 구조체를 선언해서 각 멤버변수에 값을 넣었습니다. 중요하게 볼 부분은 16라인인데 상속여부를 TRUE로 설정하여 상속을 하도록 설정했습니다.


32라인에서 CreateProcess를 하면서 5번째 인자가 TRUE로 설정되어있습니다. 이것이 자식 프로세스에게 부모 프로세스의 핸들 테이블 정보를 상속하겠다는 말입니다.






MailSender2_2.cpp


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
 
int main(void)
{
    HANDLE hMailSlot;
    TCHAR message[50];
    DWORD bytesWritten;
 
    // Get Handle
    FILE* file = fopen("InheritableHandle.txt""rt");
    fscanf(file, "%d"&hMailSlot);
    fclose(file);
    printf("Inheritable Handle : %d \n", hMailSlot);
 
    while (1)
    {
        printf("Sender2 CMD > ");
        _fgetts(message, sizeof(message) / sizeof(TCHAR), stdin);
        
        if (!WriteFile(hMailSlot, message, _tcslen(message) * sizeof(TCHAR), &bytesWritten, NULL))
        {
            printf("Unable to write! \n");
            _gettchar();
            CloseHandle(hMailSlot);
            return 1;
        }
 
        if (!_tcscmp(message, _T("Exit")))
        {
            printf("Good Bye!\n");
            break;
        }
    }
 
    CloseHandle(hMailSlot);
 
    return 0;
}
cs



MailSender2_1에서 MailSender2_2를 자식프로세스로 실행하고 있습니다. 앞서 설명에서 핸들 테이블을 상속한다고 했는데 MailSender2_2에서 부모 프로세스인 MailSender2_1의 핸들 테이블을 상속 받는 것입니다.


MailSender2_2에서 중요하게 볼 부문은 12라인입니다.

해당 코드를 살펴보면 FILE 포인터를 이용해서 파일을 열고 해당 MailSlot의 핸들 값을 받아 저장하고 있습니다.


이러한 이유는 부모 프로세스의 핸들 테이블과 자식 프로세스의 핸들 테이블은 서로 다른 핸들 테이블이기 때문에 부모 프로세스의 핸들 테이블을 상속하면서 해당 테이블의 핸들 정보를 파일로 저장한 뒤 해당 파일에서 핸들값을 가져오게 됩니다.