Dynamic analysis: run 5, blind recovery of real files (2026-10-12)
Goal
The strongest evidence for the decryptor: real files (a PDF, a DOCX, two PNG screenshots) that the live sample encrypts with no breakpoints in the crypto path and no seed capture. They are then recovered using only what a responder has, and the decrypted SHA-256 is compared with the originals.
Setup
- VM: fresh snapshot revert. The analyst placed the files in
C:\Users\deadtrace\Desktop\samples\data. - Leftover session: after reconnecting, x64dbg held a sample instance paused at the system breakpoint, launched with an unknown command line. It was stopped before any of its code ran.
- Pre-encryption inventory (
run5-blind/inventory_probe.ps1→run5-blind/inv_pre.txt): name, size, SHA-256, times and first 32 bytes of every file undersamples. This file is read again only for the final hash comparison.
| File | Size | SHA-256 (original) |
|---|---|---|
1.pdf |
97,577 | b19683e29bee572e7b3fea64ab3966ed6e97e9853a4c3569d810327aadbf2de6 |
QC-STC-T-45-26_Sample_IR_Deliverables_v1.0.docx |
42,333 | 67d6a02e1398a4c1b02d0e33410450bccfb96e1dbd537000dface2abbb7c8360 |
Screenshot 2026-09-29 125745.png |
47,803 | 9d014edc281cf477614ab09104672741d35c11ca63ea76577ea04b89c6e203a2 |
Screenshot 2026-10-05 102304.png |
117,188 | cf6a612134a1ba5d5f874a6dab7142107fe010574eb33397929efb7d22a09bd0 |
Detonation (x64dbg, image base 0x7FF781AD0000)
- Launch:
init "<exe>", "--encryption_path=C:\Users\deadtrace\Desktop\samples\data -localonly", "C:\Users\deadtrace\Desktop". - Safety gates (as in runs 2–4):
- The WMI shadow-copy executor
0x140071620was hit and forced to return (rax=0; rip=[rsp]; rsp=rsp+8). - The root gate
0x140073950showed one root,r8→C:\Users\deadtrace\Desktop\samples\data(0x27 chars).
- The WMI shadow-copy executor
- All breakpoints were deleted, then the process ran to exit. The event log shows 2 thread creations, 8 thread exits, the process exit, and no exceptions or breakpoint hits. No hook was installed and no seed was read.
- Operator log
Desktop\Log-12-10-2026-10-39-01.txt: the root was...\samples\data, 2 encryptor threads,76148 ms(the run includes the time paused at the gates).
After the run (same boot, no reboot)
- Post-run probe (
run5-blind/post_probe.ps1→run5-blind/post_run5.txt):- QPC frequency 10 MHz; 8 QPC/precise-system-time pairs;
- the listing again (name, size, SHA-256, times, first 32 bytes);
- the operator log.
- Only
samples\datachanged. The four files became<name>.akira, each +512 bytes, andakira_readme.txtwas added. The exe, zip and stage2 files kept their SHA-256. - Pull to the host: the four
.akirafiles came over via Ghidra raw import +read_memory. Each host copy matches the VM’s SHA-256 (run5-blind/akira/).
Blind recovery (run5-blind/run5_blind_recover.py)
- QPC offset: the median of the 7 clean post-run pairs (first call excluded).
- key2 window: each file’s LastWriteTime minus the offset, −8/+1 ms (the
--mtime-windowdefault). Default--gap 3800:8000. - Known plaintext from the file type, or from the file’s own unencrypted bytes:
- PNG: the 16-byte signature + IHDR header that every PNG shares.
- PDF:
%PDF-1., 7 bytes, version digit unknown.recovernow accepts 7–8 known bytes through a byte mask on the last keystream word, a 2⁻⁵⁶ filter. - DOCX: 28 bytes of the first ZIP local header, rebuilt from the central directory. Spot mode leaves that directory in the unencrypted second half.
- Encrypted extent: all four files are Spot (50%) with one region under 0xFFFF bytes, so only KCipher-2 (key2/iv2) is involved.
Results: 4 of 4 real files recovered blind, each byte-identical to its original
Log: artifacts/run5-blind-decryption-evidence-2026-10-12.txt (the fourth file: addendum 4 and
artifacts/run5-png1-iv2-anchor-2026-10-12.txt). Outputs: run5-blind/decrypted/.
Search speed: 20 workers, ~150k pairs/s (pure Python). Re-run on the numba engine later the same day: all four
files hash-exact at 16–26 s each (artifacts/run5-numba-rerun-2026-10-12.txt).
| File | Known plaintext | Search | Seeds found (key2 / iv2, gap) | Decrypted SHA-256 = original |
|---|---|---|---|---|
1.pdf |
%PDF-1. (7 B) |
1,875 s | 870401719600 / 870402152100, 4,325 ticks | ✅ b19683e29bee572e…aadbf2de6 |
QC-STC-…_v1.0.docx |
28 B rebuilt from the central directory | 1,679 s | 870405673900 / 870406067000, 3,931 ticks | ✅ 67d6a02e1398a4c1…2abbb7c8360 |
Screenshot 2026-10-05 102304.png |
PNG signature + IHDR (16 B) | 1,802 s | 870409455400 / 870409845300, 3,899 ticks | ✅ cf6a612134a1ba5d…7d22a09bd0 |
Screenshot 2026-09-29 125745.png |
PNG signature + IHDR (16 B) | 4,834 s missed (gap ≤ 8,000), then 1,240 s with --anchor iv2 |
870409493800 / 870412165600, 26,718 ticks | ✅ 9d014edc281cf477…c20e3a2 |
Timing of the three solved files. All times are relative to akira_readme.txt creation,
using the live offset. They match run 4:
| File | key2 − readme | mtime − key2 | mtime − iv2 |
|---|---|---|---|
| +2.88 ms | 1.24 ms | 0.80 ms | |
| DOCX | +6.83 ms | 2.22 ms | 1.83 ms |
| second PNG | +10.61 ms | 1.54 ms | 1.15 ms |
| first PNG | +10.65 ms | 3.60 ms | 0.93 ms |
The fourth file (first PNG): a seed-gap outlier. Its mtime is +14.25 ms after the note, the latest in the folder. Three passes found nothing, all with gaps of 3,800–8,000 ticks (addenda 1–3):
- the default window, mtime −8/+1 ms;
- ±1.5 ms around the PDF’s key2, with gaps up to 5,200 (a wrong per-thread guess);
- the rest of the window, from the note −2 ms to the mtime +1 ms.
The user first chose to stop at 3 of 4. Later the same day (addendum 4), recover --anchor iv2 searched only
the untested part of the space:
- iv2 within mtime −2.5/+0.1 ms (on solved files, iv2 sat 0.8–1.8 ms before the mtime);
- gaps 8,001–30,000 ticks.
That is 572 M pairs, and the key was found after 1,240 s. The decrypted file is hash-exact. The gap is 26,718 ticks (2.67 ms), close to run 4’s 27,109 outlier. key2 was drawn 38 µs after the second PNG’s key2, so the two files were set up on the two encryptor threads at the same moment. The first-pass explanation (a gap above 8,000) was right. Keys taken before the note was written were not needed.
Lessons for recover:
--floor: the note’s creation time bounds every file in the folder from below.- No per-thread chaining: each file is an asio coroutine, so a file’s keys can sit anywhere between the note and its mtime. Chaining from a solved file on the “same thread” is not reliable.
- Gap outliers are the main risk, and
--anchor iv2handles them. iv2 sat 0.8–1.8 ms before mtime on all four files (0.93 ms on the outlier, whose key2 was 3.6 ms before mtime). Anchoring the window on iv2 makes a large-gap search cost (iv2 window × gap span), about half of the key2-anchored equivalent. Recommended second pass:--anchor iv2 --mtime-window 2.5:0.1 --gap 8001:30000.
Decryptor changes made during this run (06-decryptor/akira_decrypt.py):
- 7-byte known plaintext through a byte mask on the last keystream word (used for
%PDF-1.); --floor/--floor-only;--anchor iv2(added for the fourth file; tool SHA-256eac5d0d6…). Validated on the second PNG: its known seeds were found in 0.8 s from a 1.3 ms iv2 window. All three suites pass.
The tool’s hash changed mid-run: the first PNG’s first pass and the PDF ran on 6949b44c…, everything
after on c7e1e660…. Both
changes only add options; the search code is the same. Test suites on c7e1e660…: selftest 8/8,
real outputs 13/13, round trip 29/29. On eac5d0d6… (with --anchor iv2): 8/8, 14/14, 29/29
(artifacts/test-run-2026-10-12.txt).
VM state
- The data folder is encrypted. Leftovers on the Desktop:
inv.ps1,post.ps1,inv_pre.txt,post_run5.txtand the operator log. - Ghidra
/decrypt_testholds the run-5 imports. - Revert recommended.