Illustration: an AI agent at a desk working through the Akira recovery, from reverse engineering and memory analysis to timestamp correlation, rebuilding the decryptor and restoring files
Ransomware analysis · File recovery

Breaking Akira’s clock: recovering files without the ransom key

An Akira-branded Windows encryptor builds every file key from the machine’s uptime. We show how a responder can turn that into decrypted files, prove it on real documents, and spell out exactly when it will not work.

Key findings

  • The encryptor seeds a fresh Yarrow-256 generator for every key with one thing only: the machine’s uptime counter, written as a decimal string.
  • That turns “guess a 128-bit key” into “guess when the file was encrypted, to a few milliseconds”. The operator’s RSA key is never needed.
  • In a blind test on a freshly reverted VM, we recovered all four real files (a PDF, a DOCX and two PNG screenshots) byte-for-byte, SHA-256 identical, using only what an incident responder would have. Three took about 30 minutes each; one timing outlier needed a second, 21-minute pass.
  • A second, fully independent run repeated the test on files the analyst picked and we had not seen, with every recovery rule fixed before encryption. Six of seven in-scope files came back SHA-256 identical in about 25 seconds each. The seventh, a 482 KB PDF, came back only partly decrypted. A PDF-aware check added afterwards recovers it too.
  • A second build (988776358d0e…) generates its keys the same way. The unchanged decryptor recovered six of its seven in-scope files hash-exact, again in about 25 seconds each. Its large PDF, which stopped at the same point, is also recovered by the new check.
  • It works best on a machine that has not been rebooted, or from a memory image. After a reboot, the first file becomes a multi-day search.
01 · The sample

A self-contained C++ encryptor that calls itself Akira

The binary is a 64-bit Windows executable written in modern C++ (C++20 coroutines on the asio I/O library), with the Nettle crypto library compiled in. It brands itself Akira: .akira extension, akira_readme.txt notes and Akira onion addresses. This build has no command-and-control, no persistence and no data theft. It encrypts and leaves.

SHA-256101bd45ab673fd6547386dff4339f1649e61b9f5c318260d365f766fbdc2d3c9
MD5427eba9f6ff2cd312c72770f13e279c1
TypePE32+ x86-64, GUI subsystem
Compiled2023-12-28 14:49 UTC
ToolchainMSVC 14.37, C++20, asio, Nettle
CiphersChaCha8 + KCipher-2, RSA-4096 key wrap

Every finding in this post comes from our own static analysis in Ghidra and seven controlled detonations under x64dbg in an isolated VM: six of this build and one of a second build. Each cryptographic primitive was re-implemented in Python and matched against data captured from the running sample.

02 · How it was analysed

Claude Code driving Ghidra and x64dbg

The whole engagement was run by Claude Code, Anthropic’s agentic CLI, on the analysis host. It worked through two MCP servers inside the malware VM: one exposing Ghidra, one exposing x64dbg. Claude never had a shell on the VM. Everything it did there went through the disassembler’s and the debugger’s own APIs, and the samples never left the VM. I set the goals, chose the test files, reverted snapshots, and made the calls on what counted as proof.

AgentClaude Code (Anthropic) on the analysis host
StaticGhidra 12.1.2 + GhidraMCP 6.0.0 server, in the VM
Dynamicx64dbg (x64) + x64dbg MCP server, in the VM
Victim VMWindows x64 guest, 2 vCPUs, snapshot reverted before each detonation
NetworkHost-only; each MCP endpoint behind its own bearer token
Host toolingPython 3.12 (numpy + numba), 20 threads, for crypto checks and key search

What each tool was used for

WhereWhat Claude did
Ghidra (static)Surveyed imports, strings and segments, then decompiled and followed cross-references from the uptime-counter calls to the per-key Yarrow seeding, the four-key draw and the RSA footer. It decoded the stack-built strings (shadow-copy command, extension tables), renamed and commented functions in the project, and found the two safety gates in each build’s own code. It also doubled as a file-transfer channel: small result files were raw-imported and read back as bytes.
x64dbg (dynamic)Launched the sample with an explicit command line and read the PEB to confirm it. It set breakpoints on the gates and forced the WMI shadow-copy executor to return without running. It read the target root from r8 before letting encryption start. In the early runs it captured seeds, keys and the 91-byte RSA plaintext live. In the blind runs it set no breakpoints in the crypto path at all. For runs 1–4 it wrote zero-filled test files straight from a zeroed debugger allocation (savedata), so each ciphertext was the raw keystream. It also launched the read-only PowerShell probes that recorded the clock pairs and file times.
Host (Python)Re-implemented Yarrow-256, KCipher-2, ChaCha8 and the chunk plan byte for byte. Built the decryptor and its test suites, and the numba search engine. Ran the blind recoveries and wrote the case notes, this post and the full report.

