Static analysis: entry point and main flow

Address: 0x14004ca40 Symbol: main (Ghidra FUN_14004ca40), reached from CRT entry @ 0x1400855b8 Callers / callees: CRT start-up → main → logger, CLI parser, drive sweep, pre-encryption steps, crypto init, thread pools

Purpose

main does all one-time set-up, then hands files to encryptor threads. Everything after the thread pools start is asynchronous C++20 coroutines on an asio IOCP pool. A flow diagram is in artifacts/diagrams/execution-flow.png.

Walkthrough

  1. Logging (FUN_14004c6f0): spdlog-style file_logger writing Log-%d-%m-%Y-%H-%M-%S.txt in the working directory.
  2. Command line (FUN_140053d70): an argh-style parser with no registered parameters. A value has to be attached with =. | Argument | Effect | Default | |—|—|—| | --encryption_path=<path> / -p=<path> | encrypt only this tree | none | | --share_file=<file> / -s=<file> | text file of UNC paths or drives, one per line; replaces the drive sweep | none | | --encryption_percent=<n> / -n=<n> | percent used by Spot and Part modes | 50 | | -localonly | skip network disks and paths | off | | -l | log the drive list and exit | off |

    Gotcha, confirmed live in run 1: -p C:\x (with a space) is parsed as a bare flag. The path comes out empty and the sample sweeps every drive.

  3. Targets, from exactly one source in priority order: the explicit path, then the share file (FUN_140041fc0), then the drive sweep (FUN_1400766e0). Classification and the five-drive limit are covered in 02-targeting-and-exclusions.md.
  4. Pre-encryption steps, run once and skipped in -l mode: update the skip list (FUN_14006ecf0), take the protected-PID snapshot (FUN_140070c30), delete shadow copies (FUN_140071d80 → FUN_140071620). See 03-pre-encryption-actions.md.
  5. Crypto init: import the RSA-4096 public key DER from 0x1400ee010 (FUN_14007ba90 / FUN_140081ac0). The RSA context gets its own QPC-seeded Yarrow instance for padding (FUN_14007bbe0).
  6. Thread pools (FUN_1400737d0): logical CPU count N, doubled when N < 5 (and treated as 2 when N = 1). 30% of N become root-folder parsers, 10% (at least one) folder parsers, and the rest encryptors. A 2-vCPU VM gets 1 / 1 / 2.
  7. Root-folder queue (FUN_140073950): roots are queued here. This was the dynamic “gate” breakpoint, where r8 holds the root path as a std::wstring.
  8. Folder parsers (FUN_1400b5fb0, FUN_1400b81d0):
    • apply the exclusions;
    • drop akira_readme.txt (FUN_140042420);
    • set the force-Full / force-Part flags;
    • enqueue each file with FUN_140074480(queue, path, fullFlag, partFlag, ctx, percent, …). The flags land in task fields +0x49 and +0x4a.
  9. Per-file coroutine (FUN_1400ae840): see 05-cryptography.md and 06-file-format-and-journal.md. Raw decompile: artifacts/ghidra/FUN_1400ae840_per_file_coroutine.decompile.c.
  10. main waits for the pools, logs timing and exits. There is no self-deletion and no persistence.

Findings

Cross-references