반응형


출처 : http://skmagic.tistory.com/379




WinDbg란?!

마이크로 소프트에서 배포하는 마이크로 소프트 윈도우의 다목적 디버거

유져모드 어플리케이션, 드라이버 및 커널모드에서 자체 운영체제 디버깅을 할 수 있다.



난 보통 덤프 파일 분석을 위해 사용하기에 덤프파일 분석을 위한 디버깅을 소개한다.


[덤프 디버깅]

1.유져모드 덤프 디버깅

2.커널모드 덤프 디버깅


(언제 다 정리하지?!)


[디버깅 방법]

1.심볼을 연결한다 (pdb파일이 있는 경로로 설정) (tip:웬만하면 경로는 영문명으로 하자)

2.소스코드를 연결한다.(안해도 콜스택정도는 나옴)

3.각종 명령어를 통해 분석할 수 있다.



[명령어]

k : 콜스택 보기

.ecxr : 예외가 발생한 상황에 저장된 컨텍스트 레코드를 보여준다(eax, ebx, ecx 등등...)

!analyze -v : Windebug가 자동분석을 해서 많은 정보를 보여준다.

u 주소값: 어셈보기



CommandoptionusageDesc
종료   
q  디버깅 종료
qd  디버깅 종료;연결해제
디버깅 환경정보   
vertarget  타겟 컴퓨터 정보 표시
version  디버그 환경 정보 표시
.lastevent  마지막 디버그 이벤트 정보 표시
||  디버깅 세션 정보 표시
sumble & sorurce   
.symfix  MS 심볼경로 설정
.sympath  심볼경로 확인/설정
.sym noisy  심볼파일 검색 과정을 출력
.srcpath  소스경로 설정
.srcnoisy .srcnoisy 1소스경로 검색 과정을 출력
모듈   
lm l로드된 모듈만 표시
  m [pattern]패턴과 일치되는 모듈만 표시
  v모듈 상세정보 표시
!lmi!lmi ntdll.dll 모듈 상세정보 표시
.reload /f [m_name]심볼을 즉시 로드
xX ntdll!*/v심볼 타입을 표시.
 X *!*abc*/t데이터 타입을 표시
  /n이름순으로 정렬
ln ln [address]해당 주소에 근접한 심볼의 정보 표시
레지스터   
r  레지스터 정보 표시
r $proc  현재 프로세스의 PEB주소( user-mode)
   현재 프로세스의 EPROcESS주소( kernel-mode)
r $thread  현재 스레드의 TEB주소( user-mode)
   현재 스레드의 ETHREAD주소( kernel-mode)
r $tpid  현재 프로세스 ID(PID)
r $tid  현재 스레드 ID(TID)
언어셈블   
u  언어셈블
  f언어셈블(함수전체)
  b언어셈블(ip이전의 8개 명령어)
콜스택   
k [n]콜스택 정보표시
  p함수정보 출력
  b인자표시
  n프레임번호
  vFPO정보 표시
  f스택 사용량 표시
break point   
bpbp 0x123456 bp 설정
bl  bp 리스트 출력
bcbc * | [frame_no] bp 삭제
bd,bebc * | [frame_no] bp disable/enable
bmbm notepad!*Win* 패턴과 일치하는 모든심볼에 bp설정
bubu aaa!bbb 로드되지 않은 심볼에 대한 bp설정
ba  특정 주소에 access시 bp
지역변수   
dvdv modulr!test*  
  /i심볼유형과 인자유형 표시
  /V변수저장 위치 표시( register or address )
데이터유형   
dtdf _EPROCESS 0xaddr 주소를 특정 데이터 형으로 변환해서 표시
du  Unicode string 표시
da  Ansi string 표시
dc   
db   
dy   
    
!address!address  
 !address [address]  
프로세스 & 스레드 정보   
!peb  PEB(Process Environment Block)표시
!teb  TEB(Thread Environment Block) 표시
    
