Microsoft MVP성태의 닷넷 이야기
디버깅 기술: 28. Windbg - 윈도우 핸들 테이블 [링크 복사], [링크+제목 복사]
조회: 24272
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 2개 있습니다.)
(시리즈 글이 5개 있습니다.)
디버깅 기술: 28. Windbg - 윈도우 핸들 테이블
; https://www.sysnet.pe.kr/2/0/935

디버깅 기술: 55. Windbg - 윈도우 핸들 테이블 (2)
; https://www.sysnet.pe.kr/2/0/1476

디버깅 기술: 151. Windows 10 - Process Explorer로 확인한 Handle 정보를 windbg에서 조회
; https://www.sysnet.pe.kr/2/0/12099

디버깅 기술: 168. windbg - 필터 드라이버 확인하는 확장 명령어(!fltkd)
; https://www.sysnet.pe.kr/2/0/12283

디버깅 기술: 194. Windbg - x64 가상 주소를 물리 주소로 변환
; https://www.sysnet.pe.kr/2/0/13500




Windbg - 윈도우 핸들 테이블

Windbg를 이용해서 Handle Table을 확인해 보고 기록을 남깁니다. 이번 글은 다음의 책에서 139페이지의 내용을 실습해 본 것입니다.

Windows 구조와 원리: OS를 관통하는 프로그래밍의 원리 
정덕영 저 | 한빛미디어 
; http://www.yes24.com/24/goods/1956142

우선, Windows 2003 x86에서, LKD 상태로 테스트를 해보았습니다.

1. 적당한 프로세스를 고르기 위해 "!process 0 0" 명령어를 실행하고,

||1:lkd> !process 0 0
...[생략]...
PROCESS 8da1b4a0  SessionId: 1  Cid: 0f84    Peb: 7ffdd000  ParentCid: 0ee8
    DirBase: 7fb1d3c0  ObjectTable: e14d04c8  HandleCount:  11.
    Image: CppTest.exe

kd> !process: 현재 Active 스레드가 속한 프로세스 정보
kd> !process 0 1: Debuggee PC에서 동작 중인 모든 프로세스 정보
kd> !process 0 7: Debuggee PC에서 동작 중인 모든 프로세스 정보 및 그것의 스레드, 호출 스택 정보

위의 "ObjectTable: e14d04c8" 값이 바로 해당 프로세스의 _HANDLE_TABLE 구조체 주소입니다.

2. _HANDLE_TABLE 구조체 값은 아래의 방법으로 구합니다.

||1:lkd> dt _HANDLE_TABLE e14d04c8
nt!_HANDLE_TABLE
   +0x000 TableCode        : 0xe2e33000
   +0x004 QuotaProcess     : 0x8da1b4a0 _EPROCESS
   +0x008 UniqueProcessId  : 0x00000f84 Void
   +0x00c HandleTableLock  : [4] _EX_PUSH_LOCK
   +0x01c HandleTableList  : _LIST_ENTRY [ 0x808ae988 - 0xe2a8327c ]
   +0x024 HandleContentionEvent : _EX_PUSH_LOCK
   +0x028 DebugInfo        : (null) 
   +0x02c ExtraInfoPages   : 0n0
   +0x030 FirstFree        : 0x30
   +0x034 LastFree         : 0
   +0x038 NextHandleNeedingPool : 0x800
   +0x03c HandleCount      : 0n11
   +0x040 Flags            : 0
   +0x040 StrictFIFO       : 0y0

위에서 "TableCode: 0xe2e33000" 값이 바로 핸들 테이블의 주소입니다.
구조체 이름이 기억나지 않는 경우에는 그냥 다음과 같이 dd 명령으로 4byte 정렬로 출력하면 핸들 테이블의 주소를 구할 수 있습니다.

||1:lkd> dd e14d04c8
e14d04c8  e2e33000 8da1b4a0 00000f84 00000000
e14d04d8  00000000 00000000 00000000 808ae988
e14d04e8  e2a8327c 00000000 00000000 00000000
e14d04f8  00000030 00000000 00000800 0000000b
e14d0508  00000000 00000000 0c15080a 7346744e
e14d0518  00003dd5 00d40000 e11caa10 00d50000
e14d0528  00760000 00000000 000002d7 00030000
e14d0538  27681de6 01cb66b2 b036160e 01cb66b1

