Hiding Directories in Windows 95/98/ME

You may need to protect specific Windows 9x directories from prying eyes, or you might want to limit directory access for multiple users of a single PC. Vinoj presents some utilities to set and manage the protection settings of your directories.


December 01, 2001
URL:http://drdobbs.com/hiding-directories-in-windows-9598me/184416380

December 2001/Hiding Directories in Windows 95/98/ME


Sometimes we need to protect specific directories in Windows 95/98/ME from prying eyes or limit access for multiple users of a single PC. The utilities that I present in this article will help you protect your data using a device driver, a logon application, and an application to manage the protection settings of your directories. The device driver and the logon application need to be small in size, so they’re written in Assembly and in C, but the size of the permission-setting application was not critical, so I’ve used MFC. I will concentrate on logon application and the device driver in this article. I won’t delve into the MFC app, but the source code is available online.

Device Driver Basics

A Virtual Device Driver (VxD), allows you to extend Windows to any extent you need. The device driver called IFSMGR (Installable File System Manager) is responsible for handling all the file operations of Windows. You might compare it to the Int 21h interface of DOS, which handles all the basic file system operations. I’ll extend the functions of this VxD, trapping the file operations and passing on the results to the original driver. This way, I can monitor and trap on the file access and protect the files that are to be protected. The IFSMGR acts on the storage device virtually. Even the scan disk utility of Windows relies on
IFSMGR for scanning the disks at the low level. This is a very powerful utility of this VxD.

Utility Overview

The application that sets the permissions is named “Mother Nature’s Protector 2001,” and it has a Windows Explorer style interface shown in Figure 1. The left side lists the users who can be logged in, and the right side lists the directories they are restricted from using. It has an Administrator and Guest by default. The Administrator has access to all the directories and the Guest has the permission only to certain directories. In the current version of this application, I have made it possible to alter the values of both defaults also. You can set the permission to the Administrator or even delete the default users, but I don’t recommend doing so; it will cause the device driver to hang.

The interface and menu items are very simple to use. The users and directories setting are separated in the menu. The application uses the file called Protector.Dat for all the permissions and passwords. This has to be in the same directory as the application. Once the logon attributes are set using this utility,

the next part is the actual logon application, which is based on the System Tray. If the computer icon in the tray is selected, the logon displays a simple dialog box to enter a member and password, shown in Figure 2. The initial logon member is the Guest by default. The password is set using the permission setting application. Note that once the logon loads, there is no way to unload it except through a boot, also the changes made after the logon utility is loaded using the permission application will not be reflected in the logon until the next boot. The logon application can be put in the Startup or in the Run key of the Registry manually, and this utility does not do anything like that.

The device driver communicates with the logon application and logs in the user if it can, otherwise it denies a login.

Hooking into IFSMGR

The device driver portion utilizes the IFSMGR and hooks onto it to monitor the file system. It uses a service called IFSMgr_InstallFileSystemApiHook to hook onto the service and extended the functionality of this VxD with our own. In example 1, the device driver sets its own hook procedure. In this example, we are monitoring the Open, Find File, Delete, Rename, and so on, of the file operations. In these operations, we do the comparing of the paths that come in the pir->ir_ppath->pp_elements variable member. The pir is the i/o request packet sent in through the VxD, and it is a pointer to this packet. After doing the comparison, we can either deny the request by setting the pir->ir_error to 5 to signal Access Denied or we can pass on the hook to the previous hook as it is done in the line, iRet=(*(*ppPrevHook))(pfn, nFunction, nDrive, nResources, Cp, pir); which passes the control to the next hook in the chain. The opening and the reading of the Protector.dat file is handled by the code in example 2. The path of the protector.dat is also given by the login application, which does so in the lpvInBuffer where it is being transferred from Ring 3 memory of the stack to the Ring 0 memory by the strcpy function. The strcpy is not the library available in the SDK but one that I wrote in Assembly. But it is not possible to communicate from Ring 3 application to Ring 0 like this, meaning a Ring 3 application cannot read the memory present in Ring 0 because of permission restrictions. Ring 0 is the highest member that can access all of the memory of the system. Ring 3 is the lowest member, which has limitations even for accessing ports. Ring 0 is where the Kernel is present and all the virtual device drivers run in this memory. Ring 3 is where all the applications run including the DLLs, OCXs, and so on. Ring 0 is where all serious things happen, including termination of misbehaving applications, memory allocation, etc. The _HeapAllocate function is used to allocate memory in Ring 0 memory for making a copy of Protector.dat file. Since it is an assembly interface, we have to write a mini assembly routine to speed up the process, which is done using HeapAllocateAsm function. This makes a call into the VMM (The kernel). The
protector.dat files path comes in the following pointer lpDIOCParms->lpvInBuffer, which is for inside communications. The return values, if any, are passed in the pointer, which is made available using lpDIOCParms->lpvOutBuffer. Remember this must not return a Ring 0 pointer, but just place a return value, if more than one, to signal some event.

The users are logged in using the routine in example 3. See how the user name and the password are passed on as two separate pointers, which are extracted by a type cast as in,

    User = (char *) pdw[0];
    Pass = (char *) pdw[1];

The protector.dat is terminated by 0xFFFF even in the permission application. It hunts for the user in the buffer, and if it finds the user it checks for the password that immediately follows the username. The entire structure of the protector.dat is as follows:

ASCIIZ UserName = Null terminated
                  user name
WORD   Password = Simple Checksum
                  of the Password
ASCIIZ Path[n]  = Null terminated
                  strings of paths
                  terminated by an
                  extra 0 for end
                  of user
Entire file terminated by a 0xFFFF.

If the login succeeds, the application will copy the user’s name to the Ring 0 string array.

Ring 3 Code for Login

The code in example 4 is responsible for loading the device driver and logging on as Guest. The communication with the VxD happens using the API called DeviceIoControl. The second parameter is the control code sent to the VxD. The FILE_MEM_OPEN control code is signaling the loading of the protector.dat file and the memory allocation, CHANGE_USER is responsible for login another user, INSTALL_HOOK is responsible for hooking onto the IFSMGR for directory access checking. Other code portions are basically application oriented and based on the user interface. You can download the entire source files and use the routines that are of interest to you.

About the Author

Vinoj Kumar is a systems programmer with ten years of computer programming experience. He is the author of Classic Utilities Using Assembly Language, and has programmed Z80 CPU assembly and 80x86 assembly. He runs a company called Mother Nature Software and likes to research robotics programming. Vinoj can be reached at [email protected].

December 2001/Hiding Directories in Windows 95/98/ME

Figure 1: The Administration app restricts access to the selected directories

December 2001/Hiding Directories in Windows 95/98/ME

Figure 2: The logon interface

December 2001/Hiding Directories in Windows 95/98/ME

Example 1:
Hooking onto IFSMGR to monitor the file system

