Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 4개 있습니다.)

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 at outlook.com

비밀번호

댓글 작성자
 




... 16  17  18  [19]  20  21  22  23  24  25  26  27  28  29  30  ...
NoWriterDateCnt.TitleFile(s)
13145정성태10/21/20225846오류 유형: 822. db2 - Password validation for user db2inst1 failed with rc = -2146500508
13144정성태10/20/20225696.NET Framework: 2059. ClrMD를 이용해 윈도우 환경의 메모리 덤프로부터 닷넷 모듈을 추출하는 방법파일 다운로드1
13143정성태10/19/20226209오류 유형: 821. windbg/sos - Error code - 0x000021BE
13142정성태10/18/20224893도서: 시작하세요! C# 12 프로그래밍
13141정성태10/17/20226697.NET Framework: 2058. [in,out] 배열을 C#에서 C/C++로 넘기는 방법 - 세 번째 이야기파일 다운로드1
13140정성태10/11/20226046C/C++: 159. C/C++ - 리눅스 환경에서 u16string 문자열을 출력하는 방법 [2]
13139정성태10/9/20225906.NET Framework: 2057. 리눅스 환경의 .NET Core 3/5+ 메모리 덤프로부터 모든 닷넷 모듈을 추출하는 방법파일 다운로드1
13138정성태10/8/20227183.NET Framework: 2056. C# - await 비동기 호출을 기대한 메서드가 동기로 호출되었을 때의 부작용 [1]
13137정성태10/8/20225569.NET Framework: 2055. 리눅스 환경의 .NET Core 3/5+ 메모리 덤프로부터 닷넷 모듈을 추출하는 방법
13136정성태10/7/20226153.NET Framework: 2054. .NET Core/5+ SDK 설치 없이 dotnet-dump 사용하는 방법
13135정성태10/5/20226385.NET Framework: 2053. 리눅스 환경의 .NET Core 3/5+ 메모리 덤프를 분석하는 방법 - 두 번째 이야기
13134정성태10/4/20225112오류 유형: 820. There is a problem with AMD Radeon RX 5600 XT device. For more information, search for 'graphics device driver error code 31'
13133정성태10/4/20225440Windows: 211. Windows - (commit이 아닌) reserved 메모리 사용량 확인 방법 [1]
13132정성태10/3/20225311스크립트: 42. 파이썬 - latexify-py 패키지 소개 - 함수를 mathjax 식으로 표현
13131정성태10/3/20227956.NET Framework: 2052. C# - Windows Forms의 데이터 바인딩 지원(DataBinding, DataSource) [2]파일 다운로드1
13130정성태9/28/20225087.NET Framework: 2051. .NET Core/5+ - 에러 로깅을 위한 Middleware가 동작하지 않는 경우파일 다운로드1
13129정성태9/27/20225376.NET Framework: 2050. .NET Core를 IIS에서 호스팅하는 경우 .NET Framework CLR이 함께 로드되는 환경
13128정성태9/23/20227946C/C++: 158. Visual C++ - IDL 구문 중 "unsigned long"을 인식하지 못하는 #import파일 다운로드1
13127정성태9/22/20226400Windows: 210. WSL에 systemd 도입
13126정성태9/15/20227008.NET Framework: 2049. C# 11 - 정적 메서드에 대한 delegate 처리 시 cache 적용
13125정성태9/14/20227197.NET Framework: 2048. C# 11 - 구조체 필드의 자동 초기화(auto-default structs)
13124정성태9/13/20226928.NET Framework: 2047. Golang, Python, C#에서의 CRC32 사용
13123정성태9/8/20227380.NET Framework: 2046. C# 11 - 멤버(속성/필드)에 지정할 수 있는 required 예약어 추가
13122정성태8/26/20227393.NET Framework: 2045. C# 11 - 메서드 매개 변수에 대한 nameof 지원
13121정성태8/23/20225379C/C++: 157. Golang - 구조체의 slice 필드를 Reflection을 이용해 변경하는 방법
13120정성태8/19/20226815Windows: 209. Windows NT Service에서 UI를 다루는 방법 [3]
... 16  17  18  [19]  20  21  22  23  24  25  26  27  28  29  30  ...