JIT opcode instrumentation macros (method code overrides) x86 only for now

Started by RawCode, June 17, 2015, 06:02:15 AM

Previous topic - Next topic

RawCode

Well, this is about using x86 assembler directly (native assembly over c#).

Not a joke...

Mono runtime expose raw function pointer for any method (it will jit method on pointer request), with this pointer you can modify raw x86 assembly of that method via unsafe pointer operations.

        unsafe public sealed override void ResolveReferences()
        {
            Log.Warning("x86 override test 1");
            Log.Warning(TEST().ToString());

            byte* mpx_1 = (byte*)typeof(Redefine).GetMethod ("TEST", BindingFlags.Static | BindingFlags.Public).MethodHandle.GetFunctionPointer ().ToPointer();
           
            *(mpx_1 + 0) = 0xB8; //mov eax,0x0
            *(mpx_1 + 1) = 7;
            *(mpx_1 + 2) = 2;
            *(mpx_1 + 3) = 2;
            *(mpx_1 + 4) = 1;
            *(mpx_1 + 5) = 0xC3; //ret

            Log.Warning("x86 override test 2");
            Log.Warning(TEST().ToString());


            Log.Warning("have a nice day!");

            //StageA();
        }


Currently i dig mono sources for safe and easy method do pass control over methods, including methods crossing domains.

I need just few days to polish stuff, then, you will be allowed to hook\override non virtual methods of any type with any code.

Basic operations will be allowed to anyone, non standard - advanced CLI\assembler

1) You make your own implementation of specific method, signature must match, no other limits.

2) You invoke specific payload method, it will compile Jmp trampoline from target method to your own and modify memory.

3) In case of profiling style, it will reassemble target method as IL or x86 and place specific call into specific location as dynamic method.
compile delegate for that method and then compile Jmp trampoline from target method to delegate.

limits are:
i have no resources to support other platforms, x86 only
performance penalty in case of major additions to method code due to delegate use
memory leaks - delegates never unload
generic runtime stability loss, single ever minor error will cause odd effects instantly or some time later

Sample dll have not yet implemented classes, entire payload stored inside     
public class Redefine : Def
https://github.com/RawCode/Rimgazer



More "useful" sample:

invoking this code will allow to build inside areas marked as edge
            byte* mpx_1 = (byte*)typeof(GenGrid).GetMethod("InNoBuildEdgeArea", BindingFlags.Static | BindingFlags.Public).MethodHandle.GetFunctionPointer().ToPointer();
           
            *(mpx_1 + 0) = 0xB8; //mov eax,0x0
            *(mpx_1 + 1) = 0;
            *(mpx_1 + 2) = 0;
            *(mpx_1 + 3) = 0;
            *(mpx_1 + 4) = 0;
            *(mpx_1 + 5) = 0xC3; //ret


[attachment deleted due to age]

justarandomgeek

First, this is a terrible, awful, horrible thing to do, and should never ever be done. I love it.

Second, wouldn't it be better to use IL Opcodes instead of raw x86, in hopes of a little portability? Or does this hack only work after JIT, and thus too late for IL?

And finally, I'd imagine this doesn't work all too well with DEP enabled?

RawCode

It works with live W7x64 Rimworld game without any additional hacks.

DEP and layout randomization will do nothing, cos memory sections allocated for methods marked RWX by memory manager, this will never change, JIT require this flags to write compiled code inside internal routines.

You can implement similar system over IL, then we will compare results.

RawCode

Well, about preJIT method replacement:

1) Mono follow "generic" rules about JITting methods - if method is invoked, all linked methods will be compiled and function pointers will be updated.

If method invoked indirectly (reflection or natives) it wont be jitted ahead of time and control will pass into function used to invoke JIT over that method.

Later that function will receive unconditional jump as second word.
After control unloading, memory used by compiler function will be reclaimed.
All methods holding reference will have bytecode updated.

Rimworld run and execute BEFORE modder have chance to inject IL updates into code, most of methods will be jitted by that time.

Method cache invalidation is painful process, due to complexity of memory manager it virtually impossible without going native. (probably there is other way, but i can't see it)
Also this will require to allocate JIT stuff or compile methods self...