我正在测试使用内存映射文件。我有一个写入命名 MMF 的服务器和一个从同名 MMF 读取并使用命名信号量来防止同时访问的客户端。
当我以交互方式运行客户端和服务器时,我可以从服务器 -> 客户端传递数据。
当我从计划任务运行服务器并将其配置为仅在用户登录时运行(并且我是它正在运行并已登录的用户)并以交互方式运行客户端时,它也可以工作
当我从计划任务运行服务器并将其设置为无论用户是否登录或我的实际所需上下文 (SYSTEM) 都运行时,我的客户端不再可以看到 MMF 已创建(即使我的日志/错误捕获确认这是)。
我似乎错过了计划任务设置中的一些环境更改,这些更改阻止了我的 MMF 被读取。任何人都可以提供任何帮助吗?
编辑:示例代码来说明问题。
服务器:
try {
$mmf = [System.IO.MemoryMappedFiles.MemoryMappedFile]::CreateOrOpen('LinkMon', 10MB)
## Create a mutex that's used to temporaily block access to the Memory Mapped File
$MmfSemaphore = [System.Threading.Semaphore]::new(1, 1, 'LinkMonSemaphore')
} catch {
$_
}
while (1) {
if ([datetime]::Now.Second % 2 -eq 0) {
$s = Get-Process | Select -First 10
} else {
$s = Get-Service | Select -First 10
}
$j = $s | ConvertTo-Json
$b = [System.Text.Encoding]::Ascii.GetBytes($j)
try {
$MmfSemaphore.WaitOne()
$MmfStream = $mmf.CreateViewStream()
$MmfBw = [System.IO.BinaryWriter]::new($MmfStream)
## [System.Text.Encoding]::Ascii.GetString(([System.Text.Encoding]::Ascii.GetBytes((("$($b.Length)").PadLeft(10, '0')))))
$MmfBw.Write(([System.Text.Encoding]::Ascii.GetBytes((("$($b.Length)").PadLeft(10, '0')))))
$MmfBw.Write($b)
$MmfStream.Position = 0
$MmfSemaphore.Release()
} catch {
$_
}
Start-Sleep -Seconds 1
}
$MmfSemaphore.Dispose()
$MmfBw.Dispose()
$MmfStream.Dispose()
$mmf.Dispose()
客户:
try {
$mmf = [System.IO.MemoryMappedFiles.MemoryMappedFile]::OpenExisting('LinkMon')
$MmfSemaphore = [System.Threading.Semaphore]::OpenExisting('LinkMonSemaphore')
} catch {
$_
}
while (1) {
try {
$MmfSemaphore.WaitOne()
$MmfStream = $mmf.CreateViewStream()
$MmfBr = [System.IO.BinaryReader]::new($MmfStream)
$DataLength = ([int]([System.Text.Encoding]::ASCII.GetString($MmfBr.ReadBytes(10))))
$Payload = $MmfBr.ReadBytes($DataLength)
$MmfStream.Position = 0
$MmfSemaphore.Release()
$o = [System.Text.Encoding]::Ascii.GetString($Payload) | ConvertFrom-Json
} catch {
$_
}
$o | ft
Start-Sleep -Milliseconds 500
}
$MmfSemaphore.Dispose()
$MmfBr.Dispose()
$MmfStream.Dispose()
$mmf.Dispose()
编辑2:
基于查看一些似乎有类似问题的 C++ 和 C# 实现,我尝试修改 MMF 的安全性,但无济于事。这是新代码,但情况仍然存在:当我在两个不同的进程中交互地运行客户端和服务器时,它们可以来回发送数据。当我在计划任务中作为系统运行服务器时,我无法读取根据我的调试日志成功创建的 MMF。当我正常运行或在提升的上下文中运行 PowerShell 时,客户端收到的错误是:使用“1”参数调用“OpenExisting”的异常:“不存在给定名称的句柄。”
服务器:
$code = @"
using System.IO.MemoryMappedFiles;
using System.Security.AccessControl;
using System.Security.Principal;
namespace mmf
{
public static class Security
{
public static MemoryMappedFileSecurity GetMmfSec()
{
var security = new MemoryMappedFileSecurity();
security.AddAccessRule(new System.Security.AccessControl.AccessRule<MemoryMappedFileRights>(new SecurityIdentifier(WellKnownSidType.WorldSid, null), MemoryMappedFileRights.FullControl, AccessControlType.Allow));
return security;
}
}
}
"@
Add-Type -Language CSharp $code
$acl = [mmf.Security]::GetMmfSec()
## Create the initial Memory Mapped File and semaphore to synchronize writes to it.
try {
$mmf = [System.IO.MemoryMappedFiles.MemoryMappedFile]::CreateOrOpen('Global\Restricted\LinkMon', 10MB, [System.IO.MemoryMappedFiles.MemoryMappedFileAccess]::ReadWrite, [System.IO.MemoryMappedFiles.MemoryMappedFileOptions]::None, $acl, [System.IO.HandleInheritability]::Inheritable)
$sd = $mmf.GetAccessControl()
$sd | ConvertTo-Json | Out-File C:\temp\server_access.txt
## Create a sempahore that's used to temporaily block access to the Memory Mapped File
$MmfSemaphore = [System.Threading.Semaphore]::new(1, 1, 'LinkMonSemaphore')
} catch {
$_
}
while (1) {
if ([datetime]::Now.Second % 2 -eq 0) {
$s = Get-Process | Select -First 10
} else {
$s = Get-Service | Select -First 10
}
$s | ft
$j = $s | ConvertTo-Json
$b = [System.Text.Encoding]::Ascii.GetBytes($j)
try {
$MmfSemaphore.WaitOne()
$MmfStream = $mmf.CreateViewStream()
$MmfBw = [System.IO.BinaryWriter]::new($MmfStream)
## [System.Text.Encoding]::Ascii.GetString(([System.Text.Encoding]::Ascii.GetBytes((("$($b.Length)").PadLeft(10, '0')))))
$MmfBw.Write(([System.Text.Encoding]::Ascii.GetBytes((("$($b.Length)").PadLeft(10, '0')))))
$MmfBw.Write($b)
$MmfStream.Position = 0 #Reset the position of the pointer back to the top of the file so we're overwriting the old info
$MmfSemaphore.Release()
} catch {
$_
}
Start-Sleep -Milliseconds 500
}
trap {
$MmfSemaphore.Dispose()
$MmfBw.Dispose()
$MmfStream.Dispose()
$mmf.Dispose()
}
客户:
try {
$mmf = [System.IO.MemoryMappedFiles.MemoryMappedFile]::OpenExisting('Global\Restricted\LinkMon', [System.IO.MemoryMappedFiles.MemoryMappedFileRights]::ReadWrite, [System.IO.HandleInheritability]::Inheritable)
$MmfSemaphore = [System.Threading.Semaphore]::OpenExisting('LinkMonSemaphore')
} catch {
$_
break
}
while (1) {
try {
$MmfSemaphore.WaitOne()
$MmfStream = $mmf.CreateViewStream()
$MmfBr = [System.IO.BinaryReader]::new($MmfStream)
$DataLength = ([int]([System.Text.Encoding]::ASCII.GetString($MmfBr.ReadBytes(10))))
$Payload = $MmfBr.ReadBytes($DataLength)
$MmfStream.Position = 0
$MmfSemaphore.Release()
$o = [System.Text.Encoding]::Ascii.GetString($Payload) | ConvertFrom-Json
} catch {
$_
}
$o | ft
Start-Sleep -Milliseconds 500
}
$MmfSemaphore.Dispose()
$MmfBr.Dispose()
$MmfStream.Dispose()
$mmf.Dispose()
编辑3:
因此,在我注意到对象管理器中的内存“部分”对象正在移动之后,我经历了一系列迭代。我想看看该位置是否重要,或者它是否在不同情况下在对象上标记了不同的 ACL(它确实四处移动并最终得到不同的 ACL)。这是我已经完成的迭代矩阵,如果这有助于任何人帮助我解决这个问题:
编辑4:
我还开始向 MMF 添加Everyone, Full Control 并尝试迭代无济于事。
我开始关注流程完整性。虽然这似乎是一项卓有成效的努力,但当服务器从 powershell_ise 以 SYSTEM 交互方式运行时(使用 psexec 将 powershell_ise 实例生成为 SYSTEM),我仍然可以让我的客户端(非提升)从 MMF 读取数据。但是,当从计划任务以 SYSTEM 身份运行时,该进程仍以“系统”完整性级别运行,但我仍然无法访问该文件。
