首页
社区
课程
招聘
[原创]VT调试BluePill的心得
2014-9-13 21:52 38130

[原创]VT调试BluePill的心得

2014-9-13 21:52
38130
VT虽然已经流行多年,但是因为参考资料较少,所以研究起来有很多困难

要学习VT,最好的参考资料之一就是NewBluePill源代码,该代码是Invisible Things Lab在2007年公开的,实现了一个基本的VMM,并加入了内存隐藏技术。阅读nbp的代码,并结合Intel开发手册作为参考,可以学习到Intel-Vt的方方面面。

因为最近在学校做一个项目,要用VT实现一些功能,所以我开始研究nbp的代码,期间遇到很多问题,困扰了我很久,经过N次BSOD后终于算是勉强把nbp弄明白了在这里跟大家分享一下经验。

一.搭建调试环境

我搜索了论坛里有关VT调试的帖子,综合了网上的各种说法,普遍认为调试VT有3种方法:
1.Windbg+bochs虚拟机
好处是可以用我们熟悉的windbg
缺点是需要手工修改一些代码并重新编译bochs,很难自己装系统(因为太慢了),最后系统跑起来也感觉特别脆,容易出错,我试了很久好像也没弄好

2.IDA+GDB+bochs
不清楚,因为我一点也不了解GDB,而且我的IDA版本好像有问题,跟bochs配合也是个蛋疼的事情
(看了评论后发现IDA+GDB+VMWare似乎是最好的解决方案,可以独立于操作系统进行调试,不依赖系统资源)

3.两个物理主机通过COM连线,进行双机调试
我有两台机器,一个台式机(有COM口),一个笔记本(没COM,使用USB转COM插头),然后买了个母对母的COM连接线(因为我两个机器端都是公头),连好后却怎么也通讯不了,也不知道问题在哪,感觉特别无奈。。后来某次跟一个做嵌入式的同学聊了聊,他说可能是因为COM线的RXD引脚和TXD引脚需要交叉接,然后就帮我焊了个小板子,回去一试还真行了


这个方法好处是可以用windbg,而且不会出现虚拟机的奇怪问题,绝对原生。缺点是需要两台物理主机,而且还得买线,对环境要求较高。调试速度也一般,因为串口通讯速率毕竟有限。

以上几个方法都不尽人意,但最后我经FC牛的提醒发现一种最完美的方法

4.VMWare10+Windbg

之前我们宁愿用bochs也不用VMWare,是因为VMWare的早期版本并不支持VT的模拟,但是却没有多少人发现VMWare10已经支持VT了。只需要选上这个选项(btw,VM10是原生中文,用着非常爽~)


后面的步骤大家就都懂了,配置一下VMWare跟Windbg的双机调试,就可以用一台电脑拿着windbg调VT了~缺点简直没有,因为这已经是我能想象的最好情况了。

顺便说一下,nbp0.32公开版本的推荐运行环境是Windows 2003 x64,所以我一开始也是先从2k3着手研究的

本文提出的方法不一定正确,但目前可以在一定程度上解决VT调试的一些问题。至于Windbg、内核调试引擎、VMM这三者之间相互作用的细节仍有待实验探究

二、解决断点问题

本以为上文结束后就可以随便调了,不过实际上又有一个奇怪的问题:一旦开启VMLAUNCH了以后,无法在VMM的代码上下断点,比如bp newbp!VmxVmexitHandler,如果断点被触发的话,虚拟机就失去了响应,Windbg也定在那里不动了。这样的话我们根本没法调关键代码,print调试大法必然是不能接受的。

这个问题也让我百思不得其解,直到现在我也不知道为什么会出现这种现象。但是我发现这跟nbp的内存隐藏有关(后记:有待进一步验证),我把nbp自己的内存管理系统取缔以后,就可以任意下断点了。哪位大牛能解释下这个BUG的细节,还望不吝赐教

