Click here to Skip to main content
Click here to Skip to main content

How to read a PE image's manifest resource using an undocumented internal .NET class method

, 21 Jan 2013 CPOL
Rate this:
Please Sign up or sign in to vote.
This short article shows how to use a undocumented internal class method from the System.Deployment.Application.Win32InterOp namespace to get a PE images manifest resource.

Introduction

This is a short article that shows you how to read a PE images manifest resource by accessing a undocumented internal .NET Framework class from inside an internal namespace by loading the class containing assembly into the executing assembly, then importing and instantiating the internal class and finally calling the method via reflection and dynamic invocation. In addition it also shows how to encapsulate the code in a reusable static method and how to convert the returned datastream the right way. The final result will be a XmlDocument object class instance which offers a comfortable way to explore the manifests internals by walking the XML document using the .NET Frameworks XML/DOM functions. 

Background  

I was looking for some quick and easy way to read the manifest file from PE images like the way Resource Editors, IDEs or Debuggers/Disassemblers can do, but unfortunately the .NET runtime did not offer an easy way to do this, at least i didnt know one until yet. The standard "easiest" way to do this is to use P/Invoke to access the manifest data compiled into a PE images resources section by using Windows API resource management functions. The steps to get the information you want look like this:

  1. LoadLibrary(Ex)
  2. FindResource 
  3. SizeOfResource  
  4. LoadResource
  5. LockResource
  6. CopyMemory or similar suitable memory copy function like Marshal.Copy(...)
  7. FreeLibrary


Another way would be to read the raw PE images binary data and parse its contents step-by-step until one gets the information needed, but this is truly very hard to do, because you have to access the PE images bytes and bits and one wrong offset calculation or data misinterpretation can result in totally useless information or corrupted resulting data. Another way would be using the Debug Help Library API, but this is again lots of P/invoke and we dont want that. There is another fully "managed" way to do this.

Looking at the .NET Frameworks source code, i found one undocumented class/method inside an undocumented namespace, that exactly does the steps above for me and i dont have import/declare any additional unnecessary P/invoke signatures. The method we are using is named GetManifestFromPEResources(...) and the implementing class we are using is the internally (internal class) declared SystemUtils class inside the System.Deployment.Application.Win32InterOpt namespace. The binary assembly dll file implementing the class is named System.Deployment.dll in my 32-bit .NET installation directory inside "\Windows\Microsoft.NET\Framework\v2.0.50727\". The same libraries can be found inside the 64-bit .NET directories as well. The method is internally using exactly the same Windows API functions above by accessing them using P/Invoke (like most of the runtime classes are doing in any .NET versions by either accessing the Windows API or the Windows Runtime for some tasks), but the good news is, that all can be done in a single call to this undocumented function and we dont have to bother what it looks inside it in detail (but we know that it is exactly what we wanted to do). Once we get the manifest data, we convert it appropriately and create a XmlDocument object from it for easy XML objects access, because we know that the manifest is plain text XML, but all this is eplained inside the code.

Using the code   

The code is quite self-explanatory and has some basic commenst to explain the steps. The classes used in the code have been declared with their full namespace qualifiers to clarify their origins in the frameworks runtime:  

The desired functions signature looks like this: 

public static byte[] GetManifestFromPEResources(string filePath) 

and the full code looks like this:

C# code

//Overloaded simplified static method returning
//only the XmlDocument or null on failure
public static System.Xml.XmlDocument GetPEFileManifest(
    System.String fileName)
{
    System.Xml.XmlDocument xmld;
    System.Exception err;

    GetPEFileManifest(
        fileName,
        out xmld,
        out err);

    return xmld;
}

