성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
[정성태] 저렇게 조각 코드 말고, 실제로 재현이 되는 예제 프로젝트를 압...
[정성태] Modules 창(Ctrl+Shift+U)을 띄워서, 해당 Op...
[정성태] 만드실 수 있습니다. 단지, Unity 엔진 내의 스크립트와 W...
글쓰기
제목
이름
암호
전자우편
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'>windbg - C# PInvoke 호출 시 마샬링을 담당하는 함수 분석</h1> <p> PInvoke는 공짜가 아닙니다. 관리 코드에서 비-관리 코드로 넘어가는 것이기 때문에 당연히 함수에 전달하는 인자 값들이 마샬링될 수밖에 없기 때문입니다. 이 부분을 windbg로 한번 분석해 볼까요? ^^<br /> <br /> 이를 위해 우선 지난 글에 소개한,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# 개발자를 위한 Win32 DLL export 함수의 호출 규약 (1) - x86 환경에서의 __cdecl, __stdcall에 대한 Name mangling ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/11132'>http://www.sysnet.pe.kr/2/0/11132</a> </pre> <br /> 예제 코드를 재사용해서 다음과 같은 함수를 C++ 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;' > #include "stdafx.h" #include <stdio.h> #include "Win32Project1.h" MyStruct _g_struct; int _g_int; __declspec(dllexport) int TestCall(int* ptr1, int** ptr2, char* pText, MyStruct* pStruct, MyStruct** ppStruct, unsigned int mark) { _g_struct.n1 = 0x33333333; _g_struct.n2 = 0x44444444; _g_int = 0x59887766; { printf("ptr1 == 0x%I64x\n", ptr1); printf("*ptr1 == 0x%I64x\n", *ptr1); printf("*ptr2 == 0x%I64x\n", *ptr2); printf("**ptr2 == 0x%I64x\n", **ptr2); printf("\n"); printf("pText == 0x%I64x\n", pText); printf("pText == %s\n", pText); printf("\n"); printf("pStruct == 0x%I64x\n", pStruct); printf("pStruct->n1 == %I64x\n", pStruct->n1); printf("pStruct->n2 == %I64x\n", pStruct->n2); printf("\n"); printf("ppStruct == 0x%I64x\n", ppStruct); printf("(*ppStruct)->n1 == %I64x\n", (*ppStruct)->n1); printf("(*ppStruct)->n2 == %I64x\n", (*ppStruct)->n2); } { *ptr1 = 0x10002000; *ptr2 = &_g_int; pStruct->n1 = 0x70555555; pStruct->n2 = 0x40999999; (*ppStruct) = (&_g_struct); } return 0x0b0b0b0b0b; } </pre> <br /> C# 측에서는 이렇게 호출해 봤습니다.<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; using System.Runtime.InteropServices; class Program { public struct MyStruct { public uint n1; public uint n2; } [DllImport("Win32Project1.dll", SetLastError = true)] internal static unsafe extern int TestCall(IntPtr ptr1, ref IntPtr ptr2, string txt, ref MyStruct structPtr, ref MyStruct* pStructPtr, uint mark); unsafe static void Main(string[] args) { // Console.ReadLine(); MyStruct m1 = new MyStruct(); m1.n1 = 0x1fffffff; m1.n2 = 0x1eeeeeee; MyStruct m2 = new MyStruct(); m2.n1 = 0x2fffffff; m2.n2 = 0x2eeeeeee; uint mark = 0x55555555; fixed (int* pInt1 = &Program.int1) fixed (int* pInt2 = &Program.int2) { IntPtr ptr1 = new IntPtr(pInt1); IntPtr ptr2 = new IntPtr(pInt2); Console.WriteLine("pInt1 == " + ptr1.ToInt64().ToString("x")); Console.WriteLine("pInt2 == " + ptr2.ToInt64().ToString("x")); string test = " 1 1 1 1 1 1 1 1 1"; MyStruct* pM2Struct = &m2; IntPtr ptr3 = new IntPtr(pM2Struct); Console.WriteLine("pM2Struct == " + ptr3.ToInt64().ToString("x")); Console.WriteLine("------------------ managed to native --------------------"); TestCall(ptr1, ref ptr2, test, ref m1, ref pM2Struct, mark); Console.WriteLine("------------------ native to managed --------------------"); Console.WriteLine(); Console.WriteLine(); Console.WriteLine("Program.int1 == " + Program.int1.ToString("x")); Console.WriteLine("Program.int2 == " + Program.int2.ToString("x")); Console.WriteLine(); Console.WriteLine("m1.n1 == " + m1.n1.ToString("x")); Console.WriteLine("m1.n2 == " + m1.n2.ToString("x")); Console.WriteLine(); Console.WriteLine("m2.n1 == " + m2.n1.ToString("x")); Console.WriteLine("m2.n2 == " + m2.n2.ToString("x")); Console.WriteLine("pM2Struct->n1 == " + pM2Struct->n1.ToString("x")); Console.WriteLine("pM2Struct->n2 == " + pM2Struct->n2.ToString("x")); } Console.WriteLine("Press any key to exit..."); Console.ReadLine(); } static int int1 = 0x10203040; static int int2 = 0x59887766; } </pre> <br /> C++ TestCall 함수 초기에 BP를 걸어 디버깅을 시작하면 화면에는 C# 측 코드가 실행된 이후이므로 다음과 같이 3개의 포인터 주소가 출력되었을 것입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > pInt1 == 7ff99b8647c4 pInt2 == 7ff99b8647c8 pM2Struct == 10fee88 ------------------ managed to native -------------------- </pre> <br /> 여기서 Program 타입의 static 필드인 int1, int2에 대한 포인터 pInt1(7ff99b8647c4), pInt2(7ff99b8647c8) 값이 pM2Struct(10fee88)의 주솟값과는 확연히 다른 것을 볼 수 있습니다. 왜냐하면 Program.int1/int2는 참조 형식으로 인해 GC Heap에 할당되었지만 pM2Struct의 대상 구조체는 값 형식이어서 스택에 할당되어 있기 때문입니다.<br /> <br /> 어쨌든, 현재 (BP에 멈춘 상태에서 확인한) RSP 스택 포인터 주소가 가리키는 메모리를 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0x00000000010FEC90 0000000000000012 ........ // [rsp+0] 0x00000000010FEC98 0000000000000014 ........ // [rsp+8] // 호출 시점의 rbx 0x00000000010FECA0 00000000010fed40 @....... // [rsp+10] // 호출 시점의 rsi 0x00000000010FECA8 00000000032c2fc8 ./,..... // [rsp+18] // 호출 시점의 rdi 0x00000000010FECB0 00007ff99b8647c4 .G...... // push r14, TestCall 메서드 초기에 [push r14, rsp + 20h]를 했으므로 이 지점 위로는 TestCall 호출 후의 스택 0x00000000010FECB8 00007ff99b970e22 "....... // TestCall 메서드 호출 후 돌아갈 주소 (pinvoke 층의 CLR 함수) <span style='color: blue; font-weight: bold'>// rcx == ptr1, rdx == ptr2, r8 == pText, r9 == pStruct를 제외한 2개의 변숫값이 스택을 통해 전달</span> 0x00000000010FECC0 0000000000000014 ........ // [rsp+0] garbage // 중간에 보정된 rsp 위치 0x00000000010FECC8 0000000055555555 UUUU.... // [rsp+8] garbage 0x00000000010FECD0 00000000010fee90 ........ // [rsp+10] garbage 0x00000000010FECD8 00000000010feb88 ........ // [rsp+18] garbage <span style='color: blue; font-weight: bold'>0x00000000010FECE0 00000000010fee68 h....... // ppStruct parameter [rsp+20] 0x00000000010FECE8 0000000055555555 UUUU.... // mark parameter [rsp+28] 0x00000000010FECF0 3120312031203120 1 1 1 1 // 스택에 복사된 문자열 0x00000000010FECF8 3120312031203120 1 1 1 1</span> 0x00000000010FED00 0000000000003120 1...... 0x00000000010FED08 00007ff99b970d40 @....... 0x00000000010FED10 00007ff99b863020 0...... // pinvoke 층이 호출되는 초기 rsp 위치 (7번의 push와 sub rsp,0D8h) 0x00000000010FED18 0000000000000922 "....... 0x00000000010FED20 0000000000000014 ........ 0x00000000010FED28 00000000032c2fc8 ./,..... 0x00000000010FED30 0000000000000000 ........ 0x00000000010FED38 000000000145edd8 ..E..... 0x00000000010FED40 00000000010fed10 ........ // rbp+0h (+0), 초기 rsp 위치에서 +30h (lea rbp,[rsp+30h]) 0x00000000010FED48 0000f4918fe3c806 ........ // [rbp+8h] 0x00000000010FED50 00007ff9fb66e9f0 ..f..... // [rbp+10h] 0x00000000010FED58 00000000010ff0a8 ........ // [rbp+18h] 0x00000000010FED60 00007ff99b865a38 8Z...... // [rbp+20h] 0x00000000010FED68 00007ff99b865a38 8Z...... // [rbp+28h] 0x00000000010FED70 00000000010fed10 ........ // [rbp+30h] 0x00000000010FED78 00007ff99b970e22 "....... // [rbp+38h] (+7) 0x00000000010FED80 00000000010fed40 @....... 0x00000000010FED88 00007ff99b865a38 8Z...... // [rbp+48h] // 초기 r10 값 백업 0x00000000010FED90 0000f4918fe3c806 ........ 0x00000000010FED98 000000000145edd0 ..E..... // [rbp+58h] (+11) // == 000000000145edd0 0x00000000010FEDA0 0000000000000000 ........ 0x00000000010FEDA8 0000000000000000 ........ 0x00000000010FEDB0 00000000010fee68 h....... 0x00000000010FEDB8 00000000010fee90 ........ 0x00000000010FEDC0 00000000010fecf0 ........ 0x00000000010FEDC8 00000000010fecf0 ........ // [rbp+88h] 0x00000000010FEDD0 00000000010fee70 p....... // [rbp+90h] (+18) 0x00000000010FEDD8 0000000300000000 ........ // [rbp+98h] 0x00000000010FEDE0 00000000010fecc0 ........ // [rbp+a0h] 0x00000000010FEDE8 00000000032c3008 .0,..... 0x00000000010FEDF0 00000000032c2fc8 ./,..... // push rbx, 이 위치에서 0d8을 뺀 010fed18 == rsp (sub rsp, 0d8h) 0x00000000010FEDF8 00000000010fee88 ........ // push rsi 0x00000000010FEE00 00000000010ff260 `....... // push rdi 0x00000000010FEE08 00000000010fef60 `....... // push r12 0x00000000010FEE10 00000000010fefd8 ........ // push r13 0x00000000010FEE18 0000000000000004 ........ // push r14 0x00000000010FEE20 00000000032c2f78 x/,..... // push rdi 0x00000000010FEE28 00007ff99b970a3b ;....... // Program.Main에서 TestCall을 호출 후 되돌아올 반환 주소, // Pinvoke 층 호출 이후 초기 rsp 위치 <span style='color: blue; font-weight: bold'>0x00000000010FEE30 00007ff99b8647c4 .G...... // 변수 ptr1의 주솟값을 그대로 복사해서 전달 0x00000000010FEE38 00000000010fee70 p....... // 변수 ptr2의 경우 "ref"이므로 ptr2 변수의 메모리 위치를 복사해서 전달 0x00000000010FEE40 00000000032c2fc8 ./,..... // string test 변수의 주솟값 (GC Heap 주소) 0x00000000010FEE48 00000000010fee90 ........ // "ref"이므로 m1 변수의 주솟값 0x00000000010FEE50 00000000010fee68 h....... // "ref"이므로 pM2Struct 변수의 주솟값 0x00000000010FEE58 0000000055555555 UUUU.... // uint mark 로컬 변수</span> 0x00000000010FEE60 0000000000000000 ........ 0x00000000010FEE68 00000000010fee88 ........ // m2 변수의 주솟값 (pM2Struct 로컬 변수) 0x00000000010FEE70 00007ff99b8647c8 .G...... // 관리 힙에 위치한 Program.int2 변수를 가리키는 주소 0x00000000010FEE78 00007ff99b8647c8 .G...... // 관리 힙에 위치한 Program.int2 변수를 가리키는 주소 0x00000000010FEE80 00007ff99b8647c4 .G...... // 관리 힙에 위치한 Program.int1 변수를 가리키는 주소 0x00000000010FEE88 2eeeeeee2fffffff .../.... // MyStruct m2의 n1, n2 필드 값 0x00000000010FEE90 1eeeeeee1fffffff ........ // MyStruct m1의 n1, n2 필드 값 0x00000000010FEE98 00000000010fefd8 ........ 0x00000000010FEEA0 00000000010feee0 ........ 0x00000000010FEEA8 00000000010fefa8 ........ 0x00000000010FEEB0 00000000010ff0a8 ........ 0x00000000010FEEB8 00007ff9faea6c53 Sl...... 0x00000000010FEEC0 00000000032c2f30 0/,..... 0x00000000010FEEC8 00007ff99b864148 HA...... </pre> <br /> 우선, C# 측에서 string으로 전달한 값이 C++ 측에서는 char*로 바뀌는데 이 과정에서 PInvoke 함수는 문자열을 스택에 복사합니다. (아래에 나오지만, 그렇게 복사된 문자열의 주솟값은 3번째 인자를 보관하는 r8 레지스터를 통해 전달합니다.)<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0x00000000010FECF0 3120312031203120 1 1 1 1 // 스택에 복사된 문자열 0x00000000010FECF8 3120312031203120 1 1 1 1 0x00000000010FED00 0000000000003120 1...... </pre> <br /> 물론, 글자 수가 별로 안 되므로 이런 식으로 스택에 복사해 전달한 것이고 만약 문자열이 길다면 (복사하지 않고) 문자열을 보관한 GC 힙의 위치를 pinning한 후 그 주솟값을 전달할 것입니다.<br /> <br /> 그외에, 다른 값 형식의 인자들은 마찬가지로 스택에 그대로 복사되거나 아니면 "ref"로 인해 그 주솟값 자체가 다시 복사되어 전달하는 형식입니다. 위와 같이 managed의 인자 값들을 native 측에서 다룰 수 있도록 마샬링해주는 함수는 최초 호출 시 동적으로 생성(이후 재사용)되며 위와 같은 예제인 경우에는 다음과 같은 코드가 생성됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 00007FF99B970C82 57 push rdi 00007FF99B970C83 41 56 push r14 00007FF99B970C85 41 55 push r13 00007FF99B970C87 41 54 push r12 00007FF99B970C89 57 push rdi 00007FF99B970C8A 56 push rsi 00007FF99B970C8B 53 push rbx 00007FF99B970C8C 48 81 EC D8 00 00 00 sub rsp,0D8h 00007FF99B970C93 48 8D 6C 24 30 lea rbp,[rsp+30h] 00007FF99B970C98 4C 89 55 48 mov qword ptr [rbp+48h],r10 // [rbp+48] == 00007ff99b865a38 00007FF99B970C9C 48 8B F1 mov rsi,rcx 00007FF99B970C9F 48 8D 7D 70 lea rdi,[rbp+70h] 00007FF99B970CA3 B9 0A 00 00 00 mov ecx,0Ah 00007FF99B970CA8 33 C0 xor eax,eax 00007FF99B970CAA F3 AB rep stos dword ptr [rdi] 00007FF99B970CAC 48 8B CE mov rcx,rsi 00007FF99B970CAF 48 89 65 00 mov qword ptr [rbp],rsp 00007FF99B970CB3 48 89 A5 A0 00 00 00 mov qword ptr [rbp+0A0h],rsp 00007FF99B970CBA 48 B8 06 C8 E3 8F 91 F4 00 00 mov rax,0F4918FE3C806h 00007FF99B970CC4 48 89 45 50 mov qword ptr [rbp+50h],rax 00007FF99B970CC8 4C 8B F1 mov r14,rcx // 초기 4개 인자를 각각 백업, r14 == rcx == ptr1 00007FF99B970CCB 48 8B DA mov rbx,rdx // rbx == rdx == ptr2 00007FF99B970CCE 49 8B F0 mov rsi,r8 // rsi == r8 == pText 00007FF99B970CD1 49 8B F9 mov rdi,r9 // rdi == r9 == pStruct 00007FF99B970CD4 4C 8B BD 10 01 00 00 mov r15,qword ptr [rbp+110h] 00007FF99B970CDB 48 8D 4D 10 lea rcx,[rbp+10h] 00007FF99B970CDF 49 8B D2 mov rdx,r10 00007FF99B970CE2 E8 59 41 53 5F call JIT_InitPInvokeFrame (07FF9FAEA4E40h) 00007FF99B970CE7 48 89 45 58 mov qword ptr [rbp+58h],rax 00007FF99B970CEB 48 8B CC mov rcx,rsp 00007FF99B970CEE 48 89 4D 30 mov qword ptr [rbp+30h],rcx 00007FF99B970CF2 48 8B CD mov rcx,rbp 00007FF99B970CF5 48 89 4D 40 mov qword ptr [rbp+40h],rcx 00007FF99B970CF9 48 8B 4D 58 mov rcx,qword ptr [rbp+58h] 00007FF99B970CFD 48 8D 45 10 lea rax,[rbp+10h] 00007FF99B970D01 48 89 41 10 mov qword ptr [rcx+10h],rax 00007FF99B970D05 48 8B 4D 48 mov rcx,qword ptr [rbp+48h] 00007FF99B970D09 E8 62 BF 53 5F call StubHelpers::DemandPermission (07FF9FAEACC70h) 00007FF99B970D0E 33 C9 xor ecx,ecx 00007FF99B970D10 89 8D 9C 00 00 00 mov dword ptr [rbp+9Ch],ecx 00007FF99B970D16 48 89 9D 90 00 00 00 mov qword ptr [rbp+90h],rbx 00007FF99B970D1D 33 C9 xor ecx,ecx 00007FF99B970D1F 48 89 8D 80 00 00 00 mov qword ptr [rbp+80h],rcx 00007FF99B970D26 48 85 F6 test rsi,rsi 00007FF99B970D29 74 74 je 00007FF99B970D9F 00007FF99B970D2B 8B 4E 08 mov ecx,dword ptr [rsi+8] 00007FF99B970D2E 8D 59 02 lea ebx,[rcx+2] 00007FF99B970D31 B9 01 00 00 00 mov ecx,1 00007FF99B970D36 BA 22 09 00 00 mov edx,922h 00007FF99B970D3B E8 B0 1E 53 5F call JIT_GetSharedNonGCStaticBase_InlineGetAppDomain (07FF9FAEA2BF0h) 00007FF99B970D40 44 8B C3 mov r8d,ebx 00007FF99B970D43 44 0F AF 05 15 33 EF FF imul r8d,dword ptr [7FF99B864060h] 00007FF99B970D4B 41 81 F8 05 01 00 00 cmp r8d,105h 00007FF99B970D52 7F 4B jg 00007FF99B970D9F 00007FF99B970D54 45 85 C0 test r8d,r8d 00007FF99B970D57 74 38 je 00007FF99B970D91 00007FF99B970D59 41 8B C8 mov ecx,r8d 00007FF99B970D5C 83 C1 0F add ecx,0Fh 00007FF99B970D5F 83 E1 F0 and ecx,0FFFFFFF0h 00007FF99B970D62 48 83 C4 30 add rsp,30h 00007FF99B970D66 48 F7 D9 neg rcx 00007FF99B970D69 48 03 CC add rcx,rsp 00007FF99B970D6C 72 02 jb 00007FF99B970D70 00007FF99B970D6E 33 C9 xor ecx,ecx 00007FF99B970D70 85 24 24 test dword ptr [rsp],esp 00007FF99B970D73 48 8B D4 mov rdx,rsp 00007FF99B970D76 48 81 EA 00 10 00 00 sub rdx,1000h 00007FF99B970D7D 48 8B E2 mov rsp,rdx 00007FF99B970D80 48 3B E1 cmp rsp,rcx 00007FF99B970D83 73 EB jae 00007FF99B970D70 00007FF99B970D85 48 8B E1 mov rsp,rcx 00007FF99B970D88 48 83 EC 30 sub rsp,30h // 이 시점에 rsp 값 중간 변경 00007FF99B970D8C 4C 8D 44 24 30 lea r8,[rsp+30h] 00007FF99B970D91 48 89 A5 A0 00 00 00 mov qword ptr [rbp+0A0h],rsp 00007FF99B970D98 4C 89 85 80 00 00 00 mov qword ptr [rbp+80h],r8 00007FF99B970D9F 4C 8B 85 80 00 00 00 mov r8,qword ptr [rbp+80h] 00007FF99B970DA6 48 8B D6 mov rdx,rsi 00007FF99B970DA9 B9 01 00 00 00 mov ecx,1 00007FF99B970DAE E8 FD 7C 2A 5C call 00007FF9F7C18AB0 00007FF99B970DB3 48 89 85 88 00 00 00 mov qword ptr [rbp+88h],rax 00007FF99B970DBA C7 85 9C 00 00 00 03 00 00 00 mov dword ptr [rbp+9Ch],3 00007FF99B970DC4 48 89 7D 78 mov qword ptr [rbp+78h],rdi 00007FF99B970DC8 4C 89 7D 70 mov qword ptr [rbp+70h],r15 00007FF99B970DCC 8B B5 18 01 00 00 mov esi,dword ptr [rbp+118h] 00007FF99B970DD2 48 63 CE movsxd rcx,esi 00007FF99B970DD5 48 8B 55 48 mov rdx,qword ptr [rbp+48h] 00007FF99B970DD9 48 8B 52 20 mov rdx,qword ptr [rdx+20h] 00007FF99B970DDD 48 8B 02 mov rax,qword ptr [rdx] <span style='color: blue; font-weight: bold'>00007FF99B970DE0 4C 89 7C 24 20 mov qword ptr [rsp+20h],r15 == ppStruct 인자 00007FF99B970DE5 48 89 4C 24 28 mov qword ptr [rsp+28h],rcx == mark 인자 00007FF99B970DEA 49 8B CE mov rcx,r14 // rcx == r14 == 00007ff99b8647c4 == ptr1의 주솟값 00007FF99B970DED 48 8B 95 90 00 00 00 mov rdx,qword ptr [rbp+90h] // rdx == ptr2의 복사된 메모리 위치 00007FF99B970DF4 4C 8B 85 88 00 00 00 mov r8,qword ptr [rbp+88h] // r8 == 00000000010fecf0 === char* pText 복사된 주소 00007FF99B970DFB 4C 8B CF mov r9,rdi // r9 == rdi == pStruct 주소 0x00000000010fee90</span> 00007FF99B970DFE 41 BB 10 00 00 00 mov r11d,10h // r11 == 0x10 00007FF99B970E04 4C 8B 55 48 mov r10,qword ptr [rbp+48h] // [rbp+48h] == 00007ff99b865a38 00007FF99B970E08 4C 89 55 20 mov qword ptr [rbp+20h],r10 // [rbp+20h] == 00007ff99b865a38 00007FF99B970E0C 4C 8D 15 0F 00 00 00 lea r10,[7FF99B970E22h] 00007FF99B970E13 4C 89 55 38 mov qword ptr [rbp+38h],r10 // [rbp+38h] == 7FF99B970E22h 00007FF99B970E17 4C 8B 55 58 mov r10,qword ptr [rbp+58h] // r10 == 000000000145edd0 00007FF99B970E1B 41 C6 42 0C 00 mov byte ptr [r10+0Ch],0 // r10 == 0145edd0 + 0c == 0145eddc, (byte)[r10+0ch] == 0 <span style='color: blue; font-weight: bold'>00007FF99B970E20 FF D0 call rax // TestCall 메서드 호출</span> 00007FF99B970E22 48 8B 55 58 mov rdx,qword ptr [rbp+58h] 00007FF99B970E26 C6 42 0C 01 mov byte ptr [rdx+0Ch],1 00007FF99B970E2A 83 3D 2B 52 F4 5F 00 cmp dword ptr [g_TrapReturningThreads (07FF9FB8B605Ch)],0 00007FF99B970E31 74 06 je 00007FF99B970E39 00007FF99B970E33 FF 15 FF 5C F4 5F call qword ptr [hlpDynamicFuncTable+68h (07FF9FB8B6B38h)] 00007FF99B970E39 8B F0 mov esi,eax 00007FF99B970E3B E8 90 5E 53 5F call StubHelpers::SetLastError (07FF9FAEA6CD0h) 00007FF99B970E40 89 75 6C mov dword ptr [rbp+6Ch],esi 00007FF99B970E43 48 8B 4D 00 mov rcx,qword ptr [rbp] 00007FF99B970E47 E8 42 00 00 00 call 00007FF99B970E8E 00007FF99B970E4C 90 nop 00007FF99B970E4D 8B 45 6C mov eax,dword ptr [rbp+6Ch] 00007FF99B970E50 48 8B 55 58 mov rdx,qword ptr [rbp+58h] 00007FF99B970E54 C6 42 0C 01 mov byte ptr [rdx+0Ch],1 00007FF99B970E58 48 8B 55 58 mov rdx,qword ptr [rbp+58h] 00007FF99B970E5C 48 8B 4D 18 mov rcx,qword ptr [rbp+18h] 00007FF99B970E60 48 89 4A 10 mov qword ptr [rdx+10h],rcx 00007FF99B970E64 48 B9 06 C8 E3 8F 91 F4 00 00 mov rcx,0F4918FE3C806h 00007FF99B970E6E 48 39 4D 50 cmp qword ptr [rbp+50h],rcx 00007FF99B970E72 74 05 je 00007FF99B970E79 00007FF99B970E74 E8 F7 49 A4 5F call JIT_FailFast (07FF9FB3B5870h) 00007FF99B970E79 90 nop 00007FF99B970E7A 48 8D A5 A8 00 00 00 lea rsp,[rbp+0A8h] 00007FF99B970E81 5B pop rbx 00007FF99B970E82 5E pop rsi 00007FF99B970E83 5F pop rdi 00007FF99B970E84 41 5C pop r12 00007FF99B970E86 41 5D pop r13 00007FF99B970E88 41 5E pop r14 00007FF99B970E8A 41 5F pop r15 00007FF99B970E8C 5D pop rbp 00007FF99B970E8D C3 ret 00007FF99B970E8E 55 push rbp 00007FF99B970E8F 41 57 push r15 00007FF99B970E91 41 56 push r14 00007FF99B970E93 41 55 push r13 00007FF99B970E95 41 54 push r12 00007FF99B970E97 57 push rdi 00007FF99B970E98 56 push rsi 00007FF99B970E99 53 push rbx 00007FF99B970E9A 48 83 EC 38 sub rsp,38h 00007FF99B970E9E 48 8B 69 30 mov rbp,qword ptr [rcx+30h] 00007FF99B970EA2 48 89 6C 24 30 mov qword ptr [rsp+30h],rbp 00007FF99B970EA7 48 8D 6D 30 lea rbp,[rbp+30h] 00007FF99B970EAB 83 BD 9C 00 00 00 02 cmp dword ptr [rbp+9Ch],2 00007FF99B970EB2 7E 16 jle 00007FF99B970ECA 00007FF99B970EB4 48 83 BD 80 00 00 00 00 cmp qword ptr [rbp+80h],0 00007FF99B970EBC 75 0C jne 00007FF99B970ECA 00007FF99B970EBE 48 8B 8D 88 00 00 00 mov rcx,qword ptr [rbp+88h] 00007FF99B970EC5 E8 CE F3 FF FF call 00007FF99B970298 00007FF99B970ECA 90 nop 00007FF99B970ECB 48 83 C4 38 add rsp,38h 00007FF99B970ECF 5B pop rbx 00007FF99B970ED0 5E pop rsi 00007FF99B970ED1 5F pop rdi 00007FF99B970ED2 41 5C pop r12 00007FF99B970ED4 41 5D pop r13 00007FF99B970ED6 41 5E pop r14 00007FF99B970ED8 41 5F pop r15 00007FF99B970EDA 5D pop rbp 00007FF99B970EDB C3 ret </pre> <br /> 중간의 "call rax"가 바로 실질적인 C++ 함수를 호출하는 역할을 하고, 보는 바와 같이 managed에서 전달한 인자들을 다시 한번 "call rax"를 위한 native 함수로 보내려고 적절한 가공 후 재전달하는 것을 볼 수 있습니다.<br /> <br /> 여기에서 의미가 있는 것은, 사실상 C++ 코드 측에서는 managed를 이해하지 못하므로 pinvoke 함수로 인해 쌓이는 인자(또는 rcx, rdx, r8, r9) 값들은 CLR 상의 어떠한 메타데이터도 담고 있지 않다는 점입니다. 이로 인해 만약 C++ 측에서 C#으로부터 string을 전달받았다 해도 스택이나 레지스터에 있는 값을 통해 어떠한 managed 정보도 얻을 수 없습니다. 일례로, string 인자를 받았다고 해서 windbg라면 sos.dll 확장을 통해 "!dumpobject [...]" 명령을 해도 아무런 데이터도 얻지 못합니다.<br /> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1512&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다.)<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1284
(왼쪽의 숫자를 입력해야 합니다.)