nbp的内存管理主要实现在paging.c里,观察nbp分配内存使用的MmAllocatePages函数,可以看出它首先用ExAllocatePoolWithTag申请内存,然后会对页做一些处理,来实现自己的内存管理(这部分我还没细看)。我们首先要把后续操作注释掉(MmAllocateContiguousPages和MmAllocateContiguousPagesSpecifyCache同理)
PVOID NTAPI MmAllocatePages (
  ULONG uNumberOfPages,
  PPHYSICAL_ADDRESS pFirstPagePA
)
{
  PVOID PageVA, FirstPage;
  PHYSICAL_ADDRESS PagePA;
  NTSTATUS Status;
  ULONG i;

  if (!uNumberOfPages)
    return NULL;

  FirstPage = PageVA = ExAllocatePoolWithTag (NonPagedPool, uNumberOfPages * PAGE_SIZE, ITL_TAG);
  if (!PageVA)
    return NULL;
  RtlZeroMemory (PageVA, uNumberOfPages * PAGE_SIZE);

  if (pFirstPagePA)
    *pFirstPagePA = MmGetPhysicalAddress (PageVA);

  /*

  for (i = 0; i < uNumberOfPages; i++) {

    // map to the same addresses in the host pagetables as they are in guest's
    PagePA = MmGetPhysicalAddress (PageVA);
    Status = MmSavePage (PagePA, PageVA, PageVA, !i ? PAT_POOL : PAT_DONT_FREE, uNumberOfPages, 0);
    if (!NT_SUCCESS (Status)) {
      DbgPrint ("MmAllocatePages(): MmSavePage() failed with status 0x%08X\n", Status);
      return NULL;
    }

    Status = MmCreateMapping (PagePA, PageVA, FALSE);
    if (!NT_SUCCESS (Status)) {
      DbgPrint
        ("MmAllocatePages(): MmCreateMapping() failed to map PA 0x%p with status 0x%08X\n", PagePA.QuadPart, Status);
      return NULL;
    }

    PageVA = (PUCHAR) PageVA + PAGE_SIZE;
  }

  */

  return FirstPage;
}


还要注释掉HvmSetupGdt函数里的一句话
//MmMapGuestTSS64 ((PTSS64) GuestTssBase, GuestTssLimit);


更改VmxSetupVMCS中设置VMCS.CR3的部分,让GuestOS使用当前系统的页目录指针,而非nbp自己的页目录
  //VmxWrite (HOST_CR3, g_PageMapBasePhysicalAddress.QuadPart);
  VmxWrite (HOST_CR3, RegGetCr3 ());


最后要把DriverEntry中MmInitManager之类的调用删掉,卸载时也不需要MmShutdownManager。

至此便移除了nbp自己实现的内存管理(似乎这技术才是亮点..以后再好好看看)。



三、解决卸载问题

nbp在卸载时会死机,因为卸载必须在VMM下进行,而DriverUnload是在non-root模式里,所以要通过Hypercall的方法把任务交给VMM,Hypercall的实现在hypercall.c里,DriverUnload最终会调用到HcMakeHypercall,但是nbp却没有对VMCALL指令作处理,只是把它当做一般的VM指令,直接返回执行失败,所以卸载的Hypercall没有被处理,系统仍在虚拟机中运行,但是随后DriverUnload会释放资源,进而导致出错死机。要让它正常卸载,我们只要处理好Hypercall即可。

首先添加对VMCALL的处理函数,修改VmxRegisterTraps

  //
  // 为所有VM指令造成的VMExit设置一个无用的处理函数,VMCALL则作为Hypercall处理
  //
  for (i = 0; i < sizeof (TableOfVmxExits) / sizeof (ULONG32); i++) {
    if (TableOfVmxExits[i] == EXIT_REASON_VMCALL) {
      if (!NT_SUCCESS (Status = TrInitializeGeneralTrap (Cpu, EXIT_REASON_VMCALL, 0,      // length of the instruction, 0 means length need to be get from vmcs later.
                                                         VmxDispatchHypercall, &Trap))) {
        _KdPrint (("VmxRegisterTraps(): Failed to register VmxDispatchHypercall with status 0x%08hX\n", Status));
        return Status;
      }
    } else {
      if (!NT_SUCCESS (Status = TrInitializeGeneralTrap (Cpu, TableOfVmxExits[i], 0,      // length of the instruction, 0 means length need to be get from vmcs later.
                                                       VmxDispatchVmxInstrDummy, &Trap))) {
        _KdPrint (("VmxRegisterTraps(): Failed to register VmxDispatchVmon with status 0x%08hX\n", Status));
        return Status;
      }
    }
    TrRegisterTrap (Cpu, Trap);
  }


然后实现Hypercall的处理函数,只需要简单地交给HcDispatchHypercall即可(其实nbp把工作都做好了,只不过把桥梁移除了,感觉nbp代码里很多问题都是作者故意的)。

static BOOLEAN NTAPI VmxDispatchHypercall (
  PCPU Cpu,
  PGUEST_REGS GuestRegs,
  PNBP_TRAP Trap,
  BOOLEAN WillBeAlsoHandledByGuestHv
)
{
  ULONG64 inst_len;
  ULONG32 exit_qualification;

  if (!Cpu || !GuestRegs)
    return TRUE;

  _KdPrint (("VmxDispatchVminstructionDummy(): Nested virtualization not supported in this build!\n"));

  inst_len = VmxRead (VM_EXIT_INSTRUCTION_LEN);
  Trap->General.RipDelta = inst_len;

  HcDispatchHypercall(Cpu, GuestRegs);

  return TRUE;
}


