IL2CPP Internals:

Il2CPP Reverse:

Tutorial:

Adventures:

Honkai Impact:

Iil2cpp codegen add handles

The call to il2cpp_codegen_add handles any operator overloading of + in the expression a + b. IL2CPP_RUNTIME_CLASS_INIT ensures that a class (Console in this case) has executed its static constructor before being used, for example in Console.WriteLine. At the top of the function, we find a boolean check which IL2CPP generates for every static method, again used to ensure that the proper initialization is done before execution proceeds.

So, why does any of this matter?

Obviously if you are reverse-engineering a game, you won’t have the Unity project so you won’t be able to look at the generated C++. But it’s important to remember that even if by some dark magic you achieve a perfect decompilation of the target binary into C++, what you will end up with is very different to the original C# source code. Whereas our idealized C++ version using printf disassembles like this on x64:

; int __cdecl main(int argc, const char **argv, const char **envp)
main proc near
sub     rsp, 28h
mov     edx, 3
lea     rcx, _Format    ; "Hello world: %d\n"
call    printf
xor     eax, eax
add     rsp, 28h
retn
main endp

The IL2CPP version, on the other hand, looks like this:

; void __fastcall Program_Main_m2325437134(Il2CppObject *__this, StringU5BU5D_t1642385972 *___args0, MethodInfo *method)
Program_Main_m2325437134 proc near
push    rbx
sub     rsp, 20h
cmp     cs:s_Il2CppMethodInitialized_8016, 0
jnz     short loc_14038BFF1
mov     ecx, cs:?Program_Main_m2325437134_MetadataUsageId@@3IB
call    ?InitializeMethodMetadata@MetadataCache@vm@il2cpp@@SAXI@Z
mov     cs:s_Il2CppMethodInitialized_8016, 1
loc_14038BFF1:
mov     rcx, cs:?Int32_t2071877448_il2cpp_TypeInfo_var@@3PEAUIl2CppClass@@EA
lea     rdx, [rsp+48h]
mov     dword ptr [rsp+48h], 3
call    ?Box@Object@vm@il2cpp@@SAPEAUIl2CppObject@@PEAUIl2CppClass@@PEAX@Z
mov     rcx, cs:?Console_t2311202731_il2cpp_TypeInfo_var@@3PEAUIl2CppClass@@EA
mov     rbx, rax
test    byte ptr [rcx+10Ah], 1
jz      short loc_14038C02B
cmp     dword ptr [rcx+0BCh], 0
jnz     short loc_14038C02B
call    ?ClassInit@Runtime@vm@il2cpp@@SAXPEAUIl2CppClass@@@Z
loc_14038C02B:
mov     rdx, cs:?_stringLiteral3443654334@@3PEAUString_t2029220233@@EA
xor     r9d, r9d
mov     r8, rbx
xor     ecx, ecx
call    Console_WriteLine_m3776981455
add     rsp, 20h
pop     rbx
retn
Program_Main_m2325437134 endp

and that disassembly is with the symbols included, which you initially won’t have.

Therefore, in order to be able to understand the disassembly of a method in an IL2CPP application, we really have to understand all of the key data structures and internal API calls that IL2CPP provides and manages, so we can cut through all of the boilerplate and drill down into the actual functionality of the application.

To this end, it can be very useful to compile small snippets of C# with IL2CPP and investigate what comes out (cryptographers will recognize this as a sort of known plaintext attack).