Windows, 你在搞什么1-2-3?

博客讲述了在开发过程中遇到的一个诡异问题,即使用微软API获取的\?全限定路径在调用fopen时失败,返回错误码123。开发者通过调试器深入到内核层面,发现是STATUS_OBJECT_NAME_INVALID错误。文章详细描述了使用GDK7进行调试的过程,包括设置断点、寻找内核模块等步骤,最后留下了一个在内核层面设置断点以进一步追踪问题的悬念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

魔都的春天来了,虽然早晚还有些凉意,但是阳光已经不一样,走在路上,有时能感到温暖的春风吹来。

在如此美好的春天里,格蠹的小伙伴们也都精神抖擞,或码代码,或斩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。

34085b34c1d69c7c5aa2b4c78dd686b7.png

于是,大家有点被“激怒”了,这是故意气人的吗?Windows,你在搞什么1-2-3?

这个问题看起来像是Windows的一个BUG,因为非FQP风格的路径支持/但是,FQP风格的却不支持。

但是如果遇到执拗的程序员,他或许坚持说,这不是BUG,这怎么能是BUG呢,这显然是个feature啊,我堂堂大Windows,用\来分隔路径,对于异类风格的/的分隔符号,当然是果断拒绝,给予严厉打击啦。^_^

那么,这到底是个BUG还是个feature呢?

因为芯片缺货,所以本来缓慢的软件进度现在倒比硬件的快了。既然如此,那就拿出点时间来格一下这个问题吧。

由于Windows不开源,要想理解它的内部逻辑,最好的方法就是上调试器,在用户空间跟踪一番后,发现这个路径居然传给了内核,并不是用户空间的问题。

b630af6bee918e060d0c2ba1df18094e.png

内核空间返回的错误码是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上复现问题。

0b1c232ce6570a4f61662544beb21e9d.png

确认问题依然可以在GDK7稳定复现后,准备上DCI调试。

b2e13a51b5221e5b27bc55faf5d55da7.png

把GDK7连到主机,设备管理器中如约出现DCI设备。

956130c85f17b182f3b242448cf09422.png

然后唤出Nano Code,选中DCI方式,以及NT内核选项,然后开始调试会话。

b7e6d2d2a1575eeab29aebfbabb08623.png

点击启动按钮后,可以深呼吸一下,因为启动这个DCI会话是个比较复杂的操作,要先拉起英特尔的OpenDCI进程。

等待片刻后,NDB的工具条上蓝色的中断按钮被点亮,这代表DCI服务初始化就绪,可以发起中断了。

541c28095cfc8ada37e25c39c624ab65.png

点下Break按钮,目标机戛然而止,立刻不动了。

1ecef1a821a392dbec76bd5d57fdda9b.png

下一步是要搜索NT内核,要在茫茫的内存空间中,先找到NT模块,再找到关键内核地标KdVersionBlock。

Found Module Name ntkrnlmp

Found KdVersionBlock at 0xfffff80680a2a3d8

Found target VersionBlock at 0xfffff80680a2a3d8

详细过程在此跳过,感兴趣的格友可以看当年GDK7发布时的系列讲座录像。

大约2秒钟后,NDB显示出NT内核的版本号,以及关键结构体的地址:

4029fd6dadf73e9aee36a47b0e1f9e74.png

这代表着在内存空间中,成功找到NT内核。

执行lm,可以看到NT内核的符号也几经成功加载。

lm

start             end                 module name

fffff806`4fe00000 fffff806`508b7000   nt         (pdb symbols)          C:\NanoCode\sym\ntkrnlmp.pdb\DCA4AD4BEEB4746D48F84C0125019E431\ntkrnlmp.pdb

因为最近一段时间忙着GDK8,所以有段时间没有调试NT内核了,又见到它,仿佛看见一位老朋友。

“老朋友啊,抱歉又对你上调试器了,因为想知道是谁在以你的名义乱搞什么123...”

64e8085bd442fa4025e2a2f0949220f0.png

.reload后,再次lm,数百个内核模块一一陈列,是谁搞的123呢?

接下来需要一个断点。设在哪里呢?按理说应该设置在NtCreateFile,但是也可以设在它调用的子函数IopCreateFile。

设好断点后,g,很快命中。

593d4d8fc16530fcfd7f1301c6cd1862.png

但这次并不是出错的情况,因为用于重现错误的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恢复目标执行。

(未完待续)

(写文章很辛苦,恳请各位读者点击“在看”,也欢迎转发)

*************************************************

正心诚意,格物致知,以人文情怀审视软件,以软件技术改变人生

扫描下方二维码或者在微信中搜索“盛格塾”小程序,可以阅读更多文章和有声读物

ba5ad6acc101d4d3f68ea307ad1b5675.png

也欢迎关注格友公众号

29d2684e61f9fe95c50c61ee1d44b96e.png

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值