创建一个支持 "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
)。
考虑一个具有显式权限的 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 的选项(除了 CreateFile
)。