All research

Write-up23 October 2026

Krizzh reverse engineers the JScript dropper, Lua loader, Donut wrapper and Remcos 7.3.1 Pro payload, with 28 figures, configuration recovery and evidence-qualified detection findings.

This report records offline analysis dated 23 October 2026. Windows execution and successful controller communication were not observed. References to the original evidence bundle and reproduction scripts describe the author's analysis package; those files and raw malware samples are not distributed with this publication.

Assessment. The attachment contains a self-contained delivery chain for a 32-bit Remcos implant. An obfuscated JScript drops LuaJIT and a companion Lua script. The Lua establishes a scheduled-execution path, attempts to interfere with inspection and prepares embedded Donut shellcode. The Donut instance contains a complete Remcos executable and its controller configuration. The recovered core supports remote command execution, surveillance, file and registry access, process manipulation and additional modules supplied through feature channels. Successful infection or communication with a controller was not observed during this investigation.

Visual evidence: 28 diagrams, 14 annotated disassembly excerpts and four decoding comparisons. Detection material includes four YARA rules, two experimental behavioral templates and an evidence-qualified ATT&CK mapping. Windows execution remains unvalidated.

Contents

  1. Executive assessment
  2. Evidence and analytical scope
  3. Delivery through JScript
  4. The Lua loader
  5. Recovering the Donut instance
  6. The recovered Windows executable
  7. Configuration and startup decisions
  8. Controller communication
  9. Command dispatch and feature channels
  10. Surveillance and remote interaction
  11. Files, registry and host control
  12. Browser data and recovery modules
  13. In-memory modules, hollowing and elevation
  14. Restart, update, watchdog and removal
  15. Forensic interpretation and detection
  16. Verification and remaining limits
  17. Appendices
  18. Visual evidence index

1. Executive assessment

The most useful way to understand this sample is to separate its delivery machinery from the implant it carries. The JScript and Lua layers make the payload inconvenient to inspect and arrange for it to run again. Remcos supplies the remote-control functionality. Donut sits between those layers, packaging the Windows executable for memory-based loading.

The initial Lua decoding produces 568,907 bytes of high-entropy data. That result can easily be mistaken for an unresolved encrypted payload. Its first instruction explains why that interpretation is incomplete: a relative CALL skips over a large embedded instance and reaches loader code. Following that structure exposes a Donut decryptor and, inside the decrypted instance, an intact Remcos PE.

The recovered executable identifies itself as Remcos 7.3.1 Pro. Its SETTINGS resource decrypts into 58 positional elements, including a trailing empty element. The populated configuration includes a controller at zxaswedryyhfrdswaaaaaacvbnmjgffds.ydns.eu:20901, a TLS certificate and matching private key, a separate peer certificate, a mutex name, installation names and surveillance settings.

The core is capable of substantially more than connecting and polling. Code paths support keyboard capture, clipboard access, JPEG screen capture, remote input, microphone capture, redirected command shells, file transfers, registry editing, process termination and suspension, browser-data clearing, and loading additional executable modules. The primary dispatcher has 82 active command entries. Some feature channels expose further dispatchers, while others accept a DLL from the controller and resolve its exports at runtime.

Those capabilities should not be confused with the sample's startup defaults. The embedded configuration disables Remcos's own installation route, ordinary offline keylogging, automatic screenshot branches, background microphone recording, automatic browser clearing, and watchdog startup. The Lua wrapper nevertheless attempts its own VBS launcher and a scheduled task every ten minutes. An operator can also enable features after connection, and saved registry state can override some defaults.

The finding is therefore a recovered remote-access implant with concrete persistence and control mechanisms. The evidence does not establish an affected victim, a successful TLS session, operator activity, or exfiltration. No endpoint logs, memory image or packet capture accompanied the sample.

Figure D01. Delivery, payload and persistence boundaries

Delivery, payload and persistence boundaries

The solid relationships are reconstructed from code and artifacts. The diagram is not a record of a successful Windows execution. Optional feature DLLs may be supplied later and are not all present in the submitted evidence.

2. Evidence and analytical scope

2.1 Evidence identity

The long hexadecimal basename belongs to the JScript: its SHA-256 is b036c2455322547e0624c6e824b3622d83f558e1ce3cd8be3a2751662082c16d. The ZIP itself has a different hash. Maintaining that distinction matters when sharing indicators or comparing submissions.

Artifact Size, bytes SHA-256
Submitted ZIP 2,841,837 3ffd2ce4714a11e3f38750019e9f667613d44709bb8490240f30e6a69302b7ab
JScript dropper 5,988,774 b036c2455322547e0624c6e824b3622d83f558e1ce3cd8be3a2751662082c16d
Lua companion 1,492,733 f394756899743eb04304fbe313bd8f44a59a57d87743dad62fa6a223db311159
LuaJIT host 511,061 d447a06cba8d3dbe80e09d73dd64576836b258961ee0d3ba56cf26cea7962b49
Decoded Donut shellcode 568,907 77ea7df41b60a9edeafbec70964c9964472e312f8d81c56e3f52b48f616e3578
Decrypted Donut instance 543,680 77594f79542a200ada3868abf28f75e51a19ebdc28c84579e9d4328b381d343a
Exact Remcos PE 537,600 79179155729934442e02925d2a97ded40e739216352159c536c47643bfe53fda

The decrypted-instance hash includes the counter after the decryptor has advanced it. A tool that preserves the original counter in its output can therefore produce a different instance hash while recovering identical plaintext and an identical PE. The included independent implementation reproduces the emulator's complete output, including the updated counter.

2.2 What was done

The ZIP was extracted with password infected. JScript data-processing code was evaluated with inert replacements for ActiveXObject and WScript. The replacements logged requested Windows operations and captured base64 object contents. They did not implement COM, launch processes or write to Windows persistence locations.

The Lua was inspected as source. Selected pure-data decoding functions were evaluated separately; the loader itself was not invoked. A cleaned analytical copy was syntax-checked after repeated VM blocks were removed. That copy is for review and is not a replacement malware launcher.

The Donut decryptor was emulated in isolation, starting directly at its decryption function and stopping at a sentinel return address. A separate pure Python implementation then reproduced the same bytes without executing shellcode. PE metadata, resources and configuration were parsed independently. Certificates were parsed and their signatures and key relationship checked.

The Remcos core was disassembled with Capstone and indexed for imports, calls and string references. Primary command tables and selected secondary routes were recovered from the code. One packet-header routine was emulated with synthetic bytes and local memcpy/memcmp substitutes. No controller command handler was run.

2.3 How to read the findings

A confirmed artifact is present in recovered bytes and independently parsed or reproduced. A code-backed behavior has an identified implementation or call path, but has not necessarily succeeded on Windows. An interpretation explains the likely role of that code and is bounded by the available evidence.

All Remcos addresses in this report use preferred virtual addresses with image base 0x400000. Donut addresses are offsets within the decoded shellcode. Lua identifiers are retained where they help correlate the narrative with the recovered source. Addresses are not live addresses from a victim machine.

Figure D02. Independent evidence supporting payload recovery

Independent evidence supporting payload recovery

Two different implementations produce the same decrypted instance. The image boundary is then checked independently against the module length and the PE section table.

3. Delivery through JScript

3.1 What the outer script hides

The archive contains one JScript file. Its size is dominated by embedded content and obfuscation. Large amounts of multilingual filler, repeated functions and string-table indirection obscure a short delivery sequence.

The script also includes a recursive debugger trap. The extraction harness neutralizes that trap while preserving the decoding logic. This is an analytical modification to the harnessed copy, not a change to the submitted evidence.

The meaningful operations reconstructed through the logging interfaces are:

Step Requested operation Analytical significance
1 cmd /c echo. > "<script>:Zone.Identifier" Attempts to overwrite the zone stream associated with the script
2 SpecialFolders("MyDocuments") Selects the drop directory
3 File-existence checks Controls whether embedded companions need to be written
4 Create a .mui marker Supplies the condition later used by Lua persistence code
5 XMLDOM base64 decoding and ADODB.Stream writes Materializes the Lua script and interpreter
6 Shell Run request Starts the interpreter with the Lua companion as its argument

The zone-stream command is sometimes described too loosely as “removing Mark of the Web.” The actual code attempts to overwrite the stream. The resulting Windows trust behavior was not tested, and the script is already executing by the time that command is issued.

Figure D03. JScript delivery branch

JScript delivery branch

The instrumented extraction harness forces the missing-companion branch to expose embedded data. File writes and process launches in this figure are requested operations, not observed Windows events.

3.2 Dropped files

The reconstructed names are:

  • ZGNICVGKWHUBRJMP.exe: the LuaJIT host.
  • PMJRBUHWKGVCINGZ: the Lua source and embedded payload.
  • ZGNICVGKWHUBRJMP.mui: the marker used by subsequent logic.

The companion name is the executable basename reversed. That relationship is later reused rather than stored as an unrelated path.

The host contains LuaJIT identification material and the normal runtime interface. Its PE subsystem is console, despite the earlier supplied notes calling it a GUI executable. This analysis establishes its interpreter role and identity strings; it does not claim a byte-for-byte match to a particular official LuaJIT distribution.

The harness records a symbolic Documents path. It cannot establish a compromised username, workstation name, original delivery email, download URL or execution time. None of those facts is inferred from the local extraction directory.

4. The Lua loader

4.1 The visible complexity and the real logic

The Lua begins with a large encoded string named ZGNICV. The remainder contains many repeated implementations of a Lua 5.1 virtual machine: opcode names, prototype readers, constants and interpreter handlers.

Those blocks initially suggest that the actual malicious program must be recovered from custom VM bytecode. That is not necessary here. The script also contains direct source implementing decoding, Windows API interaction, persistence and shellcode execution. Removing 62 structurally repeated VM blocks leaves approximately 52 KB of analytical source that still passes a Lua 5.1 syntax compilation check.

The practical consequence is important: a large collection of VM-related strings is not evidence that those VMs mediate the payload's execution. The relevant execution path can be followed directly through the source.

Figure D05. Separating meaningful Lua from camouflage

Separating meaningful Lua from camouflage

The analytical reduction does not prove the entire loader works. It establishes that the directly recoverable source remains syntactically coherent after the repeated blocks are removed.

4.2 Scheduled execution

The Lua obtains its executable path, removes the extension from the basename, and derives the companion and marker paths. If the matching .mui file exists, it creates a VBS launcher if one is not already present.

For this sample the launcher is PMJRBUHWKGVCINGZ.vbs. It uses WScript.Shell to run the LuaJIT executable with the extensionless companion file and requests a hidden window.

The Lua queries for a scheduled task named ZGNICVGKWHUBRJMP. When the query indicates that the task is absent, it constructs a task with /sc minute /mo 10, a wscript.exe action, the current USERNAME, and /f /it.

This provides a recurring launch attempt. It does not prove that task registration succeeded, and its account/session options should not be described as an unconditional boot-time service. The source supports a ten-minute scheduled-task mechanism, not a confirmed victim persistence event.

Figure D04. Lua scheduled-task persistence conditions

Lua scheduled-task persistence conditions

