diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj
index 5fb54e29..0b2b3665 100644
--- a/de4dot.code/de4dot.code.csproj
+++ b/de4dot.code/de4dot.code.csproj
@@ -70,6 +70,8 @@
+
+
diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/Deobfuscator.cs b/de4dot.code/deobfuscators/dotNET_Reactor/Deobfuscator.cs
index 7a0caa55..d2259585 100644
--- a/de4dot.code/deobfuscators/dotNET_Reactor/Deobfuscator.cs
+++ b/de4dot.code/deobfuscators/dotNET_Reactor/Deobfuscator.cs
@@ -86,6 +86,17 @@ namespace de4dot.deobfuscators.dotNET_Reactor {
methodsDecrypter.find();
}
+ public override byte[] getDecryptedModule() {
+ return methodsDecrypter.decrypt(DeobfuscatedFile);
+ }
+
+ public override IDeobfuscator moduleReloaded(ModuleDefinition module) {
+ var newOne = new Deobfuscator(options);
+ newOne.setModule(module);
+ newOne.methodsDecrypter = new MethodsDecrypter(module, methodsDecrypter);
+ return newOne;
+ }
+
public override void deobfuscateBegin() {
base.deobfuscateBegin();
}
diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/EncryptedResource.cs b/de4dot.code/deobfuscators/dotNET_Reactor/EncryptedResource.cs
new file mode 100644
index 00000000..d6e768b2
--- /dev/null
+++ b/de4dot.code/deobfuscators/dotNET_Reactor/EncryptedResource.cs
@@ -0,0 +1,191 @@
+/*
+ Copyright (C) 2011 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.IO;
+using System.Collections.Generic;
+using System.Security.Cryptography;
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+using de4dot.blocks;
+using de4dot.blocks.cflow;
+
+namespace de4dot.deobfuscators.dotNET_Reactor {
+ class EncryptedResource {
+ ModuleDefinition module;
+ MethodDefinition resourceDecrypterMethod;
+ EmbeddedResource encryptedDataResource;
+ byte[] key, iv;
+
+ public MethodDefinition ResourceDecrypterMethod {
+ get { return resourceDecrypterMethod; }
+ set { resourceDecrypterMethod = value; }
+ }
+
+ public EncryptedResource(ModuleDefinition module) {
+ this.module = module;
+ }
+
+ public EncryptedResource(ModuleDefinition module, EncryptedResource oldOne) {
+ this.module = module;
+ resourceDecrypterMethod = module.LookupToken(oldOne.resourceDecrypterMethod.MetadataToken.ToInt32()) as MethodDefinition;
+ encryptedDataResource = DotNetUtils.getResource(module, oldOne.encryptedDataResource.Name) as EmbeddedResource;
+ key = oldOne.key;
+ iv = oldOne.iv;
+
+ if (resourceDecrypterMethod == null || encryptedDataResource == null || key == null || iv == null)
+ throw new ApplicationException("Could not initialize EncryptedResource");
+ }
+
+ public bool couldBeResourceDecrypter(MethodDefinition method) {
+ if (!method.IsStatic)
+ return false;
+ if (method.Body == null)
+ return false;
+ if (method.Body.Instructions.Count < 1000)
+ return false;
+
+ var localTypes = new LocalTypes(method.Body.Variables);
+ var requiredTypes = new string[] {
+ "System.Byte[]",
+ "System.Diagnostics.StackFrame",
+ "System.IntPtr",
+ "System.IO.BinaryReader",
+ "System.IO.MemoryStream",
+ "System.Reflection.Assembly",
+ "System.Security.Cryptography.CryptoStream",
+ "System.Security.Cryptography.ICryptoTransform",
+ "System.Security.Cryptography.RijndaelManaged",
+ };
+ if (!localTypes.all(requiredTypes))
+ return false;
+
+ if (findMethodsDecrypterResource(method) == null)
+ return false;
+
+ return true;
+ }
+
+ public void init(ISimpleDeobfuscator simpleDeobfuscator) {
+ if (resourceDecrypterMethod == null)
+ return;
+
+ simpleDeobfuscator.deobfuscate(resourceDecrypterMethod);
+
+ encryptedDataResource = findMethodsDecrypterResource(resourceDecrypterMethod);
+ if (encryptedDataResource == null)
+ throw new ApplicationException("Could not find encrypted resource");
+
+ key = initArray(resourceDecrypterMethod, 32);
+ if (key == null)
+ throw new ApplicationException("Could not find resource decrypter key");
+ iv = initArray(resourceDecrypterMethod, 16);
+ if (iv == null)
+ throw new ApplicationException("Could not find resource decrypter IV");
+ var publicKeyToken = module.Assembly.Name.PublicKeyToken;
+ if (publicKeyToken != null) {
+ for (int i = 0; i < 8; i++)
+ iv[i * 2 + 1] = publicKeyToken[i];
+ }
+ }
+
+ EmbeddedResource findMethodsDecrypterResource(MethodDefinition method) {
+ foreach (var s in DotNetUtils.getCodeStrings(method)) {
+ var resource = DotNetUtils.getResource(module, s) as EmbeddedResource;
+ if (resource != null)
+ return resource;
+ }
+ return null;
+ }
+
+ static byte[] initArray(MethodDefinition method, int arraySize) {
+ int newarrIndex = findNewarr(method, arraySize);
+ if (newarrIndex < 0)
+ return null;
+
+ var resultValueArray = new Value[arraySize];
+
+ var emulator = new InstructionEmulator(method.HasThis, false, method.Parameters, method.Body.Variables);
+ var theArray = new UnknownValue();
+ emulator.push(theArray);
+
+ var instructions = method.Body.Instructions;
+ for (int i = newarrIndex + 1; i < instructions.Count; i++) {
+ var instr = instructions[i];
+ if (instr.OpCode.FlowControl != FlowControl.Next)
+ break;
+ if (instr.OpCode.Code == Code.Newarr)
+ break;
+
+ if (instr.OpCode.Code == Code.Stelem_I1) {
+ var value = emulator.pop();
+ var index = emulator.pop() as Int32Value;
+ var array = emulator.pop();
+ if (ReferenceEquals(array, theArray) && index != null && index.allBitsValid()) {
+ if (0 <= index.value && index.value < resultValueArray.Length)
+ resultValueArray[index.value] = value;
+ }
+ }
+ else
+ emulator.emulate(instr);
+ }
+
+ byte[] resultArray = new byte[resultValueArray.Length];
+ for (int i = 0; i < resultArray.Length; i++) {
+ var intValue = resultValueArray[i] as Int32Value;
+ if (intValue == null || !intValue.allBitsValid())
+ return null;
+ resultArray[i] = (byte)intValue.value;
+ }
+
+ return resultArray;
+ }
+
+ static int findNewarr(MethodDefinition method, int arraySize) {
+ var instructions = method.Body.Instructions;
+ for (int i = 0; i < instructions.Count; i++) {
+ var instr = instructions[i];
+ if (instr.OpCode.Code != Code.Newarr || i < 1)
+ continue;
+ var ldci4 = instructions[i - 1];
+ if (!DotNetUtils.isLdcI4(ldci4))
+ continue;
+ if (DotNetUtils.getLdcI4Value(ldci4) != arraySize)
+ continue;
+
+ return i;
+ }
+
+ return -1;
+ }
+
+ public byte[] decrypt() {
+ if (encryptedDataResource == null || key == null || iv == null)
+ throw new ApplicationException("Can't decrypt resource");
+
+ using (var aes = new RijndaelManaged()) {
+ aes.Mode = CipherMode.CBC;
+ using (var transform = aes.CreateDecryptor(key, iv)) {
+ var encryptedData = encryptedDataResource.GetResourceData();
+ return transform.TransformFinalBlock(encryptedData, 0, encryptedData.Length);
+ }
+ }
+ }
+ }
+}
diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/LocalTypes.cs b/de4dot.code/deobfuscators/dotNET_Reactor/LocalTypes.cs
new file mode 100644
index 00000000..baa672d9
--- /dev/null
+++ b/de4dot.code/deobfuscators/dotNET_Reactor/LocalTypes.cs
@@ -0,0 +1,55 @@
+/*
+ Copyright (C) 2011 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 Mono.Cecil.Cil;
+
+namespace de4dot.deobfuscators.dotNET_Reactor {
+ class LocalTypes {
+ Dictionary localTypes = new Dictionary(StringComparer.Ordinal);
+
+ public LocalTypes(IList locals) {
+ foreach (var local in locals) {
+ var key = local.VariableType.FullName;
+ int count;
+ localTypes.TryGetValue(key, out count);
+ localTypes[key] = count + 1;
+ }
+ }
+
+ public bool exists(string typeFullName) {
+ return localTypes.ContainsKey(typeFullName);
+ }
+
+ public bool all(string[] types) {
+ foreach (var typeName in types) {
+ if (!exists(typeName))
+ return false;
+ }
+ return true;
+ }
+
+ public int count(string typeFullName) {
+ int count;
+ localTypes.TryGetValue(typeFullName, out count);
+ return count;
+ }
+ }
+}
diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/MethodsDecrypter.cs b/de4dot.code/deobfuscators/dotNET_Reactor/MethodsDecrypter.cs
index e9f9e2db..ddad3d4e 100644
--- a/de4dot.code/deobfuscators/dotNET_Reactor/MethodsDecrypter.cs
+++ b/de4dot.code/deobfuscators/dotNET_Reactor/MethodsDecrypter.cs
@@ -19,20 +19,30 @@
using System;
using System.Collections.Generic;
+using System.IO;
using Mono.Cecil;
+using Mono.Cecil.Cil;
using de4dot.blocks;
namespace de4dot.deobfuscators.dotNET_Reactor {
class MethodsDecrypter {
ModuleDefinition module;
- MethodDefinition methodsDecrypterMethod;
+ EncryptedResource encryptedResource;
+ long xorKey;
+ bool useXorKey;
public bool Detected {
- get { return methodsDecrypterMethod != null; }
+ get { return encryptedResource.ResourceDecrypterMethod != null; }
}
public MethodsDecrypter(ModuleDefinition module) {
this.module = module;
+ this.encryptedResource = new EncryptedResource(module);
+ }
+
+ public MethodsDecrypter(ModuleDefinition module, MethodsDecrypter oldOne) {
+ this.module = module;
+ this.encryptedResource = new EncryptedResource(module, oldOne.encryptedResource);
}
public void find() {
@@ -40,66 +50,107 @@ namespace de4dot.deobfuscators.dotNET_Reactor {
var callCounter = new CallCounter();
int typesLeft = 30;
foreach (var type in module.GetTypes()) {
- if (typesLeft-- <= 0)
- break;
var cctor = DotNetUtils.getMethod(type, ".cctor");
if (cctor == null || cctor.Body == null)
continue;
+ if (typesLeft-- <= 0)
+ break;
foreach (var info in DotNetUtils.getCalledMethods(module, cctor)) {
var method = info.Item2;
var key = new MethodReferenceAndDeclaringTypeKey(method);
if (!checkedMethods.ContainsKey(key)) {
checkedMethods[key] = true;
- if (!couldBeMethodsDecrypter(method))
+ if (!DotNetUtils.isMethod(method, "System.Void", "()"))
+ continue;
+ if (!encryptedResource.couldBeResourceDecrypter(method))
continue;
}
callCounter.add(method);
}
}
- methodsDecrypterMethod = (MethodDefinition)callCounter.most();
+ encryptedResource.ResourceDecrypterMethod = (MethodDefinition)callCounter.most();
}
- bool couldBeMethodsDecrypter(MethodDefinition method) {
- if (!method.IsStatic)
- return false;
- if (method.Body == null)
- return false;
- if (method.Body.Instructions.Count < 2000)
- return false;
+ public byte[] decrypt(ISimpleDeobfuscator simpleDeobfuscator) {
+ if (encryptedResource.ResourceDecrypterMethod == null)
+ return null;
- var localTypes = new Dictionary(StringComparer.Ordinal);
- foreach (var local in method.Body.Variables)
- localTypes[local.VariableType.FullName] = true;
- var requiredTypes = new string[] {
- "System.Byte[]",
- "System.Diagnostics.StackFrame",
- "System.IntPtr",
- "System.IO.BinaryReader",
- "System.IO.MemoryStream",
- "System.Reflection.Assembly",
- "System.Security.Cryptography.CryptoStream",
- "System.Security.Cryptography.ICryptoTransform",
- "System.Security.Cryptography.RijndaelManaged",
- };
- foreach (var typeName in requiredTypes) {
- if (!localTypes.ContainsKey(typeName))
- return false;
+ encryptedResource.init(simpleDeobfuscator);
+ initXorKey();
+ var methodsData = encryptedResource.decrypt();
+
+ if (useXorKey) {
+ var stream = new MemoryStream(methodsData);
+ var reader = new BinaryReader(stream);
+ var writer = new BinaryWriter(stream);
+ int count = methodsData.Length / 8;
+ for (int i = 0; i < count; i++) {
+ long val = reader.ReadInt64();
+ val ^= xorKey;
+ stream.Position -= 8;
+ writer.Write(val);
+ }
}
- if (!isResourceString(DotNetUtils.getCodeStrings(method)))
- return false;
+ using (var fileStream = new FileStream(module.FullyQualifiedName, FileMode.Open, FileAccess.Read, FileShare.Read)) {
+ byte[] fileData = new byte[(int)fileStream.Length];
+ fileStream.Read(fileData, 0, fileData.Length);
+ var peImage = new PE.PeImage(fileData);
- return true;
+ var methodsDataReader = new BinaryReader(new MemoryStream(methodsData));
+ int patchCount = methodsDataReader.ReadInt32();
+ int mode = methodsDataReader.ReadInt32();
+ if (!useXorKey || mode == 1) {
+ for (int i = 0; i < patchCount; i++) {
+ uint rva = methodsDataReader.ReadUInt32();
+ uint data = methodsDataReader.ReadUInt32();
+ peImage.write(rva, BitConverter.GetBytes(data));
+ }
+ while (methodsDataReader.BaseStream.Position < methodsData.Length - 1) {
+ uint rva = methodsDataReader.ReadUInt32();
+ uint token = methodsDataReader.ReadUInt32();
+ int size = methodsDataReader.ReadInt32();
+ if (size > 0)
+ peImage.write(rva, methodsDataReader.ReadBytes(size));
+ }
+ }
+ else {
+ for (int i = 0; i < patchCount; i++) {
+ uint rva = methodsDataReader.ReadUInt32();
+ uint data = methodsDataReader.ReadUInt32();
+ peImage.write(rva, BitConverter.GetBytes(data));
+ }
+ int count = methodsDataReader.ReadInt32();
+ while (methodsDataReader.BaseStream.Position < methodsData.Length - 1) {
+ uint rva = methodsDataReader.ReadUInt32();
+ uint token = methodsDataReader.ReadUInt32();
+ int size = methodsDataReader.ReadInt32();
+ if (size > 0)
+ peImage.write(rva, methodsDataReader.ReadBytes(size));
+ }
+ }
+
+ return fileData;
+ }
}
- bool isResourceString(IList strings) {
- foreach (var s in strings) {
- if (DotNetUtils.getResource(module, s) != null)
- return true;
+ void initXorKey() {
+ useXorKey = false;
+
+ var instructions = encryptedResource.ResourceDecrypterMethod.Body.Instructions;
+ for (int i = 0; i < instructions.Count - 1; i++) {
+ if (instructions[i].OpCode.Code != Code.Ldind_I8)
+ continue;
+ var ldci4 = instructions[i + 1];
+ if (!DotNetUtils.isLdcI4(ldci4))
+ continue;
+
+ xorKey = DotNetUtils.getLdcI4Value(ldci4);
+ useXorKey = true;
+ return;
}
- return false;
}
}
}