반응형

MEX Debugging Extension for WinDbg

- 일반적인 디버거 작업을 단순화하고 디버거에 강력한 텍스트 필터링 기능을 제공


1. Download Link

https://www.microsoft.com/en-us/download/details.aspx?id=53304



2. install

[WinDBG installPath]\winext 에 복사 


3. load

windbg 실행후, command창에 .load mex.dll 


4. command

 

 

 

 !mex.p [process]

 [process] summary

 

 !mex.lt [process]

 [process]의 쓰레드리스트 출력

 

 !mex.us -p [process]

 [process]의 쓰레드별 콜스택 출력

 




반응형
반응형

ba(break on access)

 

ba 커맨드에 대해 설명하기 전에 한가지 재미있는(?혹은 식상할 수도) 코드를 보여드리려 합니다.

이 프로그램은 두 개의 전역변수를 가지고 있고, PrintGlobalVariable() 함수를 통해 두개의 전역변수를 출력합니다.

프로그램이 간단하니 어렵지 않게 출력 결과를 유추하실 수 있겠죠?

char g_szTitle[32] = "TestApp - User";

DWORD g_dwPort = 8080;

 

void PrintGlobalVariable();

 

int _tmain(int argc, _TCHAR* argv[])

{

             PrintGlobalVariable();

             strcpy(g_szTitle, "TestApp - GOODSMELL's COMPUTER!!!!");

             PrintGlobalVariable();

            

             return 0;

}

 

void PrintGlobalVariable()

{

             printf("g_szTitle = %s, g_dwPort  = %d\n", g_szTitle, g_dwPort);

}

 

예상하신 결과가 맞나요??

 

위의 프로그램은 아주 예전에 발생했던 문제를 간소화 해서 코딩한 것입니다.

프로그램의 타이틀은 [AppName + Remote컴퓨터명 + etc…] 형태로 되어있고, 지정된 포트를 통해 다른 컴퓨터에 접속하는 프로그램이었습니다만, 특정 컴퓨터(항상 이게 문제죠…!)에서 접속을 2회 이상시도하게 되면 접속이 되지 않는 문제 였습니다.

 

위 프로그램의 출력결과는 아래와 같습니다.


두번째 출력전에 한것이라곤 g_szTitle에 문자열을 복사한 것 뿐인데, 포트 값이 바뀌어 있네요..

이제 어느정도 감이 오시죠?

 

문제를 좀더 명확히 하기 위해 다음 한 줄의 코딩을 추가했습니다.

printf("g_szTitle address = %p, g_dwPort address = %p\n", g_szTitle, &g_dwPort);

 

출력 결과는 다음과 같습니다.


이제 문제가 명확해 졌군요.
g_szTitleg_dwPort는 다른 변수이지만 할당된 메모리 공간이 인접해 있습니다.

0x002B7058 – 0x002B7038 = 0x20(32) ,g_szTitle이 할당된 메모리(32byte)에 연속해서 g_dwPort(4byte)가 할당되어 있습니다. strcpy()함수 호출을 통해 35byte(g_szTitle:32byte)의 메모리를 변경했기때문에 g_dwPort 3byte를 덮어써 버린것이죠.  

g_szTitle(32bytes)

0x002B7038

g_dwPort(4bytes)

0x002B7058

 

 

 

 

 


위와 같은 문제가 아주 덩치가 큰 프로그램에서 발생한다고 하면, 사실 좀 난감합니다. 일단 어찌 어찌해서 g_dwPort가 변경되는 것 까지는 확인했다고 치겠습니다.

 

이 상황에서 ba 커맨드는 아주 유용합니다.

ba 커맨드의 사용포맷은 이렇습니다.(Access Size 는 붙여서 씁니다)

ba Access Size Address

n  Access

ü  e – excute(설정된 Address가 실행될 때 적중)

ü  r – read/write(설정된 AddressSize만큼 read or write 가 발생할때 적중)

ü  w - write(설정된 AddressSize만큼 write 가 발생할때 적중)

ü  I - io(설정된 Address io가 발생할때 적중) - widbg설명을 보니 port address등에 bp를 걸어 사용하면 되는듯 합니다만, 전 거의 그럴일이 없으니 pass~

n  Size

ü r/w - 1,2 or 4 (x86), 1,2,4 or 8 (x64)

ü 1e - 1

n  Address

ü  Memory 주소

 

자 그럼 위에서 하던 얘기를 마저 하겠습니다. 지금 상황에서는 다음과 같은 커맨드이면 될 것 같습니다.

ba w1 TestApp!g_dwPort

 

그럼 한 단계씩 디버깅해 보겠습니다.

n  [File>Open Excutable]메뉴를 이용해 TestApp.exe를 실행한후 wmail()함수에 breakpoint를 설정합니다. 

n  Wmain() breakpoint가 적중되면, g_dwPort ba커맨드로 breakpoint를 설정합니다.
계속 진행시키면 strcat+0xcd에서 breakpoint가 적중되었군요
.
strcpy
가 내부적으로 strcat을 호출하는가 봅니다.

 

n  그럼 이 시점에서의 CallStack strcat의 인자를 확인해 보겠습니다.
wmain
에서 호출된 strcat(strcpy)에 의해서 g_dwPort값이 변경되는 것을 알 수 있습니다
.
인자를 출력해 보니 역시나, g_szTitle에 복사하려던 문자열이네요.

 

ba 커맨드는 의외로 유용하게 사용 할 상황이 많이 있습니다. 저도 두번인가? 정말 유용하게 써먹었던 기억이 있습니다.^^
(아 그리고 string카피할때는 strncpy나 strcpy_s류의 함수를 쓰는게 좋은 습관인건 아시죠?)

 

 

 

아래는 ba관련 windbg도움말 입니다. 참고하세요.

ba (Break on Access)

The ba command sets a data breakpoint. This breakpoint is triggered when the specified memory is accessed.

Syntax

User-Mode

[~Thread] ba[ID] Access Size [Options] [Address [Passes]] ["CommandString"]

Kernel-Mode

ba[ID] Access Size [Options] [Address [Passes]] ["CommandString"]

Parameters

Thread
Specifies the thread that the breakpoint applies to. For more information about syntax, see Thread Syntax. You can specify threads only in user mode.
ID
Specifies an optional number that identifies the breakpoint. If you do not specify ID, the first available breakpoint number is used. You cannot add space between ba and the ID number. Each processor supports only a limited number of data breakpoints, but there is no restriction on the value of the ID number. If you enclose ID in square brackets ([]), ID can include any expression. For more information about the syntax, see Numerical Expression Syntax.
Access
Specifies the type of access that satisfies the breakpoint. This parameter can be one of the following values.
Option Action
e (execute) Breaks into the debugger when the CPU retrieves an instruction from the specified address.
r (read/write) Breaks into the debugger when the CPU reads or writes at the specified address.
w (write) Breaks into the debugger when the CPU writes at the specified address.
i (i/o) (Microsoft Windows XP and later versions, kernel mode only, x86-based systems only) Breaks into the debugger when the I/O port at the specified Address is accessed.

You cannot add space between Access and Size.

Note On Windows Server 2003 with Service Pack 1 (SP1), on an Itanium-based computer that uses WOW64 to emulate x86, data breakpoints do not work with the execute option but they do work with the read and write options.

Size
Specifies the size of the location, in bytes, to monitor for access. On an x86-based processor, this parameter can be 1, 2, or 4. However, if Access equals e, Size must be 1.

On an x64-based processor, this parameter can be 1, 2, 4, or 8. However, if Access equals e, Size must be 1.

On an Itanium-based processor, this parameter can be any power of 2, from 1 to 0x80000000.

You cannot add space between Access and Size.

