{
	"id": "eb8861b0-3efc-4c08-a52d-4523f88306df",
	"created_at": "2026-04-06T00:22:08.594324Z",
	"updated_at": "2026-04-10T03:32:21.224376Z",
	"deleted_at": null,
	"sha1_hash": "6a8837cf9fb684596fbe67781fd15a2a23ebf705",
	"title": "A Universal Windows Bootkit",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 482175,
	"plain_text": "A Universal Windows Bootkit\r\nBy Email\r\nPublished: 2016-05-03 · Archived: 2026-04-05 17:52:02 UTC\r\nAn analysis of the MBR bootkit referred to as “HDRoot”\r\nExecutive Summary\r\nIn October, 2015 Kaspersky released an analysis of a family of malware they dubbed “HDRoot” on their\r\nSecurelist blog. It was an installment in their ongoing series on the WINNTI group, known for targeting gaming\r\ncompanies in their APT campaigns. The Securelist blog was dismissive of the HDRoot bootkit and called out a\r\nnumber of mistakes they claimed the authors made, which brought it to be the focus of their ridicule.\r\nThe bootkit in question uses two stolen signing certificates and is capable of running without problem on any\r\nWindows system that was released in the last 16 years, from Windows 2000 to Windows 10. The one limitation is\r\nthat it will only run as an MBR bootkit and will not work on systems using UEFI. It contains the ability to install\r\nany backdoor payload to be launched in the context of a system service when Windows starts up on both 32 and\r\n64-bit systems. It also does a fairly good job of concealing the actual bootkit code, only failing to remove the\r\nbackdoor after running it at boot. This likely a conscious choice made by the authors to have the backdoor\r\nresponsible for removing itself, and not an oversight.\r\nHDRoot represents a serious commitment in time and effort to develop, and likely has been in use or development\r\nsince at least 2006. The sample analyzed here dates to sometime in 2012 or 2013, and is the same sample\r\nKasperky reports to have analyzed in their debut post on HDRoot. However, all evidence points to Kaspersky\r\ndoing their analysis with a 2006 sample, criticizing problems in the malware that are not actually present.\r\nAdditionally, they provide no hashes or other information on the actual sample they used.\r\nThe samples I analyzed in this report are detailed in appendix 1 and hashes are provided in appendix 2. They can\r\nbe found in the following git repo below:\r\nhttps://github.com/williamshowalter/hdroot-bootkit-analysis\r\nDownload White Paper\r\nTable of Contents\r\nIntroduction\r\nSample\r\nThis report\r\nOverview\r\nDropper\r\nVMProtect\r\nhttp://williamshowalter.com/a-universal-windows-bootkit/\r\nPage 1 of 26\n\nDEBUGFILE.sys - A signed kernel driver\r\nMBR\r\nVerifier\r\nrkImage\r\nSchedule service DLL\r\nConclusions\r\nReferences\r\nAppendix 1. Index of Supplemental File Repository\r\n1.1 Binary Files\r\n1.2 Code Files\r\n1.3 Evidence Files\r\n1.4 Ida Pro Files\r\nAppendix 2: Sample Hashes\r\nMD5\r\nSHA1\r\nAppendix 3: Screenshots\r\nDropper\r\nIntroduction\r\nSample:\r\nMD5: 2c85404fe7d1891fd41fcee4c92ad305\r\nSHA1: 4c3171b48d600e6337f1495142c43172d3b01770\r\nSHA256: a9a8dc4ae77b1282f0c8bdebd2643458fc1ceb3145db4e30120dd81676ff9b61\r\nOriginal Filename: net.exe\r\nProduce Name: Microsoft Windows Operating System\r\nProduct Version: 6.1.7600.16385\r\nTime Stamp: 2012/08/06 13:12:39 UTC\r\nRetrieved from malwr [1].\r\nThis report\r\nMy analysis of this HDRoot sample began as an exercise to become more familiar with low-level malware and the\r\ntechniques required for reverse engineering them. I had no prior knowledge of the WINNTI group who Kaspersky\r\nattributes this malware to, nor do I have any other samples beyond those associated with the dropper and bootkit\r\nanalyzed here. The dropper is capable of installing any PE executable as the payload for the bootkit, but does not\r\ncome bundled with any default payloads. As such, this report offers no insight into the various payloads used by\r\nthe authors. For information on the other malware associated with the WINNTI group, see Trend Micro or\r\nKaspersky’s reports on the group [2][3]. This report does, however, offer a very in-depth look into the technical\r\nworkings of the HDRoot bootkit and its components.\r\nhttp://williamshowalter.com/a-universal-windows-bootkit/\r\nPage 2 of 26\n\nI also address a number of technical inaccuracies and misrepresentations from Kaspersky’s SecureList post of\r\nOctober 2015, “I am HDRoot! Part 1,” which is the only research on this sample to be published before my own\r\n[4]. Kaspersky’s research was very helpful getting started, but I soon discovered that their analysis was not\r\nactually performed using the sample identified by MD5 in the article and thus could not be relied upon. I believe\r\nthis to be the reason for many of their criticisms for HDRoot, which they call “quite conspicuous,” and, “not what\r\nyou expect from such a serious APT actor.” The sample analyzed here is not free of criticisms, but none of the\r\nproblems addressed by Kaspersky appear to be valid on the sample they claim to have analyzed.\r\nI also freely acknowledge that the level of detail that this report goes into is impractical for almost all incident\r\nresponse purposes, and that this venture was largely done for my own education and curiosity.\r\nOverview\r\nThe malware examined here can be broken into several stages. The 64-bit dropper, which was signed with a stolen\r\ncertificate that has since been revoked, is the first component that is executed. The dropper installs the bootkit to\r\nthe hard drive along with a backdoor executable to be run on subsequent boots. The backdoor is supplied as a\r\nparameter to the dropper and can be any Win32 or Win64 executable.\r\nUpon boot, the computer will execute the maliciously installed MBR, which loads a subsequent component that I\r\nnamed the “verifier”. It is a single sector block that verifies that the rest of the bootkit and the backdoor are intact\r\nbefore running them. The bulk of the bootkit’s work is done by the next component, rkImage. The name rkImage\r\nactually comes from the interface of the dropper, which explicitly refers to it when installing the bootkit. rkImage\r\nworks by manually reading the file system from the disk in order to write the backdoor (the generic term referring\r\nto the payload) into the filesystem and redirect a Windows system service to launch the backdoor.\r\nWhen rkImage is finished it transfers execution back to the original, non-infected MBR and allows Windows to\r\nboot normally. The booting system will run the backdoor instead of the replaced system service, but will then\r\nrestore and start the legitimate service after the backdoor has ran, hiding the fact it was ever replaced.\r\nDropper\r\nThe dropper is designed to disguise itself as the Windows system utility net.exe. The properties on the executable\r\nattempt to mirror the settings found on a Windows 7 version of the utility, reporting it to a Microsoft program.\r\nWhen run without parameters, HDRoot shows the options menu as if it were net.exe. That is where the similarities\r\nend, however.\r\nhttp://williamshowalter.com/a-universal-windows-bootkit/\r\nPage 3 of 26\n\nFigure 1: Dropper disguising itself as net.exe\r\nThe dropper executable, while masquerading as the Microsoft net command, has been signed with a digital\r\ncertificate belonging to Guangzhou YuanLuo Technology Co, Ltd, a firm based in the city of Guangzhou, China\r\nwho had their signing certificate stolen by the WINNTI group. The certificate has since been revoked, and, if the\r\nsigning time and compilation dates on the executable are to be believed, it was signed in 2013 almost a year after\r\nthis version was initially compiled.\r\nhttp://williamshowalter.com/a-universal-windows-bootkit/\r\nPage 4 of 26\n\nFigure 2: Dropper’s revoked certificate\r\nWhen run with any of the legitimate net command parameters or with unrecognized parameters, no output is\r\ngiven. The only commands that provide output are the valid HDRoot commands programmed by the authors.\r\nKaspersky, by analyzing an older sample from 2006, was able to get a “help” output, rather than the “net” output,\r\nwhich contained a list of commands for that version. Most of these commands still worked on the newer sample.\r\nAll of the commands were five or less characters in length, and even short words like install were abbreviated to\r\n“inst”. Since the Kaspersky command listing was half a decade older than this sample, and that some of the\r\ncommands from their listing were no longer present, I wrote a simple, and very slow, fuzzer to attempt to check all\r\npossible commands of five or less characters. Given the length of the other commands and that input appears to be\r\ncase insensitive, this appears to be a sensible approach. The code for the fuzzer can be found in the supplemental\r\nhttp://williamshowalter.com/a-universal-windows-bootkit/\r\nPage 5 of 26\n\nfiles detailed in appendix 1.1. Screenshots for each command can be found in appendix 3.1, as well. No additional\r\ncommands to the ones Kaspersky detailed were found by the fuzzer, and the table below is the known list of\r\ncommands.\r\nCommand Description\r\ncheck Checks for the presence of the bootkit and the integrity if present.\r\nclean Removes the bootkit.\r\ninst\r\n\u003cBackdoor\u003e\r\nInstalls the bootkit\r\ninfo\r\n\u003cBackdoor\u003e\r\nShows information about the checksums and requirements for an executable if it was\r\ninstalled as the backdoor.\r\nTable 1: HDRoot dropper commands\r\nhttp://williamshowalter.com/a-universal-windows-bootkit/\r\nPage 6 of 26\n\nFigure 3: inst command output\r\nVMProtect\r\nA notable hindrance to reversing the dropper is that it was packed using VMProtect. Unlike most packers, which\r\ndecompress and then jump to the original executable code, VMProtect converts the x86 opcodes into an\r\nautomatically generated language of bytecodes to be interpreted in its own emulator. Attempting to statically\r\nanalyze the sample would prove an arduous task. There have been a few unpacking plugins for Ollydbg written\r\nfor certain versions of VMProtect, but these are generally found in forum posts and are not well maintained. I\r\nbelieve this to be the reason Kaspersky did the bulk of their analysis with a different sample that was almost, but\r\nnot quite, functionally the same. Not wanting to spend my time tackling VMProtect either, I instead used a number\r\nof dynamic analysis techniques.\r\nhttp://williamshowalter.com/a-universal-windows-bootkit/\r\nPage 7 of 26\n\nhttp://williamshowalter.com/a-universal-windows-bootkit/\r\nPage 8 of 26\n\nFigure 4: Graph overview of VMProtect’s emulator. Not fun.\r\nDEBUGFILE.sys - A signed kernel driver\r\nAt the time the dropper installs the bootkit, no changes to the filesystem or the registry are seen between snapshots\r\ntaken before and after. I took the approach of running the dropper in a continuous loop in a virtual machine,\r\nsuspending the VM, and analyzing the resulting memory image. Performing memory capture from outside the VM\r\nappeared to be the best option because there were a number of anti-debugging techniques employed along with the\r\nanti-disassembly. Using Volatility, I discovered two more PE files that were extracted inside the process, but none\r\nof the four clear text resources Kaspersky claimed to have extracted from a memory dump, providing further proof\r\nthat they did their analysis on a different sample than is listed in their blog post. The two PE files I found were\r\nkernel drivers, one 32-bit and one 64-bit. The 64-bit driver is signed, as is required by 64-bit versions of Windows,\r\nusing yet another stolen certificate, while the 32-bit driver is not signed. This certificate belongs to a South Korean\r\nvideo game company, Neowiz. The certificate, unlike the one for the dropper, has yet to be revoked (see Figure 6).\r\nThe use of the kernel drivers is fairly straightforward. Without kernel access there is no way for malware to write\r\ndirectly to the physical disk as there are no Windows API calls available to userland processes for doing so. The\r\ndropper writes out the appropriate driver to C:\\Windows\\system32\\Drivers\\DEBUGFILE.sys, and then creates a\r\nservice for it. This shows up in the memory image as a handle to the registry key\r\nHKLM\\System\\ControlSet001\\services\\DEBUGFILE. The service runs and the driver \\Driver\\DEBUGFILE is\r\ncreated. DEBUGFILE.sys is also deleted from the disk. The driver is used by the dropper to proxy its direct access\r\nto the physical disk. A number of things are done in this process. The original MBR is backed up and then\r\noverwritten by the new bootkit MBR, and then weakly encrypted components are written to disk. Near the\r\nbeginning of the disk is the component I’ve named the verifier, followed by two identical copies of the original\r\nMBR. In another section of the disk is the main component of the bootkit, rkImage, followed by the backdoor that\r\nwas installed.\r\nFigure 5: Physical Disk Layout written by DEBUGFILE.sys\r\nOne peculiar thing the malware does is install a second copy of the rkImage and backdoor files. This copy is\r\nencrypted identically to the first, and positioned such that it ends exactly 2063 sectors from the end of the drive.\r\nhttp://williamshowalter.com/a-universal-windows-bootkit/\r\nPage 9 of 26\n\nWhat makes this strange is that nothing in the bootkit will ever transfer execution to the second copy, and that the\r\nsecond copy is only installed if the drive has at least 30% free space. Kaspersky erroneously identified this\r\nbehavior as only installing if the disk has greater than 30% free space, rather than installing a redundant copy of\r\nitself. As can be seen in a Windows 7 screenshot from the appendix 1.3 files, the bootkit is perfectly capable of\r\ninstalling with less than 30% free space. The only guess I make as to the purpose of this second copy is for the\r\nindented backdoor to be able to identify if one of the copies has been modified after it starts. The dropper will also\r\ndetect a modified copy.\r\nhttp://williamshowalter.com/a-universal-windows-bootkit/\r\nPage 10 of 26\n\nFigure 6: DEBUGFILE.sys\r\nMBR\r\nFor all x86 systems not running UEFI, the boot process starts with the BIOS loading the Master Boot Record into\r\nmemory and jumping to it. By convention, the BIOS loads the MBR to the physical memory address of 0x7C00.\r\nAnother convention that many MBRs follow is to copy themselves, a single 0x200 sized sector, to the address\r\n0x600 and then transfer execution to this location. The HDRoot bootkit is no exception. This is partly because the\r\nhttp://williamshowalter.com/a-universal-windows-bootkit/\r\nPage 11 of 26\n\nonly code it actually changes in the original MBR is the jump address and the code jumped to. Most of the\r\noriginal MBR and partition table information is intact.\r\nA normal MBR would look at the partition table to find the partition with the boot flag set, and then load the\r\nvolume boot sector of that partition and transfer execution. HDRoot’s MBR works similarly by calling interrupt\r\n13 to read two sectors from disk into memory at the address 0x7A00 (through 0x7DFF). These are the verifier and\r\nthe original MBR, which now has been loaded into the location where the MBR would have originally loaded on a\r\nnon-infected system. The bootkit does not store these on disk in clear text, however. They are written to disk\r\nhaving been XOR’d with the byte value 0x76. Appendix 1.2 has a C utility that can be used to decrypt the values.\r\nA function at offset MBR+0x88 performs these read and decrypt operations, copies the partition table from the\r\ninfected MBR to the original (incase the victim has changed any partitions since the bootkit was installed), and\r\nthen transfers execution to the verifier.\r\nFigure 7: Address layout of memory loaded before rkImage\r\nhttp://williamshowalter.com/a-universal-windows-bootkit/\r\nPage 12 of 26\n\nFigure 8: Infected MBR code to load, decrypt verifier\r\nVerifier\r\nThe job of the verifier is to make sure that the bootkit is intact and that a specific set of criteria are met before\r\nallowing the bootkit to run. If any of these criteria fail the verifier will transfer the boot process to the original\r\nMBR, now at 0x7C00, without the bootkit executing. This mechanism helps prevent bricking the victim machines\r\nin the event that one or more of the hidden sectors are corrupted or overwritten.\r\nThe first criteria in the verifier process is a check for whether the alt key is pressed on the keyboard. If the alt key\r\nis pressed, the bootkit launch will be aborted. The verifier then checks for a value at 0x7A08 (+0x8 from the\r\nverifier start address). If the value is null, the startup is aborted. This value is the drive identifier of where rkImage\r\nand the backdoor are stored. This was 0x80 in all the systems I tested on, which indicates drive 0. The dropper sets\r\nthis value, and the subsequent bytes, before writing them to disk. This ensures that the bootkit was properly setup\r\nduring install, and allows for the bootkit to be stored on a separate disk from the system disk. Table 2 shows a\r\nbreakdown of the rkImage information stored in the verifier.\r\nAddress Contents\r\n0x7A02 0x55AA, not used, signals start of rkImage location data.\r\nhttp://williamshowalter.com/a-universal-windows-bootkit/\r\nPage 13 of 26\n\nAddress Contents\r\n0x7A04 CRC16 value for rkImage+Backdoor.\r\n0x7A06 Sector count for rkImage+Backdoor.\r\n0x7A08 Drive number\r\n0x7A09 Sector where rkImage+Backdoor starts.\r\n0x7A0D Next 0x55AA value, if present\r\n… …\r\nTable 2: rkImage location information in verifier\r\nThe third and final check done by the verifier is computing a CRC16 value on the encrypted contents of rkImage\r\nand the backdoor (still only encrypted with an XOR 0x76). It compares the results to the saved CRC, and if they\r\ndo not match it aborts. Otherwise it reads the entire rkImage, but not the backdoor, to 0x10000, and then decrypts\r\nboth. The last step is to copy the size and location information to the start of rkImage, so it can locate the backdoor\r\nfor installing.\r\nrkImage\r\nA significant component to reverse engineering the functionality of this bootkit was becoming familiar with the\r\nmechanics of low-level, pre-OS x86. For anyone looking to get into this I would highly recommend the\r\nIntermediate Intel x86 videos on OpenSecurityTraining.info [5]. Even just following the transition from the\r\nverifier to rkImage requires some understanding, as the processor is still in 16-bit real mode at this time and a far\r\njump is being performed, crossing a barrier between segments. This seems like a trivial thing until you find out\r\nthat GDB, even operating in 8086 mode remotely debugging the bootkit running in QEMU, has absolutely zero\r\nunderstanding of segment addressing and completely falls apart trying to set breakpoints at any address higher\r\nthan 0xFFFF. In retrospect Bochs might have been a better choice for this over QEMU, but not being familiar with\r\nit either I struggled through with QEMU, learning as I went and performing most my analysis indirectly, either\r\nstatically or dynamically through the clues left behind by the bootkit’s actions.\r\nThe first task rkImage sets itself to, like any sane code booting up, is to transfer itself from real mode to protected\r\nmode, and then to 32-bit mode. In order to enable protected mode, the Global Descriptor Table must be setup and\r\nloaded. This is actually fairly unimportant to the operation of the malware but understanding it helped getting the\r\ndisassembly properly setup in IDA to assist with the process. I will not get into the details and the contents of the\r\nsegment descriptors, but I will state that I found the clearest explanations and diagrams in the AMD Architecture\r\nProgrammer’s Manual, Vol 2., for system programming [6]. Something that caused confusion was that most\r\ndiagrams detailing the structure of segment descriptors (the entries in the Global Descriptor Table) are for the\r\ndescriptors in 64-bit mode, since the 32-bit descriptors, called legacy segment descriptors in AMD’s\r\ndocumentation, have a different structure. The work done reassembling the GDT can be seen in the rkImage IDB\r\nfile in appendix 1.4.\r\nhttp://williamshowalter.com/a-universal-windows-bootkit/\r\nPage 14 of 26\n\nOnce setup in a 32-bit environment, rkImage decrypts two sections in itself, the first containing a 32-bit DLL, and\r\nthe second containing a 64-bit DLL. These are used to launch the backdoor from a Windows system service upon\r\nWindows booting. Each DLL has a 4-byte XOR key. The data is stored in rkImage in the format: 4-byte key, 4-\r\nbyte length, encrypted PE file contents. The 32-bit DLL and its data are located at an offset of 0x4BE0 and the 64-\r\nbit values are at 0x6DE8, immediately after the previous DLL. These DLLs as packaged contain the registry path\r\nto the LanmanServer service DLL. The version of rkImage in this sample, installed by the 64-bit dropper,\r\noverwrites the LanmanServer information with the paths and values for the Schedule service. This allows for\r\nchanging the target service without the need to recompile the DLLs embedded in rkImage. Kaspersky’s\r\nobservations that there are a number of different services that different samples have targeted supports this.\r\nThe backdoor executable is then loaded into memory and decrypted with the 0x76 XOR operation. At the end of\r\nthis preparation work of loading, decrypting, and copying data, rkImage calls a function that I mark in my\r\ndisassembly as being named DETERMINE_VERSION_NT_32_64_BIT. This is the function that determines what\r\nWindows version is installed, what malicious DLL to use, and where to install it. Since the malware will attempt\r\nto boot in any Windows version from Windows 2000 to Windows 10, there is a considerable nest of switch and if\r\nstatements happening here. It first checks whether there is a “\\winnt” directory, which is present in Windows 2000,\r\nand if “\\winnt” is not found it will check for “\\windows\\system32\\kernel32.dll”. The check for kernel32.dll\r\nprevents the bootkit from continuing install on Windows 98 and lower systems, as kernel32.dll was stored in\r\n“\\windows\\system\\kernel32.dll”. If kernel32.dll is found it will check for “\\users” and “\\documents and settings”\r\nto determine if it is XP/2003, or Vista or newer. If it is not able to locate any of these, it fails out of the switch\r\nstatement and no bootkit is installed.\r\nIf the directory selected was not “\\winnt”, it will check for “\\windows\\syswow64” and set a variable indicating if\r\nthe system is 64-bit. This is used later when choosing which DLL to install (which means it is even 64-bit\r\nWindows XP / Server 2003 compatible). Then for each of the three operating system categories it will write the\r\nbackdoor to %TEMP%\\Explorer.exe (wherever %TEMP% is located for that version of Windows), and iterate\r\nthrough a list of files. If the file is present it will copy the appropriate DLL into the beginning of the file,\r\noverwriting the contents already there. The files to be overwritten in question are shown in Table 3, and appear to\r\nbe carefully selected to cause the least potential problems, with all but one of them being for different\r\narchitectures than the running host.\r\nWindows Version Path\r\nWindows 2000 \\winnt\\help\\access.hlp\r\nWindows 2000 \\winnt\\system\\OLESVR.DLL\r\nWindows XP or 2003 \\windows\\twain.dll\r\nWindows XP or 2003 \\windows\\system\\OLESVR.DLL\r\nWindows Vista/2008 + \\windows\\syswow64\\C_932.NLS\r\nWindows Vista/2008 + \\windows\\system\\OLESVR.DLL\r\nWindows Vista/2008 + \\windows\\syswow64\\kmddsp.tsp\r\nhttp://williamshowalter.com/a-universal-windows-bootkit/\r\nPage 15 of 26\n\nWindows Version Path\r\nWindows Vista/2008 + \\windows\\syswow64\\Irclass.dll\r\nTable 3: Service DLL Paths by OS, in order attempted\r\nWindows 2000 will attempt to first overwrite the access.hlp file, which, if anyone has not already disabled the\r\nhelp popups, may cause errors. Similarly, the 16-bit OLESVR.DLL file is overwritten if access.hlp does not exist.\r\nThis will only cause issues if it is used by a 16-bit application, as 32-bit applications will be using the\r\nsystem32\\OLESVR32.DLL file. Windows XP will attempt to overwrite the 16-bit version of the twain.dll library\r\nfor scanners (even using old scanners, twain32.dll should be used), and then the 16-bit OLESVR.DLL if the\r\ntwain.dll is not found.\r\nIn 64-bit Windows Vista and newer systems, the default target is C_932.NLS, which is a 32-bit National Language\r\nSupport file for the Japanese language [7]. This assumes that authors did not plan on infecting targets running 32-\r\nbit applications in Japanese, as this would cause issues. The only file that will be tried for 32-bit Windows Vista\r\nand newer is the same 16-bit OLESVR.DLL. This will only cause issues for applications run in 16-bit\r\ncompatibility/emulation mode, as they are not natively supported by Vista and newer, and is therefore unlikely to\r\naffect most targets. The other two potential target files, which are also unlikely to be used, are both 16-bit DLLs\r\nfound in the syswow64 (32-bit compatibility) directory. They are actually only labeled as compatible with\r\nWindows Server 2003 and earlier operating systems on MSDN, but are for some reason still included in the\r\nsyswow64 directory. Kmddsp.tsp is a “kernel mode device driver” for “telephony service provider” network\r\ndrivers, and IRClass.dll is an Infrared Class Coinstaller [8]. Neither should ever be used on a 64-bit system and\r\ntherefore won’t cause any issues if overwritten.\r\nOnce the DLL has been written to the appropriate file, the registry is patched to overwrite the Schedule service’s\r\nDLL path with the path to the overwritten file. This should be approximately:\r\nHKLM\\SYSTEM\\CurrentControlSet\\Services\\Schedule\\Parameters\\ServiceDLL.\r\nrkImage will then return to 16-bit real mode, handing execution back to the original MBR at 0x7C00, allowing the\r\nboot process to continue and Windows to load. It is worth noting that since Windows NT also used the C:\\WINNT\r\ndirectory, it will match the first section of the bootkit which chooses the files to write the DLL into. However,\r\nsince Windows did not introduce the svchost.exe process until Windows 2000, services did not have a Parameters\r\nsub-key or a ServiceDLL value in Windows NT. As such, if installed on Windows NT the bootkit wouldn’t be able\r\nto locate the registry key for editing, and would fail out of the installation process. Additionally, it is likely that 32-\r\nbit versions of the dropper would not allow the install to a Windows 2000 system.\r\nhttp://williamshowalter.com/a-universal-windows-bootkit/\r\nPage 16 of 26\n\nFigure 9: Diagram showing the out-of-OS boot process.\r\nSchedule service DLL\r\nThe final component of the bootkit, responsible for running the backdoor within Windows, is the DLL that\r\nreplaced the Schedule service’s DLL. The bootkit did not change the rest of the registry key, so it will be loaded\r\ninto a svchost.exe executable. The Schedule service is part of the NetworkService group, so the DLL will be\r\nloaded into the svchost.exe containing the other services for the group, and a new thread will be spawned to run\r\nthe ServiceMain for that DLL. Additionally, as happens every time a DLL is loaded, the DLL’s entry point\r\n(DLLMain, in this case) is called by the Windows loader in another thread.\r\nThe HDRoot authors chose to use the DLLMain function to start the backdoor process and ServiceMain to revert\r\nthe service registry entry back to the original path. The DLLMain thread creates another thread running the\r\nfunction I identified in my disassembly as SpawnBackdoorThread. That thread creates a process running the\r\nbackdoor, which rkImage saved to %TEMP%\\Explorer.exe. It then sets a global variable in the DLL to signal that\r\nit successfully launched the backdoor, and suspends itself before continuing.\r\nSimultaneously the ServiceMain thread reverts the registry, and waits for the backdoor to start, sleeping and\r\nperiodically checking for the flag to be set. After the flag is set it resumes the SpawnBackdoor thread, and then\r\nexits. In turn, the SpawnBackdoor thread unloads the DLL from memory and then exits itself.\r\nhttp://williamshowalter.com/a-universal-windows-bootkit/\r\nPage 17 of 26\n\nFigure 10: Flow of the malicious Schedule service\r\nThis has the effect of both threads exiting and the DLL unloading at almost the exact same time, guaranteeing that\r\nthe service manager will have to restart the service, causing the legitimate service DLL to be loaded and run from\r\nthe patched registry entry. In all my tests of this sample, not once did the real Windows service ever fail to start\r\nafter running the bootkit. This is completely contrary to Kaspersky’s claim that the bootkit breaks the service and\r\nthat all the victims must just not have cared or noticed that the service failed to start.\r\nConclusions\r\nMy analysis of the HDRoot malware shows Kaspersky’s claims that this bootkit was written sloppily is patently\r\nwrong. It also leads me to no other conclusion than that they did their entire analysis with and presented research\r\non a ten-year-old sample, passing it off as a sample from 2012 that had been in modern use. It also shows that the\r\nauthors, who have been designated as the WINNTI group, have been around for a significant period of time,\r\ndating back to at least 2006 if Kaspersky’s sample is to be believed.\r\nThe one stage of the attack in which the bootkit did not make good use of hiding techniques was in covering its\r\ntracks for the service and backdoor executables. Both the modified file hosting the DLL\r\n(C:\\Windows\\syswow64\\C_932.NLS in most my tests) and the backdoor in %TEMP%\\Explorer.exe were left\r\nintact on the file system. However, it is likely that a sophisticated backdoor run by the bootkit would know to\r\nremove these two pieces of evidence after starting itself, and it may just have been a choice of segregation of\r\nduties made by the authors.\r\nAnother criticism that can be made is the extremely weak use of encryption. The XOR cipher is little more than\r\nobfuscation and was trivial to figure out even just looking at the encrypted sectors on the disk. It can be argued,\r\nhowever, that since the entire contents of the bootkit is code that will be decrypted before it can be run, there is\r\nlittle point in hiding it from anything but simple scans, as it could just be captured from memory by analysts. To\r\nhttp://williamshowalter.com/a-universal-windows-bootkit/\r\nPage 18 of 26\n\nthat end, the simple cipher serves its purpose of not matching the signatures for executables or of a boot sector\r\nwhile on disk.\r\nOverall I was impressed with the level of detail that went into making this malware which is capable of installing\r\nitself on any Windows version, 32 or 64-bit, dating back to Windows 2000, with the exception of newer installs\r\nusing UEFI. The lack of UEFI support is unlikely to be an issue when targeting server systems, however,\r\nespecially with virtualization on the rise - very few virtual environments are virtualizing UEFI in their guests. The\r\nsmall touches, such as anticipating that the drive may have been repartitioned, are particularly impressive. Clearly\r\nsignificant thought and work went into the creation of this bootkit, and it is a mistake to dismiss it as amateur.\r\nWhile different versions of the dropper are geared toward different targets (the observed sample here targets the\r\nSchedule service on 64-bit systems), the overall framework is very flexible. The choices made in the dropper or\r\nwhen compiling the dropper are able to be tuned toward the target, choosing a service compatible with that\r\nversion of Windows. This makes narrowing down the traits of the bootkit from which services they target to be\r\nvery difficult, as it is trivial for the authors to change their target.\r\nReferences\r\n[1] “malwr,” [Online]. Available:\r\nhttps://malwr.com/analysis/NGFiNDBmMWNmYjM0NDVmZWIxNTg5OWFkMDUwYmIzNTQ/.\r\n[2] E. A. II, “Backdoor Built With Aheadlib Used in Targeted Attacks?,” Trend Micro, [Online]. Available:\r\nhttp://blog.trendmicro.com/trendlabs-security-intelligence/backdoor-built-with-aheadlib-used-in-targeted-attacks/.\r\n[3] Securelist, “WINNTI: More than just a game,” [Online]. Available: https://securelist.com/analysis/internal-threats-reports/37029/winnti-more-than-just-a-game/.\r\n[4] Securelist, “I am HDRoot Part 1,” [Online]. Available: https://securelist.com/analysis/publications/72275/i-am-hdroot-part-1/.\r\n[5] X. Kovah, “Intermediate Intel x86,” [Online]. Available:\r\nhttp://opensecuritytraining.info/IntermediateX86.html.\r\n[6] AMD, “AMD64 Architecture Programmer’s Manual, Volume 2,” [Online]. Available:\r\nhttp://developer.amd.com/wordpress/media/2012/10/24593_APM_v21.pdf.\r\n[7] Microsoft, “National Language Support (NLS) API Reference,” [Online]. Available:\r\nhttps://www.microsoft.com/resources/msdn/goglobal/default.mspx.\r\n[8] Microsoft, “Kernel-Mode Device Driver TSP,” [Online]. Available: https://msdn.microsoft.com/en-us/library/ms725209(v=vs.85).aspx.\r\nFiles are available at: https://github.com/williamshowalter/hdroot-bootkit-analysis\r\n1.1 Binary Files – hdroot-bootkit-analysis/binaries\r\nFile Description\r\nC_932.NLS 64-bit bootkit service DLL sample, as installed\r\ndriver32.sys.bin\r\n32-bit kernel driver used by the dropper to write directly to the physical\r\ndisk.\r\nhttp://williamshowalter.com/a-universal-windows-bootkit/\r\nPage 19 of 26\n\nFile Description\r\ndriver64.sys.bin\r\n64-bit signed kernel driver used by the dropper to write directly to the\r\nphysical disk.\r\ndropper64.bin 64-bit dropper sample that installs bootkit\r\nmbr-clean.bin MBR before modification, for comparison.\r\nmbr-inst.bin MBR that has been modified after install.\r\npe1_decrypted.bin\r\n32-bit bootkit service DLL sample, extracted and decrypted from\r\ndecrypted rkimage\r\npe1_encrypted_b61e1dcf.bin\r\n32-bit bootkit service DLL sample, extracted in original form from\r\ndecrypted rkimage. XOR key is 0xb64e1dcf.\r\npe2_decrypted.bin\r\n64-bit bootkit service DLL sample, extracted and decrypted from\r\ndecrypted rkimage\r\npe2_encrypted_b61e8d81.bin\r\n64-bit bootkit service DLL sample, extracted in original form from\r\ndecrypted rkimage. XOR key is 0xb64e8d81.\r\nrkimage_decrypted.bin rkImage sample, extracted from harddrive and decrypted.\r\nrkimage_encrypted.bin rkImage sample, extracted from harddrive and decrypted.\r\nrkimage_backdoor_decrypted.bin\r\nrkImage sample with example backdoor, extracted from harddrive and\r\ndecrypted.\r\nrkimage_backdoor_encrypted.bin\r\nrkImage sample with example backdoor, extracted from harddrive.\r\nObfuscated with 0x76 byte-XOR.\r\nverifier_win7_decrypted.bin\r\nverifier sample, containing the verifier sector followed by two copies of\r\nthe original mbr sector.\r\nverifier_win7_encrypted.bin\r\nVerifier sample, containing the verifier sector followed by two copies of\r\nthe original mbr sector. Obfuscated with 0x76 byte-XOR.\r\nverifier_win10_decrypted.bin\r\nverifier sample, containing the verifier sector followed by two copies of\r\nthe original mbr sector.\r\nverifier_win10_encrypted.bin\r\nVerifier sample, containing the verifier sector followed by two copies of\r\nthe original mbr sector. Obfuscated with 0x76 byte-XOR.\r\n1.2 Code Files – hdroot-bootkit-analysis/code\r\nhttp://williamshowalter.com/a-universal-windows-bootkit/\r\nPage 20 of 26\n\nFile Description\r\nconvert.c C utility to decrypt verifier and rkimage samples.\r\ndll_decryptor.c C utility to decrypt service DLL samples with 4-byte XOR keys.\r\nfuzzer.py Simple python fuzzer to discover commands to dropper64.bin\r\nproof.cpp\r\nC++ program to install as backdoor. Writes C:\\proof.txt as evidence that bootkit ran\r\nsuccessfully.\r\n1.3 Evidence Files – hdroot-bootkit-analysis/evidence\r\nFile Description\r\ncrc_error.PNG\r\nError message shown by check command when secondary bootkit image is\r\nmodified after install.\r\ndriver64_certificate.PNG Screenshot of the stolen certificate used by the 64-bit kernel driver.\r\ndriver64_valid.PNG\r\nScreenshot showing that the certificate on the kernel driver has not been\r\nrevoked.\r\ndropper64_certificate.PNG Screenshot of the stolen certificate used by the 64-bit dropper.\r\ndropper64_revoked.PNG Screenshot showing that the certificate on the dropper has been revoked.\r\nhashes_after.txt Hashes taken of files after the bootkit has run on a Windows 7 virtual machine.\r\nhashes_before.txt\r\nHashes taken of files before the bootkit has run on a Windows 7 virtual\r\nmachine.\r\nhashes_win10.txt\r\nHashes of the first and second rkImage locations on a Windows 10 virtual\r\nmachine with \u003e 30% free space.\r\ninstall_win10.PNG Screenshot of installing a backdoor on Windows 10.\r\ninstall_win10_cmd.PNG Screenshot of installing cmd.exe as the backdoor.\r\ninstall_win7.PNG Screenshot of installing a backdoor on Windows 7 with low disk space.\r\ninstaller_cmd.txt The text output of installing a backdoor on Windows 10.\r\nNeowiz.p7b Extracted certificate used in the 64-bit kernel driver.\r\nreg_service_after.txt\r\nRegistry after boot, with timestamps showing it was written to, even if the\r\nvalues didn’t change.\r\nreg_service_before.txt Registry before rebooting, with timestamps.\r\nhttp://williamshowalter.com/a-universal-windows-bootkit/\r\nPage 21 of 26\n\nFile Description\r\nvol_modules.txt Volatility output snippet from listing modules that shows the kernel driver.\r\nvol_reg_debugfile.txt\r\nVolatility output that shows a registry key for the DEBUGFILE service used by\r\nthe kernel driver.\r\n1.4 Ida Pro Files – hdroot-bootkit-analysis/ida pro\r\nFile Description\r\ndriver32.sys.idb Ida Pro file for the 32-bit kernel driver. Functionally same as the 64-bit driver.\r\ndriver64.sys.idb Ida Pro file for the 64-bit kernel driver. Functionally same as the 32-bit driver.\r\ndropper64.i64\r\nIda Pro file for the dropper sample. Largely not reversed, as the static sample is\r\npacked with VMProtect.\r\nmbr_infected.idb Ida Pro file for the bootkit MBR. Disassembly is 16-bit.\r\npe1_decrypted.idb Ida Pro file for the 32-bit service DLL. Functionally same as the 64-bit DLL.\r\npe2_decrypted.i64 Ida Pro file for the 64-bit service DLL. Functionally same as the 32-bit DLL.\r\nrkimage_decrypted.idb\r\nIda Pro file for rkImage. Contains real mode (16-bit) and protected mode (32-bit)\r\nsegments. Also has undefined data at the end because the sample disassembled was\r\nmistakenly longer than the rkimage+bootkit length.\r\nverifier_decrypted.idb\r\nIda Pro file for the verifier. Contains verifier and original MBR. Disassembly is 16-\r\nbit.\r\nAppendix 2: Sample Hashes\r\nMD5\r\n2c85404fe7d1891fd41fcee4c92ad305 dropper64.bin\r\n4dc2fc6ad7d9ed9fcf13d914660764cd driver32.sys.bin\r\n8062cbccb2895fb9215b3423cdefa396 driver64.sys.bin\r\nc7fee0e094ee43f22882fb141c089cea pe1_decrypted.bin\r\nd0cb0eb5588eb3b14c9b9a3fa7551c28 pe2_decrypted.bin\r\n76e1e42988befbf13b4f934604206250 rkimage_encrypted.bin\r\n613fd19d0abc3d018ead52afabd59fec rkimage_decrypted.bin\r\n287fac6f4dac57253ac0061be1508f9d C_932.NLS.bin\r\nSHA1\r\nhttp://williamshowalter.com/a-universal-windows-bootkit/\r\nPage 22 of 26\n\n4c3171b48d600e6337f1495142c43172d3b01770 dropper64.bin\r\n7ff22bd8667ce23e7db8c759bd03c15fb7226c76 driver32.sys.bin\r\n268dd909933c187d2798b5815674d70b930b498e driver64.sys.bin\r\n24a80cd100274e2c39180741aa688a4e73282552 pe1_decrypted.bin\r\n5d6c1a3c2d827c714b764b1c5a3e7370ed737986 pe2_decrypted.bin\r\naaf677acc05ae94f98f836fb44fd672a4b2d90db rkimage_encrypted.bin\r\n3c22ef94a737484e2f708393dcbabdfdb9d6cfbc rkimage_decrypted.bin\r\n88912b5227145d3a715ae6eeebd5935c89955721 C_932.NLS.bin\r\nAppendix 3: Screenshots\r\nDropper\r\nhttp://williamshowalter.com/a-universal-windows-bootkit/\r\nPage 23 of 26\n\nFigure 11: Dropper’s certificate\r\nhttp://williamshowalter.com/a-universal-windows-bootkit/\r\nPage 24 of 26\n\nFigure 12: Check before inst\r\nFigure 13: Check after inst\r\nhttp://williamshowalter.com/a-universal-windows-bootkit/\r\nPage 25 of 26\n\nFigure 14: Info for backdoor\r\nSource: http://williamshowalter.com/a-universal-windows-bootkit/\r\nhttp://williamshowalter.com/a-universal-windows-bootkit/\r\nPage 26 of 26",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"ETDA",
		"Malpedia",
		"MISPGALAXY"
	],
	"references": [
		"http://williamshowalter.com/a-universal-windows-bootkit/"
	],
	"report_names": [
		"a-universal-windows-bootkit"
	],
	"threat_actors": [
		{
			"id": "5bbced13-72f7-40dc-8c41-dcce75bf885e",
			"created_at": "2022-10-25T15:50:23.695735Z",
			"updated_at": "2026-04-10T02:00:05.335976Z",
			"deleted_at": null,
			"main_name": "Winnti Group",
			"aliases": [
				"Winnti Group"
			],
			"source_name": "MITRE:Winnti Group",
			"tools": [
				"PipeMon",
				"Winnti for Windows",
				"PlugX"
			],
			"source_id": "MITRE",
			"reports": null
		},
		{
			"id": "4d5f939b-aea9-4a0e-8bff-003079a261ea",
			"created_at": "2023-01-06T13:46:39.04841Z",
			"updated_at": "2026-04-10T02:00:03.196806Z",
			"deleted_at": null,
			"main_name": "APT41",
			"aliases": [
				"WICKED PANDA",
				"BRONZE EXPORT",
				"Brass Typhoon",
				"TG-2633",
				"Leopard Typhoon",
				"G0096",
				"Grayfly",
				"BARIUM",
				"BRONZE ATLAS",
				"Red Kelpie",
				"G0044",
				"Earth Baku",
				"TA415",
				"WICKED SPIDER",
				"HOODOO",
				"Winnti",
				"Double Dragon"
			],
			"source_name": "MISPGALAXY:APT41",
			"tools": [],
			"source_id": "MISPGALAXY",
			"reports": null
		},
		{
			"id": "945a572f-ebe3-4e2f-a288-512fe751cfa8",
			"created_at": "2022-10-25T16:07:24.413971Z",
			"updated_at": "2026-04-10T02:00:04.97924Z",
			"deleted_at": null,
			"main_name": "Winnti Group",
			"aliases": [
				"G0044",
				"Leopard Typhoon",
				"Wicked Panda",
				"Winnti Group"
			],
			"source_name": "ETDA:Winnti Group",
			"tools": [
				"Agentemis",
				"BleDoor",
				"Cobalt Strike",
				"CobaltStrike",
				"FunnySwitch",
				"RbDoor",
				"RibDoor",
				"RouterGod",
				"Winnti",
				"cobeacon"
			],
			"source_id": "ETDA",
			"reports": null
		},
		{
			"id": "2a24d664-6a72-4b4c-9f54-1553b64c453c",
			"created_at": "2025-08-07T02:03:24.553048Z",
			"updated_at": "2026-04-10T02:00:03.787296Z",
			"deleted_at": null,
			"main_name": "BRONZE ATLAS",
			"aliases": [
				"APT41 ",
				"BARIUM ",
				"Blackfly ",
				"Brass Typhoon",
				"CTG-2633",
				"Earth Baku ",
				"GREF",
				"Group 72 ",
				"Red Kelpie ",
				"TA415 ",
				"TG-2633 ",
				"Wicked Panda ",
				"Winnti"
			],
			"source_name": "Secureworks:BRONZE ATLAS",
			"tools": [
				"Acehash",
				"CCleaner v5.33 backdoor",
				"ChinaChopper",
				"Cobalt Strike",
				"DUSTPAN",
				"Dicey MSDN",
				"Dodgebox",
				"ForkPlayground",
				"HUC Proxy Malware (Htran)"
			],
			"source_id": "Secureworks",
			"reports": null
		}
	],
	"ts_created_at": 1775434928,
	"ts_updated_at": 1775791941,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/6a8837cf9fb684596fbe67781fd15a2a23ebf705.pdf",
		"text": "https://archive.orkl.eu/6a8837cf9fb684596fbe67781fd15a2a23ebf705.txt",
		"img": "https://archive.orkl.eu/6a8837cf9fb684596fbe67781fd15a2a23ebf705.jpg"
	}
}