Seven detonations, each with one job

RunPurposeInstrumentation
1First detonation: launch syntax, gates, key-generation captureBreakpoints on key generation
2Cipher, mode and layout validation against the Python re-implementationsBreakpoints on cipher set-up and RSA
3End-to-end: seeds plus the RSA plaintext, compared with the decryptor’s outputSeed and blob captures
4Natural seed spacing on 21 filesAn in-process uptime-counter logger, no seed breakpoints
5Blind recovery of four real filesGates only
6Independent blind test, rules frozen by hash beforehandGates only
B2Second build, recovered with the unchanged decryptorGates only

Keeping it honest

  • One case folder per sample. Every script, raw output, run log and note lives in it, with an append-only audit trail.
  • Our own evidence only. No sandbox reports or third-party write-ups were used. Every claim traces to a decompilation, a debugger capture or a test.
  • Frozen before the test. For the blind runs, the decryptor, the recovery driver and the clock collector were hashed before encryption. The run logs print those hashes, and the original files’ hashes stayed sealed until the end.
  • Small surprises, written down. Two are worth knowing if you build a similar setup:
    • the debugger bridge’s “load binary” call dropped command-line arguments, so every launch used x64dbg’s own init command and was checked against the PEB;
    • memory writes were capped at 512 bytes, so larger files took the long way round.
03 · What it does

From launch to .akira in six moves

  1. Reads its targets from --encryption_path=, a share list (--share_file=), or, with neither, a sweep of the first five drive letters. -localonly skips network shares. A bare -p is silently ignored, which turns a targeted run into a whole-machine run.
  2. Opens an operator log named Log-DD-MM-YYYY-HH-MM-SS.txt in its working directory.
  3. Deletes Volume Shadow Copies by asking WMI to start a hidden powershell.exe -Command "Get-WmiObject Win32_Shadowcopy | Remove-WmiObject". The PowerShell process appears as a child of WmiPrvSE.exe, not of the ransomware.
  4. Frees locked files with the Restart Manager, force-closing whatever holds them while sparing a built-in list of 20 system processes.
  5. Drops akira_readme.txt in each folder it enters, then hands files to a pool of encryptor threads (two on our 2-CPU VM).
  6. Encrypts each file in place, appends a 512-byte RSA footer and renames it to name.akira. A temporary name.arika journal lets an interrupted file be resumed and is deleted afterwards.
04 · How it encrypts

Strong ciphers, fed by a stopwatch

Each file gets four fresh 16-byte values, generated back to back: key1, iv1, key2 and iv2. Each value comes from its own new random generator, and every generator is seeded the same way.

SeedUptime counter × 100QueryPerformanceCounter at 10 MHz, as decimal text, e.g. 870401719600. The only input.
GeneratorFresh Yarrow-256Nettle’s Yarrow: SHA-256 pools, AES-256 output. New instance per value.
KeysFirst 16 byteskey1, iv1, key2, iv2 in turn, about 0.4 ms apart.
CiphersKCipher-2 + ChaCha8key2/iv2 drive KCipher-2; key1/iv1 drive an 8-round ChaCha with a 128-bit key.
WrapRSA-4096 footerA 91-byte blob (mode, percent, size, all four values) sealed into 512 bytes.
Key generation, per file. Everything to the right of the first box is sound cryptography. The first box decides everything.

Three modes, chosen by extension and size

The sample rarely encrypts a whole file. It reads the file in 0xFFFF-byte chunks. The first and last chunk of each encrypted region use KCipher-2, and the chunks in between use ChaCha8.

Spot most files under 2 MB: the first p% (default 50%)
encrypted
untouched
RSA
Full 153 database extensions (.sqlite, .mdf, .accdb …)
encrypted
RSA
Part VM and disk images, or anything over 2,000,000 bytes: 4 stripes
RSA
encryptedleft as plaintext512-byte RSA footer
The file after encryption. Part mode is drawn for a 3,000,000-byte file at 50%: stripes start at 0, 360,000, 720,000 and 1,080,000, as observed live. Spot and Part leave large parts of every file readable.
05 · The flaw

