Files
RABIDS/main.py
2025-12-07 22:38:46 +05:00

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_())