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