From cf2a86066ca85f74478f7c2ccb407f423322891f Mon Sep 17 00:00:00 2001 From: de4dot Date: Wed, 28 Sep 2011 04:30:00 +0200 Subject: [PATCH] Added an assembly resolver handler --- AssemblyData/AssemblyData.csproj | 2 + AssemblyData/AssemblyResolver.cs | 128 +++++++++++++++++++++++++++++++ AssemblyData/AssemblyService.cs | 4 +- AssemblyData/Utils.cs | 14 ++++ 4 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 AssemblyData/AssemblyResolver.cs diff --git a/AssemblyData/AssemblyData.csproj b/AssemblyData/AssemblyData.csproj index 0338f065..43f1a3dd 100644 --- a/AssemblyData/AssemblyData.csproj +++ b/AssemblyData/AssemblyData.csproj @@ -34,6 +34,7 @@ AnyCPU + @@ -60,6 +61,7 @@ + diff --git a/AssemblyData/AssemblyResolver.cs b/AssemblyData/AssemblyResolver.cs new file mode 100644 index 00000000..6d8d47ce --- /dev/null +++ b/AssemblyData/AssemblyResolver.cs @@ -0,0 +1,128 @@ +/* + 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.Reflection; +using System.Collections.Generic; +using System.IO; +using System.Xml; + +namespace AssemblyData { + class AssemblyResolver { + Dictionary assemblies = new Dictionary(StringComparer.Ordinal); + Dictionary assemblySearchPathsDict = new Dictionary(StringComparer.OrdinalIgnoreCase); + List assemblySearchPaths = new List(); + + public AssemblyResolver() { + AppDomain.CurrentDomain.AssemblyResolve += assemblyResolve; + } + + void addAssemblySearchPath(string path) { + if (assemblySearchPathsDict.ContainsKey(path)) + return; + assemblySearchPathsDict[path] = true; + assemblySearchPaths.Add(path); + } + + Assembly get(string assemblyFullName) { + var asmName = new AssemblyName(assemblyFullName); + + Assembly assembly; + if (assemblies.TryGetValue(asmName.FullName, out assembly)) + return assembly; + if (assemblies.TryGetValue(asmName.Name, out assembly)) + return assembly; + + return null; + } + + static string[] assemblyExtensions = new string[] { ".dll", ".exe" }; + Assembly assemblyResolve(object sender, ResolveEventArgs args) { + var assembly = get(args.Name); + if (assembly != null) + return assembly; + + var asmName = new AssemblyName(args.Name); + foreach (var path in assemblySearchPaths) { + foreach (var ext in assemblyExtensions) { + string filename; + try { + filename = Path.Combine(path, asmName.Name + ext); + if (!new FileInfo(filename).Exists) + continue; + addConfigFile(filename + ".config"); + return addAssembly(Assembly.LoadFile(filename)); + } + catch (IOException) { + } + catch (BadImageFormatException) { + } + catch (ArgumentException) { + } + catch (NotSupportedException) { + } + catch (UnauthorizedAccessException) { + } + catch (System.Security.SecurityException) { + } + } + } + + return null; + } + + public Assembly load(string filename) { + addConfigFile(filename + ".config"); + return addAssembly(Assembly.LoadFrom(filename)); + } + + Assembly addAssembly(Assembly assembly) { + var asmName = assembly.GetName(); + assemblies[asmName.FullName] = assembly; + assemblies[asmName.Name] = assembly; + return assembly; + } + + void addConfigFile(string configFilename) { + var dirName = Utils.getDirName(Utils.getFullPath(configFilename)); + addAssemblySearchPath(dirName); + + try { + using (var xmlStream = new FileStream(configFilename, FileMode.Open)) { + var doc = new XmlDocument(); + doc.Load(XmlReader.Create(xmlStream)); + foreach (var tmp in doc.GetElementsByTagName("probing")) { + var probingElem = tmp as XmlElement; + if (probingElem == null) + continue; + var privatePath = probingElem.GetAttribute("privatePath"); + if (string.IsNullOrEmpty(privatePath)) + continue; + foreach (var path in privatePath.Split(';')) + addAssemblySearchPath(Path.Combine(dirName, path)); + } + } + } + catch (IOException) { + } + catch (XmlException) { + } + } + } +} diff --git a/AssemblyData/AssemblyService.cs b/AssemblyData/AssemblyService.cs index abf652fb..c67c0f2c 100644 --- a/AssemblyData/AssemblyService.cs +++ b/AssemblyData/AssemblyService.cs @@ -18,6 +18,7 @@ */ using System; +using System.Collections.Generic; using System.Reflection; using System.Threading; @@ -26,6 +27,7 @@ namespace AssemblyData { IStringDecrypter stringDecrypter = null; ManualResetEvent exitEvent = new ManualResetEvent(false); Assembly assembly = null; + AssemblyResolver assemblyResolver = new AssemblyResolver(); public void doNothing() { } @@ -44,7 +46,7 @@ namespace AssemblyData { if (assembly != null) throw new ApplicationException("Only one assembly can be explicitly loaded"); try { - assembly = Assembly.LoadFrom(filename); + assembly = assemblyResolver.load(filename); } catch (BadImageFormatException) { throw new ApplicationException(string.Format("Could not load assembly {0}. Maybe it's 32-bit or 64-bit only?", filename)); diff --git a/AssemblyData/Utils.cs b/AssemblyData/Utils.cs index b26a4cc4..5b1aa216 100644 --- a/AssemblyData/Utils.cs +++ b/AssemblyData/Utils.cs @@ -18,6 +18,7 @@ */ using System; +using System.IO; using System.Reflection; using System.Reflection.Emit; using System.Text; @@ -171,5 +172,18 @@ namespace AssemblyData { ilg.Emit(OpCodes.Call, method); ilg.Emit(OpCodes.Ret); } + + public static string getFullPath(string path) { + try { + return Path.GetFullPath(path); + } + catch (Exception) { + return path; + } + } + + public static string getDirName(string name) { + return Path.GetDirectoryName(name); + } } }