Microsoft MVP성태의 닷넷 이야기
.NET Framework: 467. 닷넷에서 EIP/RIP 레지스터 값을 구하는 방법 [링크 복사], [링크+제목 복사],
조회: 24571
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 1개 있습니다.)
(시리즈 글이 2개 있습니다.)
.NET Framework: 467. 닷넷에서 EIP/RIP 레지스터 값을 구하는 방법
; https://www.sysnet.pe.kr/2/0/1762

.NET Framework: 2071. 닷넷에서 ESP/RSP 레지스터 값을 구하는 방법
; https://www.sysnet.pe.kr/2/0/13172




닷넷에서 EIP/RIP 레지스터 값을 구하는 방법

C++ x64에서 RIP 레지스터 값을 구하는 가장 쉬운 방법은 RtlCaptureContext Win32 API를 사용하는 것입니다.

#include "stdafx.h"

int _tmain(int argc, _TCHAR* argv[])
{
    CONTEXT ctx;

    ZeroMemory(&ctx, sizeof(CONTEXT));
    ctx.ContextFlags = CONTEXT_FULL;

    RtlCaptureContext(&ctx); // ctx.Rip 필드에 다음 코드인 'return 0'의 "xor eax, eax" 명령어를 가리키는 주소를 담고 있음.

    return 0;
}

아쉽게도 이 방법은 닷넷에서 써 먹을 수 없습니다.

[DllImport("Kernel32.dll", SetLastError = true)]
extern static void RtlCaptureContext(ref byte[] contextRecord);

static void Function()
{
    byte [] contents = new byte [1232]; // 1232 == sizeof(CONTEXT)

    byte [] length = BitConverter.GetBytes((int)1048587); // CONTEXT_FULL == 1048587;
    Array.Copy(length, 0, contents, 8 * 6, 4);            // ContextFlags의 옵셋 위치 값: 48

    RtlCaptureContext(ref contents);
}

위와 같이 실행하면 RtlCaptureContext 명령어 실행에서 프로그램이 비정상 종료합니다. 심지어 해당 닷넷 프로젝트의 "Debug" / "Enable native code debugging"을 켜고 작업하게 되면 exe 프로세스가 살아 있는 희한한 현상마저 겪게 됩니다.




그래서 다른 방법을 생각해 보았습니다. 혹시 어셈블리의 도움을 받으면 되지 않을까 싶었는데요. 아쉽게도 x86/x64 모두 eip/rip 레지스터를 직접 접근하는 것은 어셈블리 코드에서 허용되지 않았습니다.

물론, 우회 방법이 있습니다. ret 시에 스택에 보관된 반환 주소를 활용하는 것입니다. 즉, 다음과 같이 만들면 [rsp]에 보관된 리턴 주소를 얻을 수 있으므로 해당 코드가 실행된 곳의 (E/R)IP 주소를 구할 수 있는 것입니다.

.code

    GetIpRegisterValue64 PROC
        mov rax, [rsp]
        ret

    GetIpRegisterValue64 ENDP

END

이를 컴파일하고 생성된 .obj 파일을 C/C++ 프로젝트와 결합해 결과를 확인할 수 있습니다. 32비트의 상황까지 고려해 다음과 같이 만들고 디버그 모드에서 확인해 보면,

#include "stdafx.h"

#if _M_AMD64

extern "C" __int64 GetIpRegisterValue64();

#else

__declspec(naked) DWORD GetIpRegisterValue32()
{
    __asm
    {
        mov eax, [esp]
        ret
    }
}

#endif

#pragma warning(default : 4035)

int _tmain(int argc, _TCHAR* argv[])
{
#if _M_AMD64
    __int64 ipRegister = GetIpRegisterValue64();
    printf("0x%016llX\n", ipRegister);
#else
    int ipRegister = GetIpRegisterValue32();
    printf("0x%X\n", ipRegister);
#endif

    return 0;
}

