首页
社区
课程
招聘
[旧帖] [原创] 关于Windows环境下完善Ring3调试器硬件执行断点的一些思路[邀请码已发] 0.00雪花
发表于: 2011-5-10 22:45 2092

[旧帖] [原创] 关于Windows环境下完善Ring3调试器硬件执行断点的一些思路[邀请码已发] 0.00雪花

2011-5-10 22:45
2092
       关于Windows环境下完善Ring3调试器硬件执行断点的一些思路   
    众所周知,硬件执行断点在应对代码自校验一类的反调试技术上有奇效,然而,硬件执行断点也有许多不足的地方。一是其个数太少,最多只能设置4个;二是其比软件断点更容易被检测到。本文就以上两个问题进行一些简单的讨论。同时再说明一下,本文针对的是Ring3级别程序的调试,调试器以OD为例。OD插件服务驱动为OD_Driver。
    在Windows环境下,每个线程都有自己的线程上下文结构,其中当然也有一组调试寄存器。当操作系统调度一个线程时,该线程的上下文结构中各寄存器的值才会真正被装入CPU执行。在执行过程中如果遇到硬件调试异常或者其他异常,CPU发生中断,此后控制就转移给操作系统(SEH等都是以后的事情了);如果执行过程顺利,那么等该线程此次被调度所用时间片耗尽后,CPU会发生中断,操作系统处理该中断并执行线程调度算法,更新当前中断线程的上下文结构,并调度下一个线程。
    上边我们重点指出了2种中断:①调试中断 ②时间片耗尽中断(因为笔者还没有深入研究,当下姑且用一个非专业名称称呼它了)。为了实现扩充OD硬件执行断点的功能,下面我谈谈利用中断②的思路。
    传统下硬件执行断点的方式可以被称作守株待兔型的,这些有限数量的断点很被动,没有实时性,自然就降低了其被触发的概率。我们能不能让这些断点变得积极主动一点呢?上面提到过硬件断点是线程相关的,而线程总是被断断续续调度的,这就为我们在程序运行过程中灵活改变这些调试寄存器的值提供了可能性。而实际情况也是这样的。NtSetContextThread的功能就是写线程的上下文结构的。等线程被调度时,CPU按指定的上下文结构执行。然而这里我不想用这个方法,因为其过于依赖操作系统,DIY能力太差。我想提一种利用中断②的方法。即在OD_Driver中拦截②中断,使我们的驱动在执行系统线程调度算法之前接到控制。之后,先执行一个CLI指令(屏蔽中断,保证我们的例程不受打扰)。再判断当前中断的线程是否为OD中被调试进程的线程。若不是,则STI(开中断),控制传给操作系统。若是,则执行我们自己的功能(即扩充硬件执行断点数量的功能)。在我们的OD插件中可能指定多于4个的硬件执行断点,但我们在任一时刻能被使用的最多为其中4个。也就是说有候选断点存在。具体选其中哪4个,取决于我们的断点调度算法。我的一个方法是:当我们处理该中断时,先拿到EIP的值,我们认为距EIP越近的地方在线程下被调度的时候越可能被执行。按照这个思想,我们就从断点列表中选最近的4个。然而这是有局限性的,比如call和长跳转一类的指令极有可能导致我们的劳动白费。因此,我们还必须分析下代码,多照顾下长跳转和call可能把控制转移到什么地方。结合以上两点,再加上对线程被调度一次可能执行指令数量的分析,我们可以选出最合适、概率最大的4个断点。(如果有其他更好的算法,希望能与我一起讨论交流)。当选好断点以后,我们把对应寄存器值写入当前的中断环境中,然后将控制传给操作系统,这样,包括操作系统在内的所有程序就被欺骗了,除了我们可爱的OD。
    对于这种扩充硬件执行断点数量方法的评估,我认为其严重削弱了系统的性能。然而综合分析一下,我认为这种牺牲还是值得的,尤其是在处理有进攻性程序的时候。我们关心的问题是能不能触发断点,能不能躲过代码自校验。系统性能这时并不是我们首要考虑的因素。
    硬件断点容易暴露自己,尤其是在调试进攻性程序时更是这样。如何让硬件断点既隐身又有效呢?下面我将谈谈利用中断①的思路。
    Ring3程序获得自己线程上下文 的途径普遍的方法有2种,一是直接读取线程内核对象的线程上下文结构,二是主动引发异常,通过Windows的SHE机制获得。对于第一种方法,我们用OD_Driver HOOK内核函数,对调试寄存器清零返回即可(对于OD的调用特殊处理)。对于第二种情况,我们从最底层做起——拦截中断①。如果中断线程为目标线程,我们将中断环境的调试寄存器清零,然后控制传给操作系统。之后应用程序,不论是GetThreadCentext还是SEH,都不会检测到硬件断点被设置的情况了,但OD仍可以接到通知。然而问题还没有结束。之后,若我们不做处理,线程下一次被继续调度执行时,调试寄存器的值仍为0,不是我们希望的值。换句话说,在线程即将被调度的时候(或被恢复的时候)我们必须将OD的数据写入调试寄存器。“即将被调用”的时机具体怎么找呢?一是NtContinue.这是一个依赖操作系统的方法,HOOK该函数可以达到我们的目的。二是解析内核调度算法,找到线程即将被调度的时机,通过HOOK或其他技术。在这个时机改变线程上下文结构。当然,这也是难度最大的一个方法,同时其还是具有平台相关性。然而,我认为这些缺点仍然是次要的。我们不需要该驱动有很好的平台兼容性,只要其能在自己的机器上搞定被调试程序即可(这里编程即使是硬编码也无所谓)。需要声明一点的是:笔者暂时还没有研究过线程调度的相关代码,也不会利用它们做任何事情,望有兴趣的朋友们能与我讨论。
    以上是我关于硬件断点的两大问题的一些思考。由于相关系统知识的学习还处于初级阶段,对一些概念和细节难免有不准确的地方,望朋友们不吝赐教。也希望有人能对本文提到的方案给出一个客观的评估,确定下可操作性如何,实际应用是否会达到预期等。我将不胜感激。欢迎有兴趣的朋友们和我一起交流。

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 0
支持
分享
最新回复 (8)
雪    币: 130
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
非常具有探索精神,本人菜鸟。
2011-5-11 00:35
0
雪    币: 21
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
友情赞助一下。
2011-5-11 00:43
0
雪    币: 1689
活跃值: (379)
能力值: ( LV15,RANK:440 )
在线值:
发帖
回帖
粉丝
4
终于有邀请码了。庆祝一下!
2011-5-12 07:30
0
雪    币: 3
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
本人菜鸟,学习之。。。
2011-5-12 09:34
0
雪    币: 75
活跃值: (623)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
6
想法非常好,实现很难....