!gle  API의 마지막 에러코드 표시
실행 제어   
t  Trace
~.t  다른 스레드를 중지시킨 상태에서 하나의 statementt실행
g   
p  Step Over
gugu 현재함수가 복귀할 때 까지 실행
 ~0 gu 스레드 0을 제외한 모든 스레드를 freeze함
wt  내부에서 호출된 함수와 함수호출 횟수등의 정보 표시
.cxr  컨텍스트 변경
!ready   
.thread   
!thread   
.trap   
.process   
!process   
ed   
ebeb .-6 90 90 90 90 90 90 6byte를 NOP(0x90)으로 변경
!error!error [error code] 에러코드 정보표시













출처 : http://blog.naver.com/process3

[심볼관련 사용하는 주요 명령어]

심볼을 로드할 때 사용하는 명령입니다.

보통은 심볼패스를 설정한 후에 .reload 와 같이 사용합니다.

kd> .symfix e:\symbols

kd> .reload
Connected to Windows XP 2600 x86 compatible target, ptr64 FALSE
Loading Kernel Symbols
........................
Loading User Symbols

lm 을 사용하여 심볼이 로드된 상태를 봅니다.

kd> lm
start end module name
804d9000 806ede00 nt (pdb symbols) e:\symbols\ntoskrnl.pdb\8592B6763F344B562\ntoskrnl.pdb
806ee000 80701d80 hal (deferred)
f9871000 f988b580 Mup (deferred)
f996f000 f998e780 fltMgr (deferred)
...

nt 는 심볼이 로드된 것이 보이는데 나머지는 deferred 라고 나옵니다.
이것은 WinDbg 의 lazy symbol loading (deferred symbol loading) 이라는 특징 때문에 그렇습니다.

WinDbg 는 심볼을 로드할 때 꼭 필요한 심볼만 올려놓고 나머지는 deferred 로 해놓고 심볼을 올리지 않습니다. deferred 로 된 녀석들은 나중에 해당 모듈이 WinDbg 상에서 실제 사용되는 순간이 발생해야만 로드가 됩니다. 필요한 것만 그때 그때 올려주는 나름대로 효율적인 방법입니다. ^^

WinDbg Help 에 보면 .reload 는 다양한 옵션을 가지고 있는데요.
제가 유용하게 사용하는 것만 몇가지 설명합니다.

.reload /i mydrv.sys (심볼이 맞지않아도 강제로 심볼 로드하기)

예를 들어 어제 빌드한 드라이버가 BSOD 를 발생시켜서 덤프가 만들어 졌는데 심볼은 보관하지 않아서 덤프분석을 할 수 없는 문제를 만났다고 가정합시다. 다행히도 어제 소스 코드를 그대로 보관하고 있었다고 하면 그것을 그대로 빌드하여 pdb 파일을 생성할 수 있습니다. 문제는 이 pdb 파일을 로드하려고 해도 WinDbg가 TimeStamp 등을 체크하여 symbol mismatch 에러를 내면서 심볼로드를 하지 않는다는 겁니다. 이런 경우에 심볼이 맞는지 확인하지 말고 강제로 올려달라는 /i 옵션을 사용하면 심볼이 올라갑니다.

.reload /f (심볼 모두 올리기)

보통 lazy symbol loading 상태로 그냥 사용하시면 되지만 혹시 모든 모듈의 심볼을 모두 올려 놓아야 할 경우가 있다면 /f 옵션을 사용해서 모든 모듈의 심볼을 올릴 수 있습니다.

.reload /u (심볼 모두 내리기)

반대로 모든 심볼을 모두 내려야 할 경우가 있다면 /u 옵션을 사용해서 모든 모듈의 심볼을 내릴 수 있습니다.

.reload /u mydrv.sys (특정모듈 심볼 내리기)

mydrv.pdb 를 로드하여 사용하고 있는데 새로 빌드한 mydrv.pdb 를 심볼패스에 복사하면 mydrv.pdb 가 사용중이라서 복사가 실패합니다. 이런 경우 /u mydrv.sys 옵션을 줘서 특정 모듈의 심볼만 내릴 수 있습니다.

