windbg - 풀 덤프 파일로부터 .NET DLL을 추출/저장하는 방법
(2022-10-09 업데이트) 아래의 글에 설명한 방법 외에 "리눅스 환경의 .NET Core 3/5+ 메모리 덤프로부터 모든 닷넷 모듈을 추출하는 방법"에서 설명한 SaveModules.zip을 빌드한 프로그램을 사용하셔도 됩니다.
예전에도, Managed와 Native DLL에 대해 파일로 추출해서 저장하는 방법을 소개해 드렸는데요.
닷넷 응용 프로그램에서 특정 예외가 발생했을 때 풀 덤프받는 방법
; https://www.sysnet.pe.kr/2/0/1376
windbg - 덤프 파일로부터 네이티브 DLL을 추출하는 방법
; https://www.sysnet.pe.kr/2/0/1386
최근에 받은 full dump 파일로부터 .NET DLL을 저장하려고 해보니... ^^; lm 명령어에 해당 .NET DLL이 보이질 않습니다. 그런데, 그 DLL만 없는 것이 아니고 전체적으로 Managed DLL이 목록에 나타나질 않았는데, 실제로 다음은 .NET MVC Framework 기반의 웹 응용 프로그램의 풀 덤프를 받아 lm 명령으로 모듈을 나열한 결과입니다.
0:018> lm
start end module name
00000000`773f0000 00000000`774c2000 msvcr100 (deferred)
00007ff6`180b0000 00007ff6`180ba000 w3wp (deferred)
00007ffd`1d1e0000 00007ffd`1d2e6000 diasymreader (deferred)
00007ffd`1e360000 00007ffd`1e6ad000 System_Data (deferred)
00007ffd`23b00000 00007ffd`23b25000 System_EnterpriseServices_Wrapper (deferred)
00007ffd`23c30000 00007ffd`23c50000 clrcompression (deferred)
00007ffd`23d70000 00007ffd`23dd8000 OraOps12 (deferred)
00007ffd`255a0000 00007ffd`255a8000 webengine (deferred)
00007ffd`255b0000 00007ffd`255bb000 wbhst_pm (deferred)
00007ffd`257b0000 00007ffd`257be000 gzip (deferred)
00007ffd`25c10000 00007ffd`25c1b000 aspnet_filter (deferred)
00007ffd`25c20000 00007ffd`25c31000 authsspi (deferred)
00007ffd`25c40000 00007ffd`25c4e000 authbas (deferred)
00007ffd`25c50000 00007ffd`25c59000 validcfg (deferred)
00007ffd`25c60000 00007ffd`25c74000 filter (deferred)
00007ffd`25c80000 00007ffd`25ca0000 isapi (deferred)
00007ffd`25e50000 00007ffd`25e63000 wsnmp32 (deferred)
00007ffd`25e70000 00007ffd`25ebd000 System_Transactions (deferred)
00007ffd`25ec0000 00007ffd`25ef8000 oraons (deferred)
00007ffd`25f00000 00007ffd`25f96000 cryptui (deferred)
00007ffd`26010000 00007ffd`2603c000 iisfreb (deferred)
00007ffd`26040000 00007ffd`2fa25000 OraOCIEI12 (deferred)
00007ffd`2fa60000 00007ffd`2fb08000 oci (deferred)
00007ffd`2fd60000 00007ffd`2fd7d000 iisetw (deferred)
00007ffd`2ffc0000 00007ffd`2ffce000 modrqflt (deferred)
00007ffd`2ffd0000 00007ffd`2ffe0000 cscapi (deferred)
00007ffd`30000000 00007ffd`30009000 iisreqs (deferred)
00007ffd`30010000 00007ffd`3001d000 loghttp (deferred)
00007ffd`30020000 00007ffd`3002f000 custerr (deferred)
00007ffd`30030000 00007ffd`30135000 clrjit (deferred)
00007ffd`303f0000 00007ffd`303fe000 authanon (deferred)
00007ffd`30400000 00007ffd`3040d000 static (deferred)
00007ffd`30430000 00007ffd`3043a000 redirect (deferred)
00007ffd`30440000 00007ffd`3048f000 iiscore (deferred)
00007ffd`30490000 00007ffd`30566000 MSVCR120_CLR0400 (deferred)
00007ffd`30580000 00007ffd`30edc000 clr (pdb symbols) C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\sym\clr.pdb\9194AEC366064E5E9D09042A2DE1F46D2\clr.pdb
00007ffd`30ee0000 00007ffd`30eea000 version (deferred)
00007ffd`30ef0000 00007ffd`30f88000 mscoreei (deferred)
00007ffd`30fa0000 00007ffd`30faa000 protsup (deferred)
00007ffd`30fb0000 00007ffd`30fba000 dirlist (deferred)
00007ffd`30fc0000 00007ffd`30fcc000 httpapi (deferred)
00007ffd`30fd0000 00007ffd`30fd9000 defdoc (deferred)
00007ffd`30fe0000 00007ffd`31078000 webengine4 (deferred)
00007ffd`31130000 00007ffd`31194000 mscoree (deferred)
00007ffd`311b0000 00007ffd`311c1000 compstat (deferred)
00007ffd`312e0000 00007ffd`312f0000 cachhttp (deferred)
00007ffd`313e0000 00007ffd`313e8000 cachtokn (deferred)
00007ffd`31410000 00007ffd`31427000 samcli (deferred)
00007ffd`315d0000 00007ffd`3160b000 mlang (deferred)
00007ffd`317e0000 00007ffd`31819000 iisres (deferred)
00007ffd`31b80000 00007ffd`31bfb000 nativerd (deferred)
00007ffd`31c00000 00007ffd`31c4a000 iisutil (deferred)
00007ffd`31cd0000 00007ffd`31ce4000 NapiNSP (deferred)
00007ffd`31cf0000 00007ffd`31cfb000 secur32 (deferred)
00007ffd`32230000 00007ffd`3223a000 cachfile (deferred)
00007ffd`32240000 00007ffd`3225e000 w3dt (deferred)
00007ffd`32260000 00007ffd`32276000 w3wphost (deferred)
00007ffd`324d0000 00007ffd`324dc000 winrnr (deferred)
00007ffd`32810000 00007ffd`32819000 rasadhlp (deferred)
00007ffd`328f0000 00007ffd`328fb000 ktmw32 (deferred)
00007ffd`32b50000 00007ffd`32b69000 dhcpcsvc (deferred)
00007ffd`32b80000 00007ffd`32b94000 dhcpcsvc6 (deferred)
00007ffd`32c90000 00007ffd`32c98000 cachuri (deferred)
00007ffd`32ca0000 00007ffd`32cab000 w3tp (deferred)
00007ffd`32fb0000 00007ffd`33018000 FWPUCLNT (deferred)
00007ffd`33160000 00007ffd`3316a000 winnsi (deferred)
00007ffd`33170000 00007ffd`331a8000 xmllite (deferred)
00007ffd`33200000 00007ffd`33229000 IPHLPAPI (deferred)
00007ffd`34120000 00007ffd`34150000 ntmarta (deferred)
00007ffd`34500000 00007ffd`34515000 netapi32 (deferred)
00007ffd`34520000 00007ffd`346a8000 dbghelp (deferred)
00007ffd`34730000 00007ffd`34749000 nlaapi (deferred)
00007ffd`35780000 00007ffd`3581f000 SHCore (deferred)
00007ffd`35b70000 00007ffd`35b7c000 netutils (deferred)
00007ffd`35b80000 00007ffd`35b96000 wkscli (deferred)
00007ffd`35ba0000 00007ffd`35ba9000 dpapi (deferred)
00007ffd`35bb0000 00007ffd`35bba000 kernel_appcore (deferred)
00007ffd`360f0000 00007ffd`360fe000 pcwum (deferred)
00007ffd`36370000 00007ffd`363a5000 rsaenh (deferred)
00007ffd`36460000 00007ffd`3647e000 userenv (deferred)
00007ffd`364c0000 00007ffd`36563000 dnsapi (deferred)
00007ffd`366b0000 00007ffd`36708000 mswsock (deferred)
00007ffd`36710000 00007ffd`3672e000 cryptsp (deferred)
00007ffd`36940000 00007ffd`36966000 bcrypt (deferred)
00007ffd`36a60000 00007ffd`36a85000 srvcli (deferred)
00007ffd`36c30000 00007ffd`36c5b000 sspicli (deferred)
00007ffd`36c60000 00007ffd`36cc0000 bcryptPrimitives (deferred)
00007ffd`36cc0000 00007ffd`36cca000 CRYPTBASE (deferred)
00007ffd`36cd0000 00007ffd`36d67000 sxs (deferred)
00007ffd`36e50000 00007ffd`36e64000 profapi (deferred)
00007ffd`36f00000 00007ffd`36f12000 msasn1 (deferred)
00007ffd`36fd0000 00007ffd`371a7000 crypt32 (deferred)
00007ffd`37200000 00007ffd`3730f000 KERNELBASE (pdb symbols) C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\sym\kernelbase.pdb\5C8EFE66B1EA441D80F6E43552F28A7F2\kernelbase.pdb
00007ffd`37360000 00007ffd`373b7000 sechost (deferred)
00007ffd`373e0000 00007ffd`373e7000 psapi (deferred)
00007ffd`373f0000 00007ffd`375c6000 combase (deferred)
00007ffd`37610000 00007ffd`37661000 shlwapi (deferred)
00007ffd`37670000 00007ffd`377aa000 kernel32 (deferred)
00007ffd`37810000 00007ffd`37946000 rpcrt4 (deferred)
00007ffd`37950000 00007ffd`37a07000 oleaut32 (deferred)
00007ffd`37bf0000 00007ffd`37c48000 ws2_32 (deferred)
00007ffd`37f30000 00007ffd`37fd5000 advapi32 (deferred)
00007ffd`37fe0000 00007ffd`38084000 clbcatq (deferred)
00007ffd`38090000 00007ffd`38099000 nsi (deferred)
00007ffd`380a0000 00007ffd`38147000 msvcrt (deferred)
00007ffd`381c0000 00007ffd`38338000 ole32 (deferred)
00007ffd`38340000 00007ffd`39757000 shell32 (deferred)
00007ffd`39800000 00007ffd`39971000 user32 (deferred)
00007ffd`39990000 00007ffd`39ad5000 gdi32 (deferred)
00007ffd`39ae0000 00007ffd`39c8a000 ntdll (pdb symbols) C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\sym\ntdll.pdb\B17873D46FFF421587F6E743D1A4B1851\ntdll.pdb
System_EnterpriseServices_Wrapper, System_Transactions 정도만 목록에 있을 뿐 System.dll 조차도 보이질 않는데요. 이 때문에 savemodule 명령어를 위한 주소를 구할 수가 없습니다.
재미있는 것은, !name2ee의 동작도 이상하다는 점입니다. 가령, System.Web.Mvc.dll에 분명히 있는 System.Web.Mvc.MvcHandler.EndProcessRequest 메서드의 이름 풀이를 위해 DLL을 명시하면 결과를 내지 못하는데,
0:018> !name2ee System.Web.Mvc!System.Web.Mvc.MvcHandler.EndProcessRequest
0:018>
그냥 '*'로 모든 모듈을 검색해서 찾으라는 명령어로 바꾸면 다음과 같이 결과를 냅니다.
0:018> !name2ee *!System.Web.Mvc.MvcHandler.EndProcessRequest
Module: 00007ffcd1990700
Assembly: SMDiagnostics.dll
--------------------------------------
Module: 00007ffcd1e8ddc8
Assembly: Microsoft.Web.Administration.dll
--------------------------------------
Module: 00007ffcd14d52d8
Assembly: System.Runtime.Caching.dll
...[생략]...
--------------------------------------
Module: 00007ffcd1409970
Assembly: System.Web.Mvc.dll
Token: 00000000060007e2
MethodDesc: 00007ffcd1863800
Name: System.Web.Mvc.MvcHandler.EndProcessRequest(System.IAsyncResult)
JITTED Code Address: 00007ffcd170a270
...[생략]...
다행인 것은, savemodule 명령어가 .NET Module 주소 값도 받아들인다는 점입니다. 즉, 위의 !name2ee 결과로 나오는 Module 주소인 00007ffcd1409970 값을 사용해도 되는 것입니다.
이 2개의 결과가 동일한지 테스트를 위해 임시로 Windows Forms 응용 프로그램을 하나 만들었습니다. sos 확장을 로드한 후 기본 생성된 Form1 클래스를 다음과 같이 확인할 수 있습니다.
0:000> !name2ee WindowsFormsApplication1!WindowsFormsApplication1.Form1
Module: 00007fff560640c0
Assembly: WindowsFormsApplication1.exe
Token: 0000000002000002
MethodTable: 00007fff56068a00
EEClass: 00007fff561b11c0
Name: WindowsFormsApplication1.Form1
lm 명령으로 구한 start 주소는 다음과 같은데,
0:000> lm
start end module name
0000017e`06140000 0000017e`06148000 WindowsFormsApplication1 (deferred)
00007fff`85ac0000 00007fff`86352000 System_Xml_ni (deferred)
00007fff`878c0000 00007fff`887a3000 System_Windows_Forms_ni C (pdb symbols) C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\sym\System.Windows.Forms.pdb\EB5CB7959D814F46A194DAF569C175FF1\System.Windows.Forms.pdb
00007fff`8a9a0000 00007fff`8b325000 System_Core_ni (deferred)
00007fff`8b330000 00007fff`8bf00000 System_ni (deferred)
...[생략]...
이 2개의 주소 값을 기반으로 savemodule을 수행한 후,
0:000> !savemodule 00007fff560640c0 c:\temp\test1.dll
3 sections in file
section 0 - VA=2000, VASize=109c, FileAddr=200, FileSize=1200
section 1 - VA=4000, VASize=62c, FileAddr=1400, FileSize=800
section 2 - VA=6000, VASize=c, FileAddr=1c00, FileSize=200
0:000> !savemodule 0000017e`06140000 c:\temp\test2.dll
3 sections in file
section 0 - VA=2000, VASize=109c, FileAddr=200, FileSize=1200
section 1 - VA=4000, VASize=62c, FileAddr=1400, FileSize=800
section 2 - VA=6000, VASize=c, FileAddr=1c00, FileSize=200
test1.dll과 test2.dll을 .NET Reflector로 확인해 보니 동일한 코드가 나왔습니다. 끝~~~!!!
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]