Five bugs identified and patched in retail Asheron's Call client: - v3b: palette refcount over-increment (3-byte NOP at two sites) - v5: RenderSurface PurgeResource no-op stub (vtable slot 2 thunk) - v11: two dangling-pointer crash guards (NULL-check + reorder) - v14: CEnvCell::Destroy ClipPlaneList leak (18-byte JMP to cleanup thunk) - v22: unpacker stale-pointer SEH guard (whole-function __try/__except) All five ship in leakfix.dll (117 KB, SHA d282f23c…) which is loaded by acclient.exe at process start via PE import table patching by tools/install_leakfix.py. Controlled 15-client fleet soak: unpatched control died at 26h with palette exhaustion; all 14 patched clients survived past that point and reached ≥5-day uptime. Residual ~15 MB/h growth traced to d3d9.dll's internal slab allocator (260KB surface backing buffers retained after Release). See REPORT.md §10 for the full investigation; conclusion is that it's unfixable from outside d3d9. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
37 lines
1.5 KiB
Python
37 lines
1.5 KiB
Python
"""dump_va.py <exe_path> <va> [n=64]
|
|
Dump n bytes from a PE file at a given VA (using image base + .text section)."""
|
|
import struct, sys
|
|
|
|
def main():
|
|
path = sys.argv[1]
|
|
va = int(sys.argv[2], 0)
|
|
n = int(sys.argv[3]) if len(sys.argv) > 3 else 64
|
|
|
|
with open(path, 'rb') as f:
|
|
data = f.read()
|
|
pe_off = struct.unpack_from('<I', data, 0x3C)[0]
|
|
n_sec = struct.unpack_from('<H', data, pe_off + 4 + 2)[0]
|
|
opt_sz = struct.unpack_from('<H', data, pe_off + 4 + 16)[0]
|
|
opt_off = pe_off + 4 + 20
|
|
img_base = struct.unpack_from('<I', data, opt_off + 28)[0]
|
|
sec_off = opt_off + opt_sz
|
|
sections = []
|
|
for i in range(n_sec):
|
|
s = sec_off + i * 40
|
|
name = data[s:s+8].rstrip(b'\x00').decode(errors='replace')
|
|
vaddr = struct.unpack_from('<I', data, s + 12)[0]
|
|
vsize = struct.unpack_from('<I', data, s + 8)[0]
|
|
raddr = struct.unpack_from('<I', data, s + 20)[0]
|
|
rsize = struct.unpack_from('<I', data, s + 16)[0]
|
|
sections.append((name, img_base + vaddr, vsize, raddr, rsize))
|
|
for name, sec_va, vsize, raddr, rsize in sections:
|
|
if sec_va <= va < sec_va + vsize:
|
|
file_off = (va - sec_va) + raddr
|
|
chunk = data[file_off:file_off + n]
|
|
print(f"@ 0x{va:08x} section=[{name}] file_off=0x{file_off:08x}")
|
|
print(' '.join(f'{b:02x}' for b in chunk))
|
|
return
|
|
print(f"VA 0x{va:08x} not in any section")
|
|
|
|
if __name__ == '__main__':
|
|
main()
|