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

실행 시에 메서드 가로채기 - CLR Injection: Runtime Method Replacer 개선 - 세 번째 이야기(Monitor.Enter 후킹)

.NET Profiler 기술을 활용하면,

CLR Profiler - 별도 정의한 .NET 코드를 호출하도록 IL 코드 변경
; https://www.sysnet.pe.kr/2/0/10959

런타임에 .NET 응용 프로그램의 IL 코드를 변경하는 것이 가능합니다. 그런데, 만능은 아닙니다. ^^ 가령 Monitor.Enter와 같은 메서드는 IL 코드를 변경할 수 없는데, 왜냐하면 애당초 IL 코드가 없기 때문입니다.

[SecuritySafeCritical]
[__DynamicallyInvokable]
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void Enter(object obj);

보는 바와 같이 IL 코드 없이 곧바로 "InternalCall"로 내부의 Native 함수에 연결되어 있습니다. 따라서, 이런 메서드를 가로채기 하려면 trampoline과 같은 별도의 후킹 방식을 사용해야 합니다. (혹은, Monitor.Enter에 대한 모든 IL 호출을 다른 메서드로 변경하거나.)




우선, 해당 메서드의 정보를 DetourFunc 라이브러리의 MethodDesc을 이용해 살펴볼까요? ^^

// Install-Package DetourFunc -Version 1.1.0

{
    MethodDesc md = MethodDesc.ReadFromAddress(action.Method.MethodHandle.Value);
    md.Dump(Console.Out);
}

/* 출력 결과
[MethodDesc 0x7ffc31f3c300 - FCall]
        wTokenRemainder = bb40 (Token = 6003b40)
        chunkIndex = 0
        bFlags2 = 000000B1 (Flags2 == 177)
        wSlotNumber = 4
        wFlags = 20a9 (IsFullSlotNumber == False)
        MethodTablePtr = 7ffc31f55b10
        FunctionPtr = 7ffc34c626b0
*/

위의 결과와 함께 windbg로 분석을 해보면,

0:000> !dumpmd 7ffc31f3c300
Method Name:  System.Threading.Monitor.Enter(System.Object)
Class:        00007ffc320c5528
MethodTable:  00007ffc31f55b10
mdToken:      0000000006003b40
Module:       00007ffc31f31000
IsJitted:     yes
CodeAddr:     00007ffc34c626b0
Transparency: Safe critical

00007ffc34c626b0 주소의 코드들이 마치 패치를 위한 배려라도 한 것처럼,

