{
	"id": "d84f68ed-33b3-46de-9758-8f7c524be3ca",
	"created_at": "2026-04-06T00:15:09.022101Z",
	"updated_at": "2026-04-10T03:21:13.950332Z",
	"deleted_at": null,
	"sha1_hash": "d1327f61efa8ea520689df4fcbf8346746ac04fb",
	"title": "Rustock.C – Unpacking a Nested Doll",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 337362,
	"plain_text": "Rustock.C – Unpacking a Nested Doll\r\nArchived: 2026-04-05 14:14:59 UTC\r\nUnpacking Rustock.C is a challenging task. If you are tired of boring crosswords or Sudoku puzzles and feel like\r\nyour brain needs a real exercise, think about reversing Rustock.C - satisfaction (or dissatisfaction, depending on\r\nthe result) is guaranteed.\r\nRustock.C story began a week ago – when one AV vendor has publicly disclosed the new details about the latest\r\nvariant of Rustock. As soon as the sample of Rustock.C has been obtained, many researchers started their journey\r\ninto the center of the rootkit.\r\nFirst quick look at the driver code reveals a simple decoder. In spite of being simple, it is still a good idea to debug\r\nit to see what exactly it produces on its output.\r\nIn order to debug a driver, different malware researchers prefer different tools – in our case let’s start from\r\nWinDbg configured to debug a VMWare session running in debug mode. For more details of this set up, please\r\nread this article.\r\nThe very first question one might ask is how to put a breakpoint into the very beginning of the driver code?\r\nSome researchers would hook IopLoadDriver() in the kernel to intercept the code before it jumps into the driver,\r\nin order to step in it by slowly tracing single instructions.\r\nA simple known trick however, is to build a small driver (and keep it handy) with the first instruction being “int\r\n3”. Once such driver is loaded, the debugger will pop up with the Debug Breakpoint exception. Stepping out from\r\nthat place leads back into the kernel’s IopLoadDriver() function – right into the point that follows the actual call to\r\nthe driver. Now, the actual call instruction address is known - a new breakpoint needs to be placed in it.\r\nWith the new breakpoint in place, it is time to load Rustock.C driver in the virtual environment controlled by the\r\ndebugger. Once loaded, the debugger breaks at the call instruction in kernel’s IopLoadDriver(). Stepping into the\r\ndriver, placing a new breakpoint at the end of its decoder and letting it run until it hits that breakpoint allows to\r\nunpack the code that was hidden under that decoder.\r\nhttp://blog.threatexpert.com/2008/05/rustockc-unpacking-nested-doll.html\r\nPage 1 of 6\n\nThe first-layer decoder reveals us a code with a myriad of fake instructions, blocks of code that do nothing,\r\nrandom jumps from one place to another – a huge maze created with only one purpose – to complicate threat\r\nanalysis by obfuscating and hiding the truly malicious code.\r\nTracing that code within debugger might be easier with the disassembly listing of that code in the user mode.\r\nOne way to get that listing is to reconstruct the driver as a PE-executable by resetting the DLL bit in its PE-header\r\ncharacteristics and changing its subsystem from Native (0x01) to Windows GUI (0x02) to make debugger happy\r\nto load it. Another way is to reconstruct a normal PE-executable by building and compiling an Assembler program\r\nthat includes the top-level Rustock’s decryptor followed by a large stub of encoded data simply copied from the\r\noriginal driver code.\r\nBuidling a PE-executable equivalent of the Rustock.C driver helps to study the code behind the first-layer\r\ndecoder. Such program can now be loaded into a user-mode debugger, such as OllyDbg, the first-layer decoder\r\ncan now be debugged in the user mode to unpack the code behind it. Once unpacked, the entire process can be\r\ndumped and reloaded into the disassembler.\r\nAt this point of analysis, the code behind the first-layer decoder reveals interesting occurrences of DRx registers\r\nmanipulations, IN/OUT instructions, “sidt/lidt” instructions, and some other interesting code pieces - for example\r\na code that parses an MZ/PE header:\r\n00011C0A cmp word ptr [eax], ‘ZM’\r\n00011759 mov bx, [eax+3Ch]\r\n00011E31 cmp dword ptr [eax+ebx], 'EP'\r\nThe code in general now looks like “spaghetti” – and still, it’s just a second-layer decryptor. The picture below\r\nshows you its execution flow – every grey “box” in it represents a stand-alone function:\r\nhttp://blog.threatexpert.com/2008/05/rustockc-unpacking-nested-doll.html\r\nPage 2 of 6\n\nPlacing the breakpoints for all the “interesting” instructions in the driver code is a good idea. The addresses need\r\nto offset by a difference between the driver’s entry point reported with a kernel debugger and the entry point of the\r\ndriver’s PE-executable equivalent, as reported by the user mode debugger.\r\nWith the new breakpoints in place, the code will firstly break on the instruction that searches for an MZ-header of\r\nthe ntkrnlpa.exe:\r\ncmp word ptr [eax], ‘ZM’\r\nIn order to find the image base of ntkrnlpa.exe, Rustock.C looks up the stack to find the return address inside\r\nntkrnlpa.exe. It rounds that address up and starts sliding it backwards by the amount of the section alignment until\r\nit reaches the image base of ntkrnlpa.exe.\r\nOnce the start of ntkrnlpa.exe is found, the driver then parses its PE-header, locates and parses the export table.\r\nPrevious variants of Rustock contained explicit imports from ntkrnlpa.exe. This time, Rustock.C obtains kernel’s\r\nexports dynamically, by parsing its memory image – the same trick was widely used by the user-mode malware in\r\nthe past, when the kernel32.dll’s exports were dynamically obtained during run-time by using the hash values of\r\nthe export names.\r\nThe fragment of Rustock’s second-layer decryptor below parses kernel’s export table:\r\nNow that it knows kernel exports, the driver calls ExAllocatePoolWithQuotaTag() to allocate 228,381 bytes in the\r\nnon-paged pool ( tagged as “Info@”).\r\nThe rootkit code then copies itself into that pool and jumps in it to continue its execution from that place.\r\nhttp://blog.threatexpert.com/2008/05/rustockc-unpacking-nested-doll.html\r\nPage 3 of 6\n\nDuring the execution, Rustock.C repeats the same trick again – it allocates another 278,528 bytes in the non-paged\r\npool, copies itself into it and transfers there control. This way, the code of the driver \"migrates\" from one memory\r\nlocation to another. While the \"abandoned\" areas preserve the severely permutated code, and thus, not easily\r\nsuitable for scanning, the addresses of the newly allocated areas in the non-paged pool cannot be predicted. Thus,\r\neven if the infected driver and its address range in the kernel are established, it is still not clear where the final\r\n\"detectable\" form of Rustock.C code is located.\r\nFollowing memory allocation tricks, Rustock employs “lidt/sidt” instructions to patch IDT. Executing “lidt” in\r\nWinDbg might crash the operating system in the virtual machine. Therefore, “lidt” instruction needs to be skipped\r\n(by patching EIP with the address of the next instruction).\r\nAnother set of instructions that are better to be skipped with the debugger, are DRx-registers manipulations. By\r\nzeroing the debug registers Dr0-Dr3 and the debug control register DR7, the rootkit might attempt to cause trouble\r\nfor SoftIce – any suspicious instructions need to be skipped for safety reasons.\r\nFollowing that, Rustock.C driver reads the configuration of devices on a PCI bus by using IN/OUT instructions\r\nwith the PCI_CONFIG_ADDR and PCI_CONFIG_DATA constants. It then starts a few nested loops to read\r\ncertain data from the devices attached to a PCI bus. The read data is then hashed with the purpose of creating a\r\nfootprint that uniquely identifies hardware of the infected host.\r\nDebugging the Rustock.C driver is easier if the successful code execution path is saved into a map (e.g. a hand-written one). Every successfully terminated loop should be reflected in that map. The relative virtual addresses\r\nrecorded in it allow skipping long loops when the code is analysed again from the beginning – they should be\r\nconsidered “the milestones” of the code flow. If a wrong move crashes the system – the virtual machine needs to\r\nbe reverted to a clean snapshot, debugger restarted, and the entire debugging process repeated by using the\r\nsuccessful “milestones” from the map.\r\nThe map of the execution “milestones” should tell what to skip, when to break, what to patch, where to jump – in\r\norder to navigate the code successfully through all the traps that the authors of Rustock has set against emulators,\r\ndebuggers, run-time code modifications, etc.\r\nWhenever the driver attempts to access data at a non-existing address, the code needs to be unwound backwards to\r\nestablish the reason why the address is wrong. In most cases, following the logics of the code helps to understand\r\nwhat values should replace the wrong addresses.\r\nFor example, at one point of execution, Rustock.C driver crashes the session under WinDbg by calling the\r\nfollowing instruction while the contents of ESI is not a valid address:\r\nmov esi, dword ptr [esi]\r\nIn order to “guide” the code through this crash, the driver needs to be re-analysed from the very beginning to\r\ncheck if this instruction is successfully called before the failure and if it does, what the valid contents of ESI is at\r\nthat moment of time.\r\nhttp://blog.threatexpert.com/2008/05/rustockc-unpacking-nested-doll.html\r\nPage 4 of 6\n\nAs stated above, the PE-executable equivalent of the driver loaded into the user-mode debugger and disassembler\r\nhelps to navigate through the code, search instructions in it, search for the code byte sequences, place comments -\r\na good helper for the kernel debugging.\r\nThe code of Rustock.C debugged at this stage is a 2nd-layer decryptor that will eventually allocate another buffer\r\nin the non-paged pool where it will decrypt the final, but still, ridiculously permutated “spaghetti” code of the\r\ndriver – this time, with the well-recognizable strings, as shown in the following dumps:\r\nhttp://blog.threatexpert.com/2008/05/rustockc-unpacking-nested-doll.html\r\nPage 5 of 6\n\nPS: Special thanks to Frank Boldewin for exchanging his tips and ideas with me.\r\nSource: http://blog.threatexpert.com/2008/05/rustockc-unpacking-nested-doll.html\r\nhttp://blog.threatexpert.com/2008/05/rustockc-unpacking-nested-doll.html\r\nPage 6 of 6",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"references": [
		"http://blog.threatexpert.com/2008/05/rustockc-unpacking-nested-doll.html"
	],
	"report_names": [
		"rustockc-unpacking-nested-doll.html"
	],
	"threat_actors": [],
	"ts_created_at": 1775434509,
	"ts_updated_at": 1775791273,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/d1327f61efa8ea520689df4fcbf8346746ac04fb.pdf",
		"text": "https://archive.orkl.eu/d1327f61efa8ea520689df4fcbf8346746ac04fb.txt",
		"img": "https://archive.orkl.eu/d1327f61efa8ea520689df4fcbf8346746ac04fb.jpg"
	}
}