feat(libs): add rpx library build as libs target
This commit is contained in:
parent
dd2481951d
commit
9e21a054e0
|
|
@ -30,3 +30,5 @@ target_include_directories(relinker PRIVATE
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/codegen/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/relinker/include
|
||||
)
|
||||
|
||||
add_subdirectory(libs)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
add_executable(nid_patcher EXCLUDE_FROM_ALL
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/nid/src/Sha1.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/nid/src/NidCompute.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/nid/src/main.cpp
|
||||
)
|
||||
target_include_directories(nid_patcher PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/nid/include)
|
||||
target_compile_features(nid_patcher PRIVATE cxx_std_20)
|
||||
|
||||
file(GLOB LIBS_DIRS LIST_DIRECTORIES true ${CMAKE_CURRENT_SOURCE_DIR}/lib*)
|
||||
|
||||
set(LIB_TARGETS)
|
||||
|
||||
foreach(LIB_DIR ${LIBS_DIRS})
|
||||
if(IS_DIRECTORY ${LIB_DIR})
|
||||
get_filename_component(LIB_NAME ${LIB_DIR} NAME)
|
||||
|
||||
add_library(${LIB_NAME} SHARED EXCLUDE_FROM_ALL ${LIB_DIR}/Export.cpp)
|
||||
set_target_properties(${LIB_NAME} PROPERTIES
|
||||
PREFIX ""
|
||||
SUFFIX .rpx
|
||||
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/libs
|
||||
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/libs
|
||||
)
|
||||
|
||||
add_dependencies(${LIB_NAME} nid_patcher)
|
||||
|
||||
add_custom_command(TARGET ${LIB_NAME} POST_BUILD
|
||||
COMMAND $<TARGET_FILE:nid_patcher> ${LIB_NAME} $<TARGET_FILE:${LIB_NAME}>
|
||||
COMMENT "Patching NIDs"
|
||||
)
|
||||
|
||||
list(APPEND LIB_TARGETS ${LIB_NAME})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
add_custom_target(libs DEPENDS nid_patcher ${LIB_TARGETS})
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
#include <cstdint>
|
||||
|
||||
extern "C" {
|
||||
|
||||
using SceAudioOutPortId = std::int32_t;
|
||||
|
||||
std::int32_t sceAudioOutInit(void) { return 0; }
|
||||
|
||||
SceAudioOutPortId sceAudioOutOpen(std::int32_t userId, std::int32_t type, std::int32_t index, std::uint32_t len, std::uint32_t freq, std::uint32_t param) {
|
||||
(void)userId; (void)type; (void)index; (void)len; (void)freq; (void)param;
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::int32_t sceAudioOutOutput(SceAudioOutPortId handle, const void* ptr) {
|
||||
(void)handle; (void)ptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::int32_t sceAudioOutSetVolume(SceAudioOutPortId handle, std::int32_t flag, std::int32_t* vol) {
|
||||
(void)handle; (void)flag; (void)vol;
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::int32_t sceAudioOutClose(SceAudioOutPortId handle) {
|
||||
(void)handle;
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef NID_NIDCOMPUTE_HPP
|
||||
#define NID_NIDCOMPUTE_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Nid {
|
||||
|
||||
std::string ComputeNid(const std::string& symbolName, const std::string& libraryName);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
#ifndef NID_SHA1_HPP
|
||||
#define NID_SHA1_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
namespace Nid {
|
||||
|
||||
std::array<std::uint8_t, 20> Sha1(const std::vector<std::uint8_t>& data);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
#include <nid/NidCompute.hpp>
|
||||
#include <nid/Sha1.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace Nid {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char kBase64Chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-";
|
||||
|
||||
std::string _base64Encode(const std::uint8_t* data, std::size_t len) {
|
||||
std::string out;
|
||||
out.reserve(((len + 2u) / 3u) * 4u);
|
||||
for (std::size_t i = 0; i < len; i += 3u) {
|
||||
std::uint32_t group = static_cast<std::uint32_t>(data[i]) << 16u;
|
||||
if (i + 1u < len) group |= static_cast<std::uint32_t>(data[i+1u]) << 8u;
|
||||
if (i + 2u < len) group |= static_cast<std::uint32_t>(data[i+2u]);
|
||||
|
||||
out.push_back(kBase64Chars[(group >> 18u) & 0x3fu]);
|
||||
out.push_back(kBase64Chars[(group >> 12u) & 0x3fu]);
|
||||
out.push_back((i + 1u < len) ? kBase64Chars[(group >> 6u) & 0x3fu] : '=');
|
||||
out.push_back((i + 2u < len) ? kBase64Chars[(group) & 0x3fu] : '=');
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::string ComputeNid(const std::string& symbolName, const std::string& libraryName) {
|
||||
if (symbolName.empty()) throw std::invalid_argument("symbolName is empty");
|
||||
if (libraryName.empty()) throw std::invalid_argument("libraryName is empty");
|
||||
|
||||
std::vector<std::uint8_t> input;
|
||||
input.reserve(symbolName.size() + 1u + libraryName.size() + 1u + libraryName.size() + 1u);
|
||||
|
||||
for (char c : symbolName) input.push_back(static_cast<std::uint8_t>(c));
|
||||
input.push_back(0x00u);
|
||||
for (char c : libraryName) input.push_back(static_cast<std::uint8_t>(c));
|
||||
input.push_back(0x00u);
|
||||
for (char c : libraryName) input.push_back(static_cast<std::uint8_t>(c));
|
||||
input.push_back(0x00u);
|
||||
|
||||
const auto digest = Sha1(input);
|
||||
const std::string b64 = _base64Encode(digest.data(), digest.size());
|
||||
|
||||
if (b64.size() < 11u) throw std::runtime_error("base64 output too short");
|
||||
return b64.substr(0u, 11u) + '#';
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
#include <nid/Sha1.hpp>
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
namespace Nid {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr std::uint32_t _rotl32(std::uint32_t x, unsigned n) {
|
||||
return (x << n) | (x >> (32u - n));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::array<std::uint8_t, 20> Sha1(const std::vector<std::uint8_t>& data) {
|
||||
std::uint32_t h0 = 0x67452301u;
|
||||
std::uint32_t h1 = 0xEFCDAB89u;
|
||||
std::uint32_t h2 = 0x98BADCFEu;
|
||||
std::uint32_t h3 = 0x10325476u;
|
||||
std::uint32_t h4 = 0xC3D2E1F0u;
|
||||
|
||||
std::vector<std::uint8_t> msg(data);
|
||||
|
||||
const std::uint64_t bitLen = static_cast<std::uint64_t>(data.size()) * 8u;
|
||||
|
||||
msg.push_back(0x80u);
|
||||
while ((msg.size() % 64u) != 56u)
|
||||
msg.push_back(0x00u);
|
||||
|
||||
for (int i = 7; i >= 0; --i)
|
||||
msg.push_back(static_cast<std::uint8_t>((bitLen >> (i * 8u)) & 0xffu));
|
||||
|
||||
for (std::size_t offset = 0; offset < msg.size(); offset += 64u) {
|
||||
std::uint32_t w[80];
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
w[i] = (static_cast<std::uint32_t>(msg[offset + i * 4u + 0u]) << 24u)
|
||||
| (static_cast<std::uint32_t>(msg[offset + i * 4u + 1u]) << 16u)
|
||||
| (static_cast<std::uint32_t>(msg[offset + i * 4u + 2u]) << 8u)
|
||||
| (static_cast<std::uint32_t>(msg[offset + i * 4u + 3u]));
|
||||
}
|
||||
for (int i = 16; i < 80; ++i)
|
||||
w[i] = _rotl32(w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16], 1u);
|
||||
|
||||
std::uint32_t a = h0, b = h1, c = h2, d = h3, e = h4;
|
||||
|
||||
for (int i = 0; i < 80; ++i) {
|
||||
std::uint32_t f, k;
|
||||
if (i < 20) {
|
||||
f = (b & c) | ((~b) & d);
|
||||
k = 0x5A827999u;
|
||||
} else if (i < 40) {
|
||||
f = b ^ c ^ d;
|
||||
k = 0x6ED9EBA1u;
|
||||
} else if (i < 60) {
|
||||
f = (b & c) | (b & d) | (c & d);
|
||||
k = 0x8F1BBCDCu;
|
||||
} else {
|
||||
f = b ^ c ^ d;
|
||||
k = 0xCA62C1D6u;
|
||||
}
|
||||
std::uint32_t temp = _rotl32(a, 5u) + f + e + k + w[i];
|
||||
e = d; d = c; c = _rotl32(b, 30u); b = a; a = temp;
|
||||
}
|
||||
|
||||
h0 += a; h1 += b; h2 += c; h3 += d; h4 += e;
|
||||
}
|
||||
|
||||
std::array<std::uint8_t, 20> digest;
|
||||
auto _put = [&](int base, std::uint32_t v) {
|
||||
digest[base+0] = static_cast<std::uint8_t>(v >> 24u);
|
||||
digest[base+1] = static_cast<std::uint8_t>(v >> 16u);
|
||||
digest[base+2] = static_cast<std::uint8_t>(v >> 8u);
|
||||
digest[base+3] = static_cast<std::uint8_t>(v);
|
||||
};
|
||||
_put(0, h0); _put(4, h1); _put(8, h2); _put(12, h3); _put(16, h4);
|
||||
return digest;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,306 @@
|
|||
#include <nid/NidCompute.hpp>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <limits>
|
||||
|
||||
namespace {
|
||||
|
||||
struct Elf64_Ehdr {
|
||||
std::uint8_t e_ident[16];
|
||||
std::uint16_t e_type;
|
||||
std::uint16_t e_machine;
|
||||
std::uint32_t e_version;
|
||||
std::uint64_t e_entry;
|
||||
std::uint64_t e_phoff;
|
||||
std::uint64_t e_shoff;
|
||||
std::uint32_t e_flags;
|
||||
std::uint16_t e_ehsize;
|
||||
std::uint16_t e_phentsize;
|
||||
std::uint16_t e_phnum;
|
||||
std::uint16_t e_shentsize;
|
||||
std::uint16_t e_shnum;
|
||||
std::uint16_t e_shstrndx;
|
||||
};
|
||||
|
||||
struct Elf64_Shdr {
|
||||
std::uint32_t sh_name;
|
||||
std::uint32_t sh_type;
|
||||
std::uint64_t sh_flags;
|
||||
std::uint64_t sh_addr;
|
||||
std::uint64_t sh_offset;
|
||||
std::uint64_t sh_size;
|
||||
std::uint32_t sh_link;
|
||||
std::uint32_t sh_info;
|
||||
std::uint64_t sh_addralign;
|
||||
std::uint64_t sh_entsize;
|
||||
};
|
||||
|
||||
struct Elf64_Sym {
|
||||
std::uint32_t st_name;
|
||||
std::uint8_t st_info;
|
||||
std::uint8_t st_other;
|
||||
std::uint16_t st_shndx;
|
||||
std::uint64_t st_value;
|
||||
std::uint64_t st_size;
|
||||
};
|
||||
|
||||
constexpr std::uint32_t SHT_DYNSYM = 11u;
|
||||
constexpr std::uint8_t STB_LOCAL = 0u;
|
||||
|
||||
struct PeDataDirectory {
|
||||
std::uint32_t VirtualAddress;
|
||||
std::uint32_t Size;
|
||||
};
|
||||
|
||||
struct PeExportDirectory {
|
||||
std::uint32_t Characteristics;
|
||||
std::uint32_t TimeDateStamp;
|
||||
std::uint16_t MajorVersion;
|
||||
std::uint16_t MinorVersion;
|
||||
std::uint32_t Name;
|
||||
std::uint32_t Base;
|
||||
std::uint32_t NumberOfFunctions;
|
||||
std::uint32_t NumberOfNames;
|
||||
std::uint32_t AddressOfFunctions;
|
||||
std::uint32_t AddressOfNames;
|
||||
std::uint32_t AddressOfNameOrdinals;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
T _read(const std::vector<std::uint8_t>& buf, std::size_t offset) {
|
||||
if (offset + sizeof(T) > buf.size()) throw std::runtime_error("read out of bounds");
|
||||
T v;
|
||||
std::memcpy(&v, buf.data() + offset, sizeof(T));
|
||||
return v;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void _write(std::vector<std::uint8_t>& buf, std::size_t offset, const T& v) {
|
||||
if (offset + sizeof(T) > buf.size()) throw std::runtime_error("write out of bounds");
|
||||
std::memcpy(buf.data() + offset, &v, sizeof(T));
|
||||
}
|
||||
|
||||
std::string _readCStr(const std::vector<std::uint8_t>& buf, std::size_t offset) {
|
||||
if (offset >= buf.size()) throw std::runtime_error("cstr offset out of bounds");
|
||||
std::string s;
|
||||
while (offset < buf.size() && buf[offset] != 0u)
|
||||
s.push_back(static_cast<char>(buf[offset++]));
|
||||
return s;
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t> _readFile(const std::string& path) {
|
||||
std::ifstream f(path, std::ios::binary);
|
||||
if (!f) throw std::runtime_error("cannot open: " + path);
|
||||
return {std::istreambuf_iterator<char>(f), {}};
|
||||
}
|
||||
|
||||
void _writeFile(const std::string& path, const std::vector<std::uint8_t>& data) {
|
||||
std::ofstream f(path, std::ios::binary);
|
||||
if (!f) throw std::runtime_error("cannot write: " + path);
|
||||
f.write(reinterpret_cast<const char*>(data.data()), static_cast<std::streamsize>(data.size()));
|
||||
}
|
||||
|
||||
enum class BinaryFormat { Elf64, Pe };
|
||||
|
||||
BinaryFormat _detectFormat(const std::vector<std::uint8_t>& buf) {
|
||||
if (buf.size() >= 4 && buf[0] == 0x7f && buf[1] == 'E' && buf[2] == 'L' && buf[3] == 'F') return BinaryFormat::Elf64;
|
||||
if (buf.size() >= 2 && buf[0] == 'M' && buf[1] == 'Z') return BinaryFormat::Pe;
|
||||
throw std::runtime_error("unrecognized binary format");
|
||||
}
|
||||
|
||||
void _patchNidsElf(std::vector<std::uint8_t>& elf, const std::string& libraryName) {
|
||||
if (elf.size() < sizeof(Elf64_Ehdr)) throw std::runtime_error("file too small");
|
||||
if (elf[4] != 2) throw std::runtime_error("not ELF64");
|
||||
|
||||
const auto ehdr = _read<Elf64_Ehdr>(elf, 0u);
|
||||
|
||||
std::size_t dynSymOffset = 0u, dynSymSize = 0u;
|
||||
std::size_t dynStrOffset = 0u, dynStrSize = 0u;
|
||||
|
||||
for (std::uint16_t i = 0u; i < ehdr.e_shnum; ++i) {
|
||||
const std::size_t shOffset = static_cast<std::size_t>(ehdr.e_shoff) + i * sizeof(Elf64_Shdr);
|
||||
const auto shdr = _read<Elf64_Shdr>(elf, shOffset);
|
||||
if (shdr.sh_type == SHT_DYNSYM) {
|
||||
dynSymOffset = static_cast<std::size_t>(shdr.sh_offset);
|
||||
dynSymSize = static_cast<std::size_t>(shdr.sh_size);
|
||||
}
|
||||
}
|
||||
|
||||
if (dynSymOffset == 0u) throw std::runtime_error("no .dynsym section");
|
||||
|
||||
const std::size_t symCount = dynSymSize / sizeof(Elf64_Sym);
|
||||
if (symCount == 0u) throw std::runtime_error(".dynsym is empty");
|
||||
|
||||
const std::size_t dynStrSectionLink = [&]() -> std::size_t {
|
||||
for (std::uint16_t i = 0u; i < ehdr.e_shnum; ++i) {
|
||||
const std::size_t shOffset = static_cast<std::size_t>(ehdr.e_shoff) + i * sizeof(Elf64_Shdr);
|
||||
const auto shdr = _read<Elf64_Shdr>(elf, shOffset);
|
||||
if (shdr.sh_type == SHT_DYNSYM) return static_cast<std::size_t>(shdr.sh_link);
|
||||
}
|
||||
throw std::runtime_error("cannot find dynsym sh_link");
|
||||
}();
|
||||
|
||||
{
|
||||
const std::size_t shOffset = static_cast<std::size_t>(ehdr.e_shoff) + dynStrSectionLink * sizeof(Elf64_Shdr);
|
||||
const auto shdr = _read<Elf64_Shdr>(elf, shOffset);
|
||||
dynStrOffset = static_cast<std::size_t>(shdr.sh_offset);
|
||||
dynStrSize = static_cast<std::size_t>(shdr.sh_size);
|
||||
}
|
||||
|
||||
if (dynStrOffset == 0u) throw std::runtime_error("no .dynstr section");
|
||||
|
||||
std::vector<std::uint8_t> newDynStr(elf.begin() + static_cast<std::ptrdiff_t>(dynStrOffset),
|
||||
elf.begin() + static_cast<std::ptrdiff_t>(dynStrOffset + dynStrSize));
|
||||
|
||||
std::vector<std::pair<std::uint32_t, std::uint32_t>> nameRemap;
|
||||
|
||||
for (std::size_t i = 1u; i < symCount; ++i) {
|
||||
const std::size_t symOffset = dynSymOffset + i * sizeof(Elf64_Sym);
|
||||
auto sym = _read<Elf64_Sym>(elf, symOffset);
|
||||
|
||||
const std::uint8_t binding = sym.st_info >> 4u;
|
||||
if (binding == STB_LOCAL) continue;
|
||||
if (sym.st_name == 0u) continue;
|
||||
|
||||
const std::string symName = _readCStr(newDynStr, sym.st_name);
|
||||
if (symName.empty()) continue;
|
||||
|
||||
const std::string nid = Nid::ComputeNid(symName, libraryName);
|
||||
|
||||
const auto existingIt = [&]() {
|
||||
for (const auto& [orig, newIdx] : nameRemap)
|
||||
if (orig == sym.st_name) return newIdx;
|
||||
return std::numeric_limits<std::uint32_t>::max();
|
||||
}();
|
||||
|
||||
if (existingIt != std::numeric_limits<std::uint32_t>::max()) {
|
||||
sym.st_name = existingIt;
|
||||
_write(elf, symOffset, sym);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto newNameOffset = static_cast<std::uint32_t>(newDynStr.size());
|
||||
for (char c : nid) newDynStr.push_back(static_cast<std::uint8_t>(c));
|
||||
newDynStr.push_back(0u);
|
||||
|
||||
nameRemap.emplace_back(sym.st_name, newNameOffset);
|
||||
sym.st_name = newNameOffset;
|
||||
_write(elf, symOffset, sym);
|
||||
}
|
||||
|
||||
if (newDynStr.size() > dynStrSize) throw std::runtime_error("new .dynstr exceeds original size - rebuild with padding");
|
||||
|
||||
std::memcpy(elf.data() + dynStrOffset, newDynStr.data(), newDynStr.size());
|
||||
for (std::size_t i = newDynStr.size(); i < dynStrSize; ++i)
|
||||
elf[dynStrOffset + i] = 0u;
|
||||
}
|
||||
|
||||
std::size_t _peRvaToOffset(const std::vector<std::uint8_t>& pe, std::uint32_t rva, std::uint32_t peHeaderOffset, std::uint16_t numberOfSections, std::uint32_t sizeOfOptionalHeader) {
|
||||
const std::size_t sectionTableOffset = static_cast<std::size_t>(peHeaderOffset) + 4u + 20u + sizeOfOptionalHeader;
|
||||
for (std::uint16_t i = 0u; i < numberOfSections; ++i) {
|
||||
const std::size_t sectionOffset = sectionTableOffset + i * 40u;
|
||||
if (sectionOffset + 40u > pe.size()) throw std::runtime_error("section header out of bounds");
|
||||
const auto virtualSize = _read<std::uint32_t>(pe, sectionOffset + 8u);
|
||||
const auto virtualAddress = _read<std::uint32_t>(pe, sectionOffset + 12u);
|
||||
const auto sizeOfRawData = _read<std::uint32_t>(pe, sectionOffset + 16u);
|
||||
const auto pointerToRawData = _read<std::uint32_t>(pe, sectionOffset + 20u);
|
||||
const std::uint32_t effectiveSize = virtualSize != 0u ? virtualSize : sizeOfRawData;
|
||||
if (rva >= virtualAddress && rva < virtualAddress + effectiveSize)
|
||||
return static_cast<std::size_t>(pointerToRawData) + (rva - virtualAddress);
|
||||
}
|
||||
throw std::runtime_error("rva not mapped to any section");
|
||||
}
|
||||
|
||||
void _patchNidsPe(std::vector<std::uint8_t>& pe, const std::string& libraryName) {
|
||||
if (pe.size() < 0x40) throw std::runtime_error("file too small");
|
||||
|
||||
const auto peHeaderOffset = _read<std::uint32_t>(pe, 0x3cu);
|
||||
if (static_cast<std::size_t>(peHeaderOffset) + 4u > pe.size()) throw std::runtime_error("invalid pe header offset");
|
||||
if (pe[peHeaderOffset] != 'P' || pe[peHeaderOffset + 1u] != 'E' || pe[peHeaderOffset + 2u] != 0u || pe[peHeaderOffset + 3u] != 0u)
|
||||
throw std::runtime_error("not a PE file");
|
||||
|
||||
const std::size_t coffHeaderOffset = static_cast<std::size_t>(peHeaderOffset) + 4u;
|
||||
const auto numberOfSections = _read<std::uint16_t>(pe, coffHeaderOffset + 2u);
|
||||
const auto sizeOfOptionalHeader = _read<std::uint16_t>(pe, coffHeaderOffset + 16u);
|
||||
if (sizeOfOptionalHeader == 0u) throw std::runtime_error("no optional header");
|
||||
|
||||
const std::size_t optionalHeaderOffset = coffHeaderOffset + 20u;
|
||||
const auto magic = _read<std::uint16_t>(pe, optionalHeaderOffset);
|
||||
|
||||
std::size_t dataDirectoryOffset;
|
||||
if (magic == 0x20bu) {
|
||||
dataDirectoryOffset = optionalHeaderOffset + 112u;
|
||||
} else if (magic == 0x10bu) {
|
||||
dataDirectoryOffset = optionalHeaderOffset + 96u;
|
||||
} else {
|
||||
throw std::runtime_error("unsupported optional header magic");
|
||||
}
|
||||
|
||||
const auto exportDir = _read<PeDataDirectory>(pe, dataDirectoryOffset);
|
||||
if (exportDir.VirtualAddress == 0u) throw std::runtime_error("no export directory");
|
||||
|
||||
const std::size_t exportDirOffset = _peRvaToOffset(pe, exportDir.VirtualAddress, peHeaderOffset, numberOfSections, sizeOfOptionalHeader);
|
||||
const auto exportTable = _read<PeExportDirectory>(pe, exportDirOffset);
|
||||
|
||||
if (exportTable.NumberOfNames == 0u) throw std::runtime_error("no exported names");
|
||||
|
||||
const std::size_t namesArrayOffset = _peRvaToOffset(pe, exportTable.AddressOfNames, peHeaderOffset, numberOfSections, sizeOfOptionalHeader);
|
||||
|
||||
std::vector<std::pair<std::size_t, std::string>> nameOffsets;
|
||||
for (std::uint32_t i = 0u; i < exportTable.NumberOfNames; ++i) {
|
||||
const auto nameRva = _read<std::uint32_t>(pe, namesArrayOffset + i * 4u);
|
||||
const std::size_t nameOffset = _peRvaToOffset(pe, nameRva, peHeaderOffset, numberOfSections, sizeOfOptionalHeader);
|
||||
const std::string name = _readCStr(pe, nameOffset);
|
||||
if (name.empty()) throw std::runtime_error("empty exported name");
|
||||
nameOffsets.emplace_back(nameOffset, name);
|
||||
}
|
||||
|
||||
for (const auto& [nameOffset, name] : nameOffsets) {
|
||||
const std::string nid = Nid::ComputeNid(name, libraryName);
|
||||
if (nid.size() > name.size()) throw std::runtime_error("nid longer than original export name - cannot patch in place");
|
||||
|
||||
std::memcpy(pe.data() + nameOffset, nid.data(), nid.size());
|
||||
pe[nameOffset + nid.size()] = 0u;
|
||||
for (std::size_t i = nid.size() + 1u; i < name.size() + 1u; ++i)
|
||||
pe[nameOffset + i] = 0u;
|
||||
}
|
||||
}
|
||||
|
||||
void _patchNids(std::vector<std::uint8_t>& binary, const std::string& libraryName) {
|
||||
switch (_detectFormat(binary)) {
|
||||
case BinaryFormat::Elf64: _patchNidsElf(binary, libraryName); return;
|
||||
case BinaryFormat::Pe: _patchNidsPe(binary, libraryName); return;
|
||||
}
|
||||
throw std::runtime_error("unreachable");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc < 3) {
|
||||
std::cerr << "Usage: nid_patcher <library_name> <file.so> [file2.so ...]\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
const std::string libraryName = argv[1];
|
||||
|
||||
for (int i = 2; i < argc; ++i) {
|
||||
const std::string path = argv[i];
|
||||
try {
|
||||
auto binary = _readFile(path);
|
||||
_patchNids(binary, libraryName);
|
||||
_writeFile(path, binary);
|
||||
std::cout << "OK: " << path << "\n";
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "FAIL: " << path << ": " << e.what() << "\n";
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
Reference in New Issue