A 128-bit key with a few thousand possible values

The uptime counter ticks ten million times a second. If you know when a file was encrypted to within one second, there are only 107 possible seeds for each key, and every seed gives exactly one key. If you know it to within a few milliseconds, there are tens of thousands.

The four values of a file are also generated in a fixed order and very close together. On our test VM, consecutive seeds were about 0.4 ms apart (median 4,121 ticks between key2 and iv2; 20 of 21 files under 8,000). So once you have one value, the next is never far away.

Why the RSA layer does not help the attacker here

The RSA-4096 footer protects the keys from anyone without the operator’s private key. But we never need to open it. We rebuild the keys from the same uptime values the sample used, so the strong outer layer is simply bypassed.

06 · Recovery, step by step

Decryption in plain English

Guess the uptime at the moment a file was encrypted and you can rebuild that file’s keys. Many files only need key2 and iv2: at the default 50%, a Spot-mode file under about 250 KB is encrypted in at most two chunks, and both use KCipher-2.

  1. Narrow down when the file was encrypted. On the still-running victim machine, read its clock and its uptime counter at the same moment to learn how one converts to the other. The encrypted file’s “last modified” time then gives its uptime at encryption to within a few milliseconds.
  2. Guess the key. Try every uptime value in that window, about 90,000 candidates for a 9 ms window. Each candidate rebuilds a key exactly as the ransomware did.
  3. Guess the companion value. It was made about 0.4 ms after the key, so for each key candidate try the next few thousand uptime values. That is roughly 380 million pairs in total.
  4. Check each guess. Every PDF starts with %PDF-1., every PNG with the same 16 bytes. Decrypt the first few bytes with each pair; the pair that produces the right header is the right one.
  5. Decrypt the file. Replay the sample’s own chunk plan with the recovered keystream, strip the 512-byte footer, and the original is back byte for byte.

Steps 2–4 are the slow part. In pure Python they took about 30 minutes per file on our 20-thread analysis host. With the optional compiled engine (pip install numpy numba) the same search takes about 20 seconds. In the decryptor this is one command:

python akira_decrypt.py recover report.pdf.akira \
    --qpc-offset 134362707471549158 \
    --mtime 134362716175578720 \
    --known-hex 255044462d312e \
    --out report.pdf

--qpc-offset is the clock-to-uptime offset from step 1 and --mtime the file’s last-write time, both as Windows FILETIME values. --known-hex here is %PDF-1.. The values shown are from our run-5 PDF.

If the first pass misses. Occasionally the companion value comes much later than usual (section 07). Run a second pass anchored on the companion instead. On all eighteen files whose keys we have found so far, across both builds, it landed 0.5–1.9 ms before the file’s timestamp, so you can afford to try much larger gaps:

python akira_decrypt.py recover photo.png.akira \
    --qpc-offset 134362707471549158 \
    --mtime 134362716175680085 \
    --anchor iv2 --mtime-window 2.5:0.1 --gap 8001:30000 \
    --out photo.png

Large PDFs. For a file with ChaCha8 chunks, the same command goes on to search for key1 and iv1. For a PDF it falls back to the PDF checks described in section 07 on its own; --stage2 pdf runs only those.

07 · Proof on real files

Real documents recovered blind, in three runs across two builds

Run 5. For the first test we reverted the VM, placed four real files in a folder, and let the sample encrypt them with no hooks and no breakpoints in its crypto path. Two safety gates stayed in place: the shadow-copy deletion was skipped, and the target folder was confirmed before anything ran. Afterwards we used only what a responder would collect:

  • the .akira files and their last-write times;
  • one clock reading (system time and uptime counter together) taken on the same boot, after the attack;
  • known bytes from the file type, or from the file itself: the standard PNG header, %PDF-1., and for the DOCX the first ZIP header rebuilt from its central directory, which Spot mode leaves unencrypted.
