diff --git a/de4dot.blocks/cflow/InstructionEmulator.cs b/de4dot.blocks/cflow/InstructionEmulator.cs index 4a066fd6..c8337f9f 100644 --- a/de4dot.blocks/cflow/InstructionEmulator.cs +++ b/de4dot.blocks/cflow/InstructionEmulator.cs @@ -532,9 +532,8 @@ void Emulate_Sizeof(Instruction instr){ void Emulate_Newarr(Instruction instr) { var val = valueStack.Pop(); - if (val.IsInt32()) + if (val.IsInt32() && val is Int32Value { Value: < 500000 } arrSize) { - Int32Value arrSize = (Int32Value)val; List arr = new List(arrSize.Value); for (int i = 0; i < arrSize.Value; i++) { arr.Add(new UnknownValue()); diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs b/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs index 7102f70a..db13b5a5 100644 --- a/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs @@ -446,7 +446,8 @@ public override IDeobfuscator ModuleReloaded(ModuleDefMD module) { newOne.peImage = new MyPEImage(fileData); newOne.methodsDecrypter = new MethodsDecrypter(module, methodsDecrypter); newOne.proxyCallFixer = new ProxyCallFixer(module, proxyCallFixer); - newOne.devirtualizer = new Devirtualizer(module, devirtualizer); + newOne.devirtualizer = new Devirtualizer(DeobfuscatedFile, module); + newOne.devirtualizer.Find(); newOne.stringDecrypter = new StringDecrypter(module, stringDecrypter); newOne.booleanDecrypter = new BooleanDecrypter(module, booleanDecrypter); newOne.assemblyResolver = new AssemblyResolver(module, assemblyResolver); @@ -462,7 +463,7 @@ void FreePEImage() { } void RemoveMethods() { - if (methodsDecrypter.Method != null) { + if (methodsDecrypter.Method != null && options.DecryptMethods) { AddEntryPointCallToBeRemoved(methodsDecrypter.Method); } @@ -501,7 +502,7 @@ void RemoveMethods() { if (antiDebug != null) AddCallToBeRemoved(method, antiDebug); - if (methodsDecrypter.Method != null) + if (methodsDecrypter.Method != null && options.DecryptMethods) AddCallToBeRemoved(method, methodsDecrypter.Method); // AntiILDASM diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/v4/EncryptedResource.cs b/de4dot.code/deobfuscators/dotNET_Reactor/v4/EncryptedResource.cs index 2689bbc1..6e563916 100644 --- a/de4dot.code/deobfuscators/dotNET_Reactor/v4/EncryptedResource.cs +++ b/de4dot.code/deobfuscators/dotNET_Reactor/v4/EncryptedResource.cs @@ -237,13 +237,69 @@ public byte[] Encrypt(byte[] data) { } } - class DecrypterV2 : IDecrypter { + private abstract class EmulatingDecrypterBase + { + protected MethodDef method; + protected List instructions; + protected List locals; + protected readonly InstructionEmulator instrEmulator = new(); + protected Local emuLocal; + + /** + * Locates the instructions that are responsible for producing the key stream for decrypting the resource. + */ + protected bool FindStartEnd(IList instrs, out int startIndex, out int endIndex, out Local tmpLocal) { + for (int i = 0; i + 8 < instrs.Count; i++) { + if (instrs[i].OpCode.Code != Code.Conv_R_Un) + continue; + if (instrs[i + 1].OpCode.Code != Code.Conv_R8) + continue; + if (instrs[i + 2].OpCode.Code != Code.Conv_U4) + continue; + if (instrs[i + 3].OpCode.Code != Code.Add) + continue; + int newEndIndex = i + 3; + int newStartIndex = -1; + for (int j = newEndIndex; j >= 0; j--) { + // Search upwards for array access or br. + if (instrs[j].OpCode.Code != Code.Ldelem_U1 && (!instrs[j].IsBr() || instrs[j - 1].OpCode.Code == Code.Bne_Un_S)) + continue; + + // Go down to next local load, where the actual decryption should begin. + for (int k = j + 1; k < newEndIndex; k++) { + if (instrs[k].IsLdloc()) { + newStartIndex = k; + break; + } + } + break; + } + endIndex = newEndIndex; + startIndex = newStartIndex; + tmpLocal = CheckLocal(instrs[startIndex], true); + return true; + } + endIndex = 0; + startIndex = 0; + tmpLocal = null; + return false; + } + + /** + * Gets the Local referenced by the instruction if it's either ldloc or stloc (determined by isLdloc). + */ + protected Local CheckLocal(Instruction instr, bool isLdloc) { + if (isLdloc && !instr.IsLdloc()) + return null; + if (!isLdloc && !instr.IsStloc()) + return null; + + return instr.GetLocal(locals); + } + } + + class DecrypterV2 : EmulatingDecrypterBase, IDecrypter { readonly byte[] key, iv; - MethodDef method; - List instructions; - List locals; - readonly InstructionEmulator instrEmulator = new InstructionEmulator(); - Local emuLocal; Parameter emuArg; MethodDef emuMethod; bool isNewDecrypter; @@ -310,49 +366,6 @@ bool Find(IList instrs, out int startIndex, out int endIndex, out L return true; } - bool FindStartEnd(IList instrs, out int startIndex, out int endIndex, out Local tmpLocal) { - for (int i = 0; i + 8 < instrs.Count; i++) { - if (instrs[i].OpCode.Code != Code.Conv_R_Un) - continue; - if (instrs[i + 1].OpCode.Code != Code.Conv_R8) - continue; - if (instrs[i + 2].OpCode.Code != Code.Conv_U4) - continue; - if (instrs[i + 3].OpCode.Code != Code.Add) - continue; - int newEndIndex = i + 3; - int newStartIndex = -1; - for (int x = newEndIndex; x > 0; x--) - if (instrs[x].OpCode.FlowControl != FlowControl.Next) { - newStartIndex = x + 1; - break; - } - if (newStartIndex < 0) - continue; - - var checkLocs = new List(); - int ckStartIndex = -1; - for (int y = newEndIndex; y >= newStartIndex; y--) { - var loc = CheckLocal(instrs[y], true); - if (loc == null) - continue; - if (!checkLocs.Contains(loc)) - checkLocs.Add(loc); - if (checkLocs.Count == 3) - break; - ckStartIndex = y; - } - endIndex = newEndIndex; - startIndex = Math.Max(ckStartIndex, newStartIndex); - tmpLocal = CheckLocal(instrs[startIndex], true); - return true; - } - endIndex = 0; - startIndex = 0; - tmpLocal = null; - return false; - } - 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) { if (instr.OpCode == OpCodes.Call) { @@ -436,15 +449,6 @@ bool FindEnd(IList instrs, int startIndex, out int endIndex) { return false; } - Local CheckLocal(Instruction instr, bool isLdloc) { - if (isLdloc && !instr.IsLdloc()) - return null; - else if (!isLdloc && !instr.IsStloc()) - return null; - - return instr.GetLocal(locals); - } - public byte[] Decrypt(EmbeddedResource resource) { var encrypted = resource.CreateReader().ToArray(); var decrypted = new byte[encrypted.Length]; @@ -517,13 +521,7 @@ public byte[] Encrypt(byte[] data) { } } - class DecrypterV3 : IDecrypter { - readonly MethodDef method; - List instructions; - readonly List locals; - readonly InstructionEmulator instrEmulator = new InstructionEmulator(); - Local emuLocal; - + class DecrypterV3 : EmulatingDecrypterBase, IDecrypter { public DnrDecrypterType DecrypterType => DnrDecrypterType.V3; public DecrypterV3(MethodDef method) { @@ -578,48 +576,6 @@ bool Find(IList instrs, out int startIndex, out int endIndex, out L return true; } - /** - * Locates the instructions that are responsible for producing the key stream for decrypting the resource. - */ - bool FindStartEnd(IList instrs, out int startIndex, out int endIndex, out Local tmpLocal) { - for (int i = 0; i + 8 < instrs.Count; i++) { - if (instrs[i].OpCode.Code != Code.Conv_R_Un) - continue; - if (instrs[i + 1].OpCode.Code != Code.Conv_R8) - continue; - if (instrs[i + 2].OpCode.Code != Code.Conv_U4) - continue; - if (instrs[i + 3].OpCode.Code != Code.Add) - continue; - int newEndIndex = i + 3; - - int newStartIndex = -1; - for (int j = newEndIndex; j >= 0; j--) { - // Search upwards for array access. - if (instrs[j].OpCode.Code != Code.Ldelem_U1) - continue; - - // Go down to next local load, where actual decryption should begin. - for (int k = j + 1; k < newEndIndex; k++) { - if (instrs[k].IsLdloc()) { - newStartIndex = k; - break; - } - } - break; - } - - endIndex = newEndIndex; - startIndex = newStartIndex; - tmpLocal = CheckLocal(instrs[startIndex], true); - return true; - } - endIndex = 0; - startIndex = 0; - tmpLocal = null; - return false; - } - bool FindStart(IList instrs, out int startIndex, out Local tmpLocal) { for (int i = 0; i + 8 < instrs.Count; i++) { if (instrs[i].OpCode.Code != Code.Conv_U) @@ -675,18 +631,6 @@ bool FindEnd(IList instrs, int startIndex, out int endIndex) { return false; } - /** - * Gets the Local referenced by the instruction if it's either ldloc or stloc (determined by isLdloc). - */ - Local CheckLocal(Instruction instr, bool isLdloc) { - if (isLdloc && !instr.IsLdloc()) - return null; - if (!isLdloc && !instr.IsStloc()) - return null; - - return instr.GetLocal(locals); - } - public byte[] Decrypt(EmbeddedResource resource) { var encrypted = resource.CreateReader().ToArray(); var decrypted = new byte[encrypted.Length]; @@ -766,14 +710,10 @@ public byte[] Encrypt(byte[] data) { } } - class DecrypterV4 : IDecrypter { + class DecrypterV4 : EmulatingDecrypterBase, IDecrypter { readonly byte[] key, iv; MethodDef decryptMethod; MethodDef emuMethod; - List instructions; - List locals; - readonly InstructionEmulator instrEmulator = new InstructionEmulator(); - Local emuLocal; public DnrDecrypterType DecrypterType => DnrDecrypterType.V4; @@ -908,43 +848,6 @@ bool Find(IList instrs, out int startIndex, out int endIndex, out L return true; } - bool FindStartEnd(IList instrs, out int startIndex, out int endIndex, out Local tmpLocal) { - for (int i = 0; i + 8 < instrs.Count; i++) { - if (instrs[i].OpCode.Code != Code.Conv_R_Un) - continue; - if (instrs[i + 1].OpCode.Code != Code.Conv_R8) - continue; - if (instrs[i + 2].OpCode.Code != Code.Conv_U4) - continue; - if (instrs[i + 3].OpCode.Code != Code.Add) - continue; - int newEndIndex = i + 3; - int newStartIndex = -1; - for (int j = newEndIndex; j >= 0; j--) { - // Search upwards for array access. - if (instrs[j].OpCode.Code != Code.Ldelem_U1) - continue; - - // Go down to next local load, where actual decryption should begin. - for (int k = j + 1; k < newEndIndex; k++) { - if (instrs[k].IsLdloc()) { - newStartIndex = k; - break; - } - } - break; - } - endIndex = newEndIndex; - startIndex = newStartIndex; - tmpLocal = CheckLocal(instrs[startIndex], true); - return true; - } - endIndex = 0; - startIndex = 0; - tmpLocal = null; - return false; - } - bool FindStart(IList instrs, out int startIndex, out Local tmpLocal) { for (int i = 0; i + 8 < instrs.Count; i++) { if (instrs[i].OpCode.Code != Code.Conv_U) @@ -1000,15 +903,6 @@ bool FindEnd(IList instrs, int startIndex, out int endIndex) { return false; } - Local CheckLocal(Instruction instr, bool isLdloc) { - if (isLdloc && !instr.IsLdloc()) - return null; - else if (!isLdloc && !instr.IsStloc()) - return null; - - return instr.GetLocal(locals); - } - public byte[] Decrypt(EmbeddedResource resource) { var encrypted = resource.CreateReader().ToArray(); var decrypted = new byte[encrypted.Length]; diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/v4/MethodsDecrypter.cs b/de4dot.code/deobfuscators/dotNET_Reactor/v4/MethodsDecrypter.cs index d64f78f4..62383002 100644 --- a/de4dot.code/deobfuscators/dotNET_Reactor/v4/MethodsDecrypter.cs +++ b/de4dot.code/deobfuscators/dotNET_Reactor/v4/MethodsDecrypter.cs @@ -20,6 +20,7 @@ You should have received a copy of the GNU General Public License using System; using System.Collections.Generic; using System.IO; +using System.Linq; using dnlib.IO; using dnlib.DotNet; using dnlib.DotNet.MD; @@ -126,15 +127,23 @@ public bool Decrypt(MyPEImage peImage, ISimpleDeobfuscator simpleDeobfuscator, r var methodsDataReader = ByteArrayDataReaderFactory.CreateReader(methodsData); - int tmp = methodsDataReader.ReadInt32(); - if ((tmp & 0xFF000000) == 0x06000000) - methodsDataReader.ReadInt32(); - else - methodsDataReader.Position -= 4; + int tmp; + if (FindBinaryReaderMethod(simpleDeobfuscator, out var popCallsCount) && popCallsCount > 3) + for (var i = 0; i < popCallsCount; i++) + methodsDataReader.ReadInt32(); + else { + tmp = methodsDataReader.ReadInt32(); + if ((tmp & 0xFF000000) == 0x06000000) + methodsDataReader.ReadInt32(); + else + methodsDataReader.Position -= 4; + } int patchCount = methodsDataReader.ReadInt32(); - int mode = methodsDataReader.ReadInt32(); + if (patchCount > methodsDataReader.BytesLeft / 8) + patchCount = methodsDataReader.ReadInt32(); + int mode = methodsDataReader.ReadInt32(); tmp = methodsDataReader.ReadInt32(); methodsDataReader.Position -= 4; if ((tmp & 0xFF000000) == 0x06000000) { @@ -253,6 +262,53 @@ public bool Decrypt(MyPEImage peImage, ISimpleDeobfuscator simpleDeobfuscator, r return true; } + // Adapted from SychicBoy's NETReactorSlayer + private bool FindBinaryReaderMethod(ISimpleDeobfuscator simpleDeobfuscator, out int popCallsCount) { + popCallsCount = 0; + var decrypterMethod = encryptedResource.Method; + var calls = decrypterMethod.Body.Instructions + .Where(x => x.OpCode == OpCodes.Callvirt && x.Operand is MethodDef md && md.MethodSig.RetType.FullName == "System.Int32") + .Select(x => x.Operand) + .Cast(); + foreach (var method in calls) + try { + simpleDeobfuscator.Deobfuscate(method); + if (method.Body.Instructions.Count != 4) + continue; + + if (!method.Body.Instructions[0].IsLdarg() + || method.Body.Instructions[1].OpCode != OpCodes.Ldfld + || method.Body.Instructions[2].OpCode.Code is not (Code.Callvirt or Code.Call) + || method.Body.Instructions[3].OpCode != OpCodes.Ret) + continue; + + if (!method.Body.Instructions[2].Operand.ToString()!.Contains("System.Int32")) + continue; + + for (var i = 0; i < decrypterMethod.Body.Instructions.Count; i++) + try { + if (!decrypterMethod.Body.Instructions[i].IsLdloc() + || decrypterMethod.Body.Instructions[i + 1].OpCode != OpCodes.Callvirt + || decrypterMethod.Body.Instructions[i + 1].Operand is not MethodDef calledMethod + || decrypterMethod.Body.Instructions[i + 2].OpCode != OpCodes.Pop) + continue; + + if (MethodEqualityComparer.CompareDeclaringTypes.Equals(calledMethod, method)) + popCallsCount++; + } + catch { + // ignored + } + + return true; + } + catch { + // ignored + } + + return false; + } + public static bool IsNewer45Decryption(MethodDef method) { if (method == null || method.Body == null) return false; diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/v4/ResourceResolver.cs b/de4dot.code/deobfuscators/dotNET_Reactor/v4/ResourceResolver.cs index 2af744c7..9f7dbc5c 100644 --- a/de4dot.code/deobfuscators/dotNET_Reactor/v4/ResourceResolver.cs +++ b/de4dot.code/deobfuscators/dotNET_Reactor/v4/ResourceResolver.cs @@ -45,9 +45,6 @@ public ResourceResolver(ModuleDefMD module, ResourceResolver oldOne) { } public void Find(ISimpleDeobfuscator simpleDeobfuscator) { - var additionalTypes = new string[] { - "System.String", - }; foreach (var type in module.Types) { if (type.BaseType == null || type.BaseType.FullName != "System.Object") continue; @@ -59,10 +56,8 @@ public void Find(ISimpleDeobfuscator simpleDeobfuscator) { if (!DotNetUtils.IsMethod(method, "System.Reflection.Assembly", "(System.Object,System.ResolveEventArgs)") && !DotNetUtils.IsMethod(method, "System.Reflection.Assembly", "(System.Object,System.Object)")) continue; - if (method.Body.ExceptionHandlers.Count != 0) - continue; - var initMethod = GetResourceDecrypterInitMethod(method, additionalTypes, true) ?? - GetResourceDecrypterInitMethod(method, additionalTypes, false); + var initMethod = GetResourceDecrypterInitMethod(method, true) ?? + GetResourceDecrypterInitMethod(method, false); if (initMethod == null) continue; @@ -72,14 +67,14 @@ public void Find(ISimpleDeobfuscator simpleDeobfuscator) { } } - MethodDef GetResourceDecrypterInitMethod(MethodDef method, string[] additionalTypes, bool checkResource) { - if (encryptedResource.CouldBeResourceDecrypter(method, additionalTypes, checkResource)) + MethodDef GetResourceDecrypterInitMethod(MethodDef method, bool checkResource) { + if (encryptedResource.CouldBeResourceDecrypter(method, null, checkResource)) return method; foreach (var calledMethod in DotNetUtils.GetCalledMethods(module, method)) { if (!DotNetUtils.IsMethod(calledMethod, "System.Void", "()")) continue; - if (encryptedResource.CouldBeResourceDecrypter(calledMethod, additionalTypes, checkResource)) + if (encryptedResource.CouldBeResourceDecrypter(calledMethod, null, checkResource)) return calledMethod; } @@ -157,13 +152,25 @@ public EmbeddedResource MergeResources() { if (encryptedResource.Resource == null) return null; DeobUtils.DecryptAndAddResources(module, encryptedResource.Resource.Name.String, () => { + byte[] decrypted; + try { + decrypted = encryptedResource.Decrypt(); + } + catch { + return null; + } + + if (decrypted.Length < 64) + throw new Exception("Decrypted resource data has length " + decrypted.Length); + if (decrypted[0] == 0x4D && decrypted[1] == 0x5A && decrypted[62] == 0 && decrypted[63] == 0) + return decrypted; try { - return QuickLZ.Decompress(encryptedResource.Decrypt()); + return QuickLZ.Decompress(decrypted); } catch { try { - return DeobUtils.Inflate(encryptedResource.Decrypt(), true); + return DeobUtils.Inflate(decrypted, true); } catch { return null; diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/v4/vm/Devirtualizer.cs b/de4dot.code/deobfuscators/dotNET_Reactor/v4/vm/Devirtualizer.cs index 7336c2c5..147ca892 100644 --- a/de4dot.code/deobfuscators/dotNET_Reactor/v4/vm/Devirtualizer.cs +++ b/de4dot.code/deobfuscators/dotNET_Reactor/v4/vm/Devirtualizer.cs @@ -47,15 +47,6 @@ public Devirtualizer(ISimpleDeobfuscator deobfuscator, ModuleDefMD module) { _module = module; } - public Devirtualizer(ModuleDefMD module, Devirtualizer oldOne) { - _module = module; - _deobfuscator = oldOne._deobfuscator; - _vmType = DeobUtils.Lookup(module, oldOne._vmType, "Could not find VM type"); - if (oldOne._resource != null) - _resource = DotNetUtils.GetResource(module, oldOne._resource.Name.String) as EmbeddedResource; - StreamHasPrependedByte = oldOne.StreamHasPrependedByte; - } - public void Find() { _vmType = _module.Types.FirstOrDefault(type => type.Methods.Any(method => diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/v4/vm/PatternMatcher.cs b/de4dot.code/deobfuscators/dotNET_Reactor/v4/vm/PatternMatcher.cs index a8f2b62e..dfeead68 100644 --- a/de4dot.code/deobfuscators/dotNET_Reactor/v4/vm/PatternMatcher.cs +++ b/de4dot.code/deobfuscators/dotNET_Reactor/v4/vm/PatternMatcher.cs @@ -110,6 +110,7 @@ private static bool CanInterchange(Instruction ins, OpCode patOpCode) return (ins.IsLdloc() && patIns.IsLdloc()) || (ins.IsStloc() && patIns.IsStloc()) - || (ins.IsConditionalBranch() && patOpCode.Name.Replace(".s", "") == ins.OpCode.Name.Replace(".s", "")); + || (ins.IsConditionalBranch() && patOpCode.Name.Replace(".s", "") == ins.OpCode.Name.Replace(".s", "")) + || (ins.OpCode.Code is Code.Leave or Code.Leave_S && patOpCode.Code is Code.Leave or Code.Leave_S); } }