用minifilter 实现文件隐藏(大神可以绕道了),主要是根据网上的一些资料自己整合的一个系统,包括驱动层和应用层。
开发环境:win7_x64, QT5.2,WDK7600,8G
测试环境:win7_x86,2G
在看雪上也呆了一年多,经常问问题,看别人的精华帖,现在自己也来贡献一篇。
文中只处理了IRP_MJ_DIRECTORY_CONTROL这一个IRP,是在它的后操作中来实现的
CONST FLT_OPERATION_REGISTRATION Callbacks[] = {
{ IRP_MJ_DIRECTORY_CONTROL,
0,
NULL,
PtPostOperationHide
},
{ IRP_MJ_OPERATION_END }
主要是根据路径和文件名来隐藏,根据应用层传过来的信息判断是隐藏文件还是路径
NTSTATUS MHideMessage( __in PVOID ConnectionCookie,
__in_bcount_opt(InputBufferSize) PVOID InputBuffer,
__in ULONG InputBufferSize,
__out_bcount_part_opt(OutputBufferSize,*ReturnOutputBufferLength) PVOID OutputBuffer,
__in ULONG OutputBufferSize,
__out PULONG ReturnOutputBufferLength )
{
INPUT_BUFFER inputBuf;
MY_COMMAND command;
PAGED_CODE();
UNREFERENCED_PARAMETER( ConnectionCookie );
UNREFERENCED_PARAMETER( OutputBufferSize );
UNREFERENCED_PARAMETER( OutputBuffer );
KdPrint(("接收应用层传过来的消息 \n"));
RtlZeroMemory(&inputBuf ,sizeof(INPUT_BUFFER));
if ((InputBuffer == NULL ) || (InputBufferSize > sizeof(INPUT_BUFFER)))
{
return STATUS_INVALID_PARAMETER;
}
RtlCopyMemory(&inputBuf , InputBuffer,InputBufferSize);
command = inputBuf.command;
switch(command)
{
case ADD_HIDE_PATH:
KdPrint(("inputBuf.hidePath = %S \n",inputBuf.hidePath));
AddHidePath(inputBuf.hidePath);
break;
case SHOW_HIDE_PATH:
KdPrint(("即将进入ShowHidePath\n"));
ShowHidePath(inputBuf.hidePath);
break;
default:
break;
}
return STATUS_SUCCESS;
}
将传入的文件信息依次放入链表中(与后操作中的文件信息进行对比,符合就断链) ,传入要隐藏的文件
VOID AddHidePath(PWSTR xxPath){
PHIDE_PATH_LIST pathListNode ,pathList;
BOOL bRet;
KIRQL Irql;
pathListNode = (PHIDE_PATH_LIST)ExAllocatePoolWithTag(NonPagedPool,sizeof(HIDE_PATH_LIST),HIDE_TAG);
if (pathListNode == NULL)
{
return ;
}
wcscpy(pathListNode->xxPath,xxPath);
KeAcquireSpinLock(&HidePathListLock,&Irql);
InsertTailList(&HidePathListHeader,&pathListNode->listNode);
KeReleaseSpinLock(&HidePathListLock,Irql);
}
传入要取消隐藏的文件
VOID ShowHidePath(PWSTR xxPath){
PHIDE_PATH_LIST hideList;
PLIST_ENTRY pListNode;
KIRQL Irql;
KdPrint(("ShowHidePath\n"));
if (!IsListEmpty(&HidePathListHeader))
{
for (pListNode = HidePathListHeader.Flink; pListNode!=&HidePathListHeader; pListNode = pListNode->Flink)
{
hideList = CONTAINING_RECORD(pListNode,HIDE_PATH_LIST,listNode);
if (wcscmp(hideList->xxPath,xxPath) == 0)
{
KeAcquireSpinLock(&HidePathListLock,&Irql);
RemoveEntryList(&hideList->listNode);
ExFreePoolWithTag(hideList , HIDE_TAG);
KeReleaseSpinLock(&HidePathListLock,Irql);
return ;
}
}
}
}
IRP_MJ_DIRECTORY_CONTROL的后操作
//主要的隐藏函数
FLT_POSTOP_CALLBACK_STATUS
PtPostOperationHide (
__inout PFLT_CALLBACK_DATA Data,
__in PCFLT_RELATED_OBJECTS FltObjects,
__in_opt PVOID CompletionContext,
__in FLT_POST_OPERATION_FLAGS Flags
)
{
ULONG nextOffset = 0;
static wchar_t szFilePath[PAGE_SIZE] = {0};
int modified = 0;
int removedAllEntries = 1;
wchar_t *fullPathLongName = NULL;
UNICODE_STRING strFilePathName;
wchar_t* pFileName = NULL;
PFLT_FILE_NAME_INFORMATION nameInfo;
PFILE_ID_BOTH_DIR_INFORMATION currentFileInfo = 0;
PFILE_ID_BOTH_DIR_INFORMATION nextFileInfo = 0;
PFILE_ID_BOTH_DIR_INFORMATION previousFileInfo = 0;
PHIDE_PATH_LIST pthName;
UNREFERENCED_PARAMETER( FltObjects );
UNREFERENCED_PARAMETER( CompletionContext );
UNREFERENCED_PARAMETER( Data );
UNREFERENCED_PARAMETER( Flags );
if( FlagOn( Flags, FLTFL_POST_OPERATION_DRAINING ) )
{
/************************************************************************/
/* 如果Flags位FLTFL_POST_OPERATION_DRAINING,禁止执行普通的结束操作,则返回值必须是
FLT_POSTOP_FINISHED_PROCESSING */
/************************************************************************/
return FLT_POSTOP_FINISHED_PROCESSING;
}
//目录查询请求,消息类型因文件系统而异
if( Data->Iopb->MinorFunction == IRP_MN_QUERY_DIRECTORY &&
Data->Iopb->Parameters.DirectoryControl.QueryDirectory.FileInformationClass == FileIdBothDirectoryInformation &&
Data->Iopb->Parameters.DirectoryControl.QueryDirectory.Length > 0 &&
NT_SUCCESS(Data->IoStatus.Status) )
{
NTSTATUS status = FltGetFileNameInformation(Data,FLT_FILE_NAME_OPENED | FLT_FILE_NAME_QUERY_ALWAYS_ALLOW_CACHE_LOOKUP ,&nameInfo);
if (!NT_SUCCESS(status)) goto LAST_CODE;
status = FltParseFileNameInformation(nameInfo);
if (!NT_SUCCESS(status)) goto LAST_CODE;
fullPathLongName = ExAllocatePoolWithTag(NonPagedPool , PAGE_SIZE,HIDE_TAG);
if(fullPathLongName == NULL) goto LAST_CODE;
RtlZeroMemory(fullPathLongName , PAGE_SIZE);
RtlCopyMemory(fullPathLongName , nameInfo->Name.Buffer , nameInfo->Name.Length);
RtlInitUnicodeString(&strFilePathName , fullPathLongName);
if (strFilePathName.Buffer[strFilePathName.Length / sizeof(wchar_t) - sizeof(char)] != '\\')
{
strFilePathName.Buffer[strFilePathName.Length / sizeof(wchar_t)] = '\\' ;
}
FltReleaseFileNameInformation(nameInfo);
if (Data->Iopb->Parameters.DirectoryControl.QueryDirectory.MdlAddress != NULL)
{
currentFileInfo=(PFILE_BOTH_DIR_INFORMATION)MmGetSystemAddressForMdlSafe(
Data->Iopb->Parameters.DirectoryControl.QueryDirectory.MdlAddress,
NormalPagePriority );
}
else
{
currentFileInfo = (PFILE_ID_BOTH_DIR_INFORMATION)Data->Iopb->Parameters.DirectoryControl.QueryDirectory.DirectoryBuffer;
}
if(currentFileInfo==NULL) goto LAST_CODE ;
previousFileInfo = currentFileInfo;//保存下来
do{
nextOffset = currentFileInfo->NextEntryOffset;//距离下一个节点的偏移量
nextFileInfo = (PFILE_ID_BOTH_DIR_INFORMATION)((PCHAR)(currentFileInfo) + nextOffset);//下一个节点
//文件名
pFileName = (wchar_t *)ExAllocatePoolWithTag(NonPagedPool, currentFileInfo->FileNameLength + sizeof(wchar_t),HIDE_TAG);
if (pFileName == NULL)break;
RtlZeroMemory(pFileName, currentFileInfo->FileNameLength + sizeof(wchar_t));
RtlCopyMemory(pFileName, currentFileInfo->FileName, currentFileInfo->FileNameLength);
RtlStringCbPrintfExW(szFilePath, sizeof(wchar_t)*PAGE_SIZE, NULL, NULL, STRSAFE_FILL_BEHIND_NULL, L"%s%s", fullPathLongName, pFileName);
//只需要把磁盘中原有的传入即可,用户输入的在匹配函数中获取,对比用户输入与磁盘上的文件
if(SearchIsProtect(szFilePath))
{
KdPrint(("找到了要隐藏的文件 ! \n"));
//判断是不是最后一个节点
if( nextOffset == 0 )
{
previousFileInfo->NextEntryOffset = 0;
KdPrint((" %S 是最后一个节点 \n",currentFileInfo->FileName));
}
else
{
KdPrint(("%S 不是最后一个节点,即将去除这个节点 \n",currentFileInfo->FileName));
previousFileInfo->NextEntryOffset = (ULONG)((PCHAR)currentFileInfo - (PCHAR)previousFileInfo) + nextOffset;
}
modified = 1;
}
else
{
removedAllEntries = 0;
KdPrint(("没有找到 ! \n"));
previousFileInfo= currentFileInfo ;
}
currentFileInfo = nextFileInfo;
if (pFileName != NULL)
{
ExFreePool(pFileName);
}
}while(nextOffset != 0);
}
LAST_CODE:
if( modified )
{
if( removedAllEntries )
{
Data->IoStatus.Status = STATUS_NO_MORE_FILES;
}
else
{
FltSetCallbackDataDirty( Data );
}
}
if (fullPathLongName)
{
ExFreePool(fullPathLongName);
}
DbgPrint(" Leave PostMHideFile()\n");
return FLT_POSTOP_FINISHED_PROCESSING;
}
当然在驱动层还有NT路径转为DOS路径的代码,会在下面的附件里面加上。
接下来是应用层的代码,用QT写的,传给驱动层要隐藏的文件
void HideApp::noticeDriver(QStringList strList){
HANDLE hFsFilter=INVALID_HANDLE_VALUE;
DWORD dwRet=0;
BOOL bControlRet=0;
int i=0;
INPUT_BUFFER input;
int num=strList.size(); //pWhitePathList结构中的num
WritePathBuf *pWhitePathList;
DWORD length = sizeof(WritePathBuf) + (num-1) * sizeof(WritePathInfo);
pWhitePathList = (WritePathBuf *)malloc(length);
memset(pWhitePathList, 0, length);
pWhitePathList->iWritePathCount = num;
//////////////////////////////QString to Wchar///////////////////////////////////////////////////////////////////
int index=0;
for(QStringList::Iterator it=strList.begin();it!=strList.end();it++)
{
TCHAR szDir[260]={0};/////坑爹的 没初始化竟导致循环赋值字符串发生截断
TCHAR szDosName[3]={0}; //类似C:
TCHAR szDeviceName[MAX_VOLNAME_SIZE]={0}; //设备连接名称
TCHAR szDevicePath[MAX_FILE_LEN]={0};
(*it).replace("/","\\"); //路径中的斜杠换成反斜杠
qDebug()<<*it;
((*it).trimmed()).toWCharArray(szDir); //QString to wchar* array
wcsncpy_s(szDosName,szDir,2); //2个字符
QueryDosDevice(szDosName, szDeviceName, MAX_VOLNAME_SIZE);
wcscpy_s(szDevicePath,szDeviceName);
wcscat_s(szDevicePath,(szDir+2));
wcscpy_s(pWhitePathList->writePath[index].wzWritePath, MAX_FILE_LEN, szDevicePath); //白名单复制到pWhitePathList结构
index++;
}
/////////////////////////////////////////////////////////通知驱动/////////////////////////////////////////////////////
//打开内核控制设备
DWORD result;
//TCHAR sz[260] ={0};
ZeroMemory(&input, sizeof(INPUT_BUFFER));
input.command = ADD_HIDE_PATH;
for (i ; i < num ;i++)
{
printf("%S \n",pWhitePathList->writePath[i].wzWritePath);
wcscpy(input.hidePath,pWhitePathList->writePath[i].wzWritePath);
//发送 ADD_HIDE_PATH命令,准备和驱动层进行通信
FilterSendMessage(g_hPort,
&input,
sizeof(INPUT_BUFFER),
NULL,
0,
&result);
}
}
传给驱动层要显示的文件
void myTable::showPath(int row ,int colum){
QString s = mytableWidget->item(row,colum)->text();
QString SHOW2 = mytableWidget->item(row,colum+2)->text();
if (SHOW2 == "显示")
{
mytableWidget->item(row,colum+1)->setText("隐藏");
}
TCHAR szDir[260]={0};/////坑爹的 没初始化竟导致循环赋值字符串发生截断
TCHAR szDosName[3]={0}; //类似C:
TCHAR szDeviceName[MAX_VOLNAME_SIZE]={0}; //设备连接名称
TCHAR szDevicePath[MAX_FILE_LEN]={0};
INPUT_BUFFER input;
s.replace("/","\\"); //路径中的斜杠换成反斜杠
qDebug()<<s;
((s).trimmed()).toWCharArray(szDir); //QString to wchar* array
wcsncpy_s(szDosName,szDir,2); //2个字符
QueryDosDevice(szDosName, szDeviceName, MAX_VOLNAME_SIZE);
wcscpy_s(szDevicePath,szDeviceName);
wcscat_s(szDevicePath,(szDir+2));
printf("%S \n",szDevicePath);
//打开内核控制设备
DWORD result;
ZeroMemory(&input, sizeof(INPUT_BUFFER));
input.command = SHOW_HIDE_PATH;
wcscpy(input.hidePath,szDevicePath);
//发送 SHOW_HIDE_PATH命令,准备和驱动层进行通信
FilterSendMessage(g_hPort,
&input,
sizeof(INPUT_BUFFER),
NULL,
0,
&result);
}
编译驱动文件
测试前的准备及显示
测试后的显示
打开一个新的资源管理器看能不能找到被隐藏的Windows文件夹
驱动文件及驱动的源码在附件中,
生成的应用层的exe比较大(QT的依赖包比较大),大于规定的8M了,我放到了百度网盘上了,
链接:https://pan.baidu.com/s/1TrKiGXZ4IFgZWYq5qPimfQ
密码:azgx,
有需要的可以下载(只有应用层的程序,没有放应用层的源码,
驱动的源码和应用层的源码我放到CSDN上了
)
当然驱动文件都有啦,应用层可以自己写的啦!
驱动的源码和应用层的源码我放到CSDN上了
https://download.csdn.net/download/feixi7358/10363410 中
附件中的所有可执行程序要以管理员权限打开才行
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2019-2-2 10:12
被admin编辑
,原因: 图片本地化