diff --git a/NETReactorSlayer.Core/Helper/DeobUtils.cs b/NETReactorSlayer.Core/Helper/DeobUtils.cs index 428cd8a..4cdcce0 100644 --- a/NETReactorSlayer.Core/Helper/DeobUtils.cs +++ b/NETReactorSlayer.Core/Helper/DeobUtils.cs @@ -15,6 +15,7 @@ You should have received a copy of the GNU General Public License using System; using System.IO; +using System.IO.Compression; using System.Linq; using System.Security.Cryptography; using dnlib.DotNet; @@ -89,5 +90,20 @@ public static byte[] Inflate(byte[] data, int start, int len, Inflater inflater) return memStream.ToArray(); } + + public static byte[] BrotliDecompress(byte[] data) + { +#if (NETSTANDARD || NET) + var memoryStream = new MemoryStream(); + using (var brotliStream = new BrotliStream(new MemoryStream(data), CompressionMode.Decompress)) + { + brotliStream.CopyTo(memoryStream); + } + + return memoryStream.ToArray(); +#else + throw new ApplicationException("Brotli decompression not available on .NET Framework version. Use .NET6+ version"); +#endif + } } } \ No newline at end of file diff --git a/NETReactorSlayer.Core/Helper/EncryptedResource.cs b/NETReactorSlayer.Core/Helper/EncryptedResource.cs index fb39653..801f525 100644 --- a/NETReactorSlayer.Core/Helper/EncryptedResource.cs +++ b/NETReactorSlayer.Core/Helper/EncryptedResource.cs @@ -24,7 +24,7 @@ You should have received a copy of the GNU General Public License namespace NETReactorSlayer.Core.Helper { - internal class EncryptedResource : IDisposable + internal partial class EncryptedResource : IDisposable { public EncryptedResource(IContext context, MethodDef method, IList additionalTypes) { @@ -206,1116 +206,6 @@ private interface IDecrypter { byte[] Decrypt(EmbeddedResource resource); } - - #region Nested Types - - private class DecrypterV1 : IDecrypter - { - public DecrypterV1(MethodDef method) - { - _key = GetDecryptionKey(method); - _iv = GetDecryptionIV(method); - } - - public static bool CouldBeResourceDecrypter(MethodDef method, StringCounts stringCounts, - IEnumerable additionalTypes) - { - var requiredTypes = new[] - { - new List - { - "System.Byte[]", - "System.Security.Cryptography.CryptoStream", - "System.Security.Cryptography.ICryptoTransform", - "System.String", - "System.Boolean" - }, - new List - { - "System.Security.Cryptography.ICryptoTransform", - "System.IO.Stream", - "System.Int32", - "System.Byte[]", - "System.Boolean" - }, - new List - { - "System.Security.Cryptography.ICryptoTransform", - "System.Int32", - "System.Byte[]", - "System.Boolean" - } - }; - requiredTypes[0].AddRange(additionalTypes); - - if (stringCounts.All(requiredTypes[0]) || - stringCounts.All(requiredTypes[1]) || - (stringCounts.All(requiredTypes[2]) && method.Body.Instructions.Any(x => - x.OpCode.Equals(OpCodes.Newobj) && x.Operand != null && x.Operand.ToString()! - .Contains("System.Security.Cryptography.CryptoStream::.ctor")))) - return DotNetUtils.GetMethod(method.DeclaringType, - "System.Security.Cryptography.SymmetricAlgorithm", - "()") == null || (!stringCounts.Exists("System.UInt64") && - (!stringCounts.Exists("System.UInt32") || - stringCounts.Exists("System.Reflection.Assembly"))); - - return false; - } - - public byte[] Decrypt(EmbeddedResource resource) => - DeobUtils.AesDecrypt(resource.CreateReader().ToArray(), _key, _iv); - - - private readonly byte[] _key, _iv; - } - - private class DecrypterV2 : IDecrypter - { - public DecrypterV2(MethodDef method) - { - _key = GetDecryptionKey(method); - _iv = GetDecryptionIV(method); - _decrypterMethod = method; - _locals = new List(_decrypterMethod.Body.Variables); - if (!Initialize()) - throw new ApplicationException("Could not initialize decrypter"); - } - - public static bool CouldBeResourceDecrypter(StringCounts stringCounts, - IEnumerable additionalTypes) - { - var requiredTypes = new List - { - "System.Int32", - "System.Byte[]" - }; - requiredTypes.AddRange(additionalTypes); - return stringCounts.All(requiredTypes); - } - - public byte[] Decrypt(EmbeddedResource resource) - { - var encrypted = resource.CreateReader().ToArray(); - var decrypted = new byte[encrypted.Length]; - var sum = 0U; - - if (_isNewDecrypter) - for (var i = 0; i < encrypted.Length; i += 4) - { - var value = ReadUInt32(_key, i % _key.Length); - sum += value + CalculateMagic(sum + value); - WriteUInt32(decrypted, i, sum ^ ReadUInt32(encrypted, i)); - } - else - for (var j = 0; j < encrypted.Length; j += 4) - { - sum = CalculateMagic(sum + ReadUInt32(_key, j % _key.Length)); - WriteUInt32(decrypted, j, sum ^ ReadUInt32(encrypted, j)); - } - - return decrypted; - } - - private bool Initialize() - { - var origInstrs = _decrypterMethod.Body.Instructions; - if (!Find(origInstrs, out var emuStartIndex, out var emuEndIndex, out _emuLocal) && - !FindStartEnd(origInstrs, out emuStartIndex, out emuEndIndex, out _emuLocal)) - { - if (!FindStartEnd2(ref origInstrs, out emuStartIndex, out emuEndIndex, out _emuLocal, out _emuArg, - ref _emuMethod, ref _locals)) - return false; - _isNewDecrypter = true; - } - - if (!_isNewDecrypter) - for (var i = 0; i < _iv.Length; i++) - { - var array = _key; - array[i] ^= _iv[i]; - } - - var count = emuEndIndex - emuStartIndex + 1; - _instructions = new List(count); - for (var j = 0; j < count; j++) - _instructions.Add(origInstrs[emuStartIndex + j].Clone()); - return true; - } - - private Local CheckLocal(Instruction instr, bool isLdloc) - { - switch (isLdloc) - { - case true when !instr.IsLdloc(): - case false when !instr.IsStloc(): - return null; - default: - return instr.GetLocal(_locals); - } - } - - private bool Find(IList instrs, out int startIndex, out int endIndex, out Local tmpLocal) - { - startIndex = 0; - endIndex = 0; - tmpLocal = null; - if (!FindStart(instrs, out var emuStartIndex, out _emuLocal)) - return false; - if (!FindEnd(instrs, emuStartIndex, out var emuEndIndex)) - return false; - startIndex = emuStartIndex; - endIndex = emuEndIndex; - tmpLocal = _emuLocal; - return true; - } - - private bool FindEnd(IList instrs, int startIndex, out int endIndex) - { - for (var i = startIndex; i < instrs.Count; i++) - { - var instr = instrs[i]; - if (instr.OpCode.FlowControl != FlowControl.Next) - break; - if (!instr.IsStloc() || instr.GetLocal(_locals) != _emuLocal) - continue; - endIndex = i - 1; - return true; - } - - endIndex = 0; - return false; - } - - private bool FindStart(IList instrs, out int startIndex, out Local tmpLocal) - { - var i = 0; - while (i + 8 < instrs.Count) - { - Local local; - if (instrs[i].OpCode.Code.Equals(Code.Conv_U) && instrs[i + 1].OpCode.Code.Equals(Code.Ldelem_U1) && - instrs[i + 2].OpCode.Code.Equals(Code.Or) && CheckLocal(instrs[i + 3], false) != null && - (local = CheckLocal(instrs[i + 4], true)) != null && CheckLocal(instrs[i + 5], true) != null && - instrs[i + 6].OpCode.Code.Equals(Code.Add) && CheckLocal(instrs[i + 7], false) == local) - { - var instr = instrs[i + 8]; - var newStartIndex = i + 8; - if (instr.IsBr()) - { - instr = instr.Operand as Instruction; - newStartIndex = instrs.IndexOf(instr); - } - - if (newStartIndex >= 0 && instr != null && CheckLocal(instr, true) == local) - { - startIndex = newStartIndex; - tmpLocal = local; - return true; - } - } - - i++; - } - - startIndex = 0; - tmpLocal = null; - return false; - } - - private bool FindStartEnd(IList instrs, out int startIndex, out int endIndex, - out Local tmpLocal) - { - var i = 0; - while (i + 8 < instrs.Count) - { - if (instrs[i].OpCode.Code.Equals(Code.Conv_R_Un) && - instrs[i + 1].OpCode.Code.Equals(Code.Conv_R8) && - instrs[i + 2].OpCode.Code.Equals(Code.Conv_U4) && - instrs[i + 3].OpCode.Code.Equals(Code.Add)) - { - var newEndIndex = i + 3; - var newStartIndex = -1; - for (var x = newEndIndex; x > 0; x--) - if (instrs[x].OpCode.FlowControl != FlowControl.Next) - { - if (instrs[x].OpCode.Equals(OpCodes.Bne_Un) || - instrs[x].OpCode.Equals(OpCodes.Bne_Un_S)) - { - _decrypterVersion = DecrypterVersion.V69; - continue; - } - - break; - } - - var ckStartIndex = -1; - for (var y = newEndIndex; y >= 0; y--) - if (instrs[y].IsBr()) - { - if (instrs[y].Operand is not Instruction instr) - continue; - if (instrs.IndexOf(instr) < y) - { - if (instrs[y - 1].Operand is not Instruction) - continue; - instr = instrs[y - 1].Operand as Instruction; - if (instrs.IndexOf(instr) < y) - continue; - } - newStartIndex = instrs.IndexOf(instr); - ckStartIndex = newStartIndex; - break; - } - - - if (newStartIndex >= 0) - { - var checkLocs = new List(); - for (var y = newEndIndex; y >= newStartIndex; y--) - if (CheckLocal(instrs[y], true) is { } loc) - if (!checkLocs.Contains(loc)) - checkLocs.Add(loc); - - endIndex = newEndIndex; - startIndex = Math.Max(ckStartIndex, newStartIndex); - tmpLocal = CheckLocal(instrs[startIndex], true); - return true; - } - } - - i++; - } - - endIndex = 0; - startIndex = 0; - tmpLocal = null; - return false; - } - - private static bool FindStartEnd2(ref IList instrs, out int startIndex, out int endIndex, - out Local tmpLocal, out Parameter tmpArg, ref MethodDef methodDef, ref List locals) - { - foreach (var instr in instrs) - { - MethodDef method; - if (!instr.OpCode.Equals(OpCodes.Call) || (method = instr.Operand as MethodDef) == null || - method.ReturnType.FullName != "System.Byte[]") - continue; - - using var enumerator2 = DotNetUtils.GetMethodCalls(method).GetEnumerator(); - while (enumerator2.MoveNext()) - { - MethodDef calledMethod; - if ((calledMethod = enumerator2.Current as MethodDef) == null || - calledMethod.Parameters.Count != 2) - continue; - instrs = calledMethod.Body.Instructions; - methodDef = calledMethod; - locals = new List(calledMethod.Body.Variables); - startIndex = 0; - endIndex = instrs.Count - 1; - tmpLocal = null; - tmpArg = calledMethod.Parameters[1]; - return true; - } - } - - endIndex = 0; - startIndex = 0; - tmpLocal = null; - tmpArg = null; - return false; - } - - private static uint ReadUInt32(byte[] ary, int index) - { - var sizeLeft = ary.Length - index; - if (sizeLeft >= 4) - return BitConverter.ToUInt32(ary, index); - return sizeLeft switch - { - 1 => ary[index], - 2 => (uint)(ary[index] | (ary[index + 1] << 8)), - 3 => (uint)(ary[index] | (ary[index + 1] << 8) | (ary[index + 2] << 16)), - _ => throw new ApplicationException("Can't read data") - }; - } - - private static void WriteUInt32(IList ary, int index, uint value) - { - var num = ary.Count - index; - if (num >= 1) - ary[index] = (byte)value; - if (num >= 2) - ary[index + 1] = (byte)(value >> 8); - if (num >= 3) - ary[index + 2] = (byte)(value >> 16); - if (num >= 4) - ary[index + 3] = (byte)(value >> 24); - } - - private uint CalculateMagic(uint input) - { - if (_emuArg == null) - { - _instrEmulator.Initialize(_decrypterMethod, _decrypterMethod.Parameters, _locals, - _decrypterMethod.Body.InitLocals, false); - _instrEmulator.SetLocal(_emuLocal, new Int32Value((int)input)); - } - else - { - _instrEmulator.Initialize(_emuMethod, _emuMethod.Parameters, _locals, _emuMethod.Body.InitLocals, - false); - _instrEmulator.SetArg(_emuArg, new Int32Value((int)input)); - } - - var index = 0; - while (index < _instructions.Count) - { - try - { - if (_decrypterVersion != DecrypterVersion.V69) - goto Emulate; - if (!_instructions[index].IsLdloc()) - goto Emulate; - if (!TryGetLdcValue(_instructions[index + 1], out var value) || value != 0) - goto Emulate; - if (!_instructions[index + 2].OpCode.Equals(OpCodes.Bne_Un) && - !_instructions[index + 2].OpCode.Equals(OpCodes.Bne_Un_S)) - goto Emulate; - if (!_instructions[index + 3].IsLdloc()) - goto Emulate; - if (!TryGetLdcValue(_instructions[index + 4], out value) || value != 1) - goto Emulate; - if (!_instructions[index + 5].OpCode.Equals(OpCodes.Sub)) - goto Emulate; - if (!_instructions[index + 6].IsStloc()) - goto Emulate; - - switch (_instrEmulator.GetLocal(CheckLocal(_instructions[index + 6], false).Index)) - { - case Int32Value int32: - { - if (int32.Value != Int32Value.Zero.Value) - index += 7; - break; - } - case Int64Value int64: - { - if (int64.Value != Int64Value.Zero.Value) - index += 7; - break; - } - case Real8Value real8Value: - { - if (!real8Value.Value.Equals(new Real8Value(0).Value)) - index += 7; - break; - } - } - } - catch { } - - Emulate: - _instrEmulator.Emulate(_instructions[index]); - index++; - } - - if (_instrEmulator.Pop() is not Int32Value tos || !tos.AllBitsValid()) - throw new ApplicationException("Couldn't calculate magic value"); - return (uint)tos.Value; - } - - - private readonly InstructionEmulator _instrEmulator = new(); - private readonly byte[] _key, _iv; - private readonly MethodDef _decrypterMethod; - private Parameter _emuArg; - private Local _emuLocal; - private MethodDef _emuMethod; - private List _instructions; - private bool _isNewDecrypter; - private List _locals; - private DecrypterVersion _decrypterVersion = DecrypterVersion.V6X; - } - - private class DecrypterV3 : IDecrypter - { - public DecrypterV3(MethodDef method) - { - _decrypterMethod = method; - _locals = new List(_decrypterMethod.Body.Variables); - if (!Initialize()) - throw new ApplicationException("Could not initialize decrypter"); - } - - public static bool CouldBeResourceDecrypter(StringCounts stringCounts, - IEnumerable additionalTypes) - { - var requiredTypes = new List - { - "System.Reflection.Emit.DynamicMethod", - "System.Reflection.Emit.ILGenerator" - }; - requiredTypes.AddRange(additionalTypes); - return stringCounts.All(requiredTypes); - } - - public byte[] Decrypt(EmbeddedResource resource) - { - var encrypted = resource.CreateReader().ToArray(); - var decrypted = new byte[encrypted.Length]; - var sum = 0U; - - for (var i = 0; i < encrypted.Length; i += 4) - { - sum = CalculateMagic(sum); - WriteUInt32(decrypted, i, sum ^ ReadUInt32(encrypted, i)); - } - - return decrypted; - } - - private bool Initialize() - { - var origInstrs = _decrypterMethod.Body.Instructions; - if (!Find(origInstrs, out var emuStartIndex, out var emuEndIndex, out _emuLocal) && - !FindStartEnd(origInstrs, out emuStartIndex, out emuEndIndex, out _emuLocal)) - return false; - var count = emuEndIndex - emuStartIndex + 1; - _instructions = new List(count); - for (var i = 0; i < count; i++) - _instructions.Add(origInstrs[emuStartIndex + i].Clone()); - return true; - } - - private uint CalculateMagic(uint input) - { - _instrEmulator.Initialize(_decrypterMethod, _decrypterMethod.Parameters, _locals, - _decrypterMethod.Body.InitLocals, false); - _instrEmulator.SetLocal(_emuLocal, new Int32Value((int)input)); - - var index = 0; - while (index < _instructions.Count) - { - try - { - if (_decrypterVersion != DecrypterVersion.V69) - goto Emulate; - if (!_instructions[index].IsLdloc()) - goto Emulate; - if (!TryGetLdcValue(_instructions[index + 1], out var value) || value != 0) - goto Emulate; - if (!_instructions[index + 2].OpCode.Equals(OpCodes.Bne_Un) && - !_instructions[index + 2].OpCode.Equals(OpCodes.Bne_Un_S)) - goto Emulate; - if (!_instructions[index + 3].IsLdloc()) - goto Emulate; - if (!TryGetLdcValue(_instructions[index + 4], out value) || value != 1) - goto Emulate; - if (!_instructions[index + 5].OpCode.Equals(OpCodes.Sub)) - goto Emulate; - if (!_instructions[index + 6].IsStloc()) - goto Emulate; - - switch (_instrEmulator.GetLocal(CheckLocal(_instructions[index + 6], false).Index)) - { - case Int32Value int32: - { - if (int32.Value != Int32Value.Zero.Value) - index += 7; - break; - } - case Int64Value int64: - { - if (int64.Value != Int64Value.Zero.Value) - index += 7; - break; - } - case Real8Value real8Value: - { - if (!real8Value.Value.Equals(new Real8Value(0).Value)) - index += 7; - break; - } - } - } - catch { } - - Emulate: - _instrEmulator.Emulate(_instructions[index]); - index++; - } - - if (_instrEmulator.Pop() is not Int32Value tos || !tos.AllBitsValid()) - throw new ApplicationException("Couldn't calculate magic value"); - return (uint)tos.Value; - } - - private static uint ReadUInt32(byte[] ary, int index) - { - var sizeLeft = ary.Length - index; - if (sizeLeft >= 4) - return BitConverter.ToUInt32(ary, index); - return sizeLeft switch - { - 1 => ary[index], - 2 => (uint)(ary[index] | (ary[index + 1] << 8)), - 3 => (uint)(ary[index] | (ary[index + 1] << 8) | (ary[index + 2] << 16)), - _ => throw new ApplicationException("Can't read data") - }; - } - - private static void WriteUInt32(IList ary, int index, uint value) - { - var num = ary.Count - index; - if (num >= 1) - ary[index] = (byte)value; - if (num >= 2) - ary[index + 1] = (byte)(value >> 8); - if (num >= 3) - ary[index + 2] = (byte)(value >> 16); - if (num >= 4) - ary[index + 3] = (byte)(value >> 24); - } - - private bool Find(IList instrs, out int startIndex, out int endIndex, out Local tmpLocal) - { - startIndex = 0; - endIndex = 0; - tmpLocal = null; - if (!FindStart(instrs, out var emuStartIndex, out _emuLocal)) - return false; - if (!FindEnd(instrs, emuStartIndex, out var emuEndIndex)) - return false; - startIndex = emuStartIndex; - endIndex = emuEndIndex; - tmpLocal = _emuLocal; - return true; - } - - private bool FindEnd(IList instrs, int startIndex, out int endIndex) - { - for (var i = startIndex; i < instrs.Count; i++) - { - var instr = instrs[i]; - if (instr.OpCode.FlowControl != FlowControl.Next) - break; - if (!instr.IsStloc() || instr.GetLocal(_locals) != _emuLocal) - continue; - endIndex = i - 1; - return true; - } - - endIndex = 0; - return false; - } - - private bool FindStart(IList instrs, out int startIndex, out Local tmpLocal) - { - var i = 0; - while (i + 8 < instrs.Count) - { - Local local; - if (instrs[i].OpCode.Code.Equals(Code.Conv_U) && instrs[i + 1].OpCode.Code.Equals(Code.Ldelem_U1) && - instrs[i + 2].OpCode.Code.Equals(Code.Or) && CheckLocal(instrs[i + 3], false) != null && - (local = CheckLocal(instrs[i + 4], true)) != null && CheckLocal(instrs[i + 5], true) != null && - instrs[i + 6].OpCode.Code.Equals(Code.Add) && CheckLocal(instrs[i + 7], false) == local) - { - var instr = instrs[i + 8]; - var newStartIndex = i + 8; - if (instr.IsBr()) - { - instr = instr.Operand as Instruction; - newStartIndex = instrs.IndexOf(instr); - } - - if (newStartIndex >= 0 && instr != null && CheckLocal(instr, true) == local) - { - startIndex = newStartIndex; - tmpLocal = local; - return true; - } - } - - i++; - } - - startIndex = 0; - tmpLocal = null; - return false; - } - - private bool FindStartEnd(IList instrs, out int startIndex, out int endIndex, - out Local tmpLocal) - { - var i = 0; - while (i + 8 < instrs.Count) - { - if (instrs[i].OpCode.Code.Equals(Code.Conv_R_Un) && - instrs[i + 1].OpCode.Code.Equals(Code.Conv_R8) && - instrs[i + 2].OpCode.Code.Equals(Code.Conv_U4) && - instrs[i + 3].OpCode.Code.Equals(Code.Add)) - { - var newEndIndex = i + 3; - var newStartIndex = -1; - for (var x = newEndIndex; x > 0; x--) - if (instrs[x].OpCode.FlowControl != FlowControl.Next) - { - if (instrs[x].OpCode.Equals(OpCodes.Bne_Un) || - instrs[x].OpCode.Equals(OpCodes.Bne_Un_S)) - { - _decrypterVersion = DecrypterVersion.V69; - continue; - } - - break; - } - - var ckStartIndex = -1; - for (var y = newEndIndex; y >= 0; y--) - if (instrs[y].IsBr()) - { - if (instrs[y].Operand is not Instruction instr) - continue; - if (instrs.IndexOf(instr) < y) - { - if (instrs[y - 1].Operand is not Instruction) - continue; - instr = instrs[y - 1].Operand as Instruction; - if (instrs.IndexOf(instr) < y) - continue; - } - newStartIndex = instrs.IndexOf(instr); - ckStartIndex = newStartIndex; - break; - } - - - if (newStartIndex >= 0) - { - var checkLocs = new List(); - for (var y = newEndIndex; y >= newStartIndex; y--) - if (CheckLocal(instrs[y], true) is { } loc) - if (!checkLocs.Contains(loc)) - checkLocs.Add(loc); - - endIndex = newEndIndex; - startIndex = Math.Max(ckStartIndex, newStartIndex); - tmpLocal = CheckLocal(instrs[startIndex], true); - return true; - } - } - - i++; - } - - endIndex = 0; - startIndex = 0; - tmpLocal = null; - return false; - } - - private Local CheckLocal(Instruction instr, bool isLdloc) - { - switch (isLdloc) - { - case true when !instr.IsLdloc(): - case false when !instr.IsStloc(): - return null; - default: - return instr.GetLocal(_locals); - } - } - - - private readonly InstructionEmulator _instrEmulator = new(); - private readonly List _locals; - private readonly MethodDef _decrypterMethod; - private Local _emuLocal; - private List _instructions; - private DecrypterVersion _decrypterVersion = DecrypterVersion.V6X; - } - - private class DecrypterV4 : IDecrypter - { - public DecrypterV4(MethodDef method) - { - if (!FindDecrypterMethod(method)) - throw new ApplicationException("Could not find decrypter method"); - - if (!FindEmulateMethod(_decrypterMethod)) - throw new ApplicationException("Could not find emulate method"); - - _key = GetDecryptionKey(_decrypterMethod); - _iv = GetDecryptionIV(_decrypterMethod); - _locals = new List(_emuMethod.Body.Variables); - if (!Initialize()) - throw new ApplicationException("Could not initialize decrypter"); - } - - public static bool CouldBeResourceDecrypter(MethodDef method, StringCounts stringCounts, - IEnumerable additionalTypes) - { - var requiredTypes = new List - { - "System.Int32", - "System.Byte[]" - }; - requiredTypes.AddRange(additionalTypes); - if (!stringCounts.All(requiredTypes)) - return false; - - var instrs = method.Body.Instructions; - - return instrs.Where(instr => instr.OpCode == OpCodes.Newobj).Any(instr => instr.Operand is IMethod - { - FullName: "System.Void System.Diagnostics.StackFrame::.ctor(System.Int32)" - }); - } - - public byte[] Decrypt(EmbeddedResource resource) - { - var encrypted = resource.CreateReader().ToArray(); - var decrypted = new byte[encrypted.Length]; - - uint sum = 0; - for (var i = 0; i < encrypted.Length; i += 4) - { - sum = CalculateMagic(sum + ReadUInt32(_key, i % _key.Length)); - WriteUInt32(decrypted, i, sum ^ ReadUInt32(encrypted, i)); - } - - return decrypted; - } - - private bool FindDecrypterMethod(MethodDef method) - { - var instrs = method.Body.Instructions; - for (var i = 0; i < instrs.Count; i++) - { - if (instrs[i].OpCode != OpCodes.Ldsfld) - continue; - if (instrs[i + 1].OpCode != OpCodes.Ldstr) - continue; - if (instrs[i + 2].OpCode != OpCodes.Callvirt) - continue; - if (instrs[i + 3].OpCode != OpCodes.Ldarg_0) - continue; - var call = instrs[i + 4]; - if (call.OpCode != OpCodes.Call) - continue; - - _decrypterMethod = call.Operand as MethodDef; - return true; - } - - return false; - } - - private bool FindEmulateMethod(MethodDef method) - { - var instrs = method.Body.Instructions; - for (var i = 0; i < instrs.Count; i++) - { - if (instrs[i].OpCode != OpCodes.Newobj) - continue; - if (!instrs[i + 1].IsLdloc()) - continue; - if (!instrs[i + 2].IsLdloc()) - continue; - if (!instrs[i + 3].IsLdloc()) - continue; - var call = instrs[i + 4]; - if (call.OpCode != OpCodes.Call) - continue; - - _emuMethod = call.Operand as MethodDef; - return true; - } - - return false; - } - - private bool Initialize() - { - var origInstrs = _emuMethod.Body.Instructions; - - if (!Find(origInstrs, out var emuStartIndex, out var emuEndIndex, out _emuLocal)) - if (!FindStartEnd(origInstrs, out emuStartIndex, out emuEndIndex, out _emuLocal)) - return false; - - for (var i = 0; i < _iv.Length; i++) - _key[i] ^= _iv[i]; - - var count = emuEndIndex - emuStartIndex + 1; - _instructions = new List(count); - for (var i = 0; i < count; i++) - _instructions.Add(origInstrs[emuStartIndex + i].Clone()); - - return true; - } - - private bool Find(IList instrs, out int startIndex, out int endIndex, out Local tmpLocal) - { - startIndex = 0; - endIndex = 0; - tmpLocal = null; - - if (!FindStart(instrs, out var emuStartIndex, out _emuLocal)) - return false; - if (!FindEnd(instrs, emuStartIndex, out var emuEndIndex)) - return false; - startIndex = emuStartIndex; - endIndex = emuEndIndex; - tmpLocal = _emuLocal; - return true; - } - - private bool FindStartEnd(IList instrs, out int startIndex, out int endIndex, - out Local tmpLocal) - { - var i = 0; - while (i + 8 < instrs.Count) - { - if (instrs[i].OpCode.Code.Equals(Code.Conv_R_Un) && - instrs[i + 1].OpCode.Code.Equals(Code.Conv_R8) && - instrs[i + 2].OpCode.Code.Equals(Code.Conv_U4) && - instrs[i + 3].OpCode.Code.Equals(Code.Add)) - { - var newEndIndex = i + 3; - var newStartIndex = -1; - for (var x = newEndIndex; x > 0; x--) - if (instrs[x].OpCode.FlowControl != FlowControl.Next) - { - if (instrs[x].OpCode.Equals(OpCodes.Bne_Un) || - instrs[x].OpCode.Equals(OpCodes.Bne_Un_S)) - { - _decrypterVersion = DecrypterVersion.V69; - continue; - } - - break; - } - - var ckStartIndex = -1; - for (var y = newEndIndex; y >= 0; y--) - if (instrs[y].IsBr()) - { - if (instrs[y].Operand is not Instruction instr) - continue; - if (instrs.IndexOf(instr) < y) - { - if (instrs[y - 1].Operand is not Instruction) - continue; - instr = instrs[y - 1].Operand as Instruction; - if (instrs.IndexOf(instr) < y) - continue; - } - newStartIndex = instrs.IndexOf(instr); - ckStartIndex = newStartIndex; - break; - } - - - if (newStartIndex >= 0) - { - var checkLocs = new List(); - for (var y = newEndIndex; y >= newStartIndex; y--) - if (CheckLocal(instrs[y], true) is { } loc) - if (!checkLocs.Contains(loc)) - checkLocs.Add(loc); - - endIndex = newEndIndex; - startIndex = Math.Max(ckStartIndex, newStartIndex); - tmpLocal = CheckLocal(instrs[startIndex], true); - return true; - } - } - - i++; - } - - endIndex = 0; - startIndex = 0; - tmpLocal = null; - return false; - } - - private bool FindStart(IList instrs, out int startIndex, out Local tmpLocal) - { - for (var i = 0; i + 8 < instrs.Count; i++) - { - if (instrs[i].OpCode.Code != Code.Conv_U) - continue; - if (instrs[i + 1].OpCode.Code != Code.Ldelem_U1) - continue; - if (instrs[i + 2].OpCode.Code != Code.Or) - continue; - if (CheckLocal(instrs[i + 3], false) == null) - continue; - Local local; - if ((local = CheckLocal(instrs[i + 4], true)) == null) - continue; - if (CheckLocal(instrs[i + 5], true) == null) - continue; - if (instrs[i + 6].OpCode.Code != Code.Add) - continue; - if (CheckLocal(instrs[i + 7], false) != local) - continue; - var instr = instrs[i + 8]; - var newStartIndex = i + 8; - if (instr.IsBr()) - { - instr = instr.Operand as Instruction; - newStartIndex = instrs.IndexOf(instr); - } - - if (newStartIndex < 0 || instr == null) - continue; - if (CheckLocal(instr, true) != local) - continue; - - startIndex = newStartIndex; - tmpLocal = local; - return true; - } - - startIndex = 0; - tmpLocal = null; - return false; - } - - private bool FindEnd(IList instrs, int startIndex, out int endIndex) - { - for (var i = startIndex; i < instrs.Count; i++) - { - var instr = instrs[i]; - if (instr.OpCode.FlowControl != FlowControl.Next) - break; - if (!instr.IsStloc() || instr.GetLocal(_locals) != _emuLocal) - continue; - - endIndex = i - 1; - return true; - } - - endIndex = 0; - return false; - } - - private Local CheckLocal(Instruction instr, bool isLdloc) - { - switch (isLdloc) - { - case true when !instr.IsLdloc(): - case false when !instr.IsStloc(): - return null; - default: - return instr.GetLocal(_locals); - } - } - - private uint CalculateMagic(uint input) - { - _instrEmulator.Initialize(_emuMethod, _emuMethod.Parameters, _locals, _emuMethod.Body.InitLocals, - false); - _instrEmulator.SetLocal(_emuLocal, new Int32Value((int)input)); - - var index = 0; - while (index < _instructions.Count) - { - try - { - if (_decrypterVersion != DecrypterVersion.V69) - goto Emulate; - if (!_instructions[index].IsLdloc()) - goto Emulate; - if (!TryGetLdcValue(_instructions[index + 1], out var value) || value != 0) - goto Emulate; - if (!_instructions[index + 2].OpCode.Equals(OpCodes.Bne_Un) && - !_instructions[index + 2].OpCode.Equals(OpCodes.Bne_Un_S)) - goto Emulate; - if (!_instructions[index + 3].IsLdloc()) - goto Emulate; - if (!TryGetLdcValue(_instructions[index + 4], out value) || value != 1) - goto Emulate; - if (!_instructions[index + 5].OpCode.Equals(OpCodes.Sub)) - goto Emulate; - if (!_instructions[index + 6].IsStloc()) - goto Emulate; - - switch (_instrEmulator.GetLocal(CheckLocal(_instructions[index + 6], false).Index)) - { - case Int32Value int32: - { - if (int32.Value != Int32Value.Zero.Value) - index += 7; - break; - } - case Int64Value int64: - { - if (int64.Value != Int64Value.Zero.Value) - index += 7; - break; - } - case Real8Value real8Value: - { - if (!real8Value.Value.Equals(new Real8Value(0).Value)) - index += 7; - break; - } - } - } - catch { } - - Emulate: - _instrEmulator.Emulate(_instructions[index]); - index++; - } - - if (_instrEmulator.Pop() is not Int32Value tos || !tos.AllBitsValid()) - throw new ApplicationException("Couldn't calculate magic value"); - return (uint)tos.Value; - } - - private static uint ReadUInt32(byte[] ary, int index) - { - var sizeLeft = ary.Length - index; - if (sizeLeft >= 4) - return BitConverter.ToUInt32(ary, index); - return sizeLeft switch - { - 1 => ary[index], - 2 => (uint)(ary[index] | (ary[index + 1] << 8)), - 3 => (uint)(ary[index] | (ary[index + 1] << 8) | (ary[index + 2] << 16)), - _ => throw new ApplicationException("Can't read data") - }; - } - - private static void WriteUInt32(IList ary, int index, uint value) - { - var sizeLeft = ary.Count - index; - if (sizeLeft >= 1) - ary[index] = (byte)value; - if (sizeLeft >= 2) - ary[index + 1] = (byte)(value >> 8); - if (sizeLeft >= 3) - ary[index + 2] = (byte)(value >> 16); - if (sizeLeft >= 4) - ary[index + 3] = (byte)(value >> 24); - } - - - private readonly byte[] _key, _iv; - private MethodDef _decrypterMethod; - private MethodDef _emuMethod; - private List _instructions; - private readonly List _locals; - private readonly InstructionEmulator _instrEmulator = new(); - private Local _emuLocal; - private DecrypterVersion _decrypterVersion = DecrypterVersion.V6X; - } - - #endregion + } } \ No newline at end of file diff --git a/NETReactorSlayer.Core/Helper/EncryptedResource/DecrypterV1.cs b/NETReactorSlayer.Core/Helper/EncryptedResource/DecrypterV1.cs new file mode 100644 index 0000000..a327946 --- /dev/null +++ b/NETReactorSlayer.Core/Helper/EncryptedResource/DecrypterV1.cs @@ -0,0 +1,70 @@ +using System.Collections.Generic; +using System.Linq; +using de4dot.blocks; +using dnlib.DotNet; +using dnlib.DotNet.Emit; + +namespace NETReactorSlayer.Core.Helper; + +internal partial class EncryptedResource +{ + private class DecrypterV1 : IDecrypter + { + public DecrypterV1(MethodDef method) + { + _key = GetDecryptionKey(method); + _iv = GetDecryptionIV(method); + } + + public static bool CouldBeResourceDecrypter(MethodDef method, StringCounts stringCounts, + IEnumerable additionalTypes) + { + var requiredTypes = new[] + { + new List + { + "System.Byte[]", + "System.Security.Cryptography.CryptoStream", + "System.Security.Cryptography.ICryptoTransform", + "System.String", + "System.Boolean" + }, + new List + { + "System.Security.Cryptography.ICryptoTransform", + "System.IO.Stream", + "System.Int32", + "System.Byte[]", + "System.Boolean" + }, + new List + { + "System.Security.Cryptography.ICryptoTransform", + "System.Int32", + "System.Byte[]", + "System.Boolean" + } + }; + requiredTypes[0].AddRange(additionalTypes); + + if (stringCounts.All(requiredTypes[0]) || + stringCounts.All(requiredTypes[1]) || + (stringCounts.All(requiredTypes[2]) && method.Body.Instructions.Any(x => + x.OpCode.Equals(OpCodes.Newobj) && x.Operand != null && x.Operand.ToString()! + .Contains("System.Security.Cryptography.CryptoStream::.ctor")))) + return DotNetUtils.GetMethod(method.DeclaringType, + "System.Security.Cryptography.SymmetricAlgorithm", + "()") == null || (!stringCounts.Exists("System.UInt64") && + (!stringCounts.Exists("System.UInt32") || + stringCounts.Exists("System.Reflection.Assembly"))); + + return false; + } + + public byte[] Decrypt(EmbeddedResource resource) => + DeobUtils.AesDecrypt(resource.CreateReader().ToArray(), _key, _iv); + + + private readonly byte[] _key, _iv; + } +} \ No newline at end of file diff --git a/NETReactorSlayer.Core/Helper/EncryptedResource/DecrypterV2.cs b/NETReactorSlayer.Core/Helper/EncryptedResource/DecrypterV2.cs new file mode 100644 index 0000000..023b573 --- /dev/null +++ b/NETReactorSlayer.Core/Helper/EncryptedResource/DecrypterV2.cs @@ -0,0 +1,380 @@ +using System; +using System.Collections.Generic; +using de4dot.blocks; +using de4dot.blocks.cflow; +using dnlib.DotNet; +using dnlib.DotNet.Emit; + +namespace NETReactorSlayer.Core.Helper; + +internal partial class EncryptedResource +{ + private class DecrypterV2 : IDecrypter + { + public DecrypterV2(MethodDef method) + { + _key = GetDecryptionKey(method); + _iv = GetDecryptionIV(method); + _decrypterMethod = method; + _locals = new List(_decrypterMethod.Body.Variables); + if (!Initialize()) + throw new ApplicationException("Could not initialize decrypter"); + } + + public static bool CouldBeResourceDecrypter(StringCounts stringCounts, + IEnumerable additionalTypes) + { + var requiredTypes = new List + { + "System.Int32", + "System.Byte[]" + }; + requiredTypes.AddRange(additionalTypes); + return stringCounts.All(requiredTypes); + } + + public byte[] Decrypt(EmbeddedResource resource) + { + var encrypted = resource.CreateReader().ToArray(); + var decrypted = new byte[encrypted.Length]; + var sum = 0U; + + if (_isNewDecrypter) + for (var i = 0; i < encrypted.Length; i += 4) + { + var value = ReadUInt32(_key, i % _key.Length); + sum += value + CalculateMagic(sum + value); + WriteUInt32(decrypted, i, sum ^ ReadUInt32(encrypted, i)); + } + else + for (var j = 0; j < encrypted.Length; j += 4) + { + sum = CalculateMagic(sum + ReadUInt32(_key, j % _key.Length)); + WriteUInt32(decrypted, j, sum ^ ReadUInt32(encrypted, j)); + } + + return decrypted; + } + + private bool Initialize() + { + var origInstrs = _decrypterMethod.Body.Instructions; + if (!Find(origInstrs, out var emuStartIndex, out var emuEndIndex, out _emuLocal) && + !FindStartEnd(origInstrs, out emuStartIndex, out emuEndIndex, out _emuLocal)) + { + if (!FindStartEnd2(ref origInstrs, out emuStartIndex, out emuEndIndex, out _emuLocal, out _emuArg, + ref _emuMethod, ref _locals)) + return false; + _isNewDecrypter = true; + } + + if (!_isNewDecrypter) + for (var i = 0; i < _iv.Length; i++) + { + var array = _key; + array[i] ^= _iv[i]; + } + + var count = emuEndIndex - emuStartIndex + 1; + _instructions = new List(count); + for (var j = 0; j < count; j++) + _instructions.Add(origInstrs[emuStartIndex + j].Clone()); + return true; + } + + private Local CheckLocal(Instruction instr, bool isLdloc) + { + switch (isLdloc) + { + case true when !instr.IsLdloc(): + case false when !instr.IsStloc(): + return null; + default: + return instr.GetLocal(_locals); + } + } + + private bool Find(IList instrs, out int startIndex, out int endIndex, out Local tmpLocal) + { + startIndex = 0; + endIndex = 0; + tmpLocal = null; + if (!FindStart(instrs, out var emuStartIndex, out _emuLocal)) + return false; + if (!FindEnd(instrs, emuStartIndex, out var emuEndIndex)) + return false; + startIndex = emuStartIndex; + endIndex = emuEndIndex; + tmpLocal = _emuLocal; + return true; + } + + private bool FindEnd(IList instrs, int startIndex, out int endIndex) + { + for (var i = startIndex; i < instrs.Count; i++) + { + var instr = instrs[i]; + if (instr.OpCode.FlowControl != FlowControl.Next) + break; + if (!instr.IsStloc() || instr.GetLocal(_locals) != _emuLocal) + continue; + endIndex = i - 1; + return true; + } + + endIndex = 0; + return false; + } + + private bool FindStart(IList instrs, out int startIndex, out Local tmpLocal) + { + var i = 0; + while (i + 8 < instrs.Count) + { + Local local; + if (instrs[i].OpCode.Code.Equals(Code.Conv_U) && instrs[i + 1].OpCode.Code.Equals(Code.Ldelem_U1) && + instrs[i + 2].OpCode.Code.Equals(Code.Or) && CheckLocal(instrs[i + 3], false) != null && + (local = CheckLocal(instrs[i + 4], true)) != null && CheckLocal(instrs[i + 5], true) != null && + instrs[i + 6].OpCode.Code.Equals(Code.Add) && CheckLocal(instrs[i + 7], false) == local) + { + var instr = instrs[i + 8]; + var newStartIndex = i + 8; + if (instr.IsBr()) + { + instr = instr.Operand as Instruction; + newStartIndex = instrs.IndexOf(instr); + } + + if (newStartIndex >= 0 && instr != null && CheckLocal(instr, true) == local) + { + startIndex = newStartIndex; + tmpLocal = local; + return true; + } + } + + i++; + } + + startIndex = 0; + tmpLocal = null; + return false; + } + + private bool FindStartEnd(IList instrs, out int startIndex, out int endIndex, + out Local tmpLocal) + { + var i = 0; + while (i + 8 < instrs.Count) + { + if (instrs[i].OpCode.Code.Equals(Code.Conv_R_Un) && + instrs[i + 1].OpCode.Code.Equals(Code.Conv_R8) && + instrs[i + 2].OpCode.Code.Equals(Code.Conv_U4) && + instrs[i + 3].OpCode.Code.Equals(Code.Add)) + { + var newEndIndex = i + 3; + var newStartIndex = -1; + for (var x = newEndIndex; x > 0; x--) + if (instrs[x].OpCode.FlowControl != FlowControl.Next) + { + if (instrs[x].OpCode.Equals(OpCodes.Bne_Un) || + instrs[x].OpCode.Equals(OpCodes.Bne_Un_S)) + { + _decrypterVersion = DecrypterVersion.V69; + continue; + } + + break; + } + + var ckStartIndex = -1; + for (var y = newEndIndex; y >= 0; y--) + if (instrs[y].IsBr()) + { + if (instrs[y].Operand is not Instruction instr) + continue; + if (instrs.IndexOf(instr) < y) + { + if (instrs[y - 1].Operand is not Instruction) + continue; + instr = instrs[y - 1].Operand as Instruction; + if (instrs.IndexOf(instr) < y) + continue; + } + newStartIndex = instrs.IndexOf(instr); + ckStartIndex = newStartIndex; + break; + } + + + if (newStartIndex >= 0) + { + var checkLocs = new List(); + for (var y = newEndIndex; y >= newStartIndex; y--) + if (CheckLocal(instrs[y], true) is { } loc) + if (!checkLocs.Contains(loc)) + checkLocs.Add(loc); + + endIndex = newEndIndex; + startIndex = Math.Max(ckStartIndex, newStartIndex); + tmpLocal = CheckLocal(instrs[startIndex], true); + return true; + } + } + + i++; + } + + endIndex = 0; + startIndex = 0; + tmpLocal = null; + return false; + } + + private static bool FindStartEnd2(ref IList instrs, out int startIndex, out int endIndex, + out Local tmpLocal, out Parameter tmpArg, ref MethodDef methodDef, ref List locals) + { + foreach (var instr in instrs) + { + MethodDef method; + if (!instr.OpCode.Equals(OpCodes.Call) || (method = instr.Operand as MethodDef) == null || + method.ReturnType.FullName != "System.Byte[]") + continue; + + using var enumerator2 = DotNetUtils.GetMethodCalls(method).GetEnumerator(); + while (enumerator2.MoveNext()) + { + MethodDef calledMethod; + if ((calledMethod = enumerator2.Current as MethodDef) == null || + calledMethod.Parameters.Count != 2) + continue; + instrs = calledMethod.Body.Instructions; + methodDef = calledMethod; + locals = new List(calledMethod.Body.Variables); + startIndex = 0; + endIndex = instrs.Count - 1; + tmpLocal = null; + tmpArg = calledMethod.Parameters[1]; + return true; + } + } + + endIndex = 0; + startIndex = 0; + tmpLocal = null; + tmpArg = null; + return false; + } + + private static uint ReadUInt32(byte[] ary, int index) + { + var sizeLeft = ary.Length - index; + if (sizeLeft >= 4) + return BitConverter.ToUInt32(ary, index); + return sizeLeft switch + { + 1 => ary[index], + 2 => (uint)(ary[index] | (ary[index + 1] << 8)), + 3 => (uint)(ary[index] | (ary[index + 1] << 8) | (ary[index + 2] << 16)), + _ => throw new ApplicationException("Can't read data") + }; + } + + private static void WriteUInt32(IList ary, int index, uint value) + { + var num = ary.Count - index; + if (num >= 1) + ary[index] = (byte)value; + if (num >= 2) + ary[index + 1] = (byte)(value >> 8); + if (num >= 3) + ary[index + 2] = (byte)(value >> 16); + if (num >= 4) + ary[index + 3] = (byte)(value >> 24); + } + + private uint CalculateMagic(uint input) + { + if (_emuArg == null) + { + _instrEmulator.Initialize(_decrypterMethod, _decrypterMethod.Parameters, _locals, + _decrypterMethod.Body.InitLocals, false); + _instrEmulator.SetLocal(_emuLocal, new Int32Value((int)input)); + } + else + { + _instrEmulator.Initialize(_emuMethod, _emuMethod.Parameters, _locals, _emuMethod.Body.InitLocals, + false); + _instrEmulator.SetArg(_emuArg, new Int32Value((int)input)); + } + + var index = 0; + while (index < _instructions.Count) + { + try + { + if (_decrypterVersion != DecrypterVersion.V69) + goto Emulate; + if (!_instructions[index].IsLdloc()) + goto Emulate; + if (!TryGetLdcValue(_instructions[index + 1], out var value) || value != 0) + goto Emulate; + if (!_instructions[index + 2].OpCode.Equals(OpCodes.Bne_Un) && + !_instructions[index + 2].OpCode.Equals(OpCodes.Bne_Un_S)) + goto Emulate; + if (!_instructions[index + 3].IsLdloc()) + goto Emulate; + if (!TryGetLdcValue(_instructions[index + 4], out value) || value != 1) + goto Emulate; + if (!_instructions[index + 5].OpCode.Equals(OpCodes.Sub)) + goto Emulate; + if (!_instructions[index + 6].IsStloc()) + goto Emulate; + + switch (_instrEmulator.GetLocal(CheckLocal(_instructions[index + 6], false).Index)) + { + case Int32Value int32: + { + if (int32.Value != Int32Value.Zero.Value) + index += 7; + break; + } + case Int64Value int64: + { + if (int64.Value != Int64Value.Zero.Value) + index += 7; + break; + } + case Real8Value real8Value: + { + if (!real8Value.Value.Equals(new Real8Value(0).Value)) + index += 7; + break; + } + } + } + catch { } + + Emulate: + _instrEmulator.Emulate(_instructions[index]); + index++; + } + + if (_instrEmulator.Pop() is not Int32Value tos || !tos.AllBitsValid()) + throw new ApplicationException("Couldn't calculate magic value"); + return (uint)tos.Value; + } + + + private readonly InstructionEmulator _instrEmulator = new(); + private readonly byte[] _key, _iv; + private readonly MethodDef _decrypterMethod; + private Parameter _emuArg; + private Local _emuLocal; + private MethodDef _emuMethod; + private List _instructions; + private bool _isNewDecrypter; + private List _locals; + private DecrypterVersion _decrypterVersion = DecrypterVersion.V6X; + } +} \ No newline at end of file diff --git a/NETReactorSlayer.Core/Helper/EncryptedResource/DecrypterV3.cs b/NETReactorSlayer.Core/Helper/EncryptedResource/DecrypterV3.cs new file mode 100644 index 0000000..1e25e05 --- /dev/null +++ b/NETReactorSlayer.Core/Helper/EncryptedResource/DecrypterV3.cs @@ -0,0 +1,308 @@ +using System; +using System.Collections.Generic; +using de4dot.blocks.cflow; +using dnlib.DotNet; +using dnlib.DotNet.Emit; + +namespace NETReactorSlayer.Core.Helper; + +internal partial class EncryptedResource +{ + private class DecrypterV3 : IDecrypter + { + public DecrypterV3(MethodDef method) + { + _decrypterMethod = method; + _locals = new List(_decrypterMethod.Body.Variables); + if (!Initialize()) + throw new ApplicationException("Could not initialize decrypter"); + } + + public static bool CouldBeResourceDecrypter(StringCounts stringCounts, + IEnumerable additionalTypes) + { + var requiredTypes = new List + { + "System.Reflection.Emit.DynamicMethod", + "System.Reflection.Emit.ILGenerator" + }; + requiredTypes.AddRange(additionalTypes); + return stringCounts.All(requiredTypes); + } + + public byte[] Decrypt(EmbeddedResource resource) + { + var encrypted = resource.CreateReader().ToArray(); + var decrypted = new byte[encrypted.Length]; + var sum = 0U; + + for (var i = 0; i < encrypted.Length; i += 4) + { + sum = CalculateMagic(sum); + WriteUInt32(decrypted, i, sum ^ ReadUInt32(encrypted, i)); + } + + return decrypted; + } + + private bool Initialize() + { + var origInstrs = _decrypterMethod.Body.Instructions; + if (!Find(origInstrs, out var emuStartIndex, out var emuEndIndex, out _emuLocal) && + !FindStartEnd(origInstrs, out emuStartIndex, out emuEndIndex, out _emuLocal)) + return false; + var count = emuEndIndex - emuStartIndex + 1; + _instructions = new List(count); + for (var i = 0; i < count; i++) + _instructions.Add(origInstrs[emuStartIndex + i].Clone()); + return true; + } + + private uint CalculateMagic(uint input) + { + _instrEmulator.Initialize(_decrypterMethod, _decrypterMethod.Parameters, _locals, + _decrypterMethod.Body.InitLocals, false); + _instrEmulator.SetLocal(_emuLocal, new Int32Value((int)input)); + + var index = 0; + while (index < _instructions.Count) + { + try + { + if (_decrypterVersion != DecrypterVersion.V69) + goto Emulate; + if (!_instructions[index].IsLdloc()) + goto Emulate; + if (!TryGetLdcValue(_instructions[index + 1], out var value) || value != 0) + goto Emulate; + if (!_instructions[index + 2].OpCode.Equals(OpCodes.Bne_Un) && + !_instructions[index + 2].OpCode.Equals(OpCodes.Bne_Un_S)) + goto Emulate; + if (!_instructions[index + 3].IsLdloc()) + goto Emulate; + if (!TryGetLdcValue(_instructions[index + 4], out value) || value != 1) + goto Emulate; + if (!_instructions[index + 5].OpCode.Equals(OpCodes.Sub)) + goto Emulate; + if (!_instructions[index + 6].IsStloc()) + goto Emulate; + + switch (_instrEmulator.GetLocal(CheckLocal(_instructions[index + 6], false).Index)) + { + case Int32Value int32: + { + if (int32.Value != Int32Value.Zero.Value) + index += 7; + break; + } + case Int64Value int64: + { + if (int64.Value != Int64Value.Zero.Value) + index += 7; + break; + } + case Real8Value real8Value: + { + if (!real8Value.Value.Equals(new Real8Value(0).Value)) + index += 7; + break; + } + } + } + catch { } + + Emulate: + _instrEmulator.Emulate(_instructions[index]); + index++; + } + + if (_instrEmulator.Pop() is not Int32Value tos || !tos.AllBitsValid()) + throw new ApplicationException("Couldn't calculate magic value"); + return (uint)tos.Value; + } + + private static uint ReadUInt32(byte[] ary, int index) + { + var sizeLeft = ary.Length - index; + if (sizeLeft >= 4) + return BitConverter.ToUInt32(ary, index); + return sizeLeft switch + { + 1 => ary[index], + 2 => (uint)(ary[index] | (ary[index + 1] << 8)), + 3 => (uint)(ary[index] | (ary[index + 1] << 8) | (ary[index + 2] << 16)), + _ => throw new ApplicationException("Can't read data") + }; + } + + private static void WriteUInt32(IList ary, int index, uint value) + { + var num = ary.Count - index; + if (num >= 1) + ary[index] = (byte)value; + if (num >= 2) + ary[index + 1] = (byte)(value >> 8); + if (num >= 3) + ary[index + 2] = (byte)(value >> 16); + if (num >= 4) + ary[index + 3] = (byte)(value >> 24); + } + + private bool Find(IList instrs, out int startIndex, out int endIndex, out Local tmpLocal) + { + startIndex = 0; + endIndex = 0; + tmpLocal = null; + if (!FindStart(instrs, out var emuStartIndex, out _emuLocal)) + return false; + if (!FindEnd(instrs, emuStartIndex, out var emuEndIndex)) + return false; + startIndex = emuStartIndex; + endIndex = emuEndIndex; + tmpLocal = _emuLocal; + return true; + } + + private bool FindEnd(IList instrs, int startIndex, out int endIndex) + { + for (var i = startIndex; i < instrs.Count; i++) + { + var instr = instrs[i]; + if (instr.OpCode.FlowControl != FlowControl.Next) + break; + if (!instr.IsStloc() || instr.GetLocal(_locals) != _emuLocal) + continue; + endIndex = i - 1; + return true; + } + + endIndex = 0; + return false; + } + + private bool FindStart(IList instrs, out int startIndex, out Local tmpLocal) + { + var i = 0; + while (i + 8 < instrs.Count) + { + Local local; + if (instrs[i].OpCode.Code.Equals(Code.Conv_U) && instrs[i + 1].OpCode.Code.Equals(Code.Ldelem_U1) && + instrs[i + 2].OpCode.Code.Equals(Code.Or) && CheckLocal(instrs[i + 3], false) != null && + (local = CheckLocal(instrs[i + 4], true)) != null && CheckLocal(instrs[i + 5], true) != null && + instrs[i + 6].OpCode.Code.Equals(Code.Add) && CheckLocal(instrs[i + 7], false) == local) + { + var instr = instrs[i + 8]; + var newStartIndex = i + 8; + if (instr.IsBr()) + { + instr = instr.Operand as Instruction; + newStartIndex = instrs.IndexOf(instr); + } + + if (newStartIndex >= 0 && instr != null && CheckLocal(instr, true) == local) + { + startIndex = newStartIndex; + tmpLocal = local; + return true; + } + } + + i++; + } + + startIndex = 0; + tmpLocal = null; + return false; + } + + private bool FindStartEnd(IList instrs, out int startIndex, out int endIndex, + out Local tmpLocal) + { + var i = 0; + while (i + 8 < instrs.Count) + { + if (instrs[i].OpCode.Code.Equals(Code.Conv_R_Un) && + instrs[i + 1].OpCode.Code.Equals(Code.Conv_R8) && + instrs[i + 2].OpCode.Code.Equals(Code.Conv_U4) && + instrs[i + 3].OpCode.Code.Equals(Code.Add)) + { + var newEndIndex = i + 3; + var newStartIndex = -1; + for (var x = newEndIndex; x > 0; x--) + if (instrs[x].OpCode.FlowControl != FlowControl.Next) + { + if (instrs[x].OpCode.Equals(OpCodes.Bne_Un) || + instrs[x].OpCode.Equals(OpCodes.Bne_Un_S)) + { + _decrypterVersion = DecrypterVersion.V69; + continue; + } + + break; + } + + var ckStartIndex = -1; + for (var y = newEndIndex; y >= 0; y--) + if (instrs[y].IsBr()) + { + if (instrs[y].Operand is not Instruction instr) + continue; + if (instrs.IndexOf(instr) < y) + { + if (instrs[y - 1].Operand is not Instruction) + continue; + instr = instrs[y - 1].Operand as Instruction; + if (instrs.IndexOf(instr) < y) + continue; + } + newStartIndex = instrs.IndexOf(instr); + ckStartIndex = newStartIndex; + break; + } + + + if (newStartIndex >= 0) + { + var checkLocs = new List(); + for (var y = newEndIndex; y >= newStartIndex; y--) + if (CheckLocal(instrs[y], true) is { } loc) + if (!checkLocs.Contains(loc)) + checkLocs.Add(loc); + + endIndex = newEndIndex; + startIndex = Math.Max(ckStartIndex, newStartIndex); + tmpLocal = CheckLocal(instrs[startIndex], true); + return true; + } + } + + i++; + } + + endIndex = 0; + startIndex = 0; + tmpLocal = null; + return false; + } + + private Local CheckLocal(Instruction instr, bool isLdloc) + { + switch (isLdloc) + { + case true when !instr.IsLdloc(): + case false when !instr.IsStloc(): + return null; + default: + return instr.GetLocal(_locals); + } + } + + + private readonly InstructionEmulator _instrEmulator = new(); + private readonly List _locals; + private readonly MethodDef _decrypterMethod; + private Local _emuLocal; + private List _instructions; + private DecrypterVersion _decrypterVersion = DecrypterVersion.V6X; + } +} \ No newline at end of file diff --git a/NETReactorSlayer.Core/Helper/EncryptedResource/DecrypterV4.cs b/NETReactorSlayer.Core/Helper/EncryptedResource/DecrypterV4.cs new file mode 100644 index 0000000..5394a41 --- /dev/null +++ b/NETReactorSlayer.Core/Helper/EncryptedResource/DecrypterV4.cs @@ -0,0 +1,490 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using de4dot.blocks.cflow; +using dnlib.DotNet; +using dnlib.DotNet.Emit; + +namespace NETReactorSlayer.Core.Helper; + +internal partial class EncryptedResource +{ + private class DecrypterV4 : IDecrypter + { + public DecrypterV4(MethodDef method) + { + if (!FindDecrypterMethod(method)) + throw new ApplicationException("Could not find decrypter method"); + + if (!FindEmulateMethod(_decrypterMethod)) + throw new ApplicationException("Could not find emulate method"); + + _key = GetDecryptionKey(_decrypterMethod); + _iv = GetDecryptionIV(_decrypterMethod); + _locals = new List(_emuMethod.Body.Variables); + + if (!Initialize() && !InitializeNetCore()) + throw new ApplicationException("Could not initialize decrypter"); + } + + public static bool CouldBeResourceDecrypter(MethodDef method, StringCounts stringCounts, + IEnumerable additionalTypes) + { + var requiredTypes = new List + { + "System.Int32", + "System.Byte[]" + }; + requiredTypes.AddRange(additionalTypes); + if (!stringCounts.All(requiredTypes)) + return false; + + var instrs = method.Body.Instructions; + + return instrs.Where(instr => instr.OpCode == OpCodes.Newobj).Any(instr => instr.Operand is IMethod + { + FullName: "System.Void System.Diagnostics.StackFrame::.ctor(System.Int32)" + }); + } + + public byte[] Decrypt(EmbeddedResource resource) + { + var encrypted = resource.CreateReader().ToArray(); + var decrypted = new byte[encrypted.Length]; + + uint sum = 0; + for (var i = 0; i < encrypted.Length; i += 4) + { + if (_netCoreDecrypterVersion) + { + var value = ReadUInt32(_key, i % _key.Length); + sum += value + CalculateMagic(sum + value); + WriteUInt32(decrypted, i, sum ^ ReadUInt32(encrypted, i)); + } + else + { + sum = CalculateMagic(sum + ReadUInt32(_key, i % _key.Length)); + WriteUInt32(decrypted, i, sum ^ ReadUInt32(encrypted, i)); + } + + } + + var debugStringValues = Encoding.Unicode.GetString(decrypted); + + return decrypted; + } + + private bool FindDecrypterMethod(MethodDef method) + { + return FindNetFrameworkDecrypterMethod(method) || + FindNetCoreDecrypterMethod(method); + } + + private bool FindNetFrameworkDecrypterMethod(MethodDef method) + { + var instrs = method.Body.Instructions; + for (var i = 0; i < instrs.Count; i++) + { + if (instrs[i].OpCode != OpCodes.Ldsfld) + continue; + if (instrs[i + 1].OpCode != OpCodes.Ldstr) + continue; + if (instrs[i + 2].OpCode != OpCodes.Callvirt) + continue; + if (instrs[i + 3].OpCode != OpCodes.Ldarg_0) + continue; + var call = instrs[i + 4]; + if (call.OpCode != OpCodes.Call) + continue; + + _decrypterMethod = call.Operand as MethodDef; + return true; + } + + return false; + } + + private bool FindNetCoreDecrypterMethod(MethodDef method) + { + var instrs = method.Body.Instructions; + for (var i = 0; i < instrs.Count - 5; i++) + { + //this was not working with anti tamper enabled + //The instruction is modified in a later stage when enabled + //if (instrs[i].OpCode != OpCodes.Ldtoken) + // continue; + + if (instrs[i + 1].OpCode != OpCodes.Call) + continue; + if (instrs[i + 2].OpCode != OpCodes.Call) + continue; + if (instrs[i + 3].OpCode != OpCodes.Callvirt) + continue; + if (instrs[i + 4].OpCode != OpCodes.Ldstr) + continue; + if (instrs[i + 5].OpCode != OpCodes.Callvirt) + continue; + var call = instrs[i + 6]; + if (call.OpCode != OpCodes.Call) + continue; + + _decrypterMethod = call.Operand as MethodDef; + _netCoreDecrypterVersion = true; + return true; + } + + return false; + } + + private bool FindEmulateMethod(MethodDef method) + { + return FindNetFrameworkEmulateMethod(method) || + FindNetCoreEmulateMethod(method); + } + + private bool FindNetFrameworkEmulateMethod(MethodDef method) + { + var instrs = method.Body.Instructions; + for (var i = 0; i < instrs.Count; i++) + { + if (instrs[i].OpCode != OpCodes.Newobj) + continue; + if (!instrs[i + 1].IsLdloc()) + continue; + if (!instrs[i + 2].IsLdloc()) + continue; + if (!instrs[i + 3].IsLdloc()) + continue; + var call = instrs[i + 4]; + if (call.OpCode != OpCodes.Call) + continue; + + _emuMethod = call.Operand as MethodDef; + return true; + } + + return false; + } + + private bool FindNetCoreEmulateMethod(MethodDef method) + { + var instrs = method.Body.Instructions; + for (var i = 0; i < instrs.Count; i++) + { + if (instrs[i].OpCode != OpCodes.Newobj) + continue; + if (!instrs[i + 1].IsLdloc()) + continue; + var call = instrs[i + 2]; + if (call.OpCode != OpCodes.Call) + continue; + + _emuMethod = call.Operand as MethodDef; + return true; + } + + return false; + } + + private bool Initialize() + { + var origInstrs = _emuMethod.Body.Instructions; + + if (!Find(origInstrs, out var emuStartIndex, out var emuEndIndex, out _emuLocal)) + if (!FindStartEnd(origInstrs, out emuStartIndex, out emuEndIndex, out _emuLocal)) + return false; + + for (var i = 0; i < _iv.Length; i++) + _key[i] ^= _iv[i]; + + var count = emuEndIndex - emuStartIndex + 1; + _instructions = new List(count); + for (var i = 0; i < count; i++) + _instructions.Add(origInstrs[emuStartIndex + i].Clone()); + + return true; + } + + private bool InitializeNetCore() + { + var origInstrs = _emuMethod.Body.Instructions; + + if (origInstrs.FirstOrDefault(x => x.OpCode.Equals(OpCodes.Call))?.Operand is not MethodDef magicMethod) + return false; + + _emuMethod = magicMethod; + _emuParameter = magicMethod.Parameters[1]; + _instructions = []; + foreach (var instr in magicMethod.Body.Instructions) + { + _instructions.Add(instr.Clone()); + } + + return true; + } + + private bool Find(IList instrs, out int startIndex, out int endIndex, out Local tmpLocal) + { + startIndex = 0; + endIndex = 0; + tmpLocal = null; + + if (!FindStart(instrs, out var emuStartIndex, out _emuLocal)) + return false; + if (!FindEnd(instrs, emuStartIndex, out var emuEndIndex)) + return false; + startIndex = emuStartIndex; + endIndex = emuEndIndex; + tmpLocal = _emuLocal; + return true; + } + + private bool FindStartEnd(IList instrs, out int startIndex, out int endIndex, + out Local tmpLocal) + { + var i = 0; + while (i + 8 < instrs.Count) + { + if (instrs[i].OpCode.Code.Equals(Code.Conv_R_Un) && + instrs[i + 1].OpCode.Code.Equals(Code.Conv_R8) && + instrs[i + 2].OpCode.Code.Equals(Code.Conv_U4) && + instrs[i + 3].OpCode.Code.Equals(Code.Add)) + { + var newEndIndex = i + 3; + var newStartIndex = -1; + for (var x = newEndIndex; x > 0; x--) + if (instrs[x].OpCode.FlowControl != FlowControl.Next) + { + if (instrs[x].OpCode.Equals(OpCodes.Bne_Un) || + instrs[x].OpCode.Equals(OpCodes.Bne_Un_S)) + { + _decrypterVersion = DecrypterVersion.V69; + continue; + } + + break; + } + + var ckStartIndex = -1; + for (var y = newEndIndex; y >= 0; y--) + if (instrs[y].IsBr()) + { + if (instrs[y].Operand is not Instruction instr) + continue; + if (instrs.IndexOf(instr) < y) + { + if (instrs[y - 1].Operand is not Instruction) + continue; + instr = instrs[y - 1].Operand as Instruction; + if (instrs.IndexOf(instr) < y) + continue; + } + newStartIndex = instrs.IndexOf(instr); + ckStartIndex = newStartIndex; + break; + } + + + if (newStartIndex >= 0) + { + var checkLocs = new List(); + for (var y = newEndIndex; y >= newStartIndex; y--) + if (CheckLocal(instrs[y], true) is { } loc) + if (!checkLocs.Contains(loc)) + checkLocs.Add(loc); + + endIndex = newEndIndex; + startIndex = Math.Max(ckStartIndex, newStartIndex); + tmpLocal = CheckLocal(instrs[startIndex], true); + return true; + } + } + + i++; + } + + endIndex = 0; + startIndex = 0; + tmpLocal = null; + return false; + } + + private bool FindStart(IList instrs, out int startIndex, out Local tmpLocal) + { + for (var i = 0; i + 8 < instrs.Count; i++) + { + if (instrs[i].OpCode.Code != Code.Conv_U) + continue; + if (instrs[i + 1].OpCode.Code != Code.Ldelem_U1) + continue; + if (instrs[i + 2].OpCode.Code != Code.Or) + continue; + if (CheckLocal(instrs[i + 3], false) == null) + continue; + Local local; + if ((local = CheckLocal(instrs[i + 4], true)) == null) + continue; + if (CheckLocal(instrs[i + 5], true) == null) + continue; + if (instrs[i + 6].OpCode.Code != Code.Add) + continue; + if (CheckLocal(instrs[i + 7], false) != local) + continue; + var instr = instrs[i + 8]; + var newStartIndex = i + 8; + if (instr.IsBr()) + { + instr = instr.Operand as Instruction; + newStartIndex = instrs.IndexOf(instr); + } + + if (newStartIndex < 0 || instr == null) + continue; + if (CheckLocal(instr, true) != local) + continue; + + startIndex = newStartIndex; + tmpLocal = local; + return true; + } + + startIndex = 0; + tmpLocal = null; + return false; + } + + private bool FindEnd(IList instrs, int startIndex, out int endIndex) + { + for (var i = startIndex; i < instrs.Count; i++) + { + var instr = instrs[i]; + if (instr.OpCode.FlowControl != FlowControl.Next) + break; + if (!instr.IsStloc() || instr.GetLocal(_locals) != _emuLocal) + continue; + + endIndex = i - 1; + return true; + } + + endIndex = 0; + return false; + } + + private Local CheckLocal(Instruction instr, bool isLdloc) + { + switch (isLdloc) + { + case true when !instr.IsLdloc(): + case false when !instr.IsStloc(): + return null; + default: + return instr.GetLocal(_locals); + } + } + + private uint CalculateMagic(uint input) + { + _instrEmulator.Initialize(_emuMethod, _emuMethod.Parameters, _locals, _emuMethod.Body.InitLocals, + false); + if (_emuLocal != null) _instrEmulator.SetLocal(_emuLocal, new Int32Value((int) input)); + if (_emuParameter != null) _instrEmulator.SetArg(_emuParameter, new Int32Value((int) input)); + + var index = 0; + while (index < _instructions.Count) + { + try + { + if (_decrypterVersion != DecrypterVersion.V69) + goto Emulate; + if (!_instructions[index].IsLdloc()) + goto Emulate; + if (!TryGetLdcValue(_instructions[index + 1], out var value) || value != 0) + goto Emulate; + if (!_instructions[index + 2].OpCode.Equals(OpCodes.Bne_Un) && + !_instructions[index + 2].OpCode.Equals(OpCodes.Bne_Un_S)) + goto Emulate; + if (!_instructions[index + 3].IsLdloc()) + goto Emulate; + if (!TryGetLdcValue(_instructions[index + 4], out value) || value != 1) + goto Emulate; + if (!_instructions[index + 5].OpCode.Equals(OpCodes.Sub)) + goto Emulate; + if (!_instructions[index + 6].IsStloc()) + goto Emulate; + + switch (_instrEmulator.GetLocal(CheckLocal(_instructions[index + 6], false).Index)) + { + case Int32Value int32: + { + if (int32.Value != Int32Value.Zero.Value) + index += 7; + break; + } + case Int64Value int64: + { + if (int64.Value != Int64Value.Zero.Value) + index += 7; + break; + } + case Real8Value real8Value: + { + if (!real8Value.Value.Equals(new Real8Value(0).Value)) + index += 7; + break; + } + } + } + catch { } + + Emulate: + _instrEmulator.Emulate(_instructions[index]); + index++; + } + + if (_instrEmulator.Pop() is not Int32Value tos || !tos.AllBitsValid()) + throw new ApplicationException("Couldn't calculate magic value"); + return (uint)tos.Value; + } + + private static uint ReadUInt32(byte[] ary, int index) + { + var sizeLeft = ary.Length - index; + if (sizeLeft >= 4) + return BitConverter.ToUInt32(ary, index); + return sizeLeft switch + { + 1 => ary[index], + 2 => (uint)(ary[index] | (ary[index + 1] << 8)), + 3 => (uint)(ary[index] | (ary[index + 1] << 8) | (ary[index + 2] << 16)), + _ => throw new ApplicationException("Can't read data") + }; + } + + private static void WriteUInt32(IList ary, int index, uint value) + { + var sizeLeft = ary.Count - index; + if (sizeLeft >= 1) + ary[index] = (byte)value; + if (sizeLeft >= 2) + ary[index + 1] = (byte)(value >> 8); + if (sizeLeft >= 3) + ary[index + 2] = (byte)(value >> 16); + if (sizeLeft >= 4) + ary[index + 3] = (byte)(value >> 24); + } + + + private readonly byte[] _key, _iv; + private MethodDef _decrypterMethod; + private MethodDef _emuMethod; + private List _instructions; + private readonly List _locals; + private readonly InstructionEmulator _instrEmulator = new(); + private Local _emuLocal; + private Parameter _emuParameter; + private bool _netCoreDecrypterVersion = false; + private DecrypterVersion _decrypterVersion = DecrypterVersion.V6X; + } +} \ No newline at end of file diff --git a/NETReactorSlayer.Core/NETReactorSlayer.Core.csproj b/NETReactorSlayer.Core/NETReactorSlayer.Core.csproj index 365bca2..b2391df 100644 --- a/NETReactorSlayer.Core/NETReactorSlayer.Core.csproj +++ b/NETReactorSlayer.Core/NETReactorSlayer.Core.csproj @@ -10,7 +10,7 @@ - + ..\Libs\net35\de4dot.blocks.dll @@ -18,6 +18,7 @@ ..\Libs\netcoreapp3.1\de4dot.blocks.dll + diff --git a/NETReactorSlayer.Core/Stages/AssemblyResolver.cs b/NETReactorSlayer.Core/Stages/AssemblyResolver.cs index 389c9bf..b4c3674 100644 --- a/NETReactorSlayer.Core/Stages/AssemblyResolver.cs +++ b/NETReactorSlayer.Core/Stages/AssemblyResolver.cs @@ -35,7 +35,7 @@ public void Run(IContext context) return; var assemblies = new List(); - foreach (var prefix in DotNetUtils.GetCodeStrings(_resolverMethod)) + foreach (var prefix in DotNetUtils.GetCodeStrings(_resolverMethod).Distinct()) assemblies.AddRange(GetAssemblies(prefix)); if (assemblies.Count < 1) return; @@ -115,7 +115,7 @@ private IEnumerable GetAssemblies(string prefix) { var result = new List(); if (string.IsNullOrEmpty(prefix)) - return null; + return result; foreach (var rsrc in Context.Module.Resources) { if (rsrc is not EmbeddedResource resource) diff --git a/NETReactorSlayer.Core/Stages/MethodDecrypter.cs b/NETReactorSlayer.Core/Stages/MethodDecrypter.cs index 5feff43..663f205 100644 --- a/NETReactorSlayer.Core/Stages/MethodDecrypter.cs +++ b/NETReactorSlayer.Core/Stages/MethodDecrypter.cs @@ -85,23 +85,31 @@ where call.MDToken.ToInt32() == method.MDToken.ToInt32() private bool Find2() { - var cctor = Context.Module.GlobalType.FindStaticConstructor(); - if (cctor is not { HasBody: true } || !cctor.Body.HasInstructions) - return false; - foreach (var instr in cctor.Body.Instructions.Where(instr => instr.OpCode.Equals(OpCodes.Call))) - if (instr.Operand is MethodDef { DeclaringType: { }, HasBody: true } methodDef && - methodDef.Body.HasInstructions) - if (DotNetUtils.GetMethod(methodDef.DeclaringType, - "System.Security.Cryptography.SymmetricAlgorithm", "()") != null) - { - if (!EncryptedResource.IsKnownDecrypter(methodDef, Array.Empty(), true)) - continue; + var staticConstructors = new[] + { + Context.Module.EntryPoint?.DeclaringType?.FindStaticConstructor(), + Context.Module.GlobalType.FindStaticConstructor() + }; - _encryptedResource = new EncryptedResource(Context, methodDef); - if (_encryptedResource.EmbeddedResource != null) - return true; - _encryptedResource.Dispose(); - } + foreach (var cctor in staticConstructors) + { + if (cctor is not { HasBody: true } || !cctor.Body.HasInstructions) + return false; + foreach (var instr in cctor.Body.Instructions.Where(instr => instr.OpCode.Equals(OpCodes.Call))) + if (instr.Operand is MethodDef { DeclaringType: not null, HasBody: true } methodDef && + methodDef.Body.HasInstructions) + if (DotNetUtils.GetMethod(methodDef.DeclaringType, + "System.Security.Cryptography.SymmetricAlgorithm", "()") != null) + { + if (!EncryptedResource.IsKnownDecrypter(methodDef, Array.Empty(), true)) + continue; + + _encryptedResource = new EncryptedResource(Context, methodDef); + if (_encryptedResource.EmbeddedResource != null) + return true; + _encryptedResource.Dispose(); + } + } return false; } @@ -134,6 +142,13 @@ private bool FindBinaryReaderMethod(out int popCallsCount) for (var i = 0; i < decrypterMethod.Body.Instructions.Count; i++) try { + var skip = CheckNet6Calls(decrypterMethod, i, method); + if (skip) + { + popCallsCount = 4; + break; + } + if (!decrypterMethod.Body.Instructions[i].IsLdloc() || !decrypterMethod.Body.Instructions[i + 1].OpCode.Equals(OpCodes.Callvirt) || decrypterMethod.Body.Instructions[i + 1].Operand is not MethodDef calledMethod || @@ -144,7 +159,7 @@ private bool FindBinaryReaderMethod(out int popCallsCount) popCallsCount++; } catch { } - + return true; } catch { } @@ -152,11 +167,43 @@ private bool FindBinaryReaderMethod(out int popCallsCount) return false; } + private bool CheckNet6Calls(MethodDef decrypterMethod, int i, MethodDef method) + { + //.NET 6+ doesn't only pop 4 times, it assigns them and does some obfuscated things, + //but they don't seem to be important + var inst = decrypterMethod.Body.Instructions; + if (!inst[i].IsLdloc() || + !inst[i + 1].OpCode.Equals(OpCodes.Callvirt) || + inst[i + 1].Operand is not MethodDef calledMethod1 || + !inst[i + 2].IsStloc() || + + !inst[i + 3].IsLdloc() || + !inst[i + 4].OpCode.Equals(OpCodes.Callvirt) || + inst[i + 4].Operand is not MethodDef calledMethod2 || + !inst[i + 5].OpCode.Equals(OpCodes.Stsfld) || + + !inst[i + 6].IsLdloc() || + !inst[i + 7].OpCode.Equals(OpCodes.Callvirt) || + inst[i + 7].Operand is not MethodDef calledMethod3 || + !inst[i + 8].OpCode.Equals(OpCodes.Pop) || + + !inst[i + 9].IsLdloc() || + !inst[i + 10].OpCode.Equals(OpCodes.Callvirt) || + inst[i + 10].Operand is not MethodDef calledMethod4 || + !inst[i + 11].IsStloc()) + return false; + + return MethodEqualityComparer.CompareDeclaringTypes.Equals(calledMethod1, method) && + MethodEqualityComparer.CompareDeclaringTypes.Equals(calledMethod2, method) && + MethodEqualityComparer.CompareDeclaringTypes.Equals(calledMethod3, method) && + MethodEqualityComparer.CompareDeclaringTypes.Equals(calledMethod4, method); + } + private bool RestoreMethodsBody(byte[] bytes) { var dumpedMethods = new DumpedMethods(); XorEncrypt(bytes, GetXorKey(_encryptedResource.DecrypterMethod)); - var isFindDnrMethod = FindDnrCompileMethod(_encryptedResource.DecrypterMethod.DeclaringType) != null; + var isFindDnrMethod = FindDnrCompileMethod(_encryptedResource.DecrypterMethod) != null; var methodsDataReader = ByteArrayDataReaderFactory.CreateReader(bytes); int tmp; @@ -328,15 +375,19 @@ private static bool IsUsingRva(MethodDef method) instrs[i + 8].OpCode.Code.Equals(Code.Call)).Any(); } - private static CompileMethodType GetCompileMethodType(IMethod method) + private static CompileMethodType GetCompileMethodType(IMethod method, string paramStruct = null) { if (DotNetUtils.IsMethod(method, "System.UInt32", "(System.UInt64&,System.IntPtr,System.IntPtr,System.UInt32,System.IntPtr&,System.UInt32&)")) return CompileMethodType.V1; - - return DotNetUtils.IsMethod(method, "System.UInt32", - "(System.IntPtr,System.IntPtr,System.IntPtr,System.UInt32,System.IntPtr,System.UInt32&)") - ? CompileMethodType.V2 + + if (DotNetUtils.IsMethod(method, "System.UInt32", + "(System.IntPtr,System.IntPtr,System.IntPtr,System.UInt32,System.IntPtr,System.UInt32&)")) + return CompileMethodType.V2; + + return paramStruct != null && DotNetUtils.IsMethod(method, "System.UInt32", + $"(System.IntPtr,System.IntPtr,{paramStruct}&,System.UInt32,System.IntPtr,System.UInt32&)") + ? CompileMethodType.V3 : CompileMethodType.Unknown; } @@ -373,12 +424,39 @@ private static long GetXorKey(MethodDef method) return 0; } - private static MethodDef FindDnrCompileMethod(TypeDef type) => - (from method in type.Methods + private static MethodDef FindDnrCompileMethod(MethodDef methodDef) + { + var type = methodDef.DeclaringType; + var methodCandidates = (from method in type.Methods where method.IsStatic && method.Body != null let sig = method.MethodSig where sig != null && sig.Params.Count == 6 - select method).FirstOrDefault(method => GetCompileMethodType(method) != CompileMethodType.Unknown); + select method).ToList(); + var dnrCompileMethod = methodCandidates.FirstOrDefault(method => GetCompileMethodType(method) != CompileMethodType.Unknown); + + if (dnrCompileMethod == null) + { + //.NET 6+ has a different compile method signature + var paramStruct = type.NestedTypes.FirstOrDefault(typeDef => + typeDef.Fields.Count == 4 && + typeDef.Fields[0].FieldType.FullName == "System.IntPtr" && + typeDef.Fields[1].FieldType.FullName == "System.IntPtr" && + typeDef.Fields[2].FieldType.FullName == "System.IntPtr" && + typeDef.Fields[3].FieldType.FullName == "System.Int32"); + + if (paramStruct != null) + { + var operands = methodDef.Body.Instructions + .Where(x => x.OpCode.Equals(OpCodes.Ldftn) && x.Operand is MethodDef) + .Select(x => x.Operand); + dnrCompileMethod = methodCandidates.Where(method => + GetCompileMethodType(method, paramStruct.FullName) != CompileMethodType.Unknown) + .FirstOrDefault(x => operands.Contains(x)); + } + } + + return dnrCompileMethod; + } private static void PatchDwords(MyPeImage peImage, ref DataReader reader, int count) { @@ -413,6 +491,6 @@ private static void XorEncrypt(byte[] data, long xorKey) private readonly short[] _nativeLdci40 = { 85, 139, 236, 51, 192, 93, 195 }; private EncryptedResource _encryptedResource; - private enum CompileMethodType { Unknown, V1, V2 } + private enum CompileMethodType { Unknown, V1, V2, V3 } } } \ No newline at end of file diff --git a/NETReactorSlayer.Core/Stages/ResourceResolver.cs b/NETReactorSlayer.Core/Stages/ResourceResolver.cs index 55a7896..ed54f3d 100644 --- a/NETReactorSlayer.Core/Stages/ResourceResolver.cs +++ b/NETReactorSlayer.Core/Stages/ResourceResolver.cs @@ -124,8 +124,11 @@ private static byte[] Decompress(byte[] bytes) try { return QuickLz.Decompress(bytes); } catch { - try { return DeobUtils.Inflate(bytes, true); } - catch { return null; } + try { return DeobUtils.BrotliDecompress(bytes); } + catch { + try { return DeobUtils.Inflate(bytes, true); } + catch { return null; } + } } } diff --git a/NETReactorSlayer.Core/Stages/StringDecrypter.cs b/NETReactorSlayer.Core/Stages/StringDecrypter.cs index 9e5f97e..92f1271 100644 --- a/NETReactorSlayer.Core/Stages/StringDecrypter.cs +++ b/NETReactorSlayer.Core/Stages/StringDecrypter.cs @@ -263,7 +263,7 @@ private long InlineStringsDynamically() continue; var methodDef = ((IMethod)method.Body.Instructions[i + 1].Operand).ResolveMethodDef(); - if (!methodDef.HasReturnType) + if (methodDef is not {HasReturnType: true}) continue; if (TypeEqualityComparer.Instance.Equals(method.DeclaringType, methodDef.DeclaringType))