WinDbg 디버깅2007. 9. 23. 02:21
반응형

s (Search Memory)

메모리에서 원하는 문자열이나 메모리 패턴을 찾을 때 사용하는 명령입니다.
디버깅할 때 종종 아주 유용하게 사용하는 경우가 있으므로 알아두시면 좋습니다.

WinDbg Help 를 보면 아주 다양한 옵션을 사용할 수 있다는 것을 알 수 있습니다.
다 알면 유용한 것들일텐데 안타깝게도 제가 다 알지를 못하고 있습니다. -_-a

유용하게 자주 사용하는 것들만 간단히 설명합니다.

[Syntax]
s [-Type] Range Pattern

Type Description
b Byte (8 bits) (default)
w WORD (16 bits)
d DWORD (32 bits)
q QWORD (64 bits)
a ASCII string(not necessarily null-terminated)
u Unicode string(not necessarily null-terminated)

Type 을 생략하면 Byte 단위로 메모리를 검색합니다.
Range 는 시작주소와 끝주소를 표시합니다.
Pattern 은 찾아야 할 패턴입니다.

WinDbg Help 에 있는 예제를 그대로 가져와서 설명합니다.

0:000> s 0012ff40 0012ff60 'H' 'e' 'l' 'l' 'o'

위 예제는 0012ff40 부터 0012ff60 까지 범위에서 H,e,l,l,o 문자패턴을 가지고 있는 메모리를 찾아 달라는 뜻입니다.
Range 를 지정하는 방법을 '시작주소 L길이' 를 사용할 수도 있는데 다음은 위에서 보인 예제와 같은 의미입니다.

0:000> s 0012ff40 L20 'H' 'e' 'l' 'l' 'o'

패턴을 다음과 같이 ASCII 코드 값으로 적어줘도 같은 의미입니다.

0:000> s 0012ff40 L20 48 65 6c 6c 6f

이와 같은 형식은 메모리에서 특정 숫자들이 나열되어 있는 것을 찾을 때도 사용할 수 있습니다.

옵션을 사용하여 다음과 같이 사용해도 위와 같은 의미입니다.

0:000> s -a 0012ff40 L20 "Hello"

-a 라는 Type 옵션을 주고 패턴은 "Hello" 처럼 문자열 형식으로 적어줄 수 있습니다.

예제1)

제가 작성한 드라이버를 적용한 시스템에서 메모리가 부족한 문제가 발생한 경우가 있었습니다.
문제를 검토하다 보니 가장 많이 사용될 만한 메모리 할당의 내용은 50 00 50 00 43 00 32 00 3a 00 로 시작하는 패턴을 가지고 있었습니다. 정말 이것이 과도하게 할당된 것인지 확인해 보기 위해서 NonPagedPool 시작위치부터 0x10000000 길이(대충 무지 길게 잡은 겁니다 ^^)만큼 이 패턴에 대한 검색을 수행했습니다.

kd> s 810c7000 L10000000 50 00 50 00 43 00 32 00 3a 00
810c7154  50 00 50 00 43 00 32 00-3a 00 3a 00 4e 00 65 00  P.P.C.2.:.:.N.e.
810c72b4  50 00 50 00 43 00 32 00-3a 00 3a 00 4e 00 65 00  P.P.C.2.:.:.N.e.
810c7414  50 00 50 00 43 00 32 00-3a 00 3a 00 4e 00 65 00  P.P.C.2.:.:.N.e.
810c7574  50 00 50 00 43 00 32 00-3a 00 3a 00 4e 00 65 00  P.P.C.2.:.:.N.e.
810c76d4  50 00 50 00 43 00 32 00-3a 00 3a 00 4e 00 65 00  P.P.C.2.:.:.N.e.
...
...
...

그러자 그 패턴과 일치하는 메모리 위치가 수도 없이 나열되었습니다. (정말 셀 수 없었답니다. -_-;)
이것이 버그임을 s 명령으로 검증하고 메모리를 해제하도록 버그를 수정한 사례였습니다.

예제2)

수 년전에는 프로그램을 개발할 때 Easter Egg 를 삽입하는 것이 꽤 유행했었습니다. 개발자 이름이 리스트업 된다던가 화려한 그래픽이 나온다던가 하는 식으로 유명한 프로그램들은 저마다 고유한 Easter Egg 를 가지고 있었습니다.
그 때 생각한 것이 드라이버도 Easter Egg 를 가질 수 있지 않을까 하는 것이었습니다. 그래서 V3의 드라이버는 Easter Egg 를 가지고 있답니다.
자... 한번 찾아보실까요?

먼저 lmvm 으로 V3 필터 드라이버의 시작 주소를 찾습니다.

lkd> lmvm v3flt2k
start    end        module name
ebcf6000 ebd10400   V3Flt2K    (no symbols)          
    Loaded symbol image file: \??\C:\PROGRA~1\AhnLab\V3\V3Flt2K.sys
    Image path: \??\C:\PROGRA~1\AhnLab\V3\V3Flt2K.sys
    Image name: V3Flt2K.sys
    Timestamp:        Wed Jul 12 02:55:27 2006 (44B3E60F)
    CheckSum:         000258D1
    ImageSize:        0001A400
    Translations:     0000.04b0 0000.04e0 0409.04b0 0409.04e0

s 명령으로 시작주소부터 끝주소 사이에서 VIEWEGG 라는 문자열을 찾습니다.

lkd> s -a ebcf6000 ebd10400 "VIEWEGG"
ebd0ca98  56 49 45 57 45 47 47 20-20 20 2c 25 25 25 20 20  VIEWEGG   ,%%% 

찾아진 주소 ebd0ca98 가 Easter Egg 의 시작주소입니다.
이 드라이버의 Easter Egg 는 16 x 11 의 그림입니다.
db 명령으로 볼 수 있습니다.

