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

... 91  92  93  94  95  96  97  98  99  100  101  102  103  104  [105]  ...
NoWriterDateCnt.TitleFile(s)
11299정성태9/9/201719672개발 환경 구성: 330. Hyper-V VM의 Internal Network를 Private 유형으로 만드는 방법
11298정성태9/8/201722968VC++: 119. EnumProcesses / EnumProcessModules API 사용 시 주의점 [1]
11297정성태9/8/201719633디버깅 기술: 96. windbg - 풀 덤프에 포함된 모든 닷넷 모듈을 파일로 저장하는 방법
11296정성태9/8/201722829웹: 36. Edge - "이 웹 사이트는 이전 기술에서 실행되며 Internet Explorer에서만 작동합니다." 끄는 방법
11295정성태9/7/201720222디버깅 기술: 95. Windbg - .foreach 사용법
11294정성태9/4/201720006개발 환경 구성: 329. 마이크로소프트의 CoreCLR 프로파일러 예제 빌드 방법 [1]
11293정성태9/4/201720536개발 환경 구성: 328. Visual Studio(devenv.exe)를 배치 파일(.bat)을 통해 실행하는 방법
11292정성태9/4/201718781오류 유형: 419. Cannot connect to WMI provider - Invalid class [0x80041010]
11291정성태9/3/201720655개발 환경 구성: 327. 아파치 서버 2.4를 위한 mod_aspdotnet 마이그레이션
11290정성태9/3/201723873개발 환경 구성: 326. 아파치 서버에서 ASP.NET을 실행하는 mod_aspdotnet 모듈 [2]
11289정성태9/3/201721523개발 환경 구성: 325. GAC에 어셈블리 등록을 위해 gacutil.exe을 사용하는 경우 주의 사항
11288정성태9/3/201718245개발 환경 구성: 324. 윈도우용 XAMPP의 아파치 서버 구성 방법
11287정성태9/1/201727469.NET Framework: 680. C# - 작업자(Worker) 스레드와 UI 스레드 [11]
11286정성태8/28/201714809기타: 67. App Privacy Policy
11285정성태8/28/201723409.NET Framework: 679. C# - 개인 키 보안의 SFTP를 이용한 파일 업로드파일 다운로드1
11284정성태8/27/201721412.NET Framework: 678. 데스크톱 윈도우 응용 프로그램에서 UWP 라이브러리를 이용한 비디오 장치 열람하는 방법 [1]파일 다운로드1
11283정성태8/27/201717208오류 유형: 418. CSS3117: @font-face failed cross-origin request. Resource access is restricted.
11282정성태8/26/201719655Math: 22. 행렬로 바라보는 피보나치 수열
11281정성태8/26/201721464.NET Framework: 677. Visual Studio 2017 - NuGet 패키지를 직접 참조하는 PackageReference 지원 [2]
11280정성태8/24/201718490디버깅 기술: 94. windbg - 풀 덤프에 포함된 모든 모듈을 파일로 저장하는 방법
11279정성태8/23/201730127.NET Framework: 676. C# Thread가 Running 상태인지 아는 방법
11278정성태8/23/201718258오류 유형: 417. TFS - Warning - Unable to refresh ... because you have a pending edit. [1]
11277정성태8/23/201719488오류 유형: 416. msbuild - error MSB4062: The "TransformXml" task could not be loaded from the assembly
11276정성태8/23/201723812.NET Framework: 675. C# - (파일) 확장자와 연결된 실행 파일 경로 찾기 [2]파일 다운로드1
11275정성태8/23/201732793개발 환경 구성: 323. Visual Studio 설치 없이 빌드 환경 구성 - Visual Studio 2017용 Build Tools [1]
11274정성태8/22/201719354.NET Framework: 674. Thread 타입의 Suspend/Resume/Join 사용 관련 예외 처리
... 91  92  93  94  95  96  97  98  99  100  101  102  103  104  [105]  ...