From 91657ca4f0f831a7b6a1b242936bb474ea73bbab Mon Sep 17 00:00:00 2001 From: takeshix Date: Sat, 28 Apr 2018 16:42:13 +0200 Subject: [PATCH] Initial commit --- CosturaPlugin/ContextMenuCommand.cs | 97 +++++++++++++++++++ CosturaPlugin/CosturaPlugin.csproj | 55 +++++++++++ CosturaPlugin/Load.png | Bin 0 -> 8043 bytes CosturaPlugin/Properties/AssemblyInfo.cs | 30 ++++++ CosturaPlugin/Properties/launchSettings.json | 9 ++ README.md | 19 +++- 6 files changed, 208 insertions(+), 2 deletions(-) create mode 100644 CosturaPlugin/ContextMenuCommand.cs create mode 100644 CosturaPlugin/CosturaPlugin.csproj create mode 100644 CosturaPlugin/Load.png create mode 100644 CosturaPlugin/Properties/AssemblyInfo.cs create mode 100644 CosturaPlugin/Properties/launchSettings.json diff --git a/CosturaPlugin/ContextMenuCommand.cs b/CosturaPlugin/ContextMenuCommand.cs new file mode 100644 index 0000000..10dd6cf --- /dev/null +++ b/CosturaPlugin/ContextMenuCommand.cs @@ -0,0 +1,97 @@ +using System.IO; +using System.IO.Compression; +using System.Reflection; +using System.Linq; +using System.Windows.Forms; +using ICSharpCode.ILSpy; +using ICSharpCode.ILSpy.TreeNodes; +using Mono.Cecil; +using Mono.Collections.Generic; + + +namespace CosturaPlugin +{ + [ExportContextMenuEntryAttribute(Header = "_Load Embedded Resources", Icon = "Load.png")] + public class SaveAssembly : IContextMenuEntry + { + public bool IsVisible(TextViewContext context) + { + return context.SelectedTreeNodes != null && context.SelectedTreeNodes.All(n => n is AssemblyTreeNode); + } + + public bool IsEnabled(TextViewContext context) + { + return context.SelectedTreeNodes != null && context.SelectedTreeNodes.Length == 1; + } + + public void Execute(TextViewContext context) + { + if (context.SelectedTreeNodes == null) + return; + AssemblyTreeNode node = (AssemblyTreeNode)context.SelectedTreeNodes[0]; + AssemblyDefinition asm = node.LoadedAssembly.GetAssemblyDefinitionOrNull(); + ModuleDefinition module = asm.MainModule; + string assemblyPath = Path.GetDirectoryName(node.LoadedAssembly.FileName); + Collection compressed_resources = new Collection(); + foreach (var resource in module.Resources) { + if (resource.Name.StartsWith("costura.") && resource.Name.EndsWith(".dll.compressed")) { + compressed_resources.Add(resource); + + } + } + foreach (var resource in compressed_resources) { + string fileName = assemblyPath + "/" + resource.Name.Substring(8, resource.Name.LastIndexOf(".compressed") - 8); + if (File.Exists(fileName)) { + // Assembly has already been decompressed and saved in the local path, just load it. + MainWindow.Instance.CurrentAssemblyList.OpenAssembly(fileName); + } else { + EmbeddedResource er = resource as EmbeddedResource; + MemoryStream memoryStream = DecompressEmbeddedAssembly(er.GetResourceStream()); + WriteAssemblyToFile(memoryStream, fileName); + OpenAssemblyFromStream(memoryStream, fileName); + } + } + } + + private static void PrintDebugWindow(string message) + { + string caption = "CosturaPlugin Debug Output"; + MessageBoxButtons buttons = MessageBoxButtons.OK; + MessageBox.Show(message, caption, buttons); + } + + private static void OpenAssemblyFromStream(Stream stream, string fileName) + { + stream.Position = 0L; + MainWindow.Instance.CurrentAssemblyList.OpenAssembly(fileName, stream); + } + + private static MemoryStream DecompressEmbeddedAssembly(Stream embeded_resource) + { + Assembly executingAssembly = Assembly.GetExecutingAssembly(); + DeflateStream source = new DeflateStream(embeded_resource, CompressionMode.Decompress); + MemoryStream memoryStream = new MemoryStream(); + CopyTo(source, memoryStream); + memoryStream.Position = 0L; + return memoryStream; + } + + private static void CopyTo(Stream source, Stream destination) + { + byte[] array = new byte[81920]; + int count; + while ((count = source.Read(array, 0, array.Length)) != 0) { + destination.Write(array, 0, count); + } + } + + private static void WriteAssemblyToFile(MemoryStream memoryStream, string fileName) + { + memoryStream.Position = 0L; + using (FileStream output = new FileStream(fileName, FileMode.Create)) { + memoryStream.CopyTo(output); + } + } + + } +} diff --git a/CosturaPlugin/CosturaPlugin.csproj b/CosturaPlugin/CosturaPlugin.csproj new file mode 100644 index 0000000..5a3aa27 --- /dev/null +++ b/CosturaPlugin/CosturaPlugin.csproj @@ -0,0 +1,55 @@ + + + + + net461 + Costura.Plugin + + False + + False + + + + full + true + True + + + + pdbonly + true + + + + + $(MSBuildExtensionsPath)\$(VisualStudioVersion)\Bin\Microsoft.CSharp.targets + + + + + + + + C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6\System.Windows.Forms.dll + + + + + + + + + + + + + + + + + + + + + diff --git a/CosturaPlugin/Load.png b/CosturaPlugin/Load.png new file mode 100644 index 0000000000000000000000000000000000000000..549bddd281f61c9ac3cdbb398dbd97827e2c35aa GIT binary patch literal 8043 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 O0H`QwDpo64g#HilA7v5% literal 0 HcmV?d00001 diff --git a/CosturaPlugin/Properties/AssemblyInfo.cs b/CosturaPlugin/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..779a057 --- /dev/null +++ b/CosturaPlugin/Properties/AssemblyInfo.cs @@ -0,0 +1,30 @@ +#region Using directives + +using System.Reflection; +using System.Runtime.InteropServices; + +#endregion + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("CosturaPlugin")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("CosturaPlugin")] +[assembly: AssemblyCopyright("Copyright 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// This sets the default COM visibility of types in the assembly to invisible. +// If you need to expose a type to COM, use [ComVisible(true)] on that type. +[assembly: ComVisible(false)] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all the values or you can use the default the Revision and +// Build Numbers by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] diff --git a/CosturaPlugin/Properties/launchSettings.json b/CosturaPlugin/Properties/launchSettings.json new file mode 100644 index 0000000..da461db --- /dev/null +++ b/CosturaPlugin/Properties/launchSettings.json @@ -0,0 +1,9 @@ +{ + "profiles": { + "CosturaPlugin": { + "commandName": "Executable", + "executablePath": "$(OutDir)ILSpy.exe", + "commandLineArgs": "/separate" + } + } +} diff --git a/README.md b/README.md index 603f232..cc80af7 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,17 @@ -# ILSpy-CosturaPlugin -A plugin for ILSpy that loads references that have been embedded as resources with Costura. +# Costura Plugin for ILSpy + +[Costura](https://github.com/Fody/Costura) is an add-in for [Fody](https://github.com/Fody/Fody/) which allows to embedd references in binaries as resources. This means that e.g. all the DLL files that are required by a binary are added as compressed resources in the new binary. They are loaded with some compiled-in trampolin code by Costura. This plugin adds decompression and loading support for such embedded references to ILSpy to make decompilation of binaries compiled with Costura easier. + +## Installation + +A pre-built DLL is available in the [release section](https://github.com/takeshixx/ILSpy-CosturaPlugin/releases). Just copy it to the same directory where the `ILSpy.exe` resides and run `ILSpy.exe`. + +## Building + +Clone the ILSpy repository: + +``` +git clone https://github.com/icsharpcode/ILSpy.git +``` + +Copy the `CosturaPlugin` folder to the ILSpy directory. Then open `ILSpy.sln` in Visual Studio and add `CosturaPlugin/CosturaPlugin.csproj` as existing project. Then just build the `CosturaPlugin` project.