Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 1개 있습니다.)
(시리즈 글이 4개 있습니다.)
디버깅 기술: 90. windbg의 lm 명령으로 보이지 않는 .NET 4.0 ClassLibrary를 명시적으로 로드하는 방법
; https://www.sysnet.pe.kr/2/0/11256

.NET Framework: 689. CLR 4.0 환경에서 DLL 모듈의 로드 주소(Base address) 알아내는 방법
; https://www.sysnet.pe.kr/2/0/11325

.NET Framework: 690. coreclr 소스코드로 알아보는 .NET 4.0의 모듈 로딩 함수
; https://www.sysnet.pe.kr/2/0/11326

디버깅 기술: 102. windbg - .NET 4.0 이상의 환경에서 DLL의 심벌 파일 로드 방법
; https://www.sysnet.pe.kr/2/0/11330




windbg - .NET 4.0 이상의 환경에서 DLL의 심벌 파일 로드 방법

전에 설명한 데로 .NET 4.0부터 DLL 로드를 LoadLibrary Win32 API를 이용하지 않기 때문에 모듈 목록에 DLL이 나타나지 않습니다.

coreclr 소스 코드로 알아보는 .NET 4.0의 모듈 로딩 함수
; https://www.sysnet.pe.kr/2/0/11326

이 때문에 !savemodule 등의 명령어를 위해 모듈에 대한 주소가 필요할 때는 임시로 !name2ee의 출력 결과를 이용해야만 했는데요.

windbg의 lm 명령으로 보이지 않는 .NET 4.0 ClassLibrary를 명시적으로 로드하는 방법
; https://www.sysnet.pe.kr/2/0/11256

위의 내용을 정리하면 모듈 주소를 .reload /f 명령어로 직접 매핑시켜주는 것이었습니다.

0:000> !name2ee *!ClassLibrary1.Class1
Module:      601d1000
Assembly:    mscorlib.dll
--------------------------------------
Module:      00fe3ffc
Assembly:    ConsoleApp1.exe
--------------------------------------
Module:      00fe4e04
Assembly:    ClassLibrary1.dll
Token:       02000002
MethodTable: 00fe51f0
EEClass:     00fe295c
Name:        ClassLibrary1.Class1

0:000> .reload /f ClassLibrary1.dll=00fe4e04

하지만, 아쉽게도 !name2ee의 출력 주소로는 .reload /f [path]=[addr] 명령어가 PDB 로딩까지 하지는 못합니다.




.reload /f [path]=[addr] 명령어의 [addr]에 넣어 주어야 할 것은 실제 모듈의 로딩 주소여야만 합니다. 이를 위해 라이브 디버깅이라면 Process Explorer 등의 도움을 받아 로딩 주소를 알아낼 수 있습니다. 하지만 덤프 파일 분석인 경우라면 로딩 주소를 딱히 알아낼 수 있는 방법이 없습니다. (혹시 이에 대해 아시는 분 덧글 부탁드립니다. ^^)

그나마 제가 생각해 낸 방법은 가능한 모듈 로딩 주소와 가까운 주소를 구해보는 것이었습니다. 예를 들어, 아래는 64비트 응용 프로그램의 !clrstack 출력 결과를 보여줍니다.

0:000> !clrstack
OS Thread Id: 0x2200 (0)
        Child SP               IP Call Site
000000abbf4fedd8 00007ff97e4c5ef4 [HelperMethodFrame_1OBJ: 000000abbf4fedd8] System.Threading.Thread.JoinInternal(Int32)
000000abbf4feee0 00007ff9040908e8 ClassLibrary1.Utility.WaitJoin(System.Threading.Thread)
000000abbf4fef10 00007ff904090776 ConsoleApp1.Program.WaitForThreadExit(System.Threading.Thread) [D:\ConsoleApp1\Program.cs @ 26]
000000abbf4fef40 00007ff904090634 ConsoleApp1.Program.Main(System.String[]) [D:\ConsoleApp1\Program.cs @ 21]
000000abbf4ff220 00007ff9636b6793 [GCFrame: 000000abbf4ff220] 

보는 바와 같이 ConsoleApp1.exe에 있는 메서드의 호출 스택에는 PDB 파일과 연동되어 소스 코드 정보가 나오지만, ClassLibrary1.dll에 있던 Utility.WaitJoin 메서드의 소스 코드 정보가 없습니다.

일단 해당 모듈을 덤프해 보면,

0:000> !ip2md 00007ff9040908e8
MethodDesc:   00007ff903f86308
Method Name:  ClassLibrary1.Utility.WaitJoin(System.Threading.Thread)
Class:        00007ff9040d3a38
MethodTable:  00007ff903f86320
mdToken:      0000000006000006
Module:       00007ff903f85b60
IsJitted:     yes
CodeAddr:     00007ff9040908c0
Transparency: Critical