//Full static method returning error
//information on failure
public static System.Boolean GetPEFileManifest(
    System.String fileName,
    out System.Xml.XmlDocument applicationXmlManifest,
    out System.Exception error) 
{
    try
    {
        //First check valid input parameters
        if(System.String.IsNullOrEmpty(fileName) == true)
            throw new System.NullReferenceException("Parameter \"fileName\" cant be null or empty");

        //First check if file is valid
        if(System.IO.File.Exists(fileName) == false)
            throw new System.IO.FileNotFoundException("Parameter \"fileName\" does not point to a existing file");

        //Load System.Deployment.dll
        System.Reflection.Assembly SystemDeploymentAssembly = System.Reflection.Assembly.Load(
            "System.Deployment, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");

        //Get SystemUtils class from assembly
        System.Type SystemUtilsClass = SystemDeploymentAssembly.GetType(
          "System.Deployment.Application.Win32InterOp.SystemUtils");

        //Create instance of SystemUtils class 
        System.Object SystemUtils = System.Activator.CreateInstance(SystemUtilsClass);

        //Invoke the internal method named "GetManifestFromPEResources" via reflection
        System.Byte[] ManifestBytes = SystemUtils.GetType().InvokeMember(
            "GetManifestFromPEResources",
            System.Reflection.BindingFlags.InvokeMethod |
            System.Reflection.BindingFlags.Public |
            System.Reflection.BindingFlags.Static,
            null,
            SystemUtils, 
            new System.Object[] { fileName }) as System.Byte[];

        //The string holding the final xml text
        string ManifestXmlString = string.Empty;

        //Read bytes with memory stream and stream reader to make sure
        //to get the right encoded data, because some of the resources do have a BOM (Byte Order Mark)
        //Read this for more information: http://en.wikipedia.org/wiki/Byte_Order_Mark
        using (System.IO.MemoryStream ManifestBytesMemoryStream = new System.IO.MemoryStream(ManifestBytes))
        using (System.IO.StreamReader ManifestBytesStreamReader = _
                new System.IO.StreamReader(ManifestBytesMemoryStream, true))
        {
            ManifestXmlString = ManifestBytesStreamReader.ReadToEnd().Trim();
        }

        //Create a xml document and load the xml string
        System.Xml.XmlDocument ManifestXmlDocument = new System.Xml.XmlDocument();
        
        //Load the xml string 
        ManifestXmlDocument.LoadXml(ManifestXmlString);

        //Return the loaded xml document
        applicationXmlManifest = ManifestXmlDocument;

        error = null;
        return true;
    }
    catch(System.Exception err)
    {
        //Something went wrong for some reason
        error = err;
        applicationXmlManifest = null;
        return false;
    }
}

VB.NET code

'Overloaded simplified static method returning
'only the XmlDocument or null on failure
Public Shared Function GetPEFileManifest(ByVal fileName As System.String) As System.Xml.XmlDocument
    Dim xmld As System.Xml.XmlDocument
    Dim err As System.Exception

    GetPEFileManifest(fileName, xmld, err)

    Return xmld
End Function

'Full static method returning error
'information on failure
Public Shared Function GetPEFileManifest(ByVal fileName As System.String, _
       ByRef applicationXmlManifest As System.Xml.XmlDocument, _
       ByRef [error] As System.Exception) As System.Boolean
    Try
        'First check valid input parameters
        If System.[String].IsNullOrEmpty(fileName) = True Then
            Throw New System.NullReferenceException("Parameter ""fileName"" cant be null or empty")
        End If

        'First check if file is valid
        If System.IO.File.Exists(fileName) = False Then
            Throw New System.IO.FileNotFoundException("Parameter ""fileName"" does not point to a existing file")
        End If

        'Load System.Deployment.dll
        Dim SystemDeploymentAssembly As System.Reflection.Assembly = _
          System.Reflection.Assembly.Load("System.Deployment, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")

        'Get SystemUtils class from assembly
        Dim SystemUtilsClass As System.Type = _
          SystemDeploymentAssembly.[GetType]("System.Deployment.Application.Win32InterOp.SystemUtils")

        'Create instance of SystemUtils class 
        Dim SystemUtils As System.Object = System.Activator.CreateInstance(SystemUtilsClass)

        'Invoke the internal method named "GetManifestFromPEResources" via reflection
        Dim ManifestBytes As System.Byte() = TryCast(SystemUtils.[GetType]().InvokeMember(_
          "GetManifestFromPEResources", System.Reflection.BindingFlags.InvokeMethod Or _
          System.Reflection.BindingFlags.[Public] Or System.Reflection.BindingFlags.[Static], _
          Nothing, SystemUtils, New System.Object() {fileName}), System.Byte())

        'The string holding the final xml text
        Dim ManifestXmlString As String = String.Empty

        'Read bytes with memory stream and stream reader to make sure
        'to get the right encoded data, because some of the resources do have a BOM (Byte Order Mark)
        'Read this for more information: http://en.wikipedia.org/wiki/Byte_Order_Mark
        Using ManifestBytesMemoryStream As New System.IO.MemoryStream(ManifestBytes)
            Using ManifestBytesStreamReader As New System.IO.StreamReader(ManifestBytesMemoryStream, True)
                ManifestXmlString = ManifestBytesStreamReader.ReadToEnd().Trim()
            End Using
        End Using

        'Create a xml document and load the xml string
        Dim ManifestXmlDocument As System.Xml.XmlDocument = New System.Xml.XmlDocument()

        'Load the xml string 
        ManifestXmlDocument.LoadXml(ManifestXmlString)

        'Return the loaded xml document
        applicationXmlManifest = ManifestXmlDocument

        [error] = Nothing
        Return True
    Catch err As System.Exception
        'Something went wrong for some reason
        [error] = err
        applicationXmlManifest = Nothing
        Return False
    End Try
