📜 ⬆️ ⬇️

Hotpatch. Windows kernel memory patch

In the version of Windows Server 2003 SP1, a technology called hotpatching was introduced. That is, updating the system on the fly, without the need to reboot it. The technology allows you to install patches on individual functions (both user and kernel mode). In version 8.1, the possibility of installing hotpatches was eliminated. It is noteworthy that this feature can be used from user-mode'a even in the case of kernel-mode patches.

It is worth noting that such patches were released for a short time and only under Windows Server 2003 SP1.

Consider a specific patch example: Security Update KB914389. This update contains several patches of functions from the mrxsmb.sys and rdbss.sys drivers.

The patch for each driver contains two files: the driver, which will be replaced by the driver after the reboot, and the mysterious file with the * .hp.sys extension, which is an ordinary driver. It must contain a section called ".hotp1" (two spaces are required at the end). Let's take a closer look at the patching process itself.
')
First, you need to introduce the concept of hotpatchable functions. This is such a function, the first instruction of which is the two-byte instruction “mov edi, edi”, and before the beginning of the function there are five nop'ov.
There are also distinguished semi-hotpatchable functions - those with the first two-byte instruction, but not mov edi, edi.

The instruction "mov edi, edi" is entered into hotpatchable functions in order to secure hotpatch on multiprocessor systems. For example, if the first instruction were single-byte, then the following result could be obtained: one of the threads enters the function to be patched and executes the first command. At the same time, another thread installs a patch on this function, as a result of which the first one appears in the middle of the two-byte instruction “jmps-5”, which will lead to a system crash.

Below is a rather in-depth analysis of the hotpatching mechanism, which may be uninteresting to many readers.
The essence of the technology is as follows: at first, the * .hp.sys driver is loaded into memory using the MmLoadSystemImage function. Next, read all the characteristics of the patch, which are in the section ".hotp1". An exemplary structure representing the patch header is presented below. The structure is taken from here , there is no certainty in its absolute accuracy, but no disagreements with dysasm were found.

