"""add_import.py Patch a PE EXE's import table to add a new DLL import. The OS loader will pull into the process before the EXE's entry point runs — exactly what we want for leakfix.dll. Mechanism: 1. Read the PE file. 2. Add a new section called ".limport" at the end with: - new IMAGE_IMPORT_DESCRIPTOR array (existing entries + ours + null) - ILT (Import Lookup Table) and IAT for our DLL — both pointing at a single hint/name "LeakfixStub" (any name; doesn't have to exist since loading the DLL is enough to trigger its DllMain). - The DLL name string. - Hint/name table for our exported function. 3. Update OptionalHeader.DataDirectory[1] (IMPORT) to point at our new array, with the size covering all entries. 4. Write the new file. We must pick an export name that exists in leakfix.dll for the loader to resolve at load time, OR we can use ordinal #1 if we export by ordinal. Simplest: have leakfix.dll export a stub function named "leakfix_init" via __declspec(dllexport), and reference that here. """ import struct, sys, os PE_MACHINE_I386 = 0x014c def u8(b, o): return b[o] def u16(b, o): return struct.unpack_from(" len(data): data += b"\0" * (new_roff - len(data)) # Now we know RVAs. Build section bytes. sec = bytearray(raw_size) # Copy existing descriptors verbatim, then append our descriptor, then zero sec[0:len(existing_descs)] = existing_descs our_desc_off = len(existing_descs) # Our descriptor: ILT_RVA, TimeStamp, ForwarderChain, Name_RVA, IAT_RVA our_ilt_rva = new_vaddr + ilt_off our_iat_rva = new_vaddr + iat_off our_name_rva = new_vaddr + dll_name_off name_entry_rva = new_vaddr + name_table_off struct.pack_into(" (size_of_headers - section_table_off + pe_off) // 40: # Not enough room in headers for another section entry. Bail. print("ERROR: no room in PE headers for an additional section entry"); sys.exit(3) new_sh = section_table_off + num_sections * 40 name_bytes = b".limport"[:8].ljust(8, b"\0") data[new_sh:new_sh+8] = name_bytes struct.pack_into("