finished coding transformations, next_inst_pass, jcc_rewrite_pass and

reloc_transform_pass are all done....
3.0
_xeroxz 2 years ago
parent df0ba19093
commit c7eb3c83bf

@ -63,9 +63,7 @@ list(APPEND Theodosius_SOURCES
"include/obf/passes/next_inst_pass.hpp"
"include/obf/passes/reloc_transform_pass.hpp"
"include/obf/transform/add_op.hpp"
"include/obf/transform/and_op.hpp"
"include/obf/transform/operation.hpp"
"include/obf/transform/or_op.hpp"
"include/obf/transform/rol_op.hpp"
"include/obf/transform/ror_op.hpp"
"include/obf/transform/sub_op.hpp"

@ -1,7 +1,7 @@
#pragma once
#include <coff/image.hpp>
#include <recomp/reloc.hpp>
#include <cstdint>
#include <recomp/reloc.hpp>
#include <string>
#include <vector>
@ -10,7 +10,8 @@ enum sym_type_t { function, inst_split, data, section };
class symbol_t {
public:
explicit symbol_t(std::string name,
explicit symbol_t(coff::image_t* img,
std::string name,
std::uintptr_t offset,
std::vector<std::uint8_t> data,
coff::section_header_t* scn = {},
@ -23,6 +24,7 @@ class symbol_t {
std::uintptr_t allocated_at() const;
std::uint32_t size() const;
coff::section_header_t* scn() const;
coff::image_t* img() const;
std::vector<std::uint8_t>& data();
coff::symbol_t* sym() const;
sym_type_t type() const;
@ -40,5 +42,6 @@ class symbol_t {
std::vector<recomp::reloc_t> m_relocs;
sym_type_t m_sym_type;
coff::symbol_t* m_sym;
coff::image_t* m_img;
};
} // namespace theo::decomp

@ -13,9 +13,39 @@ class pass_t {
public:
explicit pass_t(decomp::sym_type_t sym_type) : m_sym_type(sym_type){};
virtual void run(decomp::symbol_t* sym) = 0;
decomp::sym_type_t sym_type() { return m_sym_type; }
std::vector<std::uint8_t> generate_transforms(xed_decoded_inst_t* inst,
recomp::reloc_t* reloc,
std::uint8_t low,
std::uint8_t high) {
auto num_transforms = transform::operation_t::random(low, high);
auto num_ops = transform::operations.size();
std::vector<std::uint8_t> new_inst_bytes;
for (auto cnt = 0u; cnt < num_transforms; ++cnt) {
std::uint32_t imm = transform::operation_t::random(
0, std::numeric_limits<std::int32_t>::max());
auto itr = transform::operations.begin();
std::advance(itr, transform::operation_t::random(0, num_ops - 1));
auto transform_bytes = itr->second->native(inst, imm);
new_inst_bytes.insert(new_inst_bytes.end(), transform_bytes.begin(),
transform_bytes.end());
reloc->add_transform(
{transform::operations[itr->second->inverse()]->get_transform(),
imm});
}
// inverse the order in which the transformations are executed...
//
std::reverse(reloc->get_transforms().begin(),
reloc->get_transforms().end());
return new_inst_bytes;
}
private:
decomp::sym_type_t m_sym_type;
};
} // namespace theo::comp::obf
} // namespace theo::obf

@ -6,11 +6,7 @@ class jcc_rewrite_pass_t : public pass_t {
explicit jcc_rewrite_pass_t() : pass_t(decomp::sym_type_t::inst_split){};
public:
static jcc_rewrite_pass_t* get() {
static jcc_rewrite_pass_t obj;
return &obj;
}
void run(decomp::symbol_t* sym){};
static jcc_rewrite_pass_t* get();
void run(decomp::symbol_t* sym);
};
} // namespace theo::comp::obf
} // namespace theo::obf

@ -3,14 +3,20 @@
namespace theo::obf {
class next_inst_pass_t : public pass_t {
explicit next_inst_pass_t() : pass_t(decomp::sym_type_t::inst_split){};
explicit next_inst_pass_t() : pass_t(decomp::sym_type_t::inst_split) {
xed_state_t istate{XED_MACHINE_MODE_LONG_64, XED_ADDRESS_WIDTH_64b};
xed_decoded_inst_zero_set_mode(&m_tmp_inst, &istate);
xed_decode(&m_tmp_inst, m_type_inst_bytes, sizeof(m_type_inst_bytes));
};
public:
static next_inst_pass_t* get() {
static next_inst_pass_t obj;
return &obj;
}
static next_inst_pass_t* get();
void run(decomp::symbol_t* sym);
void run(decomp::symbol_t* sym){};
private:
std::optional<recomp::reloc_t*> has_next_inst_reloc(decomp::symbol_t*);
xed_decoded_inst_t m_tmp_inst;
std::uint8_t m_type_inst_bytes[8] = {0x48, 0xC7, 0x04, 0x24,
0x44, 0x33, 0x22, 0x11};
};
} // namespace theo::comp::obf
} // namespace theo::obf

