首页
社区
课程
招聘
[原创]小议ring 3虚拟机调试器
2007-10-6 18:31 14022

[原创]小议ring 3虚拟机调试器

2007-10-6 18:31
14022
【  标题  】 小议ring 3虚拟机调试器
【  作者  】 linxer
【  声明  】 俺系初级选手,高手略过。失误之处敬请诸位大侠赐教!

其实虚拟机调试器已经并不是什么新鲜玩意了,估计很多高人都玩烂了,但一直没有看到这方面的文章,今天写代码写的郁闷,决定瞎掰篇文章发上来。

长假漫漫,远离家乡,不能回家,真的很羡慕那些能回家的人,也羡慕那些能趁着长假出去玩耍旅游的人……唉,一个人孤苦伶仃的窝在家里,除了睡觉就是写代码,终日与电脑为伴,研究了下ring 3的虚拟机调试器。原本计划现在应该可以支持调试多进程了,但梦想再度被现实强奸,目前只能勉强跑单任务程序。都怪自己有奢求完美主义的癖好,研究过程中想的东西太多。下面简单介绍下这个东西,高手就请飘过了。

虚拟机调试器,是虚拟机和调试器的结合,代码的执行权限在虚拟机和调试器之间来回切换,这样带来的好处就是程序基本上是步步可控的,程序的行为也一目了然,还有一个好处就是它已经摆脱了传统的下断点方法,只要代码是在虚拟机里面执行,一切断点都是逻辑的虚拟的,这样可以避开一些反调试手段(比如占据硬件断点,CRC效验,API的0xcc断点检,改写内存属性取消内存断点等),总之,它带来的好处是很可观的,但是缺点是速度慢,虚拟机的仿真能力也很重要,要考虑的东西比较多,毕竟虚拟CPU不是真实CPU,利用这个弱点就可以写出针对这种调试器的anti-debug,还有就是虚拟机和调试器的结合地方也可能是新的anti-debug的众矢之的。

下面大致说下这个东西的实现过程,由于水平有限,目前我做的也还不是太好,本可以做好了在写这个的,估计等我弄好了,我就没激情写这个了,我的文章一般都是激情 + 无知的时候写出来的,错误在所难免,凑合着看吧。

一.        X86虚拟机
关于这个,目前论坛上有几篇帖子在讲这个,以前是讲虚拟机脱壳,这个x86虚拟机应该比用来脱壳的简单一些,我们只要仿真基本的寄存器,x86指令识别,寻址系统,指令解析系统就可以了,其它的可以不管,不过为了程序很多时间的执行机会都在虚拟机上,虚拟机的执行系统最好仿真的多些,对那些没有仿真的执行,交给真实CPU去执行。这种执行权限的交替是很费时间的,这也势必要求我们指令解析系统比较强大。
另外虚拟机还必须支持异常的捕获,否则的话,要不会程序流程错乱,要不会虚拟机以后拿不到执行权限。

二.        传统调试器
唉,这个没有什么可讲的,我巨菜,在论坛混的兄弟,都是这方面高手,我就不说了,免得出丢人现眼。

三.        程序执行权限的更替
调试器建立调试进程,断在入口点或者TLS入口上,把真实CPU的状态告知虚拟机,唤醒虚拟机线程,虚拟机开始执行代码,这个时候系统中被调试的线程被调试器设置成阻塞。当发生异常或者要调用系统API(或者要进入内核),这个时候在合适的位置设置个断点,把虚拟机的状态同步到真实CPU上,虚拟机线程阻塞,唤醒被调试线程,让其进行异常处理或者进入内核去工作,工作返回后,会触发进入时设置的断点,这个时候,把断点及时取消,阻塞被调试线程,同步真实CPU状态到虚拟机中,唤醒虚拟机线程开始执行代码。就这样来回进行执行权限的更替。
当然以上是单线程情况下的权限更迭,对多线程多任务的调试,原理和上面一样,但是虚拟机线程基本上不会有休息的时候,它的休息情况由任务调度模块决定。

