Added some scripts

This commit is contained in:
2026-03-05 21:23:58 +00:00
commit 9dc113e7cb
4 changed files with 1493 additions and 0 deletions

408
mem_idx_helper.py Normal file
View File

@@ -0,0 +1,408 @@
#!/usr/bin/env python3
import struct
import sys
import os
MAGIC = 0x584D454D # 'MEMX'
HEADER_FMT = "<IIII"
REGION_FMT = "<IIIIQ"
HEADER_SIZE = struct.calcsize(HEADER_FMT)
REGION_SIZE = struct.calcsize(REGION_FMT)
class RegionIdx:
__slots__ = ("base", "size", "protect", "type", "file_offset")
def __init__(self, base, size, protect, rtype, file_offset):
self.base = base
self.size = size
self.protect = protect
self.type = rtype
self.file_offset = file_offset
def __repr__(self):
return (f"Region(base=0x{self.base:08X}, size=0x{self.size:X}, "
f"protect=0x{self.protect:X}, type=0x{self.type:X}, "
f"file_offset=0x{self.file_offset:X})")
def load_index(path):
with open(path, "rb") as f:
header_data = f.read(HEADER_SIZE)
if len(header_data) != HEADER_SIZE:
raise RuntimeError("Index file too short (no header)")
magic, version, count, reserved = struct.unpack(HEADER_FMT, header_data)
if magic != MAGIC:
raise RuntimeError(f"Bad magic 0x{magic:08X}, expected 0x{MAGIC:08X}")
if version != 1:
raise RuntimeError(f"Unsupported version {version}")
regions = []
for i in range(count):
data = f.read(REGION_SIZE)
if len(data) != REGION_SIZE:
raise RuntimeError(f"Index truncated at region {i}/{count}")
base, size, protect, rtype, file_offset = struct.unpack(REGION_FMT, data)
regions.append(RegionIdx(base, size, protect, rtype, file_offset))
return regions
def find_region_for_va(va, regions):
# TODO: maybe use binsearch, but for about 600 regions it's working fine
for r in regions:
if r.base <= va < r.base + r.size:
return r
return None
def find_region_for_offset(offset, regions):
# Find region that contains the given file offset
for r in regions:
if r.file_offset <= offset < r.file_offset + r.size:
return r
return None
def parse_int(s):
s = s.strip()
return int(s, 0)
def diff_dumps(regions1, regions2, memdump1, memdump2):
# Create maps for quick lookup
regions1_map = {r.base: r for r in regions1}
regions2_map = {r.base: r for r in regions2}
# Find region differences
bases1 = set(regions1_map.keys())
bases2 = set(regions2_map.keys())
only_in_1 = bases1 - bases2
only_in_2 = bases2 - bases1
common = bases1 & bases2
print(f"[*] Analiza struktury regionów:")
print(f" Tylko w zrzucie 1: {len(only_in_1)} regionów")
print(f" Tylko w zrzucie 2: {len(only_in_2)} regionów")
print(f" Wspólnych regionów: {len(common)}")
print()
if only_in_1:
print("[+] Regiony usunięte (tylko w zrzucie 1):")
for base in sorted(only_in_1)[:10]: # Show max 10
r = regions1_map[base]
print(f" 0x{r.base:08X} - 0x{r.base + r.size:08X} (size=0x{r.size:X})")
if len(only_in_1) > 10:
print(f" ... i {len(only_in_1) - 10} więcej")
print()
if only_in_2:
print("[+] Regiony dodane (tylko w zrzucie 2):")
for base in sorted(only_in_2)[:10]:
r = regions2_map[base]
print(f" 0x{r.base:08X} - 0x{r.base + r.size:08X} (size=0x{r.size:X})")
if len(only_in_2) > 10:
print(f" ... i {len(only_in_2) - 10} więcej")
print()
# Continue only if there are common regions
if not common:
print("[!] Brak wspólnych regionów do porównania")
return
# Check if memory dump files exist
if not os.path.isfile(memdump1):
print(f"[!] Brak pliku zrzutu pamięci: {memdump1}")
return
if not os.path.isfile(memdump2):
print(f"[!] Brak pliku zrzutu pamięci: {memdump2}")
return
print("[*] Porównywanie zawartości wspólnych regionów...")
changed_regions = []
with open(memdump1, "rb") as f1, open(memdump2, "rb") as f2:
for base in sorted(common):
r1 = regions1_map[base]
r2 = regions2_map[base]
# Check region size
if r1.size != r2.size:
changed_regions.append({
'base': base,
'type': 'size_changed',
'old_size': r1.size,
'new_size': r2.size
})
continue
# Read region data from both dumps
f1.seek(r1.file_offset)
data1 = f1.read(r1.size)
f2.seek(r2.file_offset)
data2 = f2.read(r2.size)
if len(data1) != r1.size or len(data2) != r2.size:
print(f"[!] Błąd odczytu dla regionu 0x{base:08X}")
continue
# Compare byte by byte
if data1 != data2:
# Find differing byte ranges
diffs = []
diff_start = None
for i in range(len(data1)):
if data1[i] != data2[i]:
if diff_start is None:
diff_start = i
else:
if diff_start is not None:
diffs.append((diff_start, i - 1))
diff_start = None
# Handle case where difference goes to the end
if diff_start is not None:
diffs.append((diff_start, len(data1) - 1))
changed_regions.append({
'base': base,
'type': 'content_changed',
'size': r1.size,
'diffs': diffs,
'total_changed': sum(end - start + 1 for start, end in diffs)
})
if not changed_regions:
print("[+] Brak różnic w zawartości wspólnych regionów!")
return
print(f"[+] Znaleziono {len(changed_regions)} zmienionych regionów:")
print()
for change in changed_regions[:20]: # Show max 20
base = change['base']
if change['type'] == 'size_changed':
print(f" Region 0x{base:08X}:")
print(f" Zmiana rozmiaru: 0x{change['old_size']:X} -> 0x{change['new_size']:X}")
else:
print(f" Region 0x{base:08X} (size=0x{change['size']:X}):")
print(f" Zmienione bajty: {change['total_changed']} / {change['size']} ({100.0 * change['total_changed'] / change['size']:.2f}%)")
print(f" Zakresów różnic: {len(change['diffs'])}")
# Show first 5 diff ranges
for start, end in change['diffs'][:5]:
va_start = base + start
va_end = base + end
print(f" VA 0x{va_start:08X} - 0x{va_end:08X} (offset +0x{start:X}, {end - start + 1} bajtów)")
if len(change['diffs']) > 5:
print(f" ... i {len(change['diffs']) - 5} więcej zakresów")
print()
if len(changed_regions) > 20:
print(f"... i {len(changed_regions) - 20} więcej zmienionych regionów")
print()
total_bytes_changed = sum(c.get('total_changed', 0) for c in changed_regions if c['type'] == 'content_changed')
print(f"[=] Podsumowanie: łącznie ~{total_bytes_changed} bajtów zmienionych")
def main():
if len(sys.argv) < 2:
print("Użycie: python mem_idx_helper.py Sekai_xxx.idx")
sys.exit(1)
idx_path = sys.argv[1]
if not os.path.isfile(idx_path):
print(f"Brak pliku: {idx_path}")
sys.exit(1)
print(f"[+] Wczytywanie indeksu z: {idx_path}")
regions = load_index(idx_path)
print(f"[+] Załadowano {len(regions)} regionów")
mem_guess = idx_path.rsplit(".", 1)[0] + ".mem"
print(f"[i] Zakładany plik .mem: {mem_guess}")
print()
print("Tryb interaktywny. Komendy:")
print(" va <addr> - adres wirtualny → offset w .mem")
print(" offset <offset> - offset w .mem → adres wirtualny")
print(" mr <base> <rva> - base modułu + RVA → offset w .mem")
print(" follow <addr> [offset] - rozwiązuje wskaźnik do obiektu pod danym adresem")
print(" info - podstawowe info")
print(" region <addr> - pokaż tylko info o regionie")
print(" diff <idx_path> - pokaż różnice pomiędzy dwoma zrzutami pamięci")
print(" q / quit / exit - wyjście")
print()
print("Przykłady:")
print(" va 0x14F80BD0")
print(" offset 0x1A3F4B0 # offset w .mem → VA")
print(" mr 0x0FFA0000 0xD354 # vtable Sekai: base + RVA")
print()
while True:
try:
line = input("> ").strip()
except (EOFError, KeyboardInterrupt):
print()
break
if not line:
continue
parts = line.split()
cmd = parts[0].lower()
if cmd in ("q", "quit", "exit"):
break
if cmd == "info":
total_size = sum(r.size for r in regions)
print(f"Regionów: {len(regions)}, łączny rozmiar: 0x{total_size:X} ({total_size} bajtów)")
continue
if cmd == "va":
if len(parts) != 2:
print("Użycie: va <addr>")
continue
try:
va = parse_int(parts[1])
except ValueError as e:
print(f"Błąd parsowania adresu: {e}")
continue
r = find_region_for_va(va, regions)
if not r:
print(f"VA 0x{va:08X} nie należy do żadnego regionu")
continue
offset = r.file_offset + (va - r.base)
print(f"VA 0x{va:08X} -> region base=0x{r.base:08X}, size=0x{r.size:X}")
print(f" file_offset=0x{r.file_offset:X} -> offset w .mem = 0x{offset:X} ({offset} dec)")
continue
if cmd == "offset" or cmd == "off":
if len(parts) != 2:
print("Użycie: offset <offset>")
continue
try:
offset = parse_int(parts[1])
except ValueError as e:
print(f"Błąd parsowania offsetu: {e}")
continue
r = find_region_for_offset(offset, regions)
if not r:
print(f"Offset 0x{offset:X} nie należy do żadnego regionu")
continue
va = r.base + (offset - r.file_offset)
rva = va - r.base
print(f"Offset w .mem 0x{offset:X} ({offset} dec)")
print(f" -> region base=0x{r.base:08X}, size=0x{r.size:X}")
print(f" -> VA = 0x{va:08X}")
print(f" -> RVA (offset w regionie) = 0x{rva:X}")
continue
if cmd == "mr":
if len(parts) != 3:
print("Użycie: mr <base> <rva>")
continue
try:
base = parse_int(parts[1])
rva = parse_int(parts[2])
except ValueError as e:
print(f"Błąd parsowania: {e}")
continue
va = base + rva
r = find_region_for_va(va, regions)
if not r:
print(f"VA 0x{va:08X} (base+RVA) nie należy do żadnego regionu")
continue
offset = r.file_offset + (va - r.base)
print(f"BASE 0x{base:08X} + RVA 0x{rva:X} = VA 0x{va:08X}")
print(f"VA 0x{va:08X} -> region base=0x{r.base:08X}, size=0x{r.size:X}")
print(f" file_offset=0x{r.file_offset:X} -> offset w .mem = 0x{offset:X} ({offset} dec)")
continue
if cmd == "follow":
if len(parts) != 2 and len(parts) != 3:
print("Użycie: follow <addr> [offset]")
continue
try:
va = parse_int(parts[1])
offset = 0 if len(parts) == 2 else parse_int(parts[2])
except ValueError as e:
print(f"Błąd parsowania adresu: {e}")
continue
if offset != 0:
va += offset
r = find_region_for_va(va, regions)
if not r:
print(f"VA 0x{va:08X} nie należy do żadnego regionu")
continue
offset = r.file_offset + (va - r.base)
print(f"Rozwiązywanie wskaźnika pod VA 0x{va:08X} (offset w .mem: 0x{offset:X})")
print(f" -> region base=0x{r.base:08X}, size=0x{r.size:X}")
print(f" -> file_offset=0x{r.file_offset:X}")
print(f" -> offset w .mem = 0x{offset:X}")
print(f" -> VA = 0x{va:08X}")
print(f" -> RVA (offset w regionie) = 0x{va - r.base:X}")
try:
with open(mem_guess, "rb") as f:
f.seek(offset)
data = f.read(4) # Assuming 32-bit pointer
if len(data) != 4:
print(f"Nie można odczytać wskaźnika z offsetu 0x{offset:X}")
continue
pointed_va = struct.unpack("<I", data)[0]
print(f" -> Wartość wskaźnika: VA 0x{pointed_va:08X}")
continue
except OSError as e:
print(f"Nie można otworzyć pliku zrzutu pamięci: {e}")
continue
if cmd == "region":
if len(parts) != 2:
print("Użycie: region <addr>")
continue
try:
va = parse_int(parts[1])
except ValueError as e:
print(f"Błąd parsowania adresu: {e}")
continue
r = find_region_for_va(va, regions)
if not r:
print(f"VA 0x{va:08X} nie należy do żadnego regionu")
continue
print(r)
continue
if cmd == "diff":
if len(parts) != 2:
print("Użycie: diff <idx_path>")
continue
try:
other_regions = load_index(parts[1])
except RuntimeError as e:
print(f"Błąd wczytywania indexu: {e}")
continue
other_mem_guessed = parts[1].rsplit(".", 1)[0] + ".mem"
print(f"Analiza różnic między zrzutami")
print(f" {idx_path} -> {parts[1]}")
print(f" {mem_guess} -> {other_mem_guessed}")
print()
diff_dumps(regions, other_regions, mem_guess, other_mem_guessed)
continue
print("Nieznana komenda. Dostępne: va, offset, mr, info, region, diff, quit")
if __name__ == "__main__":
main()