diff --git a/RyujinConsole/RyujinConsole.sln b/RyujinConsole/RyujinConsole.sln index c9204ff..0218ebd 100644 --- a/RyujinConsole/RyujinConsole.sln +++ b/RyujinConsole/RyujinConsole.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 -VisualStudioVersion = 17.13.35931.197 d17.13 +VisualStudioVersion = 17.13.35931.197 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RyujinConsole", "RyujinConsole\RyujinConsole.vcxproj", "{1DC1BB2C-6B3E-4084-8F26-76852C709BB4}" EndProject diff --git a/RyujinConsole/RyujinConsole/Ryujin/Ryujin.cc b/RyujinConsole/RyujinConsole/Ryujin/Ryujin.cc index 551eb64..8d938d2 100644 --- a/RyujinConsole/RyujinConsole/Ryujin/Ryujin.cc +++ b/RyujinConsole/RyujinConsole/Ryujin/Ryujin.cc @@ -159,22 +159,31 @@ bool Ryujin::run(const RyujinObfuscatorConfig& config) { RyujinPESections peSections; peSections.AddNewSection(m_strInputFilePath, chSectionName); - //Get New Opcodes - todo: improve, this only works for the first procedure - std::vector tempValued; + uintptr_t offsetVA = 0; + std::vector opcodesWithRelocsFixed; for (auto& obc : processed_procs) { - tempValued = obc.getProcessedProc().getUpdateOpcodes(); + auto tempValued = obc.getProcessedProc().getUpdateOpcodes(); //Fix relocations - obc.applyRelocationFixupsToInstructions(reinterpret_cast(imgDos), peSections.getRyujinSectionVA(), tempValued); + obc.applyRelocationFixupsToInstructions(reinterpret_cast(imgDos), peSections.getRyujinSectionVA() + offsetVA, tempValued); + + //Removendo e adicionando um salto no procedimento original e removendo opcodes originais para um salto ao novo código ofuscado + obc.removeOldOpcodeRedirect(peSections.mappedPeDiskBaseAddress(), peSections.getRyujinMappedPeSize(), reinterpret_cast(imgDos) + peSections.getRyujinSectionVA() + offsetVA); //Destructing class obc.~RyujinObfuscationCore(); + //Inserindo procedures na lista de opcodes corrigidos + opcodesWithRelocsFixed.insert(opcodesWithRelocsFixed.end(), tempValued.begin(), tempValued.end()); + + // Incrementando o offset com o tamanho dos opcodes em questão + offsetVA += tempValued.size(); + } //Process new opcodes - peSections.ProcessOpcodesNewSection(tempValued); + peSections.ProcessOpcodesNewSection(opcodesWithRelocsFixed); //Save output file peSections.FinishNewSection(m_strOutputFilePath); diff --git a/RyujinConsole/RyujinConsole/Ryujin/RyujinCore/RyujinObfuscationCore.cc b/RyujinConsole/RyujinConsole/Ryujin/RyujinCore/RyujinObfuscationCore.cc index 5457bec..20ac756 100644 --- a/RyujinConsole/RyujinConsole/Ryujin/RyujinCore/RyujinObfuscationCore.cc +++ b/RyujinConsole/RyujinConsole/Ryujin/RyujinCore/RyujinObfuscationCore.cc @@ -97,8 +97,7 @@ void RyujinObfuscationCore::addPaddingSpaces() { code.init(runtime.environment()); asmjit::x86::Assembler a(&code); - for (auto i = 0; i < MAX_PADDING_SPACE_INSTR; i++) - a.nop(); + for (auto i = 0; i < MAX_PADDING_SPACE_INSTR; i++) a.nop(); code.flatten(); @@ -148,43 +147,45 @@ uint32_t RyujinObfuscationCore::findOpcodeOffset(const uint8_t* data, size_t dat std::vector RyujinObfuscationCore::fix_branch_near_far_short(uint8_t original_opcode, uint64_t jmp_address, uint64_t target_address) { - // Mapping short opcodes to near static const std::unordered_map SHORT_TO_NEAR = { { 0x70, 0x80 }, { 0x71, 0x81 }, { 0x72, 0x82 }, { 0x73, 0x83 }, { 0x74, 0x84 }, { 0x75, 0x85 }, { 0x76, 0x86 }, { 0x77, 0x87 }, { 0x78, 0x88 }, { 0x79, 0x89 }, { 0x7A, 0x8A }, { 0x7B, 0x8B }, { 0x7C, 0x8C }, { 0x7D, 0x8D }, { 0x7E, 0x8E }, { 0x7F, 0x8F } - + }; std::vector result; - // First tries as a short jump (2 bytes) + // Tries to handle as a short jump (2 bytes) const int short_length = 2; const int64_t short_disp = static_cast(target_address) - (jmp_address + short_length); if (short_disp >= -128 && short_disp <= 127) { - - // Keeps it as a short jump + result.push_back(original_opcode); result.push_back(static_cast(short_disp)); - + return result; } - // Converts to a near jump (6 bytes) + // If it is not a conditional jump, returns the original auto it = SHORT_TO_NEAR.find(original_opcode); - if (it == SHORT_TO_NEAR.end()) throw new std::exception("[X] RyujinObfuscationCore::fix_branch_offset_cpp: Branch opcode is not suported to regenerate a branch"); - + if (it == SHORT_TO_NEAR.end()) { + + result.push_back(original_opcode); + + return result; // Does not apply conversion + } + + // Handles as a near jump (6 bytes) const uint8_t near_opcode = it->second; const int near_length = 6; const int64_t near_disp = static_cast(target_address) - (jmp_address + near_length); - // Checks for 32-bit overflow if (near_disp < INT32_MIN || near_disp > INT32_MAX) throw std::exception("[X] Offset exceeds the limit of a 32-bit signed integer."); - // Packs the displacement (little-endian) result.push_back(0x0F); result.push_back(near_opcode); @@ -436,6 +437,44 @@ void RyujinObfuscationCore::applyRelocationFixupsToInstructions(uintptr_t imageB } +void RyujinObfuscationCore::removeOldOpcodeRedirect(uintptr_t newMappedPE, std::size_t szMapped, uintptr_t newObfuscatedAddress) { + + /* + Creating signatures to search for the opcode in the PE mapped from disk. + 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(m_proc.address), 10); + auto offsetz = findOpcodeOffset(reinterpret_cast(newMappedPE), szMapped, &ucSigature, 10); + + /* + Removing all the opcodes from the original procedure and replacing them with NOP instructions. + */ + std::memset(reinterpret_cast(newMappedPE + offsetz), 0x90, m_proc.size); + + /* + Creating a new JMP opcode in such a way that it can be added to the old region that was completely replaced by NOP, + thus redirecting execution to the new obfuscated code. + */ + unsigned char ucOpcodeJmp[5]{ + 0xE9, 0, 0, 0, 0, //JMP imm + }; + + /* + Calculating the new displacement between the original code region and the target obfuscated opcode, + calculating the relative immediate offset. + */ + const uint32_t offset = newObfuscatedAddress - (m_proc.address + 5); + + //Replacing the jump opcode with the new relative immediate displacement value. + std::memcpy(&*(ucOpcodeJmp + 1), &offset, sizeof(uint32_t)); + + //Inserting the new jump opcode into the original cleaned function to redirect execution to the fully obfuscated code. + std::memcpy(reinterpret_cast(newMappedPE + offsetz), ucOpcodeJmp, 5); + +} + RyujinObfuscationCore::~RyujinObfuscationCore() { } \ No newline at end of file diff --git a/RyujinConsole/RyujinConsole/Ryujin/RyujinCore/RyujinObfuscationCore.hh b/RyujinConsole/RyujinConsole/Ryujin/RyujinCore/RyujinObfuscationCore.hh index d670ef7..14d641a 100644 --- a/RyujinConsole/RyujinConsole/Ryujin/RyujinCore/RyujinObfuscationCore.hh +++ b/RyujinConsole/RyujinConsole/Ryujin/RyujinCore/RyujinObfuscationCore.hh @@ -14,18 +14,19 @@ class RyujinObfuscationCore { private: - const int MAX_PADDING_SPACE_INSTR = 50; + const int MAX_PADDING_SPACE_INSTR = 15; std::vector m_unusedRegisters; std::vector m_obfuscated_bb; RyujinProcedure m_proc; BOOL extractUnusedRegisters(); void addPaddingSpaces(); std::vector 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); public: RyujinObfuscationCore(const RyujinObfuscatorConfig& config, const RyujinProcedure& proc); - uint32_t findOpcodeOffset(const uint8_t* data, size_t dataSize, const void* opcode, size_t opcodeSize); void applyRelocationFixupsToInstructions(uintptr_t imageBase, DWORD virtualAddress, std::vector& new_opcodes); + void removeOldOpcodeRedirect(uintptr_t newMappedPE, std::size_t szMapped, uintptr_t newObfuscatedAddress); BOOL Run(); RyujinProcedure getProcessedProc(); ~RyujinObfuscationCore(); diff --git a/RyujinConsole/RyujinConsole/Ryujin/Utils/RyujinPESections.hh b/RyujinConsole/RyujinConsole/Ryujin/Utils/RyujinPESections.hh index 1fc55e5..b3cc774 100644 --- a/RyujinConsole/RyujinConsole/Ryujin/Utils/RyujinPESections.hh +++ b/RyujinConsole/RyujinConsole/Ryujin/Utils/RyujinPESections.hh @@ -31,11 +31,21 @@ public: return m_ucResizedPE; } + uintptr_t mappedPeDiskBaseAddress() { + + return reinterpret_cast(m_dosHeader); + } + uintptr_t getRyujinSectionSize() { return m_szNewSec; } + uintptr_t getRyujinMappedPeSize() { + + return m_szFile; + } + BOOL AddNewSection(const std::string& strInputFilePath, char chSectionName[8]); BOOL ProcessOpcodesNewSection(std::vector& opcodeData); diff --git a/RyujinConsole/RyujinConsole/RyujinConsole.cc b/RyujinConsole/RyujinConsole/RyujinConsole.cc index 1668826..ed9faad 100644 --- a/RyujinConsole/RyujinConsole/RyujinConsole.cc +++ b/RyujinConsole/RyujinConsole/RyujinConsole.cc @@ -5,7 +5,7 @@ auto main() -> int { std::cout << "Hello World!\n"; - std::unique_ptr ryujin = std::make_unique("C:\\Users\\Keowu\\Documents\\GitHub\\MoFei\\x64\\Debug\\DemoObfuscation.exe", "C:\\Users\\Keowu\\Documents\\GitHub\\MoFei\\x64\\Debug\\DemoObfuscation.pdb", "C:\\Users\\Keowu\\Documents\\GitHub\\MoFei\\x64\\Debug\\DemoObfuscation2.exe"); + std::unique_ptr ryujin = std::make_unique("C:\\Users\\Keowu\\Documents\\GitHub\\Ryujin\\RyujinConsole\\x64\\Debug\\DemoObfuscation.exe", "C:\\Users\\Keowu\\Documents\\GitHub\\Ryujin\\RyujinConsole\\x64\\Debug\\DemoObfuscation.pdb", "C:\\Users\\Keowu\\Documents\\GitHub\\Ryujin\\RyujinConsole\\x64\\Debug\\DemoObfuscation.obfuscated.exe"); ryujin.get()->listRyujinProcedures(); @@ -15,7 +15,12 @@ auto main() -> int { config.m_isRandomSection = FALSE; config.m_isVirtualized = FALSE; std::vector procsToObfuscate{ - "main" + "main", + "mainCRTStartup", + "invoke_main", + "sum", + "__scrt_common_main", + "j___security_init_cookie" }; config.m_strProceduresToObfuscate.assign(procsToObfuscate.begin(), procsToObfuscate.end());