简单的API Hook用于防止文件删除?

8
我希望能够通过挂钩所需的API函数来拦截用户在任何目录中删除文件的操作,并在消息框中询问一个简单的布尔问题 "Really Would you like to Delete this file?",这个问题是为了表达我想要控制这个文件,删除它或防止删除它。

我的操作系统是Windows 8 x64,但我想编写一种通用的方法,适用于其他Windows操作系统和它们的体系结构(如果这不会导致更大的麻烦)。
在这个SO问题中,我已经读到最佳选择是通过挂钩NtSetFileInformation函数Intercept FIleSytemCall for Deletion。顺便说一下,我发现存在一个名为DeleteFile的WinAPI函数和接口ICopyHook,但我不知道它们之间的区别,但无论如何,我真的不知道如何开始做这个...
我希望澄清一下,我正在寻找一个VBNET解决方案,因为这些API-Hooking库在Google上缺乏任何VBNET代码示例,而且当涉及到复杂的代码时,C#代码翻译成VBNET会出现很大的错误。编辑:我发现了一个关于NtSetFileInformation的EasyHook库示例,似乎非常适合我的需求,但它是C#代码,我尝试翻译但没有成功:Hooking NtCreateFile API from ntdll.dll with EasyHook (c#)所以,我尝试使用Deviare库2.6,但没有任何效果。
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

基本上,上面的代码与 @mazoula 在这里 hooking another program's calls to winapi functions in vb.net 中的答案相同,他说这段代码对他有用,但我已经尝试过了(没有进行上述修改),并在 _hook.Attach(_mgr.Processes) 指令处抛出了异常。
此外,我也尝试过使用 EasyHook 库,但当我从 Explorer.exe 或 CMD 中删除文件时,它什么也没做。该代码是这个 C# 代码的翻译 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

2
将C#转换为VB.NET或者将VB.NET转换为C#都是微不足道的。甚至你可以要求.NET反射器将CIL显示为VB.NET或C#。如果你遇到困难,可以编译C#代码,打开程序集在.NET反射器中并让它生成VB.NET代码。但说实话,如果你在这个层面上还有困难,API钩子可能不适合你。 - IInspectable
@IInspectable “API hooking might not be for you” 我很诚实,是的,你完全正确,API Hooking 不适合我的经验水平,顺便说一下,那是唯一可能的解决方案(或者编写一个文件系统驱动程序,这似乎更难了……),那我该怎么办呢?我没有其他选择,API Hooking 是我唯一可以尝试的事情,当然我真的需要大力帮助来完成它。谢谢您的评论。 - ElektroStudios
是的,但你应该尝试自己解决问题,如果遇到困难,那个你提到的“文件系统驱动程序”,我认为在.NET框架中无法实现,否则会非常困难。 - SomeNickName
@Raymond Chen,但它应该只在特定文件夹中运行,这意味着没有反恶意软件需要在那里执行删除操作,但是如果发现病毒或其他问题,则避免反恶意软件进行删除,因为用户处于自己的风险承受偏好中,对于批处理文件和服务也是如此。我认为这个观点很容易理解,抱歉我的英语,谢谢您的评论! - ElektroStudios
那么您的意思是,如果批处理文件或服务尝试删除文件,批处理文件或服务挂起是可以接受的吗?这听起来不像是一个良好的用户体验。如果您真的想这样做,您需要编写一个文件系统过滤器。这不是应用程序可以完成的事情。 - Raymond Chen
显示剩余2条评论
3个回答

2

如果是我,我会重新审视我的架构,看看是否真的需要防止所有文件的删除。很可能你只需要在一些敏感目录(可能仅限于这些目录中的一些敏感文件)中防止删除。这应该会使用户界面更加友好。


2

几天前我写了这个方法来临时解决问题,但是我不能百分之百确定这种方法在所有情况下都能正常工作(例如用户可以按ctrl+z来恢复文件删除,而我的方法逻辑使用文件的日期时间属性来尝试选择最后一个被删除的文件,我不是百分之百确定),这个方法现在可行,但我想学习如何API Hook,而不是这样做。

显然,这对于永久文件删除也无法起作用。

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

1
你需要的是一个目录监视器。Visual Studio内置了此功能。 尝试使用以下代码:
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

尝试以控制台应用程序的形式运行它。


它不会阻止文件删除,只能检测到尝试。唯一阻止文件删除的方法是对试图这样做的程序进行沙盒处理。 - TheVolatileChemist
@真的很感谢你的帮助,但我已经知道了FilesystemWatcher的用法,并且我已经使用它进行了一些尝试,但那些东西正是我想要避免的。我的问题与如何使用API挂钩有关,无论如何,非常感谢你。+1 - ElektroStudios

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接