Rebooting a machine to apply the latest patches can mean significant downtime for a server, which is why Windows supports a run-time method of patching, called a hot patch (or simply hotpatch), in contrast to a cold patch, which requires a reboot. Hotpatching doesn’t simply allow files to be overwritten during execution; instead, it includes a complex series of operations that can be requested (and combined). These operations are listed in Table 3-28.
Table 3-28. Hotpatch Operations
Although hotpatches use internal kernel mechanisms, their actual implementation is no different from cold patches. The patch is delivered through Windows Update, typically as an executable file containing a program called Update.exe that performs the extraction of the patch and the update process. For hotpatches, however, an additional hotpatch file, containing the .hp extension, will be present. This file contains a special PE header called .HOT1. This header contains a data structure describing the various patch descriptors present inside the file. Each of these descriptors identifies the offset in the original file that needs to be patched, a validation mechanism (which can include a simple comparison of the old data, a checksum, or a hash), and the new data to be patched. The kernel parses the descriptors and applies the appropriate modifications. In the case of a protected process (see Chapter 5 for more information on processes) and other digitally signed images, the hotpatch must also be digitally signed in order to prevent fake patches from being applied to sensitive files or processes.
Because the hotpatch file also includes the original data, the hotpatching mechanism can also be used to uninstall a patch at run time.
Compile-time hotpatching support works by adding 7 additional bytes to the beginning of each function—4 are considered part of the end of the previous function, and 2 are part of the function prolog—that is, the function’s beginning. Here’s an example of a function that was built with hotpatching information:
lkd> u nt!NtCreateFile - 5 nt!FsRtlTeardownPerFileContexts+0x169: 82227ea5 90 nop 82227ea6 90 nop 82227ea7 90 nop 82227ea8 90 nop 82227ea9 90 nop nt!NtCreateFile: 82227eaa 8bff mov edi,edi
Notice that the five nop instructions don’t actually do anything, while the mov edi, edi at the beginning of the NtCreateFile function are also essentially meaningless—no actual state-changing operation takes place. Because 7 bytes are available, the NtCreateFile prologue can be transformed into a short jump to the buffer of five instructions available, which are then converted to a near jump instruction to the patched routine. Here’s NtCreateFile after having been hotpatched:
lkd> u nt!NtCreateFile - 5 nt!FsRtlTeardownPerFileContexts+0x169: 82227ea5 e93d020010 jmp nt_patch!NtCreateFile (922280e7) nt!NtCreateFile: 82227eaa ebfc jmp nt!FsRtlTeardownPerFileContexts+0x169 (82227ea5)
This method allows only the addition of 2 bytes to each function by jumping into the previous function’s alignment padding that it would most likely have at its end anyway.
There are some limitations to the hotpatching functionality:
Patches that third-party applications such as security software might block or that might be incompatible with the operation of third-party applications
Patches that modify a file’s export table or import table
Patches that change data structures, fix infinite loops, or contain inline assembly code