IL2CPP: Handle ROT encryption of API exports

This commit is contained in:
Katy Coe
2020-12-10 19:43:45 +01:00
parent 43d736cf03
commit 4e4f794376
4 changed files with 42 additions and 23 deletions

View File

@@ -133,7 +133,7 @@ namespace Il2CppInspector.Cpp.UnityHeaders
var apis = GetAllAPIHeaders().Where(a => a.VersionRange.Intersect(totalRange) != null).ToList(); var apis = GetAllAPIHeaders().Where(a => a.VersionRange.Intersect(totalRange) != null).ToList();
// Get the API exports for the binary // Get the API exports for the binary
var exports = binary.GetAPIExports(); var exports = binary.APIExports;
// No il2cpp exports? Just return the earliest version from the header range // No il2cpp exports? Just return the earliest version from the header range
// The API version may be incorrect but should be a subset of the real API and won't cause C++ compile errors // The API version may be incorrect but should be a subset of the real API and won't cause C++ compile errors

View File

@@ -19,6 +19,9 @@ namespace Il2CppInspector
{ {
public IFileFormatReader Image { get; } public IFileFormatReader Image { get; }
// IL2CPP-only API exports with decrypted names
public Dictionary<string, ulong> APIExports { get; } = new Dictionary<string, ulong>();
public Il2CppCodeRegistration CodeRegistration { get; protected set; } public Il2CppCodeRegistration CodeRegistration { get; protected set; }
public Il2CppMetadataRegistration MetadataRegistration { get; protected set; } public Il2CppMetadataRegistration MetadataRegistration { get; protected set; }
@@ -92,12 +95,14 @@ namespace Il2CppInspector
protected Il2CppBinary(IFileFormatReader stream, EventHandler<string> statusCallback = null) { protected Il2CppBinary(IFileFormatReader stream, EventHandler<string> statusCallback = null) {
Image = stream; Image = stream;
OnStatusUpdate = statusCallback; OnStatusUpdate = statusCallback;
DiscoverAPIExports();
} }
protected Il2CppBinary(IFileFormatReader stream, uint codeRegistration, uint metadataRegistration, EventHandler<string> statusCallback = null) { protected Il2CppBinary(IFileFormatReader stream, uint codeRegistration, uint metadataRegistration, EventHandler<string> statusCallback = null) {
Image = stream; Image = stream;
OnStatusUpdate = statusCallback; OnStatusUpdate = statusCallback;
Configure(codeRegistration, metadataRegistration); DiscoverAPIExports();
PrepareMetadata(codeRegistration, metadataRegistration);
} }
// Load and initialize a binary of any supported architecture // Load and initialize a binary of any supported architecture
@@ -119,7 +124,7 @@ namespace Il2CppInspector
// Load binary without a global-metadata.dat available // Load binary without a global-metadata.dat available
public static Il2CppBinary Load(IFileFormatReader stream, double metadataVersion, EventHandler<string> statusCallback = null) { public static Il2CppBinary Load(IFileFormatReader stream, double metadataVersion, EventHandler<string> statusCallback = null) {
var inst = LoadImpl(stream, statusCallback); var inst = LoadImpl(stream, statusCallback);
return inst.Initialize(metadataVersion) ? inst : null; return inst.FindRegistrationStructs(metadataVersion) ? inst : null;
} }
// Load binary with a global-metadata.dat available // Load binary with a global-metadata.dat available
@@ -129,7 +134,7 @@ namespace Il2CppInspector
// If it is not specified, data analysis will not be performed // If it is not specified, data analysis will not be performed
public static Il2CppBinary Load(IFileFormatReader stream, Metadata metadata, EventHandler<string> statusCallback = null) { public static Il2CppBinary Load(IFileFormatReader stream, Metadata metadata, EventHandler<string> statusCallback = null) {
var inst = LoadImpl(stream, statusCallback); var inst = LoadImpl(stream, statusCallback);
return inst.Initialize(metadata) ? inst : null; return inst.FindRegistrationStructs(metadata) ? inst : null;
} }
// Save binary to file, overwriting if necessary // Save binary to file, overwriting if necessary
@@ -141,24 +146,24 @@ namespace Il2CppInspector
} }
// Initialize binary without a global-metadata.dat available // Initialize binary without a global-metadata.dat available
public bool Initialize(double metadataVersion, EventHandler<string> statusCallback = null) { public bool FindRegistrationStructs(double metadataVersion, EventHandler<string> statusCallback = null) {
Image.Version = metadataVersion; Image.Version = metadataVersion;
if (!((FindMetadataFromSymbols() ?? FindMetadataFromCode()) is var ptrs)) if (!((FindMetadataFromSymbols() ?? FindMetadataFromCode()) is var ptrs))
return false; return false;
Configure(ptrs.Value.Item1, ptrs.Value.Item2); PrepareMetadata(ptrs.Value.Item1, ptrs.Value.Item2);
return true; return true;
} }
// Initialize binary with a global-metadata.dat available // Initialize binary with a global-metadata.dat available
public bool Initialize(Metadata metadata, EventHandler<string> statusCallback = null) { public bool FindRegistrationStructs(Metadata metadata, EventHandler<string> statusCallback = null) {
Image.Version = metadata.Version; Image.Version = metadata.Version;
if (!((FindMetadataFromSymbols() ?? FindMetadataFromCode() ?? FindMetadataFromData(metadata)) is var ptrs)) if (!((FindMetadataFromSymbols() ?? FindMetadataFromCode() ?? FindMetadataFromData(metadata)) is var ptrs))
return false; return false;
Configure(ptrs.Value.Item1, ptrs.Value.Item2, metadata); PrepareMetadata(ptrs.Value.Item1, ptrs.Value.Item2, metadata);
return true; return true;
} }
@@ -230,7 +235,7 @@ namespace Il2CppInspector
protected abstract (ulong, ulong) ConsiderCode(IFileFormatReader image, uint loc); protected abstract (ulong, ulong) ConsiderCode(IFileFormatReader image, uint loc);
// Load all of the discovered metadata in the binary // Load all of the discovered metadata in the binary
private void Configure(ulong codeRegistration, ulong metadataRegistration, Metadata metadata = null) { private void PrepareMetadata(ulong codeRegistration, ulong metadataRegistration, Metadata metadata = null) {
// Store locations // Store locations
CodeRegistrationPointer = codeRegistration; CodeRegistrationPointer = codeRegistration;
MetadataRegistrationPointer = metadataRegistration; MetadataRegistrationPointer = metadataRegistration;
@@ -367,22 +372,36 @@ namespace Il2CppInspector
// This strips leading underscores and selects only il2cpp_* symbols which can be mapped into the binary // This strips leading underscores and selects only il2cpp_* symbols which can be mapped into the binary
// (therefore ignoring extern imports) // (therefore ignoring extern imports)
// Some binaries have functions starting "il2cpp_z_" - ignore these too // Some binaries have functions starting "il2cpp_z_" - ignore these too
public Dictionary<string, ulong> GetAPIExports() { private void DiscoverAPIExports() {
var exports = Image.GetExports()? var exports = Image.GetExports()?.ToList();
.Where(e => (e.Name.StartsWith("il2cpp_") || e.Name.StartsWith("_il2cpp_") || e.Name.StartsWith("__il2cpp_"))
&& !e.Name.Contains("il2cpp_z_"));
if (exports == null) if (exports == null)
return new Dictionary<string, ulong>(); return;
var exportRgx = new Regex(@"^_+"); var exportRgx = new Regex(@"^_+");
var il2cppExports = new Dictionary<string, ulong>();
foreach (var export in exports) // Handle name rot encryption found in some binaries
if (Image.TryMapVATR(export.VirtualAddress, out _)) var anyEncrypted = false;
il2cppExports.Add(exportRgx.Replace(export.Name, ""), export.VirtualAddress);
return il2cppExports; for (var rotKey = 0; rotKey <= 25; rotKey++) {
var possibleExports = exports.Select(e => new {
Name = string.Join("", e.Name.Select(x => (char) (x >= 'a' && x <= 'z'? (x - 'a' + rotKey) % 26 + 'a' : x))),
VirtualAddress = e.VirtualAddress
}).ToList();
var foundExports = possibleExports
.Where(e => (e.Name.StartsWith("il2cpp_") || e.Name.StartsWith("_il2cpp_") || e.Name.StartsWith("__il2cpp_"))
&& !e.Name.Contains("il2cpp_z_"))
.Select(e => e);
if (foundExports.Any() && rotKey != 0 && !anyEncrypted) {
Console.WriteLine("Found encrypted export names - decrypting");
anyEncrypted = true;
}
foreach (var export in foundExports)
if (Image.TryMapVATR(export.VirtualAddress, out _))
APIExports.Add(exportRgx.Replace(export.Name, ""), export.VirtualAddress);
}
} }
} }
} }

