Patcher program generation

This commit is contained in:
York Jasper Niebuhr 2025-10-27 22:07:20 +01:00
parent 17596aff05
commit 77d9de8e48
8 changed files with 153 additions and 41 deletions

View File

@ -138,9 +138,20 @@ static bool accumulate_file(const fs::path& path) {
// Note -> could do sanity checks here // Note -> could do sanity checks here
if (target.fields.contains(field.offset)) {
std::cerr << "Duplicate field offset!" << std::endl;
return false;
}
target.fields.emplace(field.offset, field); target.fields.emplace(field.offset, field);
} }
auto fit = target.fields.begin();
for (std::size_t i = 0; i < field_count; i++) {
fit->second.idx = i;
fit++;
}
std::size_t global_target_uid = accumulate_global_target(std::move(target)); std::size_t global_target_uid = accumulate_global_target(std::move(target));
cu.local_targets.emplace(local_uid, global_target_uid); cu.local_targets.emplace(local_uid, global_target_uid);
continue; continue;

View File

@ -43,6 +43,7 @@ struct FIELD {
std::size_t size; std::size_t size;
std::size_t alignment; std::size_t alignment;
std::size_t flags; std::size_t flags;
std::size_t idx;
}; };
struct TARGET { struct TARGET {

View File

@ -1,5 +1,7 @@
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <vector>
#include <cstdint>
#include <getopt.h> #include <getopt.h>
#include <LIEF/LIEF.hpp> #include <LIEF/LIEF.hpp>
@ -7,6 +9,8 @@
#include "accumulation.h" #include "accumulation.h"
#include "symbol_collection.h" #include "symbol_collection.h"
#include <spslr_program.h>
/* /*
Notes: Notes:
Datapins for same var/symbol are randomized in order of their level, from bottom of nest to top Datapins for same var/symbol are randomized in order of their level, from bottom of nest to top
@ -16,6 +20,7 @@ Notes:
*/ */
static bool disassemble_ipin(const LIEF::ELF::Section* text, IPIN::HIT& pin); static bool disassemble_ipin(const LIEF::ELF::Section* text, IPIN::HIT& pin);
static bool assemble_patcher_program(uint64_t vaddr_pivot, std::vector<uint8_t>& program);
int main(int argc, char** argv) { int main(int argc, char** argv) {
static option long_options[] = { static option long_options[] = {
@ -117,18 +122,140 @@ int main(int argc, char** argv) {
} }
} }
// Find __spslr_program as anchor of relative addressing in patcher program
if (!global_syms.symbols.contains("__spslr_program")) {
std::cerr << "Unable to locate \"__spslr_program\"!" << std::endl;
return 1;
}
uint64_t spslr_program_ptr_address = global_syms.symbols.at("__spslr_program");
// Serialize patcher program
std::vector<uint8_t> patcher_program;
if (!assemble_patcher_program(spslr_program_ptr_address, patcher_program)) {
std::cerr << "Failed to assemble patcher program!" << std::endl;
return 1;
}
// TODO // TODO
/* /*
1. Find __spslr_program 1. Add new section .spslr with patcher program
2. Serialize entire patcher program 2. Set __spslr_program to (&.spslr - &__spslr_program)
-> all pin addresses relative to &__spslr_program 3. Output final program
-> dpatch entry components ordered by level
3. Add new section .spslr with patcher program
4. Set __spslr_program to (&.spslr - &__spslr_program)
5. Output final program
*/ */
} }
bool assemble_patcher_program(uint64_t vaddr_pivot, std::vector<uint8_t>& program) {
uint8_t buf[sizeof(SPSLR_INST)];
auto append_inst = [&](const SPSLR_INST& inst) -> bool {
int n = spslr_inst_dump(&inst, buf);
if (n < 0) {
std::cerr << "Failed to dump patcher instruction!" << std::endl;
return false;
}
std::size_t program_ptr = program.size();
program.resize(program.size() + n);
std::memcpy(program.data() + program_ptr, buf, n);
return true;
};
SPSLR_INST inst;
// Dump all targets, their fields and command randomization
for (const auto& [tuid, target] : targets) {
inst.opcode = SPSLR_TARGET;
inst.op0.target_uid = tuid;
inst.op1.target_size = target.size;
inst.op2.target_fieldcnt = target.fields.size();
if (!append_inst(inst))
return false;
for (const auto& [foff, field] : target.fields) {
inst.opcode = SPSLR_FIELD;
inst.op0.field_offset = field.offset;
inst.op1.field_size = field.size;
inst.op2.field_alignment = field.alignment;
inst.op3.field_flags = field.flags; // Note -> This should be done explicitely with SPSLR_FLAG_FIELD_FIXED
if (!append_inst(inst))
return false;
}
inst.opcode = SPSLR_RANDOMIZE;
inst.op0.randomize_target = tuid;
if (!append_inst(inst))
return false;
}
// Dump ipins with addresses relative to vaddr_pivot
auto append_ipin = [&](const CU& cu, const IPIN& ipin) -> bool {
inst.opcode = SPSLR_IPATCH;
inst.op0.ipatch_ptr = ipin.hit->vaddr + ipin.hit->imm_offset - vaddr_pivot;
inst.op1.ipatch_size = ipin.hit->imm_size;
inst.op2.ipatch_target = cu.local_targets.at(ipin.local_target);
if (!targets.at(inst.op2.ipatch_target).fields.contains(ipin.field_offset))
return false;
inst.op3.ipatch_field = targets.at(inst.op2.ipatch_target).fields.at(ipin.field_offset).idx;
return append_inst(inst);
};
// Dump dpins with addresses relative to vaddr_pivot and components ordered by level
auto append_dpin = [&](const DPIN& dpin) -> bool {
std::list<DPIN::COMPONENT> sorted_components = dpin.components;
sorted_components.sort([](const DPIN::COMPONENT& a, const DPIN::COMPONENT& b) {
return a.level > b.level; // Descending order
});
for (const DPIN::COMPONENT& comp : sorted_components) {
inst.opcode = SPSLR_DPATCH;
inst.op0.dpatch_ptr = dpin.hit->vaddr + comp.offset - vaddr_pivot;
inst.op1.dpatch_target = comp.target;
if (!append_inst(inst))
return false;
}
return true;
};
// Dump all local pins
for (const auto& [cu_uid, cu] : units) {
for (const auto& [ipin_sym, ipin] : cu.ipins) {
if (!append_ipin(cu, ipin))
return false;
}
for (const auto& [dpin_sym, dpin] : cu.dpins) {
DPIN global_dpin;
for (DPIN::COMPONENT& comp : global_dpin.components)
comp.target = cu.local_targets.at(comp.target);
if (!append_dpin(global_dpin))
return false;
}
}
// Dump global dpins
for (const auto& [dpin_sym, dpin] : global_dpins) {
if (!append_dpin(dpin))
return false;
}
// Exit patcher program
inst.opcode = SPSLR_EXIT;
if (!append_inst(inst))
return false;
return true;
}
bool disassemble_ipin(const LIEF::ELF::Section* text, IPIN::HIT& pin) { bool disassemble_ipin(const LIEF::ELF::Section* text, IPIN::HIT& pin) {
if (!text) if (!text)
return false; return false;

View File

@ -4,3 +4,5 @@ target_include_directories(spslr_selfpatch PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include> $<INSTALL_INTERFACE:include>
) )
target_include_directories(spslr_finalize PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) # For spslr_program.h

View File

@ -2,10 +2,6 @@
// TODO // TODO
int spslr_mprot(void* base, uint32_t pagecnt, uint8_t perm) {
return 0;
}
int spslr_ipatch(void* ptr, uint32_t size, uint32_t target, uint32_t field) { int spslr_ipatch(void* ptr, uint32_t size, uint32_t target, uint32_t field) {
return 0; return 0;
} }

View File

@ -3,7 +3,6 @@
#include <stdint.h> #include <stdint.h>
int spslr_mprot(void* base, uint32_t pagecnt, uint8_t perm);
int spslr_ipatch(void* ptr, uint32_t size, uint32_t target, uint32_t field); int spslr_ipatch(void* ptr, uint32_t size, uint32_t target, uint32_t field);
int spslr_dpatch(void* ptr, uint32_t target); int spslr_dpatch(void* ptr, uint32_t target);

View File

@ -49,8 +49,6 @@ static int spslr_do(const struct SPSLR_INST* inst) {
inst->op2.field_alignment, inst->op3.field_flags); inst->op2.field_alignment, inst->op3.field_flags);
case SPSLR_RANDOMIZE: case SPSLR_RANDOMIZE:
return spslr_randomize(inst->op0.randomize_target); return spslr_randomize(inst->op0.randomize_target);
case SPSLR_MPROT:
return spslr_mprot(spslr_ptr_absolute(inst->op0.mprot_ptr), inst->op1.mprot_pagecnt, inst->op2.mprot_perm);
case SPSLR_IPATCH: case SPSLR_IPATCH:
return spslr_ipatch(spslr_ptr_absolute(inst->op0.ipatch_ptr), inst->op1.ipatch_size, return spslr_ipatch(spslr_ptr_absolute(inst->op0.ipatch_ptr), inst->op1.ipatch_size,
inst->op2.ipatch_target, inst->op3.ipatch_field); inst->op2.ipatch_target, inst->op3.ipatch_field);

View File

@ -10,7 +10,6 @@ enum SPSLR_OPCODE {
SPSLR_TARGET, // uid, size, field count SPSLR_TARGET, // uid, size, field count
SPSLR_FIELD, // offset, size, alignment, flags SPSLR_FIELD, // offset, size, alignment, flags
SPSLR_RANDOMIZE, // target uid SPSLR_RANDOMIZE, // target uid
SPSLR_MPROT, // ptr, page count, perm
SPSLR_IPATCH, // ptr, size, target uid, field idx (not offset!) SPSLR_IPATCH, // ptr, size, target uid, field idx (not offset!)
SPSLR_DPATCH, // ptr, target uid SPSLR_DPATCH, // ptr, target uid
SPSLR_EXIT SPSLR_EXIT
@ -23,7 +22,6 @@ struct SPSLR_INST {
uint32_t target_uid; uint32_t target_uid;
uint32_t field_offset; uint32_t field_offset;
uint32_t randomize_target; uint32_t randomize_target;
uint64_t mprot_ptr;
uint64_t ipatch_ptr; uint64_t ipatch_ptr;
uint64_t dpatch_ptr; uint64_t dpatch_ptr;
} op0; } op0;
@ -31,7 +29,6 @@ struct SPSLR_INST {
union { union {
uint32_t target_size; uint32_t target_size;
uint32_t field_size; uint32_t field_size;
uint32_t mprot_pagecnt;
uint32_t ipatch_size; uint32_t ipatch_size;
uint32_t dpatch_target; uint32_t dpatch_target;
} op1; } op1;
@ -39,7 +36,6 @@ struct SPSLR_INST {
union { union {
uint32_t target_fieldcnt; uint32_t target_fieldcnt;
uint32_t field_alignment; uint32_t field_alignment;
uint8_t mprot_perm;
uint32_t ipatch_target; uint32_t ipatch_target;
} op2; } op2;
@ -60,14 +56,12 @@ static inline int spslr_inst_dump_opcode(const struct SPSLR_INST* inst, uint8_t*
*buf = 2; return 1; *buf = 2; return 1;
case SPSLR_RANDOMIZE: case SPSLR_RANDOMIZE:
*buf = 3; return 1; *buf = 3; return 1;
case SPSLR_MPROT:
*buf = 4; return 1;
case SPSLR_IPATCH: case SPSLR_IPATCH:
*buf = 5; return 1; *buf = 4; return 1;
case SPSLR_DPATCH: case SPSLR_DPATCH:
*buf = 6; return 1; *buf = 5; return 1;
case SPSLR_EXIT: case SPSLR_EXIT:
*buf = 7; return 1; *buf = 6; return 1;
default: default:
return -1; return -1;
} }
@ -85,12 +79,10 @@ static inline int spslr_inst_load_opcode(struct SPSLR_INST* inst, const uint8_t*
case 3: case 3:
inst->opcode = SPSLR_RANDOMIZE; return 1; inst->opcode = SPSLR_RANDOMIZE; return 1;
case 4: case 4:
inst->opcode = SPSLR_MPROT; return 1;
case 5:
inst->opcode = SPSLR_IPATCH; return 1; inst->opcode = SPSLR_IPATCH; return 1;
case 6: case 5:
inst->opcode = SPSLR_DPATCH; return 1; inst->opcode = SPSLR_DPATCH; return 1;
case 7: case 6:
inst->opcode = SPSLR_EXIT; return 1; inst->opcode = SPSLR_EXIT; return 1;
default: default:
return -1; return -1;
@ -111,8 +103,6 @@ static inline int spslr_inst_dump_op0(const struct SPSLR_INST* inst, uint8_t* bu
DUMP_OP_RET(inst->op0.field_offset); DUMP_OP_RET(inst->op0.field_offset);
case SPSLR_RANDOMIZE: case SPSLR_RANDOMIZE:
DUMP_OP_RET(inst->op0.randomize_target); DUMP_OP_RET(inst->op0.randomize_target);
case SPSLR_MPROT:
DUMP_OP_RET(inst->op0.mprot_ptr);
case SPSLR_IPATCH: case SPSLR_IPATCH:
DUMP_OP_RET(inst->op0.ipatch_ptr); DUMP_OP_RET(inst->op0.ipatch_ptr);
case SPSLR_DPATCH: case SPSLR_DPATCH:
@ -135,8 +125,6 @@ static inline int spslr_inst_load_op0(struct SPSLR_INST* inst, const uint8_t* bu
LOAD_OP_RET(inst->op0.field_offset); LOAD_OP_RET(inst->op0.field_offset);
case SPSLR_RANDOMIZE: case SPSLR_RANDOMIZE:
LOAD_OP_RET(inst->op0.randomize_target); LOAD_OP_RET(inst->op0.randomize_target);
case SPSLR_MPROT:
LOAD_OP_RET(inst->op0.mprot_ptr);
case SPSLR_IPATCH: case SPSLR_IPATCH:
LOAD_OP_RET(inst->op0.ipatch_ptr); LOAD_OP_RET(inst->op0.ipatch_ptr);
case SPSLR_DPATCH: case SPSLR_DPATCH:
@ -157,8 +145,6 @@ static inline int spslr_inst_dump_op1(const struct SPSLR_INST* inst, uint8_t* bu
DUMP_OP_RET(inst->op1.target_size); DUMP_OP_RET(inst->op1.target_size);
case SPSLR_FIELD: case SPSLR_FIELD:
DUMP_OP_RET(inst->op1.field_size); DUMP_OP_RET(inst->op1.field_size);
case SPSLR_MPROT:
DUMP_OP_RET(inst->op1.mprot_pagecnt);
case SPSLR_IPATCH: case SPSLR_IPATCH:
DUMP_OP_RET(inst->op1.ipatch_size); DUMP_OP_RET(inst->op1.ipatch_size);
case SPSLR_DPATCH: case SPSLR_DPATCH:
@ -180,8 +166,6 @@ static inline int spslr_inst_load_op1(struct SPSLR_INST* inst, const uint8_t* bu
LOAD_OP_RET(inst->op1.target_size); LOAD_OP_RET(inst->op1.target_size);
case SPSLR_FIELD: case SPSLR_FIELD:
LOAD_OP_RET(inst->op1.field_size); LOAD_OP_RET(inst->op1.field_size);
case SPSLR_MPROT:
LOAD_OP_RET(inst->op1.mprot_pagecnt);
case SPSLR_IPATCH: case SPSLR_IPATCH:
LOAD_OP_RET(inst->op1.ipatch_size); LOAD_OP_RET(inst->op1.ipatch_size);
case SPSLR_DPATCH: case SPSLR_DPATCH:
@ -203,8 +187,6 @@ static inline int spslr_inst_dump_op2(const struct SPSLR_INST* inst, uint8_t* bu
DUMP_OP_RET(inst->op2.target_fieldcnt); DUMP_OP_RET(inst->op2.target_fieldcnt);
case SPSLR_FIELD: case SPSLR_FIELD:
DUMP_OP_RET(inst->op2.field_alignment); DUMP_OP_RET(inst->op2.field_alignment);
case SPSLR_MPROT:
DUMP_OP_RET(inst->op2.mprot_perm);
case SPSLR_IPATCH: case SPSLR_IPATCH:
DUMP_OP_RET(inst->op2.ipatch_target); DUMP_OP_RET(inst->op2.ipatch_target);
case SPSLR_DPATCH: case SPSLR_DPATCH:
@ -225,8 +207,6 @@ static inline int spslr_inst_load_op2(struct SPSLR_INST* inst, const uint8_t* bu
LOAD_OP_RET(inst->op2.target_fieldcnt); LOAD_OP_RET(inst->op2.target_fieldcnt);
case SPSLR_FIELD: case SPSLR_FIELD:
LOAD_OP_RET(inst->op2.field_alignment); LOAD_OP_RET(inst->op2.field_alignment);
case SPSLR_MPROT:
LOAD_OP_RET(inst->op2.mprot_perm);
case SPSLR_IPATCH: case SPSLR_IPATCH:
LOAD_OP_RET(inst->op2.ipatch_target); LOAD_OP_RET(inst->op2.ipatch_target);
case SPSLR_DPATCH: case SPSLR_DPATCH:
@ -248,7 +228,6 @@ static inline int spslr_inst_dump_op3(const struct SPSLR_INST* inst, uint8_t* bu
case SPSLR_IPATCH: case SPSLR_IPATCH:
DUMP_OP_RET(inst->op3.ipatch_field); DUMP_OP_RET(inst->op3.ipatch_field);
case SPSLR_TARGET: case SPSLR_TARGET:
case SPSLR_MPROT:
case SPSLR_DPATCH: case SPSLR_DPATCH:
case SPSLR_RANDOMIZE: case SPSLR_RANDOMIZE:
case SPSLR_EXIT: case SPSLR_EXIT:
@ -268,7 +247,6 @@ static inline int spslr_inst_load_op3(struct SPSLR_INST* inst, const uint8_t* bu
case SPSLR_IPATCH: case SPSLR_IPATCH:
LOAD_OP_RET(inst->op3.ipatch_field); LOAD_OP_RET(inst->op3.ipatch_field);
case SPSLR_TARGET: case SPSLR_TARGET:
case SPSLR_MPROT:
case SPSLR_DPATCH: case SPSLR_DPATCH:
case SPSLR_RANDOMIZE: case SPSLR_RANDOMIZE:
case SPSLR_EXIT: case SPSLR_EXIT: