Compare commits

...

7 Commits

Author SHA1 Message Date
_xeroxz 772ddbc573 updated pass system, works good now. merge that shit
2 years ago
_xeroxz 094ed9f15f added a fuck ton of shit
2 years ago
_xeroxz 51d64b44b1 new pass system is done, old passes have been re-written to conform.
2 years ago
_xeroxz d744c6b585 expanded the pass system, added more places where passes can run...
2 years ago
_xeroxz 96c238e24f working on fixing a few things, moving code to passes...
2 years ago
_xeroxz dca70322fe removed the code that splits functions up. this code should be a pass,
2 years ago
_xeroxz 9b2fb43fb9 added some comments. preparing to move the routine split code to a
2 years ago

@ -59,6 +59,7 @@ list(APPEND Theodosius_SOURCES
"include/decomp/symbol.hpp"
"include/obf/engine.hpp"
"include/obf/pass.hpp"
"include/obf/passes/func_split_pass.hpp"
"include/obf/passes/jcc_rewrite_pass.hpp"
"include/obf/passes/next_inst_pass.hpp"
"include/obf/passes/reloc_transform_pass.hpp"
@ -78,6 +79,7 @@ list(APPEND Theodosius_SOURCES
"src/decomp/routine.cpp"
"src/decomp/symbol.cpp"
"src/obf/engine.cpp"
"src/obf/passes/func_split_pass.cpp"
"src/obf/passes/jcc_rewrite_pass.cpp"
"src/obf/passes/next_inst_pass.cpp"
"src/obf/passes/reloc_transform_pass.cpp"

@ -36,8 +36,8 @@ namespace theo::obf {
/// <summary>
/// hello world pass example of how to inherit pass_t.
/// </summary>
class hello_world_pass_t : public pass_t {
hello_world_pass_t() : pass_t(decomp::sym_type_t::all) {
class hello_world_pass_t : public generic_pass_t {
hello_world_pass_t() : generic_pass_t(decomp::sym_type_t::all) {
spdlog::info("created hello world pass...");
}
@ -47,7 +47,7 @@ class hello_world_pass_t : public pass_t {
return &obj;
}
void run(decomp::symbol_t* sym) {
void generic_pass(decomp::symbol_t* sym, sym_map_t& sym_tbl) override {
spdlog::info("[hello_world_pass_t] symbol name: {}, symbol hash: {}",
sym->name(), sym->hash());
}

@ -39,6 +39,7 @@
#include <theo.hpp>
#include <obf/engine.hpp>
#include <obf/passes/func_split_pass.hpp>
#include <obf/passes/jcc_rewrite_pass.hpp>
#include <obf/passes/next_inst_pass.hpp>
#include <obf/passes/reloc_transform_pass.hpp>
@ -114,16 +115,17 @@ int main(int argc, char* argv[]) {
//
auto engine = theo::obf::engine_t::get();
// add in our hello world pass here
//
engine->add_pass(theo::obf::hello_world_pass_t::get());
// add the rest of the passes in this order. this order is important.
//
engine->add_pass(theo::obf::func_split_pass_t::get());
engine->add_pass(theo::obf::reloc_transform_pass_t::get());
engine->add_pass(theo::obf::next_inst_pass_t::get());
engine->add_pass(theo::obf::jcc_rewrite_pass_t::get());
// add in our hello world pass here
//
engine->add_pass(theo::obf::hello_world_pass_t::get());
std::string entry_name;
std::cout << "enter the name of the entry point: ";
std::cin >> entry_name;
@ -147,5 +149,8 @@ int main(int argc, char* argv[]) {
spdlog::info("decomposed {} symbols...", res.value());
auto entry_pnt = t.compose();
spdlog::info("entry point address: {:X}", entry_pnt);
spdlog::info("press enter to execute {}", entry_name.c_str());
std::getchar();
reinterpret_cast<void (*)()>(entry_pnt)();
}

@ -15,7 +15,7 @@ test_t t = {"Hello", "World", {}};
OBF extern "C" void EntryPoint() {
t.buff[0] = 1;
t.buff[1] = 1;
t.buff[1] = 0;
if (t.buff[0])
MessageBoxA(nullptr, t.c, t.c2, nullptr);

@ -66,14 +66,13 @@ class routine_t {
explicit routine_t(coff::symbol_t* sym,
coff::image_t* img,
coff::section_header_t* scn,
std::vector<std::uint8_t>& fn,
sym_type_t dcmp_type);
std::vector<std::uint8_t>& fn);
/// <summary>
/// decompose the function into symbol(s).
/// </summary>
/// <returns>symbol(s) of the function.</returns>
std::vector<decomp::symbol_t> decompose();
/// <returns>symbol of the function.</returns>
decomp::symbol_t decompose();
/// <summary>
/// gets the section header of the section in which the symbol is located in.
@ -93,6 +92,5 @@ class routine_t {
std::vector<std::uint8_t> m_data;
coff::image_t* m_img;
coff::section_header_t* m_scn;
sym_type_t m_dcmp_type;
};
} // namespace theo::decomp

@ -139,6 +139,12 @@ class symbol_t {
/// <returns>the type of the symbol.</returns>
sym_type_t type() const;
/// <summary>
/// setter for the type value.
/// </summary>
/// <param name="type">type of symbol.</param>
void type(sym_type_t type);
/// <summary>
/// returns a vector of relocations.
/// </summary>

@ -35,6 +35,11 @@
namespace theo::obf {
/// <summary>
/// callback used by obfuscation engine to run passes...
/// </summary>
using engine_callback_t = std::function<void(decomp::symbol_t*, pass_t*)>;
/// <summary>
/// singleton obfuscation engine class. this class is responsible for keeping
/// track of the registered passes and the order in which to execute them.
@ -59,11 +64,12 @@ class engine_t {
void add_pass(pass_t* pass);
/// <summary>
/// run all the passes on the symbol. this function will only run a pass if
/// the symbol is the same type as the pass requires.
/// invokes the callback for each pass in order.
/// </summary>
/// <param name="sym">symbol to run all passes on.</param>
void run(decomp::symbol_t* sym);
/// <param name="callback">callback to be invoked. the callback is given a
/// pointer to the pass.</param>
/// <param name="sym">symbol to run callbacks on.</param>
void for_each(decomp::symbol_t* sym, engine_callback_t callback);
private:
std::vector<pass_t*> passes;

@ -44,6 +44,25 @@ extern "C" {
/// </summary>
namespace theo::obf {
/// <summary>
/// a function which is called by recomp_t to copy symbols into memory.
/// </summary>
using copier_t = std::function<void(std::uintptr_t, void*, std::uint32_t)>;
/// <summary>
/// a function which is called to allocate space for a symbol.
///
/// the first param is the size of the symbol, the second param is the
/// characteristics of the section which the symbol is allocated in.
/// </summary>
using allocator_t =
std::function<std::uintptr_t(std::uint32_t,
coff::section_characteristics_t)>;
// symbol table map associated with the symbol table data structure.
//
using sym_map_t = std::map<std::size_t, decomp::symbol_t>;
/// <summary>
/// the pass_t class is a base clase for all passes made. you must override the
/// pass_t::run virtual function and declare the logic of your pass there.
@ -63,11 +82,52 @@ class pass_t {
explicit pass_t(decomp::sym_type_t sym_type) : m_sym_type(sym_type){};
/// <summary>
/// virtual method which must be implimented by the pass that inherits this
/// class.
/// This virtual method is invoked before the recomp stage of Theodosius. This
/// allows you to manipulate symbols in a generic manner.
/// </summary>
/// <param name="sym">a symbol of the same type of m_sym_type.</param>
virtual void run(decomp::symbol_t* sym) = 0;
virtual void generic_pass(decomp::symbol_t* sym, sym_map_t& sym_tbl) = 0;
/// <summary>
/// This virtual method is invoked prior to calling the "copier". This allows
/// you to manipulate the symbol prior to it being copied into memory.
/// </summary>
/// <param name="sym">Symbol being copied into memory.</param>
/// <param name="copier">copier lambda that the pass can use. if it does, then
/// you must return true.</param> <returns>returns true if the copy was done
/// by the pass. false if copying still needs to be done.</returns>
virtual bool copier_pass(decomp::symbol_t* sym, copier_t& copier) = 0;
/// <summary>
/// This virtual method is invoked prior to calling the "allocator". This
/// allows you to manipulate the symbol prior to memory being allocated for
/// it.
/// </summary>
/// <param name="sym">The symbol that memory is being allocated for.</param>
/// <param name="size">The size of the symbol that memory is being allocated
/// for.</param>
/// <returns>returns the optional address of the allocation, if the pass didnt
/// allocate space for the symbol than this optional value will have no
/// value.</returns>
virtual std::optional<std::uintptr_t> allocation_pass(
decomp::symbol_t* sym,
std::uint32_t size,
allocator_t& allocator) = 0;
/// <summary>
/// This virtual method is invoked before the call to the "resolver". This
/// allows you the manipulate the relocation address and symbol before
/// relocations are made.
/// </summary>
/// <param name="sym">The symbol that has the relocation inside of it.</param>
/// <param name="reloc">The relocation be be applied.</param>
/// <param name="allocated_t">The address of the symbol pointed too by the
/// relocation.</param>
/// <returns></returns>
virtual std::optional<std::uintptr_t> resolver_pass(
decomp::symbol_t* sym,
recomp::reloc_t* reloc,
std::uintptr_t allocated_t) = 0;
/// <summary>
/// gets the passes symbol type.
@ -78,4 +138,29 @@ class pass_t {
private:
decomp::sym_type_t m_sym_type;
};
/// <summary>
/// generic pass class overloads non-generic pass methods to return default
/// values... this makes it so generic passes dont need to overload all of these
/// methods and return default values every single time...
/// </summary>
class generic_pass_t : public pass_t {
public:
explicit generic_pass_t(decomp::sym_type_t sym_type) : pass_t(sym_type) {}
// default non-generic passes to return generic values...
//
bool copier_pass(decomp::symbol_t* sym, copier_t& copier) { return false; }
std::optional<std::uintptr_t> allocation_pass(decomp::symbol_t* sym,
std::uint32_t size,
allocator_t& allocator) {
return {};
}
std::optional<std::uintptr_t> resolver_pass(decomp::symbol_t* sym,
recomp::reloc_t* reloc,
std::uintptr_t allocated_t) {
return {};
}
};
} // namespace theo::obf

@ -0,0 +1,42 @@
// Copyright (c) 2022, _xeroxz
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
#pragma once
#include <obf/pass.hpp>
namespace theo::obf {
class func_split_pass_t : public generic_pass_t {
explicit func_split_pass_t() : generic_pass_t(decomp::sym_type_t::function) {}
public:
static func_split_pass_t* get();
void generic_pass(decomp::symbol_t* sym, sym_map_t& sym_tbl) override;
};
} // namespace theo::obf

@ -58,11 +58,12 @@ namespace theo::obf {
/// of the next instruction. There is actually no jmp [rip] either, push/ret is
/// used.
/// </summary>
class jcc_rewrite_pass_t : public pass_t {
explicit jcc_rewrite_pass_t() : pass_t(decomp::sym_type_t::instruction) {}
class jcc_rewrite_pass_t : public generic_pass_t {
explicit jcc_rewrite_pass_t()
: generic_pass_t(decomp::sym_type_t::instruction) {}
public:
static jcc_rewrite_pass_t* get();
void run(decomp::symbol_t* sym);
void generic_pass(decomp::symbol_t* sym, sym_map_t& sym_tbl) override;
};
} // namespace theo::obf

@ -82,8 +82,9 @@ namespace theo::obf {
/// simply tells this pass that there needs to be a relocation to the next
/// symbol. the offset for these psuedo relocations is zero.
/// </summary>
class next_inst_pass_t : public pass_t {
explicit next_inst_pass_t() : pass_t(decomp::sym_type_t::instruction) {
class next_inst_pass_t : public generic_pass_t {
explicit next_inst_pass_t()
: generic_pass_t(decomp::sym_type_t::instruction) {
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));
@ -91,7 +92,7 @@ class next_inst_pass_t : public pass_t {
public:
static next_inst_pass_t* get();
void run(decomp::symbol_t* sym);
void generic_pass(decomp::symbol_t* sym, sym_map_t& sym_tbl) override;
private:
std::optional<recomp::reloc_t*> has_next_inst_reloc(decomp::symbol_t*);

@ -53,12 +53,13 @@ namespace theo::obf {
/// add rax, 0x345332567
/// ror rax, 0x5353
/// </summary>
class reloc_transform_pass_t : public pass_t {
explicit reloc_transform_pass_t() : pass_t(decomp::sym_type_t::instruction) {}
class reloc_transform_pass_t : public generic_pass_t {
explicit reloc_transform_pass_t()
: generic_pass_t(decomp::sym_type_t::instruction) {}
public:
static reloc_transform_pass_t* get();
void run(decomp::symbol_t* sym);
void generic_pass(decomp::symbol_t* sym, sym_map_t& sym_tbl) override;
private:
std::optional<recomp::reloc_t*> has_legit_reloc(decomp::symbol_t* sym);

@ -27,14 +27,11 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
#pragma once
#include <cstddef>
#include <cstdint>
#include <obf/transform/transform.hpp>
namespace theo::recomp {
/// <summary>
/// meta data about a relocation for a symbol
/// </summary>
@ -53,19 +50,16 @@ class reloc_t {
std::size_t hash,
const std::string&& sym_name)
: m_offset(offset), m_hash(hash), m_sym_name(sym_name) {}
/// <summary>
/// returns the hash of the relocation symbol.
/// </summary>
/// <returns>hash of the relocation symbol</returns>
std::size_t hash() { return m_hash; }
/// <summary>
/// returns the name of the relocation symbol.
/// </summary>
/// <returns>returns the name of the relocation symbol.</returns>
std::string name() { return m_sym_name; }
/// <summary>
/// returns the offset into the symbol to which the relocation will be
/// applied. the offset is in bytes. zero based.
@ -73,14 +67,12 @@ class reloc_t {
/// <returns>returns the offset into the symbol to which the relocation will
/// be applied. the offset is in bytes. zero based.</returns>
std::uint32_t offset() { return m_offset; }
/// <summary>
/// sets the offset to which the relocation gets applied too.
/// </summary>
/// <param name="offset">offset to which the relocation gets applied
/// too.</param>
void offset(std::uint32_t offset) { m_offset = offset; }
/// <summary>
/// adds a transformation to be applied to the relocation prior to writing it
/// into the symbol.
@ -92,7 +84,6 @@ class reloc_t {
std::pair<obf::transform::transform_t*, std::uint32_t> entry) {
m_transforms.push_back(entry);
}
/// <summary>
/// gets the vector of transformation.
/// </summary>

@ -99,6 +99,12 @@ class symbol_table_t {
/// <returns>returns the size of the symbol table.</returns>
std::uint32_t size();
/// <summary>
/// getter for underlying symbol hash map.
/// </summary>
/// <returns>returns the symbol hashmap.</returns>
std::map<std::size_t, decomp::symbol_t>& get();
private:
std::map<std::size_t, decomp::symbol_t> m_table;
};

@ -112,27 +112,26 @@ std::optional<recomp::symbol_table_t*> decomp_t::decompose(
if (sym->has_section()) {
if (sym->derived_type == coff::derived_type_id::function) {
auto scn = img->get_section(sym->section_index - 1);
auto dcmp_type =
scn->name.to_string(img->get_strings()) == INSTR_SPLIT_SECTION_NAME
? decomp::sym_type_t::instruction
: decomp::sym_type_t::function;
auto fn_size = next_sym(img, scn, sym);
auto fn_bgn = scn->ptr_raw_data + reinterpret_cast<std::uint8_t*>(img) +
sym->value;
// extract the bytes the function is composed of...
//
std::vector<std::uint8_t> fn(fn_bgn, fn_bgn + fn_size);
decomp::routine_t rtn(sym, img, scn, fn, dcmp_type);
decomp::routine_t rtn(sym, img, scn, fn);
auto syms = rtn.decompose();
m_syms->put_symbols(syms);
auto fsym = rtn.decompose();
m_syms->put_symbol(fsym);
// else the symbol isnt a function and its public or private (some data
// symbols are private)...
} else if (sym->storage_class == coff::storage_class_id::public_symbol ||
sym->storage_class == coff::storage_class_id::private_symbol) {
auto scn = img->get_section(sym->section_index - 1);
auto scn_sym = m_syms->sym_from_hash(m_scn_hash_tbl[scn]);
// if the section doesnt have a symbol then make one and put it into
// the symbol table...
// if the section containing this symbol doesnt have a symbol then make
// one and put it into the symbol table...
//
if (!scn_sym.has_value()) {
auto scn_sym_name =
@ -153,6 +152,8 @@ std::optional<recomp::symbol_table_t*> decomp_t::decompose(
scn->size_raw_data);
}
// extract the relocations needed for this section...
//
std::vector<recomp::reloc_t> relocs;
auto scn_relocs = reinterpret_cast<coff::reloc_t*>(
scn->ptr_relocs + reinterpret_cast<std::uint8_t*>(img));
@ -167,6 +168,8 @@ std::optional<recomp::symbol_table_t*> decomp_t::decompose(
sym_hash, sym_name.data()));
}
// create a new section symbol...
//
decomp::symbol_t new_scn_sym(img, scn_sym_name, 0, scn_data, scn, {},
relocs, sym_type_t::section);

@ -34,135 +34,35 @@ namespace theo::decomp {
routine_t::routine_t(coff::symbol_t* sym,
coff::image_t* img,
coff::section_header_t* scn,
std::vector<std::uint8_t>& fn,
sym_type_t dcmp_type)
: m_img(img), m_scn(scn), m_data(fn), m_dcmp_type(dcmp_type), m_sym(sym) {}
std::vector<decomp::symbol_t> routine_t::decompose() {
std::vector<decomp::symbol_t> result;
switch (m_dcmp_type) {
case function: {
std::vector<recomp::reloc_t> relocs;
auto scn_relocs = reinterpret_cast<coff::reloc_t*>(
m_scn->ptr_relocs + reinterpret_cast<std::uint8_t*>(m_img));
for (auto idx = 0u; idx < m_scn->num_relocs; ++idx) {
auto scn_reloc = &scn_relocs[idx];
// if the reloc is in the current function...
if (scn_reloc->virtual_address >= m_sym->value &&
scn_reloc->virtual_address < m_sym->value + m_data.size()) {
auto sym_reloc = m_img->get_symbol(scn_relocs[idx].symbol_index);
auto sym_name = symbol_t::name(m_img, sym_reloc);
auto sym_hash = decomp::symbol_t::hash(sym_name.data());
relocs.push_back(
recomp::reloc_t(scn_reloc->virtual_address - m_sym->value,
sym_hash, sym_name.data()));
}
}
result.push_back(decomp::symbol_t(
m_img, symbol_t::name(m_img, m_sym).data(), m_sym->value, m_data,
m_scn, m_sym, relocs, sym_type_t::function));
break;
}
case instruction: {
std::uint32_t offset = {};
xed_error_enum_t err;
xed_decoded_inst_t instr;
xed_state_t istate{XED_MACHINE_MODE_LONG_64, XED_ADDRESS_WIDTH_64b};
xed_decoded_inst_zero_set_mode(&instr, &istate);
// keep looping over the section, lower the number of bytes each time...
//
while ((err = xed_decode(&instr, m_data.data() + offset,
m_data.size() - offset)) == XED_ERROR_NONE) {
// symbol name is of the format: symbol@instroffset, I.E: main@11...
//
auto new_sym_name = symbol_t::name(m_img, m_sym);
// first instruction doesnt need the @offset...
//
if (offset)
new_sym_name.append("@").append(std::to_string(offset));
std::vector<recomp::reloc_t> relocs;
auto scn_relocs = reinterpret_cast<coff::reloc_t*>(
m_scn->ptr_relocs + reinterpret_cast<std::uint8_t*>(m_img));
// find if this instruction has a relocation or not...
// if so, return the reloc_t...
//
auto reloc = std::find_if(
scn_relocs, scn_relocs + m_scn->num_relocs,
[&](coff::reloc_t reloc) {
return reloc.virtual_address >= m_sym->value + offset &&
reloc.virtual_address <
m_sym->value + offset +
xed_decoded_inst_get_length(&instr);
});
// if there is indeed a reloc for this instruction...
//
if (reloc != scn_relocs + m_scn->num_relocs) {
auto sym_reloc = m_img->get_symbol(reloc->symbol_index);
auto sym_name = symbol_t::name(m_img, sym_reloc);
auto sym_hash = decomp::symbol_t::hash(sym_name.data());
auto reloc_offset = reloc->virtual_address - m_sym->value - offset;
relocs.push_back(
recomp::reloc_t(reloc_offset, sym_hash, sym_name.data()));
}
// add a reloc to the next instruction...
// note that the offset is ZERO... comp_t will understand that
// relocs with offset ZERO means the next instructions...
//
auto next_inst_sym =
symbol_t::name(m_img, m_sym)
.append("@")
.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()));
// get the instructions bytes
//
std::vector<std::uint8_t> inst_bytes(
m_data.data() + offset,
m_data.data() + offset + xed_decoded_inst_get_length(&instr));
result.push_back(decomp::symbol_t(m_img, new_sym_name, offset,
inst_bytes, m_scn, m_sym, relocs,
sym_type_t::instruction));
// after creating the symbol and dealing with relocs then print the
// information we have concluded...
//
char buff[255];
offset += xed_decoded_inst_get_length(&instr);
xed_format_context(XED_SYNTAX_INTEL, &instr, buff, sizeof buff, NULL,
NULL, NULL);
spdlog::info("{}: {}", new_sym_name, buff);
// need to set this so that instr can be used to decode again...
xed_decoded_inst_zero_set_mode(&instr, &istate);
}
// remove the relocation to the next symbol from the last instruction
//
auto& last_inst = result.back();
auto& last_inst_relocs = last_inst.relocs();
last_inst_relocs.erase(last_inst_relocs.end() - 1);
break;
std::vector<std::uint8_t>& fn)
: m_img(img), m_scn(scn), m_data(fn), m_sym(sym) {}
decomp::symbol_t routine_t::decompose() {
std::vector<recomp::reloc_t> relocs;
auto scn_relocs = reinterpret_cast<coff::reloc_t*>(
m_scn->ptr_relocs + reinterpret_cast<std::uint8_t*>(m_img));
// extract all of the relocations that this function has...
//
for (auto idx = 0u; idx < m_scn->num_relocs; ++idx) {
auto scn_reloc = &scn_relocs[idx];
// if the reloc is in the current function...
if (scn_reloc->virtual_address >= m_sym->value &&
scn_reloc->virtual_address < m_sym->value + m_data.size()) {
auto sym_reloc = m_img->get_symbol(scn_relocs[idx].symbol_index);
auto sym_name = symbol_t::name(m_img, sym_reloc);
auto sym_hash = decomp::symbol_t::hash(sym_name.data());
relocs.push_back(
recomp::reloc_t(scn_reloc->virtual_address - m_sym->value, sym_hash,
sym_name.data()));
}
default:
break;
}
return result;
// return the created symbol_t for this function...
//
return decomp::symbol_t(m_img, symbol_t::name(m_img, m_sym).data(),
m_sym->value, m_data, m_scn, m_sym, relocs,
sym_type_t::function);
}
coff::section_header_t* routine_t::scn() {

@ -81,6 +81,10 @@ sym_type_t symbol_t::type() const {
return m_sym_type;
}
void symbol_t::type(sym_type_t type) {
m_sym_type = type;
}
void symbol_t::allocated_at(std::uintptr_t allocated_at) {
m_allocated_at = allocated_at;
}

@ -40,10 +40,10 @@ void engine_t::add_pass(pass_t* pass) {
passes.push_back(pass);
}
void engine_t::run(decomp::symbol_t* sym) {
void engine_t::for_each(decomp::symbol_t* sym, engine_callback_t callback) {
std::for_each(passes.begin(), passes.end(), [&](pass_t* pass) {
if (sym->type() & pass->sym_type())
pass->run(sym);
callback(sym, pass);
});
}

@ -0,0 +1,135 @@
// Copyright (c) 2022, _xeroxz
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
#include <obf/passes/func_split_pass.hpp>
namespace theo::obf {
func_split_pass_t* func_split_pass_t::get() {
static func_split_pass_t obj;
return &obj;
}
void func_split_pass_t::generic_pass(decomp::symbol_t* sym,
sym_map_t& sym_tbl) {
std::uint32_t offset = {};
xed_error_enum_t err;
xed_decoded_inst_t instr;
std::vector<decomp::symbol_t> result;
xed_state_t istate{XED_MACHINE_MODE_LONG_64, XED_ADDRESS_WIDTH_64b};
xed_decoded_inst_zero_set_mode(&instr, &istate);
// keep looping over the function, lower the number of bytes each time...
//
while ((err = xed_decode(&instr, sym->data().data() + offset,
sym->data().size() - offset)) == XED_ERROR_NONE) {
// symbol name is of the format: symbol@instroffset, I.E: main@11...
//
auto new_sym_name = decomp::symbol_t::name(sym->img(), sym->sym());
// first instruction doesnt need the @offset...
//
if (offset)
new_sym_name.append("@").append(std::to_string(offset));
std::vector<recomp::reloc_t> relocs;
auto scn_relocs = reinterpret_cast<coff::reloc_t*>(
sym->scn()->ptr_relocs + reinterpret_cast<std::uint8_t*>(sym->img()));
// find if this instruction has a relocation or not...
// if so, return the reloc_t...
//
auto reloc = std::find_if(
scn_relocs, scn_relocs + sym->scn()->num_relocs,
[&](coff::reloc_t reloc) {
return reloc.virtual_address >= sym->sym()->value + offset &&
reloc.virtual_address <
sym->sym()->value + offset +
xed_decoded_inst_get_length(&instr);
});
// if there is indeed a reloc for this instruction...
//
if (reloc != scn_relocs + sym->scn()->num_relocs) {
auto sym_reloc = sym->img()->get_symbol(reloc->symbol_index);
auto sym_name = decomp::symbol_t::name(sym->img(), sym_reloc);
auto sym_hash = decomp::symbol_t::hash(sym_name.data());
auto reloc_offset = reloc->virtual_address - sym->sym()->value - offset;
relocs.push_back(
recomp::reloc_t(reloc_offset, sym_hash, sym_name.data()));
}
// add a reloc to the next instruction...
// note that the offset is ZERO... comp_t will understand that
// relocs with offset ZERO means the next instructions...
//
auto next_inst_sym = decomp::symbol_t::name(sym->img(), sym->sym())
.append("@")
.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()));
// get the instructions bytes
//
std::vector<std::uint8_t> inst_bytes(
sym->data().data() + offset,
sym->data().data() + offset + xed_decoded_inst_get_length(&instr));
result.push_back(decomp::symbol_t(sym->img(), new_sym_name, offset,
inst_bytes, sym->scn(), sym->sym(),
relocs, decomp::sym_type_t::instruction));
// after creating the symbol and dealing with relocs then print the
// information we have concluded...
//
char buff[255];
offset += xed_decoded_inst_get_length(&instr);
xed_format_context(XED_SYNTAX_INTEL, &instr, buff, sizeof buff, NULL, NULL,
NULL);
spdlog::info("[func_split_pass_t] {}: {}", new_sym_name, buff);
// need to set this so that instr can be used to decode again...
xed_decoded_inst_zero_set_mode(&instr, &istate);
}
// remove the relocation to the next symbol from the last instruction
//
auto& last_inst = result.back();
auto& last_inst_relocs = last_inst.relocs();
last_inst_relocs.erase(last_inst_relocs.end() - 1);
// insert the split instructions into the symbol table.
//
for (auto& symbol : result) {
auto itr = sym_tbl.find(symbol.hash());
if (itr != sym_tbl.end())
itr->second = symbol;
else
sym_tbl.insert({symbol.hash(), symbol});
}
}
} // namespace theo::obf

@ -37,7 +37,8 @@ jcc_rewrite_pass_t* jcc_rewrite_pass_t::get() {
return &obj;
}
void jcc_rewrite_pass_t::run(decomp::symbol_t* sym) {
void jcc_rewrite_pass_t::generic_pass(decomp::symbol_t* sym,
sym_map_t& sym_tbl) {
std::int32_t disp = {};
xed_decoded_inst_t inst;
xed_state_t istate{XED_MACHINE_MODE_LONG_64, XED_ADDRESS_WIDTH_64b};
@ -77,7 +78,7 @@ void jcc_rewrite_pass_t::run(decomp::symbol_t* sym) {
// 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);
next_inst_pass_t::get()->generic_pass(sym, sym_tbl);
}
};
} // namespace theo::obf

@ -35,7 +35,8 @@ 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) {
void next_inst_pass_t::generic_pass(decomp::symbol_t* sym, sym_map_t& sym_tbl) {
std::optional<recomp::reloc_t*> reloc;
if (!(reloc = has_next_inst_reloc(sym)).has_value())
return;

@ -36,7 +36,8 @@ reloc_transform_pass_t* reloc_transform_pass_t::get() {
return &obj;
}
void reloc_transform_pass_t::run(decomp::symbol_t* sym) {
void reloc_transform_pass_t::generic_pass(decomp::symbol_t* sym,
sym_map_t& sym_tbl) {
std::optional<recomp::reloc_t*> reloc;
if (!(reloc = has_legit_reloc(sym)).has_value())
return;

@ -40,12 +40,23 @@ recomp_t::recomp_t(decomp::decomp_t* dcmp,
void recomp_t::allocate() {
// map code & data/rdata/bss sections first...
//
static const auto engine = obf::engine_t::get();
m_dcmp->syms()->for_each([&](theo::decomp::symbol_t& sym) {
switch (sym.type()) {
case decomp::sym_type_t::section:
case decomp::sym_type_t::function:
case decomp::sym_type_t::instruction: {
sym.allocated_at(m_allocator(sym.size(), sym.scn()->characteristics));
engine->for_each(&sym, [&](decomp::symbol_t* sym, obf::pass_t* pass) {
if (sym->allocated_at())
return;
auto res = pass->allocation_pass(sym, sym->size(), m_allocator);
if (res.has_value())
sym->allocated_at(res.value());
});
if (!sym.allocated_at())
sym.allocated_at(m_allocator(sym.size(), sym.scn()->characteristics));
break;
}
default:
@ -83,7 +94,17 @@ void recomp_t::allocate() {
prot.mem_read = true;
prot.mem_write = true;
sym.allocated_at(m_allocator(sym.size(), sym.scn()->characteristics));
engine->for_each(&sym, [&](decomp::symbol_t* sym, obf::pass_t* pass) {
if (sym->allocated_at())
return;
auto res = pass->allocation_pass(sym, sym->size(), m_allocator);
if (res.has_value())
sym->allocated_at(res.value());
});
if (!sym.allocated_at())
sym.allocated_at(m_allocator(sym.size(), sym.scn()->characteristics));
}
}
});
@ -112,6 +133,15 @@ void recomp_t::resolve() {
? reloc_sym.value()->allocated_at()
: m_resolver(reloc.name());
// run passes related to post symbol relocation...
//
static const auto engine = obf::engine_t::get();
engine->for_each(&sym, [&](decomp::symbol_t* sym, obf::pass_t* pass) {
auto res = pass->resolver_pass(sym, &reloc, allocated_at);
if (res.has_value())
allocated_at = res.value();
});
if (!allocated_at) {
spdlog::error("failed to resolve reloc from symbol: {} to symbol: {}",
sym.name(), reloc.name());
@ -128,11 +158,6 @@ void recomp_t::resolve() {
reloc.offset()) = allocated_at;
break;
}
case decomp::sym_type_t::function: {
*reinterpret_cast<std::uintptr_t*>(sym.data().data() +
reloc.offset()) = allocated_at;
break;
}
case decomp::sym_type_t::instruction: {
auto& transforms = reloc.get_transforms();
std::for_each(
@ -145,6 +170,11 @@ void recomp_t::resolve() {
reloc.offset()) = allocated_at;
break;
}
case decomp::sym_type_t::function: {
*reinterpret_cast<std::uintptr_t*>(sym.data().data() +
reloc.offset()) = allocated_at;
break;
}
default:
break;
}
@ -155,8 +185,19 @@ void recomp_t::resolve() {
void recomp_t::copy_syms() {
// copy symbols into memory using the copier supplied...
//
static const auto engine = obf::engine_t::get();
m_dcmp->syms()->for_each([&](theo::decomp::symbol_t& sym) {
m_copier(sym.allocated_at(), sym.data().data(), sym.data().size());
bool cpy = false;
engine->for_each(&sym, [&](decomp::symbol_t* sym, obf::pass_t* pass) {
if (cpy)
return;
// returns true if the copy was done for this symbol...
//
cpy = pass->copier_pass(sym, m_copier);
});
if (!cpy)
m_copier(sym.allocated_at(), sym.data().data(), sym.data().size());
});
}

@ -69,6 +69,10 @@ std::optional<decomp::symbol_t*> symbol_table_t::sym_from_alloc(
: std::optional<decomp::symbol_t*>{};
}
std::map<std::size_t, decomp::symbol_t>& symbol_table_t::get() {
return m_table;
}
std::uint32_t symbol_table_t::size() {
return m_table.size();
}

@ -54,10 +54,36 @@ std::optional<std::uint32_t> theo_t::decompose() {
}
std::uintptr_t theo_t::compose() {
// run obfuscation engine on all symbols...
//
auto engine = obf::engine_t::get();
m_sym_tbl.for_each([&](decomp::symbol_t& sym) { engine->run(&sym); });
auto& sym_tbl = m_sym_tbl.get();
// run obfuscation engine on function symbols...
//
m_sym_tbl.for_each([&](decomp::symbol_t& sym) {
engine->for_each(&sym, [&](decomp::symbol_t* sym, obf::pass_t* pass) {
if (sym->type() == decomp::sym_type_t::function)
pass->generic_pass(sym, sym_tbl);
});
});
// run obfuscation engine on instruction symbols...
//
m_sym_tbl.for_each([&](decomp::symbol_t& sym) {
engine->for_each(&sym, [&](decomp::symbol_t* sym, obf::pass_t* pass) {
if (sym->type() == decomp::sym_type_t::instruction)
pass->generic_pass(sym, sym_tbl);
});
});
// run obfuscation engine on all other symbols...
//
m_sym_tbl.for_each([&](decomp::symbol_t& sym) {
engine->for_each(&sym, [&](decomp::symbol_t* sym, obf::pass_t* pass) {
if (sym->type() != decomp::sym_type_t::instruction &&
sym->type() != decomp::sym_type_t::function)
pass->generic_pass(sym, sym_tbl);
});
});
m_recmp.allocate();
m_recmp.resolve();

Loading…
Cancel
Save