From efc8da2ff898618a9beb161511e037a5bfd57a31 Mon Sep 17 00:00:00 2001 From: Seliaste Date: Mon, 30 Mar 2026 12:24:08 +0200 Subject: [PATCH] Added patching of xrefs to addresses obtained with getprocaddr (the code is not yet cleaned up) --- iat.py | 125 +++++++++++++++++++++++- rsc/upx-hostname.exe.bin_iat_wave1.json | 28 +++++- 2 files changed, 147 insertions(+), 6 deletions(-) diff --git a/iat.py b/iat.py index 5235b88..a7ae358 100644 --- a/iat.py +++ b/iat.py @@ -4,6 +4,8 @@ import lief lief.disable_leak_warning() # warnings to disable for the callback +with open("lib/WindowsDllsExport/win10-19043-exports.json", "rb") as f: + api_info = json.load(f) dump_path = "rsc/wave-0001.dump" # dump_path = "rsc/wave-0002.dump" @@ -76,7 +78,64 @@ def patch_instr_to_new_IAT_entry(pe: lief.PE.Binary, call: dict[str, str], rva: patch_direct_adress_jump(pe, rva, instruction_offset) -def patch_calls_to_new_IAT( +def patch_addr_found_in_mem(pe: lief.PE.Binary, rva: int, old_addr: str): + is_32 = pe.abstract.header.is_32 + little_endian = pe.abstract.header.endianness == lief.Header.ENDIANNESS.LITTLE + # scan memory for reference to old addr + old_addr_mem_repr = hex_address_to_memory_representation( + old_addr, + is_32, + pe.abstract.header.endianness == lief.Header.ENDIANNESS.LITTLE, + ) + new_addr = hex_address_to_memory_representation( + hex(rva + pe.imagebase), + is_32, + little_endian, + ) + adresses_to_patch = [] + for section in pe.sections: + for i in range(len(section.content)): + found = True + for j in range(len(old_addr_mem_repr)): + if ( + i + j >= len(section.content) + or section.content[i + j] != old_addr_mem_repr[j] + ): + found = False + break + if found: + old_addr_ref = hex_address_to_memory_representation( + hex( + section.virtual_address + i + pe.imagebase, + ), + is_32, + little_endian, + ) + # print( + # f"ref= { + # hex( + # section.virtual_address + i + pe.imagebase, + # ) + # }" + # ) + for section in pe.sections: + for k in range(len(section.content)): + foundxref = True + for l in range(len(old_addr_ref)): + if ( + k + l < len(section.content) + and section.content[k + l] != old_addr_ref[l] + ): + foundxref = False + break + if foundxref: + adresses_to_patch.append(section.virtual_address + k) + for addr in adresses_to_patch: + print(f"patched {hex(addr)}") + pe.patch_address(addr, new_addr, lief.Binary.VA_TYPES.RVA) + + +def patch_to_new_IAT( pe: lief.PE.Binary, imp: lief.PE.Import, entry: lief.PE.ImportEntry, rva: int ): # print(f"{imp.name}!{entry.name}: 0x{rva:010x}") @@ -84,6 +143,44 @@ def patch_calls_to_new_IAT( lambda x: x["name"] == f"{imp.name.upper()}!{entry.name}", calls ): patch_instr_to_new_IAT_entry(pe, call, rva) + # patch additional non-call related info + print(entry.name) + for func in filter( + lambda x: x["name"] == entry.name and x["dll"] == imp.name, procaddr_list + ): + # print(func["name"]) + patch_addr_found_in_mem(pe, rva, func["addr"]) + + +def get_list_of_procaddr_functions(prevwave_info): + res = [] + for call in prevwave_info: + # first only including imported dlls + res_new = {} + for export in api_info: + if ( + export["dllname"] in dll_calls_list + and export["exportname"] == call["function"] + ): + res_new = { + "name": export["exportname"], + "dll": export["dllname"], + "addr": call["func_addr"], + } + break + if res_new == {}: + # try adding a new dll + for export in api_info: + if export["exportname"] == call["function"]: + res_new = { + "name": export["exportname"], + "dll": export["dllname"], + "addr": call["func_addr"], + } + break + if res_new != {}: + res.append(res_new) + return res # wave dump file to patch @@ -150,13 +247,33 @@ pe.optional_header.addressof_entrypoint = entrypoint_format # remove all current imports pe.remove_all_imports() -# recreate all DLL imports +# recreate all DLL imports from calls detected +dll_calls_list = [] +imported_dll_list = [] +func_calls_list = [] for dll in get_used_dlls(calls): + dll_calls_list.append(dll.lower()) imported_dll = pe.add_import(dll.lower()) + imported_dll_list.append(imported_dll) # recreate all function calls related to that dll import for func in get_used_functions_from_dll(dll, calls): + func_calls_list.append(func) imported_dll.add_entry(func) +# get list of functions called with getprocaddr +procaddr_list = get_list_of_procaddr_functions(iat_data["prevwave_getprocaddr"]) +for func in procaddr_list: + if func["name"] in func_calls_list: # call already added + continue + if func["dll"] in dll_calls_list: # dll already added + imported_dll_list[dll_calls_list.index(func["dll"])].add_entry(func["name"]) + else: # we need to import the new DLL + dll_calls_list.append(func["dll"]) + imported_dll = pe.add_import(func["dll"]) + imported_dll_list.append(imported_dll) + func_calls_list.append(func["name"]) + imported_dll.add_entry(func["name"]) + # At this point, the new IAT will only be constructed when the PE is written. We therefore need to make a callback function to patch calls afterwards. # Define all sections as writeable, to help with some weird stuff we're seeing @@ -171,8 +288,6 @@ for section in pe.sections: # write result config = lief.PE.Builder.config_t() config.imports = True # allows the config of the writer to write a new IAT -config.resolved_iat_cbk = ( - patch_calls_to_new_IAT # callback after the IAT has been written -) +config.resolved_iat_cbk = patch_to_new_IAT # callback after the IAT has been written pe.write("patched.exe", config) print("Wrote the patched executable as patched.exe") diff --git a/rsc/upx-hostname.exe.bin_iat_wave1.json b/rsc/upx-hostname.exe.bin_iat_wave1.json index d21e2a1..fd637b7 100644 --- a/rsc/upx-hostname.exe.bin_iat_wave1.json +++ b/rsc/upx-hostname.exe.bin_iat_wave1.json @@ -24,5 +24,31 @@ "EBP": "0x000cff94 ", "ESP": "0x000cff8c", "eflags": "0x00000203" - } + }, + "prevwave_getprocaddr": [ + { "function": "FormatMessageA", "func_addr": "0x75985fbd" }, + { "function": "LocalFree", "func_addr": "0x75962d3c" }, + { "function": "GetModuleHandleA", "func_addr": "0x75961245" }, + { "function": "GetLastError", "func_addr": "0x759611c0" }, + { "function": "__p__commode", "func_addr": "0x752c27c3" }, + { "function": "__p__fmode", "func_addr": "0x752c27ce" }, + { "function": "__set_app_type", "func_addr": "0x752c2804" }, + { "function": "_controlfp", "func_addr": "0x752be1e1" }, + { "function": "_cexit", "func_addr": "0x752c37d4" }, + { "function": "_adjust_fdiv", "func_addr": "0x753532ec" }, + { "function": "_except_handler3", "func_addr": "0x752dd770" }, + { "function": "_XcptFilter", "func_addr": "0x752ddc75" }, + { "function": "_exit", "func_addr": "0x7531b2c0" }, + { "function": "_c_exit", "func_addr": "0x7531b2db" }, + { "function": "__setusermatherr", "func_addr": "0x753477ad" }, + { "function": "_initterm", "func_addr": "0x752bc151" }, + { "function": "__getmainargs", "func_addr": "0x752c2bc0" }, + { "function": "__initenv", "func_addr": "0x753504e8" }, + { "function": "_write", "func_addr": "0x752c4078" }, + { "function": "strchr", "func_addr": "0x752bdbeb" }, + { "function": "puts", "func_addr": "0x75328d04" }, + { "function": "exit", "func_addr": "0x752c36aa" }, + { "function": "s_perror", "func_addr": "0x6c8a1be4" }, + { "function": "CharToOemBuffA", "func_addr": "0x76aeb1b0" } + ] }