typedef struct _HOTPATCH_HEADER { DWORD Signature;//"HOT1" DWORD Version;//   = 0x00010000 DWORD FixupRgnCount;//  x86 ,       RtlpApplyRelocationFixups DWORD FixupRgnRva;//RVA   DWORD ValidationCount;//   RtlpValidateTargetRanges DWORD ValidationArrayRva;//RVA   DWORD HookCount;//,  .  ,    DWORD HookArrayRva;//   ,    RtlReadHookInformation ULONGLONG OrigHotpBaseAddress;// 86 .       ULONGLONG OrigTargetBaseAddress;//  ,     DWORD TargetNameRva;//,     ,    DWORD ModuleIdMethod;//  union { ULONGLONG Quad; GUID Guid; struct { GUID Guid; DWORD Age; } PdbSig; BYTE Hash128[16]; BYTE Hash160[20]; } TargetModuleIdValue; } HOTPATCH_HEADER, *PHOTPATCH_HEADER; 

In the event that the patch is superimposed on the hotpatchable function of x86 systems, the first instruction of the function being patched is replaced with a short jmp - jmps -5 (oppbod ebf9). It translates the control flow five bytes back where the five-byte jmp m32 instruction is placed, that is, the farthest jmp to the address specified in the hotpatch.
In other cases, regardless of the type of the function being patched, the difference in the addresses of the target module and the loaded * .hp.sys is checked. The patch is installed only if the module is loaded within + -2GB from the target module (limited by the size of the jmp'ff 25 operand). The first instruction is replaced with the six-byte instruction “jmp m32”, with rip-relative address to the target function.

And now let's look at how you can start the hotpatching process.

From ntdll.dll, the NtSetSystemInformation function is exported, which works in the same way as the NtQuerySystemInformation function that is often used at one time, that is, it accepts one of the SystemInformationClass arguments, which determines the further behavior of the function. If you pass the functions SystemInformationClass = 69, then, failing to kernel-mode via syscall, control is transferred to the function MmHotPatchRoutine.

The file is loaded into the * .hp file and the control is further transferred to the MiPerformHotpatch function.
In it, among other things, it searches for the ".hotp1" section in the loaded module, calls the RtlFindRtlPatchHeader function, and also searches for the target module in memory by iterating through all the sessions in the system. Next comes the transfer of control to the RtlInitializeHotpatch function.



We will not delve into the RtlpApplyRelocationFixups and RtlpValidateTargetRanges functions, we just say that with the help of the latter one can make sure that the target function is hotpatchable.



As a function of RtlReadHookInformation, patches are actually installed.

The structure of each patch is presented below.

 typedef struct _HOTPATCH_HOOK { WORD HookType;//  HOTPATCH_HOOK_TIPE WORD HookOptions; DWORD HookRva; DWORD HotpRva; DWORD ValidationRva; } HOTPATCH_HOOK, *PHOTPATCH_HOOK; typedef enum _HOTPATCH_HOOK_TYPE { HOTP_Hook_None = 0, HOTP_Hook_VA32 = 1, HOTP_Hook_X86_JMP = 2, HOTP_Hook_PCREL32 = 3, //not yet implemented HOTP_Hook_X86_JMP2B = 4, HOTP_Hook_VA64 = 16, HOTP_Hook_IA64_BRL = 32, HOTP_Hook_IA64_BR = 33, //not yet implemented HOTP_Hook_AMD64_IND = 48, HOTP_Hook_AMD64_CNT = 49 } HOTPATCH_HOOK_TYPE; 

Next, the RtlpReadSingleHookInformation function is called twice, in which the first time the springboard size is determined (the format and size of the jmp command), and the second time the patch is installed directly.



Also in this function, the distance between the loaded and the target module is checked. If it is more than 2GB, the patch is not installed.


Let's say we install the patch on Windows 7 x64. Let's try to implement a patch of any function. For example, you can select the FatCommonWrite function of the fastfat subsystem, which is called when writing any data to a fat32 USB flash drive. First you need to write a driver that will contain a filled ".hotp1" section and a new function.

 #pragma section (".hotp1 ") __declspec(allocate(".hotp1 ")) struct Hotp_Header { ULONG Signature; ULONG Version; ULONG FixupRgnCount; ULONG FixupRgnRva; ULONG ValidationCount; ULONG ValidationArrayRva; ULONG HookCount; ULONG HookArrayRva; ULONGLONG OrigHotpBaseAddress; ULONGLONG OrigTargetBaseAddress; ULONG TargetNameRva; ULONG ModuleIdMethod; union { ULONGLONG Quad; GUID Guid; struct { GUID guid; ULONG Age; } PdbSig; UCHAR Hash128[16]; UCHAR Hash160[20]; } TargetModuleIdValue; CHAR TagretName[13]; struct { USHORT HookType; USHORT HookOptions; ULONG HookRva; ULONG HotpRva; ULONG ValidationRva; } Hook; } hpHeader = { 0x31544F48, // "1TOH" 0x00010000, // 1.0 0x00000000, // FixupRgn 0x00000000, // FixupRgn Rva 0x00000000, // Validations 0x00000000, // Validation Rva 0x00000001, // 1 Hook 0x00005060, // HookRva 0x0000000000010000, // HotpBase 0x0000000000010000, // TargetBase 0x00005050, // Targetname Rva 0x00000000, // ModuleID 0x0000000000000000, // Quad "fastfat.sys", { 0x0030, // hook type HOTP_Hook_AMD64_IND 0x8000, // hook option +- 2GB 0x0002B6F0, // hook rva 0x0004392A, // hotp rva 0x00000000 // valid rva } }; NTSTATUS FatCommonWrite() { PINT32 p = 0; INT32 a = *p;//  ,     (: return a; } 

Now you need to write an application that will trigger the hotpatching process. To do this, we write the usual Win32 application.

 typedef struct _SYSTEM_HOTPATCH_CODE_INFORMATION { ULONG Flags; ULONG InfoSize; USHORT NameOffset; USHORT NameLength; } SYSTEM_HOTPATCH_CODE_INFORMATION; // //... //  PatchInfo  ,   ,   KB914389 // // //  . OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken); SetPrivilege(hToken, SE_DEBUG_NAME, TRUE); SetPrivilege(hToken, SE_LOAD_DRIVER_NAME, TRUE); ZwSetSystemInformation(69, PatchInfo, PatchInfo->InfoSize); 

This application will launch the hotpatching process. It will be necessary only to insert a fat32 USB flash drive into a computer, write something on it and see a terse BSOD.

FatCommonWrite before patch:



FatCommonWrite after patch:



In conclusion, it is worth noting that although this technology is not a potential vulnerability, it still represents a rather interesting way of patching memory belonging to the Windows kernel.

Source: https://habr.com/ru/post/252275/


All Articles