With the help of this great MSDN article, my first idea was to simply check if the process is using an elevated Administrator group, and using AdjustTokenGroups()
I would set the Administrator group to SE_GROUP_USE_FOR_DENY_ONLY
. Unfortunately though, we can't modify the administrator group on the currently running process as it also has the SE_GROUP_MANDATORY
attribute, which makes it inelligable for changing.
The MSDN document has this to say about it:
The AdjustTokenGroups
function cannot disable groups with the SE_GROUP_MANDATORY
attribute in the TOKEN_GROUPS
structure. Use CreateRestrictedToken
instead.
So, I am done the following code to achieve this;
bool _IsNewProcessLaunched()
{
HANDLE hToken = NULL;
bool hasRestarted = false;
if (!OpenProcessToken( GetCurrentProcess(),
TOKEN_ASSIGN_PRIMARY | TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ADJUST_GROUPS,
&hToken ))
{
return hasRestarted;
}
PSECURITY_DESCRIPTOR pSID = NULL;
SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_NT_AUTHORITY;
if(! AllocateAndInitializeSid( &SIDAuth, 2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&pSID) )
{
CloseHandle(hToken);
hToken = NULL;
return hasRestarted;
}
BOOL isAdmin = FALSE;
BOOL ok = CheckTokenMembership(NULL, pSID, &isAdmin);
// Create the SID structure for the administrator SID
SID_AND_ATTRIBUTES adminSID = {0};
adminSID.Sid = pSID;
// Create a restricted token which denies the administrator group
HANDLE restrictedToken;
CreateRestrictedToken(hToken,RESTR,DISABLE_MAX_PRIVILEGE,&adminSID,NULL,NULL,NULL,NULL,&restrictedToken);
//Create startup info
STARTUPINFO si = {0};
PROCESS_INFORMATION pi = {0};
si.lpDesktop = L"winsta0\\default";
si.cb = sizeof( si );
// Get the current executables name
TCHAR exePath[MAX_PATH];
GetModuleFileName(NULL,exePath,MAX_PATH);
// Start the new (non-administrator elevated) restricted process
if( CreateProcessAsUser(restrictedToken,exePath,NULL,NULL,NULL,TRUE,NORMAL_PRIORITY_CLASS,NULL,NULL,&si,&pi) == 0)
hasRestarted = false;
else
hasRestarted = true;
return hasRestarted;
}
But new process is still running as administrator rather than as the normal user.
How do I accomplish that?
Since you are restricting the calling process's token, the launched process will be run with the same user account as the calling process, just with restricted permissions. Remember, under UAC, administrators do not have full admin rights without elevation. CreateRestrictedToken()
creates a token with restricted permissions. So even though the user may be administrator does not mean the launched process will run with administrative rights.
BTW, there is a simplier API, known as the Safer API, that you can use instead of CreateRestrictedToken()
:
#include <WinSafer.h>
bool _IsNewProcessLaunched()
{
// Create the restricted token.
SAFER_LEVEL_HANDLE hLevel = NULL;
if (!SaferCreateLevel(SAFER_SCOPEID_USER, SAFER_LEVELID_NORMALUSER, SAFER_LEVEL_OPEN, &hLevel, NULL))
{
return false;
}
HANDLE hRestrictedToken = NULL;
if (!SaferComputeTokenFromLevel(hLevel, NULL, &hRestrictedToken, 0, NULL))
{
SaferCloseLevel(hLevel);
return false;
}
SaferCloseLevel(hLevel);
// Set the token to medium integrity.
TOKEN_MANDATORY_LABEL tml = {0};
tml.Label.Attributes = SE_GROUP_INTEGRITY;
// alternatively, use CreateWellKnownSid(WinMediumLabelSid) instead...
if (!ConvertStringSidToSid(TEXT("S-1-16-8192"), &(tml.Label.Sid)))
{
CloseHandle(hRestrictedToken);
return false;
}
if (!SetTokenInformation(hRestrictedToken, TokenIntegrityLevel, &tml, sizeof(tml) + GetLengthSid(tml.Label.Sid))))
{
LocalFree(tml.Label.Sid);
CloseHandle(hRestrictedToken);
return false;
}
LocalFree(tml.Label.Sid);
// Create startup info
STARTUPINFO si = {0};
si.cb = sizeof( si );
si.lpDesktop = L"winsta0\\default";
PROCESS_INFORMATION pi = {0};
// Get the current executable's name
TCHAR exePath[MAX_PATH+1] = {0};
GetModuleFileName(NULL, exePath, MAX_PATH);
// Start the new (non-elevated) restricted process
if (!CreateProcessAsUser(hRestrictedToken, exePath, NULL, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi))
{
CloseHandle(hRestrictedToken);
return false;
}
CloseHandle(hRestrictedToken);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return true;
}