Search code examples
.netvb.netwinapihookeasyhook

Simple API Hook for prevent file deletion?


I would like to intercept when the user deletes a file on ANY directory by hooking the needed API function(s) with the availability of asking a simple boolean question in a mesagebox "Really Would you like to Delete this file?", the question is an example to express that I would like to have control on the file, to delete it or to prevent deletion.

My OS Is Windows 8 x64, but I would like to write a method for generic usage in other Windows OS and their arquitechtures (if this don't suppose harder headaches to do it).

In this SO question I've read that the best choice is to hook the NtSetFileInformation function Intercept FIleSytemCall for Deletion by the way I've seen that exists a WinAPI function named DeleteFile and also the interface ICopyHook and I don't know the differente between these, but anyways I really don't know how to start doing this...

I would like to clarify that I'm looking for a VBNET solution, I've headaches because the absence of any VBNET Code example from these API-Hooking libraries over Google, and C# code translations to VBNET goes really wrong when a complex code is involved.

EDIT: I've found a EasyHook library Example about NtSetFileInformation which seems to be perfect for my needs, but it is C# code and I've tried to translate it without success: Hooking NtCreateFile API from ntdll.dll with EasyHook (c#)

So, I've tried this with Deviare library 2.6, but does nothing:

Public Class Form1

    Private _mgr As Deviare2.NktSpyMgr = Nothing
    Private WithEvents _hook As Deviare2.NktHook = Nothing
    Private _proc As Deviare2.INktProcess = Nothing

    Private Shadows Sub Shown() Handles MyBase.Shown

        _mgr = New Deviare2.NktSpyMgr()
        _hook = _mgr.CreateHook("ntdll.dll!NtSetFileInformation", Nothing)
        _hook.Hook()

    End Sub

    Private Sub OnFunctionCalled(ByVal proc As Deviare2.INktProcess,
                                 ByVal callInfo As Deviare2.INktHookCallInfo,
                                 ByVal rCall As Deviare.IRemoteCall) Handles _hook.OnFunctionCalled

        MsgBox("Caught function call in " & proc.Name)

    End Sub

End Class

Basically the code above is the same as @mazoula answered here hooking another program's calls to winapi functions in vb.net ,he says that the code worked for him, but I've tried it as is (without doiing my modifications above) and throwed me an exception at the _hook.Attach(_mgr.Processes) instruction.

Also I've tried this with the library EasyHook but again does nothing when I delete a file from Explorer.exe or from CMD, the code is a translation of this C# code http://www.codeproject.com/Questions/528094/DeleteFileplushookingpluswithplusEasyHookplussucce :

Imports System.Runtime.InteropServices
Imports EasyHook

Public Class Form1

    <DllImport("kernel32.dll", CharSet:=CharSet.Unicode, CallingConvention:=CallingConvention.StdCall)>
    Private Shared Function DeleteFile(filename As String) As Integer
    End Function

    <UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet:=CharSet.Unicode)>
    Private Delegate Function DeleteFileHandler(filename As String) As Integer

    Private Shared deleted As Boolean = False

    public Function DeleteFileHookInstance(filename As String) As Integer
        MsgBox("works?")
        If deleted Then
            deleted = False
            Return 1
        End If
        If MessageBox.Show((Convert.ToString("Do you really want to delete file ") & filename) + "?", "Confirm delete file", MessageBoxButtons.YesNo, MessageBoxIcon.Question) = DialogResult.Yes Then
            deleted = True
            Return DeleteFile(filename)
        Else
            Return 1
        End If
        'Assume the call is successfull
    End Function

    Public Sub Run()

        Dim hook As EasyHook.LocalHook

        Try
            MsgBox("Creating...")
            hook = LocalHook.Create(LocalHook.GetProcAddress("kernel32.dll", "DeleteFileW"), New DeleteFileHandler(AddressOf DeleteFileHookInstance), Me)
            'It stops here, the main interface receives the reported status 'Creating...' seemly forever, I understand that is for the unexpected restarting of explorer.exe
            MsgBox("Completing...")
            hook.ThreadACL.SetExclusiveACL(New Integer() {0})
            RemoteHooking.WakeUpProcess()
            MsgBox("OK")
        Catch ex As Exception
            MsgBox("CreateHook failed: " + ex.Message)
            System.Diagnostics.Process.GetCurrentProcess().Kill()
        End Try
        While True
            Application.DoEvents()
        End While
    End Sub

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Run()
    End Sub

End Class

Solution

  • few days ago I've write this approach to temporally resolve the problem, but I'm not 100% secure that the method will work properly in all the scenarios (for example the user can press ctrl+z to restore a file deletion and my method logic uses the datetime property of the files to try to pick the last deleted which I'm not 100% secure), this works by now, but I would like to learn how to API hook instead of doing this.

    Also obviously this will not work with a permanent file deletion.

    Imports System.IO
    Imports Shell32
    
    Public Class Test
    
        Private SH As New Shell
        Private RecycleBin As Folder = SH.NameSpace(ShellSpecialFolderConstants.ssfBITBUCKET)
        Private WithEvents FSW As New FileSystemWatcher
    
        Private Shadows Sub Load() _
        Handles MyBase.Load
    
            With FSW
                .Path = "C:\Test"
                .IncludeSubdirectories = True
                .Filter = "*"
                .NotifyFilter = NotifyFilters.FileName Or NotifyFilters.DirectoryName
                .EnableRaisingEvents = True
            End With
    
        End Sub
    
        Private Sub OnItemDeleted(sender As FileSystemWatcher, e As FileSystemEventArgs) _
        Handles FSW.Deleted
    
            Dim DeletedItems As IEnumerable(Of FolderItem) =
                RecycleBin.Items.Cast(Of FolderItem).
                                 Where(Function(Item) Item.Name = e.Name).
                                 OrderBy(Function(Item) Item.ModifyDate)
    
            Dim LastDeletedItem As Shell32.FolderItem = DeletedItems.LastOrDefault
    
            If LastDeletedItem IsNot Nothing Then
    
                If (LastDeletedItem.IsFolder AndAlso Directory.Exists(e.FullPath)) _
                   OrElse (Not LastDeletedItem.IsFolder AndAlso File.Exists(e.FullPath)) Then
    
                    Throw New Exception(String.Format("¿ Item has been restored ?: {0}", e.FullPath))
                    Exit Sub
    
                End If
    
                LastDeletedItem.InvokeVerb("undelete")
    
            End If
    
        End Sub
    
    End Class