feat(relinker): add relinking to windows pe (wip)
This commit is contained in:
parent
81d7adea95
commit
fce86b13b8
|
|
@ -0,0 +1,61 @@
|
|||
#ifndef CORE_ELFPATCHER_SRC_WINDOWS_WINDOWSPEFORMAT_HPP
|
||||
#define CORE_ELFPATCHER_SRC_WINDOWS_WINDOWSPEFORMAT_HPP
|
||||
|
||||
constexpr std::uint16_t kMzMagic = 0x5A4D;
|
||||
constexpr std::uint32_t kPeSignature = 0x00004550;
|
||||
constexpr std::uint16_t kMachinAmd64 = 0x8664;
|
||||
constexpr std::uint16_t kOptMagicPe32Plus = 0x020B;
|
||||
constexpr std::uint16_t kCharsDll = 0x2000;
|
||||
constexpr std::uint16_t kCharsExe = 0x0002;
|
||||
constexpr std::uint16_t kCharsLargeAddressAware = 0x0020;
|
||||
constexpr std::uint32_t kSectionAlignment = 0x1000;
|
||||
constexpr std::uint32_t kFileAlignment = 0x200;
|
||||
constexpr std::uint32_t kOsVersion = 6;
|
||||
constexpr std::uint32_t kOsVersionMinor = 0;
|
||||
constexpr std::uint32_t kSubsystemConsole = 3;
|
||||
constexpr std::uint32_t kNumberOfRvaAndSizes = 16;
|
||||
constexpr std::uint32_t kSizeOfPeHeaders = 0x400;
|
||||
|
||||
constexpr std::uint32_t kRelTypeDir64 = 10;
|
||||
constexpr std::uint32_t kRelTypeAbsolute = 0;
|
||||
constexpr std::uint32_t kRelocBlockHeaderSize = 8;
|
||||
constexpr std::uint32_t kRelocEntrySize = 2;
|
||||
|
||||
struct MzHeader {
|
||||
std::uint8_t data[0x40];
|
||||
};
|
||||
|
||||
struct PeSection {
|
||||
char name[8];
|
||||
std::uint32_t virtualSize;
|
||||
std::uint32_t virtualAddress;
|
||||
std::uint32_t rawSize;
|
||||
std::uint32_t rawOffset;
|
||||
std::uint32_t characteristics;
|
||||
std::vector<std::uint8_t> data;
|
||||
};
|
||||
|
||||
struct DataDirectory {
|
||||
std::uint32_t rva;
|
||||
std::uint32_t size;
|
||||
};
|
||||
|
||||
struct LoadSegment {
|
||||
std::uint64_t vaddr;
|
||||
std::uint64_t memSize;
|
||||
std::uint64_t fileOffset;
|
||||
std::uint64_t fileSize;
|
||||
std::uint32_t flags;
|
||||
};
|
||||
|
||||
struct ImportThunkEntry {
|
||||
std::string symbolName;
|
||||
std::uint32_t gotRva;
|
||||
};
|
||||
|
||||
struct ImportDllBlock {
|
||||
std::string dllName;
|
||||
std::vector<ImportThunkEntry> thunks;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,14 +1,206 @@
|
|||
#include <elfpatcher/windows/WindowsElfPatcher.hpp>
|
||||
#include <elfpatcher/general/ElfConstants.hpp>
|
||||
#include <domain/Types.hpp>
|
||||
#include <cstring>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
#include "WindowsPeFormat.hpp"
|
||||
#include "io/ByteWriter.hpp"
|
||||
|
||||
namespace Elfpatcher::Windows {
|
||||
|
||||
namespace {
|
||||
|
||||
MzHeader buildMzStub(std::uint32_t peOffset) {
|
||||
MzHeader h{};
|
||||
h.data[0] = 'M';
|
||||
h.data[1] = 'Z';
|
||||
std::memcpy(h.data + 0x3c, &peOffset, 4);
|
||||
return h;
|
||||
}
|
||||
|
||||
void writeSectionHeader(std::vector<std::uint8_t>& buf, std::size_t off, const PeSection& sec) {
|
||||
std::memcpy(buf.data() + off, sec.name, 8);
|
||||
WriteU32(buf, off + 8, sec.virtualSize);
|
||||
WriteU32(buf, off + 12, sec.virtualAddress);
|
||||
WriteU32(buf, off + 16, sec.rawSize);
|
||||
WriteU32(buf, off + 20, sec.rawOffset);
|
||||
WriteU32(buf, off + 24, 0);
|
||||
WriteU32(buf, off + 28, 0);
|
||||
WriteU16(buf, off + 32, 0);
|
||||
WriteU16(buf, off + 34, 0);
|
||||
WriteU32(buf, off + 36, sec.characteristics);
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t> buildRelocSection(
|
||||
const std::vector<std::uint8_t>& relaData,
|
||||
const std::uint64_t imageBase)
|
||||
{
|
||||
constexpr std::size_t kRelaEntSize = 24;
|
||||
if (relaData.size() % kRelaEntSize != 0)
|
||||
throw Domain::RelinkerException("RELA data size is not a multiple of 24");
|
||||
|
||||
std::map<std::uint32_t, std::vector<std::uint32_t>> pageToOffsets;
|
||||
|
||||
for (std::size_t i = 0; i + kRelaEntSize <= relaData.size(); i += kRelaEntSize) {
|
||||
std::uint64_t rOffset, rInfo;
|
||||
std::int64_t rAddend;
|
||||
std::memcpy(&rOffset, relaData.data() + i, 8);
|
||||
std::memcpy(&rInfo, relaData.data() + i + 8, 8);
|
||||
std::memcpy(&rAddend, relaData.data() + i + 16, 8);
|
||||
|
||||
const auto relType = static_cast<std::uint32_t>(rInfo & 0xffffffff);
|
||||
const auto symIdx = static_cast<std::uint32_t>(rInfo >> 32);
|
||||
|
||||
constexpr std::uint32_t kRelativeType = 8;
|
||||
if (relType != kRelativeType)
|
||||
continue;
|
||||
if (symIdx != 0)
|
||||
throw Domain::RelinkerException("R_X86_64_RELATIVE with non-zero symbol index");
|
||||
|
||||
if (rOffset < imageBase)
|
||||
throw Domain::RelinkerException("RELATIVE relocation target vaddr below imageBase", rOffset);
|
||||
|
||||
const std::uint64_t rva = rOffset - imageBase;
|
||||
if (rva > 0xFFFFFFFFull)
|
||||
throw Domain::RelinkerException("RELATIVE relocation RVA exceeds 32-bit range", rOffset);
|
||||
|
||||
const auto rva32 = static_cast<std::uint32_t>(rva);
|
||||
const std::uint32_t page = rva32 & ~0xFFFu;
|
||||
const std::uint32_t pageOff = rva32 & 0xFFFu;
|
||||
pageToOffsets[page].push_back(pageOff);
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t> buf;
|
||||
for (auto& [page, offsets] : pageToOffsets) {
|
||||
std::sort(offsets.begin(), offsets.end());
|
||||
auto entryCount = static_cast<std::uint32_t>(offsets.size());
|
||||
if (entryCount % 2 != 0) {
|
||||
offsets.push_back(0);
|
||||
entryCount++;
|
||||
}
|
||||
const std::uint32_t blockSize = kRelocBlockHeaderSize + entryCount * kRelocEntrySize;
|
||||
appendU32(buf, page);
|
||||
appendU32(buf, blockSize);
|
||||
for (const std::uint32_t off : offsets) {
|
||||
const auto entry = static_cast<std::uint16_t>((kRelTypeDir64 << 12) | (off & 0xFFF));
|
||||
appendU16(buf, entry);
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::uint8_t> buildImportSection(
|
||||
const std::vector<ImportDllBlock>& dlls,
|
||||
const std::uint32_t sectionRva,
|
||||
std::vector<std::pair<std::uint32_t, std::uint32_t>>& outGotPatches)
|
||||
{
|
||||
if (dlls.empty())
|
||||
return {};
|
||||
|
||||
const auto descriptorTableSize = static_cast<std::uint32_t>((dlls.size() + 1) * 20);
|
||||
|
||||
std::vector<std::uint8_t> descriptorBuf;
|
||||
std::vector<std::uint8_t> dataBuf;
|
||||
|
||||
const std::uint32_t dataBase = sectionRva + descriptorTableSize;
|
||||
|
||||
for (const auto& dll : dlls) {
|
||||
const std::uint32_t dllNameRva = dataBase + static_cast<std::uint32_t>(dataBuf.size());
|
||||
appendStr(dataBuf, dll.dllName);
|
||||
alignBuf(dataBuf, 2);
|
||||
|
||||
const std::uint32_t iltRva = dataBase + static_cast<std::uint32_t>(dataBuf.size());
|
||||
for (const auto& thunk : dll.thunks) {
|
||||
const std::uint32_t hintNameRva = dataBase + static_cast<std::uint32_t>(dataBuf.size())
|
||||
+ static_cast<std::uint32_t>((dll.thunks.size() + 1) * 8);
|
||||
appendU64(dataBuf, hintNameRva);
|
||||
}
|
||||
appendU64(dataBuf, 0);
|
||||
|
||||
for (const auto& thunk : dll.thunks) {
|
||||
appendU16(dataBuf, 0);
|
||||
appendStr(dataBuf, thunk.symbolName);
|
||||
alignBuf(dataBuf, 2);
|
||||
}
|
||||
|
||||
const std::uint32_t iatRva = dataBase + static_cast<std::uint32_t>(dataBuf.size());
|
||||
for (std::size_t i = 0; i < dll.thunks.size(); ++i) {
|
||||
const std::uint32_t iatEntryRva = iatRva + static_cast<std::uint32_t>(i * 8);
|
||||
outGotPatches.emplace_back(dll.thunks[i].gotRva, iatEntryRva);
|
||||
appendU64(dataBuf, 0);
|
||||
}
|
||||
appendU64(dataBuf, 0);
|
||||
|
||||
appendU32(descriptorBuf, iltRva);
|
||||
appendU32(descriptorBuf, 0);
|
||||
appendU32(descriptorBuf, 0);
|
||||
appendU32(descriptorBuf, dllNameRva);
|
||||
appendU32(descriptorBuf, iatRva);
|
||||
}
|
||||
|
||||
appendU32(descriptorBuf, 0);
|
||||
appendU32(descriptorBuf, 0);
|
||||
appendU32(descriptorBuf, 0);
|
||||
appendU32(descriptorBuf, 0);
|
||||
appendU32(descriptorBuf, 0);
|
||||
|
||||
std::vector<std::uint8_t> result;
|
||||
result.insert(result.end(), descriptorBuf.begin(), descriptorBuf.end());
|
||||
result.insert(result.end(), dataBuf.begin(), dataBuf.end());
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t> buildEntryStub(std::uint64_t realEntryVaddr, std::uint64_t stubVaddr) {
|
||||
std::vector<std::uint8_t> s;
|
||||
const std::uint64_t callInsnVaddr = stubVaddr + s.size() + 5;
|
||||
const std::int64_t rel = static_cast<std::int64_t>(realEntryVaddr) - static_cast<std::int64_t>(callInsnVaddr);
|
||||
if (rel < std::int64_t(-0x80000000) || rel > std::int64_t(0x7FFFFFFF))
|
||||
throw Domain::RelinkerException("Entry stub: rel32 call target out of range");
|
||||
const auto rel32 = static_cast<std::int32_t>(rel);
|
||||
s.push_back(0xE8);
|
||||
s.push_back(static_cast<std::uint8_t>(rel32 & 0xFF));
|
||||
s.push_back(static_cast<std::uint8_t>((rel32 >> 8) & 0xFF));
|
||||
s.push_back(static_cast<std::uint8_t>((rel32 >> 16) & 0xFF));
|
||||
s.push_back(static_cast<std::uint8_t>((rel32 >> 24) & 0xFF));
|
||||
s.push_back(0xC3);
|
||||
return s;
|
||||
}
|
||||
|
||||
std::uint64_t readElfU64(const std::vector<std::uint8_t>& buf, std::size_t off) {
|
||||
if (off + 8 > buf.size())
|
||||
throw Domain::RelinkerException("ELF read out of bounds", off);
|
||||
std::uint64_t v;
|
||||
std::memcpy(&v, buf.data() + off, 8);
|
||||
return v;
|
||||
}
|
||||
|
||||
std::uint32_t readElfU32(const std::vector<std::uint8_t>& buf, std::size_t off) {
|
||||
if (off + 4 > buf.size())
|
||||
throw Domain::RelinkerException("ELF read out of bounds", off);
|
||||
std::uint32_t v;
|
||||
std::memcpy(&v, buf.data() + off, 4);
|
||||
return v;
|
||||
}
|
||||
|
||||
std::string readCStr(const std::vector<std::uint8_t>& buf, std::size_t off) {
|
||||
std::string s;
|
||||
while (off < buf.size() && buf[off] != 0)
|
||||
s.push_back(static_cast<char>(buf[off++]));
|
||||
return s;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
WindowsPePatcher::WindowsPePatcher(
|
||||
std::shared_ptr<IEntryStubBuilder> entryStubBuilder,
|
||||
std::shared_ptr<IProgramHeaderLayoutBuilder> programHeaderLayoutBuilder,
|
||||
std::shared_ptr<ISectionHeaderTableBuilder> sectionHeaderTableBuilder,
|
||||
std::shared_ptr<Io::IByteWriter> byteWriter
|
||||
)
|
||||
std::shared_ptr<Io::IByteWriter> byteWriter)
|
||||
: _entryStubBuilder(std::move(entryStubBuilder))
|
||||
, _programHeaderLayoutBuilder(std::move(programHeaderLayoutBuilder))
|
||||
, _sectionHeaderTableBuilder(std::move(sectionHeaderTableBuilder))
|
||||
|
|
@ -23,7 +215,347 @@ std::vector<std::uint8_t> WindowsPePatcher::Patch(
|
|||
std::uint64_t originalPltGotVaddr,
|
||||
const std::string& runPath)
|
||||
{
|
||||
throw Domain::RelinkerException("Not implemented");
|
||||
if (sourceElf.size() < 64)
|
||||
throw Domain::RelinkerException("Source ELF too small");
|
||||
if (sourceElf[0] != 0x7f || sourceElf[1] != 'E' || sourceElf[2] != 'L' || sourceElf[3] != 'F')
|
||||
throw Domain::RelinkerException("Source is not an ELF file");
|
||||
|
||||
const std::uint64_t elfEntryVaddr = readElfU64(sourceElf, 0x18);
|
||||
|
||||
std::vector<LoadSegment> loadSegs;
|
||||
auto imageBase = std::uint64_t(-1);
|
||||
std::uint64_t imageTop = 0;
|
||||
|
||||
for (const auto& ph : originalHeaders) {
|
||||
if (ph.Type != PT_LOAD) continue;
|
||||
if (ph.MemorySize == 0) continue;
|
||||
LoadSegment seg{};
|
||||
seg.vaddr = ph.MappedAddress;
|
||||
seg.memSize = ph.MemorySize;
|
||||
seg.fileOffset = ph.Offset;
|
||||
seg.fileSize = ph.FileSize;
|
||||
seg.flags = ph.Flags;
|
||||
loadSegs.push_back(seg);
|
||||
if (seg.vaddr < imageBase) imageBase = seg.vaddr;
|
||||
const std::uint64_t top = alignUp64(seg.vaddr + seg.memSize, kSectionAlignment);
|
||||
if (top > imageTop) imageTop = top;
|
||||
}
|
||||
|
||||
if (loadSegs.empty())
|
||||
throw Domain::RelinkerException("No PT_LOAD segments found");
|
||||
if (imageBase == std::uint64_t(-1))
|
||||
throw Domain::RelinkerException("Could not determine image base");
|
||||
if (imageBase % kSectionAlignment != 0)
|
||||
throw Domain::RelinkerException("Image base is not section-aligned");
|
||||
|
||||
std::uint32_t gotRva = 0;
|
||||
bool gotFound = false;
|
||||
if (originalPltGotVaddr != 0) {
|
||||
if (originalPltGotVaddr < imageBase)
|
||||
throw Domain::RelinkerException("GOT vaddr below image base");
|
||||
const std::uint64_t gotRva64 = originalPltGotVaddr - imageBase;
|
||||
if (gotRva64 > 0xFFFFFFFFull)
|
||||
throw Domain::RelinkerException("GOT RVA exceeds 32-bit range");
|
||||
gotRva = static_cast<std::uint32_t>(gotRva64);
|
||||
gotFound = true;
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t> imageBuf(static_cast<std::size_t>(imageTop - imageBase), 0);
|
||||
|
||||
for (const auto& seg : loadSegs) {
|
||||
const std::uint64_t destOff = seg.vaddr - imageBase;
|
||||
if (destOff + seg.fileSize > imageBuf.size())
|
||||
throw Domain::RelinkerException("PT_LOAD segment exceeds image buffer", seg.vaddr);
|
||||
if (seg.fileOffset + seg.fileSize > sourceElf.size())
|
||||
throw Domain::RelinkerException("PT_LOAD segment file data out of bounds", seg.fileOffset);
|
||||
std::memcpy(imageBuf.data() + destOff, sourceElf.data() + seg.fileOffset, static_cast<std::size_t>(seg.fileSize));
|
||||
}
|
||||
|
||||
const auto loadImageRva = static_cast<std::uint32_t>(kSizeOfPeHeaders);
|
||||
if (imageBase < loadImageRva)
|
||||
throw Domain::RelinkerException("Image base too small to accommodate PE headers");
|
||||
|
||||
const std::uint64_t adjustedBase = imageBase - loadImageRva;
|
||||
if (adjustedBase % kSectionAlignment != 0)
|
||||
throw Domain::RelinkerException("Adjusted image base is not section-aligned");
|
||||
|
||||
std::vector<ImportDllBlock> importDlls;
|
||||
std::vector<std::pair<std::uint32_t, std::uint32_t>> gotPatches;
|
||||
|
||||
{
|
||||
const std::vector<std::uint8_t>& relaPlt = dynamicSection.RelaPltData;
|
||||
const std::vector<std::uint8_t>& dynStr = dynamicSection.DynStrData;
|
||||
const std::vector<std::uint8_t>& dynSym = dynamicSection.DynSymData;
|
||||
|
||||
constexpr std::size_t kRelaEnt = 24;
|
||||
constexpr std::size_t kSymEnt = 24;
|
||||
|
||||
if (relaPlt.size() % kRelaEnt != 0)
|
||||
throw Domain::RelinkerException("RelaPlt size not multiple of 24");
|
||||
|
||||
std::map<std::string, ImportDllBlock*> dllMap;
|
||||
|
||||
for (std::size_t i = 0; i + kRelaEnt <= relaPlt.size(); i += kRelaEnt) {
|
||||
std::uint64_t rOffset, rInfo;
|
||||
std::memcpy(&rOffset, relaPlt.data() + i, 8);
|
||||
std::memcpy(&rInfo, relaPlt.data() + i + 8, 8);
|
||||
|
||||
const auto symIdx = static_cast<std::uint32_t>(rInfo >> 32);
|
||||
const auto relType = static_cast<std::uint32_t>(rInfo & 0xffffffff);
|
||||
|
||||
constexpr std::uint32_t kJumpSlot = 7;
|
||||
if (relType != kJumpSlot)
|
||||
throw Domain::RelinkerException("Unexpected relocation type in RelaPlt");
|
||||
if (symIdx == 0)
|
||||
throw Domain::RelinkerException("JUMP_SLOT relocation with zero symbol index");
|
||||
|
||||
const std::size_t symOff = static_cast<std::size_t>(symIdx) * kSymEnt;
|
||||
if (symOff + 4 > dynSym.size())
|
||||
throw Domain::RelinkerException("Symbol index out of dynSym bounds");
|
||||
|
||||
std::uint32_t nameOff;
|
||||
std::memcpy(&nameOff, dynSym.data() + symOff, 4);
|
||||
if (nameOff >= dynStr.size())
|
||||
throw Domain::RelinkerException("Symbol name offset out of dynStr bounds");
|
||||
|
||||
const std::string symName = readCStr(dynStr, nameOff);
|
||||
if (symName.empty())
|
||||
throw Domain::RelinkerException("Empty symbol name in JUMP_SLOT relocation");
|
||||
|
||||
if (rOffset < imageBase)
|
||||
throw Domain::RelinkerException("JUMP_SLOT target vaddr below image base", rOffset);
|
||||
const std::uint64_t rva64 = rOffset - imageBase;
|
||||
if (rva64 > 0xFFFFFFFFull)
|
||||
throw Domain::RelinkerException("JUMP_SLOT target RVA exceeds 32-bit range");
|
||||
const auto targetGotRva = static_cast<std::uint32_t>(rva64);
|
||||
|
||||
const std::string dllName = symName + ".dll";
|
||||
if (dllMap.find(dllName) == dllMap.end()) {
|
||||
importDlls.push_back({dllName, {}});
|
||||
dllMap[dllName] = &importDlls.back();
|
||||
}
|
||||
dllMap[dllName]->thunks.push_back({symName, targetGotRva});
|
||||
}
|
||||
|
||||
const std::vector<std::uint8_t>& relaData = dynamicSection.RelaData;
|
||||
if (relaData.size() % kRelaEnt != 0)
|
||||
throw Domain::RelinkerException("RelaData size not multiple of 24");
|
||||
|
||||
constexpr std::uint32_t kGlobDat = 6;
|
||||
constexpr std::uint32_t kAbs64 = 1;
|
||||
|
||||
for (std::size_t i = 0; i + kRelaEnt <= relaData.size(); i += kRelaEnt) {
|
||||
std::uint64_t rOffset, rInfo;
|
||||
std::memcpy(&rOffset, relaData.data() + i, 8);
|
||||
std::memcpy(&rInfo, relaData.data() + i + 8, 8);
|
||||
|
||||
const auto symIdx = static_cast<std::uint32_t>(rInfo >> 32);
|
||||
const auto relType = static_cast<std::uint32_t>(rInfo & 0xffffffff);
|
||||
|
||||
constexpr std::uint32_t kRelativeType = 8;
|
||||
if (relType == kRelativeType) continue;
|
||||
|
||||
if (relType != kGlobDat && relType != kAbs64)
|
||||
throw Domain::RelinkerException("Unexpected relocation type in RelaData");
|
||||
if (symIdx == 0)
|
||||
throw Domain::RelinkerException("GLOB_DAT/ABS64 relocation with zero symbol index");
|
||||
|
||||
const std::size_t symOff = static_cast<std::size_t>(symIdx) * kSymEnt;
|
||||
if (symOff + 4 > dynSym.size())
|
||||
throw Domain::RelinkerException("Symbol index out of dynSym bounds");
|
||||
|
||||
std::uint32_t nameOff;
|
||||
std::memcpy(&nameOff, dynSym.data() + symOff, 4);
|
||||
if (nameOff >= dynStr.size())
|
||||
throw Domain::RelinkerException("Symbol name offset out of dynStr bounds");
|
||||
|
||||
const std::string symName = readCStr(dynStr, nameOff);
|
||||
if (symName.empty())
|
||||
throw Domain::RelinkerException("Empty symbol name in GLOB_DAT/ABS64 relocation");
|
||||
|
||||
if (rOffset < imageBase)
|
||||
throw Domain::RelinkerException("GLOB_DAT target vaddr below image base", rOffset);
|
||||
const std::uint64_t rva64 = rOffset - imageBase;
|
||||
if (rva64 > 0xFFFFFFFFull)
|
||||
throw Domain::RelinkerException("GLOB_DAT target RVA exceeds 32-bit range");
|
||||
const auto targetGotRva = static_cast<std::uint32_t>(rva64);
|
||||
|
||||
const std::string dllName = symName + ".dll";
|
||||
if (dllMap.find(dllName) == dllMap.end()) {
|
||||
importDlls.push_back({dllName, {}});
|
||||
dllMap[dllName] = &importDlls.back();
|
||||
}
|
||||
dllMap[dllName]->thunks.push_back({symName, targetGotRva});
|
||||
}
|
||||
}
|
||||
|
||||
const auto loadImageSize = static_cast<std::uint32_t>(imageBuf.size());
|
||||
const std::uint32_t importSectionRva = alignUp(loadImageRva + loadImageSize, kSectionAlignment);
|
||||
|
||||
std::vector<std::uint8_t> importSectionData = buildImportSection(importDlls, importSectionRva, gotPatches);
|
||||
alignBuf(importSectionData, kFileAlignment);
|
||||
|
||||
for (const auto& [gotRva_, iatRva] : gotPatches) {
|
||||
const auto destInImage = static_cast<std::uint64_t>(gotRva_);
|
||||
if (destInImage + 8 > imageBuf.size())
|
||||
throw Domain::RelinkerException("GOT patch target outside image buffer");
|
||||
const std::uint64_t iatVa = adjustedBase + iatRva;
|
||||
std::memcpy(imageBuf.data() + destInImage, &iatVa, 8);
|
||||
}
|
||||
|
||||
const std::uint32_t relocSectionRva = alignUp(importSectionRva + static_cast<std::uint32_t>(importSectionData.size()), kSectionAlignment);
|
||||
std::vector<std::uint8_t> relocSectionData = buildRelocSection(dynamicSection.RelaData, imageBase);
|
||||
alignBuf(relocSectionData, kFileAlignment);
|
||||
|
||||
const std::uint32_t stubSectionRva = alignUp(relocSectionRva + static_cast<std::uint32_t>(relocSectionData.size()), kSectionAlignment);
|
||||
const std::uint64_t stubVaddr = adjustedBase + stubSectionRva;
|
||||
std::vector<std::uint8_t> stubData = buildEntryStub(elfEntryVaddr, stubVaddr);
|
||||
alignBuf(stubData, kFileAlignment);
|
||||
|
||||
const std::uint32_t entryPointRva = stubSectionRva;
|
||||
|
||||
std::uint16_t numSections = 1;
|
||||
if (!importSectionData.empty()) numSections++;
|
||||
if (!relocSectionData.empty()) numSections++;
|
||||
numSections++;
|
||||
|
||||
const std::uint32_t sizeOfHeaders = alignUp(
|
||||
0x40 + 4 + 20 + 240 + static_cast<std::uint32_t>(numSections) * 40,
|
||||
kFileAlignment);
|
||||
|
||||
if (sizeOfHeaders > kSizeOfPeHeaders)
|
||||
throw Domain::RelinkerException("PE headers exceed reserved header block size");
|
||||
|
||||
const std::uint32_t sizeOfImage = alignUp(stubSectionRva + static_cast<std::uint32_t>(stubData.size()), kSectionAlignment);
|
||||
|
||||
const std::uint32_t mzPeOffset = 0x40;
|
||||
std::vector<std::uint8_t> result(kSizeOfPeHeaders, 0);
|
||||
|
||||
WriteU16(result, 0, kMzMagic);
|
||||
WriteU32(result, 0x3c, mzPeOffset);
|
||||
|
||||
std::size_t off = mzPeOffset;
|
||||
WriteU32(result, off, kPeSignature); off += 4;
|
||||
|
||||
WriteU16(result, off, kMachinAmd64); off += 2;
|
||||
WriteU16(result, off, numSections); off += 2;
|
||||
WriteU32(result, off, 0); off += 4;
|
||||
WriteU32(result, off, 0); off += 4;
|
||||
WriteU32(result, off, 0); off += 4;
|
||||
WriteU16(result, off, 240); off += 2;
|
||||
WriteU16(result, off, kCharsExe | kCharsLargeAddressAware); off += 2;
|
||||
|
||||
WriteU16(result, off, kOptMagicPe32Plus); off += 2;
|
||||
writeU8(result, off, 0); off += 1;
|
||||
writeU8(result, off, 0); off += 1;
|
||||
WriteU32(result, off, 0); off += 4;
|
||||
WriteU32(result, off, 0); off += 4;
|
||||
WriteU32(result, off, 0); off += 4;
|
||||
WriteU32(result, off, entryPointRva); off += 4;
|
||||
WriteU32(result, off, loadImageRva); off += 4;
|
||||
|
||||
writeU64(result, off, adjustedBase); off += 8;
|
||||
WriteU32(result, off, kSectionAlignment); off += 4;
|
||||
WriteU32(result, off, kFileAlignment); off += 4;
|
||||
WriteU16(result, off, static_cast<std::uint16_t>(kOsVersion)); off += 2;
|
||||
WriteU16(result, off, static_cast<std::uint16_t>(kOsVersionMinor)); off += 2;
|
||||
WriteU16(result, off, 0); off += 2;
|
||||
WriteU16(result, off, 0); off += 2;
|
||||
WriteU16(result, off, static_cast<std::uint16_t>(kOsVersion)); off += 2;
|
||||
WriteU16(result, off, static_cast<std::uint16_t>(kOsVersionMinor)); off += 2;
|
||||
WriteU32(result, off, 0); off += 4;
|
||||
WriteU32(result, off, sizeOfImage); off += 4;
|
||||
WriteU32(result, off, kSizeOfPeHeaders); off += 4;
|
||||
WriteU32(result, off, 0); off += 4;
|
||||
WriteU16(result, off, static_cast<std::uint16_t>(kSubsystemConsole)); off += 2;
|
||||
WriteU16(result, off, 0); off += 2;
|
||||
writeU64(result, off, 0); off += 8;
|
||||
writeU64(result, off, 0); off += 8;
|
||||
writeU64(result, off, 0); off += 8;
|
||||
writeU64(result, off, 0); off += 8;
|
||||
WriteU32(result, off, 0); off += 4;
|
||||
WriteU32(result, off, kNumberOfRvaAndSizes); off += 4;
|
||||
|
||||
for (std::uint32_t i = 0; i < kNumberOfRvaAndSizes; ++i) {
|
||||
WriteU32(result, off, 0); off += 4;
|
||||
WriteU32(result, off, 0); off += 4;
|
||||
}
|
||||
|
||||
if (!importSectionData.empty()) {
|
||||
WriteU32(result, mzPeOffset + 4 + 20 + 104, importSectionRva);
|
||||
WriteU32(result, mzPeOffset + 4 + 20 + 108, static_cast<std::uint32_t>(importDlls.size() * 20));
|
||||
}
|
||||
if (!relocSectionData.empty()) {
|
||||
WriteU32(result, mzPeOffset + 4 + 20 + 168, relocSectionRva);
|
||||
WriteU32(result, mzPeOffset + 4 + 20 + 172, static_cast<std::uint32_t>(relocSectionData.size()));
|
||||
}
|
||||
|
||||
std::size_t shdrsOff = mzPeOffset + 4 + 20 + 240;
|
||||
|
||||
auto writeSec = [&](const char* name, std::uint32_t virtualSize, std::uint32_t rva, std::uint32_t rawSize, std::uint32_t rawOff, std::uint32_t chars) {
|
||||
char n[8]{};
|
||||
std::memcpy(n, name, std::min(std::strlen(name), std::size_t(8)));
|
||||
std::memcpy(result.data() + shdrsOff, n, 8);
|
||||
WriteU32(result, shdrsOff + 8, virtualSize);
|
||||
WriteU32(result, shdrsOff + 12, rva);
|
||||
WriteU32(result, shdrsOff + 16, rawSize);
|
||||
WriteU32(result, shdrsOff + 20, rawOff);
|
||||
WriteU32(result, shdrsOff + 24, 0);
|
||||
WriteU32(result, shdrsOff + 28, 0);
|
||||
WriteU16(result, shdrsOff + 32, 0);
|
||||
WriteU16(result, shdrsOff + 34, 0);
|
||||
WriteU32(result, shdrsOff + 36, chars);
|
||||
shdrsOff += 40;
|
||||
};
|
||||
|
||||
constexpr std::uint32_t kSecExec = 0x60000020;
|
||||
constexpr std::uint32_t kSecRW = 0xC0000040;
|
||||
constexpr std::uint32_t kSecRO = 0x40000040;
|
||||
constexpr std::uint32_t kSecDiscard = 0x02000000;
|
||||
|
||||
const std::uint32_t loadRawSize = alignUp(loadImageSize, kFileAlignment);
|
||||
std::uint32_t curRawOff = kSizeOfPeHeaders;
|
||||
|
||||
writeSec(".load", loadImageSize, loadImageRva, loadRawSize, curRawOff, kSecExec | kSecRW);
|
||||
curRawOff += loadRawSize;
|
||||
|
||||
std::uint32_t importRawOff = 0;
|
||||
if (!importSectionData.empty()) {
|
||||
importRawOff = curRawOff;
|
||||
writeSec(".idata", static_cast<std::uint32_t>(importSectionData.size()), importSectionRva,
|
||||
static_cast<std::uint32_t>(importSectionData.size()), curRawOff, kSecRW);
|
||||
curRawOff += static_cast<std::uint32_t>(importSectionData.size());
|
||||
}
|
||||
|
||||
std::uint32_t relocRawOff = 0;
|
||||
if (!relocSectionData.empty()) {
|
||||
relocRawOff = curRawOff;
|
||||
writeSec(".reloc", static_cast<std::uint32_t>(relocSectionData.size()), relocSectionRva,
|
||||
static_cast<std::uint32_t>(relocSectionData.size()), curRawOff, kSecDiscard | kSecRO);
|
||||
curRawOff += static_cast<std::uint32_t>(relocSectionData.size());
|
||||
}
|
||||
|
||||
const std::uint32_t stubRawOff = curRawOff;
|
||||
writeSec(".entry", static_cast<std::uint32_t>(stubData.size()), stubSectionRva,
|
||||
static_cast<std::uint32_t>(stubData.size()), stubRawOff, kSecExec);
|
||||
|
||||
alignBuf(result, kFileAlignment);
|
||||
|
||||
{
|
||||
std::vector<std::uint8_t> loadRaw(imageBuf.begin(), imageBuf.end());
|
||||
loadRaw.resize(loadRawSize, 0);
|
||||
result.insert(result.end(), loadRaw.begin(), loadRaw.end());
|
||||
}
|
||||
|
||||
if (!importSectionData.empty())
|
||||
result.insert(result.end(), importSectionData.begin(), importSectionData.end());
|
||||
|
||||
if (!relocSectionData.empty())
|
||||
result.insert(result.end(), relocSectionData.begin(), relocSectionData.end());
|
||||
|
||||
result.insert(result.end(), stubData.begin(), stubData.end());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,12 +21,15 @@
|
|||
#include <memory>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <elfpatcher/windows/WindowsElfPatcher.hpp>
|
||||
|
||||
int main(const int argc, char* argv[]) {
|
||||
bool skipSyscallCheck = false;
|
||||
bool toIntel = false;
|
||||
bool skipUnusedNidFilter = false;
|
||||
bool writeRegistry = false;
|
||||
bool toWindows = false;
|
||||
|
||||
std::string inputPath;
|
||||
std::string outputPath;
|
||||
std::string runPath = "$ORIGIN/libs";
|
||||
|
|
@ -47,6 +50,8 @@ int main(const int argc, char* argv[]) {
|
|||
return 1;
|
||||
}
|
||||
runPath = argv[++i];
|
||||
} else if (arg == "--windows") {
|
||||
toWindows = true;
|
||||
} else if (inputPath.empty()) {
|
||||
inputPath = arg;
|
||||
} else if (outputPath.empty()) {
|
||||
|
|
@ -58,7 +63,7 @@ int main(const int argc, char* argv[]) {
|
|||
}
|
||||
|
||||
if (inputPath.empty() || outputPath.empty()) {
|
||||
std::cerr << "Usage: relinker [--skip-syscall-check] [--to-intel] [--skip-unused-nid-filter] [--registry] [--rpath <path>] <input.elf> <output.elf>\n"
|
||||
std::cerr << "Usage: relinker [--windows] [--skip-syscall-check] [--to-intel] [--skip-unused-nid-filter] [--registry] [--rpath <path>] <input.elf> <output.elf>\n"
|
||||
"Example: relinker input.elf output.elf\n";
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -104,17 +109,31 @@ int main(const int argc, char* argv[]) {
|
|||
}
|
||||
|
||||
auto byteWriter = std::make_shared<Io::ByteWriter>();
|
||||
Elfpatcher::Linux::LinuxElfPatcher elfPatcher(
|
||||
std::make_shared<Elfpatcher::EntryStubBuilder>(),
|
||||
std::make_shared<Elfpatcher::ProgramHeaderLayoutBuilder>(
|
||||
std::make_shared<Elfpatcher::SegmentFilter>(),
|
||||
|
||||
std::shared_ptr<Elfpatcher::IElfPatcher> patcher;
|
||||
if (toWindows) {
|
||||
patcher = std::make_shared<Elfpatcher::Windows::WindowsPePatcher>(
|
||||
std::make_shared<Elfpatcher::EntryStubBuilder>(),
|
||||
std::make_shared<Elfpatcher::ProgramHeaderLayoutBuilder>(
|
||||
std::make_shared<Elfpatcher::SegmentFilter>(),
|
||||
byteWriter
|
||||
),
|
||||
std::make_shared<Elfpatcher::SectionHeaderTableBuilder>(byteWriter),
|
||||
byteWriter
|
||||
),
|
||||
std::make_shared<Elfpatcher::SectionHeaderTableBuilder>(byteWriter),
|
||||
byteWriter
|
||||
);
|
||||
auto patchedElf = elfPatcher.Patch(sourceBytes, result.OriginalHeaders, result.DynamicSection, result.OriginalPltGotVaddr, runPath);
|
||||
fileWriter.Write(outputPath, patchedElf);
|
||||
);
|
||||
} else {
|
||||
patcher = std::make_shared<Elfpatcher::Linux::LinuxElfPatcher>(
|
||||
std::make_shared<Elfpatcher::EntryStubBuilder>(),
|
||||
std::make_shared<Elfpatcher::ProgramHeaderLayoutBuilder>(
|
||||
std::make_shared<Elfpatcher::SegmentFilter>(),
|
||||
byteWriter
|
||||
),
|
||||
std::make_shared<Elfpatcher::SectionHeaderTableBuilder>(byteWriter),
|
||||
byteWriter
|
||||
);
|
||||
}
|
||||
auto patched = patcher->Patch(sourceBytes, result.OriginalHeaders, result.DynamicSection, result.OriginalPltGotVaddr, runPath);
|
||||
fileWriter.Write(outputPath, patched);
|
||||
|
||||
} catch (const Domain::RelinkerException& e) {
|
||||
std::cerr << "FAIL: " << e.what();
|
||||
|
|
|
|||
Loading…
Reference in New Issue