에 보면, 위와 같은 핸들 테이블이 바로 "Layer 1"에 해당하는 것일 텐데, 그 책은 Windows 2000이라서 그런 것 같고 Windows 2003에서 테스트했을 때는 위에서 구해진 "e2e33000" 핸들 테이블 값에서 곧바로 핸들 정보가 구해졌습니다. ('책이 옳겠지' 생각하며 실습하다 시간 다 보냈습니다. ^^;)

3. 그래서, 일단 e2e33000 값을 바로 덤프해서 핸들 목록을 구할 수 있습니다.

||1:lkd> dd e2e33000
e2e33000  00000000 fffffffe e1002689 00000003
e2e33010  8dadd2e1 001f0003 e163cc09 00000003
e2e33020  8d2d2ec9 00100003 8d143589 00100003
e2e33030  8cd78823 00100020 8d3f89b1 001f0003
e2e33040  e28d3049 001f0001 e25fe639 0002000f
e2e33050  8cfbcf41 001f0003 8da4f3c9 000f037f
e2e33060  00000000 00000034 00000000 00000038
e2e33070  00000000 0000003c 00000000 00000040

Handle 테이블을 구성하는 "하나의 엔트리"는 8byte입니다. 핸들 정보를 가진 오브젝트 헤더 4byte와 Granted Access를 표현하는 4byte.

그런데, 위에서 보면 0번째 핸들 값의 Object Header 값이 00000000으로 나오는데, 즉 0번 값을 가지는 핸들이 없음을 나타냅니다.

4. 실습 삼아서, 7번 항목을 살펴보면 아래의 위치가 되겠죠.

||1:lkd> dd e2e33000
e2e33000  00000000 fffffffe e1002689 00000003
e2e33010  8dadd2e1 001f0003 e163cc09 00000003
e2e33020  8d2d2ec9 00100003 8d143589 00100003
e2e33030  8cd78823 00100020 8d3f89b1 001f0003
e2e33040  e28d3049 001f0001 e25fe639 0002000f
e2e33050  8cfbcf41 001f0003 8da4f3c9 000f037f
e2e33060  00000000 00000034 00000000 00000038
e2e33070  00000000 0000003c 00000000 00000040

이유는 알 수 없지만, 이렇게 구해진 "8d3f89b1" 주소는 정확히 _OBJECT_HEADER를 가리키지 않고 이 값에서 1을 빼주어야 합니다. (이유를 아시는 분... 댓글 부탁드립니다. ^^ 다른 글들을 봐도 그래야 한다고만 되어 있고, 이유는 안 써 있더군요.)

우선, _OBJECT_HEADER의 구조는 아래와 같고,

||1:lkd> dt _OBJECT_HEADER
nt!_OBJECT_HEADER
   +0x000 PointerCount     : Int4B
   +0x004 HandleCount      : Int4B
   +0x004 NextToFree       : Ptr32 Void
   +0x008 Type             : Ptr32 _OBJECT_TYPE
   +0x00c NameInfoOffset   : UChar
   +0x00d HandleInfoOffset : UChar
   +0x00e QuotaInfoOffset  : UChar
   +0x00f Flags            : UChar
   +0x010 ObjectCreateInfo : Ptr32 _OBJECT_CREATE_INFORMATION
   +0x010 QuotaBlockCharged : Ptr32 Void
   +0x014 SecurityDescriptor : Ptr32 Void
   +0x018 Body             : _QUAD

"8d3f89b0 = 8d3f89b1 - 1"로 구해진 "8d3f89b0" 주소로 구조체의 개별 값을 다음과 같이 구할 수 있습니다.

||1:lkd> dt _OBJECT_HEADER 8D3F89B0 

nt!_OBJECT_HEADER
   +0x000 PointerCount     : 0n2  // windbg에서는 10진수의 표현을 위해 0n prefix를 붙입니다.
   +0x004 HandleCount      : 0n1
   +0x004 NextToFree       : 0x00000001 Void
   +0x008 Type             : 0x8e342990 _OBJECT_TYPE
   +0x00c NameInfoOffset   : 0x10 ''
   +0x00d HandleInfoOffset : 0 ''
   +0x00e QuotaInfoOffset  : 0 ''
   +0x00f Flags            : 0x20 ' '
   +0x010 ObjectCreateInfo : 0x8d3f12a0 _OBJECT_CREATE_INFORMATION
   +0x010 QuotaBlockCharged : 0x8d3f12a0 Void
   +0x014 SecurityDescriptor : 0xe25f2097 Void
   +0x018 Body             : _QUAD

