我正在尝试使用NamedPipe在Win10中的应用程序和服务之间进行通信。 APP是使用C#(UWP)开发的,作为Pipe Client运行在前台。服务是C ++作为Pipe Server在后台运行的。现在的问题是,APP无法连接服务。我知道MSFT文档说管道仅在应用程序容器中受支持。但是我尝试了以下情况:我的uwp应用程序VS C#(nonUWP)服务器(不在应用程序容器中); C ++客户端VS C ++服务器(与服务相同的代码,但在前台运行)。两种情况都很好。因此,我认为安全特权可能有问题。但是我找不到异常的东西,有人可以帮我吗?
客户端(UWP / C#):
_namedPipeClientHandle[index] = CreateFileW(@"\\.\pipe\starpipe",
DESIREDACCESS.GENERIC_READ | DESIREDACCESS.GENERIC_WRITE,
SHAREMODE.FILE_SHARE_READ | SHAREMODE.FILE_SHARE_WRITE,
0,
CREATIONDISPOSITION.OPEN_EXISTING,
FLAGSANDATTRIBUTES.FILE_FLAG_OVERLAPPED,
0);
if (_namedPipeClientHandle[index] != null && _namedPipeClientHandle[index].IsInvalid == false)
{
_namedPipeClientStream[index] = new FileStream(_namedPipeClientHandle[index], FileAccess.ReadWrite, 2048, true);
isConnected[index] = true;
break;
}
服务器(C ++):
EXPLICIT_ACCESS ea[2];
SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY;
SECURITY_ATTRIBUTES sa;
if (!AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pEveryoneSID)) return false;
SecureZeroMemory(&ea, 2 * sizeof(EXPLICIT_ACCESS));
ea[0].grfAccessPermissions = FILE_ALL_ACCESS | GENERIC_WRITE | GENERIC_READ;
ea[0].grfAccessMode = SET_ACCESS;
ea[0].grfInheritance = NO_INHERITANCE;
ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
ea[0].Trustee.ptstrName = (LPTSTR)pEveryoneSID;
if (!AllocateAndInitializeSid(&SIDAuthNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pAdminSID)) return false;
ea[1].grfAccessPermissions = FILE_ALL_ACCESS | GENERIC_WRITE | GENERIC_READ;
ea[1].grfAccessMode = SET_ACCESS;
ea[1].grfInheritance = NO_INHERITANCE;
ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
ea[1].Trustee.ptstrName = (LPTSTR)pAdminSID;
DWORD dwRes = SetEntriesInAclW(2, ea, NULL, &pACL);
if (ERROR_SUCCESS != dwRes) return false;
auto secDesc = std::vector<unsigned char>(SECURITY_DESCRIPTOR_MIN_LENGTH);
PSECURITY_DESCRIPTOR pSD = (PSECURITY_DESCRIPTOR)(&secDesc[0]);
if (nullptr == pSD) return false;
if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) return false;
if (!SetSecurityDescriptorDacl(pSD, TRUE, pACL, FALSE)) return false;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = pSD;
sa.bInheritHandle = FALSE;
const char* pStrPipeName = "\\\\.\\pipe\\starpipe";
m_hPipe = CreateNamedPipeA(
pStrPipeName,
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_TYPE_BYTE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
2048,
2048,
0,
&sa);
if (m_hPipe == INVALID_HANDLE_VALUE) return false;
if (::ConnectNamedPipe(m_hPipe, NULL)) return true;
答案 0 :(得分:4)
从UWP流程(应用程序容器)名称创建的命名管道必须具有格式
"\\\\?\\pipe\\local\\SomeName"
系统将此名称转换为
"\\\\?\\pipe\\Sessions\\<SessionId>\\AppContainerNamedObjects\\<AppContainerSid>\\SomeName"
因此,桌面应用程序必须使用这种名称格式创建管道,UWP才能打开它。
但是为此需要知道uwp应用程序的appcontainer-sid及其会话ID。好的,appcontainer-sid对于具体的UWP是永久的-从未更改,但是SessionId可以不同(通常为1,但可以为另一个)。还需要在管道上设置特殊的安全描述符,以授予对SDDL_EVERYONE + SDDL_ALL_APP_PACKAGES + SDDL_ML_LOW
的访问权限。例如
"D:(A;;GA;;;WD)(A;;GA;;;AC)S:(ML;;;;;LW)"
在桌面上创建此类管道的示例
inline ULONG BOOL_TO_ERROR(BOOL f)
{
return f ? NOERROR : GetLastError();
}
volatile UCHAR guz = 0;
ULONG CreatePipeforUWP(OUT PHANDLE PipeHandle, PCWSTR PipeName, HANDLE hProcess)
{
SECURITY_ATTRIBUTES sa = { sizeof(sa), 0, FALSE };
// SDDL_EVERYONE + SDDL_ALL_APP_PACKAGES + SDDL_ML_LOW
if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(
L"D:(A;;GA;;;WD)(A;;GA;;;AC)S:(ML;;;;;LW)",
SDDL_REVISION_1, &sa.lpSecurityDescriptor, 0))
{
return GetLastError();
}
HANDLE hToken;
ULONG err = BOOL_TO_ERROR(OpenProcessToken(hProcess, TOKEN_QUERY, &hToken));
if (err == NOERROR)
{
PVOID stack = alloca(guz);
ULONG cb = 0, rcb = 128;
union {
PVOID buf;
PTOKEN_APPCONTAINER_INFORMATION AppConainer;
PWSTR sz;
};
do
{
if (cb < rcb)
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
err = BOOL_TO_ERROR(GetTokenInformation(hToken, ::TokenAppContainerSid, buf, cb, &rcb));
if (err == NOERROR)
{
ULONG SessionId;
err = BOOL_TO_ERROR(GetTokenInformation(hToken, ::TokenSessionId, &SessionId, sizeof(SessionId), &rcb));
if (err == NOERROR)
{
PWSTR szSid;
err = BOOL_TO_ERROR(ConvertSidToStringSid(AppConainer->TokenAppContainer, &szSid));
if (err == NOERROR)
{
static const WCHAR fmt[] = L"\\\\?\\pipe\\Sessions\\%d\\AppContainerNamedObjects\\%s\\%s";
rcb = (1 + _scwprintf(fmt, SessionId, szSid, PipeName)) * sizeof(WCHAR);
if (cb < rcb)
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
_swprintf(sz, fmt, SessionId, szSid, PipeName);
HANDLE hPipe = CreateNamedPipeW(sz,
PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES, 0, 0, 0, &sa);
if (hPipe == INVALID_HANDLE_VALUE)
{
err = GetLastError();
}
else
{
*PipeHandle = hPipe;
}
LocalFree(szSid);
}
}
break;
}
} while (err == ERROR_INSUFFICIENT_BUFFER);
CloseHandle(hToken);
}
LocalFree(sa.lpSecurityDescriptor);
return err;
}
ULONG CreatePipeforUWP(OUT PHANDLE PipeHandle, PCWSTR PipeName, ULONG dwProcessId)
{
if (HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, 0, dwProcessId))
{
ULONG err = CreatePipeforUWP(PipeHandle, PipeName, hProcess);
CloseHandle(hProcess);
return err;
}
return GetLastError();
}
HANDLE hPipe;
if (CreatePipeforUWP(&hPipe, L"MyPipe", *) == NOERROR)
仅在客户端(UWP)应用中
CreateFileW(L"\\\\?\\pipe\\local\\MyPipe",
FILE_GENERIC_READ|FILE_GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
但是对于此服务器,需要知道UWP会话ID(或进程ID),或假定UWP与服务器在同一会话中运行。一般来说,这不是很好。
我建议使用RPC而不是命名管道。这里没有任何问题。
桌面需要使用RpcServerRegisterIf3
并为接口设置SecurityDescriptor以便UWP访问它。说"D:P(A;;GA;;;WD)(A;;GA;;;AC)(A;;GA;;;S-1-15-2-2)S:(ML;;;;;LW)"
。并且可以很好地与UWP配合使用,例如服务器代码
ULONG InitRpcServer()
{
PSECURITY_DESCRIPTOR SecurityDescriptor;
// generic all for SDDL_ALL_APP_PACKAGES + SDDL_EVERYONE
ULONG dwError = BOOL_TO_ERROR(ConvertStringSecurityDescriptorToSecurityDescriptorW(
L"D:P(A;;GA;;;WD)(A;;GA;;;AC)(A;;GA;;;S-1-15-2-2)S:(ML;;;;;LW)", SDDL_REVISION, &SecurityDescriptor, 0));
if (dwError == ERROR_SUCCESS)
{
dwError = RpcServerRegisterIf3(hello_v1_0_s_ifspec,
NULL, NULL, RPC_IF_ALLOW_CALLBACKS_WITH_NO_AUTH,
RPC_C_LISTEN_MAX_CALLS_DEFAULT, 0x10000, 0, SecurityDescriptor);
if (dwError == RPC_S_OK)
{
dwError = RpcServerUseProtseqEpW(
(RPC_WSTR)L"ncalrpc",
RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
(RPC_WSTR)L"myname",
SecurityDescriptor);
if (dwError == RPC_S_OK)
{
dwError = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE);
}
}
LocalFree(SecurityDescriptor);
}
return dwError;
}
仅客户需要
RpcBindingFromStringBinding((RPC_WSTR)L"ncalrpc:[myname]", &IDL_handle)
答案 1 :(得分:0)
根据此page
管道仅在应用程序容器中受支持;即,从一个UWP进程到同一应用程序一部分的另一个UWP进程。另外,命名管道必须使用语法“。\ pipe \ LOCAL”作为管道名称。