Building a Deletion and Access Control Manager

.

Environment: VC++, DDK

Introduction

Deleting files can be both a heaven and a hell. Heaven is when you really need some files deleted to gather some extra space or to clean up the system’s disks and you delete them. Hell is when files get deleted because of unforseen instances, such as virus activity.

Some security measures are needed to prevent a hell activity from occurring in the system. Unfortunately, Windows 95 breeds do not provide security for such activities. Hence, if important files, such as source codes, documents, spreadsheets, and so forth — that could cost a fortune to replace — get deleted, we can do nothing but be left in dismay. Thus, the system needs to be protected from such hell activities.

We can cherish the files in specific folders and fortunately it is not too difficult to achieve that. All the file system activities pass through a device driver that can be monitored. It is called IFSMGR.VXD (Installable File System Manager). All file monitoring programs hook onto this VxD when it runs, to monitor file activities like Encryption, Virus Sentry, and so on. This Virtual Device Driver is responsible for all the file activities that happen in the system and added to it, it does-low level disk functions, which the Scandisk utility relies on!

Utility Overview

The application that sets the permissions is named “Deletion and Access Control Manager” and it has a Windows Explorer-style interface, as 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 Administrator and Guest access by default. The Administrator has access to all the directories and the Guest has permission only to certain directories. In the current version of this application, I have made it possible to also alter the values of both defaults. 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 with a left mouse click, the logon displays a simple dialog box to enter a member name and password. The initial logon member is the Guest by default. The password is set using the permission-setting application. Any changes made after the logon utility is loaded using the permission application will not be reflected in the logon until the next loading. The device driver communicates with the logon application and logs in the user if it can; otherwise, it denies a login.

Then comes Deletion Protected Paths in the tree view, which is the entry showing all the paths that are protected from deletion. If any permanent deletion happens in one of the folders mentioned in the list, the login application displays a dialog box enquiring for a password for the specific folder. This way, all the files in the folders listed are protected. Remember that Access restriction has the higher hierarchy over deletion control; this means that if you list a directory in access protection, and then if you list the same directory for deletion also, the folder is access denied. So you would not access the folders if both attributes are given for a path. All the file operations, such as open file, rename, delete, and so forth, have their access blocked, whereas only deletion is protected in the deletion protection.

The login application is set up by the Setup.exe application, to be started at boot time itself if you have selected to do so. Otherwise, you should manually select the login application from the Start menu. However, once you set up for boot-time loading, which is safer, you can rest assured that no files in the specified folder are deleted. The important files range from simple MPEG 3 music files to important documents and spreadsheets, which can be easily protected using this application. However, it works only in Windows 95 breeds. And one more important thing that I would like to mention is that if you access deny the Windows folder, you will have problems loading the Registry, to simple ding-ding sounds, and will cause a system crash very soon. So, avoid the Windows folder in this application except that you can deletion-control it.

The important extensions can only be protected from deletion. This can be configured using the Deletion menu. This means that the temporary file creation and deletion will not cause a trap while deletion controlling a folder. You can also add the recycle bins to the deletion control, which makes it safe while the bin is being flushed because it causes a trap. However, the main idea for this utility is to control any unknown deletion in the system and not be a complete deletion control system.

The Login application has two dialog boxes, one for logging in the user and other for deleted files queueing for password enquiry. I have handled one in a thread, which is the user logging process. Thus, two threads will be running in the logging in application.

The password for activating the applications is “admin”. So, for starting the permission-setting application, or for unloading or uninstalling the application, it is the same. To install the application, simply run Setup.exe.

Programming