5. "NameInfoOffset == 0x10" 값이 재미있습니다. 이 옵셋 값은 해당 _OBJECT_HEADER 항목과 연관된 것으로 개체의 이름 정보를 알 수 있는 주소에 대한 -(음수) 값입니다.

따라서, 이름 정보는 "8D3F89a0 = 8D3F89B0 - 0x10" 값으로 다음과 같이 구할 수 있습니다.

||1:lkd> dt _OBJECT_HEADER_NAME_INFO 8D3F89a0
nt!_OBJECT_HEADER_NAME_INFO
   +0x000 Directory        : 0xe25fe650 _OBJECT_DIRECTORY
   +0x004 Name             : _UNICODE_STRING "MyEvent"
   +0x00c QueryReferences  : 1

지금 살펴보고 있는 CppTest.exe에서는 다음과 같이 이벤트 개체를 생성했기 때문에, 위에서도 역시 "MyEvent" 값을 확인하게 된 것입니다.

int _tmain(int argc, _TCHAR* argv[])
{
    HANDLE hHandle = ::CreateEvent(NULL, TRUE, TRUE, L"MyEvent");

    getchar();

    ::CloseHandle(hHandle);
    return 0;
}

6. 이어서 _OBJECT_HEADER에 있던 _OBJECT_TYPE도 구해봅니다.

||1:lkd> dt _OBJECT_TYPE 8e342990
nt!_OBJECT_TYPE
   +0x000 Mutex            : _ERESOURCE
   +0x038 TypeList         : _LIST_ENTRY [ 0x8e3429c8 - 0x8e3429c8 ]
   +0x040 Name             : _UNICODE_STRING "Event"
   +0x048 DefaultObject    : (null) 
   +0x04c Index            : 9
   +0x050 TotalNumberOfObjects : 0xb06
   +0x054 TotalNumberOfHandles : 0xb69
   +0x058 HighWaterNumberOfObjects : 0x2664
   +0x05c HighWaterNumberOfHandles : 0x2718
   +0x060 TypeInfo         : _OBJECT_TYPE_INITIALIZER
   +0x0ac Key              : 0x6e657645
   +0x0b0 ObjectLocks      : [4] _ERESOURCE

7. _OBJECT_TYPE_INITIALIZER도 구해볼까요? 그런데, 이번에는 직접적인 값이 출력되지 않았습니다. ^^ 그래도 상관없습니다. 위에서 _OBJECT_TYPE의 주소가 "8e342990"이고, _OBJECT_TYPE_INITIALIZER 구조체의 옵셋값이 "0x060"으로 되어 있으니 "8E3429F0 = 8e342990 + 0x60"으로 구하면 됩니다.

||1:lkd> dt _OBJECT_TYPE_INITIALIZER 8E3429F0 
nt!_OBJECT_TYPE_INITIALIZER
   +0x000 Length           : 0x4c
   +0x002 UseDefaultObject : 0 ''
   +0x003 CaseInsensitive  : 0 ''
   +0x004 InvalidAttributes : 0x100
   +0x008 GenericMapping   : _GENERIC_MAPPING
   +0x018 ValidAccessMask  : 0x1f0003
   +0x01c SecurityRequired : 0 ''
   +0x01d MaintainHandleCount : 0 ''
   +0x01e MaintainTypeList : 0 ''
   +0x020 PoolType         : 0 ( NonPagedPool )
   +0x024 DefaultPagedPoolCharge : 0
   +0x028 DefaultNonPagedPoolCharge : 0x40
   +0x02c DumpProcedure    : (null) 
   +0x030 OpenProcedure    : (null) 
   +0x034 CloseProcedure   : (null) 
   +0x038 DeleteProcedure  : (null) 
   +0x03c ParseProcedure   : (null) 
   +0x040 SecurityProcedure : 0x809733e0     long  nt!SeDefaultObjectMethod+0
   +0x044 QueryNameProcedure : (null) 
   +0x048 OkayToCloseProcedure : (null) 

8. 다시 처음의 _OBJECT_HEADER로 돌아가면, 마지막에 Body가 보이는데요.

||1:lkd> dt _OBJECT_HEADER
nt!_OBJECT_HEADER
   +0x000 PointerCount     : Int4B
   +0x004 HandleCount      : Int4B
   +0x004 NextToFree       : Ptr32 Void
   +0x008 Type             : Ptr32 _OBJECT_TYPE
   +0x00c NameInfoOffset   : UChar
   +0x00d HandleInfoOffset : UChar
   +0x00e QuotaInfoOffset  : UChar
   +0x00f Flags            : UChar
   +0x010 ObjectCreateInfo : Ptr32 _OBJECT_CREATE_INFORMATION
   +0x010 QuotaBlockCharged : Ptr32 Void
   +0x014 SecurityDescriptor : Ptr32 Void
   +0x018 Body             : _QUAD

바로 그 부분이 다양한 커널 개체들의 구조체가 시작되는 부분입니다. 위에서 살펴본 개체가 Event이기 때문에 다음과 같이 _KEVENT로 매핑시켜서 확인해 볼 수 있는데요. 아래에서 보는 것처럼 _KEVENT는 _DISPATCHER_HEADER의 속성을 그대로 상속받은 것에 불과합니다.

||1:lkd> dt _KEVENT
nt!_KEVENT
   +0x000 Header           : _DISPATCHER_HEADER

그래서 다음과 같이 덤프할 수 있습니다.

||1:lkd> dt _DISPATCHER_HEADER 8d3f89c8
nt!_DISPATCHER_HEADER
   +0x000 Type             : 0 ''      // (Event: 0, Semaphor: 5)
   +0x001 Absolute         : 0x8a ''
   +0x001 NpxIrql          : 0x8a ''
   +0x002 Size             : 0x4 ''
   +0x002 Hand             : 0x4 ''
   +0x003 Inserted         : 0xe3 ''
   +0x003 DebugActive      : 0xe3 ''
   +0x000 Lock             : 0n-486241792
   +0x004 SignalState      : 0n1
   +0x008 WaitListHead     : _LIST_ENTRY [ 0x8d3f89d0 - 0x8d3f89d0 ]

9. 지금까지 힘들게 옵셋값들을 조정하면서 핸들 관련 정보를 살펴보았는데요. 사실, 핵심적인 정보들은 "!handle" 명령만으로 쉽게 찾아낼 수 있습니다.

다음은 (작업관리자에서 확인되는) 프로세스 ID가 F84인 프로세스가 소유한 핸들을 살펴보는 명령입니다.

||1:lkd> !handle 0 2 0f84

Searching for Process with Cid == f84
PROCESS 8da1b4a0  SessionId: 1  Cid: 0f84    Peb: 7ffdd000  ParentCid: 0ee8
    DirBase: 7fb1d3c0  ObjectTable: e14d04c8  HandleCount:  11.
    Image: CppTest.exe

Handle table at e2e33000 with 11 entries in use

0004: Object: e10026a0  GrantedAccess: 00000003 Entry: e2e33008
Object: e10026a0  Type: (8e341ad0) KeyedEvent
    ObjectHeader: e1002688 (old version)

    ...[생략]...

001c: Object: 8d3f89c8  GrantedAccess: 001f0003 Entry: e2e33038
Object: 8d3f89c8  Type: (8e342990) Event
    ObjectHeader: 8d3f89b0 (old version)

    ...[생략]...




이어서, Windows Server 2008 R2에서도 동일하게 살펴보았습니다.

1. 실습 대상인 CppTest.exe를 고르고,

||2:lkd> !process 0 0
...[생략]...
PROCESS fffffa800a4617d0
    SessionId: 2  Cid: 0b9c    Peb: 7fffffdd000  ParentCid: 0bd0
    DirBase: 1640b000  ObjectTable: fffff8a0026066f0  HandleCount:   8.
    Image: CppTest.exe
...[생략]...

2. _HANDLE_TABLE 구조체를 덤프

