在驱动程序里面,通过hook ZwQueryDirectoryFile()来实现的隐藏文件, 可以实现隐藏了,但是有一点小的问题,下面是hook ZwQueryDirectoryFile()过后的过程函数HookZwQueryDirectoryFile(),我把要hook的文件名放在链表里面,而链表的内容是通过外面的程序传给驱动程序的 具体的代码可以看附件,下面是链表和hook函数
#define dprintf if (DBG) DbgPrint
#define nprintf DbgPrint
//定义FILE_INFORMATION_CLASS的第3号结构:_FILE_BOTH_DIR_INFORMATION,这个结构是ZwQueryDirectoryFile必须参数。
typedef struct _FILE_BOTH_DIR_INFORMATION
{
ULONG NextEntryOffset;
ULONG FileIndex;
LARGE_INTEGER CreationTime;
LARGE_INTEGER LastAccessTime;
LARGE_INTEGER LastWriteTime;
LARGE_INTEGER ChangeTime;
LARGE_INTEGER EndOfFile;
LARGE_INTEGER AllocationSize;
ULONG FileAttributes;
ULONG FileNameLength;
ULONG EaSize;
CCHAR ShortNameLength;
WCHAR ShortName[12];
WCHAR FileName[1];
} FILE_BOTH_DIR_INFORMATION, *PFILE_BOTH_DIR_INFORMATION;
//定义链表
typedef struct FileProtectLink
{
UNICODE_STRING fileName; //只是文件名如:abc.txt
UNICODE_STRING allName; //全路径如:c:\aaaa\abc.txt
UNICODE_STRING nameWithoutDiskName; //除去盘符的路径 如 \aaaa\abc.txt
UNICODE_STRING passWord; //连接密码
struct FileProtectLink *next; //指到下一个结点
} FileProtectLink, *PFileProtectLink;
FileProtectLink fileProtectLinkHand; //申请的全局链表指针变量
//hook函数过程
NTSTATUS HookZwQueryDirectoryFile(
IN HANDLE hFile,
IN HANDLE hEvent OPTIONAL,
IN PIO_APC_ROUTINE IoApcRoutine OPTIONAL,
IN PVOID IoApcContext OPTIONAL,
OUT PIO_STATUS_BLOCK pIoStatusBlock,
OUT PVOID FileInformationBuffer,
IN ULONG FileInformationBufferLength,
IN FILE_INFORMATION_CLASS FileInfoClass,
IN BOOLEAN bReturnOnlyOneEntry,
IN PUNICODE_STRING PathMask OPTIONAL,
IN BOOLEAN bRestartQuery)
{
NTSTATUS rc = STATUS_SUCCESS;
UNICODE_STRING uniFileName;
PCWSTR pProcPath = NULL;
// 执行真正的 ZwQueryDirectoryFile 函数
rc = OriginalZwQueryDirectoryFile(
hFile,
hEvent,
IoApcRoutine,
IoApcContext,
pIoStatusBlock,
FileInformationBuffer,
FileInformationBufferLength,
FileInfoClass,
bReturnOnlyOneEntry,
PathMask,
bRestartQuery);
// 如果执行成功,而且 FILE_INFORMATION_CLASS 的值为 FileBothDirectoryInformation,我们就进行处理,过滤
if (NT_SUCCESS(rc) && FileInfoClass == FileBothDirectoryInformation)
{
PFileProtectLink pLink = fileProtectLinkHand.next;
while( pLink != NULL)
{
// 把执行结果赋给 pFileInfo
PFILE_BOTH_DIR_INFORMATION pFileInfo = (PFILE_BOTH_DIR_INFORMATION)FileInformationBuffer;
PFILE_BOTH_DIR_INFORMATION pLastFileInfo = NULL;
BOOLEAN bLastOne = FALSE;
// 循环检查
do
{
bLastOne = !pFileInfo->NextEntryOffset;
RtlInitUnicodeString(&uniFileName, pFileInfo->FileName);
dprintf("pFileInfo %d %d %wZ\n", (int)pFileInfo, (int)pFileInfo->NextEntryOffset, &uniFileName);
// 开始进行比较,如果找到了就隐藏这个文件或者目录
if (RtlCompareMemory(uniFileName.Buffer, pLink->fileName.Buffer, pLink->fileName.Length) == pLink->fileName.Length)
{
if (bLastOne)
{
dprintf("bLastOne %wZ\n", &uniFileName);
if (pFileInfo == (PFILE_BOTH_DIR_INFORMATION)FileInformationBuffer)
{
rc = STATUS_NO_MORE_FILES; // 隐藏文件或者目录;
}
else
pLastFileInfo->NextEntryOffset = 0;
break;
}
else // 指针往后移动
{
int iPos = (ULONG)pFileInfo - (ULONG)FileInformationBuffer;
int iLeft = (ULONG)FileInformationBufferLength - iPos - pFileInfo->NextEntryOffset;
RtlCopyMemory((PVOID)pFileInfo, (PVOID)((PCHAR)pFileInfo + pFileInfo->NextEntryOffset), (ULONG)iLeft);
dprintf("enter bLastOne else and iPos is %d iLeft is %d is %ld\n",iPos,iLeft,(ULONG)FileInformationBufferLength);
continue;
}
}
pLastFileInfo = pFileInfo;
pFileInfo = (PFILE_BOTH_DIR_INFORMATION)((PCHAR)pFileInfo + pFileInfo->NextEntryOffset);
} while (!bLastOne);
pLink = pLink->next;
}
}
return rc;
}
问题:如果链表里面fileName的值是111.txt那么在C盘根目录下有这个文件的话就全部隐藏了
他是会执行rc = STATUS_NO_MORE_FILES;这条语句引起的,下面是我的分析。
分析:
通过调试 我发现 HOOK函数也是要枚举所有当前目录下的文件的,然后而且文件名都是在内核里面用的大写字母来比较的,所以即使我们建立的文件名是小写字母的,也会被转换为大写字母来比较,但只是比较的时候是这样,文件名本身不会被改变。也就是说假设C盘根目录下面有这几个文件(当然这是举例,不是实际情况) 123.txt, aaa.txt,nina.txt,CONFIG.SYS,CAD.EXE; 那么 系统会自动创立一个链表(其实不是链表,因为和链表的情况很相似,一个文件接一个文件的,所以我把他说成链表,便于理解),把我们这几个文件根据文件名链起来,分别是123.txt->aaa.txt->CAD.EXE->CONFIG.SYS->nina.txt ,是根据文件名来的 数字的永远都是在开头,如果我们在这个文件夹里面新建立一个文件a.txt 那么这个文件会自动插入上面的那个文件链表里面 结果就是:123.txt->a.txt->aaa.txt->CAD.EXE->CONFIG.SYS->nina.txt
通过调试 发现 程序只要不把上面那个链表的第一个文件隐藏 隐藏其他的都正常运行
整个隐藏的原理其实就是调整了这个文件链,通过hook那个函数的下面这几行 把链表里面信息改了
int iPos = ((ULONG)pFileInfo) - (ULONG)FileInformationBuffer;
int iLeft = (DWORD)FileInformationBufferLength - iPos - pFileInfo->NextEntryOffset;
RtlCopyMemory( (PVOID)pFileInfo, (PVOID)( (char *)pFileInfo + pFileInfo->NextEntryOffset ), (DWORD)iLeft );
continue;
也就是说如果本来目录下的文件链表是:
123.txt->a.txt->aaa.txt->CAD.EXE->CONFIG.SYS->nina.txt
那么 我要隐藏a.txt 我就通过上面那几句,找准位置后把链表改为:
123.txt->aaa.txt->aaa.txt->CAD.EXE->CONFIG.SYS->nina.txt
也就是把要隐藏节点的后面一个节点放到当前节点上面来 那么在函数返回后 用户就不会发现有a.txt这个文件了
至于我们遇到的问题 为什么会全部隐藏 因为我们隐藏的是第一文件 而且pFileInfo->NextEntryOffset==0 所以他执行了rc= STATUS_NO_MORE_FILES所以 只要返回这个rc 就返回这个目录里面没有文件的tag,所以就全部看不到了 实现了一次性全部隐藏,根据我们的要求,根本不需要这里的对rc的改变,所以我把包括if(bLastOne)在内里面的东西全部注释起来了, 在上面的if语句里面,只要判断出名字相匹配了,就直接运行else里面的东西,不用去判断if(bLastOne),程序照样运行,只是不能隐藏第一个文件而已~~~~~~~~~
最后至于为什么不能隐藏链表的第一个文件?因为通过dbgview看到,第一个文件的pFileInfo->NextEntryOffset为0,(这个我也不知道为什么) 而且传进来的参数FileInformationBufferLength在枚举第一个文件的时候的值是616 而在第二个的时候就变成了4096 以后都是4096 而且整个过程没有看到它在哪点赋值过, 就是调用一次 然后居然在没有任何赋值的情况下自己被改变了?? 难道是在这里使用了多线程而又没有做同步?
还有就是如果隐藏第一个文件那么由于pFileInfo->NextEntryOffset==0 那么在RtlCopyMemory( (PVOID)pFileInfo, (PVOID)( (char *)pFileInfo + pFileInfo->NextEntryOffset ), (DWORD)iLeft );的时候,拷贝内存的目的地和起始地都是同一个地方,可能这个就是不能实现隐藏的原因。不知道为什么RtlCopyMemory的第二个参数(PVOID)( (char *)pFileInfo + pFileInfo->NextEntryOffset为什么要转换为char *类型??不懂?
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)