AnyPS5/core/relinker/elfpatcher/src/windows/WindowsPeWriter.cpp

84 lines
4.6 KiB
C++

#include <elfpatcher/windows/WindowsPeWriter.hpp>
#include <domain/Types.hpp>
#include <io/BufferUtils.hpp>
#include <algorithm>
namespace Elfpatcher::Windows {
std::vector<std::uint8_t> WindowsPeWriter::Write(const std::vector<PeSection>& sections, const std::uint32_t entryRva, const std::array<PeDirectory, 16>& directories, const bool windowsGui) const {
constexpr std::size_t peOffset = 0x80;
constexpr std::size_t optionalOffset = peOffset + 24;
constexpr std::size_t sectionTable = optionalOffset + 240;
if (sections.empty() || sections.size() > 96 || sectionTable + sections.size() * 40 > LoadRva)
throw Domain::RelinkerException("Invalid PE section count");
std::vector<std::uint8_t> result(LoadRva);
Io::WriteU16(result, 0, 0x5a4d);
Io::WriteU32(result, 0x3c, peOffset);
Io::WriteU32(result, peOffset, 0x4550);
Io::WriteU16(result, peOffset + 4, 0x8664);
Io::WriteU16(result, peOffset + 6, static_cast<std::uint16_t>(sections.size()));
Io::WriteU16(result, peOffset + 20, 240);
Io::WriteU16(result, peOffset + 22, directories[5].Size == 0 ? 0x23 : 0x22);
Io::WriteU16(result, optionalOffset, 0x20b);
Io::WriteU32(result, optionalOffset + 16, entryRva);
Io::WriteU64(result, optionalOffset + 24, ImageBase);
Io::WriteU32(result, optionalOffset + 32, SectionAlignment);
Io::WriteU32(result, optionalOffset + 36, FileAlignment);
Io::WriteU16(result, optionalOffset + 40, 6);
Io::WriteU16(result, optionalOffset + 42, 2);
Io::WriteU16(result, optionalOffset + 48, 6);
Io::WriteU16(result, optionalOffset + 50, 2);
Io::WriteU32(result, optionalOffset + 60, LoadRva);
Io::WriteU16(result, optionalOffset + 68, windowsGui ? 2 : 3);
Io::WriteU16(result, optionalOffset + 70, directories[5].Size == 0 ? 0x100 : 0x140);
Io::WriteU64(result, optionalOffset + 72, 0x210000);
Io::WriteU64(result, optionalOffset + 80, 0x200000);
Io::WriteU64(result, optionalOffset + 88, 0x100000);
Io::WriteU64(result, optionalOffset + 96, 0x1000);
Io::WriteU32(result, optionalOffset + 108, static_cast<std::uint32_t>(directories.size()));
std::uint32_t endRva = LoadRva;
std::uint32_t codeSize = 0;
std::uint32_t dataSize = 0;
bool foundEntry = false;
for (std::size_t index = 0; index < sections.size(); ++index) {
const auto& section = sections[index];
if (section.Name.empty() || section.Name.size() > 8 || section.Data.empty() || section.Rva != endRva)
throw Domain::RelinkerException("Invalid PE section layout: " + section.Name, section.Rva);
const auto rawSize = Io::AlignUp(CheckedRva(section.Data.size()), FileAlignment);
const auto rawOffset = CheckedRva(result.size());
const auto header = sectionTable + index * 40;
std::copy(section.Name.begin(), section.Name.end(), result.begin() + static_cast<std::ptrdiff_t>(header));
Io::WriteU32(result, header + 8, CheckedRva(section.Data.size()));
Io::WriteU32(result, header + 12, section.Rva);
Io::WriteU32(result, header + 16, rawSize);
Io::WriteU32(result, header + 20, rawOffset);
Io::WriteU32(result, header + 36, section.Characteristics);
if ((section.Characteristics & SectionExecute) != 0) {
if (codeSize == 0)
Io::WriteU32(result, optionalOffset + 20, section.Rva);
codeSize = CheckedRva(static_cast<std::uint64_t>(codeSize) + rawSize);
foundEntry |= entryRva >= section.Rva && entryRva - section.Rva < section.Data.size();
} else {
dataSize = CheckedRva(static_cast<std::uint64_t>(dataSize) + rawSize);
}
result.insert(result.end(), section.Data.begin(), section.Data.end());
result.resize(static_cast<std::size_t>(rawOffset) + rawSize);
endRva = AlignRva(section.Rva + static_cast<std::uint64_t>(section.Data.size()));
}
if (!foundEntry)
throw Domain::RelinkerException("PE entry point is not inside executable section data", entryRva);
Io::WriteU32(result, optionalOffset + 4, codeSize);
Io::WriteU32(result, optionalOffset + 8, dataSize);
Io::WriteU32(result, optionalOffset + 56, endRva);
for (std::size_t index = 0; index < directories.size(); ++index) {
const auto& directory = directories[index];
if ((directory.Rva == 0) != (directory.Size == 0) || directory.Rva > endRva || directory.Size > endRva - directory.Rva)
throw Domain::RelinkerException("Invalid PE data directory", index);
Io::WriteU32(result, optionalOffset + 112 + index * 8, directory.Rva);
Io::WriteU32(result, optionalOffset + 116 + index * 8, directory.Size);
}
return result;
}
}