윈도우즈 시스템 프로그래밍이라는 책과 해당 책의 저자이신 윤성우님의 강의를 통해 공부한 내용을 정리하는 글입니다.
프로세스의 핸들 테이블
⦁ 프로세스로 전달되는 핸들 정보가 어떻게 전달 및 저장되는지 보여준다.
⦁ 핸들 테이블은 핸들 정보를 저장하고 있는 테이블로서 프로세스별로 독립적이다.
⦁ CreateProcess 함수나 CreateMailslot과 같은 함수 호출을 통해서 리소스 생성을 요구한 결과로 핸들 정보를 얻게 될 경우, 프로세스 자신에게 속해 있는 핸들 테이블에 해당 정보가 등록된다.
핸들의 상속
⦁ 핸들의 상속을 위한 전달인자
CreateProcess의 선언
BOOL CreateProcess (
LPCTSTR lpapplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
// If the function fails, the return value is zero.
이 함수의 다섯 번째 전달인자 bInheritHandle은 자식 프로세스에게 핸들 테이블을 상속해 줄 것인지 결정하는 요소이다. TRUE를 인자로 전달할 경우 상속되고, FALSE일 경우는 이루어지지 않는다.
⦁ 상속이 되기 위한 핸들의 조건
핸들의 상속 여부는 리소스가 생성되는 순간에 프로그래머에 의해 결정된다.
CreateMailslot의 선언
HANDLE CreateMailslot (
LPCTSTR lpName,
DWORD nMaxMessageSize,
DWORD lReadTimeout,
LPSECURITY_ATTRIBUTES lpSecurityAttributes
);
//If the function fails, the return value is INVALID_HANDLE_VALUE
CreateMailslot의 경우, 마지막 전달인자는 구조체 SECURITY_ATTRIBUTES의 포인터로 정의되어 있다.
즉, SECURITY_ATTRIBUTES 구조체 변수를 적절히 초기화 해서 주소값을 인자로 전달해야 한다.
NULL이 전달될 경우 메일슬롯의 핸들은 자식 프로세스에게 전달되지 않는다.
SECRUTIY_ATTRIBUTES 구조체의 선언
typedef struct _SECURITY_ATTRIBUTES {
DWORD nLength;
LPVOID lpSecurityDescriptor;
BOOL bInheritHandle;
} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES;
첫 번째 멤버는 구조체 변수 크기를 바이트 단위로 설정해 준다.
세 번째 멤버는 상속 여부를 결정지으므로 필요하면 TRUE로 설정해야 한다.
예제
⦁ MailSender2_1.cpp
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#define SLOT_NAME _T("\\\\.\\mailslot\\mailbox")
int _tmain(int argc, TCHAR* argv[])
{
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);
if (hMailSlot == INVALID_HANDLE_VALUE)
{
_fputts(_T("Unable to create mailslot!\n"), stdout);
return 1;
}
_tprintf(_T("Inheritable Handle : %d \n"), hMailSlot);
FILE* file = _tfopen(_T("InheritableHandle.txt"), _T("wt"));
_ftprintf(file, _T("%d"), hMailSlot);
fclose(file);
STARTUPINFO si = { 0, };
PROCESS_INFORMATION pi;
si.cb = sizeof(si);
TCHAR command[] = _T("MailSender2_2.exe");
CreateProcess(NULL, command, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
while(1)
{
_fputts(_T("MY CMD>"), stdout);
_fgetts(message, sizeof(message) / sizeof(TCHAR), stdin);
if (!WriteFile(hMailSlot, message, _tcslen(message) * sizeof(TCHAR), &bytesWritten, NULL))
{
_fputts(_T("Unable to write!"), stdout);
CloseHandle(hMailSlot);
return 1;
}
if (!_tcscmp(message, _T("exit")))
{
_fputts(_T("Good Bye!"), stdout);
break;
}
}
CloseHandle(hMailSlot);
return 0;
}
자식 프로세스의 파일을 열어서 핸들 정보를 얻는 프로그램이다. 파일을 열어서 상속된 핸들값을 확인하고, 이를 이용하여 부모 프로세스와 마찬가지로 메일슬롯에 데이터를 전송하도록 구현했다.
⦁ MailSender2_2.cpp
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
int _tmain(int argc, TCHAR argv[])
{
HANDLE hMailSlot;
TCHAR message[50];
DWORD bytesWritten;
FILE* file = _tfopen(_T("InheritableHandle.txt"), _T("rt"));
_ftscanf(file, _T("%d"), &hMailSlot);
fclose(file);
_tprintf(_T("Inheritable Handle : %d \n"), hMailSlot);
while (1)
{
_fputts(_T("MY CMD>"), stdout);
_fgetts(message, sizeof(message) / sizeof(TCHAR), stdin);
if (!WriteFile(hMailSlot, message, _tcslen(message) * sizeof(TCHAR), &bytesWritten, NULL))
{
_fputts(_T("Unable to write!"), stdout);
_gettchar();
CloseHandle(hMailSlot);
return 1;
}
if (!_tcscmp(message, _T("exit")))
{
_fputts(_T("Good Bye!"), stdout);
break;
}
}
CloseHandle(hMailSlot);
return 0;
}
MailSender2_1.cpp의 자식 프로세스에 해당하는 코드이다.
Pseudo 핸들과 핸들의 중복(Duplicate)
⦁ GetCurrentProcess 함수를 통해 얻은 핸들은 가짜 핸들(Pseudo 핸들)이다. 핸들 테이블에 등록되어있지 않은 핸들이고, 현재 실행중인 프로세스를 참조하기 위한 용도로 정의한 상수가 반환되는 것이다. 그러므로 자식 프로세스로 상속되지 않는다.
⦁ 가짜 핸들은 CloseHandle로 전달되어도 아무 일이 일어나지 않는다.
⦁ DuplicateHandle 함수
BOOL DuplicateHandle (
HANDLE hSourceProcessHandle,
HANDLE hSourceHandle,
HANDLE hTargetProcessHandle,
LPHANDLE lpTargetHandle,
DWORD dwDesireAccess,
BOOL bInheritHandle,
DWORD dwOptions
);
// If the function fails, the return value is zero.
⦁ hSourceProcessHandle : 복제할 핸들을 소유하는 프로세스를 지정한다.
⦁ HSourceHandle : 복제할 핸들을 지정한다.
⦁ hTargetProcessHandle : 복제된 핸들을 소유할 프로세스를 지정한다.
⦁ lpTargetHandle : 복제된 핸들값을 저장할 변수의 주소를 지정한다.
⦁ dwDesireAccess : 복제된 핸들의 접근 권한 지정.
⦁ bInheritHandle : 복제된 핸들의 상속 여부를 지정한다.
⦁ dwOptions : DUPLICATE_SAME_ACCESS를 전달하면 원본 핸들과 동일한 접근권한 가짐. DUPLICATE_CLOSE_SOURCE가 오면 원본 핸들을 종료시킨다. 비트OR 연산으로 동시 전달 가능.
DuplicateHandle(
프로세스A 핸들,
256,
프로세스B 핸들,
&val,
.....
);
DuplicateHandle 함수가 위와 같이 사용될 경우, 프로세스 A에 존재하는 핸들 256의 정보를 프로세스 B의 핸들 테이블에 등록시키고, 등록된 핸들의 값은 변수 val에 저장하게 된다.
부모 프로세스의 핸들을 자식 프로세스에게 전달하기
⦁ DuplicateHandle.cpp
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
int _tmain(int argc, TCHAR* argv[])
{
HANDLE hProcess;
TCHAR cmdString[1024];
DuplicateHandle(GetCurrentProcess(), GetCurrentProcess(), GetCurrentProcess(), &hProcess, 0, TRUE, DUPLICATE_SAME_ACCESS);
_stprintf(cmdString, _T("%s %u"), _T("ChildProcess.exe"), (unsigned)hProcess);
STARTUPINFO si = { 0, };
PROCESS_INFORMATION pi = { 0, };
si.cb = sizeof(si);
BOOL isSuccess = CreateProcess(NULL, cmdString, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
if (isSuccess == FALSE)
{
_tprintf(_T("CreateProcess failed \n"));
return -1;
}
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
_tprintf(_T("[Parent Process]\n"));
_tprintf(_T("ooooooooooooooooooooooooooops! \n"));
return 0;
}
위 프로그램이 부모 프로세스가 된다.
⦁ DuplicateHandleChildProcess.cpp
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
int _tmain(int argc, TCHAR* argv[])
{
HANDLE hParent = (HANDLE)_ttoi(argv[1]);
DWORD isSuccess = WaitForSingleObject(hParent, INFINITE);
_tprintf(_T("[Child Process] \n"));
if (isSuccess == WAIT_FAILED)
{
_tprintf(_T("WAIT_FAILED returned!"));
Sleep(10000);
return -1;
}
else
{
_tprintf(_T("General Lee said, \"Dont't inform ") _T("the enemy my death\""));
Sleep(10000);
return 0;
}
}
위 프로그램이 자식 프로세스가 된다.
⦁ 실행결과
'윈도우즈 시스템 프로그래밍' 카테고리의 다른 글
윈도우즈 시스템 프로그래밍 8장 - 프로세스 환경변수 (0) | 2021.07.29 |
---|---|
윈도우즈 시스템 프로그래밍 8장 - 파이프 방식의 IPC (0) | 2021.07.29 |
윈도우즈 시스템 프로그래밍 7장 - Signaled vs Non-Signaled (0) | 2021.07.25 |
윈도우즈 시스템 프로그래밍 7장 - 프로세스간 통신(IPC) (0) | 2021.07.25 |
윈도우즈 시스템 프로그래밍 6장 - 커널 오브젝트와 Usage Count (0) | 2021.07.24 |