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;
+ }
}
}