Skip to content

Commit 4896f9a

Browse files
committed
Add support for PE relocation (#41)
1 parent b844ed6 commit 4896f9a

27 files changed

+519
-265
lines changed

src/LibObjectFile.Tests/PE/PEReaderTests.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ public void TestCreatePE()
109109
var exitProcessFunction = streamData.WriteHintName(new(0x178, "ExitProcess"));
110110

111111
// PEImportAddressTableDirectory comes first, it is referenced by the RIP + 0xFF1, first address being ExitProcess
112-
var peImportAddressTable = new PEImportAddressTable()
112+
var peImportAddressTable = new PEImportAddressTable64()
113113
{
114114
exitProcessFunction
115115
};
@@ -118,7 +118,7 @@ public void TestCreatePE()
118118
peImportAddressTable
119119
};
120120

121-
var peImportLookupTable = new PEImportLookupTable()
121+
var peImportLookupTable = new PEImportLookupTable64()
122122
{
123123
exitProcessFunction
124124
};
@@ -228,6 +228,11 @@ public async Task TestWindows(string sourceFile)
228228

229229
ByteArrayAssert.AreEqual(inputBuffer, outputBuffer, $"Invalid roundtrip for `{sourceFile}`");
230230
}
231+
232+
// Try to relocate the image
233+
var relocationDiagnostics = new DiagnosticBag();
234+
peImage.Relocate(0x4000, relocationDiagnostics);
235+
Assert.IsFalse(relocationDiagnostics.HasErrors, $"Relocation failed - {relocationDiagnostics}");
231236
}
232237

233238
public static IEnumerable<object[]> GetWindowsExeAndDlls()

src/LibObjectFile.Tests/Verified/PEReaderTests.TestPrinter_name=NativeConsole2Win64.exe.verified.txt

Lines changed: 38 additions & 38 deletions
Large diffs are not rendered by default.

src/LibObjectFile.Tests/Verified/PEReaderTests.TestPrinter_name=NativeConsoleWin64.exe.verified.txt

Lines changed: 36 additions & 36 deletions
Large diffs are not rendered by default.

src/LibObjectFile.Tests/Verified/PEReaderTests.TestPrinter_name=NativeLibraryWin64.dll.verified.txt

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ Sections
9999
[01] .rdata PESection Position = 0x00001400, Size = 0x00000E00, RVA = 0x00002000, VirtualSize = 0x00000C96, Characteristics = 0x40000040 (ContainsInitializedData, MemRead)
100100

101101
[00] PEImportAddressTableDirectory Position = 0x00001400, Size = 0x000000E8, RVA = 0x00002000, VirtualSize = 0x000000E8
102-
[00] PEImportAddressTable Position = 0x00001400, Size = 0x00000078, RVA = 0x00002000, VirtualSize = 0x00000078
102+
[00] PEImportAddressTable64 Position = 0x00001400, Size = 0x00000078, RVA = 0x00002000, VirtualSize = 0x00000078
103103
[0] PEImportHintName { Hint = 567, Name = GetCurrentThreadId } (RVA = 0x2C24, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x20C)
104104
[1] PEImportHintName { Hint = 1284, Name = RtlVirtualUnwind } (RVA = 0x2B62, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x14A)
105105
[2] PEImportHintName { Hint = 1277, Name = RtlLookupFunctionEntry } (RVA = 0x2B48, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x130)
@@ -115,13 +115,13 @@ Sections
115115
[12] PEImportHintName { Hint = 936, Name = IsProcessorFeaturePresent } (RVA = 0x2BD8, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x1C0)
116116
[13] PEImportHintName { Hint = 1476, Name = TerminateProcess } (RVA = 0x2BC4, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x1AC)
117117

