概述
JTAG调试器的优点是使用芯片层的硬件支持来建立调试会话,借助专门的通信电缆来访问和控制被调试系统,不用依赖软件层的调试支持,所以从CPU上电开始就可以调试,从CPU执行的第一条指令开始跟踪,CPU走到哪里,跟到哪里,可谓无孔不入,无坚不摧。
但是这样说,其实是基于一个假设,那就是调试者具有超强的汇编语言理解能力,“阅读汇编,如履平地”。原因是如果没有符号支持,那么调试时,基本就只能依赖阅读汇编、读寄存器和读原始内存了。
在今天这样的海量代码时代里,完全在汇编层调试,难度太大了,即使不畏艰难险阻,效率也太低。
所以,还是要有符号,要有软件层的语义。比如要调试操作系统内核,那么最好要有内核的调试符号。
以调试NT内核为例,这里说的“有”,包含两层含义:
一、拥有微软提供的内核符号文件(PDB)。
二、可以在调试器里把PDB的信息与内核的实际位置绑定起来,也就是能成功加载PDB文件。
第一个条件一般问题不大,微软一直保持着公开内核PDB文件的传统,但是第二个条件就有难度了。因为在做JTAG调试时,NT内核并不知道自己在被调试,它的调试支持是关闭的。没有内核的调试支持,就意味着我们连NT内核的内存地址都不知道。不知道内存地址,就无法加载PDB文件,基本问题是无法把内存中的信息与PDB中的信息对应起来。
为了解决这个问题,去年在开发GDK7和NDB(Nano Debugger)时,我们使用了一种“启发式”搜索方法,在内存空间中搜索NT内核。所谓启发式搜索是与蛮力搜索相对而言的。因为要用蛮力方式逐字节的搜索内存空间,那么速度太慢了。
进一步说,NDB会用一个算法来在内存空间中搜索NT内核,找到NT内核后,再找内核的一个关键结构体KdVersionBlock。因为NT内核也很庞大,里面的信息很多,要为它加载PDB,还需要知道它的版本信息和其它一些重要信息,比如进程链表的头结构,以及内核模块列表的头结构等。
经过很多努力,在去年六月,格蠹团队终于解决了这个问题,NDB可以在茫茫的内核空间中找到内核模块,再找到KdVersionBlock,最后成功加载内核的PDB文件,实现符号化调试。
上面截图中的时间是2020年6月6日,星期六,下午五点多,那一定是个忙碌的周末。
包含这个功能的NDB发布后,很多购买了GDK7的格友就使用这种方法来调试NT内核,既拥有JTAG技术所独有的超强控制力,又拥有与WinDBG+KD相媲美的上层语义和高效率。
但在前段时间,有用户在GDC会员群里反映,加载符号失败。
从上面的截图来看,NDB找到了NT内核模块,但是没有找到KdVersionBlock。
KdVersionBlock是变量名,它的类型名为DBGKD_GET_VERSION64。
它里面最重要的信息便是内核基地址、模块列表和调试器数据列表。
那么,为什么找不到KdVersionBlock了呢?
经过格蠹小伙伴的重现和分析,有这个问题的GDK7都曾经升级过Windows 10系统,一般都是联网后,系统自动升级了。
因为与自动升级有关,所以我最初以为微软修改了内核中的特征串,让NDB的搜索算法找不到“狐狸尾巴”了。这样推测的原因是,微软可能担心黑客依靠KdVersionBlock来窃取内核的秘密。
推测归推测,还是要上调试器查找真正的原因和对策。前两天参加了一个技术大会,并且在大会上展出了GDK7和GDK8,这是GDK系列产品首次在技术大会上亮相。
大会结束后,为了尽快解决加载符号的问题,今天一早,我便到办公室加班。中午和几个老朋友一边吃饭一边叙旧后,下午继续调试这个问题。
在尝试了几种方法都不奏效后,我想在目标系统中启用KD调试,用WinDBG + KD来查看内核的变化,于是执行bcdedit命令来启用KD。
但就在我使用bcdedit观察有问题GDK7的启动设置时,有了一个重大的发现,我发现其中的hypervisorlaunchtype 居然是Auto。
这个选项非同寻常,它代表着系统的基本格局。如果为Auto,那么意味着系统中启用了微软的hyper-v,它以VMM角色接管硬件,然后以虚拟机的方式运行Windows 10。这个特征是Windows 10引入的,目的是为了增强系统的安全性,名叫IUM。
虽然有利于安全,但是这个改动的代价也非常大,直白点说,以这种方式运行时,NT内核被架空了,由直接运行在硬件上,改为运行在虚拟机里,这不仅影响性能,而且影响系统里的软件,影响驱动程序和GPU等时间敏感的设备,...
因此,在2016年的微软Ignite大会上,曾经公开批评这个功能。Ignite是微软的大会,在这样的大会上,不好把话说的太直接,所以我引用了论语里的话,“吾党之小子狂简,斐然成章,不知所以裁之。”其中的斐然成章,当然是在说反话。
找到当年的讲义,还可以看到一段感言:
IUM是NT内核历史上最大的架构变化,我不清楚实现这个功能花多少时间,但很清楚消化和调试这一个变化所带来的问题需要更多的时间。格蠹老雷
当初没想到的是,这个功能在几年后影响了NDB,让NDB加载符号失败。
因为有了hyper-v之后,NT内核跑在了虚拟机里,物理内存的布局与没有虚拟机时大不一样,所以NDB搜索不到NT内核的“地标”了。
我是不喜欢这个IUM的,每次见到它,一般我都会把它禁止掉。
找到原因后,我立刻用已经很熟练的命令把IUM禁止掉。
bcdedit /set {current} hypervisorlaunchtype off
然后重启GDK7,再次开始调试,NDB恢复正常了。
调试设施的开发和建设非常困难,但是破坏却很容易。很庆幸NT内核始终坚持着支持调试的价值观,虽然内核不断发展,但是基本的调试设施始终没有做大的改动,否则,就要花太大力气才能解决这样的问题了。
(写文章很辛苦,恳请各位读者点击“在看”,不胜感激)
*************************************************
正心诚意,格物致知,以人文情怀审视软件,以软件技术改变人生
扫描下方二维码或者在微信中搜索“盛格塾”小程序,可以阅读更多文章和有声读物
也欢迎关注格友公众号
最后
以上就是温婉银耳汤为你收集整理的内核地标何处寻?的全部内容,希望文章能够帮你解决内核地标何处寻?所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复