07 - PE Resources

This tutorial gives an overview of the resource structure in a PE file and how they can be manipulated using LIEF

Scripts and materials are available here: materials

By Romain Thomas - @rh0main


Unlike ELF and Mach-O formats, PE enables to embed resources (icons, images, raw, dialog …) within an executable or a DLL.

These resources are usually located in the .rsrc section but this is not an absolute rule.

To retrieve the section in which resources are located, one can use the section attribute of the associated DataDirectory

binary = lief.parse("C:\\Windows\\explorer.exe")
if binary.has_resources:
  rsrc_directory = binary.data_directory(lief.PE.DataDirectory.TYPES.RESOURCE_TABLE)
  if rsrc_directory.has_section:
    print(rsrc_directory.section)
.rsrc     22e0d8    23f000    22e200    236c00    0         4.3596    CNT_INITIALIZED_DATA - MEM_READ

Resource structure

The underlying structure used to represent resources is a tree:

../_images/07_resource_tree.png

In the resource tree we basically have two kinds of node:

  1. ResourceDirectory: Contains some information about the subtree.

  2. ResourceData: Used to store raw data. These nodes are the leaf of the tree

The first 3 levels of the tree have a special meaning:

  • Level 1: The id represents the TYPE

  • Level 2: The id represents an ID to access to the resource

  • Level 3: The id represents the RESOURCE_LANGS / SUBLANG of the resource.

We can check that a given binary embed resources with the has_resources property, then we can access to this structure through the resources property which returns a ResourceDirectory representing the root of the tree.

Given a ResourceDirectory, the childs property returns an iterator (quiet similar to a list) over the sub tree associated with the node.

The following snippet retrieves the MANIFEST element and print it.

filezilla = lief.parse("filezilla.exe")

if not filezilla.has_resources:
    print("'{}' has no resources. Abort!".format(filezilla.name), file=sys.stderr)
    sys.exit(1)

root = filezilla.resources

# First level => Type ((ResourceDirectory node)
manifest_node = next(i for i in root.childs if i.id == lief.PE.ResourcesManager.TYPE.MANIFEST)
print(manifest_node)

# Second level => ID (ResourceDirectory node)
id_node = manifest_node.childs[0]
print(id_node)

# Third level => Lang (ResourceData node)
lang_node = id_node.childs[0]
print(lang_node)

manifest = bytes(lang_node.content).decode("utf8")

print(manifest)
[DIRECTORY] - ID: 0x18 - Depth: 1 - Childs : 1
    Characteristics :         0
    Time/Date stamp :         0
    Major version :           0
    Minor version :           0
    Number of name entries :  0
    Number of id entries :    1

[DIRECTORY] - ID: 0x01 - Depth: 2 - Childs : 1
    Characteristics :         0
    Time/Date stamp :         0
    Major version :           0
    Minor version :           0
    Number of name entries :  0
    Number of id entries :    1

[DATA] - ID: 0x409 - Depth: 3 - Childs : 0
    Code page :  0
    Reserved :   0
    Size :       1666
    Hash :       ffffffffb00b5419

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
  <assemblyIdentity
    name="FileZilla"
...

As manipulating a tree is not very convenient, LIEF exposes a ResourcesManager which provides an enhanced API to manipulate binary resources

Resource Manager

As mentioned previously, the ResourcesManager is a kind of wrapper over the resource tree to:

  • Parse resources that have a predefined structures like MANIFEST, ICON, VERSION

  • Access and modify these structures

This can be summarize with the following diagram:

../_images/07_pe_resource_manager.png

The ResourcesManager can be accessed with the resources_manager property. To have an overview of the binary’s resources, we can simply print the ResourcesManager instance:

filezilla = lief.parse("filezilla.exe")

