Microsoft Patching Internals

冷热补丁解析
Author: EliCZ  .

Caveat Emptor

This article was not written to read like a novel. It is a to-the-point technical dump describing the inner workings of Microsoft's cold and hot patching process. The majority of the symbolic names listed below have been derived from NTDLL and NTOSKRNL. Please post any questions you may have directly (for the benefit of others) to this article and the author will gladly respond. The article may be updated in the future to include some of these answers inline.

A companion download including examples and appropriate header files is available for download: MSPatching.zip.

Cold Patching

Replacing functions by replacing their containers - files and sections.

The image to patch is "atomically replaced" with an image that contains all code and data contained within the original plus the fixed functions and redirections to them through embedded hooks. The functions to update are statically hooked and the hooks transfer the execution to the fixed functions in the '.text_CP' or '.text_CO' section of a coldpatch module. This section is followed by the '.DBG_POR' section in situations where the original '.data' section has to be modified. In other cases, the '.text_CP' / '.text_CO' sections are followed by '.data_CP' or '.data_CO'. Overall, there can theoretically be as many _CP / _CO sections as the original image has (.text, rdata, .data, etc..). The '.DBG_POR' section contains module imports, exports and a debug information. The debug information for the coldpatch module usually consists of two entries. The first entry is of CODEVIEW type, the second is RESERVED10. RESERVED10 data contains the coldpatch debug information that is comprised of the HOTPATCH_DEBUG_HEADER structure followed by HOTPATCH_DEBUG_DATA structure. HOTPATCH_DEBUG_HEADER.Type has value DEBUG_SIGNATURE_COLDPATCH. The contents of HOTPATCH_DEBUG_DATA are used in a process called 'target module validation' when the validation of the original module fails and hotpatch checks if there is a coldpatch present.

The atomic file replacement is realized by filling the SYSTEM_HOTPATCH_CODE_INFORMATION.RenameInfo structure and calling SYSTEM_HOTPATCH_CODE_INFORMATION.Flags.HOTP_RENAME_FILES sub-function of ExApplyCodePatch (SystemHotpatchInformation class of Nt/ZwSetSystemInformation) function. HOTP_RENAME_FILES sub-function is not implemented in the newer OS versions/builds.

Replacing the image on a volume doesn't mean that all the newly created processes will load/contain the updated image. For the system purposes, security or for increasing module loading speed, a sections can be emplyed in the process of the image loading. The section for a system module (ntdll.dll, ...) is updated by HOTP_UPDATE_SYSDLL sub-function (no structure required). The section for the loader (from /KnownDlls object directory) is updated by calling HOTP_UPDATE_KNOWNDLL with AtomicSwap sub-structure filled. The old object's name is swapped with newly created temporary object (update.exe names the section 'ColdPatchInstallationInProgres') in the object directory.

Hot Patching

Replacing functions by replacing their code in the memory. Available since Server 2003 SP0, XP SP2 x86, ia64, x64.

Functions were successfully fixed on the volume. Now they should be changed in the memory - in the kernel memory for kernel modules or in the memory of user processes that contain the module to update. The functions to patch are dynamically hooked - one sufficiently long CPU instruction or data (pointer, RVA) of the function to fix is replaced with a branch instruction (pointer, RVA) that redirects an execution flow to a fixed function in the hotpatch module. After applying the hooks, the patched module in memory looks like the coldpatch module, just the targets of the branch instructions do not lie within '.text_CP' section but in the code section(s) of the hotpatch module.

The hotpatch module may contain the debug information similar to the coldpatch's one, just RESERVED10 data consists of HOTPATCH_DEBUG_HEADER only and HOTPATCH_DEBUG_HEADER.Type has value DEBUG_SIGNATURE_HOTPATCH.

The hotpatch module must contain a section named '.hotp1 ' that is at least 80 bytes (sizeof(HOTPATCH_HEADER)) long and that must begin with a HOTPATCH_HEADER structure. The structure is used for validating the target module, fixing relocations and creating intermediate RTL_PATCH_HEADER structure.

When applying the hotpatch to a user module, an updating agent enumerates the processes and creates remote threads into them that execute ntdll.LdrHotPatchRoutine. Newer OS versions/builds allow the remote thread creation from the kernel mode when HOTP_INJECT_THREAD sub-function is called and InjectInfo sub-structure is correctly filled.

LdrHotPatchRoutine checks if HOTP_USE_MODULE flag is set and the target module, whose base name is specified in UserModeInfo.TargetNameOffset, is present in the process. When applying the hotpatch to a kernel module, (ntoskrnl.MmHotPatchRoutine) both HOTP_USE_MODULE and HOTP_KERNEL_MODULE flags must be set and KernelInfo sub-structure must be filled.

