Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / MFC

Driver to Hide Processes and Files

4.57/5 (145 votes)
17 Aug 2009CPOL12 min read 697K   28.7K  
In this article, we describe the driver we created to hide processes and files in a system.

List of Contents

  1. Introduction
  2. Is this article dangerous?
  3. Legal reason to hide processes and files
  4. Projects used
  5. Project implementation
  6. Project structure
  7. NT function call scheme and hook
  8. Function index in SST
  9. Change the results returned by the original function
  10. Process hiding
  11. File hiding
  12. GUI application
  13. Communication with HideDriver
  14. Format of communication
  15. HideDriver IOCTLs
  16. How to build this solution
  17. Additional information about the build
  18. Supported Windows version and testing
  19. Bibliography
  20. Useful Links
  21. Documentation and additional info
  22. History

Introduction

We are glad to introduce our project - "The Hide Driver project".

The main idea of this work is to create a driver for hiding of selected processes and files.

The task: The processes selected by the user should be invisible for such applications as the Task Manager, Process Explorer, and others. In addition, they should not be available for such Windows API functions as EnumProcesses(), OpenProcess(), EnumProcessModules(), and other Process APIs. The files selected by the user should be invisible for such file managers as Windows Explorer, Far, Total Commander, etc. In addition, they should not be available for such Windows API functions as FindFile(), OpenFile() and other File API functions.

This article is written as the result of one education project implemented during ApriorIT students courses.

During the project implementation we add some features that were not mentioned in the initial task:

  • Correct work on the multiple-processor systems
  • Support of the wildcards in the names of files, processes, users
  • Filtering of the access by the name of the process that requested for access
  • Filtering of the access by the user name

A lot of time was spent to create the Universal Subsystem of Interceptions where the implementation of the given interception is a kind of plug-in. Due to the limited terms we implemented only two such plug-ins with expanded functionality.

More interesting features can be added using SST hooking but they were not implemented in this project frame:

  • Hiding of registry keys
  • Hiding of the list of services and drivers
  • Hiding of opened handles (file, process, etc.)
  • Storing of the parameters in the registry or *.xml file

Is This Article Dangerous?

It's up to you! One may think that the described technique is good for virus creation. But it surely was not the aim of our article. Here are some thoughts we follow:

  1. To install the driver you must have Administrator right.
  2. This is a popular technique of files and process hiding. So all antiviruses and security products know how to bypass it.
  3. When you try to install driver antivirus can block this or ask you to make a choice.
  4. All popular antiviruses use some techniques to see the hidden files. Techniques are different - for example it can be direct call to filesystem driver or using of the system internal structure.
  5. All popular antiviruses use some techniques to see hidden processes. For example hook of KiSwapContext function. KiSwapContext is called when the time quantum of the thread ends. Time quantum is in milliseconds and KiSwapContext is called very frequently so you can't hide anything from it.
  6. Antiviruses filter all calls to file system and if you try to load a virus, they will see it and it doesn't matter if this file is hidden or not. Usually antivirus uses filter drivers located under the filesystem. In this case, when you try to read something from the disk antivirus checks content using image signatures and if there is a virus block it calls and shows the warning.
  7. If you just try to search by following keywords: antirootkit, detecting hidden process, etc. you will find hundreds of pages with hundreds of various programs.

Legal Reason to Hide Processes and Files

The main and obvious application of such technology is enterprise security and management systems. You cannot rely fully on the permissions as some users (like most of you here - developers) should have more than usual access permissions on their PC. Many security systems take the approach of using such kind of techniques to make sure that only authorized person(s) can turn this system off. It helps to prevent IP theft and data leakage in many companies.

Another application of the described technique is software licensing and copy protection frameworks.

Project Implementation

The task described in the Introduction was resolved by using one of the general ways - Hooking SSDT. A lot of information about this technology can be found in the articles mentioned in the Useful Links section.

Projects Used

I would like to thank the people who developed the following projects - they made the implementation of this project easier:

Project Structure

Directory Structure

