diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj
index 0b2b3665..9c415ea5 100644
--- a/de4dot.code/de4dot.code.csproj
+++ b/de4dot.code/de4dot.code.csproj
@@ -73,6 +73,7 @@
+
diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/Deobfuscator.cs b/de4dot.code/deobfuscators/dotNET_Reactor/Deobfuscator.cs
index d2259585..99c2b14f 100644
--- a/de4dot.code/deobfuscators/dotNET_Reactor/Deobfuscator.cs
+++ b/de4dot.code/deobfuscators/dotNET_Reactor/Deobfuscator.cs
@@ -17,6 +17,7 @@
along with de4dot. If not, see .
*/
+using System.IO;
using System.Collections.Generic;
using Mono.Cecil;
using de4dot.blocks;
@@ -50,7 +51,10 @@ namespace de4dot.deobfuscators.dotNET_Reactor {
class Deobfuscator : DeobfuscatorBase {
Options options;
+ PE.PeImage peImage;
+ byte[] fileData;
MethodsDecrypter methodsDecrypter;
+ StringDecrypter stringDecrypter;
internal class Options : OptionsBase {
}
@@ -76,7 +80,11 @@ namespace de4dot.deobfuscators.dotNET_Reactor {
int val = 0;
if (methodsDecrypter.Detected)
- val = 100;
+ val += 100;
+ else if (stringDecrypter.Detected)
+ val += 100;
+ if (methodsDecrypter.Detected && stringDecrypter.Detected)
+ val += 10;
return val;
}
@@ -84,21 +92,43 @@ namespace de4dot.deobfuscators.dotNET_Reactor {
protected override void scanForObfuscator() {
methodsDecrypter = new MethodsDecrypter(module);
methodsDecrypter.find();
+ stringDecrypter = new StringDecrypter(module);
+ stringDecrypter.find();
}
public override byte[] getDecryptedModule() {
- return methodsDecrypter.decrypt(DeobfuscatedFile);
+ using (var fileStream = new FileStream(module.FullyQualifiedName, FileMode.Open, FileAccess.Read, FileShare.Read)) {
+ fileData = new byte[(int)fileStream.Length];
+ fileStream.Read(fileData, 0, fileData.Length);
+ }
+ peImage = new PE.PeImage(fileData);
+
+ if (!methodsDecrypter.decrypt(peImage, DeobfuscatedFile))
+ return null;
+
+ return fileData;
}
public override IDeobfuscator moduleReloaded(ModuleDefinition module) {
var newOne = new Deobfuscator(options);
newOne.setModule(module);
+ newOne.peImage = new PE.PeImage(fileData);
newOne.methodsDecrypter = new MethodsDecrypter(module, methodsDecrypter);
+ newOne.stringDecrypter = new StringDecrypter(module, stringDecrypter);
return newOne;
}
public override void deobfuscateBegin() {
base.deobfuscateBegin();
+
+ stringDecrypter.init(peImage, DeobfuscatedFile);
+
+ foreach (var info in stringDecrypter.DecrypterInfos) {
+ staticStringDecrypter.add(info.method, (method2, args) => {
+ return stringDecrypter.decrypt(method2, (int)args[0]);
+ });
+ }
+ DeobfuscatedFile.stringDecryptersAdded();
}
public override void deobfuscateMethodEnd(Blocks blocks) {
@@ -111,6 +141,8 @@ namespace de4dot.deobfuscators.dotNET_Reactor {
public override IEnumerable getStringDecrypterMethods() {
var list = new List();
+ foreach (var info in stringDecrypter.DecrypterInfos)
+ list.Add(info.method.MetadataToken.ToInt32().ToString("X8"));
return list;
}
}
diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/EncryptedResource.cs b/de4dot.code/deobfuscators/dotNET_Reactor/EncryptedResource.cs
index d6e768b2..0b662185 100644
--- a/de4dot.code/deobfuscators/dotNET_Reactor/EncryptedResource.cs
+++ b/de4dot.code/deobfuscators/dotNET_Reactor/EncryptedResource.cs
@@ -44,16 +44,20 @@ namespace de4dot.deobfuscators.dotNET_Reactor {
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;
+ if (oldOne.resourceDecrypterMethod != null)
+ resourceDecrypterMethod = module.LookupToken(oldOne.resourceDecrypterMethod.MetadataToken.ToInt32()) as MethodDefinition;
+ if (oldOne.encryptedDataResource != null)
+ encryptedDataResource = DotNetUtils.getResource(module, oldOne.encryptedDataResource.Name) as EmbeddedResource;
key = oldOne.key;
iv = oldOne.iv;
- if (resourceDecrypterMethod == null || encryptedDataResource == null || key == null || iv == null)
+ if (resourceDecrypterMethod == null && oldOne.resourceDecrypterMethod != null)
+ throw new ApplicationException("Could not initialize EncryptedResource");
+ if (encryptedDataResource == null && oldOne.encryptedDataResource != null)
throw new ApplicationException("Could not initialize EncryptedResource");
}
- public bool couldBeResourceDecrypter(MethodDefinition method) {
+ public bool couldBeResourceDecrypter(MethodDefinition method, IList additionalTypes) {
if (!method.IsStatic)
return false;
if (method.Body == null)
@@ -61,18 +65,16 @@ namespace de4dot.deobfuscators.dotNET_Reactor {
if (method.Body.Instructions.Count < 1000)
return false;
- var localTypes = new LocalTypes(method.Body.Variables);
- var requiredTypes = new string[] {
+ var localTypes = new LocalTypes(method);
+ var requiredTypes = new List {
"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",
};
+ requiredTypes.AddRange(additionalTypes);
if (!localTypes.all(requiredTypes))
return false;
diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/LocalTypes.cs b/de4dot.code/deobfuscators/dotNET_Reactor/LocalTypes.cs
index baa672d9..81eb3e4f 100644
--- a/de4dot.code/deobfuscators/dotNET_Reactor/LocalTypes.cs
+++ b/de4dot.code/deobfuscators/dotNET_Reactor/LocalTypes.cs
@@ -19,12 +19,17 @@
using System;
using System.Collections.Generic;
+using Mono.Cecil;
using Mono.Cecil.Cil;
namespace de4dot.deobfuscators.dotNET_Reactor {
class LocalTypes {
Dictionary localTypes = new Dictionary(StringComparer.Ordinal);
+ public LocalTypes(MethodDefinition method)
+ : this(method.Body.Variables) {
+ }
+
public LocalTypes(IList locals) {
foreach (var local in locals) {
var key = local.VariableType.FullName;
@@ -38,7 +43,7 @@ namespace de4dot.deobfuscators.dotNET_Reactor {
return localTypes.ContainsKey(typeFullName);
}
- public bool all(string[] types) {
+ public bool all(IList types) {
foreach (var typeName in types) {
if (!exists(typeName))
return false;
diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/MethodsDecrypter.cs b/de4dot.code/deobfuscators/dotNET_Reactor/MethodsDecrypter.cs
index ddad3d4e..7e223f5f 100644
--- a/de4dot.code/deobfuscators/dotNET_Reactor/MethodsDecrypter.cs
+++ b/de4dot.code/deobfuscators/dotNET_Reactor/MethodsDecrypter.cs
@@ -46,6 +46,11 @@ namespace de4dot.deobfuscators.dotNET_Reactor {
}
public void find() {
+ var additionalTypes = new string[] {
+ "System.Diagnostics.StackFrame",
+ "System.IntPtr",
+ "System.Reflection.Assembly",
+ };
var checkedMethods = new Dictionary();
var callCounter = new CallCounter();
int typesLeft = 30;
@@ -63,7 +68,7 @@ namespace de4dot.deobfuscators.dotNET_Reactor {
checkedMethods[key] = true;
if (!DotNetUtils.isMethod(method, "System.Void", "()"))
continue;
- if (!encryptedResource.couldBeResourceDecrypter(method))
+ if (!encryptedResource.couldBeResourceDecrypter(method, additionalTypes))
continue;
}
callCounter.add(method);
@@ -73,9 +78,9 @@ namespace de4dot.deobfuscators.dotNET_Reactor {
encryptedResource.ResourceDecrypterMethod = (MethodDefinition)callCounter.most();
}
- public byte[] decrypt(ISimpleDeobfuscator simpleDeobfuscator) {
+ public bool decrypt(PE.PeImage peImage, ISimpleDeobfuscator simpleDeobfuscator) {
if (encryptedResource.ResourceDecrypterMethod == null)
- return null;
+ return false;
encryptedResource.init(simpleDeobfuscator);
initXorKey();
@@ -94,46 +99,40 @@ namespace de4dot.deobfuscators.dotNET_Reactor {
}
}
- 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);
-
- 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));
- }
+ 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));
}
- 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));
- }
+ 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;
}
+ 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 true;
}
void initXorKey() {
diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/StringDecrypter.cs b/de4dot.code/deobfuscators/dotNET_Reactor/StringDecrypter.cs
new file mode 100644
index 00000000..55838f48
--- /dev/null
+++ b/de4dot.code/deobfuscators/dotNET_Reactor/StringDecrypter.cs
@@ -0,0 +1,186 @@
+/*
+ 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.Text;
+using System.Collections.Generic;
+using System.Security.Cryptography;
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+using de4dot.blocks;
+
+namespace de4dot.deobfuscators.dotNET_Reactor {
+ class StringDecrypter {
+ ModuleDefinition module;
+ EncryptedResource encryptedResource;
+ List decrypterInfos = new List();
+ byte[] decryptedData;
+ PE.PeImage peImage;
+
+ public class DecrypterInfo {
+ public MethodDefinition method;
+ public byte[] key;
+ public byte[] iv;
+
+ public DecrypterInfo(MethodDefinition method, byte[] key, byte[] iv) {
+ this.method = method;
+ this.key = key;
+ this.iv = iv;
+ }
+ }
+
+ public bool Detected {
+ get { return encryptedResource.ResourceDecrypterMethod != null; }
+ }
+
+ public IEnumerable DecrypterInfos {
+ get { return decrypterInfos; }
+ }
+
+ public StringDecrypter(ModuleDefinition module) {
+ this.module = module;
+ this.encryptedResource = new EncryptedResource(module);
+ }
+
+ public StringDecrypter(ModuleDefinition module, StringDecrypter oldOne) {
+ this.module = module;
+ this.encryptedResource = new EncryptedResource(module, oldOne.encryptedResource);
+ foreach (var oldInfo in oldOne.decrypterInfos) {
+ var method = module.LookupToken(oldInfo.method.MetadataToken.ToInt32()) as MethodDefinition;
+ if (method == null)
+ throw new ApplicationException("Could not find string decrypter method");
+ decrypterInfos.Add(new DecrypterInfo(method, oldInfo.key, oldInfo.iv));
+ }
+ }
+
+ public void find() {
+ var additionalTypes = new string[] {
+ "System.String",
+ };
+ EmbeddedResource stringsResource = null;
+ foreach (var type in module.Types) {
+ if (type.BaseType == null || type.BaseType.FullName != "System.Object")
+ continue;
+ foreach (var method in type.Methods) {
+ if (!method.IsStatic || !method.HasBody)
+ continue;
+ if (!DotNetUtils.isMethod(method, "System.String", "(System.Int32)"))
+ continue;
+ if (!encryptedResource.couldBeResourceDecrypter(method, additionalTypes))
+ continue;
+
+ var resource = DotNetUtils.getResource(module, DotNetUtils.getCodeStrings(method)) as EmbeddedResource;
+ if (resource == null)
+ throw new ApplicationException("Could not find strings resource");
+ if (stringsResource != null && stringsResource != resource)
+ throw new ApplicationException("Two different string resources found");
+
+ stringsResource = resource;
+ encryptedResource.ResourceDecrypterMethod = method;
+ decrypterInfos.Add(new DecrypterInfo(method, null, null));
+ }
+ }
+ }
+
+ public void init(PE.PeImage peImage, ISimpleDeobfuscator simpleDeobfuscator) {
+ if (encryptedResource.ResourceDecrypterMethod == null)
+ return;
+ this.peImage = peImage;
+
+ foreach (var info in decrypterInfos)
+ findKeyIv(info.method, out info.key, out info.iv);
+
+ encryptedResource.init(simpleDeobfuscator);
+ decryptedData = encryptedResource.decrypt();
+ }
+
+ void findKeyIv(MethodDefinition method, out byte[] key, out byte[] iv) {
+ key = null;
+ iv = null;
+
+ var requiredTypes = new string[] {
+ "System.Byte[]",
+ "System.IO.MemoryStream",
+ "System.Security.Cryptography.CryptoStream",
+ "System.Security.Cryptography.Rijndael",
+ };
+ foreach (var info in DotNetUtils.getCalledMethods(module, method)) {
+ var calledMethod = info.Item2;
+ if (calledMethod.DeclaringType != method.DeclaringType)
+ continue;
+ var localTypes = new LocalTypes(calledMethod);
+ if (!localTypes.all(requiredTypes))
+ continue;
+
+ var instructions = calledMethod.Body.Instructions;
+ byte[] newKey = null, newIv = null;
+ for (int i = 0; i < instructions.Count && (newKey == null || newIv == null); i++) {
+ var instr = instructions[i];
+ if (instr.OpCode.Code != Code.Ldtoken)
+ continue;
+ var field = instr.Operand as FieldDefinition;
+ if (field == null)
+ continue;
+ if (field.InitialValue == null)
+ continue;
+ if (field.InitialValue.Length == 32)
+ newKey = field.InitialValue;
+ else if (field.InitialValue.Length == 16)
+ newIv = field.InitialValue;
+ }
+ if (newKey == null || newIv == null)
+ continue;
+
+ key = newKey;
+ iv = newIv;
+ return;
+ }
+ }
+
+ DecrypterInfo getDecrypterInfo(MethodDefinition method) {
+ foreach (var info in decrypterInfos) {
+ if (info.method == method)
+ return info;
+ }
+ throw new ApplicationException("Invalid string decrypter method");
+ }
+
+ public string decrypt(MethodDefinition method, int offset) {
+ var info = getDecrypterInfo(method);
+
+ if (info.key == null) {
+ int length = BitConverter.ToInt32(decryptedData, offset);
+ return Encoding.Unicode.GetString(decryptedData, offset + 4, length);
+ }
+ else {
+ uint rva = BitConverter.ToUInt32(decryptedData, offset);
+ int length = peImage.readInt32(rva);
+ var encryptedStringData = peImage.readBytes(rva + 4, length);
+ byte[] decryptedStringData;
+ using (var aes = new RijndaelManaged()) {
+ aes.Mode = CipherMode.CBC;
+ using (var transform = aes.CreateDecryptor(info.key, info.iv)) {
+ decryptedStringData = transform.TransformFinalBlock(encryptedStringData, 0, encryptedStringData.Length);
+ }
+ }
+ return Encoding.Unicode.GetString(decryptedStringData, 0, decryptedStringData.Length);
+ }
+ }
+ }
+}