ZwReadFile失败,异常代码0xC0000005,求指点

哈尔滨-猫猫 2014-01-02 09:53:55
首先,我HOOK了SSDT的ZwReadFile函数,替换为我的_ZwReadFile,实现对系统所有进程读文件操作的过滤目的。当检测当前进程为w3wp.exe或者为ReadFileTest.exe(由于我获取进程名只获取到16个字节,所以少获取了一个'e',判断时,使用了ReadFileTest.ex)进程时,将判断这两个进程读的文件头10个字节,是否为我指定的内容,如果是,则执行相关解密操作。

其次,在进程进入我的_ZwReadFile时,是可以正常读取文件内容的,然后我想判断一下此次读的文件的头10个字节是否为我指定的内容,由于刚刚已经读到了文件结尾,下一次读时需要将文件位置设置为0,以便再次从文件头中读取文件头10个字节的内容。

于是,有如下代码:
bool IsEncrypt(HANDLE FileHandle)
{
IO_STATUS_BLOCK ioStatus;
ULONG dwReaded = 0;
FILE_POSITION_INFORMATION current_fpi;
FILE_POSITION_INFORMATION fpi;
NTSTATUS status;

InterlockedIncrement(&g_uCount);
//获取当前文件位置信息,以便读取文件头后,恢复当前文件指针位置
status = ZwQueryInformationFile(FileHandle, &ioStatus, ¤t_fpi, sizeof(FILE_POSITION_INFORMATION), FilePositionInformation);
DbgPrint("%ld", current_fpi.CurrentByteOffset);
if (NT_SUCCESS(status))
{
DbgPrint("ZwQueryInformationFile successful");
}
else
{
DbgPrint("ZwQueryInformationFile failed");
}

//移动文件指针位置到文件头部
fpi.CurrentByteOffset.QuadPart = 0i64;
DbgPrint("%ld", fpi.CurrentByteOffset);
//设置文件指针位置信息
status = ZwSetInformationFile(FileHandle, &ioStatus, &fpi, sizeof(FILE_POSITION_INFORMATION), FilePositionInformation);
if (NT_SUCCESS(status))
{
DbgPrint("ZwSetInformationFile successful");
}
else
{
DbgPrint("ZwSetInformationFile failed");
}

char *szFileHeader = (char*)ExAllocatePool(NonPagedPool, 10);
ULONG length = 10;

NTSTATUS status1 = g_pfnZwReadFile(
FileHandle,
NULL,
NULL,
NULL,
&ioStatus,
szFileHeader,
length,
NULL,
NULL);
if (NT_SUCCESS(status1))
{
DbgPrint("Read File Header successful");
}
else
{
//此处将会被执行,读取文件失败。。。
DbgPrint("Read File Header failed : %d, %d, %d", status1, ioStatus.Status, ioStatus.Information);
}

//恢复文件指针位置
ZwSetInformationFile(FileHandle, &ioStatus, ¤t_fpi, sizeof(FILE_POSITION_INFORMATION), FilePositionInformation);
InterlockedDecrement(&g_uCount);

DbgPrint("FileHeader : %s", szFileHeader);
if (!strcmp(szFileHeader, "QQ:7278449"))
{
return TRUE;
}
ExFreePool(szFileHeader);
return FALSE;
}



NTSTATUS NTAPI _ZwReadFile(
IN HANDLE FileHandle,
IN HANDLE Event OPTIONAL,
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
IN PVOID ApcContext OPTIONAL,
OUT PIO_STATUS_BLOCK IoStatusBlock,
OUT PVOID Buffer,
IN ULONG Length,
IN PLARGE_INTEGER ByteOffset OPTIONAL,
IN PULONG Key OPTIONAL
)
{
NTSTATUS status;
char szProcessName[256] = {0};
GetProcessName((ULONG)PsGetCurrentProcessId(), szProcessName);

InterlockedIncrement(&g_uCount);
status = g_pfnZwReadFile(
FileHandle,
Event,
ApcRoutine,
ApcContext,
IoStatusBlock,
Buffer,
Length,
ByteOffset,
Key);
InterlockedDecrement(&g_uCount);

if (!strcmp(szProcessName, "w3wp.exe") || !strcmp(szProcessName, "ReadFileTest.ex"))
{
DbgPrint("Current Process Name : %s", szProcessName);
DbgPrint("%s", Buffer);
if (IsEncrypt(FileHandle))
{
DbgPrint("the file is encrypt!!!");
Decrypt((char*)Buffer, Length);
}
}

return status;
}

注:g_pfnZwReadFile为HOOK后,原ZwReadFile函数的指针。
经过一周时间windbg调试+DebugView,也没能找出读文件失败原因,还请大神指点,表示感谢!
可用分全部奉上....
...全文
623 17 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
liutingting2020 2014-07-18
  • 打赏
  • 举报
回复
可以给我发一个源码吗?就发上面那个QQ就好啦。谢谢哦。
liutingting2020 2014-07-18
  • 打赏
  • 举报
回复
亲,你是不是对文件进行防护啊。我现在也在做这方面。还想多和你交流。我的QQ:865213875
zgcbj 2014-01-08
  • 打赏
  • 举报
回复
楼主,该结帖啦
哈尔滨-猫猫 2014-01-03
  • 打赏
  • 举报
回复
1.如何判断用户的ReadFile是我想要过滤的文件? 2.如果用户层只调用了CreateFile 而之后没有进行读操作呢? 3.假如打开文件时,我判断文件是否是加密的,那我保存什么呢? 文件路径:我不是所有进程读这个文件,都要给解密 文件句柄:那如果上层应用读一个文件的时候,用了两次以上CreateFile呢? 还有很多未知问题,因为我的目的是过滤一下某些进程读文件操作,而去hook 离其较远的CreateFile,中间n多问题的。
哈尔滨-猫猫 2014-01-03
  • 打赏
  • 举报
回复
g_pfnZwReadFile是原ZwReadFile的函数指针 Hook ZwReadFile的时候,要保存原函数的入口地址啊,以便于驱动卸载时恢复ssdt~ 如果Hook CreateFile会有很多问题,如:
hahayezhe112 2014-01-03
  • 打赏
  • 举报
回复
g_pfnZwReadFile 里面是什么?文件指针吗,指向读函数? 你已经Hook了ZwReadFile,怎么去调用读函数呢? 我觉得你的思路是否可以改下,改成hook打开文件的函数,在打开文件之前根据路径打开一次,读取前10个字节去判断。
哈尔滨-猫猫 2014-01-02
  • 打赏
  • 举报
回复
LARGE_INTEGER li; li.QuadPart = 0i64; 然后把&li传进去 实现从头读文件 但是效果还是不行,读文件依然失败,同样的错误。 解释一下哈,用qq用习惯了,总是习惯用 ctrl+enter换行,,,蛋碎了,又不小心按错
哈尔滨-猫猫 2014-01-02
  • 打赏
  • 举报
回复
不读不行啊,我HOOK了ZwReadFile,我最起码应该保证上层所有读操作能正常进行下去,否则上层很容易蹦掉或者卡死的。 话说DDK帮助里 我也研究了一下这个ByteOffset,也尝试了,貌似给这个值NULL,使用的应该是当前文件指针位置,也就有了我保存当前位置,又恢复当前位置的操作。 另外,根据文档,也可以这里直接传递一个PLARGE_INTEGER 大整数指针过去,可以指定文件指针位置,类似如下代码:
zgcbj 2014-01-02
  • 打赏
  • 举报
回复
你这一句话两层楼,是为了顶帖子么?好少年啊。 不过,能不能判断 IN PLARGE_INTEGER ByteOffset OPTIONAL 倒数第二个参数,判断位置,如果位置是开头,就读,不是开头就不读。 如果你要求,别管多少次ReadFile,都要判断最开始几个字节,那估计就得像你这样做了。
哈尔滨-猫猫 2014-01-02
  • 打赏
  • 举报
回复
这样分多次读取一个文件的情况,从第二次ReadFile开始 缓冲区里就不会包含有该文件的头信息了 所以我在IsEncrypt里 特意去读的文件头
哈尔滨-猫猫 2014-01-02
  • 打赏
  • 举报
回复
但,如果上层应用,读一个文件是调用两次以上ReadFile呢?
哈尔滨-猫猫 2014-01-02
  • 打赏
  • 举报
回复
嗯,一开始我也是像你这么想。但是有一种情况,这样不行。 比如上层应用读一个文件,只调用了一次ReadFile就把整个文件读取了,你说的方法,没有问题。
zgcbj 2014-01-02
  • 打赏
  • 举报
回复
你在 _ZwReadFile的开头是不是已经读到了,为什么又去 IsEncrypt(FileHandle)这里面再读一遍?直接拿buff里面的比不行吗?我不懂了。
哈尔滨-猫猫 2014-01-02
  • 打赏
  • 举报
回复
其他人说内核少用栈空间,于是才改的。但问题依旧,应该不是这里的问题(已调试验证过)
哈尔滨-猫猫 2014-01-02
  • 打赏
  • 举报
回复
char buffer[11] = {0};
哈尔滨-猫猫 2014-01-02
  • 打赏
  • 举报
回复
这里调试了,成功。本来没用堆内存,用的栈上的缓冲区,如下
zgcbj 2014-01-02
  • 打赏
  • 举报
回复
char *szFileHeader = (char*)ExAllocatePool(NonPagedPool, 10); 这个怎么没看到检查成功失败呢?
(此资源来自网络,本人不负任何责任) 易 游等等的虚拟还原功能都是用这开发的 一 安装和使用方法: 1 安装:看install.txt文件; 2 挂载:filedisk /mount 0 c:\proj\myfiledisk\a.img e: 3 卸载:filedisk /umount e: 详细看example.txt文件,在本站的下载中心有下载,包括核心层和应用层的源码。 最多可以同时挂4个设备,分别定义4个设备号,如果同一设备号使用2次,会进入OPEN_FILE两次,出现"FileDisk: IOCTL_FILE_DISK_OPEN_FILE: Media already opened\n"的错误提示; 二 基本知识: 1 关于DefineDosDevice函数: 在应用层开发中调用它来创建一个\??目录下的符号链接,如: BOOL okay = DefineDosDevice(DDD_RAW_TARGET_PATH, "test", "\\Device\\FileDisk0"); 调用成功后,将会在设备命名空间的\??目录下生成一个名为”test“的符号链接,该链接指向”“\\Device\\FileDisk0“这个对象。 而在核心态的驱动程序中,需要调用以下的函数来创建相应的符号链接: IoCreateSymbolicLink(linkname, targname); Linkname是要创建的符号链接名,相当于上面函数中的”test”,targname是该链接指向的设备对象。 2 filedisk的源映像可以是img,iso,flp等,这些都是磁盘上一个分区的平面映像,所以挂上后可以直接访问,但不能是整个硬盘的img映像或其它格式如rar等; 3 filedisk的源映像文件名称必须是全路径,即使是在当前目录下也必须是全路径; 4 源映像文件如果不存在且参数中没有指定只读,那么只要在参数中指定了大小则会主动创建它,使用前会提示先“格式化”,之后就可正常使用了; 三 核心层源码分析: 1 DriverEntry: 主函数入口;备份传入路径,查询注册表值,调用ZwCreateDirectoryObject创建设备目录,重复4次调用 FileDiskCreateDevice创建设备,初始化操作函数指针。 2 FileDiskCreateDevice:调用IoCreateDevice创建设备,KeInitializeEvent初始化事件对象,PsCreateSystemThread创建内核线程,入口函数是FileDiskThread,传入的函数参数为IoCreateDevice返回的设备对象。 3 FileDiskThread:首先调用KeSetPriorityThread更改自身线程的优先级为LOW_REALTIME_PRIORITY,然后开始for(;;),调用KeWaitForSingleObject函数等待事件对象有信号,如果等到,判断事件类型,有如下几种: IRP_MJ_READ:调用ZwReadFile读取文件,从内核到用户缓冲区; IRP_MJ_WRITE:调用ZwWriteFile写入文件,从用户到内核缓冲区; IRP_MJ_DEVICE_CONTROL:在FileDiskDeviceControl设置事件才会触发,主要有如下两种操作码: IOCTL_FILE_DISK_OPEN_FILE:调用FileDiskOpenFile。 IOCTL_FILE_DISK_CLOSE_FILE:调用FileDiskCloseFile。 FileDiskOpenFile:根据用户程序传入的映像文件全路径,调用ZwCreateFile在内核中打开它,如果文件不存在则再创建它,返回文件句柄。 FileDiskCloseFile:调用ZwClose关闭文件。 4 FileDiskCreateClose: 仅返回成功;对应Create,Close操作。 5 FileDiskReadWrite:将IO包插入队列,然后调用KeSetEvent函数,激活事件对象;对应Read,Write操作。 6 FileDiskDeviceControl:用户程序调用DeviceIoControl的响应函数,主要有如下两种操作: IOCTL_FILE_DISK_OPEN_FILE:设置好参数,将IO包插入队列,设置对象为有信号。 IOCTL_FILE_DISK_CLOSE_FILE:将IO包插入队列,设置对象为有信号。 其它的操作类型因为输入输出共用一个缓冲区,所以都采用系统默认处理,设置好需要输出的参数后,就直接从这个函数返回了。如: IOCTL_DISK_GET_DRIVE_GEOMETRY,IOCTL_CDROM_GET_DRIVE_GEOMETRY等; 7 有4种操作是自定义的:FileDiskReadWrite函数两种,FileDiskDeviceControl函数两种,对应的操作码分别是: IRP_MJ_READ,IRP_MJ_WRITE,IOCTL_FILE_DISK_OPEN_FILE,IOCTL_FILE_DISK_CLOSE_FILE这4种,在FileDiskThread中等待这4种事件发生,如果等到,就调用相应的函数处理。 四 应用层源码分析: 1 mount:调用DefineDosDevice在应用层创建一个指向设备命名空间的符号链接,用CreateFile打开此链接,然后调用DeviceIoControl,控制码是IOCTL_FILE_DISK_OPEN_FILE,内核程序响应后,执行真正打开源映像文件的操作。 2 umount: 上面大致一样,只是多一些步骤,不同的是控制码改为IOCTL_FILE_DISK_CLOSE_FILE,之后必须发送FSCTL_DISMOUNT_VOLUME

21,619

社区成员

发帖
与我相关
我的任务
社区描述
硬件/嵌入开发 驱动开发/核心开发
社区管理员
  • 驱动开发/核心开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