The Source (hotpatch) module is loaded and checked for the presence of '.hotp1 ' section, HOTPATCH_HEADER.Signature and Version. If RTL_PATCH_HEADER for the source module already exists, hooks were successfully applied and HOTP_PATCH_APPLY flag is clear, the hooks are removed. Otherwise RTL_PATCH_HEADER is created, the target module whose name is in HOTPATCH_HEADER.TargetNameRva is checked for presence and validated according to ModuleIdMethod using TargetModuleIdValue. If there's a validation mismatch, system checks whether the target module is the coldpatch according to the coldpatch debug info. If it is the coldpatch, PATCHFLAG_COLDPATCH_VALID flag is set in RTL_PATCH_HEADER.PatchFlags.

The functions to fix may access the target image, they can call its functions and use its variables (it means pointers and call and jump targets do not have to be in the hotpatch module because they point directly to the target module). Such codes must be fixed using special relocation fixups from HOTPATCH_HEADER.FixupRgnRva with respect to HOTPATCH_HEADER.OrigHotpBaseAddress and HOTPATCH_HEADER.OrigTargetBaseAddress. Then the functions function as they would be called from the original module. Number of the HOTPATCH_FIXUP_ENTRY structures in the HOTPATCH_FIXUP_REGION must be even number. If the hotpatch contains standard base relocations, they usually apply only to a pointers to hotpatch's import table (APIs).

Various places of the target module can be validated according to HOTPATCH_VALIDATION structures in HOTPATCH_HEADER.ValidationArrayRva. Validations with option HOTP_Valid_Hook_Target are skipped (those are the places to patch).

HOTPATCH_HOOK_DESCRIPTOR structures are prepared according to HOTPATCH_HOOK structures in HOTPATCH_HEADER.HookArrayRva. HOTPATCH_HOOK.HookOptions contain in their first 5 bits the length of the instruction to replace and it must be at least as long as the length of the branch instruction - the rest is, for some hook types, padded with bytes of value 0xCC.

Again there's possibility to validate the bytes that will be replaced (HOTP_Valid_Hook_Target has now no effect). If there's a mismatch and the patch place already contains the adequate branch instruction, a list of RTL_PATCH_HEADER structures in TargetModule.LDR_DATA_TABLE_ENTRY.PatchInformation is traversed and bytes at HOTPATCH_HOOK_DESCRIPTOR.CodeOffset are compared with the prepared branch instruction. If there's mismatch and the target module is the coldpatch, the validation succeeds for some hook types, for the other ones is checked whether the branch instruction points into the coldpatch.

Upon succesfull validation and hook preparation are the remaining members of RTL_PATCH_HEADER filled in, the sections of the target module are made writable and the hooks are written by calling ExApplyCodePatch with RTL_PATCH_HEADER.CodeInfo and HOTP_PATCH flag set. If the patch application succeeds, RTL_PATCH_HEADER is linked to TargetModule.LDR_DATA_TABLE_ENTRY.PatchInformation list.
  • There's no security issue: Debug and LoadDriver privileges must be enabled for all Cold/HotPatch operations except for user mode hotpatching or when applying CodeInfo directly. CodeInfo cannot be directly applied to kernel when calling ExApplyCodePatch from user mode.
  • CodeInfo is applied "os-atomically" - the preemption is unlikely.
  • Function to fix doesn't have to be compiled (/linked) with /hotpatch (/functionpadmin) option.
  • There's no public tool (special version of C compiler, linker?) for creating the cold/hotpatches. It is possible to write a tool that will add/write to '.hotp1 ' section of image created by normal compiling/linking but there are 2 problems:
    • How to write the new function with instructions pointing to target module and with this conjuncted
    • Fixup handling. Anyway, one doesn't have to use the target module functions/data so there's no need for the hotpatch fixups.
Hook Types

HOTP_Hook_None

HOTP_Hook_VA32
32 bit value/pointer, 4 bytes

HOTP_Hook_X86_JMP
x86/64 long relative jump, E9 Rel32, <-2GB..2GB-1>,
Rel32 constructed according to Hook/HotpRva,
>= 5 bytes, padded with 0xCC

HOTP_Hook_PCREL32
not yet implemented,
for fixing Rel32 of x86/64 call or jump, 4 bytes

HOTP_Hook_X86_JMP2B
x86/64 short relative jump, EB Rel8 <-128B..127B>,
Rel8 is in HotpRva,
>= 2 bytes, padded with 0xCC