||2:lkd> dt _HANDLE_TABLE fffff8a0026066f0
nt!_HANDLE_TABLE
   +0x000 TableCode        : 0xfffff8a0`02746000
   +0x008 QuotaProcess     : 0xfffffa80`0a4617d0 _EPROCESS
   +0x010 UniqueProcessId  : 0x00000000`00000b9c 
   +0x018 HandleLock       : _EX_PUSH_LOCK
   +0x020 HandleTableList  : _LIST_ENTRY [ 0xfffff8a0`01945180 - 0xfffff8a0`016cdf20 ]
   +0x030 HandleContentionEvent : _EX_PUSH_LOCK
   +0x038 DebugInfo        : (null) 
   +0x040 ExtraInfoPages   : 0
   +0x044 Flags            : 0
   +0x044 StrictFIFO       : 0y0
   +0x048 FirstFreeHandle  : 0x24
   +0x050 LastFreeHandleEntry : 0xfffff8a0`02746ff0 _HANDLE_TABLE_ENTRY
   +0x058 HandleCount      : 8
   +0x05c NextHandleNeedingPool : 0x400
   +0x060 HandleCountHighWatermark : 8

3. _HANDLE_TABLE.TableCode 값을 덤프하는데, x64이기 때문에 4byte 덤프가 아니므로 "dd" 대신에 "dq"로 해봅니다.

||2:lkd> dq 0xfffff8a002746000
fffff8a0`02746000  00000000`00000000 00000000`fffffffe
fffff8a0`02746010  fffff8a0`004cfd11 00000000`00000003
fffff8a0`02746020  fffffa80`0a49eef1 00000000`00100020
fffff8a0`02746030  fffffa80`0a4693c1 00000000`001f0003
fffff8a0`02746040  fffff8a0`025c0951 00000000`00020019
fffff8a0`02746050  fffffa80`0a339b11 00000000`001f0001
fffff8a0`02746060  fffffa80`0a167901 00000000`001f0001
fffff8a0`02746070  fffff8a0`01659601 00000000`0000000f

x64이기 때문에 _OBJECT_HEADER에 대한 포인터도 8byte이고, Granted Access도 8byte이기 때문에 하나의 엔트리 당 총 16byte를 소비합니다. 위에서는 역시 0번째 엔트리의 _OBJECT_HEADER 값이 "00000000`00000000"으로 x86과 동일하게 비어있다는 것을 알 수 있습니다.

4. 8번째 항목을 덤프하려는데, 위에서 기본 "dq" 명령으로는 7번 항목까지만 보여주고 있으므로 "L[size]"를 부가적으로 지정해주어 값을 확인합니다.

||2:lkd> dq 0xfffff8a002746000 L14
fffff8a0`02746000  00000000`00000000 00000000`fffffffe
fffff8a0`02746010  fffff8a0`004cfd11 00000000`00000003
fffff8a0`02746020  fffffa80`0a49eef1 00000000`00100020
fffff8a0`02746030  fffffa80`0a4693c1 00000000`001f0003
fffff8a0`02746040  fffff8a0`025c0951 00000000`00020019
fffff8a0`02746050  fffffa80`0a339b11 00000000`001f0001
fffff8a0`02746060  fffffa80`0a167901 00000000`001f0001
fffff8a0`02746070  fffff8a0`01659601 00000000`0000000f
fffff8a0`02746080  fffffa80`0a3af5b1 00000000`001f0003
fffff8a0`02746090  00000000`00000000 00000000`00000028

역시 이번에도 "fffffa80`0a3af5b0 = fffffa80`0a3af5b1 - 1"과 같이 1을 빼서 확인합니다.

||2:lkd> dt _OBJECT_HEADER fffffa80`0a3af5b0
nt!_OBJECT_HEADER
   +0x000 PointerCount     : 2
   +0x008 HandleCount      : 1
   +0x008 NextToFree       : 0x00000000`00000001 
   +0x010 Lock             : _EX_PUSH_LOCK
   +0x018 TypeIndex        : 0xc ''
   +0x019 TraceFlags       : 0 ''
   +0x01a InfoMask         : 0xa ''
   +0x01b Flags            : 0 ''
   +0x020 ObjectCreateInfo : 0xfffffa80`09d9d980 _OBJECT_CREATE_INFORMATION
   +0x020 QuotaBlockCharged : 0xfffffa80`09d9d980 
   +0x028 SecurityDescriptor : 0xfffff8a0`017b59cf 
   +0x030 Body             : _QUAD

x86과 차이점이 여기서 극명하게 나타나는데요. NameInfoOffset과 _OBJECT_TYPE을 확인할 수 있는 Type 멤버가 누락되어 있습니다. 방법을 모르니 그 부분은 넘어가고, Body를 알아보겠습니다.

||2:lkd> dt _KEVENT
nt!_KEVENT
   +0x000 Header           : _DISPATCHER_HEADER

_KEVENT가 _DISPATCHER_HEADER를 그대로 상속받은 것에는 변함이 없고, fffffa80`0a3af5b0 값에 Body의 옵셋값인 0x030을 더해서 다음과 같이 구해봅니다.

