Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 2개 있습니다.)

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

... 61  62  63  64  65  66  67  [68]  69  70  71  72  73  74  75  ...
NoWriterDateCnt.TitleFile(s)
11942정성태6/13/201917632개발 환경 구성: 444. 로컬의 Visual Studio Code로 원격 리눅스 머신에 접속해 개발하는 방법 [1]
11941정성태6/13/201910959오류 유형: 546. "message NETSDK1057: You are using a preview version of .NET Core" 빌드 경고 없애는 방법
11940정성태6/13/201910905개발 환경 구성: 443. Visual Studio의 Connection Manager 기능(Remote SSH 관리)을 위한 명령행 도구파일 다운로드1
11939정성태6/13/20199959오류 유형: 545. Managed Debugging Assistant 'FatalExecutionEngineError'
11938정성태6/12/201911594Math: 59. C# - 웨이트 벡터 갱신식을 이용한 퍼셉트론 분류파일 다운로드1
11937정성태6/11/201918061개발 환경 구성: 442. .NET Core 3.0 preview 5를 이용해 Windows Forms/WPF 응용 프로그램 개발 [1]
11936정성태6/10/201911313Math: 58. C# - 최소 자승법의 1차, 2차 수렴 그래프 변화 확인 [2]파일 다운로드1
11935정성태6/9/201912003.NET Framework: 843. C# - PLplot 출력을 파일이 아닌 Window 화면으로 변경
11934정성태6/7/201913182VC++: 133. typedef struct와 타입 전방 선언으로 인한 C2371 오류파일 다운로드1
11933정성태6/7/201913217VC++: 132. enum 정의를 C++11의 enum class로 바꿀 때 유의할 사항파일 다운로드1
11932정성태6/7/201911806오류 유형: 544. C++ - fatal error C1017: invalid integer constant expression파일 다운로드1
11931정성태6/6/201911914개발 환경 구성: 441. C# - CairoSharp/GtkSharp 사용을 위한 프로젝트 구성 방법
11930정성태6/5/201912497.NET Framework: 842. .NET Reflection을 대체할 System.Reflection.Metadata 소개 [1]
11929정성태6/5/201912217.NET Framework: 841. Windows Forms/C# - 클립보드에 RTF 텍스트를 복사 및 확인하는 방법 [1]
11928정성태6/5/201910851오류 유형: 543. PowerShell 확장 설치 시 "Catalog file '[...].cat' is not found in the contents of the module" 오류 발생
11927정성태6/5/201911829스크립트: 15. PowerShell ISE의 스크립트를 복사 후 PPT/Word에 붙여 넣으면 한글이 깨지는 문제 [1]
11926정성태6/4/201913251오류 유형: 542. Visual Studio - pointer to incomplete class type is not allowed
11925정성태6/4/201912058VC++: 131. Visual C++ - uuid 확장 속성과 __uuidof 확장 연산자파일 다운로드1
11924정성태5/30/201913836Math: 57. C# - 해석학적 방법을 이용한 최소 자승법 [1]파일 다운로드1
11923정성태5/30/201913467Math: 56. C# - 그래프 그리기로 알아보는 경사 하강법의 최소/최댓값 구하기파일 다운로드1
11922정성태5/29/201911546.NET Framework: 840. ML.NET 데이터 정규화파일 다운로드1
11921정성태5/28/201916442Math: 55. C# - 다항식을 위한 최소 자승법(Least Squares Method)파일 다운로드1
11920정성태5/28/201910041.NET Framework: 839. C# - PLplot 색상 제어
11919정성태5/27/201913160Math: 54. C# - 최소 자승법의 1차 함수에 대한 매개변수를 단순 for 문으로 구하는 방법 [1]파일 다운로드1
11918정성태5/25/201914356Math: 53. C# - 행렬식을 이용한 최소 자승법(LSM: Least Square Method)파일 다운로드1
11917정성태5/24/201914462Math: 52. MathNet을 이용한 간단한 통계 정보 처리 - 분산/표준편차파일 다운로드1
... 61  62  63  64  65  66  67  [68]  69  70  71  72  73  74  75  ...