Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reworked IconResource #565

Merged
merged 2 commits into from
Jun 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/guides/peimage/win32resources.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ if (dataEntry.CanRead)
}
```

The data read using `CreateReader` is raw data as it appears in the file.
AsmResolver provides rich interpretations for some of the resource types.
See the [AsmResolver.PE.Win32Resources](../win32res/index.md) extension package for all supported resource types.

Adding new data entries can be done by using the `ResourceData`
constructor:

Expand Down
9 changes: 9 additions & 0 deletions docs/guides/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,15 @@
- name: Advanced PE Image Building
href: dotnet/advanced-pe-image-building.md

- name: Win32 Resources
- name: Overview
href: win32res/index.md
- name: Version Info
href: win32res/version.md
- name: Icons and Cursors
href: win32res/icons.md


- name: PDB Symbols
- name: Overview
href: pdb/index.md
Expand Down
87 changes: 87 additions & 0 deletions docs/guides/win32res/icons.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Icons and Cursors (RT_GROUP_CURSOR, RT_GROUP_ICON, RT_CURSOR, RT_ICON)

Icons and cursors follow a near-identical structure in the PE file format, and closely resemble the ICO file format.
Each icon is actually a set of individual icon files containing the same image in various resolutions, pixel formats and color palettes.

The relevant code for reading and writing icon resources can be found in the following namespace:

```csharp
using AsmResolver.PE.Win32Resources.Icon;
```

Icon resources are represented using the `IconResource` class.
In the following, basic usage of this class is described.

## Creating Icon Resources

To create a new icon resource, simply use the constructor of the `IconResource` class, specifying the type of icons to store.

```csharp
var icons = new IconResource(IconType.Icon);
```

```csharp
var cursors = new IconResource(IconType.Cursor);
```


## Reading Icon Resources

To extract existing icons from a Portable Executable file, you will need to access the root resource directory of a `PEImage`.
Then, all icon groups can be obtained using the `FromDirectory` factory method:

```csharp
PEImage image = ...;
var icons = IconResource.FromDirectory(image.Resources, IconType.Icon);
```


## Icon Groups

Icon groups are exposed using the `Groups` property, and can be iterated and modified:

```csharp
foreach (var iconGroup in icons.Groups)
{
Console.WriteLine($"ID: {iconGroup.Id}, LCID: {iconGroup.Lcid}");
}
```

Each icon group consists of a set of `Icons`, each containing pixel data for a specific icon of a particular resolution and format:

```csharp
IconGroup iconGroup = ...;
foreach (var icon in iconGroup.Icons)
{
Console.WriteLine($"- {icon.Id}, {icon.Width}x{icon.Height}, {icon.PixelData.GetPhysicalSize()} bytes");
}
```

A single `IconEntry` contains both fields for the ICO or CUR header such as `Width` and `Height`, as well as the raw pixel data in the `PixelData` property.
AsmResolver does not provide a way to interpret or reconstruct pixel data stored in an icon entry.
It only exposes a raw `ISegment` which can be read using a `BinaryStreamReader` or turned into a byte array (see [Reading Segment Contents](../core/segments.md#reading-segment-contents)).

```csharp
byte[] rawData = icon.PixelData.WriteIntoArray();
```

> [!NOTE]
> The `PixelData` property contains the icon's raw data **excluding** the ICO or CUR header.
> When modifying icon entries, make sure the header fields in the `IconEntry` object itself are therefore updated accordingly.


All collections and properties are mutable, and as such can be used to modify icons stored in a PE file.


## Writing Icon Resources

To serialize the (modified) icon resource back to the resources of a PE image, use the `InsertIntoDirectory` method:

```csharp
PEImage image = ...;
IconResource icons = ...;
icons.InsertIntoDirectory(image.Resources);
```

The PE image can then be saved as normal.
Either rebuild the PE image (see [Writing a PE Image](../peimage/basics.md#writing-a-pe-image)), or simply add a new section to the PE file with the contents of `image.Resources` (see [Adding a new Section](../pefile/sections.md#adding-a-new-section)).
14 changes: 14 additions & 0 deletions docs/guides/win32res/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Overview

Win32 resources are additional files embedded into the PE image, and are organized by their resource type in directories typically stored in the `.rsrc` section.

While `AsmResolver.PE` provides basic traversal of these win32 resource directories (see [Win32 Resources](../peimage/win32resources.md)), it stops at exposing the raw data of each of the embedded files.
The `AsmResolver.PE.Win32Resources` package is an extension that provides a richer API for reading and writing resource data for various resource types.

The following resource types are supported by this extension package:

- [RT_CURSOR](icons.md)
- [RT_GROUP_CURSOR](icons.md)
- [RT_GROUP_ICON](icons.md)
- [RT_ICON](icons.md)
- [RT_VERSIONINFO](version.md)
125 changes: 125 additions & 0 deletions docs/guides/win32res/version.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# Version Info (RT_VERSIONINFO)

The `RT_VERSIONINFO` resource type stores metadata describing product names, version numbers and copyright holders that are associated to a Portable Executable (PE) file.

The relevant code for reading and writing version information can be found in the following namespace:

```csharp
using AsmResolver.PE.Win32Resources.Version;
```

AsmResolver represents version metadata using the `VersionInfoResource` class.
In the following, basic usage of this class is described.


## Creating Version Info

Creating a new version info resource can be done using the constructor of `VersionInfoResource`:

```csharp
var versionInfo = new VersionInfoResource();
```

By default, this creates a language-neutral version info (LCID: 0). To customize this, use the constructor overload:

```csharp
var versionInfo = new VersionInfoResource(lcid: 1033);
```


## Reading Version Info

To extract existing version information from a Portable Executable file, you will need to access the root resource directory of a `PEImage`.
Then, the main version info directory can be obtained using the `FromDirectory` factory method:

```csharp
PEImage image = ...;
var versionInfo = VersionInfoResource.FromDirectory(image.Resources);
```

If the executable contains multiple version info entries for different languages, use the `FindAllFromDirectory` method instead:

```csharp
PEImage image = ...;
var versionInfos = VersionInfoResource.FindAllFromDirectory(image.Resources);
```

To retrieve version info with a specific language identifier (LCID), use the `FromDirectory` overload accepting an extra parameter:

```csharp
PEImage image = ...;
var versionInfo = VersionInfoResource.FromDirectory(image.Resources, lcid: 1033);
```

## Fixed File Version Info

Every version info resource starts with a fixed file version info header, exposed via the `FixedVersionInfo` property.

```csharp
Console.WriteLine($"File Version: {versionInfo.FixedVersionInfo.FileVersion}");
Console.WriteLine($"Product Version: {versionInfo.FixedVersionInfo.ProductVersion}");
Console.WriteLine($"Target OS: {versionInfo.FixedVersionInfo.FileOS}");
```

All properties in this object are mutable, and thus can be modified to change the version info of the PE file.


## String and Var Tables

Version info metadata may also contain additional string tables that can contain arbitrary strings.
The way they are organized in optional blobs after the fixed version info, in good PE file format fashion with a good amount of redundancy.

String tables are stored in `StringFileInfo` instances, which can be created or extracted as follows:

```csharp
var stringInfo = new StringFileInfo();
versionInfo.AddEntry(stringInfo);
```

```csharp
var stringInfo = versionInfo.GetChild<StringFileInfo>(StringFileInfo.StringFileInfoKey);
```

A single string table can then be created as follows:

```csharp
var stringTable = new StringTable(languageIdentifier: 1033, codePage: 1200);
stringInfo.Tables.Add(stringTable);
```
```csharp
var stringTable = stringInfo.Tables[0];
```

String tables contain information such as product name and copyright information:

```csharp
Console.WriteLine($"Product Name: {stringTable[StringTable.ProductNameKey]}");
Console.WriteLine($"Copyright: {stringTable[StringTable.LegalCopyrightKey]}");
```

Each string table is accompanied with a `VarTable`, stored in the `VarFileInfo` blob, containing both the language identifier and code page:

```csharp
var varInfo = new VarFileInfo();
versionInfo.AddEntry(varTable);
```

```csharp
var varTable = new VarTable();
varTable.Values.Add(1033u | 1200u << 16);
varInfo.Tables.Add(varTable);
```


## Writing Version Info

To serialize the (modified) version info metadata back to the resources of a PE image, use the `InsertIntoDirectory` method:

```csharp
PEImage image = ...;
VersionInfoResource versionInfo = ...;
versionInfo.InsertIntoDirectory(image.Resources);
```

The PE image can then be saved as normal.
Either rebuild the PE image (see [Writing a PE Image](../peimage/basics.md#writing-a-pe-image)), or simply add a new section to the PE file with the contents of `image.Resources` (see [Adding a new Section](../pefile/sections.md#adding-a-new-section)).
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<PackageTags>exe pe directories imports exports resources dotnet cil inspection manipulation assembly disassembly</PackageTags>
<NoWarn>1701;1702;NU5105</NoWarn>
<IsTrimmable>true</IsTrimmable>
<LangVersion>12</LangVersion>
</PropertyGroup>

<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
Expand All @@ -20,6 +21,10 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Nullable" Version="1.3.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
Expand Down
Loading