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]의 쓰레드별 콜스택 출력

 




Windbg 콘솔 출력 내용 파일로 저장하는 방법


1. execute option

windbg -logo logofile.txt

2. command

.logopen / .logclose

3. menu

edit>Open/Close Log File...


VS2008 & VMWare7 원격디버깅

 

Visual Studio 원격디버깅 모니터(msvsmon.exe)를 이용하면, Visual Studio로 원격 컴퓨터의 어플리케이션을 디버깅 할 수 있습니다. 이름하야 원격디버깅….

하지만 원격 디버깅을 하기 위한 준비과정(원격 디버깅 모니터 복사/로그인 인증/ binary&.pdb 복사 등)이 다소 복잡하고, 프로젝트 별로 반복 작업이 필요하기 때문에 디버깅하기가 번거롭습니다.

물론 이 또한 공유폴더와 VS 빌드환경 구성을 통해 어느 정도 자동화 할 수는 있겠으나, 이 역시 불편하기는 매한가지입니다.

 

좀더 편하게 원격디버깅 할 수 있는 방법은 없을까?

 

Visual Studio VMWare(+Visaul Studio PlugIn)을 이용하면, 상대적으로 간편한 설정으로 원격 디버깅을 할 수 있으니, 설정 방법을 한번 익혀두면 꽤나 편리하게 써 먹을 수 있을 듯 합니다.

1.    Requirement

1)    Visual Studio 2005, 2008 ( 2010은 지원하지 않음)

2)    VMWare 6.x( Intergrated Virtual Debuggers-Visaul Studio PlugIn 설치)


3)    Host OS: Windows XP 이상

4)    Guest OS: Windows 2000 이상

 

2.    Settings

1)    VMWare Guest OS 설정

     Windows Login ID/Password설정

n Host OS Login ID/Password와 동일하게 설정합니다.

     Windows 자동로그인 설정(optional)

n 윈도우 자동로그인 설정을 하면 좀더 편하게 디버깅 할 수 있습니다.

n 참고: 2011/09/07 - [Windows Tip] - Windows 자동로그인

2)    Visual Studio 설정

     VMWare에서 제공하는 Visual Studio PlugIn을 설치하면, Visaul Studio에 아래와 같은 메뉴와 툴바가 추가됩니다.



     VMWare>options 혹은 툴바의 제일 마지막 메뉴를 선택해서 설정 창을 띄운 후, 아래와 같이 설정합니다.


n Virtual Machine

ü  원격 디버깅할 VMWare 이미지(*.vmx)를 선택합니다.

n Remote Debug Monitor Path

ü  Visual Studio 원격디버깅 모니터의 경로를 설정합니다.

ü  해당 위치를 기본으로 잡아주지만, 혹 잡히지 않는 다면 아래의 경로를 지정해 줍니다.(Visual StudioC드라이브에 디폴트 경로에 설치 기준)

ü  C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\Remote Debugger\x86\msvsmon.exe

n Guest Login Credentials

ü  윈도우의 로그인 ID/PASSWORD를 설정합니다.(Guest/Host OS는 동일 User ID/PASWORD를 사용하여 로그인 하도록 설정.)

 

3)    원격디버깅

     VMWare> Start Live Debugging in VM 혹은 ToolBar의 첫번째 메뉴를 선택해 디버깅을 시작합니다.(이 메뉴는 VM이 구동중이 아니면 VM을 구동한다.)

     좀더 자세히 살펴보면 해당 메뉴 실행시 다음의 과정을 자동화 해주는 것으로 보입니다.

n Visual Studio 원격디버깅 모니터(msvsmon.exe)가 존재하는 폴더를 Geust OS와 공유

n 디버깅 대상 프로젝트 빌드(필요시)

n 디버깅 대상프로그램의 binary & symbol(.pdb) 를 Guest OS와 공유

n Visual Studio 원격디버깅 모니터 & 디버깅 대상 App 실행(Guest OS)

n 디버깅

     , 번거로운 복사 및 설정 작업을 자동화 해준다고 보면 되겠습니다.

 

3.    Error

1)    간혹 아래와 같은 창이 뜨는 경우가 있는데, Guest OS의 방화벽문제이거나(Guest OS에서 실행되는 msvsmon.exe를 네트웍엑세스 가능하도록 풀어주면 됩니다.), Guest/Host OS의 로그인 ID/PASSWORD문제 일 수 있느니 확인 해보시기 바랍니다.
메시지에서 알수 있듯이 위의 구성으로 디버깅 하는 경우, Guest/Host OS의 로그인 ID/PASSWORD를 일치시키는 것은 필수 인듯 합니다.


 

 

