PROCESS & THREAD
프로세스
프로세스는 실핼파일(.exe)이 메모리에 로드 된 블록(인스턴스)라고 정의 내릴 수 있습니다.
프로세스는 반드시 하나 이상의 쓰레드가 있습니다. 즉 프로세스 혼자서는 아무런 일도 할 수 없습니다.
컴퓨터의 작업 관리자를 보면 Csrss.exe라는 프로세스가 있습니다.
Csrss가 프로세스를 순회하면서 프로세스의 쓰레드를 관리하고, 커널에게 쓰레드의 실행을 알립니다.
커널 모드에서 프로세스들을 EPROCESS(Executive Process)블록 구조체를 리스트형태로 관리하고 있습니다. EPROCESS 구조체는 프로세의 ID, 다음 EPROCESS구조체의 주소를 저장하는 포인터, 핸들 테이블, 쓰레드 리스트의 포인터 등의 정보를 가지고 있습니다.
프로세스의 생성과정
그림 출처 : Windows Internals 4th
프로세스는 위의 그림 처럼 동작합니다.
1. 처음 사용자에 의해 Exe파일을 실행을 합니다.
시스템은 CreateProcess()함수로 섹션 오브젝트를 생성 합니다.
2. CreateProcess()로 생성된 섹션 오브젝트와 생성된 주소 공간과 매핑을 시작합니다. 그 다음 시스템은 EPROCESS 오브젝트를 생성 합니다.
3. 첫 쓰레드와 쓰레드의 스택과 컨텍스트를 생성합니다.
**컨텍스트(Context) : CPU가 프로세스의 실행에 필요한 레지스터 데이터.
4. 윈도우 서브시스템(Csrss)에 프로세스가 새로 생성됨을 알립니다.
윈도우 서브 시스템은 다음과 같은 정보들을 가지고 프로세스와 쓰레드를 설치 합니다
윈도우 서브시스템은 새 프로세스의 정보를 가지고 프로세스 블록을 만들
고 관리하는 프로세스리스트에 추가 합니다.
** 윈도우 서브 시스템은 유저모드 에서 실행 되는 프로세스 나 이벤트 등이 사용하는 함수등을 커널에 전달하는 역할을 합니다.
윈도우 서브 시스템의 정보는 HKEY_LOCAL_MACHINE\CurrentControlSet\Control\Session Manager\SubSystems 레지스트리 값에서 확인할 수 있습니다.
5. 프로세스의 처음 쓰레드를 실행합니다.
6. 프로세스의 처음 쓰레드가 실행이 될 때 커널 모드에서는 쓰레드에 대한 컨텍스트 구조체를 초기화하고 프로세스의 디버그 정보, IRQL(Interupt Request Level) 등을 설정합니다.
7. 커널 모드에서 쓰레드 초기화 작업이 완료되면 유저모드 쓰레드에서 프로세스의 시작점을 실행하고 프로그램의 실행이 됩니다.
간단하게 프로세스의 생성과 관리 과정에 대해 알아 보았습니다.
더 자세한 내용은 Windows Internals 4th Edition 을 보시길 바랍니다.
이제 간단하게 네이티브 API(커널에서 사용하는 함수)를 이용해 서브 시스템이 관리하고 있는 프로세스와 쓰레드를 나영하는 프로그램을 만들어 보겠습니다.
이 코드는 정덕영님의 책 “Windows 구조와 원리”에 있는 코드입니다.
ZwQuerySystemInformation 네이티브 API를 이용해서 시스템 프로세스 정보를 얻어옵니다. 두 번째 함수 임자로 들어가는 SYSTEM_PROCESS 구조체를 이용해서 프로세스와 쓰레드의 리스트를 얻어와서 프로세스와 쓰레드의 주소를 얻어올 수 있습니다.
typedef struct _SYSTEM_PROCESSES { ULONG NextEntryDelta;/*다음 SYSTEMPROCESS의 주소의 오프셋*/ ULONG Threadcount; /*쓰레드 갯수*/ ULONG Reserved1[6]; /*시스템에 의해 사용되는 공간*/ LARGE_INTEGER CreateTime; /*생성된 시간*/ LARGE_INTEGER UserTime; /*유저 모드에서 실행된 총 시간 100나노 세컨드*/ LARGE_INTEGER KernelTime; /*커널 모드에서 실행된 총 시간*/ UNICODE_STRING ProcessName; /*프로세스이름*/ KPRIORITY BasePriority; /*권한 정보*/ ULONG ProcessId; /*프로세스 ID*/ ULONG InheritedFromProcessId; /*부모 프로세스 ID*/ ULONG HandleCount; /*핸들 개수*/ ULONG Reserved2[2]; /*시스템에 의해 사용되는 공간*/ VM_COUNTERS VmCounters; /*프로세스가 사용하는 가상메모리 상태를 저장 */ IO_COUNTERS IoCounters; /*프로세스가 실행한 IO카운터*/ SYSTEM_THREADS Threads[1]; /*프로세스의 쓰레드 리스트. SYSTEM_THREAD 구조체로 관리*/ } SYSTEM_PROCESSES, *PSYSTEM_PROCESSES; /**/ typedef struct _VM_COUNTERS { ULONG PeakVirtualSize; ULONG VirtualSize; ULONG PageFaultCount; ULONG PeakWorkingSetSize; ULONG WorkingSetSize; ULONG QuotaPeakPagedPoolUsage; ULONG QuotaPagedPoolUsage; ULONG QuotaPeakNonPagedPoolUsage; ULONG QuotaNonPagedPoolUsage; ULONG PagefileUsage; ULONG PeakPagefileUsage; } VM_COUNTERS, *PVM_COUNTERS; typedef struct _IO_COUNTERS { ULONGLONG ReadOperationCount; ULONGLONG WriteOperationCount; ULONGLONG OtherOperationCount; ULONGLONG ReadTransferCount; ULONGLONG WriteTransferCount; ULONGLONG OtherTransferCount; } IO_COUNTERS; typedef struct _SYSTEM_THREADS { LARGE_INTEGER KernelTime; LARGE_INTEGER UserTime; LARGE_INTEGER CreateTime; ULONG WaitTime; PVOID StartAddress; CLIENT_ID ClientId; KPRIORITY Priority; KPRIORITY BasePriority; ULONG ContextSwitchCount; THREAD_STATE State; KWAIT_REASON WaitReason; } SYSTEM_THREADS, *PSYSTEM_THREADS; |
자세한 정보는 Native API문서를 참조 하시길 바랍니다.
다음의 코드는 서브 시스템이 관리하는 프로세스리스트를 얻어와서 프로세스 정보와 프로세스에서 돌아가는 쓰레드들을 출력하는 코드입니다.
#include <windows.h> #include <iostream> #include "ntdll.h" using namespace std; void ListProcessNThread(); int main() { ListProcessNThread(); return 0; } void ListProcessNThread() { ULONG dwAllocedSize, dwNeeded; PSYSTEM_PROCESSES pProcess; NTSTATUS Status; int nThreadCount = 0; dwAllocedSize = 0x1000; while(1) { pProcess = (PSYSTEM_PROCESSES)VirtualAlloc(NULL, dwAllocedSize, MEM_COMMIT, PAGE_READWRITE); Status = ZwQuerySystemInformation(SystemProcessesAndThreadsInformation, pProcess, dwAllocedSize, &dwNeeded); if(Status == STATUS_INFO_LENGTH_MISMATCH) { VirtualFree(pProcess, dwAllocedSize, MEM_DECOMMIT); if(dwNeeded > dwAllocedSize) { dwAllocedSize = dwNeeded; } else { dwAllocedSize += 0x500; } } else if(NT_SUCCESS(Status)) { break; } else { break; } } /*프로세스리스트를 순회하면서 프로세스ID와 이름을 출력합니다*/ while(pProcess->NextEntryDelta != 0) { pProcess = (PSYSTEM_PROCESSES)((char*)pProcess + pProcess->NextEntryDelta); cout<<"PID : "<<pProcess->ProcessId<< " "<<pProcess->ProcessName.Length/2<< " "<<pProcess->ProcessName.Buffer; /*해당 프로세스에서 생성된 쓰레드 리스트를 출력합니다.*/ for(ULONG nIndex = 0; nIndex<pProcess->Threadcount; nIndex++) { cout<<" "<<pProcess->Threads[nIndex].ClientId.UniqueThread<< " "<<"Start Address : "<< pProcess->Threads[nIndex].StartAddress<<endl; } } } |
**네이티브 API를 처음 접하시는 분들은 세팅 하기 어려움이 있어서 프로젝트 파일을 첨부합니다.
참고 서적 : Windows Interanals 4th Edition
Windows 구조와 원리
Windows Via C/C++
댓글 없음:
댓글 쓰기