||2:lkd> dt _DISPATCHER_HEADER fffffa80`0a3af5e0
nt!_DISPATCHER_HEADER
   +0x000 Type             : 0 ''
   +0x001 TimerControlFlags : 0 ''
   +0x001 Absolute         : 0y0
   +0x001 Coalescable      : 0y0
   +0x001 KeepShifting     : 0y0
   +0x001 EncodedTolerableDelay : 0y00000 (0)
   +0x001 Abandoned        : 0 ''
   +0x001 Signalling       : 0 ''
   +0x002 ThreadControlFlags : 0x6 ''
   +0x002 CpuThrottled     : 0y0
   +0x002 CycleProfiling   : 0y1
   +0x002 CounterProfiling : 0y1
   +0x002 Reserved         : 0y00000 (0)
   +0x002 Hand             : 0x6 ''
   +0x002 Size             : 0x6 ''
   +0x003 TimerMiscFlags   : 0 ''
   +0x003 Index            : 0y000000 (0)
   +0x003 Inserted         : 0y0
   +0x003 Expired          : 0y0
   +0x003 DebugActive      : 0 ''
   +0x003 ActiveDR7        : 0y0
   +0x003 Instrumented     : 0y0
   +0x003 Reserved2        : 0y0000
   +0x003 UmsScheduled     : 0y0
   +0x003 UmsPrimary       : 0y0
   +0x003 DpcActive        : 0 ''
   +0x000 Lock             : 393216
   +0x004 SignalState      : 1
   +0x008 WaitListHead     : _LIST_ENTRY [ 0xfffffa80`0a3af5e8 - 0xfffffa80`0a3af5e8 ]

5. 역시 위와 같은 과정은 거의 호기심에서 찾아보는 것일 뿐, !handle 작업으로 확인할 수 있습니다.

||2:lkd> !handle 0 2 b9c
processor number 0, process 0000000000000b9c
Searching for Process with Cid == b9c
PROCESS fffffa800a4617d0
    SessionId: 2  Cid: 0b9c    Peb: 7fffffdd000  ParentCid: 0bd0
    DirBase: 1640b000  ObjectTable: fffff8a0026066f0  HandleCount:   8.
    Image: CppTest.exe

Handle table at fffff8a002746000 with 8 Entries in use
0004: Object: fffff8a0004cfd40  GrantedAccess: 00000003 Entry: fffff8a002746010
Object: fffff8a0004cfd40  Type: (fffffa8009091930) Directory
    ObjectHeader: fffff8a0004cfd10 (new version)

...[생략]...

0020: Object: fffffa800a3af5e0  GrantedAccess: 001f0003 Entry: fffff8a002746080
Object: fffffa800a3af5e0  Type: (fffffa80090f4570) Event
    ObjectHeader: fffffa800a3af5b0 (new version)

재미있는 점은, _OBJECT_HEADER에서 구할 수 없던 Type 값이 여기서 확인할 수 있습니다. ObjectHeader와 Type 값의 차이를 보면, 왠지 일반적인 Offset 값 정도로는 어림없을 것 같고... 도대체 어떤 연관고리가 있는 걸까요? ^^ 암튼, 지금은 능력밖이므로, 그냥 _OBJECT_TYPE만 확인해 봅니다.

||2:lkd> dt _OBJECT_TYPE fffffa80090f4570
nt!_OBJECT_TYPE
   +0x000 TypeList         : _LIST_ENTRY [ 0xfffffa80`090f4570 - 0xfffffa80`090f4570 ]
   +0x010 Name             : _UNICODE_STRING "Event"
   +0x020 DefaultObject    : (null) 
   +0x028 Index            : 0xc ''
   +0x02c TotalNumberOfObjects : 0xb83
   +0x030 TotalNumberOfHandles : 0xbb5
   +0x034 HighWaterNumberOfObjects : 0xd69
   +0x038 HighWaterNumberOfHandles : 0xdb5
   +0x040 TypeInfo         : _OBJECT_TYPE_INITIALIZER
   +0x0b0 TypeLock         : _EX_PUSH_LOCK
   +0x0b8 Key              : 0x6e657645
   +0x0c0 CallbackList     : _LIST_ENTRY [ 0xfffffa80`090f4630 - 0xfffffa80`090f4630 ]

그래도 역시 개체의 "이름"은 확인할 방도가 없습니다. 이 값은, !handle의 옵션을 통해서 좀 더 자세한 출력을 얻어내야 알 수 있습니다.

||2:lkd> !handle 0 b b9c
processor number 0, process 0000000000000b9c
Searching for Process with Cid == b9c
PROCESS fffffa800a4617d0
    SessionId: 2  Cid: 0b9c    Peb: 7fffffdd000  ParentCid: 0bd0
    DirBase: 1640b000  ObjectTable: fffff8a0026066f0  HandleCount:   8.
    Image: CppTest.exe

Handle table at fffff8a002746000 with 8 Entries in use
0004: Object: fffff8a0004cfd40  GrantedAccess: 00000003 Entry: fffff8a002746010
Object: fffff8a0004cfd40  Type: (fffffa8009091930) Directory
    ObjectHeader: fffff8a0004cfd10 (new version)
        HandleCount: 40  PointerCount: 78
        Directory Object: fffff8a000004c90  Name: KnownDlls

... [생략]...
0020: Object: fffffa800a3af5e0  GrantedAccess: 001f0003 Entry: fffff8a002746080
Object: fffffa800a3af5e0  Type: (fffffa80090f4570) Event
    ObjectHeader: fffffa800a3af5b0 (new version)
        HandleCount: 1  PointerCount: 2
        Directory Object: fffff8a001659630  Name: MyEvent

핸들을 직접 지정해서 "Optional Headers"를 지정해서 얻어내는 방법이 있긴 한데, 어찌된 일인지 이 목록은 "!handle 0 0 b9c"에서 구한 핸들 목록과 인덱스가 같지 않습니다. 보는 것처럼 위에서 0x20은 "Event" 유형인데 아래에서는 "ALPC Port"임이 확인됩니다. 아래의 명령어는 커널 모드의 정보를 구하기 때문에 다릅니다. 출력 결과에 보면 "Kernel Handle table"이라고 나옵니다.

||2:lkd> !handle 0x20 13 b9c
processor number 0, process 0000000000000b9c
Searching for Process with Cid == b9c
PROCESS fffffa800a4617d0
    SessionId: 2  Cid: 0b9c    Peb: 7fffffdd000  ParentCid: 0bd0
    DirBase: 1640b000  ObjectTable: fffff8a0026066f0  HandleCount:   8.
    Image: CppTest.exe

Kernel Handle table at fffff8a001150000 with 596 Entries in use
0020: Object: fffffa80090e7070  GrantedAccess: 001f0001 Entry: fffff8a000003080
Object: fffffa80090e7070  Type: (fffffa80090e2db0) ALPC Port
    ObjectHeader: fffffa80090e7040 (new version)
        HandleCount: 1  PointerCount: 4
        Directory Object: fffff8a000004c90  Name: PowerPort
Optional Headers: 
    NameInfo(fffffa80090e7020)
    HandleInfo(fffffa80090e7010)

||2:lkd> dt _OBJECT_HEADER_NAME_INFO fffffa80090e7020
nt!_OBJECT_HEADER_NAME_INFO
   +0x000 Directory        : 0xfffff8a0`00004c90 _OBJECT_DIRECTORY
   +0x008 Name             : _UNICODE_STRING "PowerPort"
   +0x018 ReferenceCount   : 0