118-
[01] PEImportAddressTable Position = 0x00001478, Size = 0x00000028, RVA = 0x00002078, VirtualSize = 0x00000028
118+
[01] PEImportAddressTable64 Position = 0x00001478, Size = 0x00000028, RVA = 0x00002078, VirtualSize = 0x00000028
119119
[0] PEImportHintName { Hint = 60, Name = memcpy } (RVA = 0x2C8C, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x274)
120120
[1] PEImportHintName { Hint = 37, Name = __std_type_info_destroy_list } (RVA = 0x2A30, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x18)
121121
[2] PEImportHintName { Hint = 8, Name = __C_specific_handler } (RVA = 0x2A18, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x0)
122122
[3] PEImportHintName { Hint = 62, Name = memset } (RVA = 0x2A50, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x38)
123123

124-
[02] PEImportAddressTable Position = 0x000014A0, Size = 0x00000048, RVA = 0x000020A0, VirtualSize = 0x00000048
124+
[02] PEImportAddressTable64 Position = 0x000014A0, Size = 0x00000048, RVA = 0x000020A0, VirtualSize = 0x00000048
125125
[0] PEImportHintName { Hint = 22, Name = _cexit } (RVA = 0x2B08, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0xF0)
126126
[1] PEImportHintName { Hint = 34, Name = _execute_onexit_table } (RVA = 0x2AF0, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0xD8)
127127
[2] PEImportHintName { Hint = 52, Name = _initialize_onexit_table } (RVA = 0x2AD4, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0xBC)
@@ -237,19 +237,19 @@ Sections
237237

238238
[11] PEImportDirectory Position = 0x00001CE0, Size = 0x00000050, RVA = 0x000028E0, VirtualSize = 0x00000050
239239
[0] ImportDllNameLink = VCRUNTIME140.dll (RVA = 0x2A5A, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x42)
240-
[0] ImportAddressTable = RVA = 0x00002078 (PEImportAddressTable[1] -> PEImportAddressTableDirectory[0] -> .rdata)
241-
[0] ImportLookupTable = RVA = 0x000029A8 (PEImportLookupTable[13] -> .rdata)
240+
[0] ImportAddressTable = RVA = 0x00002078 (PEImportAddressTable64[1] -> PEImportAddressTableDirectory[0] -> .rdata)
241+
[0] ImportLookupTable = RVA = 0x000029A8 (PEImportLookupTable64[13] -> .rdata)
242242

243243
[1] ImportDllNameLink = api-ms-win-crt-runtime-l1-1-0.dll (RVA = 0x2B12, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0xFA)
244-
[1] ImportAddressTable = RVA = 0x000020A0 (PEImportAddressTable[2] -> PEImportAddressTableDirectory[0] -> .rdata)
245-
[1] ImportLookupTable = RVA = 0x000029D0 (PEImportLookupTable[14] -> .rdata)
244+
[1] ImportAddressTable = RVA = 0x000020A0 (PEImportAddressTable64[2] -> PEImportAddressTableDirectory[0] -> .rdata)
245+
[1] ImportLookupTable = RVA = 0x000029D0 (PEImportLookupTable64[14] -> .rdata)
246246

247247
[2] ImportDllNameLink = KERNEL32.dll (RVA = 0x2C7E, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x266)
248-
[2] ImportAddressTable = RVA = 0x00002000 (PEImportAddressTable[0] -> PEImportAddressTableDirectory[0] -> .rdata)
249-
[2] ImportLookupTable = RVA = 0x00002930 (PEImportLookupTable[12] -> .rdata)
248+
[2] ImportAddressTable = RVA = 0x00002000 (PEImportAddressTable64[0] -> PEImportAddressTableDirectory[0] -> .rdata)
249+
[2] ImportLookupTable = RVA = 0x00002930 (PEImportLookupTable64[12] -> .rdata)
250250

251251

252-
[12] PEImportLookupTable Position = 0x00001D30, Size = 0x00000078, RVA = 0x00002930, VirtualSize = 0x00000078
252+
[12] PEImportLookupTable64 Position = 0x00001D30, Size = 0x00000078, RVA = 0x00002930, VirtualSize = 0x00000078
253253
[0] PEImportHintName { Hint = 567, Name = GetCurrentThreadId } (RVA = 0x2C24, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x20C)
254254
[1] PEImportHintName { Hint = 1284, Name = RtlVirtualUnwind } (RVA = 0x2B62, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x14A)
255255
[2] PEImportHintName { Hint = 1277, Name = RtlLookupFunctionEntry } (RVA = 0x2B48, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x130)
@@ -265,13 +265,13 @@ Sections
265265
[12] PEImportHintName { Hint = 936, Name = IsProcessorFeaturePresent } (RVA = 0x2BD8, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x1C0)
266266
[13] PEImportHintName { Hint = 1476, Name = TerminateProcess } (RVA = 0x2BC4, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x1AC)
267267

268-
[13] PEImportLookupTable Position = 0x00001DA8, Size = 0x00000028, RVA = 0x000029A8, VirtualSize = 0x00000028
268+
[13] PEImportLookupTable64 Position = 0x00001DA8, Size = 0x00000028, RVA = 0x000029A8, VirtualSize = 0x00000028
269269
[0] PEImportHintName { Hint = 60, Name = memcpy } (RVA = 0x2C8C, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x274)
270270
[1] PEImportHintName { Hint = 37, Name = __std_type_info_destroy_list } (RVA = 0x2A30, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x18)
271271
[2] PEImportHintName { Hint = 8, Name = __C_specific_handler } (RVA = 0x2A18, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x0)
272272
[3] PEImportHintName { Hint = 62, Name = memset } (RVA = 0x2A50, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x38)
273273

274-
[14] PEImportLookupTable Position = 0x00001DD0, Size = 0x00000048, RVA = 0x000029D0, VirtualSize = 0x00000048
274+
[14] PEImportLookupTable64 Position = 0x00001DD0, Size = 0x00000048, RVA = 0x000029D0, VirtualSize = 0x00000048
275275
[0] PEImportHintName { Hint = 22, Name = _cexit } (RVA = 0x2B08, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0xF0)
276276
[1] PEImportHintName { Hint = 34, Name = _execute_onexit_table } (RVA = 0x2AF0, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0xD8)
277277
[2] PEImportHintName { Hint = 52, Name = _initialize_onexit_table } (RVA = 0x2AD4, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0xBC)

src/LibObjectFile.Tests/Verified/PEReaderTests.TestPrinter_name=RawNativeConsoleWin64.exe.verified.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ Sections
9696
[01] .rdata PESection Position = 0x00000600, Size = 0x00000200, RVA = 0x00002000, VirtualSize = 0x0000019C, Characteristics = 0x40000040 (ContainsInitializedData, MemRead)
9797

9898
[00] PEImportAddressTableDirectory Position = 0x00000600, Size = 0x00000010, RVA = 0x00002000, VirtualSize = 0x00000010
99-
[00] PEImportAddressTable Position = 0x00000600, Size = 0x00000010, RVA = 0x00002000, VirtualSize = 0x00000010
99+
[00] PEImportAddressTable64 Position = 0x00000600, Size = 0x00000010, RVA = 0x00002000, VirtualSize = 0x00000010
100100
[0] PEImportHintName { Hint = 376, Name = ExitProcess } (RVA = 0x2180, PEStreamSectionData { RVA = 0x2180, VirtualSize = 0x1C, Position = 0x780, Size = 0x1C }, Offset = 0x0)
101101

102102

@@ -112,13 +112,13 @@ Sections
112112

113113
[05] PEImportDirectory Position = 0x00000744, Size = 0x00000028, RVA = 0x00002144, VirtualSize = 0x00000028
114114
[0] ImportDllNameLink = KERNEL32.dll (RVA = 0x218E, PEStreamSectionData { RVA = 0x2180, VirtualSize = 0x1C, Position = 0x780, Size = 0x1C }, Offset = 0xE)
115-
[0] ImportAddressTable = RVA = 0x00002000 (PEImportAddressTable[0] -> PEImportAddressTableDirectory[0] -> .rdata)
116-
[0] ImportLookupTable = RVA = 0x00002170 (PEImportLookupTable[7] -> .rdata)
115+
[0] ImportAddressTable = RVA = 0x00002000 (PEImportAddressTable64[0] -> PEImportAddressTableDirectory[0] -> .rdata)
116+
[0] ImportLookupTable = RVA = 0x00002170 (PEImportLookupTable64[7] -> .rdata)
117117

118118

119119
[06] PEStreamSectionData Position = 0x0000076C, Size = 0x00000004, RVA = 0x0000216C, VirtualSize = 0x00000004
120120

121-
[07] PEImportLookupTable Position = 0x00000770, Size = 0x00000010, RVA = 0x00002170, VirtualSize = 0x00000010
121+
[07] PEImportLookupTable64 Position = 0x00000770, Size = 0x00000010, RVA = 0x00002170, VirtualSize = 0x00000010
122122
[0] PEImportHintName { Hint = 376, Name = ExitProcess } (RVA = 0x2180, PEStreamSectionData { RVA = 0x2180, VirtualSize = 0x1C, Position = 0x780, Size = 0x1C }, Offset = 0x0)
123123

124124
[08] PEStreamSectionData Position = 0x00000780, Size = 0x0000001C, RVA = 0x00002180, VirtualSize = 0x0000001C

src/LibObjectFile/Diagnostics/DiagnosticId.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,8 @@ public enum DiagnosticId
179179
PE_ERR_BaseRelocationDirectoryInvalidSizeOfBlock = 3705,
180180
PE_ERR_InvalidBaseRelocationBlock = 3706,
181181
PE_ERR_BaseRelocationDirectoryInvalidSectionLink = 3707,
182+
PE_ERR_BaseRelocationInvalid = 3708,
183+
PE_WRN_BaseRelocationInVirtualMemory = 3709,
182184

183185
// PE Import
184186
PE_ERR_ImportDirectoryInvalidEndOfStream = 3800,

src/LibObjectFile/PE/DataDirectory/PEBaseRelocation.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public PEBaseRelocation(PEBaseRelocationType type, ushort offsetInBlock)
4343
/// <summary>
4444
/// Gets the virtual offset of the base relocation relative to the offset of the associated <see cref="PEBaseRelocationBlock"/>.
4545
/// </summary>
46-
public ushort OffsetInBlock => (ushort)(_value & VirtualOffsetMask);
46+
public ushort OffsetInPage => (ushort)(_value & VirtualOffsetMask);
4747

48-
public override string ToString() => Type == PEBaseRelocationType.Absolute ? $"{Type} Zero Padding" : $"{Type} OffsetInBlock = 0x{OffsetInBlock:X}";
48+
public override string ToString() => Type == PEBaseRelocationType.Absolute ? $"{Type} Zero Padding" : $"{Type} OffsetInBlock = 0x{OffsetInPage:X}";
4949
}

src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public PEBaseRelocationBlock()
3030
/// <summary>
3131
/// Gets or sets the linked <see cref="PESection"/> and its virtual offset within it.
3232
/// </summary>
33-
public PESectionLink SectionLink { get; set; }
33+
public PESectionLink PageLink { get; set; }
3434

3535
/// <summary>
3636
/// Gets the list of relocations for this block.
@@ -55,7 +55,7 @@ protected override unsafe void UpdateLayoutCore(PELayoutContext context)
5555
/// </summary>
5656
/// <param name="relocation">The relocation.</param>
5757
/// <returns>The RVA of the relocation.</returns>
58-
public RVA GetRVA(PEBaseRelocation relocation) => SectionLink.RVA() + relocation.OffsetInBlock;
58+
public RVA GetRVA(PEBaseRelocation relocation) => PageLink.RVA() + relocation.OffsetInPage;
5959

