feat(patcher): gen pt interp + executable entry stub
This commit is contained in:
parent
5718f6931a
commit
b05f877e15
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue