mirror of
https://github.com/QuasarApp/LIEF.git
synced 2025-05-16 13:19:34 +00:00
270 lines
8.7 KiB
ReStructuredText
270 lines
8.7 KiB
ReStructuredText
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 <https://github.com/lief-project/tutorials/tree/master/07_PE_resource>`_
|
|
|
|
------
|
|
|
|
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 :attr:`~lief.PE.DataDirectory.section` attribute of the associated :class:`~lief.PE.DataDirectory`
|
|
|
|
.. code-block:: python
|
|
|
|
binary = lief.parse("C:\\Windows\\explorer.exe")
|
|
if binary.has_resources:
|
|
rsrc_directory = binary.data_directory(lief.PE.DATA_DIRECTORY.RESOURCE_TABLE)
|
|
if rsrc_directory.has_section:
|
|
print(rsrc_directory.section)
|
|
|
|
.. code-block:: console
|
|
|
|
.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:
|
|
|
|
.. figure:: ../_static/tutorial/07/07_resource_tree.png
|
|
:scale: 90 %
|
|
:align: center
|
|
|
|
In the resource tree we basically have two kinds of node:
|
|
|
|
#. :class:`~lief.PE.ResourceDirectory`: Contains some information about the subtree.
|
|
#. :class:`~lief.PE.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 :attr:`~lief.PE.ResourceDirectory.id` represents the :class:`~lief.PE.RESOURCE_TYPES`
|
|
* Level 2: The :attr:`~lief.PE.ResourceDirectory.id` represents an ID to access to the resource
|
|
* Level 3: The :attr:`~lief.PE.ResourceDirectory.id` represents the :class:`~lief.PE.RESOURCE_LANGS` / :class:`~lief.PE.RESOURCE_SUBLANGS` of the resource.
|
|
|
|
|
|
We can check that a given binary embed resources with the :attr:`~lief.PE.Binary.has_resources` property, then we can access to this structure
|
|
through the :attr:`~lief.PE.Binary.resources` property which returns a :class:`~lief.PE.ResourceDirectory` representing the **root** of the tree.
|
|
|
|
Given a :class:`~lief.PE.ResourceDirectory`, the :attr:`~lief.PE.ResourceDirectory.childs` property returns an **iterator** (quiet similar to a ``list``) over the sub tree associated with the node.
|
|
|
|
The following snippet retrieves the :attr:`~lief.PE.RESOURCE_TYPES.MANIFEST` element and print it.
|
|
|
|
.. code-block:: python
|
|
|
|
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(filter(lambda e : e.id == lief.PE.RESOURCE_TYPES.MANIFEST, root.childs))
|
|
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)
|
|
|
|
.. code-block:: console
|
|
|
|
[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 includes a :class:`~lief.PE.ResourcesManager` which provides an enhanced API to manipulate binary resources
|
|
|
|
|
|
Resource Manager
|
|
****************
|
|
|
|
As mentioned previously, the :class:`~lief.PE.ResourcesManager` is a kind of wrapper over the resource tree to:
|
|
|
|
* Parse resources that have a predefined structures like
|
|
:attr:`~lief.PE.RESOURCE_TYPES.MANIFEST`, :attr:`~lief.PE.RESOURCE_TYPES.ICON`, :attr:`~lief.PE.RESOURCE_TYPES.VERSION` ...
|
|
* Access and modify these structures
|
|
|
|
This can be summarize with the following diagram:
|
|
|
|
.. figure:: ../_static/tutorial/07/07_pe_resource_manager.png
|
|
:scale: 110 %
|
|
:align: center
|
|
|
|
|
|
The :class:`~lief.PE.ResourcesManager` can be accessed with the :attr:`~lief.PE.Binary.resources_manager` property. To have an overview of the binary's resources, we can simply *print* the :class:`~lief.PE.ResourcesManager` instance:
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
filezilla = lief.parse("filezilla.exe")
|
|
|
|
resource_manager = filezilla.resources_manager
|
|
print(resource_manager)
|
|
|
|
.. literalinclude:: ../_static/tutorial/07/resource_manager_output.txt
|
|
|
|
Similarly to the previous example, accessing to the :attr:`~lief.PE.RESOURCE_TYPES.MANIFEST` element is as simple as:
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
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 :class:`~lief.PE.ResourcesManager` to grant *Administrator* privilege to an executable thanks to the :attr:`~lief.PE.RESOURCE_TYPES.MANIFEST` element.
|
|
|
|
The application manifest is implement as an XML document whose the documentation is available here: `MSDN <https://msdn.microsoft.com/en-us/library/windows/desktop/aa375632(v=vs.85).aspx>`_
|
|
|
|
Among these tags, the ``requestedExecutionLevel`` tag *"describes the minimum security permissions required for the application to run on the client computer."* [#f1]_
|
|
|
|
.. code-block:: xml
|
|
|
|
<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 :class:`~lief.PE.ResourcesManager` replacing the ``asInvoker`` value to ``requireAdministrator`` is as simple as:
|
|
|
|
.. code-block:: python
|
|
|
|
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 :class:`~lief.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 :class:`~lief.PE.Builder` doesn't rebuild the resource tree.
|
|
|
|
.. code-block:: python
|
|
|
|
builder = lief.PE.Builder(filezilla)
|
|
builder.build_resources(True)
|
|
builder.build()
|
|
builder.write("filezilla_rsrc.exe")
|
|
|
|
|
|
.. figure:: ../_static/tutorial/07/filezilla.png
|
|
:scale: 90 %
|
|
:align: center
|
|
|
|
|
|
|
|
Play with Icons
|
|
***************
|
|
|
|
The :meth:`~lief.PE.ResourcesManager.change_icon` method switch icons from two applications.
|
|
|
|
In the same way as the previous part, we get the :class:`~lief.PE.ResourcesManager` as follow:
|
|
|
|
.. code-block:: python
|
|
|
|
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:
|
|
|
|
.. code-block:: python
|
|
|
|
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:
|
|
|
|
.. figure:: ../_static/tutorial/07/mfc.png
|
|
:scale: 90 %
|
|
:align: center
|
|
|
|
|
|
After the switch:
|
|
|
|
.. figure:: ../_static/tutorial/07/mfc_modified.png
|
|
:scale: 90 %
|
|
:align: center
|
|
|
|
|
|
|
|
.. rubric:: References
|
|
|
|
.. [#f1] https://msdn.microsoft.com/en-us/library/6ad1fshk.aspx
|
|
|