Skip to content

Commit

Permalink
Lift some artificial restrictions to PEFile in WholeProjectDecompiler…
Browse files Browse the repository at this point in the history
… and ReflectionDisassembler.
  • Loading branch information
siegfriedpammer committed Mar 28, 2024
1 parent 921bf36 commit 2d90c45
Show file tree
Hide file tree
Showing 15 changed files with 132 additions and 110 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,6 @@ interface IProjectFileWriter
/// <param name="project">The information about the project being created.</param>
/// <param name="files">A collection of source files to be included into the project.</param>
/// <param name="module">The module being decompiled.</param>
void Write(TextWriter target, IProjectInfoProvider project, IEnumerable<ProjectItemInfo> files, PEFile module);
void Write(TextWriter target, IProjectInfoProvider project, IEnumerable<ProjectItemInfo> files, MetadataFile module);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ public void Write(
TextWriter target,
IProjectInfoProvider project,
IEnumerable<ProjectItemInfo> files,
PEFile module)
MetadataFile module)
{
const string ns = "http://schemas.microsoft.com/developer/msbuild/2003";
string platformName = TargetServices.GetPlatformName(module);
string platformName = module is PEFile peFile ? TargetServices.GetPlatformName(peFile) : "AnyCPU";
var targetFramework = TargetServices.DetectTargetFramework(module);
if (targetFramework.Identifier == ".NETFramework" && targetFramework.VersionNumber == 200)
targetFramework = TargetServices.DetectTargetFrameworkNET20(module, project.AssemblyResolver, targetFramework);
Expand Down Expand Up @@ -80,26 +80,22 @@ public void Write(
w.WriteValue(platformName);
w.WriteEndElement(); // </Platform>

if (module.Reader.PEHeaders.IsDll)
{
w.WriteElementString("OutputType", "Library");
}
else
string outputType;

switch ((module as PEFile)?.Reader.PEHeaders.PEHeader.Subsystem)
{
switch (module.Reader.PEHeaders.PEHeader.Subsystem)
{
case Subsystem.WindowsGui:
w.WriteElementString("OutputType", "WinExe");
break;
case Subsystem.WindowsCui:
w.WriteElementString("OutputType", "Exe");
break;
default:
w.WriteElementString("OutputType", "Library");
break;
}
case Subsystem.WindowsGui:
outputType = "WinExe";
break;
case Subsystem.WindowsCui:
outputType = "Exe";
break;
default:
outputType = "Library";
break;
}

w.WriteElementString("OutputType", outputType);
w.WriteElementString("LangVersion", project.LanguageVersion.ToString().Replace("CSharp", "").Replace('_', '.'));

w.WriteElementString("AssemblyName", module.Name);
Expand All @@ -123,7 +119,8 @@ public void Write(
w.WriteStartElement("PropertyGroup"); // platform-specific
w.WriteAttributeString("Condition", " '$(Platform)' == '" + platformName + "' ");
w.WriteElementString("PlatformTarget", platformName);
if (targetFramework.VersionNumber > 400 && platformName == "AnyCPU" && (module.Reader.PEHeaders.CorHeader.Flags & CorFlags.Prefers32Bit) == 0)
if (targetFramework.VersionNumber > 400 && platformName == "AnyCPU"
&& ((module as PEFile)?.Reader.PEHeaders.CorHeader.Flags & CorFlags.Prefers32Bit) == 0)
{
w.WriteElementString("Prefer32Bit", "false");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public void Write(
TextWriter target,
IProjectInfoProvider project,
IEnumerable<ProjectItemInfo> files,
PEFile module)
MetadataFile module)
{
using (XmlTextWriter xmlWriter = new XmlTextWriter(target))
{
Expand All @@ -76,7 +76,7 @@ public void Write(
}
}

static void Write(XmlTextWriter xml, IProjectInfoProvider project, IEnumerable<ProjectItemInfo> files, PEFile module)
static void Write(XmlTextWriter xml, IProjectInfoProvider project, IEnumerable<ProjectItemInfo> files, MetadataFile module)
{
xml.WriteStartElement("Project");

Expand Down Expand Up @@ -105,18 +105,30 @@ static void PlaceIntoTag(string tagName, XmlTextWriter xml, Action content)
}
}

static void WriteAssemblyInfo(XmlTextWriter xml, PEFile module, IProjectInfoProvider project, ProjectType projectType)
static void WriteAssemblyInfo(XmlTextWriter xml, MetadataFile module, IProjectInfoProvider project, ProjectType projectType)
{
xml.WriteElementString("AssemblyName", module.Name);

// Since we create AssemblyInfo.cs manually, we need to disable the auto-generation
xml.WriteElementString("GenerateAssemblyInfo", FalseString);

WriteOutputType(xml, module.Reader.PEHeaders.IsDll, module.Reader.PEHeaders.PEHeader.Subsystem, projectType);
string platformName;
CorFlags flags;
if (module is PEFile { Reader.PEHeaders: var headers } peFile)
{
WriteOutputType(xml, headers.IsDll, headers.PEHeader.Subsystem, projectType);
platformName = TargetServices.GetPlatformName(peFile);
flags = headers.CorHeader.Flags;
}
else
{
WriteOutputType(xml, isDll: true, Subsystem.Unknown, projectType);
platformName = AnyCpuString;
flags = 0;
}

WriteDesktopExtensions(xml, projectType);

string platformName = TargetServices.GetPlatformName(module);
var targetFramework = TargetServices.DetectTargetFramework(module);
if (targetFramework.Identifier == ".NETFramework" && targetFramework.VersionNumber == 200)
targetFramework = TargetServices.DetectTargetFrameworkNET20(module, project.AssemblyResolver, targetFramework);
Expand All @@ -134,7 +146,7 @@ static void WriteAssemblyInfo(XmlTextWriter xml, PEFile module, IProjectInfoProv
xml.WriteElementString("PlatformTarget", platformName);
}

if (platformName == AnyCpuString && (module.Reader.PEHeaders.CorHeader.Flags & CorFlags.Prefers32Bit) != 0)
if (platformName == AnyCpuString && (flags & CorFlags.Prefers32Bit) != 0)
{
xml.WriteElementString("Prefer32Bit", TrueString);
}
Expand Down Expand Up @@ -238,7 +250,7 @@ static void WriteResources(XmlTextWriter xml, IEnumerable<ProjectItemInfo> files
}
}

static void WriteReferences(XmlTextWriter xml, PEFile module, IProjectInfoProvider project, ProjectType projectType)
static void WriteReferences(XmlTextWriter xml, MetadataFile module, IProjectInfoProvider project, ProjectType projectType)
{
bool isNetCoreApp = TargetServices.DetectTargetFramework(module).Identifier == ".NETCoreApp";
var targetPacks = new HashSet<string>();
Expand Down Expand Up @@ -292,7 +304,7 @@ static string GetSdkString(ProjectType projectType)
}
}

static ProjectType GetProjectType(PEFile module)
static ProjectType GetProjectType(MetadataFile module)
{
foreach (var referenceName in module.AssemblyReferences.Select(r => r.Name))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ static class TargetServices
/// <param name="module">The module to get the target framework description for. Cannot be null.</param>
/// <returns>A new instance of the <see cref="TargetFramework"/> class that describes the specified <paramref name="module"/>.
/// </returns>
public static TargetFramework DetectTargetFramework(PEFile module)
public static TargetFramework DetectTargetFramework(MetadataFile module)
{
if (module is null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,6 @@ protected WholeProjectDecompiler(

public void DecompileProject(MetadataFile file, string targetDirectory, CancellationToken cancellationToken = default(CancellationToken))
{
if (file is not PEFile)
{
throw new NotSupportedException("Module is not a valid PE file!");
}
string projectFileName = Path.Combine(targetDirectory, CleanUpFileName(file.Name) + ".csproj");
using (var writer = new StreamWriter(projectFileName))
{
Expand All @@ -146,10 +142,6 @@ protected WholeProjectDecompiler(

public ProjectId DecompileProject(MetadataFile file, string targetDirectory, TextWriter projectFileWriter, CancellationToken cancellationToken = default(CancellationToken))
{
if (file is not PEFile module)
{
throw new NotSupportedException("Module is not a valid PE file!");
}
if (string.IsNullOrEmpty(targetDirectory))
{
throw new InvalidOperationException("Must set TargetDirectory");
Expand All @@ -159,15 +151,19 @@ protected WholeProjectDecompiler(
var resources = WriteResourceFilesInProject(file).ToList();
var files = WriteCodeFilesInProject(file, resources.SelectMany(r => r.PartialTypes ?? Enumerable.Empty<PartialTypeInfo>()).ToList(), cancellationToken).ToList();
files.AddRange(resources);
files.AddRange(WriteMiscellaneousFilesInProject(module));
var module = file as PEFile;
if (module != null)
{
files.AddRange(WriteMiscellaneousFilesInProject(module));
}
if (StrongNameKeyFile != null)
{
File.Copy(StrongNameKeyFile, Path.Combine(targetDirectory, Path.GetFileName(StrongNameKeyFile)), overwrite: true);
}

projectWriter.Write(projectFileWriter, this, files, module);
projectWriter.Write(projectFileWriter, this, files, file);

string platformName = TargetServices.GetPlatformName(module);
string platformName = module != null ? TargetServices.GetPlatformName(module) : "AnyCPU";
return new ProjectId(platformName, ProjectGuid, ProjectTypeGuids.CSharpWindows);
}

Expand Down Expand Up @@ -764,7 +760,7 @@ static bool IsReservedFileSystemName(string name)
}
}

public static bool CanUseSdkStyleProjectFormat(PEFile module)
public static bool CanUseSdkStyleProjectFormat(MetadataFile module)
{
return TargetServices.DetectTargetFramework(module).Moniker != null;
}
Expand Down
21 changes: 12 additions & 9 deletions ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1955,7 +1955,7 @@ public void DisassembleNamespace(string nameSpace, MetadataFile module, IEnumera
}
}

public void WriteAssemblyHeader(PEFile module)
public void WriteAssemblyHeader(MetadataFile module)
{
var metadata = module.Metadata;
if (!metadata.IsAssembly)
Expand Down Expand Up @@ -2018,7 +2018,7 @@ public void WriteAssemblyReferences(MetadataReader metadata)
}
}

public void WriteModuleHeader(PEFile module, bool skipMVID = false)
public void WriteModuleHeader(MetadataFile module, bool skipMVID = false)
{
var metadata = module.Metadata;

Expand Down Expand Up @@ -2085,17 +2085,20 @@ void WriteExportedType(ExportedType exportedType)
output.WriteLine("// MVID: {0}", metadata.GetGuid(moduleDefinition.Mvid).ToString("B").ToUpperInvariant());
}

var headers = module.Reader.PEHeaders;
output.WriteLine(".imagebase 0x{0:x8}", headers.PEHeader.ImageBase);
output.WriteLine(".file alignment 0x{0:x8}", headers.PEHeader.FileAlignment);
output.WriteLine(".stackreserve 0x{0:x8}", headers.PEHeader.SizeOfStackReserve);
output.WriteLine(".subsystem 0x{0:x} // {1}", headers.PEHeader.Subsystem, headers.PEHeader.Subsystem.ToString());
output.WriteLine(".corflags 0x{0:x} // {1}", headers.CorHeader.Flags, headers.CorHeader.Flags.ToString());
if (module is PEFile peFile)
{
var headers = peFile.Reader.PEHeaders;
output.WriteLine(".imagebase 0x{0:x8}", headers.PEHeader.ImageBase);
output.WriteLine(".file alignment 0x{0:x8}", headers.PEHeader.FileAlignment);
output.WriteLine(".stackreserve 0x{0:x8}", headers.PEHeader.SizeOfStackReserve);
output.WriteLine(".subsystem 0x{0:x} // {1}", headers.PEHeader.Subsystem, headers.PEHeader.Subsystem.ToString());
output.WriteLine(".corflags 0x{0:x} // {1}", headers.CorHeader.Flags, headers.CorHeader.Flags.ToString());
}

WriteAttributes(module, metadata.GetCustomAttributes(EntityHandle.ModuleDefinition));
}

public void WriteModuleContents(PEFile module)
public void WriteModuleContents(MetadataFile module)
{
foreach (var handle in Process(module, module.Metadata.GetTopLevelTypeDefinitions().ToArray()))
{
Expand Down
26 changes: 19 additions & 7 deletions ILSpy/Commands/IProtocolHandler.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
// Copyright (c) 2018 Siegfried Pammer
//
// 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.

using System.Reflection.Metadata;
using System.Text;
using System.Threading.Tasks;

using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.TreeView;

namespace ICSharpCode.ILSpy
{
public interface IProtocolHandler
{
ILSpyTreeNode Resolve(string protocol, PEFile module, Handle handle, out bool newTabPage);
ILSpyTreeNode Resolve(string protocol, MetadataFile module, Handle handle, out bool newTabPage);
}
}
67 changes: 39 additions & 28 deletions ILSpy/Languages/CSharpLanguage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ void AddWarningMessage(MetadataFile module, ITextOutput output, string line1, st

public override ProjectId DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options)
{
var module = assembly.GetMetadataFileOrNull() as PEFile;
var module = assembly.GetMetadataFileOrNull();
if (module == null)
{
return null;
Expand Down Expand Up @@ -427,38 +427,49 @@ public override ProjectId DecompileAssembly(LoadedAssembly assembly, ITextOutput
}
var metadata = module.Metadata;
var corHeader = module.CorHeader;
var entrypointHandle = MetadataTokenHelpers.EntityHandleOrNil(corHeader.EntryPointTokenOrRelativeVirtualAddress);
if (!entrypointHandle.IsNil && entrypointHandle.Kind == HandleKind.MethodDefinition)
if (module is PEFile peFile && corHeader != null)
{
var entrypoint = typeSystem.MainModule.ResolveMethod(entrypointHandle, new Decompiler.TypeSystem.GenericContext());
if (entrypoint != null)
var entrypointHandle = MetadataTokenHelpers.EntityHandleOrNil(corHeader.EntryPointTokenOrRelativeVirtualAddress);
if (!entrypointHandle.IsNil && entrypointHandle.Kind == HandleKind.MethodDefinition)
{
output.Write("// Entry point: ");
output.WriteReference(entrypoint, EscapeName(entrypoint.DeclaringType.FullName + "." + entrypoint.Name));
output.WriteLine();
var entrypoint = typeSystem.MainModule.ResolveMethod(entrypointHandle, new Decompiler.TypeSystem.GenericContext());
if (entrypoint != null)
{
output.Write("// Entry point: ");
output.WriteReference(entrypoint, EscapeName(entrypoint.DeclaringType.FullName + "." + entrypoint.Name));
output.WriteLine();
}
}
output.WriteLine("// Architecture: " + GetPlatformDisplayName(peFile));
if ((corHeader.Flags & System.Reflection.PortableExecutable.CorFlags.ILOnly) == 0)
{
output.WriteLine("// This assembly contains unmanaged code.");
}
string runtimeName = GetRuntimeDisplayName(module);
if (runtimeName != null)
{
output.WriteLine("// Runtime: " + runtimeName);
}
if ((corHeader.Flags & System.Reflection.PortableExecutable.CorFlags.StrongNameSigned) != 0)
{
output.WriteLine("// This assembly is signed with a strong name key.");
}
if (peFile.Reader.ReadDebugDirectory().Any(d => d.Type == DebugDirectoryEntryType.Reproducible))
{
output.WriteLine("// This assembly was compiled using the /deterministic option.");
}
if (module.Metadata.MetadataKind != MetadataKind.Ecma335)
{
output.WriteLine("// This assembly was loaded with Windows Runtime projections applied.");
}
}
output.WriteLine("// Architecture: " + GetPlatformDisplayName(module));
if ((corHeader.Flags & System.Reflection.PortableExecutable.CorFlags.ILOnly) == 0)
{
output.WriteLine("// This assembly contains unmanaged code.");
}
string runtimeName = GetRuntimeDisplayName(module);
if (runtimeName != null)
{
output.WriteLine("// Runtime: " + runtimeName);
}
if ((corHeader.Flags & System.Reflection.PortableExecutable.CorFlags.StrongNameSigned) != 0)
{
output.WriteLine("// This assembly is signed with a strong name key.");
}
if (module.Reader.ReadDebugDirectory().Any(d => d.Type == DebugDirectoryEntryType.Reproducible))
{
output.WriteLine("// This assembly was compiled using the /deterministic option.");
}
if (module.Metadata.MetadataKind != MetadataKind.Ecma335)
else
{
output.WriteLine("// This assembly was loaded with Windows Runtime projections applied.");
string runtimeName = GetRuntimeDisplayName(module);
if (runtimeName != null)
{
output.WriteLine("// Runtime: " + runtimeName);
}
}
if (metadata.IsAssembly)
{
Expand Down
7 changes: 1 addition & 6 deletions ILSpy/Languages/ILLanguage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -165,12 +165,7 @@ public override ProjectId DecompileAssembly(LoadedAssembly assembly, ITextOutput
{
output.WriteLine("// " + assembly.FileName);
output.WriteLine();
var module = assembly.GetMetadataFileOrNull() as PEFile;

if (module == null)
{
throw new NotSupportedException("This file is not a PE file");
}
var module = assembly.GetMetadataFileOrNull();

if (options.FullDecompilation && options.SaveAsProjectDirectory != null)
{
Expand Down
Loading

0 comments on commit 2d90c45

Please sign in to comment.