예상했던 대로 호출된 부근의 IP 레지스터 값을 얻을 수 있습니다.

get_ip_register_1.png

오호... 그런대로 효과가 있습니다. ^^

참고로, 다음과 같이 LABEL을 사용한 방법도 있습니다.

Walking the stack of the current thread 
; http://jpassing.com/2008/03/12/walking-the-stack-of-the-current-thread/

CONTEXT Context;
ZeroMemory( &Context, sizeof( CONTEXT ) );
Context.ContextFlags = CONTEXT_CONTROL;

__asm
{
Label:
  mov [Context.Ebp], ebp;
  mov [Context.Esp], esp;
  mov eax, [Label];
  mov [Context.Eip], eax;
}




반환 주소를 이용한 IP 주소를 얻는 방법을 닷넷에 적용해 보면 어떨까요? inline-assembly를 .NET으로 포팅하는 방법에 대해서는 지난 글에서 이미 설명드렸습니다.

C++의 inline asm 사용을 .NET으로 포팅하는 방법
; https://www.sysnet.pe.kr/2/0/1267

이를 위해 Visual Studio 디스어셈블리 창에서 x86/x64 각각에 대해 기계어 코드를 구하면 다음과 같습니다.

========= x86 =========

00FA3DB0 8B 04 24             mov         eax,dword ptr [esp]  
00FA3DB3 C3                   ret  


========= x64 =========

00007FF7457B13E0 48 8B 04 24          mov         rax,qword ptr [rsp]  
00007FF7457B13E4 C3                   ret  

다음의 코드는 위의 기계어를 그대로 .NET Delegate에 연결해 주고 있습니다.

using System;
using System.Runtime.InteropServices;

namespace ConsoleApplication2
{
    class Program
    {
        private readonly static byte[] x86GetIp =
        {
            0x8b, 0x04, 0x24,  // mov eax, dword ptr [esp]
            0xc3,   // ret
        };

        private readonly static byte[] x64GetIp =
        {
            0x48, 0x8b, 0x04, 0x24, // mov rax, qword ptr [rsp]
            0xc3,   // ret
        };

        [Flags()]
        private enum AllocationType : uint
        {
            COMMIT = 0x1000,
            RESERVE = 0x2000,
            RESET = 0x80000,
            LARGE_PAGES = 0x20000000,
            PHYSICAL = 0x400000,
            TOP_DOWN = 0x100000,
            WRITE_WATCH = 0x200000
        }

        [Flags()]
        public enum MemoryProtection : uint
        {
            EXECUTE = 0x10,
            EXECUTE_READ = 0x20,
            EXECUTE_READWRITE = 0x40,
            EXECUTE_WRITECOPY = 0x80,
            NOACCESS = 0x01,
            READONLY = 0x02,
            READWRITE = 0x04,
            WRITECOPY = 0x08,
            GUARD_Modifierflag = 0x100,
            NOCACHE_Modifierflag = 0x200,
            WRITECOMBINE_Modifierflag = 0x400
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern IntPtr VirtualAlloc(IntPtr lpAddress, UIntPtr dwSize, AllocationType flAllocationType,
            MemoryProtection flProtect);

        [DllImport("kernel32")]
        private static extern bool VirtualFree(IntPtr lpAddress, UInt32 dwSize, UInt32 dwFreeType);

        [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
        private delegate int GetIPRegister32();

        [UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)]
        private delegate long GetIPRegister64(); 

        static void Main(string[] args)
        {
            TestIt();
        }

