Add shared deobfuscator data/methods

This commit is contained in:
de4dot 2012-04-04 21:06:10 +02:00
parent 7c8259905b
commit 9cfe8431f6
16 changed files with 222 additions and 43 deletions

View File

@ -519,6 +519,16 @@ namespace de4dot.blocks {
return methodCalls;
}
public static bool hasString(MethodDefinition method, string s) {
if (method == null || method.Body == null)
return false;
foreach (var instr in method.Body.Instructions) {
if (instr.OpCode.Code == Code.Ldstr && (string)instr.Operand == s)
return true;
}
return false;
}
public static IList<string> getCodeStrings(MethodDefinition method) {
var strings = new List<string>();
if (method != null && method.Body != null) {

View File

@ -27,19 +27,23 @@ using Mono.Cecil;
namespace de4dot.code {
public class AssemblyResolver : DefaultAssemblyResolver {
public static readonly AssemblyResolver Instance = new AssemblyResolver();
Dictionary<string, bool> addedAssemblies = new Dictionary<string, bool>(StringComparer.Ordinal);
Dictionary<string, bool> addedDirectories = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
static AssemblyResolver() {
Instance.resetSearchPaths();
}
void resetSearchPaths() {
addedDirectories.Clear();
addOtherAssemblySearchPaths();
}
static void addOtherAssemblySearchPaths() {
void addOtherAssemblySearchPaths() {
addOtherAssemblySearchPaths(Environment.GetEnvironmentVariable("ProgramFiles"));
addOtherAssemblySearchPaths(Environment.GetEnvironmentVariable("ProgramFiles(x86)"));
}
static void addOtherAssemblySearchPaths(string path) {
void addOtherAssemblySearchPaths(string path) {
if (string.IsNullOrEmpty(path))
return;
addSilverlightDirs(Path.Combine(path, @"Microsoft Silverlight"));
@ -86,7 +90,7 @@ namespace de4dot.code {
}
// basePath is eg. "C:\Program Files (x86)\Microsoft Silverlight"
static void addSilverlightDirs(string basePath) {
void addSilverlightDirs(string basePath) {
try {
var di = new DirectoryInfo(basePath);
foreach (var dir in di.GetDirectories()) {
@ -98,7 +102,7 @@ namespace de4dot.code {
}
}
static void addIfExists(string basePath, string extraPath) {
void addIfExists(string basePath, string extraPath) {
try {
var path = Path.Combine(basePath, extraPath);
if (Utils.pathExists(path))
@ -125,10 +129,7 @@ namespace de4dot.code {
var assembly = module.Assembly;
if (assembly != null) {
var name = assembly.Name.FullName;
if (!addedAssemblies.ContainsKey(name) && cache.ContainsKey(name))
throw new ApplicationException(string.Format("Assembly {0} was loaded by other code.", name));
addedAssemblies[name] = true;
RegisterAssembly(assembly);
cache[name] = assembly;
}
}
@ -169,13 +170,12 @@ namespace de4dot.code {
public void removeModule(string asmFullName) {
if (string.IsNullOrEmpty(asmFullName))
return;
addedAssemblies.Remove(asmFullName);
cache.Remove(asmFullName);
}
public void clearAll() {
addedAssemblies.Clear();
cache.Clear();
resetSearchPaths();
}
}
}

View File

@ -0,0 +1,99 @@
/*
Copyright (C) 2011-2012 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using Mono.Cecil;
using de4dot.blocks;
namespace de4dot.code {
// "global" data and methods that is shared between all deobfuscators that deobfuscate
// assemblies at the same time.
public class DeobfuscatorContext : IDeobfuscatorContext {
ExternalAssemblies externalAssemblies = new ExternalAssemblies();
Dictionary<string, object> dataDict = new Dictionary<string, object>(StringComparer.Ordinal);
public void clear() {
dataDict.Clear();
externalAssemblies.unloadAll();
}
public void setData(string name, object data) {
dataDict[name] = data;
}
public object getData(string name) {
object value;
dataDict.TryGetValue(name, out value);
return value;
}
public void clearData(string name) {
dataDict.Remove(name);
}
public TypeDefinition resolve(TypeReference type) {
if (type == null)
return null;
var typeDef = type as TypeDefinition;
if (typeDef != null)
return typeDef;
return externalAssemblies.resolve(type);
}
public MethodDefinition resolve(MethodReference method) {
if (method == null)
return null;
var methodDef = method as MethodDefinition;
if (methodDef != null)
return methodDef;
var type = resolve(method.DeclaringType);
if (type == null)
return null;
foreach (var m in type.Methods) {
if (MemberReferenceHelper.compareMethodReference(method, m))
return m;
}
return null;
}
public FieldDefinition resolve(FieldReference field) {
if (field == null)
return null;
var fieldDef = field as FieldDefinition;
if (fieldDef != null)
return fieldDef;
var type = resolve(field.DeclaringType);
if (type == null)
return null;
foreach (var f in type.Fields) {
if (MemberReferenceHelper.compareFieldReference(field, f))
return f;
}
return null;
}
}
}

View File

@ -17,6 +17,7 @@
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using Mono.Cecil;
using de4dot.blocks;
@ -50,7 +51,8 @@ namespace de4dot.code {
// Loads assemblies that aren't renamed
class ExternalAssemblies {
Dictionary<string, ExternalAssembly> assemblies = new Dictionary<string, ExternalAssembly>();
Dictionary<string, ExternalAssembly> assemblies = new Dictionary<string, ExternalAssembly>(StringComparer.Ordinal);
Dictionary<string, bool> failedLoads = new Dictionary<string, bool>(StringComparer.Ordinal);
ExternalAssembly load(TypeReference type) {
var asmFullName = DotNetUtils.getFullAssemblyName(type);
@ -67,9 +69,9 @@ namespace de4dot.code {
catch (AssemblyResolutionException) {
}
if (asmDef == null) {
// If we can't load it now, we can't load it later. Make sure above code returns null.
assemblies[asmFullName] = null;
Log.w("Could not load assembly {0}", asmFullName);
if (!failedLoads.ContainsKey(asmFullName))
Log.w("Could not load assembly {0}", asmFullName);
failedLoads[asmFullName] = true;
return null;
}
if (assemblies.ContainsKey(asmDef.Name.FullName)) {
@ -89,6 +91,8 @@ namespace de4dot.code {
}
public TypeDefinition resolve(TypeReference type) {
if (type == null)
return null;
var asm = load(type);
if (asm == null)
return null;
@ -102,6 +106,7 @@ namespace de4dot.code {
pair.Value.unload(pair.Key);
}
assemblies.Clear();
failedLoads.Clear();
}
}
}

View File

@ -0,0 +1,32 @@
/*
Copyright (C) 2011-2012 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 <http://www.gnu.org/licenses/>.
*/
using Mono.Cecil;
namespace de4dot.code {
public interface IDeobfuscatorContext {
void clear();
void setData(string name, object data);
object getData(string name);
void clearData(string name);
TypeDefinition resolve(TypeReference type);
MethodDefinition resolve(MethodReference method);
FieldDefinition resolve(FieldReference field);
}
}

View File

@ -26,6 +26,7 @@ namespace de4dot.code {
public interface IObfuscatedFile {
ModuleDefinition ModuleDefinition { get; }
IDeobfuscator Deobfuscator { get; }
IDeobfuscatorContext DeobfuscatorContext { get; set; }
string Filename { get; }
string NewFilename { get; }
INameChecker NameChecker { get; }

View File

@ -38,6 +38,7 @@ namespace de4dot.code {
ModuleDefinition module;
IList<MethodDefinition> allMethods;
IDeobfuscator deob;
IDeobfuscatorContext deobfuscatorContext;
AssemblyModule assemblyModule;
IAssemblyClient assemblyClient;
DynamicStringInliner dynamicStringInliner;
@ -123,6 +124,11 @@ namespace de4dot.code {
get { return deob; }
}
public IDeobfuscatorContext DeobfuscatorContext {
get { return deobfuscatorContext; }
set { deobfuscatorContext = value; }
}
public ObfuscatedFile(Options options, IAssemblyClientFactory assemblyClientFactory) {
this.assemblyClientFactory = assemblyClientFactory;
this.options = options;

View File

@ -58,6 +58,7 @@
<Compile Include="AssemblyClient\NewProcessAssemblyServerLoader.cs" />
<Compile Include="AssemblyClient\SameAppDomainAssemblyServerLoader.cs" />
<Compile Include="AssemblyResolver.cs" />
<Compile Include="DeobfuscatorContext.cs" />
<Compile Include="deobfuscators\ArrayFinder.cs" />
<Compile Include="deobfuscators\Babel_NET\AssemblyResolver.cs" />
<Compile Include="deobfuscators\Babel_NET\BabelUtils.cs" />
@ -204,6 +205,7 @@
<Compile Include="deobfuscators\ValueInlinerBase.cs" />
<Compile Include="deobfuscators\Xenocode\Deobfuscator.cs" />
<Compile Include="deobfuscators\Xenocode\StringDecrypter.cs" />
<Compile Include="IDeobfuscatorContext.cs" />
<Compile Include="IObfuscatedFile.cs" />
<Compile Include="Log.cs" />
<Compile Include="AssemblyModule.cs" />

View File

@ -141,7 +141,7 @@ namespace de4dot.code.deobfuscators.Babel_NET {
constantsDecrypter.find();
proxyDelegateFinder = new ProxyDelegateFinder(module);
proxyDelegateFinder.findDelegateCreator();
methodsDecrypter = new MethodsDecrypter(module);
methodsDecrypter = new MethodsDecrypter(module, DeobfuscatedFile.DeobfuscatorContext);
methodsDecrypter.find();
}
@ -256,7 +256,6 @@ namespace de4dot.code.deobfuscators.Babel_NET {
}
removeProxyDelegates(proxyDelegateFinder);
methodsDecrypter.Dispose();
base.deobfuscateEnd();
}

View File

@ -27,7 +27,7 @@ using Mono.Cecil.Cil;
using de4dot.blocks;
namespace de4dot.code.deobfuscators.Babel_NET {
class ImageReader : IDisposable {
class ImageReader {
static int METHODS_SIG = 0x0000BEBA;
static int METADATA_SIG = 0x0100BEBA;
static int METHOD_NAMES_SIG = 0x0200BEBA;
@ -50,9 +50,10 @@ namespace de4dot.code.deobfuscators.Babel_NET {
Dictionary<string, int> methodOffsets;
List<TypeReference> typeReferences;
MemberReferenceConverter memberReferenceConverter;
ExternalAssemblies externalAssemblies = new ExternalAssemblies();
IDeobfuscatorContext deobfuscatorContext;
public ImageReader(ModuleDefinition module, byte[] data) {
public ImageReader(IDeobfuscatorContext deobfuscatorContext, ModuleDefinition module, byte[] data) {
this.deobfuscatorContext = deobfuscatorContext;
this.module = module;
this.reader = new BinaryReader(new MemoryStream(data));
this.memberReferenceConverter = new MemberReferenceConverter(module);
@ -74,10 +75,6 @@ namespace de4dot.code.deobfuscators.Babel_NET {
return true;
}
public void Dispose() {
externalAssemblies.unloadAll();
}
void initializeV10() {
reader.ReadInt16();
int methodNamesOffset = (int)reader.ReadInt64();
@ -254,7 +251,7 @@ namespace de4dot.code.deobfuscators.Babel_NET {
if (type.Module == module && isModuleAssembly(type.Scope))
return DotNetUtils.getType(module, type);
return externalAssemblies.resolve(type);
return deobfuscatorContext.resolve(type);
}
public CallSite readCallSite() {

View File

@ -25,8 +25,9 @@ using Mono.Cecil.Cil;
using de4dot.blocks;
namespace de4dot.code.deobfuscators.Babel_NET {
class MethodsDecrypter : IDisposable {
class MethodsDecrypter {
ModuleDefinition module;
IDeobfuscatorContext deobfuscatorContext;
Dictionary<string, ImageReader> imageReaders = new Dictionary<string, ImageReader>(StringComparer.Ordinal);
TypeDefinition methodsDecrypterCreator;
TypeDefinition methodsDecrypter;
@ -37,13 +38,9 @@ namespace de4dot.code.deobfuscators.Babel_NET {
get { return methodsDecrypterCreator != null; }
}
public MethodsDecrypter(ModuleDefinition module) {
public MethodsDecrypter(ModuleDefinition module, IDeobfuscatorContext deobfuscatorContext) {
this.module = module;
}
public void Dispose() {
foreach (var imageReader in imageReaders.Values)
imageReader.Dispose();
this.deobfuscatorContext = deobfuscatorContext;
}
public void find() {
@ -104,7 +101,7 @@ namespace de4dot.code.deobfuscators.Babel_NET {
}
void addImageReader(string name, byte[] data) {
var imageReader = new ImageReader(module, data);
var imageReader = new ImageReader(deobfuscatorContext, module, data);
if (!imageReader.initialize()) {
Log.w("Could not read encrypted methods");
return;
@ -147,9 +144,6 @@ namespace de4dot.code.deobfuscators.Babel_NET {
}
if (numNonDecryptedMethods > 0)
Log.w("{0}/{1} methods not decrypted", numNonDecryptedMethods, totalEncryptedMethods);
foreach (var imageReader in imageReaders.Values)
imageReader.Dispose();
}
List<EncryptInfo> getEncryptedMethods() {

View File

@ -48,6 +48,7 @@ namespace de4dot.code.deobfuscators {
IList<RemoveInfo<TypeDefinition>> attrsToRemove = new List<RemoveInfo<TypeDefinition>>();
IList<RemoveInfo<Resource>> resourcesToRemove = new List<RemoveInfo<Resource>>();
IList<RemoveInfo<ModuleReference>> modrefsToRemove = new List<RemoveInfo<ModuleReference>>();
IList<RemoveInfo<AssemblyNameReference>> asmrefsToRemove = new List<RemoveInfo<AssemblyNameReference>>();
List<string> namesToPossiblyRemove = new List<string>();
MethodCallRemover methodCallRemover = new MethodCallRemover();
byte[] moduleBytes;
@ -181,6 +182,7 @@ namespace de4dot.code.deobfuscators {
deleteDllResources();
deleteModuleReferences();
deleteAssemblyReferences();
}
restoreBaseType();
@ -377,6 +379,10 @@ namespace de4dot.code.deobfuscators {
modrefsToRemove.Add(new RemoveInfo<ModuleReference>(modref, reason));
}
protected void addAssemblyReferenceToBeRemoved(AssemblyNameReference asmRef, string reason) {
asmrefsToRemove.Add(new RemoveInfo<AssemblyNameReference>(asmRef, reason));
}
void deleteEmptyCctors() {
var emptyCctorsToRemove = new List<MethodDefinition>();
foreach (var type in module.GetTypes()) {
@ -553,6 +559,22 @@ namespace de4dot.code.deobfuscators {
Log.deIndent();
}
void deleteAssemblyReferences() {
if (!module.HasAssemblyReferences || asmrefsToRemove.Count == 0)
return;
Log.v("Removing assembly references");
Log.indent();
foreach (var info in asmrefsToRemove) {
var asmRef = info.obj;
if (asmRef == null)
continue;
if (module.AssemblyReferences.Remove(asmRef))
Log.v("Removed assembly reference {0} (reason: {1})", asmRef, info.reason);
}
Log.deIndent();
}
public override string ToString() {
return Name;
}

View File

@ -19,6 +19,7 @@
namespace de4dot.code.deobfuscators {
public interface IDeobfuscatedFile : ISimpleDeobfuscator {
IDeobfuscatorContext DeobfuscatorContext { get; }
void createAssemblyFile(byte[] data, string assemblyName, string extension = null);
void stringDecryptersAdded();
}

View File

@ -40,7 +40,7 @@ namespace de4dot.code.renamer {
public bool RestoreEvents { get; set; }
public bool RestoreEventsFromNames { get; set; }
Modules modules = new Modules();
Modules modules;
MemberInfos memberInfos = new MemberInfos();
DerivedFrom isDelegateClass;
MergeStateHelper mergeStateHelper;
@ -50,7 +50,7 @@ namespace de4dot.code.renamer {
"System.MulticastDelegate",
};
public Renamer(IEnumerable<IObfuscatedFile> files) {
public Renamer(IDeobfuscatorContext deobfuscatorContext, IEnumerable<IObfuscatedFile> files) {
RenameNamespaces = true;
RenameTypes = true;
RenameProperties = true;
@ -64,6 +64,7 @@ namespace de4dot.code.renamer {
RestoreEvents = true;
RestoreEventsFromNames = true;
modules = new Modules(deobfuscatorContext);
isDelegateClass = new DerivedFrom(delegateClasses);
mergeStateHelper = new MergeStateHelper(memberInfos);

View File

@ -25,6 +25,7 @@ using de4dot.blocks;
namespace de4dot.code.renamer.asmmodules {
class Modules : IResolver {
bool initializeCalled = false;
IDeobfuscatorContext deobfuscatorContext;
List<Module> modules = new List<Module>();
Dictionary<ModuleDefinition, Module> modulesDict = new Dictionary<ModuleDefinition, Module>();
AssemblyHash assemblyHash = new AssemblyHash();
@ -131,6 +132,10 @@ namespace de4dot.code.renamer.asmmodules {
get { return modules.Count == 0; }
}
public Modules(IDeobfuscatorContext deobfuscatorContext) {
this.deobfuscatorContext = deobfuscatorContext;
}
public void add(Module module) {
if (initializeCalled)
throw new ApplicationException("initialize() has been called");
@ -323,7 +328,6 @@ namespace de4dot.code.renamer.asmmodules {
}
AssemblyKeyDictionary<TypeDef> typeToTypeDefDict = new AssemblyKeyDictionary<TypeDef>();
ExternalAssemblies externalAssemblies = new ExternalAssemblies();
public TypeDef resolveOther(TypeReference type) {
if (type == null)
return null;
@ -333,7 +337,7 @@ namespace de4dot.code.renamer.asmmodules {
if (typeToTypeDefDict.tryGetValue(type, out typeDef))
return typeDef;
var typeDefinition = externalAssemblies.resolve(type);
var typeDefinition = deobfuscatorContext.resolve(type);
if (typeDefinition == null) {
typeToTypeDefDict.tryGetSimilarValue(type, out typeDef);
typeToTypeDefDict[type] = typeDef;
@ -379,7 +383,6 @@ namespace de4dot.code.renamer.asmmodules {
}
public void cleanUp() {
externalAssemblies.unloadAll();
foreach (var module in DotNetUtils.typeCaches.invalidateAll())
AssemblyResolver.Instance.removeModule(module);
}

View File

@ -30,6 +30,7 @@ using de4dot.code.AssemblyClient;
namespace de4dot.cui {
class FilesDeobfuscator {
Options options;
IDeobfuscatorContext deobfuscatorContext = new DeobfuscatorContext();
public class Options {
public IList<IDeobfuscatorInfo> DeobfuscatorInfos { get; set; }
@ -81,8 +82,10 @@ namespace de4dot.cui {
}
void detectObfuscators() {
foreach (var file in loadAllFiles(true))
foreach (var file in loadAllFiles(true)) {
removeModule(file.ModuleDefinition);
deobfuscatorContext.clear();
}
}
void deobfuscateOneAtATime() {
@ -97,6 +100,7 @@ namespace de4dot.cui {
removeModule(file.ModuleDefinition);
AssemblyResolver.Instance.clearAll();
deobfuscatorContext.clear();
}
catch (Exception ex) {
Log.w("Could not deobfuscate {0}. Use -v to see stack trace", file.Filename);
@ -124,6 +128,7 @@ namespace de4dot.cui {
DefaultStringDecrypterType = options.DefaultStringDecrypterType,
DefaultStringDecrypterMethods = options.DefaultStringDecrypterMethods,
AssemblyClientFactory = options.AssemblyClientFactory,
DeobfuscatorContext = deobfuscatorContext,
ControlFlowDeobfuscation = options.ControlFlowDeobfuscation,
KeepObfuscatorTypes = options.KeepObfuscatorTypes,
CreateDestinationDir = !onlyScan,
@ -154,6 +159,7 @@ namespace de4dot.cui {
public DecrypterType? DefaultStringDecrypterType { get; set; }
public List<string> DefaultStringDecrypterMethods { get; set; }
public IAssemblyClientFactory AssemblyClientFactory { get; set; }
public IDeobfuscatorContext DeobfuscatorContext { get; set; }
public bool ControlFlowDeobfuscation { get; set; }
public bool KeepObfuscatorTypes { get; set; }
public bool CreateDestinationDir { get; set; }
@ -185,6 +191,7 @@ namespace de4dot.cui {
int oldIndentLevel = Log.indentLevel;
try {
file.DeobfuscatorContext = options.DeobfuscatorContext;
file.load(options.CreateDeobfuscators());
}
catch (NotSupportedException) {
@ -351,7 +358,7 @@ namespace de4dot.cui {
void rename(IEnumerable<IObfuscatedFile> theFiles) {
if (!options.RenameSymbols)
return;
var renamer = new Renamer(theFiles) {
var renamer = new Renamer(deobfuscatorContext, theFiles) {
RestorePropertiesFromNames = options.RestorePropsEvents,
RestoreEventsFromNames = options.RestorePropsEvents,
};