import re, struct exe = open("HAVOC_NOCD.EXE", "rb").read() # ── Step 1: Find GetPrivateProfileStringA in the import name table ── # Import names are stored as: hint(2 bytes) + NUL-terminated name # Scan ALL occurrences of this name in the file gpps_positions = [] for m in re.finditer(b'GetPrivateProfileStringA\x00', exe): gpps_positions.append(m.start()) print(f"GetPrivateProfileStringA name at file offset 0x{m.start():06x}") if not gpps_positions: print("NOT FOUND in EXE") import sys; sys.exit(1) # The IAT entry for this function is at a dword that originally holds the INT pointer. # Find that dword by scanning for the VA of each occurrence as an INT entry. # Then find CALL [mem] (FF 15 xx xx xx xx) instructions referencing the IAT slot. print("\n=== Scanning for CALL GetPrivateProfileStringA ===") call_sites = [] for name_fo in gpps_positions: # The INT entry value = VA of hint+name = image_base + RVA of (name_fo - 2) hint_fo = name_fo - 2 # This is in a different section (idata), compute its VA # idata is usually mapped near rdata; let's scan for its VA as a dword anywhere # Actually: try both rdata and idata VA mappings for section_offset, section_rva in [(0x000400, 0x001000), (0x06ac00, 0x07bc00)]: rva_of_hint = (hint_fo - section_offset + section_rva) va_of_hint = 0x400000 + rva_of_hint needle = struct.pack('= len(exe): return f"" try: end = exe.index(b'\x00', fo) return exe[fo:end].decode('latin1', errors='replace') except: return f"" print("\n=== GetPrivateProfileStringA parameters at each call site ===") for cfo in sorted(call_sites): # Scan backward up to 128 bytes for PUSH imm32 (0x68) instructions ctx_start = max(0x400, cfo - 128) ctx = exe[ctx_start:cfo] pushes = [] # list of (offset_in_ctx, value) pos = 0 while pos < len(ctx): b = ctx[pos] if b == 0x68: # PUSH imm32 if pos + 5 <= len(ctx): val = struct.unpack_from('