81 lines
3.0 KiB
C#
81 lines
3.0 KiB
C#
using System.Buffers.Binary;
|
|
using MxGateway.Server.Configuration;
|
|
|
|
namespace MxGateway.Server.Workers;
|
|
|
|
internal static class WorkerExecutableValidator
|
|
{
|
|
private const ushort ImageFileMachineI386 = 0x014c;
|
|
private const ushort ImageFileMachineAmd64 = 0x8664;
|
|
private const int DosHeaderSignatureOffset = 0;
|
|
private const int PeHeaderOffsetPointer = 0x3c;
|
|
private const int PeSignatureSize = 4;
|
|
private const int MachineOffsetFromPeHeader = PeSignatureSize;
|
|
private const int MinimumHeaderSize = 0x40;
|
|
|
|
public static void Validate(
|
|
string executablePath,
|
|
WorkerArchitecture requiredArchitecture)
|
|
{
|
|
ushort machine = ReadMachineType(executablePath);
|
|
ushort expectedMachine = requiredArchitecture switch
|
|
{
|
|
WorkerArchitecture.X86 => ImageFileMachineI386,
|
|
WorkerArchitecture.X64 => ImageFileMachineAmd64,
|
|
_ => throw new WorkerProcessLaunchException(
|
|
WorkerProcessLaunchErrorCode.InvalidExecutable,
|
|
"Worker executable required architecture is unsupported."),
|
|
};
|
|
|
|
if (machine != expectedMachine)
|
|
{
|
|
throw new WorkerProcessLaunchException(
|
|
WorkerProcessLaunchErrorCode.InvalidExecutable,
|
|
$"Worker executable architecture does not match required {requiredArchitecture} architecture.");
|
|
}
|
|
}
|
|
|
|
private static ushort ReadMachineType(string executablePath)
|
|
{
|
|
byte[] header = new byte[MinimumHeaderSize];
|
|
using FileStream stream = File.OpenRead(executablePath);
|
|
if (stream.Read(header) < header.Length)
|
|
{
|
|
throw InvalidExecutable("Worker executable is too small to contain a valid PE header.");
|
|
}
|
|
|
|
if (header[DosHeaderSignatureOffset] != 'M' || header[DosHeaderSignatureOffset + 1] != 'Z')
|
|
{
|
|
throw InvalidExecutable("Worker executable does not contain an MZ header.");
|
|
}
|
|
|
|
int peHeaderOffset = BinaryPrimitives.ReadInt32LittleEndian(header.AsSpan(PeHeaderOffsetPointer, sizeof(int)));
|
|
if (peHeaderOffset < MinimumHeaderSize)
|
|
{
|
|
throw InvalidExecutable("Worker executable PE header offset is invalid.");
|
|
}
|
|
|
|
byte[] peHeaderBytes = new byte[PeSignatureSize + sizeof(ushort)];
|
|
stream.Position = peHeaderOffset;
|
|
if (stream.Read(peHeaderBytes) < peHeaderBytes.Length)
|
|
{
|
|
throw InvalidExecutable("Worker executable PE header is missing.");
|
|
}
|
|
|
|
if (peHeaderBytes[0] != 'P' || peHeaderBytes[1] != 'E' || peHeaderBytes[2] != 0 || peHeaderBytes[3] != 0)
|
|
{
|
|
throw InvalidExecutable("Worker executable does not contain a PE header.");
|
|
}
|
|
|
|
return BinaryPrimitives.ReadUInt16LittleEndian(
|
|
peHeaderBytes.AsSpan(MachineOffsetFromPeHeader, sizeof(ushort)));
|
|
}
|
|
|
|
private static WorkerProcessLaunchException InvalidExecutable(string message)
|
|
{
|
|
return new WorkerProcessLaunchException(
|
|
WorkerProcessLaunchErrorCode.InvalidExecutable,
|
|
message);
|
|
}
|
|
}
|