Ludeon Forums

RimWorld => Mods => Topic started by: Micktu on August 05, 2016, 02:59:26 AM

Title: New code injection method
Post by: Micktu on August 05, 2016, 02:59:26 AM
I made a proof-of-concept method call patcher: https://github.com/micktu/RimWorld-BuildProductive/blob/master/Source/MethodCallPatcher.cs (https://github.com/micktu/RimWorld-BuildProductive/blob/master/Source/MethodCallPatcher.cs)

What it does is it looks for every call to a specific method in other method's body and replaces it with a method of your choice.

Because there's no way I'm aware of to either to force Mono JIT compilation or to check if the method was compiled, it repeatedly scans the method over time until it finds what it needs. As it's just looping through a few hundreds bytes per tick with direct memory access it's not very taxing; funny thing is, method body does exist before the actual method is compiled, but I'm not sure about what it contains.

This enables more kinds of injections into methods; I'll provide an example of a better detour method than CCL has currently.
* Implies every method is consistent with its first instructions, more research needed.

Et voila, we have a detour that can actually call the base method.
Title: Re: New code injection method
Post by: Fluffy (l2032) on August 05, 2016, 03:23:37 AM
This looks very promising, am I correct in assuming that this would allow one to attach events* to method calls? E.g. leave the original call intact, but do something extra when called? With access to the original arguments + instance data?

(I'm aware it's not really an event, but you get the gist).
Title: Re: New code injection method
Post by: Micktu on August 05, 2016, 03:46:29 AM
Yeah, pretty much that, you can make hooks/callbacks. That's exactly what I had in mind.

I didn't post an actual usage sample, but in my case, I inject a command before gizmo rendering. Wanted to use it to inject commands into existing Thing classes.

       
        internal static void GizmoGridDrawer_DrawGizmoGrid(IEnumerable<Gizmo> gizmos, float startX, out Gizmo mouseoverGizmo)
        {
            Command_Action command_Action = new Command_Action();
            command_Action.defaultLabel = "Test Icon";
            command_Action.action = delegate
            {
            };
            (gizmos as List<Gizmo>).Add(command_Action);

            GizmoGridDrawer.DrawGizmoGrid(gizmos, startX, out mouseoverGizmo);
        }
Title: Re: New code injection method
Post by: 1000101 on August 05, 2016, 04:34:44 AM
Quote from: Micktu on August 05, 2016, 02:59:26 AM...a better detour method than CCL has currently.

That's a rather opinionated and biased statement.  That it's constantly scanning makes it no "better" in my opinion just different.

The only problem with this I see is that you need to scan every single method to trap all cases as opposed to CCLs current implementation which traps all cases with one change.

Anyway, it sounds promising for certain cases and look forward to seeing some actual working code using this.
Title: Re: New code injection method
Post by: Micktu on August 05, 2016, 04:41:17 AM
The current method does not allow to return to the original method. For me it's essential to be able to call the original, i.e. produce an actual hook that does not require to copy-paste decompiled code into the target method, which is the case in CCL right now. It will break constantly and will require a meticulous update on every application update. To sum it up, it doesn't really work.

The snippet above is the sampe of actual tested-to-be-working code I'll be using to inject commands into things.
Title: Re: New code injection method
Post by: 1000101 on August 05, 2016, 04:46:57 AM
CCLs detouring method does not allow calls to base.method, correct.  Doing so will cause a stack overflow due to the call being resolved back into the detour, correct.  The solution, which isn't great, is to include the entire method bodies of all base.method calls into the detour, correct.

However, scanning every method in every DLL plus the game executable 10/sec to patch call sites to trap all calls to your detour is also not the best method and will certainly impose a large performance penalty.

I'm not saying I dislike your idea just that it's sub-optimal for blanket cases.  For "I only want to redirect this one methods calls" situation, it's very good.
Title: Re: New code injection method
Post by: Micktu on August 05, 2016, 04:52:48 AM
>scanning every method in every DLL plus the game executable

No, it was never meant to work this way, this is intended to patch calls in a single method. Above I described how to use it with some additions to existing CCL method to make an actual working hook. Basically, you're not patching the game's method, but you're patching your own so you don't get stuck in an infinite loop if you try and call the base one, and return properly.

