machine code – Random IT Utensils https://blog.adamfurmanek.pl IT, operating systems, maths, and more. Tue, 25 Feb 2020 17:44:14 +0000 en-US hourly 1 https://wordpress.org/?v=6.5.2 .NET Inside Out Part 23 – Machine code address of any .NET Core method https://blog.adamfurmanek.pl/2020/08/22/net-inside-out-part-23/ https://blog.adamfurmanek.pl/2020/08/22/net-inside-out-part-23/#respond Sat, 22 Aug 2020 08:00:48 +0000 https://blog.adamfurmanek.pl/?p=3433 Continue reading .NET Inside Out Part 23 – Machine code address of any .NET Core method]]>

This is the twentieth third part of the .NET Inside Out series. For your convenience you can find other parts in the table of contents in Part 1 – Virtual and non-virtual calls in C#

.NET Core introduced tiered compilation and reworked AOT compilation. Previously, we could get address of machine code by calling GetFunctionPointer on a method handle. However, it didn’t work for NGEN-ed methods and doesn’t work for ReadyToRun code or tiered methods. How to do it?

If you go through coreclr repository you’ll find this method:

PCODE MethodDesc::GetNativeCode()

If we call it from C# we’ll be able to get the machine code address. Let’s do it:

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace MachineCodeAddress
{
    delegate int CallMethodReturnPointer();

    class Program
    {
        static void Main(string[] args)
        {
            // Run as x86
            // .load C:\users\afish\desktop\tools\sos\x86\sos.dll
            // x coreclr!MethodDesc::GetNativeCode
            // Gives
            // 79c6be0c          coreclr!MethodDesc::GetNativeCode (void)
            // Replace address below with real one (or automate extracting it from coreclr.pdb + coreclr location in runtime)
            var methodDescriptorGetNativeCode_nativeAddress = 0x7c747dbb;
            var methodHandle = typeof(FileStream).GetMethod("WriteFileNative", BindingFlags.NonPublic | BindingFlags.Instance).MethodHandle;

            RuntimeHelpers.PrepareMethod(methodHandle);
            var mainHandle = (int)methodHandle.Value;
            Console.ReadLine();

            var getCodeAddress_code = new byte[]
            {
                0xb9, (byte)(mainHandle & 0xFF), (byte)((mainHandle >> 8) & 0xFF), (byte)((mainHandle >> 16) & 0xFF), (byte)((mainHandle >> 24) & 0xFF), // b9 44 33 22 11 mov ecx,0x11223344
                0x68 , (byte)(methodDescriptorGetNativeCode_nativeAddress & 0xFF), (byte)((methodDescriptorGetNativeCode_nativeAddress >> 8) & 0xFF), (byte)((methodDescriptorGetNativeCode_nativeAddress >> 16) & 0xFF), (byte)((methodDescriptorGetNativeCode_nativeAddress >> 24) & 0xFF),// 68 bb 7d 74 7c push   0x7c747dbb
                0xc3, // c3 ret
                0xc3, // c3 ret
            };
            CallMethodReturnPointer getCodeAddress = FuncGenerator.Generate<CallMethodReturnPointer>(getCodeAddress_code);
            var codeAddress = getCodeAddress();

            // These two should be different for ReadyToRun code
            Console.WriteLine($"CodeAddress: {codeAddress.ToString("X")}");
            Console.WriteLine($"FunctionPointer: {methodHandle.GetFunctionPointer().ToString("X")}");
            Console.ReadLine();
        }
    }

    // Flags for VirtualProtect method
    public enum Protection
    {
        PAGE_NOACCESS = 0x01,
        PAGE_READONLY = 0x02,
        PAGE_READWRITE = 0x04,
        PAGE_WRITECOPY = 0x08,
        PAGE_EXECUTE = 0x10,
        PAGE_EXECUTE_READ = 0x20,
        PAGE_EXECUTE_READWRITE = 0x40,
        PAGE_EXECUTE_WRITECOPY = 0x80,
        PAGE_GUARD = 0x100,
        PAGE_NOCACHE = 0x200,
        PAGE_WRITECOMBINE = 0x400
    }

    public class FuncGenerator
    {
        // Method to unlock page for executing
        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool VirtualProtect(IntPtr lpAddress, uint dwSize, uint flNewProtect, out uint lpflOldProtect);

        // Unlocks page for executing
        private static void UnlockPage(int address)
        {
            uint old;
            VirtualProtect((IntPtr)address, 6, (uint)Protection.PAGE_EXECUTE_READWRITE, out old);
        }

        // Some internal storage for pinning
        private static IList<object> memory = new List<object>();
        private static IList<GCHandle> handles = new List<GCHandle>();

        // Pins array with code and returns address to the beginning of the array
        private static IntPtr Pin(object data)
        {
            memory.Add(data);
            var handle = GCHandle.Alloc(data);
            handles.Add(handle);

            return Marshal.ReadIntPtr(GCHandle.ToIntPtr(handle));
        }

        // Returns delegate of type T using class U for stubbing
        public static T Generate<T>(byte[] data)
        {
            // Address of machine code in array
            // We omit first 8 bytes (array type and size)
            var arrayCodeAddress = ((int)Pin(data)) + 8;

            // Unlock page so we can execute code from it
            UnlockPage(arrayCodeAddress);
            Console.WriteLine("Machine code in array address: " + arrayCodeAddress.ToString("X"));

            // Returns delegate of correct type so we have runtime type checking
            return (T)(object)Marshal.GetDelegateForFunctionPointer<T>((IntPtr)arrayCodeAddress);
        }
    }
}

General idea is we want to generate some machine code to call the method directly. To do that, we use .NET Inside Out Part 9 — Generating Func from a bunch of bytes in C# revisited approach. In lines 31-34 we pass the method descriptor via ecx register (the this pointer). Then use the trick to push address and return to it which is effectively a call to an absolute address. Finally, we return the value of the eax register. Rest of the code is explained in other part.

You can run this on some ReadyToRun method (like in the example) and see that value returned by internal method differs from the value returned by .NET API.

It works, however, we need to know the physical address of the MethodDesc::GetNativeCode() method. For the purpose of this demo I just extracted it with WinDBG, however, you can automate it in many ways. Keep in mind that the address will change between applications and system restarts.

]]>
https://blog.adamfurmanek.pl/2020/08/22/net-inside-out-part-23/feed/ 0