# 03 — Module đọc/ghi bộ nhớ (MemoryReader)

> Cách tìm offsets/pointer chain (Ghidra, Cheat Engine, x64dbg, ReClass) nằm ở
> [08-reversing-toolchain.md](08-reversing-toolchain.md). File này mô tả **thiết kế runtime**.

## 1. Trách nhiệm

- Attach vào process game (theo tên image, ví dụ `ARES.exe` — tên thật xác định khi recon).
- Đọc cấu trúc dữ liệu theo **offsets config** → dựng `GameState` snapshot bất biến.
- Ghi bộ nhớ **hạn chế và có kiểm soát** (chỉ các flag gameplay an toàn khi recon xác nhận, ví dụ auto-loot toggle của chính game; mặc định tool chỉ **đọc**).
- Validate integrity: trước mỗi phiên làm việc, verify **signature** của mỗi anchor — sai signature ⇒ dừng, không đoán mò.

## 2. Đọc như thế nào

```cpp
class MemoryReader {
public:
    // Mở process: PROCESS_VM_READ | PROCESS_QUERY_INFORMATION
    // (không xin quyền ghi trừ khi profile cho phép)
    static std::expected<MemoryReader, MemError> attach(std::wstring_view imageName);

    // Mỗi tick: resolve pointer chain → đọc các struct → trả snapshot bất biến
    std::shared_ptr<const GameState> snapshot();

private:
    win_handle process_;
    const OffsetsProfile& offsets_;   // load từ configs/offsets.json
    PointerResolver   resolver_;      // cache chain sau lần resolve đầu
    SignatureValidator validator_;    // kiểm tra byte pattern trước khi tin anchor
};
```

Đọc thực hiện bằng `ReadProcessMemory` theo **đợt gộp** (mỗi region đọc 1 lần lớn rồi parse từ buffer) thay vì hàng trăm lệnh read nhỏ — giảm latency và ít can thiệp hơn.

## 3. offsets.json — hợp đồng giữa recon và runtime

```json
{
  "game_version": "1.2.35",
  "updated": "2026-09-15",
  "anchors": {
    "module": "GameAssembly.dll",
    "player_base": {
      "signature": "48 8B 05 ?? ?? ?? ?? 48 85 C0 74 ?? 48 8B 40 10",
      "signature_offset": 3,
      "chain": [0x8, 0x20, 0x1A0]
    }
  },
  "fields": {
    "player.hp":     { "chain": ["player_base", 0x2F0], "type": "u32" },
    "player.mp":     { "chain": ["player_base", 0x2F4], "type": "u32" },
    "player.pos":    { "chain": ["player_base", 0x310], "type": "vec3f" },
    "player.suit":   { "chain": ["player_base", 0x330], "type": "u8" },
    "skill_cd":      { "chain": ["player_base", 0x400], "type": "f32[8]" },
    "entity_list":   { "chain": ["world_base", 0x18],   "type": "array<Entity, 64>" }
  }
}
```

Nguyên tắc:
- **Không hardcode offset trong code.** Mọi địa chỉ nằm trong JSON; bot theo dõi `game_version`.
- Mỗi anchor có **signature AOB** (array-of-bytes) để tự kiểm tra còn đúng chỗ không sau patch.
- Script `tools/diff_offsets.py` so sánh offsets giữa 2 version để nhận biết cái nào vỡ.

## 4. Pointer chain resolver

```mermaid
flowchart LR
    A["AOB scan\n(tìm signature trong module)"] --> B["Anchor base\n(rip-relative address)"]
    B --> C["Walk chain\n[0x8] → [0x20] → ..."]
    C --> D{Valid?\n+ sanity check}
    D -->|yes| E["Cache base\n(đọc nhanh các tick sau)"]
    D -->|no| F["Re-scan hoặc\nbáo FAIL_SAFE"]
```

- Resolve 1 lần khi attach; cache kết quả; **re-validate định kỳ** (mỗi 30s) để bắt trường hợp game re-allocate.
- Sanity check: HP trong [0, maxHP], vị trí nằm trong bounding world, pointer không null/dangling.

## 5. Entity list & world state

- Entity list là phần phức tạp nhất: đọc array địa chỉ entity, mỗi entity parse `type, hp, pos, state_id (animation/boss skill cue)`.
- Giới hạn số entity parse mỗi tick (ví dụ 64) để giữ tick ≤ 16ms.
- `state_id` của boss là đầu vào quan trọng nhất cho **dodge pro** (xem [05-game-knowledge.md](05-game-knowledge.md)).

## 6. Ghi bộ nhớ (write) — chính sách

- Mặc định: **chỉ đọc**. Input điều khiển 100% qua KMBox (HID thật).
- Chỉ cân nhắc ghi khi recon chứng minh an toàn & ít rủi ro phát hiện hơn path input (ví dụ bật sẵn game option). Mọi ghi phải:
  1. Được liệt kê trắng trong `offsets.json` ở mục `writable`.
  2. Ghi qua `SafeWriter` (kiểm tra giá trị cũ trước, ghi 1 field 1 lần, không loop ghi).
  3. Bật/tắt được bằng config, mặc định tắt.

## 7. Xử lý chống biện pháp bảo vệ của game

- Nếu process được bảo vệ (chặn `OpenProcess`), MemoryReader chuyển sang **VisionModule** làm nguồn dữ liệu — kiến trúc đảm bảo DecisionEngine không phân biệt nguồn snapshot (cùng interface `IPerceptionSource`).
- Không tự động vô hiệu hóa anti-cheat — ngoài phạm vi dự án.
