Microsoft MVP성태의 닷넷 이야기
글쓴 사람
홈페이지
첨부 파일

windbg - C# PInvoke 호출 시 마샬링을 담당하는 함수 분석

PInvoke는 공짜가 아닙니다. 관리 코드에서 비-관리 코드로 넘어가는 것이기 때문에 당연히 함수에 전달하는 인자 값들이 마샬링될 수밖에 없기 때문입니다. 이 부분을 windbg로 한번 분석해 볼까요? ^^

이를 위해 우선 지난 글에 소개한,

C# 개발자를 위한 Win32 DLL export 함수의 호출 규약 (1) - x86 환경에서의 __cdecl, __stdcall에 대한 Name mangling
; https://www.sysnet.pe.kr/2/0/11132

예제 코드를 재사용해서 다음과 같은 함수를 C++ DLL에서 export한 후,

#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;
}

C# 측에서는 이렇게 호출해 봤습니다.

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;
}

C++ TestCall 함수 초기에 BP를 걸어 디버깅을 시작하면 화면에는 C# 측 코드가 실행된 이후이므로 다음과 같이 3개의 포인터 주소가 출력되었을 것입니다.

pInt1 == 7ff99b8647c4
pInt2 == 7ff99b8647c8
pM2Struct == 10fee88
------------------ managed to native --------------------

여기서 Program 타입의 static 필드인 int1, int2에 대한 포인터 pInt1(7ff99b8647c4), pInt2(7ff99b8647c8) 값이 pM2Struct(10fee88)의 주솟값과는 확연히 다른 것을 볼 수 있습니다. 왜냐하면 Program.int1/int2는 참조 형식으로 인해 GC Heap에 할당되었지만 pM2Struct의 대상 구조체는 값 형식이어서 스택에 할당되어 있기 때문입니다.

어쨌든, 현재 (BP에 멈춘 상태에서 확인한) RSP 스택 포인터 주소가 가리키는 메모리를 보면,

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 함수)
                                                // rcx == ptr1, rdx == ptr2, r8 == pText, r9 == pStruct를 제외한 2개의 변숫값이 스택을 통해 전달
0x00000000010FECC0  0000000000000014  ........ // [rsp+0] garbage // 중간에 보정된 rsp 위치
0x00000000010FECC8  0000000055555555  UUUU.... // [rsp+8] garbage
0x00000000010FECD0  00000000010fee90  ........ // [rsp+10] garbage
0x00000000010FECD8  00000000010feb88  ........ // [rsp+18] garbage
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
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 위치
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 로컬 변수
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......

우선, C# 측에서 string으로 전달한 값이 C++ 측에서는 char*로 바뀌는데 이 과정에서 PInvoke 함수는 문자열을 스택에 복사합니다. (아래에 나오지만, 그렇게 복사된 문자열의 주솟값은 3번째 인자를 보관하는 r8 레지스터를 통해 전달합니다.)

0x00000000010FECF0  3120312031203120   1 1 1 1  // 스택에 복사된 문자열
0x00000000010FECF8  3120312031203120   1 1 1 1
0x00000000010FED00  0000000000003120   1......

물론, 글자 수가 별로 안 되므로 이런 식으로 스택에 복사해 전달한 것이고 만약 문자열이 길다면 (복사하지 않고) 문자열을 보관한 GC 힙의 위치를 pinning한 후 그 주솟값을 전달할 것입니다.

그외에, 다른 값 형식의 인자들은 마찬가지로 스택에 그대로 복사되거나 아니면 "ref"로 인해 그 주솟값 자체가 다시 복사되어 전달하는 형식입니다. 위와 같이 managed의 인자 값들을 native 측에서 다룰 수 있도록 마샬링해주는 함수는 최초 호출 시 동적으로 생성(이후 재사용)되며 위와 같은 예제인 경우에는 다음과 같은 코드가 생성됩니다.

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]  
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
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

00007FF99B970E20 FF D0                call        rax  // TestCall 메서드 호출

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  

중간의 "call rax"가 바로 실질적인 C++ 함수를 호출하는 역할을 하고, 보는 바와 같이 managed에서 전달한 인자들을 다시 한번 "call rax"를 위한 native 함수로 보내려고 적절한 가공 후 재전달하는 것을 볼 수 있습니다.

