How to Start a GUI App with Restricted Token in C++

Introduction Starting GUI applications with tailored permissions can enhance security, especially in multi-user environments. If you're looking to launch applications like Notepad.exe or Calc.exe with restricted tokens that limit permissions, you may encounter errors such as 'The application was unable to start correctly (0xc0000022)'. This article explores the implementation of the CreateRestrictedToken() API in C++ and addresses potential pitfalls leading to this error. Understanding the Error 0xc0000022 The error code 0xc0000022 typically indicates a permissions issue when trying to execute a process. It may be a result of inadequate rights or insufficient privileges in the context of the user under which the application is running. If CreateRestrictedToken() fails or if the specified token lacks the necessary permissions, applications like Notepad will not start as expected. Implementation Overview The provided C++ code, which utilizes WinAPI to create a GUI and launch Notepad with a restricted token, is a complex but manageable task. Below, you'll find a refined version of the code along with necessary explanations. C++ Code Example Here’s a complete implementation to successfully launch Notepad.exe with a restricted token: #include #include #include #include #include #include #include #include #ifndef UNICODE typedef std::string STRING; #else typedef std::wstring STRING; #endif // Global Variables: HINSTANCE hInst; STRING szTitle = _T("Restricted Notepad"); STRING szWindowClass = _T("WINDOW_CLASS"); // Function declarations ATOM MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { MyRegisterClass(hInstance); if (!InitInstance(hInstance, nCmdShow)) { return FALSE; } MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int)msg.wParam; } ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.hInstance = hInstance; wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wcex.lpszClassName = szWindowClass.c_str(); return RegisterClassEx(&wcex); } BOOL launchNotepad(HWND hWnd) { HANDLE hToken = NULL; HANDLE hRestrictedToken = NULL; std::vector groupSids; std::vector sidAndAttributes; // Specify group for restrictions std::vector groupNames = { _T("Users") }; // Add other groups as needed SID_NAME_USE sidType; // Open the current process token if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken)) { MessageBox(NULL, _T("OpenProcessToken failed!"), _T("Error"), MB_OK); return false; } // Lookup SIDs for specified groups for (const auto& groupName : groupNames) { PSID pGroupSid = NULL; STRING domainName = _T("."); DWORD domainNameSize = static_cast(domainName.size()); DWORD dwSize = 0; // Get SID if (LookupAccountName(NULL, groupName.c_str(), NULL, &dwSize, &domainName[0], &domainNameSize, &sidType) || GetLastError() == ERROR_INSUFFICIENT_BUFFER) { pGroupSid = (PSID)malloc(dwSize); if (!LookupAccountName(NULL, groupName.c_str(), pGroupSid, &dwSize, &domainName[0], &domainNameSize, &sidType)) { MessageBox(NULL, _T("Failed to lookup account name!"), _T("Error"), MB_OK); free(pGroupSid); continue; } groupSids.push_back(pGroupSid); sidAndAttributes.push_back({ pGroupSid, 0 }); } } // Create restricted token if (!CreateRestrictedToken(hToken, 0, 0, NULL, static_cast(sidAndAttributes.size()), sidAndAttributes.data(), &hRestrictedToken)) { MessageBox(NULL, _T("Failed to create restricted token!"), _T("Error"), MB_OK); return false; } // Start Notepad with restricted token STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); if (!CreateProcessAsUser(hRestrictedToken, NULL, _T("notepad.exe"), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) { MessageBox(NULL, _T("Failed to create process as user!"), _T("Error"), MB_OK); return false; } // Cleanup CloseHandle(hToken); CloseHandle(hRestrictedToken); return TRUE; } BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd; if (!CreateWindowEx(0, szWindowClass.c_str(), szTitle.c_str(), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL)) { MessageBox(NULL, _T("Window Creation Failed"), _T("Error"), MB_OK); return FALSE; } return launchNotepad(hWnd); } LRESULT CALLBA

May 6, 2025 - 13:19
 0
How to Start a GUI App with Restricted Token in C++

Introduction

Starting GUI applications with tailored permissions can enhance security, especially in multi-user environments. If you're looking to launch applications like Notepad.exe or Calc.exe with restricted tokens that limit permissions, you may encounter errors such as 'The application was unable to start correctly (0xc0000022)'. This article explores the implementation of the CreateRestrictedToken() API in C++ and addresses potential pitfalls leading to this error.

Understanding the Error 0xc0000022

The error code 0xc0000022 typically indicates a permissions issue when trying to execute a process. It may be a result of inadequate rights or insufficient privileges in the context of the user under which the application is running. If CreateRestrictedToken() fails or if the specified token lacks the necessary permissions, applications like Notepad will not start as expected.

Implementation Overview

The provided C++ code, which utilizes WinAPI to create a GUI and launch Notepad with a restricted token, is a complex but manageable task. Below, you'll find a refined version of the code along with necessary explanations.