FileSizeKnown bytesSearch time (pure Python)Re-run, compiled engineResult
1.pdf97,577 B7 (%PDF-1.)31 min21 sSHA-256 identical
QC-STC-T-45-26_Sample_IR_Deliverables_v1.0.docx42,333 B28 (ZIP header)28 min26 sSHA-256 identical
Screenshot 2026-10-05 102304.png117,188 B16 (PNG header)30 min22 sSHA-256 identical
Screenshot 2026-09-29 125745.png47,803 B16 (PNG header)81 min missed, then 21 min (second pass)13 s missed, then 16 sSHA-256 identical
Run 5 timeline in milliseconds after the ransom note was created For all four files the key2 seed comes 1.2 to 3.6 ms before the file's last-write time, and the iv2 seed 0.8 to 1.8 ms before it. Three files have iv2 about 0.4 ms after key2; the first PNG's iv2 came 2.67 ms after its key2. −2 0 4 8 12 16 ms ransom note created process ends +15.24 PDF key2 +2.88 → mtime +4.11 DOCX key2 +6.83 → mtime +9.05 PNG (2nd) key2 +10.61 → mtime +12.15 PNG (1st) 2.67 ms gap key2 +10.65 → mtime +14.25
key2 seed (recovered)file last-write timeiv2 seed (recovered)outlier seed gap
Run 5 to scale, in milliseconds after the ransom note was written. All four files were encrypted within about 15 ms. On every file, iv2 was generated 0.8–1.8 ms before the last-write time, which is why the timestamp is such a good anchor. The first PNG is the outlier: its key2 came at nearly the same moment as the second PNG’s, but its iv2 came 2.67 ms later instead of the usual 0.4 ms.

The fourth file: a timing outlier. The first pass searched the whole span from 2 ms before the ransom note to just after the file’s own timestamp, allowing any gap up to 8,000 ticks between the two seeds, and found nothing in 81 minutes. Its seeds turned out to be 26,718 ticks (2.67 ms) apart, almost seven times the usual 0.4 ms. That fits the one outlier we measured earlier (27,109 ticks), on a VM that had been up for only 15 minutes. The second pass searched from the companion value, which still sat 0.93 ms before the timestamp, and allowed gaps from 8,001 to 30,000 ticks. It found the keys in 21 minutes, and the decrypted screenshot was SHA-256 identical to the original.

Run 6: a fully independent re-test

Run 5 had a weakness: we designed the second pass while solving its fourth file. So we ran the whole test again, with three changes:

  • Rules fixed in advance. Before anything was encrypted, we recorded the SHA-256 of the decryptor, the search engine and the recovery script, together with the rules they apply: the default pass, an automatic second pass if it misses, and which known bytes to use for each file type. The run log prints the same hashes, so nothing could be tuned afterwards.
  • Files we had not seen. After a full VM revert, the analyst chose eight files and placed them in the target folder. The hashes of the originals were sealed before encryption and read only after every file had been processed.
  • Nothing in the crypto path. As in run 5, the same two safety gates were applied, then every breakpoint was removed. One clock reading after the attack gave an offset whose seven samples agreed within 7 µs.
FileSizeKnown bytesPassSearch timeResult
20358696.jpg24,287 B11 (JFIF header)114 sSHA-256 identical
photo_2026-09-23_15-02-25.jpg172,871 B11 (JFIF header)126 sSHA-256 identical
photo_2026-09-24_12-44-15.jpg214,866 B11 (JFIF header)126 sSHA-256 identical
QC-STC-T-45-26_Sample_IR_Deliverables_v1.0.docx42,333 B28 (ZIP header)126 sSHA-256 identical
Screenshot 2026-09-10 152951.png26,953 B16 (PNG header)126 sSHA-256 identical
Screenshot 2026-09-29 125745.png47,803 B16 (PNG header)124 sSHA-256 identical
Autopsy_DFIR_Validation_Playbook_Extended.pdf481,630 B7 (%PDF-1.)122 s, then 2 minBlind: middle not decrypted Recovered afterwards
Screen Recording 2026-09-24 104237.mp46,799,834 Bnone––Out of scope

What run 6 shows.

  • Six of seven in-scope files are hash-exact. All six were found by the default pass in about 25 seconds each, against roughly 30 minutes per file in run 5. That covers every file without ChaCha8 chunks.
  • No timing outlier this time. The widest seed gap was 5,570 ticks, so the automatic second pass never ran.
  • The timestamp anchor held. The companion value sat 0.55–1.86 ms before each file’s last-write time.
  • The JPEG rule worked first time. This run tested it for the first time, and all three JPEGs recovered.
  • Two inputs repeat run 5: the DOCX and one screenshot. They were encrypted with fresh keys, and the search uses nothing from a file except its type header, so they still count.
