From a3191d6101378d7c27f47e8958aa83b3e60d52b3 Mon Sep 17 00:00:00 2001 From: York Jasper Niebuhr Date: Sun, 26 Oct 2025 21:31:07 +0100 Subject: [PATCH] Beginnings of selfpatch --- selfpatch/include/spslr_program.h | 284 ++++++++++++++++++++++++++++++ selfpatch/src/selfpatch.c | 35 +++- 2 files changed, 318 insertions(+), 1 deletion(-) create mode 100644 selfpatch/include/spslr_program.h diff --git a/selfpatch/include/spslr_program.h b/selfpatch/include/spslr_program.h new file mode 100644 index 0000000..190818b --- /dev/null +++ b/selfpatch/include/spslr_program.h @@ -0,0 +1,284 @@ +#ifndef SPSLR_PROGRAM_H +#define SPSLR_PROGRAM_H + +#include +#include + +enum SPSLR_OPCODE { + SPSLR_TARGET, // uid, size, field count + SPSLR_FIELD, // offset, size, flags + SPSLR_RANDOMIZE, // target uid + SPSLR_MPROT, // ptr, page count, perm + SPSLR_IPATCH, // ptr, target uid, field idx (not offset!) + SPSLR_DPATCH, // ptr, target uid + SPSLR_EXIT +}; + +struct SPSLR_INST { + enum SPSLR_OPCODE opcode; + + union { + uint32_t target_uid; + uint32_t field_offset; + uint32_t randomize_target; + uint64_t mprot_ptr; + uint64_t ipatch_ptr; + uint64_t dpatch_ptr; + } op0; + + union { + uint32_t target_size; + uint32_t field_size; + uint32_t mprot_pagecnt; + uint32_t ipatch_target; + uint32_t dpatch_target; + } op1; + + union { + uint32_t target_fieldcnt; + uint32_t field_flags; + uint8_t mprot_perm; + uint32_t ipatch_field; + } op2; +}; + +static inline int spslr_inst_dump_opcode(const struct SPSLR_INST* inst, uint8_t* buf) { + if (!inst || !buf) + return -1; + + switch (inst->opcode) { + case SPSLR_TARGET: + *buf = 1; return 1; + case SPSLR_FIELD: + *buf = 2; return 1; + case SPSLR_RANDOMIZE: + *buf = 3; return 1; + case SPSLR_MPROT: + *buf = 4; return 1; + case SPSLR_IPATCH: + *buf = 5; return 1; + case SPSLR_DPATCH: + *buf = 6; return 1; + case SPSLR_EXIT: + *buf = 7; return 1; + default: + return -1; + } +} + +static inline int spslr_inst_load_opcode(struct SPSLR_INST* inst, const uint8_t* buf) { + if (!inst || !buf) + return -1; + + switch (*buf) { + case 1: + inst->opcode = SPSLR_TARGET; return 1; + case 2: + inst->opcode = SPSLR_FIELD; return 1; + case 3: + inst->opcode = SPSLR_RANDOMIZE; return 1; + case 4: + inst->opcode = SPSLR_MPROT; return 1; + case 5: + inst->opcode = SPSLR_IPATCH; return 1; + case 6: + inst->opcode = SPSLR_DPATCH; return 1; + case 7: + inst->opcode = SPSLR_EXIT; return 1; + default: + return -1; + } +} + +#define DUMP_OP_RET(field) { memcpy(buf, &field, sizeof(field)); return sizeof(field); } +#define LOAD_OP_RET(field) { memcpy(&field, buf, sizeof(field)); return sizeof(field); } + +static inline int spslr_inst_dump_op0(const struct SPSLR_INST* inst, uint8_t* buf) { + if (!inst || !buf) + return -1; + + switch (inst->opcode) { + case SPSLR_TARGET: + DUMP_OP_RET(inst->op0.target_uid); + case SPSLR_FIELD: + DUMP_OP_RET(inst->op0.field_offset); + case SPSLR_RANDOMIZE: + DUMP_OP_RET(inst->op0.randomize_target); + case SPSLR_MPROT: + DUMP_OP_RET(inst->op0.mprot_ptr); + case SPSLR_IPATCH: + DUMP_OP_RET(inst->op0.ipatch_ptr); + case SPSLR_DPATCH: + DUMP_OP_RET(inst->op0.dpatch_ptr); + case SPSLR_EXIT: + return 0; + default: + return -1; + } +} + +static inline int spslr_inst_load_op0(struct SPSLR_INST* inst, const uint8_t* buf) { + if (!inst || !buf) + return -1; + + switch (inst->opcode) { + case SPSLR_TARGET: + LOAD_OP_RET(inst->op0.target_uid); + case SPSLR_FIELD: + LOAD_OP_RET(inst->op0.field_offset); + case SPSLR_RANDOMIZE: + LOAD_OP_RET(inst->op0.randomize_target); + case SPSLR_MPROT: + LOAD_OP_RET(inst->op0.mprot_ptr); + case SPSLR_IPATCH: + LOAD_OP_RET(inst->op0.ipatch_ptr); + case SPSLR_DPATCH: + LOAD_OP_RET(inst->op0.dpatch_ptr); + case SPSLR_EXIT: + return 0; + default: + return -1; + } +} + +static inline int spslr_inst_dump_op1(const struct SPSLR_INST* inst, uint8_t* buf) { + if (!inst || !buf) + return -1; + + switch (inst->opcode) { + case SPSLR_TARGET: + DUMP_OP_RET(inst->op1.target_size); + case SPSLR_FIELD: + DUMP_OP_RET(inst->op1.field_size); + case SPSLR_MPROT: + DUMP_OP_RET(inst->op1.mprot_pagecnt); + case SPSLR_IPATCH: + DUMP_OP_RET(inst->op1.ipatch_target); + case SPSLR_DPATCH: + DUMP_OP_RET(inst->op1.dpatch_target); + case SPSLR_RANDOMIZE: + case SPSLR_EXIT: + return 0; + default: + return -1; + } +} + +static inline int spslr_inst_load_op1(struct SPSLR_INST* inst, const uint8_t* buf) { + if (!inst || !buf) + return -1; + + switch (inst->opcode) { + case SPSLR_TARGET: + LOAD_OP_RET(inst->op1.target_size); + case SPSLR_FIELD: + LOAD_OP_RET(inst->op1.field_size); + case SPSLR_MPROT: + LOAD_OP_RET(inst->op1.mprot_pagecnt); + case SPSLR_IPATCH: + LOAD_OP_RET(inst->op1.ipatch_target); + case SPSLR_DPATCH: + LOAD_OP_RET(inst->op1.dpatch_target); + case SPSLR_RANDOMIZE: + case SPSLR_EXIT: + return 0; + default: + return -1; + } +} + +static inline int spslr_inst_dump_op2(const struct SPSLR_INST* inst, uint8_t* buf) { + if (!inst || !buf) + return -1; + + switch (inst->opcode) { + case SPSLR_TARGET: + DUMP_OP_RET(inst->op2.target_fieldcnt); + case SPSLR_FIELD: + DUMP_OP_RET(inst->op2.field_flags); + case SPSLR_MPROT: + DUMP_OP_RET(inst->op2.mprot_perm); + case SPSLR_IPATCH: + DUMP_OP_RET(inst->op2.ipatch_field); + case SPSLR_DPATCH: + case SPSLR_RANDOMIZE: + case SPSLR_EXIT: + return 0; + default: + return -1; + } +} + +static inline int spslr_inst_load_op2(struct SPSLR_INST* inst, const uint8_t* buf) { + if (!inst || !buf) + return -1; + + switch (inst->opcode) { + case SPSLR_TARGET: + LOAD_OP_RET(inst->op2.target_fieldcnt); + case SPSLR_FIELD: + LOAD_OP_RET(inst->op2.field_flags); + case SPSLR_MPROT: + LOAD_OP_RET(inst->op2.mprot_perm); + case SPSLR_IPATCH: + LOAD_OP_RET(inst->op2.ipatch_field); + case SPSLR_DPATCH: + case SPSLR_RANDOMIZE: + case SPSLR_EXIT: + return 0; + default: + return -1; + } +} + +static inline int spslr_inst_dump(const struct SPSLR_INST* inst, uint8_t* buf) { + if (!inst || !buf) + return -1; + + int opcode_sz, op0_sz, op1_sz, op2_sz; + + if ((opcode_sz = spslr_inst_dump_opcode(inst, buf)) < 0) + return -1; + buf += opcode_sz; + + if ((op0_sz = spslr_inst_dump_op0(inst, buf)) < 0) + return -1; + buf += op0_sz; + + if ((op1_sz = spslr_inst_dump_op1(inst, buf)) < 0) + return -1; + buf += op1_sz; + + if ((op2_sz = spslr_inst_dump_op2(inst, buf)) < 0) + return -1; + buf += op2_sz; + + return opcode_sz + op0_sz + op1_sz + op2_sz; +} + +static inline int spslr_inst_load(struct SPSLR_INST* inst, const uint8_t* buf) { + if (!inst || !buf) + return -1; + + int opcode_sz, op0_sz, op1_sz, op2_sz; + + if ((opcode_sz = spslr_inst_load_opcode(inst, buf)) < 0) + return -1; + buf += opcode_sz; + + if ((op0_sz = spslr_inst_load_op0(inst, buf)) < 0) + return -1; + buf += op0_sz; + + if ((op1_sz = spslr_inst_load_op1(inst, buf)) < 0) + return -1; + buf += op1_sz; + + if ((op2_sz = spslr_inst_load_op2(inst, buf)) < 0) + return -1; + buf += op2_sz; + + return opcode_sz + op0_sz + op1_sz + op2_sz; +} + +#endif diff --git a/selfpatch/src/selfpatch.c b/selfpatch/src/selfpatch.c index 9d50fa7..b5486ae 100644 --- a/selfpatch/src/selfpatch.c +++ b/selfpatch/src/selfpatch.c @@ -1,6 +1,39 @@ #include #include +#include + +#include + +/* +To allow ASLR, all patcher addresses are relative to &__spslr_program +*/ +const uint8_t* __spslr_program = NULL; + +static int spslr_do(const struct SPSLR_INST* inst) { + // TODO -> dispatch different instructions, make args/pointers absolute + return 0; +} void spslr_selfpatch() { - printf("SPSLR -> selfpatch()\n"); + if (!__spslr_program) { + fprintf(stderr, "spslr_selfpatch has no patcher program (finalize this binary to use SPSLR)\n"); + return; + } + + int sz; + struct SPSLR_INST inst; + while ((sz = spslr_inst_load(&inst, __spslr_program)) > 0) { + if (inst.opcode == SPSLR_EXIT) + return; + + if (spslr_do(&inst) < 0) { + fprintf(stderr, "spslr_selfpatch failed to execute an instruction\n"); + exit(1); + } + + __spslr_program += sz; + } + + fprintf(stderr, "spslr_selfpatch encountered invalid instruction\n"); + exit(1); }