The task is named ZGNICVGKWHUBRJMP and runs PMJRBUHWKGVCINGZ.vbs through wscript.exe. The source conditions explain the persistence attempt; task creation has not been validated on Windows.

4.3 Native patches and inspection interference

The early G routine attempts to alter functions exported by ntdll. Its target list includes ETW functions and native process/thread operations. The replacement starts with RET and fills the rest of the patch region with NOPs.

Later routines attempt a different three-byte patch, 31 C0 C3, at ETW and AMSI entry points. In x86 terms this clears EAX and returns. Targets include EtwEventWrite, EtwEventWriteFull, EtwEventRegister, NtTraceEvent, AmsiScanBuffer, AmsiOpenSession and AmsiScanString.

These are concrete patch attempts. They are not sufficient grounds to call the bypass complete or reliable. Returning a nominal success code is not equivalent to implementing a function's output parameters. Broadly patching native functions can also interfere with the loader's own dependencies. That concern is especially relevant because the later implant contains native section-mapping code. No runtime compatibility claim is made.

Other routines inspect debugger-related state, query process debug information, attempt to hide a thread, inspect or clear hardware-breakpoint registers, and introduce delays. The source also mutates global tables and allocates distracting buffers.

One routine, fr, allocates a 1 MiB buffer filled with API names such as VirtualAllocEx, WriteProcessMemory, CreateRemoteThread, QueueUserAPC and NtCreateThreadEx. Those names are intentionally manufactured data in that routine. Counting them as separately implemented injection techniques would be a false positive in the analysis.

4.4 The three decoding layers

The meaningful decode path is short enough to describe precisely:

  1. u0Zw356QS2JN checks for a leading |. The second character selects one of 25 substitution alphabets. Ten symbols map to A-F and a-d; all other characters pass through. The result is reversed. This sample uses selector N, or zero-based table index 23.
  2. mr base64-decodes that result.
  3. m0amT8SSscq8Js9M reads the first byte as an encoded rotation key and reverses a printable-ASCII rotation over characters 33-126. The first byte is 159, so the effective key is 31. Nonprintable bytes are preserved and the key byte is discarded.

The result is the 568,907-byte Donut shellcode identified in the evidence table. The entire remaining payload is already present; these stages do not need to fetch it from the network.

Figure D06. Payload decoding decisions

Payload decoding decisions

Nonprintable bytes survive the rotation step. The leading key byte is removed. The decoded shellcode hash is 77ea7df4...616e3578; full hashes appear in the evidence table.

Byte comparison B01. Lua input and decoded shellcode prefixes

Encoded ZGNICV value, offset 0x0:

