#!/usr/bin/env python3 import struct import sys import os MAGIC = 0x584D454D # 'MEMX' HEADER_FMT = " 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 - adres wirtualny → offset w .mem") print(" offset - offset w .mem → adres wirtualny") print(" mr - base modułu + RVA → offset w .mem") print(" follow [offset] - rozwiązuje wskaźnik do obiektu pod danym adresem") print(" info - podstawowe info") print(" region - pokaż tylko info o regionie") print(" diff - 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 ") 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 ") 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 ") 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 [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(" 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 ") 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 ") 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()