{
	"id": "b5523406-e825-414a-83ae-f6abdcff62a4",
	"created_at": "2026-04-06T00:08:20.408362Z",
	"updated_at": "2026-04-10T03:22:02.603741Z",
	"deleted_at": null,
	"sha1_hash": "5cdf67eb5882eb1f489f8260b028f76b7a439ac4",
	"title": "Technical Analysis of Lockbit4.0 Evasion Tales - 0x0d4y Malware Research",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 972194,
	"plain_text": "Technical Analysis of Lockbit4.0 Evasion Tales - 0x0d4y Malware\r\nResearch\r\nBy 0x0d4y\r\nPublished: 2025-02-19 · Archived: 2026-04-05 22:05:58 UTC\r\nThe Ransomware-as-a-Service (RaaS) group Lockbit is the main pillar of this Ransomware business model,\r\nlargely due to its strong commitment to the development of its product, producing Ransomware with\r\nimplementations that are up to date on the date of each release. The Lockbit4.0 (or Lockbit Green) version is no\r\ndifferent, as it is a major update, especially in the Evasion and Obfuscation layer. In this research, I will analyze\r\nthe main Obfuscation and Evasion capabilities implemented in Lockbit4.0, and some Intelligence insights will be\r\nprovided after the analysis.\r\nBelow is the SHA256 hash of the Lockbit4.0 sample that I will analyze in this research.\r\n\"sha256\":\"21E51EE7BA87CD60F692628292E221C17286DF1C39E36410E7A0AE77DF0F6B4B\"\r\nhttps://0x0d4y.blog/lockbit4-0-evasion-tales/\r\nPage 1 of 22\n\nReverse Engineering of the Unpacking Process\r\nThe Lockbit4.0 unpacking process is quite complex, and I will try to describe my analysis based on its\r\npseudocode. Below, we can see the beginning of the unpacking algorithm.\r\nThe code starts by reading a byte from the compressed stream, storing it in rdx. It then loads the value of arg5\r\nand multiplies it by 2 (arg5 \u003c\u003c 1). This forces a carry when the most significant bit (MSB) is cleared. The carry is\r\nsaved in the c_1 flag and will be used later to determine whether the byte can be copied directly or needs to be\r\nprocessed.\r\nThe code checks whether arg5 (in temp0_1) is equal to its complement (-arg5 or temp1_1). This is because\r\ncertain values in the compressed byte stream represent special markers that need to be processed differently. If the\r\nequality is true:\r\n1. A new byte is loaded into rbx.\r\n2. The arg2 pointer is adjusted to advance 4 bytes.\r\n3. An ADC (Add with Carry) operation is performed on rbx, modifying arg5.\r\n4. A new byte is loaded into rdx, continuing the data extraction.\r\nhttps://0x0d4y.blog/lockbit4-0-evasion-tales/\r\nPage 2 of 22\n\nIf carry c_1 was previously activated, this means that the read byte (rdx) does not need any special transformation\r\nand can be copied directly to the output buffer.\r\n1. The pointer arg2 (read from the compressed stream) and arg1 (write position in the output buffer) are\r\nincremented.\r\n2. The stream continues with the next byte.\r\nIf c_1 is false, it means that the byte cannot be copied directly and must be processed in a secondary loop. In this\r\nsecondary loop, the code will execute:\r\n1. An internal function (__return_addr_1) is called and returns temporary values.\r\n2. The arg5 register is ADCed and shifted to extract more information from the compressed bytes.\r\n3. The marker special condition is tested again to see if a new decode is needed.\r\n4. If the carry extraction indicates an invalid value, a new byte is loaded and tested again.\r\nAfter decoding, the code combines the extracted values to determine whether a patch in memory is necessary.\r\n1. If the result of the combination is 0xffffffff, the algorithm interprets this as a signal to start the patching\r\nroutine.\r\nhttps://0x0d4y.blog/lockbit4-0-evasion-tales/\r\nPage 3 of 22\n\n2. Otherwise, the extracted values \r\nare written directly to the output buffer.\r\nhttps://0x0d4y.blog/lockbit4-0-evasion-tales/\r\nPage 4 of 22\n\nNow we come to the last data block of the unpacking code. If the code detects that patching is necessary, it\r\nperforms a series of operations to directly modify specific regions of memory. So this last block of code will do:\r\n1. A loop walks through a section of memory, identifying and correcting relative addresses.\r\n2. Some instructions use _bswap to reverse the order of bytes.\r\n3. A set of subtractions adjusts obfuscated values to restore the correct bytes of the original code.\r\n4. Calls to VirtualProtect are made to change the memory permissions, ensuring that the modifications are\r\napplied.\r\n5. The transferred code is cleared and prepared for execution in a region now filled with unpacked code, in\r\nthe UPX0 section. Specifically, the address will be offset 0x140013a9f (unpacked_code_UPX0). And this\r\noffset is the entry point for the unpacked Lockbit4.0.\r\nThis last loop in which VirtualProtect is called and the program flow is unconditionally changed to the unpacked\r\ncode, is clearly observed in the graphical format of the Disassembly below.\r\nhttps://0x0d4y.blog/lockbit4-0-evasion-tales/\r\nPage 5 of 22\n\nAnd below you can see that the region at offset 0x140013a9f is in fact statically empty.\r\nNow let’s analyze this algorithm dynamically, with the aim of extracting the unpacked code from Lockbit4.0.\r\nBelow we can see the exact space still empty, before the unpacking process.\r\nhttps://0x0d4y.blog/lockbit4-0-evasion-tales/\r\nPage 6 of 22\n\nAfter the unpacking process is complete, the previously empty space is now filled with the unpacked code.\r\nOnce we reach this point, we will use the Scylla plugin to dump Lockbit4.0 unpacked.\r\nhttps://0x0d4y.blog/lockbit4-0-evasion-tales/\r\nPage 7 of 22\n\nWhen comparing the original packed sample from Lockbit4.0 with the dynamically extracted unpacked version,\r\nwe can observe a big difference in DiE size and detection.\r\nhttps://0x0d4y.blog/lockbit4-0-evasion-tales/\r\nPage 8 of 22\n\nBelow we can also see the difference in data organization in the packed version.\r\nAnd below we can see the structural change of the unpacked version.\r\nhttps://0x0d4y.blog/lockbit4-0-evasion-tales/\r\nPage 9 of 22\n\nDespite the names of the sections being similar to the IOCs left by UPX, this is not a sample packed by UPX. And\r\nwhen we go to the offset where the unpacked code was written, we can see that it is filled with valid code, in this\r\ncase the Lockbit4.0 Main function.\r\nhttps://0x0d4y.blog/lockbit4-0-evasion-tales/\r\nPage 10 of 22\n\nAnalyzing Dynamic DLL and API Resolution\r\nThe great obfuscation feature implemented by Lockbit4.0 is the DLL and API resolution technique at runtime,\r\ndivided into three functions. The image below illustrates the flow that is executed whenever Lockbit4.0 needs a\r\ncertain API.\r\nhttps://0x0d4y.blog/lockbit4-0-evasion-tales/\r\nPage 11 of 22\n\nSo, let’s look first at DLL resolution via Hashing. Lockbit4.0’s hashing algorithm is relatively easy, does not\r\ninclude any extra layers of obfuscation, and is intended to obfuscate DLLs that will be resolved at runtime. The\r\nalgorithm traverses a data structure applying mathematical transformations and bitwise operations to generate an\r\naccumulative value (in the rcx_17 variable).\r\nhttps://0x0d4y.blog/lockbit4-0-evasion-tales/\r\nPage 12 of 22\n\nThis hashing algorithm traverses a data structure applying mathematical transformations and bitwise operations to\r\ngenerate an accumulative value (rcx_17). Below is an objective summary of this algorithm:\r\n1. Initialization: Defines variables, specifically in rdx_8 = 0, rcx_17 = 0x14bf.\r\n2. The Main Loop: Reads values from memory indexed by r14_2 and stops when it finds a null value.\r\n3. Conditional Conversion: If the value is an uppercase letter (A-Z), converts it to lowercase (+0x20).\r\n4. Hash Calculation: The algorithm will XORs in rdx_8 ^ 0x14bf, to ensuring variation in values.\r\n5. Hashing or Checksum: Multiplies and combines values with XOR to create a cumulative identifier.\r\n6. Iteration: It will increment rdx_8 and r14_2, advancing to the next data block.\r\nBelow we can see the Python algorithm that I developed, which is already available in HashDB for automatic\r\nresolution, through the plugin available for Ghidra, IDA and Binary Ninja.\r\ndef lockbit4_hashing(hashing):\r\n MASK_32BIT = 0xffffffff\r\n hash_value = 0x14bf\r\n char_index = 0\r\n for char in hashing:\r\n char_code = ord(char)\r\n if 0x41 \u003c= char_code \u003c= 0x5A:\r\n normalized_char = (char_code + 0x20) \u0026 MASK_32BIT\r\n else:\r\n normalized_char = char_code\r\n if char_index == 0:\r\n index_modifier = 0\r\n else:\r\n index_modifier = (char_index ^ 0x14bf) \u0026 MASK_32BIT\r\n hash_value = (index_modifier * (((char_index + 0x14bf) * normalized_char + (hash_value ^ normalized_char\r\n char_index += 1\r\n return hash_value\r\nIn the following sequence of images, we can observe the use of HashDB for resolving DLL/API Hashing in\r\nLockbit4.0.\r\nhttps://0x0d4y.blog/lockbit4-0-evasion-tales/\r\nPage 13 of 22\n\nA good example of the Hashing resolution process flow is the code below from Lockbit4.0, where we first see the\r\nresolution of the ntdll.dll Hash and the collection of its offset, followed by the resolution of the EtwEventWrite\r\nAPI Hash, storing them in a Lockbit4.0 custom structure. This piece of code is the beginning of the execution of\r\nthe ETW Patching technique, where Lockbit4.0 will collect the address of the EtwEventWrite API, to overwrite\r\nthe initial API code for the ret opcode (0xc3), thus applying the patch.\r\nAs we can see above, after the resolution, we can see that a function is executed that calculates a fake API address\r\nthrough the lockbit4_calc_fake_api_addr function, and then the real address is calculated and stored once again\r\nin the Lockbit4.0 custom struct. Below, we can see that the calculation for the correct resolution of the API\r\naddress is a simple XOR operation, with values present within the Lockbit4.0 custom struct.\r\nhttps://0x0d4y.blog/lockbit4-0-evasion-tales/\r\nPage 14 of 22\n\nContinuing the analysis of the implementation of the EDR Evasion technique via ETW Patching, and using it as an\r\nexample, to demonstrate the repeatability of the DLL/API resolution technique dynamically, below we can\r\nobserve the execution of zwWriteVirtualMemory, overwriting the EtwEventWrite API with the opcode ret\r\n(0xc3).\r\nAs we saw with the implementation of ETW Patching, all other capabilities depend on this same DLL/API\r\nresolution technique via Hashing. Capabilities such as:\r\nDisabling DLL Notification via the LdrUnRegisterDllNotification API.\r\nDeleting Volume Shadows via the IVssBackupComponents interface with the DeleteSnapshots API.\r\nDisabling the Volume Shadows Management Service via the OpenSCManager, OpenService and\r\nChangeServiceConfig APIs.\r\nEnumerating Networks via APIs such as GetIpNetTable, inet_ntoa, gethostbyaddr and NetShareEnum.\r\nLog deletion through APIs, EvtOpenSession, EvtOpenChannelEnum, EvtNextChannelPath and\r\nEvtClearLog.\r\nAnd so on.\r\nAnalysis of Cryptographic Algorithms for Obfuscation Implemented in Lockbit\r\n4.0\r\nUnlike version 3.0, Lockbit 4.0 implements two algorithms to decrypt Strings and the README that will be\r\ncreated throughout the system. The algorithm to decrypt strings is very simple, being just a logical operation with\r\nXOR, while the algorithm used to decrypt the README is the well-known RC4.\r\nBelow, we can see an example of a moment when Lockbit4.0 implements the algorithm to decrypt multiple\r\nstrings, necessary for later actions.\r\nhttps://0x0d4y.blog/lockbit4-0-evasion-tales/\r\nPage 15 of 22\n\nBelow you can see the algorithm itself, which involves logical operations with an XOR that starts with the key\r\n0x3a, and is changed by the counter in each round of the loop, making each byte have a different XOR key.\r\nhttps://0x0d4y.blog/lockbit4-0-evasion-tales/\r\nPage 16 of 22\n\nBelow is my implementation of this algorithm in Python, followed by the output of its execution.\r\ndef lb4_str_decrypt(data: bytes) -\u003e str:\r\n if len(data) % 2 != 0:\r\n raise ValueError(\"[-] Error [-]\")\r\n \r\n decrypted_chars = []\r\n key = 0x3a\r\n \r\n for i in range(0, len(data), 2):\r\n encrypted_word = int.from_bytes(data[i:i+2], byteorder='little')\r\n decrypted_word = encrypted_word ^ key\r\n \r\n if decrypted_word == 0:\r\n break\r\n \r\n decrypted_chars.append(chr(decrypted_word))\r\n \r\n return ''.join(decrypted_chars)\r\nif __name__ == \"__main__\":\r\n encrypted_data = b'\\x6b\\x00\\x00\\x00\\x66\\x00\\x3a\\x00'\r\n result = lb4_str_decrypt(encrypted_data)\r\n print(\"Decrypted String:\", result)\r\nIn addition to the XOR algorithm above used for string decryption, Lockbit4.0 also implements the well-known\r\nRC4 algorithm, with the aim of decrypting the README that will be written throughout the system during the\r\nexecution of the Ransomware. Below we can see the in-line implementation of the RC4 Algorithm present in\r\nLockbit4.0, within the Main function itself.\r\nhttps://0x0d4y.blog/lockbit4-0-evasion-tales/\r\nPage 17 of 22\n\nWithout any extra obfuscation layers, we are able to identify the RC4 key and the encrypted README.\r\nSince it is a well-known algorithm, and widely used by Malware, it is easy to implement this algorithm in Python.\r\nBelow is my implementation, followed by the output of its execution (I removed the values of the RC4 key and the\r\nlarge block of data from the encrypted README, to keep the visual appearance cleaner).\r\ndef rc4(key: bytes, data: bytes) -\u003e bytes:\r\n # KSA Phase\r\n S = list(range(256))\r\n j = 0\r\n key_length = len(key)\r\nhttps://0x0d4y.blog/lockbit4-0-evasion-tales/\r\nPage 18 of 22\n\n# PRGA Phase\r\n for i in range(256):\r\n j = (j + S[i] + key[i % key_length]) % 256\r\n S[i], S[j] = S[j], S[i]\r\n \r\n # Decryption Phase\r\n i = 0\r\n j = 0\r\n result = bytearray()\r\n for byte in data:\r\n i = (i + 1) % 256\r\n j = (j + S[i]) % 256\r\n S[i], S[j] = S[j], S[i]\r\n K = S[(S[i] + S[j]) % 256]\r\n result.append(byte ^ K)\r\n \r\n return bytes(result)\r\nif __name__ == \"__main__\":\r\n rc4_key = \"RC4_KEY\"\r\n \r\n encrypted_lb4_readme = (\r\n \"ENCRYPTED_README_DATA\"\r\n )\r\n \r\n rc4_key_bytes = bytes.fromhex(rc4_key)\r\n encrypted_readme_bytes = bytes.fromhex(encrypted_lb4_readme)\r\n \r\n decrypted_bytes = rc4(rc4_key_bytes, encrypted_readme_bytes)\r\n \r\n try:\r\n decrypted_lb4_readme = decrypted_bytes.decode(\"utf-8\")\r\n except UnicodeDecodeError:\r\n decrypted_lb4_readme = decrypted_bytes.decode(\"latin1\", errors=\"replace\")\r\n \r\n print(\"\\nLockbit4.0 Decrypted Readme:\")\r\n print(decrypted_lb4_readme)\r\nhttps://0x0d4y.blog/lockbit4-0-evasion-tales/\r\nPage 19 of 22\n\nDetection Engineering - Yara Rules\r\nBelow, contains the YARA rules I produced during the analysis of Lockbit4.0, focused on detecting code patterns\r\nfrom the packed sample, and the unpacked sample.\r\nrule lb4_packer_was_detected\r\n{\r\n meta:\r\n author = \"0x0d4y\"\r\n description = \"Detect the packer used by Lockbit4.0\"\r\n date = \"2024-02-16\"\r\n score = 100\r\n yarahub_reference_md5 = \"15796971D60F9D71AD162060F0F76A02\"\r\n yarahub_uuid = \"f6f57eca-314b-4657-906e-495ea9b92def\"\r\n yarahub_license = \"CC BY 4.0\"\r\n yarahub_rule_matching_tlp = \"TLP:WHITE\"\r\n yarahub_rule_sharing_tlp = \"TLP:WHITE\"\r\n malpedia_family = \"win.lockbit\"\r\n strings:\r\n $unpacking_loop_64b = { 8b 1e 48 83 ee fc 11 db 8a 16 72 e5 8d 41 01 41 ff d3 11 c0 01 db 75 0a }\r\n $jump_to_unpacked_code_64b = { 48 8b 2d 16 0f ?? ?? 48 8d be 00 f0 ?? ?? bb 00 ?? ?? ?? 50 49 89 e1 41 b\r\n $unpacking_loop_32b = { 8A 06 46 88 07 47 01 DB 75 ?? 8B 1E 83 EE ?? 11 DB 72 ?? 9C 29 C0 40 9D 01 DB 75\r\n $jump_to_unpacked_code_32b = { 8b ae ?? ?? ?? ?? 8d be 00 f0 ?? ?? bb 00 ?? ?? ?? 50 54 6a 04 53 57 ff d\r\n condition:\r\n uint16(0) == 0x5a4d and\r\n 1 of ($jump_to_unpacked_code_*) and\r\n 1 of ($unpacking_loop_*)\r\n}\r\nrule lb4_rc4_alg\r\n{\r\n meta:\r\n author = \"0x0d4y\"\r\nhttps://0x0d4y.blog/lockbit4-0-evasion-tales/\r\nPage 20 of 22\n\ndescription = \"Detect the implementation of RC4 Algorithm by Lockbit4.0\"\r\n date = \"2024-02-13\"\r\n score = 100\r\n yarahub_reference_md5 = \"062311F136D83F64497FD81297360CD4\"\r\n yarahub_uuid = \"4de48ced-b9fa-4286-aac4-c263ad20d67d\"\r\n yarahub_license = \"CC BY 4.0\"\r\n yarahub_rule_matching_tlp = \"TLP:WHITE\"\r\n yarahub_rule_sharing_tlp = \"TLP:WHITE\"\r\n malpedia_family = \"win.lockbit\"\r\n strings:\r\n $rc4_alg = { 48 3d 00 01 00 00 74 0c 88 84 04 ?? ?? ?? ?? 48 ff c0 eb ec 29 c9 41 b8 ?? ?? ?? ?? 4c 8d 0\r\n \r\n condition:\r\n uint16(0) == 0x5a4d and\r\n $rc4_alg\r\n}\r\nrule lb4_hashing_alg\r\n{\r\n meta:\r\n author = \"0x0d4y\"\r\n description = \"This rule detects the custom hashing algorithm of Lockbit4.0 unpacked\"\r\n date = \"2024-02-16\"\r\n score = 100\r\n yarahub_reference_md5 = \"062311F136D83F64497FD81297360CD4\"\r\n yarahub_uuid = \"d1a6d555-626d-4625-9da6-e4478cb7a142\"\r\n yarahub_license = \"CC BY 4.0\"\r\n yarahub_rule_matching_tlp = \"TLP:WHITE\"\r\n yarahub_rule_sharing_tlp = \"TLP:WHITE\"\r\n malpedia_family = \"win.lockbit\"\r\n strings:\r\n $hashing_alg = { 41 89 d0 46 0f be 04 00 45 09 c0 74 ?? 45 8d 48 ?? 45 8d 50 ?? 41 80 f9 ?? 45 0f 43 d0\r\n \r\n condition:\r\n uint16(0) == 0x5a4d and\r\n $hashing_alg\r\n}\r\nDetection Engineering - Yara Hunts\r\nWith the YARA rules produced, I carried out a Yara Hunt on UnpacMe and below is the link shared with the\r\nmatches produced by the Hunt with the YARA rules above.\r\nlb4_packer_was_detected ;\r\nlb4_rc4_alg ;\r\nhttps://0x0d4y.blog/lockbit4-0-evasion-tales/\r\nPage 21 of 22\n\nlb4_hashing_alg.\r\nConclusion\r\nThroughout the analysis, Lockbit4.0 presents us with a version that is much more concerned with implementing\r\nObfuscation techniques, such as the DLL/API Hashing technique and the DLL/API address resolution technique\r\ndivided into phases, with the clear purpose of obfuscating its intentions and slowing down the analysis. And we\r\ncan also observe its concern with implementing Endpoint Protection Software Evasion techniques, through\r\ntechniques such as ETW Patching and Disabling DLL Loading Notifications. In addition, it is also possible to\r\nobserve the introduction of the network enumeration technique in an autonomous manner, through the collection\r\nof IP addresses from the ARP Table and the Routing Table, through the IPs mentioned in the research. There is no\r\nsecret in the implementation of this technique, since it is entirely done through the use of Windows APIs, with the\r\nonly layer of complexity being the implementation of the DLL/API resolution technique dynamically.\r\nTherefore, unlike the previous version, this new version of Lockbit ransomware is focused on staying under the\r\nradar.\r\nSource: https://0x0d4y.blog/lockbit4-0-evasion-tales/\r\nhttps://0x0d4y.blog/lockbit4-0-evasion-tales/\r\nPage 22 of 22\n\n  https://0x0d4y.blog/lockbit4-0-evasion-tales/     \nBelow you can see the algorithm itself, which involves logical operations with an XOR that starts with the key\n0x3a, and is changed by the counter in each round of the loop, making each byte have a different XOR key.\n   Page 16 of 22",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"references": [
		"https://0x0d4y.blog/lockbit4-0-evasion-tales/"
	],
	"report_names": [
		"lockbit4-0-evasion-tales"
	],
	"threat_actors": [],
	"ts_created_at": 1775434100,
	"ts_updated_at": 1775791322,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/5cdf67eb5882eb1f489f8260b028f76b7a439ac4.pdf",
		"text": "https://archive.orkl.eu/5cdf67eb5882eb1f489f8260b028f76b7a439ac4.txt",
		"img": "https://archive.orkl.eu/5cdf67eb5882eb1f489f8260b028f76b7a439ac4.jpg"
	}
}