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

닷넷에서 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
정성태

[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13609정성태4/27/202470닷넷: 2250. PInvoke 호출 시 참조 타입(class)을 마샬링하는 [IN], [OUT] 특성파일 다운로드1
13608정성태4/26/2024424닷넷: 2249. C# - 부모의 필드/프로퍼티에 대해 서로 다른 자식 클래스 간에 Reflection 접근이 동작할까요?파일 다운로드1
13607정성태4/25/2024423닷넷: 2248. C# - 인터페이스 타입의 다중 포인터를 인자로 갖는 C/C++ 함수 연동
13606정성태4/24/2024475닷넷: 2247. C# - tensorflow 연동 (MNIST 예제)파일 다운로드1
13605정성태4/23/2024714닷넷: 2246. C# - Python.NET을 이용한 파이썬 소스코드 연동파일 다운로드1
13604정성태4/22/2024755오류 유형: 901. Visual Studio - Unable to set the next statement. Set next statement cannot be used in '[Exception]' call stack frames.
13603정성태4/21/2024904닷넷: 2245. C# - IronPython을 이용한 파이썬 소스코드 연동파일 다운로드1
13602정성태4/20/2024945닷넷: 2244. C# - PCM 오디오 데이터를 연속(Streaming) 재생 (Windows Multimedia)파일 다운로드1
13601정성태4/19/2024972닷넷: 2243. C# - PCM 사운드 재생(NAudio)파일 다운로드1
13600정성태4/18/2024989닷넷: 2242. C# - 관리 스레드와 비관리 스레드
13599정성태4/17/2024938닷넷: 2241. C# - WAV 파일의 PCM 사운드 재생(Windows Multimedia)파일 다운로드1
13598정성태4/16/2024981닷넷: 2240. C# - WAV 파일 포맷 + LIST 헤더파일 다운로드2
13597정성태4/15/2024977닷넷: 2239. C# - WAV 파일의 PCM 데이터 생성 및 출력파일 다운로드1
13596정성태4/14/20241083닷넷: 2238. C# - WAV 기본 파일 포맷파일 다운로드1
13595정성태4/13/20241070닷넷: 2237. C# - Audio 장치 열기 (Windows Multimedia, NAudio)파일 다운로드1
13594정성태4/12/20241088닷넷: 2236. C# - Audio 장치 열람 (Windows Multimedia, NAudio)파일 다운로드1
13593정성태4/8/20241091닷넷: 2235. MSBuild - AccelerateBuildsInVisualStudio 옵션
13592정성태4/2/20241228C/C++: 165. CLion으로 만든 Rust Win32 DLL을 C#과 연동
13591정성태4/2/20241203닷넷: 2234. C# - WPF 응용 프로그램에 Blazor App 통합파일 다운로드1
13590정성태3/31/20241084Linux: 70. Python - uwsgi 응용 프로그램이 k8s 환경에서 OOM 발생하는 문제
13589정성태3/29/20241162닷넷: 2233. C# - 프로세스 CPU 사용량을 나타내는 성능 카운터와 Win32 API파일 다운로드1
13588정성태3/28/20241279닷넷: 2232. C# - Unity + 닷넷 App(WinForms/WPF) 간의 Named Pipe 통신 [2]파일 다운로드1
13587정성태3/27/20241362오류 유형: 900. Windows Update 오류 - 8024402C, 80070643
13586정성태3/27/20241540Windows: 263. Windows - 복구 파티션(Recovery Partition) 용량을 늘리는 방법
13585정성태3/26/20241500Windows: 262. PerformanceCounter의 InstanceName에 pid를 추가한 "Process V2"
[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...