The Ring 3 login application and the device driver communicate between themselves, using a PostMessage service available in Windows 95 — which is _SHELL_PostMessage, used for communicating from the driver to the Ring 3 application. This service posts a message to our login window while a file is deleted; this handles the request by issuing a DeviceIoControl API to get the file name. This way, we communicate between the device driver and the login application. The VxD’s hook procedure looks like the following:

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_RENAME:
        case IFSFN_DIR:
          if(AlreadyInside==1)
            break;
          if(pir->ir_options & OPEN_FLAGS_REOPEN)
            break;
          AlreadyInside = 1;

          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);
             AlreadyInside=0;
              return iRet;   //Do the normal work
          }
          else{

             iRet = 5;
             pir->ir_error = 5; //If path protected "access denied"
             AlreadyInside=0;
             return iRet;
          }
          AlreadyInside=0;
          break;
        case IFSFN_DELETE:
          if(AlreadyInside==1)
             break;
          AlreadyInside = 1;
          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) == TRUE){
             iRet = 5;
             pir->ir_error = 5; //If path protected "access denied"
             AlreadyInside = 0;
             return iRet;
          }
          if(CompareDeletePath(FileNm)==FALSE){
            iRet=(*(*ppPrevHook))(pfn, nFunction, nDrive, nResources, \
                Cp, pir);
             AlreadyInside = 0;
             return iRet;   //Do the normal work
          }
          else{
             if(((DWORD)DeleteFreeBuffer - (DWORD)DeleteFileBuffer)> \
                MIN_LIMIT){
            //If buffer fill exceeds minimum limit, free the memory
                if(DeleteFilesProcessed == DeleteFiles){
//If all the files have been processed by Ring 3, just set the
//pointer to the start.
                   DeleteFilePtr = DeleteFreeBuffer = \
                           DeleteFileBuffer;
                }
                else{  //Else move the required buffer to the top
                   int Tmp = (int)DeleteFreeBuffer - \
                           (int)DeleteFilePtr;
                   unsigned char *TmpPtr, *TmpPtr1;
                   for(TmpPtr=DeleteFileBuffer, TmpPtr1= \
                           DeleteFilePtr; Tmp>0; Tmp--){
                      *(TmpPtr++) = *(TmpPtr1++);
                   }
//Move the memory fully to the top of buffer
                   DeleteFreeBuffer -= (DeleteFilePtr - \
                        DeleteFileBuffer);
                   DeleteFilePtr = DeleteFileBuffer;
                }
             }
             strcpy(DeleteFreeBuffer, FileNm);
             DeleteFreeBuffer += strlen(FileNm) + 1;
             DeleteFiles++;
             PostMessageAsm(hWnd, FILE_BEING_DELETED, 0, 0, NULL, 0);
             iRet = 0;
             pir->ir_error = 0;       //If the path is deletion
             \\protected return false success
             AlreadyInside = 0;
             return iRet;
          }
          AlreadyInside = 0;
          break;
    }
    iRet=(*(*ppPrevHook))(pfn, nFunction, nDrive, nResources, Cp, pir);
    return iRet;
}

The files that are being deleted are stored in a buffer and the caller is returned with the mark of success. This way we simply trick the system into thinking that the deletion operation was successful. Then, we call the login application by sending a message to it. The login application handles this message and issues a DeviceIoControl API to get the file name that is to be deleted. The full path is passed on to the login application. The buffer is adjusted if it overflows the memory buffer; then this process continues until all the messages have been processed by the Ring 3 login application. The login application displays a dialog box for the deleted files, one by one, and it requires a password. If the correct password for the folder is given, it then deletes the file. This is done by first turning off the driver and calling the DeleteFile API. This is because we will cause a deadlock if the driver is again sent a message to delete the same file.