resource_manager = filezilla.resources_manager
print(resource_manager)
  [Directory] ID: 01 - CURSOR
    [Directory] ID: 01
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] ID: 02
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] ID: 03
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] ID: 04
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] ID: 05
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] ID: 06
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] ID: 07
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] ID: 08
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] ID: 09
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] ID: 10
      [Data] ID: 1033 - ENGLISH/DEFAULT
  [Directory] ID: 02 - BITMAP
    [Directory] CSQUERY
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] WXBITMAP_STD_COLOURS
      [Data] ID: 1033 - ENGLISH/DEFAULT
  [Directory] ID: 03 - ICON
    [Directory] ID: 01
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] ID: 02
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] ID: 03
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] ID: 04
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] ID: 05
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] ID: 06
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] ID: 07
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] ID: 08
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] ID: 09
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] ID: 10
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] ID: 11
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] ID: 12
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] ID: 13
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] ID: 14
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] ID: 15
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] ID: 16
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] ID: 17
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] ID: 18
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] ID: 19
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] ID: 20
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] ID: 21
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] ID: 22
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] ID: 23
      [Data] ID: 1033 - ENGLISH/DEFAULT
  [Directory] ID: 04 - MENU
    [Directory] WXWINDOWMENU
      [Data] ID: 1033 - ENGLISH/DEFAULT
  [Directory] ID: 12 - GROUP_CURSOR
    [Directory] WXCURSOR_BLANK
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] WXCURSOR_BULLSEYE
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] WXCURSOR_CROSS
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] WXCURSOR_HAND
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] WXCURSOR_MAGNIFIER
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] WXCURSOR_PBRUSH
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] WXCURSOR_PENCIL
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] WXCURSOR_PLEFT
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] WXCURSOR_PRIGHT
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] WXCURSOR_ROLLER
      [Data] ID: 1033 - ENGLISH/DEFAULT
  [Directory] ID: 14 - GROUP_ICON
    [Directory] APPICON
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] WXICON_AAA
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] WXICON_SMALL_CDROM
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] WXICON_SMALL_CLOSED_FOLDER
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] WXICON_SMALL_COMPUTER
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] WXICON_SMALL_DRIVE
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] WXICON_SMALL_FILE
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] WXICON_SMALL_FLOPPY
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] WXICON_SMALL_OPEN_FOLDER
      [Data] ID: 1033 - ENGLISH/DEFAULT
    [Directory] WXICON_SMALL_REMOVEABLE
      [Data] ID: 1033 - ENGLISH/DEFAULT
  [Directory] ID: 16 - VERSION
    [Directory] ID: 01
      [Data] ID: 1033 - ENGLISH/DEFAULT
  [Directory] ID: 24 - MANIFEST
    [Directory] ID: 01
      [Data] ID: 1033 - ENGLISH/DEFAULT

Types: CURSOR - BITMAP - ICON - MENU - GROUP_CURSOR - GROUP_ICON - VERSION - MANIFEST

Langs: ENGLISH

Sub-langs: DEFAULT

Manifest
========

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
  <assemblyIdentity
    name="FileZilla"
    processorArchitecture="*"
    version="3.10.0.0"
    type="win32"
  />
  <description>FileZilla FTP client</description>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity
        type="win32"
        name="Microsoft.Windows.Common-Controls"
        version="6.0.0.0"
        processorArchitecture="*"
        publicKeyToken="6595b64144ccf1df"
        language="*"
      />
    </dependentAssembly>
  </dependency>
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
    <security>
      <requestedPrivileges>
        <requestedExecutionLevel level="asInvoker" uiAccess="false"/>
      </requestedPrivileges>
    </security>
   </trustInfo>
  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
    <application>
      <!--Vista-->
      <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
      <!--7-->
      <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
      <!--8-->
      <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
      <!--8.1-->
      <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
      <!--10 -->
      <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
    </application>
  </compatibility>
  <asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
    <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
      <dpiAware>true</dpiAware>
    </asmv3:windowsSettings>
  </asmv3:application>
</assembly>


Version
=======

type: 0
key:  VS_VERSION_INFO

Fixed file info
===============
Signature:       feef04bd
Struct version:  10000
File version:    3 - 25 - 2 - 0
Product version: 3 - 25 - 2 - 0
File OS:         WINDOWS32
File type:       APP

String file info
================
type:  1
key:   StringFileInfo

type:   1
key:    000004b0: (NEUTRAL - NEUTRAL - UTF_16)
Items:
    'Comments': 'Version 3.25.2'
    'CompanyName': 'FileZilla Project'
    'FileDescription': 'FileZilla FTP Client'
    'FileVersion': '3, 25, 2, 0'
    'InternalName': 'FileZilla 3'
    'LegalCopyright': 'Copyright (C) 2006-2016'
    'OriginalFilename': 'filezilla.exe'
    'ProductName': 'FileZilla'
    'ProductVersion': '3, 25, 2, 0'


Var file info
=============
type:         1
key:          VarFileInfo
Translations: UTF_16/NEUTRAL/NEUTRAL


Icon #0 :
Size:                            16x16 pixels
Color count:                     0
Reserved:                        0
Planes:                          1
Bit count:                       8
Hash:                            ffffffffca4fbb2f

