From 6864656381c8d55998dacc3e361931c02eb2a015 Mon Sep 17 00:00:00 2001 From: Dobin Rutishauser Date: Wed, 18 Jun 2025 21:24:35 +0200 Subject: [PATCH] refactor: make web work again (split project <-> settings) --- .gitignore | 7 +- app/storage.py | 42 ++++++------ app/templates/dev.html | 31 --------- app/templates/devs.html | 23 ------- app/templates/navigation.html | 13 +--- app/templates/project.html | 38 +++++------ app/templates/projects.html | 4 +- app/views.py | 1 + app/views_project.py | 119 +++++++++++++++++++--------------- app/views_shcdev.py | 83 ------------------------ helper.py | 19 ++---- model/defs.py | 3 +- model/project.py | 28 ++++---- model/settings.py | 90 +++++++++++++------------ phases/injector.py | 6 +- phases/templater.py | 4 +- projects/.gitkeep | 0 projects/default/.gitkeep | Bin 6 -> 0 bytes supermega.py | 50 +++++++------- web.py | 2 - 20 files changed, 214 insertions(+), 349 deletions(-) delete mode 100644 app/templates/dev.html delete mode 100644 app/templates/devs.html delete mode 100644 app/views_shcdev.py delete mode 100644 projects/.gitkeep delete mode 100644 projects/default/.gitkeep diff --git a/.gitignore b/.gitignore index b6cc594..020dbc2 100644 --- a/.gitignore +++ b/.gitignore @@ -4,16 +4,19 @@ bak/ tools/ doc/ +projects/* +!projects/default/ +!projects/default/** + data/binary/exes_more/ data/source/payload/ +data/binary/exes/* log-* *.verify.exe *.verify.dll *.infected.exe -projects/* *.test.exe -data/binary/exes/* main.obj mlink$.lnk diff --git a/app/storage.py b/app/storage.py index 1344dc6..7498d07 100644 --- a/app/storage.py +++ b/app/storage.py @@ -1,52 +1,50 @@ import pickle import os -import yaml import pickle import logging from typing import List, Tuple from model.defs import * -from model.project import WebProject - +from model.project import Settings logger = logging.getLogger("Storage") + class Storage(): def __init__(self): pass - def get_projects(self) -> List[WebProject]: - projects: List[WebProject] = [] + def get_project_settings(self) -> List[Settings]: + project_settings: List[Settings] = [] for project_name in os.listdir(PATH_WEB_PROJECT): - project = self.get_project(project_name) - if project is None: + project_setting = self.get_project_setting(project_name) + if project_setting is None: continue - projects.append(project) - return projects + project_settings.append(project_setting) + return project_settings - def get_project(self, project_name: str) -> WebProject: - logger.debug("Load project: {}".format(project_name)) + def get_project_setting(self, project_name: str) -> Settings| None: path = "{}/{}".format(PATH_WEB_PROJECT, project_name) json_path = "{}/project.pickle".format(path) if not os.path.exists(json_path): return None + logger.info("Loading project from: {}".format(json_path)) with open(json_path, "rb") as f: - project = pickle.load(f) - return project + settings = pickle.load(f) + return settings - def add_project(self, project: WebProject): - # directories and contents - os.makedirs(PATH_WEB_PROJECT + project.name, exist_ok=True) - with open("{}/{}/project.pickle".format(PATH_WEB_PROJECT, project.name), "wb") as f: - pickle.dump(project, f) + def add_project_setting(self, settings: Settings): + os.makedirs(PATH_WEB_PROJECT + settings.project_name, exist_ok=True) + with open("{}/{}/project.pickle".format(PATH_WEB_PROJECT, settings.project_name), "wb") as f: + pickle.dump(settings, f) - def save_project(self, project: WebProject): - with open("{}/{}/project.pickle".format(PATH_WEB_PROJECT, project.name), "wb") as f: - pickle.dump(project, f) + def save_project_settings(self, settings: Settings): + with open("{}/{}/project.pickle".format(PATH_WEB_PROJECT, settings.project_name), "wb") as f: + pickle.dump(settings, f) -storage = Storage() \ No newline at end of file +storage = Storage() diff --git a/app/templates/dev.html b/app/templates/dev.html deleted file mode 100644 index f1bb0b0..0000000 --- a/app/templates/dev.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - {% include 'header.html' %} - - - {% include 'navigation.html' %} - -
- -

ShcDev: {{name}}

- - - {% for file in files %} - - - - - - {% endfor %} -
{{ file['name']}}{{file["date"]}}{{file["info"]}}
- - Build - -

- -
{{log}}
- -
- - \ No newline at end of file diff --git a/app/templates/devs.html b/app/templates/devs.html deleted file mode 100644 index 54485eb..0000000 --- a/app/templates/devs.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - {% include 'header.html' %} - - - {% include 'navigation.html' %} - -
- -

ShcDevs:

- - - -
- - \ No newline at end of file diff --git a/app/templates/navigation.html b/app/templates/navigation.html index 2d660b5..805df66 100644 --- a/app/templates/navigation.html +++ b/app/templates/navigation.html @@ -18,24 +18,17 @@ Home - - - - diff --git a/app/templates/project.html b/app/templates/project.html index 97d564e..a48b93e 100644 --- a/app/templates/project.html +++ b/app/templates/project.html @@ -45,7 +45,7 @@ onchange="this.form.submit()" readonly> @@ -63,7 +63,7 @@ aria-label="SHELLCODE" onchange="this.form.submit()"> {% for shellcode in shellcodes %} @@ -71,7 +71,7 @@ - +
- {% if project.settings.plugin_guardrail != "none" %} + {% if settings.plugin_guardrail != "none" %}
@@ -279,7 +279,7 @@ aria-label="antiemulation" onchange="this.form.submit()"> {% for name in antiemulationstyles %} {% endfor %} @@ -296,7 +296,7 @@ aria-label="decoy" onchange="this.form.submit()"> {% for name in decoystyles %} {% endfor %} @@ -313,7 +313,7 @@ aria-label="virtualprotect" onchange="this.form.submit()"> {% for name in virtualprotectstyles %} {% endfor %} diff --git a/app/templates/projects.html b/app/templates/projects.html index 17ccbab..4240f46 100644 --- a/app/templates/projects.html +++ b/app/templates/projects.html @@ -11,8 +11,8 @@

Projects

diff --git a/app/views.py b/app/views.py index 9c23760..13488eb 100644 --- a/app/views.py +++ b/app/views.py @@ -15,6 +15,7 @@ logger = logging.getLogger("Views") @views.route("/") def index(): return render_template('index.html') + return redirect("/project/default", code=302) @views.route("/exes/") diff --git a/app/views_project.py b/app/views_project.py index 94996cb..0a1ca3d 100644 --- a/app/views_project.py +++ b/app/views_project.py @@ -15,7 +15,8 @@ from config import config from model.settings import Settings from model.defs import * from supermega import start -from app.storage import storage, WebProject +from app.storage import storage +from model.project import Project from sender import scannerDetectsBytes from phases.injector import verify_injected_exe from phases.templater import get_template_names @@ -36,19 +37,21 @@ thread_running = False @views_project.route("/projects") def projects_route(): - projects = storage.get_projects() - return render_template('projects.html', projects=projects) + projects_settings = storage.get_project_settings() + return render_template('projects.html', projects_settings=projects_settings) @views_project.route("/project/") def project(name): - project = storage.get_project(name) - if project == None: + project_setting = storage.get_project_setting(name) + if project_setting == None: + logger.error("Project {} not found".format(name)) return redirect("/projects", code=302) - exe_path = project.settings.inject_exe_out + project_setting.print() + is_built = False - if os.path.exists(exe_path): + if os.path.exists(project_setting.project_exe_path): is_built = True exports = [] @@ -65,16 +68,16 @@ def project(name): if config.get("avred_server") != "": has_remote = True - # when we select a shellcode - if project.settings.payload_path != "": - payload_len = os.path.getsize(project.settings.payload_path) + # payload / shellcode + if project_setting.get_payload_path() != None: + payload_len = os.path.getsize(project_setting.get_payload_path()) - # when we selected an input file - if project.settings.inject_exe_in != "" and os.path.exists(project.settings.inject_exe_in): - superpe = SuperPe(project.settings.inject_exe_in) + # injectable / exe + if project_setting.get_inject_exe_in() != None and os.path.exists(project_setting.get_inject_exe_in()): + superpe = SuperPe(project_setting.get_inject_exe_in()) #if not superpe.is_64(): # # return 500 - # return "Error: Binary {} is not 64bit".format(project.settings.inject_exe_in), 500 + # return "Error: Binary {} is not 64bit".format(project.settings.get_inject_exe_in()), 500 is_64 = superpe.is_64() is_dotnet = superpe.is_dotnet() @@ -85,17 +88,17 @@ def project(name): if rdata_section != None: data_sect_size = rdata_section.virt_size else: - logger.warning("No .rdata section found in {}".format(project.settings.inject_exe_in)) + logger.warning("No .rdata section found in {}".format(project_setting.get_inject_exe_in())) has_rodata_section = superpe.has_rodata_section() if has_rodata_section: data_sect_largest_gap_size = superpe.get_rdata_rangemanager().find_largest_gap() unresolved_dlls = pe.dllresolver.unresolved_dlls(superpe) - project_dir = os.path.dirname(os.getcwd() + "\\" + project.settings.main_dir) - log_files = get_logfiles(project.settings.main_dir) - exes = list_files_and_sizes(PATH_EXES, prepend=PATH_EXES) - exes += list_files_and_sizes(PATH_EXES_MORE, prepend=PATH_EXES_MORE) + project_dir = os.path.dirname(os.getcwd() + "\\" + project_setting.project_path) + log_files = get_logfiles(project_setting.project_path) + exes = list_files_and_sizes(PATH_EXES) + #exes += list_files_and_sizes(PATH_EXES_MORE, prepend=PATH_EXES_MORE) shellcodes = list_files_and_sizes(PATH_SHELLCODES) carrier_names = get_template_names() @@ -110,9 +113,10 @@ def project(name): return render_template('project.html', project_name = name, - project=project, + project_comment = project_setting.project_comment, is_built=is_built, project_dir=project_dir, + settings=project_setting, exes=exes, shellcodes=shellcodes, @@ -133,7 +137,7 @@ def project(name): has_rodata_section=has_rodata_section, has_remote=has_remote, - fix_missing_iat=project.settings.fix_missing_iat, + fix_missing_iat=project_setting.fix_missing_iat, guardrailstyles = guardrail_styles, antiemulationstyles = antiemulation_styles, @@ -170,18 +174,17 @@ def list_files(directory, prepend="") -> List[str]: def add_project(): if request.method == 'POST': project_name = request.form['project_name'] - - settings = Settings(project_name) comment = request.form['comment'] + # Empty settings, except name + settings = Settings(project_name) + # new project? - if storage.get_project(project_name) == None: - # Default values for web create - settings.init_payload_injectable( - FilePath("messagebox.bin"), - FilePath("data/binary/exes/procexp64.exe"), - "" - ) + if storage.get_project_setting(project_name) == None: + # Sane defaults for web + settings.injectable_base = "7z.exe" + settings.payload_base = "calc64.bin" + settings.decoder_style = "xor_2" settings.carrier_name = "alloc_rw_rx" settings.carrier_invoke_style = CarrierInvokeStyle.BackdoorCallInstr @@ -189,17 +192,20 @@ def add_project(): settings.fix_missing_iat = True # add new project - project = WebProject(project_name, settings) - project.comment = comment - storage.add_project(project) + settings.project_comment = comment + storage.add_project_setting(settings) # update project else: - settings.init_payload_injectable( - FilePath(request.form['shellcode']), - FilePath(request.form['exe']), - request.form.get('dllfunc', "") - ) + logger.info("Update project: {}".format(project_name)) + + shellcode_file = request.form['shellcode'] + injectable_file = request.form['exe'] + dll_func = request.form.get('dllfunc', "") + + settings.injectable_base = injectable_file + settings.payload_base = shellcode_file + settings.dllfunc = dll_func settings.fix_missing_iat = True if request.form.get('fix_missing_iat') != None else False settings.carrier_name = request.form['carrier_name'] @@ -216,10 +222,8 @@ def add_project(): settings.plugin_virtualprotect = request.form.get('virtualprotect', "standard") # overwrite project - project = storage.get_project(project_name) - project.settings = settings - project.comment = comment - storage.save_project(project) + #settings = storage.get_project(project_name) + storage.save_project_settings(settings) return redirect("/project/{}".format(project_name), code=302) @@ -237,14 +241,18 @@ def supermega_thread(settings: Settings): def build_project(project_name): global thread_running - project = storage.get_project(project_name) + project_settings = storage.get_project_setting(project_name) + if project_settings == None: + logger.error("Project {} not found".format(project_name)) + return redirect("/projects", code=302) - #if project.settings.inject_exe_in.endswith(".dll"): + #if project.settings.get_inject_exe_in().endswith(".dll"): # if project.settings.dllfunc == "": # logger.error("DLL injection requires a DLL function name") # return redirect("/project/{}".format(project_name), code=302) - project.settings.try_start_final_infected_exe = False + project_settings.try_start_final_infected_exe = False + project = Project(project_settings) prepare_project(project_name, project.settings) thread = Thread(target=supermega_thread, args=(project.settings, )) thread.start() @@ -266,9 +274,14 @@ def status_project(project_name): @views_project.route("/project//exec", methods=['POST', 'GET']) def start_project(project_name): - project = storage.get_project(project_name) - if project == None: + project_settings = storage.get_project_setting(project_name) + if project_settings == None: return redirect("/", code=302) + + project = Project(project_settings) + if not project.init(): + logger.error("Project {} could not be initialized".format(project_name)) + return redirect("/project/{}".format(project_name), code=302) remote = False remote_arg = request.args.get('remote') @@ -283,10 +296,10 @@ def start_project(project_name): logger.info(" Exec project: {} remote: {} no_exec: {}".format(project_name, remote, no_exec)) if remote: - logger.info(" Exec {} on server {}".format(project.settings.inject_exe_out, config.get("avred_server"))) - with open(project.settings.inject_exe_out, "rb") as f: + logger.info(" Exec {} on server {}".format(project.settings.get_inject_exe_out(), config.get("avred_server"))) + with open(project.settings.get_inject_exe_out(), "rb") as f: data = f.read() - filename = os.path.basename(project.settings.inject_exe_out) + filename = os.path.basename(project.settings.get_inject_exe_out()) try: scannerDetectsBytes(data, filename, @@ -302,11 +315,11 @@ def start_project(project_name): # Start/verify it at the end if project.settings.verify: logger.info(" Verify infected exe") - exit_code = verify_injected_exe(project.settings.inject_exe_out) + exit_code = verify_injected_exe(project.settings.get_inject_exe_out()) elif no_exec == False: - run_exe(project.settings.inject_exe_out, dllfunc=project.settings.dllfunc, check=False) + run_exe(project.settings.get_inject_exe_out(), dllfunc=project.settings.dllfunc, check=False) elif no_exec == True: - dirname = os.path.dirname(os.path.abspath(project.settings.inject_exe_out)) + dirname = os.path.dirname(os.path.abspath(project.settings.get_inject_exe_out())) logger.info(" Open folder: {}".format(dirname)) subprocess.run(['explorer', dirname]) diff --git a/app/views_shcdev.py b/app/views_shcdev.py deleted file mode 100644 index 3bfda1b..0000000 --- a/app/views_shcdev.py +++ /dev/null @@ -1,83 +0,0 @@ -from flask import Flask, Blueprint, current_app, request, redirect, url_for, render_template, send_file, make_response, session, jsonify -from threading import Thread -import os -import logging -from typing import List, Tuple -from datetime import datetime - -from observer import observer -from model.defs import * -from supermega import start -from phases.compiler import compile_dev -from phases.assembler import asm_to_shellcode -from helper import clean_tmp_files - -views_shcdev = Blueprint('views_shcdev', __name__) -logger = logging.getLogger("ViewsShcdev") - -@views_shcdev.route("/shcdev") -def devs_route(): - data = [] - for filename in os.listdir(PATH_PAYLOAD): - file_path = PATH_PAYLOAD + filename - creation_time = os.path.getctime(file_path) - readable_time = datetime.fromtimestamp(creation_time).strftime('%Y-%m-%d %H:%M:%S') - data.append({ - "name": filename, - "date": readable_time, - }) - return render_template('devs.html', data=data) - - -@views_shcdev.route("/shcdev/") -def dev_route(name): - data = [] - log = "" - path = PATH_PAYLOAD + name - for filename in os.listdir(path): - filepath = path + "/" + filename - - creation_time = os.path.getmtime(filepath) - readable_time = datetime.fromtimestamp(creation_time).strftime('%Y-%m-%d %H:%M:%S') - - info = "" - if filename.endswith(".asm"): - info = "text assembly (cleaned, from compiled .c)" - elif filename.endswith(".bin"): - info = "generated shellcode (from .exe)" - elif filename.endswith(".c"): - info = "input C code" - elif filename.endswith(".exe"): - info = "temporary shellcode holder (from .c)" - elif filename.endswith("cmdoutput.log"): - info = "command output" - with open(path + "/" + filename, "r") as f: - log += f.read() + "\n-----------------------------------\n" - elif filename.endswith("supermega.log"): - info = "supermega logging output" - with open(path + "/" + filename, "r") as f: - log += f.read() - - data.append({ - "name": filename, - "date": readable_time, - "info": info, - }) - - return render_template('dev.html', - name=name, files=data, log=log) - - -@views_shcdev.route("/shcdev//build") -def dev_build_route(name): - - c_in = PATH_PAYLOAD + "{}/main.c".format(name) - asm_out = PATH_PAYLOAD + "{}/main.asm".format(name) - build_exe = PATH_PAYLOAD + "{}/main.exe".format(name) - shellcode_out = PATH_PAYLOAD + "{}/main.bin".format(name) - - compile_dev(c_in, asm_out) - asm_to_shellcode(asm_out, build_exe, shellcode_out) - observer.write_logs(PATH_PAYLOAD + "{}/".format(name)) - clean_tmp_files() - return redirect("/shcdev/{}".format(name), code=302) diff --git a/helper.py b/helper.py index 9d39008..bbc64f2 100644 --- a/helper.py +++ b/helper.py @@ -6,7 +6,7 @@ import logging import pickle import math -from model.project import WebProject +from model.project import Project from config import config from model.defs import * from observer import observer @@ -16,15 +16,6 @@ logger = logging.getLogger("Helper") SHC_VERIFY_SLEEP = 0.2 -def write_webproject(project_name, settings): - filepath = "{}project.pickle".format(settings.main_dir) - logger.info("Write project to: {}".format(filepath)) - webProject = WebProject(project_name, settings) - webProject.comment = "Created by command line interface" - with open(filepath, "wb") as f: - pickle.dump(webProject, f) - - def clean_tmp_files(): files_to_clean = [ # compile artefacts in current working dir @@ -39,10 +30,10 @@ def clean_tmp_files(): def clean_files(settings): files_to_clean = [ # temporary files - settings.main_c_path, - settings.main_asm_path, - settings.main_shc_path, - settings.main_exe_path, + settings.project_c_path, + settings.project_asm_path, + settings.project_shc_path, + settings.project_exe_path, ] for file in files_to_clean: pathlib.Path(file).unlink(missing_ok=True) diff --git a/model/defs.py b/model/defs.py index 65f61c1..0e67415 100644 --- a/model/defs.py +++ b/model/defs.py @@ -13,14 +13,13 @@ PATH_EXES_MORE = "data/binary/exes_more/" PATH_DLLS = "data/binary/dlls/" PATH_SHELLCODES = "data/binary/shellcodes/" PATH_CARRIER = "data/source/carrier/" -PATH_PAYLOAD = "data/source/payload/" - PATH_DECODER = "data/source/decoder/" PATH_ANTIEMULATION = "data/source/antiemulation/" PATH_DECOY = "data/source/decoy/" PATH_GUARDRAILS = "data/source/guardrails/" PATH_VIRTUALPROTECT = "data/source/virtualprotect/" +PATH_PAYLOAD = "data/source/payload/" PATH_WEB_PROJECT = "projects/" diff --git a/model/project.py b/model/project.py index c9d7e70..3c84a6c 100644 --- a/model/project.py +++ b/model/project.py @@ -1,5 +1,4 @@ import logging -import shutil from model.defs import * from model.payload import Payload @@ -9,31 +8,32 @@ from model.injectable import Injectable logger = logging.getLogger("Project") -class WebProject(): - def __init__(self, name: str, settings: Settings): - self.name = name - self.settings: Settings = settings - self.comment: str = "" - - class Project(): def __init__(self, settings: Settings): - self.name: str = "" - self.comment: str = "" self.settings: Settings = settings - self.payload: Payload = Payload(self.settings.payload_path) - self.injectable: Injectable = Injectable(self.settings.inject_exe_in) - self.project_dir: str = "" - self.project_exe: str = "" + # Set by init() + self.payload: Payload + self.injectable: Injectable def init(self) -> bool: + self.payload: Payload = Payload(self.settings.get_payload_path()) + self.injectable: Injectable = Injectable(self.settings.get_inject_exe_in()) + if not self.payload.init(): return False if not self.injectable.init(): return False return True + + + def print(self): + logger.info("Project Name: {}".format(self.settings.project_name)) + logger.info("Comment: {}".format(self.settings.project_comment)) + logger.info("Settings: {}".format(self.settings.__dict__)) + logger.info("Payload Path: {}".format(self.payload.payload_path)) + logger.info("Injectable Path: {}".format(self.injectable.exe_filepath)) def prepare_project(project_name, settings): diff --git a/model/settings.py b/model/settings.py index 10c0d44..7cdb8a5 100644 --- a/model/settings.py +++ b/model/settings.py @@ -7,33 +7,46 @@ logger = logging.getLogger("Views") class Settings(): def __init__(self, project_name: str = "default"): self.project_name: str = project_name - self.payload_path: FilePath = FilePath("") + self.project_comment: str = "" + self.project_path: FilePath = FilePath("{}{}/".format(PATH_WEB_PROJECT, self.project_name)) - # Settings + # OUT: Project directories and files (based on project_path) + self.project_c_path: FilePath = FilePath(self.project_path + "main.c") + self.project_asm_path: FilePath = FilePath(self.project_path + "main.asm") + self.project_exe_path: FilePath = FilePath(self.project_path + "main.exe") + self.project_shc_path: FilePath = FilePath(self.project_path + "main.bin") + + # IN: Injectable (like "7z.exe", in data/input/exes/) + self.injectable_base: str = "" + # IN: Payload / Shellcode (like "createfile.bin", in data/input/shellcodes/) + self.payload_base: str = "" + + # Config self.carrier_name: str = "" + self.carrier_invoke_style: CarrierInvokeStyle = CarrierInvokeStyle.BackdoorCallInstr self.decoder_style: str = "xor_2" + self.payload_location: PayloadLocation = PayloadLocation.DATA self.short_call_patching: bool = False + self.fix_missing_iat = True + self.patch_show_window = True + self.dllfunc: str = "" # For DLL injection - self.plugin_antiemulation: str = "none" - self.plugin_decoy: str = "none" + # PLUGIN: Guardrail self.plugin_guardrail: str = "none" self.plugin_guardrail_data_key: str = "" self.plugin_guardrail_data_value: str = "" - self.plugin_virtualprotect: str = "standard" - self.plugin_virtualprotect_data: str = "" - self.dllfunc: str = "" # For DLL injection - - # Anti-debugging + # PLUGIN: Anti-Emulation / EDR deconditioner + self.plugin_antiemulation: str = "none" self.sir_iteration_count: int = 5 self.sir_alloc_count: int = 100 - # Injectable - self.carrier_invoke_style: CarrierInvokeStyle = CarrierInvokeStyle.BackdoorCallInstr - self.inject_exe_in: FilePath = FilePath("") - self.inject_exe_out: FilePath = FilePath("") + # PLUGIN: Other (not widely used or important) + self.plugin_virtualprotect: str = "standard" + self.plugin_virtualprotect_data: str = "" + self.plugin_decoy: str = "none" - # Debug + # DEBUG: Debug stuff (for development) self.show_command_output: bool = False self.verify: bool = False self.try_start_final_infected_exe: bool = False @@ -41,33 +54,26 @@ class Settings(): self.cleanup_files_on_exit: bool = True self.generate_asm_from_c: bool = True - # More - self.fix_missing_iat = True - self.patch_show_window = True - self.payload_location: PayloadLocation = PayloadLocation.DATA - - # directories and filenames - self.main_dir: FilePath = FilePath("{}{}/".format(PATH_WEB_PROJECT, self.project_name)) - self.main_c_path: FilePath = FilePath(self.main_dir + "main.c") - self.main_asm_path: FilePath = FilePath(self.main_dir + "main.asm") - self.main_exe_path: FilePath = FilePath(self.main_dir + "main.exe") - self.main_shc_path: FilePath = FilePath(self.main_dir + "main.bin") - self.inject_exe_out: FilePath = FilePath("{}{}".format( - self.main_dir, os.path.basename(self.inject_exe_in).replace(".exe", ".infected.exe"))) - - - def init_payload_injectable(self, shellcode: FilePath, injectable: FilePath, dll_func: str ): - self.payload_path = FilePath(PATH_SHELLCODES + shellcode) - if shellcode == "createfile.bin": - self.verify = True - self.try_start_final_infected_exe = False - else: - self.cleanup_files_on_exit = False - - self.inject_exe_in = FilePath(PATH_EXES + injectable) - self.inject_exe_out = FilePath("{}{}".format( - self.main_dir, - os.path.basename(self.inject_exe_in).replace(".exe", ".infected.exe") + def get_payload_path(self) -> FilePath: + if self.payload_base == "": + return None + return FilePath(PATH_SHELLCODES + self.payload_base) + + def get_inject_exe_in(self) -> FilePath: + if self.injectable_base == "": + return None + return FilePath(PATH_EXES + self.injectable_base) + + def get_inject_exe_out(self) -> FilePath: + return FilePath("{}{}".format( + self.project_path, + self.injectable_base.replace(".exe", ".infected.exe") )) - self.dllfunc = dll_func \ No newline at end of file + def print(self): + logger.info("Settings for project: {}".format(self.project_name)) + for attr, value in self.__dict__.items(): + if isinstance(value, FilePath): + value = str(value) + logger.info(" {}: {}".format(attr, value)) + logger.info("-" * 40) \ No newline at end of file diff --git a/phases/injector.py b/phases/injector.py index 09b5a4c..444d4c1 100644 --- a/phases/injector.py +++ b/phases/injector.py @@ -33,7 +33,7 @@ class Injector(): # superpe is a representation of the exe file. We gonna modify it, and save it at the end. # reuse from injectable - #self.superpe = SuperPe(settings.inject_exe_in) + #self.superpe = SuperPe(settings.get_inject_exe_in()) self.superpe = injectable.superpe self.function_backdoorer = FunctionBackdoorer(self.superpe) @@ -110,8 +110,8 @@ class Injector(): ## Inject def inject_exe(self): - exe_in = self.settings.inject_exe_in - exe_out = self.settings.inject_exe_out + exe_in = self.settings.get_inject_exe_in() + exe_out = self.settings.get_inject_exe_out() carrier_invoke_style: CarrierInvokeStyle = self.settings.carrier_invoke_style logger.info("-[ Injecting Carrier".format()) diff --git a/phases/templater.py b/phases/templater.py index 98dc0a2..d8cfb35 100644 --- a/phases/templater.py +++ b/phases/templater.py @@ -29,7 +29,7 @@ def create_c_from_template(settings: Settings, payload_len: int): dst = "{}{}/".format(PATH_WEB_PROJECT, settings.project_name) logger.info("-[ Carrier create Template: {}".format( - settings.main_c_path)) + settings.project_c_path)) # check that source directory exists if not os.path.exists(src): @@ -139,6 +139,6 @@ def create_c_from_template(settings: Settings, payload_len: int): 'PAYLOAD_LEN': payload_len, 'plugin_virtualprotect': plugin_virtualprotect, }) - with open(settings.main_c_path, "w", encoding='utf-8') as file: + with open(settings.project_c_path, "w", encoding='utf-8') as file: file.write(rendered_template) observer.add_text_file("main_c_rendered", rendered_template) diff --git a/projects/.gitkeep b/projects/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/projects/default/.gitkeep b/projects/default/.gitkeep deleted file mode 100644 index 49cc8ef0e116cef009fe0bd72473a964bbd07f9b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6 NcmezWkC%aq0RRg=0u=xN diff --git a/supermega.py b/supermega.py index df70d3f..7414721 100644 --- a/supermega.py +++ b/supermega.py @@ -54,10 +54,18 @@ def main(): else: setup_logging(logging.INFO) + # IN: + # Shellcode: filename + # Inject: filename + settings.injectable_base = args.inject + settings.payload_base = args.shellcode + + # Cleanup settings.try_start_final_infected_exe = args.start settings.cleanup_files_on_start = not args.no_clean_at_start settings.cleanup_files_on_exit =not args.no_clean_at_exit + # Settings settings.fix_missing_iat = not args.no_fix_iat if args.guardrail: settings.plugin_guardrail = args.guardrail @@ -71,13 +79,6 @@ def main(): settings.plugin_guardrail_data_key, settings.plugin_guardrail_data_value)) - # Shellcode: filename - # Inject: filename - settings.init_payload_injectable( - shellcode=FilePath(args.shellcode), - injectable=FilePath(args.inject), - dll_func="") - settings.decoder_style = args.decoder settings.carrier_name = args.carrier settings.payload_location = PayloadLocation.CODE # makes sense @@ -89,11 +90,10 @@ def main(): settings.carrier_invoke_style = CarrierInvokeStyle.BackdoorCallInstr settings.plugin_antiemulation = args.antiemulation - if not os.path.exists(settings.main_dir): - logger.info("Creating project directory: {}".format(settings.main_dir)) - os.makedirs(settings.main_dir) + if not os.path.exists(settings.project_path): + logger.info("Creating project directory: {}".format(settings.project_path)) + os.makedirs(settings.project_path) - write_webproject("default", settings) exit_code = start(settings) exit(exit_code) @@ -124,7 +124,7 @@ def start(settings: Settings) -> int: ret = start_real(settings) except Exception as e: logger.error(f'Error compiling: {e}') - observer.write_logs(settings.main_dir) + observer.write_logs(settings.project_path) return 1 # Cleanup files @@ -133,16 +133,16 @@ def start(settings: Settings) -> int: clean_files(settings) # Write logs (on success) - observer.write_logs(settings.main_dir) + observer.write_logs(settings.project_path) return ret def sanity_checks(settings): if 'dll_loader' in settings.carrier_name: - if not settings.payload_path.endswith(".dll"): + if not settings.get_payload_path().endswith(".dll"): raise Exception("dll loader requires a dll as payload, not shellcode") else: - if not settings.payload_path.endswith(".bin"): + if not settings.get_payload_path().endswith(".bin"): raise Exception("loader requires shellcode as payload, not DLL") @@ -158,7 +158,7 @@ def start_real(settings: Settings) -> bool: # CHECK if 64 bit if not project.injectable.superpe.is_64(): - raise Exception("Binary is not 64bit: {}".format(project.settings.inject_exe_in)) + raise Exception("Binary is not 64bit: {}".format(project.settings.get_inject_exe_in())) # Tell user if they attempt to do something stupid sanity_checks(project.settings) @@ -192,8 +192,8 @@ def start_real(settings: Settings) -> bool: if settings.generate_asm_from_c: try: phases.compiler.compile( - c_in = settings.main_c_path, - asm_out = settings.main_asm_path, + c_in = settings.project_c_path, + asm_out = settings.project_asm_path, injectable = project.injectable, settings = project.settings) except ChildProcessError as e: @@ -212,8 +212,8 @@ def start_real(settings: Settings) -> bool: # ASSEMBLE: Assemble .asm to .shc (ASM -> SHC) carrier_shellcode: bytes = phases.assembler.asm_to_shellcode( - asm_in = settings.main_asm_path, - build_exe = settings.main_exe_path) + asm_in = settings.project_asm_path, + build_exe = settings.project_exe_path) observer.add_code_file("carrier_shc", carrier_shellcode) # INJECT loader into an exe and do IAT & data references. Big task. @@ -227,13 +227,13 @@ def start_real(settings: Settings) -> bool: injector.inject_exe() except Exception as e: return False - #observer.add_code_file("exe_final", extract_code_from_exe_file_ep(settings.inject_exe_out, 300)) + #observer.add_code_file("exe_final", extract_code_from_exe_file_ep(settings.get_inject_exe_out(), 300)) # Check binary with avred if config.get("avred_server") != "": if settings.verify or settings.try_start_final_infected_exe: - filename = os.path.basename(settings.inject_exe_in) - with open(settings.inject_exe_out, "rb") as f: + filename = os.path.basename(settings.get_inject_exe_in()) + with open(settings.get_inject_exe_out(), "rb") as f: data = f.read() scannerDetectsBytes(data, filename, useBrotli=True, verify=settings.verify) else: @@ -241,12 +241,12 @@ def start_real(settings: Settings) -> bool: if settings.verify: logger.info(" Verify infected exe") payload_exit_code = phases.injector.verify_injected_exe( - settings.inject_exe_out, + settings.get_inject_exe_out(), dllfunc=settings.dllfunc) if payload_exit_code != 0: logger.warning("Payload exit code: {}".format(payload_exit_code)) elif settings.try_start_final_infected_exe: - run_exe(settings.inject_exe_out, dllfunc=settings.dllfunc, check=False) + run_exe(settings.get_inject_exe_out(), dllfunc=settings.dllfunc, check=False) if settings.plugin_guardrail != "none": logger.warning("! Remember your guardrails settings when testing") diff --git a/web.py b/web.py index eb7774c..4038fc3 100644 --- a/web.py +++ b/web.py @@ -7,7 +7,6 @@ import logging from app.views import views from app.views_project import views_project -from app.views_shcdev import views_shcdev from log import setup_logging from utils import check_deps @@ -42,5 +41,4 @@ if __name__ == "__main__": app.register_blueprint(views) app.register_blueprint(views_project) - app.register_blueprint(views_shcdev) app.run(host=args.listenip, port=args.listenport, debug=args.debug)