{
	"id": "565dd42f-875f-4094-a14a-035d2c9ff732",
	"created_at": "2026-04-06T00:06:08.786545Z",
	"updated_at": "2026-04-10T03:22:09.927946Z",
	"deleted_at": null,
	"sha1_hash": "bdeea2f239d160aa29b5d6b6817e3eff0b5360fc",
	"title": "Getting gooey with GULOADER: deobfuscating the downloader",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 807271,
	"plain_text": "Getting gooey with GULOADER: deobfuscating the downloader\r\nBy Daniel Stepanic\r\nPublished: 2023-12-06 · Archived: 2026-04-05 14:11:52 UTC\r\nOverview\r\nElastic Security Labs continues to monitor active threats such as GULOADER, also known as CloudEyE – an evasive\r\nshellcode downloader that has been highly active for years while under constant development. One of these recent changes\r\nis the addition of exceptions to its Vectored Exception Handler (VEH) in a fresh campaign, adding more complexity to its\r\nalready long list of anti-analysis tricks.\r\nWhile GULOADER’s core functionality hasn’t changed drastically over the past few years, these constant updates in their\r\nobfuscation techniques make analyzing GULOADER a time-consuming and resource-intensive process. In this post, we will\r\ntouch on the following topics when triaging GULOADER:\r\nReviewing the initial shellcode and unpacking process\r\nFinding the entrypoint of the decrypted shellcode\r\nDiscuss update to GULOADER’s VEH that obfuscates control flow\r\nProvide a methodology to patch out VEH\r\nInitial Shellcode\r\nIn our sample, GULOADER comes pre-packaged inside an NSIS (Nullsoft Scriptable Install System) installer. When the\r\ninstaller is extracted, the main components are:\r\nNSIS Script - This script file outlines all the various configuration and installation aspects.\r\nExtracted NSIS contents\r\nSystem.dll - Located under the $PLUGINSDir . This file is dropped in a temporary folder to allocate/execute the\r\nGULOADER shellcode.\r\nSystem.Dll exports\r\nShellcode - The encrypted shellcode is buried into a nested folder.\r\nhttps://www.elastic.co/security-labs/getting-gooey-with-guloader-downloader\r\nPage 1 of 10\n\nOne quick methodology to pinpoint the file hosting the shellcode can be done by monitoring ReadFile events from\r\nSysInternal’s Process Monitor after executing GULOADER. In this case, we can see that the shellcode is read in from a file\r\n( Fibroms.Hag ).\r\nShellcode Retrieved from File\r\nGULOADER executes shellcode through callbacks using different Windows API functions. The main reasoning behind this\r\nis to avoid detections centered around traditional Windows APIs used for process injection, such as CreateRemoteThread or\r\nWriteProcessMemory . We have observed EnumResourceTypesA and CallWindowProcW used by GULOADER.\r\nEnumResourceTypesA Function Call inside GULOADER\r\nBy reviewing the MSDN documentation for EnumResourceTypesA , we can see the second parameter expects a pointer to the\r\ncallback function. From the screenshot above, we can see that the newly allocated shellcode is placed into this argument.\r\nEnumResourceTypesA Function Parameters\r\nShellcode from second parameter EnumResourceTypesA call\r\nFinding Main Shellcode Entrypoint\r\nIn recent samples, GULOADER has increased the complexity at the start of the initial shellcode by including many different\r\njunk instructions and jumps. Reverse engineering of the downloader can require dealing with a long process of unwinding\r\nhttps://www.elastic.co/security-labs/getting-gooey-with-guloader-downloader\r\nPage 2 of 10\n\ncode obfuscation designed to break disassembly and control flow in some tooling, making it frustrating to find the actual\r\nstart of the core GULOADER shellcode.\r\nOne methodology for finding the initial call can be leveraging graph view inside x64dbg and using a bottom-to-top approach\r\nto look for the call eax instruction.\r\nGraph view for GULOADER main entrypoint call\r\nAnother technique to trace the initial control flow involves leveraging the reversing engineering framework Miasm. Below\r\nis a quick example where we can pass in the shellcode and disassemble the instructions to follow the flow:\r\nfrom miasm.core.locationdb import LocationDB\r\nfrom miasm.analysis.binary import Container\r\nfrom miasm.analysis.machine import Machine\r\nwith open(\"proctoring_06BF0000.bin\", \"rb\") as f:\r\n code = f.read()\r\nloc_db = LocationDB()\r\nc = Container.from_string(code, loc_db)\r\nmachine = Machine('x86_32')\r\nmdis = machine.dis_engine(c.bin_stream, loc_db=loc_db)\r\nhttps://www.elastic.co/security-labs/getting-gooey-with-guloader-downloader\r\nPage 3 of 10\n\nmdis.follow_call = True\r\nmdis.dontdis_retcall = True\r\nasm_cfg = mdis.dis_multiblock(offset=0x1400)\r\nMiasm cuts through the 142 jmp instructions and navigates through the junk instructions where we have configured it to\r\nstop on the call instruction to EAX (address: 0x3bde ).\r\nJMP loc_3afd\r\n-\u003e c_to:loc_3afd\r\nloc_3afd\r\nMOV EBX, EAX\r\nFADDP ST(3), ST\r\nPANDN XMM7, XMM2\r\nJMP loc_3b3e\r\n-\u003e c_to:loc_3b3e\r\nloc_3b3e\r\nSHL CL, 0x0\r\nPSRAW MM1, MM0\r\nPSRLD XMM1, 0xF1\r\nJMP loc_3b97\r\n-\u003e c_to:loc_3b97\r\nloc_3b97\r\nCMP DL, 0x3A\r\nPADDW XMM3, XMM5\r\nPXOR MM3, MM3\r\nJMP loc_3bde\r\n-\u003e c_to:loc_3bde\r\nloc_3bde\r\nCALL EAX\r\nTail end of Miasm\r\nGULOADER’s VEH Update\r\nOne of GULOADER’s hallmark techniques is centered around its Vectored Exception Handling (VEH) capability. This\r\nfeature gives Windows applications the ability to intercept and handle exceptions before they are routed through the standard\r\nexception process. Malware families and software protection applications use this technique to make it challenging for\r\nanalysts and tooling to follow the malicious code.\r\nGULOADER starts this process by adding the VEH using RtlAddVectoredExceptionHandler . Throughout the execution of\r\nthe GULOADER shellcode, there is code purposely placed to trigger these different exceptions. When these exceptions are\r\ntriggered, the VEH will check for hardware breakpoints. If not found, GULOADER will modify the EIP directly through the\r\nCONTEXT structure using a one-byte XOR key (changes per sample) with a one-byte offset from where the exception\r\noccurred. We will review a specific example of this technique in the subsequent section. Below is the decompilation of our\r\nsample’s VEH:\r\nhttps://www.elastic.co/security-labs/getting-gooey-with-guloader-downloader\r\nPage 4 of 10\n\nDecompilation of VEH\r\nAlthough this technique is not new, GULOADER continues to add new exceptions over time; we have recently observed\r\nthese two exceptions added in the last few months:\r\nEXCEPTION_PRIV_INSTRUCTION\r\nEXCEPTION_ILLEGAL_INSTRUCTION\r\nAs new exceptions get added to GULOADER, it can end up breaking tooling used to expedite the analysis process for\r\nresearchers.\r\nEXCEPTION_PRIV_INSTRUCTION\r\nLet’s walk through the two recently added exceptions to follow the VEH workflow. The first exception\r\n( EXCEPTION_PRIV_INSTRUCTION ), occurs when an attempt is made to execute a privileged instruction in a processor’s\r\ninstruction set at a privilege level where it’s not allowed. Certain instructions, like the example below with WRSMR expect\r\nprivileges from the kernel level, so when the program is run from user mode, it will trigger the exception due to incorrect\r\npermissions.\r\nEXCEPTION_PRIV_INSTRUCTION triggered by wrmsr instruction\r\nhttps://www.elastic.co/security-labs/getting-gooey-with-guloader-downloader\r\nPage 5 of 10\n\nEXCEPTION_ILLEGAL_INSTRUCTION\r\nThis exception is invoked when a program attempts to execute an invalid or undefined CPU instruction. In our sample, when\r\nwe run into Intel virtualization instructions such as vmclear or vmxon , this will trigger an exception.\r\nEXCEPTION_ILLEGAL_INSTRUCTION triggered by vmclear instruction\r\nOnce an exception occurs, the GULOADER VEH code will first determine which exception code was responsible for the\r\nexception. In our sample, if the exception matches any of the five below, the code will take the same path regardless.\r\nEXCEPTION_ACCESS_VIOLATION\r\nEXCEPTION_ILLEGAL_INSTRUCTION\r\nEXCEPTION_PRIV_INSTRUCTION\r\nEXCEPTION_SINGLE_STEP\r\nEXCEPTION_BREAKPOINT\r\nGULOADER will then check for any hardware breakpoints by walking the CONTEXT record found inside the\r\nEXCEPTION_POINTERS structure. If hardware breakpoints are found in the different debug registers, GULOADER will\r\nreturn a 0 into the CONTEXT record, which will end up causing the shellcode to crash.\r\nGULOADER monitoring hardware breakpoints\r\nIf there are no hardware breakpoints, GULOADER will retrieve a single byte which is 7 bytes away from the address that\r\ncaused the exception. When using the last example with vmclear , it would retrieve byte ( 0x8A ).\r\nGULOADER retrieves a single byte, 7 bytes away from the instruction, causing an exception\r\nThen, using that byte, it will perform an XOR operation with a different hard-coded byte. In our case ( 0xB8 ), this is unique\r\nper sample. Now, with a derived offset 0x32 ( 0xB8 ^ 0x8A ), GULOADER will modify the EIP address directly from the\r\nhttps://www.elastic.co/security-labs/getting-gooey-with-guloader-downloader\r\nPage 6 of 10\n\nCONTEXT record by adding 0x32 to the previous address ( 0x7697630 ) that caused the exception resulting in the next\r\ncode to execute from address ( 0x7697662 ).\r\nJunk instructions in between exceptions\r\nWith different junk instructions in between, and repeatedly hitting exceptions (we counted 229 unique exceptions in our\r\nsample), it’s not hard to see why this can break different tooling and increase analyst time.\r\nControl Flow Cleaning\r\nTo make following the control flow easier, an analyst can bypass the VEH by tracing the execution, logging the exceptions,\r\nand patching the shellcode using the previously discussed EIP modification algorithm. For this procedure, we leveraged\r\nTinyTracer, a tool written by @hasherezade that leverages Pin, a dynamic binary instrumentation framework. This will\r\nallow us to catch the different addresses that triggered the exception, so using the example above with vmclear , we can see\r\nthe address was 0x7697630 , generated an exception calling KiUserExceptionDispatcher , a function responsible for\r\nhandling user-mode exceptions.\r\nOnce all the exceptions are collected and filtered, these can be passed into an IDAPython script where we walk through each\r\naddress, calculate the offset using the 7th byte over and XOR key ( 0xB8 ), then patch out all the instructions generating\r\nexceptions with short jumps.\r\nThe following image is an example of patching instructions that trigger exceptions at addresses 0x07697630 and\r\n0x0769766C .\r\nDisassembly of patched instructions\r\nBelow is a graphic representing the control flow graph before the patching is applied globally. Our basic block with the\r\nvmclear instruction is highlighted in orange. By implementing the VEH, GULOADER flattens the control flow graph,\r\nhttps://www.elastic.co/security-labs/getting-gooey-with-guloader-downloader\r\nPage 7 of 10\n\nmaking it harder to trace the program logic.\r\nGULOADER’s control flow flattening obfuscation\r\nAfter patching the VEH with jmp instructions, this transforms the basic blocks by connecting them together, reducing the\r\ncomplexity behind the flow of the shellcode.\r\nGULOADER’s call graph obfuscation\r\nUsing this technique can accelerate the cleaning process, yet it’s important to note that it isn’t a bulletproof method. In this\r\ninstance, there still ends up being a good amount of code/functionality that will still need to be analyzed, but this definitely\r\ngoes a long way in simplifying the code by removing the VEH. The full POC script is located here.\r\nConclusion\r\nhttps://www.elastic.co/security-labs/getting-gooey-with-guloader-downloader\r\nPage 8 of 10\n\nGULOADER has many different features that can break disassembly, hinder control flow, and make analysis difficult for\r\nresearchers. Despite this and the process being imperfect, we can counter these traits through different static or dynamic\r\nprocesses to help reduce the analysis time. For example, we observed that with new exceptions in the VEH, we can still\r\ntrace through them and patch the shellcode. This process will set the analyst on the right path, closer to accessing the core\r\nfunctionality with GULOADER.\r\nBy sharing some of our workflow, we hope to provide multiple takeaways if you encounter GULOADER in the wild. Based\r\non GULOADER’s changes, it's highly likely that future behaviors will require new and different strategies. For detecting\r\nGULOADER, the following section includes YARA rules, and the IDAPython script from this post can be found here. For\r\nnew updates on the latest threat research, check out our malware analysis section by the Elastic Security Labs team.\r\nYARA\r\nElastic Security has created different YARA rules to identify this activity. Below is an example of one YARA rule to identify\r\nGULOADER.\r\nrule Windows_Trojan_Guloader {\r\n meta:\r\n author = \"Elastic Security\"\r\n creation_date = \"2023-10-30\"\r\n last_modified = \"2023-11-02\"\r\n reference_sample = \"6ae7089aa6beaa09b1c3aa3ecf28a884d8ca84f780aab39902223721493b1f99\"\r\n severity = 100\r\n arch = \"x86\"\r\n threat_name = \"Windows.Trojan.Guloader\"\r\n license = \"Elastic License v2\"\r\n os = \"windows\"\r\n strings:\r\n $djb2_str_compare = { 83 C0 08 83 3C 04 00 0F 84 [4] 39 14 04 75 }\r\n $check_exception = { 8B 45 ?? 8B 00 38 EC 8B 58 ?? 84 FD 81 38 05 00 00 C0 }\r\n $parse_mem = { 18 00 10 00 00 83 C0 18 50 83 E8 04 81 00 00 10 00 00 50 }\r\n $hw_bp = { 39 48 0C 0F 85 [4] 39 48 10 0F 85 [4] 39 48 14 0F 85 [7] 39 48 18 }\r\n $scan_protection = { 39 ?? 14 8B [5] 0F 84 }\r\n condition:\r\n 2 of them\r\n}\r\nObservations\r\nAll observables are also available for download in both ECS and STIX format.\r\nThe following observables were discussed in this research.\r\nObservable Type Name Reference\r\n6ae7089aa6beaa09b1c3aa3ecf28a884d8ca84f780aab39902223721493b1f99\r\nSHA-256\r\nWindows.Trojan.Guloader\r\nGULOADE\r\ndownloader\r\n101.99.75[.]183/MfoGYZkxZIl205.bin url NA\r\nGULOADE\r\nC2 URL\r\n101.99.75[.]183\r\nipv4-\r\naddr\r\nNA\r\nGULOADE\r\nC2 IP\r\nhttps://www.elastic.co/security-labs/getting-gooey-with-guloader-downloader\r\nPage 9 of 10\n\nReferences\r\nhttps://github.com/elastic/labs-releases/tree/main/tools/guloader\r\nhttps://malpedia.caad.fkie.fraunhofer.de/details/win.cloudeye\r\nSource: https://www.elastic.co/security-labs/getting-gooey-with-guloader-downloader\r\nhttps://www.elastic.co/security-labs/getting-gooey-with-guloader-downloader\r\nPage 10 of 10",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"ETDA",
		"Malpedia"
	],
	"references": [
		"https://www.elastic.co/security-labs/getting-gooey-with-guloader-downloader"
	],
	"report_names": [
		"getting-gooey-with-guloader-downloader"
	],
	"threat_actors": [],
	"ts_created_at": 1775433968,
	"ts_updated_at": 1775791329,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/bdeea2f239d160aa29b5d6b6817e3eff0b5360fc.pdf",
		"text": "https://archive.orkl.eu/bdeea2f239d160aa29b5d6b6817e3eff0b5360fc.txt",
		"img": "https://archive.orkl.eu/bdeea2f239d160aa29b5d6b6817e3eff0b5360fc.jpg"
	}
}