The above mentioned process is described in the following piece of code:

  case FILE_BEING_DELETED:{
      PUCHAR ExtsPtrTmp = ExtsPtr;
      if(!DeviceIoControl(hCVxD, FILE_DELETE_EXTRACT,
                          (LPVOID)0, 0,
                          (LPVOID)DelePathBuffer, 4,
                          &cbBytesReturned, NULL)){
         MessageBox(NULL, "Unable to extract!" ,
                    "Deletion & Access Control Manager Login",
                    MB_OK|MB_ICONEXCLAMATION);
         return FALSE;
      }
      PUCHAR TmpPtr = (PUCHAR)(DelePathBuffer + \
                      strlen(DelePathBuffer));
      while(*(TmpPtr--)!='\\')
        ;
      while((*(TmpPtr)!= '.') && *TmpPtr)
        TmpPtr++;
      if(*TmpPtr){
        TmpPtr++;
        while(*(BYTE *)ExtsPtrTmp != 0xFF)
        {
            if(stricmp((char *)TmpPtr, (char *) ExtsPtrTmp)==0)
               break;
             ExtsPtrTmp += strlen((char *) ExtsPtrTmp) + 1;
         }
      }
      if(*ExtsPtrTmp != 0xFF){
        ShowWindow(hWnd, SW_SHOW);
        SendMessage(GetDlgItem(hWnd, IDC_DELETION), \
                               LB_ADDSTRING, 0, \
                               (LPARAM)DelePathBuffer);
        SendMessage(GetDlgItem(hWnd, IDC_DELETION), \
                               LB_SETCURSEL, 0, 0);
        SetForegroundWindow(hWnd);
      }
      else{
        DWORD AlreadyInside = 1;
        if(!DeviceIoControl(hCVxD, DRIVER_TEMP_CLOSE,
                            (LPVOID)&AlreadyInside, 4,
                            (LPVOID)0, 0,
                            &cbBytesReturned, NULL)){
            MessageBox(NULL,\
                    "Unable to switch off Driver!",
                    "Deletion & Access Control Manager Login", \
                    MB_OK|MB_ICONEXCLAMATION);
            return FALSE;
        }
        SetFileAttributes(DelePathBuffer, 0);
        DeleteFile(DelePathBuffer);
        AlreadyInside = 0;
        if(!DeviceIoControl(hCVxD, DRIVER_TEMP_CLOSE,
           (LPVOID)&AlreadyInside, 4,
           (LPVOID)0, 0,
           &cbBytesReturned, NULL)){
           MessageBox(NULL,
                     "Unable to switch on Driver!",
                     "Deletion & Access Control Manager Login", \
                     MB_OK|MB_ICONEXCLAMATION);
           return FALSE;
        }
      }
      break;
    }

Because we cannot pass on a Ring 0 pointer to Ring 3, we need the extra DeviceIoControl to extract the path/filename which lets the device driver get the Ring 3 pointer and copy the file name into that buffer and return. This way, we communicate between the device driver and the login application. Once we are in the login application, we can do as we wish with any enhanced GUI screen, but here I have only provided a simple list box.

This is how we load the Ring 3 pointer with the file name of the deleted file:

DWORD _stdcall ExtractDeleteFile(DWORD dwDDB, DWORD hDevice, \
        LPDIOC lpDIOCParms)
{
  PDWORD pdw;
  DWORD FilePtr;
    pdw = (PDWORD)lpDIOCParms->lpvOutBuffer;
  FilePtr = pdw;
  strcpy(FilePtr, DeleteFilePtr);
  DeleteFilePtr += strlen(DeleteFilePtr) + 1;
  DeleteFilesProcessed++;
  return 0;
}

The deleted files are acknowledged this way as being successful and they are added in the Ring 3 list box. The Ring 3 application keeps on enquiring for a password for each file and deletes the file in the top of list box one by one until all the files have been deleted. This way, deletion is controlled using the Ring 0 program. Note that here we have a variable called AlreadyInside. This means that if this variable is set to nonzero, the device driver becomes inactive; this allows switching off the driver momentarily. There is no way to wait for the Ring 3 thread to complete and for us to get the notification at Ring 0, so we adopt this method. Moreover, Ring 0 should not wait for a Ring 3 thread to complete before continuing; hence, we allow Ring 0 to proceed with a false success.

The following is the Ring 3 code for handling the real file deleting from the list box. The files are deleted if you give the correct password; otherwise, all the files are skipped and not deleted.