0:000> !DumpModule /d 00007ff903f85b60
Name:       D:\ConsoleApp1\bin\Debug\ClassLibrary1.dll
Attributes: PEFile SupportsUpdateableMethods
Assembly:   000001eae1482dd0
LoaderHeap:              0000000000000000
TypeDefToMethodTableMap: 00007ff9040d10d0
TypeRefToMethodTableMap: 00007ff9040d10f0
MethodDefToDescMap:      00007ff9040d1198
FieldDefToDescMap:       00007ff9040d11d8
MemberRefToDescMap:      0000000000000000
FileReferencesMap:       00007ff9040d11f8
AssemblyReferencesMap:   00007ff9040d1200
MetaData start address:  000001eae1552088 (1936 bytes)

위와 같이 MetaData start address를 얻을 수 있습니다. 순전히 저의 감으로 "start"라는 단어에 왠지 모듈의 시작과 제법 가까울 거라는 생각이 들었습니다. 거기다 메모리 페이징이 4KB 단위로 정렬된다는 것을 감안할 때, 시작 주소는 결국 다음과 같은 순으로 찾아볼 수 있습니다.

000001eae1552000
000001eae1551000
000001eae1550000

그중에서 PE 헤더의 MZ 시그니처를 찾아낸 것은 000001eae1550000입니다.

0:000> db 000001eae1550000
000001ea`e1550000  4d 5a 90 00 03 00 00 00-04 00 00 00 ff ff 00 00  MZ..............
000001ea`e1550010  b8 00 00 00 00 00 00 00-40 00 00 00 00 00 00 00  ........@.......
000001ea`e1550020  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
000001ea`e1550030  00 00 00 00 00 00 00 00-00 00 00 00 80 00 00 00  ................
000001ea`e1550040  0e 1f ba 0e 00 b4 09 cd-21 b8 01 4c cd 21 54 68  ........!..L.!Th
000001ea`e1550050  69 73 20 70 72 6f 67 72-61 6d 20 63 61 6e 6e 6f  is program canno
000001ea`e1550060  74 20 62 65 20 72 75 6e-20 69 6e 20 44 4f 53 20  t be run in DOS 
000001ea`e1550070  6d 6f 64 65 2e 0d 0d 0a-24 00 00 00 00 00 00 00  mode....$.......

따라서 매핑을 이렇게 해주면 됩니다. ^^

0:000> .reload /f ClassLibrary1.dll=000001eae1550000

이렇게 되면 적절한 PDB 파일이 있는 경우 다음과 같이 콜 스택이 잘 나옵니다.

0:000> !clrstack
OS Thread Id: 0x2200 (0)
        Child SP               IP Call Site
000000abbf4fedd8 00007ff97e4c5ef4 [HelperMethodFrame_1OBJ: 000000abbf4fedd8] System.Threading.Thread.JoinInternal(Int32)
000000abbf4feee0 00007ff9040908e8 ClassLibrary1.Utility.WaitJoin(System.Threading.Thread) [D:\ClassLibrary1\ParamValue.cs @ 15]
000000abbf4fef10 00007ff904090776 ConsoleApp1.Program.WaitForThreadExit(System.Threading.Thread) [D:\ConsoleApp1\Program.cs @ 26]
000000abbf4fef40 00007ff904090634 ConsoleApp1.Program.Main(System.String[]) [D:\ConsoleApp1\Program.cs @ 21]
000000abbf4ff220 00007ff9636b6793 [GCFrame: 000000abbf4ff220] 




그런데 "MetaData start address: 000001eae1552088" 주소로부터 좀 더 멋있게 000001eae1550000 주소를 찾아낼 수 있는 방법이 없을까요?

혹시나 싶어서 Virtual Address 할당을 찾아봤습니다.

0:000> !vadump
BaseAddress:       0000000000000000
RegionSize:        000000007ffe0000
State:             00010000  MEM_FREE
Protect:           00000001  PAGE_NOACCESS

BaseAddress:       000000007ffe0000
RegionSize:        0000000000001000
State:             00001000  MEM_COMMIT
Protect:           00000002  PAGE_READONLY
Type:              00020000  MEM_PRIVATE

...[생략]...

BaseAddress:       000001eae14f0000
RegionSize:        0000000000010000
State:             00001000  MEM_COMMIT
Protect:           00000004  PAGE_READWRITE
Type:              00040000  MEM_MAPPED

BaseAddress:       000001eae1500000
RegionSize:        0000000000002000
State:             00001000  MEM_COMMIT
Protect:           00000004  PAGE_READWRITE
Type:              00020000  MEM_PRIVATE