Anyway, no, there's no real performance impact, the scan might take a few microseconds per frame and that's all. And it stops when it has been patched. I benchmarked a single scan, it takes 1.5-3 microseconds on my PC.

For example, if all required scans for all mods would take 50us, it's still like 0.3% of a tick, which is completely negligible in my opinion. An I'm not even doing it every tick.
Title: Re: New code injection method
Post by: 1000101 on August 05, 2016, 05:17:30 AM
Quote from: 1000101 on August 05, 2016, 04:46:57 AMI'm not saying I dislike your idea just that it's sub-optimal for blanket cases.  For "I only want to redirect this one methods calls" situation, it's very good.

Key statements, important.
Title: Re: New code injection method
Post by: Micktu on August 05, 2016, 05:25:37 AM
Maybe I'm bad at explaining things. It's not the complete solution, it's a part of a "blanket" solution that can replace the current hook method.
Title: Re: New code injection method
Post by: Fluffy (l2032) on August 05, 2016, 09:07:36 AM
So, let me phrase this in simple(r) terms to see if I understand it.

Let's say we have two methods, Vanilla.Foo( object x ), and MyMod.Bar( object x ). I want to call Bar() whenever Foo() is called.

I'd call your code to create me a patch, which starts the following process in your code;
- Every x ticks, Foo() is scanned to see if the JIT has been compiled yet.
- If Foo() was compiled, we store the first few instructions in Foo(), then override them with a call to Bar().
- We also create a 'new' method, Boo(), that consists of Foo's stored instructions, and a jump back into Foo() ['behind' the jump to Bar(), so we don't end up in an infinite loop].
- Finally, any call to Foo() from Bar() is amended to instead call Boo() [since calling Foo() would cause an infinite loop].

That sounds pretty cool. I have a few (noob) questions though;
- How sure are you that the first few instructions in Foo() won't be affected by relative memory addresses?
- Are you checking the length of Foo() when patching?
- And finally, assuming Foo() can be reliably preserved, would you mind implementing a rudimentary event based system around this? I'm envisioning OnCall( MethodInfo( Foo), delegate( pars ) ) kind of schemes, as well as PreCall and PostCall hooks to intercept and change arguments and return values on their way to and from Foo().

Assuming E and the other CCL people are on board and it all works reliably cross-platform, I think it'd be a very good idea to get this in CCL. Having multiple mods implement these techniques independently, and possibly targeting the same methods is going to be quite the mess.

Also, regarding waiting for JIT compilation. If I understand the process correctly, the main difference between your approach and CCL's is that you salvage the detoured method (which is awesome if it works). What I don't understand is why CCL doesn't have to wait for JIT, and you apparently do. I mean, presumably if the method wasn't compiled and CCL injects the detour jump, compilation would override that. So either we're never run into not yet compiled code, or there's something else going on (probably me misunderstanding something).
Title: Re: New code injection method
Post by: Micktu on August 05, 2016, 09:22:09 AM
Your explanation got a couple of thing backwards, but it's not a big deal.

>How sure are you that the first few instructions in Foo() won't be affected by relative memory addresses?
Because the stack will be in the same state as it's supposed to be when we will get there.

>Are you checking the length of Foo() when patching?
No, I just scan till its return.

>would you mind implementing a rudimentary event based system around this
I'm not sure if I will need it, but anyone can do it, it's not complicated.

>What I don't understand is why CCL doesn't have to wait for JIT, and you apparently do.
I don't know, too. Perhaps it's not compilation I'm waiting for, but optimization, so the method gets static addresses I can patch; don't really know much about how JIT is supposed to work here. I'm investigating this at the moment.




Title: Re: New code injection method
Post by: 1000101 on August 05, 2016, 11:26:38 AM
Getting the methodinfo and subsequent method pointer is supposed to actually trigger the JIT itself which makes waiting for it a bit redundant.  However, if this for some reason doesn't happen, then the injector needs to wait for it.  In my experience I have never seen this happen though inside of mono (which is what RimWorld is using).

