feat: Initial implementation of IAT obfuscation feature
- Initial IAT obfuscation feature, Ryujin can now obfuscate the IAT for the configured procedures. - New basic block context generation. - Bug fixes. - Improved obfuscation logic for better organization.
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
#pragma once;
|
#pragma once
|
||||||
|
|
||||||
struct RyujinInstruction {
|
struct RyujinInstruction {
|
||||||
|
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ bool Ryujin::run(const RyujinObfuscatorConfig& config) {
|
|||||||
proc.basic_blocks = rybb.createBasicBlocks(ucOpcodes, proc.size, proc.address);
|
proc.basic_blocks = rybb.createBasicBlocks(ucOpcodes, proc.size, proc.address);
|
||||||
|
|
||||||
//Is time to obfuscate ?
|
//Is time to obfuscate ?
|
||||||
RyujinObfuscationCore obc(config, proc);
|
RyujinObfuscationCore obc(config, proc, reinterpret_cast<uintptr_t>(m_mappedPE.get()));
|
||||||
obc.Run();
|
obc.Run();
|
||||||
|
|
||||||
//TODO: Custom passes support
|
//TODO: Custom passes support
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
#include "RyujinObfuscationCore.hh"
|
#include "RyujinObfuscationCore.hh"
|
||||||
|
|
||||||
RyujinObfuscationCore::RyujinObfuscationCore(const RyujinObfuscatorConfig& config, const RyujinProcedure& proc) {
|
RyujinObfuscationCore::RyujinObfuscationCore(const RyujinObfuscatorConfig& config, const RyujinProcedure& proc, uintptr_t ProcImageBase) {
|
||||||
|
|
||||||
m_proc = proc;
|
m_proc = proc;
|
||||||
|
m_config = config;
|
||||||
|
m_ProcImageBase = ProcImageBase;
|
||||||
|
|
||||||
if (!extractUnusedRegisters())
|
if (!extractUnusedRegisters())
|
||||||
throw std::exception("No registers avaliable for obfuscation...");
|
throw std::exception("No registers avaliable for obfuscation...");
|
||||||
@@ -121,13 +123,128 @@ void RyujinObfuscationCore::addPaddingSpaces() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RyujinObfuscationCore::obfuscateIat() {
|
||||||
|
|
||||||
|
/*
|
||||||
|
Unexpected Ryujin requires at least one unused register in the current procedure to deobfuscate the IAT during runtime
|
||||||
|
*/
|
||||||
|
if (m_unusedRegisters.size() == 0) return;
|
||||||
|
|
||||||
|
auto findBlockId = [&](ZyanU8 uopcode, ZyanI64 value) -> std::pair<int, int> {
|
||||||
|
|
||||||
|
int block_id = 0;
|
||||||
|
int opcode_id = 0;
|
||||||
|
|
||||||
|
for (auto& block : m_proc.basic_blocks) {
|
||||||
|
|
||||||
|
opcode_id = 0;
|
||||||
|
|
||||||
|
for (auto& opcode : block.opcodes) {
|
||||||
|
|
||||||
|
auto data = opcode.data();
|
||||||
|
auto size = opcode.size();
|
||||||
|
|
||||||
|
if (data[0] == uopcode) { //0xFF ?
|
||||||
|
|
||||||
|
if (std::memcmp(&*(data + 2), &value, sizeof(uint32_t)) == 0) // Is it the same memory immediate?
|
||||||
|
|
||||||
|
return std::make_pair(block_id, opcode_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
opcode_id++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
block_id++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_pair(-1, -1);
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto& block : m_obfuscated_bb) {
|
||||||
|
|
||||||
|
for (auto& instr : block.instructions) {
|
||||||
|
|
||||||
|
if (instr.instruction.info.meta.category == ZYDIS_CATEGORY_CALL && instr.instruction.operands->type == ZYDIS_OPERAND_TYPE_MEMORY) {
|
||||||
|
|
||||||
|
// Finding the block info related to the obfuscated opcode
|
||||||
|
auto block_info = findBlockId(instr.instruction.info.opcode, instr.instruction.operands->mem.disp.value);
|
||||||
|
|
||||||
|
// Call to an invalid IAT in the list of basic blocks
|
||||||
|
if (block_info.first == -1 || block_info.second == -1) continue;
|
||||||
|
|
||||||
|
// Retrieving the original opcodes where the opcodes have already been updated and obfuscated
|
||||||
|
auto& data = m_proc.basic_blocks[block_info.first].opcodes[block_info.second];
|
||||||
|
|
||||||
|
// Retrieving the "INSTRUCTION" from the basic block for our IAT call related to this context we're working with
|
||||||
|
auto orInstr = m_proc.basic_blocks[block_info.first].instructions.back(); // A call [IAT] will always be the last entry
|
||||||
|
|
||||||
|
/*
|
||||||
|
Let's calculate the IAT address that stores the resolved address for the given CALL
|
||||||
|
*/
|
||||||
|
// Calculating the VA of the next instruction after the "CALL" to the IAT in the original section
|
||||||
|
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;
|
||||||
|
|
||||||
|
// 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
|
||||||
|
};
|
||||||
|
|
||||||
|
std::memcpy(&*(pebRecoverModuleBase + 15), &iat_target_rva, sizeof(uint32_t));
|
||||||
|
|
||||||
|
new_iat_call.insert(new_iat_call.end(), std::begin(pebRecoverModuleBase), std::end(pebRecoverModuleBase));
|
||||||
|
|
||||||
|
// Replacing opcodes of the call in question with the new ones
|
||||||
|
data.assign(new_iat_call.begin(), new_iat_call.end());
|
||||||
|
|
||||||
|
std::printf("[OK] Obfuscating IAT CALL: %s\n", instr.instruction.text);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RyujinObfuscationCore::updateBasicBlocksContext() {
|
||||||
|
|
||||||
|
auto new_obfuscated_opcodes = getProcessedProc().getUpdateOpcodes();
|
||||||
|
auto bb = new RyujinBasicBlockerBuilder(ZYDIS_MACHINE_MODE_LONG_64, ZydisStackWidth_::ZYDIS_STACK_WIDTH_64);
|
||||||
|
m_obfuscated_bb = bb->createBasicBlocks(new_obfuscated_opcodes.data(), static_cast<size_t>(new_obfuscated_opcodes.size()), m_proc.address);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
BOOL RyujinObfuscationCore::Run() {
|
BOOL RyujinObfuscationCore::Run() {
|
||||||
|
|
||||||
//Add padding spaces
|
//Add padding spaces
|
||||||
addPaddingSpaces();
|
addPaddingSpaces();
|
||||||
|
|
||||||
|
//Update basic blocks view based on the new obfuscated
|
||||||
|
this->updateBasicBlocksContext();
|
||||||
|
|
||||||
|
//Obfuscate IAT for the configured procedures
|
||||||
|
if (m_config.m_isIatObfuscation) {
|
||||||
|
|
||||||
|
//First obfuscate IAT
|
||||||
|
obfuscateIat();
|
||||||
|
|
||||||
|
//Update our basic blocks context to rely 1-1 for the new obfuscated opcodes.
|
||||||
|
this->updateBasicBlocksContext();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
if (config.m_isIatObfuscation) todoAction();
|
|
||||||
|
|
||||||
if (config.m_isVirtualized) todoAction();
|
if (config.m_isVirtualized) todoAction();
|
||||||
if (config.m_isJunkCode) todoAction();
|
if (config.m_isJunkCode) todoAction();
|
||||||
@@ -258,7 +375,7 @@ void RyujinObfuscationCore::applyRelocationFixupsToInstructions(uintptr_t imageB
|
|||||||
|
|
||||||
}
|
}
|
||||||
//Fixing all Call to a memory(IAT) values from our obfuscated opcodes -> CALL [MEMORY]
|
//Fixing all Call to a memory(IAT) values from our obfuscated opcodes -> CALL [MEMORY]
|
||||||
else if (instruction.instruction.info.meta.category == ZYDIS_CATEGORY_CALL && instruction.instruction.operands->type == ZYDIS_OPERAND_TYPE_MEMORY) {
|
else if (instruction.instruction.info.meta.category == ZYDIS_CATEGORY_CALL && instruction.instruction.operands->type == ZYDIS_OPERAND_TYPE_MEMORY && !m_config.m_isIatObfuscation) {
|
||||||
|
|
||||||
// References for the vector's data and size with the obfuscated opcodes
|
// References for the vector's data and size with the obfuscated opcodes
|
||||||
auto size = new_opcodes.size();
|
auto size = new_opcodes.size();
|
||||||
|
|||||||
@@ -17,14 +17,18 @@ private:
|
|||||||
const int MAX_PADDING_SPACE_INSTR = 15;
|
const int MAX_PADDING_SPACE_INSTR = 15;
|
||||||
std::vector<ZydisRegister> m_unusedRegisters;
|
std::vector<ZydisRegister> m_unusedRegisters;
|
||||||
std::vector<RyujinBasicBlock> m_obfuscated_bb;
|
std::vector<RyujinBasicBlock> m_obfuscated_bb;
|
||||||
|
uintptr_t m_ProcImageBase;
|
||||||
RyujinProcedure m_proc;
|
RyujinProcedure m_proc;
|
||||||
|
RyujinObfuscatorConfig m_config;
|
||||||
BOOL extractUnusedRegisters();
|
BOOL extractUnusedRegisters();
|
||||||
|
void updateBasicBlocksContext();
|
||||||
void addPaddingSpaces();
|
void addPaddingSpaces();
|
||||||
|
void obfuscateIat();
|
||||||
std::vector<uint8_t> fix_branch_near_far_short(uint8_t original_opcode, uint64_t jmp_address, uint64_t target_address);
|
std::vector<uint8_t> fix_branch_near_far_short(uint8_t original_opcode, uint64_t jmp_address, uint64_t target_address);
|
||||||
uint32_t findOpcodeOffset(const uint8_t* data, size_t dataSize, const void* opcode, size_t opcodeSize);
|
uint32_t findOpcodeOffset(const uint8_t* data, size_t dataSize, const void* opcode, size_t opcodeSize);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RyujinObfuscationCore(const RyujinObfuscatorConfig& config, const RyujinProcedure& proc);
|
RyujinObfuscationCore(const RyujinObfuscatorConfig& config, const RyujinProcedure& proc, uintptr_t ProcImageBase);
|
||||||
void applyRelocationFixupsToInstructions(uintptr_t imageBase, DWORD virtualAddress, std::vector<unsigned char>& new_opcodes);
|
void applyRelocationFixupsToInstructions(uintptr_t imageBase, DWORD virtualAddress, std::vector<unsigned char>& new_opcodes);
|
||||||
void removeOldOpcodeRedirect(uintptr_t newMappedPE, std::size_t szMapped, uintptr_t newObfuscatedAddress, bool isIgnoreOriginalCodeRemove = false);
|
void removeOldOpcodeRedirect(uintptr_t newMappedPE, std::size_t szMapped, uintptr_t newObfuscatedAddress, bool isIgnoreOriginalCodeRemove = false);
|
||||||
BOOL Run();
|
BOOL Run();
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ auto main() -> int {
|
|||||||
config.m_isIatObfuscation = TRUE;
|
config.m_isIatObfuscation = TRUE;
|
||||||
std::vector<std::string> procsToObfuscate{
|
std::vector<std::string> procsToObfuscate{
|
||||||
"main",
|
"main",
|
||||||
"mainCRTStartup",
|
|
||||||
"invoke_main",
|
"invoke_main",
|
||||||
"sum",
|
"sum",
|
||||||
"__scrt_common_main",
|
"__scrt_common_main",
|
||||||
|
|||||||
Reference in New Issue
Block a user