C++ Code Example

Here’s a complete implementation to successfully launch Notepad.exe with a restricted token:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#ifndef UNICODE
typedef std::string STRING;
#else
typedef std::wstring STRING;
#endif

// Global Variables:
HINSTANCE hInst;
STRING szTitle = _T("Restricted Notepad");
STRING szWindowClass = _T("WINDOW_CLASS");

// Function declarations
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) {
    MyRegisterClass(hInstance);
    if (!InitInstance(hInstance, nCmdShow)) { return FALSE; }
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return (int)msg.wParam;
}

ATOM MyRegisterClass(HINSTANCE hInstance) {
    WNDCLASSEX wcex;
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.hInstance = hInstance;
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszClassName = szWindowClass.c_str();
    return RegisterClassEx(&wcex);
}

BOOL launchNotepad(HWND hWnd) {
    HANDLE hToken = NULL;
    HANDLE hRestrictedToken = NULL;
    std::vector groupSids;
    std::vector sidAndAttributes;
    // Specify group for restrictions
    std::vector groupNames = { _T("Users") }; // Add other groups as needed
    SID_NAME_USE sidType;

    // Open the current process token
    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken)) {
        MessageBox(NULL, _T("OpenProcessToken failed!"), _T("Error"), MB_OK);
        return false;
    }

    // Lookup SIDs for specified groups
    for (const auto& groupName : groupNames) {
        PSID pGroupSid = NULL;
        STRING domainName = _T(".");
        DWORD domainNameSize = static_cast(domainName.size());
        DWORD dwSize = 0;

        // Get SID
        if (LookupAccountName(NULL, groupName.c_str(), NULL, &dwSize, &domainName[0], &domainNameSize, &sidType) || GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
            pGroupSid = (PSID)malloc(dwSize);
            if (!LookupAccountName(NULL, groupName.c_str(), pGroupSid, &dwSize, &domainName[0], &domainNameSize, &sidType)) {
                MessageBox(NULL, _T("Failed to lookup account name!"), _T("Error"), MB_OK);
                free(pGroupSid);
                continue;
            }
            groupSids.push_back(pGroupSid);
            sidAndAttributes.push_back({ pGroupSid, 0 });
        }
    }

    // Create restricted token 
    if (!CreateRestrictedToken(hToken, 0, 0, NULL, static_cast(sidAndAttributes.size()), sidAndAttributes.data(), &hRestrictedToken)) {
        MessageBox(NULL, _T("Failed to create restricted token!"), _T("Error"), MB_OK);
        return false;
    }

    // Start Notepad with restricted token
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    if (!CreateProcessAsUser(hRestrictedToken, NULL, _T("notepad.exe"), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
        MessageBox(NULL, _T("Failed to create process as user!"), _T("Error"), MB_OK);
        return false;
    }

    // Cleanup
    CloseHandle(hToken);
    CloseHandle(hRestrictedToken);
    return TRUE;
}

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) {
    HWND hWnd;
    if (!CreateWindowEx(0, szWindowClass.c_str(), szTitle.c_str(), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL)) {
        MessageBox(NULL, _T("Window Creation Failed"), _T("Error"), MB_OK);
        return FALSE;
    }
    return launchNotepad(hWnd);
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    switch (message) {
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

Compilation Instructions

Compile the above code using Visual Studio 2022 by following these steps:

  1. Create a new project of type "Empty Project (C++)".
  2. Add a new source file (main.cpp) and insert the above code.
  3. Adjust project properties: Linker > System > Subsystem to "WINDOWS".
  4. Build and run your project to see if it successfully launches Notepad with restricted permissions.

Troubleshooting Common Issues

  • Ensure Token Permissions: Make sure that your application has been granted sufficient permissions to create processes under the restricted token. Without the proper access rights, you will encounter errors.
  • Check SID Lookup Failures: If LookupAccountName fails to retrieve the SID for the group(s), your application will not create the restricted token correctly.
  • Code Errors: Compile the code with appropriate error checks to help identify issues in the handling of tokens and permissions.

Frequently Asked Questions

What is the significance of the restricted token?

A restricted token helps to enforce a principle of least privilege, allowing only necessary permissions for a user or application while preventing access to more sensitive resources.

Why does Notepad not start correctly when restrictions are applied?

This is often due to insufficient permissions or incorrect configurations when creating the restricted token. Verifying token permissions and ensuring SID lookups succeed is critical.

Can I launch other applications using this method?

Yes, you can adapt this approach to launch any executable with specified permissions, just ensure that the command line and associated SIDs are adjusted accordingly.

Conclusion

Implementing a restricted token for starting applications in C++ can enhance security but requires careful management of permissions and error handling. If you encounter issues, double-check token permissions and error outputs to guide your debugging process.