case IDOK:
                    SendMessage(GetDlgItem(hWnd, IDC_DELETION), \
                            LB_GETTEXT, 0, (LPARAM)FirstFilePath);
  GetDlgItemText(hWnd, IDC_DELEPASSWORD, PassStr, 50);
  for(i=0; i<strlen(PassStr); i++){
    Checksum += PassStr[i];
  }
  if(Checksum!=ExtractDeletePathPassword(FirstFilePath)){
                            MessageBox(hWnd, \
"Incorrect password!  Cannot delete the file", \
"Deletion & Access Control Manager", MB_OK|MB_ICONEXCLAMATION);
                            SendMessage(GetDlgItem(hWnd, IDC_DELETION), \
                                    LB_RESETCONTENT, 0, 0);
  }
  else{
    DWORD AlreadyInside = 1;
    if(!DeviceIoControl(hCVxD, DRIVER_TEMP_CLOSE,
        (LPVOID)&AlreadyInside, 4,
        (LPVOID)0, 0,
        &cbBytesReturned, NULL)){
           MessageBox(NULL, \
           "Unable to switch off Driver!", \
           "Deletion & Access Control Manager Login",
           MB_OK|MB_ICONEXCLAMATION);
               return FALSE;
    }
    SetFileAttributes(FirstFilePath, 0);
    DeleteFile(FirstFilePath);
    AlreadyInside = 0;
    if(!DeviceIoControl(hCVxD, DRIVER_TEMP_CLOSE,
        (LPVOID)&AlreadyInside, 4,
        (LPVOID)0, 0,
        &cbBytesReturned, NULL)){
      MessageBox(NULL, \
                 "Unable to switch on Driver!", \
                 "Deletion & Access Control Manager Login",
                 MB_OK|MB_ICONEXCLAMATION);
        return FALSE;
    }
    SendMessage(GetDlgItem(hWnd, IDC_DELETION), \
                LB_DELETESTRING, 0, 0);
    SendMessage(GetDlgItem(hWnd, IDC_DELETION), \
                LB_SETCURSEL, 0, 0);

  }
  if((int)SendMessage(GetDlgItem(hWnd, IDC_DELETION), \
                      LB_GETCOUNT, 0, 0)<=0)
  ShowWindow(hDlg, SW_HIDE);
  break;

There are three data files used by these applications and device driver: Protector.dat, DeleProt.dat, and Exts.dat. The first two are used by the device driver, but Exts.dat is used by the login application for filtering the extensions that are to be protected.

Protector.dat contains folders for access restrictions and the entire file has the following format:

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

DeleProt.dat is the file containing all the folders that are deletion protected and the entire file has the following format:

ASCIIZ Path     = Contains a single folder
WORD   Password = Contains the checksum of the
                  password characters (Case Sensitive)

Exts.dat is the file containing all the extensions that are to be protected by the application in the folders mentioned in DeleProt.dat. It has a simple format:

ASCIIZ Extension = Contains a single extension

If you are using Windows Explorer, the files, when deleted, will appear as though they have been deleted from the predefined folder, but in reality they are not deleted. This can be proved; if you refresh the explorer screen, the files will reappear. But all the files that you deleted will appear in the deletion list box for you to delete one by one. Remember that once the hook is placed, any delete anywhere within Windows will be reflected in Ring 0 and will ensure that the dialog box for deletion pops up. This also includes the DOS prompt within Windows. If you manually DEL certain files, it will also appear here; even a DELTREE pops up the dialog box, but with some strange results. The files in the folder appear deleted, but finally the collapse of the branch cannot happen (which of course uses RD — Remove Directory) because there actually are files in the folder. This means that the protected directory cannot be collapsed just like that. This is the true protecting nature of this utility.

Conclusion

All in all, this utility will be very useful for controlling deletion on your machines. Whether it is MPEG 3 music files or highly valuable documents like mails and spreadsheets, they can be deleted in a controlled way. So, you can rest assured that the files are safe and sound from any digital attacks or accidents.

Downloads

Download source — 51 KB

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read