Reproduced in this article is strictly forbidden.
previous methods and their limitations
The problem background and initial attempts are shown here. The initial idea is simple, just think of using the Postprocessbuild event to inject a. NET assembly in the prepared local engineering file (IOS or Android). However, this is much more restrictive. First, it is not possible to inject il2cpp as a case of Scripting backend. Because this event is triggered, there is no. NET assembly in the local project file, only C + + code and cannot be injected with Cecil. Second, the Android platform, with MONO2X as the Scripting backend case, also needs to be packaged for Android Studio Project to use. For cases that are packaged directly into the APK, it is not easy to inject (unless you use unpacking, injecting, re-signing the packaging method, which is more cumbersome). Third, the IOS platform, which uses mono2x as the Scripting backend, is also unsuccessful. This is because packaging on the IOS platform requires a step called AOT Cross compiling, which generates a corresponding. dll.s file for all assemblies. The information contained in these files is verified at run time, and if I tamper with the assembly without ignoring the. dll.s file, it will be error-free at run time. The error message is similara script behaviour (probably XXX?) has a different seralization layout when loading. (Read * * bytes but expected * * bytes)did you #ifdef unity_editor a sections of your serialized properties in any of your scripts?Where XXX is the. NET script name, and two sets of asterisks represent two different values. This error eventually causes the script to fail to load and not run the game. Unlike the error message description, I did not write any conditional compilation code on the offending script. To solve this problem, it is estimated that the need to tamper with the. dll.s file is still very economical.
ways to tamper with the compiler
The next way to do this is to tamper with Unity's C # compiler Mcs.exe. I did not go into the experiment because a few simple experiments took more than a day to spend. I mainly tried two methods, of course, did not succeed. Method One, rename the original Mcs.exe (such as Mcs1.exe), and then write a. NET console application, occupy the original Mcs.exe position, in which the System.Diagnostic.Process class to start Mcs1.exe. In this process, I've made a number of permutations of some of the configuration of a process object, such as the environment variable (environmentvariables property), the input-output redirection (Redirectstandardxxx property), and still can't call it correctly Mcs1.exe, let's not say what happened after the call. Method Two, inject the code directly into the Mcs.exe. Because Mcs.exe is also a. NET application and appears to be without confusion, direct injection is possible. That is, "inject code into the game assembly into the compiler." "The main problem with this is that the Mcs.exe output directory is a temporary folder and there is no guarantee that there are assemblies that we rely on, such as those that need to be parsed with Mono.cecil Defaultassemblyresolver when writing to an assembly after injection."
inject by Postprocessscene callback event
Unity does not provide callbacks between the execution of Mcs.exe and subsequent steps (Il2cpp, Android packaged apk, AOT Cross compilation on IOS, etc.), but the callback event Onpostprocessscene is currently guaranteed to fire at least once between them. Thanks to Https://github.com/rayosu/UnityDllInjector reminds me. Handling DLLs In this event callback can theoretically be injected effectively on any platform, any Scripting backend. There are several key points to note in the implementation process:
- The event Onpostprocessscene corresponds to the number of packages specified in the Build Settings, so it may be executed several times and therefore need to be prevented from duplication. In addition to the methods provided in the unitydllinjector above, you can also write the injection tag directly into your target assembly. However, it is worth noting that the addition of an empty class for tagging is not good for IOS + mono2x, and guessing is also related to AOT cross-compiling. One of the insurance practices is to keep several bool constants in the game code, with a value of false, check the appropriate values before injecting, or inject if true. After the injection is complete, tamper with the corresponding bool constant to TRUE.
- The corresponding assembly for the game script must be in the Scriptassemblies folder under the same Library as the Assets peer when injected, but be aware of the Unity assemblies you rely on. I use the method provided by Unitydllinjector and still cannot guarantee to get to the required assembly. I ended up using Editorapplication.applicationcontentspath to get the Unity installation directory and find the necessary assemblies in the Data/managed directory.
Currently I have tested Android + mono/il2cpp and IOS + il2cpp, no problem. IOS + mono2x may have some problems with the Xcode link phase due to some problems with our project itself.
Performance test with Mono.cecil assisted Unity3d hand Tour (cont.)