Options
Specifies breakpoint options. You can use any number of the following options, except as indicated:
/1
Creates a "one-shot" breakpoint. After this breakpoint is triggered, the breakpoint is permanently removed from the breakpoint list.
/f PredNum
(Itanium only, user mode only) Specifies a predicate number. The breakpoint is predicated with the corresponding predicate register (for example, bp /f 4 address sets a breakpoint that is predicated with the p4 predicate register). For more information about predicate registers, see Itanium Architecture.
/p EProcess
(Kernel mode only) Specifies a process that is associated with this breakpoint. EProcess should be the actual address of the EPROCESS structure, not the PID. The breakpoint is triggered only if it is encountered in the context of this process.
/t EThread
(Kernel mode only) Specifies a thread that is associated with this breakpoint. EThread should be the actual address of the ETHREAD structure, not the thread ID. The breakpoint is triggered only if it is encountered in the context of this thread. If you use /p EProcess and /t EThread , you can enter them in either order.
/c MaxCallStackDepth
Causes the breakpoint to be active only when the call stack depth is less than MaxCallStackDepth. You cannot combine this option together with /C.
/C MinCallStackDepth
Causes the breakpoint to be active only when the call stack depth is larger than MinCallStackDepth. You cannot combine this option together with /c.
Address
Specifies any valid address. If the application accesses memory at this address, the debugger stops execution and displays the current values of all registers and flags. This address must be an offset and suitably aligned to match the Size parameter. (For example, if Size is 4, Address must be a multiple of 4.) If you omit Address, the current instruction pointer is used. For more information about the syntax, see Address and Address Range Syntax.
Passes
Specifies the number of times the breakpoint is passed by until it activates. This number can be any 16-bit value. The number of times the program counter passes through this point without breaking is one less than the value of this number. Therefore, omitting this number is the same as setting it equal to 1. Note also that this number counts only the times that the application executes past this point. Stepping or tracing past this point does not count. After the full count is reached, you can reset this number only by clearing and resetting the breakpoint.
CommandString
Specifies a list of commands to execute every time that the breakpoint is encountered the specified number of times. These commands are executed only if the breakpoint is hit after you issue a g (Go) command, instead of after a t (Trace) or p (Step) command. Debugger commands in CommandString can include parameters.

