안녕하세요. Somma, Inc.
에서 일하는 somma
입니다. ^__^
오늘은 Visual Studio Remote Debugger 를 사용할 수 없는 상황이 생겨서 어쩔 수 없이 WinDBG 를 통해서 User mode 서비스를 디버깅하다가 겪은 일에 대해서 간단히 기록을 남기려고 합니다.
앞으로 종종 기술 관련 내용을 남기려고 하니 많이 관심 가져주세요 :)
대상 프로세스 찾기
우리회사에서는 다양한 Win32 응용프로그램, 서비스 그리고 여러 종류의 커널모드 드라이버를 개발하고있습니다. 커널모드 코드를 디버깅할때는 선택의 여지 없이 WinDBG를 사용하고, Win32 서비스나 응용프로그램을 개발할땐 Visual Studio Remote Debugger를 사용합니다. WinDBG는 정말 좋은 디버거지만 여전히 구린(?) GUI 를 제공하고, 특히 STL 이나 MFC 코드를 디버깅하기에는 너무 불편합니다. 하지만 어쩔 수 없이 WINDBG 를 사용해서 Win32 응용프로그램을 디버깅을 해야 하는 경우도 자주 발생합니다.
우선 타겟 프로세스 컨텍스트로 WinDBG 를 연결하기 이해 실행중인 Win32 응용프로그램(test.exe
)을 찾고, 타겟 프로세스에 연결(Attach context)합니다.
!process
: 실행중인 프로세스 목록을 보여주는 명령입니다..process
: 타겟 프로세스의 컨텍스트로 변환/연결하는 명령입니다.
1: kd> !process 0 0 test.exe
PROCESS ffff9b04d4b91080
SessionId: 0 Cid: 0a04 Peb: b860591000 ParentCid: 0294
DirBase: 31181002 ObjectTable: ffffc50f9ed84140 HandleCount: 689.
Image: test.exe
1: kd> .process /r /p /i ffff9b04d4b91080
You need to continue execution (press 'g' <enter>) for the context
to be switched. When the debugger breaks in again, you will be in
the new process context.
1: kd> g
Break instruction exception - code 80000003 (first chance)
nt!DbgBreakPointWithStatus:
fffff805`6bdcc210 cc int 3
디버깅 심볼 로드
WinDBG 를 타겟 프로세스 컨텍스트로 전환하고, 디버깅 심볼을 로드했습니다.
!sym noisy
: 디버깅 심볼로딩 과정을 자세히 보여줍니다. 심볼 관련 에러가 발생하면 원인을 찾는데 도움이 되죠..reload
: 디버깅 심볼을 로드합니다. 기본적으로 타겟 모듈이 실행될때 심볼이 로드되는데/f
스위치를 주면 즉시 로드합니다.
0: kd> !sym noisy
noisy mode - symbol prompts on
0: kd> .reload /f test.exe
SYMSRV: BYINDEX: 0x6
c:\symbols*https://msdl.microsoft.com/download/symbols
test.pdb
82FD4CB70C444A3DB3EC89E1957BD6191
SYMSRV: UNC: c:\symbols\test.pdb\82FD4CB70C444A3DB3EC89E1957BD6191\test.pdb - path not found
SYMSRV: UNC: c:\symbols\test.pdb\82FD4CB70C444A3DB3EC89E1957BD6191\test.pd_ - path not found
SYMSRV: UNC: c:\symbols\test.pdb\82FD4CB70C444A3DB3EC89E1957BD6191\file.ptr - path not found
SYMSRV: HTTPGET: /download/symbols/test.pdb/82FD4CB70C444A3DB3EC89E1957BD6191/test.pdb
SYMSRV: HttpQueryInfo: 80190194 - HTTP_STATUS_NOT_FOUND
SYMSRV: HTTPGET: /download/symbols/test.pdb/82FD4CB70C444A3DB3EC89E1957BD6191/test.pd_
SYMSRV: HttpQueryInfo: 80190194 - HTTP_STATUS_NOT_FOUND
SYMSRV: HTTPGET: /download/symbols/test.pdb/82FD4CB70C444A3DB3EC89E1957BD6191/file.ptr
SYMSRV: HttpQueryInfo: 80190194 - HTTP_STATUS_NOT_FOUND
SYMSRV: RESULT: 0x80190194
*** WARNING: Unable to verify checksum for test.exe
DBGHELP: test - public symbols & lines
C:\test\test.pdb
심볼 탐색 오류
적당한 함수/메소드에 브레이크를 걸기 위해서 심볼네임(함수 이름같은)을 보려고 했더니, 이상한 에러가 발생하고, 심볼을 찾아볼 수 없었습니다. 맨 마지막 라인에 있는 메세지를 보니 partial PDB
라는 용어가 보이네요(저는 처음 봤습니다). 아무래도 저것 때문에 심볼을 찾아볼 수 없는것 같네요.
lmvm
: 로드된 모듈의 정보를 자세히 보는 명령입니다. (lm 명령)x
: eXamine, 특정 모듈의 정보를 조사할때 쓰는 명령입니다./D
스위치와 함께 쓰면 편해요 :-)
0: kd> lmvm test
Browse full module list
start end module name
00007ff6`af1b0000 00007ff6`afaa0000 test C (pdb symbols) C:\test\test.pdb
Loaded symbol image file: test.exe
Image path: C:\dbg\test.exe
Image name: test.exe
Browse all global symbols functions data
Timestamp: Sat May 2 23:57:56 2020 (5EAD8A74)
CheckSum: 00000000
ImageSize: 008F0000
File version: 1.5.14.31
Product version: 1.5.14.31
File flags: 0 (Mask 3F)
File OS: 40004 NT Win32
File type: 1.0 App
File date: 00000000.00000000
Translations: 0409.04b0
Information from resource tables:
CompanyName: Somma, Inc.
ProductName: Monster, Beyond Threat Intelligence
InternalName: test Agent
OriginalFilename: test Agent
ProductVersion: 1,5,14,31
FileVersion: 1,5,14,31
FileDescription: test Agent
LegalCopyright: (C)Somma, Inc. All rights reserved.
0: kd> x /D test!a*
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
DBGHELP: C:\test\test.pdb is a partial PDB and can't enumerate symbol information.
문제 해결
구글 검색을 잠시 해보니 Visual studio 2015 까지는 /DEBUG
옵션의 기본값이 /DEBUG:FULL
이었는데, Visual studio 2017 부터는 /DEBUG:FASTLINK
로 변경되었다고 합니다. 해결책은 링커옵션 Generate Debug Info
를 /DEBUG:FULL
로 변경해 주면 되는것이었네요. ( linker > Debugging
섹션의 Generate Debug Info
항목을 Generate Debug Information optimized for sharing and publishing (/DEBUG:FULL)
로 변경하면 됩니다.)
설정을 변경하고, 응용프로그램을 다시 빌드하고, 동일하게 심볼을 로드해보면 아래와 같이 정상적으로 심볼을 나열할 수 있습니다.
1: kd> x /D test!a*
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
00007ff7`7e20af50 test!aXformType = struct <anonymous-tag> [6]
00007ff7`7e20af2c test!aPrefix = char [7] "-x0"
00007ff7`7e2109c0 test!aKWCode = unsigned char [143] "_p???"
00007ff7`7e2108a0 test!aKWOffset = unsigned short [143]
00007ff7`7e210780 test!aKWNext = unsigned char [143] ""
00007ff7`7e210810 test!aKWLen = unsigned char [143] "???"
00007ff7`7e20b818 test!aJournalMagic = unsigned char [8] "???"
00007ff7`7e20c830 test!aPragmaName = struct PragmaName [71]
00007ff7`7e20b098 test!aDigits = char [33] "0123456789ABCDEF0123456789abcdef"
...
별것 아니지만 오늘도 하나 배웠습니다. :-)