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)
12017정성태9/11/201911801오류 유형: 566. 비주얼 스튜디오 - Failed to register URL "http://localhost:6879/" for site "..." application "/". Error description: Access is denied. (0x80070005)
12016정성태9/5/201912763오류 유형: 565. git fetch - warning: 'C:\ProgramData/Git/config' has a dubious owner: '(unknown)'.
12015정성태9/3/201916609개발 환경 구성: 457. 윈도우 응용 프로그램의 Socket 연결 시 time-out 시간 제어
12014정성태9/3/201911132개발 환경 구성: 456. 명령행에서 AWS, Azure 등의 원격 저장소에 파일 관리하는 방법 - cyberduck/duck 소개
12013정성태8/28/201913938개발 환경 구성: 455. 윈도우에서 (테스트) 인증서 파일 만드는 방법 [3]
12012정성태8/28/201917811.NET Framework: 859. C# - HttpListener를 이용한 HTTPS 통신 방법
12011정성태8/27/201915907사물인터넷: 57. C# - Rapsberry Pi Zero W와 PC 간 Bluetooth 통신 예제 코드파일 다운로드1
12010정성태8/27/201911267VS.NET IDE: 138. VSIX - DTE.ItemOperations.NewFile 메서드에서 템플릿 이름을 다국어로 설정하는 방법
12009정성태8/26/201911864.NET Framework: 858. C#/Windows - Clipboard(Ctrl+C, Ctrl+V)가 동작하지 않는다면?파일 다운로드1
12008정성태8/26/201911600.NET Framework: 857. UWP 앱에서 SQL Server 데이터베이스 연결 방법
12007정성태8/24/201910787.NET Framework: 856. .NET Framework 버전을 올렸을 때 오류가 발생할 수 있는 상황
12006정성태8/23/201914011디버깅 기술: 129. guidgen - Encountered an improper argument. 오류 해결 방법 (및 windbg 분석) [1]
12005정성태8/13/201911979.NET Framework: 855. 닷넷 (및 VM 계열 언어) 코드의 성능 측정 시 주의할 점 [2]파일 다운로드1
12004정성태8/12/201919730.NET Framework: 854. C# - 32feet.NET을 이용한 PC 간 Bluetooth 통신 예제 코드 [14]
12003정성태8/12/201912479오류 유형: 564. Visual C++ 컴파일 오류 - fatal error C1090: PDB API call failed, error code '3'
12002정성태8/12/201911464.NET Framework: 853. Excel Sheet를 WinForm에서 사용하는 방법 - 두 번째 이야기 [5]
12001정성태8/10/201915949.NET Framework: 852. WPF/WinForm에서 UWP의 기능을 이용해 Bluetooth 기기와 Pairing하는 방법 [1]
12000정성태8/9/201914841.NET Framework: 851. WinForm/WPF에서 Console 창을 띄워 출력하는 방법파일 다운로드1
11999정성태8/1/201910617오류 유형: 563. C# - .NET Core 2.0 이하의 Unix Domain Socket 사용 시 System.IndexOutOfRangeException 오류
11998정성태7/30/201911793오류 유형: 562. .NET Remoting에서 서비스 호출 시 SYN_SENT로 남는 현상파일 다운로드1
11997정성태7/30/201913330.NET Framework: 850. C# - Excel(을 비롯해 Office 제품군) COM 객체를 제어 후 Excel.exe 프로세스가 남아 있는 문제 [2]파일 다운로드1
11996정성태7/25/201915777.NET Framework: 849. C# - Socket의 TIME_WAIT 상태를 없애는 방법파일 다운로드1
11995정성태7/23/201918854.NET Framework: 848. C# - smtp.daum.net 서비스(Implicit SSL)를 이용해 메일 보내는 방법 [2]
11994정성태7/22/201914346개발 환경 구성: 454. Azure 가상 머신(VM)에서 SMTP 메일 전송하는 방법파일 다운로드1
11993정성태7/22/20199782오류 유형: 561. Dism.exe 수행 시 "Error: 2 - The system cannot find the file specified." 오류 발생
11992정성태7/22/201911553오류 유형: 560. 서비스 관리자 실행 시 "Windows was unable to open service control manager database on [...]. Error 5: Access is denied." 오류 발생
... 61  62  63  64  [65]  66  67  68  69  70  71  72  73  74  75  ...