Research Team: MottaSec Analysis Type: Static Code Analysis (Ghidra) Coverage: Phase 4 (Threading & Concurrency) + Phase 5 (File System Operations) Status: ✅ COMPLETE Confidence Level: 95-99%
This document provides comprehensive analysis of Akira ransomware's threading architecture and file system execution mechanisms. The malware demonstrates professional-grade software engineering with sophisticated multi-threading, efficient synchronization primitives, and intelligent file targeting strategies.
Threading Architecture:
- Dual Thread Pool Design: Separate pools for I/O-bound (folder parsing) and CPU-bound (encryption) operations
- ASIO Library: Statically-linked Boost ASIO for asynchronous I/O operations
- Dynamic Scaling: 2 to 64+ threads based on CPU count (30/10/60 split)
- Professional Synchronization: STL critical sections, condition variables, atomic operations
- Deadlock-Free: Single lock per operation, no circular dependencies
File System Operations:
- Selective Targeting: 5 extension blacklist + 11 directory blacklist + 13 protected processes
- NO Active Network Enumeration: Stealth over automation (requires manual reconnaissance)
- Process Termination: Windows Restart Manager API for force-killing locking processes
- Two-Stage File Locking: Exclusive → shared access fallback
- Atomic Renaming: SetFileInformationByHandle for race-free file operations
Performance Characteristics:
- Theoretical Throughput: ~930 files/sec on 10-thread system
- O(log n) Filtering: Red-black trees for efficient blacklist checking
- Bounded Queue: Prevents memory exhaustion
- CPU Efficiency: Condition variables (no busy-waiting)
- Thread Pool Architecture
- ASIO Library Integration
- Synchronization Mechanisms
- Task Queue System
- Producer-Consumer Pattern
- Drive Enumeration
- Directory Traversal
- File Filtering System
- File Access & Manipulation
- Restart Manager Integration
- Cross-Component Architecture
- Performance Analysis
- Security Assessment
- Detection Strategies
- Function Reference
Akira employs a sophisticated dual thread pool architecture using the Boost ASIO library. This design separates I/O-bound directory traversal from CPU-bound encryption operations, maximizing efficiency on multi-core systems.
Main Structure Size: 384 bytes (0x180)
Allocation: operator_new(0x180) in main function
Initialization Function: init_thread_pool @ 0x14007b6d0
struct thread_pool_t {
// +0x00-0x28: Core fields (40 bytes)
void* reserved[5]; // +0x00 (40 bytes)
int folder_parser_thread_count; // +0x28 (4 bytes)
int root_folder_thread_count; // +0x34 (4 bytes)
void* reserved_0x38; // +0x38 (8 bytes)
// +0x40-0x8F: Critical Section 1 - Folder Parser Pool (80 bytes)
CRITICAL_SECTION cs_folder_parser; // +0x40 (80 bytes)
// +0x90-0xDF: Critical Section 2 - Encryption Pool (80 bytes)
CRITICAL_SECTION cs_encryption; // +0x90 (80 bytes)
// +0xE0-0x127: Condition Variable 1 - Folder Parser (72 bytes)
CONDITION_VARIABLE cv_folder_parser; // +0xE0 (72 bytes)
// +0x128-0x16F: Condition Variable 2 - Encryption (72 bytes)
CONDITION_VARIABLE cv_encryption; // +0x128 (72 bytes)
// ASIO pool pointers and control blocks
void* folder_parser_pool_impl; // +0x10
void* folder_parser_pool_ctrl; // +0x18
void* encryption_pool_impl; // +0x20
void* encryption_pool_ctrl; // +0x28
// Queue state tracking
int max_queue_size; // +0x34
int current_queue_size; // +0x3C
int queue_counter; // +0xDC (wraps at 0x7FFFFFFF)
};Key Observations:
- Two completely independent thread pools
- Each pool has its own critical section and condition variable
- No shared resources between pools (no lock contention)
- Efficient memory layout (aligned for cache performance)
Location: main() function, before init_thread_pool call
Algorithm:
cpu_count = get_cpu_count();
// Boost single-core systems to minimum 2 threads
if (cpu_count == 1) {
cpu_count = 2;
}
// Calculate thread distribution
folder_parser_threads = (cpu_count * 30) / 100; // 30% for I/O
root_folder_threads = (cpu_count * 10) / 100; // 10% for root dirs
// Ensure at least 1 root folder thread
if (root_folder_threads == 0) {
root_folder_threads = 1;
}
// Remaining threads for encryption (CPU-intensive work)
encryption_threads = cpu_count - folder_parser_threads - root_folder_threads;
// Logging
log_info("Number of thread to folder parsers = %d", folder_parser_threads);
log_info("Number of thread to root folder parsers = %d", root_folder_threads);
log_info("Number of threads to encrypt = %d", encryption_threads);| CPU Cores | Folder Parser (30%) | Root Folder (10%) | Encryption (60%) | Total |
|---|---|---|---|---|
| 1 (→2) | 0 | 1 | 1 | 2 |
| 2 | 0 | 1 | 1 | 2 |
| 4 | 1 | 1 | 2 | 4 |
| 8 | 2 | 1 | 5 | 8 |
| 16 | 4 | 1 | 11 | 16 |
| 32 | 9 | 3 | 20 | 32 |
| 64 | 19 | 6 | 39 | 64 |
Design Rationale:
- 30% for Folder Parsing: I/O-bound directory traversal benefits from parallelism but doesn't need as many threads
- 10% for Root Folders: Small number of root directories (drives) to process
- 60% for Encryption: CPU-intensive ChaCha20 encryption gets the majority of threads
- Minimum Guarantee: Single-core systems boosted to 2 threads to avoid sequential execution
Function: init_thread_pool @ 0x14007b6d0
Step 1: Zero-Initialize Structure
*param_1 = 0;
param_1[1] = 0;
param_1[2] = 0;
param_1[3] = 0;
param_1[4] = 0;
param_1[5] = 0;Step 2: Store Thread Counts
*(int *)(param_1 + 6) = folder_parser_threads; // Offset 0x28
*(int *)((longlong)param_1 + 0x34) = root_folder_threads; // Offset 0x34
param_1[7] = 0; // Clear offset 0x38Step 3: Initialize Synchronization Primitives
init_critical_section((undefined4 *)(param_1 + 8), 2); // Offset 0x40, spin=2
init_critical_section((undefined4 *)(param_1 + 0x12), 2); // Offset 0x90, spin=2
init_condition_variable(param_1 + 0x1c); // Offset 0xE0
init_condition_variable(param_1 + 0x25); // Offset 0x128Critical Section Parameters:
- Spin Count = 2: Before blocking, threads attempt to acquire lock twice with busy-wait
- Purpose: Reduces context switching overhead for brief critical sections
- Trade-off: Good for locks held for microseconds, bad if held for milliseconds
Condition Variables:
- Windows native condition variables (Vista+)
- Atomic wait/release of mutex
- Efficient thread wake-up
Step 4: Create Folder Parser Thread Pool
puVar6 = (undefined8 *)operator_new(0x30); // Allocate 48 bytes for ASIO pool
*puVar6 = 0;
puVar6[1] = 0;
*(undefined4 *)(puVar6 + 1) = 1; // Initial ref count = 1
*(undefined4 *)((longlong)puVar6 + 0xc) = 1; // Weak ref count = 1
*puVar6 = std::_Ref_count_obj2<class_asio::thread_pool>::vftable;
FUN_14007b280((PRTL_CRITICAL_SECTION_DEBUG)(puVar6 + 2), (longlong)folder_parser_threads);
param_1[2] = (PRTL_CRITICAL_SECTION_DEBUG)(puVar6 + 2); // Store pool ptr at offset 0x10
param_1[3] = puVar6; // Store control block at offset 0x18Step 5: Create Encryption Thread Pool
puVar6 = (undefined8 *)operator_new(0x30); // Another 48-byte ASIO pool
*puVar6 = 0;
puVar6[1] = 0;
*(undefined4 *)(puVar6 + 1) = 1;
*(undefined4 *)((longlong)puVar6 + 0xc) = 1;
*puVar6 = std::_Ref_count_obj2<class_asio::thread_pool>::vftable;
FUN_14007b280((PRTL_CRITICAL_SECTION_DEBUG)(puVar6 + 2), (longlong)encryption_threads);
param_1[4] = (PRTL_CRITICAL_SECTION_DEBUG)(puVar6 + 2); // Store pool ptr at offset 0x20
param_1[5] = puVar6; // Store control block at offset 0x28Size: 32 bytes (0x20) per thread Allocation: Heap-allocated during thread pool creation
struct thread_object_t {
void* vftable_ptr; // +0x00 Virtual function table
void* thread_handle; // +0x08 Windows thread HANDLE
void* thread_context; // +0x10 Thread pool context pointer
void* next_thread; // +0x18 Linked list pointer
};Function: FUN_14007b280 @ 0x14007b280 (ASIO thread pool creation)
if (thread_count < 0x80000000) { // Sanity check
*(uint *)&(param_1->ProcessLocksList).Blink = (uint)thread_count & 0x7fffffff;
// Increment reference count atomically
LOCK();
piVar1 = (int *)((longlong)&pool_impl[4].DebugInfo + 4);
*piVar1 = *piVar1 + 1;
UNLOCK();
// Create worker threads
iVar2 = *(int *)&(param_1->ProcessLocksList).Blink;
lVar11 = (longlong)iVar2;
if (iVar2 != 0) {
do {
thread_obj = operator_new(0x20); // Thread object (32 bytes)
func_wrapper = operator_new(0x20); // Thread function wrapper
// Set up thread function wrapper
*func_wrapper = asio::detail::win_thread::func<...>::vftable;
func_wrapper[3] = pool_impl; // Thread pool context
// Initialize Windows thread
FUN_140038a10((longlong)thread_obj, func_wrapper);
// Add to thread list
thread_obj->LockSemaphore = prev_thread;
(param_1->ProcessLocksList).Flink = (_LIST_ENTRY *)thread_obj;
lVar11 = lVar11 + -1;
} while (lVar11 != 0);
}
}Key Points:
- Each thread is created with a unique function wrapper
- Threads are linked in a list for management
- Reference counting prevents premature cleanup
- Windows native threads (not Win32 fibers)
Library: Boost ASIO (Asynchronous I/O)
Version: Unknown (statically linked)
Deployment: Embedded in binary (no DLL dependencies)
Implementation: Windows-specific (asio::detail::win_thread)
Purpose:
- Professional-grade asynchronous I/O framework
- Cross-platform thread pool management
- Work-stealing task scheduler
- Reference-counted resource management
Type: asio::thread_pool
Virtual Function Table: std::_Ref_count_obj2<class_asio::thread_pool>::vftable
Pattern: Standard C++ shared_ptr implementation
struct asio_thread_pool_t {
void* vftable; // +0x00 Virtual function table
void* unknown_0x08; // +0x08
int ref_count; // +0x10 Strong reference count
int weak_ref_count; // +0x0C Weak reference count
// ... additional fields ...
void* thread_pool_impl; // Actual thread pool implementation
};Size: 48 bytes (0x30) per pool object
Type: asio::detail::win_thread::func<struct_asio::thread_pool::thread_function>
Purpose: Windows-specific thread entry point wrapper
Namespace: asio::detail (internal implementation)
Structure:
struct thread_func_wrapper_t {
void* vftable; // +0x00 Function wrapper vtable
void* reserved[2]; // +0x08-0x17
void* pool_context; // +0x18 Thread pool context
};Size: 32 bytes (0x20) per wrapper
Size: 200 bytes per pool Initialization: Zero-initialized via FUN_140090460
Purpose:
- Manages worker thread pool
- Implements work-stealing scheduler
- Handles task dispatch and load balancing
ASIO uses C++ shared_ptr semantics for resource management:
// Atomic increment (shared_ptr copy)
LOCK();
ref_count++;
UNLOCK();
// Atomic decrement (shared_ptr destruction)
LOCK();
old_count = ref_count--;
UNLOCK();
if (old_count == 1) {
// Last strong reference - call destructor
vtable->destroy(this);
LOCK();
weak_ref_count--;
UNLOCK();
if (weak_ref_count == 0) {
// Last weak reference - free memory
vtable->deallocate(this);
}
}Advantages:
- Automatic resource cleanup
- Thread-safe reference counting
- Prevents memory leaks
- Prevents use-after-free
Function: FUN_14007b280 @ 0x14007b280
Signature: void create_asio_thread_pool(void* asio_pool, uint thread_count)
Responsibilities:
- Create ASIO context structure (56 bytes = 0x38)
- Initialize critical section for context
- Create thread pool with specified thread count
- Spawn worker threads
- Set up thread-local storage
Key Operations:
p_Var8 = (LPCRITICAL_SECTION)operator_new(0x38); // Context structure
FUN_1400385f0(p_Var8); // Initialize context
// Create thread pool implementation
pauVar9 = (undefined1 (*) [32])operator_new(200); // 200 bytes for pool impl
FUN_140090460(pauVar9, 0, 200); // Zero-initialize
p_Var10 = FUN_14007a2d0(pauVar9, param_1, (uint)(param_2 == 1));Function: asio_post_task @ 0x14007bd00
Purpose: Submit task to ASIO thread pool for execution
Signature:
void asio_post(asio_pool* pool, task_object** task_ptr)Process:
- Take ownership of task pointer (set source to NULL)
- Wrap task in ASIO-specific handler structures
- Add to ASIO internal queue
- ASIO scheduler selects worker thread
- Worker thread calls task execution function
Key Feature: Work-stealing scheduler likely used for load balancing
Function: init_critical_section @ 0x140081d00
Library: STL (C++ Standard Library) with Windows 7+ implementation
Type: Concurrency::details::stl_critical_section_win7
Underlying Mechanism: Windows SRW locks (Slim Reader/Writer locks)
struct stl_critical_section_win7 {
uint spin_count; // +0x00 (value = 2)
void* reserved; // +0x04
vftable* vtable_ptr; // +0x08
void* srw_lock; // +0x10 (SRW LOCK handle)
uint lock_state; // +0x48 (0xFFFFFFFF when unlocked)
uint padding; // +0x4C
};Size: 80 bytes (0x50) total
void init_critical_section(undefined4 *param_1, undefined4 param_2)
{
*(undefined ***)(param_1 + 2) = Concurrency::details::stl_critical_section_win7::vftable;
*(undefined8 *)(param_1 + 4) = 0;
param_1[0x12] = 0xffffffff; // Unlocked state
param_1[0x13] = 0;
*param_1 = param_2; // Spin count
return;
}Lock Operation:
int mutex_lock(uint *mutex_ptr)
{
// Virtual function dispatch
return (**(code **)(*mutex_ptr + 8))(mutex_ptr);
}Unlock Operation:
void mutex_unlock(longlong mutex_ptr)
{
_Mtx_unlock(mutex_ptr);
}Virtual Function Table:
vtable[0]: Destructorvtable[1]: Lock functionvtable[2]: TryLock functionvtable[3]: Unlock function
With spin count = 2:
- Attempt 1: Try lock immediately (no wait)
- Attempt 2: Spin-wait (busy loop)
- Attempt 3+: Block on kernel synchronization object
Performance Trade-off:
- Good for: Locks held for microseconds (1-10 µs)
- Bad for: Locks held for milliseconds (wastes CPU cycles)
Function: init_condition_variable @ 0x1400820e8
Library: STL with Windows 7+ implementation
Type: Concurrency::details::stl_condition_variable_win7
Underlying Mechanism: Windows native condition variables (Vista+)
struct stl_condition_variable_win7 {
vftable* vtable_ptr; // +0x00
CONDITION_VARIABLE native_cv; // +0x08 Windows CV handle
// ... additional fields (total 72 bytes)
};Size: 72 bytes (0x48) total
void init_condition_variable(undefined8 *param_1)
{
*param_1 = Concurrency::details::stl_condition_variable_win7::vftable;
InitializeConditionVariable(param_1 + 1); // Windows API
return;
}Wait Operation:
void condition_variable_wait(void *cv_ptr, longlong mutex_ptr)
{
// Atomically:
// 1. Release mutex
// 2. Block thread
// 3. Re-acquire mutex before returning
FUN_140082118(cv_ptr, mutex_ptr);
}Signal One Waiter:
void condition_variable_signal(void *cv_ptr)
{
WakeConditionVariable(cv_ptr); // Windows API
}Broadcast (Signal All Waiters):
void condition_variable_broadcast(void *cv_ptr)
{
WakeAllConditionVariable(cv_ptr); // Windows API
}Windows APIs Used:
InitializeConditionVariable- Initialize CVSleepConditionVariableCS- Wait on CV (blocking)WakeConditionVariable- Signal one waiterWakeAllConditionVariable- Signal all waiters
Akira uses x86 LOCK prefix for atomic operations:
// Atomic increment
LOCK();
*counter = *counter + 1;
UNLOCK();
// Atomic decrement with return value
LOCK();
old_value = *counter;
*counter = *counter - 1;
UNLOCK();
if (old_value == 1) {
// Last reference - cleanup
destroy_resource();
}Hardware Instructions:
LOCKprefix: x86 instruction prefix- Ensures atomic read-modify-write
- Visibility across all CPU cores
- Prevents compiler/CPU reordering
Used For:
- Reference counting (ASIO shared_ptr)
- Task counter updates
- Queue size tracking
Key Finding: No deadlock risk
Analysis:
- Each operation acquires at most ONE critical section
- Folder parser tasks → Folder parser CS (offset +0x40)
- Encryption tasks → Encryption CS (offset +0x90)
- No nested locking observed
- No circular dependencies
Result: Deadlock-free design
Size: 288 bytes (0x120) Allocation Function: allocate_task @ 0x14003a4a0 Constructor Function: task_constructor @ 0x1400c4030
struct task_object_t {
// +0x00-0x08: Function pointers
void* execution_function; // +0x00 Entry point (FUN_1400c45a0)
void* cleanup_function; // +0x08 Destructor (LAB_1400c3db0)
// +0x10-0x40: Linked list node structure (48 bytes)
void* node_field_0x10; // +0x10
void* node_field_0x18; // +0x18
void* node_field_0x20; // +0x20
void* node_field_0x28; // +0x28
void* node_field_0x30; // +0x30
void* node_field_0x38; // +0x38
// +0x40-0x90: Task-specific data
void* context_ptr; // +0x41 (param_10)
void* shared_ptr_1; // +0x49 Crypto context (moved)
void* shared_ptr_1_ctrl; // +0x51 Ref count control block
void* shared_ptr_2; // +0x59 Thread pool (moved)
void* shared_ptr_2_ctrl; // +0x61 Ref count control block
byte string_buffer[32]; // +0x69 File path (wstring)
void* additional_context; // +0x89
uint integer_param; // +0x91
byte flag_param; // +0x95
uint task_flags; // +0x9C (0x10002 or 0x2)
// +0xA0-0x120: Additional fields (128 bytes)
// ... reserved/internal ASIO structures ...
};Function: FUN_1400c4030 @ 0x1400c4030
Step 1: Set Function Pointers
param_2[1] = &LAB_1400c3db0; // Cleanup function
*param_2 = FUN_1400c45a0; // Execution functionStep 2: Move Shared Pointers (Transfer Ownership)
// Move shared_ptr_1 (crypto context)
*(void**)(task + 0x49) = *param_4;
*(void**)(task + 0x51) = param_4[1];
*param_4 = 0; // Clear source
param_4[1] = 0;
// Move shared_ptr_2 (thread pool)
*(void**)(task + 0x59) = *param_5;
*(void**)(task + 0x61) = param_5[1];
*param_5 = 0; // Clear source
param_5[1] = 0;Move Semantics: C++11 move semantics prevent unnecessary reference count operations
Step 3: Move String Data
// Move wstring (32 bytes) - file path
*(longlong*)(task + 0x69) = param_6[0];
*(longlong*)(task + 0x71) = param_6[1];
*(longlong*)(task + 0x79) = param_6[2];
*(longlong*)(task + 0x81) = param_6[3];
// Clear source string
param_6[2] = 0;
param_6[3] = 7; // SSO flag (Small String Optimization)
*(word*)param_6 = 0;Step 4: Store Additional Parameters
*(void**)(task + 0x41) = param_10; // Context pointer
*(uint*)(task + 0x91) = *param_8; // Integer parameter
*(byte*)(task + 0x95) = *param_9; // Byte flagStep 5: Set Task Flags
uint flags = 0x10002; // Default flags
if (*param_1 != 0) {
flags = 2; // Alternative flags if status != 0
}
*(uint*)(task + 0x9C) = flags;Step 6: Initialize Linked List Node
void** node = task + 2; // Offset 0x10
*node = 0;
// ... zero-initialize 48 bytes ...
*node = task; // Self-reference
*param_3 = node; // Return pointer to nodeQueue State Structure:
struct thread_pool_queue_state {
int max_queue_size; // +0x34 Maximum tasks allowed
int current_queue_size; // +0x3C Current task count
int queue_counter; // +0xDC Task counter (statistics)
};Capacity Management:
max_queue_size: Set during initialization (prevents unbounded growth)current_queue_size: Atomically incremented/decrementedqueue_counter: Wraps at 0x7FFFFFFF, used for overflow detection
Function: enqueue_encrypt_task @ 0x14007b850
Complete Enqueue Sequence:
void enqueue_encrypt_task(thread_pool *pool, ...)
{
uint *mutex = (uint *)(pool + 0x12); // Critical section at offset 0x90
// Step 1: Acquire lock
int result = mutex_lock(mutex);
if (result != 0) {
FUN_1400812e0(5); // Log error code 5
__debugbreak(); // Fatal error
return;
}
// Step 2: Overflow detection
if (*(int *)(pool + 0xdc) == 0x7fffffff) {
*(int *)(pool + 0xdc) = 0x7ffffffe; // Wrap counter
FUN_1400812e0(6); // Log error code 6
}
// Step 3: Wait if queue is full (producer blocks)
while (*(int *)(pool + 0x34) <= *(int *)(pool + 0x3c)) {
// Atomically release mutex and wait
FUN_140082118(pool + 0x25, mutex);
// Mutex re-acquired here after wake-up
}
// Step 4: Increment active task counter (atomic)
LOCK();
*(int *)(pool + 0x3c) = *(int *)(pool + 0x3c) + 1;
UNLOCK();
// Step 5: Build task object
task_object* task = allocate_task(0x120);
task_constructor(&status, task, ...);
// Step 6: Post task to ASIO thread pool
FUN_14007bd00(&pool[4], &task_ptr);
// Step 7: Release lock
if (lock_acquired) {
_Mtx_unlock(mutex);
}
// Step 8: Cleanup (reference counting)
// ...
}Key Features:
- Blocks producer when queue full (backpressure)
- Atomic counter updates
- Overflow detection and logging
- Clean error handling
Inferred from worker thread behavior:
void worker_thread_loop(thread_pool* pool)
{
while (!shutdown_flag) {
mutex_lock(&pool->queue_mutex);
// Wait for task
while (queue_empty) {
condition_variable_wait(&pool->queue_cv, &pool->queue_mutex);
}
// Remove task from queue
task_object* task = dequeue_task(pool);
// Decrement counter (atomic)
LOCK();
pool->current_queue_size--;
UNLOCK();
// Signal producers (wake if waiting)
condition_variable_signal(&pool->queue_cv);
mutex_unlock(&pool->queue_mutex);
// Execute task (outside critical section)
task->execution_function(task);
// Cleanup task
task->cleanup_function(task);
operator_delete(task);
}
}Overflow Detection:
if (pool->queue_counter == 0x7FFFFFFF) {
pool->queue_counter = 0x7FFFFFFE; // Wrap to prevent overflow
log_error(6); // Log overflow event (non-fatal)
}Purpose:
- Detect if 2+ billion tasks have been queued
- Prevents integer overflow
- Logs event for statistics
- Operation continues normally
Flow Control:
- Producer blocks when
current_queue_size >= max_queue_size - Prevents memory exhaustion
- Provides backpressure
- CPU-efficient waiting (no busy loop)
Akira implements a classic producer-consumer pattern with condition variables for efficient thread synchronization.
Role: Creates encryption tasks for discovered files
Workflow:
1. Traverse directory tree (filesystem enumeration)
2. For each file:
a. Apply filters (extensions, directories, processes)
b. If file passes filters:
- Build task parameters (file path, crypto context, etc.)
- Call enqueue_encrypt_task()
- Task added to queue
- Worker threads notified via condition variable
3. Continue until all files processed
4. Signal shutdown
Blocking Behavior:
- Producer blocks when queue is full
- Wakes up when consumer removes task
- Re-checks queue state after wake
- Continues if space available
Role: Process encryption tasks from queue
Workflow:
1. Thread created during thread pool initialization
2. Enter main loop:
a. Lock queue mutex
b. Wait for task (blocks on condition variable if empty)
c. Dequeue task
d. Unlock queue mutex
e. Execute task (encrypt file - outside critical section)
f. Task cleanup (free memory, decrement ref counts)
g. Decrement task count (atomic)
h. Signal producers via condition variable (if needed)
i. Return to step 2a
3. Thread exits when shutdown signaled
Non-Blocking Execution:
- File encryption happens outside critical section
- Only queue operations are locked
- Maximizes parallelism
- Minimizes lock contention
Visual Representation:
Producer Thread Consumer Thread
=============== ===============
Lock queue mutex Lock queue mutex
↓ ↓
Check if queue full Check if queue empty
↓ ↓
[If full] [If empty]
Wait on CV (release mutex) Wait on CV (release mutex)
← Wake up (mutex re-acquired) ← Wake up (mutex re-acquired)
↓ ↓
Add task to queue Remove task from queue
↓ ↓
Increment task count (atomic) Decrement task count (atomic)
↓ ↓
Signal CV (wake consumer) Signal CV (wake producer)
↓ ↓
Unlock queue mutex Unlock queue mutex
↓ ↓
Continue Execute task (outside lock)
↓
Task cleanup
CPU Efficiency:
- Condition variables (no busy-waiting)
- Threads sleep when no work available
- Minimal CPU waste
- Fast wake-up (kernel-level notification)
Memory Efficiency:
- Bounded queue size prevents unlimited growth
- Task objects freed immediately after execution
- Move semantics (no unnecessary copies)
- Reference counting prevents leaks
Scalability:
- Lock-free when queue has space and tasks available
- Separate queues for folder parsing and encryption
- Minimal lock contention
- Scales linearly with core count (up to I/O bottleneck)
Akira enumerates logical drives and classifies them for selective encryption targeting. Critical Finding: NO active network share enumeration observed.
Size: 40 bytes (0x28)
Container: std::vector<drive_entry_t>
struct drive_entry_t {
// +0x00-0x1F: Drive path as std::wstring (32 bytes)
wchar_t* string_ptr; // +0x00 (8 bytes)
size_t string_length; // +0x08 (8 bytes)
size_t string_capacity; // +0x10 (8 bytes)
size_t string_flags; // +0x18 (8 bytes) - SSO: 7 = inline
// +0x20-0x27: Drive classification flags (8 bytes)
bool is_network; // +0x20 (1 byte) - TRUE if DRIVE_REMOTE
bool is_accessible; // +0x21 (1 byte) - TRUE if encryptable
byte padding[6]; // +0x22-0x27 (6 bytes)
};Function: initialize_drive_list @ 0x14007e6a0
| Type | Windows Constant | is_network | is_accessible | Encrypted? |
|---|---|---|---|---|
| 0 | DRIVE_UNKNOWN | FALSE | FALSE | ❌ No |
| 1 | DRIVE_NO_ROOT_DIR | FALSE | FALSE | ❌ No |
| 2 | DRIVE_REMOVABLE | FALSE | TRUE | ✅ Yes |
| 3 | DRIVE_FIXED | FALSE | TRUE | ✅ Yes |
| 4 | DRIVE_REMOTE | TRUE | FALSE* | ✅ Yes |
| 5 | DRIVE_CDROM | FALSE | TRUE | ✅ Yes |
| 6 | DRIVE_RAMDISK | FALSE | TRUE | ✅ Yes |
*Note: Network drives (type 4) are encrypted despite is_accessible=FALSE flag
is_network flag @ 0x14007e7f0:
CMP EAX, 0x4 ; Compare drive type to 4
SETZ byte ptr [RBP+0x27] ; is_network = (type == 4)is_accessible flag @ 0x14007e7f6:
DEC EAX ; type = type - 1
TEST EAX, 0xFFFFFFFB ; AND with ~0x4 (binary: ...11111011)
SETZ byte ptr [RBP+0x28] ; is_accessible = ((type-1) & ~4) == 0Boolean Logic:
is_network = (drive_type == 4)is_accessible = (drive_type in {2, 3, 5, 6})
Detection: GetDriveTypeW() returns type 4 for mapped drives
- Only pre-mapped drives are found (e.g., Z:)
- No active enumeration
- Stealth over automation
Command-Line Parameter:
akira.exe --share_file "C:\shares.txt"Function: read_share_file @ 0x140042830
File Format:
\\server1\share1
\\server2\data
\\192.168.1.100\backups
Share Entry Structure (32 bytes):
struct share_path_entry_t {
wchar_t* path_ptr; // +0x00 (8 bytes)
size_t path_length; // +0x08 (8 bytes)
size_t path_capacity; // +0x10 (8 bytes)
size_t path_flags; // +0x18 (8 bytes) - SSO: 7 = inline
};Container: std::vector<share_path_entry_t>
❌ NO Active Network Enumeration:
- No
NetShareEnumAPI calls - No
WNetEnumResourceAPI calls - No
WNetOpenEnumAPI calls - No Active Directory queries
- No SMB/CIFS scanning
Security Implication: Stealth over automation. Requires manual reconnaissance by operators.
GetLogicalDriveStringsW- Enumerate all logical drives (A:-Z:)GetDriveTypeW- Classify drive type (0-6)
Akira uses Windows file enumeration APIs combined with ASIO task-based recursion for parallel directory traversal across multiple worker threads.
Function: folder_processor_worker @ 0x1400bf190 Type: Thread pool worker Size: ~3000+ lines decompiled (8,454 bytes) Purpose: Main directory traversal and file discovery engine
Task Type Field: task->type at offset +0x94 (uint16_t)
switch (task->type) {
case 2:
// Main directory processing (recursive traversal)
process_directory_recursively(task);
break;
case 3:
case 1:
case 0xFFFF:
// Cleanup and finalization
cleanup_task_resources(task);
break;
default:
__fastfail(FAST_FAIL_INVALID_ARG);
}Size: 88 bytes (0x58)
struct directory_iterator_impl_t {
// +0x00-0x0F: Reference counting (std::shared_ptr pattern)
void* vftable_ptr; // +0x00 (8 bytes)
int ref_count; // +0x08 (4 bytes) - Strong refs
int weak_ref_count; // +0x0C (4 bytes) - Weak refs
// +0x10-0x2F: Iterator state
void* state_ptr; // +0x10 (8 bytes)
qword reserved_18; // +0x18 (8 bytes)
qword reserved_20; // +0x20 (8 bytes)
qword reserved_28; // +0x28 (8 bytes)
// +0x30-0x4F: Current directory path (std::wstring)
wchar_t* path_ptr; // +0x30 (8 bytes)
size_t path_length; // +0x38 (8 bytes)
size_t path_capacity; // +0x40 (8 bytes)
size_t path_flags; // +0x48 (8 bytes) - SSO: 7 = inline
// +0x50-0x57: Windows search handle
HANDLE find_handle; // +0x50 (8 bytes) - FindFirstFileExW
};Function: open_directory_iterator @ 0x14006f4c0
Algorithm:
DWORD open_directory(const wchar_t* path, WIN32_FIND_DATAW* entry) {
// 1. Validate path length > 0
if (wcslen(path) == 0) return ERROR_INVALID_PARAMETER;
// 2. Append wildcard
wstring search_path = path + L"\\*";
// 3. Open directory
HANDLE hFind = FindFirstFileExW(
search_path.c_str(),
FindExInfoBasic, // Optimization: no short name
entry,
FindExSearchNameMatch,
NULL,
FIND_FIRST_EX_LARGE_FETCH // Optimization: fetch multiple entries
);
if (hFind == INVALID_HANDLE_VALUE) {
DWORD error = GetLastError();
// 4. Handle errors
if (error == ERROR_ACCESS_DENIED) {
return 0; // Treat as empty directory (skip silently)
}
return error;
}
// 5. Skip "." and ".." entries
while (is_dot_or_dotdot(entry->cFileName)) {
if (!FindNextFileW(hFind, entry)) {
DWORD error = GetLastError();
FindClose(hFind);
return error;
}
}
return 0; // Success
}Optimizations:
FindExInfoBasic: Skip short (8.3) filenames for performanceFIND_FIRST_EX_LARGE_FETCH: Request multiple entries per syscall
Function: find_next_file_wrapper @ 0x140080b0c
DWORD advance_iterator(HANDLE handle, WIN32_FIND_DATAW* entry) {
BOOL result = FindNextFileW(handle, entry);
if (!result) {
DWORD error = GetLastError();
if (error == ERROR_NO_MORE_FILES) {
return 0x12; // Normal end of iteration
}
return error; // Real error
}
return 0; // Success
}Error Handling:
ERROR_NO_MORE_FILES(0x12): Normal end (not an error)ERROR_ACCESS_DENIED(5): Treated as empty directory- Other errors: Logged and skipped
Key Observation: Recursion achieved via task queue, not function call stack.
Advantages:
- Parallel traversal across multiple threads
- No stack overflow (depth unlimited)
- Work-stealing load balancing
- Efficient for deep directory trees
Flow:
1. Open directory iterator
2. Loop through entries with FindNextFileW
3. For each entry:
a. Check if dot/dotdot → skip
b. Extract entry name
c. Check entry type (directory vs file)
d. Apply filters (see Section 8)
e. If directory and not blacklisted:
- Enqueue new directory traversal task
f. If file and not blacklisted:
- Enqueue encryption task
4. Continue until iterator exhausted
5. Close handle (FindClose)
6. Cleanup iterator structure
FindFirstFileExW- Start directory enumeration (optimized)FindNextFileW- Get next entryFindClose- Close search handle
Akira employs three filtering mechanisms to avoid encrypting system-critical files and maintain OS operability for ransom payment.
Initialization: init_extension_blacklist @ 0x140001ac0
Global Data: DAT_140102138 @ 0x140102138 (std::set)
Total Count: 5 extensions
static const wchar_t* BLACKLIST_EXTENSIONS[] = {
L".exe", // Executables
L".dll", // Dynamic Link Libraries
L".lnk", // Shortcuts
L".sys", // System drivers
L".msi" // Installers
};Purpose: Keep Windows functional, allow browser access for payment instructions
Initialization: init_directory_blacklist @ 0x1400018a0
Global Data: DAT_140102148 @ 0x140102148 (std::set)
Total Count: 11 directories (ALL IDENTIFIED)
std::set<wstring> excluded_directories = {
L"tmp", // Temporary directory
L"winnt", // Legacy Windows NT
L"temp", // Temp files
L"thumb", // Thumbnail cache
L"$Recycle.Bin", // Recycle Bin (mixed case)
L"$RECYCLE.BIN", // Recycle Bin (uppercase)
L"System Volume Information", // Shadow copies/restore points
L"Boot", // Boot configuration
L"Windows", // OS directory
L"Trend Micro", // Antivirus evasion
L"ProgramData", // System app data
};Case Sensitivity: Case-sensitive matching (proven by dual Recycle Bin entries)
Security Implication: Case-sensitive filtering on case-insensitive Windows filesystem can be bypassed by changing directory name case.
Initialization: init_protected_process_names @ 0x140001c10 (6,314 bytes with obfuscated strings)
Global Data: DAT_140102158 @ 0x140102158 (std::set)
Total Count: 13 critical Windows processes
static const wchar_t* CRITICAL_PROCESSES[] = {
L"spoolsv.exe", // Print Spooler
L"fontdrvhost.exe", // Font Driver Host
L"explorer.exe", // Windows Explorer
L"sihost.exe", // Shell Infrastructure
L"SearchUI.exe", // Windows Search
L"lsass.exe", // Security Authority
L"LogonUI.exe", // Logon UI
L"winlogon.exe", // Logon Process
L"services.exe", // Service Control Manager
L"csrss.exe", // Runtime Subsystem
L"smss.exe", // Session Manager
L"conhost.exe", // Console Host
L"wininit.exe" // Windows Initialization
};Purpose: Avoid terminating files locked by critical processes (prevents system crash during encryption)
String Obfuscation: Process names stored with custom XOR-based encoding, decoded at runtime
Status: ❌ DOES NOT EXIST
Confirmed via exhaustive string searches - no filename-specific filtering (e.g., desktop.ini, thumbs.db)
Type: std::set<std::wstring>
Implementation: Red-black tree (self-balancing BST)
Complexity: O(log n) lookup
Size: 64 bytes per node
struct tree_node {
tree_node* parent; // +0x00 (8 bytes)
tree_node* left; // +0x08 (8 bytes)
tree_node* right; // +0x10 (8 bytes)
byte color; // +0x18 (1 byte) - Red=0, Black=1
byte is_leaf; // +0x19 (1 byte)
byte padding[6]; // +0x1A-0x1F
// Embedded wstring (32 bytes)
wchar_t* key_ptr; // +0x20 (8 bytes)
size_t key_length; // +0x28 (8 bytes)
size_t key_capacity; // +0x30 (8 bytes)
size_t key_flags; // +0x38 (8 bytes) - SSO: 7 = inline
};Function 1: STL Set Lookup - stl_set_find @ 0x14005dea0
// Performs O(log n) binary search in red-black tree
iterator stl_set_find(
std::set<wstring>* set_ptr, // Blacklist set
iterator_result* result, // Output iterator
const wchar_t* search_string // Name to find
) {
// Binary search in red-black tree
// Returns iterator to found element or end()
}Function 2: String Comparison - stl_wstring_compare @ 0x14005df80
// Character-by-character wide string comparison
int stl_wstring_compare(
void* set_ptr,
tree_node* iterator,
const wchar_t* search_string
) {
// Returns 0 if match (string in blacklist)
// Returns non-zero if no match
}Directory Filtering @ 0x1400bf6f1:
// Extract directory name from WIN32_FIND_DATAW
wstring dirname = get_directory_name(entry);
// Lookup in blacklist (DAT_140102148)
auto iter = stl_set_find(&DAT_140102148, &result, dirname);
bool is_blacklisted = (stl_wstring_compare(&DAT_140102148, iter[2], dirname) == 0);
if (is_blacklisted) {
// SKIP this directory - do not recurse
} else {
// PROCESS this directory - enqueue traversal task
enqueue_directory_task(...);
}Extension Filtering @ 0x1400bfe16:
// Extract file extension from filepath
wstring extension = get_file_extension(filepath);
// Lookup in blacklist (DAT_140102138)
auto iter = stl_set_find(&DAT_140102138, &result, extension);
bool is_blacklisted = (stl_wstring_compare(&DAT_140102138, iter[2], extension) == 0);
if (is_blacklisted) {
// SKIP this file - do not encrypt
} else {
// ENCRYPT this file - enqueue encryption task
enqueue_encrypt_task(...);
}| Operation | Complexity | Description |
|---|---|---|
| Directory Lookup | O(log 11) ≈ 3-4 comparisons | Binary search |
| Extension Lookup | O(log 5) ≈ 2-3 comparisons | Binary search |
| String Compare | O(min(n,m)) | Character-by-character |
Memory Footprint:
- Extension blacklist: ~400 bytes (5 nodes × 64 bytes + strings)
- Directory blacklist: ~800 bytes (11 nodes × 64 bytes + strings)
- Process blacklist: ~900 bytes (13 nodes × 64 bytes + strings)
- Total: ~2.1 KB
Timing:
- Average lookup time: ~100-200 CPU cycles
- Negligible compared to I/O operations (disk access)
Akira uses sophisticated file access techniques including two-stage locking, atomic renaming, and Windows Restart Manager integration.
Function: file_encryption_state_machine @ 0x1400b6f10 Size: Massive state machine with 19+ states Type: State machine pattern for asynchronous encryption workflow
Step 1: Remove Read-Only Attribute
DWORD attrs = GetFileAttributesW(filepath);
if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_READONLY)) {
// Remove read-only bit (bitwise XOR with 0x1)
SetFileAttributesW(filepath, attrs ^ FILE_ATTRIBUTE_READONLY);
}Purpose: Ensure writable access even if user marked file read-only
Step 2A: Exclusive Lock (First Attempt)
HANDLE hFile = CreateFileW(
filepath,
GENERIC_READ | GENERIC_WRITE | DELETE, // 0xC0010000
0, // No sharing (exclusive)
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, // 0x40000080
NULL
);Access Rights:
GENERIC_READ(0x80000000): Read file contentGENERIC_WRITE(0x40000000): Write encrypted dataDELETE(0x00010000): Required for atomic rename
Share Mode: 0 (exclusive lock - no other processes can access)
Outcome:
- Success = full exclusive control
- Failure (ERROR_SHARING_VIOLATION) → try fallback
Step 2B: Shared Lock (Fallback)
Trigger: ERROR_SHARING_VIOLATION (0x20) or ERROR_LOCK_VIOLATION (0x21)
if (error == 0x20 || error == 0x21) {
// Attempt to unlock via Restart Manager (see Section 10)
restart_manager_unlock_file(filepath);
// Retry with shared access
hFile = CreateFileW(
filepath,
GENERIC_READ | GENERIC_WRITE, // 0xC0000000 (no DELETE permission)
FILE_SHARE_READ | FILE_SHARE_WRITE, // 0x3 (allow sharing)
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
NULL
);
}Key Insight: Can encrypt files open in other programs (best-effort encryption)
Limitation: Rename may fail without DELETE permission
Step 3: Handle Validation
if (hFile == INVALID_HANDLE_VALUE || hFile == NULL) {
log_error("File handle not found! " + filename);
return; // Skip this file (non-fatal)
}LARGE_INTEGER fileSize = {0};
if (!GetFileSizeEx(hFile, &fileSize)) {
log_error("Get file size failed! " + filename);
CloseHandle(hFile);
return;
}
if (fileSize.QuadPart == 0) {
log_error("File size invalid! " + filename);
CloseHandle(hFile);
return; // SKIP zero-byte files
}Critical Finding: Zero-byte files explicitly rejected (unusual behavior, potential detection vector)
Purpose: Integrate Windows file HANDLE with Boost ASIO for async I/O
Structure (112 bytes):
struct asio_handle_wrapper_t {
void* vtable_ptr; // +0x00 (8 bytes)
int ref_count; // +0x08 (4 bytes) - Atomic
int weak_ref_count; // +0x0C (4 bytes) - Atomic
byte asio_object[96]; // +0x10 (96 bytes) - ASIO internal state
};Reference Counting (Automatic Cleanup):
// Atomic decrement
LOCK();
ref_count--;
old_count = ref_count;
UNLOCK();
if (old_count == 0) {
// Last strong reference - call destructor (closes handle)
vtable->destroy(this);
LOCK();
weak_ref_count--;
UNLOCK();
if (weak_ref_count == 0) {
// Last weak reference - free memory
vtable->deallocate(this);
}
}Advantages:
- Automatic resource management (RAII pattern)
- Thread-safe cleanup (atomic ref counting)
- Integration with ASIO thread pool
- Exception-safe (cleanup guaranteed)
string extension = ".arika";
wstring new_name = original_filename + L".arika";
// Example: document.pdf → document.pdf.arika// Allocate FILE_RENAME_INFO structure
size_t struct_size = sizeof(FILE_RENAME_INFO) + (new_name.length() * sizeof(wchar_t));
FILE_RENAME_INFO* rename_info = (FILE_RENAME_INFO*)malloc(struct_size);
rename_info->ReplaceIfExists = TRUE;
rename_info->RootDirectory = NULL;
rename_info->FileNameLength = new_name.length() * sizeof(wchar_t);
wcscpy(rename_info->FileName, new_name.c_str());
// Perform atomic rename
BOOL success = SetFileInformationByHandle(
hFile, // File must be open
FileRenameInfo, // Info class = 3
rename_info,
struct_size
);
if (!success) {
log_error("file rename failed. System error: " + GetLastError());
}Advantages:
- Atomic Operation: No race condition window
- File Stays Open: No need to close/reopen handle
- Works with Locks: Even if file is in use by other processes
- Rename + Delete Atomic: Can replace existing file atomically
if (auto_save_file_exists) {
DeleteFileW(auto_save_filepath);
}Purpose: Remove temporary files (e.g., Word ~$file.docx)
| Attempt | Access Rights | Share Mode | Purpose |
|---|---|---|---|
| 1st | READ+WRITE+DELETE | 0 (exclusive) | Full exclusive lock |
| 2nd | READ+WRITE | READ+WRITE (3) | Shared lock if locked |
Implications:
- Can encrypt files open in other programs (best-effort)
- Rename may fail if no DELETE permission granted
- Multiple
CreateFileWattempts detectable by EDR
12 Distinct Error Messages:
- "Crypt context not found!" @ 0x1400dc580
- "File handle not found!" @ 0x1400dc5a0
- "Get file size failed!" @ 0x1400dc5c0
- "File size invalid!" @ 0x1400dc5e0
- "Init cipher failed!" @ 0x1400dc628
- "get auto save file name failed!" @ 0x1400dc648
- "Encrypt pack id failed!" @ 0x1400dc670
- "Failed to make full encrypt!" @ 0x1400dc6d8
- "Failed to write header!" @ 0x1400dc77c
- "Failed to make spot encrypt!" @ 0x1400dc700
- "Failed to make part encrypt!" @ 0x1400dc728
- "file rename failed. System error:" @ 0x1400dc754
All logged to global logger: DAT_140102188 (log level 4 = ERROR)
Detection Opportunity: Error strings aid reverse engineering and dynamic analysis
GetFileAttributesW/SetFileAttributesW- Attribute manipulationCreateFileW- File opening (two attempts with different parameters)GetFileSizeEx- Size detectionSetFileInformationByHandle- Atomic rename operationDeleteFileW- Auto-save cleanup
Critical Discovery: Akira uses Windows Restart Manager API to force-terminate processes holding file locks, enabling successful encryption of in-use files.
Function: restart_manager_unlock_file @ 0x140078cc0
Protected Processes Function: populate_process_blacklist @ 0x140078ac0
Complete Sequence:
CreateFileW → ERROR_SHARING_VIOLATION (0x20)
↓
restart_manager_unlock_file(filepath)
↓
1. RmStartSession(session_key, session_handle)
↓
2. RmRegisterResources(session, 1, &filepath, 0, NULL, 0, NULL)
↓
3. RmGetList(session, &needed_size, &count, NULL, &reboot_reason)
↓
4. Allocate buffer (needed_size × sizeof(RM_PROCESS_INFO))
↓
5. RmGetList(session, &needed_size, &count, process_info, &reboot_reason)
↓
6. For each process:
- Check if PID in protected_process_list (DAT_1401021b8)
- If protected: SKIP
- If not protected: Add to kill list
↓
7. RmShutdown(session, RmForceShutdown, NULL) // FORCE TERMINATE
↓
8. RmEndSession(session)
↓
CreateFileW (retry with shared access)
Global Data Structures:
DAT_140102158@ 0x140102158:std::set<wstring>(process names)DAT_1401021b8@ 0x1401021b8:std::vector<DWORD>(process PIDs)
Initialization: init_protected_process_names @ 0x140001c10
Size: 6,314 bytes with custom XOR obfuscation
Total Count: 13 critical Windows processes
static const wchar_t* PROTECTED_PROCESSES[] = {
L"csrss.exe", // Client/Server Runtime Subsystem
L"wininit.exe", // Windows Initialization
L"spoolsv.exe", // Print Spooler
L"lsass.exe", // Local Security Authority
L"smss.exe", // Session Manager
L"winlogon.exe", // Windows Logon Process
L"services.exe", // Service Control Manager
L"conhost.exe", // Console Host
L"System", // System process (PID 4)
L"Registry", // Registry process
L"Memory Compression", // Memory Compression (Windows 10+)
L"fontdrvhost.exe", // Font Driver Host
L"explorer.exe" // Windows Explorer
};PID Population Function:
void populate_process_blacklist(void* pid_vector) {
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32W pe = {sizeof(PROCESSENTRY32W)};
Process32FirstW(hSnapshot, &pe);
do {
wstring proc_name = pe.szExeFile;
// Check if in protected process names set
auto iter = stl_set_find(&DAT_140102158, proc_name);
if (iter != end()) {
// Add PID to vector
vector_push_back(pid_vector, pe.th32ProcessID);
}
} while (Process32NextW(hSnapshot, &pe));
CloseHandle(hSnapshot);
}❌ NO EDR/AV in Protected List:
- No antivirus processes
- No EDR agents
- No security monitoring tools
Implication: Akira can and will terminate security software processes to gain file access.
Behavioral Detection Rule:
sequence:
- api: RmStartSession
- api: RmGetList
- api: RmShutdown
parameters:
dwShutdownFlags: 1 # RmForceShutdown = MALICIOUS
severity: CRITICAL
description: "Process force-termination via Restart Manager (ransomware behavior)"YARA Rule:
import "pe"
rule Akira_RestartManager {
strings:
$api1 = "RmStartSession" ascii
$api2 = "RmRegisterResources" ascii
$api3 = "RmGetList" ascii
$api4 = "RmShutdown" ascii
$api5 = "RmEndSession" ascii
condition:
pe.is_pe and
all of ($api*)
}RmStartSession- Start Restart Manager sessionRmRegisterResources- Register file/process/service for trackingRmGetList- Get list of processes using registered resourcesRmShutdown- Shutdown/restart affected applicationsRmForceShutdown(flag = 1): Force terminate without saving
RmEndSession- End Restart Manager session
┌──────────────────────────────────────────────────────────────┐
│ PHASE 5.1: Drive Enumeration │
│ │
│ GetLogicalDriveStringsW → GetDriveTypeW │
│ ↓ │
│ Drive List: [C:\, D:\, E:\, Z:\ (network)] │
│ ↓ │
│ + Share File Input: \\server\share (if --share_file) │
└─────────────────┬─────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ PHASE 5.2: Directory Traversal │
│ │
│ For each drive/share: │
│ ├─ Open directory iterator (FindFirstFileExW) │
│ ├─ Loop: FindNextFileW │
│ └─ For each entry → Apply filters (Phase 5.3) │
└─────────────────┬─────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ PHASE 5.3: File Filtering │
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Directory? │ │ File? │ │
│ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Check Dir │ │ Check Ext │ │
│ │ Blacklist │ │ Blacklist │ │
│ │ (11 dirs) │ │ (5 exts) │ │
│ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │
│ [MATCH] → SKIP [MATCH] → SKIP │
│ │ │ │
│ [NO MATCH] [NO MATCH] │
│ │ │ │
│ ▼ ▼ │
│ Enqueue Dir Task → Phase 5.4 (File Access) │
└─────────────────────────────┬───────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ PHASE 5.4: File Access & Manipulation │
│ │
│ 1. Remove READ_ONLY attribute │
│ 2. CreateFileW (exclusive) → [FAIL] → CreateFileW (shared) │
│ 3. [SHARING VIOLATION] → Restart Manager (force kill) │
│ 4. GetFileSizeEx → [0 bytes] → SKIP │
│ 5. Wrap in ASIO handle (reference counted) │
│ 6. → Pass to PHASE 6 (Encryption - see cryptography doc) │
│ 7. SetFileInformationByHandle (rename to .arika) │
│ 8. Reference count cleanup (automatic close) │
└──────────────────────────────────────────────────────────────┘
Main Thread
│
├─ initialize_drive_list()
│ ↓
│ Drive Vector Built
│ ↓
├─ init_thread_pool(folder_threads=30%, encryption_threads=60%)
│ ↓
│ Thread Pool Created (N workers)
│ ↓
└─ For each drive:
enqueue_directory_task()
↓
┌───────────────────┴────────────────────────┐
│ │
Worker Thread 1 Worker Thread N
(Folder Parser) (Folder Parser)
│ │
├─ folder_processor_worker()
│ ↓ │
│ Open directory │
│ (FindFirstFileExW) │
│ ↓ │
│ For each entry: │
│ ├─ is_dot_or_dotdot? → skip
│ ├─ Get entry name │
│ ├─ Check filters │
│ │ (O(log n) lookup)
│ ├─ If dir → enqueue_directory_task()
│ └─ If file → enqueue_encrypt_task()
│ │
└────────────────────────┘
↓
Encryption Workers
(60% of threads)
↓
File Encryption
(Phase 3 - ChaCha20)
Logical Drives → Drive List (40 bytes each)
↓
Directory Iterator (88 bytes)
↓
WIN32_FIND_DATAW (592 bytes)
↓
┌──────────┴──────────┐
│ │
Directory Name File Path
│ │
▼ ▼
STL set lookup STL set lookup
(DAT_140102148) (DAT_140102138)
Red-black tree Red-black tree
O(log 11) O(log 5)
│ │
▼ ▼
[Skip/Process] [Skip/Encrypt]
│ │
└──────────┬──────────┘
▼
File Handle (HANDLE)
↓
ASIO Wrapper (112 bytes)
↓
Reference Counted
↓
Task Object (288 bytes)
↓
Task Queue
↓
→ Encryption Worker →
↓
ChaCha20 Encryption
(Phase 3 - Crypto)
Thread Pool Structure (384 bytes total):
├── Fields (40 bytes)
├── Thread Counts (8 bytes)
├── Critical Section 1 (80 bytes) ← Folder parser
├── Critical Section 2 (80 bytes) ← Encryption
├── Condition Variable 1 (72 bytes)
├── Condition Variable 2 (72 bytes)
└── ASIO Pool Pointers (32 bytes)
Per-Thread Overhead:
├── Thread Object (32 bytes)
├── Thread Stack (1-8 MB default)
└── ASIO Context (shared 56 bytes)
Per-Task Overhead:
├── Task Object (288 bytes)
└── Task Parameters (embedded)
Filtering Data Structures:
├── Extension blacklist (5 nodes × 64 bytes) ≈ 400 bytes
├── Directory blacklist (11 nodes × 64 bytes) ≈ 800 bytes
├── Process blacklist (13 nodes × 64 bytes) ≈ 900 bytes
└── Total: ~2.1 KB (negligible)
Dynamic Allocations (Per File):
├── Directory Iterator (88 bytes) - reused
├── ASIO Handle Wrapper (112 bytes) - per open file
├── Task Object (288 bytes) - per queued task
└── WIN32_FIND_DATAW (592 bytes) - per iterator
| Component | Operation | Complexity | Notes |
|---|---|---|---|
| Drive Enum | Enumerate drives | O(D) | D = number of drives (≤26) |
| Dir Traversal | File enumeration | O(F) | F = total files/folders |
| Filter Check | Blacklist lookup | O(log 11) + O(log 5) | ~6-7 comparisons max |
| File Open | CreateFileW | O(1) | Constant time API call |
| Encryption | ChaCha20 | O(N) | N = file size (see Phase 3) |
Total File Processing: O(F × (log K + N))
- F = number of files
- K = filter size (≈16 total entries)
- N = average file size
CPU Utilization:
- Near 100% CPU usage during encryption phase
- Well-balanced workload distribution (30/10/60 split)
- Minimal idle time on worker threads
- Condition variables prevent busy-waiting
I/O Patterns:
- Parallel directory traversal (30% of threads)
- Concurrent file encryption (60% of threads)
- High throughput on NVMe storage
- Sequential scan flag for disk optimization
Bottlenecks:
- Single producer (main thread) for task enqueue
- File system metadata operations (NTFS overhead)
- ChaCha20 encryption speed (CPU-bound)
- I/O bound on HDD systems
| CPU Cores | Threads | Est. Throughput | Bottleneck |
|---|---|---|---|
| 2 | 2 | 50-100 MB/s | CPU-bound |
| 4 | 4 | 200-400 MB/s | CPU-bound |
| 8 | 8 | 400-800 MB/s | Mixed |
| 16 | 16 | 800-1600 MB/s | I/O-bound |
| 32 | 32 | 1-2 GB/s | I/O-bound |
| 64+ | 64+ | 2+ GB/s | I/O-bound |
Assumptions:
- NVMe SSD storage (>2 GB/s read/write)
- Average file size 10 MB
- 50% files pass filters
- ChaCha20 throughput ~400 MB/s per core
Test Scenario:
- 10 worker threads (8-core system)
- 100,000 files
- 50% pass filters (50,000 encrypted)
- Average file 10 MB
Time Breakdown (Per File):
- Filter check: 6-7 comparisons × 2 µs = ~15 µs
- File open: ~500 µs (NVMe)
- GetFileSizeEx: ~50 µs
- Encryption: ~10 ms per file (ChaCha20 @ 1 GB/s)
- Rename: ~200 µs (SetFileInformationByHandle)
- Total per file: ~10.765 ms
Theoretical Throughput:
- Single thread: ~93 files/sec
- 10 threads: ~930 files/sec
- Total time for 50K files: ~54 seconds (~1 minute)
Actual Performance: Likely I/O bound
- HDD: ~100 MB/s sequential (slower)
- SSD SATA: ~500 MB/s sequential
- SSD NVMe: ~2000 MB/s sequential (theoretical max)
| Component | Size | Count | Total |
|---|---|---|---|
| Thread Pool | 384 bytes | 1 | ~400 bytes |
| Thread Object | 32 bytes | ≤64 | ~2 KB |
| ASIO Pool | 48 bytes | 2 | ~100 bytes |
| ASIO Context | 56 bytes | 2 | ~112 bytes |
| Dir Iterator | 88 bytes | Per thread | ~880 bytes (10 threads) |
| Extension Set | ~400 bytes | 1 | ~400 bytes |
| Directory Set | ~800 bytes | 1 | ~800 bytes |
| Process Set | ~900 bytes | 1 | ~900 bytes |
| Task Object | 288 bytes | Per file | Variable (queue bounded) |
| ASIO Handle | 112 bytes | Per open file | Variable |
Static Memory: ~5.5 KB Dynamic Memory (Peak): Depends on queue size (likely 100-1000 tasks × 288 bytes = 28-288 KB) Total Estimated: <500 KB for threading infrastructure
1. System Preservation:
- Excludes Windows, Boot → System remains bootable
- Excludes .exe, .dll, .sys → Applications still run
- Excludes critical processes → No BSOD
- Victim can pay ransom using browser
2. Professional Engineering:
- Boost ASIO library (industry standard)
- STL containers (maintainable, efficient)
- Reference counting (no handle leaks)
- Atomic operations (race-free)
- Comprehensive error handling
- Clean architecture (separation of concerns)
3. Stealth:
- No active network enumeration (reduces detection)
- Minimal API footprint
- Quiet failures (logs errors, continues)
- String obfuscation (protected process names)
4. Efficiency:
- O(log n) filtering (fast blacklist checks)
- Parallel traversal (30% threads for I/O)
- Parallel encryption (60% threads for CPU)
- Sequential scan hint (disk optimization)
- Bounded queue (prevents memory exhaustion)
5. Process Termination:
- Restart Manager API (legitimate Windows feature)
- Force-kills processes holding file locks
- NO security software protection
- Can terminate EDR/AV agents
6. Robustness:
- Two-stage file locking (exclusive → shared)
- Atomic renaming (race-free)
- Zero-byte file skip (avoids corruption)
- Deadlock-free design
- Exception-safe resource management
1. Predictable Patterns:
- Two
CreateFileWattempts with specific flags:- 1st:
dwDesiredAccess=0xC0010000,dwShareMode=0 - 2nd:
dwDesiredAccess=0xC0000000,dwShareMode=3
- 1st:
SetFileInformationByHandlewithFileRenameInfo(unusual)- Sequential scan flag (uncommon for normal apps)
- Error strings aid reverse engineering
2. Behavioral Signatures:
- High file I/O rate across entire filesystem
- Repeated attribute modifications (
SetFileAttributesWto remove READ_ONLY) - Mass file renaming to
.arikaextension - Sharing violation retries (ERROR_SHARING_VIOLATION handling)
- Restart Manager process termination (RmShutdown with RmForceShutdown)
3. Memory Artifacts:
- STL red-black tree structures (predictable memory layout)
- ASIO handle wrappers (112-byte allocations)
- Task objects (288-byte allocations)
- Predictable string patterns:
- L".exe", L".dll", L".sys", L".lnk", L".msi"
- L"Windows", L"Boot", L"$Recycle.Bin"
- L".arika"
4. Logic Flaws:
- Case-sensitive filtering on Windows: Can be bypassed by changing directory name case
- Example: Rename "Windows" to "windows" to avoid exclusion
- Zero-byte file skip (unusual, detection vector)
- No filename filtering (desktop.ini, thumbs.db encrypted)
- NO security software in protected process list
5. Network Stealth Trade-off:
- Requires manual share enumeration (--share_file)
- Operators must pre-enumerate network
- Misses dynamically mapped shares
CVSS Assessment (Hypothetical):
Theoretical Vulnerability: Case-Sensitive Directory Filtering on Case-Insensitive Filesystem
- CVSS v3.1 Vector:
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N - Base Score: 3.3 (LOW)
- Impact: Attacker expects "Windows" to be encrypted, but "windows" bypasses filter
Note: This is not a vulnerability in the traditional sense, but a logic flaw that could be exploited by defenders to protect critical directories.
Rule 1: Threading Patterns
rule Akira_Threading_ASIO {
meta:
description = "Akira ransomware - ASIO threading patterns"
author = "MottaSec"
date = "2025-01-15"
hash = "SAMPLE_HASH"
strings:
// ASIO library signatures
$asio1 = "asio::thread_pool" ascii
$asio2 = "asio::detail::win_thread" ascii
$asio3 = "Concurrency::details::stl_critical_section_win7" ascii
// Synchronization patterns (opcodes)
$pattern1 = { 48 8D 0D [4] E8 [4] 48 8D 0D [4] E8 } // Thread pool init
condition:
uint16(0) == 0x5A4D and // PE file
(2 of ($asio*) or $pattern1)
}Rule 2: File System Operations
rule Akira_FileSystem {
meta:
description = "Akira ransomware - File system operations"
author = "MottaSec"
date = "2025-01-15"
strings:
// Extension blacklist
$ext1 = ".exe" wide
$ext2 = ".dll" wide
$ext3 = ".sys" wide
$ext4 = ".lnk" wide
$ext5 = ".msi" wide
// Directory blacklist
$dir1 = "System Volume Information" wide
$dir2 = "Trend Micro" wide
$dir3 = "$Recycle.Bin" wide
$dir4 = "$RECYCLE.BIN" wide
// Error messages
$err1 = "File handle not found! " wide
$err2 = "Get file size failed! " wide
$err3 = "file rename failed. System error:" wide
// Akira extension
$arika = ".arika" wide
condition:
uint16(0) == 0x5A4D and
(
(3 of ($ext*)) or
(2 of ($dir*))
) and
(2 of ($err*)) and
$arika
}Rule 3: Restart Manager
rule Akira_RestartManager {
meta:
description = "Akira ransomware - Process termination via Restart Manager"
author = "MottaSec"
date = "2025-01-15"
strings:
$api1 = "RmStartSession" ascii
$api2 = "RmRegisterResources" ascii
$api3 = "RmGetList" ascii
$api4 = "RmShutdown" ascii
$api5 = "RmEndSession" ascii
condition:
uint16(0) == 0x5A4D and
all of ($api*)
}Critical API Combinations:
Threading:
- InitializeConditionVariable
- SleepConditionVariableCS
- WakeConditionVariable
- WakeAllConditionVariable
File System:
- GetLogicalDriveStringsW
- GetDriveTypeW
- FindFirstFileExW
- FindNextFileW
- GetFileAttributesW
- SetFileAttributesW
- CreateFileW
- GetFileSizeEx
- SetFileInformationByHandle
- DeleteFileW
Process Termination:
- RmStartSession
- RmRegisterResources
- RmGetList
- RmShutdown
- RmEndSession
- CreateToolhelp32Snapshot
- Process32FirstW
- Process32NextW
Rule 1: Mass File Access Pattern
name: "Akira - Mass File Access Pattern"
description: "Detects rapid file access with specific flags"
severity: HIGH
sequence:
- event: CreateFileW
count: ">100"
time_window: "1 minute"
filters:
dwDesiredAccess: 0xC0010000
dwShareMode: 0
dwFlagsAndAttributes: 0x40000080
action: ALERT + BLOCKRule 2: Attribute Manipulation Pattern
name: "Akira - Mass READ_ONLY Removal"
description: "Detects mass removal of read-only attribute"
severity: MEDIUM
sequence:
- event: GetFileAttributesW
next:
- event: SetFileAttributesW
filters:
operation: "XOR with FILE_ATTRIBUTE_READONLY"
count: ">50"
time_window: "1 minute"
action: ALERTRule 3: Atomic Rename Pattern
name: "Akira - Mass Atomic Rename to .arika"
description: "Detects mass file renaming to .arika extension"
severity: CRITICAL
sequence:
- event: SetFileInformationByHandle
filters:
FileInformationClass: FileRenameInfo
NewFileName: "regex:.*\\.arika$"
count: ">20"
time_window: "30 seconds"
action: ALERT + BLOCK + ISOLATERule 4: Sharing Violation Retry Pattern
name: "Akira - File Lock Retry Pattern"
description: "Detects two-stage file locking attempts"
severity: MEDIUM
sequence:
- event: CreateFileW
result: ERROR_SHARING_VIOLATION
next:
- event: CreateFileW
filters:
same_file: true
dwShareMode: 3 # Shared access
count: ">10"
time_window: "1 minute"
action: ALERTRule 5: Restart Manager Process Termination
name: "Akira - Forced Process Termination"
description: "Detects malicious use of Restart Manager"
severity: CRITICAL
sequence:
- event: RmStartSession
- event: RmGetList
- event: RmShutdown
filters:
dwShutdownFlags: 1 # RmForceShutdown
action: ALERT + BLOCK + ISOLATERule 6: Zero-Byte Skip Pattern
name: "Akira - Zero-Byte File Skip"
description: "Detects unusual zero-byte file rejection"
severity: LOW
sequence:
- event: GetFileSizeEx
result: 0
next:
- event: CloseHandle
time_delta: "<10ms"
count: ">10"
time_window: "1 minute"
action: LOGIDS Rules (Snort/Suricata):
❌ NO direct network signatures - Akira does not perform active network enumeration.
Recommended Monitoring:
- SMB anomaly detection (unusual workstation-to-workstation SMB)
- Mass file access to network shares from single host
- Rapid file modification rates on file servers
- Unusual .arika file extensions appearing on SMB shares
Example Suricata Rule (SMB anomaly):
alert smb any any -> any any (
msg:"Possible Akira - Rapid SMB File Operations";
flow:to_server,established;
threshold:type both, track by_src, count 100, seconds 60;
classtype:ransomware;
sid:9000001;
rev:1;
)
Hunt for Structures:
1. STL Set Red-Black Trees:
Address: 0x140102138 (extension blacklist)
Address: 0x140102148 (directory blacklist)
Address: 0x140102158 (process blacklist)
Pattern:
- 64-byte aligned nodes
- Parent/left/right pointers
- Color byte (red=0, black=1)
- Embedded wstring (32 bytes)
2. ASIO Handle Wrappers:
Size: 112 bytes
Pattern:
- Vtable pointer at +0x00
- Ref count at +0x08 (typically 1-10)
- Weak ref count at +0x0C
- ASIO object at +0x10 (96 bytes)
3. Task Objects:
Size: 288 bytes
Pattern:
- Function pointers at +0x00, +0x08
- Linked list node at +0x10-0x40
- Shared pointers at +0x49, +0x59
- Wide string at +0x69 (file path)
- Task flags at +0x9C (0x10002 or 0x2)
4. Thread Pool Structure:
Size: 384 bytes
Pattern:
- Thread counts at +0x28, +0x34
- Critical sections at +0x40, +0x90 (80 bytes each)
- Condition variables at +0xE0, +0x128 (72 bytes each)
Volatility Plugin Recommendation:
# Hypothetical Volatility plugin
class AkiraThreadPoolScanner(obj.ProfileModification):
def check(self, offset):
# Scan for 384-byte structure
# Check for critical section patterns
# Verify condition variable initialization
# Validate thread counts (30/10/60 split)| Address | Name | Purpose | Status |
|---|---|---|---|
| 0x14007b6d0 | init_thread_pool | Initialize dual thread pool | ✅ Renamed (Phase 2) |
| 0x14007b850 | enqueue_encrypt_task | Add task to encryption queue | ✅ Renamed (Phase 2) |
| 0x14007b280 | create_asio_thread_pool | Create ASIO thread pool | ⏳ Analyzed |
| 0x1400385f0 | init_asio_context | Initialize ASIO context | ⏳ Analyzed |
| 0x14007a2d0 | create_thread_pool_impl | Create thread pool implementation | ⏳ Analyzed |
| 0x14007bb60 | build_task_object | Allocate and build task | ⏳ Analyzed |
| 0x1400c4030 | task_constructor | Initialize task structure | ⏳ Analyzed |
| 0x14007bd00 | asio_post_task | Submit task to ASIO pool | ⏳ Analyzed |
| 0x1400c45a0 | task_execution_function | Task entry point | ⏳ Analyzed |
| 0x140038a10 | init_win_thread | Initialize Windows thread | ⏳ Analyzed |
| 0x14007d200 | setup_thread_context | Setup thread context | ⏳ Analyzed |
| 0x140081d00 | init_critical_section | Initialize critical section | ⏳ Analyzed |
| 0x1400820e8 | init_condition_variable | Initialize condition variable | ⏳ Analyzed |
| 0x140082118 | condition_variable_wait | Wait on condition variable | ⏳ Analyzed |
| 0x1400812e0 | log_error_code | Log synchronization errors | ⏳ Analyzed |
| 0x14003a4a0 | allocate_task | Allocate task (0x120 bytes) | ⏳ Analyzed |
| Address | Name | Purpose | Status |
|---|---|---|---|
| 0x14007e6a0 | initialize_drive_list | Enumerate and classify drives | ✅ Renamed |
| 0x14007f4e0 | vector_realloc_drives | Vector reallocation | ✅ Renamed |
| 0x140042830 | read_share_file | Parse share file input | ✅ Renamed |
| Address | Name | Purpose | Status |
|---|---|---|---|
| 0x1400bf190 | folder_processor_worker | Main traversal worker | ✅ Renamed |
| 0x140070db0 | directory_iterator_ctor | Iterator constructor | ✅ Renamed |
| 0x14006f4c0 | open_directory_iterator | Open directory | ✅ Renamed |
| 0x140080b0c | find_next_file_wrapper | Advance iterator | ✅ Renamed |
| 0x14006e440 | is_dot_or_dotdot | Check if dot entry | ✅ Renamed |
| Address | Name | Purpose | Status |
|---|---|---|---|
| 0x140001ac0 | init_extension_blacklist | Init extension blacklist | ✅ Renamed |
| 0x1400018a0 | init_directory_blacklist | Init directory blacklist | ✅ Renamed |
| 0x14005dea0 | stl_set_find | STL set find() | ✅ Renamed |
| 0x14005df80 | stl_wstring_compare | Wide string compare | ✅ Renamed |
| 0x1400cd260 | cleanup_directory_blacklist | Cleanup dir blacklist | ✅ Renamed |
| 0x1400cd2a0 | cleanup_extension_blacklist | Cleanup ext blacklist | ✅ Renamed |
| Address | Name | Purpose | Status |
|---|---|---|---|
| 0x1400b6f10 | file_encryption_state_machine | File encryption workflow | ✅ Renamed |
| 0x140078cc0 | restart_manager_unlock_file | Force unlock via RM | ✅ Renamed |
| 0x140078ac0 | populate_process_blacklist | Build protected PID list | ✅ Renamed |
| 0x140001c10 | init_protected_process_names | Init process names (obfuscated) | ✅ Renamed |
| 0x140040b50 | init_asio_file_handle | Initialize ASIO handle | ⏳ Pending |
| 0x14007bfd0 | enqueue_encryption_task | Enqueue encryption task | ✅ Renamed |
| Address | Name | Type | Purpose |
|---|---|---|---|
| 0x140102138 | DAT_140102138 | std::set | Extension blacklist (5 entries) |
| 0x140102148 | DAT_140102148 | std::set | Directory blacklist (11 entries) |
| 0x140102158 | DAT_140102158 | std::set | Protected process names (13 entries) |
| 0x1401021b8 | DAT_1401021b8 | std::vector | Protected process PIDs (dynamic) |
| 0x140102188 | DAT_140102188 | Logger* | Global error logger |
| 0x1400f9fd0 | DAT_1400f9fd0 | wstring | ".arika" extension |
Threading & Concurrency (Phase 4): ✅ Complete thread pool architecture documented (384-byte structure) ✅ ASIO library integration analyzed (Boost ASIO statically linked) ✅ Thread allocation algorithm mapped (30/10/60 split) ✅ Synchronization mechanisms analyzed (critical sections, condition variables) ✅ Task queue system understood (288-byte task objects, producer-consumer) ✅ Deadlock-free design verified (single lock per operation)
File System Operations (Phase 5): ✅ Drive enumeration complete (40-byte drive structure, no active network enum) ✅ Directory traversal analyzed (88-byte iterator, task-based recursion) ✅ File filtering system documented (5 extensions, 11 directories, 13 processes) ✅ File access strategies mapped (two-stage locking, atomic rename) ✅ Restart Manager integration discovered (force process termination) ✅ Protected process list extracted (11 critical processes, NO EDR/AV)
Rating: VERY HIGH
Akira demonstrates professional-grade software engineering:
- Industry-Standard Libraries: Boost ASIO, STL containers
- Proper Synchronization: Critical sections, condition variables, atomic operations
- Efficient Algorithms: Red-black trees (O(log n)), work-stealing scheduler
- Clean Architecture: Separation of concerns, reference counting, exception safety
- Performance Optimization: Thread pooling, bounded queues, parallel I/O
This level of sophistication suggests:
- Experienced C++ developers (5+ years)
- Professional development practices
- Likely APT-level malware development
- Significant financial backing
Defender Opportunities:
-
Behavioral Detection:
- Restart Manager RmForceShutdown (malicious use)
- Mass file access with specific flags (0xC0010000, 0xC0000000)
- Atomic renaming to .arika extension
- Two-stage file locking pattern
-
Logic Flaws:
- Case-sensitive filtering on case-insensitive filesystem
- Zero-byte file skip (unusual behavior)
- NO security software in protected process list
-
Memory Artifacts:
- STL structures (predictable memory layout)
- ASIO handle wrappers (112-byte allocations)
- Task objects (288-byte allocations)
Phase 6: Encryption Strategy Analysis
Focus areas:
- Full vs. Part vs. Spot encryption modes
- Chunk size selection algorithms
- Footer generation and structure
- File size thresholds
- Encryption mode performance trade-offs
Key Function: encrypt_file_worker (encryption mode dispatch)
Document Status: ✅ COMPLETE Total Functions Analyzed: 40+ functions Total Structures Mapped: 15+ structures (byte-accurate) Documentation Size: ~3,000 lines (comprehensive technical detail) Confidence Level: 95-99% Date Completed: 2025-01-15
Research Credit: MottaSec Contact: https://www.linkedin.com/company/mottasec GitHub: https://github.com/MottaSec X (Twitter): https://x.com/mottasec_
END OF THREADING & EXECUTION ANALYSIS