Advanced Debugging
About AdvDbg Consult Train Services Products Tools Community Contact  
欢迎光临 高端调试 登录 | 注册 | FAQ
 
  ACPI调试
Linux内核调试
Windows内核调试
 
  调试战役
调试原理
新工具观察
 
  Linux
Windows Vista
Windows
 
  Linux驱动
WDF
WDM
 
  PCI Express
PCI/PCI-X
USB
无线通信协议
 
  64位CPU
ARM
IA-32
  CPU Info Center
 
  ACPI标准
系统认证
Desktop
服务器
 
  Embedded Linux
嵌入式开发工具
VxWorks
WinCE
嵌入式Windows
 
  格蠹调试套件(GDK)
  格蠹学院
  小朱书店
  老雷的微博
  《软件调试》
  《格蠹汇编》
  《软件调试(第二版)》
沪ICP备11027180号-1

Windows内核调试

帖子发起人: 王宇   发起时间: 2008-11-08 02:06 上午   回复: 10

Print Search
帖子排序:    
   2008-11-08, 02:06 上午
WANGyu 离线,最后访问时间: 2012/9/10 3:34:00 王宇

发帖数前10位
男
注册: 2007-05-08
发 贴: 306
请大家讨论一个问题
Reply Quote

有关于多核下安全地 N 字节 Patch 内核的问题。

我们都知道 Patch 是有风险的,可有时候我们又不得不冒着风险。

我想知道究竟这个风险有多大,又分为哪些情况,它们可不可以尽量避免,如何避免。

请您谈谈您对此问题的理解,越深刻越好,越详细越好,谢谢您。


为了不涉及公司的代码利益,我自主编写了我的 Patch 引擎,做法如下图(恕我不过多涉及细节):



简言之 即其它核空转(DISPATCH_LEVEL),本核操作(DISPATCH_LEVEL),本核完成(PASSIVE_LEVEL),其它核继续工作。

请您参考讨论。


IP 地址: 已记录   报告
   2008-11-08, 09:04 上午
Raymond 离线,最后访问时间: 2020/7/3 3:40:25 格蠹老雷

发帖数前10位
注册: 2005-12-19
发 贴: 1,303
Re: 请大家讨论一个问题
Reply Quote

上面的贴是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的内核函数。

 


IP 地址: 已记录   报告
   2008-11-08, 10:56 上午
nightxie 离线,最后访问时间: 2010/4/3 8:32:07 nightxie

发帖数前25位
注册: 2008-06-09
发 贴: 43
Re: 请大家讨论一个问题
Reply Quote
这里我有个地方没看明白。
首先王宇讨论的是“多核下安全地 N 字节 Patch 内核的问题”。
我的理解是怎么在驱动上下功夫,让Patch内核更安全。这里Raymond前辈说的“注意,8056d62c是一个按四字节对齐的地址,这样,hotpatch时,就可以利用32位CPU的一条指令以原子方式(LOCK前缀)把这里的指令替换掉,不需要任何软件方面的锁。”
我没明白这句话的意思,我猜测可能的两种含义:
(1)是在我们的驱动中用原子方式替换掉8056d62c这的指令吗?那么即使这样,服务例程是从8056d62e 开始的,这样替换8056d62c的指令有什么意义?
(2)或者是说我们应该在8056d62c地址写入原子指令达到一些目的,这里就更不懂了。在8056d62c写入原子指令,了不起4个字节可操作,况且服务例程从8056d62e 开始,那么这样能干些什么?

不知道我的问题表达清楚没。
哎,论坛方式讨论问题就是感觉不方便啊。。。。
IP 地址: 已记录   报告
   2008-11-08, 12:21 下午
Raymond 离线,最后访问时间: 2020/7/3 3:40:25 格蠹老雷

发帖数前10位
注册: 2005-12-19
发 贴: 1,303
Re: 请大家讨论一个问题
Reply Quote

