• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

SamboyCoding / Cpp2IL / 25641285720

10 May 2026 10:18PM UTC coverage: 35.104% (-0.2%) from 35.33%
25641285720

Pull #542

github

web-flow
Merge 9249bcb5b into 6af99f218
Pull Request #542: Remove static mutable state from LibCpp2IL 2: Electric Boogaloo

1877 of 6693 branches covered (28.04%)

Branch coverage included in aggregate %.

303 of 569 new or added lines in 66 files covered. (53.25%)

12 existing lines in 11 files now uncovered.

4394 of 11171 relevant lines covered (39.33%)

268486.35 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/Cpp2IL.Core/Il2CppApiFunctions/BaseKeyFunctionAddresses.cs
1
using System.Collections.Generic;
2
using System.Diagnostics.CodeAnalysis;
3
using System.Linq;
4
using System.Runtime.CompilerServices;
5
using Cpp2IL.Core.Logging;
6
using Cpp2IL.Core.Model.Contexts;
7
using Cpp2IL.Core.Utils;
8
using Iced.Intel;
9
using LibCpp2IL;
10
using LibCpp2IL.Reflection;
11

12
namespace Cpp2IL.Core.Il2CppApiFunctions;
13

14
[SuppressMessage("ReSharper", "InconsistentNaming")]
15
public abstract class BaseKeyFunctionAddresses
16
{
17
    public ulong il2cpp_codegen_initialize_method; //Either this
18
    public ulong il2cpp_codegen_initialize_runtime_metadata; //Or this, are present, depending on metadata version, but not exported.
19
    public ulong il2cpp_vm_metadatacache_initializemethodmetadata; //This is thunked from the above (but only pre-27?)
20
    public ulong il2cpp_runtime_class_init_export; //Api function (exported)
21
    public ulong il2cpp_runtime_class_init_actual; //Thunked from above
22
    public ulong il2cpp_object_new; //Api Function (exported)
23
    public ulong il2cpp_vm_object_new; //Thunked from above
24
    public ulong il2cpp_codegen_object_new; //Thunked TO above
25
    public ulong il2cpp_array_new_specific; //Api function (exported)
26
    public ulong il2cpp_vm_array_new_specific; //Thunked from above
27
    public ulong SzArrayNew; //Thunked TO above.
28
    public ulong il2cpp_type_get_object; //Api function (exported)
29
    public ulong il2cpp_vm_reflection_get_type_object; //Thunked from above
30
    public ulong il2cpp_resolve_icall; //Api function (exported)
31
    public ulong InternalCalls_Resolve; //Thunked from above.
32

33
    public ulong il2cpp_string_new; //Api function (exported)
34
    public ulong il2cpp_vm_string_new; //Thunked from above
35
    public ulong il2cpp_string_new_wrapper; //Api function
36
    public ulong il2cpp_vm_string_newWrapper; //Thunked from above
37
    public ulong il2cpp_codegen_string_new_wrapper; //Not sure if actual name, used in ARM64 attribute gens, thunks TO above.
38

39
    public ulong il2cpp_value_box; //Api function (exported)
40
    public ulong il2cpp_vm_object_box; //Thunked from above
41

42
    public ulong il2cpp_object_unbox; //Api function
43
    public ulong il2cpp_vm_object_unbox; //Thunked from above
44

45
    public ulong il2cpp_raise_exception; //Api function (exported)
46
    public ulong il2cpp_vm_exception_raise; //Thunked from above
47
    public ulong il2cpp_codegen_raise_exception; //Thunked TO above. don't know real name.
48

49
    public ulong il2cpp_vm_object_is_inst; //Not exported, not thunked. Can be located via the Type#IsInstanceOfType icall.
50

51
    public ulong AddrPInvokeLookup; //TODO Re-find this and fix name
52

53
    public IEnumerable<KeyValuePair<string, ulong>> Pairs => resolvedAddressMap;
×
54

55
    protected ApplicationAnalysisContext _appContext = null!; //Always initialized before used
56

57
    protected LibCpp2IlReflectionCache ReflectionCache =>
NEW
58
        _appContext.LibCpp2IlContext.ReflectionCache;
×
59

60
    private readonly Dictionary<string, ulong> resolvedAddressMap = [];
×
61
    private readonly HashSet<ulong> resolvedAddressSet = [];
×
62

63
    public bool IsKeyFunctionAddress(ulong address)
64
    {
65
        return address != 0 && resolvedAddressSet.Contains(address);
×
66
    }
67

68
    private void FindExport(string name, out ulong ptr)
69
    {
70
        Logger.Verbose($"\tLooking for Exported {name} function...");
×
71
        ptr = _appContext.Binary.GetVirtualAddressOfExportedFunctionByName(name);
×
72

73
        Logger.VerboseNewline(ptr == 0 ? "Not found" : $"Found at 0x{ptr:X}");
×
74
    }
×
75

76
    public virtual void Find(ApplicationAnalysisContext applicationAnalysisContext)
77
    {
78
        _appContext = applicationAnalysisContext;
×
79
        Init(applicationAnalysisContext);
×
80

81
        //Try to find System.Exception (should always be there)
82
        if (applicationAnalysisContext.Binary.InstructionSetId == DefaultInstructionSets.X86_32 || applicationAnalysisContext.Binary.InstructionSetId == DefaultInstructionSets.X86_64)
×
83
            //TODO make this abstract and implement in subclasses.
84
            TryGetInitMetadataFromException();
×
85

86
        //New Object
87
        FindExport("il2cpp_object_new", out il2cpp_object_new);
×
88

89
        //Type => Object
90
        FindExport("il2cpp_type_get_object", out il2cpp_type_get_object);
×
91

92
        //Resolve ICall
93
        FindExport("il2cpp_resolve_icall", out il2cpp_resolve_icall);
×
94

95
        //New String
96
        FindExport("il2cpp_string_new", out il2cpp_string_new);
×
97

98
        //New string wrapper
99
        FindExport("il2cpp_string_new_wrapper", out il2cpp_string_new_wrapper);
×
100

101
        //Box Value
102
        FindExport("il2cpp_value_box", out il2cpp_value_box);
×
103

104
        //Unbox Value
105
        FindExport("il2cpp_object_unbox", out il2cpp_object_unbox);
×
106

107
        //Raise Exception
108
        FindExport("il2cpp_raise_exception", out il2cpp_raise_exception);
×
109

110
        //Class Init
111
        FindExport("il2cpp_runtime_class_init", out il2cpp_runtime_class_init_export);
×
112

113
        //New array of fixed size
114
        FindExport("il2cpp_array_new_specific", out il2cpp_array_new_specific);
×
115

116
        //Object IsInst
117
        il2cpp_vm_object_is_inst = GetObjectIsInstFromSystemType();
×
118

119
        AttemptInstructionAnalysisToFillGaps();
×
120

121
        FindThunks();
×
122
        InitializeResolvedAddresses();
×
123
    }
×
124

125
    protected void TryGetInitMetadataFromException()
126
    {
127
        //Exception.get_Message() - first call is either to codegen_initialize_method (< v27) or codegen_initialize_runtime_metadata
128
        Logger.VerboseNewline("\tLooking for Type System.Exception, Method get_Message...");
×
129

NEW
130
        var type = ReflectionCache.GetType("Exception", "System")!;
×
131
        Logger.VerboseNewline("\t\tType Located. Ensuring method exists...");
×
132
        var targetMethod = type.Methods!.FirstOrDefault(m => m.Name == "get_Message");
×
133
        if (targetMethod != null) //Check struct contains valid data 
×
134
        {
135
            Logger.VerboseNewline($"\t\tTarget Method Located at {targetMethod.MethodPointer}. Taking first CALL as the (version-specific) metadata initialization function...");
×
136

NEW
137
            var disasm = X86Utils.GetMethodBodyAtVirtAddressNew(targetMethod.MethodPointer, false, _appContext.Binary);
×
138
            var calls = disasm.Where(i => i.Mnemonic == Mnemonic.Call).ToList();
×
139

140
            if (calls.Count == 0)
×
141
            {
142
                Logger.WarnNewline("Couldn't find any call instructions in the method body. This is not expected. Will not have metadata initialization function.");
×
143
                return;
×
144
            }
145

146
            if (_appContext.MetadataVersion < 27)
×
147
            {
148
                il2cpp_codegen_initialize_method = calls.First().NearBranchTarget;
×
149
                Logger.VerboseNewline($"\t\til2cpp_codegen_initialize_method => 0x{il2cpp_codegen_initialize_method:X}");
×
150
            }
151
            else
152
            {
153
                il2cpp_codegen_initialize_runtime_metadata = calls.First().NearBranchTarget;
×
154
                Logger.VerboseNewline($"\t\til2cpp_codegen_initialize_runtime_metadata => 0x{il2cpp_codegen_initialize_runtime_metadata:X}");
×
155
            }
156
        }
157
    }
×
158

159
    protected virtual void AttemptInstructionAnalysisToFillGaps()
160
    {
161
    }
×
162

163
    private void FindThunks()
164
    {
165
        if (il2cpp_object_new != 0)
×
166
        {
167
            Logger.Verbose("\t\tMapping il2cpp_object_new to vm::Object::New...");
×
168
            il2cpp_vm_object_new = FindFunctionThisIsAThunkOf(il2cpp_object_new, true);
×
169
            Logger.VerboseNewline($"Found at 0x{il2cpp_vm_object_new:X}");
×
170
        }
171

172
        if (il2cpp_vm_object_new != 0)
×
173
        {
174
            Logger.Verbose("\t\tLooking for il2cpp_codegen_object_new as a thunk of vm::Object::New...");
×
175

176
            var potentialThunks = FindAllThunkFunctions(il2cpp_vm_object_new, 16);
×
177

178
            //Sort by caller count in ascending order
179
            var list = potentialThunks.Select(ptr => (ptr, count: GetCallerCount(ptr))).ToList();
×
180
            list.SortByExtractedKey(pair => pair.count);
×
181

182
            //Sort in descending order - most called first
183
            list.Reverse();
×
184

185
            //Take first as the target
186
            il2cpp_codegen_object_new = list.FirstOrDefault().ptr;
×
187

188
            Logger.VerboseNewline($"Found at 0x{il2cpp_codegen_object_new:X}");
×
189
        }
190

191
        if (il2cpp_type_get_object != 0)
×
192
        {
193
            Logger.Verbose("\t\tMapping il2cpp_resolve_icall to Reflection::GetTypeObject...");
×
194
            il2cpp_vm_reflection_get_type_object = FindFunctionThisIsAThunkOf(il2cpp_type_get_object);
×
195
            Logger.VerboseNewline($"Found at 0x{il2cpp_vm_reflection_get_type_object:X}");
×
196
        }
197

198
        if (il2cpp_resolve_icall != 0)
×
199
        {
200
            Logger.Verbose("\t\tMapping il2cpp_resolve_icall to InternalCalls::Resolve...");
×
201
            InternalCalls_Resolve = FindFunctionThisIsAThunkOf(il2cpp_resolve_icall);
×
202
            Logger.VerboseNewline($"Found at 0x{InternalCalls_Resolve:X}");
×
203
        }
204

205
        if (il2cpp_string_new != 0)
×
206
        {
207
            Logger.Verbose("\t\tMapping il2cpp_string_new to String::New...");
×
208
            il2cpp_vm_string_new = FindFunctionThisIsAThunkOf(il2cpp_string_new);
×
209
            Logger.VerboseNewline($"Found at 0x{il2cpp_vm_string_new:X}");
×
210
        }
211

212
        if (il2cpp_string_new_wrapper != 0)
×
213
        {
214
            Logger.Verbose("\t\tMapping il2cpp_string_new_wrapper to String::NewWrapper...");
×
215
            il2cpp_vm_string_newWrapper = FindFunctionThisIsAThunkOf(il2cpp_string_new_wrapper);
×
216
            Logger.VerboseNewline($"Found at 0x{il2cpp_vm_string_newWrapper:X}");
×
217
        }
218

219
        if (il2cpp_vm_string_newWrapper != 0)
×
220
        {
221
            Logger.Verbose("\t\tMapping String::NewWrapper to il2cpp_codegen_string_new_wrapper...");
×
222
            il2cpp_codegen_string_new_wrapper = FindAllThunkFunctions(il2cpp_vm_string_newWrapper, 0, il2cpp_string_new_wrapper).FirstOrDefault();
×
223
            Logger.VerboseNewline($"Found at 0x{il2cpp_codegen_string_new_wrapper:X}");
×
224
        }
225

226
        if (il2cpp_value_box != 0)
×
227
        {
228
            Logger.Verbose("\t\tMapping il2cpp_value_box to Object::Box...");
×
229
            il2cpp_vm_object_box = FindFunctionThisIsAThunkOf(il2cpp_value_box);
×
230
            Logger.VerboseNewline($"Found at 0x{il2cpp_vm_object_box:X}");
×
231
        }
232

233
        if (il2cpp_object_unbox != 0)
×
234
        {
235
            Logger.Verbose("\t\tMapping il2cpp_object_unbox to Object::Unbox...");
×
236
            il2cpp_vm_object_unbox = FindFunctionThisIsAThunkOf(il2cpp_object_unbox);
×
237
            Logger.VerboseNewline($"Found at 0x{il2cpp_vm_object_unbox:X}");
×
238
        }
239

240
        if (il2cpp_raise_exception != 0)
×
241
        {
242
            Logger.Verbose("\t\tMapping il2cpp_raise_exception to il2cpp::vm::Exception::Raise...");
×
243
            il2cpp_vm_exception_raise = FindFunctionThisIsAThunkOf(il2cpp_raise_exception, true);
×
244
            Logger.VerboseNewline($"Found at 0x{il2cpp_vm_exception_raise:X}");
×
245
        }
246

247
        if (il2cpp_vm_exception_raise != 0)
×
248
        {
249
            Logger.Verbose("\t\tMapping il2cpp::vm::Exception::Raise to il2cpp_codegen_raise_exception...");
×
250
            il2cpp_codegen_raise_exception = FindAllThunkFunctions(il2cpp_vm_exception_raise, 4, il2cpp_raise_exception).FirstOrDefault();
×
251
            Logger.VerboseNewline($"Found at 0x{il2cpp_codegen_raise_exception:X}");
×
252
        }
253

254
        if (il2cpp_runtime_class_init_export != 0)
×
255
        {
256
            Logger.Verbose("\t\tMapping il2cpp_runtime_class_init to il2cpp:vm::Runtime::ClassInit...");
×
257
            il2cpp_runtime_class_init_actual = FindFunctionThisIsAThunkOf(il2cpp_runtime_class_init_export);
×
258
            Logger.VerboseNewline($"Found at 0x{il2cpp_runtime_class_init_actual:X}");
×
259
        }
260

261
        if (il2cpp_array_new_specific != 0)
×
262
        {
263
            Logger.Verbose("\t\tMapping il2cpp_array_new_specific to vm::Array::NewSpecific...");
×
264
            il2cpp_vm_array_new_specific = FindFunctionThisIsAThunkOf(il2cpp_array_new_specific);
×
265
            Logger.VerboseNewline($"Found at 0x{il2cpp_vm_array_new_specific:X}");
×
266
        }
267

268
        if (il2cpp_vm_array_new_specific != 0)
×
269
        {
270
            Logger.Verbose("\t\tLooking for SzArrayNew as a thunk function proxying Array::NewSpecific...");
×
271
            SzArrayNew = FindAllThunkFunctions(il2cpp_vm_array_new_specific, 4, il2cpp_array_new_specific).FirstOrDefault();
×
272
            Logger.VerboseNewline($"Found at 0x{SzArrayNew:X}");
×
273
        }
274
    }
×
275

276
    protected abstract ulong GetObjectIsInstFromSystemType();
277

278
    /// <summary>
279
    /// Given a function at addr, find a function which serves no purpose other than to call addr.
280
    /// </summary>
281
    /// <param name="addr">The address of the function to call.</param>
282
    /// <param name="maxBytesBack">The maximum number of bytes to go back from any branching instructions to find the actual start of the thunk function.</param>
283
    /// <param name="addressesToIgnore">A list of function addresses which this function must not return</param>
284
    /// <returns>The address of the first function in the file which thunks addr, starts within maxBytesBack bytes of the branch, and is not contained within addressesToIgnore, else 0 if none can be found.</returns>
285
    protected abstract IEnumerable<ulong> FindAllThunkFunctions(ulong addr, uint maxBytesBack = 0, params ulong[] addressesToIgnore);
286

287
    /// <summary>
288
    /// Given a function at thunkPtr, return the address of the function that said function exists only to call.
289
    /// That is, given a function which performs no meaningful operations other than to call x, return the address of x.
290
    /// </summary>
291
    /// <param name="thunkPtr">The address of the thunk function</param>
292
    /// <param name="prioritiseCall">True to prioritise "call" statements - conditional flow transfer - over "jump" statements - unconditional flow transfer. False for the inverse.</param>
293
    /// <returns>The address of the thunked function, if it can be found, else 0</returns>
294
    protected abstract ulong FindFunctionThisIsAThunkOf(ulong thunkPtr, bool prioritiseCall = false);
295

296
    protected abstract int GetCallerCount(ulong toWhere);
297

298
    protected virtual void Init(ApplicationAnalysisContext context)
299
    {
NEW
300
        _appContext = context;
×
UNCOV
301
    }
×
302

303
    private void InitializeResolvedAddresses()
304
    {
305
        resolvedAddressMap.Clear();
×
306
        resolvedAddressSet.Clear();
×
307

308
        AddResolved(il2cpp_codegen_initialize_method);
×
309
        AddResolved(il2cpp_codegen_initialize_runtime_metadata);
×
310
        AddResolved(il2cpp_vm_metadatacache_initializemethodmetadata);
×
311
        AddResolved(il2cpp_runtime_class_init_export);
×
312
        AddResolved(il2cpp_runtime_class_init_actual);
×
313
        AddResolved(il2cpp_object_new);
×
314
        AddResolved(il2cpp_vm_object_new);
×
315
        AddResolved(il2cpp_codegen_object_new);
×
316
        AddResolved(il2cpp_array_new_specific);
×
317
        AddResolved(il2cpp_vm_array_new_specific);
×
318
        AddResolved(SzArrayNew);
×
319
        AddResolved(il2cpp_type_get_object);
×
320
        AddResolved(il2cpp_vm_reflection_get_type_object);
×
321
        AddResolved(il2cpp_resolve_icall);
×
322
        AddResolved(InternalCalls_Resolve);
×
323

324
        AddResolved(il2cpp_string_new);
×
325
        AddResolved(il2cpp_vm_string_new);
×
326
        AddResolved(il2cpp_string_new_wrapper);
×
327
        AddResolved(il2cpp_vm_string_newWrapper);
×
328
        AddResolved(il2cpp_codegen_string_new_wrapper);
×
329

330
        AddResolved(il2cpp_value_box);
×
331
        AddResolved(il2cpp_vm_object_box);
×
332

333
        AddResolved(il2cpp_object_unbox);
×
334
        AddResolved(il2cpp_vm_object_unbox);
×
335

336
        AddResolved(il2cpp_raise_exception);
×
337
        AddResolved(il2cpp_vm_exception_raise);
×
338
        AddResolved(il2cpp_codegen_raise_exception);
×
339

340
        AddResolved(il2cpp_vm_object_is_inst);
×
341

342
        AddResolved(AddrPInvokeLookup);
×
343

344
        void AddResolved(ulong address, [CallerArgumentExpression(nameof(address))] string name = "")
345
        {
346
            resolvedAddressSet.Add(address);
×
347
            resolvedAddressMap[name] = address;
×
348
        }
×
349
    }
×
350
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc