魔都的春天来了,虽然早晚还有些凉意,但阳光却不一样,走在路上,有时能感受到温暖的春风吹来。
在如此美丽的春天,格甲的朋友们也精力充沛,或代码,或切割BUG,天天进步。
但是在测试新版本Nano Code当时,我遇到了一个奇怪的BUG。经过一番跟踪,小伙伴们确是下面这句话fopen识别:
fp = fopen(full_path, mode);
第一个参数代表的文件路径是:
"\\\\?\\d:\\work\\nano\\nd\\ndi\\x64\\debug\\data/ntp.cfg"
这条路径/ntp.cfg它是动态拼接的,所以它不同于以前的写作方法。考虑到宽平台,使用/而不是\\。
但根据经验,Windows也支持/,比如把full_path去掉前面的参数\\?\,然后就可以成功打开文件了。
"d:\\work\\nano\\nd\\ndi\\x64\\debug\\data/ntp.cfg"
也就是说,这样写是可以的,但是前面加上\\?\就不行了。
那么\\?\它是从哪里来的?看看代码,通过微软GetModuleFileName() API获得。这种写法也有一个华丽的名字,叫做Fully Qualified Path(FQP)。
这是微软的一个API返回了\\?\,但是包含这个\\?\调用另一条路径API但失败了。这让朋友们很难理解。
当fopen失败时,在Watch窗口通过@err观察API其实错码是123。
所以,大家都有点激怒,这是故意气人吗?Windows,1-2-3你在做什么?
这个问题看起来像是Windows的一个BUG,因为非FQP风格的路径支持/但是,FQP但不支持风格。
但如果他遇到一个固执的程序员,他可能会坚持说,这不是BUG,这怎么能是BUG这显然是个feature啊,我很大Windows,用\分隔路径,当然是果断拒绝,严厉打击异类风格/的分隔符号。^_^
所以,这到底是个BUG还是个feature呢?
由于芯片缺货,原本缓慢的软件进度比硬件快。在这种情况下,花点时间来解决这个问题。
由于Windows如果不开源,了解其内部逻辑的最好方法是提升调试器。跟踪用户空间后,发现这条路径实际上传递给了核心,这不是用户空间的问题。
内核空间返回的错误代码是C0000033,不是123,是C0000033。其含义为STATUS_OBJECT_NAME_INVALID。
//
// MessageId: STATUS_OBJECT_NAME_INVALID
//
// MessageText:
//
// Object Name invalid.
//
#define STATUS_OBJECT_NAME_INVALID ((NTSTATUS)0xC0000033L)
追了一番,进了内核,怎么办?还要追吗?再追就有点麻烦了。内核调试。
这时,一个小伙伴说,不怕,用,GDK7啊。
是的,可以用GDK7.先把问题路径放在小程序里,然后在GDK7上复现问题。
仍然可以确认问题GDK7稳定复现后,准备DCI调试。
把GDK7连接到主机,设备管理器如期出现DCI设备。
然后唤出Nano Code,选中DCI方式,以及NT内核选项,然后开始调试会话。
点击启动按钮后,您可以深呼吸,因为启动此按钮DCI会话是一个复杂的操作,首先拉起英特尔OpenDCI进程。
等一会儿,NDB工具条上的蓝色中断按钮被点亮,代表DCI初始化服务可以中断。
点下Break按钮,目标机突然停止,立即停止。
下一步是搜索NT内核,在浩瀚的内存空间中,首先要找到NT模块,然后找到关键的核心地标KdVersionBlock。
Found Module Name ntkrnlmp
Found KdVersionBlock at 0xfffff80680a2a3d8
Found target VersionBlock at 0xfffff80680a2a3d8
这里跳过了详细的过程。感兴趣的格友可以看到当年GDK7发布时的系列讲座视频。
大约2秒后,NDB显示出NT关键结构体的内核版本号和地址:
这意味着在内存空间中成功发现NT内核。
执行lm,可以看到NT几次成功加载内核符号。
lm
start end module name
fffff806`4fe00000 fffff806`508b7000 nt (pdb symbols) C:\NanoCode\sym\ntkrnlmp.pdb\DCA4AD4BEEB4746D48F84C0125019E431\ntkrnlmp.pdb
因为最近一段时间忙着GDK所以有一段时间没有调试NT核心,又看到了,仿佛看到了一个老朋友。
老朋友,对不起,我又给你上了调试器,因为我想知道谁在以你的名义乱搞123。..”
.reload后,再次lm,数百个核心模块一一展示,谁做了123?
接下来需要一个断点。它在哪里?应该设置在理论上NtCreateFile,但也可以设置在其调用的子函数中IopCreateFile。
设置断点后,g,很快命中。
但这不是一个错误,因为它被用来重现错误fopen123还没有运行。
创建文件是一个非常频繁的操作,因此需要消除干扰。怎么做?
可以先在GDK7上用windbg打开fopen123,然后在ntdll!NtCreateFile设置断点,命中后让它等待。
而后在NDB中,找到fopen123进程。
[ndb]!process 0 0 fopen123.exe
!process 0 0 fopen123.exe
PROCESS ffffe28eeafd0080
SessionId: 1 Cid: 0504 Peb: 00c73000 ParentCid: 1d7c
FreezeCount 1
DirBase: 3324f002 ObjectTable: ffffaa8fb17743c0 HandleCount: 59.
Image: fopen123.exe
设置另一个条件断点:
bp /p ffffe28eeafd0080 nt!IopCreateFile
然后再bl观察:
1 e fffff806`80c649a0 0001 (0001) nt!IopCreateFile
Match process data ffffe28e`eafd0080
条件断点埋伏后,g实现恢复目标。
(未完待续)
(写文章很辛苦,恳请各位读者点击“在看”,也欢迎转发)
*************************************************
正心诚意,格物致知,以人文情怀审视软件,以软件技术改变人生
扫描下方二维码或者在微信中搜索“盛格塾”小程序,可以阅读更多文章和有声读物
也欢迎关注格友公众号