{
	"id": "ceba5ea9-a23d-477f-96ad-16a0871f29d3",
	"created_at": "2026-04-06T00:13:32.80686Z",
	"updated_at": "2026-04-10T13:11:46.27162Z",
	"deleted_at": null,
	"sha1_hash": "bf3ffb9774b7389446bdca157854cd5341b0c4b9",
	"title": "UPX Anti-Unpacking Techniques in IoT Malware",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 79318,
	"plain_text": "UPX Anti-Unpacking Techniques in IoT Malware\r\nBy Albert Zsigovits\r\nPublished: 2020-08-17 · Archived: 2026-04-05 13:15:28 UTC\r\nAttackers are always at the forefront of inventing new techniques to stay covert. It comes by no surprise that their\r\ntradecraft is also subject to continuous improvement and development. One interesting facet of their tactics is how\r\nthey are utilizing binary packing. Packing plays an important part in evasion and covert deploying of malicious\r\nbinaries: \r\nIt helps attackers to avoid endpoint anti-virus detection software when deploying the malicious binary to\r\nthe target device \r\nPacking reduces the size of the binary on disk and in transit significantly: this comes handy, when low\r\nvisibility is required from the attacker’s side or for instance, an exploit kit requires small binaries to be\r\ndelivered, otherwise it would break, and crooks would not be able to disseminate malicious binaries\r\nproperly\r\nPacking also enables to hide plain-text strings seen normally in the binary, throttling the analysis that\r\ndefenders may do on the binary\r\nA Primer on Packing \r\nThere are commonly four packer types that we distinguish, but oftentimes the boundary between these might be\r\nthin. These are: \r\nCompressors: greatly reduces the size of the binary\r\nCryptors: using cryptographic algorithm to obfuscate the contents of the binary\r\nProtectors: used widely as copyright protection, for ex.: Virtual machine (VM)-based digital rights and\r\ncopy protection\r\nInstaller: binary wrapped around an installer for easy installment\r\nThere are many ways of identifying packed binaries:  \r\nExamining visual representation of the binary: to explore similarities by visualizing certain byte patterns of\r\nthe binary; other application of it is to spot important structures in the binary or to analyze given file\r\nformats in order to better understand it\r\nNon-standard section names \r\nSection with both Writable and Execute permissions may be a possible sign of a packer \r\nAddress of entry point somewhere else, then in the first section\r\nPresence of certain function calls\r\nIncreased entropy: taking the frequency of each byte value that are present in each block or section, then\r\napplying a certain entropy formula to calculate entropy scores for given sections: higher entropy scores\r\nmay indicate the presence of encryption or packing\r\nhttps://cujo.com/upx-anti-unpacking-techniques-in-iot-malware/\r\nPage 1 of 9\n\nVery few imports and very few recognizable strings\r\nUsing file identification tools (file, trid, etc..)\r\nUnpacking mechanism\r\nA simple routine stub code is embedded into the now packed binary, that also acts as the entry point. As it starts\r\nrunning, it will allocate a new memory region in which it unpacks the original code. Then the program code\r\njumps to the Original Entry Point (OEP) and continues with the execution of the original, unpacked program.\r\nOne of most known packers is UPX. It is an open-source implementation of an advanced file compressor,\r\nsupporting lots of executable types, Linux and Windows too. Over the years, UPX has been judged both as a\r\nlegitimate and a gray zone tool, as both innocent and malicious programs like to use and abuse it commonly.\r\nUPX has been abused in a few different ways for many years:\r\nUse of Vanilla UPX: malware developers just take the original UPX compressor and apply it to their\r\nmalware. Easy to unpack, either automatically or manually. \r\nUse of Vanilla UPX, then the packed binary is hex modified: from an attacker perspective, the goal is to\r\nbreak automatic unpacking by modifying some hex bytes. This will break the automatic upx –\r\nd unpacking method. Some of the most common modifications include:\r\nRewriting the UPX! magic headers\r\nELF magic bytes are modified\r\nCopyright string is modified\r\nSection header names are modified\r\nExtra junk bytes added throughout the binary\r\nCustom UPX: since UPX is open source, anyone can go and look at its source code on Github and modify\r\ncertain methods or re-write complete functions. Once the custom UPX program code is compiled, and then\r\napplied to a malicious binary, there is no way to get a full picture and understand its custom functions or\r\nmodified routines right off the bat. Our only resort to understand the mechanism of the custom packing is\r\nto manually reverse engineer it.\r\nUPX Header Structures  Abused\r\nSince it was not an easy task to find abused, malicious packed binaries to every plot, we created skeleton,\r\npacked programs, that will raise different error messages on different abuse scenarios, to try to emulate all\r\nexceptions:\r\np_info corrupted\r\nhttps://cujo.com/upx-anti-unpacking-techniques-in-iot-malware/\r\nPage 2 of 9\n\nl_info corrupted\r\np_info corrupted\r\nIn order to understand what each of these corruption means, we need to dig further down and try to understand\r\nhow the UPX header builds up after packing. We will find valuable information in the open source project’s\r\nsource code, inside linux.h:\r\nstruct b_info // 12-byte header before each compressed block\r\n{ \r\nuint32_t sz_unc; // uncompressed_size\r\nuint32_t sz_cpr; // compressed_size\r\nunsigned char b_method; // compression algorithm\r\nunsigned char b_ftid; // filter id\r\nunsigned char b_cto8; // filter parameter\r\nunsigned char b_unused; // unused\r\n}; \r\nstruct l_info // 12-byte trailer in header for loader (offset 116)\r\n{ \r\nuint32_t l_checksum; // checksum\r\nuint32_t l_magic; // UPX! magic [55 50 58 21]\r\nuint16_t l_lsize; // loader size\r\nuint8_t l_version; // version info\r\nuint8_t l_format; // UPX format \r\n}; \r\nstruct p_info // 12-byte packed program header follows stub loader\r\n{ \r\nhttps://cujo.com/upx-anti-unpacking-techniques-in-iot-malware/\r\nPage 3 of 9\n\nuint32_t p_progid; // program header id [00 00 00 00]\r\nuint32_t p_filesize; // filesize [same as blocksize]\r\nuint32_t p_blocksize; // blocksize [same as filesize] \r\n}; \r\nSo, each of these structs store important information for the packer to work properly, so when the unpacking\r\nmethod is initiated, the target program is uncompressed as intended. If the corresponding hex values to these\r\nstructs are altered, we will get the previously seen error messages.\r\nRundown\r\nNow that we have an understanding how the fields and structures build up, let’s look at an example where UPX\r\nhas been abused in some way or shape. Looking at the following hash:\r\nbc88a57e1203f5eec08d34b59d9de43fa121f9d92cc773c17ebfbe848a2f88cd\r\nPacked malware UPX header\r\nPacked malware UPX trailer\r\nWe need to focus on the underlined hex values. The trained eye will immediately spot\r\nthat at 0x98, the UPX! Magic header has been altered with the hex bytes of YTS. That is part of\r\nthe l_info structure. If we go further and try to match the bytes with the previously shown code structure, it is clear\r\nthat “20 08” is the loader size. 0D is the version info and 0C should be the UPX\r\nformat. Right after l_info structure is the p_info structure at 0xA0. From the source code we know that p_progid\r\nshould be “00 00 00 00″. After that comes p_filesize and p_blocksize, both storing the same size value, but in\r\nour case, it has been altered and erased. Fortunately, the value for the filesize and blocksize is also stored\r\nat 0x5C80, which is “58 B2 00 00”. We just need to put this value into p_filesize and p_blocksize. The values at\r\nthese 3 offsets should always be the same. We also see in the trailer section that the string YTS appears twice. We\r\nalso need to alter these back to UPX! (55 50 58 21). The trailer section also contains “0D 0C” again, which is the\r\nversion and format info from the l_info. \r\nWe looked at l_info and p_info, but still have not touched b_info. Actually, there are two b_info structures in a\r\nUPX packed binary, one for the compressed target program and one for the compressed part of the loader itself. \r\nIf we look inside i386-linux.elf-entry.S (ELF x86), we will find that the offset of the first struct b_info for the\r\ncompressed program is given in .long O_BINFO. The other b_info for the compressed part of the loader, is\r\nlocated soon after the instruction call unfold near the label main:, reached from _start.\r\nhttps://cujo.com/upx-anti-unpacking-techniques-in-iot-malware/\r\nPage 4 of 9\n\nOffset of the first struct b_info for the compressed program\r\nWe can even debug the whole process and find the exact offsets by uncommenting the seen int3 instruction and\r\nrecompiling the UPX binary. Once we debugged a sample file and found the offsets, we can make e of them and\r\nsee the corresponding hex values:\r\nUncomment the int3 instruction to manually debug\r\nThe first b_info struct at 0x118\r\nThe second b_info struct at 0x559A\r\nThe b_method and the b_ftid must be the same for all b_info in the same file. There is a quick way to gain that\r\ninformation out of a binary, by running upx ––fileinfo on a sample, packed binary.\r\nUPX fileinfo argument\r\nWe have yet to see values for b_info being altered in the wild, but this might be another abuse surface for UPX\r\npacked binaries at later stages. Currently, the most prevalent UPX abuses are alteration of l_info and p_info. \r\nLet’s summarize our findings of values for the structures in detail:\r\nhttps://cujo.com/upx-anti-unpacking-techniques-in-iot-malware/\r\nPage 5 of 9\n\nOnce we put those in, our UPX packed binary now successfully unpacks. \r\nMozi\r\nLet’s look at another example: Mozi is one of the prevalent IoT malware families in 2020. It is a perfect example\r\nfor p_info alteration, as UPX packed Mozi binaries have been observed to come with 0 value of\r\nthe p_filesize and p_blocksize fields. This will defeat automatic unpacking, and in order to get the unpacked\r\nbinary, we would need to figure out the correct values of these fields. Employing what we learned previously we\r\nquickly find the corresponding filesize values in the trailer, and we can add that into p_info: “E1 A6 1E 00”\r\nhttps://cujo.com/upx-anti-unpacking-techniques-in-iot-malware/\r\nPage 6 of 9\n\nAn example of employing UPX header corruption and erased p_filesize and p_blocksize fields\r\nAfter fixing the corrupted UPX header and the values of p_filesize and p_blocksize\r\nManual Unpacking from radare2\r\nWhere the automatic unpacking does not work with upx –d tool, even after fixing all the mentioned\r\ndiscrepancies and modified fields, we may attempt to manually extract the unpacked executable image from\r\nmemory, like the following:\r\nResources:\r\nhttps://github.com/upx/upx \r\nhttps://github.com/radareorg/radare2 \r\nhttps://github.com/upx/upx/issues/389 \r\nhttps://github.com/upx/upx/blob/master/src/stub/src/i386-linux.elf-entry.S \r\nhttps://github.com/upx/upx/blob/master/src/stub/src/amd64-linux.elf-entry.S \r\nAppendix\r\nhttps://cujo.com/upx-anti-unpacking-techniques-in-iot-malware/\r\nPage 7 of 9\n\nAppendix A\r\nAppendix B\r\nAppendix C\r\nUsed malware hash for analysis: \r\nbc88a57e1203f5eec08d34b59d9de43fa121f9d92cc773c17ebfbe848a2f88cd \r\nSpecial thanks to @unixfreaxjp for his previous research on ELF packing. \r\nhttps://cujo.com/upx-anti-unpacking-techniques-in-iot-malware/\r\nPage 8 of 9\n\nSource: https://cujo.com/upx-anti-unpacking-techniques-in-iot-malware/\r\nhttps://cujo.com/upx-anti-unpacking-techniques-in-iot-malware/\r\nPage 9 of 9",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://cujo.com/upx-anti-unpacking-techniques-in-iot-malware/"
	],
	"report_names": [
		"upx-anti-unpacking-techniques-in-iot-malware"
	],
	"threat_actors": [],
	"ts_created_at": 1775434412,
	"ts_updated_at": 1775826706,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/bf3ffb9774b7389446bdca157854cd5341b0c4b9.pdf",
		"text": "https://archive.orkl.eu/bf3ffb9774b7389446bdca157854cd5341b0c4b9.txt",
		"img": "https://archive.orkl.eu/bf3ffb9774b7389446bdca157854cd5341b0c4b9.jpg"
	}
}