有人有使用 GetAppContainerNamedObjectPath 的经验吗?

Anyone has experience on using GetAppContainerNamedObjectPath?

最近我遇到了一个叫 GetAppContainerNamedObjectPath 的 Windows API。但是我不知道如何使用它。

我找到了这个 api (https://docs.microsoft.com/en-us/windows/win32/api/securityappcontainer/nf-securityappcontainer-getappcontainernamedobjectpath) 的 msdn 页面。但是没有正确的例子和注释,参数写得不好。

最后出现 ERROR_INVALID_PARAMETER(87) 错误,这告诉我输入的参数有问题。这是我试过的。

#define TokenIsAppContainer 29
#define TokenAppContainerSid 31
#define TokenAppContainerNumber 32

typedef struct _TOKEN_APPCONTAINER_INFORMATION {
    PSID TokenAppContainer;
} TOKEN_APPCONTAINER_INFORMATION, *PTOKEN_APPCONTAINER_INFORMATION;

void GetAppContainerProcessInfo(CString & procName)
{
    DWORD dwSize = 0;
    DWORD dwResult;
    HANDLE hToken;
    PTOKEN_APPCONTAINER_INFORMATION pAppCoInfo; 
    WCHAR wcsDebug[1024] = {0,};
    WCHAR * pwSID = NULL;

    typedef BOOL (WINAPI *_LPGETAPPCONTAINERNAMEOBJECTPATH)(HANDLE, PSID, ULONG, LPWSTR, PULONG);

    static _LPGETAPPCONTAINERNAMEOBJECTPATH lpGetAppContainerNamedObjectPath = NULL;

    if (0 == lpGetAppContainerNamedObjectPath)
    {
        HMODULE hKernel32 = LoadLibraryExW(L"kernel32.dll", NULL, 0);
        if (hKernel32)
        {
            lpGetAppContainerNamedObjectPath = reinterpret_cast<_LPGETAPPCONTAINERNAMEOBJECTPATH>(GetProcAddress(hKernel32, "GetAppContainerNamedObjectPath"));
        }
    }

    if (lpGetAppContainerNamedObjectPath)
    {
        DWORD processId = (DWORD)_ttoi((LPCTSTR)procName);
        //HANDLE hProcess = GetProcessHandleByProcessName(procName);
        HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId);

        if(!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
        {
            dwResult = GetLastError();
            swprintf_s( wcsDebug, _countof(wcsDebug), L"OpenProcessToken Error(%u) PID(%d)\n", dwResult, processId );
            AfxMessageBox(wcsDebug);
            return;
        }

        if (!GetTokenInformation(hToken, (TOKEN_INFORMATION_CLASS) TokenAppContainerSid, NULL, dwSize, &dwSize))
        {
            dwResult = GetLastError();
            if( dwResult != ERROR_INSUFFICIENT_BUFFER ) 
            {
                swprintf_s( wcsDebug, _countof(wcsDebug), L"GetTokenInformation Error %u\n", dwResult );
                AfxMessageBox(wcsDebug);
                return;
            }
        }

        pAppCoInfo = (PTOKEN_APPCONTAINER_INFORMATION) GlobalAlloc( GPTR, dwSize );

        if (!GetTokenInformation(hToken, (TOKEN_INFORMATION_CLASS) TokenAppContainerSid, pAppCoInfo, dwSize, &dwSize))
        {
            dwResult = GetLastError();
            swprintf_s( wcsDebug, _countof(wcsDebug), L"GetTokenInformation Error %u\n", dwResult );
            AfxMessageBox(wcsDebug);
            return;
        }

        WCHAR wcsNamedObjectPath[MAX_PATH];
        ULONG ulRetlen = 0;

        BOOL bRet = lpGetAppContainerNamedObjectPath(hToken, pAppCoInfo->TokenAppContainer, _countof(wcsNamedObjectPath), wcsNamedObjectPath, &ulRetlen );
        if (bRet)
        {
            swprintf_s( wcsDebug, _countof(wcsDebug), L"GetAppContainerNamedObjectPath Path(%s)\n", wcsNamedObjectPath );
            AfxMessageBox(wcsDebug);
        }
        else
        {
            dwResult = GetLastError();
            swprintf_s( wcsDebug, _countof(wcsDebug), L"GetAppContainerNamedObjectPath Error %u\n", dwResult );
            AfxMessageBox(wcsDebug);
        }

        if (pwSID)
            LocalFree(pwSID);

        CloseHandle(hToken)
        CloseHandle(hProcess);
    }
}

作为旁注,我尝试使用 wchar_t * 并通过调用 GetAppContainerNamedObjectPath 两次来动态分配内存缓冲区。但是还是没有机会。 Return 长度不是 return 有意义的值。

如果你在 GetAppContainerNamedObjectPath 之后调用 RtlGetLastNtStatus(); 而不是 GetLastError(); 你会得到

STATUS_INVALID_PARAMETER_MIX - 指定了无效的参数组合。

这为您提供了更多信息,比较了简单的无效参数。

然后寻找函数签名

BOOL
WINAPI
GetAppContainerNamedObjectPath(
    _In_opt_ HANDLE Token,
    _In_opt_ PSID AppContainerSid,
    _In_ ULONG ObjectPathLength,
    _Out_writes_opt_(ObjectPathLength) LPWSTR ObjectPath,
    _Out_ PULONG ReturnLength
    );

In_opt声明的TokenAppContainerSid——这意味着该参数是可选的,您可以传递 0 代替其中之一。然后问问你自己 - 你查询 TokenAppContainerSid 的令牌是什么?如果您将此令牌传递给 api ,系统是否无法为您执行此操作?明显可以。所以你不需要自己做。你真的需要将 Token 传递给 api,在这种情况下 AppContainerSid 必须为 0。或者你可以传递 AppContainerSid 到 api 并且在这种情况下 Token 必须为 0。当 AppContainerSidToken 不为零 - 你得到了 STATUS_INVALID_PARAMETER_MIX

另请注意 - 如果您需要获取令牌,则不需要使用 PROCESS_ALL_ACCESS 打开进程。 PROCESS_QUERY_LIMITED_INFORMATION 就够了


真的api不会做大魔法。它 return 给你

AppContainerNamedObjects\<Sid>

路径,其中应用程序容器 sid 的字符串形式。(有些像 S-1-15-2-...