성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
[정성태] 저렇게 조각 코드 말고, 실제로 재현이 되는 예제 프로젝트를 압...
[정성태] Modules 창(Ctrl+Shift+U)을 띄워서, 해당 Op...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>C# DLL에서 제공하는 Win32 export 함수의 내부 동작 방식(VT Fix up Table)</h1> <p> 지난 글에서 IL 코드 수준의 DLL Export 함수를 구현하는 방법을 살펴봤습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# DLL에서 Win32 C/C++처럼 dllexport 함수를 제공하는 방법 - 네 번째 이야기(IL 코드로 직접 구현) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12120'>https://www.sysnet.pe.kr/2/0/12120</a> </pre> <br /> 그러면서 "Inside IL Assembler" 책에 실린 다이어그램으로 동작 방식을 어렴풋이 알게 되었는데요.<br /> <br /> ["Figure 18-3. Indirect referencing of v-table entries from the EAT" - 출처: <a target='tab' href='https://books.google.co.kr/books?id=Xv_0AwAAQBAJ&pg=PA353'>https://books.google.co.kr/books?id=Xv_0AwAAQBAJ&pg=P9A353</a>]<br /> <img alt='il_export_1.png' src='/SysWebRes/bbs/il_export_1.png' /><br /> <br /> 이렇게 제공된 DLL export C# 함수가 정확히 어떻게 동작할 수 있었는지 파헤쳐 보는 것이 이번 글의 목적입니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 예제를 간단하게 하기 위해, 다음과 같이 3개의 함수를,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; public static class Class1 { public static void Func1() // export할 함수 1 { Console.WriteLine("Func1"); return; } public static int Func2() // export할 함수 2 { return 5; } public static void Func3(int n) // export할 함수 3 { Console.WriteLine(n); } } </pre> <br /> ildasm.exe로 IL 코드로 번역 후 ".export ..."만 끼워 넣어,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > .method public hidebysig static void Func1() cil managed { // Code size 15 (0xf) <span style='color: blue; font-weight: bold'>.export [1] // 1 == ordinal number</span> .maxstack 8 IL_0000: nop ...[생략]... IL_000e: ret } // end of method Class1::Func1 .method public hidebysig static int32 Func2() cil managed { // Code size 7 (0x7) <span style='color: blue; font-weight: bold'>.export [2] // 2 == ordinal number</span> .maxstack 1 .locals init ([0] int32 V_0) IL_0000: nop ...[생략]... IL_0006: ret } // end of method Class1::Func2 .method public hidebysig static void Func3(int32 n) cil managed { // Code size 9 (0x9) <span style='color: blue; font-weight: bold'>.export [3] // 3 == ordinal number</span> .maxstack 8 IL_0000: nop ...[생략]... IL_0008: ret } // end of method Class1::Func3 </pre> <br /> ilasm.exe로 어셈블한 DLL을 대상으로 테스트를 진행하겠습니다. 이렇게 한 경우, vtfixup 테이블은 1개만 구성되고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > .vtfixup [3] int64 fromunmanaged at VT_01 .data VT_01 = int64(0)[3] </pre> <br /> <a target='tab' href='https://www.sysnet.pe.kr/2/0/12126'>지난번</a>에 구현해 둔 <a target='tab' href='https://www.sysnet.pe.kr/2/0/12126'>PEImage 라이브러리</a>를 이용하면 DLL 파일 상의 Fix up 테이블 내용을 확인할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // Install-Package WindowsPE -Version 1.1.5 using System; using WindowsPE; class Program { static void Main(string[] _) { PEImage img = PEImage.ReadFromFile(@"ClassLibrary1.dll"); WriteInfo(img); } public static void WriteInfo(PEImage img) { if (img == null) { Console.WriteLine("img == null"); return; } foreach (var vtf in img.EnumerateVTableFixups()) { Console.WriteLine(vtf + ", " + vtf.Type.ToString()); for (int i = 0; i < vtf.Count; i++) { int itemSize = vtf.GetItemSize(); uint itemPos = (uint)(vtf.rva + (i * itemSize)); long vtableItem = (itemSize == 8) ? img.Read<long>(itemPos) : img.Read<int>(itemPos); Console.WriteLine($"\tVTable[{i}] {vtableItem:x} at 0x{itemPos:x}"); } } } } <span style='color: blue; font-weight: bold'>/* RVA: 0x4000, # of entries: 3, Type: 0x0006, COR_VTABLE_64BIT, COR_VTABLE_FROM_UNMANAGED VTable[0] 6000001 at 0x4000 VTable[1] 6000002 at 0x4008 VTable[2] 6000003 at 0x4010 */</span> </pre> <br /> 일단 여기까지가 "Figure 18-3. Indirect referencing of v-table entries from the EAT" 그림에서의 다이어그램 중 "VT Fix up Table"과 "V-Table"의 값을 확인한 것입니다. 그리고, 파일 상에서의 "EAT" 테이블에 출력된 export 함수의 기계어 주소 코드를 다음의 코드로 확인할 수 있고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > foreach (ExportFunctionInfo efi in img.EnumerateExportFunctions()) { Console.WriteLine($"{efi.Name} at 0x{efi.RvaAddress:x8}"); byte[] code = img.ReadBytes(efi.RvaAddress, 1024); } </pre> <br /> 이 글에서 사용하는 ClassLibrary1.dll 예제라면 다음과 같이 출력됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Func1 at 0x00002622 Func2 at 0x0000262e Func3 at 0x0000263a </pre> <br /> 해당 EAT에 기록된 함수의 주소 0x00002622, 0x0000262e, 0x0000263a의 코드는 어떻게 되어 있을까요? 이를 확인하려면 disassembler가 있어야 하는데 마침 "<a target='tab' href='https://www.sysnet.pe.kr/2/0/10916'>Udis86 Assembler</a>"의 C# 포팅이 NuGet에 있으니 이를 추가하고,<br /> <a name='sharpdisasm'></a> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Install-Package SharpDisasm </pre> <br /> 각각의 함수에 대한 기계어 코드를 덤프할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > foreach (ExportFunctionInfo efi in img.EnumerateExportFunctions()) { Console.WriteLine($"{efi.Name} at 0x{efi.RvaAddress:x8}"); byte[] code = img.ReadBytes(efi.RvaAddress, 1024); var disasm = new SharpDisasm.Disassembler(code, mode, 0, true); foreach (var insn in disasm.Disassemble().Take(2)) { Console.Write("\t" + insn.ToString()); if (insn.Operands.Length == 2) { long value = insn.Operands[1].Value; long rvaValue = value - img.BaseAddress.ToInt64(); ulong vTableValue = img.Read<ulong>((uint)rvaValue); if (ILDecoder.IsMethodDef((long)vTableValue) == true) { Console.Write($" // methodDef Token: {vTableValue:x8}"); } else { Console.Write($" // Code Address: {vTableValue:x8}"); } } Console.WriteLine(); } } </pre> <br /> 출력 결과를 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Func1 at 0x00002622 0000000000000000 48 a1 00 40 00 10 00 00 00 00 mov rax, [0x10004000] // methodDef Token: 06000001 000000000000000a ff e0 jmp rax Func2 at 0x0000262e 0000000000000000 48 a1 08 40 00 10 00 00 00 00 mov rax, [0x10004008] // methodDef Token: 06000002 000000000000000a ff e0 jmp rax Func3 at 0x0000263a 0000000000000000 48 a1 10 40 00 10 00 00 00 00 mov rax, [0x10004010] // methodDef Token: 06000003 000000000000000a ff e0 jmp rax </pre> <br /> 엉뚱하게 methodDef 토큰 값을 "rax"에 가져와 jmp하는 코드로 이뤄져 있습니다. 당연히, DLL 파일 상으로만 저렇게 되어 있을 뿐 실행 중에, 즉 해당 DLL이 메모리에 올라왔을 때는 [0x10004000], [0x10004008], [0x10004008] 주소에 있는 값은 "Marshaling Thunks" 역할을 하는 함수의 주소로 바뀔 것입니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 런타임 시에 "Jump Stubs"의 rax 값들이 실제로 어떻게 변하는지에 대해서는 해당 DLL을 로드한 상태의 PE 이미지를 조사해 보면 됩니다. 이를 위해 DLL의 메서드를 호출한 다음 그것의 BaseAddress로부터 PE Image를 분석하면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [DllImport("ClassLibrary1.dll")] public static extern void Func1(); static void Main(string[] _) { // ClassLibrary1.dll 자체의 PE Image 덤프 { PEImage img = PEImage.ReadFromFile(@"ClassLibrary1.dll"); WriteInfo(img); } // Func1을 호출해 ClassLibrary1.dll이 로딩된 후의 BaseAddress로부터 PE Image 덤프 { Func1(); PEImage img = PEImage.FromLoadedModule("ClassLibrary1.dll"); WriteInfo(img); } } </pre> <br /> 그 변화를 확연히 파악할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // Func1을 호출해 ClassLibrary1.dll이 로딩된 후의 BaseAddress로부터 PE Image 덤프 RVA: 0x4000, # of entries: 3, Type: 0x0006, COR_VTABLE_64BIT, COR_VTABLE_FROM_UNMANAGED VTable[0] <span style='color: blue; font-weight: bold'>7ff9d6cc004c</span> at 0x4000 VTable[1] <span style='color: blue; font-weight: bold'>7ff9d6cc009c</span> at 0x4008 VTable[2] <span style='color: blue; font-weight: bold'>7ff9d6cc00ec</span> at 0x4010 Func1 at 0x00002622 0000000000000000 48 a1 00 40 09 5d 00 00 00 00 mov rax, [0x5d094000] // Code Address: <span style='color: blue; font-weight: bold'>7ff9d6cc004c</span> 000000000000000a ff e0 jmp rax Func2 at 0x0000262e 0000000000000000 48 a1 08 40 09 5d 00 00 00 00 mov rax, [0x5d094008] // Code Address: <span style='color: blue; font-weight: bold'>7ff9d6cc009c</span> 000000000000000a ff e0 jmp rax Func3 at 0x0000263a 0000000000000000 48 a1 10 40 09 5d 00 00 00 00 mov rax, [0x5d094010] // Code Address: <span style='color: blue; font-weight: bold'>7ff9d6cc00ec</span> 000000000000000a ff e0 jmp rax </pre> <br /> <hr style='width: 50%' /><br /> <br /> 재미있는 것은, 저렇게 "Marshaling Thunks" 코드가 생성되고 V-Table의 methodDef 토큰 값이 그 주소로 바뀌는 시점이 export 함수의 호출 시점이 아니라는 것입니다. 물론, 처음 로딩을 위해서는 함수 호출이 있어야 하겠지만 일단 어떤 것이라도 1개의 함수가 불린다면 EAT에 등록된 모든 함수의 "Marshaling Thunks" 코드 생성이 이뤄집니다.<br /> <br /> 그렇다면 그 시점이 어떻게 될까요? 이를 위해 windbg의 힘을 빌려 살펴보겠습니다. 우선, 함수가 실행되는 시점에 Thunks 코드가 생성되는지는 windbg에서 해당 함수의 진입 시점으로 BP를 걸어보면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > bp ClassLibrary1!Func1 </pre> <br /> 하지만, 실제로 해보면 이미 Thunks 코드가 생성된 후임을 알게 됩니다. 그래서 이번에는 아예 ClassLibrary1 DLL이 로드되는 시점으로 BP를 걸고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:000> sxe ld:ClassLibrary1.dll 0:000> g </pre> <br /> 로딩이 되자마자 BreakPoint가 잡히면 BaseAddress를 통해,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > ModLoad: 00007ffa`fb150000 00007ffa`fb1ee000 C:\WINDOWS\System32\msvcrt.dll ModLoad: 00007ffa`fbb20000 00007ffa`fbbb7000 C:\WINDOWS\System32\sechost.dll ...[생략]... ModLoad: 00007ffa`fb260000 00007ffa`fb3b6000 C:\WINDOWS\System32\ole32.dll ModLoad: 00007ffa`e60e0000 00007ffa`e622f000 C:\Windows\Microsoft.NET\Framework64\v4.0.30319\clrjit.dll ModLoad: <span style='color: blue; font-weight: bold'>00000000`6a1a0000</span> 00000000`6a1aa000 C:\ClassLibrary1\bin\Debug\ClassLibrary1.dll </pre> <br /> Func1의 EAT에 등록된 주소를 덤프하고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:000> <span style='color: blue; font-weight: bold'>!u 00000000`6a1a0000+2622</span> Unmanaged code 00000000`6a1a2622 48a100401a6a00000000 mov rax,qword ptr [ClassLibrary1!Func3+0x19c6 (<span style='color: blue; font-weight: bold'>00000000`6a1a4000</span>)] 00000000`6a1a262c ffe0 jmp rax 00000000`6a1a262e 48a108401a6a00000000 mov rax,qword ptr [ClassLibrary1!Func3+0x19ce (<span style='color: blue; font-weight: bold'>00000000`6a1a4008</span>)] 00000000`6a1a2638 ffe0 jmp rax 00000000`6a1a263a 48a110401a6a00000000 mov rax,qword ptr [ClassLibrary1!Func3+0x19d6 (<span style='color: blue; font-weight: bold'>00000000`6a1a4010</span>)] 00000000`6a1a2644 ffe0 jmp rax </pre> <br /> 출력된 00000000`6a1a4000, 00000000`6a1a4008, 00000000`6a1a4010 주소의 값을 methodDef 토큰에서 Thunks 코드의 주소로 쓰는 동작(write)에서 멈출 수 있도록 BP를 걸면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:000> <span style='color: blue; font-weight: bold'>ba w8 00000000`6a1a4000</span> 0:000> <span style='color: blue; font-weight: bold'>ba w8 00000000`6a1a4008</span> 0:000> <span style='color: blue; font-weight: bold'>ba w8 00000000`6a1a4010</span> </pre> <br /> 이후 'g'키를 눌러 프로그램 진행을 계속하면 다음과 같이 mscoreei!PatchVTableEntriesForDLLAttach 함수에서 멈추는 것을 볼 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:000> <span style='color: blue; font-weight: bold'>g</span> <span style='color: blue; font-weight: bold'>Breakpoint 0 hit</span> <span style='color: blue; font-weight: bold'>mscoreei!PatchVTableEntriesForDLLAttach+0x29d:</span> 00007ffa`ed209838 4403dd add r11d,ebp </pre> <br /> 그리고 이때의 callstack을 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:000> <span style='color: blue; font-weight: bold'>k</span> # Child-SP RetAddr : Call Site <span style='color: blue; font-weight: bold'>00 00000000`003bd5a0 00007ffa`ed1fadc3 : mscoreei!PatchVTableEntriesForDLLAttach+0x29d</span> 01 00000000`003bd620 00007ffa`ed8f1b3d : mscoreei!CorDllMain+0x145 02 00000000`003bd6a0 00007ffa`ed8f1b97 : MSCOREE!ShellShim__CorDllMain+0xe9 <span style='color: blue; font-weight: bold'>03 00000000`003bd6e0 00007ffa`fbca50a1 : MSCOREE!CorDllMain_Exported+0x37</span> 04 00000000`003bd710 00007ffa`fbce9405 : ntdll!LdrpCallInitRoutine+0x65 05 00000000`003bd780 00007ffa`fbce91f8 : ntdll!LdrpInitializeNode+0x1b1 06 00000000`003bd8c0 00007ffa`fbcaaa97 : ntdll!LdrpInitializeGraphRecurse+0x80 07 00000000`003bd900 00007ffa`fbca2591 : ntdll!LdrpPrepareModuleForExecution+0xbf 08 00000000`003bd940 00007ffa`fbca22a8 : ntdll!LdrpLoadDllInternal+0x199 09 00000000`003bd9c0 00007ffa`fbca1764 : ntdll!LdrpLoadDll+0xa8 0a 00000000`003bdb70 00007ffa`f8f556d0 : ntdll!LdrLoadDll+0xe4 <span style='color: blue; font-weight: bold'>0b 00000000`003bdc60 00007ffa`eb26a417 : KERNELBASE!LoadLibraryExW+0x170</span> 0c 00000000`003bdcd0 00007ffa`eb26a4a4 : clr!CLRLoadLibraryExWorker+0x54 <span style='color: blue; font-weight: bold'>0d 00000000`003bdd20 00007ffa`eb26a705 : clr!CLRLoadLibraryEx+0x51</span> 0e 00000000`003bdd70 00007ffa`eb1931d8 : clr!LocalLoadLibraryHelper+0x31 0f 00000000`003bdda0 00007ffa`eb24bd68 : clr!NDirect::LoadLibraryModule+0x402 10 00000000`003be600 00007ffa`eb24bcd5 : clr!NDirect::NDirectLink+0x80 11 00000000`003be930 00007ffa`eb24b8e1 : clr!NDirect::GetStubForILStub+0x4e 12 00000000`003be980 00007ffa`eb24bc05 : clr!GetStubForInteropMethod+0x65 13 00000000`003be9c0 00007ffa`eb10118c : clr!MethodDesc::DoPrestub+0xef2 14 00000000`003bebe0 00007ffa`eb0f4855 : clr!PreStubWorker+0x3cc 15 00000000`003bef20 00007ffa`8bba08b2 : clr!ThePreStub+0x55 16 00000000`003befd0 00007ffa`eb0f6bd3 : 0x00007ffa`8bba08b2 17 00000000`003bf010 00007ffa`eb0f6aa0 : clr!CallDescrWorkerInternal+0x83 18 00000000`003bf050 00007ffa`eb0f7130 : clr!CallDescrWorkerWithHandler+0x4e 19 00000000`003bf090 00007ffa`eb1cf622 : clr!MethodDescCallSite::CallTargetWorker+0x102 1a 00000000`003bf190 00007ffa`eb1cffe7 : clr!RunMain+0x25f 1b 00000000`003bf370 00007ffa`eb1cfe9a : clr!Assembly::ExecuteMainMethod+0xb7 1c 00000000`003bf660 00007ffa`eb1cf7e3 : clr!SystemDomain::ExecuteMainMethod+0x643 1d 00000000`003bfc60 00007ffa`eb1cf761 : clr!ExecuteEXE+0x3f 1e 00000000`003bfcd0 00007ffa`eb1d0ca4 : clr!_CorExeMainInternal+0xb2 1f 00000000`003bfd60 00007ffa`ed1d8c01 : clr!CorExeMain+0x14 20 00000000`003bfda0 00007ffa`ed8fa56c : mscoreei!CorExeMain+0x112 21 00000000`003bfe00 00007ffa`fb0a7bd4 : MSCOREE!CorExeMain_Exported+0x6c 22 00000000`003bfe30 00007ffa`fbceced1 : KERNEL32!BaseThreadInitThunk+0x14 23 00000000`003bfe60 00000000`00000000 : ntdll!RtlUserThreadStart+0x21 </pre> <br /> 사실상 DLL 로딩 시점에 Thunks 코드 구성이 완료되는 것을 확인할 수 있습니다. 이 단계에서 "Figure 18-3. Indirect referencing of v-table entries from the EAT" 그림에서의 다이어그램 중 EAT, Jump Stubs, V-Table, VT Fix up Table, Marshaling Thunks까지 완료된 것입니다. 하지만 여전히 Func1, Func2, Func3의 IL 코드는 JIT되지 않은 상태인데 이것은 Thunks 코드가 실행되는 시점에 "<a target='tab' href='http://www.sysnet.pe.kr/2/0/1023'>Windbg 환경에서 확인해 본 .NET 메서드 JIT 컴파일 전과 후</a>"의 방식과 유사하게 JIT 컴파일이 될 것이기 때문입니다.<br /> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1544&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> 예전에, Windows의 DLL Loader를 직접 구현하는 방법에 대해 글을 본 적이 있습니다. 핵심은 <a target='tab' href='https://www.sysnet.pe.kr/2/0/11858'>IAT</a>, <a target='tab' href='https://www.sysnet.pe.kr/2/0/12093'>EAT</a>의 주소와 함께 ".reloc" 섹션 내용을 반영하는 것이었는데요, 위와 같은 .NET DLL의 동작까지 반영하려면 문제가 더욱 어려워지게 되었습니다. ^^<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
3303
(왼쪽의 숫자를 입력해야 합니다.)