The PDF: a limitation we predicted

The PDF is over 262 KB, so the middle of its encrypted half is ChaCha8, not KCipher-2. We found its KCipher-2 keys in 22 seconds, so the first and last 64 KB of the encrypted half decrypt correctly. The ChaCha8 search tested 24 million candidate pairs. It accepts a key only if the output looks like zeros or plain text, and nothing passed. Compressed PDF data passes neither test, which is the likely cause, but this run cannot rule out that the right key fell outside the search window. We flagged this risk in writing before the run. We later confirmed the first cause and recovered the file with a PDF-aware check (see Afterwards: recovering the two PDFs). The MP4 was out of scope because no fixed header rule exists for it yet.

A second build, same flaw

A different Akira-branded build, SHA-256 988776358d0e45a4907dc1f4906a916f1b3595a31fa44d8e04e563a32557eb42, was the next test. It is the same C++ codebase recompiled with new string-obfuscation constants, a new victim chat code and its own operator RSA key. We read its code before running it:

  • Same random source. It imports no secure random API. Each key and IV still comes from a fresh Yarrow-256 seeded with nothing but the uptime counter, written as a decimal string.
  • Same key set and footer. It draws the same four values in the same order, packs them into the same 91-byte blob, and wraps that in the same 512-byte RSA footer.
  • Same file handling. It keeps the same ciphers, the same 2,000,000-byte Part-mode threshold and the same command-line options.
  • Different hiding. Its shadow-copy command decodes with different constants, but it is the same command.

We then repeated run 6 exactly:

  • a fresh revert;
  • the analyst’s files;
  • the original hashes sealed before encryption;
  • the same two safety gates, located in this build’s own code;
  • no breakpoints during encryption.

Recovery used build 1’s decryptor byte-for-byte unchanged.

FileSizeKnown bytesPassSearch timeResult
20358696.jpg24,287 B11 (JFIF header)115 sSHA-256 identical
photo_2026-09-23_15-02-25.jpg172,871 B11 (JFIF header)126 sSHA-256 identical
photo_2026-09-24_12-44-15.jpg214,866 B11 (JFIF header)130 sSHA-256 identical
QC-STC-T-45-26_Sample_IR_Deliverables_v1.0.docx42,333 B28 (ZIP header)126 sSHA-256 identical
Screenshot 2026-09-10 152951.png26,953 B16 (PNG header)128 sSHA-256 identical
Screenshot 2026-09-29 125745.png47,803 B16 (PNG header)122 sSHA-256 identical
Detection Lab Automated BAS-to-Splunk Detection Engineering Loop.pdf595,456 B7 (%PDF-1.)122 s, then 2 minBlind: middle not decrypted Recovered afterwards
rule.yml981 Bnone––Out of scope
AnyDesk.exe8,375,224 B–––Not encrypted (.exe excluded)

The result matches build 1.

  • Every file without ChaCha8 chunks came back hash-exact, all on the default pass.
  • The timestamp anchor held. The companion value sat 0.9–1.9 ms before each file’s timestamp, and the second pass never ran.
  • The large PDF stopped at the same ChaCha8 limit, until the PDF check described next.

Reading the code showed the key generation was the same. This run confirmed what reading code cannot: the chunk layout, the cipher details and the timing all line up.

Afterwards: recovering the two PDFs

Both blind runs left one PDF half-decrypted. We went back to find out whether the ChaCha8 key was missing from the search window or the check was rejecting it.

The check was wrong, not the search. In both files, the bytes at the start of the first ChaCha8 chunk are compressed. In the run-6 PDF the whole ChaCha8 region sits inside one 291 KB compressed drawing stream, and the zeros/text check rejected all 971 genuine 64-byte samples of it, so even the right key could never pass. In the build-2 PDF the first bytes are the tail of a compressed stream. About half of the region further in would have passed, but the check never looks there.

After stage 1, most of a PDF is already readable: the KCipher-2 chunks and the whole unencrypted second half. That allows two better checks:

  • Object headers from the index. A PDF’s cross-reference table lists the exact byte position of every object, and each object starts with text such as 26 0 obj. In the build-2 PDF the table sat in the readable half and pointed at 968 objects inside the ChaCha8 chunks. ChaCha8 can produce its keystream at any position directly, so each candidate key costs one 64-byte block.
  • Keep decompressing. When a compressed drawing stream starts in a readable chunk and runs into the ChaCha8 chunk, feed each candidate’s output to the decompressor and check that what comes out is valid PDF drawing syntax: numbers followed by operators that take the right number of values. In a trial, the best wrong key produced 24% valid commands; the right key produced 100%.
