Mono x86-32 JIT machine code hooks

Started by RawCode, December 24, 2015, 05:38:45 AM

Previous topic - Next topic

TheGentlmen

Opps, sorry mate...

You can get it here:
https://gitlab.com/RimGentzCo/FunctionJumping/tree/master

And yes, its set to public so ya don't have to login...

RawCode

there is no reason to copy classes since you can use generics or emit code at runtime

oncall injections are useless sine they do not allow to change outcome of invokation without special implementation.

TheGentlmen

Quote from: RawCode on January 03, 2016, 02:53:59 AM
there is no reason to copy classes since you can use generics or emit code at runtime

How would generics solve the problem? How would generics change OnCalled's function signature, or OnCalledGate's function signature?

        public static void OnCalledGate()   
        {
            if (OnCalled != null)
                OnCalled();  //How do generics pass parameters forward?
        }



Why would you emit code at runtime? How would you emit code at runtime? What is 'emitting code at runtime'?

Srsly tho, I'm not the pro assembly coder (I don't even know assembly). In all my yeas of coding C++, for the most part I've avoided lambas, pointers, C, Inline Assembly, Generics and Overriding Operators... I'd always pass them over to some poor sod to do for me. Saying "emit code at runtime to solve problem" has no meaning to me.

Quote from: RawCode on January 03, 2016, 02:53:59 AM
oncall injections are useless sine they do not allow to change outcome of invokation without special implementation.

What? How do they NOT change the outcome?

I clearly showed calling the same 3 functions gave different results every time using my events system, does that NOT change outcome?

The output was:
A API
B API
C OVERRIDE
81
HIT ENTER!!!



A NORM
B NORM
C NORM
9
HIT ENTER!!!



0
HIT ENTER!!!



A NORM
B NORM
A NORM
B NORM
C NORM
9
HIT ENTER!!!

Is that not different results from calling the same 3 functions?
Did you even compile the program?


I asked for CONVOLUTED answers, not OBSCURE answers.

RawCode


function void blabla(some arguments)
<injection point>
calculate damage, not invocation, just some fancy math
do damage to pawn(local)
do damage to environment(arg)
increment some counter()
corrupt soul of developer()
<injection point>
return 42


Plan A:
Please remove soul corruption effect with your "framework".

Plan B:
Please alter pawn damage, special note, it is not just passed as argument, it is calculated inside function.

note: outcome != returned value.

Actually both "plans" are perfectly possible, but in both cases there is absolutely no need for delegates and additional classes, just event bus provided by api and nothing more

TheGentlmen

#19
Quote from: RawCode on January 03, 2016, 11:28:40 PM

function void blabla(some arguments)
<injection point>
calculate damage, not invocation, just some fancy math
do damage to pawn(local)
do damage to environment(arg)
increment some counter()
corrupt soul of developer()
<injection point>
return 42


Plan A:
Please remove soul corruption effect with your "framework".
>>1- I never said its a framework.
2- Please remove 'soul corruption effect' with your detours. Inlighten me how?
RawCode's detours:
    1-Copy past 'blabla' into 'blabla2'
    2-Remove 'soul corruption effect' from 'blabla2'
    3-Detour from 'blabla' to blabla2'.
Now here is how ya do it in mine:
    1-Copy past 'blabla' into 'blabla2'
    2-Remove 'soul corruption effect' from 'blabla2'
    3-Init event
    4-Suscribe to event


Plan B:
Please alter pawn damage, special note, it is not just passed as argument, it is calculated inside function.
>>Just Override the WHOLE GODDAM function. Now was that hard?

note: outcome != returned value.
>>FALSE!

Since you only seam to understand code, let me give you this:

public static bool IsRawcodeInCorrect(){
if  (NumOfOutcomes == 1){Console.WriteLine("Returned value is the outcome!"); return true;}
bool outcomesEqual = true; Outcome firstOutcome = outcomes[0]; outcomes.Remove(0);
foreach (Outcome _outcome in outcomes[]){if (_outcome != firstOutcome) {outcomesEqual = false; break;}}
If (outcomesEqual){Console.WriteLine("All outcomes are equal so the last outcome is the return value!"); return true;}else{Console.WriteLine("Gosh, RawCode is correct... :("); return false;}}


Output: True. In both examples only ONE function was a subscriber.

IF more than 1 delegates subscribe THEN we will only return the LAST outcome, as we can't return all of them.


Actually both "plans" are perfectly possible, but in both cases there is absolutely no need for delegates and additional classes, just event bus provided by api and nothing more

Implement an event buss then. You have done nothing but criticize my work with false information.

PS: C# Events NEED delegates to work, so go on implementing your custom event system.





RawCode + not reading code + criticizing my hard work = Pain for RawCode

RawCode

#20
QuoteImplement an event buss then. You have done nothing but criticize my work with false information.

done nothing...
kay.

Quotenote: outcome != returned value.
>>FALSE!

you just wasting my time.

Fluffy (l2032)

wow gent, cool down a bit. RawCode's done a significant chunk of research and work to get the whole detour thing to be possible in the first place - that's not exactly nothing.

As for your 'event system', by your own admission it's currently pretty much useless because you have to create a gazillion extra classes. So what did you expect? A pat on the head?

As for C# needing delegates for events, that may or may not be true, but C# also doesn't normally allow detouring/hooking function calls, yet with some black magic RawCode made that possible. I don't know if a proper simple to use event system is possible/feasible, but I know yours isn't it, and that if anyone can make it happen it's probably RawCode.