        private static void TestIt()
        {
            IntPtr _codePointer;

            GetIPRegister32 _x86Call;
            GetIPRegister64 _x64Call;

            byte[] codeBytes = x86GetIp;

            if (IntPtr.Size == 8)
            {
                codeBytes = x64GetIp;
            }

            _codePointer = VirtualAlloc(IntPtr.Zero, new UIntPtr((uint)codeBytes.Length),
                AllocationType.COMMIT | AllocationType.RESERVE,
                MemoryProtection.EXECUTE_READWRITE
            );

            Marshal.Copy(codeBytes, 0, _codePointer, codeBytes.Length);

            if (IntPtr.Size == 4)
            {
                _x86Call = (GetIPRegister32)Marshal.GetDelegateForFunctionPointer(
                      _codePointer, typeof(GetIPRegister32));

                Console.WriteLine(_x86Call());
            }
            else
            {
                _x64Call = (GetIPRegister64)Marshal.GetDelegateForFunctionPointer(
                      _codePointer, typeof(GetIPRegister64));

                long result = _x64Call();
                Console.WriteLine(result);
            }

            if (_codePointer != IntPtr.Zero)
            {
                VirtualFree(_codePointer, 0, 0x8000);
                _codePointer = IntPtr.Zero;
            }
        }
    }
}

처음엔 이렇게 해주면 실행한 곳의 IP 주소를 구할 수 있을 거라 생각했는데... 아쉽게도 delegate가 별도의 클래스라는 점을 간과하고 있었습니다.

실제로 windbg를 붙여서 디버깅하기 쉽게 Main과 TestIt 메서드에 다음과 같이 코드를 추가해 두고,

static void Main(string[] args)
{
    Console.WriteLine("attach debugger");
    Console.ReadLine();

    // ... [생략] ...

    TestIt();
}

private static void TestIt()
{
    // ... [생략] ...

    if (IntPtr.Size == 4)
    {
        // ... [생략] ...
    }
    else
    {
        // ... [생략] ...

        Console.WriteLine("watch this");
        Console.ReadLine();

        long result = _x64Call();

        Console.WriteLine(result);
    }

    // ... [생략] ...
}

실행한 다음 "attach debugger" 메시지가 출력되었을 때 windbg를 실행시켜 붙인 다음 아래와 같은 과정을 통해 실제 _x64Call이 일어날 시점의 메서드 깊이까지 추적해 보았습니다.

0:003> .loadby sos clr

0:003> !clrstack
OS Thread Id: 0x134c (3)
Unable to walk the managed stack. The current thread is likely not a 
managed thread. You can run !threads to get a list of managed threads in
the process
Failed to start stack walk: 80070057

0:003> !threads
ThreadCount:      2
UnstartedThread:  0
BackgroundThread: 1
PendingThread:    0
DeadThread:       0
Hosted Runtime:   no
                                                                                                        Lock  
       ID OSID ThreadOBJ           State GC Mode     GC Alloc Context                  Domain           Count Apt Exception
   0    1 2a8c 0000000000fd37b0    2a020 Preemptive  0000000002E366D0:0000000002E37FD0 0000000000fc78c0 1     MTA 
   2    2 1d3c 0000000000fdfa50    2b220 Preemptive  0000000000000000:0000000000000000 0000000000fc78c0 0     MTA (Finalizer) 

0:001> ~0s
ntdll!NtReadFile+0xa:
00007ffb`1482ad6a c3              ret

0:000> !clrstack
OS Thread Id: 0x2a8c (0)
        Child SP               IP Call Site
0000000000e8eaa0 00007ffb1482ad6a [InlinedCallFrame: 0000000000e8eaa0] Microsoft.Win32.Win32Native.ReadFile(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr)
0000000000e8eaa0 00007ffb00aa6c92 [InlinedCallFrame: 0000000000e8eaa0] Microsoft.Win32.Win32Native.ReadFile(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr)
0000000000e8ea60 00007ffb00aa6c92 DomainNeutralILStubClass.IL_STUB_PInvoke(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr)
0000000000e8eb60 00007ffb013bc983 System.IO.__ConsoleStream.ReadFileNative(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte[], Int32, Int32, Boolean, Boolean, Int32 ByRef)
0000000000e8ebe0 00007ffb013bc7f8 System.IO.__ConsoleStream.Read(Byte[], Int32, Int32)
0000000000e8ec50 00007ffb00a06e23 System.IO.StreamReader.ReadBuffer()
0000000000e8eca0 00007ffb00a509f7 System.IO.StreamReader.ReadLine()
0000000000e8ecf0 00007ffb013c574d System.IO.TextReader+SyncTextReader.ReadLine()
0000000000e8ed50 00007ffaa27001a7 ConsoleApplication2.Program.Main(System.String[]) [c:\...\ConsoleApplication2\Program.cs @ 68]
0000000000e8f060 00007ffb01d80453 [GCFrame: 0000000000e8f060] 

보시는 바와 같이 현재 Console.ReadLine에 실행이 멈춰 있습니다. 이제 TestIt 메서드에 BreakPoint를 걸고 실행을 계속하면 TestIt 메서드 진입점에서 멈춥니다.

0:000> !bpmd ConsoleApplication2 ConsoleApplication2.Program.TestIt
Found 1 methods in module 00007ffaa25e2fc8...
MethodDesc = 00007ffaa25e4110
Adding pending breakpoints..

0:000> g
(2d00.2a8c): CLR notification exception - code e0444143 (first chance)
JITTED ConsoleApplication2!ConsoleApplication2.Program.TestIt()
Setting breakpoint: bp 00007FFAA270020D [ConsoleApplication2.Program.TestIt()]
Breakpoint 0 hit
00007ffa`a270020d 90              nop

다시 'g' 명령을 내려서 계속 실행하면 _x64Call() 호출 바로 전의 Console.ReadLine까지 실행되는데, 거기서 디스어셈블리 창을 보면,

00007ffa`a26f03a5 90              nop
00007ffa`a26f03a6 e875c8b55e      call    mscorlib_ni+0xcfcc20 (00007ffb`0124cc20)
00007ffa`a26f03ab 4889842498000000 mov     qword ptr [rsp+98h],rax ss:00000000`006be9c8=60ea6b0000000000
00007ffa`a26f03b3 90              nop
00007ffa`a26f03b4 488b4c2430      mov     rcx,qword ptr [rsp+30h]
00007ffa`a26f03b9 488b4908        mov     rcx,qword ptr [rcx+8]
00007ffa`a26f03bd 488b442430      mov     rax,qword ptr [rsp+30h]
00007ffa`a26f03c2 ff5018          call    qword ptr [rax+18h]           
00007ffa`a26f03c5 48898424a0000000 mov     qword ptr [rsp+0A0h],rax
00007ffa`a26f03cd 488b8424a0000000 mov     rax,qword ptr [rsp+0A0h]
00007ffa`a26f03d5 4889442440      mov     qword ptr [rsp+40h],rax

내부에서 델리게이트 타입의 내부 함수를 호출하는 단계로 넘어갑니다. 이 안으로 진입해서 디버깅을 계속해 보면, 다음과 같이 실제로 우리가 만든 기계어 코드로의 호출이 발견됩니다.

00007ffa`a26f0630 48894c2408      mov     qword ptr [rsp+8],rcx ss:00000000`006be930=b066580200000000
00007ffa`a26f0635 55              push    rbp
00007ffa`a26f0636 53              push    rbx
00007ffa`a26f0637 56              push    rsi
00007ffa`a26f0638 57              push    rdi
00007ffa`a26f0639 4154            push    r12
00007ffa`a26f063b 4155            push    r13
00007ffa`a26f063d 4156            push    r14
00007ffa`a26f063f 4157            push    r15
00007ffa`a26f0641 4881eca8000000  sub     rsp,0A8h
00007ffa`a26f0648 488d6c2420      lea     rbp,[rsp+20h]
00007ffa`a26f064d 4c895560        mov     qword ptr [rbp+60h],r10
00007ffa`a26f0651 c7450000000000  mov     dword ptr [rbp],0
00007ffa`a26f0658 48c7450800000000 mov     qword ptr [rbp+8],0
00007ffa`a26f0660 48c7451000000000 mov     qword ptr [rbp+10h],0
00007ffa`a26f0668 488b4560        mov     rax,qword ptr [rbp+60h]
00007ffa`a26f066c 48894518        mov     qword ptr [rbp+18h],rax
00007ffa`a26f0670 488b5518        mov     rdx,qword ptr [rbp+18h]
00007ffa`a26f0674 488d4d28        lea     rcx,[rbp+28h]
00007ffa`a26f0678 e81b43645f      call    clr!JIT_InitPInvokeFrame (00007ffb`01d34998)  
00007ffa`a26f067d 48894568        mov     qword ptr [rbp+68h],rax
...
00007ffa`a26f06b9 488b4d70        mov     rcx,qword ptr [rbp+70h]
00007ffa`a26f06bd 488b4518        mov     rax,qword ptr [rbp+18h]
00007ffa`a26f06c1 48894538        mov     qword ptr [rbp+38h],rax
00007ffa`a26f06c5 488d0514000000  lea     rax,[00007ffa`a26f06e0]
00007ffa`a26f06cc 48894550        mov     qword ptr [rbp+50h],rax
00007ffa`a26f06d0 488b4568        mov     rax,qword ptr [rbp+68h]
00007ffa`a26f06d4 c7400c00000000  mov     dword ptr [rax+0Ch],0
00007ffa`a26f06db 4533db          xor     r11d,r11d
00007ffa`a26f06de ffd1            call    rcx                   

00000000`00a3fffe 0000            add     byte ptr [rax],al
00000000`00a40000 488b0424        mov     rax,qword ptr [rsp] ss:00000000`006be838=e0066fa2fa7f0000
00000000`00a40004 c3              ret
00000000`00a40005 0000            add     byte ptr [rax],al

결국, 우리가 얻게 되는 IP 주소는 닷넷 델리게이트 타입의 Invoke 함수가 위치한 코드의 주소일 뿐입니다. (업데이트: 이러한 불일치를 C# 9.0의 함수 포인터를 이용하면 해결할 수 있습니다.)




호출 스택을 풀이해서 부모의 부모까지 가자니 왠지 귀찮습니다. (이런 코드는 자칫 안정성에도 위협이 될 수 있으니 조심해야 합니다.) 그래서 조금 더 생각해 보니, 예전에 쓴 글이 생각났습니다. ^^

상황별 GetFunctionPointer 반환값 정리
; https://www.sysnet.pe.kr/2/0/1027

그렇습니다. 우리는 GetFunctionPointer 메서드를 통해 함수의 진입점 주소를 알 수 있습니다.

static void Main(string[] args)
{
    foreach (MethodInfo mi in typeof(Program).GetMethods(BindingFlags.NonPublic | BindingFlags.Static))
    {
        if (mi.Name == "TestIt")
        {
            RuntimeHelpers.PrepareMethod(mi.MethodHandle);
            Console.WriteLine(mi.MethodHandle.GetFunctionPointer().ToString("x")); // TestIt 메서드의 진입점 주소
        }
    }

    TestIt();
}

위의 코드 실행 결과로 출력된 값이 해당 메서드의 주소인지 windbg를 통해 확인해 볼 수 있습니다. 일단, 출력 결과는 "00007ffaa26e03c0" 값이었습니다.

0:003> .loadby sos clr

0:003> !bpmd ConsoleApplication2 ConsoleApplication2.Program.TestIt
Found 1 methods in module 00007ffaa25c2fc8...
MethodDesc = 00007ffaa25c4110
Adding pending breakpoints...

0:003> g
(31d8.3268): CLR notification exception - code e0444143 (first chance)
JITTED ConsoleApplication2!ConsoleApplication2.Program.TestIt()
Setting breakpoint: bp 00007FFAA26E03FD [ConsoleApplication2.Program.TestIt()]
Breakpoint 0 hit
00007ffa`a26e03fd 90              nop

오호~~~ 그런데 값이 일치하지 않는군요. 화면의 GetFunctionPointer 출력값은 00007ffa`a26e03c0이지만, BP가 걸린 곳은 00007ffa`a26e03fd입니다. 차이값은 61(= 00000000`0000003d) 바이트 정도입니다. name2ee 명령어를 통해 확인해도 역시 GetFunctionPointer 값과 일치합니다.

0:000> !name2ee ConsoleApplication2.exe!ConsoleApplication2.Program.TestIt
Module:      00007ffaa25c2fc8
Assembly:    ConsoleApplication2.exe
Token:       0000000006000004
MethodDesc:  00007ffaa25c4110
Name:        ConsoleApplication2.Program.TestIt()
JITTED Code Address: 00007ffaa26e03c0

혹시나 싶어 00007ffaa26e03c0 주소를 역어셈블하면 다음과 같이 나옵니다.

00007ffa`a26e03c0 4881ecc8000000  sub     rsp,0C8h
00007ffa`a26e03c7 33c0            xor     eax,eax
00007ffa`a26e03c9 4889442438      mov     qword ptr [rsp+38h],rax
00007ffa`a26e03ce 4889442430      mov     qword ptr [rsp+30h],rax
00007ffa`a26e03d3 4889442428      mov     qword ptr [rsp+28h],rax
00007ffa`a26e03d8 33c0            xor     eax,eax
00007ffa`a26e03da 88442448        mov     byte ptr [rsp+48h],al
00007ffa`a26e03de 4889442440      mov     qword ptr [rsp+40h],rax
00007ffa`a26e03e3 4889442420      mov     qword ptr [rsp+20h],rax
00007ffa`a26e03e8 48b838345ca2fa7f0000 mov rax,7FFAA25C3438h
00007ffa`a26e03f2 8b00            mov     eax,dword ptr [rax]
00007ffa`a26e03f4 85c0            test    eax,eax
00007ffa`a26e03f6 7405            je      00007ffa`a26e03fd
00007ffa`a26e03f8 e8f335b15f      call    clr!JIT_DbgIsJustMyCode (00007ffb`021f39f0)

00007ffa`a26e03fd 90              nop
00007ffa`a26e03fe 48b87857431200000000 mov rax,12435778h
00007ffa`a26e0408 488b00          mov     rax,qword ptr [rax]
00007ffa`a26e040b 4889442438      mov     qword ptr [rsp+38h],rax
00007ffa`a26e0410 e8fb5bb35e      call    mscorlib_ni+0xcc6010 (00007ffb`01216010)
00007ffa`a26e0415 89442458        mov     dword ptr [rsp+58h],eax
00007ffa`a26e0419 33c0            xor     eax,eax

아하... 보니까 "JITTED Code Address: 00007ffaa26e03c0" 값은 실제 함수의 진입점을 가리키는 반면 디버거가 BP로 멈춘 지점은 함수의 prolog 부분이 모두 실행된 다음의 위치를 가리킨 것입니다.

자... 그럼 이것으로 GetFunctionPointer 메서드가 반환한 값을 믿을 수 있게 되었습니다.

문제는, 특정 명령어 위치의 IP 주소를 어떻게 구하느냐입니다. 이를 위해 StackFrame 타입을 이용할 수 있습니다. StackFrame에는 해당 타입이 생성된 시기의 (E/R)IP 옵셋값을 담고 있기 때문입니다.

using System;
using System.Diagnostics;

class Program
{
    static void Function()
    {
        StackFrame st = new StackFrame(0, true);
        IntPtr funcAddress = st.GetMethod().MethodHandle.GetFunctionPointer();

        int offset = st.GetNativeOffset();

        Console.WriteLine("func address: " + funcAddress.ToInt64().ToString("x"));
        Console.WriteLine("offset: " + offset.ToString("x"));
    }

    static void Main(string[] args)
    {
        Console.WriteLine("attach debugger");
        Console.ReadLine();

        Function();
    }
}

따라서 GetFunctionPointer 반환값과 GetNativeOffset 값을 합치면 그 명령어가 수행된 IP 주소를 구할 수 있습니다. 실제로 위의 프로그램을 실행시켜서 windbg로 붙여 보면 다음과 같은 디스어셈블 코드를 보게 됩니다.

00007ffa`a26d00f0 4881ecb8000000  sub     rsp,0B8h
00007ffa`a26d00f7 33c0            xor     eax,eax
00007ffa`a26d00f9 4889442440      mov     qword ptr [rsp+40h],rax
00007ffa`a26d00fe 4889442438      mov     qword ptr [rsp+38h],rax
00007ffa`a26d0103 89442430        mov     dword ptr [rsp+30h],eax
00007ffa`a26d0107 4889442428      mov     qword ptr [rsp+28h],rax
00007ffa`a26d010c 48c744242000000000 mov   qword ptr [rsp+20h],0
00007ffa`a26d0115 4889442448      mov     qword ptr [rsp+48h],rax
00007ffa`a26d011a 48b838345ba2fa7f0000 mov rax,7FFAA25B3438h
00007ffa`a26d0124 8b00            mov     eax,dword ptr [rax]
00007ffa`a26d0126 85c0            test    eax,eax
00007ffa`a26d0128 7405            je      00007ffa`a26d012f
00007ffa`a26d012a e8c138b25f      call    clr!JIT_DbgIsJustMyCode (00007ffb`021f39f0)
     * 
00007ffa`a26d012f 90              nop
00007ffa`a26d0130 488d0df184525e  lea     rcx,[mscorlib_ni+0x6a8628 (00007ffb`00bf8628)]
00007ffa`a26d0137 e8c421665f      call    clr!JIT_TrialAllocSFastMP_InlineGetThread (00007ffb`01d32300)
00007ffa`a26d013c 4889442450      mov     qword ptr [rsp+50h],rax
00007ffa`a26d0141 488b442450      mov     rax,qword ptr [rsp+50h]
00007ffa`a26d0146 4889442458      mov     qword ptr [rsp+58h],rax
00007ffa`a26d014b 41b001          mov     r8b,1
00007ffa`a26d014e 33d2            xor     edx,edx
00007ffa`a26d0150 488b4c2458      mov     rcx,qword ptr [rsp+58h]
00007ffa`a26d0155 e8d638c35e      call    mscorlib_ni+0xdb3a30 (00007ffb`01303a30)
00007ffa`a26d015a 4c8b5c2458      mov     r11,qword ptr [rsp+58h]  

이때의 GetFunctionPointer 반환값은 00007ffa`a26d00f0이고, GetNativeOffset은 6a였습니다. 이를 더하면 00007ffa`a26d015a 값이 되고 이는 new StackFrame의 호출 (call mscorlib_ni+0xdb3a30 (00007ffb`01303a30)) 바로 다음 주소를 가리키고 있습니다. 이 정도면... 뭐 그런대로 원하는 목적을 달성했습니다. ^^

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




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

[연관 글]






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

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

비밀번호

댓글 작성자
 



2015-01-12 09시21분
쉘코드 인코더 구현 / GetPC - 현재 실행 코드(eip, pc) 구하는 방법
; http://hyunmini.tistory.com/63
정성태

... 61  62  63  64  65  66  67  68  [69]  70  71  72  73  74  75  ...
NoWriterDateCnt.TitleFile(s)
12211정성태4/27/202019315개발 환경 구성: 486. WSL에서 Makefile로 공개된 리눅스 환경의 C/C++ 소스 코드 빌드
12210정성태4/20/202020794.NET Framework: 903. .NET Framework의 Strong-named 어셈블리 바인딩 (1) - app.config을 이용한 바인딩 리디렉션 [1]파일 다운로드1
12209정성태4/13/202017463오류 유형: 614. 리눅스 환경에서 C/C++ 프로그램이 Segmentation fault 에러가 발생한 경우 (2)
12208정성태4/12/202016059Linux: 29. 리눅스 환경에서 C/C++ 프로그램이 Segmentation fault 에러가 발생한 경우
12207정성태4/2/202015958스크립트: 19. Windows PowerShell의 NonInteractive 모드
12206정성태4/2/202018526오류 유형: 613. 파일 잠금이 바로 안 풀린다면? - The process cannot access the file '...' because it is being used by another process.
12205정성태4/2/202015167스크립트: 18. Powershell에서는 cmd.exe의 명령어를 지원하진 않습니다.
12204정성태4/1/202015199스크립트: 17. Powershell 명령어에 ';' (semi-colon) 문자가 포함된 경우
12203정성태3/18/202018041오류 유형: 612. warning: 'C:\ProgramData/Git/config' has a dubious owner: '...'.
12202정성태3/18/202021275개발 환경 구성: 486. .NET Framework 프로젝트를 위한 GitLab CI/CD Runner 구성
12201정성태3/18/202018507오류 유형: 611. git-credential-manager.exe: Using credentials for username "Personal Access Token". [1]
12200정성태3/18/202018592VS.NET IDE: 145. NuGet + Github 라이브러리 디버깅 관련 옵션 3가지 - "Enable Just My Code" / "Enable Source Link support" / "Suppress JIT optimization on module load (Managed only)"
12199정성태3/17/202016234오류 유형: 610. C# - CodeDomProvider 사용 시 Unhandled Exception: System.IO.DirectoryNotFoundException: Could not find a part of the path '...\f2_6uod0.tmp'.
12198정성태3/17/202019596오류 유형: 609. SQL 서버 접속 시 "Cannot open user default database. Login failed."
12197정성태3/17/202018954VS.NET IDE: 144. .NET Core 콘솔 응용 프로그램을 배포(publish) 시 docker image 자동 생성 - 두 번째 이야기 [1]
12196정성태3/17/202016028오류 유형: 608. The ServicedComponent being invoked is not correctly configured (Use regsvcs to re-register).
12195정성태3/16/202018341.NET Framework: 902. C# - 프로세스의 모든 핸들을 열람 - 세 번째 이야기
12194정성태3/16/202021035오류 유형: 607. PostgreSQL - Npgsql.NpgsqlException: sorry, too many clients already
12193정성태3/16/202018035개발 환경 구성: 485. docker - SAP Adaptive Server Enterprise 컨테이너 실행 [1]
12192정성태3/14/202020044개발 환경 구성: 484. docker - Sybase Anywhere 16 컨테이너 실행
12191정성태3/14/202021117개발 환경 구성: 483. docker - OracleXE 컨테이너 실행 [1]
12190정성태3/14/202015731오류 유형: 606. Docker Desktop 업그레이드 시 "The process cannot access the file 'C:\Program Files\Docker\Docker\resources\dockerd.exe' because it is being used by another process."
12189정성태3/13/202021340개발 환경 구성: 482. Facebook OAuth 처리 시 상태 정보 전달 방법과 "유효한 OAuth 리디렉션 URI" 설정 규칙
12188정성태3/13/202026106Windows: 169. 부팅 시점에 실행되는 chkdsk 결과를 확인하는 방법
12187정성태3/12/202015709오류 유형: 605. NtpClient was unable to set a manual peer to use as a time source because of duplicate error on '...'.
12186정성태3/12/202017468오류 유형: 604. The SysVol Permissions for one or more GPOs on this domain controller and not in sync with the permissions for the GPOs on the Baseline domain controller.
... 61  62  63  64  65  66  67  68  [69]  70  71  72  73  74  75  ...