Things you probably shouldn't do: Bending Mono to your will
                    In the last post I described a way
                    to manipulate IL2CPP CLR in a somewhat unorthodox way by hooking into its API. While occasionally
                    useful, it does limit you to only IL2CPP builds, making it impractical. In this post, I will
                    describe how to achieve a similar trick with Mono. I'm assuming you know what CLR is, basics of
                    __dllexport and how C# code is compiled, so if you're fuzzy on these concepts check
                    out previous blog post.
                
Unity and Mono
                    By default, all Unity applications run inside Mono CLR. This allows Unity to quickly compile code
                    and support multiple platforms without complicating things too much. This is true for both builds
                    and Unity Editor. It's a commonly known fact, however there are a few interesting nuances that
                    were needed to make it work. 
                    When a regular (non-Unity) C# executable is launched, it usually immediately loads a pre-installed
                    CLR which is then able to JIT-compile and call application's entry point. 
                    Your Unity projects, however, do not define an entry point (that is to say none of your .cs files have a
                    Main method). Furthermore, all your code is NOT compiled into an executable - it is
                    compiled into a DLL (Assembly-CSharp.dll). 
                    So where does the executable in your builds
                    come from and how does your DLL factor into it?
                    You have, without a doubt, seen contents of a Unity Mono build. I will be using a Windows Standalone
                    build in this example, but builds for all platforms consist of these pieces (some of them may be a
                    little harder to find)
                
 A Windows Standalone build with Mono runtime
                A Windows Standalone build with Mono runtime
            
                    If you read the previous blog post about IL2CPP you may be noticing something familiar -
                    UnityPlayer.dll. The secret sauce that makes Unity such a powerful engine, it is an
                    unmanaged assembly that does most of the heavy lifting in your projects. Just as IL2CPP builds have
                    a Main-ish method that hands execution off to UnityPlayer.dll, so do
                    executables in Mono builds. These executables and dlls aren't unique to your project and can be
                    found in %EditorFolder%/Data/PlaybackEngines/windowsstandalonesupport/Variations folder
                    along with a few other things that are shared between all projects built with the same Unity
                    version.
                
                    Then, of course, there's %ProjectName%_Data folder. It contains all the resources
                    Unity needs to run your game: meshes, textures, sounds and, most importantly, managed assemblies
                    (found in %ProjectName%_Data/Managed folder). This is where your
                    Assembly-CSharp dll resides, but you will also find a lot of other dlls - from various
                    ones that enable Unity functionality to some fundamental ones like mscorlib. Unlike
                    regular C# applications, Unity builds include every managed assembly that is referenced in your
                    project. This is one of two tricks that allows Unity builds to run without requiring users to
                    install .Net or Mono CLR.
                    
                    The other trick is CLR itself. Including all managed assemblies does little if there's no CLR to
                    process them. Unity cannot assume that there is one, so it does the next best thing and ships your
                    builds with one. That is the purpose of MonoBleedingEdge folder, the last piece of the
                    puzzle. Inside there you will find a file called mono-2.0-bdwgc.dll - a Mono CLR.
                    It is worth noting that this is not the same CLR you'd get by running a regular C# application
                    with a mono
                        command. Just like IL2CPP CLR, it is a branch of Mono adapted for a more specific purpose.
                    Unlike IL2CPP CLR though, it resembles the original much more closely with only a few additions here
                    and there.
                    For those curious, "bdwgc" in the filename stands for "Boehm–Demers–Weiser Garbage
                        Collector" - an algorithm used by both Mono and IL2CPP builds. There is a detailed Unity
                        blog post that addresses why this specific GC was chosen and how it impacts your
                    applications, but that is not really relevant to the topic at hand.
                
What can I do with this information?
                    Now that we got theory out of the way let's see how we can use this knowledge in practice. As
                    was the case with IL2CPP, mono-2.0-bdwgc.dll must be exporting some sort of API that is
                    used by UnityPlayer.dll. In IL2CPP builds finding these methods was fairly simple - you
                    just search the entire source code for __dllexport. But how do we find API of an
                    un-managed dll?
                    As discussed in the previous blog post, __dllimports work by linking to target methods
                    at runtime. That means that applications must have some form of a list of methods that need to be
                    linked and list of methods that can be linked to.
                    Enter Dependency Walker - a well-established tool
                    that looks through executable files to find said lists. Drag an executable (managed or un-managed) in
                    there and it will show a tree of dependencies with all of their imports and exports. Be warned
                    though that processing a file can take a bit and Dependency Walker will max out your CPU to the
                    point that you might not be able to do anything else (honestly, I can't help but admire in a
                    strange way its ability to squeeze every last drop of processing power for itself). So what do we
                    get when we drag mono-2.0-bdwgc.dll in there?
                
 mono-2.0-bdwgc.dll exports
                mono-2.0-bdwgc.dll exports
            After about 30 seconds of unresponsiveness the likes of which you may not have seen since Windows 98 you will get something that looks like the picture above. The highlighted area is where you will find the exported methods. For our purposes you will only need function names, so to simplify navigation you can select all exports (Ctrl+A), copy them (Ctrl+C) and paste them in a text editor of your choosing. As you will quickly notice Mono API is quite substantial. Dll from Unity 2020.3 build exports a whopping 1156 methods! As with IL2CPP I will leave these methods for you to discover, but here are some that caught my eye.
                    
                        - mono_compile_method
- mono_gc_walk_heap
- mono_method_print_code
- mono_stack_walk
- mono_threads_request_thread_dump
- mono_unity_domain_mempool_chunk_foreach
- mono_unity_gc_disable / mono_unity_gc_enable
- mono_unity_gc_heap_foreach
- mono_unity_stop_gc_world / mono_unity_start_gc_world
                Getting method signatures right can be a bit tricky. A good place to start is Mono's GitHub repo, but if it's
                    not there you might have to do some reverse engineering or brute-force the problem. Once you have
                    the signature you can DllImport the method into C# code with this little chunk of code
                
[DllImport("mono-2.0-bdwgc", CallingConvention = CallingConvention.Cdecl, EntryPoint = "mono_object_get_size")]
                    public static extern uint GetObjectSize(object obj);
                
                Beware that if you wish to use this trick on Android you need to replace "mono-2.0-bdwgc" with "libmonobdwgc-2.0". As was noted in the previous blog post messing with CLR API can be dangerous and lead to some undesirable behavior. Extreme caution and meticulous testing is advised. API can also change between releases, so be sure to pay extra attention when porting your project to a newer Unity version. It is worth noting that Unity updated their CLR in 2021.2 release with prior releases using Mono 5.11 and latest ones using Mono 6.13.