PDFWhat made it testableCheckCandidates passingResult
Run 6 (build 1), 482 KBOne compressed drawing stream covers the whole ChaCha8 regionKeep decompressing1 of 23,970,816SHA-256 identical
Build 2, 595 KB968 object headers at known positionsObject headers1 of 24,373,969SHA-256 identical

Both keys were inside the normal search window, with seed gaps of 3,910–4,287 ticks, which is ordinary for this sample. Both checks are now in the decryptor as a fallback for PDFs: if the zeros/text check finds nothing, recover retries with them on its own, and the unchanged command line recovered the two files in 8 and 5 minutes. On the build-2 PDF the decompression check, run on its own, found the same key. With the fallback, every in-scope file from both builds is recoverable. We still report the blind runs as six of seven, because the decryptor frozen for those runs did not have this check.

Earlier validation

Before the blind test, the decryptor reproduced byte-exact output for real .akira files from all three modes (Spot, Full and a 3 MB Part file). Keys rebuilt from captured seeds matched the keys inside the sample’s own RSA plaintext blob, and a seed off by a single tick produced garbage. An earlier blind run on another file found its seeds from the timestamp alone in 28 minutes. Test suites: 8/8 built-in checks, 15/15 on real sample output, 29/29 round trips, and now 3/3 synthetic PDFs for the PDF fallback.

08 · Limitations

When this recovery will and will not work

This is a real weakness, but it is a timing attack. Everything depends on knowing when each file was encrypted. Read these before promising anyone their data back.

CriticalDo not reboot the victim

The clock-to-uptime offset must come from the same boot the ransomware ran in: a live, un-rebooted machine, or a memory image taken before shutdown (it holds both clocks). After a reboot, Windows only records the boot time to about half a second; on our VM it was 352 ms off. The first file then needs a search window around a second wide, which takes days with our Python tool.

CriticalFile timestamps must survive

The .akira file’s last-write time is the anchor. Copy files with their timestamps preserved, or record them before copying. Restoring from backup, re-saving, or timestomping destroys the anchor. The search then falls back to the run’s time span, which is far more expensive.

RequiredKnown bytes at the start of the file

The check in step 4 needs at least 7 bytes of plaintext at offset 0. Most document, image, archive and database formats have a fixed header, but plain text and some proprietary formats do not. Video formats such as MP4 have no rule in the tool yet.

PartialLarge files: ChaCha8 middle

Spot files over about 262 KB, database files (Full mode) and files over 2 MB (Part mode) also use ChaCha8, which needs key1 and iv1. The general check accepts a ChaCha8 key only if the output looks like zeros or text, which compressed data rarely does. For PDFs the decryptor now falls back to two PDF-specific checks, which recovered both of our PDFs. They still need either objects inside the ChaCha8 chunks or a drawing stream at the chunk boundary. Other large compressed files, such as Office documents, archives and video, have no such check yet and come back partly decrypted.

CostSeconds per file, if the window is tight

Each file needs its own search: about 20 seconds on a 20-thread host with the compiled engine, or 30 minutes in pure Python. The cost grows with the time window. After a reboot (a window of about a second), building the candidate keys alone takes tens of minutes per file. A GPU port would help there, and we have not built one.

Not guaranteedTiming outliers

One of our four real files had its two seeds 2.67 ms apart instead of about 0.4 ms, so the standard search missed it. A second pass anchored on the companion value, with gaps up to 30,000 ticks, recovered it in 21 minutes. In run 6, none of the seven files was an outlier. Busy or freshly booted machines will produce more such files, and an even longer stall would fall outside that second pass too.

ScopeMeasured on one setup

Seed spacing and timestamp behaviour were measured on one 2-CPU VM running with a 1 ms system timer. A slower or faster CPU shifts the spacing. Hosts at the default 15.6 ms timer tick, and very large files, need wider windows; neither was tested.

ScopeTwo builds tested

The method is validated on two builds of the same C++ codebase: 101bd45a… and 98877635…. Other Akira-branded encryptors, such as builds from other codebases or later rewrites, may generate keys differently. Check the key generation in the code before relying on this method for another sample.

ScopeOperator key path untested