||2:lkd> dt _OBJECT_HEADER_HANDLE_INFO fffffa80090e7010
nt!_OBJECT_HEADER_HANDLE_INFO
   +0x000 HandleCountDataBase : 0xfffffa80`090b9740 _OBJECT_HANDLE_COUNT_DATABASE
   +0x000 SingleEntry      : _OBJECT_HANDLE_COUNT_ENTRY

이번엔 여기까지!



[이 토픽에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 5/9/2023]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2014-02-11 05시04분
[김준기] ObjectHeader 연산할때 1을 빼는건 ( 실제로는 & 0xFFFFFFFE ), Handle Table 구조에서 최상위 비트가 Lock Bit 이기 때문일거에요.
[guest]
2014-02-11 10시04분
김준기 님 감사드립니다. 말씀해 주신대로 ^^ Lock Bit라고 해서 검색해 보니 관련 설명들이 나오는군요.
정성태
2015-08-21 07시24분
정성태

[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13600정성태4/18/2024263닷넷: 2242. C# - 관리 스레드와 비관리 스레드
13599정성태4/17/2024285닷넷: 2241. C# - WAV 파일의 PCM 사운드 재생(Windows Multimedia)파일 다운로드1
13598정성태4/16/2024302닷넷: 2240. C# - WAV 파일 포맷 + LIST 헤더파일 다운로드1
13597정성태4/15/2024369닷넷: 2239. C# - WAV 파일의 PCM 데이터 생성 및 출력파일 다운로드1
13596정성태4/14/2024744닷넷: 2238. C# - WAV 기본 파일 포맷파일 다운로드1
13595정성태4/13/2024869닷넷: 2237. C# - Audio 장치 열기 (Windows Multimedia, NAudio)파일 다운로드1
13594정성태4/12/20241007닷넷: 2236. C# - Audio 장치 열람 (Windows Multimedia, NAudio)파일 다운로드1
13593정성태4/8/20241050닷넷: 2235. MSBuild - AccelerateBuildsInVisualStudio 옵션
13592정성태4/2/20241206C/C++: 165. CLion으로 만든 Rust Win32 DLL을 C#과 연동
13591정성태4/2/20241166닷넷: 2234. C# - WPF 응용 프로그램에 Blazor App 통합파일 다운로드1
13590정성태3/31/20241072Linux: 70. Python - uwsgi 응용 프로그램이 k8s 환경에서 OOM 발생하는 문제
13589정성태3/29/20241142닷넷: 2233. C# - 프로세스 CPU 사용량을 나타내는 성능 카운터와 Win32 API파일 다운로드1
13588정성태3/28/20241195닷넷: 2232. C# - Unity + 닷넷 App(WinForms/WPF) 간의 Named Pipe 통신파일 다운로드1
13587정성태3/27/20241154오류 유형: 900. Windows Update 오류 - 8024402C, 80070643
13586정성태3/27/20241295Windows: 263. Windows - 복구 파티션(Recovery Partition) 용량을 늘리는 방법
13585정성태3/26/20241094Windows: 262. PerformanceCounter의 InstanceName에 pid를 추가한 "Process V2"
13584정성태3/26/20241047개발 환경 구성: 708. Unity3D - C# Windows Forms / WPF Application에 통합하는 방법파일 다운로드1
13583정성태3/25/20241156Windows: 261. CPU Utilization이 100% 넘는 경우를 성능 카운터로 확인하는 방법
13582정성태3/19/20241416Windows: 260. CPU 사용률을 나타내는 2가지 수치 - 사용량(Usage)과 활용률(Utilization)파일 다운로드1
13581정성태3/18/20241586개발 환경 구성: 707. 빌드한 Unity3D 프로그램을 C++ Windows Application에 통합하는 방법
13580정성태3/15/20241136닷넷: 2231. C# - ReceiveTimeout, SendTimeout이 적용되지 않는 Socket await 비동기 호출파일 다운로드1
13579정성태3/13/20241493오류 유형: 899. HTTP Error 500.32 - ANCM Failed to Load dll
13578정성태3/11/20241628닷넷: 2230. C# - 덮어쓰기 가능한 환형 큐 (Circular queue)파일 다운로드1
13577정성태3/9/20241873닷넷: 2229. C# - 닷넷을 위한 난독화 도구 소개 (예: ConfuserEx)
13576정성태3/8/20241543닷넷: 2228. .NET Profiler - IMetaDataEmit2::DefineMethodSpec 사용법
[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...