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

[release/8.0-rc2] Make HostModel PEUtils always read/write little endian #92441

Merged
merged 2 commits into from
Sep 22, 2023
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
121 changes: 39 additions & 82 deletions src/installer/managed/Microsoft.NET.HostModel/AppHost/PEUtils.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Buffers.Binary;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Reflection.PortableExecutable;

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

try
{
accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref pointer);
byte* bytes = pointer + accessor.PointerOffset;

// https://en.wikipedia.org/wiki/Portable_Executable
// Validate that we're looking at Windows PE file
if (((ushort*)bytes)[0] != PEOffsets.DosImageSignature
|| accessor.Capacity < PEOffsets.DosStub.PESignatureOffset + sizeof(uint))
{
return false;
}
return true;
}
finally
{
if (pointer != null)
{
accessor.SafeMemoryMappedViewHandle.ReleasePointer();
}
}
// https://en.wikipedia.org/wiki/Portable_Executable
// Validate that we're looking at Windows PE file
ushort signature = AsLittleEndian(accessor.ReadUInt16(0));
return signature == PEOffsets.DosImageSignature;
}

public static bool IsPEImage(string filePath)
Expand All @@ -60,40 +47,15 @@ public static bool IsPEImage(string filePath)
/// <param name="accessor">The memory accessor which has the apphost file opened.</param>
internal static unsafe void SetWindowsGraphicalUserInterfaceBit(MemoryMappedViewAccessor accessor)
{
byte* pointer = null;

try
{
accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref pointer);
byte* bytes = pointer + accessor.PointerOffset;

// https://en.wikipedia.org/wiki/Portable_Executable
uint peHeaderOffset = ((uint*)(bytes + PEOffsets.DosStub.PESignatureOffset))[0];

if (accessor.Capacity < peHeaderOffset + PEOffsets.PEHeader.Subsystem + sizeof(ushort))
{
throw new AppHostNotPEFileException("Subsystem offset out of file range.");
}

ushort* subsystem = ((ushort*)(bytes + peHeaderOffset + PEOffsets.PEHeader.Subsystem));

// https://docs.microsoft.com/en-us/windows/desktop/Debug/pe-format#windows-subsystem
// The subsystem of the prebuilt apphost should be set to CUI
if (subsystem[0] != (ushort)PEOffsets.Subsystem.WindowsCui)
{
throw new AppHostNotCUIException(subsystem[0]);
}

// Set the subsystem to GUI
subsystem[0] = (ushort)PEOffsets.Subsystem.WindowsGui;
}
finally
{
if (pointer != null)
{
accessor.SafeMemoryMappedViewHandle.ReleasePointer();
}
}
// https://learn.microsoft.com/windows/win32/debug/pe-format#windows-subsystem
// The subsystem of the prebuilt apphost should be set to CUI
uint peHeaderOffset;
ushort subsystem = GetWindowsSubsystem(accessor, out peHeaderOffset);
if (subsystem != (ushort)Subsystem.WindowsCui)
throw new AppHostNotCUIException(subsystem);

// Set the subsystem to GUI
accessor.Write(peHeaderOffset + PEOffsets.PEHeader.Subsystem, AsLittleEndian((ushort)Subsystem.WindowsGui));
}

public static unsafe void SetWindowsGraphicalUserInterfaceBit(string filePath)
Expand All @@ -113,32 +75,7 @@ public static unsafe void SetWindowsGraphicalUserInterfaceBit(string filePath)
/// <param name="accessor">The memory accessor which has the apphost file opened.</param>
internal static unsafe ushort GetWindowsGraphicalUserInterfaceBit(MemoryMappedViewAccessor accessor)
{
byte* pointer = null;

try
{
accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref pointer);
byte* bytes = pointer + accessor.PointerOffset;

// https://en.wikipedia.org/wiki/Portable_Executable
uint peHeaderOffset = ((uint*)(bytes + PEOffsets.DosStub.PESignatureOffset))[0];

if (accessor.Capacity < peHeaderOffset + PEOffsets.PEHeader.Subsystem + sizeof(ushort))
{
throw new AppHostNotPEFileException("Subsystem offset out of file range.");
}

ushort* subsystem = ((ushort*)(bytes + peHeaderOffset + PEOffsets.PEHeader.Subsystem));

return subsystem[0];
}
finally
{
if (pointer != null)
{
accessor.SafeMemoryMappedViewHandle.ReleasePointer();
}
}
return GetWindowsSubsystem(accessor, out _);
}

public static unsafe ushort GetWindowsGraphicalUserInterfaceBit(string filePath)
Expand All @@ -151,5 +88,25 @@ public static unsafe ushort GetWindowsGraphicalUserInterfaceBit(string filePath)
}
}
}

private static ushort GetWindowsSubsystem(MemoryMappedViewAccessor accessor, out uint peHeaderOffset)
{
// https://en.wikipedia.org/wiki/Portable_Executable
if (accessor.Capacity < PEOffsets.DosStub.PESignatureOffset + sizeof(uint))
throw new AppHostNotPEFileException("PESignature offset out of file range.");

peHeaderOffset = AsLittleEndian(accessor.ReadUInt32(PEOffsets.DosStub.PESignatureOffset));
if (accessor.Capacity < peHeaderOffset + PEOffsets.PEHeader.Subsystem + sizeof(ushort))
throw new AppHostNotPEFileException("Subsystem offset out of file range.");

// https://learn.microsoft.com/windows/win32/debug/pe-format#windows-subsystem
return AsLittleEndian(accessor.ReadUInt16(peHeaderOffset + PEOffsets.PEHeader.Subsystem));
}

private static ushort AsLittleEndian(ushort value)
=> BitConverter.IsLittleEndian ? value : BinaryPrimitives.ReverseEndianness(value);

private static uint AsLittleEndian(uint value)
=> BitConverter.IsLittleEndian ? value : BinaryPrimitives.ReverseEndianness(value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Microsoft.NET.HostModel.AppHost;
using Microsoft.DotNet.CoreSetup.Test;
using System.Diagnostics;
using System.Reflection.PortableExecutable;

namespace Microsoft.NET.HostModel.Tests
{
Expand Down Expand Up @@ -111,7 +112,9 @@ public void ItCanSetWindowsGUISubsystem()
BitConverter
.ToUInt16(File.ReadAllBytes(destinationFilePath), SubsystemOffset)
.Should()
.Be(2);
.Be((ushort)Subsystem.WindowsGui);

Assert.Equal((ushort)Subsystem.WindowsGui, PEUtils.GetWindowsGraphicalUserInterfaceBit(destinationFilePath));
}
}

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

Assert.Equal(42, PEUtils.GetWindowsGraphicalUserInterfaceBit(sourceAppHostMock));
Assert.Throws<AppHostNotCUIException>(() =>
HostWriter.CreateAppHost(
sourceAppHostMock,
Expand Down