View File

@@ -162,7 +162,7 @@ namespace Il2CppInspector.Model
// Populate AvailableAPIs with actual API symbols from Binary.GetAPIExports() and their matching header signatures // Populate AvailableAPIs with actual API symbols from Binary.GetAPIExports() and their matching header signatures
// NOTE: This will only be filled with exports that actually exist in both the binary and the API header, // NOTE: This will only be filled with exports that actually exist in both the binary and the API header,
// and have a mappable address. This prevents exceptions when cross-querying the header and binary APIs. // and have a mappable address. This prevents exceptions when cross-querying the header and binary APIs.
var exports = TypeModel.Package.Binary.GetAPIExports() var exports = TypeModel.Package.Binary.APIExports
.Where(e => CppTypeCollection.TypedefAliases.ContainsKey(e.Key)) .Where(e => CppTypeCollection.TypedefAliases.ContainsKey(e.Key))
.Select(e => new { .Select(e => new {
VirtualAddress = e.Value, VirtualAddress = e.Value,

View File

@@ -138,7 +138,7 @@ typedef size_t uintptr_t;
// We could use model.AvailableAPIs here but that would exclude outputting the address // We could use model.AvailableAPIs here but that would exclude outputting the address
// of API exports which for some reason aren't defined in our selected API header, // of API exports which for some reason aren't defined in our selected API header,
// so although it doesn't affect the C++ compilation, we use GetAPIExports() instead for completeness // so although it doesn't affect the C++ compilation, we use GetAPIExports() instead for completeness
var exports = model.Package.Binary.GetAPIExports(); var exports = model.Package.Binary.APIExports;
foreach (var export in exports) { foreach (var export in exports) {
writeCode($"#define {export.Key}_ptr 0x{model.Package.BinaryImage.MapVATR(export.Value):X8}"); writeCode($"#define {export.Key}_ptr 0x{model.Package.BinaryImage.MapVATR(export.Value):X8}");