@ -1,17 +0,0 @@
#pragma once
#include <obf/transform/operation.hpp>
namespace theo::obf::transform {
class and_op_t : public operation_t {
explicit and_op_t()
: operation_t([&](std::size_t val,
std::uint32_t imm) -> std::size_t { return val & imm; },
XED_ICLASS_AND) {}
public:
static and_op_t* get() {
static and_op_t obj;
return &obj;
}
};
} // namespace theo::obf::transform

@ -19,14 +19,13 @@ class operation_t {
: m_transform(op), m_type(type) {}
std::vector<std::uint8_t> native(const xed_decoded_inst_t* inst,
std::size_t imm) {
std::uint32_t imm) {
std::uint32_t inst_len = {};
std::uint8_t inst_buff[XED_MAX_INSTRUCTION_BYTES];
xed_error_enum_t err;
xed_encoder_request_init_from_decode((xed_decoded_inst_s*)inst);
xed_encoder_request_t* req = (xed_encoder_request_t*)inst;
xed_state_t istate{XED_MACHINE_MODE_LONG_64, XED_ADDRESS_WIDTH_64b};
switch (m_type) {
case XED_ICLASS_ROR:
@ -54,6 +53,7 @@ class operation_t {
xed_iclass_enum_t inverse() { return m_inverse_op[m_type]; }
transform_t* get_transform() { return &m_transform; }
xed_iclass_enum_t type() { return m_type; }
static std::size_t random(std::size_t lowest, std::size_t largest) {
std::random_device rd;
@ -67,9 +67,10 @@ class operation_t {
xed_iclass_enum_t m_type;
std::map<xed_iclass_enum_t, xed_iclass_enum_t> m_inverse_op = {
{XED_ICLASS_ADD, XED_ICLASS_SUB}, {XED_ICLASS_SUB, XED_ICLASS_ADD},
{XED_ICLASS_AND, XED_ICLASS_OR}, {XED_ICLASS_OR, XED_ICLASS_AND},
{XED_ICLASS_ROL, XED_ICLASS_ROR}, {XED_ICLASS_ROR, XED_ICLASS_ROL},
{XED_ICLASS_ADD, XED_ICLASS_SUB},
{XED_ICLASS_SUB, XED_ICLASS_ADD},
{XED_ICLASS_ROL, XED_ICLASS_ROR},
{XED_ICLASS_ROR, XED_ICLASS_ROL},
{XED_ICLASS_XOR, XED_ICLASS_XOR}};
};

@ -1,17 +0,0 @@
#pragma once
#include <obf/transform/operation.hpp>
namespace theo::obf::transform {
class or_op_t : public operation_t {
explicit or_op_t()
: operation_t([&](std::size_t val,
std::uint32_t imm) -> std::size_t { return val | imm; },
XED_ICLASS_OR) {}
public:
static or_op_t* get() {
static or_op_t obj;
return &obj;
}
};
} // namespace theo::obf::transform

@ -2,8 +2,6 @@
#include <obf/transform/operation.hpp>
#include <obf/transform/add_op.hpp>
#include <obf/transform/and_op.hpp>
#include <obf/transform/or_op.hpp>
#include <obf/transform/rol_op.hpp>
#include <obf/transform/ror_op.hpp>
#include <obf/transform/sub_op.hpp>
@ -11,8 +9,9 @@
namespace theo::obf::transform {
inline std::map<xed_iclass_enum_t, operation_t*> operations = {
{XED_ICLASS_ADD, add_op_t::get()}, {XED_ICLASS_SUB, sub_op_t::get()},
{XED_ICLASS_AND, and_op_t::get()}, {XED_ICLASS_OR, or_op_t::get()},
{XED_ICLASS_ROL, rol_op_t::get()}, {XED_ICLASS_ROR, ror_op_t::get()},
{XED_ICLASS_XOR, xor_op_t::get()}};
{XED_ICLASS_ADD, add_op_t::get()},
{XED_ICLASS_SUB, sub_op_t::get()},
{XED_ICLASS_ROL, rol_op_t::get()},
{XED_ICLASS_ROR, ror_op_t::get()},
/*{XED_ICLASS_XOR, xor_op_t::get()}*/};
}

@ -14,19 +14,20 @@ class reloc_t {
std::size_t hash() { return m_hash; }
std::string name() { return m_sym_name; }
std::uint16_t offset() { return m_offset; }
void offset(std::uint16_t offset) { m_offset = offset; }
void add_transform(
std::pair<obf::transform::transform_t*, std::size_t> entry) {
std::pair<obf::transform::transform_t*, std::uint32_t> entry) {
m_transforms.push_back(entry);
}
std::vector<std::pair<obf::transform::transform_t*, std::size_t>>&
std::vector<std::pair<obf::transform::transform_t*, std::uint32_t>>&
get_transforms() {
return m_transforms;
}
private:
std::vector<std::pair<obf::transform::transform_t*, std::size_t>>
std::vector<std::pair<obf::transform::transform_t*, std::uint32_t>>
m_transforms;
std::string m_sym_name;
std::size_t m_hash;

@ -27,7 +27,7 @@ int main(int argc, char* argv[]) {
};
theo::recomp::copier_t copier = [&](std::uintptr_t ptr, void* buff,
std::uint32_t size) {
std::uint32_t size) {
std::memcpy((void*)ptr, buff, size);
};
@ -47,6 +47,6 @@ int main(int argc, char* argv[]) {
spdlog::info("decomposed {} symbols...", res.value());
auto entry_pnt = t.compose("EntryPoint");
spdlog::info("entry point address: {:X}", entry_pnt);
// reinterpret_cast<void (*)()>(entry_pnt)();
std::getchar();
reinterpret_cast<void (*)()>(entry_pnt)();
}

@ -89,8 +89,8 @@ std::optional<recomp::symbol_table_t*> decomp_t::decompose() {
reinterpret_cast<std::uint8_t*>(img) + scn->ptr_raw_data +
scn->size_raw_data);
decomp::symbol_t new_scn_sym(scn_sym_name, 0, scn_data, scn, {}, {},
sym_type_t::section);
decomp::symbol_t new_scn_sym(img, scn_sym_name, 0, scn_data, scn,
{}, {}, sym_type_t::section);
spdlog::info(
"generating symbol for section: {} sym name: {} hash: {:X} "
@ -104,8 +104,8 @@ std::optional<recomp::symbol_table_t*> decomp_t::decompose() {
// create a symbol for the data...
//
decomp::symbol_t new_sym(
sym->name.to_string(img->get_strings()).data(), sym->value, {},
scn, sym, {}, sym_type_t::data);
img, sym->name.to_string(img->get_strings()).data(), sym->value,
{}, scn, sym, {}, sym_type_t::data);
spdlog::info("adding data symbol: {} located inside of section: {}",
new_sym.name(),
@ -123,7 +123,8 @@ std::optional<recomp::symbol_table_t*> decomp_t::decompose() {
// them...
std::vector<std::uint8_t> data(sym->value, 0);
decomp::symbol_t bss_sym(sym->name.to_string(img->get_strings()).data(),
decomp::symbol_t bss_sym(img,
sym->name.to_string(img->get_strings()).data(),
{}, data, {}, sym, {}, sym_type_t::data);
m_syms->add_symbol(bss_sym);

@ -28,12 +28,12 @@ std::vector<decomp::symbol_t> routine_t::decompose() {
sym_hash, scn_reloc->virtual_address);
relocs.push_back(recomp::reloc_t(scn_reloc->virtual_address, sym_hash,
sym_name.data()));
sym_name.data()));
}
result.push_back(decomp::symbol_t(
m_sym->name.to_string(m_img->get_strings()).data(), m_sym->value,
m_data, m_scn, m_sym, relocs, sym_type_t::function));
m_img, m_sym->name.to_string(m_img->get_strings()).data(),
m_sym->value, m_data, m_scn, m_sym, relocs, sym_type_t::function));
break;
}
case inst_split: {
@ -98,8 +98,8 @@ std::vector<decomp::symbol_t> routine_t::decompose() {
.append(std::to_string(offset +
xed_decoded_inst_get_length(&instr)));
relocs.push_back(recomp::reloc_t(0, decomp::symbol_t::hash(next_inst_sym),
next_inst_sym.data()));
relocs.push_back(recomp::reloc_t(
0, decomp::symbol_t::hash(next_inst_sym), next_inst_sym.data()));
// get the instructions bytes
//
@ -107,8 +107,8 @@ std::vector<decomp::symbol_t> routine_t::decompose() {
m_data.data() + offset,
m_data.data() + offset + xed_decoded_inst_get_length(&instr));
result.push_back(decomp::symbol_t(new_sym_name, offset, inst_bytes,
m_scn, m_sym, relocs,
result.push_back(decomp::symbol_t(m_img, new_sym_name, offset,
inst_bytes, m_scn, m_sym, relocs,
sym_type_t::inst_split));
// after creating the symbol and dealing with relocs then print the

@ -1,7 +1,8 @@
#include <decomp/symbol.hpp>
namespace theo::decomp {
symbol_t::symbol_t(std::string name,
symbol_t::symbol_t(coff::image_t* img,
std::string name,
std::uintptr_t offset,
std::vector<std::uint8_t> data,
coff::section_header_t* scn,
@ -15,6 +16,7 @@ symbol_t::symbol_t(std::string name,
m_relocs(relocs),
m_sym_type(dcmp_type),
m_sym(sym),
m_img(img),
m_allocated_at(0) {}
std::string symbol_t::name() const {
@ -33,6 +35,10 @@ coff::section_header_t* symbol_t::scn() const {
return m_scn;
}
coff::image_t* symbol_t::img() const {
return m_img;
}
std::uint32_t symbol_t::size() const {
return m_data.size();
}

@ -0,0 +1,53 @@
#include <obf/passes/jcc_rewrite_pass.hpp>
#include <obf/passes/next_inst_pass.hpp>
namespace theo::obf {
jcc_rewrite_pass_t* jcc_rewrite_pass_t::get() {
static jcc_rewrite_pass_t obj;
return &obj;
}
void jcc_rewrite_pass_t::run(decomp::symbol_t* sym) {
std::int32_t disp = {};
xed_decoded_inst_t inst;
xed_state_t istate{XED_MACHINE_MODE_LONG_64, XED_ADDRESS_WIDTH_64b};
xed_decoded_inst_zero_set_mode(&inst, &istate);
xed_decode(&inst, sym->data().data(), XED_MAX_INSTRUCTION_BYTES);
// if the instruction is branching...
if ((disp = xed_decoded_inst_get_branch_displacement(&inst))) {
disp += xed_decoded_inst_get_length(&inst);
// update displacement...
xed_decoded_inst_set_branch_displacement(
&inst, sym->data().size() - xed_decoded_inst_get_length(&inst),
xed_decoded_inst_get_branch_displacement_width(&inst));
xed_encoder_request_init_from_decode(&inst);
xed_encoder_request_t* req = &inst;
// update jcc in the buffer...
std::uint32_t len = {};
xed_encode(req, sym->data().data(), xed_decoded_inst_get_length(&inst),
&len);
// create a relocation to the instruction the branch would normally go
// too...
auto offset = disp < 0 ? sym->offset() - std::abs(disp)
: sym->offset() + std::abs(disp);
auto sym_name =
std::string(
sym->sym()->name.to_string(sym->img()->get_strings()).data())
.append("@")
.append(std::to_string(offset));
sym->relocs().push_back(
recomp::reloc_t(0, decomp::symbol_t::hash(sym_name), sym_name.data()));
// run next_inst_pass on this symbol to generate the transformations for the
// relocation to the jcc branch dest instruction...
next_inst_pass_t::get()->run(sym);
}
};
} // namespace theo::obf

@ -0,0 +1,73 @@
#include <obf/passes/next_inst_pass.hpp>
namespace theo::obf {
next_inst_pass_t* next_inst_pass_t::get() {
static next_inst_pass_t obj;
return &obj;
}
void next_inst_pass_t::run(decomp::symbol_t* sym) {
std::optional<recomp::reloc_t*> reloc;
if (!(reloc = has_next_inst_reloc(sym)).has_value())
return;
xed_decoded_inst_t inst = m_tmp_inst;
std::vector<std::uint8_t> new_inst_bytes =
generate_transforms(&inst, reloc.value(), 3, 6);
// add a push [rip+offset] and update reloc->offset()...
//
std::uint32_t inst_len = {};
std::uint8_t inst_buff[XED_MAX_INSTRUCTION_BYTES];
xed_error_enum_t err;
xed_encoder_request_t req;
xed_state_t istate{XED_MACHINE_MODE_LONG_64, XED_ADDRESS_WIDTH_64b};
xed_encoder_request_zero_set_mode(&req, &istate);
xed_encoder_request_set_effective_operand_width(&req, 64);
xed_encoder_request_set_iclass(&req, XED_ICLASS_PUSH);
xed_encoder_request_set_mem0(&req);
xed_encoder_request_set_operand_order(&req, 0, XED_OPERAND_MEM0);
xed_encoder_request_set_base0(&req, XED_REG_RIP);
xed_encoder_request_set_seg0(&req, XED_REG_INVALID);
xed_encoder_request_set_index(&req, XED_REG_INVALID);
xed_encoder_request_set_scale(&req, 0);
xed_encoder_request_set_memory_operand_length(&req, 8);
xed_encoder_request_set_memory_displacement(&req, new_inst_bytes.size() + 1,
1);
if ((err = xed_encode(&req, inst_buff, sizeof(inst_buff), &inst_len)) !=
XED_ERROR_NONE) {
spdlog::info("failed to encode instruction... reason: {}",
xed_error_enum_t2str(err));
assert(err == XED_ERROR_NONE);
}
new_inst_bytes.insert(new_inst_bytes.begin(), inst_buff,
inst_buff + inst_len);
// put a return instruction at the end of the decrypt instructions...
//
new_inst_bytes.push_back(0xC3);
sym->data().insert(sym->data().end(), new_inst_bytes.begin(),
new_inst_bytes.end());
reloc.value()->offset(sym->data().size());
sym->data().resize(sym->data().size() + 8);
}
std::optional<recomp::reloc_t*> next_inst_pass_t::has_next_inst_reloc(
decomp::symbol_t* sym) {
auto res = std::find_if(
sym->relocs().begin(), sym->relocs().end(),
[&](recomp::reloc_t& reloc) -> bool { return !reloc.offset(); });
return res != sym->relocs().end() ? res._Ptr
: std::optional<recomp::reloc_t*>();
}
} // namespace theo::obf

@ -27,42 +27,9 @@ void reloc_transform_pass_t::run(decomp::symbol_t* sym) {
assert(err == XED_ERROR_NONE);
}
// determine how many transformations we will generate...
//
auto num_transforms = transform::operation_t::random(1, 10);
auto num_ops = transform::operations.size();
for (auto cnt = 0u; cnt < num_transforms; ++cnt) {
// generate random imm value...
//
std::size_t imm = transform::operation_t::random(
0, std::numeric_limits<std::size_t>::max());
// get a random transformation...
//
auto itr = transform::operations.begin();
std::advance(itr, transform::operation_t::random(0, num_ops - 1));
// generate the native code for the transformation...
//
auto transform_bytes = itr->second->native(&inst, imm);
// insert the bytes of the native instruction into the data buffer...
//
sym->data().insert(sym->data().end(), transform_bytes.begin(),
transform_bytes.end());
// add the inverse operation to the relocation for encrypting the relocation
// pointer...
//
reloc.value()->add_transform(
{transform::operations[itr->second->inverse()]->get_transform(), imm});
}
// inverse the order in which the transformations are executed...
//
std::reverse(reloc.value()->get_transforms().begin(),
reloc.value()->get_transforms().end());
auto transforms_bytes = generate_transforms(&inst, reloc.value(), 3, 6);
sym->data().insert(sym->data().end(), transforms_bytes.begin(),
transforms_bytes.end());
};
std::optional<recomp::reloc_t*> reloc_transform_pass_t::has_legit_reloc(

@ -96,20 +96,15 @@ void recomp_t::resolve() {
break;
}
case decomp::sym_type_t::inst_split: {
// TODO: change this if statement into a sanity check with an assert
// after coding the next_inst_pass to ensure it works...
//
if (reloc.offset()) {
auto& transforms = reloc.get_transforms();
std::for_each(
transforms.begin(), transforms.end(),
[&](std::pair<obf::transform::transform_t*, std::size_t>& t) {
allocated_at = (*t.first)(allocated_at, t.second);
});
*reinterpret_cast<std::uintptr_t*>(sym.data().data() +
reloc.offset()) = allocated_at;
}
auto& transforms = reloc.get_transforms();
std::for_each(
transforms.begin(), transforms.end(),
[&](std::pair<obf::transform::transform_t*, std::uint32_t>& t) {
allocated_at = (*t.first)(allocated_at, t.second);
});
*reinterpret_cast<std::uintptr_t*>(sym.data().data() +
reloc.offset()) = allocated_at;
break;
}
default:

Loading…
Cancel
Save