Assembler for everyone: __jmp31 function implementation for mono\C#

Started by RawCode, December 18, 2016, 09:17:48 AM

Previous topic - Next topic

RawCode

Well, new things in this topic (compared to my prev threads):

1) Allocation of RWX memory;
2) Calling into RWX memory without much magic;

using System;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;

namespace rc.kagimono.cmu
{
unsafe public class CodeManipulationUtils
{
//this method will allocate new codemanager
//you now allowed to access global codemanager with mono.dll shipped with game*
//*it's possible to access it anyway, but there are no reasons to do it
[DllImport("__Internal")]
static extern private unsafe void* mono_code_manager_new();

//this method will allocate memory with RWX permissions
//since you have exclusive access to that memory, no reasons to follow commit procedure
[DllImport("__Internal")]
static extern private unsafe void* mono_code_manager_reserve(void* MonoCodeManager, int size);

//you already seen this struct, do not cache results anywhere and do not store it
[StructLayout(LayoutKind.Explicit,Size=32)]
public unsafe struct reinterpret_cast_struct_32
{
[FieldOffset(0)] public object target;
[FieldOffset(0)] public void* pointer;
}

static void* u_MonoCodeManager;
static void* u_RWXM;
static CodeManipulationUtils()
{
u_MonoCodeManager = mono_code_manager_new ();
u_RWXM = mono_code_manager_reserve (u_MonoCodeManager, 512);
Console.WriteLine ("RWX allocation " + (int)u_RWXM);
}


//ease of use bits, it's possible to allocate delegate object by other means
delegate int __jmp_delegate();

//method to allocate delegate, never actually called
[MethodImpl(MethodImplOptions.NoInlining)]
static public int __jmp_dummy_method()
{
throw new Exception ("Jump failure");
return 0;
}

//this method explained in forum post, line by line
[MethodImpl(MethodImplOptions.NoInlining)]
static public int __jmp31(void* _dest)
{
__jmp_delegate callsite = __jmp_dummy_method;
reinterpret_cast_struct_32 rcs = new reinterpret_cast_struct_32 ();
rcs.target = callsite;
((int*)rcs.pointer) [3] = (int)_dest;
return callsite ();
}

//load bytearray into RWX memory and call into it
[MethodImpl(MethodImplOptions.NoInlining)]
static public int __asm32(params byte[] x8632)
{
Marshal.Copy (x8632, 0, new IntPtr (u_RWXM), x8632.Length);
return __jmp32 (u_RWXM);
}
}
}


Simple things explained in comments, complex stuff is __jmp32, explanation line by line:

__jmp_delegate callsite = __jmp_dummy_method;
cause runtime to create and initialize multicast delegate that extends __jmp_delegate inner (hidden) type, this is syntax sugar, real code is not "single line".

reinterpret_cast_struct_32 rcs = new reinterpret_cast_struct_32 ();
rcs.target = callsite;
((int*)rcs.pointer) [3] = (int)_dest;

allocate memory to host reinterpret_cast_struct_32 instance (32 bytes of memory)
store reference to our delegate object into first word of allocated memory
read that memory as int* to overcome type safety rules of c#
then replace 4th word of delegate object with provided pointer

return callsite ();
invoke our delegate

this method allows to call any pointer as function, but perform no safety checks of any kind, calling invalid pointer will crash runtime without any warning.

Usage:

//print "some value" extracted via ASM code
Console.WriteLine (CodeManipulationUtils.__asm32 (0xB8, 0xD2, 0x04, 0x00, 0x00, 0xC3));



//call "wtf1" method
CodeManipulationUtils.__jmp31 (typeof(Kagimono).GetMethod ("wtf1").MethodHandle.GetFunctionPointer ().ToPointer ());


__jmp31 called this way because this is not "jmp" and "true" jump is not possible without changing how jitter works