feat: New HVPass (extension for code obfuscation) + MiniVM code mutation (for HVPass and standard MiniVM); Bug fixes for extracted unused registers (with future XMM support); Bug fixes for junk/mutation overwriting the RAX register unexpectedly; Improvements and added support for fixing relocation offsets in memory mov instructions; articles/projects diagrams and more.

- New HVPass feature – This feature allows the code VM to run through Microsoft’s Hypervisor API, adding an extra layer of analysis difficulty.
- MiniVM (normal) or MiniVM + HVPass – Now support junk/mutation in the stub, making the logic and instructions randomized at each interaction, further protecting the stub’s code.
- Bug fix – Fixed an issue in the extraction of unused registers from candidate procedures, where some registers were not being handled correctly.
- Bug fix – Fixed an issue in the extraction of XMM registers to enable junk/mutation support for multimedia registers.
- Bug fix – Fixed a problem in the junk/mutation logic for the instructions cdqe and cbw, which were incorrectly overwriting the RAX register, breaking results even when the registers were in use.
- Bug fix – Some instructions were not having relocations properly fixed by the RIP-relative relocation algorithm; this has now been corrected.
- Articles + Project Diagrams as well.

Some of these issues, as well as feature suggestions like HVPass, were discovered or suggested by the reviewers of Ryujin’s article.
This commit is contained in:
keowu
2025-08-28 21:20:58 -03:00
parent d8c37b2d4c
commit 2f5f9e2bd5
10 changed files with 8439 additions and 334 deletions

View File

@@ -67,12 +67,13 @@ Options:
--Troll Crashes the entire OS if a debugger is detected (requires --AntiDebug).
--AntiDump Inserts anti-dump mechanisms that break the binary in memory, making dumps harder to analyze.
--MemoryProtection Protects obfuscated code against in-memory or on-disk patching.
--HVPass Protect some parts of Ryujin using Microsoft Hypervisor APIs
--procs <comma,separated,names> Procedures to obfuscate (default: main, invoke_main, ...)
--help Show this help message
In Action Usage Example:
RyujinConsole.exe --input C:\\Users\\Keowu\\Documents\\GitHub\\Ryujin\\compiled\\release\\DemoObfuscation.exe --pdb C:\\Users\\Keowu\\Documents\\GitHub\\Ryujin\\compiled\\release\\RyujinConsole.pdb --output C:\\Users\\Keowu\\Documents\\GitHub\\Ryujin\\compiled\\release\\DemoObfuscation.ryujin.exe --virtualize --junk --encrypt --AntiDebug --troll --AntiDump --procs main,sub,subadd,sum,invoke_main,__scrt_common_main,j___security_init_cookie
RyujinConsole.exe --input C:\\Users\\Keowu\\Documents\\GitHub\\Ryujin\\compiled\\release\\DemoObfuscation.exe --pdb C:\\Users\\Keowu\\Documents\\GitHub\\Ryujin\\compiled\\release\\RyujinConsole.pdb --output C:\\Users\\Keowu\\Documents\\GitHub\\Ryujin\\compiled\\release\\DemoObfuscation.ryujin.exe --virtualize --junk --encrypt --AntiDebug --troll --AntiDump --iat --HVPass --procs main,sub,subadd,sum,invoke_main,__scrt_common_main,j___security_init_cookie
)";
@@ -131,6 +132,7 @@ auto main(int argc, char* argv[]) -> int {
config.m_isAntiDebug = has_flag(args, "--AntiDebug");
config.m_isAntiDump = has_flag(args, "--AntiDump");
config.m_isMemoryProtection = has_flag(args, "--MemoryProtection");
config.m_isHVPass = has_flag(args, "--HVPass");
// Registering a new custom pass for invocation via callback
config.RegisterCallback(RyujinCustomPassDemo);

View File

@@ -35,6 +35,7 @@ public:
bool m_isTrollRerversers; // The user wants to trick and use a special feature to troll reversers when their debugs be detected making they loose all the progress
bool m_isAntiDump; // Enable Anti Dump technic for Ryujin protected binary
bool m_isMemoryProtection; // Memory CRC32 protection
bool m_isHVPass; // Run some features of ryujin using Microsoft Hypervisor Framework API
RyujinObfuscatorProcs m_strProceduresToObfuscate; // Names of the procedures to obfuscate
RyujinCallbacks m_callbacks; // Ryujin Custom Pass Callbacks

View File

@@ -37,6 +37,7 @@ public:
bool m_isTrollRerversers; // The user wants to trick and use a special feature to troll reversers when their debugs be detected making they loose all the progress
bool m_isAntiDump; // Enable Anti Dump technic for Ryujin protected binary
bool m_isMemoryProtection; // Memory CRC32 protection
bool m_isHVPass; // Run some features of ryujin using Microsoft Hypervisor Framework API
RyujinObfuscatorProcs m_strProceduresToObfuscate; // Names of the procedures to obfuscate
RyujinCallbacks m_callbacks; // Ryujin Custom Pass Callbacks

File diff suppressed because it is too large Load Diff

View File

@@ -18,54 +18,139 @@ RyujinProcedure RyujinObfuscationCore::getProcessedProc() {
BOOL RyujinObfuscationCore::extractUnusedRegisters() {
std::vector<ZydisRegister> candidateRegs = {
ZYDIS_REGISTER_RAX,
ZYDIS_REGISTER_RCX,
ZYDIS_REGISTER_RDX,
ZYDIS_REGISTER_RBX,
ZYDIS_REGISTER_RSI,
ZYDIS_REGISTER_RDI,
ZYDIS_REGISTER_R8,
ZYDIS_REGISTER_R9,
ZYDIS_REGISTER_R10,
ZYDIS_REGISTER_R11,
ZYDIS_REGISTER_R12,
ZYDIS_REGISTER_R13,
ZYDIS_REGISTER_R14,
ZYDIS_REGISTER_R15,
// List of all general-purpose registers considered as candidates for junk/mutation during comparison.
const std::vector<ZydisRegister> candidateGprRegs = {
ZYDIS_REGISTER_RAX, ZYDIS_REGISTER_RCX, ZYDIS_REGISTER_RDX,
ZYDIS_REGISTER_RBX, ZYDIS_REGISTER_RSI, ZYDIS_REGISTER_RDI,
ZYDIS_REGISTER_R8, ZYDIS_REGISTER_R9, ZYDIS_REGISTER_R10,
ZYDIS_REGISTER_R11, ZYDIS_REGISTER_R12, ZYDIS_REGISTER_R13,
ZYDIS_REGISTER_R14, ZYDIS_REGISTER_R15
};
// List of XMM registers considered as candidates for junk/mutation during comparison.
const std::vector<ZydisRegister> candidateXmmRegs = {
ZYDIS_REGISTER_XMM0, ZYDIS_REGISTER_XMM1, ZYDIS_REGISTER_XMM2, ZYDIS_REGISTER_XMM3,
ZYDIS_REGISTER_XMM4, ZYDIS_REGISTER_XMM5, ZYDIS_REGISTER_XMM6, ZYDIS_REGISTER_XMM7,
ZYDIS_REGISTER_XMM8, ZYDIS_REGISTER_XMM9, ZYDIS_REGISTER_XMM10, ZYDIS_REGISTER_XMM11,
ZYDIS_REGISTER_XMM12, ZYDIS_REGISTER_XMM13, ZYDIS_REGISTER_XMM14, ZYDIS_REGISTER_XMM15
};
m_unusedRegisters.clear();
std::set<ZydisRegister> usedRegs;
for (auto blocks : m_proc.basic_blocks) {
// Regardless of everything, the stack manipulation registers RSP and RBP will always be considered as used.
usedRegs.insert(ZYDIS_REGISTER_RSP);
usedRegs.insert(ZYDIS_REGISTER_RBP);
for (auto instr : blocks.instructions) {
for (const auto& block : m_proc.basic_blocks) {
for (auto i = 0; i < instr.instruction.info.operand_count; ++i) {
for (const auto& instr : block.instructions) {
const auto& dinfo = instr.instruction.info;
const uint8_t opcount = dinfo.operand_count;
for (uint8_t i = 0; i < opcount; ++i) {
const ZydisDecodedOperand& op = instr.instruction.operands[i];
if (op.type == ZYDIS_OPERAND_TYPE_REGISTER) usedRegs.insert(op.reg.value);
else if (op.type == ZYDIS_OPERAND_TYPE_POINTER) {
// Registers with explicit operands
if (op.type == ZYDIS_OPERAND_TYPE_REGISTER) {
if (op.mem.base != ZYDIS_REGISTER_NONE) usedRegs.insert(op.mem.base);
if (op.mem.index != ZYDIS_REGISTER_NONE) usedRegs.insert(op.mem.index);
ZydisRegister reg = op.reg.value;
ZydisRegisterClass cls = ZydisRegisterGetClass(reg);
// Normalizing GPRs to GPR64 registers
if (cls == ZYDIS_REGCLASS_GPR8 || cls == ZYDIS_REGCLASS_GPR16 || cls == ZYDIS_REGCLASS_GPR32 || cls == ZYDIS_REGCLASS_GPR64) {
int16_t id = ZydisRegisterGetId(reg);
// Considering the lower-nibble registers
if (cls == ZYDIS_REGCLASS_GPR8 && id >= 4 && id <= 7)
id -= 4;
ZydisRegister reg64 = ZydisRegisterEncode(ZYDIS_REGCLASS_GPR64, id);
if (reg64 != ZYDIS_REGISTER_NONE) usedRegs.insert(reg64);
}
// Fetching XMM registers
else if (cls == ZYDIS_REGCLASS_XMM)
usedRegs.insert(reg);
// Checking for segment registers
else if (cls == ZYDIS_REGCLASS_SEGMENT)
usedRegs.insert(reg);
}
// Registers in use with memory operands
else if (op.type == ZYDIS_OPERAND_TYPE_MEMORY) {
if (op.mem.base != ZYDIS_REGISTER_NONE) {
ZydisRegister base = op.mem.base;
ZydisRegisterClass cls = ZydisRegisterGetClass(base);
if (cls == ZYDIS_REGCLASS_GPR8 || cls == ZYDIS_REGCLASS_GPR16 || cls == ZYDIS_REGCLASS_GPR32 || cls == ZYDIS_REGCLASS_GPR64) {
int16_t id = ZydisRegisterGetId(base);
ZydisRegister base64 = ZydisRegisterEncode(ZYDIS_REGCLASS_GPR64, id);
if (base64 != ZYDIS_REGISTER_NONE) usedRegs.insert(base64);
}
else
usedRegs.insert(base);
}
// Collecting index registers
if (op.mem.index != ZYDIS_REGISTER_NONE) {
ZydisRegister idx = op.mem.index;
ZydisRegisterClass cls = ZydisRegisterGetClass(idx);
if (cls == ZYDIS_REGCLASS_GPR8 || cls == ZYDIS_REGCLASS_GPR16 || cls == ZYDIS_REGCLASS_GPR32 || cls == ZYDIS_REGCLASS_GPR64) {
int16_t id = ZydisRegisterGetId(idx);
ZydisRegister idx64 = ZydisRegisterEncode(ZYDIS_REGCLASS_GPR64, id);
if (idx64 != ZYDIS_REGISTER_NONE) usedRegs.insert(idx64);
}
else
usedRegs.insert(idx);
}
// Hackfix for segment registers like: cs:[rax]
if (op.mem.segment != ZYDIS_REGISTER_NONE)
usedRegs.insert(op.mem.segment);
}
}
}
}
ZydisRegister freeReg = ZYDIS_REGISTER_NONE;
for (auto reg : candidateRegs)
if (usedRegs.count(reg) == 0) m_unusedRegisters.push_back(reg);
/*
Based on the collected registers, compare each register with the list of used registers
so we can build the unique set of used registers.
*/
for (ZydisRegister r : candidateGprRegs)
if (usedRegs.count(r) == 0)
m_unusedRegisters.push_back(r);
return m_unusedRegisters.size() >= 2; //Theres unused regs for be used by us ?
/*
* TEMPORARILY DISABLED until Ryujin implements support for multimedia registers.
*
* Based on the collected XMM registers, compare each register with the list of used XMM registers
* to build the unique set of used XMM registers.
*/
//for (ZydisRegister r : candidateXmmRegs)
// if (usedRegs.count(r) == 0)
// m_unusedRegisters.push_back(r);
// We need at least 2 registers in order for obfuscation to work.
return (m_unusedRegisters.size() >= 2); // Seriously, Keowu? Yes. We need room to run some passes.
}
void RyujinObfuscationCore::addPaddingSpaces() {
@@ -295,7 +380,7 @@ void RyujinObfuscationCore::insertJunkCode() {
// Ignore stack unused registers, if the feature for extracting unused register fail
if (idx == 4 /*RSP*/ || idx == 5 /*RBP*/) continue;
// Converting GB Register Index to a GB Register
auto regx = a.gpz(uint32_t(idx));
@@ -370,13 +455,33 @@ void RyujinObfuscationCore::insertJunkCode() {
case 31: a.stc(); break;
case 32: a.clc(); break;
case 33: a.cmc(); break;
case 34: a.cdqe(); break;
case 35: a.cbw(); break;
//////////////////////////
// Different logic for these registers because they overwrite RAX
//--------------------------------------------------
case 34: {
a.push(asmjit::x86::rax);
a.pushf();
a.cdqe();
a.popf();
a.pop(asmjit::x86::rax);
break;
}
case 35: {
a.push(asmjit::x86::rax);
a.pushf();
a.cbw();
a.popf();
a.pop(asmjit::x86::rax);
break;
}
///////////////////////////////
case 36: a.sbb(regx, value); break;
case 37: a.bsf(regx, regx); break;
default: break;
}
}
// Junk Code Out
@@ -788,11 +893,13 @@ void RyujinObfuscationCore::insertVirtualization() {
// Breaking Decompilers
insertBreakDecompilers(a);
// Saving the current value of RCX
a.push(asmjit::x86::rcx);
// Saving the current value of RDX
a.push(asmjit::x86::rdx);
// Setup stack for MS HV Code MiniVMm stub
if (m_config.m_isHVPass) a.sub(asmjit::x86::rsp, 0x28);
// Storing in the first argument RCX the value of the register from the first operand of the mathematical operation
a.mov(asmjit::x86::rcx, mapZydisToAsmjitGp(instr.instruction.operands[0].reg.value));
// Storing in the second argument RDX the value of the bytecode sequence to be interpreted by the Ryujin MiniVM
@@ -819,6 +926,8 @@ void RyujinObfuscationCore::insertVirtualization() {
a.call(asmjit::x86::rax);
// Storing the result of the MiniVM execution stored in RAX into the correct register to continue the normal execution flow
a.mov(mapZydisToAsmjitGp(instr.instruction.operands[0].reg.value), asmjit::x86::rax);
// Setup stack for MS HV Code MiniVMm stub
if (m_config.m_isHVPass) a.add(asmjit::x86::rsp, 0x28);
// Restoring the original value of RDX
a.pop(asmjit::x86::rdx);
// Restoring the original value of RCX
@@ -2653,21 +2762,45 @@ void RyujinObfuscationCore::applyRelocationFixupsToInstructions(uintptr_t imageB
auto size = new_opcodes.size();
auto data = new_opcodes.data();
//Avoid memory op for stack
if (instruction.instruction.operands->mem.base == ZYDIS_REGISTER_RSP) {
std::printf("Invalid relocation fix candidate for -> %s\n", instruction.instruction.text);
continue;
}
// Getting the memory immediate offset value to build the signature
const uint32_t memmory_immediate_offset = mem->disp.value;
// Creating a signature to search for the offset in the obfuscated opcodes
ZyanI64 offset = 0;
unsigned char ucOpcodeSignature[7]{ 0 };
std::memcpy(&ucOpcodeSignature, reinterpret_cast<void*>(instruction.addressofinstruction), 3); // 3 BYTES do opcode relativo ao LEA ou MOV
std::memcpy(&*(ucOpcodeSignature + 3), &memmory_immediate_offset, sizeof(memmory_immediate_offset));
// Finding the offset of the "LEA" or "MOV" that uses memory-relative addressing
const ZyanI64 offset = findOpcodeOffset(data, size, &ucOpcodeSignature, 7);
// If original instruction is not a mov reg, cs[](beacuse it has 6 not 7 bytes)
if (*reinterpret_cast<unsigned char*>(instruction.addressofinstruction) != 0x8B) {
// Creating a signature to search for the offset in the obfuscated opcodes
std::memcpy(&ucOpcodeSignature, reinterpret_cast<void*>(instruction.addressofinstruction), 3); // 3 BYTES do opcode relativo ao LEA ou MOV
std::memcpy(&*(ucOpcodeSignature + 3), &memmory_immediate_offset, sizeof(memmory_immediate_offset));
// Finding the offset of the "LEA" or "MOV" that uses memory-relative addressing
offset = findOpcodeOffset(data, size, &ucOpcodeSignature, 7);
}
else {
// Creating a signature to search for the offset in the obfuscated opcodes
std::memcpy(&ucOpcodeSignature, reinterpret_cast<void*>(instruction.addressofinstruction), 6); // 6 bytes do opcode relativo MOV CODE SEGMENT: mov reg, cs:addr
// Finding the offset of the "MOV reg, cs:addr" that uses memory-relative addressing
offset = findOpcodeOffset(data, size, &ucOpcodeSignature, 6);
}
// If we don't find any offset, there may be an issue or bug.
if (offset == 0) {
std::printf("[X] Invalid lea reference or uknown lea detected.....\n");
std::printf("[X] Invalid lea/mov reference or uknown lea/mov detected.....\n");
continue;
}
@@ -2694,12 +2827,181 @@ void RyujinObfuscationCore::applyRelocationFixupsToInstructions(uintptr_t imageB
const uintptr_t new_memory_immediate = target_original - new_obfuscated_rip;
// Fixing the immediate value for the "LEA" or "MOV" instruction with the corrected relative immediate value
std::memcpy(&*(data + offset + 3), &new_memory_immediate, sizeof(uint32_t)); // 3 bytes for the size of the LEA or MOV opcode
if (*reinterpret_cast<unsigned char*>(instruction.addressofinstruction) != 0x8B)
std::memcpy(&*(data + offset + 3), &new_memory_immediate, sizeof(uint32_t)); // 3 bytes for the size of the LEA or MOV opcode
else
std::memcpy(&*(data + offset + 2), &new_memory_immediate, sizeof(uint32_t)); // 3 bytes for the size of the LEA or MOV opcode
std::printf("[OK] Fixing -> %s - from %X to %X\n", instruction.instruction.text, mem->disp.value, new_memory_immediate);
}
}
else if ((instruction.instruction.info.mnemonic == ZYDIS_MNEMONIC_MOV || instruction.instruction.info.mnemonic == ZYDIS_MNEMONIC_LEA) && instruction.instruction.operands->type == ZYDIS_OPERAND_TYPE_MEMORY && instruction.instruction.operands->mem.type == ZYDIS_MEMOP_TYPE_MEM) {
if (instruction.instruction.info.length > 5) {
// References for data and vector size with obfuscated opcodes
auto size = new_opcodes.size();
auto data = new_opcodes.data();
const ZydisDecodedOperandMem* mem = &instruction.instruction.operands[0].mem;
//Avoid memory op for stack
if (instruction.instruction.operands->mem.base == ZYDIS_REGISTER_RSP) {
//std::printf("Invalid relocation fix candidate for -> %s\n", instruction.instruction.text);
continue;
}
// Getting the memory immediate offset value to build the signature
const uint32_t memmory_immediate_offset = mem->disp.value;
ZyanI64 offset = 0, fix_byte = 0;
unsigned char ucOpcodeSignature[11]{ 0 };
if (instruction.instruction.info.length == 6) {
std::memcpy(&ucOpcodeSignature, reinterpret_cast<void*>(instruction.addressofinstruction), 6);
offset = findOpcodeOffset(data, size, &ucOpcodeSignature, 6);
fix_byte = 2;
}
else if (instruction.instruction.info.length == 7) {
std::memcpy(&ucOpcodeSignature, reinterpret_cast<void*>(instruction.addressofinstruction), 7);
offset = findOpcodeOffset(data, size, &ucOpcodeSignature, 7);
fix_byte = 3;
}
else if (instruction.instruction.info.length == 10) {
std::memcpy(&ucOpcodeSignature, reinterpret_cast<void*>(instruction.addressofinstruction), 10);
offset = findOpcodeOffset(data, size, &ucOpcodeSignature, 10);
fix_byte = 2;
}
else {
std::printf("ERROR Unexpected Instruction mov cs[], reg/imm -> %s - %d\n", instruction.instruction.text, instruction.instruction.info.length);
continue;
}
// Retrieving the instruction address in the original section
const uintptr_t original_address = instruction.addressofinstruction;
// Calculating new address in the obfuscated section
const uintptr_t obfuscated_va_address = ((imageBase + virtualAddress + offset));
/*
Calculating new displacement for the immediate value
*/
// Calculating the address of the instruction following the original instruction
const uintptr_t original_rip = original_address + instruction.instruction.info.length;
// Calculating the original target address of the original instruction
const uintptr_t target_original = original_rip + memmory_immediate_offset;
// Calculating the address of the instruction following the obfuscated instruction
const uintptr_t new_obfuscated_rip = obfuscated_va_address + instruction.instruction.info.length;
// New memory immediate value for the instruction
const uintptr_t new_memory_immediate = target_original - new_obfuscated_rip;
// Fixing the immediate value for the "LEA" or "MOV" instruction with the corrected relative immediate value
std::memcpy(&*(data + offset + fix_byte), &new_memory_immediate, sizeof(uint32_t));
std::printf("[OK] Fixing -> %s - from %X to %X\n", instruction.instruction.text, mem->disp.value, new_memory_immediate);
}
}
else if ((instruction.instruction.info.mnemonic == ZYDIS_MNEMONIC_ADD || instruction.instruction.info.mnemonic == ZYDIS_MNEMONIC_SUB || instruction.instruction.info.mnemonic == ZYDIS_MNEMONIC_XOR) && (instruction.instruction.operands[0].type == ZYDIS_OPERAND_TYPE_MEMORY || instruction.instruction.operands[1].type == ZYDIS_OPERAND_TYPE_MEMORY)) {
// References for data and vector size with obfuscated opcodes
auto size = new_opcodes.size();
auto data = new_opcodes.data();
//Avoid memory op for stack
if (instruction.instruction.operands->mem.base == ZYDIS_REGISTER_RSP) {
std::printf("Invalid relocation fix candidate for -> %s\n", instruction.instruction.text);
continue;
}
ZydisDecodedOperandMem* mem;
if (instruction.instruction.operands[0].type == ZYDIS_OPERAND_TYPE_MEMORY)
mem = &instruction.instruction.operands[0].mem;
else
mem = &instruction.instruction.operands[1].mem;
// Getting the memory immediate offset value to build the signature
const uint32_t memmory_immediate_offset = mem->disp.value;
ZyanI64 offset = 0, fix_byte = 0;
unsigned char ucOpcodeSignature[11]{ 0 };
if (instruction.instruction.info.length == 6) {
std::memcpy(&ucOpcodeSignature, reinterpret_cast<void*>(instruction.addressofinstruction), 6);
offset = findOpcodeOffset(data, size, &ucOpcodeSignature, 6);
fix_byte = 2;
}
else if (instruction.instruction.info.length == 7) {
std::memcpy(&ucOpcodeSignature, reinterpret_cast<void*>(instruction.addressofinstruction), 7);
offset = findOpcodeOffset(data, size, &ucOpcodeSignature, 7);
fix_byte = 3;
}
else {
std::printf("ERROR Unexpected Instruction bitwise/math [], reg or bitwise/math reg, [] -> %s - %d\n", instruction.instruction.text, instruction.instruction.info.length);
continue;
}
// Retrieving the instruction address in the original section
const uintptr_t original_address = instruction.addressofinstruction;
// Calculating new address in the obfuscated section
const uintptr_t obfuscated_va_address = ((imageBase + virtualAddress + offset));
/*
Calculating new displacement for the immediate value
*/
// Calculating the address of the instruction following the original instruction
const uintptr_t original_rip = original_address + instruction.instruction.info.length;
// Calculating the original target address of the original instruction
const uintptr_t target_original = original_rip + memmory_immediate_offset;
// Calculating the address of the instruction following the obfuscated instruction
const uintptr_t new_obfuscated_rip = obfuscated_va_address + instruction.instruction.info.length;
// New memory immediate value for the instruction
const uintptr_t new_memory_immediate = target_original - new_obfuscated_rip;
// Fixing the immediate value for the "LEA" or "MOV" instruction with the corrected relative immediate value
std::memcpy(&*(data + offset + fix_byte), &new_memory_immediate, sizeof(uint32_t));
std::printf("[OK] Fixing Math/Bitwise Operators -> %s | %d\n", instruction.instruction.text, instruction.instruction.info.length);
}
else if (instruction.instruction.info.meta.category == ZYDIS_CATEGORY_COND_BR || instruction.instruction.info.meta.category == ZYDIS_CATEGORY_UNCOND_BR) {
@@ -2743,6 +3045,12 @@ void RyujinObfuscationCore::applyRelocationFixupsToInstructions(uintptr_t imageB
*/
auto obfuscated_target_address = basic_block_obfuscated.instructions.at(0).addressofinstruction;
/*
Preventing problems.
When IAT is obfuscated. theres no JUMP Address(it's always "0")! because it is solved during runtime. so the reloc is fixed, let's advance to the next blog!
*/
if (obfuscated_jmp_address == 0 && m_config.m_isIatObfuscation) continue;
/*
Let's fix our new branch. Previously it was a "near" jump, but now it will be "far" depending on the jump length.
This procedure will perform the calculation and generate a far or near branch depending on the need
@@ -2815,9 +3123,18 @@ void RyujinObfuscationCore::removeOldOpcodeRedirect(uintptr_t newMappedPE, std::
We will use findOpcodeOffset to find the exact offset of the procedure's start
in the unmapped region with the SEC_IMAGE flag.
*/
unsigned char ucSigature[10]{ 0 };
std::memcpy(ucSigature, reinterpret_cast<void*>(m_proc.address), 10);
auto offsetz = findOpcodeOffset(reinterpret_cast<unsigned char*>(newMappedPE), szMapped, &ucSigature, 10);
unsigned char* ucSigature = new unsigned char[m_proc.size] { 0 };
std::memcpy(ucSigature, reinterpret_cast<void*>(m_proc.address), m_proc.size);
auto offsetz = findOpcodeOffset(reinterpret_cast<unsigned char*>(newMappedPE), szMapped, ucSigature, m_proc.size);
delete[] ucSigature;
/*
* Future assert
if (!offsetz) {
std::printf("[X] Fatal Error on removeOldOpcodeRedirect -> %s\n", m_proc.name);
exit(-1);
}*/
// Based on the obfuscation configuration, some users can decide to not remove the original code from the original procedure after obfuscation.
if (!isIgnoreOriginalCodeRemove) std::memset(reinterpret_cast<void*>(newMappedPE + offsetz), 0x90, m_proc.size); // Removing all the opcodes from the original procedure and replacing them with NOP instructions.

View File

@@ -17,7 +17,7 @@ class RyujinObfuscationCore {
private:
const int MAX_PADDING_SPACE_INSTR = 14;
const int MAX_JUNK_GENERATION_ITERATION = 5;
const int MAX_JUNK_GENERATION_ITERATION = 8;
std::vector<ZydisRegister> m_unusedRegisters;
std::vector<RyujinBasicBlock> m_obfuscated_bb;
uintptr_t m_ProcImageBase;

View File

@@ -35,6 +35,7 @@ public:
bool m_isTrollRerversers; // The user wants to trick and use a special feature to troll reversers when their debugs be detected making they loose all the progress
bool m_isAntiDump; // Enable Anti Dump technic for Ryujin protected binary
bool m_isMemoryProtection; // Memory CRC32 protection
bool m_isHVPass; // Run some features of ryujin using Microsoft Hypervisor Framework API
RyujinObfuscatorProcs m_strProceduresToObfuscate; // Names of the procedures to obfuscate
RyujinCallbacks m_callbacks; // Ryujin Custom Pass Callbacks

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,709 @@
{
"type": "excalidraw",
"version": 2,
"source": "https://excalidraw.com",
"elements": [
{
"id": "M8c9eQZB7X5MZItasDS9w",
"type": "rectangle",
"x": 391.20001220703125,
"y": 192.60000610351562,
"width": 344.79998779296875,
"height": 307.20001220703125,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a0",
"roundness": {
"type": 3
},
"seed": 1615675767,
"version": 129,
"versionNonce": 2118539607,
"isDeleted": false,
"boundElements": [
{
"id": "c1iXEu4S5qWnIgYbjx1Zc",
"type": "arrow"
}
],
"updated": 1749686313933,
"link": null,
"locked": false
},
{
"id": "46FHFpNht2wlAuCrvcVyc",
"type": "text",
"x": 516,
"y": 154.1999969482422,
"width": 58.23994445800781,
"height": 25,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a1",
"roundness": null,
"seed": 554549911,
"version": 55,
"versionNonce": 171168151,
"isDeleted": false,
"boundElements": [],
"updated": 1749685574176,
"link": null,
"locked": false,
"text": "Ryujin",
"fontSize": 20,
"fontFamily": 5,
"textAlign": "left",
"verticalAlign": "top",
"containerId": null,
"originalText": "Ryujin",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "xdJ2irJYkaNC9r3QEIouH",
"type": "rectangle",
"x": 833.6000366210938,
"y": 205.40000915527344,
"width": 144.79998779296875,
"height": 121.59999084472656,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a2",
"roundness": {
"type": 3
},
"seed": 679450583,
"version": 45,
"versionNonce": 916855385,
"isDeleted": false,
"boundElements": [
{
"id": "GSUC-T1BCvyWszlmgyl-H",
"type": "arrow"
},
{
"id": "KVVsXm6d78Iek_N-4mTZ5",
"type": "arrow"
}
],
"updated": 1749686124093,
"link": null,
"locked": false
},
{
"id": "u0hyTPfKwpUasROfCYwNp",
"type": "text",
"x": 856,
"y": 235,
"width": 114.89994812011719,
"height": 75,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a3",
"roundness": null,
"seed": 994638937,
"version": 37,
"versionNonce": 1352983513,
"isDeleted": false,
"boundElements": [],
"updated": 1749685595466,
"link": null,
"locked": false,
"text": "mov rax, 10\nadd rbx, 20\nsub rcx, 30",
"fontSize": 20,
"fontFamily": 5,
"textAlign": "left",
"verticalAlign": "top",
"containerId": null,
"originalText": "mov rax, 10\nadd rbx, 20\nsub rcx, 30",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "GSUC-T1BCvyWszlmgyl-H",
"type": "arrow",
"x": 831.2000122070312,
"y": 271,
"width": 212.79998779296875,
"height": 3.20001220703125,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a4",
"roundness": {
"type": 2
},
"seed": 568230297,
"version": 79,
"versionNonce": 1291788375,
"isDeleted": false,
"boundElements": [],
"updated": 1749685606290,
"link": null,
"locked": false,
"points": [
[
0,
0
],
[
-212.79998779296875,
3.20001220703125
]
],
"lastCommittedPoint": null,
"startBinding": {
"elementId": "xdJ2irJYkaNC9r3QEIouH",
"focus": -0.05938365959726409,
"gap": 2.4000244140625
},
"endBinding": null,
"startArrowhead": null,
"endArrowhead": "arrow",
"elbowed": false
},
{
"id": "kJtdz2klrATJ98T_MfkEO",
"type": "rectangle",
"x": 480.8000183105469,
"y": 244.60000610351562,
"width": 118.39999389648438,
"height": 68,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a5",
"roundness": {
"type": 3
},
"seed": 832654263,
"version": 38,
"versionNonce": 842147095,
"isDeleted": false,
"boundElements": [],
"updated": 1749685611598,
"link": null,
"locked": false
},
{
"id": "L_pSiLFD-D1qnLQGHAGoK",
"type": "text",
"x": 498.3999938964844,
"y": 256.6000061035156,
"width": 88.41993713378906,
"height": 50,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a6",
"roundness": null,
"seed": 1291545143,
"version": 17,
"versionNonce": 54828087,
"isDeleted": false,
"boundElements": [],
"updated": 1749685618526,
"link": null,
"locked": false,
"text": "custom\nbytecode",
"fontSize": 20,
"fontFamily": 5,
"textAlign": "left",
"verticalAlign": "top",
"containerId": null,
"originalText": "custom\nbytecode",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "KVVsXm6d78Iek_N-4mTZ5",
"type": "arrow",
"x": 578.4000244140625,
"y": 359,
"width": 198.4000244140625,
"height": 10.399993896484375,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a7",
"roundness": {
"type": 2
},
"seed": 479607415,
"version": 100,
"versionNonce": 1093613177,
"isDeleted": false,
"boundElements": [],
"updated": 1749686207162,
"link": null,
"locked": false,
"points": [
[
0,
0
],
[
198.4000244140625,
10.399993896484375
]
],
"lastCommittedPoint": null,
"startBinding": null,
"endBinding": {
"elementId": "NFjixAoh2CztYuP1DNIN-",
"focus": -0.4045620317282552,
"gap": 10.39996337890625
},
"startArrowhead": null,
"endArrowhead": "arrow",
"elbowed": false
},
{
"id": "NFjixAoh2CztYuP1DNIN-",
"type": "text",
"x": 787.2000122070312,
"y": 363.8000183105469,
"width": 731.2396240234375,
"height": 25,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a8",
"roundness": null,
"seed": 1290016793,
"version": 81,
"versionNonce": 318460471,
"isDeleted": false,
"boundElements": [
{
"id": "iFf2168HoefeSA26Qvyxp",
"type": "arrow"
},
{
"id": "KVVsXm6d78Iek_N-4mTZ5",
"type": "arrow"
}
],
"updated": 1749686206810,
"link": null,
"locked": false,
"text": "Insert a jump into the original code to vmentry(for execute the bytecode)",
"fontSize": 20,
"fontFamily": 5,
"textAlign": "left",
"verticalAlign": "top",
"containerId": null,
"originalText": "Insert a jump into the original code to vmentry(for execute the bytecode)",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "j5zq_Cm9IYKmHffzVgFTn",
"type": "rectangle",
"x": 1548.4000244140625,
"y": 304.6000061035156,
"width": 208,
"height": 180.00006103515625,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a9",
"roundness": {
"type": 3
},
"seed": 1910262775,
"version": 76,
"versionNonce": 1150487223,
"isDeleted": false,
"boundElements": [],
"updated": 1749686159293,
"link": null,
"locked": false
},
{
"id": "Bgo_NssAKTn1SdIHyYsMn",
"type": "text",
"x": 1576.4000244140625,
"y": 319,
"width": 205.4998779296875,
"height": 150,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "aA",
"roundness": null,
"seed": 792391961,
"version": 88,
"versionNonce": 1848161657,
"isDeleted": false,
"boundElements": [
{
"id": "iFf2168HoefeSA26Qvyxp",
"type": "arrow"
}
],
"updated": 1749686197118,
"link": null,
"locked": false,
"text": "nop\nnop\npush rcx\nmov rcx, ptrbytecode\ncall vmentry\nmov rax...",
"fontSize": 20,
"fontFamily": 5,
"textAlign": "left",
"verticalAlign": "top",
"containerId": null,
"originalText": "nop\nnop\npush rcx\nmov rcx, ptrbytecode\ncall vmentry\nmov rax...",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "iFf2168HoefeSA26Qvyxp",
"type": "arrow",
"x": 1469.5999755859375,
"y": 383.8000183105469,
"width": 78.4000244140625,
"height": 21.5999755859375,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "aB",
"roundness": {
"type": 2
},
"seed": 1319005721,
"version": 21,
"versionNonce": 1768435865,
"isDeleted": false,
"boundElements": [],
"updated": 1749686197118,
"link": null,
"locked": false,
"points": [
[
0,
0
],
[
78.4000244140625,
21.5999755859375
]
],
"lastCommittedPoint": null,
"startBinding": {
"elementId": "NFjixAoh2CztYuP1DNIN-",
"focus": -0.7045371306368834,
"gap": 5
},
"endBinding": {
"elementId": "Bgo_NssAKTn1SdIHyYsMn",
"focus": -0.4601077518323214,
"gap": 28.4000244140625
},
"startArrowhead": null,
"endArrowhead": "arrow",
"elbowed": false
},
{
"id": "Az2n4_JQ_Rep62w0qbnpI",
"type": "arrow",
"x": 641.2000122070312,
"y": 364.20001220703125,
"width": 63.20001220703125,
"height": 49.600006103515625,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "aC",
"roundness": {
"type": 2
},
"seed": 313306169,
"version": 38,
"versionNonce": 499366105,
"isDeleted": false,
"boundElements": [],
"updated": 1749686269123,
"link": null,
"locked": false,
"points": [
[
0,
0
],
[
-63.20001220703125,
49.600006103515625
]
],
"lastCommittedPoint": null,
"startBinding": null,
"endBinding": null,
"startArrowhead": null,
"endArrowhead": "arrow",
"elbowed": false
},
{
"id": "O5yT6LnxD7xhW31GADKYB",
"type": "text",
"x": 468.3999938964844,
"y": 432.20001220703125,
"width": 820.1596069335938,
"height": 50,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "aD",
"roundness": null,
"seed": 137944663,
"version": 136,
"versionNonce": 1340294711,
"isDeleted": false,
"boundElements": [],
"updated": 1749686305048,
"link": null,
"locked": false,
"text": "ryujin will insert bytecode in some section\nthe vm will interpret it and return back with the full context to not broke the code",
"fontSize": 20,
"fontFamily": 5,
"textAlign": "left",
"verticalAlign": "top",
"containerId": null,
"originalText": "ryujin will insert bytecode in some section\nthe vm will interpret it and return back with the full context to not broke the code",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "c1iXEu4S5qWnIgYbjx1Zc",
"type": "arrow",
"x": 434.3999938964844,
"y": 503.20001220703125,
"width": 170.39999389648438,
"height": 156.79998779296875,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "aE",
"roundness": {
"type": 2
},
"seed": 1561486455,
"version": 28,
"versionNonce": 1291089975,
"isDeleted": false,
"boundElements": [],
"updated": 1749686313933,
"link": null,
"locked": false,
"points": [
[
0,
0
],
[
170.39999389648438,
156.79998779296875
]
],
"lastCommittedPoint": null,
"startBinding": {
"elementId": "M8c9eQZB7X5MZItasDS9w",
"focus": 0.8835765524851282,
"gap": 3.399993896484375
},
"endBinding": null,
"startArrowhead": null,
"endArrowhead": "arrow",
"elbowed": false
},
{
"id": "WLU2ZgAEJXhP7nLOkpjLn",
"type": "text",
"x": 663.2000122070312,
"y": 647.2000122070312,
"width": 690.9995727539062,
"height": 25,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "aF",
"roundness": null,
"seed": 870526871,
"version": 98,
"versionNonce": 999258681,
"isDeleted": false,
"boundElements": [],
"updated": 1749686340544,
"link": null,
"locked": false,
"text": "The vm will only allow some simple menemonic for multiplication(for now)",
"fontSize": 20,
"fontFamily": 5,
"textAlign": "left",
"verticalAlign": "top",
"containerId": null,
"originalText": "The vm will only allow some simple menemonic for multiplication(for now)",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "tLeEn07Up0otFbkxmXxyS",
"type": "text",
"x": 359.60003662109375,
"y": 86.39999389648438,
"width": 1619.8392333984375,
"height": 25,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "aG",
"roundness": null,
"seed": 1720073049,
"version": 213,
"versionNonce": 1953987225,
"isDeleted": false,
"boundElements": [],
"updated": 1749687328143,
"link": null,
"locked": false,
"text": "to not use too much space we already have padding with nop -> compile the instructions to vm bytecode with a maximum of 8 bytes and interpret eah one individually",
"fontSize": 20,
"fontFamily": 5,
"textAlign": "left",
"verticalAlign": "top",
"containerId": null,
"originalText": "to not use too much space we already have padding with nop -> compile the instructions to vm bytecode with a maximum of 8 bytes and interpret eah one individually",
"autoResize": true,
"lineHeight": 1.25
}
],
"appState": {
"gridSize": 20,
"gridStep": 5,
"gridModeEnabled": false,
"viewBackgroundColor": "#ffffff",
"lockedMultiSelections": {}
},
"files": {}
}

File diff suppressed because one or more lines are too long