Object Categories
A, B라는 별도의 프로세스가 존재할 시
- A의 핸들을 B에서 받아서 사이즈 조정이 가능할까? -> 가능(윈도우 핸들은 public to all processes)
- A에서 만든 GUI 팬을 B에서 그림그리기가 가능할까? -> 불가능(private to a process)
- A에서 만든 파일을 B에서 파일핸들을 통해 접근이 가능할까 -> 상황에 따라 다름(process specific) 어떻게 핸들을 전달했느냐에 따라 다르다
세 가지 경우의 수가 있는데 어떤경우 인지 자세히 살펴보자.
- 모두
CreateXXX
을 통해 만든다 -
CreateXXX
로 생성할 경우 Window Object가 생성되며 세 가지 종류로 나뉜다 - User Object
- 윈도우와 관련된 Object (윈도우 창, 메뉴 창 등)
- public to all processes
- 파괴 함수 : DestroyXXX()
- 관련 DLL : User32.dll
- GDI Object
- 그래픽 관련 Object (폰트, 프러쉬 등)
- private to a process
- 파괴 함수 : DeleteXXX()
- 관련 DLL : Gdi32.dll
- Kernel Object
- 파일, 메모리, 프로세스등 UI 이외의 작업에 관련된 Object
- process specific
- 파괴 함수 : CloseHandleXXX()
- 관련 DLL : Kernel.dll
- 참고사이트
Kernel Object
커널(Kernel Object)는 아래와 같이 구성되어있다
보안 속성(Security Attribute)
이름
참조 계수(Reference Counting)
SIGNAL
WAIT LIST
...(기타)
대표적인 예로 CreateWaitableTimerEx
를 보자면
이게 User, GDI, Kernel Object중 뭐인지 헷갈릴수 있는데 보안설정을 설정함을 봐선 Kernel Object임을 안다
HANDLE CreateWaitableTimerExW(
LPSECURITY_ATTRIBUTES lpTimerAttributes, // 보안속성을 설정함을 볼수있음
LPCWSTR lpTimerName,
DWORD dwFlags,
DWORD dwDesiredAccess
);
자세한 설명은 이후에 진행된다.
우선은 커널오브젝트 핸들을 다른 프로세스에 넘길수 있을까?
#include <stdio.h>
#include <Windows.h>
#include <tchar.h>
int _tmain()
{
HANDLE hFile = CreateFile(_T("a.txt"), /* ... */);
_tprintf(_T("FILE HANDLE : %x\n"), hFile);
// 다른 프로세스에 파일 핸들 전달?
HWND hwnd = FindWindow(0, _T("Friend"));
SendMessage(hwnd, WM_APP+100, 0, (LPARAM)hFile);
}
일단 다른 프로세스에서 파일의 핸들을 사용하지 못한다.
넘겨준 핸들은 실제 물리적 주소값을 의미하는 것이 아니라 Kernel내부적으로 관리하는 Object Table내의 index이다.
해당 index에서 파일이 접근 가능한지 쓸 수 있는지 등을 OS에서 알려주고 파일에 접근하게 된다.
따라서 파일의 핸들(단순 index)만으론 파일에 접근이 불가능 하다
- 좀 더 자세히 설명하자면 프로세스마다 PKO(Process Kernel Object)를 관리한다.
CreateFile
을 할경우 File Kernel Object가 생성되며 내부에 참조계수를 1 올린다- File Kernel Object를 생성한 프로세스는 PKO에서 생성된 File Kernel Object를 등록하고 물리주소를 기록한다
-
따라서 그냥 HANDLE을 전달한다면 전달받은 프로세스는 자신의 PKO에서 HANDLE을 찾아보고 없다면 사용불가!
- 그래도 하고싶다면? ->
DuplicateHandle()
과 같은 Win32에서 제공하는 API를 사용해야 한다. DuplicateHandle()
는 PKO 주소자체를 복사해준다-
그렇게 되는순간 File Kernel Object의 참조계수는 1증가한다
- 참고) 참조 계수로 관리되는 Kernel Object는 프로세스의 생명주기와 일치하지 않기에 OS가 소유한다고 한다
- 참고)
CloseHandle()
을 통해서 참조 계수의 카운트를 1줄이게 된다
#include <stdio.h>
#include <Windows.h>
#include <tchar.h>
int _tmain()
{
HANDLE hFile = CreateFile(_T("a.txt"), /* ... */);
_tprintf(_T("FILE HANDLE : %x\n"), hFile);
// DuplicateHandle 써보기
HWND hwnd = FindWindow(0, _T("Friend"));
// FILE의 핸들을 다른 프로세스에 복사하기
// 1. 프로세스 ID구하기
DWORD pid;
DWORD tid = GetWindowThreadProcessId(hwnd, &pid);
// 2. 프로세스 ID를 가지고 핸들 얻기
HANDLE hProcess = OpenProcess(PROCESS_DUP_HANDLE, 0, pid);
// 3. DuplicateHandle() 함수로 핸들 복사
HANDLE handle; // 상대방 테이블의 핸들
DuplicateHandle(GetCurrentProcess(), hFile,
hProcess, &handle,
DUPLICATE_SAME_ACCESS, 0, 0);
SendMessage(hwnd, WM_APP + 100, 0, (LPARAM)handle);
}
LRESULT __stdcall WndProc(HWND /* ... */)
{
switch(message)
{
case WM_APP + 100;
{
HANDLE hFile = (HANDLE)lParam;
DWORD len;
char data[256] = "hello";
BOOL b = WriteFile(hFile, data, 256, &len);
CloseHandle(hFile);
}
}
}
PKO를 보고싶다면??
ProcessExplorer.exe를 이용하자(주의할 점은 관리자 모드로 실행)
int _tmain()
{
getchar();
HANDLE h1 = CreateFile(_T("a.txt"),
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, 0);
getchar();
HANDLE h2 = CreateEvent(0, 0, 0, _T("MyEvent"));
getchar();
HANDLE h3 = CreateMutex(0, 0, _T("MyMutex"));
getchar(); CloseHandle(h3);
getchar(); CloseHandle(h2);
getchar(); CloseHandle(h1);
}