feat(patcher): gen pt interp + executable entry stub

This commit is contained in:
boykopovar 2026-08-03 23:04:11 +03:00
parent 5718f6931a
commit b05f877e15
2 changed files with 73 additions and 5 deletions

View File

@ -17,6 +17,7 @@ public:
private:
static constexpr std::uint32_t PT_LOAD = 1;
static constexpr std::uint32_t PT_DYNAMIC = 2;
static constexpr std::uint32_t PT_INTERP = 3;
static constexpr std::uint32_t PT_GNU_RELRO = 0x6474e552;
static constexpr std::uint32_t PF_X = 0x1;
static constexpr std::uint32_t PF_W = 0x2;
@ -42,6 +43,8 @@ private:
[[nodiscard]] bool _isSceSpecificSegment(std::uint32_t type) const;
[[nodiscard]] bool _isNullPageLoad(const ProgramHeader& ph) const;
[[nodiscard]] ProgramHeader _makeLoadHeader(std::uint64_t offset, std::uint64_t size) const;
[[nodiscard]] std::vector<std::uint8_t> _buildEntryStub(std::uint64_t stubVaddr, std::uint64_t realEntryVaddr) const;
void _writeInterp(std::vector<std::uint8_t>& buf, std::size_t phEntOff, std::uint64_t interpOff, std::uint64_t interpSize) const;
void _writeU16(std::vector<std::uint8_t>& buf, std::size_t offset, std::uint16_t v) const;
void _writeU32(std::vector<std::uint8_t>& buf, std::size_t offset, std::uint32_t v) const;
void _writeU64(std::vector<std::uint8_t>& buf, std::size_t offset, std::uint64_t v) const;

View File

@ -15,11 +15,11 @@ void ElfPatcher::_writeU16(std::vector<std::uint8_t>& buf, std::size_t offset, s
std::memcpy(buf.data() + offset, &v, 2);
}
void ElfPatcher::_writeU32(std::vector<std::uint8_t>& buf, std::size_t offset, std::uint32_t v) const {
void ElfPatcher::_writeU32(std::vector<std::uint8_t>& buf, const std::size_t offset, std::uint32_t v) const {
std::memcpy(buf.data() + offset, &v, 4);
}
void ElfPatcher::_writeU64(std::vector<std::uint8_t>& buf, std::size_t offset, std::uint64_t v) const {
void ElfPatcher::_writeU64(std::vector<std::uint8_t>& buf, const std::size_t offset, std::uint64_t v) const {
std::memcpy(buf.data() + offset, &v, 8);
}
@ -56,7 +56,7 @@ bool ElfPatcher::_isNullPageLoad(const ProgramHeader& ph) const {
ProgramHeader ElfPatcher::_makeLoadHeader(std::uint64_t offset, std::uint64_t size) const {
ProgramHeader ph{};
ph.Type = PT_LOAD;
ph.Flags = PF_R;
ph.Flags = PF_R | PF_X;
ph.Offset = offset;
ph.MappedAddress = offset;
ph.PhysicalAddress = offset;
@ -66,6 +66,40 @@ ProgramHeader ElfPatcher::_makeLoadHeader(std::uint64_t offset, std::uint64_t si
return ph;
}
void ElfPatcher::_writeInterp(std::vector<std::uint8_t>& buf, std::size_t phEntOff, std::uint64_t interpOff, std::uint64_t interpSize) const {
ProgramHeader ph{};
ph.Type = PT_INTERP;
ph.Flags = PF_R;
ph.Offset = interpOff;
ph.MappedAddress = interpOff;
ph.PhysicalAddress = interpOff;
ph.FileSize = interpSize;
ph.MemorySize = interpSize;
ph.Alignment = 1;
_writeProgramHeader(buf, phEntOff, ph);
}
std::vector<std::uint8_t> ElfPatcher::_buildEntryStub(std::uint64_t stubVaddr, std::uint64_t realEntryVaddr) const {
std::vector<std::uint8_t> s;
s.push_back(0x58);
s.push_back(0x48); s.push_back(0x89); s.push_back(0xe3);
s.push_back(0x48); s.push_back(0x83); s.push_back(0xec); s.push_back(0x30);
s.push_back(0x48); s.push_back(0x83); s.push_back(0xe4); s.push_back(0xf0);
s.push_back(0x89); s.push_back(0x04); s.push_back(0x24);
s.push_back(0x48); s.push_back(0x89); s.push_back(0x5c); s.push_back(0x24); s.push_back(0x08);
s.push_back(0x48); s.push_back(0x89); s.push_back(0xe7);
s.push_back(0x48); s.push_back(0x31); s.push_back(0xf6);
const std::uint64_t jmpInsnVaddr = stubVaddr + s.size();
const std::uint64_t jmpNextVaddr = jmpInsnVaddr + 5;
const std::int32_t rel32 = static_cast<std::int32_t>(realEntryVaddr - jmpNextVaddr);
s.push_back(0xe9);
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));
return s;
}
std::uint32_t ElfPatcher::_fixLoadFlags(std::uint32_t originalFlags) const {
std::uint32_t flags = originalFlags;
if ((flags & (PF_R | PF_W | PF_X)) == 0)
@ -140,17 +174,42 @@ void ElfPatcher::PatchAndWrite(
for (std::uint8_t b : dynSegBuf)
buf.push_back(b);
const std::uint64_t realEntryVaddr = *reinterpret_cast<const std::uint64_t*>(buf.data() + 24);
const std::uint64_t stubOff = static_cast<std::uint64_t>(buf.size());
const auto stubBytes = _buildEntryStub(stubOff, realEntryVaddr);
for (std::uint8_t b : stubBytes)
buf.push_back(b);
_writeU64(buf, 24, stubOff);
static constexpr char kInterp[] = "/lib64/ld-linux-x86-64.so.2";
const std::uint64_t interpOff = static_cast<std::uint64_t>(buf.size());
constexpr std::uint64_t interpSize = sizeof(kInterp);
for (std::size_t i = 0; i < interpSize; i++)
buf.push_back(static_cast<std::uint8_t>(kInterp[i]));
const std::uint64_t extraBlockOff = dynStrOff;
const std::uint64_t extraBlockSize = static_cast<std::uint64_t>(buf.size()) - extraBlockOff;
std::uint16_t keptCount = 0;
for (const auto& ph : originalHeaders) {
if (_isSceSpecificSegment(ph.Type))
continue;
if (ph.Type == PT_DYNAMIC)
continue;
keptCount++;
}
const std::uint16_t neededPh = keptCount + 3;
if (neededPh > phNum)
throw RelinkerException(
"Not enough program header slots: need " + std::to_string(neededPh) +
", available " + std::to_string(phNum));
std::uint16_t writtenPh = 0;
for (const auto& ph : originalHeaders) {
if (_isSceSpecificSegment(ph.Type))
continue;
if (ph.Type == PT_DYNAMIC)
continue;
if (writtenPh >= phNum)
break;
const std::size_t phEntOff = static_cast<std::size_t>(phOff) + writtenPh * phEntSize;
if (ph.Type == PT_LOAD) {
ProgramHeader fixed = ph;
@ -183,6 +242,12 @@ void ElfPatcher::PatchAndWrite(
writtenPh++;
}
{
const std::size_t phEntOff = static_cast<std::size_t>(phOff) + writtenPh * phEntSize;
_writeInterp(buf, phEntOff, interpOff, interpSize);
writtenPh++;
}
_writeU16(buf, 56, writtenPh);
std::ofstream out(outputPath, std::ios::binary);