HOTP_Hook_VA64
64 bit value/pointer, 8 bytes

HOTP_Hook_IA64_BRL
ia64 branch, at HookRva must be a supported template type
>= 16 bytes

HOTP_Hook_IA64_BR
not yet implemented

HOTP_Hook_AMD64_IND
x86/64 absolute indirect jump, FF 25 [Offset32 / Rip+Rel32]
HotpRva (+Rip) must point to a variable that contains a pointer to fixed function,
>= 6 bytes, padded with 0xCC

HOTP_Hook_AMD64_CNT
16bit value/pointer, 2 bytes Hook combinations are allowed - HOTP_Hook_X86_JMP2B + HOTP_Hook_X86_JMP is typical. When the distance hotpatch-target exceeds 2GB, HOTP_Hook_AMD64_IND must be employed on x86/64. One then needs a place to store the pointer specified in [Offset32 / Rip+Rel32]. For x86 it can be inside the hotpach module but for x64 not. HOTP_Hook_AMD64_IND + HOTP_Hook_VA64 is the solution. /hotpatch option for x64 is not yet implemented but I would suggest:

  Buffer: //for HOTP_Hook_VA64
    8x nop
  FnStart:
    48 8D A4 24 00 00 00 00  lea rsp, [rsp + 0]  - 2 bytes more than required
   or
    0F 8x 00 00 00 00   j?? ___FCKpd___06 - as long as required but slower

  In Colpatch/After Hotpatching it could look like:
  Buffer:
    Ptr64FnContinue
  FnStart:
    FF 25 F2 FF FF FF  jmp qword ptr [Buffer] //[Rip-14]
    CC
    CC

Of course there's possibility to make a triple patch: JMP2B -> IND -> VA64.

x86 Patch Examples

Function created with /hotpatch, "semi-hotpachable" function, "non-hotpatchable" function. You may notice there's replaced more than one CPU instruction (5x nop with long relative jmp) but the nops are not involved in the function - they serve as buffer.

Original FunctionColdPatch/After HotPatchingIn Cold/HotPatch
5x 90         5x   nop
FnStart:      
8B FF         mov  edi, edi
55            push ebp
8B EC         mov  ebp, esp
56            push esi
57            push edi
8B 35 g_Data  mov  esi, g_Data


5x 90         5x   nop
FnStart:      
55            push ebp
8B EC         mov  ebp, esp
56            push esi
57            push edi
8B 35 g_Data  mov  esi, g_Data


FnStart:      
55            push ebp
8B EC         mov  ebp, esp
56            push esi
57            push edi
8B 35 g_Data  mov  esi, g_Data
E9 Rel32      jmp  FnContinue
FnStart:      
EB F9         jmp  $-5
55            push ebp
8B EC         mov  ebp, esp
56            push esi
57            push edi
8B 35 g_Data  mov  esi, g_Data


E9 Rel32      jmp  FnContinue
FnStart:      
55            push ebp
EB F8         jmp  $-6
56            push esi
57            push edi
8B 35 g_Data  mov  esi, g_Data


FnStart:      
55            push ebp
8B EC         mov  ebp, esp
56            push esi
57            push edi
E9 Rel32      jmp  FnContinue
CC            int  3
FnStart:      
FnContinue:   
55            push ebp
8B EC         mov  ebp, esp
56            push esi
57            push edi
8B 35 g_Data  mov  esi, g_Data
              ; fixup required


FnStart:      
55            push ebp
FnContinue:   
8B EC         mov  ebp, esp
56            push esi
57            push edi
8B 35 g_Data  mov  esi, g_Data
              ; fixup required

FnStart:      
55            push ebp
8B EC         mov  ebp, esp
56            push esi
57            push edi
FnContinue:   
8B 35 g_Data  mov  esi, g_Data
              ; fixup required


References

