Windows에서 커널디버깅을 통해 프로세스와 쓰레드를 손으로 찾아가며 확인하는 방법입니다.
사용된 PC는 Windows 2003 서버버젼 입니다.
kd> dt -b nt!_EPROCESS
+0x000 Pcb : _KPROCESS
+0x000 Header : _DISPATCHER_HEADER
+0x010 ProfileListHead : _LIST_ENTRY
+0x000 Flink : Ptr32
+0x004 Blink : Ptr32
+0x018 DirectoryTableBase : Uint4B // CR3 레지스터가 저장되어 있다
+0x020 LdtDescriptor : _KGDTENTRY
....
+0x048 SwapListEntry : _SINGLE_LIST_ENTRY
+0x000 Next : Ptr32
+0x04c VdmTrapcHandler : Ptr32
+0x050 ThreadListHead : _LIST_ENTRY // 쓰레드 리스트의 헤드
+0x000 Flink : Ptr32
+0x004 Blink : Ptr32
+0x058 ProcessLock : Uint4B
+0x05c Affinity : Uint4B
+0x060 AutoAlignment : Pos 0, 1 Bit
+0x060 DisableBoost : Pos 1, 1 Bit
...
+0x090 RundownProtect : _EX_RUNDOWN_REF
+0x000 Count : Uint4B
+0x000 Ptr : Ptr32
+0x094 UniqueProcessId : Ptr32 // 프로세스 아이디
+0x098 ActiveProcessLinks : _LIST_ENTRY // 모든 프로세스 목록 관리 리스트
+0x000 Flink : Ptr32
+0x004 Blink : Ptr32
+0x0a0 QuotaUsage : Uint4B
...
+0x0cc DebugPort : Ptr32
+0x0d0 ExceptionPort : Ptr32
+0x0d4 ObjectTable : Ptr32 // 문제의 오브젝트 핸들테이블
+0x0d8 Token : _EX_FAST_REF
...
+0x14c Spare0 : Ptr32
+0x158 PageDirectoryPte : _HARDWARE_PTE
+0x158 Filler : Uint8B
+0x160 Session : Ptr32
+0x164 ImageFileName : UChar // 프로세스의 이름
+0x174 JobLinks : _LIST_ENTRY
+0x000 Flink : Ptr32
+0x004 Blink : Ptr32
+0x17c LockedPagesList : Ptr32
+0x180 ThreadListHead : _LIST_ENTRY
+0x000 Flink : Ptr32
+0x004 Blink : Ptr32
...
+0x1e8 Vm : _MMSUPPORT
+0x000 WorkingSetExpansionLinks : _LIST_ENTRY
+0x000 Flink : Ptr32
+0x004 Blink : Ptr32
+0x008 LastTrimTime : _LARGE_INTEGER
+0x000 LowPart : Uint4B
+0x004 HighPart : Int4B
+0x000 u : __unnamed
+0x000 LowPart : Uint4B
+0x004 HighPart : Int4B
+0x000 QuadPart : Int8B
+0x010 Flags : _MMSUPPORT_FLAGS
+0x014 PageFaultCount : Uint4B
...
+0x030 NextEstimationSlot : Uint4B
+0x034 NextAgingSlot : Uint4B
+0x038 EstimatedAvailable : Uint4B
+0x03c WorkingSetSize : Uint4B // 프로세스 워킹셋 사이즈
+0x040 WorkingSetMutex : _EX_PUSH_LOCK
+0x000 Locked : Pos 0, 1 Bit
+0x000 Waiting : Pos 1, 1 Bit
+0x000 Waking : Pos 2, 1 Bit
+0x000 MultipleShared : Pos 3, 1 Bit
+0x000 Shared : Pos 4, 28 Bits
+0x000 Value : Uint4B
+0x000 Ptr : Ptr32
+0x230 MmProcessLinks : _LIST_ENTRY
+0x000 Flink : Ptr32
+0x004 Blink : Ptr32
+0x238 ModifiedPageCount : Uint4B
+0x23c JobStatus : Uint4B
+0x240 Flags : Uint4B
+0x240 CreateReported : Pos 0, 1 Bit
...
+0x24a SubSystemMinorVersion : UChar
+0x24b SubSystemMajorVersion : UChar
+0x24a SubSystemVersion : Uint2B
+0x24c PriorityClass : UChar
+0x250 VadRoot : _MM_AVL_TABLE // 유저영역의 모든 메모리 설명
+0x270 Cookie : Uint4B
커널 레이어에서 EPROCESS 얻기
PEPROCESS PsGetCurrentProcess()
현재 프로세스의 EPROCESS을 얻어올수 있음
NTSTATUS PsLookupProcessByProcessId(IN ULONG ProcessID, OUT PVOID *EProcess )
다른 프로세스의 EPROCESS를 얻어올수 있음
프로세스를 구성하는 요소
각 프로세스의 독립적 메모리 공간의 사용은 Intel x86의 페이징 시스템을 그대로 사용함
위의 CR3 저장공간에 따라 실제 물리공간 결정 어드레스 오프셋은 물리매핑을 위한 각
페이지 디렉토리, 페이지 테이블 등의 오프셋
유저 메모리 영역 관리
kd> !process 85e3b8a8
PROCESS 85e3b8a8 SessionId: 0 Cid: 0aa8 Peb: 7ffdf000 ParentCid: 01b4
DirBase: 1e7f5000 ObjectTable: e217d008 HandleCount: 55.
Image: uedit32.exe
VadRoot 85331468 Vads 85 Clone 0 Private 740. Modified 2. Locked 0.
DeviceMap e206f720
Token e260f938
ElapsedTime 00:07:44.406
UserTime 00:00:00.000
KernelTime 00:00:00.000
QuotaPoolUsage[PagedPool] 39256
QuotaPoolUsage[NonPagedPool] 3592
Working Set Sizes (now,min,max) (2064, 50, 345) (8256KB, 200KB, 1380KB)
PeakWorkingSetSize 2064
VirtualSize 41 Mb
PeakVirtualSize 42 Mb
PageFaultCount 2983
MemoryPriority BACKGROUND
BasePriority 8
CommitCharge 1035
THREAD 85e3b638 Cid 0aa8.05f0 Teb: 7ffde000 Win32Thread: e126a4e0 WAIT: (Unknown) UserMode Non-Alertable
85334ff0 SynchronizationEvent
Not impersonating
DeviceMap e206f720
Owning Process 85e3b8a8 Image: uedit32.exe
Wait Start TickCount 227979 Ticks: 6558 (0:00:01:42.468)
Context Switch Count 1298 LargeStack
UserTime 00:00:00.0406
KernelTime 00:00:00.0281
Start Address kernel32!BaseProcessStartThunk (0x77e6b5c7)
Win32 Start Address 0x0050cec5
Stack Init f5664000 Current f5663bc4 Base f5664000 Limit f5661000 Call 0
Priority 10 BasePriority 8 PriorityDecrement 0
Kernel stack not resident.
ChildEBP RetAddr
f5663bdc 80820128 nt!KiSwapContext+0x25 (FPO: [Uses EBP] [0,0,4])
...
f5663d4c 7c82ed54 nt!KiFastCallEntry+0xf8 (FPO: [0,0] TrapFrame @ f5663d64)
0012fed4 00000000 ntdll!KiFastSystemCallRet (FPO: [0,0,0])
THREAD 853cf020 Cid 0aa8.0af4 Teb: 7ffdd000 Win32Thread: e290a8b8 WAIT: (Unknown) UserMode Non-Alertable
8533a2a0 Semaphore Limit 0x7fffffff
Not impersonating
DeviceMap e206f720
Owning Process 85e3b8a8 Image: uedit32.exe
Wait Start TickCount 264026
Context Switch Count 15 LargeStack
UserTime 00:00:00.0000
KernelTime 00:00:00.0000
Start Address kernel32!BaseThreadStartThunk (0x77e6b5bb)
LPC Server thread working on message Id 442d
Stack Init f53f4000 Current f53f3c24 Base f53f4000 Limit f53f1000 Call 0
Priority 9 BasePriority 8 PriorityDecrement 0
Kernel stack not resident.
ChildEBP RetAddr
f53f3c3c 80820128 nt!KiSwapContext+0x25 (FPO: [Uses EBP] [0,0,4])
...
0158fe18 0003f1bb ntdll!KiFastSystemCallRet (FPO: [0,0,0])
WARNING: Frame IP not in any known module. Following frames may be wrong.
0158ff84 00015360 0x3f1bb
00000000 00000000 0x15360
kd> !vad 85331468
VAD level start end commit
857ac690 ( 6) 10 10 1 Private READWRITE
853b6280 ( 5) 20 20 1 Private READWRITE
...
853a51d8 ( 6) 1410 1420 0 Mapped READONLY
85e47320 ( 7) 1430 1477 72 Private READWRITE
85e42c20 ( 3) 1480 1481 0 Mapped READONLY
85333b98 ( 5) 1690 16d7 72 Private READWRITE
Total VADs: 46 average level: 5 maximum depth: 7
핸들테이블
Windows 객체 관리자는 핸들 숫자값을 핸들 테이블의 색인 정보로 활용해서 참조하고자 하는 커널 객체에 접근
kd> !handle 07fc
processor number 0, process 85e36020
PROCESS 85e36020 SessionId: 0 Cid: 05c0 Peb: 7ffdc000 ParentCid: 0ad8
DirBase: 183e5000 ObjectTable: e2879988 HandleCount: 249.
Image: windbg.exe
New version of handle table at e2e92000 with 249 Entries in use
07fc: Object: e1006d08 GrantedAccess: 00000003
Object: e1006d08 Type: (85f92900) KeyedEvent
ObjectHeader: e1006cf0
HandleCount: 30 PointerCount: 31
Directory Object: e1001668 Name: CritSecOutOfMemoryEvent
e1006d08 은 핸들 테이블에 존재하며 이는 3단계로 분해되서 색인처리 됨
위를 분해하면 커널 객체 포인터를 찾을 수 있음
[31..26][25..18][17..10][9..2][1..0]
i 256 j 256 k 256
x 를 핸들이라고 하면
x 가 가르키는 테이블 의 i 인덱스
x2 = *x[i]
x2 이 가르키는 테이블의 j 인덱스
x3 = *x2[j]
x3 가 가르키는 테이블의 k 인덱스
p_kobj = *x3[k]
p_kobj 는 커널 객체의 Object 포인터 이다...
한편, 위의 [31..26] 은 Audit on Close , Protect from close, Inheritable 플래그로 사용,
[1..0] 은 Lock Flag 으로 사용한다는 전설이 내려온다구 한다...
이렇게 쓰는 이유는 에너지절약 차원...
프로세스가 가지는 핸들의 테이블 메모리를 최대한 줄일 수 있음
정리하면서 무엇보다 이거 보면서 솔루션 만드느라
조낸 고생하신 올드드래곤 옹 생각이 난다...
-_-;;;
참고는 Windows 구조와 원리 그리고 Codes , 정덕영, "프로세스와 쓰레드"편