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

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:
- Create a new project of type "Empty Project (C++)".
- Add a new source file (main.cpp) and insert the above code.
- Adjust project properties: Linker > System > Subsystem to "WINDOWS".
- 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.