{
	"id": "77271e5e-aa3c-4363-970b-dd77b76b3027",
	"created_at": "2026-04-06T00:21:08.199734Z",
	"updated_at": "2026-04-10T03:21:59.22627Z",
	"deleted_at": null,
	"sha1_hash": "6db95314633beab5f7bacc7e3abaa69a65eac4c2",
	"title": "Reversing malware in a custom format: Hidden Bee elements | Malwarebytes Labs",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 216507,
	"plain_text": "Reversing malware in a custom format: Hidden Bee elements |\r\nMalwarebytes Labs\r\nBy hasherezade\r\nPublished: 2018-08-29 · Archived: 2026-04-05 13:08:14 UTC\r\nMalware can be made of many components. Often, we encounter macros and scripts that work as malicious\r\ndownloaders. Some functionalities can also be achieved by position-independent code—so-called shellcode. But\r\nwhen it comes to more complex elements or core modules, we almost take it for granted that it will be a PE file\r\nthat is a native Windows executable format.\r\nThe reason for this is simple: It is much easier to provide complex functionality within a PE file than within a\r\nshellcode. PE format has a well-defined structure, allowing for much more flexibility. We have certain headers\r\nthat define what imports should be loaded and where, as well as how the relocations should be applied. This is a\r\ndefault format generated when we compile applications for Windows, and its structure is then used by Windows\r\nLoader to load and execute our application. Even when the malware authors write custom loaders, they are mostly\r\nfor the PE format.\r\nHowever, sometimes we find exceptions. Last time, when we analyzed payloads related to Hidden Bee (dropped\r\nby the Underminer exploit kit), we noticed something unusual. There were two payloads dropped that didn’t\r\nfollow the PE format. Yet, their structure looked well organized and more complex than we usually encounter\r\ndealing with pieces of shellcode. We decided to take a closer look and discovered that the authors of this malware\r\nactually created their own executable format, following a consistent structure.\r\nOverview\r\nThe first payload: b3eb576e02849218867caefaa0412ccd (with .wasm extension, imitating Web Assembly) is a\r\nloader, downloading and unpacking a Cabinet file:\r\nThe second payload: 11310b509f8bf86daa5577758e9d1eb5, unpacked from the Cabinet:\r\nhttps://blog.malwarebytes.com/threat-analysis/2018/08/reversing-malware-in-a-custom-format-hidden-bee-elements/\r\nPage 1 of 8\n\nWe can see at first that in contrast to most shellcodes, it does not start from a code, but from some headers.\r\nComparing both modules, we can see that the header has the same structure in both cases.\r\nHeaders\r\nWe took a closer look to decipher the meaning of particular fields in the header.\r\nThe first DWORD: 0x10000301 is the same in both. We didn’t find this number corresponding to any of the\r\npieces within the module. So, we assume it is a magic number that makes an identifier of this format.\r\nNext, two WORDs are offsets to elements related to loading the imports. The first one (0x18) points to the list of\r\nDLLs. The second block (0x60) looks more mysterious at first. Its meaning can be understood when we load the\r\nmodule in IDA. We can see the cross-references to those fields:\r\nWe see that they are used as IAT—they are supposed to be filled with the addresses to the imported functions:\r\nhttps://blog.malwarebytes.com/threat-analysis/2018/08/reversing-malware-in-a-custom-format-hidden-bee-elements/\r\nPage 2 of 8\n\nThe next value is a DWORD (0x2A62). If we follow it in IDA, we see that it leads to the beginning of a new\r\nfunction:\r\nThis function is not referenced by any other functions so we can suspect that it is the program’s Entry Point.\r\nThe meaning of the next value (0x509C) is easy to guess because it is the same as the size of the full module.\r\nThen, we have the last two DWORDs of the header. The second DWORD (0x4D78) leads to the structure that is\r\nvery similar to the PE’s relocations. We can guess that it must be a relocation table of the module, and the previous\r\nDWORD specifies its size.\r\nThis is how we were able to reconstruct the full header:\r\ntypedef struct { DWORD magic;\r\nWORD dll_list; WORD iat; DWORD ep; DWORD mod_size;\r\nDWORD relocs_size; DWORD relocs; } t_bee_hdr;\r\nhttps://blog.malwarebytes.com/threat-analysis/2018/08/reversing-malware-in-a-custom-format-hidden-bee-elements/\r\nPage 3 of 8\n\nImports\r\nAs we know from the header, the list of the DLLs starts at the offset 0x18. We can see that each of the DLL’s\r\nnames are prepended with a number:\r\nThe numbers are not corresponding with a DLL name: In two different modules, the same DLL had different\r\nnumbers assigned. But if we sum up all the numbers, we find that their total sum is the same as the number of\r\nDWORDs in the IAT. So, we can make an educated guess that those numbers are specifying how many functions\r\nwill be imported from a particular DLL.\r\nWe can describe it as the following structure (where the name’s length is not specified):\r\ntypedef struct { WORD func_count; char name; } t_dll_name;\r\nThen, the IAT comes as a list of DWORDs:\r\nIt is common in malware that when the function’s names are not given as an explicit string, they are imported by\r\nchecksum. The same is done in this case. Guessing the appropriate function that was used for calculating the\r\nchecksum can be more difficult. Fortunately, we found it in the loader component:\r\nDWORD checksum(char *func_name) { DWORD result = 0x1505; while ( *func_name ) result = *func_\r\nKnowing that we paired appropriate checksums with the function’s names:\r\nhttps://blog.malwarebytes.com/threat-analysis/2018/08/reversing-malware-in-a-custom-format-hidden-bee-elements/\r\nPage 4 of 8\n\nOnce the address of the function is retrieved, it is stored in the IAT in place of the checksum.\r\nRelocations\r\nCreating a relocation table is simple. It consists of the list of DWORDs that are identifying the offsets of the\r\nplaces in the code to which we should add the base where the module has been loaded. Without relocations\r\napplied, the module will crash (so, it is not position-independent like a typical shellcode).\r\nComparison to PE format\r\nWhile the PE format is complex, with a variety of headers, this one contains only essentials. Most of\r\nthe information that is usually stored in a PE header is completely omitted here.\r\nYou can see a PE format visualized by Ange Albertini here.\r\nCompare it with the visualization of the currently analyzed format:\r\nhttps://blog.malwarebytes.com/threat-analysis/2018/08/reversing-malware-in-a-custom-format-hidden-bee-elements/\r\nPage 5 of 8\n\nStatic analysis\r\nWe can load this code into IDA as a blob of raw code. However, we will be missing important information. Due to\r\nthe fact that the file doesn’t follow a PE structure, and its import table is non-standard, we will have a hard time\r\nunderstanding which API calls are being made at which offset. To solve this problem, I made a tool that resolves\r\nhashes into function names and generates a TAG file to mark the offsets where each function’s address is going to\r\nbe filled.\r\nThose tags can be loaded into IDA using an IFL plugin:\r\nhttps://blog.malwarebytes.com/threat-analysis/2018/08/reversing-malware-in-a-custom-format-hidden-bee-elements/\r\nPage 6 of 8\n\nHaving all the API functions tagged, it is much easier to understand which actions are performed by the module.\r\nHere, for example, we can see that it will be establishing the connection with the C2 server:\r\nDynamic analysis\r\nThis format is custom, so it is not supported by the typical tools for analysis. However, after\r\nunderstanding it, we can write our own tools, such as the parser for the headers and loader that will\r\nhelp to run this format and analyze it dynamically.\r\nIn contrast to PE, the module doesn’t have any sections. So, we need to load it in a continuous memory region\r\nwith RWX (read-write-execute) access. Walking through the relocations list, we will add the value of the base at\r\nwhich the module was loaded to the listed addresses. Then, we have to resolve the imported functions by their\r\nhashes and fill the addresses in the thunks. After preparing the stage, it just needs to jump at the Entry Point of the\r\nmodule. We will load the prepared loader under the debugger and follow to the entry point of the loaded module.\r\nSimple but rare\r\nThe elements described here are pretty simple—they serve as a first stage of the full malware package,\r\ndownloading other pieces and injecting them into processes. However, what makes them interesting is the fact that\r\nhttps://blog.malwarebytes.com/threat-analysis/2018/08/reversing-malware-in-a-custom-format-hidden-bee-elements/\r\nPage 7 of 8\n\ntheir authors have shown some creativity and decided to invent a custom format that is less complex than a full-fledged PE, but goes a step further than a typical piece of shellcode.\r\nSuch module, in contrast to independent shellcode, is not self-sufficient and cannot be loaded in a trivial way, but\r\nmust be parsed first. Given the fact that the format is custom, it is not supported by existing tools. This is where\r\nprogramming skills come in handy for a malware analyst.\r\nFortunately, fully custom formats are rather uncommon in the malware world; usually, authors rely heavily on\r\nexisting formats, from time to time corrupting or customizing selected parts of PE headers.\r\nAbout the author\r\nUnpacks malware with as much joy as a kid unpacking candies.\r\nSource: https://blog.malwarebytes.com/threat-analysis/2018/08/reversing-malware-in-a-custom-format-hidden-bee-elements/\r\nhttps://blog.malwarebytes.com/threat-analysis/2018/08/reversing-malware-in-a-custom-format-hidden-bee-elements/\r\nPage 8 of 8",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"references": [
		"https://blog.malwarebytes.com/threat-analysis/2018/08/reversing-malware-in-a-custom-format-hidden-bee-elements/"
	],
	"report_names": [
		"reversing-malware-in-a-custom-format-hidden-bee-elements"
	],
	"threat_actors": [],
	"ts_created_at": 1775434868,
	"ts_updated_at": 1775791319,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/6db95314633beab5f7bacc7e3abaa69a65eac4c2.pdf",
		"text": "https://archive.orkl.eu/6db95314633beab5f7bacc7e3abaa69a65eac4c2.txt",
		"img": "https://archive.orkl.eu/6db95314633beab5f7bacc7e3abaa69a65eac4c2.jpg"
	}
}