diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj index 55c6a658..7a89a627 100644 --- a/de4dot.code/de4dot.code.csproj +++ b/de4dot.code/de4dot.code.csproj @@ -157,6 +157,7 @@ + diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs b/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs index 85526e2a..360c0e9d 100644 --- a/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs @@ -26,6 +26,7 @@ using dnlib.DotNet; using dnlib.DotNet.Emit; using dnlib.DotNet.Writer; using de4dot.blocks; +using de4dot.blocks.cflow; namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { public class DeobfuscatorInfo : DeobfuscatorInfoBase { @@ -142,6 +143,15 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { get { return startedDeobfuscating ? options.InlineMethods : true; } } + public override IEnumerable BlocksDeobfuscators { + get { + var list = new List(); + if (CanInlineMethods) + list.Add(new DnrMethodCallInliner()); + return list; + } + } + public Deobfuscator(Options options) : base(options) { this.options = options; @@ -333,8 +343,12 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { } var compileMethod = MethodsDecrypter.FindDnrCompileMethod(methodsDecrypter.Method.DeclaringType); - if (compileMethod == null) - return DeobfuscatorInfo.THE_NAME + " < 4.0"; + if (compileMethod == null) { + DeobfuscatedFile.Deobfuscate(methodsDecrypter.Method); + if (!MethodsDecrypter.IsNewer45Decryption(methodsDecrypter.Method)) + return DeobfuscatorInfo.THE_NAME + " < 4.0"; + return DeobfuscatorInfo.THE_NAME + " 4.5+"; + } DeobfuscatedFile.Deobfuscate(compileMethod); bool compileMethodHasConstant_0x70000000 = DeobUtils.HasInteger(compileMethod, 0x70000000); // 4.0-4.1 DeobfuscatedFile.Deobfuscate(methodsDecrypter.Method); @@ -346,11 +360,27 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { return DeobfuscatorInfo.THE_NAME + " 4.0"; } if (!hasCorEnableProfilingString) { - // 4.x or 4.5 + // 4.x or 4.5 - 4.6 bool callsReverse = DotNetUtils.CallsMethod(methodsDecrypter.Method, "System.Void System.Array::Reverse(System.Array)"); if (!callsReverse) - return DeobfuscatorInfo.THE_NAME + " 4.x"; - return DeobfuscatorInfo.THE_NAME + " 4.5"; + return DeobfuscatorInfo.THE_NAME + " 4.0 - 4.4"; + + int numIntPtrSizeCompares = CountCompareSystemIntPtrSize(methodsDecrypter.Method); + if (module.IsClr40) { + switch (numIntPtrSizeCompares) { + case 9: return DeobfuscatorInfo.THE_NAME + " 4.5"; + case 10:return DeobfuscatorInfo.THE_NAME + " 4.6"; + } + } + else { + switch (numIntPtrSizeCompares) { + case 8: return DeobfuscatorInfo.THE_NAME + " 4.5"; + case 9: return DeobfuscatorInfo.THE_NAME + " 4.6"; + } + } + + // Should never be reached unless it's a new version + return DeobfuscatorInfo.THE_NAME + " 4.5+"; } // 4.2-4.4 @@ -364,6 +394,29 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { return DeobfuscatorInfo.THE_NAME + " 4.3"; } + static int CountCompareSystemIntPtrSize(MethodDef method) { + if (method == null || method.Body == null) + return 0; + int count = 0; + var instrs = method.Body.Instructions; + for (int i = 1; i < instrs.Count - 1; i++) { + var ldci4 = instrs[i]; + if (!ldci4.IsLdcI4() || ldci4.GetLdcI4Value() != 4) + continue; + if (!instrs[i + 1].IsConditionalBranch()) + continue; + var call = instrs[i - 1]; + if (call.OpCode.Code != Code.Call) + continue; + var calledMethod = call.Operand as MemberRef; + if (calledMethod == null || calledMethod.FullName != "System.Int32 System.IntPtr::get_Size()") + continue; + + count++; + } + return count; + } + static bool FindString(MethodDef method, string s) { foreach (var cs in DotNetUtils.GetCodeStrings(method)) { if (cs == s) diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/v4/DnrMethodCallInliner.cs b/de4dot.code/deobfuscators/dotNET_Reactor/v4/DnrMethodCallInliner.cs new file mode 100644 index 00000000..2a3324b6 --- /dev/null +++ b/de4dot.code/deobfuscators/dotNET_Reactor/v4/DnrMethodCallInliner.cs @@ -0,0 +1,59 @@ +/* + Copyright (C) 2011-2013 de4dot@gmail.com + + This file is part of de4dot. + + de4dot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + de4dot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with de4dot. If not, see . +*/ + +using System; +using System.Collections.Generic; +using dnlib.DotNet.Emit; +using de4dot.blocks; +using de4dot.blocks.cflow; + +namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { + class DnrMethodCallInliner : MethodCallInliner { + public DnrMethodCallInliner() + : base(false) { + } + + protected override Instruction GetFirstInstruction(IList instrs, ref int index) { + var instr = GetFirstInstruction(instrs, index); + if (instr != null) + index = instrs.IndexOf(instr); + return DotNetUtils.GetInstruction(instrs, ref index); + } + + Instruction GetFirstInstruction(IList instrs, int index) { + try { + var instr = instrs[index]; + if (!instr.IsBr()) + return null; + instr = instr.Operand as Instruction; + if (instr == null) + return null; + if (!instr.IsLdcI4() || instr.GetLdcI4Value() != 0) + return null; + instr = instrs[instrs.IndexOf(instr) + 1]; + if (!instr.IsBrtrue()) + return null; + return instrs[instrs.IndexOf(instr) + 1]; + } + catch { + return null; + } + } + } +} diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/v4/MethodsDecrypter.cs b/de4dot.code/deobfuscators/dotNET_Reactor/v4/MethodsDecrypter.cs index 8d538834..cc9a7dc3 100644 --- a/de4dot.code/deobfuscators/dotNET_Reactor/v4/MethodsDecrypter.cs +++ b/de4dot.code/deobfuscators/dotNET_Reactor/v4/MethodsDecrypter.cs @@ -121,8 +121,8 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { } } - static short[] nativeLdci4 = new short[] { 0x55, 0x8B, 0xEC, 0xB8, -1, -1, -1, -1, 0x5D, 0xC3 }; - static short[] nativeLdci4_0 = new short[] { 0x55, 0x8B, 0xEC, 0x33, 0xC0, 0x5D, 0xC3 }; + readonly static short[] nativeLdci4 = new short[] { 0x55, 0x8B, 0xEC, 0xB8, -1, -1, -1, -1, 0x5D, 0xC3 }; + readonly static short[] nativeLdci4_0 = new short[] { 0x55, 0x8B, 0xEC, 0x33, 0xC0, 0x5D, 0xC3 }; public bool Decrypt(MyPEImage peImage, ISimpleDeobfuscator simpleDeobfuscator, ref DumpedMethods dumpedMethods, Dictionary tokenToNativeCode, bool unpackedNativeFile) { if (encryptedResource.Method == null) return false; @@ -157,27 +157,29 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { } } else if (!hooksJitter || mode == 1) { - // DNR 3.9.8.0, 4.0, 4.1, 4.2, 4.3, 4.4 - - // If it's .NET 1.x, then offsets are used, not RVAs. - bool useOffsets = unpackedNativeFile && module.IsClr1x; + // DNR 3.9.8.0, 4.0+ PatchDwords(peImage, methodsDataReader, patchCount); + bool oldCode = !IsNewer45Decryption(encryptedResource.Method); while (methodsDataReader.Position < methodsData.Length - 1) { uint rva = methodsDataReader.ReadUInt32(); - uint token = methodsDataReader.ReadUInt32(); // token, unknown, or index - int size = methodsDataReader.ReadInt32(); - if (size > 0) { - var newData = methodsDataReader.ReadBytes(size); - if (useOffsets) - peImage.DotNetSafeWriteOffset(rva, newData); - else - peImage.DotNetSafeWrite(rva, newData); + int size; + if (oldCode) { + methodsDataReader.ReadUInt32(); // token, unknown, or index + size = methodsDataReader.ReadInt32(); } + else + size = methodsDataReader.ReadInt32() * 4; + + var newData = methodsDataReader.ReadBytes(size); + if (unpackedNativeFile) + peImage.DotNetSafeWriteOffset(rva, newData); + else + peImage.DotNetSafeWrite(rva, newData); } } else { - // DNR 4.0 - 4.5 (jitter is hooked) + // DNR 4.0+ (jitter is hooked) var methodDef = peImage.DotNetFile.MetaData.TablesStream.MethodTable; var rvaToIndex = new Dictionary((int)methodDef.Rows); @@ -258,6 +260,33 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { return true; } + public static bool IsNewer45Decryption(MethodDef method) { + if (method == null || method.Body == null) + return false; + + var instrs = method.Body.Instructions; + for (int i = 0; i < instrs.Count - 4; i++) { + var ldci4 = instrs[i]; + if (!ldci4.IsLdcI4() || ldci4.GetLdcI4Value() != 4) + continue; + if (instrs[i + 1].OpCode.Code != Code.Mul) + continue; + ldci4 = instrs[i + 2]; + if (!ldci4.IsLdcI4() || ldci4.GetLdcI4Value() != 4) + continue; + if (instrs[i + 3].OpCode.Code != Code.Ldloca_S && instrs[i + 3].OpCode.Code != Code.Ldloca) + continue; + var call = instrs[i + 4]; + if (call.OpCode.Code != Code.Call) + continue; + if (!DotNetUtils.IsPinvokeMethod(call.Operand as MethodDef, "kernel32", "VirtualProtect")) + continue; + + return true; + } + return false; + } + static void PatchDwords(MyPEImage peImage, IBinaryReader reader, int count) { for (int i = 0; i < count; i++) { uint rva = reader.ReadUInt32(); @@ -363,6 +392,12 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { Array.Copy(encrypted, resourceData, resourceData.Length); } + enum CompileMethodType { + Unknown, + V1, // <= DNR 4.5.0.0 (2012-11-06 <= endDate < 2013-01-31) + V2, // >= DNR 4.5.0.0 (2012-11-06 < startDate <= 2013-01-31) + } + public static MethodDef FindDnrCompileMethod(TypeDef type) { foreach (var method in type.Methods) { if (!method.IsStatic || method.Body == null) @@ -370,11 +405,19 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { var sig = method.MethodSig; if (sig == null || sig.Params.Count != 6) continue; - if (!DotNetUtils.IsMethod(method, "System.UInt32", "(System.UInt64&,System.IntPtr,System.IntPtr,System.UInt32,System.IntPtr&,System.UInt32&)")) + if (GetCompileMethodType(method) == CompileMethodType.Unknown) continue; return method; } return null; } + + static CompileMethodType GetCompileMethodType(MethodDef method) { + if (DotNetUtils.IsMethod(method, "System.UInt32", "(System.UInt64&,System.IntPtr,System.IntPtr,System.UInt32,System.IntPtr&,System.UInt32&)")) + return CompileMethodType.V1; + if (DotNetUtils.IsMethod(method, "System.UInt32", "(System.IntPtr,System.IntPtr,System.IntPtr,System.UInt32,System.IntPtr,System.UInt32&)")) + return CompileMethodType.V2; + return CompileMethodType.Unknown; + } } }