As to scanning until the return, what happens if it has branches which jump over the return or has multiple returns?  Wouldn't getting the method length from the method info be a more reliable way to do this?
Title: Re: New code injection method
Post by: Micktu on August 05, 2016, 01:45:00 PM
>Getting the methodinfo and subsequent method pointer is supposed to actually trigger the JIT
Yeah, I was aware, that's why I was wondering. Turned out to be optimization: the calls are not routed as static until it's executed once.

> if it has branches which jump over the return or has multiple returns?
No, it doesn't. It's just that I haven't seen anything like this in native JIT-compiled code. Have you?

>Wouldn't getting the method length from the method info be a more reliable way to do this?
Does it provide native function size? I didn't think it was possible. Can you point me in the right direction?

Anyway, I'm trying to drop the waiting method and just make it a reliable hook. Trying out a few ways currently.
Title: Re: New code injection method
Post by: 1000101 on August 05, 2016, 03:54:51 PM
Quote from: Micktu>Getting the methodinfo and subsequent method pointer is supposed to actually trigger the JIT
Yeah, I was aware, that's why I was wondering. Turned out to be optimization: the calls are not routed as static until it's executed once.

Under normal .Net that is true, but I've found that mono seems to compile it when getting the MethodInfo.  I can't say this is true under all circumstances but the current detouring works under this assumption and that's the key thing - it works.

Quote from: Micktu> if it has branches which jump over the return or has multiple returns?
No, it doesn't. It's just that I haven't seen anything like this in native JIT-compiled code. Have you?

>Wouldn't getting the method length from the method info be a more reliable way to do this?
Does it provide native function size? I didn't think it was possible. Can you point me in the right direction?

I could have sworn there was a field for the length of the method but I could be wrong.  I just checked the MSDN and there is no field listed, I might be getting my wires crossed.

Quote from: MicktuAnyway, I'm trying to drop the waiting method and just make it a reliable hook. Trying out a few ways currently.

CCL will still need it's current detouring method as it requires a guarunteed detour during it's module initializer.  Those particular detours don't rely on calling any base methods so they don't need to worry about their destruction.  However, a "cleaner" detouring would be nice for "regular" usage which preserves the original or at least makes it accessible.
Title: Re: New code injection method
Post by: biship on August 06, 2016, 06:09:49 AM
I don't have 1/10th the coding knowledge you guys have... so posting this here as it might be semi-relevant.
For most moddable games, someone eventually comes out with a performance meter of some kind. Is there one for Rimworld?
I'm looking for, or willing to learn how to make, a dll to monitor the frequency (to start with) mod methods fire.
To paint a picture - a mod is supposed to fire when a pawn's mood changes, yet fires needlessly on every pawn item interaction?
The extension of this would be to determine how long each time each mod activation consumes.

From the code changes I follow on github, I know you guys are aware of the need to optimize your own code. Was just wondering if there is a way to determine the impact of other peoples code ingame. Thanks for any replies.
Title: Re: New code injection method
Post by: Micktu on August 06, 2016, 02:56:15 PM
So after a day and some of some hardcore ass-in-chair action I've managed to produce another solution that is dumb and brilliant at the same time.

Basically, it acts as an override: if the method was called from some distant galaxy, it calls your hook/detour/whateveryoucallit. However, if it's called from YOUR method, it calls the base method. It still relies on guessing the boundaries of your method once, but now I have a pretty good idea how to make it reliable.

It's really really hacky, I'll publish it once I tidy it up a little bit.
Title: Re: New code injection method
Post by: 1000101 on August 06, 2016, 04:27:02 PM
Quote from: biship on August 06, 2016, 06:09:49 AM
I don't have 1/10th the coding knowledge you guys have... so posting this here as it might be semi-relevant.
For most moddable games, someone eventually comes out with a performance meter of some kind. Is there one for Rimworld?
I'm looking for, or willing to learn how to make, a dll to monitor the frequency (to start with) mod methods fire.
To paint a picture - a mod is supposed to fire when a pawn's mood changes, yet fires needlessly on every pawn item interaction?
The extension of this would be to determine how long each time each mod activation consumes.