7c 4e 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 77 77 36 2d 73 53 4a 34 37 29 28 34
|N&&&&&&&&&&&&&&&&&&ww6-sSJ47)(4

Decoded Donut shellcode, offset 0x0:

e8 c0 4b 08 00 c0 4b 08 00 50 11 c0 04 b1 46 91 79 19 ec 11 b5 db c5 cb 86 73 53 be 1b 39 38 0c
..K...K..P....F.y........sS..98.

These are prefixes at two different transformation stages, not an equal-length block-cipher pair.

4.5 Page protection and the exception handler

The loader requests an allocation of the decoded length plus 8,192 plus 600,000,000 bytes. That is a large virtual allocation request. It is not evidence that the same amount was resident in physical memory.

After copying the shellcode, it appends 256 NOP bytes and calls Kr. This routine obtains the system page size and prepares page-level protection and exception handling. xr transforms each page by XORing 64-bit words with an evolving mask derived from a runtime value and page index. The exception callback checks for access violations within the tracked region, changes protection, applies the matching XOR transformation and restores executable access.

The mechanism is designed to conceal the in-memory shellcode until a page is needed. It is a protection layer applied after the embedded data has been decoded. Recovering the original Donut instance did not require executing that mechanism or guessing its runtime-generated key.

A limited FFI declaration audit accepted the three literal declaration blocks under a Linux x64 LuaJIT parser. This only checks declaration syntax in that runtime. It does not validate Windows x86 calling conventions, structure layout, callback behavior or successful native patching.

Figure D07. VEH page-protection cycle

VEH page-protection cycle

This is the protection sequence expressed by the source. It was not executed as a Windows exception-handling test. It is separate from decrypting the original embedded Donut instance.

5. Recovering the Donut instance

5.1 The instruction that changes the analysis

The decoded blob starts:

E8 C0 4B 08 00    call 0x84bc5

The CALL pushes the address immediately after the instruction and transfers execution to offset 0x84bc5. The skipped region begins at offset 5 and is 0x84bc0 bytes long. That region is the embedded instance.

At the target, the loader retrieves the instance pointer and selects its architecture path. In 32-bit interpretation, xor eax,eax; dec eax; js ... reaches the x86 loader at 0x880bb. The shellcode also contains a 64-bit loader region. The embedded executable recovered in this case is x86.

This structure explains the high entropy. Much of the blob is encrypted data inside a valid executable wrapper. A lack of an MZ header at offset zero is expected and does not imply that the analysis has reached an inaccessible final layer.

Figure D08. Donut shellcode regions and architecture selection

Donut shellcode regions and architecture selection

The recovered executable is x86. The CALL target is calculated as 5 + 0x84bc0. The data between the instruction and its target accounts for most of the high-entropy blob.

Evidence E01. The CALL that exposes the embedded instance

Offsets from the beginning of decoded shellcode. Comments annotate the selected instructions; this is a local excerpt rather than a full function listing.

00000000  call    0x84bc5  ; target = next instruction + 0x84bc0; pushed return address points at instance

5.2 Locating the decryptor

The x86 loader identifies the decryption function at shellcode offset 0x8aaa9. Its call site at 0x8946d supplies:

Argument Instance-relative value
Key +0x04
Counter +0x14
Encrypted data +0x23c
Data length Instance length minus 0x23c

The instance length is the first DWORD, 0x84bc0. Its encryption/entropy mode at +0x234 is 3.

The isolated emulator mapped the shellcode and a stack, supplied those arguments and started at the decryptor itself. It stopped on return. It did not start the shellcode entry point, resolve Windows APIs or invoke the embedded image.

Evidence E02. Arguments supplied to the instance decryptor

Offsets from the beginning of decoded shellcode. Comments annotate the selected instructions; this is a local excerpt rather than a full function listing.

00089456  mov     eax, dword ptr [edi]
00089458  sub     eax, 0x23c  ; exclude the unencrypted 0x23c-byte prefix
0008945d  push    eax  ; fourth argument: encrypted data length
0008945e  lea     eax, [edi + 0x23c]  ; third argument: encrypted data pointer
00089464  push    eax
00089465  lea     eax, [edi + 0x14]  ; second argument: counter
00089468  push    eax
00089469  lea     eax, [edi + 4]  ; first argument: key
0008946c  push    eax
0008946d  call    0x8aaa9  ; call the standalone decryptor at shellcode offset 0x8aaa9

5.3 Independent cipher reconstruction

The decryptor uses a 16-byte key and a 16-byte counter. It XORs four little-endian words of counter state with the key, applies a 16-round add/rotate/XOR permutation, XORs the key again and uses the resulting 16 bytes as a keystream block. The counter is incremented from its final byte toward its first, equivalent to a 128-bit big-endian increment.

The permutation follows the Chaskey-style round structure visible in the instructions:

v0 += v1;  v2 += v3
v1 = rol(v1, 5)  XOR v0
v3 = rol(v3, 8)  XOR v2
v0 = rol(v0, 16)
v2 += v1;  v0 += v3
v1 = rol(v1, 7)  XOR v2
v3 = rol(v3, 13) XOR v0
v2 = rol(v2, 16)

All additions are modulo 2^32. The sequence repeats sixteen times for each counter block. The independent Python reconstruction in verify_donut_cipher.py reproduces all 543,680 bytes of the emulator output, including the advanced counter. This is an independent check of the decryption logic, not merely another invocation of the same machine code.

Figure D09. Independent reconstruction of the Donut cipher

Independent reconstruction of the Donut cipher

The increment is equivalent to a 128-bit big-endian counter. The Python reconstruction matches all 543680 bytes of emulator output, including the advanced counter.

Evidence E03. The add-rotate-XOR permutation

Offsets from the beginning of decoded shellcode. Comments annotate the selected instructions; this is a local excerpt rather than a full function listing.

0008ab0d  mov     dword ptr [esp + 0x30], 0x10  ; round counter = 16
0008ab15  add     ecx, edi
0008ab17  add     eax, esi
0008ab19  rol     edi, 5  ; rotation constant 5
0008ab1c  xor     edi, ecx
0008ab1e  rol     esi, 8  ; rotation constant 8
0008ab21  xor     esi, eax
0008ab23  rol     ecx, 0x10  ; rotation constant 16
0008ab26  add     eax, edi
0008ab28  add     ecx, esi
0008ab2a  rol     edi, 7  ; rotation constant 7
0008ab2d  rol     esi, 0xd  ; rotation constant 13
0008ab30  xor     edi, eax
0008ab32  xor     esi, ecx
0008ab34  rol     eax, 0x10
0008ab37  sub     dword ptr [esp + 0x30], 1  ; decrement round counter
0008ab3c  jne     0x8ab15  ; repeat the round

Evidence E04. Counter carry propagates from the final byte

Offsets from the beginning of decoded shellcode. Comments annotate the selected instructions; this is a local excerpt rather than a full function listing.

0008ab84  mov     ebx, dword ptr [esp + 0x10]
0008ab88  sub     edx, ecx
0008ab8a  add     ebp, ecx
0008ab8c  mov     ecx, dword ptr [esp + 0x34]
0008ab90  add     byte ptr [ecx], 1  ; increment one counter byte
0008ab93  jne     0x8ab9d  ; stop carrying when the increment did not wrap
0008ab95  dec     ecx  ; otherwise move to the preceding byte
0008ab96  lea     eax, [ebx + ecx]
0008ab99  test    eax, eax
0008ab9b  jg      0x8ab90  ; continue carry propagation

Byte comparison B02. Instance bytes before and after decryption

Ciphertext instance, offset 0x240:

e7 f9 dc c3 ca 29 19 ce 28 04 18 91 6b 50 d3 43 df a0 a3 77 4c 9d 6a a1 c7 36 f7 8a d9 52 23 b4
.....)..(...kP.C...wL.j..6...R#.

Decrypted instance, offset 0x240:

6f 6c 65 33 32 3b 6f 6c 65 61 75 74 33 32 3b 77 69 6e 69 6e 65 74 3b 6d 73 63 6f 72 65 65 3b 73
ole32;oleaut32;wininet;mscoree;s

Both excerpts use instance-relative offset 0x240. The decrypted bytes begin the embedded DLL-name list.

5.4 Extracting the exact PE

Decryption exposes readable instance content such as ole32;oleaut32;wininet;mscoree;shell32. The embedded-module branch uses an instance type of 1 at +0x920. The module begins at +0xd60.

Within the module, the payload length at +0x524 is 0x83400, or 537,600 bytes. The PE begins at +0x528. Extracting exactly that range produces a valid Windows executable whose final raw section ends at the same length.

Extracting all bytes from the MZ header to the end of the instance would append 1,336 unrelated bytes. That produces a plausible-looking PE with the wrong hash. The module length and the section table independently establish the correct boundary.

Figure D10. Exact executable boundary inside the instance

Exact executable boundary inside the instance

The container length and raw-section end are independent boundary checks. A valid-looking MZ header alone does not justify copying every remaining instance byte.

Byte comparison B03. The PE header inside the encrypted instance

Ciphertext instance, offset 0x1288:

13 0b 6f 1a 39 d4 6a ae ee 10 85 91 63 26 44 88 e1 9f cb 7c 80 aa a3 2c 86 c4 c5 51 8b fd cf 2f
..o.9.j.....c&D....|...,...Q.../

Decrypted instance, offset 0x1288:

4d 5a 90 00 03 00 00 00 04 00 00 00 ff ff 00 00 b8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00
MZ......................@.......

0x1288 = module offset 0xd60 + PE data offset 0x528. The MZ header becomes visible after instance decryption.

6. The recovered Windows executable

6.1 Identity and layout

The final image is a PE32 x86 executable with image base 0x400000, entry RVA 0x3ebe4, GUI subsystem 2 and image size 589,824 bytes. Its recorded linker timestamp is 22 August 2026 at 13:35:27 UTC. The timestamp is attacker-controlled metadata and is not a deployment date.

The Remcos identity is supported by several strings in operational code paths, including Remcos Agent initialized (, Remcos restarted by watchdog!, Remcos v and the nearby version 7.3.1 Pro. The identification is also consistent with its configuration format and control architecture.

Section RVA Virtual size Raw offset Raw size
.text 0x1000 0x60697 0x400 395,264
.rdata 0x62000 0x1890c 0x60c00 100,864
.data 0x7b000 0xadfc 0x79600 3,584
.rsrc 0x86000 0x4ab0 0x7a400 19,456
.reloc 0x8b000 0x4298 0x7f000 17,408

The resources include four icon images, an icon group and the 1,186-byte SETTINGS object. There is no version-information resource supplying an independently authenticated product identity.

6.2 What the imports do and do not establish

The import table includes file/process APIs, keyboard and clipboard functions, GDI screen-capture operations, registry access, Windows audio capture, Winsock, WinINet and URL download functions. These make useful entry points for reverse engineering, but imports alone do not establish when a feature runs.

This report follows those entry points into handlers where possible. That distinction separates the evidence for a working code path from a broad family description copied from a product feature list.

7. Configuration and startup decisions

7.1 SETTINGS decryption

The substantive startup routine is 0x41426b. It records the module handle, resolves additional APIs, gets its executable path and obtains SETTINGS through 0x41fa84.

The resource format is simple:

one byte: key length
key-length bytes: RC4 key
remaining bytes: RC4 ciphertext

Here the key length is 23. The key is f6dc4c855207805cdd97676cd40fcaeb57f9e2e641ab09. The decrypting path at 0x414e65 initializes the RC4 helper and decrypts the remaining resource bytes. Splitting the plaintext on 7c 1e 1e 1f 7c gives 58 elements; element 57 is empty because of the trailing delimiter.

The configuration vector is stored at 0x47f0ac. The indexed accessor at 0x401e70 returns 24-byte string objects. The same accessor serves other vectors, including command arguments, so an immediate index is not sufficient to identify a configuration field. The object passed in ECX must be tracked as well.

Figure D11. SETTINGS resource and positional fields

SETTINGS resource and positional fields

The index count is 58 because the final split element is empty. Certificate and private-key fields are binary and must not be decoded as ordinary text.

Evidence E05. Locating the named configuration resource

Preferred image virtual addresses, with image base 0x400000. Comments annotate the selected instructions; this is a local excerpt rather than a full function listing.

0041fa84  push    ebx
0041fa85  push    edi
0041fa86  push    0xa  ; RT_RCDATA = 10
0041fa88  push    0x476754  ; pointer to ASCII SETTINGS
0041fa8d  push    dword ptr [0x47ce4c]  ; module handle retained by startup
0041fa93  mov     ebx, ecx
0041fa95  call    dword ptr [0x462178]  ; FindResourceA

Byte comparison B04. SETTINGS key prefix, ciphertext and recovered endpoint

Resource key-length byte and key, offset 0x0:

17 f6 dc 4c 85 52 07 80 5c dd 97 67 6c d4 0f ca eb 57 f9 e2 e6 41 ab 09
...L.R..\..gl....W...A..

Encrypted configuration prefix, offset 0x18:

61 d1 e9 2e e9 a9 22 1f 9a cf 87 7d 79 cb 0f 65 bb 8b cd c6 de 93 f9 d1 e2 6e 49 bc 91 d8 72 36
a....."....}y..e.........nI...r6

Decrypted configuration prefix, offset 0x0:

7a 78 61 73 77 65 64 72 79 79 68 66 72 64 73 77 61 61 61 61 61 61 63 76 62 6e 6d 6a 67 66 66 64
zxaswedryyhfrdswaaaaaacvbnmjgffd

The first resource byte is 0x17, or 23. Only the remaining ciphertext is decrypted; the private TLS key is not shown in this excerpt.

7.2 Identity, saved state and paths

Field 14 supplies the mutex chrome-B1MV8Z. The call at 0x413303 uses CreateMutexA and tests GetLastError against 183, ERROR_ALREADY_EXISTS. This confirms the single-instance role.

The core constructs registry state locations under Software\ using related identifiers, with suffixed forms including -Sys. Values observed in these paths include name, group, hlight, licence, exepath, okmode, elev, SysPw and SysPwR. Some are configuration state; others support coordination with feature or helper processes. Their existence in code does not establish that they were created on a victim.

Path selector 0x413d32 subtracts ASCII 0 and selects among nine roots. Selector 8 chooses ProgramData, falling back to ProgramFiles; 6 chooses AppData; 7 chooses UserProfile; 1 uses the current executable directory. The full selector table is retained in the configuration appendix.

The sample uses Chrome.exe and chrome as installation-related names. The keylogger is configured with remcos and logs.dat as path/name components and selector 8 as its root. Because installation is disabled in the embedded defaults, an installation filename should not be reported as a file known to have been written.

Evidence E06. The mutex consumer confirms field 14

Preferred image virtual addresses, with image base 0x400000. Comments annotate the selected instructions; this is a local excerpt rather than a full function listing.

00413303  mov     ecx, 0x47f5ec  ; string object populated from configuration field 14
00413308  call    0x401f97
0041330d  push    eax  ; lpName argument
0041330e  push    1
00413310  push    0
00413312  call    dword ptr [0x46228c]  ; CreateMutexA
00413318  mov     dword ptr [0x47ce48], eax
0041331d  call    dword ptr [0x462180]  ; GetLastError
00413323  xor     ecx, ecx
00413325  cmp     eax, 0xb7  ; ERROR_ALREADY_EXISTS = 183
0041332a  setne   cl  ; return false when another instance owns the name
0041332d  mov     eax, ecx
0041332f  ret     

7.3 Defaults that change the interpretation

Feature or setting Embedded value Consequence in the traced startup path
Remcos installation, field 3 raw 00 Core installation route is skipped
Offline logger, field 15 ASCII 0 No ordinary offline mode selected unless saved okmode overrides it
Screenshot gates, fields 20 and 22 raw 00 Automatic screenshot threads are skipped
Background microphone, field 35 raw 00 Automatic recording thread is skipped
Browser clearing, field 43 raw 00 Automatic clearing path is skipped
Watchdog, field 50 raw 00 Watchdog thread is not launched by this gate
Repair monitor, field 51 raw 00 Repair/reinstallation monitor is skipped
Console mode, field 40 ASCII 0 No automatic console display through this setting
Initial connection delay, field 41 ASCII 0 No configured initial sleep
Retry delay, field 2 ASCII 1 One-second delay after the endpoint list is exhausted

Fields 4 and 5 are enabled persistence selectors, but they do not override the separate installation gate. Field 44 is an enabled subordinate browser-clearing option, but field 43 still controls whether that startup path is entered.

The offline logger also reads a stored registry value named okmode. The code at 0x40bf07 can replace the embedded mode with that saved value before choosing a logging path. A recovered configuration therefore describes defaults, not a complete snapshot of an already-running implant's state.

Figure D12. Startup defaults versus later activation

Startup defaults versus later activation

This map does not include the Lua scheduled task, which is independent of the Remcos installation flag. A saved registry value can change the offline logger decision.

8. Controller communication

8.1 Endpoint selection and transport

The connection manager is 0x4190c4. It applies the optional initial delay, splits the configured endpoint list and parses the selected hostname, port and transport flag. It explicitly compares the third component against ASCII 0; zero chooses TLS off and any other value chooses TLS on.

The recovered record is:

zxaswedryyhfrdswaaaaaacvbnmjgffds.ydns.eu:20901:1

The 1 therefore requests TLS. This is confirmed by the comparison and the corresponding “TLS On” branch, rather than inferred from a generic TLS string elsewhere in the binary.

Socket creation at 0x404287 uses SOCK_STREAM and IPPROTO_TCP. WSAStartup requests 2.2. getaddrinfo supplies the address family and resolved endpoint; 0x404329 performs the connection and enters the TLS setup path when enabled.

On failure or disconnection the core closes the transport and advances to the next endpoint record. When it reaches the end of the list, it resets the index and sleeps for field 2 multiplied by 1,000. Only one endpoint record was recovered in this configuration.

The hostname was not resolved or contacted. Its current IP address, availability and ownership are outside the evidence.

Figure D13. Connection selection and retry control

Connection selection and retry control

Field 41 is zero and field 2 is one in this sample. This is a code-derived retry model; no DNS lookup, TCP connection or TLS session was performed.

Evidence E07. The endpoint flag selects TLS

Preferred image virtual addresses, with image base 0x400000. Comments annotate the selected instructions; this is a local excerpt rather than a full function listing.

0041925c  push    2  ; third parsed endpoint component, index 2
0041925e  lea     ecx, [ebp - 0xc]
00419261  call    0x401e70
00419266  mov     edx, 0x47075c  ; pointer to ASCII 0
0041926b  mov     ecx, eax
0041926d  call    0x405aba  ; string comparison
00419272  test    al, al
00419274  je      0x419284
00419276  mov     byte ptr [0x47cb8a], 0  ; zero flag selects TLS off
0041927d  mov     eax, 0x4761b8
00419282  jmp     0x419290
00419284  mov     byte ptr [0x47cb8a], 1  ; nonzero flag selects TLS on
0041928b  mov     eax, 0x4761b0

Evidence E08. Retry delay is seconds converted to milliseconds

Preferred image virtual addresses, with image base 0x400000. Comments annotate the selected instructions; this is a local excerpt rather than a full function listing.

00419d95  push    2  ; configuration field 2
00419d97  mov     ecx, edi
00419d99  xor     ebx, ebx
00419d9b  call    0x401e70
00419da0  mov     ecx, eax
00419da2  call    0x401f97
00419da7  push    eax
00419da8  call    0x445737  ; atoi-like conversion of the field string
00419dad  imul    eax, eax, 0x3e8  ; seconds to milliseconds
00419db3  pop     ecx
00419db4  push    eax
00419db5  call    dword ptr [0x4622c8]  ; Sleep; sample value 1 requests a 1000 ms sleep

8.2 Certificate material

The configuration contains a local certificate in field 54, an EC private key in field 55 and a separate certificate in field 56. The initialization routine at 0x404197 loads certificate, key and peer-certificate material through distinct functions. Associated error strings identify failures at those stages.

Independent parsing establishes:

Property Field 54, local certificate Field 56, peer certificate
Curve secp256r1 secp256r1
Subject Empty Empty
Issuer Empty Empty
Self-signature Verified Verified
Matches field 55 private key Yes No
Not before, UTC 2026-10-11 06:09:00 2026-10-11 06:08:59
Not after, UTC 2147-10-11 06:09:00 2147-10-11 06:08:59
SHA-256 1bb977b55395b73e5abd4d29c274b6d5e16f1d5e30ec04f270e6f01c96326fb5 9f14d9fc276cab30a47b557db642644a49c7faf58005f1a8c192e40173212455

The fields are consistent, parseable TLS material, and the local certificate/key relationship is cryptographically verified. Their dates are embedded values, not independent observations of campaign activity. The presence of a peer certificate does not, by itself, prove strict certificate pinning; the complete validation policy was not reconstructed.

The private key is preserved in the encrypted evidence archive. It is not reproduced in this report.

Figure D14. TLS object relationships and verification limits

TLS object relationships and verification limits

Both certificates use P-256 and empty subjects/issuers, with no DNS SAN names found. Correct key matching does not establish strict certificate pinning or a successful handshake.

8.3 Registration and application framing

After connection, the manager sends registration command 0x4b (75). The record includes the configured label, version, access level and host/user-related values. Several fields have been traced, but the complete registration schema is not presented as fully named.

The socket constructor at 0x404151 initializes DWORD 0x08ff0424. In little-endian representation this provides the three-byte marker 24 04 FF and an initial ordinary-header length of eight.

Offset Meaning
0-2 Marker 24 04 FF
3 Compression flag
4-7 Length of the transmitted body, little-endian uint32
8-11, compressed frames only Uncompressed body length, little-endian uint32
Body Four-byte command ID followed by command data, optionally compressed

The sender at 0x4045eb constructs the body and attempts compression for sufficiently large messages. It resolves RtlCompressBuffer dynamically and passes compression format 2, LZNT1. Failed or unsuitable compression falls back to plain framing. The inverse path uses RtlDecompressBuffer.

The receive loop at 0x40483a accumulates TCP bytes and tracks incomplete frames. 0x4048f8 extracts complete messages, decompresses them when needed, dispatches them and retains any remaining buffered data. The protocol is not dependent on recv calls returning one message at a time.

The header parser at 0x404b9e was checked independently with three synthetic inputs: a plain frame header, a compressed frame header and an invalid marker. The valid inputs returned the expected eight- and twelve-byte header lengths; the invalid marker was rejected. These checks confirm the header layout, not the behavior of every malformed frame or a working controller session.

Figure D15. Plain and compressed application-frame layouts

Plain and compressed application-frame layouts

The graph explains the alternatives rather than byte widths. The adjacent offset table is the authoritative byte layout. The isolated parser accepted both layouts and rejected an invalid marker.

Figure D16. TCP buffering and message dispatch

TCP buffering and message dispatch

The receiver handles split frames and multiple frames in one TCP buffer. Exact error/disconnect behavior depends on the caller; the diagram does not claim exhaustive malformed-input validation.

Evidence E14. Ordinary packet header versus compressed extension

Preferred image virtual addresses, with image base 0x400000. Comments annotate the selected instructions; this is a local excerpt rather than a full function listing.

00404be8  cmp     byte ptr [esi], 0  ; test compression flag copied from the header
00404beb  je      0x404c06
00404bed  push    4  ; compressed frames carry four more header bytes
00404bef  lea     eax, [edi + 8]  ; extra field begins at byte offset 8
00404bf2  push    eax
00404bf3  push    dword ptr [esp + 0x2c]
00404bf7  call    0x440c20
00404bfc  add     esp, 0xc
00404bff  push    0xc  ; compressed header size = 12
00404c01  pop     eax
00404c02  mov     byte ptr [ebx], al  ; store selected header size
00404c04  jmp     0x404c08
00404c06  mov     al, byte ptr [ebx]

8.4 Structured argument encoding

Many commands split their structured data on 7c 1e 1e 1f 7c. The stored reversed literal at 0x4703a0 is reconstructed by 0x4203b0 into the global separator object at 0x47f13c.

This delimiter is shared by several structured-data paths, including configuration processing. It should not be applied indiscriminately to every payload: file contents, image data and DLLs can contain arbitrary binary bytes and may use dedicated feature channels.

9. Command dispatch and feature channels

9.1 Primary dispatcher

The callback at 0x419dc8 reads the four-byte command ID, removes it from the message and prepares the remaining arguments. It subtracts one from the command value and bounds-checks the result against 0xd7, covering original IDs 1 through 216.

A byte translation table at 0x41b804 selects a target index. A DWORD table at 0x41b6b8 supplies the corresponding handler address. Eighty-two IDs choose active entries; the remaining 134 choose the default path at 0x41b689.

The number 216 is therefore a range bound, not a count of implemented features. Nor are 82 primary entries necessarily 82 independent capabilities: some start a secondary channel, some update state, and several implement closely related window or process operations.

The full primary map appears in Appendix A. The accompanying CSV preserves all 216 slots, including defaults. JSON records retain handler addresses, calls, string references and analyst labels.

Figure D17. Primary command translation table

Primary command translation table

82 is the number of active primary IDs. It is not the total number of nested commands or independent capabilities. Slot and handler inventories accompany the report.

Evidence E09. The two-stage primary command table

Preferred image virtual addresses, with image base 0x400000. Comments annotate the selected instructions; this is a local excerpt rather than a full function listing.

00419e2f  dec     esi  ; convert command IDs 1..216 into indices 0..215
00419e30  pop     ecx
00419e31  cmp     esi, 0xd7  ; upper bound is 215, not a feature count
00419e37  ja      0x41b689  ; out-of-range IDs select the default path
00419e3d  movzx   eax, byte ptr [esi + 0x41b804]  ; byte translation table
00419e44  xor     ebx, ebx
00419e46  jmp     dword ptr [eax*4 + 0x41b6b8]  ; indexed DWORD target table

9.2 Secondary namespaces

Feature channels reuse the common transport while defining their own message layouts and dispatchers. A primary command can therefore open a socket, send a feature identifier and transfer subsequent control to a different callback.

The additional work in this investigation maps routes in:

  • File management at 0x408fbd.
  • Registry operations at 0x41893d.
  • Camera control at 0x403e64.
  • Recovery coordination at 0x417afe.
  • Service control at 0x41e9d4.
  • Remote input at 0x41dc35.

The command namespace matters. Service commands use a leading byte and a small table; the screen-input callback uses ASCII operation characters. Those should not be merged with the four-byte primary IDs into one ambiguous command list.

9.3 Modules supplied after connection

The executable includes a manual PE loader and several module adapters. Camera, chat, tunnelling, recovery, service and startup-enumeration paths resolve named exports from received DLLs.

That design explains why a core executable can expose a feature without containing its full implementation. The adapter proves how the core expects to call the module. It does not recover a DLL that is absent from the submitted evidence. This distinction is particularly important for credential and cookie recovery.

Figure D18. Feature channels and shared module loader

Feature channels and shared module loader

These adapters establish module interfaces. They do not supply the internals of DLLs absent from the original evidence. The transport and loader are shared across several features.

10. Surveillance and remote interaction

10.1 Keyboard capture

The keyboard implementation can be followed from dispatch to Windows APIs. Primary commands 19 and 20 reach the online start/stop routines at 0x40cd00 and 0x40ce56. Commands 204 and 205 configure or disable offline mode and persist okmode.

The hook setup at 0x40c0b9 calls SetWindowsHookExA with hook ID 13, WH_KEYBOARD_LL, and callback 0x40c0a5. Processing reaches 0x40c16c, which separates key events and routes them to ordinary or special-key logic. ToUnicodeEx in 0x40c1e5 translates key information using keyboard-layout state.

Separate routines record modifiers and readable tokens including [Enter], [BckSp], [Ctrl+V], [Alt] and function-key names. The foreground-window monitor at 0x40cae3 polls window text and adds application context. Clipboard retrieval is integrated into relevant shortcut handling.

The result is not just a stream of scan codes. It is an attempt to create readable, contextual records of user input. Such records could contain passwords or other sensitive text if capture is enabled and the user types it, but this investigation does not contain captured victim input.

The offline writer at 0x40c51d handles buffered data and uses the RC4 helper family in stored-data paths. The limit is enforced by 0x40c46c, which compares GetFileSize against the configured threshold. Field 7 is multiplied by 1,000, making this sample's limit 100,000,000 bytes. It is a file-size setting, not a timing delay.

Online logging opens a separate channel, sends identifying command 176 and transmits buffered key data using command 90. Primary commands 21-23 read, decode or remove stored logs. The embedded mode is zero, but saved registry state or subsequent commands can change that state.

Figure D19. Keyboard capture, context and output paths

Keyboard capture, context and output paths

Hook setup is at 0x40c0b9; offline size checking is at 0x40c46c. Embedded mode zero does not automatically start ordinary offline logging, but saved okmode can override it.

Evidence E10. Installing the low-level keyboard hook

Preferred image virtual addresses, with image base 0x400000. Comments annotate the selected instructions; this is a local excerpt rather than a full function listing.

0040c0d4  push    edi
0040c0d5  push    edi
0040c0d6  call    dword ptr [0x4620a4]  ; GetModuleHandleA
0040c0dc  push    eax
0040c0dd  push    0x40c0a5  ; hook callback address
0040c0e2  push    0xd  ; WH_KEYBOARD_LL = 13
0040c0e4  call    dword ptr [0x462358]  ; SetWindowsHookExA
0040c0ea  mov     dword ptr [esi], eax  ; retain returned hook handle

10.2 Screen capture and control

Primary command 16 initializes capture through 0x41cd49 and 0x41cf69. The bitmap acquisition routine at 0x41d480 creates display and compatible device contexts, creates a bitmap, performs StretchBlt/BitBlt operations and retrieves pixels with GetDIBits. Cursor-related routines can add cursor information.

The frame-encoding path at 0x41d0bd selects image/jpeg through GDI+ and streams. Background capture paths use timestamped names such as wnd_YYYYMMDD_HHMMSS and time_YYYYMMDD_HHMMSS, and can write transformed image data to disk.

Remote-input callback 0x41dc35 reads a leading ASCII operation character. It routes screen-update requests, keyboard input, coordinate-bearing mouse operations and wheel events to the appropriate helpers. The wheel paths explicitly pass ±120 with mouse-event flag 0x800. Other input helpers use SendInput.

The implant can therefore interact with the desktop as well as observe it. The source also contains an attempt to alter PromptOnSecureDesktop under the Windows system-policy key. That is an attempted privileged policy change; its presence does not establish that the required access was available.

Figure D20. Screen observation and remote input

Screen observation and remote input

Frame acquisition and input injection are separate paths. The diagram shows how they support remote interaction, not an observed interactive session.

10.3 Audio capture and playback

Primary command 29 reaches 0x40167a, which opens and starts a wave-input device. Buffer setup uses waveInPrepareHeader and waveInAddBuffer; the stop/release path is selected by command 30. Background recording at 0x40181b is separately gated by field 35, which is off in this configuration.

Media playback is another feature. The helper at 0x41f18a uses mciSendStringW with open, play, pause, resume, status, stop and close requests. The alarm.wav string belongs to this workflow. It is not evidence that microphone recordings were collected or that an alarm was played on a victim.

10.4 Camera access

Primary command 27 establishes the camera feature channel and command 28 closes it. The secondary adapter receives a DLL and resolves OpenCamera, CloseCamera, GetFrame and FreeFrame.

Within that channel, command 60 loads the adapter module; 62 closes the camera; 63 opens it and continues toward frame acquisition; 64 opens it and enters a capture path with a delay. Command 61 reaches frame acquisition only while the camera-open state is set. The export names and call sites establish the interface, not the behavior of a missing camera DLL on a particular device.

11. Files, registry and host control

11.1 Command execution

Primary command 14 reaches the shell implementation at 0x4056da. It creates pipes, launches cmd.exe with CreateProcessA and handles redirected input/output using PeekNamedPipe, ReadFile and WriteFile. This gives the operator a command-execution path in the implant's current security context.

Commands 13 and 15 provide ShellExecute-based launch routes. The current privilege level remains important: a remote shell capability does not itself imply administrator or SYSTEM access.

11.2 File management and transfers

Primary command 152 establishes a file-management channel through 0x408de5, with callback 0x408fbd. The recovered secondary routes cover drive enumeration, directory browsing, execution, creation, renaming, deletion, wallpaper changes, attributes, archive operations and transfers.

Representative routes include:

Secondary ID Handler Observed role
135 0x40917f Enumerate logical drives
136 0x4090ec Browse directory contents
137 0x40906d Execute/open a selected file
138 0x40926c File-transfer helper; logs uploaded-file results through its callee
140 0x40968d Delete selected file/directory paths
141 0x4094a7 Rename selected path
142 0x4092f8 Create a directory
145 0x4097dc Set wallpaper through the file view
194 / 195 0x409896 / 0x40984d ZIP-module-backed archive operations
199 0x409bab Set file attributes
212 0x409c46 Receive a file through the dedicated transfer receiver
218 / 219 0x409c26 / 0x409c06 File-operation helpers; move/copy semantics not fully named here

The receiver at 0x408935 creates an output file and receives data on a dedicated socket. Error handling reports failures to create or receive the file. The upload-oriented helper 0x40a2c9 logs “Uploaded file” and “Failed to upload file” outcomes. Those labels are retained as the application's perspective rather than used to guess an unobserved traffic direction.

Primary command 143 starts a separate search/enumeration path; command 144 cancels it. Command 178 coordinates an additional download/write/execute workflow. These routes expand the operator's ability to obtain, replace or stage files after the initial implant is present.

Figure D21. File-management channel and transfer feedback

File-management channel and transfer feedback

The primary channel is a bootstrap. Secondary IDs 135, 136, 137, 140, 141, 142 and 212 map to drives, browsing, execution, deletion, renaming, directory creation and receiving a file respectively.

11.3 Registry and startup entries

Primary command 47 establishes the registry-management channel. Its secondary dispatcher at 0x41893d handles IDs 53-57:

Secondary ID Observed role
53 Enumerate a registry location through the shared helper
54 Delete a registry value through the root-resolution/value-delete path
55 Delete a key using SHDeleteKeyW loaded from Shlwapi.dll
56 Write a value through the registry write helper
57 Create a key with RegCreateKeyExW

The code refreshes views and reports results around these operations. Root resolution and value-type handling are separate helpers. Startup enumeration is modular, while primary commands 215 and 216 modify startup/registry entries and request updated data.

11.4 Process, window and power control

Process enumeration uses CreateToolhelp32Snapshot, Process32FirstW and Process32NextW through 0x4152e7. Command 7 reaches OpenProcess and TerminateProcess. Commands 200 and 201 resolve to NtSuspendProcess and NtResumeProcess through their stored API pointers.

Window commands enumerate, minimize, maximize, restore, hide or rename windows, and can terminate the process owning a selected window. Clipboard commands read text, write content or clear the clipboard. These are implemented control paths, not merely string indicators.

Power-control command 39 attempts to enable SeShutdownPrivilege, then selects ExitWindowsEx or a dynamically resolved SetSuspendState path. The code supports logoff, shutdown/restart and suspend/hibernate-style requests, subject to normal privilege and platform constraints.

11.5 Host discovery and external lookups

The core collects Windows version/build and architecture, user/access level, processes, windows and installed-software information. Command 206 reaches a feature that uses GetExtendedTcpTable and GetExtendedUdpTable, including IPv4/IPv6 variants, to enumerate network connections.

Command 151 starts dxdiag, reads a temporary sysinfo.txt and deletes it. Command 17 retrieves an IP-geolocation service URL:

hxxps://pro.ip-api[.]com/line/?key=QPVvv1rHQJD2pd2&fields=25948155

That embedded URL is a separate service, not the configured controller. It was not requested during analysis. Its presence is not evidence of ownership or abuse of the service by the sample's operator.

12. Browser data and recovery modules

12.1 Clearing browser state

The browser-clearing path is a concrete built-in capability. Primary command 24 starts 0x41230a, which launches worker 0x41201a. The worker processes Chromium-family Login Data/StoredLogins and Cookies/Network\Cookies paths, along with Firefox profiles containing cookies.sqlite, logins.json and key3.db.

The covered path families include Chrome, Edge, Brave, Yandex, Opera, Opera GX and Firefox. Firefox-specific routines call DeleteFileW directly. Chromium routines traverse the corresponding profile/data paths through their helper chain. A completion message states “Cleared browsers logins and cookies.”

The code supports an attempt to remove saved login and session data. It does not show that this clearing routine exfiltrates credentials. A hypothesis that clearing forces the user to reauthenticate would require additional evidence about operator intent and execution.

The automatic startup path is disabled by field 43. Field 44 is subordinate to that gate, and field 45 supplies a repeat interval converted from minutes. The feature remains available to an operator-issued command.

12.2 Recovery through received code

Cookie recovery is a separate workflow. Primary command 198 reaches 0x406fa6; its module setup at 0x40727c resolves RecoverCookies and FreeData. Related orchestration includes CooLib staging, browser executable discovery and a werfault.exe helper path.

Other adapters resolve recovery_run_str and recovery_free_str. Recovery coordination uses stored values such as SysPw and SysPwR. The orchestration and module interface are present in the core; the complete received DLL implementations are not established as present in the submitted evidence.

This is the boundary of the current recovery analysis. The core tells us how recovery is requested and consumed. It does not justify inventing a browser-password decryption algorithm for a DLL that has not been recovered.

Figure D22. Browser clearing versus module-based recovery

Browser clearing versus module-based recovery

The built-in clearing worker is not evidence of exfiltration. Module interfaces indicate recovery orchestration, while the missing DLL implementation remains a separate analytical target.

13. In-memory modules, hollowing and elevation

13.1 Manual module loading

Primary commands 43 and 44 accept executable material and reach 0x416d6d / 0x416d79. The loader checks MZ/PE signatures and machine 0x14c, validates the image layout, allocates space, handles sections, relocations and imports, sets protections and performs initialization. Export lookup is at 0x416fed; cleanup is at 0x417122.

Adapters use that loader to obtain feature exports:

Feature Expected exports
Camera OpenCamera, CloseCamera, GetFrame, FreeFrame
Chat DisplayMessage, GetMessage, FreeMessage, CloseChat
Tunnelling StartForward, StartReverse, StopForward, StopReverse, GetDirectListeningPort
Services GetSvc, StartSvc, StopSvc, PauseSvc, ResumeSvc, RestartSvc, SetSvcStartup
Startup inventory GetStartups
Recovery recovery_run_str, recovery_free_str
Cookie recovery RecoverCookies, FreeData
Miscellaneous control FunFunc

The service adapter provides a useful example of the distinction between interface and implementation. Its channel dispatches eight leading-byte operations: module setup, enumeration, start, stop, pause, resume, restart and startup-mode changes. The core resolves the required exports and then calls the stored pointers. The service-management DLL itself is a separate analytical target when available.

Figure D23. Manual PE loader control flow

Manual PE loader control flow

The loader is at 0x416d79, export resolution at 0x416fed and cleanup at 0x417122. The presence of the loader does not mean every future module has been recovered.

Evidence E11. A PE loader, rather than only suggestive strings

Preferred image virtual addresses, with image base 0x400000. Comments annotate the selected instructions; this is a local excerpt rather than a full function listing.

00416da0  mov     eax, 0x5a4d  ; MZ signature
00416da5  cmp     word ptr [ebp], ax  ; read signature from incoming bytes
00416da9  jne     0x416fc4
00416daf  mov     edx, dword ptr [ebp + 0x3c]
00416db2  mov     ecx, ebx
00416db4  add     edx, 0xf8
00416dba  call    0x416819
00416dbf  test    eax, eax
00416dc1  je      0x416fcf
00416dc7  mov     ebx, dword ptr [ebp + 0x3c]
00416dca  add     ebx, ebp
00416dcc  cmp     dword ptr [ebx], 0x4550  ; PE signature at the parsed header
00416dd2  jne     0x416fc4
00416dd8  mov     eax, 0x14c  ; IMAGE_FILE_MACHINE_I386 = 0x14c
00416ddd  cmp     word ptr [ebx + 4], ax  ; reject the wrong machine type
00416de1  jne     0x416fc4

13.2 Process hollowing

A separate native implementation exists at 0x41c6f4, reached through helper 0x412b3b. Unlike the Lua's manufactured API-name buffers, this routine contains an actual process-replacement sequence.

It resolves ZwCreateSection, ZwMapViewOfSection, ZwUnmapViewOfSection and ZwClose. It checks the incoming image, creates a process suspended, obtains the initial thread context and reads the remote image-base location. It then creates/maps a section, copies headers and sections, applies supported relocation fixups, updates the image-base pointer with WriteProcessMemory, adjusts the thread context and calls ResumeThread.

Failure handling includes unmapping/freeing state and terminating the newly created process. The implementation is therefore more specific than a generic “injection” label: it is a section-backed hollowing/replacement path. No instance of that path was executed during this investigation.

Figure D24. Section-backed process hollowing path

Section-backed process hollowing path

Based on routine 0x41c6f4 and its failure paths. This is genuine implementation evidence in the Remcos core, distinct from API names planted as decoys in the Lua source.

Evidence E12. Creating the replacement process suspended

Preferred image virtual addresses, with image base 0x400000. Comments annotate the selected instructions; this is a local excerpt rather than a full function listing.

0041c80b  push    4  ; dwCreationFlags = CREATE_SUSPENDED, 4
0041c80d  push    ebx
0041c80e  push    ebx
0041c80f  push    ebx
0041c810  push    dword ptr [esp + 0x4c]
0041c814  push    dword ptr [esp + 0x54]
0041c818  call    dword ptr [0x462144]  ; CreateProcessW; actual process-creation call in hollowing routine
0041c81e  test    eax, eax

Evidence E13. Redirecting and resuming the initial thread

Preferred image virtual addresses, with image base 0x400000. Comments annotate the selected instructions; this is a local excerpt rather than a full function listing.

0041ca0e  mov     eax, dword ptr [esi + 0x28]  ; incoming image entry RVA
0041ca11  add     eax, ecx  ; combine entry RVA with the mapped image base
0041ca13  push    ebp
0041ca14  mov     dword ptr [ebp + 0xb0], eax  ; update initial thread context
0041ca1a  push    dword ptr [edi + 4]
0041ca1d  call    dword ptr [0x462148]  ; SetThreadContext; install the modified context
0041ca23  test    eax, eax
0041ca25  je      0x41ca43
0041ca27  push    dword ptr [edi + 4]
0041ca2a  call    dword ptr [0x462138]  ; ResumeThread; resume the primary thread
0041ca30  cmp     eax, -1
0041ca33  je      0x41ca43  ; failure path if resume returns -1

The core contains an elevation moniker at 0x408493, combining Elevation:Administrator!new: with CLSID {3E5FC7F9-9A51-4367-9063-A120244FBEC7} and passing it to CoGetObject. Other helpers manipulate current-process identity/PEB-related information and use an elev registry flag for relaunch coordination.

These establish an elevation-related attempt. They do not demonstrate that the technique succeeds on any given Windows release, security configuration or user account. The report deliberately stops short of describing the sample as invariably elevated.

14. Restart, update, watchdog and removal

The core supports its own lifecycle operations in addition to the Lua's recurring task.

  • Command 33 exits through 0x415501.
  • Command 34 invokes uninstall helper 0x413330, which removes persistence state and builds a script to retry deletion of the current file/directory.
  • Command 35 restarts through a generated VBS helper.
  • Command 36 uses URLDownloadToFileW and update helper 0x4136ee to replace the current image and launch the replacement through a script.
  • Primary command 37 opens a secondary update-related channel; its callback can accept replacement data and pass it into the same update lifecycle.

The watchdog thread at 0x4171a8 uses PID/mutex state and launch helpers, with messages for watchdog activation, failure and restart. A separate repair/reinstallation monitor exists at 0x417804. Both are gated off in the embedded defaults.

Uninstall behavior should not be treated as proof that all artifacts would disappear. The core and the outer Lua establish different mechanisms. Removing the core's Run-key state does not, on the evidence available, prove removal of the Lua wrapper's scheduled task or its dropped interpreter and script.

Figure D25. Core lifecycle operations and wrapper boundary

Core lifecycle operations and wrapper boundary

The core uninstall helper is not proof that the outer Lua task or all dropped files are removed. The two persistence layers must be assessed independently.

15. Forensic interpretation and detection

15.1 What to look for in an affected environment

The strongest evidence is a combination of content identity, execution relationships and configuration-specific artifacts. A lone generic filename such as Chrome.exe or logs.dat is weak evidence on its own.

Observable Expected relationship Confidence and qualification
JScript hash Original delivery script Exact sample identity
LuaJIT and Lua hashes Dropped interpreter and companion Exact recovered objects
Task ZGNICVGKWHUBRJMP Ten-minute task pointing to the VBS launcher Concrete source-defined attempt; creation not observed
PMJRBUHWKGVCINGZ.vbs Starts the interpreter and Lua companion Source-defined launcher
Mutex chrome-B1MV8Z Single-instance test Confirmed consumer in core
Related Software\ registry state Client state, saved mode and helper coordination Exact full path/root depends on the traced branch
Controller hostname and TCP port 20901 TLS-enabled configured endpoint Configuration only; no resolution or connection observed
Certificate fingerprints Local/peer certificate material Exact recovered DER objects
Packet marker 24 04 FF Application framing Normally inside TLS for this configuration
ProgramData-related remcos/logs.dat components Offline log path construction Logger disabled by default; saved state can override

Content hashes will change if an operator rebuilds or repackages the sample. The command framing and module interfaces are broader indicators, but they need context. The packet marker alone is not sufficient for attribution, and a network sensor cannot assume it sees plaintext application framing inside TLS.

15.2 Interpreting possible telemetry

If the chain ran, useful corroboration would include script-host execution, creation of the interpreter and companion, task-registration activity, VBS execution, LuaJIT launching with the extensionless argument, DNS resolution and outbound TCP/TLS communication. Memory inspection could reveal the decoded shellcode, the loaded Remcos image, or received modules depending on the capture point.

Those are evidence to seek, not events reconstructed from this file alone. There is no basis here to assign a real compromised account, establish dwell time, count successful commands or identify stolen data.

15.3 Attribution boundaries

Remcos identification is strong. Operator attribution is not. The group label GP, licence string, dynamic-DNS hostname, certificate dates and selected filenames are useful pivots, but none identifies a human operator by itself. The hostname's provider should not be confused with the operator or assumed to endorse the activity.

No infrastructure enrichment was performed. Public MITRE documentation was consulted for technique names only. This report does not claim a malware-distribution URL, associated campaign, historical IP address or relationship to another investigation merely because similar tooling is involved.

15.4 File rules and measured coverage

Four YARA rules accompany this report in detections/remcos_layers.yar. The rules deliberately separate a candidate Remcos string cluster from exact instance and wrapper indicators. A rule name is a statement of scope, not a claim of family-wide sensitivity.

Rule Basis Intended scope Observed positive
Remcos_Core_String_Cluster Valid PE structure, size limit and at least three of four meaningful core strings Family-oriented candidate; related variants untested Recovered Remcos PE
Remcos_Settings_Instance_79179155 SETTINGS key prefix, identity strings and PE header This configuration instance Recovered Remcos PE
Lua_Loader_ZGNICV_Combination Decoder identifiers, exception/protection API names and embedded-data variable This Lua wrapper Original recovered Lua
JScript_Dropper_ZGNICVGK_Combination Drop-name prefix and ActiveX, WScript and file-save strings This outer script Original JScript

All four rules compile under yara-python 4.5.4. The core PE matches two rules, the Lua matches its wrapper rule and the JScript matches its delivery rule. The LuaJIT host matches none, which is a necessary negative control: a legitimate interpreter is not malicious merely because this delivery chain ships it. The decoded shellcode also matches none. These are file-layer rules, with an explicit gap at that intermediate stage.

The negative-control corpus contains 526 distinct local installed files, deduplicated by SHA-256: 16 Windows PE files, 150 ELF files, 180 Python files and 180 JavaScript files. There were zero observed matches. The full path, size, category and hash manifest is retained in detections/yara_test_results.json. These files are assumed benign from their installed runtime/system provenance; there is no independent clean-file attestation. The corpus is small and strongly biased toward this analysis environment, particularly the Windows PE subset. Zero observed matches is not an estimated production false-positive rate.

No related malware variants were available or tested. The candidate core rule therefore needs independent related-variant tests before it can be described as a reliable family detector. Wrapper names, the resource key and the embedded host can change. Next-stage validation should use a separate clean Windows corpus, unrelated malware, multiple independently acquired Remcos versions and differently wrapped samples, recording hashes and expected results before changing the signatures. Keep evaluation samples separate from samples used to tune the rules.

Figure D28. Detection testing and coverage boundaries

Detection testing and coverage boundaries

The accompanying test results separate actual sample/benign tests from missing related-variant and Windows telemetry coverage. Synthetic fixtures validate predicate logic, not a deployed SIEM integration.

15.5 Behavioral logic and correlation

Two experimental Sigma-format process-creation templates accompany the file signatures. rapid_vbs_task.yml looks for schtasks.exe with /create, /sc minute, /mo 10 and wscript.exe in the command line. This translates the source's ten-minute VBS task into a triage predicate. It does not establish that a task was created successfully. A legitimate administrator can produce the same command shape.

sample_interpreter_launch.yml requires the recovered host hash, an image path under Documents and the Lua companion identifier in the command line. Requiring the conjunction avoids treating the host hash as malicious on its own. It is intentionally narrow: missing image hashes, renamed companions and execution outside Documents create missed detections. A legitimate analyst reproducing the sample-shaped launch can also match it.

The YAML parses, and a small reference evaluator passes nine synthetic predicate fixtures. One deliberately benign administration fixture matches the task predicate, documenting a collision instead of hiding it. Other fixtures demonstrate the missing-hash and alternate-directory gaps. Neither template has been compiled through a Sigma backend or tested against actual Windows event logs. The reference evaluator supports only the modifiers used here; it is not a replacement for a Sigma implementation. Results are retained in detections/behavior_test_results.json.

For operational correlation, join the candidate interpreter launch and task command to file-creation records for the companion and VBS launcher on the same host and user. Use process ancestry or a process GUID when available; apply a bounded time window and corroborate the registered task action with task telemetry. A ten-minute grouping window is a starting hypothesis to tune against real data, not a measured property of this execution. Increase confidence when several independent artifacts agree. Compare the task against authorized automation, software inventories and the analyst's own activity before escalation. The logic requires process command lines and, for the narrow interpreter rule, SHA-256 image hashes.

The configured hostname and port are useful retrospective pivots, but no connection was observed. TLS encrypts the application framing during the configured transport path. The 24 04 ff application marker should therefore not be promoted into a plaintext network signature for ordinary encrypted traffic. A sensor would need decrypted traffic or endpoint instrumentation to inspect that application header.

Figure D27. Behavioral correlation for a defender

Behavioral correlation for a defender

This is a proposed correlation model, not telemetry observed in this case. A single interpreter, VBS script or scheduled task is not enough to identify this infection.

15.6 ATT&CK mapping with evidence boundaries

The following mappings use the official technique pages linked in the table, consulted on 23 October 2026. They identify implementations or attempted behavior in the recovered code. They do not turn static capabilities into observed victim activity, and tactic assignments are intentionally omitted.

Technique Sample-specific evidence Evidential status
T1053.005, Scheduled Task Lua builds task ZGNICVGKWHUBRJMP with a ten-minute VBS action, section 4.2 Source-level persistence attempt; task registration unobserved
T1140, Deobfuscate/Decode Files or Information Lua decoding, Donut decryptor at shellcode offset 0x8aaa9, SETTINGS consumer 0x41fa84 Decoded artifacts and selected decryption independently reproduced
T1620, Reflective Code Loading Manual PE loader 0x416d79, export resolver 0x416fed Recovered core implementation; optional DLL execution unobserved
T1055.012, Process Hollowing 0x41c6f4 creates a suspended process, maps image sections and changes thread state Recovered core implementation; successful hollowing unobserved
T1056.001, Keylogging Hook setup 0x40c0b9, callback 0x40c0a5, WH_KEYBOARD_LL and ToUnicodeEx Recovered capability; ordinary offline startup mode disabled unless overridden
T1113, Screen Capture Acquisition 0x41d480, JPEG path 0x41d0bd Recovered capability; automatic screenshot defaults disabled
T1123, Audio Capture Microphone commands 29/30 and waveIn path Recovered capability; background recording default disabled
T1115, Clipboard Data Primary command 40 opens the clipboard and returns its text, section 9 and Appendix A Recovered command path; collection unobserved

15.7 Windows validation still required

The figures explain code-derived behavior and isolated tests. They cannot resolve whether all stages cooperate under Windows. A full execution test was not performed, and the environment used here did not provide a Windows guest. In particular, the possible incompatibilities in the broad native patches remain material.

Integration question Evidence needed in an isolated Windows test Present status
Do the native patches permit continued execution? Original and patched bytes, target export resolution, page-protection results and the first failing instruction if a fault occurs Unvalidated
Does persistence register and launch correctly? Requested command, exit status, registered task action/principal, launcher contents and a subsequent task-triggered process event Source recovered; runtime unvalidated
Does page protection and the VEH cooperate? Handler registration, fault address, exception code, protection transition and resumed instruction pointer Source recovered; runtime unvalidated
Does the implant initialize after the wrapper? Entry transfer, module initialization, mutex creation and connection-attempt telemetry PE recovered; full-chain runtime unvalidated
Are the detection templates useful in practice? Captured process/task/file events, backend-converted queries and approved-administration controls Synthetic predicate fixtures only

A future test should preserve the original hash and use a disposable Windows guest with controlled connectivity and no access to the real controller. Separate evidence from the unmodified chain from any later run that changes a patch or configuration. Capture the failure first; a modified run cannot establish that the submitted sample works unchanged. Record the Windows build, architecture, launch context and observation settings with the resulting traces. Until those observations exist, the report supports payload recovery and substantial implementation analysis, with the execution outcome unresolved.

16. Verification and remaining limits

The central extraction result is supported by several independent checks rather than one successful-looking decode:

Check Result What it establishes
Embedded-object capture Reproduced Lua and host hashes Outer decoding yields stable objects
Lua analytical copy Syntax compilation succeeds after removing 62 repeated blocks The selected source remains syntactically coherent
Donut machine-code decryptor Isolated emulation completes The identified routine decrypts the instance
Independent Python cipher All 543,680 bytes match emulator output Cipher reconstruction independently reproduces decryption
Exact PE boundary Module length equals raw-section end Final executable is not padded with instance residue
SETTINGS RC4 decryption yields coherent positional data Configuration recovery is structurally consistent
Certificates Both self-signatures valid; local key matches field 54 Embedded TLS objects are internally consistent
Primary dispatcher 82 active / 134 default entries Command-table counts follow the binary
Header routine Plain/compressed accepted; invalid marker rejected Header fields and lengths match the code
Secondary routes Comparison/table routes extracted without running handlers Additional namespaces are documented

The full Windows chain was not launched. No controller or sample-referenced geolocation service was contacted. Public MITRE documentation was consulted only to name the techniques in section 15.6. No feature command was issued. No successful persistence, elevation, capture, hollowing or exfiltration event is claimed.

Several areas remain bounded: the full certificate-validation policy, every registration field, every secondary command's detailed argument schema, some configuration semantics and the internals of controller-supplied modules absent from the evidence. Compiler/runtime and TLS-library functions have not all been assigned semantic names. Candidate function boundaries in the generated disassembly can include internal targets and should be checked before using them as a formal call graph.

The report nevertheless reaches the core result decisively: the submitted script carries a recoverable Remcos executable, complete controller configuration and extensive remote-control code. The remaining uncertainty concerns runtime success and specific optional implementations, not the identity of the payload or the recovery of its bytes.

Figure D26. What each verification layer actually establishes

What each verification layer actually establishes

The evidence establishes extraction and selected code behavior. It does not establish that native patches, exception handling, task creation and implant startup work together on Windows.

Appendix A. Complete primary command map

These are active entries in this recovered primary dispatcher, not a universal Remcos protocol specification. Handler addresses are preferred VAs. Some labels describe an adapter or workflow because the implementing module is supplied separately. The 134 remaining IDs in 1-216 choose the default path at 0x41b689; command_map.csv preserves every slot.

ID Handler VA Observed behavior
1 0x419e4d Status/keepalive reply and connection timing controls
2 0x41a024 Connection timeout/control reset
3 0x41b12d Enumerate installed software
6 0x41a269 Enumerate processes
7 0x41a2c7 Terminate process
8 0x41a3bc Enumerate windows
9 0x41a336 Minimize window (CloseWindow)
10 0x41a35b Maximize window
11 0x41a382 Restore window
12 0x41a38e Terminate process owning window, then refresh windows
13 0x41a3fb Launch cmd.exe through ShellExecute
14 0x41a455 Remote command shell with pipes
15 0x41a499 Open/execute supplied path through ShellExecuteW
16 0x41a4c2 Screen capture / remote desktop session
17 0x419fd2 Fetch IP-geolocation service output
18 0x41a5dd Keylogger configuration/status query
19 0x41a604 Start online keylogger
20 0x41a613 Stop online keylogger
21 0x41a734 Read keylogger logs
22 0x41a759 Retrieve/decode selected keylogger logs
23 0x41a7a4 Delete keylogger logs and refresh log view
24 0x41b238 Clear browser logins and cookies
27 0x41a7d6 Camera plugin channel
28 0x41a88a Stop camera channel and close plugin camera
29 0x41a8a5 Start microphone capture
30 0x41a927 Stop microphone object / recording
31 0x41a931 Password-recovery plugin channel and system-password helper
32 0x41a996 Delete supplied file
33 0x41a9b4 Exit Remcos
34 0x41a9c6 Uninstall and remove persistence
35 0x41b63f Restart Remcos through a VBS helper
36 0x41a9e0 Download replacement executable and update
37 0x41aa66 Secondary update channel; replacement-data callback reaches update helper
38 0x41aac7 Display message box
39 0x41ad75 Log off / shutdown / restart / suspend / hibernate controls
40 0x41af1c Read clipboard text
41 0x41ae82 Set clipboard content
42 0x41af01 Clear clipboard
43 0x41af74 Load PE module from received bytes in memory
44 0x41afa8 Decode supplied binary then load PE module in memory
45 0x41aff9 FunFunc plugin channel
46 0x41b003 Receive staged file and execute through ShellExecuteExA
47 0x41b025 Registry editor channel
48 0x41b047 Chat plugin channel
49 0x41b069 Change stored client display name
50 0x41b10b Forward/reverse tunnel plugin channel
52 0x41b157 Windows services plugin channel
56 0x41b2a7 Registry value write helper
104 0x41ab53 Startup enumeration plugin
143 0x41a04f File search/enumeration with command arguments
144 0x41a15a Cancel file search
146 0x41a17f Set desktop wallpaper
148 0x41a3c6 Set window title
149 0x41b179 System information channel
151 0x41b1c0 Generate dxdiag system report and return it
152 0x41a02d File manager / file transfer channel
153 0x41a7b4 Screenshot archive retrieval channel
154 0x41a974 Additional recovery-plugin channel
158 0x41b5ce Start/play audio-file path
159 0x41b5d8 Request audio-file workflow
160 0x41b5e2 Disable deferred audio-file flag
162 0x41b59c Configure audio-file input and optional immediate playback
163 0x41b1e2 Audio-control secondary channel
171 0x41b5ec Set elevation request flag for relaunch
172 0x41b1ec Show/create Remcos console
173 0x41a386 Show window normally
174 0x41a38a Hide window
178 0x41a1f8 Download/write/execute workflow on a secondary channel
197 0x41b0c7 Set stored highlight value
198 0x41b646 Cookie-recovery plugin workflow (RecoverCookies / FreeData)
200 0x41a2ec Suspend process via NtSuspendProcess
201 0x41a311 Resume process via NtResumeProcess
202 0x41b42d Read file and return binary data
203 0x41b4d9 Write supplied file and return file data
204 0x41a622 Enable/configure offline keylogger and persist okmode
205 0x41a700 Disable offline keylogger and persist okmode
206 0x41b252 Enumerate TCP/UDP connections over secondary channel
210 0x41b555 Set stored client group
211 0x41b669 Elevation/relaunch helper workflow
214 0x41abbc Receive/load startup enumeration plugin
215 0x41abdf Delete startup/registry entry and refresh enumeration
216 0x41aca7 Set startup/registry entry and refresh enumeration

Appendix B. Configuration consumers and complete raw field inventory

B.1 Reviewed consumers

The table below names consumers established in code. Raw NUL, ASCII zero and a decimal selector are different encodings and are retained as such.

Field Value Code-backed meaning and relevant address
0 hostname:20901:1 Endpoint list, split into records and host/port/TLS parts at 0x4190c4
1 GP Label used in initial host registration, 0x4194cb onward
2 1 Reconnect interval in seconds; atoi × 1000 then Sleep at 0x419d95-0x419db5
3 raw NUL Gate for Remcos installation behavior; read at 0x414337 and 0x4146f0. Off here
4 raw 01 First Run-key persistence selector, copied at 0x4146b5; consumed by 0x412f69
5 raw 01 Second Run-key persistence selector, copied at 0x4146cf; consumed by 0x412f69
7 100000 Keylogger log-size limit, converted to 100,000,000 bytes, not a delay. Setup 0x40bc7d and GetFileSize comparison at 0x40c4b1-0x40c4c7
8 raw NUL Additional Explorer Run-key selector, copied at 0x4146e9 and consumed by 0x412f69
9 8 Installation root selector. ASCII 8 selects ProgramData, falling back to ProgramFiles
10 Chrome.exe Installation executable name, passed into installer 0x413093
14 chrome-B1MV8Z Confirmed single-instance mutex name
15 0 Default offline-keylogger mode. Mode 1 starts ordinary offline logging; mode 2 selects another logging path. Registry okmode may override it in 0x40bf07
16 8 Log root selector: ProgramData / fallback ProgramFiles
17 logs.dat Keylogger log filename
20 raw NUL Gate for a screenshot thread path, 0x414af9
22 raw NUL Gate for a second screenshot thread path, 0x414b4c
35 raw NUL Gate for background microphone recording, 0x414b9b
37 1 Recording path selector used only if the microphone branch is enabled
38 MicRecords Recording path/name component
40 0 Console/debug-output mode; zero skips automatic console display
41 0 Initial connection delay in seconds, multiplied by 1000 at 0x419111
42 two NUL bytes Additional offline-keylogger argument, passed at 0x414ac6; exact filter semantics not fully traced
43 raw NUL Gate for automatic browser-login/cookie clearing, 0x414c0e
44 raw 01 Subordinate option for browser clearing, consumed only if field 43 enables that path
45 0 Browser-clearing repeat interval, converted to minutes × 60,000 in 0x41230a
46 raw NUL Gate for an elevation-related startup attempt, 0x41465c
48 chrome Installation subdirectory/name component
49 remcos Keylogger directory/name component
50 raw NUL Watchdog gate, copied to 0x47ce66 and checked before starting 0x4171a8
51 raw NUL Repair/reinstallation-monitor gate, copied to 0x47ce67 and checked before starting 0x417804
52 594F6AE6F44FEFCA561F7CA0A8B7CC0C Stored as licence at 0x414949-0x414972
53 raw NUL Screenshot-thread option read alongside fields 20 and 22
54 DER certificate Local TLS certificate, passed into 0x404197
55 DER EC private key Local TLS key, passed into 0x404197
56 DER certificate Peer certificate, passed into 0x404197

B.2 All recovered positional elements

The table retains unknown fields rather than assigning meanings from an unverified external schema. Binary certificate/private-key material is represented by its type and size; the exact bytes remain in the encrypted evidence bundle. The final empty element is a split artifact, not a meaningful enabled setting.

Index Length Recovered value
0 50 b'zxaswedryyhfrdswaaaaaacvbnmjgffds.ydns.eu:20901:1\x1e'
1 2 GP
2 1 1
3 1 b'\x00'
4 1 b'\x01'
5 1 b'\x01'
6 1 1
7 6 100000
8 1 b'\x00'
9 1 8
10 22 Chrome.exe (UTF-16LE)
11 1 b'\x00'
12 1 b'\x00'
13 1 0
14 13 chrome-B1MV8Z
15 1 0
16 1 8
17 18 logs.dat (UTF-16LE)
18 1 b'\x00'
19 1 b'\x00'
20 1 b'\x00'
21 2 10
22 1 b'\x00'
23 2 b'\x00\x00'
24 1 5
25 1 6
26 11 Screenshots
27 1 b'\x00'
28 1 b'\x00'
29 1 b'\x00'
30 1 b'\x00'
31 1 b'\x00'
32 1 b'\x00'
33 1 b'\x00'
34 1 b'\x00'
35 1 b'\x00'
36 1 5
37 1 1
38 10 MicRecords
39 1 b'\x00'
40 1 0
41 1 0
42 2 b'\x00\x00'
43 1 b'\x00'
44 1 b'\x01'
45 1 0
46 1 b'\x00'
47 1 1
48 14 chrome (UTF-16LE)
49 14 remcos (UTF-16LE)
50 1 b'\x00'
51 1 b'\x00'
52 32 594F6AE6F44FEFCA561F7CA0A8B7CC0C
53 1 b'\x00'
54 258 DER local certificate
55 121 DER EC private key, matches field 54
56 260 DER peer certificate
57 0 Empty trailing element

B.3 Path selectors

ASCII selector Root
0 Temp environment directory
1 Current executable directory
2 SystemDrive
3 WinDir
4 Windows system directory, using system32/SysWOW64 logic
5 ProgramFiles
6 AppData
7 UserProfile
8 ProgramData, falling back to ProgramFiles

Appendix C. Additional secondary-channel routes

The supplied secondary_routes.json records the raw route extraction, addresses and supporting references. The comparison tracer follows arithmetic and branches and stops before feature-handler execution. It is not a test of actual file, camera or registry operations.

C.1 Service channel

The dispatcher at 0x41e9d4 reads a leading byte and uses the table at 0x41ee38.

Byte Handler VA Role
0 0x41ea35 Load service module, then continue to enumeration
1 0x41ea6c GetSvc
2 0x41eaa4 StartSvc
3 0x41eb31 StopSvc
4 0x41eba6 PauseSvc
5 0x41ec1b ResumeSvc
6 0x41ec90 RestartSvc
7 0x41ed23 SetSvcStartup

C.2 Camera channel

ID Handler VA Role
60 0x403f89 Receive/load DLL and resolve camera exports
61 0x403f54 Request frame acquisition while camera-open state is set
62 0x403f42 CloseCamera and clear open state
63 0x403f2b OpenCamera and continue to acquisition
64 0x403ec5 OpenCamera, delay and request capture

C.3 Recovery channel

ID Handler VA Role
31 0x417be9 Receive/load recovery module through recovery_run_str / recovery_free_str adapter
68 0x417b48 Execute recovery or helper coordination according to access-level path

C.4 Remote-input channel

The leading control byte is an ASCII character, not a primary command DWORD.

Character Branch/check Observed role
0 0x41dc5e Screen-session/frame request using capture object 0x47f9e8
1 0x41dc97 Keyboard-input helper
2 0x41dcf7 Coordinate-bearing mouse input helper
3 0x41dd57 Additional coordinate-bearing mouse input helper
4 0x41ddbb Mouse movement/position input helper
5 0x41de0c Wheel event with -120 delta
6 0x41de16 Wheel event with +120 delta
7 0x41de2d Screen-session/frame request using capture object 0x47fab0

The detailed button/coordinate interpretation should be taken from the cited helper implementations rather than inferred from operation order alone.

Appendix D. Evidence package and reproduction

The evidence archive is AES-encrypted with password infected and contains malicious objects. Recovered executable material uses .bin filenames. The original script is retained for reproducibility and should be treated as executable malware content.

File or group Purpose
Original b036…js Exact outer script
embedded1 / embedded3 Recovered Lua and LuaJIT host
loader_clean.lua Analytical source with repeated VM blocks removed
decoded_stage.bin Decoded Donut shellcode
donut_instance.bin Decrypted instance, including advanced counter
final_payload.exe.bin Exact 537,600-byte Remcos PE
resource_10_SETTINGS.bin Original encrypted configuration resource
remcos_config.bin / config_fields.json Decrypted bytes and complete positional field representation
config_field_54.der / 55.der / 56.der Certificate/key objects
deep/disassembly.asm Indexed annotated disassembly
deep/command_map.csv All 216 primary slots
deep/command_table.json Active primary routes and supporting references
deep/secondary_routes.json Additional recovered channel routes
deep/*validation.json Independent verification results
manifest.json Computed per-file sizes and SHA-256 values

Dependencies used for the analytical scripts include Python, pyzipper, lupa, pefile, pycryptodomex, unicorn, capstone, cryptography and Node.js. None of the following steps requires contacting the sample's infrastructure.

From the package root, with the analysis directory present:

node analysis/extract.js
python analysis/preprocess.py
python analysis/decode.py
python analysis/decrypt_instance.py
python analysis/final_extract.py
python analysis/deep/verify_donut_cipher.py
python analysis/deep/verify_header.py

The first command evaluates the original script with inert Windows-interface substitutes and neutralizes its recursive debugger trap. The decryption script starts only at the standalone Donut decryptor. The header test starts only at the isolated header-reader routine. None should be confused with a full sandbox detonation.

The cipher and header verification outputs are retained in JSON so another analyst can check both the conclusion and its scope. The certificate validation records preserve signature and key-match results without including the private-key value in the narrative report.

Appendix E. Findings that required correction or qualification

Several attractive claims in the supplied notes did not survive closer examination:

  • The Lua's repeated VM implementations do not make a custom bytecode deserializer necessary for this sample's payload recovery.
  • High entropy and the absence of an MZ header did not establish an inaccessible final encryption layer. The first instruction points to an executable Donut wrapper.
  • The Donut identification rests on structure and reproduced decryption, not the Lua's deliberately manufactured DonutStub string.
  • The Lua's buffer of injection API names is decoy data. Genuine process hollowing was established separately in the Remcos PE.
  • An AMSI/ETW patch attempt is not proof of a complete or stable bypass.
  • Remcos's supported surveillance features are not all enabled at startup in the recovered defaults.
  • Fifty-eight split elements include a trailing empty element; they should not be described as 58 independently meaningful options.
  • The mutex and licence fields are now confirmed by their consumers rather than left as generic identifiers.
  • The local certificate/private-key relationship and both self-signatures are now independently verified.
  • The outer ZIP and the JScript have different hashes. The long basename matches the JScript.
  • A primary command range of 1-216 contains 82 active entries, not 216 implemented features.
  • Optional feature interfaces do not recover the contents of controller-supplied DLLs that are absent from the evidence.

These qualifications do not weaken the payload identification. They make the account more useful: each conclusion can be tied to recovered bytes, a specific code path or a narrowly defined verification result.

Appendix F. Visual evidence index

The 28 figures are embedded in their relevant sections. The separate visual atlas provides vector SVG, PNG, Mermaid and Graphviz source for every figure. Its illustrated report uses relative SVG image links for readers without Mermaid support. Colors aid navigation; captions state the evidential limits.

Figure Subject Report section
D01 Delivery, payload and persistence boundaries 1. Executive assessment
D02 Independent evidence supporting payload recovery 2. Evidence and analytical scope
D03 JScript delivery branch 3. Delivery through JScript
D04 Lua scheduled-task persistence conditions 4. The Lua loader
D05 Separating meaningful Lua from camouflage 4. The Lua loader
D06 Payload decoding decisions 4. The Lua loader
D07 VEH page-protection cycle 4. The Lua loader
D08 Donut shellcode regions and architecture selection 5. Recovering the Donut instance
D09 Independent reconstruction of the Donut cipher 5. Recovering the Donut instance
D10 Exact executable boundary inside the instance 5. Recovering the Donut instance
D11 SETTINGS resource and positional fields 7. Configuration and startup decisions
D12 Startup defaults versus later activation 7. Configuration and startup decisions
D13 Connection selection and retry control 8. Controller communication
D14 TLS object relationships and verification limits 8. Controller communication
D15 Plain and compressed application-frame layouts 8. Controller communication
D16 TCP buffering and message dispatch 8. Controller communication
D17 Primary command translation table 9. Command dispatch and feature channels
D18 Feature channels and shared module loader 9. Command dispatch and feature channels
D19 Keyboard capture, context and output paths 10. Surveillance and remote interaction
D20 Screen observation and remote input 10. Surveillance and remote interaction
D21 File-management channel and transfer feedback 11. Files, registry and host control
D22 Browser clearing versus module-based recovery 12. Browser data and recovery modules
D23 Manual PE loader control flow 13. In-memory modules, hollowing and elevation
D24 Section-backed process hollowing path 13. In-memory modules, hollowing and elevation
D25 Core lifecycle operations and wrapper boundary 14. Restart, update, watchdog and removal
D26 What each verification layer actually establishes 16. Verification and remaining limits
D27 Behavioral correlation for a defender 15. Forensic interpretation and detection
D28 Detection testing and coverage boundaries 15. Forensic interpretation and detection

The atlas also contains the 14 annotated assembly excerpts, four byte comparisons and detection test results as structured JSON. These displays make the central recovery and control-flow findings inspectable without rerunning the reproduction scripts.

Detection downloads

The YARA rules and experimental Sigma-format templates below accompany the original report. The behavioural templates were tested against synthetic fixtures, not Windows telemetry or a production SIEM backend. Validate and tune them before deployment.

More research

Write-up27 September 2026

OmniStealer: an npm supply-chain investigation

Ashlynn traces a malicious npm dependency through a staged JavaScript and Python stealer, infrastructure research, exfiltration and a qualified DPRK attribution assessment.

  • Malware analysis
  • Supply chain
  • Threat intelligence

Write-up18 April 2026

BrowserSwap: browser-side cryptocurrency address hijacking

An investigation of a browser-side Bitcoin replacement campaign: Google Sheets delivery, obfuscation, exchange-specific manipulation, wallet indicators and detection guidance.

  • Browser security
  • Cryptocurrency
  • Malware analysis

Write-up10 July 2025

Uncovering an extension botnet

Eoka compares JiffyReader releases, investigates added request interception and proxy functionality, and documents the developers' responses and disclosure concerns.

  • Browser extensions
  • Supply chain
  • Investigations

Questions about this research?

Get in touch and the authors will get back to you.