.reload mydrv.sys (특정모듈 심볼 로드하기)

위와 같이 내려진 특정 모듈의 심볼만 다시 올리려면 mydrv.sys 처럼 모듈 이름을 줘서 로드합니다.


로드된 모듈 리스트 보기
lm 명령을 이용하면 된다.

lm k : Kernel Mode 모듈 표시
lm u : User Mode 모듈 표시
lm m : 패턴을 검사하여 해당하는 것만 보여줌 <lm m my*>

lkd> lm
start end module name
00c80000 00c90000 NateOnHook40u (export symbols) C:\Program Files\NATEON\BIN\NateOnHook40u.dll
00cb0000 00cb9000 MgHookDll C (export symbols) C:\Program Files\LG Software\On Screen Display\MgHookDll.dll
01000000 0106a000 windbg (pdb symbols) D:\Symbol\WebSymbol\windbg.pdb\D6EF677AA54441279479F0307F05A8941\windbg.pdb
016a0000 01784000 ext (export symbols) C:\Program Files\Debugging Tools for Windows\winext\ext.dll
01790000 017c1000 kext (pdb symbols) D:\Symbol\WebSymbol\kext.pdb\6B643FC4E9F94FF4ABA4CEF1FD6F89D61\kext.pdb


모듈의 심볼(Symbol) 검사
x 모듈!패턴 을 입력하면 된다.

lkd> x nt!Ke*
804f8c02 nt!KeQuerySystemTime = <no type information>
804f8c9e nt!KeEnableInterrupts = <no type information>
80500e38 nt!KeSwitchKernelStack = <no type information>
804fad32 nt!KeReadStateProcess = <no type information>
804f9188 nt!KeReleaseInterruptSpinLock = <no type information>


데이터 타입(Date Type) 표시
dt 데이터 타입 을 입력하면 된다.

lkd> dt _EPROCESS
+0x000 Pcb : _KPROCESS
+0x06c ProcessLock : _EX_PUSH_LOCK
+0x070 CreateTime : _LARGE_INTEGER
+0x078 ExitTime : _LARGE_INTEGER
+0x080 RundownProtect : _EX_RUNDOWN_REF
+0x084 UniqueProcessId : Ptr32 Void
+0x088 ActiveProcessLinks : _LIST_ENTRY
+0x090 QuotaUsage : [3] Uint4B
+0x09c QuotaPeak : [3] Uint4B
+0x0a8 CommitCharge : Uint4B



메모리 덤프(Memory Dump)
d* 명령들을 이용하면 된다.

db : Byte 형식 + Ascii 로 표시
dd : 데이터를 4Byte 형식으로 표시

lkd> db 8053db18
8053db18 8b ff 55 8b ec 8b 45 08-8b 4d 0c 8b 55 14 89 48 ..U...E..M..U..H
8053db28 0c 8b 4d 10 89 48 10 03-ca 89 48 14 8b 4d 18 83 ..M..H....H..M..
8053db38 c1 fe 89 48 18 8b 4d 1c-89 48 20 66 8b 4d 20 66 ...H..M..H f.M f



디스어셈블리(Disassembly)
u 주소 를 이용하면 된다. 특정 함수를 디스어셈블리 하고 싶으면 uf 주소 를 하면 된다.

u 주소 : 주소에서 일부분만 디스어셈블리
u 주소1 주소2 : 주소1에서 주소 2까지 디스어셈블리

lkd> u 8053db18 or uf nt!NtOpenProcess
nt!KeInitializeProfile:
8053db18 8bff mov edi,edi
8053db1a 55 push ebp
8053db1b 8bec mov ebp,esp
8053db1d 8b4508 mov eax,[ebp+0x8]
8053db20 8b4d0c mov ecx,[ebp+0xc]


