diff --git a/ObfuGuard/ObfuGuard.vcxproj b/ObfuGuard/ObfuGuard.vcxproj index 99631ee..1718ca8 100644 --- a/ObfuGuard/ObfuGuard.vcxproj +++ b/ObfuGuard/ObfuGuard.vcxproj @@ -164,6 +164,7 @@ + diff --git a/ObfuGuard/ObfuGuard.vcxproj.filters b/ObfuGuard/ObfuGuard.vcxproj.filters index a26b56b..211a3a4 100644 --- a/ObfuGuard/ObfuGuard.vcxproj.filters +++ b/ObfuGuard/ObfuGuard.vcxproj.filters @@ -74,6 +74,9 @@ + + Header Files + Header Files\pe diff --git a/ObfuGuard/cfflattening/cfflattening.cpp b/ObfuGuard/cfflattening/cfflattening.cpp index 0a9767f..8202697 100644 --- a/ObfuGuard/cfflattening/cfflattening.cpp +++ b/ObfuGuard/cfflattening/cfflattening.cpp @@ -111,7 +111,7 @@ bool obfuscatecff::apply_control_flow_flattening(std::vectorfunc_id, { 0x50 }); push_rax.inst_id = first_inst_id; - push_rax.is_first_instruction = false; + push_rax.is_first_instruction = true; auto it = func->instructions.insert(func->instructions.begin(), push_rax); instruction_t push_f{}; push_f.load(func->func_id, { 0x66, 0x9C }); it = func->instructions.insert(it + 1, push_f); @@ -196,6 +196,8 @@ bool obfuscatecff::apply_control_flow_flattening(std::vectorinstructions.end() - 1)->inst_id; }); + if (last_inst == func->instructions.end()) continue; + // Find the next block in the execution chain auto next_block_iter = std::find_if(blocks.begin(), blocks.end(), [&](const basic_block& blk) { return blk.block_id == block_iter->next_block; }); diff --git a/ObfuGuard/constants.h b/ObfuGuard/constants.h new file mode 100644 index 0000000..a504c40 --- /dev/null +++ b/ObfuGuard/constants.h @@ -0,0 +1,35 @@ +#pragma once +#include + +namespace ObfuGuard { + + // PE format constants + constexpr uint32_t PE_FILE_ALIGNMENT = 0x200; + constexpr uint32_t PE_SECTION_ALIGNMENT = 0x1000; + constexpr uint32_t PE_MAX_SECTIONS = 96; + constexpr uint32_t PE_SECTION_SAFETY_MARGIN = 10; + constexpr uint32_t PE_RESERVED_SYSTEM_SECTIONS = 5; + constexpr uint32_t PE_HEADER_SIZE = 0x1000; + constexpr uint32_t MAX_PE_IMAGE_SIZE = 512 * 1024 * 1024; + + // CFF obfuscation constants + constexpr uint32_t CFF_SECTION_SIZE = 10'000'000; + constexpr const char* CFF_SECTION_NAME = ".0Cff"; + constexpr const char* CFF_DEV_SECTION_NAME = ".0Dev"; + + // Junk code injection constants + constexpr uint32_t MAX_JUNK_ITERATIONS = 500; + constexpr uint32_t MIN_TRAMPOLINE_PATCH_SIZE = 5; + constexpr uint32_t MAX_TRAMPOLINE_PATCH_SIZE = 0x1000; + constexpr uint32_t MAX_FUNC_SCAN_SIZE = 8192; + constexpr uint32_t DEFAULT_SECTION_SIZE = 0x1000; + constexpr uint32_t LARGE_BINARY_SIZE_THRESHOLD = 350 * 1024; + constexpr uint32_t MIN_FUNCTION_SIZE = 5; + + // PDB constants + constexpr uint64_t SYM_LOAD_BASE_ADDRESS = 0x10000000; + + // Instruction format buffer + constexpr size_t INSTRUCTION_FORMAT_BUFFER_SIZE = 256; + +} diff --git a/ObfuGuard/func2rva/func2rva.cpp b/ObfuGuard/func2rva/func2rva.cpp index b8db5f4..cb0dac1 100644 --- a/ObfuGuard/func2rva/func2rva.cpp +++ b/ObfuGuard/func2rva/func2rva.cpp @@ -92,25 +92,13 @@ namespace FuncToRVA { // Namespace for resolving functions to RVAs // Return list of resolved functions. If not initialized, return empty list and report error. const std::vector& RVAResolver::get_functions_info() const { if (!is_initialized_) { - static std::vector empty_list; - std::cerr << "Error: RVAResolver has not been initialized. Please call initialize() first." << std::endl; - return empty_list; + throw std::runtime_error("RVAResolver has not been initialized. Please call initialize() first."); } return resolved_functions_list_; } - // Display list of functions, let user select one function to get RVA - bool RVAResolver::select_function_rva_interactive(uint32_t& out_rva) { - if (!is_initialized_) { - std::cerr << "Error: RVAResolver has not been initialized. Please call initialize() first." << std::endl; - return false; - } - - if (resolved_functions_list_.empty()) { - std::cout << "Info: No functions from PDB available to select." << std::endl; - return false; - } - + // Display the function table (header, columns, and all rows) + void RVAResolver::display_function_table() const { std::cout << "\nAvailable functions from PDB for file: " << pe_path_str_ << std::endl; std::cout << "PE ImageBase: 0x" << std::hex << image_base_ << std::dec << std::endl; if (has_text_section_for_reference_) { @@ -140,6 +128,21 @@ namespace FuncToRVA { // Namespace for resolving functions to RVAs << func_info.name << std::endl; } std::cout << "----------------------------------------------------------------------------------------------------" << std::endl; + } + + // Display list of functions, let user select one function to get RVA + bool RVAResolver::select_function_rva_interactive(uint32_t& out_rva) { + if (!is_initialized_) { + std::cerr << "Error: RVAResolver has not been initialized. Please call initialize() first." << std::endl; + return false; + } + + if (resolved_functions_list_.empty()) { + std::cout << "Info: No functions from PDB available to select." << std::endl; + return false; + } + + display_function_table(); int choice = 0; while (true) { @@ -196,35 +199,7 @@ namespace FuncToRVA { // Namespace for resolving functions to RVAs return false; } - std::cout << "\nAvailable functions from PDB for file: " << pe_path_str_ << std::endl; - std::cout << "PE ImageBase: 0x" << std::hex << image_base_ << std::dec << std::endl; - if (has_text_section_for_reference_) { - std::cout << "Using PDB offsets relative to '.text' section (RVA: 0x" - << std::hex << text_section_rva_ << std::dec << ")" << std::endl; - } - else { - std::cout << "Warning: '.text' section not found. Displayed RVAs may be PDB offsets or 0 if unable to compute." << std::endl; - } - std::cout << "----------------------------------------------------------------------------------------------------" << std::endl; - std::cout << std::setw(7) << "No." << " | " - << std::setw(12) << "RVA (Hex)" << " | " - << std::setw(12) << "Offset (Hex)" << " | " - << std::setw(10) << "Size" << " | " - << "Function Name" << std::endl; - std::cout << "----------------------------------------------------------------------------------------------------" << std::endl; - - for (size_t i = 0; i < resolved_functions_list_.size(); ++i) { - const auto& func_info = resolved_functions_list_[i]; - std::cout << std::setw(7) << std::left << i + 1 << " | " - << "0x" << std::hex << std::setw(10) << std::left << func_info.rva - << " | " - << "0x" << std::hex << std::setw(10) << std::left << func_info.pdb_offset - << " | " - << std::dec << std::setw(10) << std::left << func_info.size - << " | " - << func_info.name << std::endl; - } - std::cout << "----------------------------------------------------------------------------------------------------" << std::endl; + display_function_table(); std::string input_str; while (true) { diff --git a/ObfuGuard/func2rva/func2rva.h b/ObfuGuard/func2rva/func2rva.h index 8ecf174..eaa96c9 100644 --- a/ObfuGuard/func2rva/func2rva.h +++ b/ObfuGuard/func2rva/func2rva.h @@ -47,6 +47,7 @@ namespace FuncToRVA { bool has_text_section_for_reference_ = false; // Flag indicating if .text section exists bool load_pe_and_parse_pdb(); + void display_function_table() const; }; // Display function list from PE file and allow user to select one function, returns RVA of that function. diff --git a/ObfuGuard/junkcode/junkcode.cpp b/ObfuGuard/junkcode/junkcode.cpp index 7887b4b..d6f8ed8 100644 --- a/ObfuGuard/junkcode/junkcode.cpp +++ b/ObfuGuard/junkcode/junkcode.cpp @@ -212,16 +212,18 @@ bool TrampolineInjector::get_and_relocate_original_function_code( << ", New rel offset: 0x" << new_relative_offset << std::dec << std::endl;*/ } else { - std::cout << ""; - /*std::cerr << "Error: Cannot relocate " << (insn[0].bytes[0] == 0xE8 ? "CALL" : "JMP") + std::cerr << "Warning: Cannot relocate " << (insn[0].bytes[0] == 0xE8 ? "CALL" : "JMP") << " at VA 0x" << std::hex << old_instr_va - << " - target too far (offset: 0x" << new_relative_offset_64 << ")" << std::dec << std::endl;*/ + << " - relative offset 0x" << new_relative_offset_64 + << " exceeds INT32 range. Skipping relocation for this instruction." + << std::dec << std::endl; + // Skip relocation: keep original bytes to avoid crash from wrong offset + std::vector original_instr_bytes(insn[0].bytes, insn[0].bytes + insn[0].size); + instr_bytes = original_instr_bytes; } } else if (insn[0].bytes[0] == 0xFF) { - std::cout << ""; - /*std::cout << "Warning: Indirect CALL/JMP at VA 0x" << std::hex << insn[0].address - << " - may need manual verification" << std::dec << std::endl;*/ + // Indirect CALL/JMP -- no relocation needed } } else if (op->type == X86_OP_MEM && is_64_bit) { @@ -324,114 +326,109 @@ bool TrampolineInjector::create_new_section(const std::string& section_name, uin // Create ASM instruction sequence that doesn't affect logic (junk) to insert into code std::string TrampolineInjector::get_random_junk_instruction() { - std::vector junk_instructions; - - if (is_64_bit) { - junk_instructions = { - // Basic no-op equivalents - "mov rax, rax", - "mov rbx, rbx", - "mov rcx, rcx", - "mov rdx, rdx", - - // Self-canceling operation pairs - "add r8, 0x10; sub r8, 0x10", - "add r9, 0x20; sub r9, 0x20", - "add r10, 0x30; sub r10, 0x30", - "add r11, 0x40; sub r11, 0x40", - "add r12, 0x50; sub r12, 0x50", - "add r13, 0x60; sub r13, 0x60", - "add r14, 0x70; sub r14, 0x70", - "add r15, 0x80; sub r15, 0x80", - - // Reversed - "sub r8, 0x15; add r8, 0x15", - "sub r9, 0x25; add r9, 0x25", - "sub r10, 0x35; add r10, 0x35", - "sub r11, 0x45; add r11, 0x45", - - // More complex math sequences - "add r8, 0x100; sub r8, 0x80; sub r8, 0x80", - "sub r9, 0x200; add r9, 0x100; add r9, 0x100", - "add r10, 0x50; add r10, 0x50; sub r10, 0xA0", - - // Simple XOR patterns - "xor r8, 0x1234; xor r8, 0x1234", - "xor r9, 0x5678; xor r9, 0x5678", - "xor r10, 0x9ABC; xor r10, 0x9ABC", - "xor r11, 0xDEF0; xor r11, 0xDEF0", - - // Bit rotation pairs (safe — rotate is fully reversible) - "rol r8, 3; ror r8, 3", - "ror r9, 5; rol r9, 5", - "rol r10, 7; ror r10, 7", - "ror r11, 4; rol r11, 4", - - // Cross-register stack - "push r8; push r9; pop r9; pop r8", - - // Bitwise operations that don't change value - "or r8, 0", - "and r8, -1", - "or r9, 0", - "and r9, -1", - "or r10, 0", - "and r10, -1", - - // rol/ror operations that don't change value - "rol r8, 1; ror r8, 1", - "rol r9, 2; ror r9, 2", - "ror r10, 3; rol r10, 3", - "ror r11, 4; rol r11, 4", - - // INC/DEC pairs - "inc r8; dec r8", - "inc r9; dec r9", - "dec r10; inc r10", - "dec r11; inc r11", - - // Multiple operations that don't change value - "push r8; pop r8; push r9; pop r9", - "add r8, 1; add r8, 1; sub r8, 2", - "sub r9, 5; add r9, 3; add r9, 2", - - }; - } - else { - junk_instructions = { - "mov eax, eax", - "mov ebx, ebx", - "mov ecx, ecx", - "mov edx, edx", - "mov esi, esi", - "mov edi, edi", - - "add esi, 0x10; sub esi, 0x10", - "add edi, 0x20; sub edi, 0x20", - "sub esi, 0x15; add esi, 0x15", - "sub edi, 0x25; add edi, 0x25", - - "xor esi, 0x1234; xor esi, 0x1234", - "xor edi, 0x5678; xor edi, 0x5678", - - "push esi; pop esi", - "push edi; pop edi", - "push eax; push ebx; pop ebx; pop eax", - - "test esi, esi", - "test edi, edi", - "cmp esi, esi", - "cmp edi, edi", - "lea esi, [esi]", - "lea edi, [edi]", - "inc esi; dec esi", - "inc edi; dec edi", - "rol esi, 1; ror esi, 1", - "rol edi, 2; ror edi, 2" - }; - } - - return junk_instructions[rand() % junk_instructions.size()]; + static const std::vector junk_64bit = { + // Basic no-op equivalents + "mov rax, rax", + "mov rbx, rbx", + "mov rcx, rcx", + "mov rdx, rdx", + + // Self-canceling operation pairs + "add r8, 0x10; sub r8, 0x10", + "add r9, 0x20; sub r9, 0x20", + "add r10, 0x30; sub r10, 0x30", + "add r11, 0x40; sub r11, 0x40", + "add r12, 0x50; sub r12, 0x50", + "add r13, 0x60; sub r13, 0x60", + "add r14, 0x70; sub r14, 0x70", + "add r15, 0x80; sub r15, 0x80", + + // Reversed + "sub r8, 0x15; add r8, 0x15", + "sub r9, 0x25; add r9, 0x25", + "sub r10, 0x35; add r10, 0x35", + "sub r11, 0x45; add r11, 0x45", + + // More complex math sequences + "add r8, 0x100; sub r8, 0x80; sub r8, 0x80", + "sub r9, 0x200; add r9, 0x100; add r9, 0x100", + "add r10, 0x50; add r10, 0x50; sub r10, 0xA0", + + // Simple XOR patterns + "xor r8, 0x1234; xor r8, 0x1234", + "xor r9, 0x5678; xor r9, 0x5678", + "xor r10, 0x9ABC; xor r10, 0x9ABC", + "xor r11, 0xDEF0; xor r11, 0xDEF0", + + // Bit rotation pairs (safe -- rotate is fully reversible) + "rol r8, 3; ror r8, 3", + "ror r9, 5; rol r9, 5", + "rol r10, 7; ror r10, 7", + "ror r11, 4; rol r11, 4", + + // Cross-register stack + "push r8; push r9; pop r9; pop r8", + + // Bitwise operations that don't change value + "or r8, 0", + "and r8, -1", + "or r9, 0", + "and r9, -1", + "or r10, 0", + "and r10, -1", + + // rol/ror operations that don't change value + "rol r8, 1; ror r8, 1", + "rol r9, 2; ror r9, 2", + "ror r10, 3; rol r10, 3", + "ror r11, 4; rol r11, 4", + + // INC/DEC pairs + "inc r8; dec r8", + "inc r9; dec r9", + "dec r10; inc r10", + "dec r11; inc r11", + + // Multiple operations that don't change value + "push r8; pop r8; push r9; pop r9", + "add r8, 1; add r8, 1; sub r8, 2", + "sub r9, 5; add r9, 3; add r9, 2", + }; + + static const std::vector junk_32bit = { + "mov eax, eax", + "mov ebx, ebx", + "mov ecx, ecx", + "mov edx, edx", + "mov esi, esi", + "mov edi, edi", + + "add esi, 0x10; sub esi, 0x10", + "add edi, 0x20; sub edi, 0x20", + "sub esi, 0x15; add esi, 0x15", + "sub edi, 0x25; add edi, 0x25", + + "xor esi, 0x1234; xor esi, 0x1234", + "xor edi, 0x5678; xor edi, 0x5678", + + "push esi; pop esi", + "push edi; pop edi", + "push eax; push ebx; pop ebx; pop eax", + + "test esi, esi", + "test edi, edi", + "cmp esi, esi", + "cmp edi, edi", + "lea esi, [esi]", + "lea edi, [edi]", + "inc esi; dec esi", + "inc edi; dec edi", + "rol esi, 1; ror esi, 1", + "rol edi, 2; ror edi, 2" + }; + + const auto& pool = is_64_bit ? junk_64bit : junk_32bit; + return pool[rand() % pool.size()]; } // Fill remaining memory space with NOPs (no-operation) @@ -503,6 +500,65 @@ void TrampolineInjector::fill_remaining_space_with_nops(uint64_t address, size_t } } +// Patch a region with random junk instructions, returns bytes written +size_t TrampolineInjector::patch_junk_region(ks_engine* ks, uint64_t start_address, size_t region_size, const LIEF::PE::Section& section) { + const size_t MAX_JUNK_ITERATIONS = 500; + uint64_t current_address = start_address; + size_t remaining = region_size; + size_t iteration_count = 0; + + uint64_t section_start_va = image_base + section.virtual_address(); + uint64_t section_end_va = section_start_va + section.virtual_size(); + + while (remaining > 0 && iteration_count < MAX_JUNK_ITERATIONS) { + iteration_count++; + + std::string junk_asm = get_random_junk_instruction(); + unsigned char* junk_encode = nullptr; + size_t junk_asm_size = 0; + size_t junk_count = 0; + + if (ks_asm(ks, junk_asm.c_str(), current_address, &junk_encode, &junk_asm_size, &junk_count) == KS_ERR_OK && junk_count > 0) { + if (junk_asm_size <= remaining) { + // Check bounds before patching + uint64_t patch_end = current_address + junk_asm_size; + + if (patch_end > section_end_va) { + std::cerr << "Error: Junk patch would exceed section bounds. Stopping." << std::endl; + ks_free(junk_encode); + break; + } + + std::vector junk_bytes(junk_encode, junk_encode + junk_asm_size); + binary->patch_address(current_address, junk_bytes); + + current_address += junk_asm_size; + remaining -= junk_asm_size; + ks_free(junk_encode); + } + else { + ks_free(junk_encode); + fill_remaining_space_with_nops(current_address, remaining); + current_address += remaining; + remaining = 0; + } + } + else { + fill_remaining_space_with_nops(current_address, remaining); + current_address += remaining; + remaining = 0; + } + } + + if (iteration_count >= MAX_JUNK_ITERATIONS && remaining > 0) { + std::cerr << "Warning: Maximum junk iterations reached. Filling remaining space with NOPs." << std::endl; + fill_remaining_space_with_nops(current_address, remaining); + current_address += remaining; + } + + return current_address - start_address; +} + // Create a JMP from original address to relocated code, insert junk to hide bool TrampolineInjector::create_trampoline(uint64_t original_func_va, uint64_t new_func_va, size_t original_size) { ks_engine* ks; @@ -622,65 +678,8 @@ bool TrampolineInjector::create_trampoline(uint64_t original_func_va, uint64_t n uint64_t current_address = original_func_va; - // Patch junk code before JMP instruction with iteration limit - size_t remaining_before = junk_before_size; - size_t junk_iteration_count = 0; - const size_t MAX_JUNK_ITERATIONS = 500; // Limit iterations to prevent infinite loops - - /*std::cout << "Phase 1: Adding " << remaining_before << " bytes of junk before JMP..." << std::endl;*/ - - while (remaining_before > 0 && junk_iteration_count < MAX_JUNK_ITERATIONS) { - junk_iteration_count++; - - std::string junk_asm = get_random_junk_instruction(); - unsigned char* junk_encode = nullptr; - size_t junk_asm_size = 0; - size_t junk_count = 0; - - if (ks_asm(ks, junk_asm.c_str(), current_address, &junk_encode, &junk_asm_size, &junk_count) == KS_ERR_OK && junk_count > 0) { - if (junk_asm_size <= remaining_before) { - // CHECK BOUNDS BEFORE PATCHING - uint64_t patch_end = current_address + junk_asm_size; - uint64_t section_start_va = image_base + original_section->virtual_address(); - uint64_t section_end_va = section_start_va + original_section->virtual_size(); - - if (patch_end > section_end_va) { - std::cerr << "Error: Junk patch would exceed section bounds. Stopping." << std::endl; - ks_free(junk_encode); - break; - } - - std::vector junk_bytes(junk_encode, junk_encode + junk_asm_size); - binary->patch_address(current_address, junk_bytes); - - /*std::cout << " Added junk: " << junk_asm - << " (" << junk_asm_size << " bytes) at VA 0x" - << std::hex << current_address << std::dec << std::endl;*/ - - current_address += junk_asm_size; - remaining_before -= junk_asm_size; - ks_free(junk_encode); - } - else { - ks_free(junk_encode); - fill_remaining_space_with_nops(current_address, remaining_before); - current_address += remaining_before; - remaining_before = 0; - } - } - else { - fill_remaining_space_with_nops(current_address, remaining_before); - current_address += remaining_before; - remaining_before = 0; - } - } - - // Check if iteration limit is exceeded - if (junk_iteration_count >= MAX_JUNK_ITERATIONS) { - std::cerr << "Warning: Maximum junk iterations reached. Filling remaining space with NOPs." << std::endl; - fill_remaining_space_with_nops(current_address, remaining_before); - current_address += remaining_before; - } + // Patch junk code before JMP instruction + current_address += patch_junk_region(ks, current_address, junk_before_size, *original_section); // Patch JMP instruction at current address /*std::cout << "Phase 2: Patching JMP at VA 0x" << std::hex << current_address @@ -700,64 +699,8 @@ bool TrampolineInjector::create_trampoline(uint64_t original_func_va, uint64_t n binary->patch_address(current_address, jmp_bytes); current_address += jmp_size; - // Patch junk code after JMP instruction with iteration limit - size_t remaining_after = junk_after_size; - junk_iteration_count = 0; // Reset counter - - /*std::cout << "Phase 3: Adding " << remaining_after << " bytes of junk after JMP..." << std::endl;*/ - - // If no junk after JMP instruction, fill with NOPs - while (remaining_after > 0 && junk_iteration_count < MAX_JUNK_ITERATIONS) { - junk_iteration_count++; - - std::string junk_asm = get_random_junk_instruction(); - unsigned char* junk_encode = nullptr; - size_t junk_asm_size = 0; - size_t junk_count = 0; - - if (ks_asm(ks, junk_asm.c_str(), current_address, &junk_encode, &junk_asm_size, &junk_count) == KS_ERR_OK && junk_count > 0) { - if (junk_asm_size <= remaining_after) { - // CHECK BOUNDS BEFORE PATCHING - uint64_t patch_end = current_address + junk_asm_size; - uint64_t section_start_va = image_base + original_section->virtual_address(); - uint64_t section_end_va = section_start_va + original_section->virtual_size(); - - if (patch_end > section_end_va) { - std::cerr << "Error: Final junk patch would exceed section bounds. Stopping." << std::endl; - ks_free(junk_encode); - break; - } - - std::vector junk_bytes(junk_encode, junk_encode + junk_asm_size); - binary->patch_address(current_address, junk_bytes); - - /*std::cout << " Added junk: " << junk_asm - << " (" << junk_asm_size << " bytes) at VA 0x" - << std::hex << current_address << std::dec << std::endl;*/ - - current_address += junk_asm_size; - remaining_after -= junk_asm_size; - ks_free(junk_encode); - } - else { - ks_free(junk_encode); - fill_remaining_space_with_nops(current_address, remaining_after); - remaining_after = 0; - } - } - else { - fill_remaining_space_with_nops(current_address, remaining_after); - remaining_after = 0; - } - } - - // Check if iteration limit is exceeded - if (junk_iteration_count >= MAX_JUNK_ITERATIONS) { - std::cerr << "Warning: Maximum final junk iterations reached. Filling remaining space with NOPs." << std::endl; - fill_remaining_space_with_nops(current_address, remaining_after); - } - - /*std::cout << "Completed advanced trampoline with embedded JMP" << std::endl;*/ + // Patch junk code after JMP instruction + patch_junk_region(ks, current_address, junk_after_size, *original_section); ks_close(ks); return true; } diff --git a/ObfuGuard/junkcode/junkcode.h b/ObfuGuard/junkcode/junkcode.h index 76e54a6..969cda9 100644 --- a/ObfuGuard/junkcode/junkcode.h +++ b/ObfuGuard/junkcode/junkcode.h @@ -9,6 +9,8 @@ #include #include +#include + // Forward declaration namespace LIEF { namespace PE { @@ -43,6 +45,7 @@ class TrampolineInjector { std::string get_random_junk_instruction(); void fill_remaining_space_with_nops(uint64_t address, size_t size); + size_t patch_junk_region(ks_engine* ks, uint64_t start_address, size_t region_size, const LIEF::PE::Section& section); std::string generate_unique_section_name(const std::string& function_name, int index); public: diff --git a/ObfuGuard/obfuscatecff/obfuscatecff.cpp b/ObfuGuard/obfuscatecff/obfuscatecff.cpp index 9b718a1..a5608ea 100644 --- a/ObfuGuard/obfuscatecff/obfuscatecff.cpp +++ b/ObfuGuard/obfuscatecff/obfuscatecff.cpp @@ -6,10 +6,7 @@ #include #include -#define REG_PAIR(zreg, areg) { ZYDIS_REGISTER_##zreg, x86::areg } - -ZydisFormatter formatter; -ZydisDecoder decoder; +#define REG_PAIR(zreg, areg) { ZYDIS_REGISTER_##zreg, asmjit::x86::areg } int obfuscatecff::instruction_id = 0; int obfuscatecff::function_iterator = 0; @@ -35,21 +32,19 @@ obfuscatecff::obfuscatecff(pe64* pe) throw std::runtime_error("failed to init formatter"); } -// Initialize function list from pdbparser with function_t structure -void obfuscatecff::create_functions(std::vectorfunctions) { +void obfuscatecff::create_functions(const std::vector& functions) { - auto text_section = this->pe->get_section(".text"); // find .text section in PE file + auto text_section = this->pe->get_section(".text"); if (!text_section) throw std::runtime_error("couldn't find .text section"); - std::vectorvisited_rvas; // list of analyzed RVAs + std::unordered_set visited_rvas; - // initialize functions in PE file from pdbparser - for (auto function : functions) { - if (function.obfuscate == false) // skip function if not marked for obfuscation + for (const auto& function : functions) { + if (!function.obfuscate) continue; - if (std::find(visited_rvas.begin(), visited_rvas.end(), function.offset) != visited_rvas.end()) // skip if RVA already analyzed + if (visited_rvas.count(function.offset)) continue; if (function.size < 5) // skip if function size is too small continue; @@ -63,7 +58,7 @@ void obfuscatecff::create_functions(std::vectorfunctions) { int current_func_id = function_iterator++; function_t new_function(current_func_id, function.name, function.offset, function.size); - new_function.ctfflattening = function.ctfflattening; + new_function.cff_flattening = function.cff_flattening; std::vector runtime_addresses; @@ -84,7 +79,7 @@ void obfuscatecff::create_functions(std::vectorfunctions) { new_function.inst_id_index[new_instruction.inst_id] = inst_index; } - visited_rvas.push_back(function.offset); // mark RVA as analyzed + visited_rvas.insert(function.offset); this->functions.push_back(new_function); // add new function to the function list for (auto runtime_address = runtime_addresses.begin(); runtime_address != runtime_addresses.end(); ++runtime_address) { @@ -548,7 +543,7 @@ void obfuscatecff::run(PIMAGE_SECTION_HEADER new_section, bool obfuscate_entry_p if (func->has_jumptables) continue; - if (func->ctfflattening) + if (func->cff_flattening) this->apply_control_flow_flattening(func); } @@ -564,7 +559,7 @@ void obfuscatecff::run(PIMAGE_SECTION_HEADER new_section, bool obfuscate_entry_p } -uint32_t obfuscatecff::get_added_size() { // total memory obfuscated +uint32_t obfuscatecff::get_added_size() const { return this->total_size_used; } diff --git a/ObfuGuard/obfuscatecff/obfuscatecff.h b/ObfuGuard/obfuscatecff/obfuscatecff.h index b09ca33..3e8741f 100644 --- a/ObfuGuard/obfuscatecff/obfuscatecff.h +++ b/ObfuGuard/obfuscatecff/obfuscatecff.h @@ -7,14 +7,11 @@ #include #include #include -using namespace asmjit; - -class obfuscatecff { // Class that performs control flow flattening obfuscation +class obfuscatecff { private: - struct instruction_t; // Structure describing a machine instruction - struct function_t; // Structure describing a function - pe64* pe; + ZydisFormatter formatter; + ZydisDecoder decoder; struct func_id_instr_id { int func_id; int inst_index; @@ -25,12 +22,12 @@ class obfuscatecff { // Class that performs control flow flattening obfuscation static int instruction_id; static int function_iterator; - static std::unordered_map lookupmap; + static std::unordered_map lookupmap; - // generate code using asmjit - JitRuntime rt; - CodeHolder code; - x86::Assembler assm; + // generate code using asmjit + asmjit::JitRuntime rt; + asmjit::CodeHolder code; + asmjit::x86::Assembler assm; std::vector functions; @@ -65,11 +62,11 @@ class obfuscatecff { // Class that performs control flow flattening obfuscation public: obfuscatecff(pe64* pe); - void create_functions(std::vector functions); + void create_functions(const std::vector& functions); void run(PIMAGE_SECTION_HEADER new_section, bool obfuscate_entry_point); - uint32_t get_added_size(); + uint32_t get_added_size() const; struct instruction_t { int inst_id; @@ -119,11 +116,11 @@ class obfuscatecff { // Class that performs control flow flattening obfuscation uint32_t offset; uint32_t size; - function_t(int func_id, std::string name, uint32_t offset, uint32_t size) + function_t(int func_id, const std::string& name, uint32_t offset, uint32_t size) : func_id(func_id), name(name), offset(offset), size(size) { }; - bool ctfflattening = true; + bool cff_flattening = true; bool has_jumptables = false; }; }; \ No newline at end of file diff --git a/ObfuGuard/pdbparser/pdbparser.h b/ObfuGuard/pdbparser/pdbparser.h index 5771bff..8c73662 100644 --- a/ObfuGuard/pdbparser/pdbparser.h +++ b/ObfuGuard/pdbparser/pdbparser.h @@ -27,7 +27,7 @@ class pdbparser { std::string name; uint32_t size = 0; bool obfuscate = true; - bool ctfflattening = true; + bool cff_flattening = true; }; pdbparser(pe64* pe);