lkd> db ebd0ca98 L10*b
ebd0ca98  56 49 45 57 45 47 47 20-20 20 2c 25 25 25 20 20  VIEWEGG   ,%%% 
ebd0caa8  20 20 20 20 20 20 20 20-2c 25 25 25 60 25 3d 2d          ,%%%`%=-
ebd0cab8  20 20 20 20 20 20 20 2c-25 25 60 28 20 27 7c 20         ,%%`( '|
ebd0cac8  20 20 20 20 20 20 20 25-25 40 20 2f 5c 5f 2f 20         %%@ /\_/
ebd0cad8  2c 25 2e 2b 2d 25 25 20-22 40 5f 5f 20 20 20 20  ,%.+-%% "@__   
ebd0cae8  25 2f 20 20 20 20 20 20-20 20 7c 5f 5f 60 5c 20  %/        |__`\
ebd0caf8  25 5c 20 20 7c 20 5c 20-20 20 2f 20 20 2f 2f 20  %\  | \   /  //
ebd0cb08  20 3e 20 40 2d 2d 2d 5c-20 7c 20 20 5b 2f 20 20   > @---\ |  [/ 
ebd0cb18  3c 20 3c 60 20 20 20 20-7c 7c 20 20 20 20 20 20  < <`    ||     
ebd0cb28  20 60 5c 5c 20 20 20 20-7c 7c 20 20 20 20 20 20   `\\    ||     
ebd0cb38  5e 5e 5e 22 22 5e 5e 5e-22 22 5e 5e 5e 5e 5e 5e  ^^^""^^^""^^^^^^

크엇... 뭐가 보이시나요? ^^
귀여운 유니콘이 잘 보이시나요?

V3 필터 드라이버의 Easter Egg 를 찾는 방법이었습니다.
여러분의 PC 에 V3 를 사용하고 계신다면 지금 당장 한번 찾아보시지요. ^^

http://www.driveronline.org/bbs/view.asp?tb=tipbbs&GotoPage=4&s_bulu=&s_key=&no=50

반응형
Posted by GreeMate
WinDbg 디버깅2007. 9. 23. 02:19
반응형

dt (Display Type)

타입을 보여주는 명령인데 주로 구조체 타입을 확인할 때 사용합니다.

이 명령 역시 WinDbg Help 에는 무지 다양하고 복잡한 옵션들이 있습니다.
하지만 제가 주로 사용하는 구조체 확인에 대한 내용만 다루도록 하겠습니다.

1) dt 구조체타입이름
- 구조체의 멤버 필드를 확인합니다.

kd> dt _IO_STACK_LOCATION
   +0x000 MajorFunction    : UChar
   +0x001 MinorFunction    : UChar
   +0x002 Flags            : UChar
   +0x003 Control          : UChar
   +0x004 Parameters       : __unnamed
   +0x014 DeviceObject     : Ptr32 _DEVICE_OBJECT
   +0x018 FileObject       : Ptr32 _FILE_OBJECT
   +0x01c CompletionRoutine : Ptr32   
   +0x020 Context          : Ptr32 Void

구조체타입이름은 다음과 같이 선언된 구조체의 경우 맨 앞에 선언한 구조체 이름을 사용합니다.

typedef struct _IO_STACK_LOCATION {
    ...
} IO_STACK_LOCATION, *PIO_STACK_LOCATION;

2) dt 구조체타입이름 구조체주소
- 구조체의 멤버 필드와 실제 내용을 확인합니다.

kd> dt _IO_STACK_LOCATION 816441c0
   +0x000 MajorFunction    : 0xe ''
   +0x001 MinorFunction    : 0 ''
   +0x002 Flags            : 0x1 ''
   +0x003 Control          : 0 ''
   +0x004 Parameters       : __unnamed
   +0x014 DeviceObject     : 0x815d2030 _DEVICE_OBJECT
   +0x018 FileObject       : 0x8166b288 _FILE_OBJECT
   +0x01c CompletionRoutine : (null)
   +0x020 Context          : (null)

3) dt 구조체타입이름 멤버필드이름 [구조체주소]
- 구조체에서 특정한 멤버필드만 확인합니다.

kd> dt _IO_STACK_LOCATION MajorFunction
   +0x000 MajorFunction : UChar

kd> dt _IO_STACK_LOCATION MajorFunction 816441c0
   +0x000 MajorFunction : 0xe ''

4) dt -r 구조체타입이름 [구조체주소]
- 구조체의 멤버필드중 구조체가 있으면 펼쳐서 보여줍니다.

kd> dt -r _IO_STACK_LOCATION 816441c0
   +0x000 MajorFunction    : 0xe ''
   +0x001 MinorFunction    : 0 ''
   +0x002 Flags            : 0x1 ''
   +0x003 Control          : 0 ''
   +0x004 Parameters       : __unnamed
      +0x000 Create           : __unnamed
         +0x000 SecurityContext  : (null)
         +0x004 Options          : 0
         +0x008 FileAttributes   : 0x18
         +0x00a ShareAccess      : 0x22
         +0x00c EaLength         : 0
      +0x000 CreatePipe       : __unnamed
         +0x000 SecurityContext  : (null)
         +0x004 Options          : 0
      ...

5) dt -r[숫자] 구조체타입이름 [구조체주소]
- 구조체의 멤버필드중 구조체가 있으면 숫자만큼만 펼쳐서 보여줍니다.

kd> dt -r1 _IO_STACK_LOCATION 816441c0
   +0x000 MajorFunction    : 0xe ''
   +0x001 MinorFunction    : 0 ''
   +0x002 Flags            : 0x1 ''
   +0x003 Control          : 0 ''
   +0x004 Parameters       : __unnamed
      +0x000 Create           : __unnamed
      +0x000 CreatePipe       : __unnamed
   ...

6) dt 변수이름
- 심볼이 맞춰져 있다면 변수이름에 해당하는 타입과 정보를 보여줍니다.

kd> dt pIrpStack
Local var @ 0xf9c8bc30 Type _IO_STACK_LOCATION*
0x816441c0
   +0x000 MajorFunction    : 0xe ''
   +0x001 MinorFunction    : 0 ''
   +0x002 Flags            : 0x1 ''
   +0x003 Control          : 0 ''
   +0x004 Parameters       : __unnamed
   +0x014 DeviceObject     : 0x815d2030 _DEVICE_OBJECT
   +0x018 FileObject       : 0x8166b288 _FILE_OBJECT
   +0x01c CompletionRoutine : (null)
   +0x020 Context          : (null)

http://www.driveronline.org/bbs/view.asp?tb=tipbbs&GotoPage=4&s_bulu=&s_key=&no=56

반응형
Posted by GreeMate
WinDbg 디버깅2007. 9. 23. 02:19
반응형

아래에서 핸들에 대한 주제를 올려주셨으니 !handle 명령에 대해서 알아보도록 하겠습니다. ^^

!handle은 특정 또는 모든 핸들에 대한 정보를 보여주는 명령입니다.

유저모드의 형식
!handle [Handle [UMFlags [TypeName]]]

커널모드의 형식
!handle [Handle [KMFlags [Process [TypeName]]]]

플래그의 자세한 설명은 언제나 그렇듯이 WinDbg Help 를 참조하시기 바랍니다. ^^
진행하면서 필요한 것만 간단히 설명하겠습니다.

아래의 예제들은 WinDbg Help !handle 에 있는 예제입니다.
WinDbg Help 에 쓸만한 예제도 있더군요. ^^

유저모드 먼저 간단히 살펴봅니다.

아래와 같이 명령어만 주고 인자를 생략하면 현재 프로세스의 핸들 테이블을 모두 보여주고
UMFlags는 기본값인 0x1 이 적용되어 handle type 만 보여주게 됩니다.

0:000> !handle
Handle 4
  Type          Section
Handle 8
  Type          Event
Handle c
  Type          Event
Handle 10
  Type          Event
Handle 14
  Type          Directory
Handle 5c
  Type          File
6 Handles
Type            Count
Event           3
Section         1
File            1
Directory       1

아래는 특정 핸들 8 을 보여주는 예제입니다.
UMFlags 에 f 를 주어 가능한 모든 Flag 를 다 켰습니다. 이 핸들에 대한 모든 정보를 보여줍니다.

0:000> !handle 8 f
Handle 8
  Type          Event
  Attributes    0
  GrantedAccess 0x100003:
         Synch
         QueryState,ModifyState
  HandleCount   2
  PointerCount  3
  Name         
  Object Specific Information
    Event Type Auto Reset
    Event is Waiting


커널모드에 대해서 살펴봅니다.

아래 예제는 Handle 에 0 을 주어 모든 핸들을 보이게 하고 KMFlags 에 4 를 주어 해제된 핸들도 포함하여 보이게 합니다.

kd> !handle 0 4
processor number 0
PROCESS 80559800  SessionId: 0  Cid: 0000    Peb: 00000000  ParentCid: 0000
    DirBase: 00039000  ObjectTable: e1000d60  TableSize: 380.
    Image: Idle

New version of handle table at e1002000 with 380 Entries in use

0000: free handle, Entry address e1002000, Next Entry fffffffe
0004: Object: 80ed5238  GrantedAccess: 001f0fff
0008: Object: 80ed46b8  GrantedAccess: 00000000
000c: Object: e1281d00  GrantedAccess: 000f003f
0010: Object: e1013658  GrantedAccess: 00000000
......
0168: Object: ffb6c748  GrantedAccess: 00000003 (Protected)
016c: Object: ff811f90  GrantedAccess: 0012008b
0170: free handle, Entry address e10022e0, Next Entry 00000458
0174: Object: 80dfd5c8  GrantedAccess: 001f01ff
......

사실 해제된 핸들을 특별히 볼 일은 별로 없으니 위와 같이 사용하는 경우는 없을 것 같네요.

좀 더 현실적인 예로 제 자리에서 그냥 !handle 을 사용한 결과를 보여드립니다.
현재 프로세스의 핸들 테이블을 모두 보고 싶을 때 사용하면 좋습니다.
KMFlags 의 기본값은 0x3 이라 좀 더 자세한 내용을 볼 수 있습니다.

lkd> !handle
processor number 0, process 80562f00
PROCESS 80562f00  SessionId: none  Cid: 0000    Peb: 00000000  ParentCid: 0000
    DirBase: 00039000  ObjectTable: e1003e60  HandleCount: 275.
    Image: Idle

Handle table at e16a9000 with 275 Entries in use
0004: Object: 867b5a00  GrantedAccess: 001f0fff Entry: e1004008
Object: 867b5a00  Type: (867b5040) Process
    ObjectHeader: 867b59e8
        HandleCount: 2  PointerCount: 72

0008: Object: 867b5340  GrantedAccess: 00000000 Entry: e1004010
Object: 867b5340  Type: (867b5e70) Thread
    ObjectHeader: 867b5328
        HandleCount: 1  PointerCount: 1

000c: Object: e1010478  GrantedAccess: 00000000 Entry: e1004018
Object: e1010478  Type: (867ab980) Key
    ObjectHeader: e1010460
        HandleCount: 1  PointerCount: 3
        Directory Object: 00000000  Name: \REGISTRY

0010: Object: e13aa168  GrantedAccess: 000f003f Entry: e1004020
Object: e13aa168  Type: (867ab980) Key
    ObjectHeader: e13aa150
        HandleCount: 1  PointerCount: 1
        Directory Object: 00000000  Name: \REGISTRY\MACHINE\SYSTEM\CONTROLSET001\CONTROL\SESSION MANAGER\MEMORY MANAGEMENT\PREFETCHPARAMETERS

...

보통 10 이라는 핸들이 어떤 Object 인지 궁금할 때 다음과 같이 사용합니다.

lkd> !handle 10
processor number 0, process 80562f00
PROCESS 80562f00  SessionId: none  Cid: 0000    Peb: 00000000  ParentCid: 0000
    DirBase: 00039000  ObjectTable: e1003e60  HandleCount: 275.
    Image: Idle

Handle table at e16a9000 with 275 Entries in use
0010: Object: e13aa168  GrantedAccess: 000f003f Entry: e1004020
Object: e13aa168  Type: (867ab980) Key
    ObjectHeader: e13aa150
        HandleCount: 1  PointerCount: 1
        Directory Object: 00000000  Name: \REGISTRY\MACHINE\SYSTEM\CONTROLSET001\CONTROL\SESSION MANAGER\MEMORY MANAGEMENT\PREFETCHPARAMETERS

모든 핸들을 보였던 바로 위 예제에 이 내용이 포함되어 있는 것을 볼 수 있습니다.

아래는 KMFlags 의 0x10 플래그(커널 핸들 테이블 보이기 플래그)를 사용하여 커널 핸들 테이블의 핸들 0x14 를 보는 예제입니다.

kd> !handle 14 13
processor number 0
PROCESS 80559800  SessionId: 0  Cid: 0000    Peb: 00000000  ParentCid: 0000
    DirBase: 00039000  ObjectTable: e1000d60  TableSize: 380.
    Image: Idle

Kernel New version of handle table at e1002000 with 380 Entries in use
0014: Object: e12751d0  GrantedAccess: 0002001f
Object: e12751d0  Type: (80ec8db8) Key
    ObjectHeader: e12751b8
        HandleCount: 1  PointerCount: 1
        Directory Object: 00000000  Name: \REGISTRY\MACHINE\SYSTEM\CONTROLSET001\CONTROL\SESSION MANAGER\EXECUTIVE


!handle 의 마지막 인자인 [Process [TypeName]] 을 활용하면 특정 프로세스의 핸들을 볼 수도 있고 특정 타입의 핸들을 볼 수도 있습니다.
필요할 때 활용하시면서 익혀 보시기 바랍니다.

http://www.driveronline.org/bbs/view.asp?tb=tipbbs&GotoPage=3&s_bulu=&s_key=&no=68

반응형
Posted by GreeMate
WinDbg 디버깅2007. 9. 23. 02:17
반응형

시스템 오브젝트의 정보를 보여주는 명령어입니다.

형식은 다음과 같이 사용할 수 있습니다.

!object Address
!object Path
!object 0 Name

아래 예제에서 각각의 경우에 대하여 설명합니다.

1) !object Address

오브젝트의 주소를 알고 있을 때 주소를 주면 오브젝트의 정보가 자세히 나옵니다.

아래 예제는 WinDbg Help 의 예제입니다.
!handle 의 결과를 보면 핸들이 어떤 오브젝트를 가리키는지 나옵니다.

kd> !handle
processor number 0
PROCESS 80a02920  Cid: 0002    Peb: 00000000  ParentCid: 0000
    DirBase: 0006c805  ObjectTable: 80a03788  TableSize:  54.
    Image: System
006c: Object: 80967768  GrantedAccess: 00100003
Object: 80967768  Type: (809d5c20) File
    ObjectHeader: 80967750
        HandleCount: 1  PointerCount: 1
        Directory Object: 00000000  Name: \WINNT\system32\config\software {Partition1}

이 정보는 !object 에서 나오는 결과와 동일합니다.

kd> !object 80967768
Object: 80967768  Type: (809d5c20) File
    ObjectHeader: 80967750
    HandleCount: 1  PointerCount: 1
    Directory Object: 00000000  Name: \WINNT\system32\config\software {Partition1}

오브젝트 주소를 알고 있다면 위와 같이 바로 !object 명령으로 자세한 정보를 볼 수 있습니다.


2) !object Path

!object 명령을 이용해서 오브젝트들의 구성을 볼 수 있습니다.
마치 winobj 유틸리티를 사용하는 것과 같은 느낌입니다.
먼저 Root 를 봅니다.

kd> !object \
Object: e1001690  Type: (81586490) Directory
    ObjectHeader: e1001678
    HandleCount: 0  PointerCount: 40
    Directory Object: 00000000  Name: \
    109 symbolic links snapped through this directory

    Hash Address  Type          Name
    ---- -------  ----          ----
     00  e1006850 Directory     ArcName
         81570218 Device        Ntfs
     01  e16839b8 Port          SeLsaCommandPort
     02  812d2818 Device        FatCdrom
     03  e1007160 Key           \REGISTRY
     05  e1711db8 Port          ThemeApiPort
     06  e1782888 Port          XactSrvLpcPort
     09  e14c4c38 Directory     NLS
     10  e1000150 SymbolicLink  DosDevices
     13  e135aa38 Port          SeRmCommandPort
     14  e16cdf18 Port          LsaAuthenticationPort
         814e8030 Device        Dfs
         813fdf18 Event         LanmanServerAnnounceEvent
     16  e1289f58 Directory     Driver
     19  e1006778 Directory     Device
     20  e13d8bb8 Directory     Windows
     21  813b1fc0 Event         SAM_SERVICE_STARTED
         e13d8a08 Directory     Sessions
     22  e13d8ae0 Directory     RPC Control
         e12e3400 Port          SmApiPort
         8131b400 Device        Fat
     23  e14c3030 Directory     BaseNamedObjects
         e1001510 Directory     KernelObjects
     24  e1289e80 Directory     FileSystem
         e1000308 Directory     GLOBAL??
     25  8129faa0 WaitablePort  NLAPublicPort
     26  e18cc1d0 Port          SmSsWinStationApiPort
         e1001428 Directory     ObjectTypes
     ...

Type 이 Directory 라고 되어 있는 것들은 하위에 다른 오브젝트들을 포함하고 있는 것입니다.
ObjectTypes 라는 Directory를 들여다 봅니다.

kd> !object \ObjectTypes
Object: e1001428  Type: (81586490) Directory
    ObjectHeader: e1001410
    HandleCount: 0  PointerCount: 25
    Directory Object: e1001690  Name: ObjectTypes

    Hash Address  Type          Name
    ---- -------  ----          ----
     00  81586490 Type          Directory
     01  8154d858 Type          Mutant
         81552ca0 Type          Thread
     03  81570a80 Type          FilterCommunicationPort
     05  8153c040 Type          Controller
     07  8154c040 Type          Profile
         8154dbf8 Type          Event
         81586660 Type          Type
     09  8154c880 Type          Section
         8154da28 Type          EventPair
         815862c0 Type          SymbolicLink
     10  8154cad0 Type          Desktop
     11  8154d2e8 Type          Timer
     12  8153c900 Type          File
         8154cca0 Type          WindowStation
     16  8153cca0 Type          Driver
     ...
     34  8154d4b8 Type          Semaphore

시스템에 존재하는 오브젝트 Type 들이 모두 보여집니다.

이번에는 FileSystem 이라는 Directory를 들여다 봅니다.

kd> !object \FileSystem
Object: e1289e80  Type: (81586490) Directory
    ObjectHeader: e1289e68
    HandleCount: 0  PointerCount: 23
    Directory Object: e1001690  Name: FileSystem

    Hash Address  Type          Name
    ---- -------  ----          ----
     00  81570340 Driver        Ntfs
     01  812d2040 Driver        Fastfat
         813c9040 Driver        NetBIOS
     02  815707c0 Driver        sr
     05  814284b0 Driver        Rdbss
     15  81425cc0 Driver        Msfs
     17  813c3cc0 Driver        MRxSmb
     18  8142a340 Device        UdfsCdRomRecognizer
     19  812d72e0 Driver        Srv
     24  81519628 Driver        Mup
         81537f38 Driver        RAW
     25  813bbf38 Driver        Npfs
         8149a368 Driver        Fs_Rec
     26  e1289da8 Directory     Filters
     30  813a0970 Driver        MRxVPC
     31  813d8bf8 Driver        Cdfs
     32  8137c138 Device        FatCdRomRecognizer
         813730e0 Device        CdfsRecognizer
         81570f38 Driver        FltMgr
     34  81382138 Device        FatDiskRecognizer
     35  812b9330 Driver        MRxDAV
     36  81456030 Device        UdfsDiskRecognizer

파일시스템으로 등록된 것들이 보이네요.
Filters Directory 로 한번 더 들어가 볼까요?

kd> !object \FileSystem\Filters
Object: e1289da8  Type: (81586490) Directory
    ObjectHeader: e1289d90
    HandleCount: 0  PointerCount: 4
    Directory Object: e1289e80  Name: Filters

    Hash Address  Type          Name
    ---- -------  ----          ----
     11  81570958 Device        FltMgrMsg
     31  81570698 Device        SystemRestore
     32  81570e10 Device        FltMgr

Filter Manager Device 인 FlgMgr 도 보이네요.

이처럼 시스템에 존재하는 Object 들의 구성을 마음대로 볼 수 있으므로 필요한 정보를 확인할 때 유용하게 사용할 수 있습니다.


3) !object 0 Name

Name 은 오브젝트의 Type 이름입니다.
특정 오브젝트를 찾을 때 사용할 수 있습니다.

File Type 인 것들을 찾아봅니다.

kd> !object 0 File
Scanning 1571 objects of type 'File'
Object: 8153be70  Type: (8153c900) File
    ObjectHeader: 8153be58
    HandleCount: 0  PointerCount: 1
Object: 815659a0  Type: (8153c900) File
    ObjectHeader: 81565988
    HandleCount: 0  PointerCount: 1
    Directory Object: 00000000  Name: \WINDOWS\system32\ntdll.dll {HarddiskVolume1}
Object: 814a2758  Type: (8153c900) File
    ObjectHeader: 814a2740
    HandleCount: 0  PointerCount: 1
    Directory Object: 00000000  Name: \WINDOWS\system32\drivers\vpc-8042.sys {HarddiskVolume1}
Object: 814a0090  Type: (8153c900) File
    ObjectHeader: 814a0078
    HandleCount: 0  PointerCount: 1
    Directory Object: 00000000  Name: \WINDOWS\system32\drivers\vpc-s3.sys {HarddiskVolume1}
Object: 81499918  Type: (8153c900) File
    ObjectHeader: 81499900
    HandleCount: 0  PointerCount: 1
    Directory Object: 00000000  Name: \WINDOWS\system32\drivers\vmsrvc.sys {HarddiskVolume1}
Object: 814991c0  Type: (8153c900) File
    ObjectHeader: 814991a8
    HandleCount: 0  PointerCount: 1
    Directory Object: 00000000  Name: \WINDOWS\system32\ntdll.dll {HarddiskVolume1}
Object: 8148a8e8  Type: (8153c900) File
    ObjectHeader: 8148a8d0
    HandleCount: 0  PointerCount: 1
    Directory Object: 00000000  Name: \WINDOWS\system32\drivers\vpc-8042.sys {HarddiskVolume1}
...

File Type 을 가진 모든 것들이 뿌려지므로 이 중에서 찾으려고 했던 특정 파일이 있는지 확인할 수 있습니다.

Driver Type 을 가지는 것들도 찾아봅니다.

kd> !object 0 Driver
Scanning 84 objects of type 'Driver'
Object: 8153c780  Type: (8153cca0) Driver
    ObjectHeader: 8153c768
    HandleCount: 0  PointerCount: 57
    Directory Object: e1289f58  Name: PnpManager
Object: 81581cf8  Type: (8153cca0) Driver
    ObjectHeader: 81581ce0
    HandleCount: 0  PointerCount: 4
    Directory Object: e1289f58  Name: ACPI_HAL
Object: 815819a0  Type: (8153cca0) Driver
    ObjectHeader: 81581988
    HandleCount: 0  PointerCount: 4
    Directory Object: e1289f58  Name: WMIxWDM
Object: 81537f38  Type: (8153cca0) Driver
    ObjectHeader: 81537f20
    HandleCount: 0  PointerCount: 5
    Directory Object: e1289e80  Name: RAW
Object: 81537a88  Type: (8153cca0) Driver
    ObjectHeader: 81537a70
    HandleCount: 0  PointerCount: 26
    Directory Object: e1289f58  Name: ACPI
Object: 81522948  Type: (8153cca0) Driver
    ObjectHeader: 81522930
    HandleCount: 0  PointerCount: 8
    Directory Object: e1289f58  Name: PCI
Object: 81575bd8  Type: (8153cca0) Driver
    ObjectHeader: 81575bc0
    HandleCount: 0  PointerCount: 6
    Directory Object: e1289f58  Name: isapnp
Object: 8151b518  Type: (8153cca0) Driver
    ObjectHeader: 8151b500
    HandleCount: 0  PointerCount: 5
    Directory Object: e1289f58  Name: IntelIde
...

시스템에 로드된 드라이버들의 리스트를 볼 수 있습니다.

몇가지 다른 타입의 오브젝트들에 대해서 해봤는데 다음과 같은 오류가 발생했습니다.

kd> !object 0 Process
Scanning 21 objects of type 'Process'
*** objects of the same type are only linked together if the 4000 flag is set in NtGlobalFlags
Total of 0 objects of type 'Process'

kd> !object 0 Thread
Scanning 312 objects of type 'Thread'
*** objects of the same type are only linked together if the 4000 flag is set in NtGlobalFlags
Total of 0 objects of type 'Thread'

kd> !object 0 Timer
Scanning 29 objects of type 'Timer'
*** objects of the same type are only linked together if the 4000 flag is set in NtGlobalFlags
Total of 0 objects of type 'Timer'

gflags.exe 를 사용해서 0x4000 플래그를 켜주면 정상적으로 보일 것 같기는 하지만 실제로 해 보지는 않았습니다.

사실 위에서 정상적으로 볼 수 있었던 File 과 Driver 같은 경우도 verifier 를 켰기 때문에 나오는 것이지 verifier 가 설정되지 않았다면 위와 같은 오류가 발생합니다.
이 점 때문에 일반적인 경우는 잘 써먹지 못하겠지만 특별히 이와 같은 디버깅이 필요할 경우를 만나시면 gflags.exe 로 플래그를 켜서 응용해 보시기 바랍니다. ^^

http://www.driveronline.org/bbs/view.asp?tb=tipbbs&GotoPage=3&s_bulu=&s_key=&no=69

반응형
Posted by GreeMate
WinDbg 디버깅2007. 9. 23. 02:16
반응형

오늘은 대드락 디버깅을 해 보겠습니다.

대드락이라함은 두 개의 쓰레드가 두 개의 동기화 객체를 엇갈리게 소유하고 있어서 교착상태에 빠진것을 의미합니다.

한 프로세스에 있는 두 개의 쓰레드가 대드락인 경우는 그 프로세스가 '응답없음' 상태일 꺼구요.
두 프로세스에 각각 존재하는 두 개의 쓰레드가 대드락인 경우는 두 프로세스가 '응답없음' 상태일 겁니다.

더욱 좋지 않는 경우는 대드락인 두 개의 동기화 객체중 하나라도 시스템(OS 또는 파일시스템 등)에서 사용하는 동기화 객체라면 이것이 풀리지 않음으로 인해서 시스템이 멈추는 현상이 발생합니다. (어떤 경우는 마우스 마저 움직이지 않고 화면이 완전히 얼어버립니다. ^^)

일반적으로 대드락의 원인을 찾는 것은 어려운 분석에 속하지만 쉬운 예제를 통해서 가볍게 접근해 보겠습니다. 이번 예제의 시나리오는 MyApp.exe 프로세스가 '응답없음' 상태인 것이 발견되어 원인을 찾아가는 과정입니다.
먼저 MyApp.exe 의 프로세스 정보를 찾아봅니다.

kd> !process 0 0 MyApp.exe
PROCESS 81570b90  SessionId: 0  Cid: 01e4    Peb: 7ffdf000  ParentCid: 01e8
    DirBase: 0bc9c000  ObjectTable: e20a9d60  HandleCount:  44.
    Image: MyApp.exe

프로세스 주소로 프로세스 정보를 확인합니다.

kd> !process 81570b90 
PROCESS 81570b90  SessionId: 0  Cid: 01e4    Peb: 7ffdf000  ParentCid: 01e8
    DirBase: 0bc9c000  ObjectTable: e20a9d60  HandleCount:  44.
    Image: MyApp.exe
    VadRoot 813898a8 Vads 58 Clone 0 Private 175. Modified 81. Locked 0.
    ...
        THREAD 8158c9f8  Cid 1e4.3b0  Teb: 7ffde000 Win32Thread: e1a53660 WAIT: (Executive) KernelMode Non-Alertable
            f9fdf480  Mutant - owning thread 81116030
        IRP List:
            81397b90: (0006,0094) Flags: 00000000  Mdl: 00000000
        Not impersonating
        ...

ChildEBP RetAddr 
f2208bac 804f82e4 nt!KiSwapContext+0x2e (FPO: [EBP 0xf2208be0] [0,0,4])
f2208bb8 804f24b2 nt!KiSwapThread+0x44 (FPO: [0,0,2])
f2208be0 f9fdf9e4 nt!KeWaitForSingleObject+0x1c0 (FPO: [Non-Fpo])
f2208c34 804e8185 MyDrv!MyDrvDeviceControl+0xe4 (FPO: [Non-Fpo]) (CONV: stdcall) [d:\project\test\mydrv\mydrv.c @ 272]
f2208c44 8055887c nt!IopfCallDriver+0x31 (FPO: [0,0,1])
f2208c58 805595a7 nt!IopSynchronousServiceTail+0x5e (FPO: [Non-Fpo])
f2208d00 80552468 nt!IopXxxControlFile+0x5a5
f2208d34 8052a421 nt!NtDeviceIoControlFile+0x28 (FPO: [Non-Fpo])

쓰레드가 하나 있는데 이 녀석이 깨어나지 않는 것이로군요.
콜스택의 마지막 부분에서 KeWaitForSingleObject 를 호출한 것이 보입니다.
KeWaitForSingleObject 를 호출한 MyDrv.sys 의 MyDrvDeviceControl 함수를 확인해 봅니다.

265: case MYDRV_IOCTL_MUTEX1:
266: KeWaitForSingleObject( &MutexA, Executive, KernelMode, FALSE, NULL );
267:
268: interval1.QuadPart = -30000000;
269: KeDelayExecutionThread( KernelMode, FALSE, &interval1 );
270: KeWaitForSingleObject( &MutexB, Executive, KernelMode, FALSE, NULL );
271:
272: KeReleaseMutex( &MutexA, FALSE ); // LINE 272
273: KeReleaseMutex( &MutexB, FALSE );

콜스택에서 표시하는 소스라인 272 는 KeWaitForSingleObject 가 리턴했을 때의 위치입니다. 따라서 현재 상태는 270 라인을 호출하고 있는 상태입니다. MutexB 를 얻어야 하는데 다른 쓰레드가 획득하고 있어서 얻지 못하고 있는 상황입니다.

우리는 어떤 쓰레드가 MutexB 를 획득하고 있는지를 찾아야 합니다.
이번 예제가 간단한 예제가 될 수 있는 이유는 KMUTEX 의 속성을 이용하기 때문입니다.
KMUTEX 는 내부에 자신을 소유한 쓰레드 정보를 포함하고 있습니다. 따라서 MutexB 의 내용을 보면 이 뮤텍스를 소유하고 있는 쓰레드를 알 수 있습니다.

kd> dd mydrv!MutexB
f9fdf480  00080002 00000000 8158ca68 8158ca68
f9fdf490  81116040 81116040 81116030 00000100

MutexB 의 주소는 f9fdf480 이네요.
dt 명령으로 내용을 자세히 봅니다.

kd> dt KMUTEX f9fdf480 
   +0x000 Header           : _DISPATCHER_HEADER
   +0x010 MutantListEntry  : _LIST_ENTRY [ 0x81116040 - 0x81116040 ]
   +0x018 OwnerThread      : 0x81116030
   +0x01c Abandoned        : 0 ''
   +0x01d ApcDisable       : 0x1 ''

중간에 OwnerThraed 라는 정보가 보입니다.
이것이 바로 이미 이 뮤텍스를 소유하고 있는 쓰레드를 나타냅니다.
쓰레드 정보를 봅니다.

kd> !thread 0x81116030
THREAD 81116030  Cid 428.60c  Teb: 7ffde000 Win32Thread: e20f6cb8 WAIT: (Executive) KernelMode Non-Alertable
    f9fdf4a0  Mutant - owning thread 8158c9f8
IRP List:
    81396c10: (0006,0094) Flags: 00000000  Mdl: 00000000
...
ChildEBP RetAddr  Args to Child             
f26ddbac 804f82e4 811160a0 81116030 804f24b2 nt!KiSwapContext+0x2e
f26ddbb8 804f24b2 81116240 817b6bd8 81396c10 nt!KiSwapThread+0x44
f26ddbe0 f9fdfa59 00000000 00000000 00000000 nt!KeWaitForSingleObject+0x1c0
f26ddc34 804e8185 8120ec98 81396c10 8069f2f0 MyDrv!MyDrvDeviceControl+0x159 (CONV: stdcall) [d:\project\test\mydrv\mydrv.c @ 287]
f26ddc44 8055887c 81412ac0 81396c80 81396c10 nt!IopfCallDriver+0x31
f26ddc58 805595a7 8120ec98 81396c10 81412ac0 nt!IopSynchronousServiceTail+0x5e
f26ddd00 80552468 000000a0 00000000 00000000 nt!IopXxxControlFile+0x5a5
f26ddd34 8052a421 000000a0 00000000 00000000 nt!NtDeviceIoControlFile+0x28

앞서 봤던 콜스택과 비슷한 콜스택이 보입니다.
하지만 소스라인이 다르네요. 소스코드를 확인해 봅니다.

280: case MYDRV_IOCTL_MUTEX2:
281: KeWaitForSingleObject( &MutexB, Executive, KernelMode, FALSE, NULL );
282:
283: interval2.QuadPart = -30000000;
284: KeDelayExecutionThread( KernelMode, FALSE, &interval2 );
285: KeWaitForSingleObject( &MutexA, Executive, KernelMode, FALSE, NULL );
286:
287:  KeReleaseMutex( &MutexB, FALSE ); // LINE 287
288: KeReleaseMutex( &MutexA, FALSE );

소스코드도 비슷해 보이지만 약간 다른 내용을 가지고 있습니다.
281 라인에서 이 쓰레드가 MutexB 를 얻은 것을 확인할 수 있습니다.
285 라인에서 MutexA 를 얻으려고 하지만 획득하지 못하고 대기하는 모습을 볼 수 있습니다.

MutexA 는 이미 앞의 쓰레드 8158c9f8 이 획득한 것을 266 라인에서 보이고 있습니다.
MutexA 의 OwnerThread 에 8158c9f8 이 들어있는지 확인해 봅니다.

kd> dd mydrv!MutexA
f9fdf4a0  00080002 00000000 811160a0 811160a0
f9fdf4b0  8158ca08 8158ca08 8158c9f8 00000100

kd> dt KMUTEX f9fdf4a0 
   +0x000 Header           : _DISPATCHER_HEADER
   +0x010 MutantListEntry  : _LIST_ENTRY [ 0x8158ca08 - 0x8158ca08 ]
   +0x018 OwnerThread      : 0x8158c9f8
   +0x01c Abandoned        : 0 ''
   +0x01d ApcDisable       : 0x1 ''

정확하게 0x8158c9f8 이 들어있네요.

말 그대로 대드락 이지요.
쓰레드 8158c9f8 는 MutexA 를 획득하고 MutexB 를 요청하고 있고
쓰레드 81116030 은 MutexB 를 획득하고 MutexA 를 요청하고 있습니다.
영원히 풀릴 수가 없겠죠.

대드락 분석은 여기까지 하고 나면 위와 같은 구조가 나오지 않도록 코드를 수정하는 일만 남습니다.
대드락이 발생하지 않도록 코드를 작성하는 한가지 팁은 동기화 객체를 두개 이상 획득하는 코드를 만들지 않는 것입니다.
동기화 객체를 하나 획득했으면 얼른 일을 처리하고 풀어주고 다른 동기화 객체를 요청하는 식으로 처리하는 것이지요.

이번 대드락 분석은 KMUTEX 의 속성을 이용해서 간단하게 해 볼 수 있었습니다.
뮤텍스에 이런 쓰레드 정보가 없었다면 훨씬 복잡한 분석을 해야 하는 상황으로 갔을 것입니다.
모든 쓰레드를 펼쳐놓고 콜스택을 일일히 봐가면서 대드락의 가능성을 점검해야 하는 분석이죠.
동기화 객체로 세마포어, 이벤트, 스핀락 등을 사용했다면 이런 분석을 해야 할 것입니다.
내부에 소유한 쓰레드 정보가 없기 때문입니다.

ERESOURCE 같은 경우도 내부에 쓰레드 정보가 있기 때문에 KMUTEX 와 비슷한 접근방법을 취할 수가 있습니다. !locks 명령어가 일부 분석을 도와 주기도 하구요.
하지만 왠지 기억에 분석이 쉽지 않았던 것 같은 기분이 드는데요. ^^
나중에 적절한 예제가 만들어지면 분석하는 시간을 가지도록 해 보겠습니다.

http://www.driveronline.org/bbs/view.asp?tb=tipbbs&GotoPage=2&s_bulu=&s_key=&no=73

반응형
Posted by GreeMate
WinDbg 디버깅2007. 9. 23. 02:14
반응형

멋진 글을 써야만 한다는 중압감을 벗어버리고 가벼운 글을 올려봅니다. ^^

.hh (Open HTML Help File)

이 명령어는 제가 늘 언급하는 WinDbg Help 를 띄워주는 일을 해 줍니다.
저도 예전에는 일일이 WinDbg Help 메뉴를 마우스로 클릭했었으나 이 명령을 알고난 다음부터 노가다 안하기로 했습니다.

.hh 알고 싶은 명령어

형식으로 사용하시면 WinDbg Help 가 뜨면서 "찾을 키워드 입력" 창에 제가 적어준 명령어가 적혀있습니다.
Enter 만 치면 바로 명령어의 사용법을 볼 수 있죠.

예제를 보겠습니다.

kd> .hh !object

위와 같이 명령하면 아래와 같은 화면이 나타납니다.


여기서 단지 Enter 만 한번 더 쳐 주시면 바로 내용이 나타납니다.

WinDbg 메뉴의 Help-Index 클릭하고 !object 쳐주는 일을 .hh 명령어 한방으로 처리한 것입니다.
빠르고 간편하죠.

그래서 고수일수록 WinDbg 에 Command 창 하나만 띄워놓고 사용하나 봅니다.
전 아직도 3~4개 띄워놓고 사용합니다만...
Command 창 하나만 남겨놓는 그날까지 열심히 공부해 봅시다. ^^

http://www.driveronline.org/bbs/view.asp?tb=tipbbs&GotoPage=2&s_bulu=&s_key=&no=75

반응형
Posted by GreeMate
WinDbg 디버깅2007. 9. 23. 02:13
반응형

덤프분석보다는 live debugging 중에 종종 사용하는 명령어들을 설명합니다.

.reboot (Reboot Target Computer)

커널디버깅을 하다가 시스템을 재부팅하고 싶을 때 사용합니다.
.reboot 명령어만 입력하면 아래와 같이 바로 재부팅 됩니다.

kd> .reboot
Shutdown occurred...unloading all symbol tables.
Waiting to reconnect...


.crash (Force System Crash)

커널디버깅을 하다가 현재 상태를 덤프파일로 남기고 싶을 때 사용합니다.
.crash 명령어만 입력하면 아래와 같이 덤프를 만들고 바로 재부팅 됩니다.

kd> .crash
Shutdown occurred...unloading all symbol tables.
Waiting to reconnect...

만들어지는 덤프파일은 시스템의 설정에 따라갑니다. (미니덤프 or 커널덤프 or 전체덤프)
그리고 생성되는 덤프파일의 BugCheck 코드는 BugCheck 0xE2(MANUALLY_INITIATED_CRASH)입니다.


.dump (Create Dump File)

커널모드에서는 시스템을 죽이지 않고 덤프파일을 생성하고 싶을 때 사용합니다.
유저모드에서는 프로세스를 죽이지 않고 덤프파일을 생성하고 싶을 때 사용합니다.

커널모드나 유저모드나 /f 옵션을 주면 전체덤프를 생성합니다.
세부적인 다양한 옵션이 있지만 저 같은 경우 무조건 다 들어있는 /f 만 사용합니다.
나머지는 모릅니다. ^^

다음은 커널모드에서 실행한 예입니다.

kd> .dump /f c:\temp\nocrash.dmp
Creating a full kernel dump over the COM port is a VERY VERY slow operation.
This command may take many HOURS to complete.  Ctrl-C if you want to terminate the command.
Creating c:\temp\nocrash.dmp - Full kernel dump
Percent written 0
Percent written 1
Percent written 2
Percent written 3
...

Serial 로 연결했더니 너무 느려서 중간에 끊었습니다. -_-;;;
1394 로 연결하면 시간이 많이 걸리지 않습니다.

유저모드에서 전체덤프를 생성할 때는 /f 옵션보다 /ma 가 조금 더 많은 정보가 있다고 권장하네요.

0:000> .dump /ma c:\temp\ie.dmp
Creating c:\temp\ie.dmp - mini user dump
Dump successfully written

mini dump 라고 나오지만 full dump 보다 크기도 크고 정보도 좀 더 들어간다고 합니다.

http://www.driveronline.org/bbs/view.asp?tb=tipbbs&GotoPage=2&s_bulu=&s_key=&no=81

반응형
Posted by GreeMate
WinDbg 디버깅2007. 9. 23. 02:11
반응형

Jeff 님의 예제를 그대로 들고와서 손을 좀 봤습니다.
그렇게라도 시작하니 일단 감은 좀 잡겠더라구요.

메모리 4GB 를 장착한 시스템에서 덤프를 받았는데 덤프상에서 물리 메모리 2GB 위쪽의 데이터는 덤프파일에 포함되지 않는 것 같아서 이것을 검증해 보고 싶었습니다. 물리 메모리 2GB 위쪽 전체를 뒤지는 방법으로... -_-;;; 물론 노가다는 스크립트가 합니다. ^^

스크립트 testmem.txt 는 다음과 같습니다.
물리주소 80000000 부터 ffffffff 까지 4000 씩 더해 가면서 살펴보는 스크립트입니다.

$$ WinDbg script to get data above 2GB of physical memory in complete memory dump
r $t0 = 0
.for (r $t0 = 80000000; (@$t0 < ffffffff ); r $t0 = @$t0 + 4000)
{
    dc /p @$t0
}

출력은 다음과 같습니다.

3: kd> $$><myscript\testmem.txt
Last set context:
Last set context:
80000000  ???????? ???????? ???????? ????????  ????????????????
Last set context:
80010000  ???????? ???????? ???????? ????????  ????????????????
...

8ffb0000  ???????? ???????? ???????? ????????  ????????????????
Last set context:
8ffc0000  ???????? ???????? ???????? ????????  ????????????????
Last set context:
...

fffe0000  ???????? ???????? ???????? ????????  ????????????????
Last set context:
ffff0000  ???????? ???????? ???????? ????????  ????????????????
Last set context:

살펴본 메모리들이 전혀 존재하지 않음을 간단하게 확인할 수 있었답니다.

http://www.driveronline.org/bbs/view.asp?tb=tipbbs&GotoPage=1&s_bulu=&s_key=&no=87

반응형
Posted by GreeMate
WinDbg 디버깅2007. 9. 23. 02:09
반응형

메모리릭과 마찬가지로 핸들릭도 원인을 찾기 어려운 문제중 하나입니다.

그 많은 핸들중에서 어떤 것이 릭이 발생하는 핸들인지 알아내는 것도 어렵고
도대체 어느 함수에서 그 핸들을 생성하고 닫지 않고 있는 것인지 알아내는 것도
어려운 문제입니다.

이 문제를 쉽게 풀어주는 명령어가 있으니 이것이 바로 !htrace 입니다.
XP 이후에서만 사용할 수 있고 커널모드 보다는 유저모드에서 좀 더 유용합니다.

자세한 사용법은 WinDbg Help 를 참고하시고 여기서는 사용예로 설명합니다.

일단 이 명령어는 그냥 사용할 수는 없습니다.
WinDbg Help 에는 해당 프로세스에 대해서 Application Verifier로 Detect invalid handle usage
옵션을 켜줘야 한다고 나와 있습니다. 반드시 이런 식으로 사용해야 한다면 무지 불편하겠지만
다행스럽게도 Applicaton Verifier 를 설치하지 않아도 아래와 같이 사용하면 동작합니다.

[사용예]
notepad.exe 가 핸들릭을 발생시키는 우리의 프로세스라고 가정합니다.

notepad.exe 를 실행하고 WinDbg 의 Attach Process 로 attach 합니다.
이 상태에서 !htrace 를 사용하면 다음과 같이 오류 메시지가 발생합니다.

0:001> !htrace
Handle tracing is not enabled for this process. Use "!htrace -enable" to enable it.

하지만 친절하게도 사용할 수 있는 방법을 가르쳐 주네요.
Application Verifier 없이도 -enable 옵션을 사용하면 동작합니다.

0:001> !htrace -enable
Handle tracing enabled.
Handle tracing information snapshot successfully taken.

이 때부터 사용하는 핸들은 내용 추적이 가능합니다.
g 로 진행시키고 notepad 에 몇자 적어놓고 나서 Break 를 잡습니다.

이제 !htrace 명령어를 수행하면 그동안 Open/Close 된 핸들의 정보가 나옵니다.
가장 유용한 정보는 Open/Close 가 수행된 콜스택을 보여준다는 부분입니다.
어떤 API 에 의해 Open 된 것인지 보이기 때문에 핸들의 타입을 정확히 알 수 있고
콜스택이 보이기 때문에 어떤 목적으로 어떤 행위를 하고 있었다는 것이 확인됩니다.

0:001> !htrace
--------------------------------------
Handle = 0x00000238 - CLOSE
Thread ID = 0x00000df8, Process ID = 0x00000708

0x77f56c74: ADVAPI32!RegCloseKey+0x00000076
0x3af3c261: imekr70!ImeToAsciiEx+0x00008278
0x3af3c285: imekr70!ImeToAsciiEx+0x0000829c
0x3af3b3c1: imekr70!ImeToAsciiEx+0x000073d8
0x77cf8734: USER32!InternalCallWinProc+0x00000028
0x77cf8816: USER32!UserCallWinProcCheckWow+0x00000150
0x77cfb89b: USER32!SendMessageWorker+0x000004a5
0x77d4b63b: USER32!SendMessageToUI+0x00000052
0x77d4c2de: USER32!ImeSetContextHandler+0x000001b8
0x77d4c6ec: USER32!ImeWndProcWorker+0x000002f4
--------------------------------------
Handle = 0x00000238 - OPEN
Thread ID = 0x00000df8, Process ID = 0x00000708

0x77f576eb: ADVAPI32!RegOpenKeyExA+0x00000119
0x3af3c232: imekr70!ImeToAsciiEx+0x00008249
0x3af3c285: imekr70!ImeToAsciiEx+0x0000829c
0x3af3b3c1: imekr70!ImeToAsciiEx+0x000073d8
0x77cf8734: USER32!InternalCallWinProc+0x00000028
0x77cf8816: USER32!UserCallWinProcCheckWow+0x00000150
0x77cfb89b: USER32!SendMessageWorker+0x000004a5
0x77d4b63b: USER32!SendMessageToUI+0x00000052
0x77d4c2de: USER32!ImeSetContextHandler+0x000001b8
0x77d4c6ec: USER32!ImeWndProcWorker+0x000002f4
--------------------------------------
Handle = 0x00000234 - CLOSE
Thread ID = 0x00000df8, Process ID = 0x00000708

0x746615d9: MSCTF!CCicEvent::Uninit+0x00000012
0x7467faff: MSCTF!CicMarshalWndProc+0x00000161
0x77cf8734: USER32!InternalCallWinProc+0x00000028
0x77cf8816: USER32!UserCallWinProcCheckWow+0x00000150
0x77cf89cd: USER32!DispatchMessageWorker+0x00000306
0x77cf8a10: USER32!DispatchMessageW+0x0000000f
0x01002a12: notepad!WinMain+0x000000dc
0x01007511: notepad!WinMainCRTStartup+0x00000174
0x7c816fd7: kernel32!BaseProcessStart+0x00000023
--------------------------------------
Handle = 0x00000230 - CLOSE
Thread ID = 0x00000df8, Process ID = 0x00000708

0x746615d9: MSCTF!CCicEvent::Uninit+0x00000012
0x7467faff: MSCTF!CicMarshalWndProc+0x00000161
0x77cf8734: USER32!InternalCallWinProc+0x00000028
0x77cf8816: USER32!UserCallWinProcCheckWow+0x00000150
0x77cf89cd: USER32!DispatchMessageWorker+0x00000306
0x77cf8a10: USER32!DispatchMessageW+0x0000000f
0x01002a12: notepad!WinMain+0x000000dc
0x01007511: notepad!WinMainCRTStartup+0x00000174
0x7c816fd7: kernel32!BaseProcessStart+0x00000023
--------------------------------------
Handle = 0x00000234 - OPEN
Thread ID = 0x00000df8, Process ID = 0x00000708

0x7c81325a: kernel32!OpenEventA+0x0000006a
0x7467d2a8: MSCTF!CCicEvent::Open+0x00000022
0x7467f672: MSCTF!HandleSendReceiveMsg+0x0000028c
0x7467faff: MSCTF!CicMarshalWndProc+0x00000161
0x77cf8734: USER32!InternalCallWinProc+0x00000028
0x77cf8816: USER32!UserCallWinProcCheckWow+0x00000150
0x77cf89cd: USER32!DispatchMessageWorker+0x00000306
0x77cf8a10: USER32!DispatchMessageW+0x0000000f
0x01002a12: notepad!WinMain+0x000000dc
0x01007511: notepad!WinMainCRTStartup+0x00000174
0x7c816fd7: kernel32!BaseProcessStart+0x00000023
--------------------------------------
Handle = 0x00000230 - OPEN
Thread ID = 0x00000df8, Process ID = 0x00000708

0x7c81325a: kernel32!OpenEventA+0x0000006a
0x7467d2a8: MSCTF!CCicEvent::Open+0x00000022
0x7467f424: MSCTF!HandleSendReceiveMsg+0x0000005c
0x7467faff: MSCTF!CicMarshalWndProc+0x00000161
0x77cf8734: USER32!InternalCallWinProc+0x00000028
0x77cf8816: USER32!UserCallWinProcCheckWow+0x00000150
0x77cf89cd: USER32!DispatchMessageWorker+0x00000306
0x77cf8a10: USER32!DispatchMessageW+0x0000000f
0x01002a12: notepad!WinMain+0x000000dc
0x01007511: notepad!WinMainCRTStartup+0x00000174
0x7c816fd7: kernel32!BaseProcessStart+0x00000023
--------------------------------------
Handle = 0x0000022c - CLOSE
Thread ID = 0x00000df8, Process ID = 0x00000708

0x746615d9: MSCTF!CCicEvent::Uninit+0x00000012
0x7467faff: MSCTF!CicMarshalWndProc+0x00000161
0x77cf8734: USER32!InternalCallWinProc+0x00000028
0x77cf8816: USER32!UserCallWinProcCheckWow+0x00000150
0x77cf89cd: USER32!DispatchMessageWorker+0x00000306
0x77cf8a10: USER32!DispatchMessageW+0x0000000f
0x01002a12: notepad!WinMain+0x000000dc
0x01007511: notepad!WinMainCRTStartup+0x00000174
0x7c816fd7: kernel32!BaseProcessStart+0x00000023
--------------------------------------
Handle = 0x00000228 - CLOSE
Thread ID = 0x00000df8, Process ID = 0x00000708

0x746615d9: MSCTF!CCicEvent::Uninit+0x00000012
0x7467faff: MSCTF!CicMarshalWndProc+0x00000161
0x77cf8734: USER32!InternalCallWinProc+0x00000028
0x77cf8816: USER32!UserCallWinProcCheckWow+0x00000150
0x77cf89cd: USER32!DispatchMessageWorker+0x00000306
0x77cf8a10: USER32!DispatchMessageW+0x0000000f
0x01002a12: notepad!WinMain+0x000000dc
0x01007511: notepad!WinMainCRTStartup+0x00000174
0x7c816fd7: kernel32!BaseProcessStart+0x00000023
--------------------------------------
Handle = 0x0000022c - OPEN
Thread ID = 0x00000df8, Process ID = 0x00000708

0x7c81325a: kernel32!OpenEventA+0x0000006a
0x7467d2a8: MSCTF!CCicEvent::Open+0x00000022
0x7467f672: MSCTF!HandleSendReceiveMsg+0x0000028c
0x7467faff: MSCTF!CicMarshalWndProc+0x00000161
0x77cf8734: USER32!InternalCallWinProc+0x00000028
0x77cf8816: USER32!UserCallWinProcCheckWow+0x00000150
0x77cf89cd: USER32!DispatchMessageWorker+0x00000306
0x77cf8a10: USER32!DispatchMessageW+0x0000000f
0x01002a12: notepad!WinMain+0x000000dc
0x01007511: notepad!WinMainCRTStartup+0x00000174
0x7c816fd7: kernel32!BaseProcessStart+0x00000023
--------------------------------------
Handle = 0x00000150 - CLOSE
Thread ID = 0x00000df8, Process ID = 0x00000708

0x74661eeb: MSCTF!CCicFileMappingStatic::Close+0x00000021
0x7467faff: MSCTF!CicMarshalWndProc+0x00000161
0x77cf8734: USER32!InternalCallWinProc+0x00000028
0x77cf8816: USER32!UserCallWinProcCheckWow+0x00000150
0x77cf89cd: USER32!DispatchMessageWorker+0x00000306
0x77cf8a10: USER32!DispatchMessageW+0x0000000f
0x01002a12: notepad!WinMain+0x000000dc
0x01007511: notepad!WinMainCRTStartup+0x00000174
0x7c816fd7: kernel32!BaseProcessStart+0x00000023
--------------------------------------
Handle = 0x00000228 - OPEN
Thread ID = 0x00000df8, Process ID = 0x00000708

0x7c81325a: kernel32!OpenEventA+0x0000006a
0x7467d2a8: MSCTF!CCicEvent::Open+0x00000022
0x7467f424: MSCTF!HandleSendReceiveMsg+0x0000005c
0x7467faff: MSCTF!CicMarshalWndProc+0x00000161
0x77cf8734: USER32!InternalCallWinProc+0x00000028
0x77cf8816: USER32!UserCallWinProcCheckWow+0x00000150
0x77cf89cd: USER32!DispatchMessageWorker+0x00000306
0x77cf8a10: USER32!DispatchMessageW+0x0000000f
0x01002a12: notepad!WinMain+0x000000dc
0x01007511: notepad!WinMainCRTStartup+0x00000174
0x7c816fd7: kernel32!BaseProcessStart+0x00000023
--------------------------------------

...

--------------------------------------
Parsed 0xB2 stack traces.
Dumped 0xB2 stack traces.

주의할 점은 같은 핸들에 대해서 Open 되고 Close 된 것이 모두 나오기 때문에
차근차근 확인해 보셔야 합니다.

이 정보만 가지고도 많은 도움이 되지만 핸들이 닫히지 않고 계속 남아 있는 릭
상황을 확인할 때는 -diff 옵션을 사용하면 더 좋습니다.

-diff 옵션을 사용하면 -enable 을 실행할 때 snapshot 잡힌 정보와 -diff 할 때의
정보를 비교하여 닫히지 않고 열려있는 핸들만 보여줍니다. 핸들릭을 잡는 상황이면
이렇게 보여지는 핸들중에서 문제의 핸들을 찾는 것이 훨씬 쉬울 것입니다.

0:001> !htrace -diff
Handle tracing information snapshot successfully taken.
0xb2 new stack traces since the previous snapshot.
Ignoring handles that were already closed...
Outstanding handles opened since the previous snapshot:
--------------------------------------
Handle = 0x000000f4 - OPEN
Thread ID = 0x00000df8, Process ID = 0x00000708

0x7c8094b1: kernel32!CreateFileMappingA+0x0000006e
0x746657f3: MSCTF!CSharedBlockNT::Init+0x00000147
0x746949f9: MSCTF!CSharedHeap::Init+0x00000072
0x74694b22: MSCTF!EnsureSharedHeap+0x0000004a
0x74680357: MSCTF!CProxy::proxy_Param+0x00000031
0x74680ec5: MSCTF!CProxyITfLangBarItemSink::OnUpdate+0x0000003c
0x7467cd8f: MSCTF!CLangBarItemMgr::OnUpdateHandler+0x00000036
0x77cf8734: USER32!InternalCallWinProc+0x00000028
0x77cf8816: USER32!UserCallWinProcCheckWow+0x00000150
0x77cf89cd: USER32!DispatchMessageWorker+0x00000306
0x77cf8a10: USER32!DispatchMessageW+0x0000000f
--------------------------------------
Handle = 0x000000cc - OPEN
Thread ID = 0x00000df8, Process ID = 0x00000708

0x7c80e98b: kernel32!CreateMutexA+0x00000065
0x7466245b: MSCTF!CCicMutex::Init+0x00000016
0x74664b22: MSCTF!CSharedBlockNT::Init+0x000000c5
0x746949f9: MSCTF!CSharedHeap::Init+0x00000072
0x74694b22: MSCTF!EnsureSharedHeap+0x0000004a
0x74680357: MSCTF!CProxy::proxy_Param+0x00000031
0x74680ec5: MSCTF!CProxyITfLangBarItemSink::OnUpdate+0x0000003c
0x7467cd8f: MSCTF!CLangBarItemMgr::OnUpdateHandler+0x00000036
0x77cf8734: USER32!InternalCallWinProc+0x00000028
0x77cf8816: USER32!UserCallWinProcCheckWow+0x00000150
0x77cf89cd: USER32!DispatchMessageWorker+0x00000306
--------------------------------------
Displayed 0x2 stack traces for outstanding handles opened since the previous snapshot.

두 개의 핸들이 계속 남아 있었음을 보이고 있습니다.
하나는 파일맵핑 핸들이고 하나는 뮤텍스 핸들이네요.

이제 콜스택을 잘 보면서 어떤 목적으로 열어놓은 핸들인지 확인하고 이것을 왜 닫지 않는지
소스코드를 보면서 문제의 원인을 찾으면 됩니다.

좀 더 시간이 있었으면 예제를 만들어서 보기 좋게 보여드렸을 텐데 요즘 시간이 녹녹치 않네요. ^^
복잡하지만 활용하실 수 있을 정도만 적어 봤습니다. 모두에게 도움이 되시기 바랍니다.

http://www.driveronline.org/bbs/view.asp?tb=tipbbs&GotoPage=1&s_bulu=&s_key=&no=92

반응형
Posted by GreeMate
WinDbg 디버깅2007. 9. 23. 02:08
반응형

두번째 스크립트는 핸들 테이블을 전부 보여주는 스크립트입니다.

덤프를 받아서 !handle 명령으로 핸들을 보고 싶은 경우가 있는데 어쩐 일인지 !handle
명령어가 오류가 발생하여 핸들 정보를 보여주지 못하는 경우가 있습니다.

어찌 할까 고심하다가 !process 명령에서 나오는 ObjectTable 이라는 부분이
결국 핸들 테이블이라는 사실을 알게되어 ObjectTable 을 따라가 보기로 했습니다.

kd> !process
PROCESS 866d1020  SessionId: 0  Cid: 09fc    Peb: 7ffde000  ParentCid: 02e0
    DirBase: 15662000  ObjectTable: 81bc0828  HandleCount: 295.
    Image: windbg.exe

ObjectTable 을 추적하는 과정이 쉽지는 않았지만 여러분들이 올려주신 핸들에 대한 자료와
정덕영 님의 "Windows 구조와 원리 그리고 Codes" 를 참고하여 해낼 수 있었습니다.

스크립트가 생각보다 복잡해서 괜히 보시는 분들 심란하게 하는게 아닌가 싶으면서도
언젠가 이런 것이 필요하신 분들이 그 때 가서라도 분석하며 활용할 수 있을 것 같아서
일단 공유합니다. 물론 저 개인의 갈무리이기도 합니다. ^^

이번 스크립트를 작성하면서 알게된 사실...

1. $t0 ~ $t19 는 미리 정의된 가상 레지스터(Pseudo-Register)이다.
   r $t0 = 0 이런식으로 쓰는 이유가 레지스터로 취급하기 때문이었더군요.
   r eax = 0 이렇게 사용하는 것과 같습니다.
   그래서 값을 읽을 때도 @eax 하는 것 처럼 @$t0 이렇게 하는 거구요.

2. 스트립트로 파라미터를 전달할 때는 명령이 약간 달라집니다.
   $$>a<myscript... 로 시작해야 합니다.
   a 를 끼워넣어서 파라미터가 존재한다는 것을 알리는 것입니다.

이제 스크립트를 보입니다.
주석은 자세히 달았으나 보기 쉽지는 않을 거라는거... 죄송합니다. ^^

$$
$$ WinDbg script to display ObjectTable in a process
$$
$$ 프로세스 정보중 ObjectTable 의 주소를 입력받아서 옵젝트 테이블(핸들 테이블)에
$$ 존재하는 모든 옵젝트(핸들의 대상)의 타입을 보여준다.
$$
$$ 대상 OS 는 Win2k 이다.
$$ XP 는 핸들 테이블 구조가 다르므로 이 스크립트를 적용할 수 없다.
$$
$$ 사용예) kd> $$>a<myscript\display_2k_objtbl.txt 81bc0828 
$$ Written by GreeMate
$$

$$ 파라미터로 전달된 arg1 값을 가상 레지스터 $t10 에 대입한다.
r $t10 = ${$arg1}
.printf "ObjectTable: %x\n", @$t10

$$ ObjectTable 은 _HANDLE_TABLE 타입이므로 C++ 형식으로 접근해서 Table 주소를 구한다.
r $t10 = @@c++(((nt!_HANDLE_TABLE *)@$t10)->Table)
.printf "ObjectTable->Table: %x\n", @$t10

$$ Win2k 핸들 테이블은 3 Level 포인터로 256개씩 구성되어 있으므로 순차적으로 접근한다.
.for (r $t0 = 0; (@$t0 < 0n256); r $t0 = @$t0 + 1)
{
  $$ Table 포인터가 0 이면 중지하고 아니면 포인터의 값을 $t11 에 대입한다.
  .if (@$t10 == 0) { .break }
  r $t11 = poi(@$t10)

  $$ 다음 레벨의 포인터 테이블을 순차적으로 접근한다.
  .for (r $t1 = 0; (@$t1 < 0n256); r $t1 = @$t1 + 1)
  {
    $$ Table 포인터가 0 이면 중지하고 아니면 포인터의 값을 $t12 에 대입한다.
    .if (@$t11 == 0) { .break }
    r $t12 = poi(@$t11)
 
    $$ 다음 레벨의 포인터 테이블을 순차적으로 접근한다.
    .for (r $t2 = 0; (@$t2 < 0n256); r $t2 = @$t2 + 1)
    {
      .if (@$t12 == 0) { .break }
 
      $$ 실제 테이블에는 ObjectHeader 포인터와 AccessMask 가 저장되어 있다.
      $$ 따라서 8 바이트씩 건너 뛰어야 ObjectHeader 를 순차적으로 접근할 수 있다.

      r $t19 = @$t12 + (@$t2 * 8)
   
      $$ 저장된 ObjectHeader 포인터가 0 이면 건너뛴다.
      .if (poi(@$t19) == 0) { .continue }

      $$ 저장된 ObjectHeader 포인터는 특별한 목적으로 최상위 비트가 Clear 되어 저장되므로 최상위비트를 켜준다.
      r $t19 = poi(@$t19) + 0x80000000
      .printf "Object Header: %x\n", @$t19
   
      $$ $t19는 ObjectHeader이므로 C++ 형식으로 접근해서 Type 필드를 구한다.
      r $t19 = @@c++(((nt!_OBJECT_HEADER *)@$t19)->Type)
      .printf "Object Type : %x\n", @$t19

      $$ ObjectType 구조체에서 Name 필드를 표시한다.
      dt nt!_OBJECT_TYPE Name @$t19
    }
 
    $$ 테이블의 다음 엔트리로
    r $t11 = @$t11 + 4
    }

  $$ 테이블의 다음 엔트리로
  r $t10 = @$t10 + 4
}

실행예>

0: kd> $$>a<myscript\display_2k_objtbl.txt 81bc0828
ObjectTable: 81bc0828
ObjectTable->Table: e1e5f000
Object Header: e1e58078
Object Type : 8207c880
   +0x040 Name : _UNICODE_STRING "Section"
Object Header: 81bc1008
Object Type : 8207f1a0
   +0x040 Name : _UNICODE_STRING "Event"
Object Header: 81bc03a8
Object Type : 8207f1a0
   +0x040 Name : _UNICODE_STRING "Event"
Object Header: 81bbffc8
Object Type : 8207f1a0
   +0x040 Name : _UNICODE_STRING "Event"
Object Header: 81c4e238
Object Type : 82086040
   +0x040 Name : _UNICODE_STRING "Directory"
Object Header: 81ba6d08
Object Type : 8207f1a0
   +0x040 Name : _UNICODE_STRING "Event"
Object Header: 81bbfca8
Object Type : 8207f1a0
   +0x040 Name : _UNICODE_STRING "Event"
Object Header: 81d19bb8
Object Type : 82086040
   +0x040 Name : _UNICODE_STRING "Directory"
...

http://www.driveronline.org/bbs/view.asp?tb=tipbbs&GotoPage=1&s_bulu=&s_key=&no=93

반응형
Posted by GreeMate