{
	"id": "1159a693-f624-47ac-866c-960f3c4abeba",
	"created_at": "2026-04-06T01:31:46.8362Z",
	"updated_at": "2026-04-10T03:21:10.71632Z",
	"deleted_at": null,
	"sha1_hash": "0948ea10634dbb40171cd779d87cfc5cba45bcb5",
	"title": "TrueBot Analysis Part II - Static unpacker",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 1013745,
	"plain_text": "TrueBot Analysis Part II - Static unpacker\r\nBy Robert Giczewski\r\nPublished: 2023-02-18 · Archived: 2026-04-06 00:23:29 UTC\r\nIn my last post, I described how to identify the decryption key, the encrypted blob and how the decryption\r\nalgorithm works in a packed TrueBot sample. Doing this manually with help of your favorite\r\nDisassembler/Decompiler is quite easy, but I guess, that’s not why you are here. What we want is a static\r\nunpacker, written in Python, without using any external tools or too many dependencies (except Malduck 🦆).\r\nDepending on the sample you’re analyzing, writing a static unpacker can be a challenging task, especially if\r\nyou’re dealing with several layers of encryption, junk code, control-flow obfuscation and so on.\r\nFortunately, TrueBot doesn’t make it particularly difficult for us here. Nevertheless, the code will end up looking a\r\nbit ugly since we want to write an unpacker for all samples available to us. But maybe that is just because of my\r\nprogramming style. By the way, I do not use a lot of error handling in my code so please be merciful.\r\nIn a nutshell, the basic methodology for our code looks as follows:\r\n1. Identify the encrypted blob, ideally with its length.\r\n2. Locate and parse the decryption key and the value for the AND operation.\r\n3. Decrypt and save the dump.\r\nAs I already described in Part I of my analysis, the most common variant in those packed samples is a DLL Export\r\nwhich directly calls the decryption function with the offset of the decrypted blob and the blob size as arguments.\r\nThis call can be identified easily and without false positives, at least in the samples I analyzed. In order to\r\naccomplish this, we use Malduck, our “ducky companion in malware analysis journeys”.\r\nTo find the call, we utilize Malduck’s built-in Yara wrapper, looking for the two pushes and the beginning of the\r\ncall, see the green box in the screenshot above. Since we don’t know the exact size of the blob, we’re using the\r\nwildcards ?? and estimate that the size is between 0x40000 and 0x6FFFF.\r\npe = malduck.procmempe.from_file(filename=abs_file_path, image=True)\r\ns1 = YaraString('68 ?? ?? (04 | 05 | 06) 00 68 ?? ?? ?? ?? E8',\r\n type=YaraString.HEX)\r\nhttps://malware.love/malware_analysis/reverse_engineering/2023/02/18/analyzing-truebot-static-unpacking.html\r\nPage 1 of 4\n\ndecrypt_blob_call = Yara(name=\"decrypt_blob_call\", strings={\"call\": s1}, condition=\"all of them\")\r\nmatch = pe.yarav(ruleset=decrypt_blob_call)\r\noffset = None\r\nif match:\r\n for _, v in match.elements[\"decrypt_blob_call\"].elements.items():\r\n offset = v[0] # there should only be one match (hopefully)\r\nOur file is loaded as memory-mapped PE file, so we will use yarav() to perform yara matching region-wise.\r\nThis will also help us to debug more easily because we can confirm matching offsets in our Disassembler (check\r\nthe hex value against the virtual address in the screenshot above).\r\nSince we are now ( 0x10001620 in this example) near the position where the decryption function is called, we can\r\ndetermine the length and the virtual address of the encrypted blob and also get the virtual address of the\r\ndecryption function.\r\nTo get the blob size, we need to read 4 bytes, starting from the identified address before +1 (because of the push\r\nopcode), see the screenshot below.\r\nYou can either call pe.readv(addr, length) or just use Malduck’s handy helper functions like uint32v(addr)\r\nwhich for example reads an unsigned 32-bit value at the given address.\r\nblob_size = pe.uint32v(vaddr + 1) #Read unsigned 32-bit value at address.\r\nhttps://malware.love/malware_analysis/reverse_engineering/2023/02/18/analyzing-truebot-static-unpacking.html\r\nPage 2 of 4\n\nGetting the virtual address where the decrypted blob is stored, works similar.\r\nblob_va = pe.uint32v(vaddr + 1 + 4 + 1)\r\nNow we only need the key and the value for the “AND” operation to decrypt the blob. The approach is similar to\r\nthe one already described above. We know the virtual address of the decryption functions and have an\r\napproximate idea how big the function is. Therefore, we can now search for the required information in between\r\nthis function, see the code to find the key here and to find the value for the “AND” operation here.\r\nAfter collecting the blob and the decryption material, we should be able to decrypt the blob with help of the\r\ndecryption function mentioned in Part I of this series.\r\nI’ve published the whole code on github and tested against all the samples available to me. When running the\r\nscript on all samples, it should look like this.\r\nhttps://malware.love/malware_analysis/reverse_engineering/2023/02/18/analyzing-truebot-static-unpacking.html\r\nPage 3 of 4\n\nLike most static unpackers/config extractors/etc., this code might break easily if some bytes at specific positions\r\nchange and you will probably have to continuously adapt the Unpacker to new samples. I am therefore very\r\ninterested in new samples. If someone has some, please get in touch with me.\r\nNow that we have a bunch of unpacked samples, the next post in this series will focus on TrueBot’s capabilities\r\nbefore we then write a Config Extractor using Python and Malduck.\r\nSource: https://malware.love/malware_analysis/reverse_engineering/2023/02/18/analyzing-truebot-static-unpacking.html\r\nhttps://malware.love/malware_analysis/reverse_engineering/2023/02/18/analyzing-truebot-static-unpacking.html\r\nPage 4 of 4\n\nYou can either call which for example reads pe.readv(addr, an unsigned length) or just 32-bit value at use Malduck’s handy the given address. helper functions like uint32v(addr)\nblob_size = pe.uint32v(vaddr + 1) #Read unsigned 32-bit value at address.\n   Page 2 of 4",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"references": [
		"https://malware.love/malware_analysis/reverse_engineering/2023/02/18/analyzing-truebot-static-unpacking.html"
	],
	"report_names": [
		"analyzing-truebot-static-unpacking.html"
	],
	"threat_actors": [],
	"ts_created_at": 1775439106,
	"ts_updated_at": 1775791270,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/0948ea10634dbb40171cd779d87cfc5cba45bcb5.pdf",
		"text": "https://archive.orkl.eu/0948ea10634dbb40171cd779d87cfc5cba45bcb5.txt",
		"img": "https://archive.orkl.eu/0948ea10634dbb40171cd779d87cfc5cba45bcb5.jpg"
	}
}