WDK와 WinDbg를 이렇게 깔끔하게 제공하는 페이지가 있었다니...ㅋㅋ
'WinDbg'에 해당되는 글 37건
- 2014.08.05 WDK and WinDbg downloads
- 2012.07.17 [Tip] Kernel Debugging with WinDbg
- 2010.05.25 [WinDbg 사용] VirtualKd 연동 2
- 2010.04.21 [WinDbg 사용] DriverEntry 찾기 2
- 2009.07.20 WinDbg로 쉽게 배우는 Windows Debugging 17
- 2008.06.17 [WinDbg 명령] !stacks
- 2008.01.22 [WinDbg 사용] 익스텐션(extension) DLL 예제
- 2007.10.14 [WinDbg 사용] 심볼 스토어 생성
- 2007.09.23 [WinDbg 사용] WinDbg 사용팁 정리
- 2007.09.23 [WinDbg 명령] !process
매우 기초적인 WinDbg 사용 방법에 대한 글이지만 간략히 잘 정리되어 있어 링크해 봅니다.
처음 시작하시는 분들이 참고하시기에 괜찮을 것 같네요. ^^
http://www.cmlab.csie.ntu.edu.tw/~cathyp/eBooks/WindowsNT/Driver/kernel_debugging_tutorial.pdf
http://hexblog.com/2010/04/kernel_debugging_with_ida_pro_1.html
IDA로 커널 디버깅을 할 수 있다는 포스팅을 보고 따라해 보려고 했는데요.
IDA까지 안쓰더라도 VirtualKd를 설치해서 VMWare와 WinDbg를 연결해 커널디버깅을 할 수 있더군요.
그런데 이게 감동입니다.
분명 커널 디버깅인데 코드를 trace하는 속도가 거의 APP 디버깅과 같더군요.
이 좋은 걸 이제야 알다니... 흑흑
간혹 악성 드라이버를 분석할 일이 있는데 이 녀석의 시작점인 DriverEntry부터 보고 싶은 경우 어찌할 바를 몰라 고민하는 경우가 있었습니다.
그러다 생각한 것이 커널 내부함수인 IopLoadDriver로 부터 따라가서 들어가는 방법인데요. 노가다가 좀 심합니다. 그래도 정리해 보면 좋을 것 같아서 포스팅해 봅니다. (누가 좀 효율적인 방법 좀 알면 알려주세요. -_-a)
XP SP2 기준이구요 예제로 Null 드라이버를 로드해 봤습니다.
먼저 IopLoadDriver에 브레이크 포인트를 겁니다.
kd> bl
0 e 805a5ba3 0001 (0001) nt!IopLoadDriver
g로 실행하고 나서 OsrLoader 같은 것으로 Null 드라이버를 로드합니다. 그러면 드라이버를 로드하는 과정에서 브레이크 포인트가 걸립니다.
nt!IopLoadDriver:
805a5ba3 8bff mov edi,edi
kd> k
ChildEBP RetAddr
f9e93d4c 805a627c nt!IopLoadDriver
f9e93d74 804e626b nt!IopLoadUnloadDriver+0x45
f9e93dac 8057f16b nt!ExpWorkerThread+0x100
f9e93ddc 804fa27a nt!PspSystemThreadStartup+0x34
00000000 00000000 nt!KiThreadStartup+0x16
콜스택을 보면 시스템에서 드라이버를 로드하는 과정임을 볼 수 있습니다. IopLoadDriver 시작위치에서 멈췄네요.
이제 새로운 명령어인 pc(Step to Next Call)을 사용해 볼껀데요.
이 명령을 사용하면 현재 실행주소로부터 다음 call을 만날 때까지 쭉~ 실행됩니다.
다음 call에서 멈추지요. 자... 이제 pc를 수행합니다.
nt!IopLoadDriver+0x4b:
805a5bee e836b0fcff call nt!NtQueryKey (80570c29)
NtQueryKey를 호출하는 부분에서 멈췄네요.
앞뒤의 코드를 모두 보이면 아래와 같습니다.
805a5be2 895d9c mov dword ptr [ebp-64h],ebx
805a5be5 899d78ffffff mov dword ptr [ebp-88h],ebx
805a5beb 895da8 mov dword ptr [ebp-58h],ebx
805a5bee e836b0fcff call nt!NtQueryKey (80570c29)
805a5bf3 3d05000080 cmp eax,80000005h
805a5bf8 740b je nt!IopLoadDriver+0x6a (805a5c05)
805a5bfa 3d230000c0 cmp eax,0C0000023h
805a5bff 0f856d330400 jne nt!IopLoadDriver+0x5e (805e8f72)
코드에 특이사항이 없으므로 다시 pc를 실행하여 다음 call까지 실행하게 합니다.
nt!IopLoadDriver+0x78:
805a5c13 e8ac6ffaff call nt!ExAllocatePoolWithTag (8054cbc4)
메모리를 할당하는 부분이네요. 원하는 부분이 아니므로 또 pc를 합니다.
엔터만 쳐서 이전 명령을 그대로 수행하는 WinDbg의 기능을 사용해 보죠.
nt!IopLoadDriver+0x99:
805a5c34 e8f0affcff call nt!NtQueryKey (80570c29)
kd>
nt!IopLoadDriver+0xca:
805a5c65 e85a6ffaff call nt!ExAllocatePoolWithTag (8054cbc4)
kd>
nt!IopLoadDriver+0x114:
805a5caf e8cd46f5ff call nt!RtlAppendUnicodeToString (804fa381)
kd>
nt!IopLoadDriver+0x11f:
805a5cba e8d75af6ff call nt!HeadlessKernelAddLogEntry (8050b796)
kd>
nt!IopLoadDriver+0x1e6:
805a5cd8 e8c956f3ff call nt!ExAcquireResourceSharedLite (804db3a6)
kd>
nt!IopLoadDriver+0x1fd:
805a5cf5 e8ac26f6ff call nt!RtlEqualString (805083a6)
kd>
nt!IopLoadDriver+0x1fd:
805a5cf5 e8ac26f6ff call nt!RtlEqualString (805083a6)
관심있는 부분까지 계속 진행해야 하는데 이 쯤에서 RtlEqualString이 반복됩니다.
루프인 것 같아서 뒤부분의 코드를 살펴 봅니다.
805a5cfc 0f8531330400 jne nt!IopLoadDriver+0x23a (805e9033)
805a5d02 8b3f mov edi,dword ptr [edi]
805a5d04 ebdd jmp nt!IopLoadDriver+0x208 (805a5ce3)
805a5d06 2e005300 add byte ptr cs:[ebx],dl
805a5d0a 59 pop ecx
805a5d0b 005300 add byte ptr [ebx],dl
805a5d0e 0000 add byte ptr [eax],al
805a5d10 0000 add byte ptr [eax],al
805a5d12 8bce mov ecx,esi
805a5d14 e83779f3ff call nt!ExReleaseResourceLite (804dd650)
루프 바깥 부분에 브레이크 포인트를 걸어주고 그냥 go를 해버려서 루프를 빠져나가려고 합니다.
루프 바깥에 브레이크 포인트를 걸 지점을 찾아야 하는데 앞에서 call 한 흐름을 보면 다행히 RtlEqualString이 반복되기 직전에 ExAcquireResourceSharedLite를 호출한 것이 보이네요.
그러므로 ExReleaseResourceLite에 걸어주면 적당해 보입니다.
kd> bl
0 e 805a5ba3 0001 (0001) nt!IopLoadDriver
1 e 805a5d14 0001 (0001) nt!IopLoadDriver+0x212
kd> g
Breakpoint 1 hit
nt!IopLoadDriver+0x212:
805a5d14 e83779f3ff call nt!ExReleaseResourceLite (804dd650)
루프를 빠져나오면서 브레이크 포인트가 걸렸습니다.
이제부터는 다시 call을 확인하면서 진행합니다.
nt!IopLoadDriver+0x222:
805a5d24 e8dcfbffff call nt!IopBuildFullDriverPath (805a5905)
kd>
nt!IopLoadDriver+0x31a:
805a5d3e e806fdffff call nt!IopGetDriverNameFromKeyNode (805a5a49)
kd>
nt!IopLoadDriver+0x36b:
805a5d8f e8dff4ffff call nt!MmLoadSystemImage (805a5273)
kd>
nt!IopLoadDriver+0x428:
805a5da2 e82e4af5ff call nt!RtlImageNtHeader (804fa7d5)
kd>
nt!IopLoadDriver+0x43a:
805a5db4 e829200000 call nt!IopPrepareDriverLoading (805a7de2)
kd>
nt!IopLoadDriver+0x47f:
805a5df0 e8590bfcff call nt!ObCreateObject (8056694e)
kd>
nt!IopLoadDriver+0x4c7:
805a5e3c e89449f5ff call nt!RtlImageNtHeader (804fa7d5)
kd>
nt!IopLoadDriver+0x4fb:
805a5e70 e8ae05fcff call nt!ObInsertObject (80566423)
kd>
nt!IopLoadDriver+0x536:
805a5ea0 e84301fcff call nt!ObReferenceObjectByHandle (80565fe8)
kd>
nt!IopLoadDriver+0x547:
805a5eb1 e8032ffcff call nt!NtClose (80568db9)
kd>
nt!IopLoadDriver+0x563:
805a5ecd e8f26cfaff call nt!ExAllocatePoolWithTag (8054cbc4)
kd>
nt!IopLoadDriver+0x5b1:
805a5f1b e8a46cfaff call nt!ExAllocatePoolWithTag (8054cbc4)
kd>
nt!IopLoadDriver+0x5e8:
805a5f3d e8fed7fdff call nt!NtQueryObject (80583740)
kd>
nt!IopLoadDriver+0x61e:
805a5f59 e8666cfaff call nt!ExAllocatePoolWithTag (8054cbc4)
kd>
nt!IopLoadDriver+0x669:
805a5fa4 ff572c call dword ptr [edi+2Ch]
뭔가 로드되고 있는 것처럼 보이지 않나요? ^^
마지막 call이 제가 찾던 부분입니다. 어떤 함수주소를 호출하는게 보이죠?
저는 이것이 로드될 드라이버의 DriverEntry이기를 바라고 있죠.
앞쪽 코드를 보면 push 두번이 보이므로 2개의 파라미터를 가진다는 것을 알 수 있습니다.
805a5f9d ffb570ffffff push dword ptr [ebp-90h]
805a5fa3 57 push edi
805a5fa4 ff572c call dword ptr [edi+2Ch] ds:0023:81593884={Null!DriverEntry (fa13159a)}
805a5fa7 3bc3 cmp eax,ebx
그런데 DriverEntry가 2개의 파라미터를 가지자나요.
DriverEntry(
__in struct _DRIVER_OBJECT *DriverObject,
__in PUNICODE_STRING RegistryPath
)
그래서 현재 스택에 push된 파라미터를 확인해 보니 DriverObject와 RegistryPath가 확인됩니다.
f9e9bc84 814a9330 814fe000 00000000 f657bcf4
kd> !drvobj 814a9330
Driver object (814a9330) is for:
\Driver\Null
Driver Extension List: (id , addr)
Device Object list:
kd> dS 814fe000
814fe008 "\REGISTRY\MACHINE\SYSTEM\Control"
814fe048 "Set001\Services\Null"
DriverEntry 진입점을 찾았네요.
그런데 역시 노가다는 심하네요. ^^
드디어 출간되었다.
디버깅 책을 하나 썼으면 좋겠다라고 상상했을 때로부터 따지면 6년만에 이루어진 것이다.
디버깅 책을 쓰기로 결심하고 시작한 후로는 2년만에 해 낸것이다. 후련하다. ^^
WinDbg나 디버깅에 대해 질문하는 사람들이 의외로 많기에 기본적인 내용만 담긴 핸드북 같은 것만 있어도 참 좋겠다라는 생각에서 시작한 작업이었다. 덕분에 오히려 내가 가장 큰 수혜자가 된 것 느낌이다. 나부터 까먹은 것들을 들춰볼 수 있기 때문이고 누군가 질문을 할 때 여기를 봐라 하고 던져줄 수 있기 때문이다.
나오고 나니 느낌이 좀 묘하다. 미처 생각치 못했는데 마치 자식과 같은 느낌이라고나 할까...
어떻게 내놓기는 했는데 이 녀석이 앞으로 잘 커나갈 수 있을지 걱정도 되고 사회에 도움이 좀 되는 녀석이 되어야 하는데 그렇게 잘 가르킨 것인지 돌아보게 되기도 하고 말이다.
많은 사람들에게 도움을 주는 녀석이 되기를 기대해 본다.
커널스택을 보여주는 명령입니다.
모든 프로세스의 모든 쓰레드를 보여주기 때문에 다음과 같이 명령하면 !process 0 7 과 같은 역할을 합니다.
!stacks 2
뒤의 숫자 2 를 1 이나 0 으로 사용할 수도 있는데 숫자가 작을 수록 더 간단한 정보를 보여줍니다.
!process 0 7 과 비슷한 출력을 보이는 명령어를 구지 설명하는 이유는 !stacks 만의 특출난 기능이 있기 때문인데요.
!stacks 2 FilterString
처럼 뒤에 FilerString 을 주면 FilterString 이 포함된 콜스택만 보여줍니다. XP 이상에서만 지원하는 기능이지만 매우 편리한 기능입니다. 예를 들어 시스템이 대드락이나 행에 빠져있을 경우 자신의 드라이버에서 행이 걸려 있는지 간단하게 확인할 수 있습니다. !process 0 7 을 사용하면 모든 쓰레드의 콜스택을 펼쳐놓고 찾아야 하지만 이렇게 하면 콜스택에 드라이버가 존재하는 쓰레드만 보여주기 때문에 출력 내용이 간단하고 찾기가 수월해 집니다.
예제)
NDIS 드라이버가 걸려있는 쓰레드만 찾아 봅니다.
kd> !stacks 2 NDIS
Proc.Thread .Thread Ticks ThreadState Blocker
Max cache size is : 1048576 bytes (0x400 KB)
Total memory in cache : 0 bytes (0 KB)
Number of regions cached: 0
0 full reads broken into 0 partial reads
counts: 0 cached/0 uncached, 0.00% cached
bytes : 0 cached/0 uncached, 0.00% cached
** Prototype PTEs are implicitly decoded
[863a5490 System]
4.00006c 863a75b8 0001015 Blocked nt!KiSwapContext+0x2e
nt!KiSwapThread+0x46
nt!KeRemoveQueue+0x20e
NDIS!ndisWorkerThread+0x30
nt!PspSystemThreadStartup+0x34
nt!KiThreadStartup+0x16
[8613eda0 smss.exe]
[85ff21c0 csrss.exe]
[8611d0a0 winlogon.exe]
[8610a1b8 services.exe]
[85ff91d8 lsass.exe]
[860f37c8 svchost.exe]
...
깔끔하게 NDIS 가 생성해 놓은 WorkerThread 하나만 나왔네요. 현재 다른 쓰레드에서는 NDIS 드라이버의 기능이 호출되지 않고 있다는 것을 알 수 있습니다.
http://www.driveronline.org/bbs/view.asp?tb=tipbbs&GotoPage=1&s_bulu=&s_key=&no=102
오늘은 제가 어떤 익스텐션 DLL 을 만들었는지 소개합니다.
목적은 "커널덤프 파일에서 파일 옵젝트를 가지고 커널덤프에 존재하는 파일데이터를 모아서 실제 파일로 만드는 것" 입니다.
먼저 익스텐션 DLL 작성에는 두가지가 있다는 점을 알아두셔야 합니다.
1. DbgEng extension DLL
2. WdbgExts estension DLL
1번은 dbgeng.h 에 있는 Debugger Engine API 를 사용하여 익스텐션 DLL 을 만드는 것을 말합니다. Debugger Engine API 는 디버거를 만들 수 있을 정도의 기능을 가졌다고 합니다. 막강하다는 느낌과 함께 복잡할 것 같다는 생각이 드네요. ^^
2번은 wdbgexts.h 에 있는 WdbgExts API 를 사용하여 익스텐션 DLL 을 만드는 것을 말합니다. Old style extension DLL 이라고도 하네요. Old style 이라 그런지 처음 배우기는 이게 훨씬 쉬운 것 같아 이걸 선택했습니다. ^^
지난번에 소개했던 sdk\samples\simplext 가 바로 Old style extension DLL 예제입니다.
소스는 다음 두개입니다.
exts.c - WdbgExts extension DLL 기본골격
simple.c - 예제 command 들
exts.c 는 전혀 수정하지 않고 simple.c 만 myext.c 로 이름을 바꾸고 여기에 제가 원하는 명령 ef (extract file) 를 작성했습니다. 물론 SOURCE 파일도 수정해서 myext.dll 이 만들어지도록 했습니다.
그리고 ntddk.h 를 포함할 수가 없어서 nttype.h 를 별도로 만들어서 NT 커널 구조체를 사용합니다.
myext.dll 이 빌드되면 역시 WinDbg 설치폴더 밑의 winext 폴더로 복사하고 명령할 땐 다음과 같이 합니다. 먼저 help 를 보지요.
kd> !myext.help
Help for extension dll myext.dll
help - Shows this help
ef <fileobject address> <filename> - It extracts file from shared section
ef 명령은 FileObject 주소를 하나 골라서 넣어야 합니다. 만들어질 파일 이름하구요.
kd> !myext.ef 85f636e0 C:\temp\test.file
Creating "C:\temp\test.file" with file object(85f636e0)
SHARED_CACHE_MAP
================
FileSize : 12671
FileSize(Hex) : 0x317f
InitialVacbs[0] : 0x86792fa0
InitialVacbs[1] : 0x0
InitialVacbs[2] : 0x0
InitialVacbs[3] : 0x0
Vacbs : 0x85f04638
FileObject : 0x85f636e0
.Done!
출력에서 짐작하시겠지만 FileObject 로부터 SectionObjectPointer, SharedCacheMap, Vacb를 따라가서 시스템 캐쉬의 메모리를 가져오고 이것들을 모아서 파일로 만드는 것입니다. 현재는 DataSectionObject 나 ImageSectionObject 는 따라가지 않습니다. SharedCacheMap 만을 따라가서 파일을 만듭니다.
따라서 SectionObjectPointer 나 SharedCacheMap 이 NULL 이면 시스템캐쉬에 파일이 올라와 있지 않다는 뜻이고 이런 FileObject 에 대해서는 파일을 만들어내지 못합니다. 생각보다 이런 경우가 많습니다. 이 익스텐션 명령이 모든 FileObject 를 파일로 만들어 줄거라는 환상은 갖지 마시라는 뜻입니다. ^^
SharedCacheMap 이 존재하더라도 파일이 한번이라도 읽힌 적이 있어서 캐쉬에 있을 경우에만 유효한 데이터를 가져옵니다. 파일의 특정영역이 읽힌적이 없어서 캐쉬에 없다면 파일을 만들 때 이 영역은 0으로 채워서 만듭니다. 즉, 불완전한 파일이 만들어지게 됩니다. 이점도 반드시 기억해 주시구요.
이 과정은 "Windows 구조와 원리 그리고 Codes", "Windows Internals" 를 참고해서 만들었습니다. 여러분도 책을 참고해서 구현하실 수 있을 겁니다.
이제부터 구현한 내용을 약간 기술적으로 설명해 봅니다.
머리 아프신분들은 안보셔도 됩니다. ^^
!fileobj 명령을 사용하면 !ef 명령 안에서 하는 모든 동작을 하고 정보를 보여줍니다.
다만 파일로 만들어 주지만 않을 뿐이죠.
kd> !fileobj 85f636e0
\TestProj\DbgExt\myext\sample.txt
Device Object: 0x867b4900 \Driver\Ftdisk
Vpb: 0x8676e530
Access: Read SharedRead
Flags: 0x44042
Synchronous IO
Cache Supported
Cleanup Complete
Handle Created
File Object is currently busy and has 0 waiters.
FsContext: 0xe170dd90 FsContext2: 0xe170dee8
CurrentByteOffset: 317f
Cache Data:
Section Object Pointers: 866131dc
Shared Cache Map: 85f04608 File Offset: 317f in VACB number 0
Vacb: 86792fa0
Your data is at: cf08317f
데이터가 존재하는 메모리 주소까지 보여줍니다. !ef 명령에서는 저 메모리 주소를 따라가서 파일로 저장해 주는 과정을 수행합니다.
FileObject 부터 따라가는 과정을 보면
kd> dt _FILE_OBJECT 85f636e0
ntdll!_FILE_OBJECT
+0x000 Type : 5
+0x002 Size : 112
+0x004 DeviceObject : 0x867b4900 _DEVICE_OBJECT
+0x008 Vpb : 0x8676e530 _VPB
+0x00c FsContext : 0xe170dd90
+0x010 FsContext2 : 0xe170dee8
+0x014 SectionObjectPointer : 0x866131dc _SECTION_OBJECT_POINTERS
...
kd> dt 0x866131dc _SECTION_OBJECT_POINTERS
ntdll!_SECTION_OBJECT_POINTERS
+0x000 DataSectionObject : 0x86613260
+0x004 SharedCacheMap : 0x85f04608
+0x008 ImageSectionObject : (null)
kd> dt _SHARED_CACHE_MAP 0x85f04608
nt!_SHARED_CACHE_MAP
+0x000 NodeTypeCode : 767
+0x002 NodeByteSize : 304
+0x004 OpenCount : 0
+0x008 FileSize : _LARGE_INTEGER 0x317f
+0x010 BcbList : _LIST_ENTRY [ 0x85f04618 - 0x85f04618 ]
+0x018 SectionSize : _LARGE_INTEGER 0x40000
+0x020 ValidDataLength : _LARGE_INTEGER 0x317f
+0x028 ValidDataGoal : _LARGE_INTEGER 0x317f
+0x030 InitialVacbs : [4] 0x86792fa0 _VACB
+0x040 Vacbs : 0x85f04638 -> 0x86792fa0 _VACB
+0x044 FileObject : 0x85f636e0 _FILE_OBJECT
...
kd> dt _VACB 0x86792fa0
nt!_VACB
+0x000 BaseAddress : 0xcf080000
+0x004 SharedCacheMap : 0x85f04608 _SHARED_CACHE_MAP
+0x008 Overlay : __unnamed
+0x010 LruList : _LIST_ENTRY [ 0x805587e0 - 0x86793538 ]
kd> db 0xcf080000
cf080000 3b 3b 3b 20 54 72 69 6e-69 74 79 20 54 6f 74 61 ;;; Trinity Tota
cf080010 6c 20 53 65 63 75 72 69-74 79 0d 0a 3b 3b 3b 20 l Security..;;;
cf080020 54 68 69 73 20 66 69 6c-65 20 63 6f 6e 74 61 69 This file contai
cf080030 6e 73 20 61 6c 6c 20 63-6f 6d 6d 6f 6e 20 73 74 ns all common st
cf080040 72 69 6e 67 73 20 64 65-66 69 6e 65 64 20 69 6e rings defined in
cf080050 20 55 73 65 72 20 49 6e-74 65 72 66 61 63 65 20 User Interface
cf080060 50 72 6f 74 6f 74 79 70-65 20 0d 0a 0d 0a 3b 3b Prototype ....;;
cf080070 3b 20 49 44 20 4e 61 6d-69 6e 67 20 43 6f 6e 76 ; ID Naming Conv
이렇게 따라와서 메모리의 내용을 파일로 쓰는 겁니다.
파일이 1MB 이하일 땐 InitialVacbs 가 사용되고 1MB 초과일 땐 Vacbs 가 사용된다는 둥 VACB 는 256KB 블럭을 가리킨다는 둥 고려할 내용이 몇가지 있지만 책을 통해서 공부해 보시는게 좋을 것 같습니다.
익스텐션 DLL 소스와 바이너리를 첨부합니다.
맘대로 수정해서 사용하셔도 되구요 버그 발견해서 보고해 주시거나 수정해 주시는 것은 더욱 환영입니다. ^^
http://www.driveronline.org/bbs/view.asp?tb=tipbbs&GotoPage=1&s_bulu=&s_key=&no=98
심볼 스토어(Symbol Store)란 쉽게 말하면 MS 웹심볼 서버 같이 심볼파일들을 모든 버전에 대해서 모아 놓는 곳을 의미합니다. 모든 심볼파일들을 모아 놓고 심볼경로로 여기만 지정하면 디버깅하는 모듈의 심볼파일이 저절로 찾아져서 매번 심볼파일을 찾고 복사하는 작업을 할 필요가 없습니다.
제 PC 는 D:\MySymbol 이라는 심볼 스토어를 만들었습니다.
(말이 심볼 스토어지 일반적인 그냥 폴더입니다. ^^)
모듈을 빌드하고 테스트할 때마다 매번 빌드된 심볼파일을 심폴패스에 복사해야 하는데 버전이 다르면 기존 것을 지우고 복사해야 하는 등의 번거로운 점이 있었기 때문에 이것을 해결하고 싶었습니다.
그래서 D:\MySymbol 을 만들어서 빌드한 심볼파일을 그때 그때 심볼 스토어 형식으로 더하면 D:\MySymbol 에는 제가 개발하는 모듈의 모든 버전들에 대한 심볼파일들이 체크썸에 따라 분류되어 쌓입니다. 새로 빌드한 심볼파일의 복사가 실패하지도 않을 것이고 과거 버전들에 대한 디버깅도 가능해 집니다.
심볼파일을 더하기 편하도록 배치파일 AddMySym.bat 을 만들었습니다.
배치파일 내부에서 symstore.exe 를 사용하는데 WinDbg 폴더에 있는 심볼 스토어 유틸리티입니다.
(저는 WinDbg 경로를 패스로 뚫어놔서 배치파일 상에서 그냥 symstore 를 사용하도록 되어있습니다.)
심볼파일 경로를 파라미터로 받으므로 다음과 같이 명령창에서 하셔도 되구요.
C:\Test> AddMySym C:\Project\Bin\MyDrv.pdb
편하게 사용하려면 이 배치파일을 바탕화면에 복사해 놓거나 단축아이콘을 만들어 놓고
심볼파일을 들어다가 Drag & Drop 해 넣으면 됩니다.
WinDbg 사용하실 때는 심볼패스에 D:\MySymbol 을 추가해 놓으시면 됩니다.
D:\MySymbol 의 용량이 커지게 되면 나중에 한꺼번에 지우면 되지요. 어차피 개발중인 것들이었으므로...
물론 배포하는 버전의 심볼은 항상 따로 보관하는 곳이 있어야 겠지요.
AddMySym.bat 을 첨부합니다.
사용하실 분은 로컬경로만 자신에 맞게 수정하시고 사용하시면 됩니다.
Tip) 네트워크 공유 폴더를 심볼 스토어로 사용하시면 자신의 팀 모듈들, 제품 모듈들에 대한 심볼 스토어를 만들수 있습니다. 팀원들이 공유하는 심볼 스토어가 만들어 지는 것이지요.
http://www.driveronline.org/bbs/view.asp?tb=tipbbs&GotoPage=1&s_bulu=&s_key=&no=94
앞으로 시간될 때마다 하나씩 정리해서 올리려고 하는데요.
대강의 분류는 제목에 다음과 같이 표시할 예정입니다.
[WinDbg 사용] : WinDbg 환경설정이나 사용가이드 등의 내용
[WinDbg 분석] : 덤프파일 분석 예제
[WinDbg 명령] : 유용하게 사용할 수 있는 명령어
시작해 보니 생각보다 시간이 많이 들기는 하는데 한번 하는데까지 해 보겠습니다.
모두가 WinDbg를 쉽게 사용하는 그날까지~~~
http://www.driveronline.org/bbs/view.asp?tb=tipbbs&GotoPage=6&s_bulu=&s_key=&no=23
!process 명령의 기본적인 형식은 다음과 같습니다.
!process Process Flags
자세한 사용법은 WinDbg 도움말을 참고하시는게 항상 좋구요. ^^
여기서는 제가 자주 사용하는 방법에 대해서만 간단히 설명 드리려고 합니다.
1) !process 0 0
모든 프로세스를 보고 싶을 때 사용합니다.
Process에 0 을 주면 모든 프로세스라는 뜻이 됩니다.
Flags에 0 을 주면 가장 간단히 보여달라는 뜻이 됩니다.
예제)
kd> !process 0 0
**** NT ACTIVE PROCESS DUMP ****
PROCESS 867b5a00 SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000
DirBase: 06c20000 ObjectTable: e1003e60 HandleCount: 281.
Image: System
PROCESS 860a1220 SessionId: none Cid: 0578 Peb: 7ffde000 ParentCid: 0004
DirBase: 0f89e000 ObjectTable: e16d73c8 HandleCount: 21.
Image: smss.exe
PROCESS 86433b20 SessionId: 0 Cid: 05ec Peb: 7ffd9000 ParentCid: 0578
DirBase: 113af000 ObjectTable: e100fa68 HandleCount: 489.
Image: csrss.exe
PROCESS 863fcda0 SessionId: 0 Cid: 0604 Peb: 7ffd7000 ParentCid: 0578
DirBase: 14214000 ObjectTable: e15a6698 HandleCount: 471.
Image: winlogon.exe
...
2) !process 0 0 ImageName.exe
특정 프로세스를 찾을 때 사용합니다.
모든 프로세스 중에서 원하는 프로세스 이름과 일치하는 녀석만 찾아서 보여줍니다.
프로세스 이름은 위에서 보여지는 것처럼 확장자까지 일치해야 합니다.
예제)
kd> !process 0 0 notepad.exe
PROCESS 859a1020 SessionId: 0 Cid: 04d8 Peb: 7ffdf000 ParentCid: 0150
DirBase: 0d413000 ObjectTable: e197b118 HandleCount: 53.
Image: notepad.exe
3) !process Process 7
특정 프로세스의 정보를 자세히 보고 싶을 때 사용합니다.
Flags 는 비트마다 의미가 있는데 7 로 주면 1,2,3 비트 모두 켠 것이므로 가장 자세히 보여줍니다.
각각의 의미는 역시 WinDbg 도움말!!!
예제)
위에서 찾아진 notepad.exe의 정보중 PROCESS 859a1020 를 참고해서 다음과 같이 명령합니다.
kd> !process 859a1020 7
PROCESS 859a1020 SessionId: 0 Cid: 04d8 Peb: 7ffdf000 ParentCid: 0150
DirBase: 0d413000 ObjectTable: e197b118 HandleCount: 53.
Image: notepad.exe
VadRoot 864c5cb8 Vads 68 Clone 0 Private 225. Modified 19. Locked 0.
DeviceMap e287fc90
Token e15c9570
ElapsedTime 00:00:19.390
UserTime 00:00:00.046
KernelTime 00:00:00.250
QuotaPoolUsage[PagedPool] 34280
QuotaPoolUsage[NonPagedPool] 2720
Working Set Sizes (now,min,max) (1003, 50, 345) (4012KB, 200KB, 1380KB)
PeakWorkingSetSize 1004
VirtualSize 32 Mb
PeakVirtualSize 37 Mb
PageFaultCount 1088
MemoryPriority BACKGROUND
BasePriority 8
CommitCharge 488
THREAD 859a1da8 Cid 04d8.00a4 Teb: 7ffde000 Win32Thread: e2fe45c8 WAIT: (WrUserRequest) UserMode Non-Alertable
85dd62c8 SynchronizationEvent
Not impersonating
DeviceMap e287fc90
Owning Process 859a1020 Image: notepad.exe
Wait Start TickCount 151791 Ticks: 1168 (0:00:00:18.250)
Context Switch Count 797 LargeStack
UserTime 00:00:00.0031
KernelTime 00:00:00.0250
Win32 Start Address windbg!`string' (0x0100739d)
Start Address kernel32!BaseProcessStartThunk (0x7c810665)
Stack Init ec962000 Current ec961c20 Base ec962000 Limit ec95d000 Call 0
Priority 10 BasePriority 8 PriorityDecrement 0 DecrementCount 16
ChildEBP RetAddr Args to Child
ec961c38 804e3bd2 859a1e18 859a1da8 804e3c1e nt!KiSwapContext+0x2f (FPO: [Uses EBP] [0,0,4])
ec961c44 804e3c1e 000025ff e2fe45c8 00000000 nt!KiSwapThread+0x6b (FPO: [0,0,0])
ec961c6c bf802fa0 00000001 0000000d 00000001 nt!KeWaitForSingleObject+0x1c2 (FPO: [Non-Fpo])
ec961ca8 bf801b80 000025ff 00000000 00000001 win32k!xxxSleepThread+0x192 (FPO: [Non-Fpo])
ec961cec bf80f24e ec961d18 000025ff 00000000 win32k!xxxRealInternalGetMessage+0x418 (FPO: [Non-Fpo])
ec961d4c 804df99f 0007fefc 00000000 00000000 win32k!NtUserGetMessage+0x27 (FPO: [Non-Fpo])
ec961d4c 7c93eb94 0007fefc 00000000 00000000 nt!KiFastCallEntry+0xfc (FPO: [0,0] TrapFrame @ ec961d64)
0007fed8 00000000 00000000 00000000 00000000 ntdll!KiFastSystemCallRet (FPO: [0,0,0])
콜스택까지 보이므로 이 프로세스가 행이 걸려있다면 어디에서 멈춰있는지 확인하는 목적으로 종종 사용하기도 합니다.
4) !process 0 7
모든 프로세스의 정보를 자세히 보고 싶을 때 사용합니다.
저같은 경우 모든 프로세스, 쓰레드의 콜스택을 보고 싶을 때 사용합니다.
여러 프로세스, 쓰레드 간의 DeadLock 문제가 발생했다면 모든 콜스택 중에서 제가 작성한 드라이버가 존재하는 콜스택을 검색하고 이것들을 분석하여 원인을 찾기도 합니다.
나중에 DeadLock 분석 예제에서 활용방법을 좀 더 자세히 보게 될 것입니다.
예제)
kd> !process 0 7
**** NT ACTIVE PROCESS DUMP ****
PROCESS 867b5a00 SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000
DirBase: 06c20000 ObjectTable: e1003e60 HandleCount: 282.
Image: System
VadRoot 867ac3c0 Vads 4 Clone 0 Private 3. Modified 8487. Locked 0.
DeviceMap e1003148
Token e1000b40
ElapsedTime 00:33:54.765
UserTime 00:00:00.000
KernelTime 00:00:36.078
QuotaPoolUsage[PagedPool] 0
QuotaPoolUsage[NonPagedPool] 0
Working Set Sizes (now,min,max) (70, 0, 345) (280KB, 0KB, 1380KB)
PeakWorkingSetSize 619
VirtualSize 1 Mb
PeakVirtualSize 3 Mb
PageFaultCount 8781
MemoryPriority BACKGROUND
BasePriority 8
CommitCharge 7
THREAD 867b5788 Cid 0004.0008 Teb: 00000000 Win32Thread: 00000000 WAIT: (WrFreePage) KernelMode Non-Alertable
80568e10 SynchronizationEvent
80569e00 NotificationTimer
Not impersonating
DeviceMap e1003148
Owning Process 867b5a00 Image: System
Wait Start TickCount 130223 Ticks: 9 (0:00:00:00.140)
Context Switch Count 17103
UserTime 00:00:00.0000
KernelTime 00:00:02.0187
Start Address nt!Phase1Initialization (0x806b07cb)
Stack Init f7c46000 Current f7c45798 Base f7c46000 Limit f7c43000 Call 0
Priority 0 BasePriority 0 PriorityDecrement 0 DecrementCount 0
ChildEBP RetAddr Args to Child
f7c457b0 804e3bd2 00000000 867b5788 804e3b83 nt!KiSwapContext+0x2f (FPO: [Uses EBP] [0,0,4])
f7c457bc 804e3b83 80701aa8 003de060 00000000 nt!KiSwapThread+0x6b (FPO: [0,0,0])
f7c457f4 804eb549 00000002 f7c45828 00000001 nt!KeWaitForMultipleObjects+0x284 (FPO: [Non-Fpo])
f7c45840 806af2bd 867b5788 00000000 00000044 nt!MmZeroPageThread+0x61 (FPO: [Non-Fpo])
f7c45dac 80576128 80087000 00000000 00000000 nt!Phase1Initialization+0x1287 (FPO: [Non-Fpo])
f7c45ddc 804f1c81 806b07cb 80087000 00000000 nt!PspSystemThreadStartup+0x34 (FPO: [Non-Fpo])
00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16
THREAD 867b4d20 Cid 0004.0010 Teb: 00000000 Win32Thread: 00000000 WAIT: (WrQueue) UserMode Non-Alertable
8056b4c0 Unknown
Not impersonating
DeviceMap e1003148
Owning Process 867b5a00 Image: System
Wait Start TickCount 130233 Ticks: 22 (0:00:00:00.343)
Context Switch Count 6541
UserTime 00:00:00.0000
KernelTime 00:00:00.0109
Start Address nt!ExpWorkerThread (0x804e42f1)
Stack Init f7c62000 Current f7c61d24 Base f7c62000 Limit f7c5f000 Call 0
Priority 13 BasePriority 13 PriorityDecrement 0 DecrementCount 16
ChildEBP RetAddr Args to Child
f7c61d3c 804e3bd2 867b4d90 867b4d20 804e42d6 nt!KiSwapContext+0x2f (FPO: [Uses EBP] [0,0,4])
f7c61d48 804e42d6 867b4098 8056b4c0 867b4d20 nt!KiSwapThread+0x6b (FPO: [0,0,0])
f7c61d74 804e438e 00000001 00000001 00000000 nt!KeRemoveQueue+0x22a (FPO: [Non-Fpo])
f7c61dac 80576128 867b4098 00000000 00000000 nt!ExpWorkerThread+0xcc (FPO: [Non-Fpo])
f7c61ddc 804f1c81 804e42f1 00000000 00000000 nt!PspSystemThreadStartup+0x34 (FPO: [Non-Fpo])
00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16
...
PROCESS 860a1220 SessionId: none Cid: 0578 Peb: 7ffde000 ParentCid: 0004
DirBase: 0f89e000 ObjectTable: e16d73c8 HandleCount: 21.
Image: smss.exe
VadRoot 86443d30 Vads 16 Clone 0 Private 28. Modified 10. Locked 0.
DeviceMap e1003148
Token e181cde0
ElapsedTime 00:34:19.515
UserTime 00:00:00.015
KernelTime 00:00:00.015
QuotaPoolUsage[PagedPool] 5228
QuotaPoolUsage[NonPagedPool] 640
Working Set Sizes (now,min,max) (99, 50, 345) (396KB, 200KB, 1380KB)
PeakWorkingSetSize 121
VirtualSize 3 Mb
PeakVirtualSize 11 Mb
PageFaultCount 217
MemoryPriority BACKGROUND
BasePriority 11
CommitCharge 41
THREAD 860a0da8 Cid 0578.057c Teb: 7ffdd000 Win32Thread: 00000000 WAIT: (UserRequest) UserMode Non-Alertable
86433b20 ProcessObject
863fcda0 ProcessObject
Not impersonating
DeviceMap e1003148
Owning Process 860a1220 Image: smss.exe
Wait Start TickCount 25411 Ticks: 107191 (0:00:27:54.859)
Context Switch Count 43
UserTime 00:00:00.0000
KernelTime 00:00:00.0015
Start Address 0x4858a4c8
Stack Init f71d2000 Current f71d1960 Base f71d2000 Limit f71cf000 Call 0
Priority 11 BasePriority 11 PriorityDecrement 0 DecrementCount 0
Kernel stack not resident.
THREAD 863b4ac0 Cid 0578.05c4 Teb: 7ffdc000 Win32Thread: 00000000 WAIT: (WrLpcReceive) UserMode Non-Alertable
866b90a8 Semaphore Limit 0x7fffffff
Not impersonating
DeviceMap e1003148
Owning Process 860a1220 Image: smss.exe
Wait Start TickCount 25411 Ticks: 107201 (0:00:27:55.015)
Context Switch Count 9
UserTime 00:00:00.0000
KernelTime 00:00:00.0000
Start Address 0x485893b2
Stack Init f71ce000 Current f71cdc34 Base f71ce000 Limit f71cb000 Call 0
Priority 11 BasePriority 11 PriorityDecrement 0 DecrementCount 0
Kernel stack not resident.
...
PROCESS 86433b20 SessionId: 0 Cid: 05ec Peb: 7ffd9000 ParentCid: 0578
DirBase: 113af000 ObjectTable: e100fa68 HandleCount: 476.
Image: csrss.exe
VadRoot 858c76b8 Vads 129 Clone 0 Private 342. Modified 1742. Locked 0.
DeviceMap e1003148
Token e1825720
ElapsedTime 00:34:17.718
UserTime 00:00:01.765
KernelTime 00:00:04.046
QuotaPoolUsage[PagedPool] 112960
QuotaPoolUsage[NonPagedPool] 6072
Working Set Sizes (now,min,max) (1693, 50, 345) (6772KB, 200KB, 1380KB)
PeakWorkingSetSize 1696
VirtualSize 79 Mb
PeakVirtualSize 79 Mb
PageFaultCount 4537
MemoryPriority BACKGROUND
BasePriority 13
CommitCharge 421
THREAD 86431da8 Cid 05ec.05fc Teb: 7ffdc000 Win32Thread: e18f2eb0 WAIT: (WrLpcReceive) UserMode Non-Alertable
865a9090 Semaphore Limit 0x7fffffff
Not impersonating
DeviceMap e1003148
Owning Process 86433b20 Image: csrss.exe
Wait Start TickCount 132197 Ticks: 459 (0:00:00:07.171)
Context Switch Count 1804 LargeStack
UserTime 00:00:00.0609
KernelTime 00:00:00.0109
Win32 Start Address 0x0001fc07
LPC Server thread working on message Id 1fc07
Start Address 0x75a84616
Stack Init f7166000 Current f7165c34 Base f7166000 Limit f7163000 Call 0
Priority 14 BasePriority 13 PriorityDecrement 0 DecrementCount 16
ChildEBP RetAddr Args to Child
f7165c4c 804e3bd2 86431e18 86431da8 804e3c1e nt!KiSwapContext+0x2f (FPO: [Uses EBP] [0,0,4])
f7165c58 804e3c1e e1648b60 80563ec0 e1648b60 nt!KiSwapThread+0x6b (FPO: [0,0,0])
f7165c80 8057643b 00000001 00000010 8057ad01 nt!KeWaitForSingleObject+0x1c2 (FPO: [Non-Fpo])
f7165d30 80576641 00000094 005affd0 00000000 nt!NtReplyWaitReceivePortEx+0x3dc (FPO: [Non-Fpo])
f7165d4c 804df99f 00000094 005affd0 00000000 nt!NtReplyWaitReceivePort+0x18 (FPO: [Non-Fpo])
f7165d4c 7c93eb94 00000094 005affd0 00000000 nt!KiFastCallEntry+0xfc (FPO: [0,0] TrapFrame @ f7165d64)
005afff4 00000000 00000000 00000000 00000000 ntdll!KiFastSystemCallRet (FPO: [0,0,0])
THREAD 864d6d18 Cid 05ec.060c Teb: 7ffda000 Win32Thread: e1563008 WAIT: (WrLpcReceive) UserMode Non-Alertable
865a9090 Semaphore Limit 0x7fffffff
Not impersonating
DeviceMap e1003148
Owning Process 86433b20 Image: csrss.exe
Wait Start TickCount 132529 Ticks: 147 (0:00:00:02.296)
Context Switch Count 1862 LargeStack
UserTime 00:00:00.0781
KernelTime 00:00:00.0281
Start Address 0x75a84616
Stack Init f7126000 Current f7125c34 Base f7126000 Limit f7123000 Call 0
Priority 13 BasePriority 13 PriorityDecrement 0 DecrementCount 16
ChildEBP RetAddr Args to Child
f7125c4c 804e3bd2 864d6d88 864d6d18 804e3c1e nt!KiSwapContext+0x2f (FPO: [Uses EBP] [0,0,4])
f7125c58 804e3c1e 00000000 80563ec0 e1648b60 nt!KiSwapThread+0x6b (FPO: [0,0,0])
f7125c80 8057643b 00000001 00000010 8057ad01 nt!KeWaitForSingleObject+0x1c2 (FPO: [Non-Fpo])
f7125d30 80576641 00000094 0071ffd0 0071feec nt!NtReplyWaitReceivePortEx+0x3dc (FPO: [Non-Fpo])
f7125d4c 804df99f 00000094 0071ffd0 0071feec nt!NtReplyWaitReceivePort+0x18 (FPO: [Non-Fpo])
f7125d4c 7c93eb94 00000094 0071ffd0 0071feec nt!KiFastCallEntry+0xfc (FPO: [0,0] TrapFrame @ f7125d64)
0071fff4 00000000 00000000 00000000 00000000 ntdll!KiFastSystemCallRet (FPO: [0,0,0])
...
PROCESS 863fcda0 SessionId: 0 Cid: 0604 Peb: 7ffd7000 ParentCid: 0578
DirBase: 14214000 ObjectTable: e15a6698 HandleCount: 471.
Image: winlogon.exe
VadRoot 86095198 Vads 188 Clone 0 Private 1156. Modified 2210. Locked 0.
DeviceMap e1003148
Token e1560af0
ElapsedTime 00:34:17.843
UserTime 00:00:00.343
KernelTime 00:00:01.296
QuotaPoolUsage[PagedPool] 62420
QuotaPoolUsage[NonPagedPool] 33176
Working Set Sizes (now,min,max) (920, 50, 345) (3680KB, 200KB, 1380KB)
PeakWorkingSetSize 4351
VirtualSize 57 Mb
PeakVirtualSize 59 Mb
PageFaultCount 7284
MemoryPriority BACKGROUND
BasePriority 13
CommitCharge 1805
THREAD 860f1da8 Cid 0604.061c Teb: 7ffdd000 Win32Thread: 00000000 WAIT: (WrLpcReceive) UserMode Non-Alertable
866b24b0 Semaphore Limit 0x7fffffff
860f1e98 NotificationTimer
Not impersonating
DeviceMap e1003148
Owning Process 863fcda0 Image: winlogon.exe
Wait Start TickCount 130833 Ticks: 1919 (0:00:00:29.984)
Context Switch Count 112
UserTime 00:00:00.0000
KernelTime 00:00:00.0000
Start Address kernel32!BaseThreadStartThunk (0x7c810659)
Stack Init ef0b6000 Current ef0b5c4c Base ef0b6000 Limit ef0b3000 Call 0
Priority 13 BasePriority 13 PriorityDecrement 0 DecrementCount 0
Kernel stack not resident.
ChildEBP RetAddr Args to Child
ef0b5c64 804e3bd2 860f1e18 860f1da8 804e3c1e nt!KiSwapContext+0x2f (FPO: [Uses EBP] [0,0,4])
ef0b5c70 804e3c1e e13d23a8 80563ec0 e13d23a8 nt!KiSwapThread+0x6b (FPO: [0,0,0])
ef0b5c98 8057643b 00000001 00000010 863fcd01 nt!KeWaitForSingleObject+0x1c2 (FPO: [Non-Fpo])
ef0b5d48 804df99f 00000154 00c5ff70 00000000 nt!NtReplyWaitReceivePortEx+0x3dc (FPO: [Non-Fpo])
ef0b5d48 7c93eb94 00000154 00c5ff70 00000000 nt!KiFastCallEntry+0xfc (FPO: [0,0] TrapFrame @ ef0b5d64)
00c5ff80 00000000 00000000 00000000 00000000 ntdll!KiFastSystemCallRet (FPO: [0,0,0])
THREAD 860f7da8 Cid 0604.0620 Teb: 7ffdc000 Win32Thread: 00000000 WAIT: (WrLpcReceive) UserMode Non-Alertable
866b2090 Semaphore Limit 0x7fffffff
Not impersonating
DeviceMap e1003148
Owning Process 863fcda0 Image: winlogon.exe
Wait Start TickCount 70172 Ticks: 62593 (0:00:16:18.015)
Context Switch Count 36
UserTime 00:00:00.0000
KernelTime 00:00:00.0000
Win32 Start Address RPCRT4!ThreadStartRoutine (0x77d86bf0)
Start Address kernel32!BaseThreadStartThunk (0x7c810659)
Stack Init f7cca000 Current f7cc9c4c Base f7cca000 Limit f7cc7000 Call 0
Priority 13 BasePriority 13 PriorityDecrement 0 DecrementCount 0
Kernel stack not resident.
THREAD 860f8da8 Cid 0604.0624 Teb: 7ffdb000 Win32Thread: 00000000 WAIT: (DelayExecution) UserMode Alertable
860f8e98 NotificationTimer
Not impersonating
DeviceMap e1003148
Owning Process 863fcda0 Image: winlogon.exe
Wait Start TickCount 25469 Ticks: 107306 (0:00:27:56.656)
Context Switch Count 8
UserTime 00:00:00.0000
KernelTime 00:00:00.0000
Win32 Start Address ntdll!RtlpTimerThread (0x7c95798d)
Start Address kernel32!BaseThreadStartThunk (0x7c810659)
Stack Init f7cd2000 Current f7cd1cbc Base f7cd2000 Limit f7ccf000 Call 0
Priority 13 BasePriority 13 PriorityDecrement 0 DecrementCount 0
Kernel stack not resident.
...
이상으로 !process 의 사용에 대해서 간단히 보여드렸습니다.
http://www.driveronline.org/bbs/view.asp?tb=tipbbs&GotoPage=5&s_bulu=&s_key=&no=35