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

SamboyCoding / Cpp2IL / 30166321939

25 Jul 2026 04:47PM UTC coverage: 37.297% (-0.2%) from 37.493%
30166321939

push

github

SamboyCoding
Decompiler: Find and remove write barriers

2875 of 8674 branches covered (33.15%)

Branch coverage included in aggregate %.

0 of 54 new or added lines in 4 files covered. (0.0%)

2 existing lines in 1 file now uncovered.

5271 of 13167 relevant lines covered (40.03%)

167893.48 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 il2cpp_codegen_write_barrier; //Not exported, not thunked. Located via corlib methods which store a reference into a field. Zero if the build has write barriers disabled.
52

53
    public ulong AddrPInvokeLookup; //TODO Re-find this and fix name
54

55
    public IEnumerable<KeyValuePair<string, ulong>> Pairs => resolvedAddressMap;
×
56

57
    protected ApplicationAnalysisContext _appContext = null!; //Always initialized before used
58

59
    protected LibCpp2IlReflectionCache ReflectionCache =>
60
        _appContext.LibCpp2IlContext.ReflectionCache;
×
61

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

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

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

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

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

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

88
        //New Object
89
        FindExport("il2cpp_object_new", out il2cpp_object_new);
×
90

91
        //Type => Object
92
        FindExport("il2cpp_type_get_object", out il2cpp_type_get_object);
×
93

94
        //Resolve ICall
95
        FindExport("il2cpp_resolve_icall", out il2cpp_resolve_icall);
×
96

97
        //New String
98
        FindExport("il2cpp_string_new", out il2cpp_string_new);
×
99

100
        //New string wrapper
101
        FindExport("il2cpp_string_new_wrapper", out il2cpp_string_new_wrapper);
×
102

103
        //Box Value
104
        FindExport("il2cpp_value_box", out il2cpp_value_box);
×
105

106
        //Unbox Value
107
        FindExport("il2cpp_object_unbox", out il2cpp_object_unbox);
×
108

109
        //Raise Exception
110
        FindExport("il2cpp_raise_exception", out il2cpp_raise_exception);
×
111

112
        //Class Init
113
        FindExport("il2cpp_runtime_class_init", out il2cpp_runtime_class_init_export);
×
114

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

118
        //Object IsInst
119
        il2cpp_vm_object_is_inst = GetObjectIsInstFromSystemType();
×
120

121
        //GC write barrier
NEW
122
        il2cpp_codegen_write_barrier = GetWriteBarrier();
×
123

UNCOV
124
        AttemptInstructionAnalysisToFillGaps();
×
125

126
        FindThunks();
×
127
        InitializeResolvedAddresses();
×
128
    }
×
129

130
    protected void TryGetInitMetadataFromException()
