Skip to content

Commit 13a97c8

Browse files
authored
Make HostModel PEUtils always read/write little endian (#92392)
This makes IsPEImage, GetWindowsGraphicalUserInterfaceBit, and SetWindowsGraphicalUserInterfaceBit in PEUtils explicitly read / write in little endian (per PE format) instead of whatever the system is.
1 parent 4ad310d commit 13a97c8

File tree

2 files changed

+44
-83
lines changed
  • src/installer

2 files changed

+44
-83
lines changed
Lines changed: 39 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System;
5+
using System.Buffers.Binary;
46
using System.IO;
57
using System.IO.MemoryMappedFiles;
8+
using System.Reflection.PortableExecutable;
69

710
namespace Microsoft.NET.HostModel.AppHost
811
{
@@ -15,29 +18,13 @@ public static class PEUtils
1518
/// <returns>true if the accessor represents a PE image, false otherwise.</returns>
1619
internal static unsafe bool IsPEImage(MemoryMappedViewAccessor accessor)
1720
{
18-
byte* pointer = null;
21+
if (accessor.Capacity < PEOffsets.DosStub.PESignatureOffset + sizeof(uint))
22+
return false;
1923

20-
try
21-
{
22-
accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref pointer);
23-
byte* bytes = pointer + accessor.PointerOffset;
24-
25-
// https://en.wikipedia.org/wiki/Portable_Executable
26-
// Validate that we're looking at Windows PE file
27-
if (((ushort*)bytes)[0] != PEOffsets.DosImageSignature
28-
|| accessor.Capacity < PEOffsets.DosStub.PESignatureOffset + sizeof(uint))
29-
{
30-
return false;
31-
}
32-
return true;
33-
}
34-
finally
35-
{
36-
if (pointer != null)
37-
{
38-
accessor.SafeMemoryMappedViewHandle.ReleasePointer();
39-
}
40-
}
24+
// https://en.wikipedia.org/wiki/Portable_Executable
25+
// Validate that we're looking at Windows PE file
26+
ushort signature = AsLittleEndian(accessor.ReadUInt16(0));
27+
return signature == PEOffsets.DosImageSignature;
4128
}
4229

4330
public static bool IsPEImage(string filePath)
@@ -60,40 +47,15 @@ public static bool IsPEImage(string filePath)
6047
/// <param name="accessor">The memory accessor which has the apphost file opened.</param>
6148
internal static unsafe void SetWindowsGraphicalUserInterfaceBit(MemoryMappedViewAccessor accessor)
6249
{
63-
byte* pointer = null;
64-
65-
try
66-
{
67-
accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref pointer);
68-
byte* bytes = pointer + accessor.PointerOffset;
69-
70-
// https://en.wikipedia.org/wiki/Portable_Executable
71-
uint peHeaderOffset = ((uint*)(bytes + PEOffsets.DosStub.PESignatureOffset))[0];
72-
73-
if (accessor.Capacity < peHeaderOffset + PEOffsets.PEHeader.Subsystem + sizeof(ushort))
74-
{
75-
throw new AppHostNotPEFileException("Subsystem offset out of file range.");
76-
}
77-
78-
ushort* subsystem = ((ushort*)(bytes + peHeaderOffset + PEOffsets.PEHeader.Subsystem));
79-
80-
// https://docs.microsoft.com/en-us/windows/desktop/Debug/pe-format#windows-subsystem
81-
// The subsystem of the prebuilt apphost should be set to CUI
82-
if (subsystem[0] != (ushort)PEOffsets.Subsystem.WindowsCui)
83-
{
84-
throw new AppHostNotCUIException(subsystem[0]);
85-
}
86-
87-
// Set the subsystem to GUI
88-
subsystem[0] = (ushort)PEOffsets.Subsystem.WindowsGui;
89-
}
90-
finally
91-
{
92-
if (pointer != null)
93-
{
94-
accessor.SafeMemoryMappedViewHandle.ReleasePointer();
95-
}
96-
}
50+
// https://learn.microsoft.com/windows/win32/debug/pe-format#windows-subsystem
51+
// The subsystem of the prebuilt apphost should be set to CUI
52+
uint peHeaderOffset;
53+
ushort subsystem = GetWindowsSubsystem(accessor, out peHeaderOffset);
54+
if (subsystem != (ushort)Subsystem.WindowsCui)
55+
throw new AppHostNotCUIException(subsystem);
56+
57+
// Set the subsystem to GUI
58+
accessor.Write(peHeaderOffset + PEOffsets.PEHeader.Subsystem, AsLittleEndian((ushort)Subsystem.WindowsGui));
9759
}
9860

