// See: IL2CPP/Il2CppInspector.cs public void PostProcessPackage(Il2CppInspector.Il2CppInspector package, PluginPostProcessPackageEventInfo data) { // This is called once per IL2CPP binary after it has been merged with global-metadata.dat // and all of the data linked together // It contains surrogate properties to everything in Metadata and Il2CppBinary // plus all calculated default field values (FieldDefaultValue), default parameter values (ParameterDefaultValue), // field offsets (FieldOffsets), custom attribute generators (CustomAttributeGenerators), // function addresses (FunctionAddresses) and metadata usages (MetadataUsages) etc. // Set data.IsDataModified if you modify the Il2CppInspector }
// A "package" is the combination of global-metadata.dat, the application binary, // and some analysis which links them both together into a single unit, the Il2CppInspector object. // This executes after all the low-level processing and analysis of the application is completed, // but before any higher-level abstractions are created, such as the .NET type model or C++ application model. // Therefore this is a good place to make any final changes to the data that the high level models and output modules will rely on // In this case, we are going to acquire all of the string literals that we deferred earlier public void PostProcessPackage(Il2CppInspector.Il2CppInspector package, PluginPostProcessPackageEventInfo data) { // Don't do anything if this isn't for us if (!IsOurs) { return; } // Tell the user what is happening in case it takes a while PluginServices.For(this).StatusUpdate("Decrypting string literals"); // Calculate the number of string literals // This calculation depends on being able to scan MetadataUsages for all of the StringLiteral uses // and finding the one with the highest index. The creation of MetadataUsages requires data // from both global-metadata.dat and the application binary; this data is merged together // when the Il2CppInspector object is initialized, so this is the earliest opportunity we have to examine it var stringLiteralCount = package.MetadataUsages.Where(u => u.Type == MetadataUsageType.StringLiteral).Max(u => u.SourceIndex) + 1; // Create a delegate which internally is a function pointer to the GetStringLiteralFromIndex function in the DLL var pGetStringLiteralFromIndex = (GetStringLiteralFromIndex) Marshal.GetDelegateForFunctionPointer(ModuleBase + Offsets[game.Value].GetStringLiteralFromIndex, typeof(GetStringLiteralFromIndex)); var stringLiterals = new List <string>(); var length = 0; // For each index, call the delegate with the decrypted metadata byte array, index and a pointer as arguments // In this case, the function returns an array of UTF8-encoded characters, // and populates 'length' with the number of bytes returned for (uint index = 0; index < stringLiteralCount; index++) { var decryptedBytesUnmanaged = pGetStringLiteralFromIndex(metadataBlob, index, ref length); var str = new byte[length]; Marshal.Copy(decryptedBytesUnmanaged, str, 0, length); stringLiterals.Add(Encoding.UTF8.GetString(str)); } // If we had used IGetStringLiterals above, we would have set data.StringLiterals, // but here we modify the package (the Il2CppInspector object) directly instead package.Metadata.StringLiterals = stringLiterals.ToArray(); // We don't set FullyProcessed so that other plugins can perform further post-processing modifications // IsDataModified tells Il2CppInspector that the contents of its internal data structures have been changed // Note this is different from IsStreamModified; changing the data in memory // does not automatically rewrite the stream. data.IsDataModified = true; }