From 487f061d6c3e5310a7493761587b53ae122472fe Mon Sep 17 00:00:00 2001 From: keowu Date: Fri, 25 Jul 2025 20:30:20 -0300 Subject: [PATCH] feat: Finalize memory protection logic, add GUI/CLI options, and fix README typos - Added the original source code that generates the memory protection stub as a comment in the stub. - Added a memory protection flag to the CLI and an option to the GUI. - Improved CLI help text to be more detailed and explanatory. - Updated README.md. --- README.md | 2 +- RyujinConsole/RyujinConsole/RyujinConsole.cc | 5 + .../RyujinCore/RyujinObfuscationCore.cc | 139 +++++++++++++++++- RyujinGUI/RyujinApp.cc | 13 ++ RyujinGUI/RyujinApp.hh | 1 + 5 files changed, 158 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9d6dedb..005166d 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ - Troll Reversers(Exclusive) - Anti-Dump - Anti-Disassembly + Anti-Decompiler -- Memory Protection(CRC32 - Planned - **TODO**) +- Memory Protection(CRC32) - Custom Passes(Planned - **TODO**) --- diff --git a/RyujinConsole/RyujinConsole/RyujinConsole.cc b/RyujinConsole/RyujinConsole/RyujinConsole.cc index aa429e8..ee3d1d7 100644 --- a/RyujinConsole/RyujinConsole/RyujinConsole.cc +++ b/RyujinConsole/RyujinConsole/RyujinConsole.cc @@ -24,6 +24,10 @@ Options: --iat Enable IAT obfuscation --random-section Use random PE section --keep-original Keep original code (don't remove it) + --AntiDebug Inserts anti-debugging capabilities and terminates the protected binary if a debugger is detected. + --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. --procs Procedures to obfuscate (default: main, invoke_main, ...) --help Show this help message @@ -87,6 +91,7 @@ auto main(int argc, char* argv[]) -> int { config.m_isTrollRerversers = has_flag(args, "--troll"); config.m_isAntiDebug = has_flag(args, "--AntiDebug"); config.m_isAntiDump = has_flag(args, "--AntiDump"); + config.m_isMemoryProtection = has_flag(args, "--MemoryProtection"); if (has_flag(args, "--procs")) { auto rawList = args["--procs"]; diff --git a/RyujinCore/Ryujin/RyujinCore/RyujinObfuscationCore.cc b/RyujinCore/Ryujin/RyujinCore/RyujinObfuscationCore.cc index ccfcd73..d6b0324 100644 --- a/RyujinCore/Ryujin/RyujinCore/RyujinObfuscationCore.cc +++ b/RyujinCore/Ryujin/RyujinCore/RyujinObfuscationCore.cc @@ -1990,7 +1990,144 @@ void RyujinObfuscationCore::insertMemoryProtection() { std::vector memoryProtectionShellcode = { /* - TODO + #pragma optimize("", off) + __declspec(noinline) __declspec(safebuffers) void RyujinMemoryCrc32Protection() { + + #ifdef _M_X64 + auto* peb = reinterpret_cast(__readgsqword(0x60)); + #else + auto* peb = reinterpret_cast(__readfsdword(0x30)); + #endif + + if (!peb || !peb->ImageBaseAddress || !peb->Ldr) return; + + auto* base = reinterpret_cast(peb->ImageBaseAddress); + auto* dos = reinterpret_cast(base); + + if (dos->e_magic != IMAGE_DOS_SIGNATURE) return; + + auto* nt = reinterpret_cast(base + dos->e_lfanew); + if (nt->Signature != IMAGE_NT_SIGNATURE) return; + + auto* section = IMAGE_FIRST_SECTION(nt); + + char ryujinName[8] { '.', 'R', 'y', 'u', 'j', 'i', 'n', 0 }; + + IMAGE_SECTION_HEADER* ryujin = nullptr; + for (int i = 0; i < nt->FileHeader.NumberOfSections; ++i, ++section) { + + bool isMatch = true; + + for (int j = 0; j < 8; ++j) + if (section->Name[j] != ryujinName[j]) { + + isMatch = false; + + break; + } + + if (isMatch) { + + ryujin = section; + + break; + } + + } + + if (!ryujin) return; + + wchar_t ntdllStr[] { 'n','t','d','l','l','.','d','l','l', 0 }; + char ntTermStr[] { 'N','t','T','e','r','m','i','n','a','t','e','P','r','o','c','e','s','s', 0 }; + + BYTE* ntdllBase = nullptr; + for (auto* link = peb->Ldr->InMemoryOrderModuleList.Flink; link != &peb->Ldr->InMemoryOrderModuleList; link = link->Flink) { + + auto* entry = CONTAINING_RECORD(link, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks); + + if (!entry->BaseDllName.Buffer) continue; + + auto* modName = entry->BaseDllName.Buffer; + auto* target = ntdllStr; + + bool match = true; + + for (; *target && *modName; ++target, ++modName) { + + wchar_t ca = (*modName >= 'A' && *modName <= 'Z') ? *modName + 0x20 : *modName; + wchar_t cb = (*target >= 'A' && *target <= 'Z') ? *target + 0x20 : *target; + if (ca != cb) { match = false; break; } + + } + + if (match && !*target && !*modName) { + + ntdllBase = reinterpret_cast(entry->DllBase); + + break; + } + + } + + if (!ntdllBase) return; + + auto* dosHdr = reinterpret_cast(ntdllBase); + auto* ntHdr = reinterpret_cast(ntdllBase + dosHdr->e_lfanew); + auto& dir = ntHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; + + if (!dir.VirtualAddress) return; + + auto* exportDir = reinterpret_cast(ntdllBase + dir.VirtualAddress); + auto* names = reinterpret_cast(ntdllBase + exportDir->AddressOfNames); + auto* ordinals = reinterpret_cast(ntdllBase + exportDir->AddressOfNameOrdinals); + auto* functions = reinterpret_cast(ntdllBase + exportDir->AddressOfFunctions); + + NtTerminateProcess_t pNtTerminate = nullptr; + + for (DWORD i = 0; i < exportDir->NumberOfNames; ++i) { + + const char* fname = reinterpret_cast(ntdllBase + names[i]); + + bool match = true; + for (int j = 0; ntTermStr[j] || fname[j]; ++j) + if (ntTermStr[j] != fname[j]) { match = false; break; } + + if (match) { + + pNtTerminate = reinterpret_cast( + ntdllBase + functions[ordinals[i]]); + + break; + } + + } + + if (pNtTerminate) { + + const uint8_t* data = base + ryujin->VirtualAddress; + + uint32_t crc = 0xFFFFFFFF; + for (size_t i = 0; i < ryujin->NumberOfLinenumbers; ++i) { + + crc ^= data[i]; + + for (int j = 0; j < 8; ++j) { + if (crc & 1) + crc = (crc >> 1) ^ 0xB0B0C400; + else + crc >>= 1; + } + + } + + auto crcvalue = crc ^ 0xFFFFFFFF; + + if (ryujin->PointerToLinenumbers != crcvalue) + pNtTerminate(reinterpret_cast(-1), crcvalue); + } + + } + #pragma optimize("", on) */ 0x48, 0x81, 0xEC, 0x58, 0x01, 0x00, 0x00, 0x65, 0x48, 0x8B, 0x04, 0x25, 0x60, 0x00, 0x00, 0x00, 0x48, 0x89, 0x84, 0x24, 0x80, 0x00, 0x00, 0x00, diff --git a/RyujinGUI/RyujinApp.cc b/RyujinGUI/RyujinApp.cc index e369e45..a878c09 100644 --- a/RyujinGUI/RyujinApp.cc +++ b/RyujinGUI/RyujinApp.cc @@ -206,6 +206,13 @@ bool RyujinApp::OnInit() { ); + m_isMemoryProtection = DrawnStyledCheckbox( + + panel, + "Memory Protection" + + ); + optionsSizer->Add( m_virtualize @@ -250,6 +257,11 @@ bool RyujinApp::OnInit() { m_isAntiDump + ); + optionsSizer->Add( + + m_isMemoryProtection + ); optionsBox->Add( @@ -697,6 +709,7 @@ auto RyujinApp::BindRunEvent(wxFrame* frame) -> void { core.m_isRandomSection = m_randomSection->IsChecked(); core.m_isVirtualized = m_virtualize->IsChecked(); core.m_isAntiDump = m_isAntiDump->IsChecked(); + core.m_isMemoryProtection = m_isMemoryProtection->IsChecked(); if (m_isAntiDebugWithTroll->IsChecked()) { diff --git a/RyujinGUI/RyujinApp.hh b/RyujinGUI/RyujinApp.hh index 6ae2547..4bf09a6 100644 --- a/RyujinGUI/RyujinApp.hh +++ b/RyujinGUI/RyujinApp.hh @@ -23,6 +23,7 @@ private: wxCheckBox* m_isAntiDebugWithTroll = nullptr; wxCheckBox* m_isAntiDebugNormal = nullptr; wxCheckBox* m_isAntiDump = nullptr; + wxCheckBox* m_isMemoryProtection = nullptr; wxListBox* m_procList = nullptr; wxGauge* m_progress = nullptr;