---
# System prepended metadata

title: Lost in file system

---

# 1) Mô tả đề & hint

**Title:** Lost in file system (Medium)
**Cho:** 1 file `.img` \~3 GiB (USB image)
**Triệu chứng:** Partition table có vẻ hỏng → FTK/Autopsy không nhận ra filesystem.
**Hint:** *Liên quan đến NTFS boot sector.*

**Ý tưởng tổng quát:**
Khi bảng phân vùng hỏng, OS/forensic suite không tự tìm thấy volume. Nhưng nếu bên trong có **NTFS**, ta vẫn có thể:

* Tự **quét raw image** để tìm **NTFS boot sector** (“NTFS    ” ở offset 0x03, chữ ký 0x55AA ở 0x1FE).
* Xác thực thông số, tính vị trí **backup boot sector** (NTFS lưu ở **sector cuối** của volume).
* Mount volume bằng **offset byte** hoặc dùng Sleuth Kit để duyệt nội dung.
* Nếu cần, **phục hồi** dữ liệu (file đã xoá), và xử lý thêm các “bẫy” (ví dụ file ảnh bị cắt header).

---

# 2) Công cụ dùng

* **Sleuth Kit**: `fsstat`, `fls`, `icat`, `tsk_recover`
* **ntfs-3g**: mount NTFS với tuỳ chọn ADS
* **xxd / hexdump / strings / dd**: kiểm tra & vá byte
* (tuỳ) **pngcheck / exiftool / binwalk / zsteg**: nếu cần mổ xẻ ảnh
* **Script quét** (mình viết sẵn): `ntfs_boot_tools.py`
```python!
#!/usr/bin/env python3
"""
ntfs_boot_tools.py
------------------
Scan a raw disk image for NTFS volumes, parse their boot sectors,
and (optionally) repair a corrupted primary boot sector by copying
the backup boot sector from the end of the volume.

Usage:
  # List candidate NTFS volumes (with parsed fields)
  python ntfs_boot_tools.py scan /path/to/disk.img

  # Verify a specific candidate (by byte offset) and compare primary vs backup BS
  python ntfs_boot_tools.py verify /path/to/disk.img --offset-bytes 1048576

  # Repair (write!) the primary boot sector from the backup for the given offset
  # Writes to a *new* file ending with .repaired.img to keep the original safe
  python ntfs_boot_tools.py repair /path/to/disk.img --offset-bytes 1048576

Notes:
- Offsets are in BYTES (not sectors). If you have start LBA, use offset = LBA * 512.
- This script assumes a 512-byte boot sector size when copying (standard for NTFS).
- Always work on a COPY of the image. The 'repair' subcommand writes to a new file.
"""

import argparse
import os
import struct
from dataclasses import dataclass

NTFS_OEM = b"NTFS    "
SECTOR_SIZE_CANDIDATES = (512, 1024, 2048, 4096)

@dataclass
class NTFSBoot:
    offset_bytes: int         # start of the NTFS volume within the image
    bytes_per_sector: int
    sectors_per_cluster: int
    total_sectors: int        # 64-bit
    mft_lcn: int              # Logical Cluster Number
    mftmirr_lcn: int
    clusters_per_mft_record: int  # signed int8 or int32 if negative
    clusters_per_index_record: int  # signed int8 or int32 if negative
    valid_signature: bool

def parse_ntfs_boot(buf: bytes, offset_bytes: int) -> NTFSBoot | None:
    # Basic sanity: "NTFS    " at offset 0x03 and 0x55AA at 0x1FE
    if len(buf) < 512:
        return None
    if buf[3:11] != NTFS_OEM:
        return None
    valid_sig = buf[510:512] == b'\x55\xaa'

    bps = struct.unpack_from("<H", buf, 0x0B)[0]
    spc = buf[0x0D]
    # Reserved sectors for NTFS is typically 0
    # total_sectors (Q) at 0x28
    total_sectors = struct.unpack_from("<Q", buf, 0x28)[0]
    mft_lcn = struct.unpack_from("<Q", buf, 0x30)[0]
    mftmirr_lcn = struct.unpack_from("<Q", buf, 0x38)[0]

    # clusters per MFT/index record can be signed int8 or a negative power-of-two encoded
    def parse_cp_record(off: int) -> int:
        val = struct.unpack_from("b", buf, off)[0]
        if val < 0:
            # size in bytes is 2**(-val); we store the raw signed byte
            return val
        return val

    cpm = parse_cp_record(0x40)
    cpi = parse_cp_record(0x44)

    # sanity checks
    if bps not in SECTOR_SIZE_CANDIDATES:  # typical
        return None
    if spc == 0 or spc > 128:
        return None
    if total_sectors == 0:
        return None

    return NTFSBoot(
        offset_bytes=offset_bytes,
        bytes_per_sector=bps,
        sectors_per_cluster=spc,
        total_sectors=total_sectors,
        mft_lcn=mft_lcn,
        mftmirr_lcn=mftmirr_lcn,
        clusters_per_mft_record=cpm,
        clusters_per_index_record=cpi,
        valid_signature=bool(valid_sig),
    )

def human(n: int) -> str:
    for unit in ["B","KiB","MiB","GiB","TiB"]:
        if n < 1024 or unit == "TiB":
            return f"{n:.0f} {unit}"
        n /= 1024
    return f"{n:.0f} B"

def scan(path: str, max_hits: int = 50) -> list[NTFSBoot]:
    hits: list[NTFSBoot] = []
    bs = 512  # scan on 512-byte boundaries
    chunk = 1024 * 1024
    size = os.path.getsize(path)
    with open(path, "rb") as f:
        pos = 0
        while pos < size and len(hits) < max_hits:
            data = f.read(chunk)
            if not data:
                break
            # scan within this chunk at 512B alignment
            for off in range(0, len(data) - 512 + 1, bs):
                if data[off+3:off+11] == NTFS_OEM:
                    buf = data[off:off+512]
                    boot = parse_ntfs_boot(buf, pos + off)
                    if boot:
                        hits.append(boot)
            pos += len(data)
    return hits

def describe(boot: NTFSBoot) -> str:
    size_bytes = boot.total_sectors * boot.bytes_per_sector
    clustersz = boot.bytes_per_sector * boot.sectors_per_cluster
    return (
        f"Offset: {boot.offset_bytes} bytes ({human(boot.offset_bytes)})\n"
        f"  Bytes/sector: {boot.bytes_per_sector}\n"
        f"  Sectors/cluster: {boot.sectors_per_cluster}\n"
        f"  Cluster size: {clustersz} bytes\n"
        f"  Total sectors: {boot.total_sectors}  => Volume size ~ {human(size_bytes)}\n"
        f"  $MFT LCN: {boot.mft_lcn}\n"
        f"  $MFTMirr LCN: {boot.mftmirr_lcn}\n"
        f"  Boot signature 0x55AA present: {boot.valid_signature}\n"
    )

def read_sector(path: str, abs_byte_off: int, size: int = 512) -> bytes:
    with open(path, "rb") as f:
        f.seek(abs_byte_off)
        return f.read(size)

def verify(path: str, offset_bytes: int) -> None:
    primary = read_sector(path, offset_bytes, 512)
    boot = parse_ntfs_boot(primary, offset_bytes)
    if not boot:
        print("❌ No valid NTFS boot sector at that offset.")
        return
    print("Primary boot sector:")
    print(describe(boot))

    # Compute backup boot sector absolute offset (last sector of the volume)
    vol_size_bytes = boot.total_sectors * boot.bytes_per_sector
    backup_abs = boot.offset_bytes + vol_size_bytes - 512
    backup = read_sector(path, backup_abs, 512)

    boot2 = parse_ntfs_boot(backup, backup_abs)
    print("Backup boot sector:")
    if boot2:
        print(describe(boot2))
    else:
        print("⚠️ Backup does not parse as valid NTFS boot sector.")

def repair(path: str, offset_bytes: int) -> str:
    # Create a copy for safety
    out_path = path + ".repaired.img"
    if os.path.exists(out_path):
        raise SystemExit(f"Output already exists: {out_path}")
    print(f"Creating copy: {out_path} (this may take a while)...")
    # Copy in chunks
    bs = 1024 * 1024
    total = os.path.getsize(path)
    with open(path, "rb") as src, open(out_path, "wb") as dst:
        while True:
            data = src.read(bs)
            if not data:
                break
            dst.write(data)

    # Parse from the copy and perform the write there
    primary = read_sector(out_path, offset_bytes, 512)
    boot = parse_ntfs_boot(primary, offset_bytes)
    if not boot:
        raise SystemExit("No valid-looking NTFS boot sector header at the given offset in the copy. Aborting.")

    vol_size_bytes = boot.total_sectors * boot.bytes_per_sector
    backup_abs = boot.offset_bytes + vol_size_bytes - 512
    backup = read_sector(out_path, backup_abs, 512)
    if backup[3:11] != NTFS_OEM or backup[510:512] != b'\x55\xaa':
        raise SystemExit("Backup boot sector does not look valid. Aborting.")

    # Write backup over primary in the copy
    with open(out_path, "r+b") as f:
        f.seek(offset_bytes)
        f.write(backup)
    print("✅ Wrote backup boot sector over primary (in the *copy*).")
    return out_path

def main():
    ap = argparse.ArgumentParser()
    sub = ap.add_subparsers(dest="cmd", required=True)

    ap_scan = sub.add_parser("scan", help="Scan image for NTFS boot sectors")
    ap_scan.add_argument("image")
    ap_scan.add_argument("--max-hits", type=int, default=50)

    ap_verify = sub.add_parser("verify", help="Compare primary vs backup boot sector for a candidate")
    ap_verify.add_argument("image")
    ap_verify.add_argument("--offset-bytes", type=int, required=True)

    ap_repair = sub.add_parser("repair", help="Copy backup boot sector over primary (writes to a new .repaired.img)")
    ap_repair.add_argument("image")
    ap_repair.add_argument("--offset-bytes", type=int, required=True)

    args = ap.parse_args()

    if args.cmd == "scan":
        hits = scan(args.image, args.max_hits)
        if not hits:
            print("No NTFS boot sectors found.")
            return
        for i, h in enumerate(hits, 1):
            print(f"[{i}] Candidate NTFS volume at offset {h.offset_bytes} bytes")
            print(describe(h))
    elif args.cmd == "verify":
        verify(args.image, args.offset_bytes)
    elif args.cmd == "repair":
        out = repair(args.image, args.offset_bytes)
        print(f"Output image: {out}")
    else:
        ap.print_help()

if __name__ == "__main__":
    main()
```