clr!JIT_MonEnter:
00007ffc`34c626b0 33d2            xor     edx,edx
00007ffc`34c626b2 6666666666660f1f840000000000 nop word ptr [rax+rax]
clr!JIT_MonEnterWorker_InlineGetThread:
00007ffc`34c626c0 56              push    rsi
00007ffc`34c626c1 488bf2          mov     rsi,rdx
00007ffc`34c626c4 4885c9          test    rcx,rcx
00007ffc`34c626c7 0f84a0010000    je      clr!JIT_MonEnterWorker_InlineGetThread_GetThread_PatchLabel+0x1a0 (00007ffc`34c6286d)
clr!JIT_MonEnterWorker_InlineGetThread_GetThread_PatchLabel:
...[생략]...

웬만한 길이의 trampoline 코드를 넘어서는 nop 코드를 마이크로소프트가 끼워두었습니다. (왜 그랬는지 궁금하군요. ^^;)

덕분에 아무런 걱정 없이 trampoline 패치를 할 수 있지만,

using DetourFunc;
using DetourFunc.Clr;
using System;
using System.Threading;

namespace ConsoleApp1
{
    public delegate void MonitorEnterMethodDelegate(object obj);

    class Program
    {
        static MonitorEnterMethodDelegate s_originalMethod;
        static MonitorEnterMethodRefDelegate s_originalMethodRef;

        static void Main(string[] _)
        {
            Person obj = new Person { Name = "TEST" };

            IntPtr ptrBodyTestMethod;
            {
                MonitorEnterMethodDelegate enterMethod = Monitor.Enter;
                MethodDesc md = MethodDesc.ReadFromMethodInfo(enterMethod.Method);
                ptrBodyTestMethod = md.GetNativeFunctionPointer();
            }

            IntPtr ptrBodyReplaceMethod;

            {
                MonitorEnterMethodDelegate action = Replaced_TestMethod;
                MethodDesc md = MethodDesc.ReadFromMethodInfo(action.Method);
                ptrBodyReplaceMethod = md.GetNativeFunctionPointer();
            }

            using (var item = new TrampolinePatch<MonitorEnterMethodDelegate>())
            {
                if (item.JumpPatch(ptrBodyTestMethod, ptrBodyReplaceMethod) == true)
                {
                    s_originalMethod = item.GetOriginalFunc();
                }

                Console.WriteLine("[After trampoline]");
                Monitor.Enter(obj);

                Console.WriteLine("[before Exit]");
                Monitor.Exit(obj);
            }

            Console.ReadLine();            
        }

        public static void Replaced_TestMethod(object obj)
        {
            Console.WriteLine("Replaced_TestMethod called!: " + obj?.ToString());
            Console.WriteLine("[before orgmethod]");

            s_originalMethod?.Invoke(obj);
        }
    }

    public class Person
    {
        public string Name { get; set; }

        public override string ToString()
        {
            return Name;
        }
    }
}

매끄러웠던 진행과 달리, 실행해 보면 예상치 않은 결과에 당황하게 됩니다. ^^;

[After trampoline]
Replaced_TestMethod called!: TEST
[before orgmethod]

[before Exit]

Unhandled Exception: System.Threading.SynchronizationLockException: Object synchronization method was called from an unsynchronized block of code.
   at ConsoleApp1.Program.Main(String[] _) in C:\ConsoleApp1\ConsoleApp1\Program.cs:line 84

그러니까, 분명히 가로채기는 성공했고 s_originalMethod 메서드 호출까지도 예외 없이 성공했는데, Monitor.Exit 호출 시 해당 object 인자가 동기화에 사용된 적이 없다고 하는 것입니다.




정리해 보면, 결국 다음의 3가지 메서드에 전달된 object 인자 중 어떤 하나(또는 셋 모두)는 다른 주소를 가리킨다는 것을 의미합니다.

Monitor.Enter(object obj)
Replaced_TestMethod(object obj)
s_originalMethod?.Invoke(obj)

원인을 밝히기 위해 windbg로 해당 메서드들의 object 인자를 추적하다가 s_originalMethod?.Invoke 호출 내에서 다음과 같이 마샬러가 수행되는 것을 발견했습니다.

[Marshal.GetDelegateForFunctionPointer가 반환한 s_originalMethod 델리게이트]

...[생략]...
00007ffb`d577a5f0 488d55a8        lea     rdx,[rbp-58h]
00007ffb`d577a5f4 488b4d18        mov     rcx,qword ptr [rbp+18h]
00007ffb`d577a5f8 e8c3755d5f      call    clr!StubHelpers::ObjectMarshaler__ConvertToNative (00007ffc`34d51bc0)
00007ffb`d577a5fd c745c401000000  mov     dword ptr [rbp-3Ch],1 ss:00000000`00dce824=00000000
...[생략]...

즉, ObjectMarshaler__ConvertToNative 함수에 의해 마샬링된 값을 Marshal.GetDelegateForFunctionPointer가 입력으로 받았던 함수에 전달하는 것입니다. 실제로 그 원본 함수에 전달하는 시점에 다시 rcx 값을 덤프해 보면,

0:000> dd @rcx L6
00000000`004fe738 0000000d 00000000 007b0018 00000000 00000000 00000000 

어디서 많이 봤던 패턴이 나옵니다.

C# - Marshal.GetNativeVariantForObject 사용 시 메모리 누수(Memory Leak) 발생 및 해결 방법
; https://www.sysnet.pe.kr/2/0/12157#object_marshal

그러니까, s_originalMethod?.Invoke(obj) 호출은 내부적으로 다음과 같은 식의 마샬링 작업을 수행했던 것입니다.

// 가상 코드
void s_originalMethod(object obj)
{
    IntPtr pAlloc = Marshal.AllocHGlobal(24); // x86 == 16, x64 == 24
    Marshal.GetNativeVariantForObject(obj, pAlloc);

    VARIANT vt = new VARIANT(pAlloc);

    call [_trampline_jmp_for_monitor_enter](vt);
}

당연히, 원본 Monitor.Enter는 (object가 아닌) VARIANT 타입의 값을 받게 되고 엉뚱한 객체의 sync block에 대해 잠금 표시를 수행한 것입니다. 그리고, 순서가 바뀌긴 했지만 바로 이 문제를 파악하다가 알게 된 내용들을 정리한 것이 아래의 글들이었습니다.

C# - Marshal.GetNativeVariantForObject 사용 시 메모리 누수(Memory Leak) 발생 및 해결 방법
; https://www.sysnet.pe.kr/2/0/12157

C# - Marshal.GetIUnknownForObject/GetIDispatchForObject 사용 시 메모리 누수(Memory Leak) 발생
; https://www.sysnet.pe.kr/2/0/12158

C#에서 만든 COM 객체를 C/C++로 P/Invoke Interop 시 메모리 누수(Memory Leak) 발생
; https://www.sysnet.pe.kr/2/0/12162

결국, Marshal.GetDelegateForFunctionPointer로 동적 생성한 delegate 함수는 내부적으로 Object 등의 객체를 마샬링하는 과정에 Marshal.GetNativeVariantForObject를 수행하고, 여기에서도 VariantClear를 수행하지 않으므로 메모리 누수 현상이 동일하게 발생합니다. 확인을 위해 원문의 코드를 다음과 같이 변경시키면,

while (true)
{
    Person obj = new Person { Name = "TEST" };
    Monitor.Enter(obj); // 가로채기되었으므로 s_originalMethod 호출 내부의 메모리 누수 발생
}

메모리가 지속적으로 증가하는 것을 볼 수 있습니다.




자, 그럼 어떻게 해야 Monitor.Enter를 (메모리 누수도 없이) 정상적으로 가로채기 할 수 있을까요? 이 문제를 해결하려면 최종적으로 s_originalMethod?.Invoke의 호출에 원본 객체를 자동 마샬링 과정 없이 전달해야 합니다.

그러고 보니 닷넷 객체를 가리키는 변수가 담은 값의 실체가 실은 GC 힙의 주소라는 것을 알고 있으니, 따라서 object가 아닌 IntPtr로 s_originalMethod?.Invoke에 전달한다면 내부적으로 마샬링이 발생하지 않을 수 있습니다. 마침 객체에 대한 GC Heap 상의 주소를 얻는 메서드를 MethodDesc 타입에 추가해 두었으니, 이를 이용해 다음과 같은 식으로 처리할 수 있습니다.

public delegate void MonitorEnterMethodRefDelegate(IntPtr objAddress);

class Program
{
    static MonitorEnterMethodRefDelegate s_originalMethodRef;

    static void Main(string[] _)
    {
        ...[생략]...

        using (var item = new TrampolinePatch<MonitorEnterMethodRefDelegate>())
        {
            if (item.JumpPatch(ptrBodyTestMethod, ptrBodyReplaceMethod) == true)
            {
                s_originalMethodRef = item.GetOriginalFunc();
            }

            Monitor.Enter(obj);
            Monitor.Exit(obj);
        }

        Console.ReadLine();            
    }

    public static void Replaced_TestMethod(object obj)
    {
        IntPtr objAddr = MethodDesc.GetObjectAddress(obj);
        s_originalMethodRef?.Invoke(objAddr);
    }
}

바뀐 코드로 실행해 보면, 정상적으로 Monitor.Exit가 실행이 되며 동기화가 풀리는 것을 확인할 수 있습니다. 정리해 보면, C#에서 Trampoline 기법을 이용해 우회한 메서드가 표면상 IL 코드이긴 해도 결국 내부적으로는 [Managed-To-Native] 층을 거쳐 인자 값들이 마샬링된다는 사실을 잊어서는 안 됩니다.

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




예전의 출력 결과 유형이 IL인 반면, 이번 Monitor.Enter의 경우 "FCall"이라고 나옵니다.

/* 출력 결과
[MethodDesc 0x7ffc31f3c300 - FCall]
        wTokenRemainder = bb40 (Token = 6003b40)
        chunkIndex = 0
        bFlags2 = 000000B1 (Flags2 == 177)
        wSlotNumber = 4
        wFlags = 20a9 (IsFullSlotNumber == False)
        MethodTablePtr = 7ffc31f55b10
        FunctionPtr = 7ffc34c626b0
*/

이참에 ^^ 기존에 만들어 둔 MethodDesc에,

C# 코드로 접근하는 MethodDesc, MethodTable
; https://www.sysnet.pe.kr/2/0/12142

FCall 유형의 메서드에 대한 필드를,

class FCallMethodDesc : public MethodDesc
{
    DWORD   m_dwECallID;
#ifdef HOST_64BIT
    DWORD   m_padding;
#endif
};

추가해 다음과 같은 식의 구조체를 마련해 둘 수 있습니다.

[StructLayout(LayoutKind.Sequential)]
internal struct FCallMethodDescInternalx86
{
    readonly MethodDescInternal _methodDesc;
    readonly uint _dwECallID;

    public FCallMethodDescInternalx86(MethodDescInternal methodDesc, uint dwECallID)
    {
        _methodDesc = methodDesc;
        _dwECallID = dwECallID;
    }
}

[StructLayout(LayoutKind.Sequential)]
internal struct FCallMethodDescInternalx64
{
    readonly MethodDescInternal _methodDesc;
    readonly uint _dwECallID;
    readonly uint _padding;

    public FCallMethodDescInternalx64(MethodDescInternal methodDesc, uint dwECallID, uint padding)
    {
        _methodDesc = methodDesc;
        _dwECallID = dwECallID;
        _padding = padding;
    }
}

실제로 x64 환경에서 Monitor.Enter의 MethodDesc에 대해 덤프를 해 보면,

0:000> dd 7ffc31f3c300 L6
00007ffc`31f3c300  b100bb40 20a95404 004f0001 00000000
00007ffc`31f3c310  34c626b0 00007ffc

dwECallID와 padding을 확인할 수 있습니다.

m_dwECallID == 004f0001
m_padding == 00000000

또한, IL 메서드와 마찬가지로 FCall 메서드의 경우에도 FCallMethodDesc 구조체의 바로 다음 (x64의 경우) 8바이트가 실제 메서드의 주소입니다.

0:000> dq 00007ffc`31f3c310 L1
00007ffc`31f3c310  00007ffc`34c626b0 // 00007ffc`34c626b0 == clr!JIT_MonEnter




마지막으로, trampoline 과정을 실어 보겠습니다. 우선 Monitor.Enter의 trampoline 가로채기를 위해 아래의 원본 함수에서,

clr!JIT_MonEnter:
00007ffc`34c626b0 33d2            xor     edx,edx
00007ffc`34c626b2 6666666666660f1f840000000000 nop word ptr [rax+rax]
clr!JIT_MonEnterWorker_InlineGetThread:
00007ffc`34c626c0 56              push    rsi
00007ffc`34c626c1 488bf2          mov     rsi,rdx
00007ffc`34c626c4 4885c9          test    rcx,rcx
00007ffc`34c626c7 0f84a0010000    je      clr!JIT_MonEnterWorker_InlineGetThread_GetThread_PatchLabel+0x1a0 (00007ffc`34c6286d)
clr!JIT_MonEnterWorker_InlineGetThread_GetThread_PatchLabel:
00007ffc`34c626cd 654c8b1c2598140000 mov   r11,qword ptr gs:[1498h]
00007ffc`34c626d6 448b152b09a100  mov     r10d,dword ptr [clr!g_SpinConstants (00007ffc`35673008)]
00007ffc`34c626dd 418b4308        mov     eax,dword ptr [r11+8]
00007ffc`34c626e1 83e05f          and     eax,5Fh
00007ffc`34c626e4 0f8583010000    jne     clr!JIT_MonEnterWorker_InlineGetThread_GetThread_PatchLabel+0x1a0 (00007ffc`34c6286d)
00007ffc`34c626ea 4c8d41fc        lea     r8,[rcx-4]
00007ffc`34c626ee 418b00          mov     eax,dword ptr [r8]
00007ffc`34c626f1 a9ffff0018      test    eax,1800FFFFh
00007ffc`34c626f6 7528            jne     clr!JIT_MonEnterWorker_InlineGetThread_GetThread_PatchLabel+0x53 (00007ffc`34c62720)
00007ffc`34c626f8 418b532c        mov     edx,dword ptr [r11+2Ch]
00007ffc`34c626fc 81faff030000    cmp     edx,3FFh
00007ffc`34c62702 0f8765010000    ja      clr!JIT_MonEnterWorker_InlineGetThread_GetThread_PatchLabel+0x1a0 (00007ffc`34c6286d)
00007ffc`34c62708 0bd0            or      edx,eax
00007ffc`34c6270a f0410fb110      lock cmpxchg dword ptr [r8],edx
00007ffc`34c6270f 754f            jne     clr!JIT_MonEnterWorker_InlineGetThread_GetThread_PatchLabel+0x93 (00007ffc`34c62760)
00007ffc`34c62711 4183432801      add     dword ptr [r11+28h],1
00007ffc`34c62716 4885f6          test    rsi,rsi
00007ffc`34c62719 7403            je      clr!JIT_MonEnterWorker_InlineGetThread_GetThread_PatchLabel+0x51 (00007ffc`34c6271e)
00007ffc`34c6271b c60601          mov     byte ptr [rsi],1
00007ffc`34c6271e 5e              pop     rsi
00007ffc`34c6271f c3              ret

(다행히 우리가 교체할 메서드 - 이 글에서는 Replaced_TestMethod - 의 위치가 clr!JIT_MonEnter 위치에서 +/- 2GB 이내의 위치여서) 처음 5바이트를 Replaced_TestMethod의 주소로 패치하고,

clr!JIT_MonEnter:
00007ffc`34c626b0 e92b5cb2a0      jmp     00007ffb`d57882e0
00007ffc`34c626b5 6666660f1f840000000000 nop word ptr [rax+rax]
clr!JIT_MonEnterWorker_InlineGetThread:
00007ffc`34c626c0 56              push    rsi
00007ffc`34c626c1 488bf2          mov     rsi,rdx
00007ffc`34c626c4 4885c9          test    rcx,rcx
...[생략]...

패치 위치의 원본 코드를 VirualAlloc으로 할당받은 주소에 (패치 이후의 원본 함수로 JMP하는 코드까지 포함해) 보관해 두는 것으로 trampoline 처리는 완료됩니다.

00000000`00660000 33d2            xor     edx,edx
00000000`00660002 6666666666660f1f840000000000 nop word ptr [rax+rax]
00000000`00660010 48b8c026c634fc7f0000 mov rax,offset clr!JIT_MonEnterWorker_InlineGetThread (00007ffc`34c626c0)
00000000`0066001a ffe0            jmp     rax
00000000`0066001c 0000            add     byte ptr [rax],al

가로채기가 완료된 코드를 실행하면 이후 원본 함수를 실행하기 전 Marshal.GetDelegateForFunctionPointer로 동적 생성한 s_originalMethod 메서드의 body를 지나게 되는데,

00007ffb`d577a572 57              push    rdi
00007ffb`d577a573 4156            push    r14
00007ffb`d577a575 4155            push    r13
00007ffb`d577a577 4154            push    r12
00007ffb`d577a579 57              push    rdi
00007ffb`d577a57a 56              push    rsi
00007ffb`d577a57b 53              push    rbx
00007ffb`d577a57c 4881ecd8000000  sub     rsp,0D8h
00007ffb`d577a583 c5f877          vzeroupper
00007ffb`d577a586 488dac2410010000 lea     rbp,[rsp+110h]
00007ffb`d577a58e 4c899560ffffff  mov     qword ptr [rbp-0A0h],r10
00007ffb`d577a595 4889a510ffffff  mov     qword ptr [rbp-0F0h],rsp
00007ffb`d577a59c 48894d10        mov     qword ptr [rbp+10h],rcx
00007ffb`d577a5a0 48895518        mov     qword ptr [rbp+18h],rdx ss:00000000`00d5e898=0000000000000000
00007ffb`d577a5a4 488d8d28ffffff  lea     rcx,[rbp-0D8h]
00007ffb`d577a5ab 498bd2          mov     rdx,r10
00007ffb`d577a5ae e88da84e5f      call    clr!JIT_InitPInvokeFrame (00007ffc`34c64e40)
00007ffb`d577a5b3 48894580        mov     qword ptr [rbp-80h],rax
00007ffb`d577a5b7 488bcc          mov     rcx,rsp
00007ffb`d577a5ba 48898d48ffffff  mov     qword ptr [rbp-0B8h],rcx
00007ffb`d577a5c1 488bcd          mov     rcx,rbp
00007ffb`d577a5c4 48898d58ffffff  mov     qword ptr [rbp-0A8h],rcx
00007ffb`d577a5cb 488b4d80        mov     rcx,qword ptr [rbp-80h]
00007ffb`d577a5cf 488d8528ffffff  lea     rax,[rbp-0D8h]
00007ffb`d577a5d6 48894110        mov     qword ptr [rcx+10h],rax
00007ffb`d577a5da 33c9            xor     ecx,ecx
00007ffb`d577a5dc 4863c9          movsxd  rcx,ecx
00007ffb`d577a5df e8ac6a625f      call    clr!StubHelpers::DemandPermission (00007ffc`34da1090)
00007ffb`d577a5e4 33d2            xor     edx,edx
00007ffb`d577a5e6 8955c4          mov     dword ptr [rbp-3Ch],edx
00007ffb`d577a5e9 90              nop
00007ffb`d577a5ea 66c745a80000    mov     word ptr [rbp-58h],0
00007ffb`d577a5f0 488d55a8        lea     rdx,[rbp-58h]
00007ffb`d577a5f4 488b4d18        mov     rcx,qword ptr [rbp+18h]
00007ffb`d577a5f8 e8c3755d5f      call    clr!StubHelpers::ObjectMarshaler__ConvertToNative (00007ffc`34d51bc0)
00007ffb`d577a5fd c745c401000000  mov     dword ptr [rbp-3Ch],1
00007ffb`d577a604 90              nop
00007ffb`d577a605 c4e17a6f4da8    vmovdqu xmm1,xmmword ptr [rbp-58h]
00007ffb`d577a60b c4e17a7f4d90    vmovdqu xmmword ptr [rbp-70h],xmm1
00007ffb`d577a611 488b55b8        mov     rdx,qword ptr [rbp-48h]
00007ffb`d577a615 488955a0        mov     qword ptr [rbp-60h],rdx
00007ffb`d577a619 488d9560ffffff  lea     rdx,[rbp-0A0h]
00007ffb`d577a620 488b4d10        mov     rcx,qword ptr [rbp+10h]
00007ffb`d577a624 e847a46d5f      call    clr!StubHelpers::GetDelegateTarget (00007ffc`34e54a70)
00007ffb`d577a629 48894588        mov     qword ptr [rbp-78h],rax
00007ffb`d577a62d c4e17a6f4d90    vmovdqu xmm1,xmmword ptr [rbp-70h]
00007ffb`d577a633 c4e17a7f8d68ffffff vmovdqu xmmword ptr [rbp-98h],xmm1
00007ffb`d577a63c 488b4da0        mov     rcx,qword ptr [rbp-60h]
00007ffb`d577a640 48898d78ffffff  mov     qword ptr [rbp-88h],rcx

00007ffb`d577a647 488d8d68ffffff  lea     rcx,[rbp-98h]
00007ffb`d577a64e 4533db          xor     r11d,r11d
00007ffb`d577a651 488b8560ffffff  mov     rax,qword ptr [rbp-0A0h]
00007ffb`d577a658 48898538ffffff  mov     qword ptr [rbp-0C8h],rax
00007ffb`d577a65f 488d0515000000  lea     rax,[00007ffb`d577a67b]
00007ffb`d577a666 48898550ffffff  mov     qword ptr [rbp-0B0h],rax
00007ffb`d577a66d 488b4580        mov     rax,qword ptr [rbp-80h]
00007ffb`d577a671 c6400c00        mov     byte ptr [rax+0Ch],0
00007ffb`d577a675 488b4588        mov     rax,qword ptr [rbp-78h]
00007ffb`d577a679 ffd0            call    rax

00007ffb`d577a67b 488b5580        mov     rdx,qword ptr [rbp-80h]
00007ffb`d577a67f c6420c01        mov     byte ptr [rdx+0Ch],1
00007ffb`d577a683 833dbe89ef5f00  cmp     dword ptr [clr!g_TrapReturningThreads (00007ffc`35673048)],0
00007ffb`d577a68a 7406            je      00007ffb`d577a692
00007ffb`d577a68c ff157695ef5f    call    qword ptr [clr!hlpDynamicFuncTable+0x68 (00007ffc`35673c08)]
00007ffb`d577a692 e809c64e5f      call    clr!StubHelpers::SetLastError (00007ffc`34c66ca0)
00007ffb`d577a697 90              nop
00007ffb`d577a698 90              nop
..[생략]...

00007ffb`d577a679 주소의 call rax는 00000000`00660000로 실행이 전달돼 원본 Monitor.Enter의 처리를 진행하게 됩니다. 또한 이전에 언급한 ObjectMarshaler__ConvertToNative로 인해 s_originalMethod.Invoke에 전달된 닷넷 객체가 마샬링 되는데,

clr!StubHelpers::ObjectMarshaler__ConvertToNative:
00007ffc`34d51bc0 488bc4          mov     rax,rsp
00007ffc`34d51bc3 48895010        mov     qword ptr [rax+10h],rdx
00007ffc`34d51bc7 4156            push    r14
00007ffc`34d51bc9 4881ec60010000  sub     rsp,160h
00007ffc`34d51bd0 48c7442458feffffff mov   qword ptr [rsp+58h],0FFFFFFFFFFFFFFFEh
00007ffc`34d51bd9 48895808        mov     qword ptr [rax+8],rbx
00007ffc`34d51bdd 48897018        mov     qword ptr [rax+18h],rsi
00007ffc`34d51be1 48897820        mov     qword ptr [rax+20h],rdi
00007ffc`34d51be5 488bda          mov     rbx,rdx
00007ffc`34d51be8 488d3dd1ffffff  lea     rdi,[clr!StubHelpers::ObjectMarshaler__ConvertToNative (00007ffc`34d51bc0)]
00007ffc`34d51bef 48897c2438      mov     qword ptr [rsp+38h],rdi
00007ffc`34d51bf4 48894c2428      mov     qword ptr [rsp+28h],rcx
00007ffc`34d51bf9 83a4249000000000 and     dword ptr [rsp+90h],0
00007ffc`34d51c01 4889bc24a0000000 mov     qword ptr [rsp+0A0h],rdi
00007ffc`34d51c09 488d05e8036e00  lea     rax,[clr!HelperMethodFrame_1OBJ::`vftable' (00007ffc`35431ff8)]
00007ffc`34d51c10 4889442478      mov     qword ptr [rsp+78h],rax
00007ffc`34d51c15 488d442428      lea     rax,[rsp+28h]
00007ffc`34d51c1a 4889842450010000 mov     qword ptr [rsp+150h],rax
00007ffc`34d51c22 488d8c24a8000000 lea     rcx,[rsp+0A8h]
00007ffc`34d51c2a e8c132f1ff      call    clr!LazyMachStateCaptureState (00007ffc`34c64ef0)
00007ffc`34d51c2f 488d4c2478      lea     rcx,[rsp+78h]
00007ffc`34d51c34 e8f732f1ff      call    clr!HelperMethodFrame::Push (00007ffc`34c64f30)
00007ffc`34d51c39 488b8c2498000000 mov     rcx,qword ptr [rsp+98h]
00007ffc`34d51c41 33f6            xor     esi,esi
00007ffc`34d51c43 4532f6          xor     r14b,r14b
00007ffc`34d51c46 8a05ec139200    mov     al,byte ptr [clr!g_StackProbingEnabled (00007ffc`35673038)]
00007ffc`34d51c4c 84c0            test    al,al
00007ffc`34d51c4e 741a            je      clr!StubHelpers::ObjectMarshaler__ConvertToNative+0xaa (00007ffc`34d51c6a)
00007ffc`34d51c50 e877052400      call    clr!DefaultRetailStackProbeWorker (00007ffc`34f921cc)
00007ffc`34d51c55 8a05dd139200    mov     al,byte ptr [clr!g_StackProbingEnabled (00007ffc`35673038)]
00007ffc`34d51c5b 84c0            test    al,al
00007ffc`34d51c5d 740b            je      clr!StubHelpers::ObjectMarshaler__ConvertToNative+0xaa (00007ffc`34d51c6a)
00007ffc`34d51c5f 488d4c2448      lea     rcx,[rsp+48h]
00007ffc`34d51c64 e82b162400      call    clr!SOIntolerantTransitionHandler::CtorImpl (00007ffc`34f93294)
00007ffc`34d51c69 90              nop
00007ffc`34d51c6a b800400000      mov     eax,4000h
00007ffc`34d51c6f 488bd3          mov     rdx,rbx
00007ffc`34d51c72 668503          test    word ptr [rbx],ax
00007ffc`34d51c75 740c            je      clr!StubHelpers::ObjectMarshaler__ConvertToNative+0xc3 (00007ffc`34d51c83)
00007ffc`34d51c77 488d4c2428      lea     rcx,[rsp+28h]
00007ffc`34d51c7c e83ffbf9ff      call    clr!OleVariant::MarshalOleRefVariantForObject (00007ffc`34cf17c0)
00007ffc`34d51c81 eb14            jmp     clr!StubHelpers::ObjectMarshaler__ConvertToNative+0xd7 (00007ffc`34d51c97)
00007ffc`34d51c83 488d442428      lea     rax,[rsp+28h]
00007ffc`34d51c88 4889442430      mov     qword ptr [rsp+30h],rax
00007ffc`34d51c8d 488d4c2430      lea     rcx,[rsp+30h]
00007ffc`34d51c92 e879000000      call    clr!OleVariant::MarshalOleVariantForObject (00007ffc`34d51d10)
00007ffc`34d51c97 c644244800      mov     byte ptr [rsp+48h],0 ss:00000000`0078e548=00
00007ffc`34d51c9c 803d9513920000  cmp     byte ptr [clr!g_StackProbingEnabled (00007ffc`35673038)],0
00007ffc`34d51ca3 740b            je      clr!StubHelpers::ObjectMarshaler__ConvertToNative+0xf0 (00007ffc`34d51cb0)
00007ffc`34d51ca5 488d4c2448      lea     rcx,[rsp+48h]
00007ffc`34d51caa e81d162400      call    clr!SOIntolerantTransitionHandler::DtorImpl (00007ffc`34f932cc)
00007ffc`34d51caf 90              nop
00007ffc`34d51cb0 eb17            jmp     clr!StubHelpers::ObjectMarshaler__ConvertToNative+0x109 (00007ffc`34d51cc9)
00007ffc`34d51cb2 488b9c2478010000 mov     rbx,qword ptr [rsp+178h]
00007ffc`34d51cba 488b7c2438      mov     rdi,qword ptr [rsp+38h]
00007ffc`34d51cbf 488b742440      mov     rsi,qword ptr [rsp+40h]
00007ffc`34d51cc4 448a742420      mov     r14b,byte ptr [rsp+20h]
00007ffc`34d51cc9 4584f6          test    r14b,r14b
00007ffc`34d51ccc 7409            je      clr!StubHelpers::ObjectMarshaler__ConvertToNative+0x117 (00007ffc`34d51cd7)
00007ffc`34d51cce 488bd6          mov     rdx,rsi
00007ffc`34d51cd1 e862c10f00      call    clr!UnwindAndContinueRethrowHelperAfterCatch (00007ffc`34e4de38)
00007ffc`34d51cd6 cc              int     3
00007ffc`34d51cd7 488d4c2478      lea     rcx,[rsp+78h]
00007ffc`34d51cdc e88f32f1ff      call    clr!HelperMethodFrame::Pop (00007ffc`34c64f70)
00007ffc`34d51ce1 488d8c24a8000000 lea     rcx,[rsp+0A8h]
00007ffc`34d51ce9 e8e226f1ff      call    clr!HelperMethodFrameRestoreState (00007ffc`34c643d0)
00007ffc`34d51cee 85c0            test    eax,eax
00007ffc`34d51cf0 0f8503ffffff    jne     clr!StubHelpers::ObjectMarshaler__ConvertToNative+0x39 (00007ffc`34d51bf9)
00007ffc`34d51cf6 4c8d9c2460010000 lea     r11,[rsp+160h]
00007ffc`34d51cfe 498b5b10        mov     rbx,qword ptr [r11+10h]
00007ffc`34d51d02 498b7320        mov     rsi,qword ptr [r11+20h]
00007ffc`34d51d06 498b7b28        mov     rdi,qword ptr [r11+28h]
00007ffc`34d51d0a 498be3          mov     rsp,r11
00007ffc`34d51d0d 415e            pop     r14
00007ffc`34d51d0f c3              ret

아마도 MarshalOleRefVariantForObject 호출이 내부적으로는 Marshal.GetNativeVariantForObject 코드와 유사한 역할을 수행하는 듯합니다.




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

[연관 글]





[최초 등록일: ]
[최종 수정일: 3/2/2020 ]

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)
12038정성태10/17/2019537오류 유형: 570. SQL Server 2019 RC1 - SQL Client Connectivity SDK 설치 오류
12037정성태10/15/2019845.NET Framework: 867. C# - Encoding.Default 값을 바꿀 수 있을까요?파일 다운로드1
12036정성태10/21/20191498.NET Framework: 866. C# - 고성능이 필요한 환경에서 GC가 발생하지 않는 네이티브 힙 사용파일 다운로드1
12035정성태10/13/2019722개발 환경 구성: 461. C# 8.0의 #nulable 관련 특성을 .NET Framework 프로젝트에서 사용하는 방법파일 다운로드1
12034정성태1/31/2020997개발 환경 구성: 460. .NET Core 환경에서 (프로젝트가 아닌) C# 코드 파일을 입력으로 컴파일하는 방법 [1]
12033정성태10/11/20191419개발 환경 구성: 459. .NET Framework 프로젝트에서 C# 8.0 컴파일러를 사용하는 방법
12032정성태11/25/2019889.NET Framework: 865. .NET Core 2.2/3.0 웹 프로젝트를 IIS에서 호스팅(Inproc, out-of-proc)하는 방법 - AspNetCoreModuleV2 소개
12031정성태10/7/2019557오류 유형: 569. Azure Site Extension 업그레이드 시 "System.IO.IOException: There is not enough space on the disk" 예외 발생
12030정성태11/12/20192292.NET Framework: 864. .NET Conf 2019 Korea - "닷넷 17년의 변화 정리 및 닷넷 코어 3.0" 발표 자료 [1]파일 다운로드1
12029정성태9/27/2019664제니퍼 .NET: 29. Jennifersoft provides a trial promotion on its APM solution such as JENNIFER, PHP, and .NET in 2019 and shares the examples of their application.
12028정성태9/26/2019795.NET Framework: 863. C# - Thread.Suspend 호출 시 응용 프로그램 hang 현상을 해결하기 위한 시도파일 다운로드1
12027정성태9/26/2019505오류 유형: 568. Consider app.config remapping of assembly "..." from Version "..." [...] to Version "..." [...] to solve conflict and get rid of warning.
12026정성태9/26/2019688.NET Framework: 862. C# - Active Directory의 LDAP 경로 및 정보 조회
12025정성태9/25/2019799제니퍼 .NET: 28. APM 솔루션 제니퍼, PHP, .NET 무료 사용 프로모션 2019 및 적용 사례 (8)
12024정성태9/20/2019908.NET Framework: 861. HttpClient와 HttpClientHandler의 관계
12023정성태9/19/2019847.NET Framework: 860. ServicePointManager.DefaultConnectionLimit와 HttpClient의 관계파일 다운로드1
12022정성태9/12/20191839개발 환경 구성: 458. C# 8.0 (Preview) 신규 문법을 위한 개발 환경 구성 [3]
12021정성태7/1/20204543도서: 시작하세요! C# 8.0 프로그래밍 [3]
12020정성태9/11/2019879VC++: 134. SYSTEMTIME 값 기준으로 특정 시간이 지났는지를 판단하는 함수
12019정성태9/11/2019695Linux: 23. .NET Core + 리눅스 환경에서 Environment.CurrentDirectory 접근 시 주의 사항
12018정성태9/25/2019595오류 유형: 567. IIS - Unrecognized attribute 'targetFramework'. Note that attribute names are case-sensitive. (D:\lowSite4\web.config line 11)
12017정성태9/11/2019984오류 유형: 566. 비주얼 스튜디오 - Failed to register URL "http://localhost:6879/" for site "..." application "/". Error description: Access is denied. (0x80070005)
12016정성태9/5/20191560오류 유형: 565. git fetch - warning: 'C:\ProgramData/Git/config' has a dubious owner: '(unknown)'.
12015정성태9/3/20192353개발 환경 구성: 457. 윈도우 응용 프로그램의 Socket 연결 시 time-out 시간 제어
12014정성태6/23/20201227개발 환경 구성: 456. 명령행에서 AWS, Azure 등의 원격 저장소에 파일 관리하는 방법 - cyberduck/duck 소개
12013정성태8/28/20191208개발 환경 구성: 455. 윈도우에서 (테스트) 인증서 파일 만드는 방법
1  2  3  4  5  6  7  8  9  [10]  11  12  13  14  15  ...