almost done writing docs. i need to add license to each header file and

create a doxygen...
master
_xeroxz 2 years ago
parent 8b7229ac75
commit 966d1e814c

@ -6,8 +6,19 @@
#include <vector>
namespace theo::decomp {
/// <summary>
/// meta symbol type. this is an abstraction upon the coff symbol storage/class
/// type.
/// </summary>
enum sym_type_t { function, instruction, data, section };
/// <summary>
/// symbol_t is an abstraction upon the coff symbol. this allows for easier
/// manipulation of the symbol. symbols can be different things, sections,
/// functions, and even instructions (when functions are broken down).
///
/// this class is used throughout theodosius and is a keystone of the project.
/// </summary>
class symbol_t {
public:
explicit symbol_t(coff::image_t* img,

@ -4,15 +4,38 @@
#include <vector>
namespace theo::obf {
/// <summary>
/// singleton obfuscation engine class. this class is responsible for keeping
/// track of the registered passes and the order in which to execute them.
/// </summary>
class engine_t {
explicit engine_t(){};
public:
/// <summary>
/// get the singleton object of this class.
/// </summary>
/// <returns>the singleton object of this class.</returns>
static engine_t* get();
/// <summary>
/// add a pass to the engine. the order in which you call this function
/// matters as the underlying data structure that contains the passes is a
/// vector.
/// </summary>
/// <param name="pass">a pointer to the pass in which to add to the
/// engine.</param>
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.
/// </summary>
/// <param name="sym">symbol to run all passes on.</param>
void run(decomp::symbol_t* sym);
private:
std::vector<pass_t*> passes;
};
} // namespace theo::comp::obf
} // namespace theo::obf

@ -9,13 +9,42 @@ extern "C" {
#include <xed-interface.h>
}
/// <summary>
/// this is the main namespace for obfuscation related things.
/// </summary>
namespace theo::obf {
/// <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.
///
/// in the constructor of your pass you must call the super constructor (the
/// pass_t constructor) and pass it the type of symbol which you are interesting
/// in receiving.
/// </summary>
class pass_t {
public:
/// <summary>
/// the explicit constructor of the pass_t base class.
/// </summary>
/// <param name="sym_type">the type of symbol in which the pass will run on.
/// every symbol passed to the virtual "run" instruction will be of this
/// type.</param>
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.
/// </summary>
/// <param name="sym">a symbol of the same type of m_sym_type.</param>
virtual void run(decomp::symbol_t* sym) = 0;
/// <summary>
/// gets the passes symbol type.
/// </summary>
/// <returns>the passes symbol type.</returns>
decomp::sym_type_t sym_type() { return m_sym_type; }
private:
decomp::sym_type_t m_sym_type;
};

@ -2,6 +2,32 @@
#include <obf/pass.hpp>
namespace theo::obf {
/// <summary>
/// jcc rewrite pass which rewrites rip relative jcc's so that they are position
/// independent.
///
/// given the following code:
///
/// jnz label1
/// ; other code goes here
/// label1:
/// ; more code here
///
/// the jnz instruction will be rewritten so that the following code is
/// generated:
///
/// jnz br2
/// br1:
/// jmp [rip] ; address after this instruction contains the address
/// ; of the instruction after the jcc.
/// br2:
/// jmp [rip] ; address after this instruction contains the address of where
/// ; branch 2 is located.
///
/// its important to note that other passes will encrypt (transform) the address
/// 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){};

@ -2,6 +2,56 @@
#include <obf/pass.hpp>
namespace theo::obf {
/// <summary>
/// This pass is used to generate transformations and jmp code to change RIP to
/// the next instruction.
///
/// given the following code (get pml4 address from cr3):
///
/// get_pml4:
/// 0: 48 c7 c0 ff 0f 00 00 mov rax,0xfff
/// 7: 48 f7 d0 not rax
/// a: 0f 20 da mov rdx,cr3
/// d: 48 21 c2 and rdx,rax
/// 10: b1 00 mov cl,0x0
/// 12: 48 d3 e2 shl rdx,cl
/// 15: 48 89 d0 mov rax,rdx
/// 18: c3 ret
///
/// this pass will break up each instruction so that it can be anywhere in a
/// linear virtual address space. this pass will not work on rip relative code,
/// however clang will not generate such code when compiled with
/// "-mcmodel=large"
///
/// get_pml4@0:
/// mov rax, 0xFFF
/// push [next_inst_addr_enc]
/// xor [rsp], 0x3243342
/// ; a random number of transformations here...
/// ret
/// next_inst_addr_enc:
/// ; encrypted address of the next instruction goes here.
///
/// get_pml4@7:
/// not rax
/// push [next_inst_addr_enc]
/// xor [rsp], 0x93983498
/// ; a random number of transformations here...
/// ret
/// next_inst_addr_enc:
/// ; encrypted address of the next instruction goes here.
///
/// this process is continued for each instruction in the function. the last
/// instruction "ret" will have no code generated for it as there is no next
/// instruction.
///
///
/// this pass also only runs at the instruction level, theodosius internally
/// breaks up functions inside of the ".split" section into individual
/// instruction symbols. this process also creates a psuedo relocation which
/// 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) {
xed_state_t istate{XED_MACHINE_MODE_LONG_64, XED_ADDRESS_WIDTH_64b};

@ -4,6 +4,25 @@
#include <obf/transform/transform.hpp>
namespace theo::obf {
/// <summary>
/// this pass is like the next_inst_pass, however, relocations are encrypted
/// with transformations instead of the address of the next instruction. this
/// pass only runs at the instruction level and appends transformations into the
/// reloc_t object of the instruction symbol.
///
/// given the following code:
///
/// mov rax, &MessageBoxA
///
/// this pass will generate a random number of transformations to encrypt the
/// address of "MessageBoxA". These transformations will then be applied by
/// theodosius internally when resolving relocations.
///
/// mov rax, enc_MessageBoxA
/// xor rax, 0x389284324
/// 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){};

@ -3,6 +3,15 @@
#include <recomp/reloc.hpp>
namespace theo::obf::transform {
/// <summary>
/// generate a sequence of transformations given an instruction that has a
/// relocation in it.
/// </summary>
/// <param name="inst">instruction that has a relocation in it.</param>
/// <param name="reloc">meta data relocation object for the instruction.</param>
/// <param name="low">lowest number of transformations to generate.</param>
/// <param name="high">highest number of transformations to generate.</param>
/// <returns></returns>
inline std::vector<std::uint8_t> generate(xed_decoded_inst_t* inst,
recomp::reloc_t* reloc,
std::uint8_t low,

@ -1,10 +1,10 @@
#pragma once
#include <spdlog/spdlog.h>
#include <bit>
#include <bitset>
#include <functional>
#include <map>
#include <random>
#include <bit>
#include <bitset>
#define XED_ENCODER
extern "C" {
@ -12,14 +12,50 @@ extern "C" {
#include <xed-interface.h>
}
/// <summary>
/// this namespace encompasses the code for transforming relocations.
/// </summary>
namespace theo::obf::transform {
/// <summary>
/// lambda function which takes in a 64bit value (relocation address) and a
/// 32bit value (random value used in transformation).
/// </summary>
using transform_t = std::function<std::size_t(std::size_t, std::uint32_t)>;
/// <summary>
/// operation_t is the base class for all types of transformations. classes that
/// inherit this class are singleton and simply call the super constructor
/// (operation_t::operation_t).
/// </summary>
class operation_t {
public:
/// <summary>
/// explicit constructor for operation_t
/// </summary>
/// <param name="op">lambda function when executed applies
/// transformations.</param> <param name="type">type of transformation, such
/// as XOR, ADD, SUB, etc...</param>
explicit operation_t(transform_t op, xed_iclass_enum_t type)
: m_transform(op), m_type(type) {}
/// <summary>
/// generates a native transform instruction given an existing instruction. it
/// works like so:
///
/// mov rax, &MessageBoxA ; original instruction with relocation
///
/// ; this function takes the first operand and out of the original
/// ; instruction and uses it to generate a transformation.
///
/// xor rax, 0x39280928 ; this would be an example output for the xor
/// ;operation.
///
/// </summary>
/// <param name="inst">instruction with a relocation to generate a
/// transformation for.</param> <param name="imm">random 32bit number used in
/// the generate transform.</param> <returns>returns the bytes of the native
/// instruction that was encoded.</returns>
std::vector<std::uint8_t> native(const xed_decoded_inst_t* inst,
std::uint32_t imm) {
std::uint32_t inst_len = {};
@ -53,10 +89,32 @@ class operation_t {
return std::vector<std::uint8_t>(inst_buff, inst_buff + inst_len);
}
/// <summary>
/// gets the inverse operation of the current operation.
/// </summary>
/// <returns>the inverse operation of the current operation.</returns>
xed_iclass_enum_t inverse() { return m_inverse_op[m_type]; }
/// <summary>
/// gets a pointer to the lambda function which contains the transform logic.
/// </summary>
/// <returns>a pointer to the lambda function which contains the transform
/// logic.</returns>
transform_t* get_transform() { return &m_transform; }
/// <summary>
/// gets the operation type. such as XED_ICLASS_ADD, XED_ICLASS_SUB, etc...
/// </summary>
/// <returns>the operation type. such as XED_ICLASS_ADD, XED_ICLASS_SUB,
/// etc...</returns>
xed_iclass_enum_t type() { return m_type; }
/// <summary>
/// generate a random number in a range.
/// </summary>
/// <param name="lowest">lowest value of the range.</param>
/// <param name="largest">highest value of the range.</param>
/// <returns>a random value in a range.</returns>
static std::size_t random(std::size_t lowest, std::size_t largest) {
std::random_device rd;
std::mt19937 gen(rd());

@ -6,6 +6,9 @@
#include <obf/transform/xor_op.hpp>
namespace theo::obf::transform {
/// <summary>
/// map of all of the operations and their type.
/// </summary>
inline std::map<xed_iclass_enum_t, operation_t*> operations = {
{XED_ICLASS_ADD, add_op_t::get()},
{XED_ICLASS_SUB, sub_op_t::get()},

@ -1,37 +1,97 @@
#pragma once
#include <decomp/decomp.hpp>
#include <obf/engine.hpp>
#include <recomp/symbol_table.hpp>
#include <decomp/decomp.hpp>
/// <summary>
/// this namespace encompasses all recomposition related code.
/// </summary>
namespace theo::recomp {
/// <summary>
/// a function which is called by recomp_t to resolve external symbols
/// </summary>
using resolver_t = std::function<std::uintptr_t(std::string)>;
/// <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)>;
/// <summary>
/// the main class responsible for recomposition
/// </summary>
class recomp_t {
public:
explicit recomp_t(decomp::decomp_t* dcmp);
/// <summary>
/// the explicit constructor for the recomp_t class.
/// </summary>
/// <param name="dcmp">pointer to a decomp_t class.</param>
/// <param name="alloc">lambda function which is used to allocate memory for
/// symbols.</param> <param name="copy">lambda function used to copy bytes
/// into allocations.</param> <param name="resolve">lambda function used to
/// resolve external symbols.</param>
explicit recomp_t(decomp::decomp_t* dcmp,
allocator_t alloc,
copier_t copy,
resolver_t resolve);
allocator_t alloc,
copier_t copy,
resolver_t resolve);
/// <summary>
/// when called, this function allocates space for every symbol.
/// </summary>
void allocate();
/// <summary>
/// when called, this function resolves all relocations in every symbol.
/// </summary>
void resolve();
/// <summary>
/// when called, this function copies symbols into allocations.
/// </summary>
void copy_syms();
/// <summary>
/// setter for the allocater lambda function.
/// </summary>
/// <param name="alloc">lambda function which allocates memory for
/// symbols.</param>
void allocator(allocator_t alloc);
/// <summary>
/// setter for the copier lambda function.
/// </summary>
/// <param name="copy">copier lambda function used to copy bytes into
/// allocations made by the allocator.</param>
void copier(copier_t copy);
/// <summary>
/// setter for the resolve lambda function.
/// </summary>
/// <param name="resolve">lambda function to resolve external symbols.</param>
void resolver(resolver_t resolve);
/// <summary>
/// resolves the address of a function given its name.
/// </summary>
/// <param name="sym">the name of the symbol to resolve the location
/// of.</param> <returns>the address of the symbol.</returns>
std::uintptr_t resolve(const std::string&& sym);
private:
void gen_reloc_trans(decomp::symbol_t* sym);
decomp::decomp_t* m_dcmp;
resolver_t m_resolver;
copier_t m_copier;
allocator_t m_allocator;
};
} // namespace theo::comp
} // namespace theo::recomp

@ -4,23 +4,69 @@
#include <obf/transform/transform.hpp>
namespace theo::recomp {
/// <summary>
/// meta data about a relocation for a symbol
/// </summary>
class reloc_t {
public:
/// <summary>
/// explicit constructor for this class.
/// </summary>
/// <param name="offset">offset into the symbol data where the relocation is
/// at. all relocations are assumed to be linear virtual addresses of the
/// symbol.</param>
/// <param name="hash">hash of the symbol to which the relocation is
/// of.</param> <param name="sym_name">the name of the symbol to which the
/// relocation is of.</param>
explicit reloc_t(std::uint32_t offset,
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.
/// </summary>
/// <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.
/// </summary>
/// <param name="entry">a pair containing a lambda function that when executed
/// transforms a relocation. the second value in the pair is a random value
/// which is passed to the lambda.</param>
void add_transform(
std::pair<obf::transform::transform_t*, std::uint32_t> entry) {
m_transforms.push_back(entry);
}
/// <summary>
/// gets the vector of transformation.
/// </summary>
/// <returns>returns the vector of transformations.</returns>
std::vector<std::pair<obf::transform::transform_t*, std::uint32_t>>&
get_transforms() {
return m_transforms;

@ -14,7 +14,6 @@ namespace theo::recomp {
/// </summary>
class symbol_table_t {
public:
/// <summary>
/// default constructor. does nothing.
/// </summary>
@ -33,27 +32,41 @@ class symbol_table_t {
void put_symbol(decomp::symbol_t& sym);
/// <summary>
/// add a vector of symbol to m_table
/// add a vector of symbol to m_table
/// </summary>
/// <param name="syms"></param>
void put_symbols(std::vector<decomp::symbol_t>& syms);
/// <summary>
/// returns an optional pointer to a symbol from the symbol table given the symbols hash (hash of its name)
/// the hash is produced by theo::decomp::symbol_t::hash
/// returns an optional pointer to a symbol from the symbol table given the
/// symbols hash (hash of its name) the hash is produced by
/// theo::decomp::symbol_t::hash
/// </summary>
/// <param name="hash">hashcode of the symbol to get from the symbol table...</param>
/// <returns>returns an optional pointer to a theo::decomp::symbol_t</returns>
/// <param name="hash">hashcode of the symbol to get from the symbol
/// table...</param> <returns>returns an optional pointer to a
/// theo::decomp::symbol_t</returns>
std::optional<decomp::symbol_t*> sym_from_hash(std::size_t hash);
/// <summary>
/// returns an optional pointer to a symbol given its allocation location.
/// </summary>
/// <param name="allocated_at">the address where the symbol is allocated at.</param>
/// <returns>returns an optional pointer to a theo::decomp::symbol_t</returns>
/// <param name="allocated_at">the address where the symbol is allocated
/// at.</param> <returns>returns an optional pointer to a
/// theo::decomp::symbol_t</returns>
std::optional<decomp::symbol_t*> sym_from_alloc(std::uintptr_t allocated_at);
/// <summary>
/// this function is a wrapper function that allows you to get at each entry
/// in the symbol table by reference.
/// </summary>
/// <param name="fn">a callback function that will be called for each
/// symbol</param>
void for_each(std::function<void(decomp::symbol_t& sym)> fn);
/// <summary>
/// returns the size of the symbol table.
/// </summary>
/// <returns>returns the size of the symbol table.</returns>
std::uint32_t size();
private:

@ -34,18 +34,18 @@ using lnk_fns_t =
/// the main class which encapsulates a symbol table, decomp, and recomp
/// objects. This class is a bridge that connects all three: decomp, obf,
/// recomp.
///
///
/// You will create an object of this type when using theo.
/// </summary>
class theo_t {
public:
/// <summary>
/// explicit constructor for theo class.
/// </summary>
/// <param name="lib">a vector of bytes consisting of a lib</param>
/// <param name="lnkr_fns"></param>
/// <param name="entry_sym">the name of the function which will be used as the entry point</param>
/// <param name="entry_sym">the name of the function which will be used as the
/// entry point</param>
explicit theo_t(std::vector<std::uint8_t>& lib,
lnk_fns_t lnkr_fns,
const std::string&& entry_sym);
@ -53,11 +53,13 @@ class theo_t {
/// <summary>
/// decomposes the lib file and return the number of symbols that are used.
/// </summary>
/// <returns>optional amount of symbols that are used. no value if decomposition fails.</returns>
/// <returns>optional amount of symbols that are used. no value if
/// decomposition fails.</returns>
std::optional<std::uint32_t> decompose();
/// <summary>
/// compose the decomposed module. This will run obfuscation passes, the map and resolve symbols to each other.
/// compose the decomposed module. This will run obfuscation passes, the map
/// and resolve symbols to each other.
/// </summary>
/// <returns>returns the address of the entry point symbol</returns>
std::uintptr_t compose();

@ -1,7 +1,6 @@
#include <recomp/recomp.hpp>
namespace theo::recomp {
recomp_t::recomp_t(decomp::decomp_t* dcmp) : m_dcmp(dcmp) {}
recomp_t::recomp_t(decomp::decomp_t* dcmp,
allocator_t alloc,
copier_t copy,

@ -4,7 +4,9 @@ namespace theo {
theo_t::theo_t(std::vector<std::uint8_t>& lib,
lnk_fns_t lnkr_fns,
const std::string&& entry_sym)
: m_dcmp(lib, &m_sym_tbl), m_recmp(&m_dcmp), m_entry_sym(entry_sym) {
: m_dcmp(lib, &m_sym_tbl),
m_recmp(&m_dcmp, {}, {}, {}),
m_entry_sym(entry_sym) {
m_recmp.allocator(std::get<0>(lnkr_fns));
m_recmp.copier(std::get<1>(lnkr_fns));
m_recmp.resolver(std::get<2>(lnkr_fns));

Loading…
Cancel
Save