P.S. I also had no idea what 'emiting code' could possibly mean. However, we both clearly have internet access, a quick google search for C# emit code gives a few interesting results. Instead of criticizing the man for giving answers you don't understand, maybe try a little bit harder to understand first.

RawCode

Issue is simple, each *event* MUST have "human made" body that specially designed to handle possible changes from mods using API.

There is no way to perform this automatically for most methods for "reasons":


set field
call method that include field
perform calculations that include previous set and method invocations
call do_pawn_damage(calculated value)


Since field set and method invocation are both "outcome changing" such things are not allowed to happen before handlers process "event" and decide, is such event allowed to happen or not and how it should happen.

Sadly, there is no way to pass information about event to bus, since such information not yet exists.
Lines before actual damage used to calculate damage may include damage to armor, damage to shields, various counter and stuff.

Just moving damage part to event will result in ugly thing: pawn "blocked damage by jedi trick" but armor and shield still got damage and bleeding wound is here...

Only viable way to handle such situation - to provide different implementation that can handle all stuff properly, calculate everything in "non outcome changing" manner.

There is no need in custom class per each event, "this" is argument #1 and can be processed inside static method just fine.
There is no need in delegates, as probably nobody aware - delegates are syntax sugar and will be compiled into ugly stuff that include hidden interface and other ugly things.

As for private methods and fields - i will provide magical "stuff" that will allow to both compile and execute code that violate all protection rules soonish.

akisute

Well, as a Mac player, I sometimes want you guys to remember other platforms :'(

https://github.com/RimWorldCCLTeam/CommunityCoreLibrary/issues/54

The current implementation in CCL inspired by code here doesn't work in Mac (most likely Linux too). This is not a matter of your code but CLL, but...

I'm not a hardcore C# developer but I still understand you have to do some real black magic to detour (or hook) the function calls without touching the prebuilt code of Core Rimworld. If you could happen to find out the black magic that works in all platforms that would be awesome...

RawCode

It will work on all platforms, issue is endianess, not x64 or different version of mono (mono shipped with game and have fixed version).

Single check and 2 lines to handle endianess and you will have magic on mac\linux.

Actually support of all platforms and all possible cases is reason why i avoid going native, all this much more simple to implement in pure C.

1000101

Modern Macs use x86 so endianness isn't an issue.

Some functions (methods/properties/whatever you want to call them) can't be detoured for "reasons."  Some of these reasons include but are not limited to, tighter memory restrictions (code sections may not be writeable), the original function may be inlined by the JIT (small, simple functions such as property getters), the original function may too small (not enough code to over-write with the jump).

Nobody in the CCL team has a Mac to test with and the detouring code should be considered experimental at this point.  Also, this is a fairly advanced topic so a much greater understanding of what the compiler, what the JIT, how the hardware and platform works is required.  This isn't a trivial topic.

While I have been a programmer for decades (C, C++, ASM, Pascal, various flavours of BASIC, some scripting languages) and understand x86 hardware and the Windows platform, C# specifics and it's compiler are relatively new to me.

All these things need to be taken into consideration when trying to do unsafe things such a code detours (aka "hot patching").

RawCode has done something I tried about a year ago although through a different means (I tried replacing the JIT's vtable table entry for the function) without success.  It's a learning process for us all and it needs to be undertaken with an open mind and respecting each others input.  Sometimes it's the seemingly simplest questions which have the most obvious answers which lead to what you're looking for.

And don't forget your rubber duck.
(2*b)||!(2*b) - That is the question.
There are 10 kinds of people in this world - those that understand binary and those that don't.

Powered By

RawCode

QuoteSome of these reasons include but are not limited to, tighter memory restrictions (code sections may not be writeable), the original function may be inlined by the JIT (small, simple functions such as property getters), the original function may too small (not enough code to over-write with the jump).

0) applicable only for mono shipped with unity v2c

1) It will throw CPU level exception (crash runtime) if you try to modify native function, but, you can modify MPF before doing this and this will work just fine.

JIT generated code marked RWX on allocation and W permission never revoked.

2) Inline, generics, virtual, native and interface calls are special cases that will require special handling.

3) functions that do something or return something are at least 32 bytes, function that do completely nothing - well, they do nothing, there is no reason to hook them.

1000101

Quote3) functions that do something or return something are at least 32 bytes, function that do completely nothing - well, they do nothing, there is no reason to hook them.

I don't disagree with anything else you said except this.  I've written many functions which have compiled to less than 32 bytes.  The smallest function you can write is 1 byte (retf) although this is generally useless, it serves the point.  There is no guarantee of a "minimum size" for a function.

That being said, you would generally have "padding" for alignment to the next function, but the amount of padding depends on the machine word size targeted.  Most modern compilers use paragraph (16-byte) alignment but there, again, is no guarantee of the padding used unless (a) the compiler allows you to specify the alignment or (b) you are writing code in assembler where you can explicitly control such things.

/nitpick
(2*b)||!(2*b) - That is the question.
There are 10 kinds of people in this world - those that understand binary and those that don't.

Powered By

RawCode

i already explained all this and don't see reasons to repeat it.

if method cannot be altered due to low size or some special handling from engine, all methods that call it will be changed instead.

i already have somewhat working prototype that replaces calls to method A inside method B instead of injecting jumps.

isistoy

Hey!
There was that call to some ticking method, that was detecting factory comp classes for stats calculations, but wouldn't consider inheritance...
You mean this could allow me to inject a rewritten method and have custom inheritance to work?
<Stay on the scene like a State machine>