- Inside Update.exe - http://www.microsoft.com/technet/prodtechnol/windowsserver2003/deployment/winupdte.mspx
- KB packages for Server2003 x86 that contain the cold/hotpatches: 819696, 823182, 888113, 893086, 899588, 901190  
This version contains full core source codes! This is the only difference between this and the normal package! P.A.T.C.H. is a professional solution for applications/games patching and updating. It contains all features you need: installer, patcher, launcher, repairer, patches creator, FTP uploader, etc. An all-in-one, smart and clean solution! It can generate very small patches thanks to its included file binary diffing algorithm. What does it mean? It means that if you change only 5 bytes in your game, P.A.T.C.H. will create a patch that will change only those 5 bytes on users' builds, instead of downloading the entire edited file. Your users will be able to maintain updated their application copies with no pain or headaches, by saving bandwidth and time. Your users will love you! In addition, you will be able to create small-sized patches (and upload them) quickly with included tools! It includes Unity integrated tools and standalone version (if someone prefers to use it). You can export Unity integrated P.A.T.C.H. for each supported platform, thanks to Unity. Standalone P.A.T.C.H. can be compiled for each platform you need with Xamarin/Mono. You can check out this feature in P.A.T.C.H. documentation. P.A.T.C.H. supports each type of application: Unity based or standalone. Main features: - Binary diffing algorithm - Very small patches - Bandwidth saving - Checking for patches hash - Strong patches ZIP compression - A lot of raised event to hook to monitor what P.A.T.C.H. is doing - Encrypted configs - Customizable settings - Very flexible - No tricky configurations, no headaches! - Simple server side - Comes with launcher source code - If download fails, it will take care to download again failed patch for customizable attempts amount - Launching argument to avoid obsolete clients - Patch rollback feature: if patch process fails all changes will be discarded to avoid a build corruption - Hash validation for patched files - Linear and non-linear patches application - Files download over HTTP, HTTPS, FTP and file system - One-click-deploy - FTP Uploader - Installer and Repairer feature - Shortcuts creation - In-game embedded patcher - Self-update feature - WPF and WinForms examples too - Command Line tool
### 三级标题:Monkey Patching 的定义与特性 Monkey Patching 是一种在运行时动态修改或扩展类、模块、函数等行为的技术。它允许开发者在不更改原始源代码的前提下,通过替换对象的属性或方法,来改变代码的执行方式或增加新功能。这一技术特别适用于动态编程语言,如 Python,因为它提供了高度的灵活性和可扩展性[^1]。 ### 三级标题:工作原理 Monkey Patching 的核心在于运行时的动态替换。这意味着可以在程序启动时或执行过程中,对已存在的类、模块或函数进行修改。例如,在 Python 中,可以通过重新赋值的方式,将一个模块中的函数替换为另一个实现,从而改变其行为。这种技术的应用场景非常广泛,包括但不限于修复第三方库中的 bug、为现有代码添加新功能、以及在测试中模拟特定行为[^2]。 ### 三级标题:应用场景 - **修复第三方库中的 bug**:当使用第三方库时,如果发现其中存在 bug,但又无法直接修改库的源代码,可以使用 Monkey Patching 技术临时修复该 bug。 - **添加新功能**:无需修改现有类或函数的源代码,即可为其添加新的功能。 - **测试**:在测试过程中,可以使用 Monkey Patching 替换某些函数或方法,以便在特定条件下运行测试,或模拟某些行为[^3]。 ### 三级标题:Python 中的实现示例 在 Python 中,Monkey Patching 可以通过简单的赋值操作来实现。例如,假设有一个模块 `sub.py`,其中导入了标准库的 `json` 模块。为了提高性能,希望将 `json` 替换为更快的 `ujson` 模块。可以通过以下方式实现: ```python # main.py import json import ujson def monkey_patch_json(): json.__name__ = 'ujson' json.dumps = ujson.dumps json.loads = ujson.loads monkey_patch_json() print('main.py', json.__name__) import sub ``` 在这个例子中,`monkey_patch_json` 函数通过修改 `json` 模块的 `__name__` 属性,并将其 `dumps` 和 `loads` 方法替换为 `ujson` 的对应方法,实现了对 `json` 模块的行为修改。这样,在 `sub.py` 中即使没有显式导入 `ujson`,也可以使用经过优化的 `ujson` 模块的功能[^4]。 ### 三级标题:注意事项 尽管 Monkey Patching 提供了极大的灵活性,但也存在一些潜在的风险和注意事项: - **维护难度增加**:过度使用 Monkey Patching 可能会使代码难以理解和维护,因为行为的改变不是显式的。 - **冲突风险**:如果多个库或模块都尝试对同一个对象进行 Monkey Patching,可能会导致不可预测的行为。 - **调试困难**:由于行为可以在运行时被修改,这可能使得调试变得更加复杂。 ### 三级标题:总结 Monkey Patching 是一种强大的技术,它允许开发者在不修改原始代码的情况下,动态地修改或扩展代码的行为。然而,这种技术的使用应当谨慎,以避免引入不必要的复杂性和潜在的问题。在适当的情况下,Monkey Patching 可以极大地提升开发效率和代码的灵活性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值