下面主要以代碼爲主(未經嚴格測試,僅供學習參考),實現了
1、 保護文件/目錄不被刪除
2、 隱藏文件/目錄
3、 隱藏進程
4、 保護進程不被結束
5、 保護註冊表鍵不被打開
6、 保護註冊表鍵不被刪除
網上有幾篇文章介紹了部分功能,並提供的源碼。所以我主要把對源碼的理解寫下來,並對源碼做簡化,更利於理解。
保護文件/目錄不被刪除
掛鉤 ZwSetInformationFile
NTSTATUS
ZwSetInformationFile(
IN HANDLE FileHandle,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN PVOID FileInformation,
IN ULONG Length,
IN FILE_INFORMATION_CLASS FileInformationClass
);
當FileInformationClass= FileDispositionInformation時,FileInformation指向一個
FILE_DISPOSITION_INFORMATION 結構,其定義如下:
typedef struct _FILE_DISPOSITION_INFORMATION {
BOOLEAN DeleteFile;
} FILE_DISPOSITION_INFORMATION;
如果DeleteFile被設爲TRUE時,那麼當 ZwClose 被調用後文件將被刪除。這種情況下我們只要返回STATUS_NO_SUCH_FILE或STATUS_ACCESS_DENIED。
NTSTATUS Hook_ZwSetInformationFile(
IN HANDLE FileHandle,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN PVOID FileInformation,
IN ULONG Length,
IN FILE_INFORMATION_CLASS FileInformationClass
)
{
NTSTATUS rc ;
// sets the DeleteFile member of a FILE_DISPOSITION_INFORMATION to TRUE,
// so the file can be deleted when ZwClose is called to release the last open
// handle to the file object.
// The caller must have opened the file with the DELETE flag set
// in the DesiredAccess parameter.
if ( FileInformationClass == FileDispositionInformation )
{
// 取文件名稱
PVOID Object;
if ( ObReferenceObjectByHandle( FileHandle , 0 , 0 , KernelMode ,
&Object , NULL ) == STATUS_SUCCESS )
{
int BytesReturn ;
UCHAR Name[1024] ;
BOOLEAN *pbDeleteFile = NULL ;
PUNICODE_STRING lpuName = NULL ;
extern NTSTATUS ObQueryNameString(void *, void *, int size, int *);
RtlZeroMemory( Name , sizeof(Name) ) ;
if ( ObQueryNameString( Object , Name , sizeof(Name) ,
&BytesReturn ) == STATUS_SUCCESS )
{
lpuName = (PUNICODE_STRING) Name ;
}
ObDereferenceObject(Object) ;
if ( lpuName->Length > 0 &&
wcscmp( lpuName->Buffer , L”要保護的文件名” ) == 0 )
{
return STATUS_NO_SUCH_FILE ;
}
}
}
rc = gfn_RealZwSetInformationFile( FileHandle , IoStatusBlock ,
FileInformation , Length , FileInformationClass ) ;
return rc ;
}
隱藏文件/目錄
掛鉤 ZwQueryDirectoryFile
注:該函數未文檔化,在ntifs.h內定義,掛鉤前先用 extern 聲明。
extern NTSYSAPI NTSTATUS NTAPI ZwQueryDirectoryFile(
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);
參數比較多,看得頭都大了^_^,其實真正重要的參數就三個
鉤子函數先調用真正的函數,當FileInfoClass= FileBothDirectoryInformation(3)時,
FileInformationBuffer返回請求的目錄下的子目錄和文件,是一組FILE_BOTH_DIR_
INFORMATION結構:
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;
我們要隱藏某個文件或目錄,只需要把該結點從鏈表中刪除。需要注意的是,hEvent
參數或IoApcRoutine參數如果傳入有效的值,表示函數以異步的方式處理,這時應該不做其它處理,直接調用真正的函數。
NTSTATUS Hook_ZwQueryDirectoryFile (
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;
// 執行真正的ZwQueryDirectoryFile函數
rc = ((gfn_RealZwQueryDirectoryFile))(
hFile,
hEvent,
IoApcRoutine,
IoApcContext,
pIoStatusBlock,
FileInformationBuffer,
FileInformationBufferLength,
FileInfoClass,
bReturnOnlyOneEntry,
PathMask,
bRestartQuery);
if ( NT_SUCCESS(rc) &&
FileInfoClass == FileBothDirectoryInformation &&
hEvent == NULL &&
IoApcRoutine == NULL )
{
PVOID Object;
BOOLEAN bLastOne ;
PFILE_BOTH_DIR_INFORMATION pLastFileInfo ;
PFILE_BOTH_DIR_INFORMATION pFileInfo ;
WCHAR wszPath[1024] = L"" ;
// 取父目錄路徑
if ( ObReferenceObjectByHandle( hFile , 0 , 0 , KernelMode ,
&Object , NULL ) == STATUS_SUCCESS )
{
// 遍歷鏈表,重點部分!
pLastFileInfo = NULL;
pFileInfo = (PFILE_BOTH_DIR_INFORMATION)FileInformationBuffer ;
do
{
bLastOne = !( pFileInfo->NextEntryOffset );
if ( pFileInfo->FileName )
{
DbgPrint( "[hooksys] find file&directory = %S " , pFileInfo->FileName ) ;
if ( wcsstr( pFileInfo->FileName , L"hidefile" ) != NULL )
{
if( bLastOne )
{
if(pFileInfo ==
(PFILE_BOTH_DIR_INFORMATION)
FileInformationBuffer )
{
rc = STATUS_NO_SUCH_FILE ;
}
else
{
pLastFileInfo->NextEntryOffset = 0;
}
break;
}
else
{
int iPos = ((ULONG)pFileInfo) –
(ULONG)FileInformationBuffer;
int iLeft = (ULONG)FileInformationBufferLength - iPos –
pFileInfo->NextEntryOffset;
RtlCopyMemory( (PVOID)pFileInfo, (PVOID)( (char *)pFileInfo
+ pFileInfo->NextEntryOffset ), (ULONG)iLeft );
continue;
}
}
}
// 移到下一個結點
pLastFileInfo = pFileInfo;
pFileInfo = (PFILE_BOTH_DIR_INFORMATION)( (ULONG)pFileInfo
+ pFileInfo->NextEntryOffset );
}while(!bLastOne);
}
return rc ; }
pFileInfo->FileName 只提供文件名或目錄名,如果要取得完整路徑,需要通過hFile取,如同上面的例子一樣。我使用比較簡單的算法,只要文件/目錄名中含有”hidefile”,就讓它消失!其實這個只能在explorer中隱藏,在命令提示符下仍然可以cd進去,爲了更徹底點,
我攔截了ZwOpenFile
NTSTATUS Hook_ZwOpenFile(
OUT PHANDLE FileHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN ULONG ShareAccess,
IN ULONG OpenOptions
)
{
NTSTATUS rc;
if ( ObjectAttributes->ObjectName )
{
DbgPrint( "[hooksys] ZwOpenFile = %S " ,
ObjectAttributes->ObjectName->Buffer ) ;
if ( wcsstr( ObjectAttributes->ObjectName->Buffer , L"hidefile" ) != 0 )
return STATUS_NO_SUCH_FILE ;
}
rc = gfn_RealZwOpenFile( FileHandle , DesiredAccess ,
ObjectAttributes , IoStatusBlock , ShareAccess , OpenOptions ) ;
return rc ;
}
NTSTATUS
NTAPI
ZwQuerySystemInformation
(
IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
IN OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength OPTIONAL
);
typedef enum _SYSTEM_INFORMATION_CLASS {