WriteFile
从R3 ,到磁盘
1:kernel32 WriteFile
1)
挺惊讶的,符号好使了,
前面大概4条判断,根据句柄判断要写到什么地方,一共有4个地方可能要去,
stdin stdout stderr 还有最后一个 控制台
2)然后就是一个异步的判断
如果是异步,那么整理信息,然后调用NtWriteFile,根据返回值判断情况成功了,返回1
否则返回0
这里不考虑,主要考虑同步的情况
3)同步的位置
倒是不拖拉,直接就走Nt函数准备进内核了
2:
1)内核里面,一个深情的问候,先获取有用的信息,ETHREAD,和FILE_OBJECT,还有设备对象
2)打了招呼,开始干活
Zw函数的标准玩法,先判断处于用户模式还是内核模式,其实我就很搞不懂,
驱动里面,Zw函数和Nt函数就那么点区别,一个判断内核模式,一个不判断内核模式,
判断了之后,简单地说,就是修改模式之后,Zw调用Nt,然后再走原有流程,
这里直接判断了,应该是优化了吧。
3)做完环境判断之后,
开始准备可能用到的结构了,
4)准备结构之后,还有一个步骤,
1.就是判断是否为同步操作,
如果是同步操作的话,那么后面直接走
2.并行的步骤还有个判断异步的
这里顺带判断了,如果传入字符个数是0个,并且又是非管道或者邮槽操作,
(这里要说一下,其实,管道等的操作,写入地址是NULL,写入字符个数0个,实际上是个常规操作,不算是违规操作)
5)这里先看同步操作吧
1。首先,设置当前状态,给文件系统,告诉它我现在在忙,我有同步的事情正在做
如果以前有人设置了,那么v17 = 0,否则减个引用计数,然后v17 = 1。
2。如果之前有人设置过了,那么这里自己获取一个文件对象锁
这里有个细节,其实挺奇怪的,这里加了锁,但是后面没有地方Release这个锁,所以是不是可以考虑,这个锁根本就不会加,
也就是说不会走到这里,也就是说当前文件不会有其他人在用,但是这不科学,这个理论说不通。
其实后面有地方SetEvnet了。是不是就为了这个锁而存在的。
3。
如果要写入的字符个数为0个,并且v35,没有修正,或者值有异常,
那么就把它修正为当前文件的偏移位置。
4。又是一个分水岭,这里,如果这个成员有值,则继续走内部流程,否则就走到外面去了,外面就是和异步流程相同的其它流程了
其实,这个成员是干嘛的,我就没搞清楚
An opaque member, set only by file systems, that points to handle-specific information and that is used for Cache Manager interaction.
MSDN上,只有这么一段话,说一个不透明的成员,只有文件系统可以操作它,类似于句柄,是和CM交互用的。
我是不是可以猜测成,这个东西的存在,就是说CM中,我要改写的这个文件已经进入缓存了,我直接修改缓存就可以了,所以才出现这个判断。
没有缓存的话,那就自然不是简单操作就能完成任务的。
5。这个判断,真的搞得我好郁闷,
首先判断 v35 的高位 < 0 ,就是说,它必须是 0xFFFF,
然后并且,v35 的值不为 -1 ,就是说,v35的值必须是 0xFFFF0000 ~ 0xFFFFFFFE
这是要判断什么呢,根据前面的经验,实际上这里出现这种值得可能性几乎没有,因为在前面已经通过各种设置,
设置了v35的值,不会变成这个样子,除非是文件位置的有意为之。
v35实际上是文件指针当前偏移,
6。终于到了同步最后一个位置了
这里的操作比较好理解,首先

1 typedef 2 BOOLEAN 3 FAST_IO_WRITE ( 4 __in struct _FILE_OBJECT *FileObject, 5 __in PLARGE_INTEGER FileOffset, 6 __in ULONG Length, 7 __in BOOLEAN Wait, 8 __in ULONG LockKey, 9 __in PVOID Buffer, 10 __out PIO_STATUS_BLOCK IoStatus, 11 __in struct _DEVICE_OBJECT *DeviceObject 12 );
fastiowrite的原型是这样的,也就是说,调用的时候,函数包含了,哪个设备(文件所在设备),哪个文件(FILE_OBJECT 对应的文件),从哪写,写多少,甚至是否加锁是否等待都有了,
返回值是 BOOLEAN,符合要求,返回成功失败,如果函数调用成功了,判断 STATUS,是否执行也成功,执行也成功的话,
走进函数内部了。
第一个函数 IopUpdateWriteOperationCount ,具体做什么的网上没找到,自己看,发现它内部先更新了一个全局的写操作计数器,
然后又更新了一下ETHREAD里面的一个位置,很可能是解锁相关的
第二个函数 IopUpdateWriteTransferCount ,看名字,以及参数的用途,大概是提交修改字符的个数,
后续一系列的返回数据整理,然后设置Event,如果文件在忙,就给文件设置为空闲状态,如果文件在等待,那么就给它一个Event,
最后释放了句柄,就可以返回了。
6)异步操作部分
1。也是先判断偏移
不符合标准,直接就滚蛋了,这里不管
2。这里是我最喜欢的部分,创建IRP,然后整理IRP的初始化的部分
这部分操作结束之后
3。代码开始逐渐明朗了,把两块互斥的指针初始化
如果是 DO_BUFFERED_IO 的话,那么就是用 systembuffer
如果设置成功的话,标记IRP标志为48,也就是0x30,翻译成中文就是
这里使用的是 Buffered IO,用完之后,管理器需要把Buffer 释放
如果设置失败,那么就修改flags值为16,也就是0x10,和上面区别就是,你别删了,其实根本就用不了
4。代码第二块
也就是非 DO_BUFFERED_IO 的情况下,先判断是否为 MDL 方式,如果是的话,给IRP创建一个MDL,然后LOCK住,
如果不是MDL的情况,那么直接挂UserBuffer 为应用层传来的空间,这种方法非常危险,
标识位为0,鸡毛都没有
5。这块,主要是告诉下面处理的地方,这里都是什么操作
最后就是一个分发操作,开始让IRP干活去吧
至此,整个流程基本完成了,
实际上我们可以看出,
大体流程并不是很多,
大致流程就是
应用层的 WriteFile
然后进内核NtWriteFile
里面判断同步异步,
同步走磁盘设备的FastIoWrite
调用失败,或者异步的话,就自己创建IRP来实现,
整体流程就这么短。
剩下的就是FastIoWrite里面,或者文件系统驱动去对照IRP写磁盘了。
还有几处我不懂得地方,就是那个UserBuffer,为什么它在里面竟然写了东西,
IDA实际上没有识别出那个UserBuffer,是我自己根据偏移算出来的,
也不知道我算得是否正确
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」