---

# 3) Bước 1 – Quét image tìm NTFS boot sector

Chạy script:

```bash!
python3 ntfs_boot_tools.py scan recovery_USB.img
```

**Kết quả thực tế:**

```bash!
┌──(kali㉿kali)-[~/Desktop/CHALL4N6ATHONG]
└─$ python3 ntfs_boot_tools.py scan recovery_USB.img 
[1] Candidate NTFS volume at offset 16777216 bytes
Offset: 16777216 bytes (16 MiB)
  Bytes/sector: 512
  Sectors/cluster: 8
  Cluster size: 4096 bytes
  Total sectors: 6258559  => Volume size ~ 3 GiB
  $MFT LCN: 262144
  $MFTMirr LCN: 2
  Boot signature 0x55AA present: True

[2] Candidate NTFS volume at offset 3221159424 bytes
Offset: 3221159424 bytes (3 GiB)
  Bytes/sector: 512
  Sectors/cluster: 8
  Cluster size: 4096 bytes
  Total sectors: 6258559  => Volume size ~ 3 GiB
  $MFT LCN: 262144
  $MFTMirr LCN: 2
  Boot signature 0x55AA present: True
```

**Nhận xét:**

* \[1] = **primary boot sector** của NTFS ở **offset 16 MiB**.
* \[2] = sector gần **cuối volume** → nhiều khả năng là **backup boot sector**.

