From 12e94921615daf9c135fd48581665b1076fa5e93 Mon Sep 17 00:00:00 2001 From: Katy Coe Date: Sun, 6 Dec 2020 16:51:14 +0100 Subject: [PATCH] IL2CPP: Automatically decrypt XORed strings --- Il2CppInspector.Common/IL2CPP/Metadata.cs | 40 +++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/Il2CppInspector.Common/IL2CPP/Metadata.cs b/Il2CppInspector.Common/IL2CPP/Metadata.cs index cc3dcf7..3baaa92 100644 --- a/Il2CppInspector.Common/IL2CPP/Metadata.cs +++ b/Il2CppInspector.Common/IL2CPP/Metadata.cs @@ -143,9 +143,49 @@ namespace Il2CppInspector // Get all metadata string literals Position = Header.stringOffset; + + // Naive implementation: this works for normal IL2CPP metadata but isn't good enough when the strings are encrypted while (Position < Header.stringOffset + Header.stringCount) Strings.Add((int)Position - Header.stringOffset, ReadNullTerminatedString()); + // To check for encryption, find every single string start position by scanning all of the definitions + var stringOffsets = + Images.Select(x => x.nameIndex) + .Concat(Assemblies.Select(x => x.aname.nameIndex)) + .Concat(Assemblies.Select(x => x.aname.cultureIndex)) + .Concat(Events.Select(x => x.nameIndex)) + .Concat(Fields.Select(x => x.nameIndex)) + .Concat(Methods.Select(x => x.nameIndex)) + .Concat(Params.Select(x => x.nameIndex)) + .Concat(Properties.Select(x => x.nameIndex)) + .Concat(Types.Select(x => x.nameIndex)) + .Concat(Types.Select(x => x.namespaceIndex)) + .Concat(GenericParameters.Select(x => x.nameIndex)) + .OrderBy(x => x) + .Distinct() + .ToList(); + + // Now confirm that all the keys are present + // If they aren't, that means one or more of the null terminators wasn't null, indicating potential encryption + // Only do this if we need to because it's very slow + if (stringOffsets.Except(Strings.Keys).Any()) { + + // Start again + Strings.Clear(); + Position = Header.stringOffset; + + // Read in all of the strings as if they are fixed length rather than null-terminated + foreach (var offset in stringOffsets.Zip(stringOffsets.Skip(1), (a, b) => (current: a, next: b))) { + var encryptedString = ReadBytes(offset.next - offset.current - 1); + + // The null terminator is the XOR key + var xorKey = ReadByte(); + + var decryptedString = Encoding.GetString(encryptedString.Select(b => (byte) (b ^ xorKey)).ToArray()); + Strings.Add(offset.current, decryptedString); + } + } + // Get all managed code string literals var stringLiteralList = ReadArray(Header.stringLiteralOffset, Header.stringLiteralCount / Sizeof(typeof(Il2CppStringLiteral)));