Simple API Hook for prevent file deletion?
Asked Answered
C

3

8

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
Chellman answered 28/12, 2013 at 13:52 Comment(7)
Converting C# to VB.NET and vice versa is trivial. So trivial even that you can ask .NET Reflect to show CIL as either VB.NET or C#. If you are having difficulty, compile the C# code, open the assembly in .NET Reflector and have it produce VB.NET. Honestly, though, if you're struggling at that level, API hooking might not be for you.Pappas
@Pappas API hooking might not be for you I'm honest, yes you are totally right AP Hooking is not for my experience level, by the way that is the only possible solution (that or writting a file system driver that seems really even more harder...) so what can I do?, I have no alternatives, API Hooking is the only thing that I can try and of course I really need big help to carry it out. thanks for commentChellman
yes, but you should try on your own and try to figure it out yourself and if you get stuck on something, that "file system driver" you mention, you got no way of doing that with the .net framework afaik, or it would be pretty harsh..Rowel
This is not a good idea. What if the program trying to delete a file is a batch file or a service or some other program that doesn't show UI? (What if it's an antimalware program trying to delete an infected file? Displaying UI may deadlock since the delete may be coming from a UI thread. So the UI is waiting for a delete, but the delete is waiting for UI. What if it is Word deleting a temporary autosave file?) Also, you are injecting the CLR, which is problematic.Heman
@Raymond Chen but it should be working only in a specific folder, that means no antimalware should need to do removal operations there, but if a virus is found or something else then avoid the deletion by the antimalware because the user is in his own risk taking preference of this "anti-deletion" system, the same for batch files and services, I think is easy to understand that point of view, sorry for my english, thankyou for comment!.Chellman
So you're saying that if a batch file or service tries to delete a file, it's okay for the batch file or service to hang? This does not sound like a good user experience. If you really want to do this, you need to write a file system filter. This is not something that can be done from an application.Heman
Hi, I'm Deviare developer, _hook.Attach(_mgr.Processes) throws an exception if it tries to hook an application you haven't access rights. I recommend to trasverse the Processes list and hook only the needed ones passing an individual INktProcess object to the attach call.Fusion
C
2

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
Chellman answered 6/1, 2014 at 3:47 Comment(0)
K
2

If it were me, I would revisit my architecture to see whether I really needed to prevent any and all file deletions. Likely you only need to prevent deletions in a few sensitive directories (and possibly just a few sensitive files in those directories). This should make for a less-annoying UI.

Keystroke answered 6/1, 2014 at 0:57 Comment(0)
C
2

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
Chellman answered 6/1, 2014 at 3:47 Comment(0)
B
1

What you need is a directory watcher. There is built in functionality in Visual studio. Try the below code:

Imports System.IO

Module Watcher

Const DirectoryName As String = "C:\Database\2010Admin Updated"

Private intCreated As Integer



Sub Main()

Dim Watcher As New FileSystemWatcher(DirectoryName)

Try

' Add delegates for events

AddHandler Watcher.Created, New FileSystemEventHandler(AddressOf Watcher_Created)

AddHandler Watcher.Changed, New FileSystemEventHandler(AddressOf Watcher_Changed)

AddHandler Watcher.Deleted, New FileSystemEventHandler(AddressOf Watcher_Deleted)

AddHandler Watcher.Renamed, New RenamedEventHandler(AddressOf Watcher_Renamed)

' Must do this

Watcher.EnableRaisingEvents = True

' Display instructions and wait to exit

Console.WriteLine("Make changes to the " & DirectoryName & "directory.")

Console.WriteLine("You will see the events generated.")

Console.WriteLine("Press ENTER to exit.")

Console.ReadLine()

Finally

Watcher.Dispose()

End Try

End Sub

Private Sub Watcher_Created(ByVal sender As Object, ByVal e As FileSystemEventArgs)

Dim objWriter As New System.IO.StreamWriter("C:\dbupdated.txt", True)

Console.WriteLine("Event: File {0} {1}", e.FullPath, e.ChangeType)

objWriter.WriteLine(Now() & "Event: File {0} {1}", e.FullPath, e.ChangeType)

objWriter.WriteLine()

objWriter.Close()

intCreated += 1

If intCreated Mod 2 = 0 Then

MsgBox("File Created: " & e.FullPath)

End If

End Sub

Private Sub Watcher_Changed(ByVal sender As Object, ByVal e As FileSystemEventArgs)

Dim objWriter As New System.IO.StreamWriter("C:\dbupdated.txt", True)

Console.WriteLine("Event: File {0} {1}", e.FullPath, e.ChangeType)

objWriter.WriteLine(Now() & "Event: File {0} {1}", e.FullPath, e.ChangeType)

objWriter.WriteLine()

objWriter.Close()

End Sub

Private Sub Watcher_Deleted(ByVal sender As Object, ByVal e As FileSystemEventArgs)

Dim objWriter As New System.IO.StreamWriter("C:\dbupdated.txt", True)

Console.WriteLine("Event: File {0} {1}", e.FullPath, e.ChangeType)

objWriter.WriteLine(Now() & "Event: File {0} {1}", e.FullPath, e.ChangeType)

objWriter.WriteLine()

objWriter.Close()

End Sub

Private Sub Watcher_Renamed(ByVal sender As Object, ByVal e As RenamedEventArgs)

Dim objWriter As New System.IO.StreamWriter("C:\dbupdated.txt", True)

Console.WriteLine("Event: File {0} {1} to {2}", e.OldFullPath, e.ChangeType, e.FullPath)

objWriter.WriteLine(Now() & "Event: File {0} {1} to {2}", e.OldFullPath, e.ChangeType, e.FullPath)

objWriter.WriteLine()

objWriter.Close()

End Sub



End Module

Try running it (as a console application).

Branle answered 31/12, 2013 at 23:10 Comment(2)
It won't. It only detects attempts. The only way to stop file deletion is to sandbox the program which is attempting to do so.Branle
@Really I appreciate your help but I already knew the usage of the FilesystemWatcher and I did an approach using it, but that things are what I want to avoid my question is related about how to use api hooking, anyways really thankyou. +1Chellman

© 2022 - 2024 — McMap. All rights reserved.