四.        异常处理
虚拟机中要支持ring 3能出现的所有异常捕获,否则一切都空谈。这也需要对win的异常处理机制比较熟悉,当捕获到异常时,按win的异常处理机制,在马上要执行的第一个异常处理回调函数入口下断点,把权限交给真实CPU,让它进行异常处理,这样当执行到虚拟机下的断点的时候,虚拟机又可以获得控制权了,取出这个异常处理回调函数的返回地址,这个要用来判定什么时候异常处理退出的,在退出的地方要把执行权限再次交给真实CPU的,在退出的地方,虚拟机也要获得下条用户空间代码的位置,设置一个断点,保证异常处理彻底结束时,虚拟机又可以拿到执行权限。当然,上面说的是最简单的情况,比如SEH的展开之类的,会比较复杂,但是处理过程是一样的,关键是要对异常处理流程熟悉。
内存属性导致的异常比较特殊,这里特说下,由于我是在虚拟机上引入了缓存,缓存以页为单位,把进程的内存属性要反映到这些缓存上,我们访问的时候,就会导致同样的异常,虚拟机调试器对这种异常进行捕获,然后它也就O了。

五.        API调用
这个地方一言难尽,说简单很简单,说复杂比有点,关键是虚拟机策略问题。要保证用户空间代码绝大多数都在虚拟机里执行,要看这个。如果一发现API调用,就把执行权限交给真实CPU,这个方法最省事,但是有时候会很无奈,API传回调函数进去了怎么办,像MFC这种恶心的东西咋办。还有一种方法就是对API也放入虚拟机里执行,直到其要进入内核,才让把执行权限交给真实CPU,虚拟机本来就慢,在这样搞,会死人的。
API调用时,如果采用第一种方法,涉及到CPU权限的更迭,如果采用第二种方法,某些API也涉及CPU权限的更迭。这种更迭还是依靠在返回处第一条执行上下断点。

六.        虚拟CPU没有仿真指令的执行问题
对虚拟机里目前搞不定的指令,一律交给真实CPU去执行,比如一些MMX指令,使用频率很低,加上指令仿真特别体力活,去仿真它们需要很大的勇气,还是让真实CPU解决它们吧,这里要注意一点就是,跳转指令还是要全部仿真的,否则执行权限的更替会比较复杂,不复杂的话会没有安全感,把那些乱蹦的指令仿真完了,我们就可以安心的在其紧挨的指令上下断点了,来实现虚拟机和真实CPU执行权限的交互。

七.        虚拟机缓存
这个东西搞的我不行了,如果不从效率考虑,它完全是多余的,没用它的话,每次执行指令,包括指令,操作数都要从被调试进程空间去,执行完后,如果更改了数据,在把数据写回去,系统调用是巨耗CPU时间的,为了不让频繁调用ReadProcessMemory/WriteProcessMemory,最好还是拿点空间出来cache吧,这里面有几个算法要实现,比如怎么cache,cache的边界问题,脏数据的避免等等都是要考虑的,唉,一言难尽,不说了。这几天我很多时间都在排除它的错误,调试虚拟机里运行的程序比较要人命。

八.        对多线程多任务的支持(这块我还没实现,纯属胡说八道)
调试器对进程/线程事件进行处理,当收到创建事件的时候,阻塞之,把它放入活动任务队列,等待虚拟机虚拟执行,被虚拟执行的线程在处理异常或者调用API的时候,这个时候可以选择新的线程进行执行,把原来的线程放入阻塞任务队列中,只要是工作良好的进程线程,按这种方式调度执行应该不会出现问题,因为同步互斥都是win的任务,与虚拟机无关。但是光用上面的方式任务切换是不行的。有些程序会用轮循方式来进行同步,这就要求虚拟调试器有自己的任务调度模块,对活动任务队列中的线程进行调度执行。

好了,就说到这里,吃饭去了。

BTW:伟大的致敬给***大哥,虽然只跟他聊过两次天,他说的很多东西我都听不明白。但是他对调试器的理解让我受益匪浅。当然要说明一点,虽然他跟国内某款虚拟机调试器有着一些关系,但是我们并没有说过这个东西,也没有谈论它的技术问题,他给我灌输的是更高级别的调试器思想。谢谢!

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

