feat: Functional MiniVM Entry and Stable VM Stub for Running Virtualized Bytecode (To Be Improved in Future Commits)

- Ryujin can now interpret MiniVM bytecode. It inserts the MiniVM entry routine's RVA to enable execution of the VM interpreter.
- This is just the initial implementation and will be significantly improved in future commits. We're pushing our first working solution for now.
This commit is contained in:
keowu
2025-06-16 21:32:34 -03:00
parent 3a3a92f7ca
commit da1c91d07a
4 changed files with 75 additions and 10 deletions

View File

@@ -156,8 +156,43 @@ bool Ryujin::run(const RyujinObfuscatorConfig& config) {
RyujinPESections peSections;
peSections.AddNewSection(m_strInputFilePath, chSectionName);
uintptr_t offsetVA = 0;
uintptr_t offsetVA = 0, miniVmEnterAddress = 0;
std::vector<unsigned char> opcodesWithRelocsFixed;
//Insert minivm enter routine
if (config.m_isVirtualized) {
std::vector<unsigned char> miniVmEnter{
0x48, 0x89, 0x4C, 0x24, 0x08, 0x48, 0x83, 0xEC, 0x28, 0x48, 0x8B, 0x44,
0x24, 0x30, 0x48, 0xC1, 0xE8, 0x10, 0x48, 0x25, 0xFF, 0x00, 0x00, 0x00,
0x88, 0x44, 0x24, 0x01, 0x48, 0x8B, 0x44, 0x24, 0x30, 0x48, 0xC1, 0xE8,
0x08, 0x48, 0x25, 0xFF, 0x00, 0x00, 0x00, 0x88, 0x04, 0x24, 0x48, 0x8B,
0x44, 0x24, 0x30, 0x48, 0x25, 0xFF, 0x00, 0x00, 0x00, 0x48, 0x89, 0x44,
0x24, 0x10, 0x48, 0xC7, 0x44, 0x24, 0x08, 0x00, 0x00, 0x00, 0x00, 0x0F,
0xB6, 0x04, 0x24, 0x88, 0x44, 0x24, 0x04, 0x80, 0x7C, 0x24, 0x04, 0x01,
0x74, 0x17, 0x80, 0x7C, 0x24, 0x04, 0x02, 0x74, 0x27, 0x80, 0x7C, 0x24,
0x04, 0x03, 0x74, 0x37, 0x80, 0x7C, 0x24, 0x04, 0x04, 0x74, 0x42, 0xEB,
0x53, 0x48, 0x8B, 0x44, 0x24, 0x10, 0x48, 0x8B, 0x4C, 0x24, 0x08, 0x48,
0x03, 0xC8, 0x48, 0x8B, 0xC1, 0x48, 0x89, 0x44, 0x24, 0x08, 0xEB, 0x45,
0x48, 0x8B, 0x44, 0x24, 0x10, 0x48, 0x8B, 0x4C, 0x24, 0x08, 0x48, 0x2B,
0xC8, 0x48, 0x8B, 0xC1, 0x48, 0x89, 0x44, 0x24, 0x08, 0xEB, 0x2E, 0x48,
0x8B, 0x44, 0x24, 0x08, 0x48, 0x0F, 0xAF, 0x44, 0x24, 0x10, 0x48, 0x89,
0x44, 0x24, 0x08, 0xEB, 0x1C, 0x33, 0xD2, 0x48, 0x8B, 0x44, 0x24, 0x08,
0x48, 0xF7, 0x74, 0x24, 0x10, 0x48, 0x89, 0x44, 0x24, 0x08, 0xEB, 0x09,
0x48, 0xC7, 0x44, 0x24, 0x08, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8B, 0x44,
0x24, 0x08, 0x48, 0x83, 0xC4, 0x28, 0xC3
};
opcodesWithRelocsFixed.insert(opcodesWithRelocsFixed.end(), miniVmEnter.begin(), miniVmEnter.end());
miniVmEnterAddress = peSections.getRyujinSectionVA();
offsetVA += miniVmEnter.size();
}
for (auto& obc : processed_procs) {
auto tempValued = obc.getProcessedProc().getUpdateOpcodes();
@@ -168,6 +203,9 @@ bool Ryujin::run(const RyujinObfuscatorConfig& config) {
// Removing and adding a jump in the original procedure and removing original opcodes for a jump to the new obfuscated code
obc.removeOldOpcodeRedirect(peSections.mappedPeDiskBaseAddress(), peSections.getRyujinMappedPeSize(), reinterpret_cast<uintptr_t>(imgDos) + peSections.getRyujinSectionVA() + offsetVA, config.m_isIgnoreOriginalCodeRemove);
// Inserindo MiniVMEnter
if (config.m_isVirtualized) obc.InsertMiniVmEnterProcedureAddress(reinterpret_cast<uintptr_t>(imgDos), miniVmEnterAddress, tempValued);
// Destructing class
obc.~RyujinObfuscationCore();

View File

@@ -440,7 +440,9 @@ void RyujinObfuscationCore::insertVirtualization() {
// <20> uma instru<72><75>o candidata a ser virtualizada pela minivm ??
auto isValidToSRyujinMiniVm = [&](RyujinInstruction instr) {
return instr.instruction.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER && instr.instruction.operands[1].type == ZYDIS_OPERAND_TYPE_IMMEDIATE;
return instr.instruction.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER && instr.instruction.operands[1].type == ZYDIS_OPERAND_TYPE_IMMEDIATE &&
//Ignorando registradores e opera<72><61>es de stack
(instr.instruction.operands[0].reg.value != ZYDIS_REGISTER_RSP && instr.instruction.operands[0].reg.value != ZYDIS_REGISTER_RBP);
};
// Vamos mapear o registrador do Zydis para o ASMJIT
@@ -766,8 +768,13 @@ void RyujinObfuscationCore::insertVirtualization() {
asmjit::x86::Assembler a(&code);
a.push(asmjit::x86::rcx);
a.mov(asmjit::x86::rcx, translateToMiniVmBytecode(instr.instruction.operands[0].reg.value, opType, instr.instruction.operands[1].imm.value.u));
a.mov(asmjit::x86::rax, 0x8888888888888888); // Endere<72>o a ser substituido pelo endere<72>o da nossa minivm
a.mov(asmjit::x86::rcx, translateToMiniVmBytecode(instr.instruction.operands[0].reg.value, opType, instr.instruction.operands[1].imm.value.u)); //TODO: e se o reg ex: rax j<> tiver uma valor para um add, devemos armazenar rax tamb<6D>m ?
a.emit(asmjit::x86::Inst::kIdRdgsbase, asmjit::x86::rax);
a.add(asmjit::x86::rax, 0x60);
a.mov(asmjit::x86::rax, asmjit::x86::ptr(asmjit::x86::rax));
a.add(asmjit::x86::rax, 0x10);
a.mov(asmjit::x86::rax, asmjit::x86::ptr(asmjit::x86::rax));
a.add(asmjit::x86::rax, asmjit::imm(0x88));
a.call(asmjit::x86::rax);
a.mov(mapZydisToAsmjitGp(instr.instruction.operands[0].reg.value), asmjit::x86::rax);
a.pop(asmjit::x86::rcx);
@@ -1143,6 +1150,25 @@ void RyujinObfuscationCore::applyRelocationFixupsToInstructions(uintptr_t imageB
}
void RyujinObfuscationCore::InsertMiniVmEnterProcedureAddress(uintptr_t imageBase, uintptr_t virtualAddress, std::vector<unsigned char>& new_opcodes) {
//Inserting Ryujin MiniVm Address on each vm entry reference
if (m_config.m_isVirtualized) {
auto size = new_opcodes.size();
auto data = new_opcodes.data();
unsigned char ucSignature[]{ 0x48, 0x05, 0x88, 0x00, 0x00, 0x00 };
for (auto i = 0; i < size; i++)
if (std::memcmp(&*(data + i), ucSignature, 6) == 0) {
std::printf("FIND!!\n");
std::memset(&*(data + i + 2), 0, 4);
std::memcpy(&*(data + i + 2), &virtualAddress, sizeof(uint32_t));
}
}
}
void RyujinObfuscationCore::removeOldOpcodeRedirect(uintptr_t newMappedPE, std::size_t szMapped, uintptr_t newObfuscatedAddress, bool isIgnoreOriginalCodeRemove) {
/*

View File

@@ -67,6 +67,7 @@ public:
RyujinObfuscationCore(const RyujinObfuscatorConfig& config, const RyujinProcedure& proc, uintptr_t ProcImageBase);
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 InsertMiniVmEnterProcedureAddress(uintptr_t imageBase, uintptr_t virtualAddress, std::vector<unsigned char>& new_opcodes);
BOOL Run();
RyujinProcedure getProcessedProc();
~RyujinObfuscationCore();

View File

@@ -11,17 +11,17 @@ auto main() -> int {
RyujinObfuscatorConfig config;
config.m_isIgnoreOriginalCodeRemove = FALSE;
config.m_isJunkCode = FALSE;
config.m_isJunkCode = TRUE;
config.m_isRandomSection = FALSE;
config.m_isVirtualized = TRUE;
config.m_isIatObfuscation = TRUE;
config.m_isEncryptObfuscatedCode = FALSE;
std::vector<std::string> procsToObfuscate{
//"main"
//"invoke_main",
"sum"
// "__scrt_common_main",
// "j___security_init_cookie"
"sum",
"main",
"invoke_main"
"__scrt_common_main",
"j___security_init_cookie"
};
config.m_strProceduresToObfuscate.assign(procsToObfuscate.begin(), procsToObfuscate.end());