IL2CPP: Handle ROT encryption of API exports
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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)
|
|
||||||
if (Image.TryMapVATR(export.VirtualAddress, out _))
|
|
||||||
il2cppExports.Add(exportRgx.Replace(export.Name, ""), export.VirtualAddress);
|
|
||||||
|
|
||||||
return il2cppExports;
|
// Handle name rot encryption found in some binaries
|
||||||
|
var anyEncrypted = false;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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}");
|
||||||
|
|||||||
Reference in New Issue
Block a user