2

In C# or VB.NET, I would like to know which would be the best approach to determine the PEFileKinds of an assembly loaded through Reflection. In other words, determine whether an assembly is a WinExe, Console application, or a Dynamic link library.

I found this solution (the other proposed solutions in that question are not effective), but If I'm not wrong I think it implies to assume that the file loaded is a .NET assembly, and, seems a little bit tidy to manually parse the PE header.

I also found this other solution but reading the comments it seem not effective in some circumstances.

For those reasons, I wonder if exists a truly safe, managed way, preferably through Reflection, to determine the PE file kind of a loaded assembly.

I'm sure the System.Reflection.Emit.PEFileKinds enumeration does not exists only for decorative purposes, if that enum exists then its logic to think that there could be a member/function that I missed inside the Reflection namespaces that internally use that enum to return the PE file kind of a Assembly object, however, I managed to look at the private members of the Assembly class through Reflection and other related classes and I found nothing relevant.

ElektroStudios
  • 19,105
  • 33
  • 200
  • 417
  • Given that `PEFileKinds` is in the `System.Reflection.Emit` namespace, it probably is only used when writing, not reading, in the regular .NET Framework reflection APIs. I can answer when I research it more myself, but in the meantime you might consider looking at `System.Reflection.Metadata` on NuGet (although it's not well-documented) or the third-party `Mono.Cecil` library. – Joe Sewell Sep 08 '18 at 00:01
  • 1
    If you don't want to analyze the PE format yourself, use an existing library like https://github.com/secana/PeNet – Lex Li Sep 08 '18 at 01:59

1 Answers1

3

According to a search of the reference source, the PEFileKinds enum is only used in AssemblyBuilder and ModuleBuilder (and the non-public helper types for them). This enum and the classes are found in the System.Reflection.Emit namespace - e.g., they're intended for writing assemblies, not reading.

However, the official System.Reflection.Metadata NuGet package exposes the relevant values of an assembly's PE header in its System.Reflection.PortableExecutable namespace. You can use these headers to reverse engineer the equivalent PEFileKinds value. Here's an example in C#:

using (var stream = File.OpenRead(filenameAndExtension))
{
    using (var peFile = new PEReader(stream))
    {
        var headers = peFile.PEHeaders;
        Console.WriteLine($"Reading {filenameAndExtension} with System.Reflection.Metadata");
        Console.WriteLine($"  IsDll: {headers.IsDll}");
        Console.WriteLine($"  IsExe: {headers.IsExe}");
        Console.WriteLine($"  IsConsoleApplication: {headers.IsConsoleApplication}");

        PEFileKinds reverseEngineeredKind;

        // NOTE: the header values cause IsConsoleApplication to return
        //       true for DLLs, so we need to check IsDll first
        if (headers.IsDll)
        {
            reverseEngineeredKind = PEFileKinds.Dll;
        }
        else if (headers.IsConsoleApplication)
        {
            reverseEngineeredKind = PEFileKinds.ConsoleApplication;
        }
        else
        {
            reverseEngineeredKind = PEFileKinds.WindowApplication;
        }
        Console.WriteLine($"  Reverse-engineered kind: {reverseEngineeredKind}");
    }
}

I ran this code on assemblies I generated with System.Reflection.Emit to ensure its accuracy. The full program is in this gist.

You can probably also get this information with third-party libraries, like Mono.Cecil or, as Lex Li mentioned, PeNet.

Joe Sewell
  • 6,067
  • 1
  • 21
  • 34
  • 1
    Hi. First I want to comment that I will never understand what reason could have a person to downvote an answer like this. Secondly, my idea was to also avoid the usage of third-party libraries, however, since the library you mentioned is official from Microsoft then I'm more than satisfied with this solution. Also it works as expected. Thanks for your help. – ElektroStudios Sep 08 '18 at 09:13
  • Also, it seem using this library I also could determine whether an assembly is strong-name signed, so I can avoid using unmanaged code implementing the native IClrStrongName interface to do the same. I know this is not part of my question but I'm very interested about it, maybe could you help me with a simple Yes/No answer here in the commentary box to let me know whether with this code I can determine strong-name signed or maybe I'm wrong about the meaning of this flag?: `bool isStrongNameSigned = peReader.PEHeaders.CorHeader.Flags.HasFlag(CorFlags.StrongNameSigned);` – ElektroStudios Sep 08 '18 at 10:04
  • 1
    @ElektroStudios I think that flag indicates whether the assembly claims to be strong-named, but it doesn't necessarily mean that the signature is valid. – Joe Sewell Sep 08 '18 at 18:23