上一个帖子是有些关键点没能点明。王宇的Patch N字节是个大题目。实际操作中,一种可行的办法(Nt系统实用的)是把新的函数(A')复制到要修补模块的地址空间中,因为这时不会有人调用A',所以根本不用考虑多核和并发的问题。

接下来需要把原来函数(A)的入口点Patch进一条跳转指令。因为原函数是在使用的,可能有CPU正在执行或者即将执行它,所以这时就要格外注意并发和多核问题了。如何注意呢?干净利索的办法就是我说的四字节对齐方法。以原子方式写入一条跳转指令,那么新的调用便会被导向到新的函数A'。

因为在今天广泛使用的平坦地址模型下,使用只有两个字节的近跳转指令就可以跳到新的函数了。所以,要做的就是把两个字节的跳转指令和两个补位用的INT 3(或者NOP)合成四个字节,然后写过去。

 


IP 地址: 已记录   报告
   2008-11-08, 16:18 下午
nightxie 离线,最后访问时间: 2010/4/3 8:32:07 nightxie

发帖数前25位
注册: 2008-06-09
发 贴: 43
Re: 请大家讨论一个问题
Reply Quote
差不多能明白您的意思了。
谢谢^_^

记得原来看到MJ前辈也发表过这一类的方法,就是在
8bff mov edi,edi
patch后
EB F9 jmp $-5
然后在例程上面的nop或int3中再实现一个跳转到新的函数中。
IP 地址: 已记录   报告
   2008-11-08, 18:04 下午
WANGyu 离线,最后访问时间: 2012/9/10 3:34:00 王宇

发帖数前10位
男
注册: 2007-05-08
发 贴: 306
Re: 请大家讨论一个问题
Reply Quote

离 Alex 的水平还差十万八千里...  >_<

我提出讨论的是一个大的问题,我先抛块砖。(恕我不会过多谈论代码,我只发两张截图)

首先,我并不关心 Hook 的细节。nightxie 所说的“短跳+长跳”法,或者是常见的前 5 字节 Inline Hook,再或者是微点爱用的 Call Hook 都只是“论术”,OK 我们不回避,先“论术”:


在我的 Hook 引擎DEMO 里可以自动选择 Hook 点(默认是 Call Hook),上述的都支持:


/* ff15 Call Hook */


/* 短跳 + 长跳 Hook */


但是,这些伎俩有一个使用前提,就是如何 Patch 才是安全的? 这是“论道”:

问题可以分两类:
01. 你在 Ptach 的同时(或间隙),有别的线程(本核上的,其它核上的)也执行过来;
02. 你在 Ptach 之前,已经有线程执行在你要 ptach 的长度里(假设你要 Patch 100 字节,该线程执行在这之间),然后系统线程调度到了你这,你开始强制修改。

对于单核来说,我们可以选择关中断,或者提升 IRQL 保证线程不被切换以解决问题一。可是对于多核来说,这些做法无法控制全部的CPU。有些人想到了自旋锁,但是,系统根本不和你自旋。

DPC+空转法似乎成了唯一解,它可以最终解决问题一。

有人会说没必要,因为 Intel 有 F0 指令前缀 (lock prefix),但是锁总线也是有应用前提的:

• Reading or writing a byte
• Reading or writing a word aligned on a 16-bit boundary
• Reading or writing a doubleword aligned on a 32-bit boundary

显而易见的前提就是对齐 + 长度限制,试想我如果 Patch 5 字节、11 字节、103 字节 或是 N 字节,我该怎么用 F0 前缀呢?它没法保证原子性一次性操作那么多字节(且不一定对齐)。

反过来,我们可以思考为什么 N 字节 Patch 就是不安全的?(问题二)

或许是有一个线程A正好执行在 5 字节 或 103 字节中,此时,系统强制发生了线程切换,它的执行环境被保存,然后系统调度到了我这,我疯狂并开心地改写了这 103 字节,等到线程A再次获得执行时,它会发现——世界变了——随即 BSOD。

接着我又想到去扫描全部的系统线程,分析他们的执行点,但问题更多——如果这103字节里有 call、有等待的话.....

或许还有什么别的情况等或解决方法您补充...

如果上述假设成立,那么只能说明 多指令多字节 Patch 问题是无解的,而我们平时之所以不发生问题只能说明这是一个类似于10亿分之一的小概率事件问题?


IP 地址: 已记录   报告
   2008-11-08, 18:22 下午
Raymond 离线,最后访问时间: 2020/7/3 3:40:25 格蠹老雷

发帖数前10位
注册: 2005-12-19
发 贴: 1,303
Re: 请大家讨论一个问题
Reply Quote
要把思路调整一下。假定要修补函数A,需要改变它的100个字节,那么不要想着就在原来的A上直接补。更可行的方法是把一个离线补好的A'复制到内存中。当A'准备好之后,好比修好了一条新的水渠,接下来的工作只是把水引过来。也就是把A的入口改一下,跳过来。跳过来之后,其实A和A'还是同时存在一段时间的。这样问题就简单很多。否则的话,就好像是没修好渠就把水引过来^_^。

IP 地址: 已记录   报告
   2008-11-08, 18:28 下午
WANGyu 离线,最后访问时间: 2012/9/10 3:34:00 王宇

发帖数前10位
男
注册: 2007-05-08
发 贴: 306
Re: 请大家讨论一个问题
Reply Quote
感谢张老师!
张老师的做法叫“迂回”

有时候真觉得写Hook引擎是一件没什么意义的事情
钩子有风险 Hook需谨慎,“自动化”或者“干扰引擎”的想法都是比较疯狂的...
IP 地址: 已记录   报告
   2008-11-09, 11:44 上午
nightxie 离线,最后访问时间: 2010/4/3 8:32:07 nightxie

发帖数前25位
注册: 2008-06-09
发 贴: 43
Re: 请大家讨论一个问题
Reply Quote
"写Hook引擎是一件没什么意义的事情
钩子有风险 Hook需谨慎,“自动化”或者“干扰引擎”的想法都是比较疯狂的... "

感觉很有道理。。。

从来没想给patch 100多个字节的情况,那个太恐怖了。
IP 地址: 已记录   报告
   2008-11-09, 19:53 下午
WANGyu 离线,最后访问时间: 2012/9/10 3:34:00 王宇

发帖数前10位
男
注册: 2007-05-08
发 贴: 306
Re: 请大家讨论一个问题
Reply Quote
不恐怖~~ 已经实现了
IP 地址: 已记录   报告
   2008-11-10, 08:50 上午
sudami 离线,最后访问时间: 2009/8/28 11:12:14 sudami

发帖数前75位
注册: 2008-09-24
发 贴: 17
Re: 请大家讨论一个问题
Reply Quote
张老师所说的把原来的执行路线引到我们的新路线,也是需要长跳/短跳. 只是patch的字节没有那么多了.风险可能会小一些.

wy所说的情况确实存在,以我的理解,是不好解决的.所以没见过哪个安全软件patch掉那么多字节的.
唯独江民变态些,在它要hook点的前后留一堆nop.足足够20字节了. 可想而知它的这种做法不稳定因素太大了...
IP 地址: 已记录   报告
高端调试 » 软件调试 » Windows内核调试 » Re: 请大家讨论一个问题

 
Legal Notice Privacy Statement Corporate Governance Corporate Governance
(C)2004-2020 ADVDBG.ORG All Rights Reserved.