메모리 영역 속성 보기(VA Dump)
!vadump 명령을 사용하면 된다. 만약 특정 메모리의 속성을 보고 싶다면 !vprot 주소 명령을 사용하면 된다.

0:000> !vadump
BaseAddress: 00000000
RegionSize: 00010000
State: 00010000 MEM_FREE
Protect: 00000001 PAGE_NOACCESS

BaseAddress: 00010000
RegionSize: 00001000
State: 00001000 MEM_COMMIT
Protect: 00000004 PAGE_READWRITE
Type: 00020000 MEM_PRIVATE


0:000> !vprot 30c191c
BaseAddress: 030c1000
AllocationBase: 030c0000
AllocationProtect: 00000080 PAGE_EXECUTE_WRITECOPY
RegionSize: 00011000
State: 00001000 MEM_COMMIT
Protect: 00000010 PAGE_EXECUTE
Type: 01000000 MEM_IMAGE


프로세스 관련
모든 프로세스를 보기위해서는 !process 0 0 를 입력하면 된다. 디버거를 특정 프로세스에 붙이고 싶으면 .process /i [pid] 를 입력하면 된다.


lkd> !process 0 0
**** NT ACTIVE PROCESS DUMP ****
PROCESS 8a3a3490 SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000
DirBase: 00780000 ObjectTable: e1001c70 HandleCount: 521.
Image: System

PROCESS 8a184158 SessionId: none Cid: 03f0 Peb: 7ffdd000 ParentCid: 0004
DirBase: 17a40020 ObjectTable: e163dd70 HandleCount: 20.
Image: smss.exe

PROCESS 89df4da0 SessionId: 0 Cid: 0440 Peb: 7ffd5000 ParentCid: 03f0
DirBase: 17a40040 ObjectTable: e1c6cb18 HandleCount: 626.
Image: csrss.exe


구조체 내용 보기

!strct _KMUTANT ff93a330

이렇게 명령을 주면 자료구조랑 값들이 같이 출력이 된다.


WinDBG 디버깅 과정 파일로 저장하기

Windbg를 이용해서 디버깅을 하다보면,

디버깅과정을 저장할 필요가 생깁니다.
또는 아래와 같은 상황이 있을수 있을겁니다.


- 다른분의 도움을 통해 디버깅한 내용을 참고하고 싶은경우
- 수십시간동안 켜놓은 결과를 저장해야하는경우. -> 화면 버퍼를 넘어가면 그동안의 내용이 사라지죠~
- .cls 를 습관적으로 사용하는경우. -> 제경우입니다. T.T

이때 유용한 명령어가 바로.

.logxxx 계열 명령어입니다.


.logopen logfile
>> 디버깅 과정을 저장할 로그파일 명을 지정합니다.
ex) .logopen “c:\dbglog\logs.txt”

.logfile
>> 현재 기록중인 디버깅 로그파일의 상태를 표시합니다.

.logclose
>> 기록중이던 로그를 종료(완료)합니다.