여기에서 의미가 있는 것은, 사실상 C++ 코드 측에서는 managed를 이해하지 못하므로 pinvoke 함수로 인해 쌓이는 인자(또는 rcx, rdx, r8, r9) 값들은 CLR 상의 어떠한 메타데이터도 담고 있지 않다는 점입니다. 이로 인해 만약 C++ 측에서 C#으로부터 string을 전달받았다 해도 스택이나 레지스터에 있는 값을 통해 어떠한 managed 정보도 얻을 수 없습니다. 일례로, string 인자를 받았다고 해서 windbg라면 sos.dll 확장을 통해 "!dumpobject [...]" 명령을 해도 아무런 데이터도 얻지 못합니다.

(첨부 파일은 이 글의 예제 코드를 포함합니다.)




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]





[최초 등록일: ]
[최종 수정일: 11/26/2019 ]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer@outlook.com

비밀번호

댓글 쓴 사람
 




1  2  3  4  5  6  7  8  9  10  11  12  13  [14]  15  ...
NoWriterDateCnt.TitleFile(s)
11939정성태6/13/20191041오류 유형: 545. Managed Debugging Assistant 'FatalExecutionEngineError'
11938정성태6/12/20191333Math: 59. C# - 웨이트 벡터 갱신식을 이용한 퍼셉트론 분류파일 다운로드1
11937정성태6/11/20193446개발 환경 구성: 442. .NET Core 3.0 preview 5를 이용해 Windows Forms/WPF 응용 프로그램 개발 [1]
11936정성태6/10/20191239Math: 58. C# - 최소 자승법의 1차, 2차 수렴 그래프 변화 확인파일 다운로드1
11935정성태5/7/20201443.NET Framework: 843. C# - PLplot 출력을 파일이 아닌 Window 화면으로 변경
11934정성태6/7/20191804VC++: 133. typedef struct와 타입 전방 선언으로 인한 C2371 오류파일 다운로드1
11933정성태6/7/20191741VC++: 132. enum 정의를 C++11의 enum class로 바꿀 때 유의할 사항파일 다운로드1
11932정성태6/7/20191396오류 유형: 544. C++ - fatal error C1017: invalid integer constant expression파일 다운로드1
11931정성태6/6/20191441개발 환경 구성: 441. C# - CairoSharp/GtkSharp 사용을 위한 프로젝트 구성 방법
11930정성태6/26/20191814.NET Framework: 842. .NET Reflection을 대체할 System.Reflection.Metadata 소개
11929정성태6/5/20191285.NET Framework: 841. Windows Forms/C# - 클립보드에 RTF 텍스트를 복사 및 확인하는 방법
11928정성태6/5/20191227오류 유형: 543. PowerShell 확장 설치 시 "Catalog file '[...].cat' is not found in the contents of the module" 오류 발생
11927정성태6/5/20191484스크립트: 15. PowerShell ISE의 스크립트를 복사 후 PPT/Word에 붙여 넣으면 한글이 깨지는 문제 [1]
11926정성태6/4/20191977오류 유형: 542. Visual Studio - pointer to incomplete class type is not allowed
11925정성태6/4/20191693VC++: 131. Visual C++ - uuid 확장 속성과 __uuidof 확장 연산자파일 다운로드1
11924정성태5/30/20191845Math: 57. C# - 해석학적 방법을 이용한 최소 자승법 [1]파일 다운로드1
11923정성태5/30/20191595Math: 56. C# - 그래프 그리기로 알아보는 경사 하강법의 최소/최댓값 구하기파일 다운로드1
11922정성태5/30/20191098.NET Framework: 840. ML.NET 데이터 정규화파일 다운로드1
11921정성태5/28/20191824Math: 55. C# - 다항식을 위한 최소 자승법(Least Squares Method)파일 다운로드1
11920정성태5/28/20191230.NET Framework: 839. C# - PLplot 색상 제어
11919정성태5/27/20191753Math: 54. C# - 최소 자승법의 1차 함수에 대한 매개변수를 단순 for 문으로 구하는 방법 [1]파일 다운로드1
11918정성태5/27/20191846Math: 53. C# - 행렬식을 이용한 최소 자승법(LSM: Least Square Method)파일 다운로드1
11917정성태5/24/20191761Math: 52. MathNet을 이용한 간단한 통계 정보 처리 - 분산/표준편차파일 다운로드1
11916정성태5/24/20191243Math: 51. MathNET + OxyPlot을 이용한 간단한 통계 정보 처리 - Histogram파일 다운로드1
11915정성태5/24/20191469Linux: 11. 리눅스의 환경 변수 관련 함수 정리 - putenv, setenv, unsetenv
11914정성태5/24/20192031Linux: 10. 윈도우의 GetTickCount와 리눅스의 clock_gettime파일 다운로드1
1  2  3  4  5  6  7  8  9  10  11  12  13  [14]  15  ...