// By Cypher. Website: http://www.cypherjb.com/. Email: cypher.jb@gmail.com NTSTATUS NTAPI FileNinja::NtQueryDirectoryFile_Hook(IN HANDLE FileHandle, IN HANDLE EventHandle OPTIONAL, IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, IN PVOID ApcContext OPTIONAL, OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID FileInformation, IN ULONG Length, IN FILE_INFORMATION_CLASS FileInformationClass, IN BOOLEAN ReturnSingleEntry, IN PUNICODE_STRING FileName OPTIONAL, IN BOOLEAN RestartScan) { // Call the original function to get the needed data NTSTATUS RetVal = Get()->m_pNtQueryDirectoryFile->GetOrig() (FileHandle, EventHandle, ApcRoutine, ApcContext, IoStatusBlock, FileInformation, Length, FileInformationClass, ReturnSingleEntry, FileName, RestartScan); // If function fails don't bother trying to use the data if (!NT_SUCCESS(RetVal)) return RetVal; // Handle all known and relevant file information classes and unlink // any entries that shouldn't be seen. switch (static_cast(FileInformationClass)) { case FileDirectoryInformation: return Get()->HideFiles(FileInformation); case FileFullDirectoryInformation: return Get()->HideFiles(FileInformation); case FileBothDirectoryInformation: return Get()->HideFiles(FileInformation); case FileNamesInformation: return Get()->HideFiles(FileInformation); case FileIdBothDirectoryInformation: return Get()->HideFiles(FileInformation); case FileIdFullDirectoryInformation: return Get()->HideFiles(FileInformation); default: break; } // Return value from trampoline return RetVal; } template NTSTATUS HideFiles(PVOID pFileInfo) { // Pointers to the linked list T* pCurrent = static_cast(pFileInfo); T* pPrev = 0; // Loop until there are no more files to process for (;;) { // Wide string to store the file name (initialized in case the given // file name in the structure is empty or otherwise invalid) std::wstring FileName(L""); // Set the file name string to the string in the structure if it's valid. // Buffer not guaranteed to be zero terminated so the string length in // the structure needs to be used (size is stored in bytes not chars) if (pCurrent->FileNameLength) { FileName = std::wstring(pCurrent->FileName, pCurrent->FileNameLength / sizeof(wchar_t)); } // Check if file should be hidden if (ShouldHideFile(ToLower(FileName))) { // Debug output std::wcout << boost::wformat(L"NtQueryDirectoryFile: Hiding " L"file: '%s'.") %FileName << std::endl; // Check for EOL if (pCurrent->NextEntryOffset == 0) { // Hide current file if (pPrev) pPrev->NextEntryOffset = 0; else return STATUS_NO_SUCH_FILE; // No files left to process break; } else { // If we need to hide the first entry then we can't simply null // the NextEntryOffset of the previous file because there isn't // one! // Note: Implementation notes removed. Have fun fixing this one. // It's a fairly easy fix, but should deter the copy-pasters. // This MUST be fixed if you want the code to work on Vista and 7 if (pPrev == 0) { // } // Hide current file else { pPrev->NextEntryOffset += pCurrent->NextEntryOffset; } } } else { // Check for EOL if (pCurrent->NextEntryOffset == 0) break; // Next file pPrev = pCurrent; } // Next file pCurrent = reinterpret_cast(reinterpret_cast(pCurrent) + pCurrent->NextEntryOffset); } // Success return STATUS_SUCCESS; }