Ghidra headless post-script (pyghidra/Jython) that extracts the scripting
"surface" of Aidem Media engine DLLs into a versionable snapshot.json, for
diffing engine versions. All four axes validated on the golden pair
(PIKLIB8.dll / MSVC6 vs bloomoodll.dll / MSVC8):
- types : CMC_ObjectsContainer::resolve factory ladder
(script name -> C++ class, ctor, object size; + dispatch_addr,
via_module_iface for the dual MULTIARRAY branch)
- methods : CMC_*_Runner::prepareMthHashSet (name -> id) + inheritance chain
- events : CMC_*::getBehavioursList (ordered per-class list)
- fields : CMC_* ctor -> CMElement::getProperty<T>Value (name + type)
(+ bonus struct_layout: this+offset stores via decompiler P-code)
Extraction rests on semantic anchors (call targets, referenced string
literals, push/immediate operands), never decompiled-C text, so the same
script works across both compilers despite ILT stubs, undefined string
literals, unnamed FUN_ ctors and an MSVC6 inline-strcpy off-by-one.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
55 lines
2.7 KiB
Markdown
55 lines
2.7 KiB
Markdown
# aidem_media_playground
|
|
|
|
Narzędzie do **analizy różnicowej** silników gier Aidem Media (Piklib / BlooMoo).
|
|
Cel: katalog gier (z ISO/ZIP), wersje silnika z hashami, oraz ekstrakcja i porównywanie
|
|
"powierzchni silnika" — typów, metod, eventów i pól klas `CMC_*` — między wersjami.
|
|
|
|
## Status
|
|
|
|
Faza 1: **ekstraktor `snapshot.json` z Ghidry** (walidacja na golden pair PIKLIB8 ↔ bloomoo).
|
|
Infrastruktura (FastAPI + worker + DB + front) dochodzi dopiero, gdy format snapshotu
|
|
będzie sprawdzony na realnych binariach.
|
|
|
|
## Architektura (docelowa)
|
|
|
|
Modularny monolit + worker. Backend Python/FastAPI tylko zleca, wersjonuje i diffuje.
|
|
Cała ekstrakcja żyje w **workerze = Ghidra headless + ten skrypt**, bo wymaga dostępu
|
|
do call-grafu, referencji i vtable. Worker emituje `snapshot.json`, monolit go konsumuje.
|
|
|
|
```
|
|
Front (centrum dowodzenia) ─ FastAPI (katalog/hashe/diff) ─ PostgreSQL
|
|
│ kolejka
|
|
Worker: Ghidra headless + extract_engine_surface.py
|
|
```
|
|
|
|
## Zasada ekstrakcji
|
|
|
|
Ekstrakcja stoi na **kotwicach semantycznych** (cele wywołań, referowane literały
|
|
stringów, immediaty `PUSH`), a **nie** na tekście dekompilatu. Dzięki temu jeden skrypt
|
|
działa na MSVC6 (Piklib) i MSVC8 (BlooMoo) mimo różnego kodu wynikowego.
|
|
|
|
| Co | Kotwica w Ghidrze | Status |
|
|
|----|-------------------|--------|
|
|
| Typy | `CMC_ObjectsContainer::resolve`: `operator==("NAME")` → `operator_new(SIZE)` → `CMC_X::CMC_X` | ✅ (+ `dispatch_addr`, `via_module_iface`) |
|
|
| Metody | `CMC_*_Runner::prepareMthHashSet`: `CInteger(id)` + `CStringHashCode("NAME")` + `CHashtable::put` | ✅ (+ `method_inheritance`) |
|
|
| Eventy | `CMC_*::getBehavioursList`: lista literałów `CXString` | ✅ (lista per klasa, bez dziedziczenia) |
|
|
| Pola (skryptowe) | ctory `CMC_*`: literały czytane przez `CMElement::getProperty<T>Value` → nazwa + typ pola (FPS, PRELOAD, VISIBLE…) | ✅ (+ `field_inheritance`) |
|
|
| Layout C++ (bonus) | ctory `CMC_*`: store'y `this+offset` przez P-code (rozmyte, `is_vtable`) | ✅ pod `struct_layout` |
|
|
|
|
## Uruchomienie ekstraktora
|
|
|
|
**W GUI Ghidry** (najszybciej do walidacji): skopiuj `ghidra_scripts/extract_engine_surface.py`
|
|
do swojego katalogu skryptów (Script Manager → *Manage Script Directories*), otwórz program,
|
|
uruchom skrypt. Wynik trafi do `<NazwaProgramu>.snapshot.json` w katalogu roboczym Ghidry.
|
|
|
|
**Headless** (tryb docelowy):
|
|
```bash
|
|
analyzeHeadless <projDir> <projName> -process PIKLIB8.dll \
|
|
-postScript extract_engine_surface.py "$(pwd)/snapshots/PIKLIB8.snapshot.json"
|
|
```
|
|
|
|
## Format snapshotu
|
|
|
|
`schema_version`, `binary{name,sha256,engine,compiler,factory_addr}`, oraz listy
|
|
`types` / `methods` / `events` / `fields`. Diff = operacje na zbiorach dwóch snapshotów.
|