4.    Reference

1)    VMWare Intergrated Virtual Debuggers-Visaul Studio PlugIn 매뉴얼

 

 

 

 

 

 

 


StackFrame구성 - Stack Overflow

 

CallStack에 대한 이해를 돕고자 간단한 Quiz로 포스팅을 시작하겠습니다.

아래 와 같은 코드가 있습니다.

void Dummy()

{

           printf("In Dummy Function()\n");

 

           exit(0);

}

 

void SomFunction()

{

           BYTE Buf[4];

 

           printf("In SomFunction()\n");

            

           return ;

}

 

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

{

           printf("Before call SomFunction()\n");

 

           SomFunction();

          

           printf("After call SomFunction()\n");

          

           return 0;

}

 

asm 코드를 쓰지 않고 SomFunction()을 수정하여 아래와 같은 결과를 출력한 후, 프로그램이 종료되도록 수정할 수 있을까요?

 

. 감이 오지 않는 분들은 아래의 글을 먼저 읽고 오시기 바랍니다.

[디버깅을 위한 기초지식 #3] CallStack - 프로시저 호출에 따른 스택의 구성

 

이제 어느 정도 방향을 잡으셨나요?

 

아래와 같은 방법을 사용하면 위의 문제를 해결할 수 있을 것 같습니다.

1.     Return Address가 저장되어 있는 Stack상의 Address를 구한다.

A.     asm 코드를 사용하지 않으려면, 해당 주소를 구하기 위해 로컬변수 Buf address를 통해 구해야 할 것 같습니다.

2.     1에서 구한 Address Dummy() 함수의 Address Overwrite하게 되면, SomeFunction이 모든 처리를 마치고 리턴할 때 Dummy()가 실행되게 됩니다..

생각보다 복잡하지는 않군요.

 

그럼 한단계씩 따라가 보겠습니다.

1.     SomeFunction이 복귀할 ReturnAddress가 저장된 Stack상의 주소 구하기

A.     스택 구성에 대한 링크된 포스트의 그림에 따르면 첫번째 로컬 변수의 위치는 ebp-0x4이므로, szBuf의 주소+0x4(szBuf사이즈) ebp의 주소가 됩니다.

B.     Rerturn Address의 주소는 ebp+0x4이므로, [ Buf+0x4/*ebp*/+4/*ebp+4*/ ]로 계산이 되겠습니다.

void SomFunction()

{

           BYTE Buf[8];

           DWORD *pDw = (DWORD *)(Buf+0x8/*ebp*/+4/*ebp+4*/);

 

           *pDw = (DWORD)Dummy;

 

           printf("In SomFunction()\n");

            

           return ;

}


 

2. 그럼 이제 구해진 주소에 Dummy()함수의 주소를 write해주기만 하면 되겠군요

*pDw = (DWORD)Dummy;



혹시 위의 코드를 기반으로 혹은 코드를 작성해서 따라해 보신분…?

머야? 안돼잖아~ 하신분도 있으리라 생각됩니다.

 

저도 테스트를 하다 보니 CallStack내에서의 첫번째 로컬 변수의 주소가 항상 ebp – 0x4의 주소에 위치하지는 않더군요.

 

위의 테스트 코드는 아래의 환경하에서 정상 동작했습니다.

n  VC++ 2008, Release모드, optimize off

 

n  Optimize(최적화)옵션을 켜게 되면, SomFunction()함수의 코드가 wmain()함수 안에 삽입되어 버리거나, stack frame이 생략되는 등 실제 작성된 코드와는 많은 부분이 달라질 수 있습니다.
실제로 아래의 코드는 [ release mode: 최적화-속도 최대화 ]를 설정하여 컴파일한 코드로 SomFunction()의 코드가 wmain()함수 안쪽에 박혀(?)있습니다.


 

n  또한 링크된 포스트의 그림에서 설명한 대로라면 SomeFunction()안의 szBuf변수의 사이즈를 8로 잡을 경우, 첫 번째 로컬변수의 주소가 ebp-0x8로 할당될것으로 생각했으나, 실제로는  ebp-0xc위치로 할당되었습니다.(어떠한 다른 규칙이 있는 것인지 제가 잘못 이해한것인지? 답을 아시는 분은 좀 알려주세요~)


 

별 활용성은 없는 코드이 겠으나, 위의 내용을 완벽히 이해하시면 CallStack BackTrace라던지 함수 호출 흐름 분석에 꽤 도움이 되지 않을까 생각합니다.


Calling Convention

콜링컨벤션
스택을 이용하여 파라미터를 전달할 때 파라미터를 스택에 넣는 순서와 전달된 파라미터를 어느곳에서 해제할 것인지 등을 결정하는 방식

) __cdecl