BaseAddress:       000001eae1502000
RegionSize:        000000000000e000
State:             00002000  MEM_RESERVE
Type:              00020000  MEM_PRIVATE

BaseAddress:       000001eae1510000
RegionSize:        0000000000001000
State:             00001000  MEM_COMMIT
Protect:           00000004  PAGE_READWRITE
Type:              00020000  MEM_PRIVATE

...[생략]...

아쉽게도 vm map 상황에서도 000001eae1552088 영역의 baseaddress가 000001eae1502000로써 000001eae1500000와는 분리되어 있습니다. 하나로 합쳐서 관리했으면 vadump를 이용해 정확한 시작 주소를 한 번에 찾을 수 있었는데... 희망이 없어졌군요. ^^




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 6/1/2021]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2017-10-17 01시07분
windbg - .NET 4.0 이상의 환경에서 모든 DLL에 대한 심벌 파일을 로드하는 파이썬 스크립트
; http://www.sysnet.pe.kr/2/0/11331
정성태

... 151  152  153  154  155  156  157  158  [159]  160  161  162  163  164  165  ...
NoWriterDateCnt.TitleFile(s)
1073정성태6/20/201127148오류 유형: 127. Visual Studio에서 WCF 서비스의 이름 변경 시 발생할 수 있는 오류
1072정성태6/19/201126612.NET Framework: 224. EF 4.1 Code First에서 Identity 칼럼 생성하는 방법파일 다운로드1
1071정성태6/19/201130152.NET Framework: 223. Entity Framework 4.1의 Code First를 이용한 SQL Azure 데이터베이스 생성 [3]파일 다운로드1
1070정성태6/19/201127680.NET Framework: 222. Windows Azure - VM Role 베타 프로그램 참여 [2]
1069정성태6/18/201127762.NET Framework: 221. Cache 영향을 받지 않는 DNS 이름 풀이 [2]파일 다운로드1
1068정성태6/16/201125372개발 환경 구성: 127. Portable Library - 닷넷 N-Screen용 공통 라이브러리 제작 [1]
1067정성태6/15/201124935오류 유형: 126. Windows failed to apply the Group Policy Folder Options settings. [1]
1066정성태6/14/201127948개발 환경 구성: 126. MSDN 구독자 - Windows Azure 무료 서비스 신청하는 방법 [4]
1065정성태6/13/201132772개발 환경 구성: 125. Firebird - 유니코드 기본 문자셋 지정
1064정성태6/11/201127430웹: 22. Visual Studio 2010에서 CSS 3 인텔리센스(intellisense) 지원하는 방법 [1]
1063정성태6/10/201129013웹: 21. Sysnet 웹 사이트의 CSS 2.1 변환 기록 [1]
1062정성태6/9/201129189웹: 20. Sysnet 웹 사이트의 HTML5 변환 기록 [1]
1061정성태6/8/201127431오류 유형: 125. 인터넷 익스플로러 - 개발자 도구에서 정지점(BP: Breakpoint) 설정이 안 되는 경우 [1]
1060정성태6/8/201124001VC++: 51. PHP 모듈의 F5 디버깅
1059정성태6/6/201129117VC++: 50. PHP 모듈 - php_mysql 빌드하는 방법파일 다운로드1
1058정성태6/5/201132755개발 환경 구성: 124. .NET 개발자가 처음 해보는 PHP + MySQL 연동 [2]
1057정성태6/4/201130134VC++: 49. 소스 코드로부터 php5apache2_2.dll 생성하는 방법파일 다운로드1
1056정성태6/2/201128295VC++: 48. 윈도우에서 Apache Module - Content Handler 컴파일파일 다운로드1
1055정성태6/1/201125525오류 유형: 124. MVC 프로젝트의 Site.Master 관련 오류 정리
1054정성태5/31/201129755.NET Framework: 220. ASP.NET MVC Web Site 프로젝트 - 단위 테스트 작성파일 다운로드1
1053정성태5/31/201132305VC++: 47. Apache Module에 대한 'F5 디버그 (Start with debugging)' [2]
1052정성태5/30/201129928.NET Framework: 219. ASP.NET MVC Web Site 프로젝트 구성하기파일 다운로드1
1051정성태5/28/201138412VC++: 46. 윈도우에서 Apache Module 컴파일 (VC++)파일 다운로드1
1050정성태5/28/201124599오류 유형: 123. Firebird - Exception of type 'FirebirdSql.Data.Common.IscException' was thrown.
1049정성태5/28/201130282.NET Framework: 218. WCF REST 서비스 - 웹 브라우저 측 Ajax 호출 캐시 [1]
1048정성태5/27/201132213개발 환경 구성: 123. Apache 소스를 윈도우 환경에서 빌드하기
... 151  152  153  154  155  156  157  158  [159]  160  161  162  163  164  165  ...