From 8f2c5ffc1b96b197de41ac9d752de81e4503c811 Mon Sep 17 00:00:00 2001 From: Eusebiu Marcu Date: Fri, 5 Aug 2011 13:54:41 +0200 Subject: [PATCH] Use a custom assembly resolver when decompile a third party assembly. The order is: DefaultAssemblyResolver, folder where the decompiled assembly is located then GAC. --- .../ILSpyAddIn/ILSpyAddIn.csproj | 2 + .../ILSpyAddIn/LaunchILSpy/Fusion.cs | 216 ++++++++++++++++++ .../LaunchILSpy/ILSpyAssemblyResolver.cs | 118 ++++++++++ .../ViewContent/DecompiledViewContent.cs | 3 +- 4 files changed, 338 insertions(+), 1 deletion(-) create mode 100644 src/AddIns/DisplayBindings/ILSpyAddIn/LaunchILSpy/Fusion.cs create mode 100644 src/AddIns/DisplayBindings/ILSpyAddIn/LaunchILSpy/ILSpyAssemblyResolver.cs diff --git a/src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyAddIn.csproj b/src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyAddIn.csproj index 44512a97fa..8bb7218718 100644 --- a/src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyAddIn.csproj +++ b/src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyAddIn.csproj @@ -64,6 +64,8 @@ Properties\GlobalAssemblyInfo.cs + + diff --git a/src/AddIns/DisplayBindings/ILSpyAddIn/LaunchILSpy/Fusion.cs b/src/AddIns/DisplayBindings/ILSpyAddIn/LaunchILSpy/Fusion.cs new file mode 100644 index 0000000000..b25db8aff6 --- /dev/null +++ b/src/AddIns/DisplayBindings/ILSpyAddIn/LaunchILSpy/Fusion.cs @@ -0,0 +1,216 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using System.Text; + +namespace ICSharpCode.ILSpyAddIn.LaunchILSpy +{ + // .NET Fusion COM interfaces + [ComImport(), Guid("E707DCDE-D1CD-11D2-BAB9-00C04F8ECEAE"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IAssemblyCache + { + [PreserveSig()] + int UninstallAssembly(uint dwFlags, + [MarshalAs(UnmanagedType.LPWStr)] string pszAssemblyName, + IntPtr pvReserved, + out uint pulDisposition); + + [PreserveSig()] + int QueryAssemblyInfo(uint dwFlags, + [MarshalAs(UnmanagedType.LPWStr)] string pszAssemblyName, + IntPtr pAsmInfo); + + [PreserveSig()] + int CreateAssemblyCacheItem(uint dwFlags, + IntPtr pvReserved, + out IAssemblyCacheItem ppAsmItem, + [MarshalAs(UnmanagedType.LPWStr)] string pszAssemblyName); + + [PreserveSig()] + int CreateAssemblyScavenger(out object ppAsmScavenger); + + [PreserveSig()] + int InstallAssembly(uint dwFlags, + [MarshalAs(UnmanagedType.LPWStr)] string pszManifestFilePath, + IntPtr pvReserved); + } + + [ComImport(), Guid("9E3AAEB4-D1CD-11D2-BAB9-00C04F8ECEAE"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IAssemblyCacheItem + { + void CreateStream([MarshalAs(UnmanagedType.LPWStr)] string pszName, + uint dwFormat, + uint dwFlags, + uint dwMaxSize, + out IStream ppStream); + + void IsNameEqual(IAssemblyName pName); + + void Commit(uint dwFlags); + + void MarkAssemblyVisible(uint dwFlags); + } + + [ComImport(), Guid("CD193BC0-B4BC-11D2-9833-00C04FC31D2E"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IAssemblyName + { + [PreserveSig()] + int SetProperty(uint PropertyId, IntPtr pvProperty, uint cbProperty); + + [PreserveSig()] + int GetProperty(uint PropertyId, IntPtr pvProperty, ref uint pcbProperty); + + [PreserveSig()] + int Finalize(); + + [PreserveSig()] + int GetDisplayName([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder szDisplayName, + ref uint pccDisplayName, + uint dwDisplayFlags); + + [PreserveSig()] + int BindToObject(object refIID, + object pAsmBindSink, + IApplicationContext pApplicationContext, + [MarshalAs(UnmanagedType.LPWStr)] string szCodeBase, + long llFlags, + int pvReserved, + uint cbReserved, + out int ppv); + + [PreserveSig()] + int GetName(ref uint lpcwBuffer, + [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwzName); + + [PreserveSig()] + int GetVersion(out uint pdwVersionHi, out uint pdwVersionLow); + + [PreserveSig()] + int IsEqual(IAssemblyName pName, + uint dwCmpFlags); + + [PreserveSig()] + int Clone(out IAssemblyName pName); + } + + [ComImport(), Guid("7C23FF90-33AF-11D3-95DA-00A024A85B51"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IApplicationContext + { + void SetContextNameObject(IAssemblyName pName); + + void GetContextNameObject(out IAssemblyName ppName); + + void Set([MarshalAs(UnmanagedType.LPWStr)] string szName, + int pvValue, + uint cbValue, + uint dwFlags); + + void Get([MarshalAs(UnmanagedType.LPWStr)] string szName, + out int pvValue, + ref uint pcbValue, + uint dwFlags); + + void GetDynamicDirectory(out int wzDynamicDir, + ref uint pdwSize); + } + + [ComImport(), Guid("21B8916C-F28E-11D2-A473-00C04F8EF448"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IAssemblyEnum + { + [PreserveSig()] + int GetNextAssembly(out IApplicationContext ppAppCtx, + out IAssemblyName ppName, + uint dwFlags); + + [PreserveSig()] + int Reset(); + + [PreserveSig()] + int Clone(out IAssemblyEnum ppEnum); + } + + + [ComImport(), Guid("1D23DF4D-A1E2-4B8B-93D6-6EA3DC285A54"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IHistoryReader + { + [PreserveSig()] + int GetFilePath([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder wzFilePath, + ref uint pdwSize); + + [PreserveSig()] + int GetApplicationName([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder wzAppName, + ref uint pdwSize); + + [PreserveSig()] + int GetEXEModulePath([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder wzExePath, + ref uint pdwSize); + + void GetNumActivations(out uint pdwNumActivations); + + void GetActivationDate(uint dwIdx, // One-based! + out long /* FILETIME */ pftDate); + + [PreserveSig()] + int GetRunTimeVersion(ref long /* FILETIME */ pftActivationDate, + [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder wzRunTimeVersion, + ref uint pdwSize); + + void GetNumAssemblies(ref long /* FILETIME */ pftActivationDate, + out uint pdwNumAsms); + + void GetHistoryAssembly(ref long /* FILETIME */ pftActivationDate, + uint dwIdx, // One-based! + [MarshalAs(UnmanagedType.IUnknown)] out object ppHistAsm); + + } + + internal static class Fusion + { + [DllImport("fusion.dll", CharSet=CharSet.Auto)] + internal static extern int CreateAssemblyCache(out IAssemblyCache ppAsmCache, + uint dwReserved); + + // dwFlags: 1 = Enumerate native image (NGEN) assemblies + // 2 = Enumerate GAC assemblies + // 4 = Enumerate Downloaded assemblies + // + [DllImport("fusion.dll", CharSet=CharSet.Auto)] + internal static extern int CreateAssemblyEnum(out IAssemblyEnum ppEnum, + IApplicationContext pAppCtx, + IAssemblyName pName, + uint dwFlags, + int pvReserved); + + [DllImport("fusion.dll", CharSet=CharSet.Auto)] + internal static extern int CreateAssemblyNameObject(out IAssemblyName ppName, + string szAssemblyName, + uint dwFlags, + int pvReserved); + + // ????? + [DllImport("fusion.dll")] + internal static extern int CreateApplicationContext(out IApplicationContext ppAppContext, + uint dw); + + [DllImport("fusion.dll")] + internal static extern int GetCachePath(uint flags, + [MarshalAs(UnmanagedType.LPWStr)] StringBuilder wzDir, + ref uint pdwSize); + + public static string GetGacPath(bool isCLRv4 = false) + { + const uint ASM_CACHE_ROOT = 0x08; // CLR V2.0 + const uint ASM_CACHE_ROOT_EX = 0x80; // CLR V4.0 + uint flags = isCLRv4 ? ASM_CACHE_ROOT_EX : ASM_CACHE_ROOT; + + const int size = 260; // MAX_PATH + StringBuilder b = new StringBuilder(size); + uint tmp = size; + GetCachePath(flags, b, ref tmp); + return b.ToString(); + } + } +} diff --git a/src/AddIns/DisplayBindings/ILSpyAddIn/LaunchILSpy/ILSpyAssemblyResolver.cs b/src/AddIns/DisplayBindings/ILSpyAddIn/LaunchILSpy/ILSpyAssemblyResolver.cs new file mode 100644 index 0000000000..8c4771895a --- /dev/null +++ b/src/AddIns/DisplayBindings/ILSpyAddIn/LaunchILSpy/ILSpyAssemblyResolver.cs @@ -0,0 +1,118 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.IO; +using System.Linq; +using System.Text; + +using Mono.Cecil; + +namespace ICSharpCode.ILSpyAddIn.LaunchILSpy +{ + class ILSpyAssemblyResolver : DefaultAssemblyResolver + { + readonly DirectoryInfo directoryInfo; + + public ILSpyAssemblyResolver(string decompiledAssemblyFolder) + { + if (string.IsNullOrEmpty(decompiledAssemblyFolder)) + throw new ArgumentException("Invalid working folder"); + + this.directoryInfo = new DirectoryInfo(decompiledAssemblyFolder); + } + + public override AssemblyDefinition Resolve(AssemblyNameReference name) + { + try { + // search default + var defaultAssembly = base.Resolve(name); + if (defaultAssembly != null) + return defaultAssembly; + } catch (AssemblyResolutionException) { + // serach into assemblyDecompiledFolder + var file = this.directoryInfo.GetFiles() + .Where(f => f != null && f.FullName.Contains(name.Name)) + .FirstOrDefault(); + if (file != null) + return AssemblyDefinition.ReadAssembly(file.FullName); + + // search using ILSpy's GacInterop.FindAssemblyInNetGac() + string fileInGac = FindAssemblyInNetGac(name); + if (!string.IsNullOrEmpty(fileInGac)) + return AssemblyDefinition.ReadAssembly(fileInGac); + } + return null; + } + + #region FindAssemblyInGac + // This region is based on code from Mono.Cecil: + + // Author: + // Jb Evain (jbevain@gmail.com) + // + // Copyright (c) 2008 - 2010 Jb Evain + // + // Permission is hereby granted, free of charge, to any person obtaining + // a copy of this software and associated documentation files (the + // "Software"), to deal in the Software without restriction, including + // without limitation the rights to use, copy, modify, merge, publish, + // distribute, sublicense, and/or sell copies of the Software, and to + // permit persons to whom the Software is furnished to do so, subject to + // the following conditions: + // + // The above copyright notice and this permission notice shall be + // included in all copies or substantial portions of the Software. + // + // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + // + + static readonly string[] gac_paths = { Fusion.GetGacPath(false), Fusion.GetGacPath(true) }; + static readonly string[] gacs = { "GAC_MSIL", "GAC_32", "GAC" }; + static readonly string[] prefixes = { string.Empty, "v4.0_" }; + + /// + /// Gets the file name for an assembly stored in the GAC. + /// + public static string FindAssemblyInNetGac (AssemblyNameReference reference) + { + // without public key, it can't be in the GAC + if (reference.PublicKeyToken == null) + return null; + + for (int i = 0; i < 2; i++) { + for (int j = 0; j < gacs.Length; j++) { + var gac = Path.Combine (gac_paths [i], gacs [j]); + var file = GetAssemblyFile (reference, prefixes [i], gac); + if (File.Exists (file)) + return file; + } + } + + return null; + } + + static string GetAssemblyFile (AssemblyNameReference reference, string prefix, string gac) + { + var gac_folder = new StringBuilder () + .Append (prefix) + .Append (reference.Version) + .Append ("__"); + + for (int i = 0; i < reference.PublicKeyToken.Length; i++) + gac_folder.Append (reference.PublicKeyToken [i].ToString ("x2")); + + return Path.Combine ( + Path.Combine ( + Path.Combine (gac, reference.Name), gac_folder.ToString ()), + reference.Name + ".dll"); + } + #endregion + } +} diff --git a/src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/DecompiledViewContent.cs b/src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/DecompiledViewContent.cs index 3237536bc5..b3b38cfc88 100644 --- a/src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/DecompiledViewContent.cs +++ b/src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/DecompiledViewContent.cs @@ -9,6 +9,7 @@ using System.Threading; using ICSharpCode.Core; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Ast; +using ICSharpCode.ILSpyAddIn.LaunchILSpy; using ICSharpCode.ILSpyAddIn.ViewContent; using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.Utils; @@ -151,7 +152,7 @@ namespace ICSharpCode.ILSpyAddIn ReaderParameters readerParameters = new ReaderParameters(); // Use new assembly resolver instance so that the AssemblyDefinitions can be garbage-collected // once the code is decompiled. - readerParameters.AssemblyResolver = new DefaultAssemblyResolver(); + readerParameters.AssemblyResolver = new ILSpyAssemblyResolver(Path.GetDirectoryName(assemblyFile)); ModuleDefinition module = ModuleDefinition.ReadModule(assemblyFile, readerParameters); TypeDefinition typeDefinition = module.GetType(fullTypeName);