The decryptor can also open the RSA footer with the operator’s private key, should it ever leak. That path is tested only with our own RSA key pair, since the real key is not available.

09 · What responders should do

Preserve the clock before anything else

  1. Do not reboot or power off the affected machine. Isolate it from the network instead.
  2. Capture memory if you can, then record a clock pair with the snippet below. Run it a few times and keep the median.
  3. Collect, with timestamps intact: the .akira files, every akira_readme.txt (its creation time marks when a folder started), the operator’s Log-*.txt, and the System event log.
  4. Work on copies, smallest and most important files first.
Add-Type @'
using System; using System.Runtime.InteropServices;
public static class Clk {
  [DllImport("kernel32.dll")] public static extern bool QueryPerformanceCounter(out long c);
  [DllImport("kernel32.dll")] public static extern bool QueryPerformanceFrequency(out long f);
  [DllImport("kernel32.dll")] public static extern void GetSystemTimePreciseAsFileTime(out long ft);
}
'@
$f = 0L; $q0 = 0L; $ft = 0L; $q1 = 0L
[void][Clk]::QueryPerformanceFrequency([ref]$f)
[void][Clk]::QueryPerformanceCounter([ref]$q0)
[Clk]::GetSystemTimePreciseAsFileTime([ref]$ft)
[void][Clk]::QueryPerformanceCounter([ref]$q1)
$q = [long](($q0 + $q1) / 2)
"freq=$f  qpc=$q  filetime=$ft  offset=$($ft - $q)"

With a 10 MHz counter (freq=10000000, the usual value) the offset is simply FILETIME minus the counter, and it goes straight into --qpc-offset. On other frequencies, scale the counter to 100 ns units first.

10 · Indicators

Indicators of compromise

TypeValueContext
SHA-256101bd45ab673fd6547386dff4339f1649e61b9f5c318260d365f766fbdc2d3c9sample
MD5427eba9f6ff2cd312c72770f13e279c1sample
SHA-256988776358d0e45a4907dc1f4906a916f1b3595a31fa44d8e04e563a32557eb42second build, same codebase
MD54aecef9ddc8d07b82a6902b27f051f34second build
Victim code6729-HK-NIZN-WOPQchat login in the second build’s note
RSA key (DER SHA-256)a40a55f2600b9199181bc6c0778e64b2e75145c4e6f715da6d23a72407539aaaembedded RSA-4096 public key
Onionakiral2iz6a7qgd3ayp3l6yub7xx2uep76idk3u2kollpj5z3z636bad.onionleak site in the note
Onionakiralkzxzq2dsrzsrvbr2xgbbu2wgsmxryd4csgfameg52n7efvr2id.onionnegotiation chat in the note
Victim code6188-EA-ABYW-YGRGchat login in the note
Extension.akiraencrypted file, original size + 512 B
Extension.arikatemporary 532-byte per-file journal
File nameakira_readme.txtransom note, one per folder
File nameLog-DD-MM-YYYY-HH-MM-SS.txtoperator log in the working directory
Command linepowershell.exe -Command "Get-WmiObject Win32_Shadowcopy | Remove-WmiObject"hidden, parent WmiPrvSE.exe
Arguments--encryption_path= --share_file= --encryption_percent= -localonlyoperator launch syntax
11 · Detection

Catch it before the first file

The earliest reliable signal is the shadow-copy deletion: PowerShell started by WmiPrvSE.exe with Win32_Shadowcopy and Remove-WmiObject on its command line. It happens before any file is touched. After that, a burst of .arika creations followed by .akira renames is unmistakable. A YARA rule and Sigma rules are in the downloads below.

ATT&CKTechniqueIn this sample
T1486Data Encrypted for ImpactChaCha8 + KCipher-2, RSA-4096 footer
T1490Inhibit System RecoveryVolume Shadow Copies removed
T1047Windows Management InstrumentationWin32_Process.Create starts PowerShell
T1059.001PowerShellshadow-copy removal
T1564.003Hidden WindowPowerShell launched hidden
T1489Service StopRestart Manager closes file holders
T1057 / T1083 / T1135 / T1082Discoveryprocesses, files, network shares, CPU count
T1027Obfuscated Files or Informationper-string mod-127 obfuscation of three string groups
12 · Tools and files

Decryptor, scripts and rules

The tools are standard-library Python 3.8+ and PowerShell. GitHub links coming soon