{
	"id": "c0f63177-2d7b-487c-ab39-78bb379c9b08",
	"created_at": "2026-04-06T00:14:07.963007Z",
	"updated_at": "2026-04-10T13:12:42.192428Z",
	"deleted_at": null,
	"sha1_hash": "22b1a3038701db3411391a0fccda307290a68458",
	"title": "Technical Analysis of Xloader Versions 6 and 7 P1 | ThreatLabz",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 419444,
	"plain_text": "Technical Analysis of Xloader Versions 6 and 7 P1 | ThreatLabz\r\nBy ThreatLabz\r\nPublished: 2025-01-27 · Archived: 2026-04-05 15:02:37 UTC\r\nIn the following sections, we provide a detailed analysis of Xloader, focusing on the malware’s behavior,\r\nobfuscation, and anti-analysis techniques.\r\nBehavior\r\n \r\nPersistence\r\nXloader establishes persistence by making a copy of itself in a subdirectory under  %APPDATA%\r\n(or  %PROGRAMFILES% if the user has sufficient privileges) with the following format:\r\n%APPDATA%\\\\.exe\r\nXloader then adds an entry in the Windows registry, either under the  Run key or, if that fails, under\r\nthe  Policies key as follows:\r\n\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run\\\r\n\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\Run\r\nXloader’s entry is placed in either the  HKCU ( HKEY_CURRENT_USER ) or HKLM ( HKEY_LOCAL_MACHINE ) registry\r\nhive, based on the user's privileges. The name of the entry is randomly generated with 5-12 uppercase\r\nalphanumeric characters. The registry value will then be set to the path of the Xloader executable in\r\nthe  %APPDATA% or  %PROGRAMFILES% directory.\r\nProcess injection\r\nThe figure below is a high-level view of how Xloader injects into multiple processes to evade antivirus and\r\nendpoint security software.\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-xloader-versions-6-and-7-part-1\r\nPage 1 of 12\n\nFigure 1: A high-level view of how Xloader injects malicious code into target processes. \r\nXloader first creates a new instance of its own executable through process hollowing. Next, Xloader injects the\r\nnext stage into the  explorer.exe process to establish network communication. In this case, Xloader uses the\r\nasynchronous procedure call (APC) queue technique to inject an x64 shellcode. Xloader uses the native\r\nAPI  NtQueueApcThread instead of higher-level APIs, likely to evade antivirus hooks. The original and hollowed\r\nXloader processes will then terminate.\r\nFinally, Xloader launches an executable file in the SysWOW64 Windows directory that will also be targeted for\r\ncode injection. Xloader uses a combination of the Windows API functions\r\nincluding  CreateProcessInternal ,  NtCreateSection ,  NtMapViewOfSection , and  NtResumeThread to inject\r\ncode into the remote target process. The code injected into  explorer.exe and the code injected in the\r\nSysWOW64 process run concurrently and use shared memory sections to communicate.\r\nThe list of target executable filenames in the SysWOW64 directory varies across different samples and versions.\r\nIn one sample of Xloader version 6.2, the list of executables included the following:\r\nSearchFilterHost.exe\r\nIsv.exe\r\nUserAccountControlSettings.exe\r\nsysteminfo.exe\r\nSyncHost.exe\r\nprint.exe\r\nsdiagnhost.exe\r\nfixmapi.exe\r\nmsiexec.exe\r\ntakeown.exe\r\nsystray.exe\r\nnet1.exe\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-xloader-versions-6-and-7-part-1\r\nPage 2 of 12\n\nIn a sample from Xloader 7.5, the following target executable filenames were identified:\r\nnslookup.exe\r\nsrdelayed.exe\r\nmakecab.exe\r\nsetx.exe\r\nrunonce.exe\r\nauditpol.exe\r\nnotepad.exe\r\nsetupugc.exe\r\nAtBroker.exe\r\nRMActivate_isv.exe\r\nmountvol.exe\r\ndfrgui.exe\r\nXloader will choose the first entry in the executable filename list. However, if the executable is not found in the\r\nWindows SysWOW64 directory, it will then proceed to the next entry in the list until one of the executables is\r\nlocated.\r\nCode obfuscation\r\nIn the following sections, we examine Xloader’s custom encryption and obfuscation layers.\r\nPrevious versions of Xloader had two types of encrypted functions that we refer to as the following:\r\nNOPUSHEBP : The function’s entire code is encrypted.\r\nPUSHEBP : These functions start with the well-known push ebp prologue followed by encrypted code.\r\nMany of Xloader’s encryption layers continue to use an encryption algorithm that uses a combination of RC4 and\r\ntwo rounds of adjacent byte subtraction. However, in Xloader versions 6 and 7, an additional encryption layer has\r\nbeen added to the  NOPUSHEBP functions as shown in the figure below. \r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-xloader-versions-6-and-7-part-1\r\nPage 3 of 12\n\nFigure 2: A high-level diagram for Xloader versions 6 and 7, which leverage three main functions to decrypt and\r\nexecute critical parts of code.\r\nXloader executes three main functions: Main Function 1, Main Function 2, and Main Function 3. Each function\r\nimplements a decryption routine for subsequent encrypted functions. The most noteworthy of these is Main\r\nFunction 3, which itself is dynamically decrypted through multiple layers, and responsible for decrypting a new\r\nadditional encryption layer on top of the NOPUSHEBP encrypted functions.\r\nMain Function 1\r\nMain Function 1 is responsible for establishing persistence on the victim’s computer by copying itself to the\r\nappropriate directories and modifying the necessary registry keys explained in the previous section. In addition,\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-xloader-versions-6-and-7-part-1\r\nPage 4 of 12\n\nMain Function 1 decrypts and calls Main Function 2 as explained below.\r\nMain Function 1 uses an egg-hunting technique that searches for two DWORD ID values to locate the memory\r\naddress range of the encrypted Main Function 2. Note that all of these DWORD values in Xloader are calculated\r\ndynamically at runtime by performing an XOR operation with hardcoded values to evade static signature-based\r\ndetection. The first layer of the code is decrypted using Xloader’s RC4 and subtraction algorithm using a 20-byte\r\nkey stored in the malware’s global configuration. Once the first layer is decrypted, another DWORD value is used\r\nto delineate the end of the second encryption layer. The key to decrypt the second layer is the same DWORD\r\nvalue used to mark the beginning of the first encryption layer (padded with zeros until the length is 20).\r\nOnce the code is decrypted, the function prologue bytes  55 8B EC are written at the beginning of the decrypted\r\ncode and 4 no-op (NOP) opcodes are written at the end.\r\nA Python implementation of the Main Function 1 decryption code is shown below: \r\n# The malware keeps a common 0x14 len key at configobj + 0x410\r\n# This offset could change from one sample to another\r\nid_find_encode = get_hardcoded_id_find_encode(binary)\r\nkey_layer1 = keys['keys_0x14_stored_in_configobj'][0x410]\r\nid_end_layer1 = get_hardcoded_id_end_layer1(binary)\r\nid_end_layer2 = get_hardcoded_id_end_layer2(binary)\r\nkey_layer2 = padding(id_find_encode)\r\nif id_find_encode in binary:\r\n encode_base = binary.index(id_find_encode) + 4\r\n if id_end_layer1 in binary and id_end_layer2 in binary:\r\n # Decrypt layer 1\r\n end_layer1 = binary[encode_base:].index(id_end_layer1) + encode_base\r\n decode_layer1 = rc4_sub(binary[encode_base:end_layer1], key_layer1)\r\n \r\n # Add layer 1 decrypted code\r\n binary = binary[:encode_base] + decode_layer1 + binary[end_layer1:]\r\n # Decrypt layer 2\r\n end_layer2 = binary[encode_base:].index(id_end_layer2) + encode_base\r\n decode_layer2 = rc4_sub(binary[encode_base:end_layer2], key_layer2)\r\n # Add layer 2 decrypted code\r\n binary = binary[:encode_base] + decode_layer2 + binary[end_layer2:]\r\n # Add the function prologue and NOPs\r\n start = encode_base\r\n end = end_layer1\r\n if end_layer2 \u003e end_layer1:\r\n end = end_layer2\r\n decrypted_code = binary[start:end]\r\n decrypted_function = b\"\\x55\\x8B\\xEC\" + decrypted_code + b\"\\x90\\x90\\x90\\x90\"\r\n return decrypted_function\r\nMain Function 2\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-xloader-versions-6-and-7-part-1\r\nPage 5 of 12\n\nMain Function 2 executes Xloader’s main loop that calls the functions that communicate with the C2 server and\r\nsteal data from the victim’s computer. The function also decrypts and calls Main Function 3.\r\nThe decryption code in Main Function 2 is similar to Main Function 1 with two layers decrypted by Xloader’s\r\nRC4 and subtraction algorithm. The first layer is decrypted using the key stored in the global configuration and\r\nthe second layer is decrypted using a 20-byte key that is constructed dynamically (rather than using the DWORD\r\nID value used to mark the beginning of the first encryption layer).\r\nMain Function 3\r\nAs mentioned previously, Main Function 3 is primarily responsible for decrypting a new encryption layer on top\r\nof NOPUSHEBP functions. Main Function 3 uses another egg-hunting technique to search for these encrypted\r\nblocks. For example, in the sample  66ebf028ab0f226b6e4c6b17cec00102b1255a4e59b6ae7b32b062a903135cc9 ,\r\nXloader leverages 7 DWORD IDs used to find the beginning of the encrypted blocks, and another 7 IDs used to\r\nlocate the end of the encrypted blocks. Each block is decrypted using Xloader’s RC4 and subtraction algorithm\r\nwith a key obtained from the malware’s global configuration. Note that after each block is decrypted, the code is\r\nstill encrypted in the  NOPUSHEBP functions.\r\nEncrypted NOPUSHEBP functions\r\nThe decryption process of  NOPUSHEBP functions remains the same as previous versions with the only exception\r\nbeing how the IDs and decryption keys are dynamically calculated.\r\nThe following code shows a Python implementation of Xloader’s  NOPUSHEBP function decryption:\r\n# Find the required seeds and xor keys from the binary\r\nnopushebp_tag1_seed = get_nopushebp_tag1_seed(binary)\r\nnopushebp_tag2_seed = get_nopushebp_tag2_seed(binary)\r\nxor_key_tags = get_xor_key_tags(binary)\r\nnopushebp_key_seed = get_nopushebp_key_seed(binary)\r\nnopushebp_key_xor = get_nopushebp_key_xor(binary)\r\n# Calculate the limit tags and key\r\nnopushebp_tag1 = xor(nopushebp_tag1_seed, xor_key_tags)\r\nnopushebp_tag2 = xor(nopushebp_tag2_seed, xor_key_tags)\r\nnopushebp_key = xor(nopushebp_key_seed, nopushebp_key_xor)\r\n# Decrypt the function\r\nif nopushebp_tag1 in binary and nopushebp_tag2 in binary:\r\n enc = binary.split(nopushebp_tag1)[1].split(nopushebp_tag2)[0]\r\n decrypted_function = b\"\\x55\\x8B\\xEC\" + \\\r\n rc4_sub(enc, nopushebp_key) + \\\r\n b\"\\x90\\x90\\x90\\x90\"\r\nVersion 7.5 of Xloader introduced a slight modification to decrypt NOPUSHEBP functions with the construction of\r\nthe RC4 keys and some functions contain multiple layers of encryption.\r\nEncrypted PUSHEBP functions\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-xloader-versions-6-and-7-part-1\r\nPage 6 of 12\n\nXloader’s  PUSHEBP functions are very similar to prior versions, with additional complexities added in version 7.5\r\nthat leverage XOR operations to derive the RC4 key.\r\nCode encryption on API calls\r\nXloader now encrypts its own code before calling critical APIs, like  ZwSetThreadContext , involved in process\r\ninjection, as shown in the figure below. \r\nFigure 3: Code protection technique prior to calling  ZwSetThreadContext in Xloader 6.2.\r\nOnce the API returns, the original code is decrypted again. This is likely a mechanism designed to thwart analysis\r\nplatforms that generate memory dumps when specific system calls are executed (for example, memory allocation,\r\nprocess creation, etc.), since the important parts of the code will remain encrypted. A similar technique is\r\nimplemented in modern versions of SmokeLoader's stager component, which decrypts code blocks when needed\r\nand re-encrypts them after use.\r\nData obfuscation\r\nPrevious versions of Xloader stored all critical information (for example, the encrypted strings and the encryption\r\nkeys) in a set of static encrypted data blocks that we called  PUSHEBP data blocks because the encrypted data was\r\npreceded by a push ebp prologue.\r\nIn Xloader versions 6 and 7, encrypted  PUSHEBP data blocks no longer exist. When the malware requires a string,\r\nkey, seed, or constant, the value is constructed dynamically. As a result, there are no hardcoded keys in the\r\nmalware code.\r\nString obfuscation\r\nIn previous versions of Xloader, the malware’s encrypted strings were contained in an encrypted  PUSHEBP data\r\nblock. In the new versions, there are dedicated functions that build, decrypt, and return each string, as shown in\r\nthe figure below.\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-xloader-versions-6-and-7-part-1\r\nPage 7 of 12\n\nFigure 4: Function to decrypt the  COMPUTERNAME string in Xloader 6.2.\r\nAll of these functions operate by pushing the encrypted string on the stack. Xloader then calls a function that\r\ninitializes a 20-byte key. The key is then used to decrypt the string using Xloader’s RC4 and subtraction\r\nalgorithm.\r\nStack strings\r\nIn addition to the encrypted strings described above, Xloader builds some plaintext strings on the stack\r\nthrough  NOPUSHEBP functions. Since the code itself in these functions is encrypted and protected, the strings are\r\ntoo.\r\nAPI obfuscation\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-xloader-versions-6-and-7-part-1\r\nPage 8 of 12\n\nEarlier versions of Xloader used two encrypted  PUSHEBP data blocks that contained tables of 32-bit cyclic\r\nredundancy check (CRC32) values for Windows API function names. In versions 6 and 7, these  PUSHEBP data\r\nblocks have been removed. Now, each CRC32 value is created by its own specific code block.\r\nThere are two main types of functions that calculate the CRC32 values for Windows API function names. One\r\nfunction requires a seed and an encrypted CRC32, while the other function only requires an encrypted CRC32\r\nvalue. These functions are described below.\r\nThe first API resolution function dynamically builds each API CRC32 value from two parameters. The functions\r\nmaintain a consistent code structure with two parameters consisting of a 1-byte XOR key and an encrypted\r\nCRC32 DWORD value as shown in the figure below.\r\nFigure 5: Algorithm to derive the API hash using a seed and an encrypted CRC32 value in Xloader 6.2.\r\nIn the example above, an XOR operation is performed with a 20-byte seed ( 15 2B 13 8F 74 EB 03 60 8E 08 48\r\nEA 8F 61 89 7D 9E A4 A6 C1 ) and a hardcoded byte ( 0x48 ). Another XOR operation is performed using the\r\nresult and the 1-byte XOR key provided to the function. This generates the decryption key, which is then used by\r\nXloader’s RC4 and subtraction algorithm to decrypt the API function name's CRC32 value.\r\nIn other cases, the API CRC32 value is calculated with inline code (instead of a dedicated function). In these\r\ncases, only the RC4 encrypted CRC32 value is provided as an argument to a function that builds a 20-byte RC4\r\nkey dynamically (with 5 DWORDs encoded by a single XOR key). Xloader’s RC4 with subtraction algorithm is\r\nthen used to decrypt the final API CRC32 value. \r\nXloader then computes the CRC32 value for each export function name (converted to lowercase) and the\r\npreviously calculated CRC32 to locate the address of each required API function.\r\nNTDLL hook evasion\r\nXloader versions 6 and 7 load a copy of ntdll and call the library’s API functions through the copy instead of the\r\noriginal library. This ensures that if breakpoints are set on the exported functions of the library or if a monitoring\r\ntool attempts to perform hooks, they will be unable to properly trace Xloader’s behavior. This technique is used by\r\nother malware families including SmokeLoader.\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-xloader-versions-6-and-7-part-1\r\nPage 9 of 12\n\nXloader obfuscation evolution\r\nThe table below outlines the obfuscation techniques used in Formbook and various versions of Xloader.\r\nTechnique V2 V4 V6/V7\r\nRC4 with subtraction encryption Yes Yes Yes\r\nCustom lookup table decryption algorithm Yes Yes No\r\nPUSHEBP data blocks Yes Yes No\r\nPUSHEBP code blocks with\r\nplaintext limit IDs\r\nYes No No\r\nPUSHEBP. code blocks with\r\nencrypted limit IDs\r\nNo Yes Yes\r\nPUSHEBP decryption with \r\nXOR key passed by caller\r\nNo No No/Yes\r\nNOPUSHEBP code blocks\r\nKey from PUSHEBP data blocks\r\nNo Yes No\r\nNOPUSHEBP code blocks\r\nwith egg-hunting \r\nNo No Yes\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-xloader-versions-6-and-7-part-1\r\nPage 10 of 12\n\nTechnique V2 V4 V6/V7\r\nNOPUSHEBP code blocks\r\nwith dynamic key \r\nNo No Yes\r\nNOPUSHEBP code blocks\r\nTwo RC4 with subtraction layers\r\nKey 1 built dynamically \r\nKey 2 from global config\r\nNo No No/Yes\r\nStrings in PUSHEBP data blocks Yes Yes No\r\nStack-based string obfuscation No No Yes\r\nDecoy C2s stored as encrypted strings Yes Yes Yes\r\nDecoy C2s encrypted with additional layers Yes Yes Yes\r\nReal C2 in PUSHEBP data blocks Yes Yes No\r\nReal C2 among the encrypted strings No No Yes\r\nC2 keys in data blocks Yes Yes No\r\nC2 keys built dynamically No No Yes\r\nAPI CRC values in PUSHEBP data blocks Yes Yes No\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-xloader-versions-6-and-7-part-1\r\nPage 11 of 12\n\nTechnique V2 V4 V6/V7\r\nAPI CRCs encrypted with RC4 and subtraction No No Yes\r\nTable 1: Comparison of Xloader obfuscation techniques by version.\r\nAs the table above demonstrates, Xloader continues to add new layers of encryption and obfuscation with each\r\nnew release to complicate manual and automated analysis. Xloader now contains multiple layers of encryption for\r\ncode, strings, constants, and API hashes. Furthermore, the Xloader author has implemented additional measures\r\nover time to decrypt critical information only when necessary, and scattered code and data across multiple\r\nsections. Xloader’s encryption algorithms have also been changed significantly over time from a custom\r\nencryption algorithm that used a lookup table to an algorithm that leverages RC4 and subtraction. These\r\nmodifications are designed to better evade detection by endpoint security software and stay one step ahead.\r\nExplore more Zscaler blogs\r\nSource: https://www.zscaler.com/blogs/security-research/technical-analysis-xloader-versions-6-and-7-part-1\r\nhttps://www.zscaler.com/blogs/security-research/technical-analysis-xloader-versions-6-and-7-part-1\r\nPage 12 of 12",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"MITRE",
		"Malpedia"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://www.zscaler.com/blogs/security-research/technical-analysis-xloader-versions-6-and-7-part-1"
	],
	"report_names": [
		"technical-analysis-xloader-versions-6-and-7-part-1"
	],
	"threat_actors": [],
	"ts_created_at": 1775434447,
	"ts_updated_at": 1775826762,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/22b1a3038701db3411391a0fccda307290a68458.pdf",
		"text": "https://archive.orkl.eu/22b1a3038701db3411391a0fccda307290a68458.txt",
		"img": "https://archive.orkl.eu/22b1a3038701db3411391a0fccda307290a68458.jpg"
	}
}