diff --git a/app/views_project.py b/app/views_project.py index 24c56fe..552b86a 100644 --- a/app/views_project.py +++ b/app/views_project.py @@ -59,6 +59,7 @@ def project(name): payload_len = 0 unresolved_dlls = [] has_remote = False + has_rodata_section = False if config.get("avred_server") != "": has_remote = True diff --git a/model/defs.py b/model/defs.py index 3b72049..b893915 100644 --- a/model/defs.py +++ b/model/defs.py @@ -22,13 +22,16 @@ PATH_WEB_PROJECT = "projects/" # Correlated with real template files # in data/plugins/ - - class DecoderStyle(Enum): PLAIN_1 = "plain_1" XOR_1 = "xor_1" +class PayloadLocation(Enum): + CODE = "code" + DATA = "data" + + class CarrierInvokeStyle(Enum): ChangeEntryPoint = "change AddressOfEntryPoint" BackdoorCallInstr = "hijack branching instruction in entrypoint" diff --git a/model/settings.py b/model/settings.py index 5b628aa..d0bb990 100644 --- a/model/settings.py +++ b/model/settings.py @@ -5,7 +5,7 @@ logger = logging.getLogger("Views") class Settings(): - def __init__(self, web=""): + def __init__(self): self.payload_path: FilePath = "" # Settings @@ -29,6 +29,10 @@ class Settings(): self.generate_asm_from_c: bool = True self.generate_shc_from_asm: bool = True + # More + self.fix_missing_iat = False + self.payload_location = PayloadLocation.CODE + def prep_web(self, project_name): self.main_dir = "{}{}/".format(PATH_WEB_PROJECT, project_name) diff --git a/phases/assembler.py b/phases/assembler.py index d21e1cc..7e69ea4 100644 --- a/phases/assembler.py +++ b/phases/assembler.py @@ -9,9 +9,9 @@ from helper import * logger = logging.getLogger("Assembler") -def asm_to_shellcode(asm_in: FilePath, build_exe: FilePath, shellcode_out: FilePath): +def asm_to_shellcode(asm_in: FilePath, build_exe: FilePath) -> bytes: """Takes ASM source file asm_in, compiles it into build_exe, extracts its code section and write into shellcode_out""" - logger.info("--[ Assemble to exe: {} -> {} -> {}".format(asm_in, build_exe, shellcode_out)) + logger.info("--[ Assemble to exe: {} -> {}".format(asm_in, build_exe)) run_process_checkret([ config.get("path_ml64"), asm_in, @@ -22,24 +22,14 @@ def asm_to_shellcode(asm_in: FilePath, build_exe: FilePath, shellcode_out: FileP if not os.path.isfile(build_exe): raise Exception("Compiling failed") code = extract_code_from_exe_file(build_exe) - observer.add_code_file("carrier_shc", code) - with open(shellcode_out, 'wb') as f: - f.write(code) + return code def merge_loader_payload( - shellcode_in: FilePath, - shellcode_out: FilePath, + shellcode_in: bytes, payload_data: bytes, decoder_style: DecoderStyle -): - logger.info("--[ Merge stager with payload -> {}".format( - shellcode_out)) - observer.add_code_file("payload_shc", payload_data) - - with open(shellcode_in, 'rb') as input1: - data_stager = input1.read() - +) -> bytes: if decoder_style == DecoderStyle.PLAIN_1: # Nothing to do pass @@ -48,12 +38,7 @@ def merge_loader_payload( logger.info("---[ XOR payload with key 0x{:X}".format(xor_key)) payload_data = bytes([byte ^ xor_key for byte in payload_data]) - logger.info("---[ Size: Stager: {} and Payload: {} Sum: {} ".format( - len(data_stager), len(payload_data), len(data_stager)+len(payload_data))) + logger.info("---[ Size: Carrier: {} and Payload: {} Sum: {} ".format( + len(shellcode_in), len(payload_data), len(shellcode_in)+len(payload_data))) - with open(shellcode_out, 'wb') as output: - # append them - data = data_stager + payload_data - output.write(data) - observer.add_code_file("loader_shc", data) - \ No newline at end of file + return shellcode_in + payload_data diff --git a/phases/injector.py b/phases/injector.py index 81e9e80..99e227a 100644 --- a/phases/injector.py +++ b/phases/injector.py @@ -16,25 +16,23 @@ logger = logging.getLogger("Injector") def inject_exe( - main_shc_path: FilePath, - settings: Settings, - project: Project, + main_shc: bytes, + settings: Settings, # Temp + carrier: Carrier, ): - shellcode_in = project.payload.payload_path exe_in = settings.inject_exe_in exe_out = settings.inject_exe_out carrier_invoke_style: CarrierInvokeStyle = settings.carrier_invoke_style source_style: FunctionInvokeStyle = settings.source_style - logger.info("--[ Injecting: {} into {} -> {}".format( - shellcode_in, exe_in, exe_out + logger.info("--[ Injecting: into {} -> {}".format( + exe_in, exe_out )) # Read prepared loader shellcode # And check if it fits into the target code section - main_shc = file_readall_binary(main_shc_path) shellcode_len = len(main_shc) - code_sect_size = project.carrier.superpe.get_code_section().Misc_VirtualSize + code_sect_size = carrier.superpe.get_code_section().Misc_VirtualSize if shellcode_len + 128 > code_sect_size: raise Exception("Error: Shellcode {}+128 too small for target code section {}".format( shellcode_len, code_sect_size @@ -46,7 +44,7 @@ def inject_exe( # Patch IAT if necessary if source_style == FunctionInvokeStyle.iat_reuse: - for iatRequest in project.carrier.get_all_iat_requests(): + for iatRequest in carrier.get_all_iat_requests(): iat_name = superpe.get_replacement_iat_for("KERNEL32.dll", iatRequest.name) superpe.patch_iat_entry("KERNEL32.dll", iat_name, iatRequest.name) @@ -54,7 +52,7 @@ def inject_exe( shellcode_offset: int = 0 # file offset if superpe.is_dll() and settings.dllfunc != "" and carrier_invoke_style == CarrierInvokeStyle.ChangeEntryPoint: - # Special case. put it at the beginning of the exported DLL function + # Special case: DLL exported function direct overwrite logger.info("--[ Overwrite DLL function {} with shellcode".format(settings.dllfunc)) rva = superpe.getExportEntryPoint(settings.dllfunc) @@ -118,8 +116,8 @@ def inject_exe( function_backdoorer.backdoor_function(addr, shellcode_rva, shellcode_len) if source_style == FunctionInvokeStyle.iat_reuse: - injected_fix_iat(superpe, project.carrier) - injected_fix_data(superpe, project.carrier) + injected_fix_iat(superpe, carrier) + injected_fix_data(superpe, carrier) # changes from console to UI (no console window) if necessary superpe.patch_subsystem() diff --git a/supermega.py b/supermega.py index 92fb2c1..72fc440 100644 --- a/supermega.py +++ b/supermega.py @@ -167,18 +167,21 @@ def start_real(settings: Settings): # Assemble: Assemble .asm to .shc (ASM -> SHC) if settings.generate_shc_from_asm: - phases.assembler.asm_to_shellcode( + carrier_shellcode: bytes = phases.assembler.asm_to_shellcode( asm_in = settings.main_asm_path, - build_exe = settings.main_exe_path, - shellcode_out = settings.main_shc_path) - + build_exe = settings.main_exe_path) + observer.add_code_file("carrier_shc", carrier_shellcode) + # Merge: shellcode/loader with payload (SHC + PAYLOAD -> SHC) - if True: - phases.assembler.merge_loader_payload( - shellcode_in = settings.main_shc_path, - shellcode_out = settings.main_shc_path, + if settings.payload_location == PayloadLocation.CODE: + logger.info("--[ Merge carrier with payload".format()) + full_shellcode = phases.assembler.merge_loader_payload( + shellcode_in = carrier_shellcode, payload_data = project.payload.payload_data, decoder_style = settings.decoder_style) + observer.add_code_file("full_shc", full_shellcode) + elif settings.payload_location == PayloadLocation.DATA: + logger.error("Not impolemented yet: PayloadLocation.DATA") # RWX Injection (optional): obfuscate loader+payload #if project.exe_host.rwx_section != None: @@ -190,7 +193,7 @@ def start_real(settings: Settings): # shutil.move(settings.main_shc_path + ".sgn", settings.main_shc_path) # inject merged loader into an exe - phases.injector.inject_exe(settings.main_shc_path, settings, project) + phases.injector.inject_exe(full_shellcode, settings, project.carrier) observer.add_code_file("exe_final", extract_code_from_exe_file_ep(settings.inject_exe_out, 300)) if config.get("avred_server") != "":