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
정성태

... 106  107  108  109  110  111  112  113  114  115  116  117  118  [119]  120  ...
NoWriterDateCnt.TitleFile(s)
10949정성태4/28/201619843.NET Framework: 575. SharedDomain과 JIT 컴파일파일 다운로드1
10948정성태4/28/201623772.NET Framework: 574. .NET - 눈으로 확인하는 SharedDomain의 동작 방식 [3]파일 다운로드1
10947정성태4/27/201621635.NET Framework: 573. .NET CLR4 보안 모델 - 4. CLR4 보안 모델에서의 조건부 APTCA 역할파일 다운로드1
10946정성태4/26/201624483VS.NET IDE: 106. Visual Studio 2015 확장 - INI 파일을 위한 사용자 정의 포맷 기능 (Syntax Highlighting)파일 다운로드1
10945정성태4/26/201618204오류 유형: 327. VSIX 프로젝트 빌드 시 The "VsTemplatePaths" task could not be loaded from the assembly 오류 발생
10944정성태4/22/201619446디버깅 기술: 80. windbg - 풀 덤프 파일로부터 텍스트 파일의 내용을 찾는 방법
10943정성태4/22/201624308디버깅 기술: 79. windbg - 풀 덤프 파일로부터 .NET DLL을 추출/저장하는 방법 [1]
10942정성태4/19/201619628디버깅 기술: 78. windbg 사례 - .NET 예외가 발생한 시점의 오류 분석 [1]
10941정성태4/19/201619529오류 유형: 326. Error MSB8020 - The build tools for v120_xp (Platform Toolset = 'v120_xp') cannot be found.
10940정성태4/18/201622787Windows: 116. 프로세스 풀 덤프 시간을 줄여 주는 Process Reflection [3]
10939정성태4/18/201623820.NET Framework: 572. .NET APM 비동기 호출의 Begin...과 End... 조합 [3]파일 다운로드1
10938정성태4/13/201623399오류 유형: 325. 파일 삭제 시 오류 - Error 0x80070091: The directory is not empty.
10937정성태4/13/201631621Windows: 115. UEFI 모드로 윈도우 10 설치 가능한 USB 디스크 만드는 방법
10936정성태4/8/201642285Windows: 114. 삼성 센스 크로노스 7 노트북의 운영체제를 USB 디스크로 새로 설치하는 방법 [3]
10935정성태4/7/201626602웹: 32. Edge에서 Google Docs 문서 편집 시 한영 전환키가 동작 안하는 문제
10934정성태4/5/201625347디버깅 기술: 77. windbg의 콜스택 함수 인자를 쉽게 확인하는 방법 [1]
10933정성태4/5/201630947.NET Framework: 571. C# - 스레드 선호도(Thread Affinity) 지정하는 방법 [8]파일 다운로드1
10932정성태4/4/201623249VC++: 96. C/C++ 식 평가 - printf("%d %d %d\n", a, a++, a);
10931정성태3/31/201623508개발 환경 구성: 283. Hyper-V 내에 구성한 Active Directory 환경의 시간 구성 방법 [3]
10930정성태3/30/201621450.NET Framework: 570. .NET 4.5부터 추가된 CLR Profiler의 실행 시 Rejit 기능
10929정성태3/29/201631575.NET Framework: 569. ServicePointManager.DefaultConnectionLimit의 역할파일 다운로드1
10928정성태3/28/201637283.NET Framework: 568. ODP.NET의 완전한 닷넷 버전 Oracle ODP.NET, Managed Driver [2]파일 다운로드1
10927정성태3/25/201626516.NET Framework: 567. System.Net.ServicePointManager의 DefaultConnectionLimit 속성 설명
10926정성태3/24/201626041.NET Framework: 566. openssl의 PKCS#1 PEM 개인키 파일을 .NET RSACryptoServiceProvider에서 사용하는 방법 [10]파일 다운로드1
10925정성태3/24/201620362.NET Framework: 565. C# - Rabin-Miller 소수 생성 방법을 이용하여 RSACryptoServiceProvider의 개인키를 직접 채워보자 - 두 번째 이야기파일 다운로드1
10924정성태3/22/201620977오류 유형: 324. Visual Studio에서 Azure 클라우드 서비스 생성 시 Failed to initialize the PowerShell host 에러 발생
... 106  107  108  109  110  111  112  113  114  115  116  117  118  [119]  120  ...