From the code changes I follow on github, I know you guys are aware of the need to optimize your own code. Was just wondering if there is a way to determine the impact of other peoples code ingame. Thanks for any replies.

This is not really relevant to the current discussion, please create a new thread for it.  :)
Title: Re: New code injection method
Post by: biship on August 06, 2016, 05:04:14 PM
Then, I don't have a 1/10th the clue what this thread is about. :)
Title: Re: New code injection method
Post by: Micktu on August 06, 2016, 09:56:25 PM
So I'll drop this monstrosity here: https://github.com/micktu/RimWorld-BuildProductive/blob/injection/Source/HookInjector.cs (https://github.com/micktu/RimWorld-BuildProductive/blob/injection/Source/HookInjector.cs)

There's still a lot of refactoring and testing to do, but the idea is there.

It lets me do this:

(https://monosnap.com/file/Be0aLaGD62RirXl7Nqxx5WBoLkI6vq.png)

To get this:

(https://monosnap.com/file/jREt2QDlB6khWS980zwO9qyfqY4unv.png)

I'll also make sure that it still works correctly even if the proc was already rerouted by other mods using CCL.

Was it worth the effort? Not sure. Also thing like this risks hella lot of maintenance even if it's stable.
Title: Re: New code injection method
Post by: RawCode on August 06, 2016, 10:46:01 PM
method is good.

there is no reason to reject again and again things you don't like for some personal reasons.
Title: Re: New code injection method
Post by: Micktu on August 07, 2016, 12:05:22 PM
After browsing Mono sources for a while I've found that the correct method to find generated code size is to hook into Mono JIT's mono_destroy_compile (MonoCompile *cfg). The MonoCompile struct is supposed to contain code length then, it will be freed afterwards, and the info forever lost. Not doing it at the moment though.
Title: Re: New code injection method
Post by: Micktu on August 07, 2016, 12:37:21 PM
Yep, that was correct.

(https://monosnap.com/file/d831P72vFYRl7F6ssOtOrsm4e7sqs9.png)

Highlighted is the function pointer, followed by code_size and code_len fields, containing correct values (I don't know why both fields are needed, the compiler seems to care about code_len in the end).
Title: Re: New code injection method
Post by: Micktu on August 07, 2016, 07:11:58 PM
A cute small function injected before mono_empty_compile() keeps track of last 256 functions compiled with their sizes.

(https://monosnap.com/file/8TX1BNVa9nDj4KXZ86kXHkGc7g03Go.png)
Title: Re: New code injection method
Post by: Micktu on August 07, 2016, 07:48:37 PM
Aaand it worked. Prototype code here: https://github.com/micktu/RimWorld-BuildProductive/blob/injection/Source/MethodSizeHelper.cs (https://github.com/micktu/RimWorld-BuildProductive/blob/injection/Source/MethodSizeHelper.cs)

This thing is getting reliable. Let's make it work on x64 again.
Title: Re: New code injection method
Post by: RawCode on August 08, 2016, 03:32:04 AM
this is bloat, proper way for calculation method size is:

[MethodImpl(MethodImplOptions.NoInlining)]
static public void MethodFastPrint(RuntimeMethodHandle hx)
{
void* fpraw = hx.GetFunctionPointer().ToPointer();
void* jitinforaw = mono_jit_info_table_find (mono_domain_get (), fpraw);
u_MonoJitInfo jitinfo = *(u_MonoJitInfo*)jitinforaw;
int size = jitinfo.code_size;
}


where

[DllImport("__Internal")]
static extern private unsafe void* mono_jit_info_table_find(void* MonoDomain, void* ptr2function);

[DllImport("__Internal")]
static extern private unsafe void* mono_domain_get ();


and memory map is

[StructLayout(LayoutKind.Explicit)] public unsafe struct u_MonoJitInfo
{
[FieldOffset(0) ] public int  *dmethod;
[FieldOffset(8) ] public int  *code_start;
[FieldOffset(16)] public int   code_size;
}


linked table contains ALL methods ever compiled for domain, you don't need to inject anything to keep track of them, runtime already do it.
Title: Re: New code injection method
Post by: Micktu on August 08, 2016, 08:28:55 AM
Thanks, I'll verify one I'll get to my PC.

Edit: Yes, that seems just about right! Let's implement.

Actually it wasn't bloat, it was only the beginning of bloat, considering I can't get away with a static address on *nix and have to dlinfo/dlsym modules etc. So it sucked.

This is so much better.
Title: Re: New code injection method
Post by: Longwelwind on August 08, 2016, 08:39:23 AM
I believe you can use Mono.Cecil to do that but more easily.
Back when I was modding Planetbase, we did something similar to add event hooks to the game. I began to convert what I did for Planetbase to Rimworld, but since CCL already exists, I didn't continue.
The repo is available here. (https://github.com/Longwelwind/PhiScript) Basically, what it does is injecting MSIL code into the Assembly-CSharp.dll assembly to call the library that will load mods and offer them event hooks. It reads a XML file to know what kind of code to inject in the assembly. For example:

<Class Name="PlayDataLoader" Location="0">
    <Method Name="LoadAllPlayData">
      <Instruction OpCode="Call" Assembly="PhiScript" Type="PhiScript.Phi" Method="StaticLaunch" />
    </Method>
</Class>

This configuration would inject a static call to a static method called "Phi" in a class called "PhiScript" in an assembly called "PhiScript" in the PlayDataLoader (one of the earliest called method in Rimworld), allowing me to catch this event and add behaviour for this.
The advantage of using Mono.Cecil is that it takes care of pretty much everything: calculating addresses, offsets, adding assemblies dependencies into the modified dependencies and such.
Title: Re: New code injection method
Post by: Micktu on August 08, 2016, 08:44:28 AM
Cecil cannot patch assemblies at runtime. You can only get away with Cecil (and it works perfectly well) if you patch the assembly before loading it.

Anyway, we're beyond patching assemblies, we're patching native code here.
Title: Re: New code injection method
Post by: RawCode on August 08, 2016, 09:24:19 AM
Quote from: Longwelwind on August 08, 2016, 08:39:23 AM
I believe you can use Mono.Cecil to do that but more easily.
Back when I was modding Planetbase, we did something similar to add event hooks to the game. I began to convert what I did for Planetbase to Rimworld, but since CCL already exists, I didn't continue.
The repo is available here. (https://github.com/Longwelwind/PhiScript) Basically, what it does is injecting MSIL code into the Assembly-CSharp.dll assembly to call the library that will load mods and offer them event hooks. It reads a XML file to know what kind of code to inject in the assembly. For example:

<Class Name="PlayDataLoader" Location="0">
    <Method Name="LoadAllPlayData">
      <Instruction OpCode="Call" Assembly="PhiScript" Type="PhiScript.Phi" Method="StaticLaunch" />
    </Method>
</Class>

This configuration would inject a static call to a static method called "Phi" in a class called "PhiScript" in an assembly called "PhiScript" in the PlayDataLoader (one of the earliest called method in Rimworld), allowing me to catch this event and add behaviour for this.
The advantage of using Mono.Cecil is that it takes care of pretty much everything: calculating addresses, offsets, adding assemblies dependencies into the modified dependencies and such.

base files modifications is not allowed, read manual first.
Title: Re: New code injection method
Post by: Micktu on August 08, 2016, 09:46:01 AM
So, thanks again, RawCode-sensei! It does work as expected.

Implemented like this so it should work on x64 out of the box (maybe will have to go with libmono instead of __Internal on *nix though):


        [DllImport("__Internal")]
        public static extern IntPtr mono_jit_info_table_find(IntPtr domain, IntPtr addr);

        [DllImport("__Internal")]
        public static extern IntPtr mono_domain_get();

        [StructLayout(LayoutKind.Sequential)]
        public struct MonoJitInfo
        {
            public IntPtr d;
            public IntPtr n;
            public IntPtr code_start;
            public uint unwind_info;
            public int code_size;
            // The rest is omitted
        }



            var infoPtr = Platform.mono_jit_info_table_find(Platform.mono_domain_get(), targetPtr);
            var ji = (Platform.MonoJitInfo)Marshal.PtrToStructure(infoPtr, typeof(Platform.MonoJitInfo));


Now I can finally get rid of terrible, terrible size scan code.
Title: Re: New code injection method
Post by: Micktu on August 08, 2016, 05:00:36 PM
It's finished!

Tested on Windows, OSX, and Ubuntu x64 (and I assume x86 would work too). I also believe it's stable and reliable for most methods. Still can't hook into IEnumerable<> generators though.

It can also stack overrides on the same function multiple times (if multiple mods need to hook to the same thing), and correctly handles CCL-detoured methods, so you can hook to these too.

You need these files (these are direct links): HookInjector.cs (https://raw.githubusercontent.com/micktu/RimWorld-BuildProductive/injection/Source/HookInjector.cs) Platform.cs (https://raw.githubusercontent.com/micktu/RimWorld-BuildProductive/injection/Source/Platform.cs) AsmHelper.cs (https://raw.githubusercontent.com/micktu/RimWorld-BuildProductive/injection/Source/AsmHelper.cs) - they will be moved into master branch some time later.

Define your override:
internal static void PreLoadUtility_CheckVersionAndLoad(string path, ScribeMetaHeaderUtility.ScribeHeaderMode mode, Action loadAct)
{
    Log.Message("!!! CheckVersionAndLoad");
    PreLoadUtility.CheckVersionAndLoad(path, mode, loadAct);
}


And then hook it up:
HookPatcher = new HookInjector();
HookPatcher.Inject(typeof(PreLoadUtility), "CheckVersionAndLoad", typeof(VerseExtensions));

As you can see, if you don't specify the target method name, it defaults to YourClass.SourceClassName_SourceMethodName().

You don't need to create multiple injectors (although it won't break if you do), one is enough.
Title: Re: New code injection method
Post by: 1000101 on August 08, 2016, 05:30:46 PM
Interesting code.

It could use some polish and needs some work to match the coding style and structure before being added to CCL though.

I'm eager to see a complete working example.  :)
Title: Re: New code injection method
Post by: Micktu on August 08, 2016, 05:53:56 PM
>It could use some polish and needs some work to match the coding style and structure before being added to CCL though.
Nice joke, the polish and coding style of college students, right. Actually, you know what, don't touch the source code, I'll make it an assembly and you can use that if you wish.

>I'm eager to see a complete working example
So make it. Test it, mr. part of the community.
Title: Re: New code injection method
Post by: RawCode on August 08, 2016, 09:11:54 PM
doh...

This is not about making blackbox assembly and giving it to other, it's about making code and explaining how it works, to allow other individuals to learn how it works.

In longrun, such activity will allow others to improve base work, just like it happened with my initial commit, improved by community to handle x64.
After researching changes, i managed to greatly improve base work, result is ABS hooks posted recently.

"Coding style" do matter, if base code implement "cryptic" features like indirect invocation, magic numbers, offsets instead of names, "var", "using" or homebrew lambdas.
Reason behind is simple - such code harder to understand and follow for others.

I have private builds that use "stuff" including "var" and 3 *random* letters as varname, but, i never post "raw" code on forum, because it looks like after obfuscation and impossible to follow.
Title: Re: New code injection method
Post by: Micktu on August 08, 2016, 09:25:57 PM
Oh no no no, var is a production-quality blessing. I scold people for not using that, because BaseContainerInfoHandler baseContainerInfoHandler = new BaseContainerInfoHandler() is obviously fun and improves readability immensely. That's exactly why Java is unusable, as well as C++ before C++11.

I agree on everything else though, no magic number, no ambiguity. That thing was rewritten about 4 times, I'm sure every line is in its place besides a couple of line breaks and brackets. I went ways to actually make it clean and readable, and I'm satisfied with the result.

This is also the last time I argue about style.

Back on topic, what I overlooked is that you have to be able to Invoke() from inside of your stuff to call private base methods, but now you obviously end up in a stack overflow and crash. I'll make it do an extra check later.
Title: Re: New code injection method
Post by: Micktu on August 09, 2016, 08:14:09 AM
After a little though I believe checking for Invoke wouldn't work, because there are other places that might want to Invoke your method if it's a delegate.

We need figure out a way to call private methods directly if possible, maybe through IL preprocessing. RawCode please try to think about it.

Or I'll make an alternative check that does not involve call stack. Someeting dumb like setting a flag when you enter your method and clearing it when you leave it. Not thread-safe of course.
Title: Re: New code injection method
Post by: Micktu on August 09, 2016, 09:05:08 AM
So when I found out that on x64 stack alloc sometimes is a single instruction and can't be reliably replaced with a jump, I weighted different solutions and tried copying the whole method as a rescue. I assumed it's rare, and it was only small methods that can be this way and that it will work in most cases.

Turned out I was wrong, it's very common:

(http://take.ms/wXpm5)

And the methods can be huge:
(http://take.ms/YAFBX)
But the good news is that it works perfectly well. It is even is able to resolve its trampolines successfully in its new location (thanks mono programmers).

Unfortunately this won't work on x86 because of relative addressing that has to be handled, and that increases complexity too much to be nice. But then again, it's not a problem on x86 because I'm yet to see a stack alloc pattern that's shorter than 5 bytes.

By the way I'm still patching with relative jumps in x64 because the chances that memory pages will end up 2Gb apart are maybe one in a thousand years.
Title: Re: New code injection method
Post by: RawCode on August 09, 2016, 11:21:51 PM
I never hit any issues with recursive calling, i need some kind of example to implement a fix.

Also stack allocation for methods is not issue at all, just like methods below 5 bytes.

You can change stack allocation for hooked methods, it does not matter as long as you restore stack pointer correctly before return, in other case runtime crash due to jump into invalid memory offset.
(return opcode is actually pop and jmp, call is push and jmp, i abused this feature to implement indirect invocation - last method of ABS hook)

Methods below 5 bytes ALWAYS inlined (only exception is noinline flag), you can't hook them  anyway, ever if you manage to hook them, this won't have any effect with exception to reflection based invocations.

And yes, methods can be very large, especially methods with exception handling (also they have return opcode in unexpected locations)
Title: Re: New code injection method
Post by: Micktu on August 10, 2016, 09:02:18 AM
>I never hit any issues with recursive calling, i need some kind of example to implement a fix.
It's obvious: I check for return address. If you Invoke, the return address is not the function you're checking against, but mono_runtime_invoke's address. Therefore your function gets called again and you crash with stack overflow.

>Also stack allocation for methods is not issue at all, just like methods below 5 bytes.
It's not, reliable detection of what can be copied and replaced is. It's not trivial unless you write your own disassembler thing to analyze the instructions. Which is a good solution, but it increases complexity manyfold.

>And yes, methods can be very large, especially methods with exception handling (also they have return opcode in unexpected locations)
What I meant is that I assumed that methods that do not make use of stack variables are usually small, and this turns out to not be true.



Title: Re: New code injection method
Post by: Micktu on August 10, 2016, 09:09:11 AM
Anyway, the current working example is now up on workshop, imma cross my fingers and hope it works for everyone. The source is at https://github.com/micktu/RimWorld-BuildProductive (https://github.com/micktu/RimWorld-BuildProductive) if you want to take a look at usage.

Another problem I stumbled upon is that sometimes hooking into certain methods ends up in segfault on stack allocation for no apparent reason in random places within execution chain. E.g you can't hook to Designator.DesignateThing(), it's called from a Command's delegate, and I think it happens when the hooked method is called from within a delegate. Hope I arrive at a better explanation later. Ah, and it only happens with x64 compiler. I'll try some things like adding prolog to such functions, or maybe try to wrap them in a delegate of my own and see if it works.
Title: Re: New code injection method
Post by: RawCode on August 10, 2016, 08:50:07 PM
QuoteIt's obvious: I check for return address. If you Invoke, the return address is not the function you're checking against, but mono_runtime_invoke's address. Therefore your function gets called again and you crash with stack overflow.

its your fault, same will happen if you manage to hook constructor or allocator, if pointer does not belong to managed method, you should not modify it.
Title: Re: New code injection method
Post by: Micktu on August 11, 2016, 07:27:52 AM
So there have been dozens of new installs, and nobody complained yet, so I assume the method is working an stable. Feel free to use it people, it's the Injection folder in the repo.

I'll make it handle invokes properly later.