IPC:UWP C#管道客户端在连接C ++服务器上失败

时间:2020-02-27 10:40:54

标签: c# c++ uwp pipe ipc

我正在尝试使用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;

2 个答案:

答案 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”作为管道名称。