6060
/// <summary>
6161
/// Reads the address from the section data.
@@ -70,7 +70,7 @@ public ulong ReadAddress(PEFile file, PEBaseRelocation relocation)
7070
throw new InvalidOperationException($"The base relocation type {relocation.Type} not supported. Only Dir64 is supported for this method.");
7171
}
7272

73-
var vaOfReloc = SectionLink.RVA() + relocation.OffsetInBlock;
73+
var vaOfReloc = PageLink.RVA() + relocation.OffsetInPage;
7474

7575
if (!file.TryFindByRVA(vaOfReloc, out var container))
7676
{
@@ -125,7 +125,7 @@ public override unsafe void Read(PEImageReader reader)
125125
}
126126

127127

128-
SectionLink = new PESectionLink(section, (uint)(block.PageRVA - section.RVA));
128+
PageLink = new PESectionLink(section, (uint)(block.PageRVA - section.RVA));
129129

130130
var sizeOfRelocations = block.SizeOfBlock - sizeof(ImageBaseRelocation);
131131

@@ -156,7 +156,7 @@ public override unsafe void Write(PEImageWriter writer)
156156
{
157157
var block = new ImageBaseRelocation
158158
{
159-
PageRVA = SectionLink.RVA(),
159+
PageRVA = PageLink.RVA(),
160160
SizeOfBlock = (uint)Size
161161
};
162162

@@ -174,7 +174,7 @@ public override unsafe void Write(PEImageWriter writer)
174174

175175
public override void Verify(PEVerifyContext context)
176176
{
177-
context.VerifyObject(SectionLink.Container, this, nameof(SectionLink), false);
177+
context.VerifyObject(PageLink.Container, this, nameof(PageLink), false);
178178
}
179179

180180
protected override bool PrintMembers(StringBuilder builder)
@@ -184,7 +184,7 @@ protected override bool PrintMembers(StringBuilder builder)
184184
builder.Append(", ");
185185
}
186186

187-
builder.Append($"Section = {SectionLink.Container?.Name}, Block RVA = {SectionLink.RVA()}, Relocations[{Relocations.Count}]");
187+
builder.Append($"Section = {PageLink.Container?.Name}, Block RVA = {PageLink.RVA()}, Relocations[{Relocations.Count}]");
188188
return true;
189189
}
190190

src/LibObjectFile/PE/DataDirectory/PEBoundImportAddressTable32.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) Alexandre Mutel. All rights reserved.
1+
// Copyright (c) Alexandre Mutel. All rights reserved.
22
// This file is licensed under the BSD-Clause 2 license.
33
// See the license.txt file in the project root for more information.
44

@@ -32,4 +32,6 @@ public override void WriteAt(uint offset, ReadOnlySpan<byte> source)
3232
var buffer = MemoryMarshal.AsBytes(CollectionsMarshal.AsSpan(Entries));
3333
DataUtils.WriteAt(buffer, offset, source);
3434
}
35+
36+
public override unsafe bool CanReadWriteAt(uint offset, uint size) => offset + size <= (uint)Entries.Count * sizeof(VA32);
3537
}

src/LibObjectFile/PE/DataDirectory/PEBoundImportAddressTable64.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) Alexandre Mutel. All rights reserved.
1+
// Copyright (c) Alexandre Mutel. All rights reserved.
22
// This file is licensed under the BSD-Clause 2 license.
33
// See the license.txt file in the project root for more information.
44

@@ -32,4 +32,6 @@ public override void WriteAt(uint offset, ReadOnlySpan<byte> source)
3232
var buffer = MemoryMarshal.AsBytes(CollectionsMarshal.AsSpan(Entries));
3333
DataUtils.WriteAt(buffer, offset, source);
3434
}
35+
36+
public override unsafe bool CanReadWriteAt(uint offset, uint size) => offset + size <= (uint)Entries.Count * sizeof(VA64);
3537
}

0 commit comments

Comments
 (0)