Skip to content

Commit

Permalink
Merge pull request #405 from Washi1337/feature/fluent-patching-api
Browse files Browse the repository at this point in the history
Fluent Patching API
  • Loading branch information
Washi1337 committed Jan 24, 2023
2 parents f912d31 + 8274efb commit be42dd4
Show file tree
Hide file tree
Showing 22 changed files with 899 additions and 113 deletions.
4 changes: 2 additions & 2 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Table of Contents:

overview
faq
segments


.. toctree::
Expand Down Expand Up @@ -47,15 +48,14 @@ Table of Contents:
.. toctree::
:maxdepth: 1
:caption: .NET assemblies and modules
:name: sec-peimage
:name: sec-dotnet

dotnet/index
dotnet/basics
dotnet/advanced-module-reading
dotnet/member-tree
dotnet/type-signatures
dotnet/importing
dotnet/methods
dotnet/managed-method-bodies
dotnet/unmanaged-method-bodies
dotnet/dynamic-methods
Expand Down
6 changes: 3 additions & 3 deletions docs/pefile/sections.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ This can be used to read the data that is present in the section. If you want to
.. code-block:: csharp
byte[] data = section.ToArray();
The ``Sections`` property is mutable, which means you can add new sections and remove others from the PE.

Expand All @@ -42,8 +42,8 @@ Some sections (such as `.data` or `.bss`) contain uninitialized data, and might
var section = new PESection(".asmres", SectionFlags.MemoryRead | SectionFlags.ContentUninitializedData);
var physicalContents = new DataSegment(new byte[] {1, 2, 3, 4});
section.Contents = new VirtualSegment(physicalContents, 0x1000); // Create a new segment with a virtual size of 0x1000 bytes.
peFile.Sections.Add(section);
For more advanced section building, see :ref:`pe-building-sections`.
For more advanced section building, see :ref:`pe-building-sections` and :ref:`segments`.
70 changes: 54 additions & 16 deletions docs/peimage/pe-building.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,50 @@
PE File Building
================

An ``IPEImage`` is a higher-level abstraction of an actual PE file, and hides many of the actual implementation details such as raw section layout and contents of data directories. While this makes reading and interpreting a PE very easy, it makes writing a more involved process.
An ``IPEImage`` is a higher-level abstraction of an actual PE file, and hides many of the actual implementation details such as raw section layout and contents of data directories.
While this makes reading and interpreting a PE very easy, it makes writing a more involved process.