유용하게 쓰세요~
(단, .logopen 이전의 내용은 저장되지 않습니다.` ^^)
참고하세요)
.logopen 정보 역시 Windbg WorkSpace 에 함께 저장됩니다.~ ^^





GUI환경의 디버깅툴인 windbg 사용법을 알아보자.

사용자 삽입 이미지


일단 각 필요파일들을 download해야겠지?

◎ 필요한 파일들
* windbg 다운로드
32bit버전 : http://www.microsoft.com/whdc/devtools/debugging/installx86.mspx
64bit버전 : http://www.microsoft.com/whdc/devtools/debugging/install64bit.mspx

* 심볼파일 다운로드
http://www.microsoft.com/whdc/devtools/debugging/symbolpkg.mspx
위의 링크에서 각 OS에 맞는 파일 다운로드하여 설치

◎ 설치할때 특별하게 주의할 점은 없다. 모르겠다 싶으면 그냥 "다음(Next)" 버튼만 눌러라.
단, 기억할것은 심볼파일을 설치할때 어디에 설치했는지 잘 봐두도록..
기본설치 위치는 해당 OS폴더 밑의 Symbols이다. 예를 들면 XP인 경우는
"C:\Windows\Symbols"에 설치된다.

◎ 실행방법 및 심볼파일 경로 설정
1. 실행창에서
시작 -> 실행 창 또는 명령프롬프트 창에서 아래와 같이 입력
windbg -y 심볼파일경로 -i OS이미지경로 -z 덤프파일경로
예> windbg -y SRV*C:\Windows\Symbols*http://msdl.microsoft.com/download/symbols -i d:\i386 -z c:\windows\memory.dmp

2. 시작 -> 모든 프로그램 -> Debugging Tools for Windows -> WinDbg 실행
File -> Symbol File Path... 실행
Symbol Search Path창이 뜨면 아래 경로 입력하고 확인 클릭
경로 : SRV*C:\Windows\Symbols*http://msdl.microsoft.com/download/symbols
File -> Image File Path... 실행
OS 이미지 경로입력 (OS CD의 i386에 있음)
File -> Open Crash Dump... 실행
Dump파일 경로 입력

◎ Remote Debugging 방법
* Debugger사용하는 방법
- 서버, 클라이언트 모두 windbg를 설치, 서버측에 심볼파일 설치
1. 서버(debug하려는 기계)셋팅 :
복수사용자(클라이언트) : 콘솔창 : WinDbg -server npipe:pipe=pipename
단일사용자 : WinDbg실행 -> 명령창: .server npipe=pipename
2. 클라이언트 셋팅:
1) WinDbg -remote npipe:server=Server, pipe=Pipename[,password=password]
2) WinDbg 실행 -> File-> Connect to Remote Session 선택
npipe:server=Server,pipe=PipeName[,password=Password]입력
* Remote.exe 사용하는 방법
- 서버, 클라이언트 모두 debugging tool 설치, 심볼파일은 클라이언트 설치

◎ 주요 명령어


참조 :
1. MSDN
2. 네이버 윈도우시스템프로그래밍 카페




참고 자료

Windbg Tutorial
http://www.codeproject.com/KB/debug/windbg_part1.aspx

Windows에서 디버깅을 위해 만든 작은 메모리 덤프 파일을 읽는 방법
http://support.microsoft.com/default.aspx?scid=kb;ko;315263

Windbg로 덤프 남기는법
http://msdn.microsoft.com/en-us/library/ff562428.aspx

잊지 말아야 할 점은, windbg는 symbol과 source code의 매칭을 해주지 않는다.
반드시 Symbol File Path와, Source File Path 메뉴 에서 경로를 제대로 지정해주어야, 덤프 분석시 정보가 제대로 출력된다.

windbg shell command

덤프 생성
.dump /f C:\경로\파일명.dmp

덤프 정보 분석
!analyze [-v]

지역변수 보기
dv

콜스택 보기
kb

콜스택에 번호 매겨서 보기
kn

스택 프레임 번호에 맞추기 (0번부터 시작)
.frame [번호]

Display Exception Context Record
.ecxr

레지스터 보기
r

디스 어셈블해서 보기
u [주소]

직접 메모리 내용 보기
dd [주소]

메모리의 내용과 심벌을 일치시켜 보여준다.
dds [주소]

프로세스 리스팅
!process 0 0

프로세스 어태치 시키기
.process /i [pid]


할당된 가상메모리 덤프

올리디버거의 Memory Map윈도의 기능

!vadump [-v]


해당메모리 주소가 어떤 속성인지 알려줌

!vprot [주소]


현재 프로세스내에 동작중인 스레드의 스택을 보여줌

!uniqstack [-b]

-b옵션을 주면 스택에 담긴 아규먼트까지 보여준다


현재 스레드에 할당된 권한(Privilege)를 보여줌

!token


각 스레드가 동작한 시간

!runaway


레지스트리 정보 확인

!dreg

예) 0:000> !dreg System\CurrentControlSet\Services\Tcpip!*


해당 주소를 UNICODE_STRING구조체 형식으로 살펴봄

!ustr [주소]

typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING;


해당 주소를 ANSI_STRING혹은 OEM_STRING구조체로 살펴봄

!str [주소]

typedef struct _STRING {
USHORT Length;
USHORT MaximumLength;
PCHAR Buffer;
} STRING;
typedef STRING ANSI_STRING;
typedef STRING OEM_STRING;



스레드 로컬 스토리지 슬롯을 살펴봄

!tls

모든 슬롯 출력

예) !tls -1

Specifies the thread environment block (TEB). If this is 0 or omitted, the current thread is used

예) !tls 0


현재 스레드의 TEB정보를 출력

!teb


현재 프로세스의 PEB정보를 출력

!peb


잘 알려진 몇몇 STL템플릿정보를 출력

!stl


로딩된 dll모듈의 베이스주소와 길이 모듈명을 출력한다

lm


해당모듈의 자세한 정보를 출력

!lmi [모듈]

예) !lmi 00400000


반복적인 디버거명령을 실행시키면서, 링크드리스트 정보를 출력함

!list

예) !list "-t ntdll!_LIST_ENTRY.Flink -e -x \"dd @$extret l4; dt ntdll!_RTL_CRITICAL_SECTION_DEBUG @$extret-0x8\" ntdll!RtlCriticalSectionList"


현재 Shared User-mode Page를 출력함

현재 타임존과 시스템루트, TickCount와 시간을 출력함

!kuser


로드된 모듈들의 리로케이션되기전의 주소를 출력한다

!imgreloc [주소]


최근 에러코드를 리턴한다

!gle


에러코드를 가지고 무슨 에러인지 설명을 보여준다

!error [에러코드번호]


글로벌 플래그를 설정 혹은 보여준다

!gflag


로드된 모듈들에 대한 커스터마이징(?)된 출력을 해준다.

!for_each_module ["명령어"]

예) !for_each_module .echo @#ModuleIndex : @#Base @#End @#ModuleName @#ImageName @#LoadedImageName

로드된 모듈에서 MZ로 시작되는것을 찾는다
예) !for_each_module s-a @#Base @#End "MZ"


가상메모리에서 이미지헤더를 검색한다 (MZ검색)

.imgscan


표현식을 헥사, 8진수, 2진수, 시간형, Float형, Double 형으로 변환한 형태로 보여준다

.formats [표현식]

디버그 레지스터 확인
0:000> rm 0x20;r
dr0=00000000 dr1=00000000 dr2=00000000
dr3=00000000 dr6=00000000 dr7=00000000
ntdll!KiFastSystemCallRet:
7c93eb94 6a01 push 1

범용레지스터 확인
0:000> rm 0x01;r
eax=00000000 ebx=00000000 ecx=00000006 edx=7c9ac080 esi=7c93e88e edi=00000000
eip=7c93eb94 esp=0007fde8 ebp=0007fee4 iopl=0 nv up ei pl zr na pe nc
ntdll!KiFastSystemCallRet:
7c93eb94 6a01 push 1

레지스터 값 변경
r eip=7c931230
r eax = @ebx
r zf=0


특정 주소에 어셈블 코드 삽입
a <위치>
예) a eip
00401000 sub esp, 10


특정 주소에 원하는 값 삽입
e[옵션] <주소>
예) eb <주소>
00401000 90
00401001 90
00401002 90


현재 보여주는 숫자의 진수바꾸기
n <base>
예) n 8
예) n 16 # 16진수
예) n 10 #10진수로 보여줌


메모리가 참조하고 있는 데이터를 살펴보기
예) dpa esp 현재 스택을 아스키형태로 보여줌
예) dpu esp 현재 스택을 유니코드형태로 보여줌


메모리의 내용을 심벌과 매핑시켜서 보여줌
예) dds esp 현재 콜 스택을 보여줌


반응형

+ Recent posts