Dynamic analysis: victim timestamps as seed anchors (run-4 outputs, 2026-10-12)

Question

recover needs a window for the key2 seed, which is the machine’s uptime in ns when the file was processed. So far that window came from debugger captures. Can artifacts that a responder actually has (file times, boot records, the operator log) place it instead? Run 4 left 21 .akira files on the VM whose exact seeds were logged in-process, so every timestamp can be scored against the truth.

Method (read-only on the VM; no sample code ran)

Units: QPC runs at 10 MHz, so QPC ticks and FILETIME share the 100 ns unit, and seed = QPC × 100.

Results

1. The QPC offset measured on the live VM

2. .akira last-write time vs the logged seeds (21 of 21 files matched to one seed row)

| Difference (live offset) | Min | Median | Max | |—|—|—|—| | mtime − key1 | +1.28 ms | +2.34 ms | +6.28 ms | | mtime − key2 | +0.45 ms | +1.39 ms | +5.36 ms | | mtime − iv2 | +0.06 ms | +0.98 ms | +3.82 ms | | mtime − per-file coroutine end | −1.43 ms | −0.53 ms | −0.12 ms |

3. Other anchors from the same run

| Artifact | Relation found | Use | |—|—|—| | akira_readme.txt mtime | 1.3 ms before the folder’s first key1 seed | marks the start of a folder’s processing | | Operator log name Log-11-10-2026-23-26-18.txt | local start time to the second (main start by QPC: 22:26:18.250Z) | coarse run start | | Operator log creation time | 3.6 ms before the main-start QPC read | precise run start in system time | | Operator log last-write time | 0.03 ms before the main-end QPC read | precise run end in system time | | Last log line 423038 ms | equals (main-end − main-start) QPC / 10⁴, truncated | confirms the sample’s own timer is QPC |

The log fixes the run’s start and end in system time, not in QPC, so it does not remove the need for the offset.

4. Boot-time sources a responder has after a reboot

| Source | Value | Error vs the QPC offset | |—|—|—| | WMI LastBootUpTime | 14:01:30.500Z | +352 ms | | Kernel-General 12 StartTime (event XML) | 14:01:30.5000000Z | +352 ms (KeBootTime, kept at 0.5 s granularity) | | Kernel-General 12 TimeCreated | 14:01:31.645Z | +1,497 ms | | EventLog 6005 TimeCreated | 14:01:39.768Z | +9,620 ms | | BootCKCL.etl / ShutdownCKCL.etl | not present on this VM (WDI\LogFiles missing) | untested |

So after a reboot the standard sources fix the offset only to a few hundred ms. The error is one constant per boot, though, and every file encrypted in that boot shares it.

Blind recovery test

recover --qpc-offset <live offset> --mtime <probe mtime> with the default window (8 ms back, 1 ms forward) and default --gap 3800:8000 used no seed values. Results: artifacts/timestamp-blind-recover-2026-10-12.txt.

File key2 candidates KCipher-2 pairs Result
gap_00.txt.akira (Spot; its key2 → iv2 gap is the 7,404-tick outlier) 90,001 378,094,201 true key2/iv2 pair found after 1,704 s (28 min); decrypted to all zeros
gap_db_0.sqlite.akira – – stopped at the user’s request seconds after it started: it competed with the VM for CPU, and gap_00 already shows the method

So the seeds were found from victim-side data alone: the file’s last-write time and the QPC offset measured on the same boot.

On speed: the window is split into 1,407 slices of 64 key2 seeds, taken roughly in order by 20 workers, and the search stops at the first hit. The true key2 lay in slice 957, 68% of the way through, so ~2.6·10⁸ pairs were tested in 1,704 s. That is ~150k pairs/s, as the cost tables assume; a full sweep of this window takes ~42 min.

What this means for recovery

| Situation | key2 window for the first file | Pairs at gap span 4,200 | Time at ~150k pairs/s | |—|—|—|—| | Victim still on the same boot (or a memory image): offset measured live, 1 ms tick | 9 ms (−8/+1) | 3.8·10⁸ | ~42 min (~14 min with --gap 3800:5200) | | Same, host at the default 15.6 ms tick | ~25 ms | 1.1·10⁹ | ~2 h | | Rebooted: offset from KeBootTime (LastBootUpTime / event 12 StartTime) | ~±0.6 s | ~5·10¹⁰ | ~4 days (one file only) |

Procedure:

  1. Before any reboot, record QPC and precise system time on the victim. A memory image serves the same purpose, since it holds both clocks in KUSER_SHARED_DATA.
  2. Copy the .akira files with their timestamps preserved, or record each file’s LastWriteTime.
  3. Start with the smallest files. Their mtime sits closest to key2.
  4. After the first file is solved, its seeds pin the offset to about ±2.5 ms even if the boot source was coarse. Every other file of that boot then costs the same as the live case.

Untested:

Artifacts