131
    {
132
        //Exception.get_Message() - first call is either to codegen_initialize_method (< v27) or codegen_initialize_runtime_metadata
133
        Logger.VerboseNewline("\tLooking for Type System.Exception, Method get_Message...");
×
134

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

142
            var disasm = X86Utils.GetMethodBodyAtVirtAddressNew(targetMethod.MethodPointer, false, _appContext.Binary);
×
143
            var calls = disasm.Where(i => i.Mnemonic == Mnemonic.Call).ToList();
×
144

145
            if (calls.Count == 0)
×
146
            {
147
                Logger.WarnNewline("Couldn't find any call instructions in the method body. This is not expected. Will not have metadata initialization function.");
×
148
                return;
×
149
            }
150

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

164
    protected virtual void AttemptInstructionAnalysisToFillGaps()
165
    {
166
    }
×
167

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

177
        if (il2cpp_vm_object_new != 0)
×
178
        {
179
            Logger.Verbose("\t\tLooking for il2cpp_codegen_object_new as a thunk of vm::Object::New...");
×
180

181
            var potentialThunks = FindAllThunkFunctions(il2cpp_vm_object_new, 16);
×
182

183
            //Sort by caller count in ascending order
184
            var list = potentialThunks.Select(ptr => (ptr, count: GetCallerCount(ptr))).ToList();
×
185
            list.SortByExtractedKey(pair => pair.count);
×
186

187
            //Sort in descending order - most called first
188
            list.Reverse();
×
189

190
            //Take first as the target
191
            il2cpp_codegen_object_new = list.FirstOrDefault().ptr;
×
192

193
            Logger.VerboseNewline($"Found at 0x{il2cpp_codegen_object_new:X}");
×
194
        }
195

196
        if (il2cpp_type_get_object != 0)
×
197
        {
198
            Logger.Verbose("\t\tMapping il2cpp_resolve_icall to Reflection::GetTypeObject...");
×
199
            il2cpp_vm_reflection_get_type_object = FindFunctionThisIsAThunkOf(il2cpp_type_get_object);
×
200
            Logger.VerboseNewline($"Found at 0x{il2cpp_vm_reflection_get_type_object:X}");
×
201
        }
202

203
        if (il2cpp_resolve_icall != 0)
×
204
        {
205
            Logger.Verbose("\t\tMapping il2cpp_resolve_icall to InternalCalls::Resolve...");
×
206
            InternalCalls_Resolve = FindFunctionThisIsAThunkOf(il2cpp_resolve_icall);
×
207
            Logger.VerboseNewline($"Found at 0x{InternalCalls_Resolve:X}");
×
208
        }
209

210
        if (il2cpp_string_new != 0)
×
211
        {
212
            Logger.Verbose("\t\tMapping il2cpp_string_new to String::New...");
×
213
            il2cpp_vm_string_new = FindFunctionThisIsAThunkOf(il2cpp_string_new);
×
214
            Logger.VerboseNewline($"Found at 0x{il2cpp_vm_string_new:X}");
×
215
        }
216

217
        if (il2cpp_string_new_wrapper != 0)
×
218
        {
219
            Logger.Verbose("\t\tMapping il2cpp_string_new_wrapper to String::NewWrapper...");
×
220
            il2cpp_vm_string_newWrapper = FindFunctionThisIsAThunkOf(il2cpp_string_new_wrapper);
×
221
            Logger.VerboseNewline($"Found at 0x{il2cpp_vm_string_newWrapper:X}");
×
222
        }
223

224
        if (il2cpp_vm_string_newWrapper != 0)
×
225
        {
226
            Logger.Verbose("\t\tMapping String::NewWrapper to il2cpp_codegen_string_new_wrapper...");
×
227
            il2cpp_codegen_string_new_wrapper = FindAllThunkFunctions(il2cpp_vm_string_newWrapper, 0, il2cpp_string_new_wrapper).FirstOrDefault();
×
228
            Logger.VerboseNewline($"Found at 0x{il2cpp_codegen_string_new_wrapper:X}");
×
229
        }
230

231
        if (il2cpp_value_box != 0)
×
232
        {
233
            Logger.Verbose("\t\tMapping il2cpp_value_box to Object::Box...");
×
234
            il2cpp_vm_object_box = FindFunctionThisIsAThunkOf(il2cpp_value_box);
×
235
            Logger.VerboseNewline($"Found at 0x{il2cpp_vm_object_box:X}");
×
236
        }
237

238
        if (il2cpp_object_unbox != 0)
×
239
        {
240
            Logger.Verbose("\t\tMapping il2cpp_object_unbox to Object::Unbox...");
×
241
            il2cpp_vm_object_unbox = FindFunctionThisIsAThunkOf(il2cpp_object_unbox);
×
242
            Logger.VerboseNewline($"Found at 0x{il2cpp_vm_object_unbox:X}");
×
243
        }
244

245
        if (il2cpp_raise_exception != 0)
×
246
        {
247
            Logger.Verbose("\t\tMapping il2cpp_raise_exception to il2cpp::vm::Exception::Raise...");
×
248
            il2cpp_vm_exception_raise = FindFunctionThisIsAThunkOf(il2cpp_raise_exception, true);
×
249
            Logger.VerboseNewline($"Found at 0x{il2cpp_vm_exception_raise:X}");
×
250
        }
251

252
        if (il2cpp_vm_exception_raise != 0)
×
253
        {
254
            Logger.Verbose("\t\tMapping il2cpp::vm::Exception::Raise to il2cpp_codegen_raise_exception...");
×
255
            il2cpp_codegen_raise_exception = FindAllThunkFunctions(il2cpp_vm_exception_raise, 4, il2cpp_raise_exception).FirstOrDefault();
×
256
            Logger.VerboseNewline($"Found at 0x{il2cpp_codegen_raise_exception:X}");
×
257
        }
258

259
        if (il2cpp_runtime_class_init_export != 0)
×
260
        {
261
            Logger.Verbose("\t\tMapping il2cpp_runtime_class_init to il2cpp:vm::Runtime::ClassInit...");
×
262
            il2cpp_runtime_class_init_actual = FindFunctionThisIsAThunkOf(il2cpp_runtime_class_init_export);
×
263
            Logger.VerboseNewline($"Found at 0x{il2cpp_runtime_class_init_actual:X}");
×
264
        }
265

266
        if (il2cpp_array_new_specific != 0)
×
267
        {
268
            Logger.Verbose("\t\tMapping il2cpp_array_new_specific to vm::Array::NewSpecific...");
×
269
            il2cpp_vm_array_new_specific = FindFunctionThisIsAThunkOf(il2cpp_array_new_specific);
×
270
            Logger.VerboseNewline($"Found at 0x{il2cpp_vm_array_new_specific:X}");
×
271
        }
272

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

281
    protected abstract ulong GetObjectIsInstFromSystemType();
282

283
    /// <summary>
284
    /// Locates Il2CppCodeGenWriteBarrier, the GC write barrier emitted after every reference store into a
285
    /// heap object. Returns 0 where it can't be found, including builds which have write barriers disabled.
286
    /// </summary>
NEW
287
    protected virtual ulong GetWriteBarrier() => 0;
×
288

289
    /// <summary>
290
    /// Given a function at addr, find a function which serves no purpose other than to call addr.
291
    /// </summary>
292
    /// <param name="addr">The address of the function to call.</param>
293
    /// <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>
294
    /// <param name="addressesToIgnore">A list of function addresses which this function must not return</param>
295
    /// <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>
296
    protected abstract IEnumerable<ulong> FindAllThunkFunctions(ulong addr, uint maxBytesBack = 0, params ulong[] addressesToIgnore);
297

298
    /// <summary>
299
    /// Given a function at thunkPtr, return the address of the function that said function exists only to call.
300
    /// That is, given a function which performs no meaningful operations other than to call x, return the address of x.
301
    /// </summary>
302
    /// <param name="thunkPtr">The address of the thunk function</param>
303
    /// <param name="prioritiseCall">True to prioritise "call" statements - conditional flow transfer - over "jump" statements - unconditional flow transfer. False for the inverse.</param>
304
    /// <returns>The address of the thunked function, if it can be found, else 0</returns>
305
    protected abstract ulong FindFunctionThisIsAThunkOf(ulong thunkPtr, bool prioritiseCall = false);
306

307
    protected abstract int GetCallerCount(ulong toWhere);
308

309
    protected virtual void Init(ApplicationAnalysisContext context)
310
    {
311
        _appContext = context;
×
312
    }
×
313

314
    private void InitializeResolvedAddresses()
315
    {
316
        resolvedAddressMap.Clear();
×
317
        resolvedAddressSet.Clear();
×
318

319
        AddResolved(il2cpp_codegen_initialize_method);
×
320
        AddResolved(il2cpp_codegen_initialize_runtime_metadata);
×
321
        AddResolved(il2cpp_vm_metadatacache_initializemethodmetadata);
×
322
        AddResolved(il2cpp_runtime_class_init_export);
×
323
        AddResolved(il2cpp_runtime_class_init_actual);
×
324
        AddResolved(il2cpp_object_new);
×
325
        AddResolved(il2cpp_vm_object_new);
×
326
        AddResolved(il2cpp_codegen_object_new);
×
327
        AddResolved(il2cpp_array_new_specific);
×
328
        AddResolved(il2cpp_vm_array_new_specific);
×
329
        AddResolved(SzArrayNew);
×
330
        AddResolved(il2cpp_type_get_object);
×
331
        AddResolved(il2cpp_vm_reflection_get_type_object);
×
332
        AddResolved(il2cpp_resolve_icall);
×
333
        AddResolved(InternalCalls_Resolve);
×
334

335
        AddResolved(il2cpp_string_new);
×
336
        AddResolved(il2cpp_vm_string_new);
×
337
        AddResolved(il2cpp_string_new_wrapper);
×
338
        AddResolved(il2cpp_vm_string_newWrapper);
×
339
        AddResolved(il2cpp_codegen_string_new_wrapper);
×
340

341
        AddResolved(il2cpp_value_box);
×
342
        AddResolved(il2cpp_vm_object_box);
×
343

344
        AddResolved(il2cpp_object_unbox);
×
345
        AddResolved(il2cpp_vm_object_unbox);
×
346

347
        AddResolved(il2cpp_raise_exception);
×
348
        AddResolved(il2cpp_vm_exception_raise);
×
349
        AddResolved(il2cpp_codegen_raise_exception);
×
350

351
        AddResolved(il2cpp_vm_object_is_inst);
×
352

NEW
353
        AddResolved(il2cpp_codegen_write_barrier);
×
354

UNCOV
355
        AddResolved(AddrPInvokeLookup);
×
356

357
        void AddResolved(ulong address, [CallerArgumentExpression(nameof(address))] string name = "")
358
        {
359
            resolvedAddressSet.Add(address);
×
360
            resolvedAddressMap[name] = address;
×
361
        }
×
362
    }
×
363
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc