mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-22 19:06:15 +08:00
[Loader] Restore PS5 SELF header support (#342)
Accept both PS4 and PS5 SELF signatures without treating version, key type, or flags as layout markers. Add synthetic coverage for valid header variants and malformed structural fields. Signed-off-by: missatjuhvdk1 <177474143+missatjuhvdk1@users.noreply.github.com> Co-authored-by: missatjuhvdk1 <177474143+missatjuhvdk1@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
b9eee497aa
commit
aa25f6e978
@@ -16,7 +16,9 @@ namespace SharpEmu.Core.Loader;
|
||||
|
||||
public sealed class SelfLoader : ISelfLoader
|
||||
{
|
||||
private const uint SelfMagic = 0x4F153D1D;
|
||||
private const uint ElfMagic = 0x7F454C46;
|
||||
private const uint Ps4SelfMagic = 0x4F153D1D;
|
||||
private const uint Ps5SelfMagic = 0x5414F5EE;
|
||||
private const ulong SelfSegmentFlag = 0x800;
|
||||
private const int PageSize = 0x1000;
|
||||
private const ulong ImportStubBaseAddress = 0x0000_7000_0000_0000UL;
|
||||
@@ -355,10 +357,11 @@ public sealed class SelfLoader : ISelfLoader
|
||||
throw new InvalidDataException("Input image is too small to contain an ELF header.");
|
||||
}
|
||||
|
||||
if (imageData.Length >= sizeof(uint) && BinaryPrimitives.ReadUInt32BigEndian(imageData[..sizeof(uint)]) == SelfMagic)
|
||||
var magic = BinaryPrimitives.ReadUInt32BigEndian(imageData[..sizeof(uint)]);
|
||||
if (magic is Ps4SelfMagic or Ps5SelfMagic)
|
||||
{
|
||||
var selfHeader = ReadUnmanaged<SelfHeader>(imageData, 0);
|
||||
if (!selfHeader.HasKnownLayout || selfHeader.Unknown != 0x22)
|
||||
if (!selfHeader.HasKnownLayout)
|
||||
{
|
||||
throw new InvalidDataException("SELF header signature is not recognized.");
|
||||
}
|
||||
@@ -381,13 +384,11 @@ public sealed class SelfLoader : ISelfLoader
|
||||
// acceptable here; anything else — most commonly a still-encrypted
|
||||
// retail eboot — must be reported clearly rather than failing later
|
||||
// with an opaque "not a valid ELF header" message.
|
||||
const uint ElfMagicBigEndian = 0x7F454C46; // "\x7fELF"
|
||||
var leadingWord = BinaryPrimitives.ReadUInt32BigEndian(imageData[..sizeof(uint)]);
|
||||
if (leadingWord != ElfMagicBigEndian)
|
||||
if (magic != ElfMagic)
|
||||
{
|
||||
throw new InvalidDataException(
|
||||
$"Image is neither a decrypted ELF nor a recognized fake-signed SELF " +
|
||||
$"(leading bytes 0x{leadingWord:X8}). This is almost certainly a still-encrypted " +
|
||||
$"(leading bytes 0x{magic:X8}). This is almost certainly a still-encrypted " +
|
||||
$"retail eboot — SharpEmu has no decryption keys and requires a decrypted / " +
|
||||
$"fake-signed (fSELF) image.");
|
||||
}
|
||||
@@ -2789,28 +2790,27 @@ public sealed class SelfLoader : ISelfLoader
|
||||
private readonly ushort _size2;
|
||||
private readonly ulong _fileSize;
|
||||
private readonly ushort _segmentCount;
|
||||
private readonly ushort _unknown;
|
||||
private readonly ushort _flags;
|
||||
private readonly uint _padding;
|
||||
|
||||
public ushort SegmentCount => _segmentCount;
|
||||
|
||||
public ushort Unknown => _unknown;
|
||||
|
||||
public ulong FileSize => _fileSize;
|
||||
|
||||
// Version, key type, and flags are signing metadata and vary across
|
||||
// valid SELF images. They do not change the fixed header layout.
|
||||
public bool HasKnownLayout =>
|
||||
_ident0 == 0x4F &&
|
||||
_ident1 == 0x15 &&
|
||||
_ident2 == 0x3D &&
|
||||
_ident3 == 0x1D &&
|
||||
_ident4 == 0x00 &&
|
||||
((_ident0 == 0x4F &&
|
||||
_ident1 == 0x15 &&
|
||||
_ident2 == 0x3D &&
|
||||
_ident3 == 0x1D) ||
|
||||
(_ident0 == 0x54 &&
|
||||
_ident1 == 0x14 &&
|
||||
_ident2 == 0xF5 &&
|
||||
_ident3 == 0xEE)) &&
|
||||
_ident5 == 0x01 &&
|
||||
_ident6 == 0x01 &&
|
||||
_ident7 == 0x12 &&
|
||||
_ident8 == 0x01 &&
|
||||
_ident9 == 0x01 &&
|
||||
_ident10 == 0x00 &&
|
||||
_ident11 == 0x00;
|
||||
_ident7 == 0x12;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
using System.Buffers.Binary;
|
||||
using SharpEmu.Core.Loader;
|
||||
using SharpEmu.Core.Memory;
|
||||
using Xunit;
|
||||
|
||||
namespace SharpEmu.Libs.Tests.Loader;
|
||||
|
||||
public sealed class SelfLoaderTests
|
||||
{
|
||||
private const uint Ps4SelfMagic = 0x4F153D1D;
|
||||
private const uint Ps5SelfMagic = 0x5414F5EE;
|
||||
private const int SelfHeaderSize = 0x20;
|
||||
private const int ElfHeaderSize = 0x40;
|
||||
|
||||
[Theory]
|
||||
[InlineData(Ps4SelfMagic, (byte)0x00, 0x0000_0101u, (ushort)0x22)]
|
||||
[InlineData(Ps5SelfMagic, (byte)0x00, 0x0000_0101u, (ushort)0x22)]
|
||||
[InlineData(Ps5SelfMagic, (byte)0x10, 0x1000_0101u, (ushort)0x32)]
|
||||
public void Load_AcceptsSupportedSelfHeaderVariants(
|
||||
uint magic,
|
||||
byte version,
|
||||
uint keyType,
|
||||
ushort flags)
|
||||
{
|
||||
var imageData = CreateSelfImage(magic, version, keyType, flags);
|
||||
|
||||
var image = new SelfLoader().Load(imageData, new VirtualMemory());
|
||||
|
||||
Assert.True(image.IsSelf);
|
||||
Assert.Equal(2, image.ElfHeader.AbiVersion);
|
||||
Assert.Empty(image.ProgramHeaders);
|
||||
Assert.Empty(image.MappedRegions);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0x05, (byte)0x00)]
|
||||
[InlineData(0x06, (byte)0x02)]
|
||||
[InlineData(0x07, (byte)0x00)]
|
||||
public void Load_RejectsUnsupportedStructuralSelfHeaderValues(int offset, byte value)
|
||||
{
|
||||
var imageData = CreateSelfImage(Ps5SelfMagic, 0x10, 0x1000_0101, 0x32);
|
||||
imageData[offset] = value;
|
||||
|
||||
Assert.Throws<InvalidDataException>(() =>
|
||||
new SelfLoader().Load(imageData, new VirtualMemory()));
|
||||
}
|
||||
|
||||
private static byte[] CreateSelfImage(uint magic, byte version, uint keyType, ushort flags)
|
||||
{
|
||||
var imageData = new byte[SelfHeaderSize + ElfHeaderSize];
|
||||
var selfHeader = imageData.AsSpan(0, SelfHeaderSize);
|
||||
BinaryPrimitives.WriteUInt32BigEndian(selfHeader, magic);
|
||||
selfHeader[0x04] = version;
|
||||
selfHeader[0x05] = 0x01;
|
||||
selfHeader[0x06] = 0x01;
|
||||
selfHeader[0x07] = 0x12;
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(selfHeader[0x08..], keyType);
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(selfHeader[0x0C..], SelfHeaderSize);
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(selfHeader[0x10..], (ulong)imageData.Length);
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(selfHeader[0x18..], 0);
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(selfHeader[0x1A..], flags);
|
||||
|
||||
WriteMinimalElfHeader(imageData.AsSpan(SelfHeaderSize, ElfHeaderSize));
|
||||
return imageData;
|
||||
}
|
||||
|
||||
private static void WriteMinimalElfHeader(Span<byte> header)
|
||||
{
|
||||
header.Clear();
|
||||
header[0x00] = 0x7F;
|
||||
header[0x01] = (byte)'E';
|
||||
header[0x02] = (byte)'L';
|
||||
header[0x03] = (byte)'F';
|
||||
header[0x04] = 2;
|
||||
header[0x05] = 1;
|
||||
header[0x06] = 1;
|
||||
header[0x07] = 9;
|
||||
header[0x08] = 2;
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(header[0x10..], 3);
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(header[0x12..], 62);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(header[0x14..], 1);
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(header[0x20..], ElfHeaderSize);
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(header[0x34..], ElfHeaderSize);
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(header[0x36..], 0x38);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user