---

# 4) Bước 2 – So sánh primary vs. backup

Xác thực từ offset primary:

```bash
python3 ntfs_boot_tools.py verify recovery_USB.img --offset-bytes 16777216
```

**Kết quả thực tế:**
```bash!
┌──(kali㉿kali)-[~/Desktop/CHALL4N6ATHONG]
└─$ python3 ntfs_boot_tools.py verify recovery_USB.img --offset-bytes 16777216

Primary boot sector:
Offset: 16777216 bytes (16 MiB)
  Bytes/sector: 512
  Sectors/cluster: 8
  Cluster size: 4096 bytes
  Total sectors: 6258559  => Volume size ~ 3 GiB
  $MFT LCN: 262144
  $MFTMirr LCN: 2
  Boot signature 0x55AA present: True

Backup boot sector:
⚠ Backup does not parse as valid NTFS boot sector.
```
* Primary: **OK**
* Backup: **“không parse được”** theo vị trí *chuẩn* (tính từ TotalSectors).

Kiểm tra tay 2 sector “cuối volume”:

```bash
# sector “chuẩn” (expected) theo công thức: toàn 0x00
xxd -g 1 -l 512 -s 3221158912 recovery_USB.img

# sector kế tiếp (+512 B): có NTFS Boot Sector hợp lệ
xxd -g 1 -l 512 -s 3221159424 recovery_USB.img
# -> thấy "EB 52 90 4E 54 46 53 20 20 20 20 ... 55 AA"
```