You must enclose this command string in quotation marks, and you should separate multiple commands by semicolons. You can use standard C control characters (such as \n and \"). Semicolons that are contained in second-level quotation marks (\") are interpreted as part of the embedded quoted string.

This parameter is optional

Environment

Modes User mode, kernel mode
Targets Live debugging only
Platforms All

Comments

The debugger uses the ID number to refer to the breakpoint in later bc (Breakpoint Clear), bd (Breakpoint Disable), and be (Breakpoint Enable) commands.

Use the bl (Breakpoint List) command to list all existing breakpoints, their ID numbers, and their status.

Use the .bpcmds (Display Breakpoint Commands) command to list all existing breakpoints, their ID numbers, and the commands that were used to create them.

The ba command provides the same functionality that the debug registers provide. You can break execution when the particular memory location is read from, written to, or executed.

The breakpoint is satisfied only when the access occurs at the given address and for the specified number of bytes. If the memory that is accessed overlaps the specified area to monitor, the breakpoint is not satisfied.

Although the size is required for all breakpoint types, an execute breakpoint is satisfied only if the address is the first byte in the instruction.

When you debug a multiprocessor system in kernel mode, breakpoints that you set by using bp (Set Breakpoint) or ba apply to all processors. For example, if the current processor is 3 and you type ba e1 MemoryAddress to put a breakpoint at MemoryAddress, any processor (not only processor 3) that executes at that address causes a breakpoint trap.

You cannot set the initial breakpoint in a user-mode process by using the ba command.

You cannot create multiple breakpoints at the same address that differ only in their CommandString values. However, you can create multiple breakpoints at the same address that have different restrictions (for example, different values of the /p, /t, /c, and /C options).

When you debug in kernel mode, the target computer distinguishes between user-mode and kernel-mode data breakpoints. A user-mode data breakpoint cannot affect kernel execution or memory access. A kernel-mode data breakpoint might affect user-mode execution or memory access, depending on whether the user-mode code is using the debug register state and whether there is a user-mode debugger that is attached.

To apply the current process' existing data breakpoints to a different register context, use the .apply_dbp (Apply Data Breakpoint to Context) command.

The following examples show the ba command. The following command sets a breakpoint for read access on 4 bytes of the variable myVar.

0:000> ba r4 myVar

The following command adds a breakpoint on all serial ports with addresses from 0x3F8 through 0x3FB. This breakpoint is triggered if anything is read or written to these ports.

kd> ba i4 3f8

Additional Information

For more information about and examples of using breakpoints, other breakpoint commands and methods of controlling breakpoints, and information about how to set breakpoints in user space from a kernel debugger, see Using Breakpoints. For more information about conditional breakpoints, see Setting a Conditional Breakpoint.

Build machine: CAPEBUILD
반응형
반응형

Windbg Case by Case

 

n  Windbg vmware를 이용한 디버깅 환경 구축
[디버깅/WinDbg] - VMware WinDbg 이용한 커널 디버깅

n  브레이크 포인트에 조건을 걸어 보자
[디버깅/WinDbg] - [WinDbg] 조건부 브레이크 포인트

n  커널모드에서 특정 프로세스 디버깅
[디버깅/WinDbg] - Kernel 모드에서 특정 프로세스 디버깅하기

n  wow64환경에서 x86프로셋 디버깅
[디버깅/WinDbg] - WOW64 디버깅

n 메모리 엑세스 시점 잡기
[디버깅/WinDbg]ba(break on access) 메모리 엑세스 시점


 

반응형
반응형

조건부 breadkpoint

 

아래와 같은 코드를 포함한 TestApp.exe 프로그램이 있고, 지역변수 i의 값이 9999인 상황에 문제가 발생한다 가정해 보자.

void DoSomething( int iValue )

{

             static int iStaticValue = 0;

            

             iStaticValue = iValue;

}

 

void SomeFunction()

{

             int i = 0;

             for ( i = 0; i < 10000; i++ )

             {

                           DoSomething( i );

             }

}

 

이 경우 다음과 같은 선택이 가능할 것이다.

1.      해당 함수에 breakpoint를 걸고 9999번을 트레이스 한다.(설마?)

2.      i값을 디버깅 상태에서 수정해서 디버깅을 한다.(어디까지나 이건 가정이고 만들어낸 예시 코드니까 이경우엔 부가적인 문제가 있다고 치자 - 예시는 예시일 뿐, 꼬투리 잡지 말자~)

3.      다음과 같은 코드를 작성한 후, “int k = 0;”breakpoint를 걸어서 디버깅한다.

void SomeFunction()

{

             int i = 0;

             for ( i = 0; i < 10000; i++ )

             {

                           if ( i == 9999 )

                           {

                                        int k = 0;

                           }

                           DoSomething( i );

             }

}

아마 많은 개발자들이 이와 같은 방법으로 디버깅을 할 꺼라 생각된다.

 

하지만 이러한 상황이 Unload를 지원하지 않는 드라이버의 코드라면? 혹은 여러 모듈이 얽혀서 돌아가는 시스템의 일부라면 여간 성가신 작업이 아닐 수 없다.

 

바로 이러한 상황에 사용하면 편리한 것이 조건부 breakpoint이다.

 

위의 상황에 적용해 보자면 아래와 같은 설정이 되겠다.(SomeFunction breakpoint를 걸려면 주소를 써야하니 보기 편하게 DoSomething() breakpoint를 걸어본다)

bp TestApp!DoSomething ".if@@( iValue == 9999 ) {} .else {gc}"

breakpoint에서 멈춰진 후 iValue의 값을 보면 9999임을 알 수 있다.

 

그럼 조건부 breakpoint 문법에 대해 조금 자세히 살펴보자

bp FuncAddress “.if @@(Condition) { Commands } .elsif @@(Condition) { Commands } .else { Commands }”

n  @@( )
괄호안에는 C/C++타입의 코드를 사용할 수 있을 뿐만 아니라, 정확한 심볼이 있다면 변수의 이름도 사용 가능하다..

n  { Commands }

n  Breakpoint가 적중되었을때 실행될 Windbg Command를 입력한다.

n  메시지를 출력하려면 .echo String 을 쓴다

n  두개 이상의 명령을 사용하려면, ;로 각 명령어를 구분한다.

n  .else 조건(조건이 만족하지 않을 경우)에는 g 커맨드 대신 gc(Go from Conditional Breakpoint)를 사용한다.(gc대신 g를 사용하면 해당 함수를 한 줄씩 step trace하는 동안 해당 함수를 도달 했을때 다음 명령(F10)이 아니라 F5를 누른 것처럼 그냥 진행되어 버린다.)

n  bp FuncAddress ntimes
breakpoint를 설정하면 해당 breakpointntimes만큼 적중(?)되었을 때 breakpoint가 걸리게 된다.
- [ bp TestApp!DoSomthing 10 ]

 

* WinDbg 도움말 키워드 - 'Setting a Conditional Breakpoint' 

* 참고사이트

http://blogs.msdn.com/debuggingtoolbox/archive/2008/06/12/special-command-if-and-j-to-use-in-breakpoints-and-scripts.aspx

http://www.driveronline.org/bbs/view.asp?tb=tipbbs&no=91

반응형
반응형

 

Kernel 모드에서 특정 프로세스 디버깅하기

 

이번 포스트에서는 WinDbg Debugee OS에 연결 한 후(Kernel-Mode), TestApp.exe프로세스를 디버깅하는 절차에 대해 설명합니다.

 

n  디버깅 하려는 TestApp.exe EPROCESS 주소를 확인하기 !process 0 0 TestApp.exe명령을 입력합니다.


n  .process /i EPROCESS주소를 입력하여 해당 프로세스 컨텍스트로 이동합니다.
Context switching
을 위해 ‘g’를 입력하라고 하는군요. ‘g’를 입력해서 진행시키면 context가 변경된 후, 다시 break가 걸립니다.

 

n  TestApp.exe의 심볼을 로드하기 위해 .reload /user 명령을 입력합니다.

 

n  이제 디버깅 하려는 프로세스의 컨텍스트로 이동되었고 심볼도 로드되었으므로, 디버깅하려는 함수에 breakpoint를 설정합니다.

 

n  보시는 것처럼 TestApp OK버튼을 누르면, DoSomething에서 breakpoint가 걸리는 것을 확인 할 수 있습니다.

 

n  로드된 프로세스의 심볼을 언로드 하려면 .reload /u /user명령을 입력하시면 됩니다.

 

이후의 디버깅은 알아서~ 하시면 됩니다.



n  kernel-mode 디버깅 환경에서 특정 프로세스의 user function에 breakpoint 설정하는 방법

> bp /p @$proc KERNEL32!CreateThreadStub


n  user module list

> lmu

반응형
반응형

WinDbg 명령어정리

Command

option

Desc

종료

q

디버깅 종료

qd

디버깅 종료;연결해제

User-mode : Target Application은 종료되지 않는다.

Kernel-mode : Debugee OS Pending되지 않고 계속해서 동작한다.

디버깅 환경정보

vertarget

타겟 컴퓨터 정보 표시

version

디버그 환경 정보 표시

.lastevent

마지막 디버그 이벤트 정보 표시

||

디버깅 세션 정보 표시

symble & sorurce

.symfix

MS 심볼경로 설정

.sympath

심볼경로 확인/설정

!sym noisy

심볼파일 검색 과정을 출력

!sym quiet

심볼파일 검색 과정을 출력하지 않음

.srcpath

.srcpath+ d:\project

소스경로 설정

.srcnoisy

.srcnoisy 1

소스경로 검색 과정을 출력

모듈

lm

로드된 모듈 표시

lm m nt*

패턴과 일치되는 모듈 표시

v

모듈 상세정보 표시

!lmi

!lmi ntdll.dll

모듈 상세정보 표시

.reload

/f test.sys

심볼을 즉시 로드

/i test.sys

TimeStamp가 맞지 않아도 강제로 심볼 로드

/user

[kd] User symbol load

x

x nt!*

x *!*abc*

x /v nt!NtCreateFile

x /t nt!NtCreateFile

x /n nt!ntCreate*

심볼 타입을 표시.

데이터 타입을 표시

이름순으로 정렬

ln

ln [address]

해당 주소에 근접한 심볼의 정보 표시

!dh

!dh [Option] Address

-f  Display file headers

-s  Display Section Headers

-a  Display all header nformation

displays the headers for the specified image

레지스터

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

v

f

콜스택 정보표시

함수정보 출력

인자표시

프레임번호

FPO정보 표시

스택 사용량 표시

break point

bp

bp 0x123456

bp 설정

bl

bp 리스트 출력

bc

bc * | [frame_no]

bp 삭제

bd,be

bd * | [frame_no]

bp disable/enable

bm

bm notepad!*Win*

패턴과 일치하는 모든심볼에 bp설정

bu

bu aaa!bbb

로드되지 않은 심볼에 대한 bp설정

ba

특정 주소에 access bp

지역변수

dv

dv modulr!test*

/i

/V

심볼유형과 인자유형 표시

변수저장 위치 표시( register or address )

데이터유형

dt

df _EPROCESS 0xaddr

dt _PEB @$peb

주소를 특정 데이터 형으로 변환해서 표시

Current Process PEB정보 디스플레이

du

dpu



Unicode string 표시

da

dpa

Ansi string 표시

dc

db

dy

!address

!address

!address [address]

dds

dds [Options] [Range]


dds esp esp+100

Display Words and Symbols

esp 부터 esp+100까지의 값을 출력

- callstack이 깨진경우, stack확인의 용도로 사용할 수 있다

프로세스 & 스레드 정보

!peb

PEB(Process Environment Block)표시

!teb

TEB(Thread Environment Block) 표시

~*kb

모든 thread의 콜스택 표시

!gle

API의 마지막 에러코드 표시

실행 제어

t

Trace

~.t

다른 스레드를 중지시킨 상태에서 하나의 statementt 실행

g

p

Step Over

gu

gu


~0 gu

현재함수가 복귀할 때 까지 실행

스레드 0을 제외한 모든 스레드를 freeze

wt

-oR

내부에서 호출된 함수와 함수호출 횟수등의 정보 표시

(특정 API 내부에서 호출되는 함수와 결과를 한눈에 확인 할 수 있다.)

.cxr

컨텍스트 변경

!ready

.thread

!thread

.trap

.process

!process

ed

eb

eb .-6 90 90 90 90 90 90

6byteNOP(0x90)으로 변경

!error

!error [error code]

에러코드 정보표시

 

 

 

 

 

 

Set Exceptions

sxe

 

sxe av(0xc0000005)


sxe ld:[moduleName]


sxe ud:[moduleName]

Set Exceptions

Break when access violation

Break when [module] load

Break when [module] unlod

sxd

sxd av

Disable Break when av(first chance)

Create Dump

.dump

/f  full user-mode dump

/m  minidump

/u  Append date,time,PID

Create Dump File

.writemem

.writemem FileName Range

 

: Range – BaseAddr L”DumpSize”

 

.writemem c:\a.dll 0x00030000 L28000

writes a section of memory to a file

WinDBG 설정

.lines -e

라인정보 표시

.enable_unicode 1

Watch, local변수 창에 유니코드 표시

ed Kd_DEFAULT_MASK 8

Vista이상: DbgPrint출력 활성화

 

반응형

+ Recent posts