创建一个支持 "Append Data" NTFS 特权的 FileStream

Creating a FileStream that honours the "Append Data" NTFS privilege

考虑一个具有显式权限的 NTFS 文件夹创建文件/写入数据应用于仅此文件夹,以及Create Folders / Append Data 应用于 Files Only(和 Read Attributes,因为我知道这是必要的)。

> icacls x:\pathto\folder
x:\pathto\folder CONTOSO\Domain Users:(CI)(S,WD)
                 CONTOSO\Domain Users:(OI)(IO)(S,AD,REA,RA)
                 BUILTIN\Administrators:(I)(OI)(CI)(F)
                 NT AUTHORITY\SYSTEM:(I)(OI)(CI)(F)

理论上,这应该允许用户在文件不存在时创建文件,如果文件存在则附加到文件,但不允许覆盖或截断文件的内容,也不要删除它。

现在,鉴于此,如何实际 打开 该文件进行附加?

我试过简单的 command-line/batch 重定向:

> echo foo >> x:\pathto\folder\thefile.txt
> echo foo >> x:\pathto\folder\thefile.txt
Access denied.

...和 ​​VBScript

Dim fso : Set fso = CreateObject("Scripting.FileSystemObject")
Dim stream : Set stream = fso.OpenTextFile("x:\pathto\folder\thefile.txt", 8, True, 0)  ' 8-ForAppending
stream.WriteLine("foo")
-----
> cscript writefoo.vbs //nologo //e:vbs
> cscript writefoo.vbs //nologo //e:vbs
Microsoft VBScript Runtime Error(2, 14): Permission denied

...和 ​​.NET(通过 PowerShell)

> (1..2) | %{
>   Write-Host $_
>   $f = [IO.File]::AppendText('x:\pathto\folder\thefile.txt')
>   $f.WriteLine("foo")
>   $f.Dispose()
> }
1
2
Exception calling "AppendText" with "1" argument(s): Access to the path "x:\pathto\folder\thefile.txt" is denied.

...以及另一种 .NET 方法(同样通过 PowerShell)

> (1..2) | %{
>   Write-Host $_
>   $f = [IO.File]::Open('x:\pathto\folder\thefile.txt', [IO.FileMode]::Append, [IO.FileAccess]::Write, [IO.FileShare]::None)
>   $f = New-Object IO.StreamWriter -Arg $f
>   $f.WriteLine("foo")
>   $f.Dispose()
> }
1
2
Exception calling "Open" with "4" argument(s): Access to the path "x:\pathto\folder\thefile.txt" is denied.

在所有情况下,使用 Sysinternals Procmon 运行,我可以看到底层 CreateFile 总是请求 Generic Write 以获得其权限。

如果我理解正确,Generic Write 表示 Win32 的 FILE_GENERIC_WRITE,它是 FILE_APPEND_DATA | FILE_WRITE_DATA(和其他)的位标志组合,需要 Create Files / Write 文件目录.

上的数据

但是,如果我将 Create Files / Write Data 放置在文件上,则文件不再是仅附加的——它们不能被删除和重写,但它们 可以 截断 并重写——这违背了目的。

那么我可以使用什么机制来真正打开文件,真的 仅用于追加?

您真的应该只打开带有追加数据的文件。 Difference between FILE_WRITE_DATA and FILE_APPEND_DATA

echo等方法不能只用Append Data访问来追加数据(里面还有其他复杂的访问。根据你的测试),但是为什么不创建一个"Append"命令(比如echo) 直接使用 CreateFile?可以使用 CreateFile.

严格控制访问权限

Append.cpp:

#include <windows.h>
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
    if (argc != 3)
        return 0;
    //cout << argv[1] << "," << argv[2] << endl;
    HANDLE hFile = CreateFile(argv[2], FILE_APPEND_DATA, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    WriteFile(hFile,argv[1],strlen(argv[1]),0,0);
    CloseHandle(hFile);
}

然后将Append.exe复制到要执行echo或"C:\Windows\System32"的文件夹中。使用 it like 命令:

Append foo x:\pathto\folder\thefile.txt

想通了,至少对于 PowerShell/.NET 是这样。 .NET FileStream 对象提供了一个带有 FileSystemRights 枚举参数的构造函数,它允许您明确限制底层 CreateFile 调用不需要 Generic Write.

> (1..3) | %{
>   Write-Host $_
>   $f = New-Object -TypeName 'IO.FileStream' `
>                   -ArgumentList $private:file,`
>                                 ([IO.FileMode]::Append),`
>                                 ([Security.AccessControl.FileSystemRights]::AppendData),`
>                                 ([IO.FileShare]::Read),`
>                                 4KB,`
>                                 ([IO.FileOptions]::None)
>   $f = New-Object -TypeName 'IO.StreamWriter' `
>                   -ArgumentList $f
>   $f.WriteLine("foo")
>   $f.Dispose()
> }
1
2
3

据我所知没有 VBScript 或 Batch 的选项(除了 滚动自己的 COM 类型或 append.exe,它直接通过 C++/Win32 调用 CreateFile)。