收藏
点赞7
打赏
分享
最新回复 (18)
雪    币: 32408
活跃值: (18750)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
kanxue 8 2007-10-6 18:44
2
0
好像现在开发虚拟机调试器的人还不少,期待早日见到实用的作品。
雪    币: 1946
活跃值: (238)
能力值: (RANK:330 )
在线值:
发帖
回帖
粉丝
Bughoho 8 2007-10-6 19:56
3
0
太强大了,终于看见你上来卖肉了。
看标题错想成是对虚拟机壳的调试器去了,细细一看,原来是这样。
文章朴实,笔画流畅,引人深思之处不忘吃饭调侃一番,高潮落幕还有秘密隐现。
雪    币: 6073
活跃值: (2236)
能力值: (RANK:1060 )
在线值:
发帖
回帖
粉丝
forgot 26 2007-10-6 20:12
4
0
***=还可色
雪    币: 103
活跃值: (1708)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
TeLeMan 1 2007-10-6 21:23
5
0
虽然从原理上讲,可以做到100%不可识别,但实际上很难做到,因此这类工具公开就没太大意义了。
雪    币: 87
活跃值: (47)
能力值: ( LV12,RANK:250 )
在线值:
发帖
回帖
粉丝
wangdell 6 2007-10-7 00:36
6
0
学习...
雪    币: 217
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
hengking 2007-10-8 11:19
7
0
哎,我也在做这个东西玩,cpu指令仿真已经完成,包括mmx指令,差点累死人的,不过mmx指令在vc上无法单步执行,会导致vc挂掉,比较郁闷

内存管理
多线程多任务的支持
api调用
都是一堆的问题

我现在设想是:ring3上所有代码,包括ms自身的模块,都在虚拟机里面执行,ring 0下面的功能自己模拟实现,这样可以让整个调试器看起来比较爽,不会被执行的代码所干扰
雪    币: 214
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zzsx 2007-10-10 02:30
8
0
http://ida-x86emu.sourceforge.net/

IDA-x86Emu是一个基于IDA的x86虚拟机调试器。
雪    币: 226
活跃值: (15)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
十三少 2 2007-10-10 04:37
9
0
功能似乎还很弱。
雪    币: 87
活跃值: (47)
能力值: ( LV12,RANK:250 )
在线值:
发帖
回帖
粉丝
wangdell 6 2007-10-10 09:05
10
0
不会用,怎么用的,是IDA的plugin吗?不像啊。
雪    币: 1733
活跃值: (726)
能力值: ( LV9,RANK:490 )
在线值:
发帖
回帖
粉丝
yijun8354 12 2007-10-10 12:23
11
0
应该就是IDA的插件~~
雪    币: 87
活跃值: (47)
能力值: ( LV12,RANK:250 )
在线值:
发帖
回帖
粉丝
wangdell 6 2007-10-10 15:32
12
0
IDA插不是plw后缀的可执行文件吗(dll)
解压后的文件好象不是可执行的,也没有后缀.拷贝到plugin目录没反应啊.
高手们指点一下..........
雪    币: 214
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zzsx 2007-10-11 03:13
13
0
这是个开源程序,如果你有IDA SDK的话,可以编译源代码。
雪    币: 87
活跃值: (47)
能力值: ( LV12,RANK:250 )
在线值:
发帖
回帖
粉丝
wangdell 6 2007-10-11 08:56
14
0
非常感谢,晚上去试一下。
雪    币: 87
活跃值: (47)
能力值: ( LV12,RANK:250 )
在线值:
发帖
回帖
粉丝
wangdell 6 2007-10-11 16:49
15
0
晕了,晕了。
刚刚恍然大悟,我说怎么好像怎么看不太懂,才搞清楚,这里说的是用虚拟机技术的调试器。
我本以为是调试虚拟机的调试器。
虽然我急切希望有调试虚拟机的工具,不过虚拟机调试器也是对抗反调试的好东东。
雪    币: 217
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
hengking 2007-10-25 17:25
16
0
楼主有没有搞出来,出来透露一下啊

我发现搞这个特麻烦,要支持虚拟执行ms的系统API,就得模拟进程环境,需要搞定环境变量块,PPB,进程参数块,PEB,TEB等等,这些数据光是定义都把头搞大了,现在虚拟执行只要遇到这些数据的读取就完蛋
雪    币: 1746
活跃值: (292)
能力值: (RANK:450 )
在线值:
发帖
回帖
粉丝
linxer 11 2007-10-25 19:31
17
0
楼上应该是全模拟吧,这个工作会异常浩瀚,简直不是人干的,也需要很强很强的功力,我全部用系统的了
哈哈,我看了下rordbg,没有用这种思路搞了,换了种方法
雪    币: 217
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
hengking 2007-10-26 13:35
18
0
全部用系统的api太容易被绕过了,我现在已经找全资料,准备苦一把,就差最后一步了。
我发现ring 0下的反而好模拟,ntoskrnl的ssdt表才不到300个函数,win32k的也不过几百个函数,相对mmx指令来说,少多了。mmx指令的工作量是全部cpu指令的一半,用了几万行。
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
plwhj 2007-11-7 17:52
19
0
有同感噢!!!
游客
登录 | 注册 方可回帖
返回