C 또는 C++프로그램에서 파라미터 전달시 디폴트로 사용하는 방식으로 이 방식에 의한 파라미터 전달은 오른쪽에서 왼족 방향으로 이루어지게 되며 프로시저를 호출한 쪽에서 파라미터에 대한 해제를 책임진다.


main()

{

           Sum(1, 2);

}

 

 

int Sum( int a, int b )

{

           return a+b;

}

Main:

           push    2

           push    1

           call       sum

           add      esp, 8

 

Sum:

           push    ebp ( 이전 프로시저의 ebp값을 저장 )

           mov     ebp, esp( 이전 프로시저의 ebp값을, push한 시점의 esp를 현재 스택 프레임의 ebp로 설정)

           mov     eax, dword ptr[ebp+0x8]

           add      eax, dword ptr[ebp+0xc]

           pop      ebp

           ret

) __stdcall
Windows API의 프로시저에서 사용하는 방식으로 파라미터 전달은 __cdecl방식과 같이 오른쪽에서 왼쪽방향으로 스택에 저장하게 되지만 파라미터의 해제는 프로시저가 복귀되기 전에 이루어 진다.

main()

{

           Sum(1, 2);

}

 

int Sum( int a, int b )

{

           return a+b;

}

Main:

           push    2

           push    1

           call       sum

 

Sum:

           push    ebp

           mov     ebp, esp

           mov     eax, dword ptr[ebp+0x8]

           add      eax, dword ptr[ebp+0xc]

           pop      ebp

           ret       8

장점

- 함수의 독립성이 뛰어남. 즉 프로시저를 부르기 전에 스택에 파라미터를 쌓아놓고 그 프로시저를 부르기만 하면 그 함수가 리턴되어진 후에는 그 프로시저의 스택포인터(esp)가 이전 상태로 복원되어 있으므로, 복귀된 후에 호출한 프로시저에 대하여 더 이상 신경을 쓰지 않아도 된다.

- 스택을 해제하는 코드가 호출한 프로시저 안에 있으므로 만약 이 프로시저가 여러곳에서 호출된다 하더라도 스택을 해제하는 코드는 프로시저 내에 하나만 존재하므로, 결국 __cdecl 방식에 비해 코드의 크기가 줄어든다.

) __fastcall

__fastcall 방식에서는 처음 두개의 파라미터는 스택을 이용하지않고 ecx edx 레지스터를 사용하며 그 이상의 파라미터에 대해서만 오른쪽에서 왼쪽 방향으로 스택에 저장하게 된다. 이 방식에서의 스택 제거는 __stdcall과 동일하다

) 정리

Calling Convention

파라미터 전달

스택메모리 제거

__cdecl

오른쪽에서 왼쪽 방향으로 스택에 저장

호출한 쪽에서 처리

__stdcall

오른쪽에서 왼쪽 방향으로 스택에 저장

호출 당한 쪽에서 처리

__fastcall

처음 두개의 파라미터는 ecx edx 레지스터에 저장, 그 이상의 파라미터는 오른쪽에서 왼쪽 방향으로 스택에 저장

호출 당한 쪽에서 처리

 

 

 



이 그림을 이해하는 것만으로도 (물론 대부분의 경우 디버거가 해주겠지만) 디버거의 도움을 받지 않더라도 함수 호출관계, 파라미터, 변수 등에 대한 많은 부가 정보를 읽을 수 있습니다.

프로그램 경력이 미천할 때( 지금도 마찬가지 입니다만…) 드라이버 개발자 분께서 어셈코드를 보고, 파라미터가 몇 개인 함수고,,, 인자가 어쩌고~ 하시면서 분석하는 과정을 경이롭게 지켜보았던 적이 있었는데… calling convention stack frame의 구성에 대한 이해가 수반된다면 얼추 흉내는 낼 수 있을거 같습니다. .

 
2011/07/25 - [디버깅] - Stack Frame구성 - Stack Overflow

2011/07/18 - [디버깅] - [디버깅을 위한 기초지식 #4] Calling Convention

+ Recent posts