然后就可以正常卸载nbp了。

还有几个地方比较麻烦,要改的地方比较多。一个是Svm,这个是nbp在AMD处理器下的实现,相当于AMD版的Vt,这个用的比较少,而且内容跟Vt相似,如果只是研究Vt的话可以移除Svm的代码。另外就是x86的移植问题,我感觉nbp本身肯定是在x86下测试过的,但是发布版里故意加了一些BUG,都是一些小毛病,对照着编译错误细心改一下就行,不用很多添加新代码。

最后给出我修改的nbp代码(改的不是很细致,似乎某些多核情况下会BSOD,有时间我再改进)

ps:打个广告,最近在做图像匹配和机器学习方面的研究,不知有没有这方面的大牛能指教一下  qq474516292

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

上传的附件:
收藏
点赞1
打赏
分享
最新回复 (46)
雪    币: 2660
活跃值: (3401)
能力值: ( LV13,RANK:1760 )
在线值:
发帖
回帖
粉丝
安于此生 34 2014-9-13 22:07
2
0
第一张图亮了...
雪    币: 357
活跃值: (2593)
能力值: ( LV3,RANK:25 )
在线值:
发帖
回帖
粉丝
KooJiSung 2014-9-13 22:15
3
0
买2个usb2com,串口线看着太难受
雪    币: 231
活跃值: (2631)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
sxpp 1 2014-9-13 22:28
4
0
我早说vm支持vt,没人相信,只是自己技术有限罢啦
雪    币: 119
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
libocdf 2014-9-13 22:55
5
0
前排顶楼!!
雪    币: 6991
活跃值: (1222)
能力值: ( LV11,RANK:180 )
在线值:
发帖
回帖
粉丝
JoenChen 4 2014-9-14 00:06
6
0
你在开玩笑吧, windbg可以调试VT的root模式指令? Vmware虽然可以模拟VT指令, 但是windbg收到到调试消息啊, windbg可是要依赖int 1, int 3的.
雪    币: 8861
活跃值: (2369)
能力值: ( LV12,RANK:760 )
在线值:
发帖
回帖
粉丝
cvcvxk 10 2014-9-14 00:08
7
0
bluepills的代码就是拿来参考一下~~
雪    币: 204
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
hunxiaozi 2014-9-14 00:26
8
0
第一张图不错
雪    币: 228
活跃值: (115)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
exediy 1 2014-9-14 07:36
9
0
首先硬件虚化如果有接管中断的话是无法使用WINDBG的!
然后顺便说下,其实VMWare针对系统调试这块下了很大的努力!VMWare可以开启GDB调试模式,因为不依赖系统,所以套上IDA后可以调试系统的全部...包括硬件虚化,MSR,Dbgkernel等等全部的一切!
雪    币: 260
活跃值: (42)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
droiyan 2014-9-14 08:35
10
0
AMD的CPU很受伤啊,ramooflax不挑食:
https://github.com/sduverger/ramooflax
雪    币: 2134
活跃值: (14)
能力值: (RANK:170 )
在线值:
发帖
回帖
粉丝
Aker 4 2014-9-14 08:54
11
0
10年左右也折腾这玩意一段时间,没有总结,感谢楼主
雪    币: 227
活跃值: (66)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
Naylon 2 2014-9-14 09:04
12
0
我还没有深入了解内核调试引擎和windbg原理,求指点
vmm的代码确实是可以调的,比较奇怪的是我用XP x86测试bp VmxVmexitHandler就会死机..但是2k3 x64就能断下来正常调,不知道什么原因,看来问题还真是没完全解决
雪    币: 227
活跃值: (66)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
Naylon 2 2014-9-14 09:08
13
0
我又测试了一下,不知为何win2k3 x64可以断在VmExitHandler并任意调试,winxp x86就直接死机,代码是一样的
雪    币: 228
活跃值: (115)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
exediy 1 2014-9-14 09:19
14
0
你把NBP的代码加进处理中断的试下,结果就是不管你上什么系统,包死!!
退出虚拟机WINDBG就失踪了..这酸爽简直无法相信!

其实,简单来说这帖子是水贴.....开始是错的,结束也是错的.更别说心得.
用WINDBG调试硬件虚化是一个伪命题!!!调试器依赖系统资源,硬件虚化高于系统资源.你现在能调试的只是一个简单的陷入虚化,退出虚化的过程,实际过程中硬件虚化会因为接管了中断,等等一系列系统资源导致无法调试!如果不接管这些系统资源,只是陷入退出虚化,那要调试器何用?要硬件虚化何用?


