上面的贴是2:06 AM发出的,王宇勤奋呀!天道酬勤,我看到了中国的Alex在崛起^_^ 很喜欢王宇的这个提议。这个问题虽然很久以前就思考过,但是始终没有深究。
先抛块砖吧:
首先,下面这篇来自微软官方的公开paper介绍了Windows系统中hotpatch的基础,没有读过的朋友不妨先读一读。
http://technet.microsoft.com/en-us/library/cc787843.aspx
第二,在NT内核中有一系列包含hotpatch的函数,虽然没有仔细考察每一个,但想必与hotpatch有关。
lkd> x nt!*hotpatch* 805df092 nt!RtlFreeHotPatchData = <no type information> 805ae9e0 nt!MmHotPatchRoutine = <no type information> 80552fe8 nt!MiHotPatchList = <no type information> 805ae84a nt!MiPerformHotPatch = <no type information> 805dfd6c nt!RtlInitializeHotPatch = <no type information> 805dfdf6 nt!RtlCreateHotPatch = <no type information> 805defb4 nt!RtlpFreeHotpatchMemory = <no type information> 805df022 nt!RtlGetHotpatchHeader = <no type information> 805def90 nt!RtlpAllocateHotpatchMemory = <no type information> 805ae80a nt!MiRundownHotpatchList = <no type information>
第三,大多数内核服务的入口函数的开头两个字节都是mov edi,edi,这个是为hotpatch准备的。
例如:
lkd> u nt!NtCreateFile nt!NtCreateFile: 8056d62e 8bff mov edi,edi 8056d630 55 push ebp 8056d631 8bec mov ebp,esp
注意,上面的mov edi,edi指令的起始地址是xxxxxxxe,这不是一个按四字节对齐的地址,但是它的前两个字节通常是无用的INT 3指令:
lkd> u 8056d62c nt!NtRemoveIoCompletion+0x1a8: 8056d62c cc int 3 8056d62d cc int 3 nt!NtCreateFile: 8056d62e 8bff mov edi,edi 8056d630 55 push ebp 8056d631 8bec mov ebp,esp 8056d633 33c0 xor eax,eax 8056d635 50 push eax 8056d636 50 push eax
注意,8056d62c是一个按四字节对齐的地址,这样,hotpatch时,就可以利用32位CPU的一条指令以原子方式(LOCK前缀)把这里的指令替换掉,不需要任何软件方面的锁。因为这个原因,hotpatch时把要patch函数的起始指令替换为新指令是很安全的,不管是单核还是多核。
但是!NtReadFile的开头几个字节不是:
lkd> u nt!NtReadFile nt!NtReadFile: 80570afa 6a68 push 68h 80570afc 68908a4d80 push offset nt!GUID_DOCK_INTERFACE+0x374 (804d8a90) 80570b01 e89a69fcff call nt!_SEH_prolog (805374a0) 80570b06 33f6 xor esi,esi
最后,nt!MiPerformHotPatch函数应该是实际进行hotpatch的内核函数。
|