Icon #1 :
Size:                            32x32 pixels
Color count:                     0
Reserved:                        0
Planes:                          1
Bit count:                       8
Hash:                            79eaa8a1

Icon #2 :
Size:                            48x48 pixels
Color count:                     0
Reserved:                        0
Planes:                          1
Bit count:                       8
Hash:                            1b38942

Icon #3 :
Size:                            48x48 pixels
Color count:                     0
Reserved:                        0
Planes:                          1
Bit count:                       20
Hash:                            281bc584

Icon #4 :
Size:                            0x0 pixels
Color count:                     0
Reserved:                        0
Planes:                          1
Bit count:                       20
Hash:                            13a82f1


<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
  <assemblyIdentity
    name="FileZilla"
    processorArchitecture="*"
    version="3.10.0.0"
    type="win32"
  />
  <description>FileZilla FTP client</description>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity
        type="win32"
        name="Microsoft.Windows.Common-Controls"
        version="6.0.0.0"
        processorArchitecture="*"
        publicKeyToken="6595b64144ccf1df"
        language="*"
      />
    </dependentAssembly>
  </dependency>
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
    <security>
      <requestedPrivileges>
        <requestedExecutionLevel level="asInvoker" uiAccess="false"/>
      </requestedPrivileges>
    </security>
   </trustInfo>
  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
    <application>
      <!--Vista-->
      <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
      <!--7-->
      <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
      <!--8-->
      <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
      <!--8.1-->
      <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
      <!--10 -->
      <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
    </application>
  </compatibility>
  <asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
    <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
      <dpiAware>true</dpiAware>
    </asmv3:windowsSettings>
  </asmv3:application>
</assembly>

Similarly to the previous example, accessing to the MANIFEST element is as simple as:

filezilla = lief.parse("filezilla.exe")

resources_manager = filezilla.resources_manager

if not resources_manager.has_manifest:
    print("'{}' has no manifest. Abort!".format(filezilla.name), file=sys.stderr)
    sys.exit(1)

manifest = resources_manager.manifest
print(manifest)

Play with Manifest

Now we will see how we can use practically the ResourcesManager to grant Administrator privilege to an executable thanks to the MANIFEST element.

The application manifest is implement as an XML document for which the documentation is available here: MSDN

Among these tags, the requestedExecutionLevel tag “describes the minimum security permissions required for the application to run on the client computer.” [1]

<requestedPrivileges>
  <requestedExecutionLevel level="..." uiAccess="..."/>
</requestedPrivileges>

This tag has the following options:

  • Level: Indicates the security level the application is requesting

    • asInvoker: Same permission as the process that started it

    • highestAvailable: The application will run with the highest permission level that it can

    • requireAdministrator: The application will run with administrator permissions

  • uiAccess (Optional): Indicates whether the application requires access to protected user interface elements

    • true

    • false

Thanks to the ResourcesManager replacing the asInvoker value to requireAdministrator is as simple as:

filezilla = lief.parse("filezilla.exe")

resources_manager = filezilla.resources_manager

if not resources_manager.has_manifest:
    print("'{}' has no manifest. Abort!".format(filezilla.name), file=sys.stderr)
    sys.exit(1)

manifest = resources_manager.manifest
manifest = manifest.replace("asInvoker", "requireAdministrator")
resources_manager.manifest = manifest

The PE Builder can be configured to rebuild or not the resource tree. To take account of modifications we need to rebuild it:

Warning

By default the Builder doesn’t rebuild the resource tree.

builder = lief.PE.Builder(filezilla)
builder.build_resources(True)
builder.build()
builder.write("filezilla_rsrc.exe")
../_images/filezilla.png

Play with Icons

The change_icon() method switch icons from two applications.

In the same way as the previous part, we get the ResourcesManager as follow:

mfc = lief.parse("mfc.exe")
cmd = lief.parse("cmd.exe")

mfc_rsrc_manager = mfc.resources_manager
cmd_rsrc_manager = cmd.resources_manager

Then we can switch the first icons of the applications:

mfc_icons = mfc_rsrc_manager.icons
cmd_icons = cmd_rsrc_manager.icons
for i in range(min(len(mfc_icons), len(cmd_icons))):
    mfc_rsrc_manager.change_icon(mfc_icons[i], cmd_icons[i])

The MFC icons before switching:

../_images/mfc.png

After the switch:

../_images/mfc_modified.png

References