int     _cdecl OurFileHook(pIFSFunc pfn, int nFunction, int nDrive, int nResources, int Cp, pioreq
         pir)
{
        int iRet;
        unsigned long fHan;
        DWORD pAction;
        DWORD iLen;

        iRet=0;
        switch(nFunction){
                case IFSFN_OPEN:
                case IFSFN_FINDOPEN:
                case IFSFN_RENAME:
                case IFSFN_DELETE:
                case IFSFN_DIR:
                        if((nDrive & 0xFF) != 0xFF){
                                FileNm[0]= nDrive + '@';
                                FileNm[1]=':';
                                iLen=2;
                                iLen+=UniToBCSPath(&FileNm[2], 
pir->ir_ppath->pp_elements, MAX_PATH, BCS_OEM); } else{ iLen=FormNetPath(FileNm, pir); } if(ComparePath(FileNm) == FALSE){ iRet=(*(*ppPrevHook))(pfn, nFunction, nDrive,
nResources, Cp, pir); return iRet; //Do the normal work } else{ iRet = 5; pir->ir_error = 5; //If the path is protected
//return "access denied" return iRet; } } iRet=(*(*ppPrevHook))(pfn, nFunction, nDrive, nResources, Cp, pir); return iRet; }
December 2001/Hiding Directories in Windows 95/98/ME

Example 2:
Opening and reading of the Protector.dat file

DWORD _stdcall OpenAndReadFile(DWORD dwDDB, DWORD hDevice, LPDIOC lpDIOCParms)
{
    PDWORD pdw;
    int pAction=1;   //Fail/Open
    DWORD bRead;
    int Error;
    unsigned char *ProtFileTmp;
    pdw = (PDWORD)lpDIOCParms->lpvOutBuffer;
    ProtFileTmp = (unsigned char *) lpDIOCParms->lpvInBuffer;
        strcpy(ProtectorFileName, ProtFileTmp);
    if(R0_OpenCreateFile(FALSE, 0x0, 0, pAction, R0_NO_CACHE, ProtectorFileName,
                       &pHandle, &pAction))
        return -1;
    if(R0_GetFileSize(pHandle, &bRead))
        return -1;
    if(!(UsersAndPaths = HeapAllocateAsm(bRead+2, HEAPZEROINIT))){
        R0_CloseFile(pHandle);
        return -1;
    }
    if(R0_ReadFile(FALSE, pHandle, bRead, 0, UsersAndPaths, &bRead)){
        R0_CloseFile(pHandle);
        HeapFreeAsm(UsersAndPaths, 0);
        return -1;
    }
    if(R0_CloseFile(pHandle))
        return -1;
    *(WORD *) (UsersAndPaths+bRead) = 0xFFFF;
    Out_Debug_String("PROTECTOR: Memory allocated & File Opened!\n\r");
    return(NO_ERROR);
}
December 2001/Hiding Directories in Windows 95/98/ME

Example 3: The user login routine

DWORD ChangeUser(DWORD dwDDB, DWORD hDevice, LPDIOC lpDIOCParms)
{
    PDWORD pdw;
    unsigned char *User, *Pass, *BufferTmp=UsersAndPaths;
    unsigned char UserTmp[20], Password[20];
    WORD Checksum=0, i, j;
    BOOL Success = FALSE;
    pdw = (PDWORD)lpDIOCParms->lpvInBuffer;
    User = (char *) pdw[0];
    Pass = (char *) pdw[1];
    strcpy(UserTmp, User);
    strcpy(Password, Pass);
    for(i=0; i<strlen(Password); i++){
        Checksum += Password[i];
    }
    while(*BufferTmp != 0xFF){
        j=strlen(BufferTmp);
        if(j == strlen(UserTmp)){
            for(i=0;i<j;i++){
                if((BufferTmp[i] & 0xDF) != (UserTmp[i] & 0xDF))
                    break;
            }
            if(i==j){
                if(*(WORD *)(BufferTmp+j+1) == Checksum)
                    Success = TRUE;
                else
                    Success = FALSE;
                break;
            }
        }
        else{
            BufferTmp += j + 3;   //Skip 0 & password
            while(*(WORD *)(BufferTmp++))    //Search for word 0
                ;
            BufferTmp++;
        }
    }
    if(Success){
        UserPathPtr = BufferTmp+j+3;
        strcpy(LoggedUser, UserTmp);
        return 0;
    }
    else
        return -1;
}
December 2001/Hiding Directories in Windows 95/98/ME

Example 4: Loading the device driver and logging on as Guest

BOOL LoadTheVxD(void)
{
    DWORD       dwErrorCode;
    char      *UserInfo[2] = {{"Guest"}, {"Test"}};
        char cPath[MAX_PATH], *cPathTmp;
        strcpy(cPath, GetCommandLine());
        cPathTmp = cPath;
        cPathTmp++;
        for(i=strlen(cPathTmp); cPathTmp[i] != '\\'; i--)
                ;
        cPathTmp[++i]=0;
        strcat(cPathTmp, "Protector.dat");
        strcpy(cPath, cPathTmp);

    // Dynamically load and prepare to call CVXDSAMP
    // The CREATE_NEW flag is not necessary
    hCVxD = CreateFile("\\\\.\\PROTECT.VXD", 0,0,0,
                        CREATE_NEW, FILE_FLAG_DELETE_ON_CLOSE, 0);

    if ( hCVxD == INVALID_HANDLE_VALUE )
    {
        dwErrorCode = GetLastError();
        if ( dwErrorCode == ERROR_NOT_SUPPORTED )
        {
            MessageBox(NULL, "Unable to open VxD,\nDevice does not support 
DeviceIOCTL", "Protector Login", MB_OK); } else { MessageBox(NULL,"Unable to open VxD\n", "Protector Login", MB_OK); } return FALSE; } else { if(!DeviceIoControl(hCVxD, FILE_MEM_OPEN, (LPVOID)cPath, 4, (LPVOID)RetInfo, sizeof(RetInfo), &cbBytesReturned, NULL)){ MessageBox(NULL, "Unable to open file Protector.dat!",
"Protector Login", MB_OK); CloseHandle(hCVxD); return FALSE; } if(!DeviceIoControl(hCVxD, CHANGE_USER, (LPVOID)UserInfo, sizeof(UserInfo), (LPVOID)RetInfo, sizeof(RetInfo), &cbBytesReturned, NULL)){ MessageBox(NULL, "Unable to logon the user!", "Protector
Login", MB_OK); CloseHandle(hCVxD); return FALSE; } DeviceIoControl(hCVxD, INSTALL_HOOK, (LPVOID)NULL, 0, (LPVOID)RetInfo, sizeof(RetInfo), &cbBytesReturned, NULL); } return(TRUE); }

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.