End Function

Code in detail (C#):

First we check the input parameters to make sure we got a valid filename to access. Then we load the assembly holding the method we want by using the:

System.Reflection.Assembly.Load()  

method. There are several overloads for that method and maybe one wants to use another variant of it. Take a look at its documentation in the MSDN library. In our case the assembly we are looking for to load is the "System.Deployment" and we use the long form of the assembly name to reference and load it.: 

"System.Deployment, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" 

There are possibly other versions/cultures of that assembly, but I didnt investigated on that yet. Please keep in your mind that once the assembly is loaded into the applications AppDomain, it cant be unloaded for several reasons i dont want to discuss here. There are lots of articles here showing how to load a external assembly into a seperate AppDomain and unload it after using it just for the case you want to unload it. After loading it into the exectuing assembly, we access the desired classes and methods inside the loaded assembly by using reflection:

//Get SystemUtils class from assembly
System.Type SystemUtilsClass = SystemDeploymentAssembly.GetType("System.Deployment.Application.Win32InterOp.SystemUtils");
 
//Create instance of SystemUtils class 
System.Object SystemUtils = System.Activator.CreateInstance(SystemUtilsClass);
 
//Invoke the internal method named "GetManifestFromPEResources" via reflection
System.Byte[] ManifestBytes = SystemUtils.GetType().InvokeMember(
    "GetManifestFromPEResources",
    System.Reflection.BindingFlags.InvokeMethod |
    System.Reflection.BindingFlags.Public |
    System.Reflection.BindingFlags.Static,
    null,
    SystemUtils, 
    new System.Object[] { fileName }) as System.Byte[];

First we get the SystemUtils class type by querying the loaded assembly which gets the Type object with the specified name, performing a case-sensitive search for further usage. Important here is to use the FQN (Full Qualified Name) of the type residing in the assembly. After successfully obtaining the SystemUtils class type, we create a instance of it by using the Activator classes CreateInstance(...) method. Finally we make a call to the hidden GetManifestFromPEResources(...) method by invoking the member method of the instanciated class. To make a successfull call, you have to pass the correct case of the methods name and the right binding flags as used above. For more information please refer to the MSDN documentation. After getting the byte[] array holding the PE manifest, we need to convert it into a string. This is done like this:

//The string holding the final xml text
string ManifestXmlString = string.Empty;

//Read bytes with memory stream and stream reader to make sure
//to get the right encoded data, because some of the resources do have a BOM (Byte Order Mark)
//Read this for more information: http://en.wikipedia.org/wiki/Byte_Order_Mark
using (System.IO.MemoryStream ManifestBytesMemoryStream = new System.IO.MemoryStream(ManifestBytes))
using (System.IO.StreamReader ManifestBytesStreamReader = new System.IO.StreamReader(ManifestBytesMemoryStream, true))
{
    ManifestXmlString = ManifestBytesStreamReader.ReadToEnd().Trim();
} 

The reason why we are using the StreamReader class is that it is aware of the BOM (Byte Order Mark) in the manifests byte[] arrays first bytes and we dont have to take care of it anymore. If there is a BOM in the array (there isnt always a BOM in the resource), the StreamReader will handle it for us, if not it will simply read the bytes into the stream. Looking at the BOM inside a hex editor it looks like this:

Finally we create a XmlDocument from the XML string we got and return it to the caller:

                //Create a xml document and load the xml string
System.Xml.XmlDocument ManifestXmlDocument = new System.Xml.XmlDocument();

//Load the xml string 
ManifestXmlDocument.LoadXml(ManifestXmlString);

//Return the loaded xml document
applicationXmlManifest = ManifestXmlDocument; 

The reasons why i decided to pack the xml text into a XmlDocument class is quite simple. Once the manifest is mapped into a XmlDocument, it can fully be accessed by the .NET Frameworks XML/DOM functions and can also be manipulated (read, written, extended, splitted, etc.) the way one wants to change it.

Summary  

Although this all can be done with P/Invoke by using the single Windows API calls, i personally find it more "clean" to use this undocumented function, since it encapsulates all the calls inside one single function and all is done from outside the function in plain managed code and this undocumented class/method is available from .NET 2.0 up to the latest version of the .NET runtime library. Always keep in your mind: Undocumented functions are always a matter of taste and are always possibly subject to change or can be fully unavailable in future releases/updates, so one has to decide if he/she wants to use this function or completely rewrite it using the Windows API functions to have a own codebase for future usage.

Points of Interest

There is also the other way: Updating a PE's manifest resource, but i wont cover this here and it is not the purpose of this article. The only thing i want to point out is, if someone is interested for sure, that it can be done with the Resource functions from the Windows API in the following order:

  1. BeginUpdateResource
  2. UpdateResource
  3. EndUpdateResource

History      

  • 19/01/2013 - Initial article release 
  • 22/01/2013 - Added VB.NET code and corrected a few typos

External Links 

Byte Oder Mark - http://de.wikipedia.org/wiki/Byte_Order_Mark
Resource Functions (Windows) -  http://msdn.microsoft.com/en-us/library/windows/desktop/ff468902%28v=vs.85%29.aspx
Application Manifests (Windows) - http://msdn.microsoft.com/en-us/library/windows/desktop/aa374191%28v=vs.85%29.aspx
Reflection in the .NET Framework - http://msdn.microsoft.com/en-us/library/f7ykdhsy.aspx

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Kerem Guemruekcue
Software Developer (Senior)
Germany Germany
I am a former developer from germany who has worked on a broad range of IT technologies and software/hardware related tasks regarding Microsoft Windows and Linux systems. My primary focus and field of application was the development of system software/hardware, troubleshooting on software/hardware, designing and teaching how to write code and build working systems, but i am not tied to anything special at all. Currently i am developing in C, C++, C# and a little in Assembly if there is need for it. Beside that there are lots of other languages and stuff, but nothing really worth to be menitioned here. In my spare time i try to be as much as i can with my family, i love sports and reading. I also try to contribute code and solutions and help wherever i can.

Comments and Discussions

 
QuestionWhat is the purpose of this? PinmemberFatCatProgrammer20-Jan-13 18:55 
AnswerRe: What is the purpose of this? PinmemberKerem Guemruekcue20-Jan-13 19:18 
GeneralRe: What is the purpose of this? PinmemberFatCatProgrammer21-Jan-13 6:40 
GeneralRe: What is the purpose of this? [modified] PinmemberKerem Guemruekcue21-Jan-13 7:24 
GeneralRe: What is the purpose of this? PinmemberFatCatProgrammer21-Jan-13 9:27 
GeneralRe: What is the purpose of this? PinmemberKerem Guemruekcue21-Jan-13 12:33 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.1411019.1 | Last Updated 21 Jan 2013
Article Copyright 2013 by Kerem Guemruekcue
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid