2CLoader: A New Malware Loader Delivering Vidar and Remus

IntroductionIn August 2026, Zscaler ThreatLabz identified a new loader, which we track as 2CLoader. ThreatLabz has observed the loader being used to distribute information stealers including Vidar and Remus in addition to XWorm RAT. 2CLoader has the ability to perform a wide range of anti-analysis and evasion techniques, including indirect system calls, anti-analysis checks, and installing Windows APIs hooks. In this blog post, ThreatLabz provides a technical deep dive into 2CLoader, covering its core features, evasion techniques, loader configuration, network communication, payload decryption, and execution options. Key TakeawaysIn August 2026, ThreatLabz identified a new loader tracked as 2CLoader.2CLoader implements multiple anti-analysis techniques, including anti-VM, anti-debug, and user activity checks.2CLoader employs several evasion techniques to hinder detection by endpoint security tools, including indirect system calls and inline trampoline hooks.2CLoader supports multiple payload execution and persistence mechanisms, depending on its configuration. Based on observed delivery trends, 2CLoader has primarily been used to deliver Vidar and Remus. Technical AnalysisThe following sections analyze 2CLoader’s core features and cover malware delivery trends associated with the loader.String encryptionImportant strings in 2CLoader are decrypted at runtime using an inlined bitwise XOR operation applied to global values. The key in the analyzed sample is 0x37.Indirect system callsFor the Nt* APIs listed below, 2CLoader prefers indirect system calls to evade inline API hooks commonly used by security software for detection.NtProtectVirtualMemory NtUnmapViewOfSection NtQueryInformationProcess NtDelayExecution NtSetContextThread NtGetContextThread2CLoader uses the Hell’s Gate technique to perform indirect system calls. The loader maps a fresh copy of ntdll from disk, retrieves the export addresses of the APIs listed above, and checks whether each stub begins with one of the following two patterns.4C 8B D1 mov r10, rcx

Introduction to Malware Binary Triage (IMBT) Course

Looking to level up your skills? Get 10% off using coupon code: MWNEWS10 for any flavor.

Enroll Now and Save 10%: Coupon Code MWNEWS10

Note: Affiliate link – your enrollment helps support this platform at no extra cost to you.

B8 xx xx xx xx mov eax, ssnorF3 0F 1E FA endbr64 # CET-prefixed variant
4C 8B D1 mov r10, rcx
B8 xx xx xx xx mov eax, ssnIf the stub matches one of these patterns, 2CLoader extracts and stores the 4-byte syscall ID. The loader also walks the Process Environment Block (PEB) to obtain the in-memory module base of ntdll.dll and scans executable sections for the syscall gadget (0F 05 C3 which maps to syscall, ret). 2CLoader stores the six syscall gadget addresses in global variables for later use when performing indirect system calls.If indirect system call initialization fails, a fallback mechanism resolves the six APIs listed above using GetProcAddress and stores the resulting export addresses in global variables.Loader configuration2CLoader stores its configuration and encrypted payload as a Portable Executable (PE) resource. The resource has the following structure:[final payload] [optional payload dropped] [optional message shown using MessageBoxW] [configuration]The configuration occupies the last 0xDC bytes of the resource and begins with the 4-byte magic value 2C 3D 4E 5F. The configuration uses the following structure: struct 2cloader_config

{

uint32_t magic; // Magic bytes 2C 3D 4E 5F.

uint32_t fl; // Payload execution options, explained in later sections. Sent in the registration message.

uint32_t payload_size_before_decompression; // Final payload size before decompression.

uint32_t aes_gcm_nonce_size; // AES-GCM nonce length used for final payload decryption.

uint32_t aes_gcm_tag_size; // AES-GCM tag length used for final payload decryption.

uint8_t aes_gcm_nonce[0xC]; // AES-GCM nonce for the final encrypted payload.

uint8_t aes_gcm_tag[0x10]; // AES-GCM authentication tag for the final encrypted payload.

uint8_t sha256_seed_xor2[0x10]; // Bitwise XOR applied to bytes 0x00-0x0F of the 32-byte AES key seed.

uint8_t sha256_seed_xor1[0x10]; // Bitwise XOR applied to bytes 0x10-0x1F of the 32-byte AES key seed.

uint32_t sum_of_bytes_checksum; // Byte-sum checksum used to validate the derived AES-256 key.

uint32_t persistence_option; // Persistence options, explained in later sections.

uint32_t opt_flag; // Customizable options flag (anti-VM, anti-debug, etc.). Explained in later sections.

uint32_t optional_payload_dropped_size; // Size of the additional payload to be dropped.

uint32_t messageboxw_message_size; // Size of the appended MessageBox caption/text block.

uint8_t xor_add_after_rolling_xor; // Per-byte increment used in the second XOR stage.

uint8_t xor_key_after_rolling_xor; // Initial constant used in the second XOR stage.

uint8_t rolling_xor_seed; // Seed for the first rolling XOR layer.

uint8_t unk_67; // Not used anywhere; set to zero.

uint32_t sleeptime; // Malware sleep time in milliseconds (ms) before payload decryption.

uint8_t ID[0x10]; // ID used in the start message.

char target_executable_name_used_in_injection[0x40]; // Target executable names used for injection.

uint32_t bt; // Sent in the registration message with the key name bt. Not used elsewhere.

char pn[0x10]; // Sent in the registration message with key name pn. Contains a file name such as default.exe or build_tbuild.exe. Not used elsewhere.

uint32_t unk_D0; // Not used anywhere; set to zero.

uint32_t scheduled_tasks_trigger_interval_hours; // Scheduled task trigger interval in hours.

uint32_t decompressed_buffersize_of_payload; // Final payload size after decompression.

};Payload decryption2CLoader first decrypts the resource body (excluding the last 0xDC bytes) using a rolling XOR driven by the rolling_xor_seed from the configuration. Each byte is XOR’ed with a value that starts with the rolling_xor_seed and incremented by one for each subsequent byte. After that, the loader applies a second XOR layer using the xor_key_after_rolling_xor (from the configuration) as the initial value and xor_add_after_rolling_xor as the per-byte increment. This produces the AES-GCM ciphertext that the loader uses in the next stage.2CLoader then derives the AES key from its own image. The loader calculates the SHA256 of the first executable section in memory, usually .text, and uses that 32-byte digest as the base key material. The first half of that digest is XOR’ed with the sha256_seed_xor2 field and the second half is XOR’ed with sha256_seed_xor1. The resulting 32-byte buffer is the AES key, which is validated using sum_of_bytes_checksum. If the checksum matches, the loader uses aes_gcm_nonce and aes_gcm_tag to perform AES-GCM decryption of the transformed resource body.In the final step, 2CLoader treats the AES plaintext as three concatenated parts. The first part is the final payload, the second part is an optional dropped payload of size optional_payload_dropped_size, and the third part is an optional MessageBoxW block of size messageboxw_message_size. Only the first payload is decompressed, and only when decompressed_buffersize_of_payload is nonzero. Decompression uses Xpress Huffman and expands the first part from payload_size_before_decompression to decompressed_buffersize_of_payload, while the second and third parts are left unchanged. The payload decryption script is available in the ThreatLabz GitHub repository.ANALYST NOTE: If the 0x1 flag in opt_flag is enabled, 2CLoader also performs a timing-based check after calculating the SHA256 digest. It runs an arithmetic operation in a loop 3,000,000 times. If this loop completes in fewer than 2,000,000 CPU cycles, all 32 bytes of the SHA256 digest are XOR’ed with 0xFF. This is intended to mislead emulators by producing an incorrect key seed.Custom options flag (opt_flag)The opt_flag configuration value controls which custom options 2CLoader enables. Multiple options can be combined using a bitwise OR operation. The supported values and their behavior are listed in the table below.ValueFunctionality0x1Anti-VM check (described in the next section).0x10Anti-debug check. 2CLoader uses three methods to check whether it is being debugged: The IsDebuggerPresent API.The CheckRemoteDebuggerPresent API.A timing-based check that measures how long a loop of 1000 integer operations takes to execute. If the loop takes more than 200 ms, the malware assumes it is being debugged. If a debugger is detected, the payload is not decrypted.0x20During payload execution, prioritize RunPE over LoadPE. Both methods are explained in later sections.0x40User activity check. 2CLoader captures the current cursor position, then loops up to 50 times, sleeping for 100 ms in each iteration. On each iteration, it checks whether the cursor has moved from the original position or whether the left mouse button or Enter key is pressed. The payload is decrypted even if there is no user activity, but the loop is only exited if there is user activity or five seconds have passed.0x80While creating the optional payload process, 2CLoader spoofs the parent process as explorer.exe and enables SeDebugPrivilege for the optional payload process.0x1002CLoader creates a temporary file whose name is derived from the configuration’s ID field, using the pattern %TEMP%\aw_<ID-as-hex>.lk. The file is opened with share mode 0. Because the handle remains open and the file is created with FILE_FLAG_DELETE_ON_CLOSE, it acts as a temporary lock. If another malware instance with the same ID is already running, the new instance exits immediately.0x4002CLoader places inline trampoline hooks in Windows APIs, described in a later section.Table 1: Supported opt_flag values and their functions in 2CLoader.Anti-VM detectionThe anti-VM logic uses two decision models. The first is a hard-fail path, in which a single condition is enough to classify the victim machine as a VM. The second is a score-based path, in which 2CLoader builds a score from multiple checks. If any hard-fail check triggers or the final score is below 8, the loader exits before decrypting the payload.Hard-fail checks: 2CLoader first performs a CPUID-based (EAX = 1) virtualization check. If the hypervisor bit is not set, this check passes immediately. If the hypervisor bit is set, the malware queries the hypervisor vendor with CPUID EAX=0x40000000. VMware, VirtualBox, KVM, Xen, Parallels, and QEMU TCG are blocklisted; Microsoft Hyper-V is explicitly allowed. The malware also checks loaded modules, running process names, VM-related MAC address prefixes, and registry keys associated with VirtualBox, VMware, and Wine against a blocklist. For registry keys, it checks whether a key exists and contains subkeys or values. If any of these checks match, the anti-VM check fails. A timing check also causes failure if the anti-VM checks take more than 200 ms.Score-based checks: If 2CLoader passes the hard-fail stage, it proceeds to a score-based environment check. The base score is 5. The malware adds one point for each of the following conditions:The current process count is above 25. The logical processor count is at least 2. Total physical memory is at least 2 GB. Total disk size is at least 40 GB. System uptime is greater than 3 minutes. ProcessDebugPort is 0. %APPDATA%\Microsoft\Windows\Recent* contains at least 2 files. Screen resolution is larger than 800 x 600.The current username does not match the blocklist. The computer name does not match the blocklist. The cursor position changes within 500 ms.2CLoader continues checking even if the score has reached 8. A final score of 8 or higher causes 2CLoader to treat the system as a physical machine.The complete blocklist is available in the ThreatLabz GitHub repository.PersistencePersistence for 2CLoader is controlled by the persistence_option field in the configuration. The loader can enable multiple persistence methods simultaneously by combining the flags using a bitwise OR operation. In all cases, the persistence entry points to the current malware file path. The supported values and their corresponding persistence methods are listed in the table below.ValuePersistence Method0x1Uses HKCU\Software\Microsoft\Windows\CurrentVersion\Run for persistence under the value name SecurityHealthService.exe.0x2Copies the malware to the Startup folder.0x4Creates a scheduled task using an XML file with a LogonTrigger for the current user and the task name SecurityHealthService.exe.0x8Uses HKCU\Software\Microsoft\Windows\CurrentVersion\RunOnce for persistence under the value name SecurityHealthService.exe.0x10Uses the Load value under HKCU\Software\Microsoft\Windows NT\CurrentVersion\Windows.0x20Uses the UserInitMprLogonScript value under HKCU\Environment.Table 2: Supported persistence_option values and their corresponding persistence methods in 2CLoader.Inline trampoline hooksAfter payload decryption, if the 0x400 flag in opt_flag is enabled, 2CLoader sets inline trampoline hooks on a set of Windows API functions in the current process. In the analyzed sample, there are around 20 such hooks, but only a few important ones are discussed here. The common pattern is that the original API entry point is patched to redirect execution through a malware-controlled trampoline, allowing the loader to modify the returned data.These hooks perform two functions: network data tampering where API return buffers are scanned and modified, and environment spoofing, where values related to host identity, such as username, computer name, volume serial number, registry data, or environment variables, are replaced with random, but properly formatted values. The replacement data is crafted to preserve the expected structure of the original value. The table below shows the hooked APIs and their behavior.Hooked APIHook behaviorInternetReadFile / WinHttpReadDataScans the received data for IPv4 addresses and replaces the last two octets with random but valid decimal values.RegQueryValueExA / RegQueryValueExWIf the queried value name matches one of the targeted values, replaces the returned data with similarly formatted spoofed data. For example, the data returned by an API call querying BaseBoardManufacturer is replaced with one of the strings (ASUSTeK INC., American Megatrends, Dell Inc., MSI, ASRock, Gigabyte) chosen at random. Likewise, for an API call querying MachineGuid, the data is replaced with a random string that follows the GUID format.GetUserNameA / GetUserNameWReplaces the username with a random username from the list (admin, user, gamer, john, alex, player).GetVolumeInformationA / GetVolumeInformationWReplaces the volume serial number with a random serial number.GetComputerNameA / GetComputerNameW/GetComputerNameExA / GetComputerNameExWReplaces the computer name with a random name starting with DESKTOP-.GetEnvironmentVariableA / GetEnvironmentVariableWIf the queried environment variable’s name matches a targeted name, replaces the returned value with similarly formatted spoofed data. For example, if the queried variable is USERNAME, the username is replaced with one of the strings (admin, user, gamer, john, alex, player) chosen at random.Table 3: Windows APIs hooked by 2CLoader and the behavior of their hooks.The goal of environment spoofing is to provide fake system information about the victim’s system while still matching the format expected by the API call. The exact intention behind these hooks is not clear, but may be designed to confuse malware sandboxes.Payload execution optionsThe opt_flag and fl fields control how 2CLoader executes the payload. Each field can contain multiple values combined with a bitwise OR operation. Before executing the final payload, 2CLoader processes two optional blobs appended after the final payload in the decrypted buffer. The first optional blob is treated as an additional PE file. If present, the loader writes it to a temporary .exe file, launches it, and if execution succeeds, schedules it for deletion on reboot. If the 0x80 flag in opt_flag is set, the loader spoofs explorer.exe as the parent process (parent process ID, or PPID, spoofing) and enables SeDebugPrivilege for the new process. The second optional blob contains the caption followed by the message text, separated by a null terminator. If neither of the bits in the 0x6 mask is set in fl and the 0x20 flag in opt_flag is not set, the loader displays the message box in the current thread before continuing. Otherwise, it copies the message block to heap memory and displays it from a helper thread so execution can continue asynchronously.If the 0x2 flag in fl is set, the payload is treated as a managed .NET assembly and executed directly from memory via Common Language Runtime (CLR) hosting. In this mode, the loader loads mscoree.dll, initializes the CLR, creates a SAFEARRAY from the payload bytes, loads the assembly from memory, and invokes its entry point. If the 0x2 flag in fl is not set but the 0x4 flag is set, the loader forces the RunPE path. The figure below shows how the loader selects the execution method based on fl.Figure 1: Pseudocode of the 2CLoader execution method based on fl.When neither the 0x2 nor the 0x4 flag in fl is set, 2CLoader enters the next branch. Here, the loader switches between LoadPE and RunPE based on the 0x20 flag in opt_flag. When this flag is not set, it tries LoadPE first and falls back to RunPE if that fails. When the flag is set, it tries RunPE first and falls back to LoadPE if needed. The LoadPE path is an in-process manual PE loader that maps the payload into the current process, fixes relocations and imports, updates PEB->ImageBaseAddress to the base address of the mapped payload, spoofs the command line to svchost.exe, and starts the payload in a new thread.The RunPE path creates a suspended process, using the target image specified in the configuration field target_executable_name_used_in_injection that defaults to dllhost.exe. The loader first creates a temporary file, immediately marks it as DeletePending via NtSetInformationFile(FileDispositionInformation), and writes the payload to this file. The file handle is then passed to NtCreateSection with the SEC_IMAGE allocation attribute, creating an image section from the payload. That SEC_IMAGE section is mapped into the suspended target process, the remote PEB->ImageBaseAddress is updated to the base address of the newly mapped section, the suspended primary thread is redirected to the payload entry point, and execution is resumed.The RunPE path supports both x64 and x86 payloads. For x64 payloads, if the 0x400 flag in opt_flag is set, the loader performs an additional step before the payload entry point is reached. In this mode, hooks are placed inside the remote process on WinHttpReadData and InternetReadFile. The purpose of these hooks is to scan received data for IPv4 addresses and replace the last two octets with random but valid decimal values while preserving the IPv4 format.Network communication2CLoader uses HTTP for command-and-control (C2) communication. The C2 URL is encrypted using the same string encryption scheme. Outbound messages are formatted as JSON and then XOR-encrypted before being sent in the body of an HTTP POST request. The XOR key used for network encryption is 4A 7B 3C 1D 8E 5F A0 D1 62 93 24 F5 B6 47 C8 09 E1 72 53 84 35 A6 D7 18 49 FA 0B 6C 9D 2E BF 50.The first message sent to the C2 server is a registration message. Its keys are listed below:KeyDescriptionidID field from the configuration, sent as 32 hexadecimal characters.sEvent name, set to start during registration.okSuccess flag, set to 0 or 1 based on the event status. Set to 1 during registration. tMalware elapsed time, set to 0 during registration.osOS version retrieved using RtlGetVersion, formatted as [major, minor, build].pidCurrent malware process ID.admIndicates whether the current token is elevated. Set to 1 if elevated; otherwise, 0.flThe fl field from the configuration, containing the payload execution options.optThe opt_flag field from the configuration.cpuLogical processor count.archProcessor architecture.dllIndicates whether the malware is running as a DLL. Set to 1 if yes; otherwise, 0.ramTotal physical memory in MB.lcLocale retrieved using GetUserDefaultLocaleName.btThe bt field from the configuration.pnThe pn field from the configuration.pathCurrent malware path.Table 4: JSON keys used in 2CLoader’s registration message.The example below shows an example 2CLoader network communication for a registration message.POST /api/beacon HTTP/1.1
Connection: Keep-Alive
Content-Type: application/octet-stream
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36
Content-Length: 252
Host: aware-cr1.comAn example POST body (after XOR decryption) is shown below:{“id”:“18c070b8d032336a244440df1115123d”,“s”:“start”,“ok”:1,“t”:0,“os”:[10,0,19044],“pid”:1408,“adm”:0,“fl”:0,“opt”:1152,“cpu”:4,“arch”:9,“dll”:0,“ram”:4195,“lc”:“en-US”,“bt”:1787666555,“pn”:“default.exe”,“path”:“C:/Users/Bruno/Desktop/executable.exe”}After registration, 2CLoader sends additional event messages to report execution status. These messages are also constructed as JSON and then encrypted using the same XOR-based scheme. The event format is as follows:{
“id”:<32 hexadecimal characters from ID>,
“s”:<event name>,
“ok”:<0 or 1>,
“t”:<Total malware elapsed time in ms>,
“x”:<additional argument for the event>,
“ec”:<error code from GetLastError()>,
“pid”:<malware process ID>
}Malware deliveryThreatLabz identified a number of 2CLoader samples to identify which malware families were being distributed by the loader. ThreatLabz found 2CLoader used to primarily deliver information stealers including Vidar and Remus. The pie chart below illustrates the distribution of the malware families distributed via 2CLoader.Figure 2: Distribution of malware families delivered by 2CLoader in samples identified by ThreatLabz.ThreatLabz has developed a Python script to decrypt 2CLoader payloads. The script is available in our GitHub repository. ConclusionThreatLabz has identified a new malware family that we track as 2CLoader. This highly customizable loader has a rich feature set and supports multiple payload execution methods based on its configuration along with numerous anti-analysis checks. 2CLoader has been used to distribute Vidar and Remus, which are used to steal credentials that can be used for subsequent attacks. Therefore, organizations should ensure proper security tooling is implemented to detect and prevent these attacks. Zscaler CoverageZscaler’s multilayered cloud security platform detects indicators related to 2CLoader at various levels. The figure below depicts the Zscaler Cloud Sandbox, showing detection details for 2CLoader.Figure 3: Zscaler Cloud Sandbox report for 2CLoader.In addition to sandbox detections, Zscaler’s multilayered cloud security platform detects indicators related to the campaign at various levels with the following threat name:Win64.Loader.2CLoader Indicators Of Compromise (IOCs)Host indicatorsSHA256 2CLoader samples5edcaa75a28e5cd700bf7643b275fe5d28649aa41a0711f391ec1fca795a4e8a0017821181723261801e24abb9d33c739f382889d46dbf46d320c87ac62e5ca606185d74edbdc06f99095e96f74aa2e49a1cda2d02a294c11a9ac35a0231075e066d83b98a2081e0bb075c94376aebc9b0fd6499025cab1762d83bbb4d7576c60a2ef2c360cf6e3e3e5d844ca03fecc31924ba0e8c3ecd8aefa778cae10fb19e0d2abd7d872196abd951f1d7ed6406486499e5d5acc04f28fcd4f45b1851711e0e4d6c385922938ecc1962dbc7e5950b086459b172b70a945414cffe4395aa270ee6df8a309443c86c0bba8b376f39531513d3451b46bebd4b79bb9d5bf8dcb11337ed6fe9c6205b569670a40eab42b51ba69c5c1724d61474e8595acd90ecfb1447ed0893b9095f671e2f35a2a0127890040b5459534813b0f83c3e1fffa0bf1b195181a2603b0b2608d49134af8c170225c2e06873c1fe5cd537db9018807f246717653bc2ae2e09036bac56c9d79940c652972a73f4c6aa9b925c28cce0952ad9b5e1c9952e95cfa55a4255e952962b6208ae1ca610caf1c7c2383be7e74b2c786f7009cc5e1fba5471a88b23b34dce6e1578ee9f664ec62bf48227ba384b2d43d592630ad1e012da63ef7279f95dd4a8e94964e12ca2f996051875574fa630cf47caf9700a74a8ea4a728b8b7c88cb1f8e22261b4ac01575b112c1e224ca3286ff477ccd888479b96d0ffb2bd53962862db7267a4bd1c8d8f8c22fec63d4331fd58d489e9fb888a5e4193d0e36f6ff29063808c59a164d98e2683505497245d46e7064ba4b4cb578659f73ee354ad26cf2cc1e7f59bd3d15028e1e46a38b4c16f5ebd5c633b7a793ffa2cd96daddc5a503d82ed4fbe636109d89164ec02bNetwork indicatorsURLDescriptionhttps://aware-cr1[.]com/api/beacon2CLoader C2http://62.60.226[.]185/t0907.exe2CLoader ITW URL

Article Link: Technical Analysis of 2CLoader | ThreatLabz