```bash!
┌──(kali㉿kali)-[~/Desktop/CHALL4N6ATHONG]
└─$ xxd -g 1 -l 512 -s 3221158912 recovery_USB.img       
bffefc00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
bffefc10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
bffefc20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
bffefc30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
bffefc40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
bffefc50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
bffefc60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
bffefc70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
bffefc80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
bffefc90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
bffefca0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
bffefcb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
bffefcc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
bffefcd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
bffefce0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
bffefcf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
bffefd00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
bffefd10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
bffefd20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
bffefd30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
bffefd40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
bffefd50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
bffefd60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
bffefd70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
bffefd80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
bffefd90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
bffefda0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
bffefdb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
bffefdc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
bffefdd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
bffefde0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
bffefdf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
                                                                                                                                                                           
┌──(kali㉿kali)-[~/Desktop/CHALL4N6ATHONG]
└─$ xxd -g 1 -l 512 -s 3221159424 recovery_USB.img
bffefe00: eb 52 90 4e 54 46 53 20 20 20 20 00 02 08 00 00  .R.NTFS    .....
bffefe10: 00 00 00 00 00 f8 00 00 3f 00 ff 00 00 80 00 00  ........?.......
bffefe20: 00 00 00 00 80 00 80 00 7f 7f 5f 00 00 00 00 00  .........._.....
bffefe30: 00 00 04 00 00 00 00 00 02 00 00 00 00 00 00 00  ................
bffefe40: f6 00 00 00 01 00 00 00 35 c4 4c 72 f5 4c 72 ae  ........5.Lr.Lr.
bffefe50: 00 00 00 00 fa 33 c0 8e d0 bc 00 7c fb 68 c0 07  .....3.....|.h..
bffefe60: 1f 1e 68 66 00 cb 88 16 0e 00 66 81 3e 03 00 4e  ..hf......f.>..N
bffefe70: 54 46 53 75 15 b4 41 bb aa 55 cd 13 72 0c 81 fb  TFSu..A..U..r...
bffefe80: 55 aa 75 06 f7 c1 01 00 75 03 e9 dd 00 1e 83 ec  U.u.....u.......
bffefe90: 18 68 1a 00 b4 48 8a 16 0e 00 8b f4 16 1f cd 13  .h...H..........
bffefea0: 9f 83 c4 18 9e 58 1f 72 e1 3b 06 0b 00 75 db a3  .....X.r.;...u..
bffefeb0: 0f 00 c1 2e 0f 00 04 1e 5a 33 db b9 00 20 2b c8  ........Z3... +.
bffefec0: 66 ff 06 11 00 03 16 0f 00 8e c2 ff 06 16 00 e8  f...............
bffefed0: 4b 00 2b c8 77 ef b8 00 bb cd 1a 66 23 c0 75 2d  K.+.w......f#.u-
bffefee0: 66 81 fb 54 43 50 41 75 24 81 f9 02 01 72 1e 16  f..TCPAu$....r..
bffefef0: 68 07 bb 16 68 52 11 16 68 09 00 66 53 66 53 66  h...hR..h..fSfSf
bffeff00: 55 16 16 16 68 b8 01 66 61 0e 07 cd 1a 33 c0 bf  U...h..fa....3..
bffeff10: 0a 13 b9 f6 0c fc f3 aa e9 fe 01 90 90 66 60 1e  .............f`.
bffeff20: 06 66 a1 11 00 66 03 06 1c 00 1e 66 68 00 00 00  .f...f.....fh...
bffeff30: 00 66 50 06 53 68 01 00 68 10 00 b4 42 8a 16 0e  .fP.Sh..h...B...
bffeff40: 00 16 1f 8b f4 cd 13 66 59 5b 5a 66 59 66 59 1f  .......fY[ZfYfY.
bffeff50: 0f 82 16 00 66 ff 06 11 00 03 16 0f 00 8e c2 ff  ....f...........
bffeff60: 0e 16 00 75 bc 07 1f 66 61 c3 a1 f6 01 e8 09 00  ...u...fa.......
bffeff70: a1 fa 01 e8 03 00 f4 eb fd 8b f0 ac 3c 00 74 09  ............<.t.
bffeff80: b4 0e bb 07 00 cd 10 eb f2 c3 0d 0a 41 20 64 69  ............A di
bffeff90: 73 6b 20 72 65 61 64 20 65 72 72 6f 72 20 6f 63  sk read error oc
bffeffa0: 63 75 72 72 65 64 00 0d 0a 42 4f 4f 54 4d 47 52  curred...BOOTMGR
bffeffb0: 20 69 73 20 63 6f 6d 70 72 65 73 73 65 64 00 0d   is compressed..
bffeffc0: 0a 50 72 65 73 73 20 43 74 72 6c 2b 41 6c 74 2b  .Press Ctrl+Alt+
bffeffd0: 44 65 6c 20 74 6f 20 72 65 73 74 61 72 74 0d 0a  Del to restart..
bffeffe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
bffefff0: 00 00 00 00 00 00 8a 01 a7 01 bf 01 00 00 55 aa  ..............U.
```

**Kết luận:** đề **gài bẫy off-by-one**: backup boot sector bị đẩy **+1 sector** so với vị trí chuẩn; sector “chuẩn” bị zero hoá.

> Điểm quan trọng: **primary BS vẫn tốt** → có thể mount/duyệt dữ liệu luôn, không cần repair.

---

# 5) Bước 3 – Mount volume theo offset (vượt mặt partition table hỏng)

Mount read-only, bật hiển thị file hệ thống & ADS:

```bash
sudo mkdir -p /mnt/ntfs
sudo mount -o ro,loop,offset=16777216,show_sys_files,streams_interface=windows recovery_USB.img /mnt/ntfs

ls -la /mnt/ntfs
# thấy $MFT, $LogFile, $Boot, System Volume Information, ...
```

(Autopsy/FTK không thấy vì **partition table** hỏng. Khi add image, hãy **nhập offset thủ công**: Start sector = 16 MiB / 512 = **32768**, FS = NTFS.)

```bash!
──(kali㉿kali)-[~/Desktop/CHALL4N6ATHONG]
└─$ sudo mkdir -p /mnt/ntfs
[sudo] password for kali: 
Sorry, try again.
[sudo] password for kali: 
                                                                                                                                                                           
┌──(kali㉿kali)-[~/Desktop/CHALL4N6ATHONG]
└─$ sudo mount -o ro,loop,offset=16777216,show_sys_files,streams_interface=windows recovery_USB.img /mnt/ntfs
                                                                                                                                                                           
┌──(kali㉿kali)-[~/Desktop/CHALL4N6ATHONG]
└─$ ls -la /mnt/ntfs
total 8008
drwxrwxrwx  1 root root    4096 Sep 16 09:37  .
drwxr-xr-x 15 root root    4096 Sep 16 12:56  ..
-rwxrwxrwx  1 root root    2560 Sep 16 09:33 '$AttrDef'
-rwxrwxrwx  1 root root       0 Sep 16 09:33 '$BadClus'
-rwxrwxrwx  1 root root   97792 Sep 16 09:33 '$Bitmap'
-rwxrwxrwx  1 root root    8192 Sep 16 09:33 '$Boot'
drwxrwxrwx  1 root root       0 Sep 16 09:33 '$Extend'
-rwxrwxrwx  1 root root 7684096 Sep 16 09:33 '$LogFile'
-rwxrwxrwx  1 root root  262144 Sep 16 09:33 '$MFT'
-rwxrwxrwx  1 root root    4096 Sep 16 09:33 '$MFTMirr'
-rwxrwxrwx  1 root root       0 Sep 16 09:33 '$Secure'
-rwxrwxrwx  1 root root  131072 Sep 16 09:33 '$UpCase'
-rwxrwxrwx  1 root root       0 Sep 16 09:33 '$Volume'
drwxrwxrwx  1 root root       0 Sep 16 09:33 'System Volume Information'
```

---

# 6) Bước 4 – Dò cấu trúc với Sleuth Kit (không cần mount)

```bash
# Thông số FS (xác nhận)
fsstat -o 32768 recovery_USB.img

# Liệt kê đệ quy (cả deleted & ADS)
fls -o 32768 -r -m / recovery_USB.img | head -200

# Chỉ lọc các entry có dấu ':' (ADS)
fls -o 32768 -r recovery_USB.img | grep ':'
```

**Kết quả thực tế:**

```bash!
┌──(kali㉿kali)-[~/Desktop/CHALL4N6ATHONG]
└─$ fsstat -o 32768 recovery_USB.img
FILE SYSTEM INFORMATION
--------------------------------------------
File System Type: NTFS
Volume Serial Number: AE724CF5724CC435
OEM Name: NTFS    
Volume Name: USB
Version: Windows XP

METADATA INFORMATION
--------------------------------------------
First Cluster of MFT: 262144
First Cluster of MFT Mirror: 2
Size of MFT Entries: 1024 bytes
Size of Index Records: 4096 bytes
Range: 0 - 256
Root Directory: 5

CONTENT INFORMATION
--------------------------------------------
Sector Size: 512
Cluster Size: 4096
Total Cluster Range: 0 - 782318
Total Sector Range: 0 - 6258558

$AttrDef Attribute Values:
$STANDARD_INFORMATION (16)   Size: 48-72   Flags: Resident
$ATTRIBUTE_LIST (32)   Size: No Limit   Flags: Non-resident
$FILE_NAME (48)   Size: 68-578   Flags: Resident,Index
$OBJECT_ID (64)   Size: 0-256   Flags: Resident
$SECURITY_DESCRIPTOR (80)   Size: No Limit   Flags: Non-resident
$VOLUME_NAME (96)   Size: 2-256   Flags: Resident
$VOLUME_INFORMATION (112)   Size: 12-12   Flags: Resident
$DATA (128)   Size: No Limit   Flags: 
$INDEX_ROOT (144)   Size: No Limit   Flags: Resident
$INDEX_ALLOCATION (160)   Size: No Limit   Flags: Non-resident
$BITMAP (176)   Size: No Limit   Flags: Non-resident
$REPARSE_POINT (192)   Size: 0-16384   Flags: Non-resident
$EA_INFORMATION (208)   Size: 8-8   Flags: Resident
$EA (224)   Size: 0-65536   Flags: 
$LOGGED_UTILITY_STREAM (256)   Size: 0-65536   Flags: Non-resident
                                                                                                                                                                           
┌──(kali㉿kali)-[~/Desktop/CHALL4N6ATHONG]
└─$ fls -o 32768 -r -m / recovery_USB.img
0|/$AttrDef ($FILE_NAME)|4-48-2|r/rr-xr-xr-x|0|0|82|1757990014|1757990014|1757990014|1757990014
0|/$AttrDef|4-128-1|r/rr-xr-xr-x|0|0|2560|1757990014|1757990014|1757990014|1757990014
0|/$BadClus ($FILE_NAME)|8-48-3|r/rr-xr-xr-x|0|0|82|1757990014|1757990014|1757990014|1757990014
0|/$BadClus|8-128-2|r/rr-xr-xr-x|0|0|0|1757990014|1757990014|1757990014|1757990014
0|/$BadClus:$Bad|8-128-1|r/rr-xr-xr-x|0|0|3204378624|1757990014|1757990014|1757990014|1757990014
0|/$Bitmap ($FILE_NAME)|6-48-2|r/rr-xr-xr-x|0|0|80|1757990014|1757990014|1757990014|1757990014
0|/$Bitmap|6-128-4|r/rr-xr-xr-x|0|0|97792|1757990014|1757990014|1757990014|1757990014
0|/$Boot ($FILE_NAME)|7-48-2|r/rr-xr-xr-x|48|0|76|1757990014|1757990014|1757990014|1757990014
0|/$Boot|7-128-1|r/rr-xr-xr-x|48|0|8192|1757990014|1757990014|1757990014|1757990014
0|/$Extend ($FILE_NAME)|11-48-3|d/dr-xr-xr-x|0|0|80|1757990014|1757990014|1757990014|1757990014
0|/$Extend|11-144-4|d/dr-xr-xr-x|0|0|656|1757990014|1757990014|1757990014|1757990014
0|/$Extend/$Deleted ($FILE_NAME)|29-48-1|d/dr-xr-xr-x|0|0|82|1757990015|1757990015|1757990015|1757990015
0|/$Extend/$Deleted|29-144-2|d/dr-xr-xr-x|0|0|48|1757990015|1757990015|1757990015|1757990015
0|/$Extend/$ObjId ($FILE_NAME)|25-48-1|r/rr-xr-xr-x|0|0|78|1757990015|1757990015|1757990015|1757990015
0|/$Extend/$ObjId:$O|25-144-2|r/rr-xr-xr-x|0|0|48|1757990015|1757990015|1757990015|1757990015
0|/$Extend/$Quota ($FILE_NAME)|24-48-1|r/rr-xr-xr-x|0|0|78|1757990015|1757990015|1757990015|1757990015
0|/$Extend/$Quota:$O|24-144-3|r/rr-xr-xr-x|0|0|88|1757990015|1757990015|1757990015|1757990015
0|/$Extend/$Quota:$Q|24-144-2|r/rr-xr-xr-x|0|0|208|1757990015|1757990015|1757990015|1757990015
0|/$Extend/$Reparse ($FILE_NAME)|26-48-1|r/rr-xr-xr-x|0|0|82|1757990015|1757990015|1757990015|1757990015
0|/$Extend/$Reparse:$R|26-144-2|r/rr-xr-xr-x|0|0|48|1757990015|1757990015|1757990015|1757990015
0|/$Extend/$RmMetadata ($FILE_NAME)|27-48-1|d/dr-xr-xr-x|0|0|88|1757990015|1757990015|1757990015|1757990015
0|/$Extend/$RmMetadata|27-144-2|d/dr-xr-xr-x|0|0|336|1757990015|1757990015|1757990015|1757990015
0|/$Extend/$RmMetadata/$Repair ($FILE_NAME)|28-48-1|r/rr-xr-xr-x|0|0|80|1757990015|1757990015|1757990015|1757990015
0|/$Extend/$RmMetadata/$Repair|28-128-4|r/rr-xr-xr-x|0|0|0|1757990015|1757990015|1757990015|1757990015
0|/$Extend/$RmMetadata/$Repair:$Config|28-128-2|r/rr-xr-xr-x|0|0|8|1757990015|1757990015|1757990015|1757990015
0|/$Extend/$RmMetadata/$Repair:$Corrupt|28-128-6|r/rr-xr-xr-x|0|0|2359296|1757990015|1757990015|1757990015|1757990015
0|/$Extend/$RmMetadata/$Repair:$Verify|28-128-8|r/rr-xr-xr-x|0|0|512000|1757990015|1757990015|1757990015|1757990015
0|/$Extend/$RmMetadata/$Txf ($FILE_NAME)|31-48-1|d/dr-xr-xr-x|0|0|74|1757990015|1757990015|1757990015|1757990015
0|/$Extend/$RmMetadata/$Txf|31-144-2|d/dr-xr-xr-x|0|0|48|1757990015|1757990015|1757990015|1757990015
0|/$Extend/$RmMetadata/$TxfLog ($FILE_NAME)|30-48-1|d/dr-xr-xr-x|0|0|80|1757990015|1757990015|1757990015|1757990015
0|/$Extend/$RmMetadata/$TxfLog|30-144-2|d/dr-xr-xr-x|0|0|568|1757990015|1757990015|1757990015|1757990015
0|/$Extend/$RmMetadata/$TxfLog/$Tops ($FILE_NAME)|32-48-1|r/rr-xr-xr-x|0|0|76|1757990015|1757990015|1757990015|1757990015
0|/$Extend/$RmMetadata/$TxfLog/$Tops|32-128-2|r/rr-xr-xr-x|0|0|100|1757990015|1757990015|1757990015|1757990015
0|/$Extend/$RmMetadata/$TxfLog/$Tops:$T|32-128-4|r/rr-xr-xr-x|0|0|1048576|1757990015|1757990015|1757990015|1757990015
0|/$Extend/$RmMetadata/$TxfLog/$TxfLog.blf ($FILE_NAME)|33-48-2|r/rrwxrwxrwx|0|0|88|1757990015|1757990015|1757990015|1757990015
0|/$Extend/$RmMetadata/$TxfLog/$TxfLog.blf|33-128-1|r/rrwxrwxrwx|0|0|65536|1757990015|1757990015|1757990015|1757990015
0|/$Extend/$RmMetadata/$TxfLog/$TxfLogContainer00000000000000000001 ($FILE_NAME)|34-48-2|r/rrwxrwxrwx|0|0|138|1757990015|1757990015|1757990015|1757990015
0|/$Extend/$RmMetadata/$TxfLog/$TxfLogContainer00000000000000000001|34-128-1|r/rrwxrwxrwx|0|0|2097152|1757990015|1757990015|1757990015|1757990015
0|/$Extend/$RmMetadata/$TxfLog/$TxfLogContainer00000000000000000002 ($FILE_NAME)|35-48-2|r/rrwxrwxrwx|0|0|138|1757990015|1757990015|1757990015|1757990015
0|/$Extend/$RmMetadata/$TxfLog/$TxfLogContainer00000000000000000002|35-128-1|r/rrwxrwxrwx|0|0|2097152|1757990015|1757990015|1757990015|1757990015
0|/$Extend/$UsnJrnl ($FILE_NAME)|39-48-1|r/rr-xr-xr-x|0|0|82|1757990019|1757990019|1757990019|1757990019
0|/$Extend/$UsnJrnl:$J|39-128-3|r/rr-xr-xr-x|0|0|1128|1757990019|1757990019|1757990019|1757990019
0|/$Extend/$UsnJrnl:$Max|39-128-4|r/rr-xr-xr-x|0|0|32|1757990019|1757990019|1757990019|1757990019
0|/$LogFile ($FILE_NAME)|2-48-2|r/rr-xr-xr-x|0|0|82|1757990014|1757990014|1757990014|1757990014
0|/$LogFile|2-128-1|r/rr-xr-xr-x|0|0|7684096|1757990014|1757990014|1757990014|1757990014
0|/$MFT ($FILE_NAME)|0-48-3|r/rr-xr-xr-x|0|0|74|1757990014|1757990014|1757990014|1757990014
0|/$MFT|0-128-6|r/rr-xr-xr-x|0|0|262144|1757990014|1757990014|1757990014|1757990014
0|/$MFTMirr ($FILE_NAME)|1-48-2|r/rr-xr-xr-x|0|0|82|1757990014|1757990014|1757990014|1757990014
0|/$MFTMirr|1-128-1|r/rr-xr-xr-x|0|0|4096|1757990014|1757990014|1757990014|1757990014
0|/$Secure ($FILE_NAME)|9-48-7|r/rr-xr-xr-x|0|0|80|1757990014|1757990014|1757990014|1757990014
0|/$Secure:$SDS|9-128-8|r/rr-xr-xr-x|0|0|263544|1757990014|1757990014|1757990014|1757990014
0|/$Secure:$SDH|9-144-11|r/rr-xr-xr-x|0|0|56|1757990014|1757990014|1757990014|1757990014
0|/$Secure:$SII|9-144-14|r/rr-xr-xr-x|0|0|56|1757990014|1757990014|1757990014|1757990014
0|/$UpCase ($FILE_NAME)|10-48-2|r/rr-xr-xr-x|0|0|80|1757990014|1757990014|1757990014|1757990014
0|/$UpCase|10-128-1|r/rr-xr-xr-x|0|0|131072|1757990014|1757990014|1757990014|1757990014
0|/$UpCase:$Info|10-128-4|r/rr-xr-xr-x|0|0|32|1757990014|1757990014|1757990014|1757990014
0|/$Volume ($FILE_NAME)|3-48-1|r/rr-xr-xr-x|0|0|80|1757990014|1757990014|1757990014|1757990014
0|/$Volume|3-128-3|r/rr-xr-xr-x|0|0|0|1757990014|1757990014|1757990014|1757990014
0|/System Volume Information ($FILE_NAME)|36-48-2|d/dr-xr-xr-x|0|0|116|1757990018|1757990018|1757990018|1757990018
0|/System Volume Information|36-144-1|d/dr-xr-xr-x|0|0|280|1757990019|1757990019|1757990019|1757990018
0|/System Volume Information/IndexerVolumeGuid ($FILE_NAME)|38-48-2|r/rrwxrwxrwx|0|0|100|1757990019|1757990019|1757990019|1757990019
0|/System Volume Information/IndexerVolumeGuid|38-128-1|r/rrwxrwxrwx|0|0|76|1757990019|1757990019|1757990019|1757990019
0|/System Volume Information/WPSettings.dat ($FILE_NAME)|37-48-2|r/rrwxrwxrwx|0|0|94|1757990018|1757990018|1757990018|1757990018
0|/System Volume Information/WPSettings.dat|37-128-1|r/rrwxrwxrwx|0|0|12|1757990019|1757990018|1757990018|1757990018
0|/lab ($FILE_NAME) (deleted)|41-48-2|-/drwxrwxrwx|0|0|72|1757990213|1757990213|1757990213|1757990213
0|/lab (deleted)|41-144-1|-/drwxrwxrwx|0|0|48|1757990276|1757990276|1757990276|1757990213
0|/lab/flag.png ($FILE_NAME) (deleted)|40-48-5|-/rrwxrwxrwx|0|0|82|1757990127|1757988938|1757990207|1757990127
0|/lab/flag.png (deleted)|40-128-3|-/rrwxrwxrwx|0|0|6183|1757990127|1757988938|1757990219|1757990127
0|/$OrphanFiles|256|V/V---------|0|0|0|0|0|0|0
                                                                                                                                                                           
┌──(kali㉿kali)-[~/Desktop/CHALL4N6ATHONG]
└─$ fls -o 32768 -r recovery_USB.img | grep ':'

r/r 4-128-1:    $AttrDef
r/r 8-128-2:    $BadClus
r/r 8-128-1:    $BadClus:$Bad
r/r 6-128-4:    $Bitmap
r/r 7-128-1:    $Boot
d/d 11-144-4:   $Extend
+ d/d 29-144-2: $Deleted
+ r/r 25-144-2: $ObjId:$O
+ r/r 24-144-3: $Quota:$O
+ r/r 24-144-2: $Quota:$Q
+ r/r 26-144-2: $Reparse:$R
+ d/d 27-144-2: $RmMetadata
++ r/r 28-128-4:        $Repair
++ r/r 28-128-2:        $Repair:$Config
++ r/r 28-128-6:        $Repair:$Corrupt
++ r/r 28-128-8:        $Repair:$Verify
++ d/d 31-144-2:        $Txf
++ d/d 30-144-2:        $TxfLog
+++ r/r 32-128-2:       $Tops
+++ r/r 32-128-4:       $Tops:$T
+++ r/r 33-128-1:       $TxfLog.blf
+++ r/r 34-128-1:       $TxfLogContainer00000000000000000001
+++ r/r 35-128-1:       $TxfLogContainer00000000000000000002
+ r/r 39-128-3: $UsnJrnl:$J
+ r/r 39-128-4: $UsnJrnl:$Max
r/r 2-128-1:    $LogFile
r/r 0-128-6:    $MFT
r/r 1-128-1:    $MFTMirr
r/r 9-128-8:    $Secure:$SDS
r/r 9-144-11:   $Secure:$SDH
r/r 9-144-14:   $Secure:$SII
r/r 10-128-1:   $UpCase
r/r 10-128-4:   $UpCase:$Info
r/r 3-128-3:    $Volume
d/d 36-144-1:   System Volume Information
+ r/r 38-128-1: IndexerVolumeGuid
+ r/r 37-128-1: WPSettings.dat
-/d * 41-144-1: lab
+ -/r * 40-128-3:       flag.png
V/V 256:        $OrphanFiles
```

→ Thấy thư mục **/lab** & **flag.png** đã **deleted** (inode/attr `40-128-3`).

---

# 7) Bước 5 – Phục hồi file đã xoá

**Cách A – icat theo inode/attr (nhanh-gọn):**

```bash
istat -o 32768 recovery_USB.img 40-128-3   # xem chi tiết
icat  -o 32768 recovery_USB.img 40-128-3 > flag.png
file flag.png
```
```bash!
┌──(kali㉿kali)-[~/Desktop/CHALL4N6ATHONG]
└─$ istat -o 32768 recovery_USB.img 40-128-3 
MFT Entry Header Values:
Entry: 40        Sequence: 2
$LogFile Sequence Number: 2128984
Not Allocated File
Links: 1

$STANDARD_INFORMATION Attribute Values:
Flags: Archive
Owner ID: 0
Security ID: 264  (S-1-5-21-252484922-3200378321-683739697-1000)
Last User Journal Update Sequence Number: 896
Created:        2025-09-16 09:35:27.271860000 (+07)
File Modified:  2025-09-16 09:15:38.529650000 (+07)
MFT Modified:   2025-09-16 09:36:59.437564900 (+07)
Accessed:       2025-09-16 09:35:27.273867300 (+07)

$FILE_NAME Attribute Values:
Flags: Archive
Name: flag.png
Parent MFT Entry: 41    Sequence: 1
Allocated Size: 8192    Actual Size: 6183
Created:        2025-09-16 09:35:27.271860000 (+07)
File Modified:  2025-09-16 09:15:38.529650000 (+07)
MFT Modified:   2025-09-16 09:36:47.910818700 (+07)
Accessed:       2025-09-16 09:35:27.273867300 (+07)

Attributes: 
Type: $STANDARD_INFORMATION (16-0)   Name: N/A   Resident   size: 72
Type: $FILE_NAME (48-5)   Name: N/A   Resident   size: 82
Type: $DATA (128-3)   Name: N/A   Non-Resident   size: 6183  init_size: 6183
2245 2246 
                                                                                                                                                                           
┌──(kali㉿kali)-[~/Desktop/CHALL4N6ATHONG]
└─$ icat -o 32768 recovery_USB.img 40-128-3 > flag.png
                                                                                                                                                                           
┌──(kali㉿kali)-[~/Desktop/CHALL4N6ATHONG]
└─$ xxd flag.png | head                                    
00000000: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000010: 0000 04bf 0000 01f9 0806 0000 0060 459c  .............`E.
00000020: 4100 0000 0173 5247 4200 aece 1ce9 0000  A....sRGB.......
00000030: 0004 6741 4d41 0000 b18f 0bfc 6105 0000  ..gAMA......a...
00000040: 0009 7048 5973 0000 0ec3 0000 0ec3 01c7  ..pHYs..........
00000050: 6fa8 6400 0017 bc49 4441 5478 5eed dd3f  o.d....IDATx^..?
00000060: 6b5b 79be c7f1 6fd4 0816 66ab 9907 b020  k[y...o...f.... 
00000070: 08c6 e046 6c63 6e71 e1b2 6048 e174 e902  ...Flcnq..`H.t..
00000080: eadc 2c66 b9c1 5c6e 9122 a549 e32e 9d60  ..,f..\n.".I...`
00000090: ba74 36ec 2c7a 04e9 dc18 4c08 08f6 01cc  .t6.,z....L.....
                                                                                                                                                                           
┌──(kali㉿kali)-[~/Desktop/CHALL4N6ATHONG]
└─$ 
```
**Cách B – Recover hàng loạt:**

```bash
mkdir out
tsk_recover -o 32768 -a recovery_USB.img out
# -> tìm out/lab/flag.png
```

---

# 8) Bước 6 – Vá file ảnh bị “cắt đầu”

`xxd flag.png` cho thấy **16 byte đầu = 0x00** (PNG signature + IHDR length/type bị xoá).
Từ offset `0x10` đã có **IHDR data** hợp lệ (width=1215, height=505, 8-bit RGBA, …), `sRGB`, `gAMA`, `pHYs`, `IDAT` vẫn nguyên.

**Cách vá: ghi đè 16 byte đầu** bằng **PNG signature** + **IHDR length/type**:

```bash
cp flag.png flag.orig.png  # backup

# 16 byte: 89 50 4E 47 0D 0A 1A 0A  00 00 00 0D  49 48 44 52
echo '89504e470d0a1a0a0000000d49484452' | xxd -r -p | dd of=flag.png bs=1 seek=0 conv=notrunc

file flag.png
# pngcheck -v flag.png   # nếu có
# xdg-open flag.png       # mở ảnh lấy flag
```
![image](https://hackmd.io/_uploads/rynReFIogl.png)

![flag](https://hackmd.io/_uploads/ryP1-tUjlx.png)


---

# 9) Tóm tắt logic & vì sao cách này hiệu quả

* **Partition table hỏng** chỉ làm công cụ “tự động” bị mù; **volume NTFS bên trong vẫn nguyên**.
* Dùng **signature-based scan** tìm boot sector & **tính offset** → bypass partition table.
* **Backup BS off-by-one** là bẫy: sector chuẩn bị zero, sector kế tiếp chứa BS hợp lệ.
* Mount theo **offset 16 MiB**, hoặc dùng **Sleuth Kit** (khỏi mount) để liệt kê & phục hồi.
* File **flag.png** bị **xoá** và **cắt header**; chỉ cần **icat** + **vá 16 byte đầu** là xong.

---

# 10) Phụ lục – Script quét & sửa NTFS boot sector

## 10.1. Script quét/verify/repair (đã dùng ở trên)

* Chức năng:

  * `scan` – quét “NTFS    ”, parse boot-sector, in thông số + offset
  * `verify` – so sánh primary vs. backup (tính chuẩn)
  * `repair` – **(tuỳ chọn)** chép **backup→primary** nhưng **viết vào bản copy** `*.repaired.img`


**Cách dùng nhanh:**

```bash
# Quét ứng viên NTFS
python3 ntfs_boot_tools.py scan recovery_USB.img

# So sánh primary/backup cho offset cụ thể (bytes)
python3 ntfs_boot_tools.py verify recovery_USB.img --offset-bytes 16777216

# (Tuỳ chọn) Sửa primary từ backup (viết sang .repaired.img)
python3 ntfs_boot_tools.py repair recovery_USB.img --offset-bytes 16777216
```

> Trong bài này **không cần repair** vì primary BS OK. Nếu muốn “sửa tay” bẫy off-by-one (copy sector **6291327** → **32768** trên bản copy), dùng:
>
> ```bash
> cp --sparse=always recovery_USB.img recovery_USB.img.repaired.img
> dd if=recovery_USB.img of=recovery_USB.img.repaired.img bs=512 count=1 skip=6291327 seek=32768 conv=notrunc
> ```

## 10.2. Tính toán tham khảo

* **Start offset (bytes)** = **16 MiB** = `16777216`
* **Start LBA** = `16777216 / 512` = **32768**
* **TotalSectors** = `6258559` (từ boot sector)
* **Backup (expected)** = `offset + TotalSectors*512 - 512` = `3221158912` (bị **zero**)
* **Backup (actual)** = `expected + 512` = `3221159424` (BS hợp lệ)
* `$MFT` LCN = `262144` → Absolute offset = `16777216 + 262144*4096 = 1090519040` (có “FILE” magic)

---

