Windows平台内核级文件访问
1.背景
在windows平台下,应用程序通常使用API函数来进行文件访问,创建,打开,读写文件。从kernel32的CreateFile/ReadFile/WriteFile函数,到本地系统服务,再到FileSystem及其FilterDriver,经历了很多层次。在每个层次上,都存在着安全防护软件,病毒或者后门作监视或者过滤的机会。作为安全产品开发者,我们需要比别人走得更远,因此我们需要一个底层的“windows平台内核级文件访问”的方法来确保我们能够看到正确的干净的文件系统。
2.用途
直接的内核级别文件访问,在信息安全领域内有广泛的用途。用于入侵者的方面,可以让他绕过杀毒软件,IDS等安全保护系统的监视。用于检测者的方面,可以看到一个干净的系统,以此来查杀隐藏的后门或者rootkit。用于监控者的方面,则可以了解最新的绕过监控的技术,可以根据来设计更新的监控方案。
3.直接访问FSD的内核级别文件访问
FSD(FileSystemDriver)层是文件API函数经过本地系统服务层(native API)最后到达的驱动层次。如果我们可以模仿操作系统,在我们自己的驱动程序里直接向FSD发送IRP,就可以绕过那些native API 和win32 API了,也就可以绕过设置在这些层次上面的API钩子等监控措施。
3.1文件的Create和Open
文件的Create和Open可以通过发送IRP_MJ_CREATE给FSD,或者调用IoCreateFile函数来完成。Create和Open的区别实际上在于IoCreateFile/IRP_MJ_CREATE的一个参数Disposition的取值。使用IoCreateFile函数的样例代码:
HANDLE openfile(WCHAR* name,ACCESS_MASK access,ULONG share)
{
//return 0 for error.
HANDLE hfile;
IO_STATUS_BLOCK iosb;
int stat;
OBJECT_ATTRIBUTES oba;
UNICODE_STRING nameus;
///
if(KeGetCurrentIrql()>PASSIVE_LEVEL){return 0;}
RtlInitUnicodeString(&nameus,name);
InitializeObjectAttributes(&oba,&nameus,OBJ_KERNEL_HANDLE|OBJ_CASE_INSENSITIVE,0,0);
stat=IoCreateFile(&hfile,access,&oba,&iosb,0,FILE_ATTRIBUTE_NORMAL,share,FILE_OPEN,0,0,0,0,0,0);
if(!NT_SUCCESS(stat)){return 0;}
return hfile;
}
HANDLE createnewfile(WCHAR* name,ACCESS_MASK access,ULONG share)
{
//return 0 for error.
HANDLE hfile;
IO_STATUS_BLOCK iosb;
int stat;
OBJECT_ATTRIBUTES oba;
UNICODE_STRING nameus;
///
if(KeGetCurrentIrql()>PASSIVE_LEVEL){return 0;}
RtlInitUnicodeString(&nameus,name);
InitializeObjectAttributes(&oba,&nameus,OBJ_KERNEL_HANDLE|OBJ_CASE_INSENSITIVE,0,0);
stat=IoCreateFile(&hfile,access,&oba,&iosb,0,//AllocationSize this set to 0 that when file opened it was zeroed.
FILE_ATTRIBUTE_NORMAL,share,FILE_OVERWRITE_IF,0,0,0,0,0,0);
if(!NT_SUCCESS(stat)){return 0;}
return hfile;
}
通过发送IRP_MJ_CREATE给FSD的方法与此类似,可以参考IFSDDK document的IRP_MJ_CREATE说明。不同于上面方法的是需要自己创建一个FILE_OBJECT,好于上面方法的是这种方法不需要一个HANDLE,HANDLE是线程依赖的,FileObject则是线程无关。
3.2文件的Read和Write
我们通过给FSD发送IRP_MJ_READ来读取文件,给FSD发送IRP_MJ_WRITE来改写文件。
如果我们是通过一个HANDLE来执行(如使用IoCreateFile打开的文件),就要先用ObReferenceObjectByHandle函数来获得这个Handle对应的FileObject。我们只能给FileObject发送IRP。
stat=ObReferenceObjectByHandle(handle,GENERIC_READ,*IoFileObjectType,KernelMode,(PVOID*)&fileob,0);
之后我们使用IoAllocateIrp分配一个IRP。根据FileObject->DeviceObject->Flags的值,我们判断目标文件系统使用什么样的IO方式。
if(fileob->DeviceObject->Flags & DO_BUFFERED_IO)
{
irp->AssociatedIrp.SystemBuffer=buffer;//buffered io
}
else if(fileob->DeviceObject->Flags & DO_DIRECT_IO)
{
mdl=IoAllocateMdl(buffer,count,0,0,0);
MmBuildMdlForNonPagedPool(mdl);
irp->MdlAddress=mdl;//direct io
}
else
{
irp->UserBuffer=buffer;//neither i/o, use kernel buffer
}
对每种不同的IO方式使用不同的地址传递方式。随后我们填充IRP内的各个参数域,就可以发送IRP了。以Read为例:
irpsp->FileObject=fileob;
irpsp->MajorFunction=IRP_MJ_READ;
irpsp->MinorFunction=IRP_MN_NORMAL;//0
irpsp->Parameters.Read.ByteOffset=offsetused;
irpsp->Parameters.Read.Key=0;
irpsp->Parameters.Read.Length=count;
接着要考虑如果IRP不能及时完成,会异步的返回的情况,我们安装一个CompletionRoutine,在CompletionRoutine里面设置一个事件为已激活,通知我们的主线程读取或者写入操作已经完成。
IoSetCompletionRoutine(irp,IoCompletion,&event,1,1,1);
NTSTATUS
IoCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
KeSetEvent((PRKEVENT)Context, IO_DISK_INCREMENT, 0);
return STATUS_MORE_PROCESSING_REQUIRED;
}
现在可以发送IRP了。如果不采取特殊的措施的话,IRP发送目标是FileObject对应的DeviceObject。发送后,等待IRP的完成并且释放资源,返回。
stat=IoCallDriver(fileob->DeviceObject,irp);
if(stat==STATUS_PENDING){
KeWaitForSingleObject(&event, Executive,KernelMode,0,0);
stat=irp->IoStatus.Status;
}
if(!NT_SUCCESS(stat))
{
IoFreeIrp(irp);
if(mdl){IoFreeMdl(mdl);}//if DO_DIRECT_IO
return -1;
}
stat=irp->IoStatus.Information;//bytes read
IoFreeIrp(irp);
if(mdl){IoFreeMdl(mdl);}//if DO_DIRECT_IO
return stat;
3.3文件的Delete
Delete实际上是通过向FSD发送IRP_MJ_SET_INFORMATION的IRP,并把IrpSp->Parameters.SetFile.FileInformationClass设置为FileDispositionInformation,用一个FILE_DISPOSITION_INFORMATION结构填充buffer来执行的。
fdi.DeleteFile=TRUE;
irpsp->MajorFunction=IRP_MJ_SET_INFORMATION;
irpsp->Parameters.SetFile.Length = sizeof(FILE_DISPOSITION_INFORMATION);
irpsp->Parameters.SetFile.FileInformationClass = FileDispositionInformation;
irpsp->Parameters.SetFile.DeleteHandle = (HANDLE)handle;
3.4文件的Rename
类似于Delete,Rename是向FSD发送IRP_MJ_SET_INFORMATION的IRP,把IrpSp->Parameters.SetFile.FileInformationClass设置为FileRenameInformation,填充buffer为FILE_RENAME_INFORMATION结构。
fri.ReplaceIfExists=TRUE;
fri.RootDirectory=0;//Set fri.FileName to full path name.
fri.FileNameLength=wcslen(filename)*2;
wcscpy(fri.FileName,filename);//If the RootDirectory member is NULL, and the file is being moved to a different directory, this member specifies the full pathname to be assigned to the file.
irpsp->MajorFunction=IRP_MJ_SET_INFORMATION;
irpsp->Parameters.SetFile.Length = sizeof(FILE_FILE_RENAME_INFORMATION);
irpsp->Parameters.SetFile.FileInformationClass = FileRenameInformation;
综上,于是我们可以在驱动里面通过发送IRP来直接访问文件系统了,绕过了native API 和win32 API层次。
4.绕过文件系统过滤驱动和钩子
有了第三部分的内容,我们目前可以直接给FSD发送请求操作文件。但是这还不够,因为有很多的杀毒软件或者监视工具使用FSD Filter Driver或者FSD Hook的办法来监控文件操作。在今天这篇文章里我讲一些原理性的东西,提供绕过FSD Filter Driver / FSD Hook的思路。
4.1对付文件系统过滤驱动
文件系统过滤驱动Attach在正常的文件系统之上,监视和过滤我们的文件访问。文件系统驱动栈就是由这一连串的Attach起来的过滤驱动组成。我们可以用IoGetRelatedDeviceObject这个函数来获得一个FileObject对应的最底层的那个功能驱动对象(FDO)。但是这样虽然绕过了那些过滤驱动,却同时也绕过了正常的FSD如Ntfs/Fastfat,因为正常的FSD也是作为一个过滤驱动存在的。磁盘文件对象的对应的最底层的FDO是Ftdisk.sys,它已经因为过于底层而不能处理我们投递的IRP请求。
其实正常的FSD信息存储在一个Vpb结构中,我们可以使用IoGetBaseFileSystemDeviceObject这个未公开的内核函数来得到它。它就是我们发送IRP的目标了。
4.2对付替换DispatchRoutine的FSD Hook
这是一种常用的FSD Hook方式。我们需要得到原本的DispatchRoutine,向原本的DispatchRoutine发送我们的IRP。这里提供一个思路:我们可以读取原本FSD驱动的.INIT段或者.TEXT段,查找其DriverEntry函数,在它的DriverEntry函数中肯定设置了自己的DriverObject的各个DispatchRoutine。在这个函数中我们就能找到我们想要的DispatchRoutine的地址。只需要使用特征码搜索的方法就可以搜索到这个值。
4.3对付Inline Hook DispatchRoutine函数本身的FSD Hook
这种Hook方法比较狠毒,但不是非常常见于安全产品中,一般应用在木马和rootkit上,比如我自己写的rootkit。它没有更改DriverObject里面的DispatchRoutine的函数指针,而是向函数开头写入汇编指令的JMP来跳转函数。对付它的基本思路就是读取存在磁盘上的FSD的文件,加载到内存一份干净的备份,察看我们要调用的DispatchRoutine开头的几个字节和这个干净备份是否一致。如果不一致,尤其是存在JMP,RET,INT3一类的汇编指令的时候,很可能就是存在了Inline Hook。(但要充分考虑重定位的情况。)如果存在Inline Hook,我们就把干净的函数开头拷贝过来覆盖掉被感染的函数头。然后在发送IRP,就不会被Inline Hook监视或篡改了。
http://www.cnblogs.com/lzjsky/archive/2010/11/19/1881599.html
Windows平台内核级文件访问的更多相关文章
- cocos3.9 windows平台 AssetsManager创建文件失败问题
在做热更新功能时用到了AssetsManager,发现在windows平台总是报CREATE_FILE错误,errorStr "Can't renamefile from: xxx.tmp ...
- everything基于Windows平台快速搜索文件
在Windows搜索文件,自带的搜索效率很低.高效.速度是你忠心的选择... 速度真是杠杠的 下载: http://www.voidtools.com/downloads/ https://files ...
- quick-x在windows平台打包加密文件
D:\quick-cocos2d-x-2.2.1-rc\bin>compile_scripts -i ..\mygames\myth\scripts -o ..\mygames\myth\res ...
- Windows 平台上长路径名文件的解决方法
https://www.ibm.com/developerworks/cn/java/j-lo-longpath.html
- linux内核编程入门--系统调用监控文件访问
参考的资料: hello world https://www.cnblogs.com/bitor/p/9608725.html linux内核监控模块--系统调用的截获 https://www. ...
- 如何下载Github单个文件(Windows平台)
如何下载Github单个文件(Windows平台) 前提 安装Chrome 浏览器 Chrome浏览器 安装迅雷软件 安装Chrome 迅雷插件 可能商店里迅雷插件有好几种,这里使用这一种 一般使用者 ...
- C#更改文件访问权限所有者(适用于各个Windows版本)
前面也提到了,前段时间在做Online Judge系统,在正式上线前有几个比较老的版本,其中第一个版本使用ACL来控制权限以确保安全(但是这个版本完全建立在IIS上,所以这样做是没效果的),遇到了一些 ...
- windows平台 python生成 pyd文件
Python的文件类型介绍: .py python的源代码文件 .pyc Python源代码import后,编译生成的字节码 .pyo Python源代码编译优化生成的字节 ...
- 排错-windows平台下访问oracle em出现空白的解决方法
排错-windows平台下访问oracle em出现空白的解决方法 by:授客 QQ:1033553122 问题描述 IE浏览器本地访问oem,出现空白页面,就左上角有一行字符 http://loca ...
随机推荐
- Javascript获取当前时间戳的方法
定义日期: Date 对象用于处理日期和时间. 可以通过 new 关键词来定义 Date 对象.以下代码定义了名为 myDate 的 Date 对象: var myDate=new Dat ...
- 各种语言中的urlencode方法
转载自:http://blog.sina.com.cn/s/blog_3f195d2501000a9b.html URLENCODE和URLDECODE是比较常用的URL参数转换方法,为以后使用方便, ...
- Http网络请求
前提说明: 1.Str.Empty 其实就是 string.Empty 2.@object.IsNull() 的IsNull() 是判断“是否等于null”的扩展方法,等同于 @object==nu ...
- laravel5.1框架简介及安装
最近自己出来实习了,进入了一个新的环境,不仅是生活中,在代码和架构中也完全是一个新的架构.由于公司使用laravel5.1框架,所以最近学习了laravel5.1框架,好了接下来就简单介绍一下lara ...
- 使用Task简化Silverlight调用Wcf
原文http://www.cnblogs.com/lemontea/archive/2012/12/09/2810549.html 从.Net4.0开始,.Net提供了一个Task类来封装一个异步操作 ...
- jQuery粘性跟随滚动条滚动的导航栏源代码下载
jQuery粘性跟随滚动条滚动的导航栏源代码下载 作者:网页模板 大小:0.005MB 点击次数:3494 发布时间:2014-03-07 12:55 分享到:0 特效介绍 jQuery粘性跟随滚动条 ...
- poj3086---数论
#include <stdio.h> #include <stdlib.h> int T(int n) { ,i; ;i<=n;i++) { sum+=i; } retu ...
- 编程内功修炼之数据结构—BTree(三)总结
BTree必须通过各种编程约束,使得不脱离BTree的本身特性: 1)BTree关键字插入操作:插入过程中,如果节点关键字达到上限,添加分裂约束,从而控制每个节点的关键字数维持在 t-1~2*t-1内 ...
- 通过属性集名称获取属性集id
Mage::getModel('eav/entity_attribute_set')->load('属性集名称', 'attribute_set_name')->getAttributeS ...
- 10个Laravel4开发者必用扩展包
Laravel是一个新的基于最新PHP版本号语法,支持IoC等设计模式的高速开发框架.眼下最新版本号为4.2,推荐安装PHP版本号5.5+. 本文列举10个基本软件包,都是开发人员使用Laravel框 ...