feat: Finalized IAT Obfuscation Feature Implementation
- Completed the implementation of the IAT obfuscation logic. - Integrated AsmJit-based code emission for obfuscation. - Obfuscated the PEB and its offset entries to evade automated scanning. - Added a randomized algorithm to generate a unique key for each iteration. - Implemented runtime polymorphic IAT resolution to disrupt disassemblers and decompilers.
This commit is contained in:
@@ -186,22 +186,75 @@ void RyujinObfuscationCore::obfuscateIat() {
|
||||
const uintptr_t next_instruction_address = orInstr.addressofinstruction + orInstr.instruction.info.length;
|
||||
|
||||
// Calculating the target address of the IAT using the memory immediate from the original instruction
|
||||
const uint32_t iat_target_rva = (next_instruction_address + orInstr.instruction.operands->mem.disp.value) - m_ProcImageBase;
|
||||
uint32_t iat_target_rva = (next_instruction_address + orInstr.instruction.operands->mem.disp.value) - m_ProcImageBase;
|
||||
|
||||
/*
|
||||
Let's obfuscate our RVA
|
||||
*/
|
||||
// Generating two random bytes for the key
|
||||
std::mt19937 rng(std::random_device{}());
|
||||
// A single random value of 2 bytes (uint16_t)
|
||||
std::uniform_int_distribution<uint16_t> dist(0, 0xFFFF);
|
||||
uint16_t xorKey = dist(rng);
|
||||
|
||||
// Obfsucate the RVA with a XOR
|
||||
iat_target_rva ^= xorKey;
|
||||
|
||||
// Obfuscate PEB offset from automatic scan
|
||||
unsigned char PebGsOffset = 0x60 ^ (xorKey & 0xFF);
|
||||
unsigned char ImageBasePeb = 0x10 ^ (xorKey & 0xFF);
|
||||
|
||||
// A new vector to store our corrected IAT
|
||||
std::vector<ZyanU8> new_iat_call;
|
||||
|
||||
unsigned char pebRecoverModuleBase[24]{
|
||||
0x65, 0x48, 0x8B, 0x04, 0x25, 0x60, 0x00, 0x00, 0x00, // mov rax, gs:60h
|
||||
0x48, 0x8B, 0x40, 0x10, // mov rax, qword ptr ds:[rax+0x10]
|
||||
0x48, 0x05, 0x00, 0x00, 0x00, 0x00, // add rax, 22228h
|
||||
0x48, 0x8B, 0x00, //mov rax, qword ptr ds:[rax] -> Recover the IAT jump value stored in the data segment
|
||||
0xFF, 0xD0//call rax
|
||||
};
|
||||
//Begin ASMJIT configuration
|
||||
asmjit::JitRuntime runtime;
|
||||
asmjit::CodeHolder code;
|
||||
code.init(runtime.environment());
|
||||
asmjit::x86::Assembler a(&code);
|
||||
|
||||
std::memcpy(&*(pebRecoverModuleBase + 15), &iat_target_rva, sizeof(uint32_t));
|
||||
// Using `rdgsbase rax` to store the base address of the GS segment in RAX -> rdgsbase rax
|
||||
a.emit(asmjit::x86::Inst::kIdRdgsbase, asmjit::x86::rax);
|
||||
|
||||
new_iat_call.insert(new_iat_call.end(), std::begin(pebRecoverModuleBase), std::end(pebRecoverModuleBase));
|
||||
// Adding the obfuscated offset of the PEB in the GS segment -> add rax, PebGsOffset
|
||||
a.add(asmjit::x86::rax, PebGsOffset);
|
||||
|
||||
// Undoing the XOR operation with the obfuscated RAX value and the XOR key -> xor rax, lastByteXorKey
|
||||
a.xor_(asmjit::x86::rax, asmjit::imm(xorKey & 0xFF));
|
||||
|
||||
// Accessing the resulting address to retrieve the PEB instance -> mov rax, [rax]
|
||||
a.mov(asmjit::x86::rax, asmjit::x86::ptr(asmjit::x86::rax));
|
||||
|
||||
// Adding the obfuscated offset of the ImageBase field in the PEB -> add rax, ImageBasePeb
|
||||
a.add(asmjit::x86::rax, ImageBasePeb);
|
||||
|
||||
// Undoing the XOR operation with the obfuscated value and the XOR key -> xor rax, lastByteXorKey
|
||||
a.xor_(asmjit::x86::rax, asmjit::imm(xorKey & 0xFF));
|
||||
|
||||
// Accessing the resulting address to retrieve the PEB+ImageBase instance -> mov rax, [rax]
|
||||
a.mov(asmjit::x86::rax, asmjit::x86::ptr(asmjit::x86::rax));
|
||||
|
||||
// Adding the RVA that points to the entry in the IAT -> add rax, imm32 -> Adding the offset of the IAT entry
|
||||
a.add(asmjit::x86::rax, asmjit::imm(iat_target_rva));
|
||||
|
||||
// Undoing the XOR operation with the obfuscated value and the XOR key -> xor rax, xorKey
|
||||
a.xor_(asmjit::x86::rax, asmjit::imm(xorKey));
|
||||
|
||||
// mov rax, [rax] -> retrieving the resolved address for the IAT entry by the OS loader
|
||||
a.mov(asmjit::x86::rax, asmjit::x86::ptr(asmjit::x86::rax));
|
||||
|
||||
// call rax -> Calling the IAT
|
||||
a.call(asmjit::x86::rax);
|
||||
|
||||
// Obtaining the new section buffer
|
||||
auto& opcodeBuffer = code.sectionById(0)->buffer();
|
||||
// Obtaining the pointer to the buffer of raw opcode data generated
|
||||
const auto pOpcodeBuffer = opcodeBuffer.data();
|
||||
// Reserving space in the IAT vector
|
||||
new_iat_call.reserve(opcodeBuffer.size());
|
||||
|
||||
// Storing each opcode individually in the vector for our new IAT call
|
||||
for (auto i = 0; i < opcodeBuffer.size(); ++i) new_iat_call.push_back(static_cast<ZyanU8>(pOpcodeBuffer[i]));
|
||||
|
||||
// Replacing opcodes of the call in question with the new ones
|
||||
data.assign(new_iat_call.begin(), new_iat_call.end());
|
||||
@@ -245,9 +298,8 @@ BOOL RyujinObfuscationCore::Run() {
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
if (config.m_isVirtualized) todoAction();
|
||||
if (config.m_isJunkCode) todoAction();
|
||||
if (config.m_isVirtualized) todoAction();
|
||||
*/
|
||||
|
||||
return TRUE;
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <cstdint>
|
||||
#include <random>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
#include <asmjit/asmjit.h>
|
||||
#include <Zydis/Zydis.h>
|
||||
|
||||
Reference in New Issue
Block a user