# 25. Đặc tả Pipeline Reverse Engineering Tĩnh & Offset Registry (Static RE Pipeline & Build-Keyed Offset Registry)

> **Mã nhiệm vụ**: `TASK-RE-PIPELINE-2026` (Phương án C — Static Disassembly làm gốc rễ)
> **Mốc thời gian**: 10/09/2026
> **Chuyên trách**: Sub-Agent Reverse Engineering & Documentation Specialist
> **Toolchain**: Ghidra 12.1.3 (`C:\Tools\ghidra_12.1.3_PUBLIC`) + JDK 25.0.4.1 LTS (Temurin) + PyGhidra 3.1.0 / JPype 1.5.2 (pin)
> **Tài liệu tham chiếu**: `AGENTS.md` Rule 8/11/14, `23_pointer_chain_resolver_architecture.md`, `docs/research/06_static_disassembly_poe2_build_6A9E477A.md`

---

## 1. Động lực & Bằng chứng Root Cause (dữ liệu thật, Rule 14)

Sự cố 10/09/2026: `captures/20260910_080001_393_PERIODIC_ANALYSIS_RAM.json` (PID 36104) ghi nhận
`"address": "0x0", "xyz_address": "0x0", "hp": 1, "max_hp": 1` — toàn bộ thang fallback
4 cấp của `PointerChainResolver` sụp đổ trên client build mới.

**Nguyên nhân gốc rễ** (đối chiếu `bin/Release/memprobe_report.txt`, 162.659 bytes):

| Layout | .text VSize | .rdata VA | .data VA | Số phiên xuất hiện |
|---|---|---|---|---|
| BUILD CŨ | `0x2E840BE` | `0x2E86000` | `0x3A03000` | **5** |
| BUILD MỚI | `0x2E83B6E` | `0x2E85000` | `0x3A02000` | **1** |

- `.text` co lại đúng **0x550** byte → mọi section phía sau dịch **−0x1000** (page-aligned).
- Hệ quả: 8 static root RVA hardcode (`pointer_chain_resolver.hpp` `kKnownStaticRoots`,
  `tools/memprobe.cpp:1042`) suy ra từ build cũ **lệch hoàn toàn**; cả 4 AOB pattern
  trong `docs/development/06` mục 3 đều `[MISS]` (log: `[MISS] InGameStateBase`, `[MISS] VolumetricFog`, `[MISS] EntityListRoot`, `[MISS] AreaExploration`).
- Kết luận kiến trúc: **thiếu build fingerprint**. Vá bằng cách dò lại RVA rồi hardcode tiếp = vá triệu chứng (cấm theo Rule 8).

## 2. Kiến trúc tổng thể

```
[PyGhidra RE Pipeline]  ──>  data/poe2_offsets.db (SQLite WAL, SSoT Python)
        │                          │
        └──> data/re_out/<BID>/    └──> bin/Release/offsets_<BID>.toml (build-keyed)
             extraction.json                │
                                            ▼
              [C++ OffsetRegistry] ── nạp qua OffsetsStore ──> [PointerChainResolver]
                                                                Tier 2a: registry roots
                                                                Tier 2b: 8 RVA compiled (CÓ GATE)
                                                                Tier 3/4: AOB / heap scan
```

C++ Core **không thêm dependency SQLite** — chỉ nạp TOML build-keyed qua `OffsetsStore` mở rộng.

## 3. Các module mới & Invariant

### 3.1. Build Fingerprint — `src/core/memory/pe_fingerprint.hpp/.cpp` + `tools/re_pipeline/fingerprint.py`
- Parse PE từ đĩa (không attach, không Admin, không cần game chạy): DOS/COFF/Optional PE32+ + bảng section.
- SHA-256 tự chứa (NIST FIPS 180-4, test vector chuẩn trong `TestSha256NistVectorsAndPeFingerprint`).
- `SectionLayoutHash()` = FNV-1a 64-bit trên bảng section — **byte-identical 2 tầng**
  (Python `fnv1a` đóng gói `<H`/`<I` little-endian đúng thứ tự C++). Giá trị đo được
  trên build `6A9E477A`: **`0x5C4322F5CDC0551A`** (khớp stdout C++ Test 60 và `fingerprint.py`).
- Build record thật: `data/builds/6A9E477A.json` — SHA-256 tệp
  `e0cde00d395dbb339f2c2e0812c3d872fe5425c04a88af544ab06e19b07a7504`, 78.319.448 bytes,
  `SizeOfImage=0x4C9C000`, `TimeDateStamp 0x6A9E477A` (07/09/2026 05:11:22 UTC).

### 3.2. INV-BUILD-01 (Build-Keyed Offset Knowledge)
Tri thức offset chỉ dùng được khi fingerprint client KHỚP registry:
- `OffsetRegistry::CheckBuild()` → `Match / NoRecord / LiveUnavailable / StampMismatch / LayoutMismatch`.
- `PointerChainResolver::SetBuildGate(armed, matches)`; `CompiledStaticRootsAllowed()`
  == false khi arm+mismatch → **Tier 2b (8 RVA compiled) bị vô hiệu**; Tier 2a (registry
  roots build-matched) và Tier 3/4 vẫn hoạt động.
- Wire runtime: `GameSession::Initialize()` → `ApplyOffsetRegistry()` (chỉ chế độ game thật;
  ưu tiên `offsets_<ShortId>.toml`, fallback `offsets.toml`).
- 🔒 Anti silent-bypass: registry không ghi build fingerprint → `NoRecord` → **không dùng**.

### 3.3. INV-OFFSET-01 (Zero Absolute Heap Addresses — chốt chặn 2 chiều)
- `OffsetsStore::Set()` **từ chối ghi**, `Load()` **từ chối nạp** 9 khóa cấm
  (`player_addr, xyz_addr, shield_addr, mana_addr, spirit_addr, ingame_addr, terrain_addr, camera_addr, fog_density_addr`),
  bộ đếm `RejectedOnSetCount()/RejectedOnLoadCount()` phục vụ test hồi quy.
- Sửa tận gốc `game_session.cpp` (trước đây dòng 889 ghi `xyz_addr` heap tuyệt đối —
  vi phạm mục 6 của `06_api_and_ipc_specification.md` và mâu thuẫn chính
  `SaveOffsets()`): nay suy ra **`pos_offset` tương đối** (≤4096 so với player base).
- `SaveOffsets()` persist thêm `stat_offset` / `pos_offset` (giá trị thực địa 0x3B8 / 0x518).
- Hỗ trợ giá trị chuỗi (AOB pattern) qua bảng `m_strings` + `TryParseValue()` nghiêm ngặt.

# [PART-2-ANCHOR]

### 3.4. INV-SIG-UNIQUE (AOB pattern chỉ khớp đúng 1 lần trong .text)
- `re_extract.py` sinh pattern từ chính instruction RIP-relative (wildcard 4 byte disp32
  cuối lệnh — ngữ nghĩa khớp `AobScanner::ResolveRipTarget(reader, match, 3)`).
- **Tự mở rộng** bằng lệnh kế tiếp (vẫn wildcard mọi disp32) tới khi `match_count == 1`
  hoặc hết `--pattern-window` (mặc định 24, khuyến nghị 40).
- Bằng chứng thực nghiệm trên smoke test (`AutoPOE2_MemProbe.exe`, 360 functions,
  15.725 references, 681 global slots): pattern ngưỡng `FF 15 ?? ?? ?? ??` khớp **677 lần**;
  sau auto-extension: **11/12 candidate đạt UNIQUE** (vd
  `48 8B 05 ? ? ? ? 48 33 C4 48 89 85 ? ? ? ? 4D 8B F1`). Đây chính là lý do 4 pattern cũ trong docs 06 vô nghĩa.
- Xếp hạng ứng viên theo **fan-in** (số instruction trỏ cùng một slot .data/.rdata).

### 3.5. Offset Registry — `tools/re_pipeline/build_registry.py` + `offset_registry.hpp/.cpp`
- Đầu vào: `data/re_out/<BID>/extraction.json` (schema `autopoe2.re_extraction/v1`, provenance đầy đủ).
- Đầu ra: `data/poe2_offsets.db` (SQLite WAL; bảng `builds`, `signatures`, `offsets`) +
  `bin/Release/offsets_<BID>.toml` (khóa `build_time_date_stamp`, `build_section_layout_hash`,
  `static_root_N` RVA, `aob_pattern_N`, `aob_rip_offset_N`).
- `target='unreviewed'` = **HUMAN-IN-THE-LOOP**: ngữ nghĩa (InGameState/LifeComponent/…)
  chỉ được gán sau khi đối chuẩn live (`label-registry` + `captures/*_RAM.json`).
  Build `6A9E477A` (10/09/2026): probe `0x45C9C90` **chưa** đủ LifeComponent CI `{1,1}` — `target` giữ `unreviewed`.

## 4. Quy trình vận hành

```powershell
# 0. Toolchain (1 lần): JDK 25 + Ghidra 12.1.3 + PyGhidra
winget install EclipseAdoptium.Temurin.25.JDK
# Giải nén ghidra_12.1.3_PUBLIC_20260817.zip (SHA-256 93a5d11a...fd54) vào C:\Tools
$env:GHIDRA_INSTALL_DIR = 'C:\Tools\ghidra_12.1.3_PUBLIC'
python -m pip install -r requirements-dev.txt

# 1. Fingerprint build client (User Mode, không cần game)
python tools/re_pipeline/fingerprint.py --hash --json data/builds/<BUILD_ID>.json

# 2. Smoke test pipeline trên binary nhỏ (chứng minh pipeline, không cần game)
python tools/re_pipeline/re_extract.py --exe bin/Release/AutoPOE2_MemProbe.exe --max-candidates 12 --pattern-window 40 --out data/re_out/SMOKE/extraction.json

# 3. Chạy thật trên client (LÂU: auto-analysis .text 48.77MB, 1-6 giờ)
python tools/re_pipeline/re_extract.py --max-candidates 40 --pattern-window 40

# 4. Sinh Offset Registry (SQLite + TOML build-keyed)
python tools/re_pipeline/build_registry.py --extraction data/re_out/<BUILD_ID>/extraction.json

# 5. (Cần Admin, client đang chạy) Đối chuẩn live -> gán ngữ nghĩa target
#    Dùng OffsetRegistry build-keyed + PointerChainResolver::ValidateInGameState
#    (KHÔNG dùng 4 AOB hardcode docs/06 — đã [MISS] trên 6A9E477A).
bin/Release/AutoPOE2_MemProbe.exe label-registry
python tools/re_pipeline/label_targets.py --report bin/Release/live_label_<BUILD_ID>.json
```

# [PART-3-ANCHOR]

## 5. Bằng chứng xác minh (thực thi thật trong phiên 10/09/2026)

| Cổng kiểm thử | Lệnh | Kết quả |
|---|---|---|
| C++ full suite | `.\build\bin\Release\AutoPOE2_Tests.exe` | **1332/1332 PASS** (10/09/2026 18:28) — Test 62–66: fingerprint HUD, Life sát đầu region, dump PID 36104 không Mana inline `+0x18`, `ScanCurMaxPair` + `MatchAt` khóa Mana `+0x420` / Spirit `+0x44C` |
| Python RE pipeline | `python -m pytest tests/test_re_pipeline.py -q` | **13 passed** (0.67s) — gán target InGameState / từ chối `player_addr` |
| Live MemProbe | `probe-pools 5331D2003E8 --mana 828 --spirit 138 --spirit-cur 8 --fast` (Admin, PID 36104, 18:21:59) rồi `label-registry` (18:30:15) | Life vẫn `{1,1}` ES 2416 Ward 826; Mana heap `0x5331D200808` = Life+1056; Spirit `0x5331D200834` = Life+1100; labels **Mana=`0x420` Spirit=`0x44C`**; INV-OFFSET-01: không persist heap |
| Labeler | `python tools/re_pipeline/label_targets.py --report bin/Release/live_label_6A9E477A.json` | 10/09/2026 18:30:15: `EnergyShield` `0xC`, `Ward` `0x20`, **`Mana` `0x420`**, **`Spirit` `0x44C`**. InGameState giữ `unreviewed` |
| Smoke Ghidra | `re_extract.py --exe AutoPOE2_MemProbe.exe` | ExitCode=0, 12.8s, 11 UNIQUE (build `6AA21741`) |
| Full client Ghidra | `re_extract.py --max-candidates 40 --pattern-window 40 --project-dir data/re_ghidra/client_6A9E477A` | ExitCode=0, **8066.23s (~2h14m)**; functions **115,021**; refs **4,602,282**; slots **115,566**; 40 candidates / **29 UNIQUE** (8 `.data` + 20 `.rdata` + 1 `tdb` loại khỏi registry) |
| Registry builder | `build_registry.py --extraction data/re_out/6A9E477A/extraction.json` | `offsets_6A9E477A.toml` + SQLite **28** signature in-image, **8** `static_root_*` RVA; live target **thu hồi** về `unreviewed` (CI maxHP=1 ≠ 1330) |

## 6. Ranh giới & việc còn lại
1. Human-in-the-loop: **LifeComponent CI đã dump** (15:19:27 + 18:21:59, PID 36104, `{1,1}` + ES 2416 + Ward 826). `EnergyShield=+0xC`, `Ward=+0x20`, **`Mana=+0x420`**, **`Spirit=+0x44C`**. **InGameState / LocalPlayer / EntityMap vẫn `unreviewed`**. `ptrdeep` depth 4 từ Life (maxOff `0x2000`) không gặp static root (1.39M match, frontier 50k). Không persist heap `0x5331D2003E8`.
2. **INV-SIG-CROSSBUILD**: xác chứng pattern trên ≥2 build (cột `cross_build_ok` hiện = 0).
3. Tier 3 của `PointerChainResolver` chưa tiêu thụ `aob_pattern_N` từ registry (hiện chỉ Tier 2a/2b có gate) — kế hoạch nối tiếp.
4. ~~Chạy analyse đầy đủ trên PathOfExile.exe~~ **DONE 10/09/2026 13:15:06** — artefact `data/re_out/6A9E477A/extraction.json`, log `data/re_out/6A9E477A/extract.log`, Ghidra project `data/re_ghidra/client_6A9E477A` (tách khỏi smoke MemProbe).
5. 7 test GUI Tkinter (`test_control_center.py`, `test_optical_hp_sensor.py`, `test_assistant_tool.py::test_auto_quester`) **FAIL có sẵn từ trước** — không thuộc phạm vi task này.
6. Cảnh báo EULA: reverse engineering client có thể trái điều khoản GGG — quyết định chấp nhận rủi ro thuộc về người vận hành; pipeline chỉ đọc binary trên đĩa, 0 footprint runtime.
7. Mana 828 / Spirit 8/138: **DONE** 10/09/2026 18:30:15 — cùng allocation Life, relative `+0x420` / `+0x44C`. Rage HUD=0 chưa khóa (không có cặp max độc nhất).


