├── CosturaPlugin
├── Images
│ ├── Chop.png
│ └── Load.png
├── Properties
│ ├── launchSettings.json
│ └── AssemblyInfo.cs
├── CosturaPlugin.csproj
└── ContextMenuCommand.cs
├── LICENSE
└── README.md
/CosturaPlugin/Images/Chop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/takeshixx/ILSpy-CosturaPlugin/HEAD/CosturaPlugin/Images/Chop.png
--------------------------------------------------------------------------------
/CosturaPlugin/Images/Load.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/takeshixx/ILSpy-CosturaPlugin/HEAD/CosturaPlugin/Images/Load.png
--------------------------------------------------------------------------------
/CosturaPlugin/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "CosturaPlugin": {
4 | "commandName": "Executable",
5 | "executablePath": "$(OutDir)ILSpy.exe",
6 | "commandLineArgs": "/separate"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 takeshix
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/CosturaPlugin/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | #region Using directives
2 |
3 | using System.Reflection;
4 | using System.Runtime.InteropServices;
5 |
6 | #endregion
7 |
8 | // General Information about an assembly is controlled through the following
9 | // set of attributes. Change these attribute values to modify the information
10 | // associated with an assembly.
11 | [assembly: AssemblyTitle("CosturaPlugin")]
12 | [assembly: AssemblyDescription("")]
13 | [assembly: AssemblyConfiguration("")]
14 | [assembly: AssemblyCompany("")]
15 | [assembly: AssemblyProduct("CosturaPlugin")]
16 | [assembly: AssemblyCopyright("Copyright 2018")]
17 | [assembly: AssemblyTrademark("")]
18 | [assembly: AssemblyCulture("")]
19 |
20 | // This sets the default COM visibility of types in the assembly to invisible.
21 | // If you need to expose a type to COM, use [ComVisible(true)] on that type.
22 | [assembly: ComVisible(false)]
23 |
24 | // The assembly version has following format :
25 | //
26 | // Major.Minor.Build.Revision
27 | //
28 | // You can specify all the values or you can use the default the Revision and
29 | // Build Numbers by using the '*' as shown below:
30 | [assembly: AssemblyVersion("1.1.0.0")]
31 |
--------------------------------------------------------------------------------
/CosturaPlugin/CosturaPlugin.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | net472
6 | Costura.Plugin
7 | False
8 |
9 |
10 |
11 | full
12 | true
13 | True
14 |
15 |
16 |
17 | pdbonly
18 | true
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Costura Plugin for ILSpy
2 |
3 | [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.
4 |
5 | ## Installation
6 |
7 | 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`.
8 |
9 |
10 | ## Usage
11 |
12 | This plugin will add two context menu items:
13 |
14 | * `Load Embedded References`: Embeded references will be decompressed and the original DLL files will be stored in the same path as the assembly they have been extracted from. The extracted DLL files will be added to ILSpy automatically.
15 | * `Remove Costura Module Initializer`: This option will remove the `AssemblyLoader.Attach();` call in the module initializer in order to ignore all embedded references. This is required if one wants to patch any of the embedded references and use them in the actual assembly. The original embedded references will still be in the resources of a binary, but they will be ignored in favour of the previously extracted DLL files.
16 |
17 |
18 | ## Building
19 |
20 | Clone the ILSpy repository:
21 |
22 | ```
23 | git clone https://github.com/icsharpcode/ILSpy.git
24 | ```
25 |
26 | 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.
27 |
--------------------------------------------------------------------------------
/CosturaPlugin/ContextMenuCommand.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.IO.Compression;
3 | using System.Reflection;
4 | using System.Linq;
5 | using System.Windows.Forms;
6 | using ICSharpCode.ILSpy;
7 | using ICSharpCode.ILSpy.TreeNodes;
8 | using Mono.Cecil;
9 | using Mono.Cecil.Cil;
10 |
11 |
12 | namespace CosturaPlugin
13 | {
14 | [ExportContextMenuEntryAttribute(Header = "_Load Embedded References", Category = "CosturaPlugin", Icon = "Images/Load.png")]
15 | public class LoadEmbeddedReferences : IContextMenuEntry
16 | {
17 | public bool IsVisible(TextViewContext context)
18 | {
19 | return context.SelectedTreeNodes != null && context.SelectedTreeNodes.All(n => n is AssemblyTreeNode);
20 | }
21 |
22 | public bool IsEnabled(TextViewContext context)
23 | {
24 | return context.SelectedTreeNodes != null && context.SelectedTreeNodes.Length == 1;
25 | }
26 |
27 | public void Execute(TextViewContext context)
28 | {
29 | if (context.SelectedTreeNodes == null)
30 | return;
31 | AssemblyTreeNode node = (AssemblyTreeNode)context.SelectedTreeNodes[0];
32 | AssemblyDefinition asm = AssemblyDefinition.ReadAssembly(node.LoadedAssembly.FileName);
33 | ModuleDefinition module = asm.MainModule;
34 | string assemblyPath = Path.GetDirectoryName(node.LoadedAssembly.FileName);
35 | foreach (var resource in module.Resources) {
36 | if (! resource.Name.StartsWith("costura.") && ! resource.Name.EndsWith(".dll.compressed")) {
37 | continue;
38 | }
39 | string fileName = assemblyPath + "/" + resource.Name.Substring(8, resource.Name.LastIndexOf(".compressed") - 8);
40 | if (File.Exists(fileName)) {
41 | // Assembly has already been decompressed and saved in the local path, just load it.
42 | MainWindow.Instance.CurrentAssemblyList.OpenAssembly(fileName);
43 | } else {
44 | EmbeddedResource er = resource as EmbeddedResource;
45 | MemoryStream memoryStream = DecompressEmbeddedAssembly(er.GetResourceStream());
46 | WriteAssemblyToFile(memoryStream, fileName);
47 | OpenAssemblyFromStream(memoryStream, fileName);
48 | }
49 | }
50 | }
51 |
52 | private static void PrintDebugWindow(string message)
53 | {
54 | string caption = "CosturaPlugin Debug Output";
55 | MessageBoxButtons buttons = MessageBoxButtons.OK;
56 | MessageBox.Show(message, caption, buttons);
57 | }
58 |
59 | private static void OpenAssemblyFromStream(Stream stream, string fileName)
60 | {
61 | stream.Position = 0L;
62 | MainWindow.Instance.CurrentAssemblyList.OpenAssembly(fileName, stream);
63 | }
64 |
65 | private static MemoryStream DecompressEmbeddedAssembly(Stream embeded_resource)
66 | {
67 | Assembly executingAssembly = Assembly.GetExecutingAssembly();
68 | DeflateStream source = new DeflateStream(embeded_resource, CompressionMode.Decompress);
69 | MemoryStream memoryStream = new MemoryStream();
70 | CopyTo(source, memoryStream);
71 | memoryStream.Position = 0L;
72 | return memoryStream;
73 | }
74 |
75 | private static void CopyTo(Stream source, Stream destination)
76 | {
77 | byte[] array = new byte[81920];
78 | int count;
79 | while ((count = source.Read(array, 0, array.Length)) != 0) {
80 | destination.Write(array, 0, count);
81 | }
82 | }
83 |
84 | private static void WriteAssemblyToFile(MemoryStream memoryStream, string fileName)
85 | {
86 | memoryStream.Position = 0L;
87 | using (FileStream output = new FileStream(fileName, FileMode.Create)) {
88 | memoryStream.CopyTo(output);
89 | }
90 | }
91 |
92 | }
93 |
94 | [ExportContextMenuEntryAttribute(Header = "_Remove Costura Module Initializer", Category = "CosturaPlugin", Icon = "Images/Chop.png")]
95 | public class DefuseCostura : IContextMenuEntry
96 | {
97 | public bool IsVisible(TextViewContext context)
98 | {
99 | return context.SelectedTreeNodes != null && context.SelectedTreeNodes.All(n => n is AssemblyTreeNode);
100 | }
101 |
102 | public bool IsEnabled(TextViewContext context)
103 | {
104 | return context.SelectedTreeNodes != null && context.SelectedTreeNodes.Length == 1;
105 | }
106 |
107 | public void Execute(TextViewContext context)
108 | {
109 | if (context.SelectedTreeNodes == null)
110 | return;
111 | AssemblyTreeNode node = (AssemblyTreeNode)context.SelectedTreeNodes[0];
112 | AssemblyDefinition asm = AssemblyDefinition.ReadAssembly(node.LoadedAssembly.FileName);
113 | ModuleDefinition module = asm.MainModule;
114 |
115 | foreach (var type in module.Types) {
116 | if (type.FullName == "") {
117 | foreach (var method in type.Methods) {
118 | if (method.Name == ".cctor") {
119 | ClearAttachCallFromMethod(method);
120 | }
121 | }
122 |
123 | }
124 | }
125 | SaveFileDialog dlg = new SaveFileDialog();
126 | dlg.FileName = node.LoadedAssembly.FileName;
127 | dlg.Filter = "Assembly|*.dll;*.exe";
128 | if (dlg.ShowDialog() == DialogResult.OK) {
129 | asm.MainModule.Write(dlg.FileName);
130 | module.Write(dlg.FileName);
131 | }
132 | }
133 |
134 | private static void ClearAttachCallFromMethod(MethodDefinition methodDefinition)
135 | {
136 | var body = methodDefinition.Body;
137 | var processor = body.GetILProcessor();
138 |
139 | foreach (var instruction in body.Instructions.ToList()) {
140 | if (instruction.OpCode == OpCodes.Call) {
141 | processor.Remove(instruction);
142 | } else if (instruction.OpCode == OpCodes.Ret) {
143 | break;
144 | }
145 | }
146 | }
147 |
148 | private static void PrintDebugWindow(string message)
149 | {
150 | string caption = "CosturaPlugin Debug Output";
151 | MessageBoxButtons buttons = MessageBoxButtons.OK;
152 | MessageBox.Show(message, caption, buttons);
153 | }
154 | }
155 | }
156 |
--------------------------------------------------------------------------------