.\bin - folder with binary files
.\lib - folder with library files
.\obj - folder with object files
.\src - folder with source files
   |
   |-> .\Common        - Files that are shared between projects.
   |-> .\STLPort       - Directory with STLPort 4.6 ported for
          using in windows drivers.
   |-> .\drvCppLib     - Kernel Library to develop driver in C++.
   |-> .\drvCppLibTest - Kernel Driver to test drvCppLib.
   |-> .\drvUtils      - Kernel Library with utils for kernel mode projects.
   |-> .\HideDriver    - Kernel Driver installed by Gui App. Performs main work.
   |-> .\HideDriverGui - Win32 Application used to run driver and communicate with it.
   |-> .\Utils         - Win32 Library with utils for user mode projects.
   |-> .\UtilsPortable - Directory with headers for user mode and kernel mode projects.
   |-> .\UtilsPortableUnitTest - Win32 Application with unit test for UtilsPortable.

Project Structure

Image 1

NT Function Call Scheme and Hook

The scheme below describes the normal call cycle of functions FindFirstFile(), FindNextFile() used to enumerate files and folders.

Image 2

The next scheme describes the situation when HideDriver is installed.

Image 3

So let's make a conclusion after all of this.

When hook is installed, you can:

  • Call original function or not
  • Change the results returned by the original function
  • Even if original function returned successful status, you can return an error

Function Index in SST

Let's look at the structures described in NT function call scheme.

[Code from the file src\HideDriver\ServiceTableDef.h]

C++
typedef struct _SYSTEM_SERVICE_TABLE
{
    PNTPROC ServiceTable;
    PDWORD  CounterTable;
    ULONG   ServiceLimit;
    PBYTE   ArgumentTable;
}
SYSTEM_SERVICE_TABLE ,
    * PSYSTEM_SERVICE_TABLE ,
    * * PPSYSTEM_SERVICE_TABLE ;

typedef struct _SERVICE_DESCRIPTOR_TABLE {
    SYSTEM_SERVICE_TABLE ntoskrnl;  //SST for ntoskrnl.exe
    SYSTEM_SERVICE_TABLE win32k;    //SST for win32k.sys
    SYSTEM_SERVICE_TABLE unused1;
    SYSTEM_SERVICE_TABLE unused2;
}
SERVICE_DESCRIPTOR_TABLE ,
    * PSERVICE_DESCRIPTOR_TABLE,
    * * PPSERVICE_DESCRIPTOR_TABLE ;

Where PNTPROC is defined as:

C++
typedef PVOID* PNTPROC;

So ServiceTable is a simple array of pointers to NtXXX functions.

Question is: how to find out what is the index of function NtQueryDirectoryFile in the ServiceTable array?

First Approach (wrong)

Driver stores many tables with all indexes of functions for all versions of Windows.

If we use these tables, we have to:

  • store all indexes for all versions of Windows and service packs for each function
  • update index tables for each new OS version or service pack

For some similar tasks such as offset in EPROCESS structure, there is no other way of finding the index in the structure.

But for SST, another way exists.

Second Approach (right)

To retrieve the index for all versions of Windows, we need to find some place in the system where it is always used.

Lets disable function ZwQueryDirectoryFile in WinDBG by executing u command: Image 4

As you can see, the underlined number is the index of ZwQueryDirectoryFile function in the ServiceTable array.

Disabling other ZwXXX functions, you can see that all of these functions look the same and start with: mov eax, SST_Index.

The code below retrieves this index:

[Code from file src\HideDriver\HookFactory.cpp]

C++
SSTHook CreateSSTHook(IN const PVOID pNewFuncPtr,IN PUNICODE_STRING function_name)
{
    ...

    PVOID pTrueFuncPtr_ZW=MmGetSystemRoutineAddress(function_name);

    if(pTrueFuncPtr_ZW == NULL)
        throw std::exception(__FUNCTION__"Can't get function address");

    // Skip command byte, move to index byte
    ULONG mFuncID = *(PULONG)((PUCHAR) pTrueFuncPtr_ZW + 1);

    ...
}

Change the Results Returned by the Original Function

After hook is installed and we receive NtQueryDirectorFile calls, we need to cut information about a file from the List to hide it.

Let's look at NtQueryDirectoryFile and NtQuerySystemInformation function definitions:

C++
NTSTATUS NtQuerySystemInformation(
    SYSTEM_INFORMATION_CLASS SystemInformationClass,
    PVOID SystemInformation,
    ULONG SystemInformationLength,
    PULONG ReturnLength
);

NTSTATUS NtQueryDirectoryFile(
    HANDLE  FileHandle,
    HANDLE  Event  OPTIONAL,
    PIO_APC_ROUTINE  ApcRoutine  OPTIONAL,
    PVOID  ApcContext  OPTIONAL,
    PIO_STATUS_BLOCK  IoStatusBlock,
    PVOID  FileInformation,
    ULONG  Length,
    FILE_INFORMATION_CLASS  FileInformationClass,
    BOOLEAN  ReturnSingleEntry,
    PUNICODE_STRING  FileName  OPTIONAL,
    BOOLEAN  RestartScan
    );

Here is one of the possible results returned by this function:

C++
typedef struct _FILE_FULL_DIRECTORY_INFORMATION {
    ULONG NextEntryOffset;
    ULONG Unknown;
    LARGE_INTEGER CreationTime;
    LARGE_INTEGER LastAccessTime;
    LARGE_INTEGER LastWriteTime;
    LARGE_INTEGER ChangeTime;
    LARGE_INTEGER EndOfFile;
    LARGE_INTEGER AllocationSize;
    ULONG FileAttributes;
    ULONG FileNameLength;
    ULONG EaInformationLength;
    WCHAR FileName[1];
} FILE_FULL_DIRECTORY_INFORMATION, *PFILE_FULL_DIRECTORY_INFORMATION;

// SystemProcessesAndThreadsInformation
typedef struct _SYSTEM_PROCESSES_INFORMATION {
    ULONG                       NextEntryDelta;
    ULONG                       ThreadCount;
    ULONG                       Reserved1[6];
    LARGE_INTEGER               CreateTime;
    LARGE_INTEGER               UserTime;
    LARGE_INTEGER               KernelTime;
    UNICODE_STRING              ProcessName;
    KPRIORITY                   BasePriority;
    ULONG                       ProcessId;
    ULONG                       InheritedFromProcessId;
    ULONG                       HandleCount;
    ULONG                       SessionId;
    ULONG                       Reserved2;
    VM_COUNTERS                 VmCounters;
#if (VER_PRODUCTBUILD >= 2195)
    IO_COUNTERS                 IoCounters;
#endif // (VER_PRODUCTBUILD >= 2195)
    SYSTEM_THREADS_INFORMATION  Threads[1];
} SYSTEM_PROCESSES_INFORMATION, *PSYSTEM_PROCESSES_INFORMATION;

These two functions returned information that look very similar.

That's why we created several utility functions which operate with these two structures. This utility is showed in the code below:

[Code from file src\UtilsPortable\ListUtils.h]

C++
#pragma once
#include "boost/function.hpp"
#include "boost/bind.hpp"

namespace utils
{

struct NtList
{
    size_t NextEntryOffset;
};

#define LastEntryOffset 0

inline bool IsEntryLast(NtList* pList)
{
    return (pList->NextEntryOffset == LastEntryOffset);
}

////////////////////////////////////// FOR EACH SECTION ///////////////////////////

inline NtList*
GetNextEntryPointer(NtList* list)
{
    if(list->NextEntryOffset == 0)
        throw std::exception("No more entries inside list");

    return (NtList*)((char*)list + list->NextEntryOffset);
}

template<class Visitor>
inline void
ListForEach( NtList* list, Visitor visitor )
{
    while( true )
    {
        visitor(list);

        if( IsEntryLast(list) )
            break;

        list = GetNextEntryPointer(list);
    }
}
///////////////////////////////// ENTRY COUNT SECTION /////////////////////////////

inline void
CountEntryVisitor(NtList* list,size_t* entryCount)
{
    *entryCount += 1;
}

inline size_t
GetEntryCount(NtList* list)
{
    size_t count = 0;
    ListForEach(list,boost::bind(&CountEntryVisitor,_1,&count));
    return count;
}

////////////////////////////////// LIST SIZE SECTION /////////////////////////////

inline void
SizeEntryVisitor(NtList* list,size_t* size)
{
    size += list->NextEntryOffset;
}
inline size_t
GetListSize(NtList* list)
{
    size_t size = 0;
    ListForEach(list,boost::bind(&SizeEntryVisitor,_1,&size));
    return size;
}

///////////////////////////////// CUT SECTION ////////////////////////////////////

inline void
CutNextEntryByFakeOffset( NtList* list )
{
    NtList* pNextEntry = GetNextEntryPointer(list);

    if( IsEntryLast(pNextEntry) )
        list->NextEntryOffset = LastEntryOffset;
    else
        list->NextEntryOffset = list->NextEntryOffset + pNextEntry->NextEntryOffset;
}

template<class Checker>
inline void
CutFromListByFakeOffset_IfImpl( NtList* list,
                                Checker checker )
{
    if( IsEntryLast(list) )
        return; // Last entry already dispatched

    while(true)
    {
        NtList* pNextEntry = GetNextEntryPointer(list);
        if( !checker(pNextEntry) )
            break;

        CutNextEntryByFakeOffset(list);

        if( IsEntryLast(list) )
            break;
    }
}

template<class Checker>
inline void
CutFromListByFakeOffset_If( NtList* list
                          , Checker checker )
{
    ListForEach( list, boost::bind(&CutFromListByFakeOffset_IfImpl<Checker>,_1,
                                   boost::ref(checker) ) );
}

}//namespace utils

Process Hiding

To hide processes, we need to cut the information about them from the list returned by NtQuerySystemInformation().

It's very easy using utils described above.

[Code from file src\UtilsPortable\IProcessChecker.h]

C++
struct NtQuerySysInfoParams
{
    SYSTEM_INFORMATION_CLASS SystemInformationClass;
    PVOID SystemInformation;
    ULONG SystemInformationLength;
    PULONG ReturnLength;
};

struct IProcessChecker
{
    virtual bool
    CheckProcess( wchar_t* imageName
                , size_t nameSize )=0;
};

[Code from file src\UtilsPortable\ProcessHideAlgorithm.h]

C++
inline bool
CheckProcEntry( utils::NtList* list,
                const NtQuerySysInfoParams& params,
                IProcessChecker* checker )
{
    SYSTEM_PROCESSES_INFORMATION* info =
        (SYSTEM_PROCESSES_INFORMATION*)list;

    return checker->CheckProcess(info->ProcessName.Buffer,
                                 info->ProcessName.Length/2);
}

inline NTSTATUS
HideProcessImpl( const NtQuerySysInfoParams& params,
                 IProcessChecker* checker )
{
    utils::NtList* pList =
        (utils::NtList*)params.SystemInformation;

    // First entry always exist
    // because first entry is idle process
    utils::CutFromListByFakeOffset_If(pList,
        boost::bind(&CheckProcEntry,_1,params,checker));

    return STATUS_SUCCESS;
}

File Hiding

To hide a file, we need to cut the information about it from the list returned by NtQueryDirectoryFile().

Hiding algorithm starts from NewNtQueryDirectoryFile() function which receives calls every time someone calls NtQueryDirectoryFile() function in the system.

NewNtQueryDirectoryFile() calls original function and processes returned data by passing it to the HideFile() function from UtilsPortable\HideAlgorithm.h.

[Code from file src\HookFile.cpp]

C++
NTSTATUS NewNtQueryDirectoryFile(IN HANDLE FileHandle,
                                 IN HANDLE Event OPTIONAL,
                                 IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
                                 IN PVOID ApcContext OPTIONAL,
                                 OUT PIO_STATUS_BLOCK IoStatusBlock,
                                 OUT PVOID FileInformation,
                                 IN ULONG FileInformationLength,
                                 IN FILE_INFORMATION_CLASS FileInformationClass,
                                 IN BOOLEAN ReturnSingleEntry,
                                 IN PUNICODE_STRING FileName OPTIONAL,
                                 IN BOOLEAN RestartScan)
{
    using namespace HideAlgorithm;

    NtQueryDirParams params = {FileHandle,Event,ApcRoutine,ApcContext,
        IoStatusBlock,FileInformation,FileInformationLength,
        FileInformationClass,ReturnSingleEntry,FileName,RestartScan};

    NTSTATUS status = OriginalHandlerWrapper(params);
    if( !NT_SUCCESS(status) )
        return status;

    params.RestartScan = FALSE; // Search must be continued
    // Save original handler to request more data in future
    HideParams hideParams = {params,gFileChecker,&OriginalHandlerWrapper};

    switch(FileInformationClass)
    {
    case FileDirectoryInformation:
        return HideFile<FILE_DIRECTORY_INFORMATION>(hideParams);
    case FileFullDirectoryInformation:
        return HideFile<FILE_FULL_DIRECTORY_INFORMATION>(hideParams);
    case FileBothDirectoryInformation:
        return HideFile<FILE_BOTH_DIRECTORY_INFORMATION>(hideParams);
    case FileNamesInformation:
        return HideFile<FILE_NAMES_INFORMATION>(hideParams);
    case FileIdBothDirectoryInformation: // Used by Vista and later explorer
        return HideFile<FILE_ID_BOTH_DIR_INFORMATION>(hideParams);
    case FileIdFullDirectoryInformation: // Used by Vista and later explorer
        return HideFile<FILE_ID_FULL_DIR_INFORMATION>(hideParams);
    default:return status;
    }
}

When we know what type of information is requested, we can cut the process from the list using ListUtils from UtilsPortable.

The first step is to establish correspondence between callers and algorithm.

It is done by specifying virtual function and parameters in the code below.

[Code from file src\UtilsPortable\IFileChecker.h]

C++
struct NtQueryDirParams
{
    HANDLE FileHandle;
    HANDLE Event;
    PIO_APC_ROUTINE ApcRoutine;
    PVOID ApcContext;
    PIO_STATUS_BLOCK IoStatusBlock;
    PVOID FileInformation;
    ULONG FileInformationLength;
    FILE_INFORMATION_CLASS FileInformationClass;
    BOOLEAN ReturnSingleEntry;
    PUNICODE_STRING FileName;
    BOOLEAN RestartScan;
};

struct IFileChecker
{
    virtual bool
    CheckFile( wchar_t* fileName
             , size_t nameSize
             , const NtQueryDirParams& params )=0;
};

Original handler needs to request more data if request is finished with the flag ReturnSingleEntry == TRUE.

[Code from file src\UtilsPortable\FileHideAlgorithm.h]

C++
typedef NTSTATUS(*OriginalHandlerWrapperPtr)(const NtQueryDirParams& params);

struct HideParams
{
    const NtQueryDirParams& callParams;
    IFileChecker* checker;
    OriginalHandlerWrapperPtr wrapperPtr;
};

template<class InfoType>
inline bool
CheckFileEntry( utils::NtList* list,
                const HideParams& params )
{
    InfoType* info = (InfoType*)list;
    return params.checker->CheckFile(info->FileName,
                                     info->FileNameLength/2,
                                     params.callParams);
}

The next step is to protect top level functions from exceptions that can arise during the hiding of processes.

Unhandled exception will cause the system crash.

C++
template<class InfoType>
inline NTSTATUS
HideFile( const HideParams& params )
{
    try
    {
        return HideFileImpl<InfoType>(params);
    }
    catch(const std::exception& ex)
    {
#ifdef KdPrint // For use in user mode environment
        KdPrint( (__FUNCTION__" std::exception: %s\n",ex.what()) );
#endif
    }
    return STATUS_SUCCESS;
}

After this, we need to process the first entry separately from the next entries.

We should do it because we need to shift the buffer to hide the first entry but to hide the next entries, we need just to set fake offset.

C++
template<class InfoType>
inline NTSTATUS
HideFileImpl( const HideParams& params )
{
    NTSTATUS status = FirstEntryProcessor<InfoType>(params);
    if( !NT_SUCCESS(status) )
        return status;

    status = NextEntryProcessor<InfoType>(params);
    if( !NT_SUCCESS(status) )
        return status;

    return STATUS_SUCCESS;
}

In the code below, you can see the algorithm of hiding of the first entry from the list returned by NtQueryDirectoryFile().

C++
template<class InfoType>
inline NTSTATUS
FirstEntryProcessor( const HideParams& params )
{
    utils::NtList* pList =
        (utils::NtList*)params.callParams.FileInformation;

    utils::NtList* pCurEntry = pList;

    while(true)
    {
        if(!CheckFileEntry<InfoType>(pCurEntry,params))
        {
            if(pList == pCurEntry)
                break; // Nothing to hide

            ShiftBuffer(pList,pCurEntry);
            break; // First entry hiding complete
        }

        // This entry needs to be hidden
        if( utils::IsEntryLast(pCurEntry) == false )
        {
            // Move to next entry to check.
            // This is needed to shift buffer only once.
            pCurEntry = utils::GetNextEntryPointer(pCurEntry);
        }
        else
        {
            // Reached last entry
            // This mean that all data needs to be hidden

            // Try to request more data
            NTSTATUS status = params.wrapperPtr(params.callParams);
            if( !NT_SUCCESS(status) )
                return status;

            // Move to begin and resume checking
            pCurEntry = pList;
        }
    }
    return STATUS_SUCCESS;
}

Algorithm of hiding the next entries is the same as the algorithm of process hiding.

C++
template<class InfoType>
inline NTSTATUS
NextEntryProcessor( const HideParams& params )
{
    utils::NtList* pList =
        (utils::NtList*)params.callParams.FileInformation;

    if( utils::IsEntryLast(pList) )
        return STATUS_SUCCESS;

    utils::CutFromListByFakeOffset_If(pList,
        boost::bind(&CheckFileEntry<InfoType>,_1,params));

    return STATUS_SUCCESS;
}

GUI Application

The GUI application enables a user to choose processes or/and files for hiding in an easy way. You can find an example of such application in the attached file. It was developed using MFC. It should be also mentioned that you can create your own GUI application following the rules in the Format of communication section.

Communication with HideDriver

IOCTLs and the DeviceIoCotrol() routine should be used for communication between the user-mode application and driver.

You can find additional information about such communication implementations in the article: "Driver Development Part 2: Introduction to Implementing IOCTLs".

Wrapper class DriverWork was developed for communication with drivers. This class wraps DeviceIoControl() function and other service functions.

Here is an example of using of DriverWork class to send IOCTL to HideDriver:

[Code from file src\HideDriverGUI\FileForm.cpp]

C++
void FileForm::OnMenuAdd()
{
...
    try
    {
        utils::DriverWork::Exchange(
            _T("\\\\.\\HideDriver"),
            IOCTL_ADD_FILE_HIDE_RULE,
            str,                     // Input string
            size,                    // Size of input string
            (PWCHAR)ret_data,        // Output string
            sizeof(ret_data),        // Size of buffer for output string
            &BytesReturned);
    }
    catch(const std::exception& ex)
    {
        ::MessageBoxA(this->GetSafeHwnd(),ex.what(),"Error",MB_ICONERROR|MB_OK);
        return;
    }
...
}

Below you can see the code of DriverWork::Exchange() function:

[Code from file src\Utils\DriverWork.cpp]

C++
void DriverWork::Exchange(LPCTSTR driverName,
                          unsigned long ioctlCode,
                          PWCHAR pInStr,
                          DWORD inStr_size,
                          PWCHAR pOutStr,
                          DWORD outStr_size,
                          PDWORD bytesReturned)
{
    HANDLE hHandle =
        CreateFile( driverName,
                    GENERIC_READ | GENERIC_WRITE,
                    FILE_SHARE_READ | FILE_SHARE_WRITE,
                    NULL,
                    OPEN_EXISTING,
                    FILE_ATTRIBUTE_NORMAL,
                    NULL );
    if(hHandle == INVALID_HANDLE_VALUE)
        throw std::runtime_error("Can't get handle to driver: " + GetLastErrorStr());

    HandleGuard guard1(hHandle);

    if( !DeviceIoControl(hHandle,
                         ioctlCode,
                         pInStr, inStr_size,  // Input
                         pOutStr, outStr_size,// Output
                         bytesReturned,
                         NULL) )
    {
        throw std::runtime_error("Driver communication error: " + GetLastErrorStr());
    }
}

Format of Communication

All input strings (parameters) are UNICODE strings. If an error occurs, driver returns ASCII string with the error description.

All HideDriver IOCTLs (except CLEAR IOCTLs) work with HIDE_RULE. HIDE_RULE supports the following options:

  • Hide from the list of processes, it means that only selected processes should not see this process or file
  • Hide from the list of users, it means that only selected users should not see this process or file

These options can be used together.

Format of HIDE_RULE string must be: process(file)_name_to_hide;access_user_name;access_process_name.

Where:

  • process(file)_name_to_hide - process name (file path) to hide
  • access_user_name - name of user, who should not see this process(file)
  • access_process_name - name of process, which should not see this process(file)

To insert several processes or user names, separate them by ',' character.

Example:

process_name_to_hide;user_name1,user_name2;process_name1,process_name2

All names support wildcards:

"*" - Matches all characters

"?" - Matches any single character

Some Examples

*;*;* - Hide all processes from all users and processes. Just for fun.

System;*;* - Hide process system from all users and processes.

er*;*;* - Hide all processes whose name starts with "er" characters from everybody.

System;*;Rob - Hide process "System" from User: Rob

HideDriver IOCTLs

All IOCTLs that can process HideDriver are located in file Ioctl.h.

[Code from file src\Common\Ioctl.h]

C++
/*-----------------------------------------------------------------------*/
/*                            Process hide IOCTLs                        */

#define IOCTL_ADD_PROCESS_HIDE_RULE CTL_CODE( \
    FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)

/*
This IOCTL used to add process hide rule to hide list.
Input string must be HIDE_RULE.
*/

#define IOCTL_DEL_PROCESS_HIDE_RULE CTL_CODE( \
    FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS)

/*
This IOCTL used to delete process hide rule from hide list.
Input string must be HIDE_RULE.
*/

#define IOCTL_CLEAR_PROCESS_HIDE_RULES CTL_CODE( \
    FILE_DEVICE_UNKNOWN, 0x803, METHOD_BUFFERED, FILE_ANY_ACCESS)

/*
This IOCTL used to clear process hide list.
Input string should be empty.
*/

#define IOCTL_QUERY_PROCESS_HIDE_RULES CTL_CODE( \
    FILE_DEVICE_UNKNOWN, 0x804, METHOD_BUFFERED, FILE_ANY_ACCESS)

/*
This IOCTL used to get process hide rule list.
Input string should be empty.

Format of output string:
HIDE_RULEs separated by '\n' character
and ends with '\0' character.

Example:
    *;*;*\nSystem;*;*\0 - two hide rules (*;*;*) and (System;*;*)
*/
/*-----------------------------------------------------------------------*/


/*-----------------------------------------------------------------------*/
/*                                File hide IOCTLs                       */

#define IOCTL_ADD_FILE_HIDE_RULE CTL_CODE( \
    FILE_DEVICE_UNKNOWN, 0x901, METHOD_BUFFERED, FILE_ANY_ACCESS)

/*
This IOCTL used to add file hide rule to hide list.
Input string must be HIDE_RULE.
*/

#define IOCTL_DEL_FILE_HIDE_RULE CTL_CODE( \
    FILE_DEVICE_UNKNOWN, 0x902, METHOD_BUFFERED, FILE_ANY_ACCESS)

/*
This IOCTL used to delete file hide rule from hide list.
Input string must be HIDE_RULE.
*/

#define IOCTL_CLEAR_FILE_HIDE_RULES CTL_CODE( \
    FILE_DEVICE_UNKNOWN, 0x903, METHOD_BUFFERED, FILE_ANY_ACCESS)

/*
This IOCTL used to clear file hide list.
Input string should be empty.
*/

#define IOCTL_QUERY_FILE_HIDE_RULES CTL_CODE( \
    FILE_DEVICE_UNKNOWN, 0x904, METHOD_BUFFERED, FILE_ANY_ACCESS)

/*
This IOCTL used to get file hide rule list.
Input string should be empty.

Format of output string same as in IOCTL_QUERY_PROCESS_HIDE_RULES.
*/

/*-----------------------------------------------------------------------*/

How to Build this Solution

  1. Install Windows Driver Developer Kit 2003
    http://www.microsoft.com/whdc/devtools/ddk/default.mspx
  2. Set global environment variable "BASEDIR" to path of installed DDK.
    Computer -> Properties -> Advanced -> Environment variables ->System Variables -> New
    Like this: BASEDIR -> c:\winddk\3790
    (You have to restart your computer after this.)
  3. Download and install boost (tested with 1.38 version)
    http://www.boost.org/users/download/
  4. Set global environment variable "BOOST" to path of installed boost.

If you choose Visual Studio 2003, then you can simply open HideDriver_vs7.sln and build all.

If you choose Visual Studio 2005 or 2008, you will need files from Visual Studio 2003 to do it. Copy files located here: C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\crt\src\intel\st_lib\ (But surely this path depends on the Visual Studio install path) to the project folder: HideDriver_source\src\drvCppLib\lib_copy\.

After this, you can use the file "HideDriver_vs8.sln" in Visual Studio 2005 and file "HideDriver_vs9.sln" in Visual Studio 2008.

Additional Information About the Build

You will also need the file from $VCInstllPath\crt\src\intel\st_lib\ - Single Threaded C++ Run-Time is located in this directory. Any other version of run-time library can't be used for the driver building.

This method was originally discovered by Gary Nebbett in the article: "Enabling C++ exceptions and RTTI in kernel-mode code". It's like nobody has invented something better since that time.

Supported Windows Version and Testing

All tests were performed with Driver Verifier Enabled with all options ON except low resource simulation.

  • Windows 2000, SP4
  • Windows XP, SP3
  • Windows 2003 Server, R2
  • Windows Vista, SP0,SP1
  • Windows 2008 Server
  • Windows 7 Beta, Build 7000

All versions are x86; x64 windows version is not supported because of PatchGuard.

Bibliography

  • Mark Russinovich, David Solomon. Microsoft Windows Internals ((Fourth Edition) ed.)
  • Greg Hoglund, Jamie Butler. Rootkits: Subverting the Windows Kernel
  • Gary Nebbett. Windows NT/2000 Native API Reference
  • Sven B. Schreiber. Undocumented Windows 2000 Secrets - A Programmer's Cookbook

Useful Links

We recommend the article "Driver Development" by Toby Opferman:

It would also be useful to look through other articles (Part 2, Part 3, Part 4, Part 5, Part 6,Part 7).

Also, a lot of knowledge that helped us to develop this project was obtained here:

  1. Hooking Windows NT System Services
  2. http://www.osronline.com/
  3. http://www.rootkit.com/
  4. http://msdn.microsoft.com/
  5. http://www.boost.org/

    For Russian speaking readers, we recommend these sources:

  6. http://wasm.ru/
  7. http://wasm.ru/article.php?article=apihook_3
  8. http://wasm.ru/article.php?article=hidingnt

Additional Info

Additional information can be found at the Apriorit Education page.

History

21/01/2009 (by Ivan Romanenko & Sergey Popenko)

  • Initial version of this article

12/02/2009 (by Ivan Romanenko)

  • Added possibility to build solution in VS2003, VS2005, VS2008
  • Added the topic "How to build this solution"

29/04/2009 (by Ivan Romanenko)

  • Project
    • Bugfix
      • Wrong hiding of the first file in the directory
      • Wrong synchronization that locks PC
      • Allocation memory on height IRQL
      • Bug with build using DDK2003
      • Bug in filtering by user
      • Removed using SingleMode and changed HookMng
    • New
      • Using Wildmat instead of own wild cards implementation
      • Unit tests
      • Using BOOST and STLPort
      • Changes in project structure, utils and tests are added
  • Article
    • Bugfix
      • Wrong language in Ioctl.h
      • Fixed spelling error at code comments
    • New
      • 11 new topics added

13/08/2009 (by Ivan Romanenko)

  • Project 
    • Bugfix 
      • Fixed: synchronization bug in CReadWriteSection class 
      • Fixed: wrong name converting for network shares 
      • Added: heuristics for offset receiving. Absolute offsets are not used any more, except for case when DriverVerifier substitutes ntoskrnl code under Vista.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)