你有办法预知线程在一个时间片里(假设时间片100ms,主频3.0G单核,约能执行12000000条指令)执行代码跨度有多大吗?也就是说 你有办法知道在下一个时间片里 线程会执行那些代码吗?,如果这个确定不下来,就没法取舍哪个硬件断点了.
间接跳转没有一些统计信息(比如剖析)很难正确预测的,剖析需要插桩..代码自校验又惨了.

还有一个非常要命的,一旦你预测失败,本应该断下来的却没有断下来,怎么办?多运行几次??会让人发疯的
2011-5-12 10:33
0
雪    币: 0
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
正好看到windows 驱动部分,这里参考一下
2011-5-12 10:59
0
雪    币: 1689
活跃值: (379)
能力值: ( LV15,RANK:440 )
在线值:
发帖
回帖
粉丝
8
谢谢楼上的分析。

只是我还有个疑问是,操作系统可以为线程分配时间片,理论上讲调度运行线程的时间片长度是可控的。而楼上的假设100ms又有些不现实。这个值确实有点太大了。要是时间片长度到了毫秒数量级,系统非卡死不可……

当然,这些仍是在没有研究内核代码情况下的妄加分析。难免有错误的地方。希望有兴趣的继续研究一下这个话题。更深层次的结论,只有去研究核心代码了。55
2011-5-12 17:15
0
雪    币: 135
活跃值: (106)
能力值: ( LV2,RANK:140 )
在线值:
发帖
回帖
粉丝
9
这个实际价值不大,貌似有一个更好的,内存隐藏技术,就可以随便下int3了
2012-4-6 09:52
0
游客
登录 | 注册 方可回帖
返回
//