Unfortunately, the PE file format is not well-structured. Compilers and linkers have a lot of freedom when it comes to the actual design and layout of the final PE file. For example, one compiler might place the Import Address Table (IAT) in a separate section (such as the MSVC), while others will put it next to the code in the text section (such as the C# compiler). This degree of freedom makes it virtually impossible to make a completely generic PE file builder, and as such AsmResolver does not come with one out of the box.
Unfortunately, the PE file format is not well-structured.
Compilers and linkers have a lot of freedom when it comes to the actual design and layout of the final PE file.
For example, one compiler might place the Import Address Table (IAT) in a separate section (such as the MSVC), while others will put it next to the code in the text section (such as the C# compiler).
This degree of freedom makes it virtually impossible to make a completely generic PE file builder, and as such AsmResolver does not come with one out of the box.

It is still possible to build PE files from instances of ``IPEImage``, it might just take more manual labor than you might expect at first. This article will discuss certain strategies that can be adopted when constructing new PE files from PE images.
It is still possible to build PE files from instances of ``IPEImage``, it might just take more manual labor than you might expect at first.
This article will discuss certain strategies that can be adopted when constructing new PE files from PE images.


.. _pe-building-sections:

Building Sections
-----------------

As discussed in :ref:`pe-file-sections`, the ``PESection`` class represents a single section in the PE file. This class exposes a property called ``Contents`` which is of type ``ISegment``. A lot of models in AsmResolver implement this interface, and as such, these models can directly be used as the contents of a new section.
As discussed in :ref:`pe-file-sections`, the ``PESection`` class represents a single section in the PE file.
This class exposes a property called ``Contents`` which is of type ``ISegment``.
A lot of models in AsmResolver implement this interface, and as such, these models can directly be used as the contents of a new section.

.. code-block:: csharp
.. code-block:: csharp
var peFile = new PEFile();
var text = new PESection(".text",
SectionFlags.ContentCode | SectionFlags.MemoryRead | SectionFlags.MemoryExecute);
text.Content = new CodeSegment(new byte[] { ... } );
text.Content = new DataSegment(new byte[] { ... } );
peFile.Sections.Add(text);
In a lot of cases, however, sections do not consist of a single data structure, but often comprise many segments concatenated together, potentially with some padding in between. Assigning a single instance of ``ISegment`` might therefore not be as convenient. To facilitate more complicated structures, the ``SegmentBuilder`` class can help a lot with building sections like these. This is a class that combines a collection of ``ISegment`` instances into one, allowing you to concatenate segments that belong together easily. Below an example that builds up a ``.text`` section that consists of a .NET data directory as well as some native code. In the example, the native code is aligned to a 4-byte boundary:
In a lot of cases, however, sections do not consist of a single data structure, but often comprise many segments concatenated together, potentially with some padding in between.
To compose new segments from a collection of smaller sub-segments structures, the ``SegmentBuilder`` class can be used.
This is a class that combines a collection of ``ISegment`` instances into one, allowing you to concatenate segments that belong together easily.
Below an example that builds up a ``.text`` section that consists of a .NET data directory as well as some native code.
In the example, the native code is aligned to a 4-byte boundary:

.. code-block:: csharp
.. code-block:: csharp
var peFile = new PEFile();
var textBuilder = new SegmentBuilder();
textBuilder.Add(new DotNetDirectory { ... });
textBuilder.Add(new CodeSegment(new byte[] { ... } ), alignment: 4);
textBuilder.Add(new DataSegment(new byte[] { ... } ), alignment: 4);
var text = new PESection(".text",
SectionFlags.ContentCode | SectionFlags.MemoryRead | SectionFlags.MemoryExecute);
Expand All @@ -44,12 +55,38 @@ In a lot of cases, however, sections do not consist of a single data structure,
peFile.Sections.Add(text);
Using complex PE models
PE files can also be patched using the ``PatchedSegment`` API.
Below is an example of patching some data in the ``.text`` section of a PE File with some literal data ``{1, 2, 3, 4}``, as well as writing an address to a symbol in the imports table:

.. code-block:: csharp
var peFile = PEFile.FromFile("input.exe");
var section = peFile.Sections.First(s => s.Name == ".text");
var someSymbol = peImage
.Imports.First(m => m.Name == "ucrtbased.dll")
.Symbols.First(s => s.Name == "puts");
section.Contents = section.Contents.AsPatchedSegment() // Create patched segment.
.Patch(offset: 0x10, data: new byte[] {1, 2, 3, 4}) // Apply literal bytes patch
.Patch(offset: 0x20, AddressFixupType.Absolute64BitAddress, someSymbol); // Apply address fixup patch.
More detailed information on how to use segments can be found in :ref:`segments`.


Using complex PE models
-----------------------

The PE file format defines many complex data structures. While some of these models are represented in AsmResolver using classes that derive from ``ISegment``, a lot of the classes that represent these data structures do not implement this interface, and as such cannot be used as a value for the ``Contents`` property of a ``PESection`` directly. This is due to the fact that most of these models are not required to be one single entity or chunk of continuous memory within the PE file. Instead, they are often scattered around the PE file by a compiler. For example, the Import Directory has a second component the Import Address Table which is often put in a completely different PE section (usually ``.text`` or ``.data``) than the Import Directory itself (in ``.idata`` or ``.rdata``). To make reading and interpreting these data structures more convenient for the end-user, the ``AsmResolver.PE`` package adopted some design choices to abstract these details away to make things more natural to work with. The downside of this is that writing these structures requires you to specify where AsmResolver should place these models in the final PE file.
The PE file format defines many complex data structures.
While some of these models are represented in AsmResolver using classes that derive from ``ISegment``, a lot of the classes that represent these data structures do not implement this interface, and as such cannot be used as a value for the ``Contents`` property of a ``PESection`` directly.
This is due to the fact that most of these models are not required to be one single entity or chunk of continuous memory within the PE file. Instead, they are often scattered around the PE file by a compiler.
For example, the Import Directory has a second component the Import Address Table which is often put in a completely different PE section (usually ``.text`` or ``.data``) than the Import Directory itself (in ``.idata`` or ``.rdata``).
To make reading and interpreting these data structures more convenient for the end-user, the ``AsmResolver.PE`` package adopted some design choices to abstract these details away to make things more natural to work with.
The downside of this is that writing these structures requires you to specify where AsmResolver should place these models in the final PE file.

In ``AsmResolver.PE``, most models for which is the case reside in their own namespace, and have their own set of classes dedicated to constructing new segments defined in a ``Builder`` sub-namespace. For example, the Win32 resources directory models reside in ``AsmResolver.PE.Win32Resources``, but the actual builder classes are put in a sub namespace called ``AsmResolver.PE.Win32Resources.Builder``.
In ``AsmResolver.PE``, most models for which is the case reside in their own namespace, and have their own set of classes dedicated to constructing new segments defined in a ``Builder`` sub-namespace.
For example, the Win32 resources directory models reside in ``AsmResolver.PE.Win32Resources``, but the actual builder classes are put in a sub namespace called ``AsmResolver.PE.Win32Resources.Builder``.

.. code-block:: csharp
Expand Down Expand Up @@ -94,7 +131,8 @@ A more complicated structure such as the Imports Directory can be build like the
Using PEFileBuilders
--------------------

As a lot of the PE file-building process will be similar for many types of PE file layouts (such as the construction of the file and optional headers), AsmResolver comes with a base class called ``PEFileBuilderBase`` that abstracts many of these similarities away. Rather than defining and building up everything yourself, the ``PEFileBuilderBase`` allows you to override a couple of methods:
As a lot of the PE file-building process will be similar for many types of PE file layouts (such as the construction of the file and optional headers), AsmResolver comes with a base class called ``PEFileBuilderBase`` that abstracts many of these similarities away.
Rather than defining and building up everything yourself, the ``PEFileBuilderBase`` allows you to override a couple of methods:

.. code-block:: csharp
Expand All @@ -116,10 +154,10 @@ As a lot of the PE file-building process will be similar for many types of PE fi
protected override IEnumerable<DataDirectory> CreateDataDirectories(PEFile peFile, IPEImage image, MyBuilderContext context)
{
/* Create data directories here */
}
}
}
public class MyBuilderContext
public class MyBuilderContext
{
/* Define here additional state data to be used in your builder. */
}
Expand All @@ -132,4 +170,4 @@ This can then be used like the following:
IPEImage image = ...
var builder = new MyPEFileBuilder();
PEFile file = builder.CreateFile(image);
PEFile file = builder.CreateFile(image);
Loading

0 comments on commit be42dd4

Please sign in to comment.