不过这个优秀是怎么回事? 难道版主没发现这个调试的方法方式是错误的?这优秀给的很失专业!
还是看看前人们的文章吧.
http://bbs.pediy.com/showthread.php?t=188703
http://bbs.pediy.com/showthread.php?t=135229
这才是真正的解决方案
雪    币: 227
活跃值: (66)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
Naylon 2 2014-9-14 09:44
15
0
我使用的方法能在VmxDispatchXXX下断,能让我跟进VmxVmexitHandler来修复VMCALL未处理的BUG,完成NBP的卸载。对我来说这足以解决我遇到的问题,也许也能解决别人的一些问题,所以叫分享心得。

而且我没说我是对的,你也没证明你是对的。发帖就是想跟大家讨论
雪    币: 228
活跃值: (115)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
exediy 1 2014-9-14 09:58
16
0
你这样怎么可能修复什么东西呢?连调试方法都是错误的.
至于我说的对不对你完全自己想下硬件虚化运行的级别是在哪!而系统调试器又在哪!

最后说明一点,就是这个NBP之所以双机调试是因为之前没有行之有效的调试的工具(完整版本的NBP接管了系统资源,无法用WINDBG调试),唯一的办法就是WINBDG输出调试信息来确定代码的正确性!!!
而且既然是心得就必须是正确的,一个临时替补方案只会误导众人!

最后顺便说下,版主给这贴优秀给的很失专业!!!
雪    币: 227
活跃值: (66)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
Naylon 2 2014-9-14 10:36
17
0
修复了什么我已经讲得很清楚啦,认真看过再做评价吧

临时替补方案我觉得没什么不合理呀。相对论出现之前,牛顿误导了世人几百年,可即便是在现代,你能放弃经典力学这个错误的理论吗?能解决所需就不失为好方法,当问题上升到另一个高度后,可以再寻求更好的方法。

说实话我真是有点没想明白,我guestOS和VMM使用相同的IDT,仅拿int3做讨论的话,在VMM里下断,触发后就在VMM里跟windbg交互,此时windbg直接调试的就是VMM。如果断点断在non-root里,那windbg的交互对象就是GuestOS(此时调试过程中可能会VMExit到VMM里,情况比较复杂,但是仅仅调试VMM应该没有问题呢)。毕竟windbg只是个远程通讯工具,被调试机处于哪个模式,就在那个模式下跟windbg沟通,windbg可能会出现上下不一致的情况,但是正常调应该没问题。

至少从目前的实验现象来看,VMM我断在哪里都可以。你说让NBP接管中断会崩溃,我没有试,请你给出实验证明。只说理论不过是想当然罢了。

btw:这么在意优秀是因为你一个都没有嘛
雪    币: 260
活跃值: (64)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
beijingren 2014-9-14 11:16
18
0
讨论的很热烈啊,围观楼主且站队支持楼主!
雪    币: 2
活跃值: (17)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
nxtxfxsx 2014-9-14 12:13
19
0
支持LZ,不知道喷LZ的是什么心态
雪    币: 2
活跃值: (17)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
nxtxfxsx 2014-9-14 12:14
20
0
你觉得这个“优秀”给得不对,那你就发个厉害的啊。不然就闭嘴。凭什么说LZ的帖不好。
雪    币: 1517
活跃值: (3290)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
小希希 2014-9-14 12:25
21
0
com口调试?慢死了,买根1394线,买2个1394 卡,这调的才爽

bluepills 只看intel 和 amd 的 vt 初始化部分,其他的看不懂...
雪    币: 227
活跃值: (66)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
Naylon 2 2014-9-14 13:14
22
0
想过1394,台式机还好,笔记本好像不太能行啊,太少见了这玩意
雪    币: 2134
活跃值: (14)
能力值: (RANK:170 )
在线值:
发帖
回帖
粉丝
Aker 4 2014-9-14 16:24
23
0
感谢exediy指出的问题,给出优秀主要着力点在卸载处理上,相信不少人学习bluepill也碰到过类似问题,也相信不少人因为工作忙或者其他原因没有太深究问题,导致分析常常会卡住要重来浪费时间,如果论题能让大家对这个问题有进一步认识,我想也值得鼓励。
论坛作为共同学习提高的地方,有更好的方法也可以更大家一起分享进步。
雪    币: 1487
活跃值: (955)
能力值: (RANK:860 )
在线值:
发帖
回帖
粉丝
仙果 19 2014-9-14 18:33
24
0
楼主的图片大亮!
雪    币: 228
活跃值: (115)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
exediy 1 2014-9-14 18:35
25
0
又见喷子!
游客
登录 | 注册 方可回帖
返回