9961
public static unsafe void SetWindowsGraphicalUserInterfaceBit(string filePath)
@@ -113,32 +75,7 @@ public static unsafe void SetWindowsGraphicalUserInterfaceBit(string filePath)
11375
/// <param name="accessor">The memory accessor which has the apphost file opened.</param>
11476
internal static unsafe ushort GetWindowsGraphicalUserInterfaceBit(MemoryMappedViewAccessor accessor)
11577
{
116-
byte* pointer = null;
117-
118-
try
119-
{
120-
accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref pointer);
121-
byte* bytes = pointer + accessor.PointerOffset;
122-
123-
// https://en.wikipedia.org/wiki/Portable_Executable
124-
uint peHeaderOffset = ((uint*)(bytes + PEOffsets.DosStub.PESignatureOffset))[0];
125-
126-
if (accessor.Capacity < peHeaderOffset + PEOffsets.PEHeader.Subsystem + sizeof(ushort))
127-
{
128-
throw new AppHostNotPEFileException("Subsystem offset out of file range.");
129-
}
130-
131-
ushort* subsystem = ((ushort*)(bytes + peHeaderOffset + PEOffsets.PEHeader.Subsystem));
132-
133-
return subsystem[0];
134-
}
135-
finally
136-
{
137-
if (pointer != null)
138-
{
139-
accessor.SafeMemoryMappedViewHandle.ReleasePointer();
140-
}
141-
}
78+
return GetWindowsSubsystem(accessor, out _);
14279
}
14380

14481
public static unsafe ushort GetWindowsGraphicalUserInterfaceBit(string filePath)
@@ -151,5 +88,25 @@ public static unsafe ushort GetWindowsGraphicalUserInterfaceBit(string filePath)
15188
}
15289
}
15390
}
91+
92+
private static ushort GetWindowsSubsystem(MemoryMappedViewAccessor accessor, out uint peHeaderOffset)
93+
{
94+
// https://en.wikipedia.org/wiki/Portable_Executable
95+
if (accessor.Capacity < PEOffsets.DosStub.PESignatureOffset + sizeof(uint))
96+
throw new AppHostNotPEFileException("PESignature offset out of file range.");
97+
98+
peHeaderOffset = AsLittleEndian(accessor.ReadUInt32(PEOffsets.DosStub.PESignatureOffset));
99+
if (accessor.Capacity < peHeaderOffset + PEOffsets.PEHeader.Subsystem + sizeof(ushort))
100+
throw new AppHostNotPEFileException("Subsystem offset out of file range.");
101+
102+
// https://learn.microsoft.com/windows/win32/debug/pe-format#windows-subsystem
103+
return AsLittleEndian(accessor.ReadUInt16(peHeaderOffset + PEOffsets.PEHeader.Subsystem));
104+
}
105+
106+
private static ushort AsLittleEndian(ushort value)
107+
=> BitConverter.IsLittleEndian ? value : BinaryPrimitives.ReverseEndianness(value);
108+
109+
private static uint AsLittleEndian(uint value)
110+
=> BitConverter.IsLittleEndian ? value : BinaryPrimitives.ReverseEndianness(value);
154111
}
155112
}

src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.AppHost.Tests/AppHostUpdateTests.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using Microsoft.NET.HostModel.AppHost;
1212
using Microsoft.DotNet.CoreSetup.Test;
1313
using System.Diagnostics;
14+
using System.Reflection.PortableExecutable;
1415

1516
namespace Microsoft.NET.HostModel.Tests
1617
{
@@ -111,7 +112,9 @@ public void ItCanSetWindowsGUISubsystem()
111112
BitConverter
112113
.ToUInt16(File.ReadAllBytes(destinationFilePath), SubsystemOffset)
113114
.Should()
114-
.Be(2);
115+
.Be((ushort)Subsystem.WindowsGui);
116+
117+
Assert.Equal((ushort)Subsystem.WindowsGui, PEUtils.GetWindowsGraphicalUserInterfaceBit(destinationFilePath));
115118
}
116119
}
117120

@@ -153,6 +156,7 @@ public void ItFailsToSetGUISubsystemWithWrongDefault()
153156
string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock");
154157
string appBinaryFilePath = "Test/App/Binary/Path.dll";
155158

159+
Assert.Equal(42, PEUtils.GetWindowsGraphicalUserInterfaceBit(sourceAppHostMock));
156160
Assert.Throws<AppHostNotCUIException>(() =>
157161
HostWriter.CreateAppHost(
158162
sourceAppHostMock,

0 commit comments

Comments
 (0)