{
	"id": "a8827fde-32ab-42cf-ad9f-9972b5f6c8f1",
	"created_at": "2026-04-06T00:19:48.644358Z",
	"updated_at": "2026-04-10T03:23:51.596683Z",
	"deleted_at": null,
	"sha1_hash": "d8a2d47ea8b18105abaf54283070ade0e2303a10",
	"title": "Tackling Gootkit's Traps",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 54213,
	"plain_text": "Tackling Gootkit's Traps\r\nBy Authors \u0026 Contributors\r\nArchived: 2026-04-05 16:19:01 UTC\r\nGootkit is an advanced banking trojan first discovered in mid-2014. Known for using various techniques to evade\r\ndetection, the malware also has its own unique twists: it’s partially written in JavaScript and it incorporates the\r\nnode.js runtime environment.\r\nGootkit employs several checks in order to identify a virtual environment and halt its propagation sequence once\r\nthat happens. In this post, we’ll demonstrate a way to tackle these anti-research methods in order to have the\r\nsample executing in a virtual environment.\r\nFirst Glance\r\nRunning the sample in a virtual machine results in the creation of several threads, but no modifications to the\r\nsystem are made. Without interruptions, Gootkit’s process would continue to run forever.\r\nRunning the sample in a debugger allows us to interrupt the process and inspect its execution flow. Whenever the\r\ndebugger is paused, Gootkit’s process is sleeping.\r\nAccording to the return address, the call comes from the malware (not from a system library) and the parameter is\r\n-1, meaning: sleep forever.\r\nHow can we make the malware go to a more interesting execution flow in the code?\r\nDiving Deeper into the Assembly Code\r\nLooking at the assembly of the function that calls the sleep API, there are three possible execution paths: #1 and\r\n#2 are paths that we could force the malware to take by patching the conditional jumps that lead to them; #3 is the\r\npath it takes before reaching the sleep API.\r\nThere are 3 possible paths of execution. We’ll use a shortcut in order to decide quickly which one is the most\r\ninteresting one to follow. That shortcut involves checking cross-references(xrefs) from each function, which will\r\nindicate where most of the activity would take place.\r\nExecution path #1, contains a call to sub_4047EC, which had no xrefs.\r\nExecution path #2, contains a call to sub_409450, which had just a few xrefs.\r\nExecution path #3 creates a thread that will execute sub_40C8C3, which had many xrefs.\r\nSo, execution path #3, the newly created thread, seems like the one to take because it has a massive function\r\ngraph, indicating that all the activity will most likely happen there.\r\nhttps://www.f5.com/labs/articles/threat-intelligence/tackling-gootkit-s-traps\r\nPage 1 of 4\n\nAfter placing a breakpoint at the beginning of the thread function(0x40C8C3), we start stepping through. One of\r\nthe subroutines gets stuck in an endless loop, which means Gootkit is sleeping again!\r\nRecap #1: There are two threads, one of which is sleeping forever, but it is not the interesting one. The second one\r\nis in a sleep loop, and our goal is to force it to take a different branch in order to execute the interesting\r\nfunctionality.\r\nThe only way to break the loop is to make the string comparison(StrStrIW) fail. The comparison parameters:\r\nHard-coded string “Xeon”\r\nData from\r\nHKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0\\ProcessorNameString\r\nIf we changed this value in the registry, we could successfully overcome this hurdle. But, would that be the end?\r\nWould it run? Sadly, after making this change, Gootkit goes back to sleep in another endless loop, this time in a\r\ndifferent subroutine.\r\nRecap #2: Again, there are two threads, one that is sleeping forever (but not interesting); the other is running in a\r\nsleep loop.\r\nAfter inspecting the condition that leads us to the sleep API call, it becomes clear that this branch is taken when\r\nthe “GetProcAddress” fails. But, why would this simple API fail?\r\nAt second glance, it is evident that the first parameter of the “GetProcAddress” API, the HModule, is always equal\r\nto null because of the XOR instruction highlighted in red. The conclusion is that this API is meant to fail! This\r\nwhole function is a trap, and our goal is to avoid entering it altogether.\r\nFollowing the Traps\r\nWe’ll rename the function to “trap” and then check all the function references to see how many traps are there.\r\nThere are 17 traps to overcome and they are all in the same function, so we assume this is the function making all\r\nthe environment checks. We’ll have to check each condition that leads to a trap and make sure to change the\r\nenvironment (or patch the binary) in a way that would bypass the trap.\r\nFigure 8 shows a code snippet from the sub_409AE2 function.\r\nNote that each “while” loop is performing string decryption on the sequences of bytes shown in the variables\r\nabove the loop. When following the execution in a debugger, the strings are decrypted, and some meaningful\r\nindicators of VM checks are visible. (See appendix for decryption function details.)\r\nIn this code snippet, three checks are evident:\r\nMAC address check\r\nChecking the presence of “dbghelp.dll” — debugger indicator\r\nhttps://www.f5.com/labs/articles/threat-intelligence/tackling-gootkit-s-traps\r\nPage 2 of 4\n\nChecking the presence of “sbiedll.dll” — sandboxie indicator\r\nBy following the traps and patching the system accordingly, the environment is prepared for Gootkit to run in.\r\nThe rest of the checks include:\r\nCompare user name to \"CurrentUser\"/\"Sandbox\"\r\nCompare computer name to \"SANDBOX\"/\"7SILVIA\"\r\nHKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\SystemBiosVersion\" compare with AMI,\r\nVirtualBox, BOCHS, INTEL 640000, 55274-640-2673064-23950, and other serials\r\nAfter patching a virtual machine and running the sample, it’s clear that it is no longer stuck in an endless loop and\r\nthat the sample continues its propagation in the system.\r\nThe Road Not Taken\r\nPatching the virtual environment by changing the MAC address and several registry values is one way to get the\r\nsample to run. It is the recommended way to go when running Gootkit samples in an automated analysis system.\r\nHowever, this post would not be complete without presenting the other solution: patching the binary itself.\r\nEvery time there’s a conditional jump to the trap function, one could change it to a simple jump opcode, thus\r\nskipping the trap function. This would direct the flow from the jump to the next code block while skipping the trap\r\nfunction.\r\nHowever, this patch must be applied 17 times, and that’s a tedious task. Instead, since all the calls to the trap\r\nfunction come from one function, its preferable to patch and bypass the call to that function.\r\nConclusion\r\nWhen analyzing malware, especially intricate, multi-threaded ones like Gootkit, it is important to choose the\r\ninteresting thread to follow and to keep in mind that even functions that look like they contain seemingly\r\n“ordinary” code may be used as a deception.\r\nMD5: 47f8d6d3e4410218c4c64701cb7d7d3d\r\nAppendix\r\nThe decryption function is an XOR loop with a hard-coded key. The encrypted string and the key are hard-coded\r\nsequences of bytes.\r\nIn Python:\r\nAn example of the string decryption loop and its parameters:\r\nencrypted_string = \"\\x3A\\x03\\x05\\x15\\x55\\x0E\\x25\\x4F\\x08\\x1C\\x5D\\x62\"\r\nhttps://www.f5.com/labs/articles/threat-intelligence/tackling-gootkit-s-traps\r\nPage 3 of 4\n\nkey = \"\\x49\\x61\\x6C\\x70\\x31\\x62\"\r\nSource: https://www.f5.com/labs/articles/threat-intelligence/tackling-gootkit-s-traps\r\nhttps://www.f5.com/labs/articles/threat-intelligence/tackling-gootkit-s-traps\r\nPage 4 of 4",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"ETDA",
		"Malpedia"
	],
	"references": [
		"https://www.f5.com/labs/articles/threat-intelligence/tackling-gootkit-s-traps"
	],
	"report_names": [
		"tackling-gootkit-s-traps"
	],
	"threat_actors": [
		{
			"id": "d90307b6-14a9-4d0b-9156-89e453d6eb13",
			"created_at": "2022-10-25T16:07:23.773944Z",
			"updated_at": "2026-04-10T02:00:04.746188Z",
			"deleted_at": null,
			"main_name": "Lead",
			"aliases": [
				"Casper",
				"TG-3279"
			],
			"source_name": "ETDA:Lead",
			"tools": [
				"Agentemis",
				"BleDoor",
				"Cobalt Strike",
				"CobaltStrike",
				"RbDoor",
				"RibDoor",
				"Winnti",
				"cobeacon"
			],
			"source_id": "ETDA",
			"reports": null
		}
	],
	"ts_created_at": 1775434788,
	"ts_updated_at": 1775791431,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/d8a2d47ea8b18105abaf54283070ade0e2303a10.pdf",
		"text": "https://archive.orkl.eu/d8a2d47ea8b18105abaf54283070ade0e2303a10.txt",
		"img": "https://archive.orkl.eu/d8a2d47ea8b18105abaf54283070ade0e2303a10.jpg"
	}
}