How can I "unelevate" a process - c++

I have an elevated process that has been started after the user has answered "Yes" in the UAC dialog.
The process is started fine, everything works as expected.
Now I need to "unelevate" that process at some point, in other words the process should become not elevated exactly as if it had been launched normally by the user.
Sample scenario
User A is logged on
User A launches process P1 which will be elevated via UAC
Elevated process P1 lauchches process P2 and P2 should'nt be elevated and should run again under user A.
Is there a way to do this?

the elevated process have linked token - it refers to non elevated user session. we can use this linked token in 2 ways:
first way:
get it as TokenPrimary (for this we need have SE_TCB_PRIVILEGE
when we query this token)
call CreateProcessAsUser with this token. for this we need also
SE_ASSIGNPRIMARYTOKEN_PRIVILEGE and SE_INCREASE_QUOTA_PRIVILEGE
for get all this privileges - enumerate processes, query it tokens,
and if process token have all this 3 privileges - impersonate with
it, before call CreateProcessAsUser. because elevated token have
SE_DEBUG_PRIVILEGE the task is possible
second way:
query the logon session id from linked token (AuthenticationId
from TOKEN_STATISTICS)
found process with the same AuthenticationId in process token.
use this process as parent process by help
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS
implementation for way 1:
static volatile UCHAR guz;
ULONG RunNonElevated(HANDLE hToken, HANDLE hMyToken, PCWSTR lpApplicationName, PWSTR lpCommandLine)
{
ULONG err;
PVOID stack = alloca(guz);
ULONG cb = 0, rcb = FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges[SE_MAX_WELL_KNOWN_PRIVILEGE]);
union {
PVOID buf;
PTOKEN_PRIVILEGES ptp;
};
do
{
if (cb < rcb)
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
if (GetTokenInformation(hToken, TokenPrivileges, buf, cb, &rcb))
{
if (ULONG PrivilegeCount = ptp->PrivilegeCount)
{
int n = 3;
BOOL fAdjust = FALSE;
PLUID_AND_ATTRIBUTES Privileges = ptp->Privileges;
do
{
switch (Privileges->Luid.LowPart)
{
case SE_ASSIGNPRIMARYTOKEN_PRIVILEGE:
case SE_INCREASE_QUOTA_PRIVILEGE:
case SE_TCB_PRIVILEGE:
if (!(Privileges->Attributes & SE_PRIVILEGE_ENABLED))
{
Privileges->Attributes |= SE_PRIVILEGE_ENABLED;
fAdjust = TRUE;
}
if (!--n)
{
err = NOERROR;
if (DuplicateTokenEx(hToken,
TOKEN_ADJUST_PRIVILEGES|TOKEN_IMPERSONATE,
0, SecurityImpersonation, TokenImpersonation,
&hToken))
{
if (fAdjust)
{
AdjustTokenPrivileges(hToken, FALSE, ptp, rcb, NULL, NULL);
err = GetLastError();
}
if (err == NOERROR)
{
if (SetThreadToken(0, hToken))
{
TOKEN_LINKED_TOKEN tlt;
if (GetTokenInformation(hMyToken, TokenLinkedToken, &tlt, sizeof(tlt), &rcb))
{
STARTUPINFO si = { sizeof (si) };
PROCESS_INFORMATION pi;
if (!CreateProcessAsUserW(tlt.LinkedToken, lpApplicationName, lpCommandLine,
NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
{
err = GetLastError();
}
CloseHandle(tlt.LinkedToken);
if (err == NOERROR)
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
}
else
{
err = GetLastError();
}
SetThreadToken(0, 0);
}
else
{
err = GetLastError();
}
}
CloseHandle(hToken);
}
else
{
err = GetLastError();
}
return err;
}
}
} while (Privileges++, --PrivilegeCount);
}
return ERROR_NOT_FOUND;
}
} while ((err = GetLastError()) == ERROR_INSUFFICIENT_BUFFER);
return err;
}
ULONG RunNonElevated(HANDLE hMyToken, PCWSTR lpApplicationName, PWSTR lpCommandLine)
{
static TOKEN_PRIVILEGES tp = {
1, { { { SE_DEBUG_PRIVILEGE } , SE_PRIVILEGE_ENABLED } }
};
AdjustTokenPrivileges(hMyToken, FALSE, &tp, sizeof(tp), NULL, NULL);
ULONG err = NOERROR;
// much more effective of course use NtQuerySystemInformation(SystemProcessesAndThreadsInformation) here
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0), hToken;
if (hSnapshot != INVALID_HANDLE_VALUE)
{
PROCESSENTRY32W pe = { sizeof(pe) };
if (Process32FirstW(hSnapshot, &pe))
{
err = ERROR_NOT_FOUND;
do
{
if (pe.th32ProcessID && pe.th32ParentProcessID)
{
if (HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pe.th32ProcessID))
{
if (OpenProcessToken(hProcess, TOKEN_QUERY|TOKEN_DUPLICATE, &hToken))
{
err = RunNonElevated(hToken, hMyToken, lpApplicationName, lpCommandLine);
CloseHandle(hToken);
}
else
{
err = GetLastError();
}
CloseHandle(hProcess);
}
else
{
err = GetLastError();
}
}
} while (err && Process32NextW(hSnapshot, &pe));
}
else
{
err = GetLastError();
}
CloseHandle(hSnapshot);
}
return err;
}
ULONG RunNonElevated(PCWSTR lpApplicationName, PWSTR lpCommandLine)
{
HANDLE hToken;
ULONG err = NOERROR;
if (OpenProcessToken(NtCurrentProcess(), TOKEN_QUERY|TOKEN_ADJUST_PRIVILEGES, &hToken))
{
TOKEN_ELEVATION_TYPE tet;
ULONG rcb;
if (GetTokenInformation(hToken, ::TokenElevationType, &tet, sizeof(tet), &rcb))
{
if (tet == TokenElevationTypeFull)
{
RunNonElevated(hToken, lpApplicationName, lpCommandLine);
}
else
{
err = ERROR_ALREADY_ASSIGNED;
}
}
else
{
err = GetLastError();
}
CloseHandle(hToken);
}
else
{
err = GetLastError();
}
return err;
}
implementation for way 2:
ULONG CreateProcessEx(HANDLE hProcess,
PCWSTR lpApplicationName,
PWSTR lpCommandLine)
{
SIZE_T Size = 0;
STARTUPINFOEX si = { sizeof(si) };
PROCESS_INFORMATION pi;
InitializeProcThreadAttributeList(0, 1, 0, &Size);
ULONG err = GetLastError();
if (err = ERROR_INSUFFICIENT_BUFFER)
{
si.lpAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST)alloca(Size);
if (InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &Size))
{
if (UpdateProcThreadAttribute(si.lpAttributeList, 0,
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &hProcess, sizeof(hProcess), 0, 0) &&
CreateProcessW(lpApplicationName, lpCommandLine, 0, 0, 0,
EXTENDED_STARTUPINFO_PRESENT, 0, 0, &si.StartupInfo, &pi))
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
else
{
err = GetLastError();
}
DeleteProcThreadAttributeList(si.lpAttributeList);
}
else
{
err = GetLastError();
}
}
else
{
err = GetLastError();
}
return err;
}
ULONG CreateProcessEx(LUID AuthenticationId,
PCWSTR lpApplicationName,
PWSTR lpCommandLine)
{
ULONG err = ERROR_NOT_FOUND;
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot != INVALID_HANDLE_VALUE)
{
PROCESSENTRY32W pe = { sizeof(pe) };
ULONG rcb;
if (Process32First(hSnapshot, &pe))
{
err = ERROR_NOT_FOUND;
BOOL found = FALSE;
do
{
if (pe.th32ProcessID && pe.th32ParentProcessID)
{
if (HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION|PROCESS_CREATE_PROCESS, FALSE, pe.th32ProcessID))
{
HANDLE hToken;
if (OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
{
TOKEN_STATISTICS ts;
if (GetTokenInformation(hToken, TokenStatistics, &ts, sizeof(ts), &rcb))
{
if (ts.AuthenticationId.LowPart == AuthenticationId.LowPart &&
ts.AuthenticationId.HighPart == AuthenticationId.HighPart)
{
found = TRUE;
err = CreateProcessEx(hProcess,
lpApplicationName,
lpCommandLine);
}
}
CloseHandle(hToken);
}
CloseHandle(hProcess);
}
}
} while (!found && Process32Next(hSnapshot, &pe));
}
else
{
err = GetLastError();
}
CloseHandle(hSnapshot);
}
else
{
err = GetLastError();
}
return err;
}
ULONG CreateProcessEx(PCWSTR lpApplicationName,
PWSTR lpCommandLine)
{
HANDLE hToken;
ULONG err = NOERROR;
if (OpenProcessToken(NtCurrentProcess(), TOKEN_QUERY, &hToken))
{
union {
TOKEN_ELEVATION_TYPE tet;
TOKEN_LINKED_TOKEN tlt;
};
ULONG rcb;
if (GetTokenInformation(hToken, TokenElevationType, &tet, sizeof(tet), &rcb))
{
if (tet == TokenElevationTypeFull)
{
if (GetTokenInformation(hToken, TokenLinkedToken, &tlt, sizeof(tlt), &rcb))
{
TOKEN_STATISTICS ts;
BOOL fOk = GetTokenInformation(tlt.LinkedToken, TokenStatistics, &ts, sizeof(ts), &rcb);
CloseHandle(tlt.LinkedToken);
if (fOk)
{
err = CreateProcessEx(ts.AuthenticationId,
lpApplicationName,
lpCommandLine);
}
else
{
err = GetLastError();
}
}
else
{
err = GetLastError();
}
}
else
{
err = ERROR_ALREADY_ASSIGNED;
}
}
else
{
err = GetLastError();
}
CloseHandle(hToken);
}
else
{
err = GetLastError();
}
return err;
}
and test:
WCHAR ApplicationName[MAX_PATH];
if (GetEnvironmentVariableW(L"ComSpec", ApplicationName, RTL_NUMBER_OF(ApplicationName)))
{
WCHAR cmdline[] = L"cmd.exe /k whoami /priv /groups\r\n";
CreateProcessEx(ApplicationName, cmdline);
RunNonElevated(ApplicationName, cmdline);
}
for way #2 theoretical we can not found process with same logon id (AuthenticationId) as in our linked token. but way #1 always must work. always exist system process which have SeTcbPrivilege (for get primary form of linked token) + SeAssignPrimaryTokenPrivilege (for CreateProcessAsUser) (SeIncreaseQuotaPrivilege listen in msdn as typical require for CreateProcessAsUser but in my test this worked even if this privilege not enabled ). however all system processes (running as LocalSystem) have this 3 privilege in token (begin from smss.exe) and some system processes always run in system.
so way #1 must never fail and preferred. also we can here use for example inherited handles from our process, for interact with child process. this is impossible in way #2. it shown rather for completeness of the picture
at begin we check TOKEN_ELEVATION_TYPE and do job, only if it is TokenElevationTypeFull. in case TokenElevationTypeLimited we not elevated process - so nothing todo.
case TokenElevationTypeDefault mean or UAC if off (LUA disabled) or we run as built-in Administrator, and lua not filter tokens for this account (so all processes is "elevated" or more exactly it tokens not filtered via CreateRestrictedToken(..LUA_TOKEN..) ) - in this case also no sense try run "not elevated" process under this user

Related

Shell Execute From Explorer

Trying to follow this sample:
https://devblogs.microsoft.com/oldnewthing/20040520-00/?p=39243
void ShellExecuteFromExplorer(PCWSTR pszFile, PCWSTR pszParameters = nullptr, PCWSTR pszDirectory = nullptr, PCWSTR pszOperation = nullptr, int nShowCmd = SW_SHOWNORMAL)
{
CComPtr<IShellFolderViewDual> spFolderView;
GetDesktopAutomationObject(IID_PPV_ARGS(&spFolderView));
CComPtr<IDispatch> spdispShell;
spFolderView->get_Application(&spdispShell);
CComQIPtr<IShellDispatch2>(spdispShell)
->ShellExecute(CComBSTR(pszFile),
CComVariant(pszParameters ? pszParameters : L""),
CComVariant(pszDirectory ? pszDirectory : L""),
CComVariant(pszOperation ? pszOperation : L""),
CComVariant(nShowCmd));
}
In this case, how do i retrieve the pid from the process being launch from the ShellExecute?
not need use this way for create unelevated process. for instance we can use shell process (explorer) as parent for new process with help PROC_THREAD_ATTRIBUTE_PARENT_PROCESS. example of code
ULONG ExecFromShell(_In_opt_ PCWSTR lpApplicationName,
_Inout_opt_ PWSTR lpCommandLine)
{
if (HWND hwnd = GetShellWindow())
{
ULONG dwProcessId, dwThreadId;
if (dwThreadId = GetWindowThreadProcessId(hwnd, &dwProcessId))
{
if (HANDLE hProcess = OpenProcess(PROCESS_CREATE_PROCESS|READ_CONTROL,
FALSE, dwProcessId))
{
PROCESS_INFORMATION pi;
STARTUPINFOEXW si { sizeof(si)};
SIZE_T s = 0;
ULONG dwError;
__0:
switch (dwError = InitializeProcThreadAttributeList(
si.lpAttributeList, 1, 0, &s) ? NOERROR : GetLastError())
{
case ERROR_INSUFFICIENT_BUFFER:
if (!si.lpAttributeList)
{
si.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)alloca(s);
goto __0;
}
break;
case NOERROR:
if (UpdateProcThreadAttribute(si.lpAttributeList, 0,
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
&hProcess, sizeof(hProcess), 0, 0))
{
ULONG cb = 0;
SECURITY_ATTRIBUTES sa = { sizeof(sa) };
__1:
switch (GetKernelObjectSecurity(hProcess,
DACL_SECURITY_INFORMATION|LABEL_SECURITY_INFORMATION,
sa.lpSecurityDescriptor, cb, &cb) ? NOERROR : GetLastError())
{
case ERROR_INSUFFICIENT_BUFFER:
if (!sa.lpSecurityDescriptor)
{
sa.lpSecurityDescriptor = alloca(cb);
goto __1;
}
break;
case NOERROR:
if (CreateProcessW(lpApplicationName, lpCommandLine, &sa, &sa,
FALSE, EXTENDED_STARTUPINFO_PRESENT, 0, 0, &si.StartupInfo, &pi))
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
else
{
dwError = GetLastError();
}
break;
}
}
else
{
dwError = GetLastError();
}
DeleteProcThreadAttributeList(si.lpAttributeList);
break;
}
CloseHandle(hProcess);
return dwError;
}
return GetLastError();
}
}
return ERROR_NOT_FOUND;
}
In this case, how do i retrieve the pid from the process being launch from the ShellExecute?
You can't. just like ::ShellExecute(), IShellDispatch2::ShellExecute() simply does not provide any information about the new process.

Can I mapping network drive in very special way using winapi?

I have to start my application with admin privileges ( very important ).
When I execute this code without admin privileges everything is perfect. There is an icon in MyComputer.
NETRESOURCE nrServer;
memset(&nrServer, 0, sizeof (NETRESOURCE));
nrServer.dwType = RESOURCETYPE_ANY;
nrServer.lpLocalName = L"S:";
nrServer.lpRemoteName = L"\\\\192.168.32.36\\folderName";
nrServer.lpProvider = L"";
auto dwError = WNetAddConnection2(&nrServer, L"user", L"pass", 0);
But when I execute this code above in application with admin privileges, there is no icon in MyComputer.
I think that can be usefull:
Mapped network drives are not showing in My Computer
Is there any way to execute winapi function as not admin when my application has admin privileges?
from WNetAddConnection2W
the WNet functions create and delete network drive letters in the MS-DOS device namespace associated with a logon session because
MS-DOS devices are identified by AuthenticationID (a locally unique
identifier, or LUID, associated with a logon session.)
also
if a code that runs as LocalSystem calls the WNetAddConnection2 function, then the mapped drive is visible to all
user logon sessions.
technically this mean if code run as LocalSystem in the \GLOBAL??\ folder will be create symbolic link to network disk. otherwise link will be created under
\Sessions\0\DosDevices\<token LogonId>\
and will be visible only for threads(processes) which have the same LogonId in token
if your code have admin privileges - it usually (almost always) have debug privileges. with this we can open process with LocalSystem token and impersonate it before call WNetAddConnection2.
possible also get TCB privilege and after this call WTSQueryUserToken, convert primary token to impersonation token, via DuplicateToken, and impersonate - SetThreadToken. and call WNetAddConnection2 finally.
ok. i try first simply impersonate to LocalSystem
let we have function
NTSTATUS ImpersonateSystemOrTcbToken(bool bTcb);
which set LocalSystem or token with Tcb privileges to current thread (as far i know all LocalSystem tokens have TCB privilege but anyway write 2 different code for get exactly token with TCB or with LocalSystem)
and
HRESULT AdjustDebugPrivilegesToThread();
which enable debug privileges in current thread token (it must exist in admin token)
in this case code can be next:
inline HRESULT BOOL_TO_HRESULT(BOOL f)
{
return f ? NOERROR : HRESULT_FROM_WIN32(GetLastError());
}
HRESULT MapRemoteDrive(PCWSTR local, PCWSTR remote, PCWSTR username, PCWSTR password)
{
NETRESOURCEW nr = {
0, RESOURCETYPE_DISK, 0, 0, const_cast<PWSTR>(local), const_cast<PWSTR>(remote)
};
return HRESULT_FROM_WIN32(WNetAddConnection2W(&nr, password, username, 0));
}
HRESULT MapRemoteDriveEx1(PCWSTR local, PCWSTR remote, PCWSTR username, PCWSTR password)
{
HRESULT hr = BOOL_TO_HRESULT(ImpersonateSelf(::SecurityImpersonation));
if (SUCCEEDED(hr))
{
if (SUCCEEDED(hr = AdjustDebugPrivilegesToThread()) &&
SUCCEEDED(hr = HRESULT_FROM_NT(ImpersonateSystemOrTcbToken(false))))
{
hr = MapRemoteDrive(local, remote, username, password);
// WNetCancelConnection2W(local, 0, TRUE);
}
SetThreadToken(0, 0);
}
return hr;
}
code work ok and really network location created, but with next view:
despite this - drive is browsed correct on click. i not research why is Disconected word in description. but possible some problems with permissions here
if try create drive for concrete LUID, code will be more complex
HRESULT MapRemoteDriveEx2(PCWSTR local, PCWSTR remote, PCWSTR username, PCWSTR password)
{
HRESULT hr = BOOL_TO_HRESULT(ImpersonateSelf(::SecurityImpersonation));
if (SUCCEEDED(hr))
{
HANDLE hToken, hImpToken;
if (SUCCEEDED(hr = AdjustDebugPrivilegesToThread()) &&
SUCCEEDED(hr = HRESULT_FROM_NT(ImpersonateSystemOrTcbToken(true))) &&
SUCCEEDED(hr = BOOL_TO_HRESULT(WTSQueryUserToken(WTSGetActiveConsoleSessionId(), &hToken))))
{
hr = BOOL_TO_HRESULT(DuplicateToken(hToken, ::SecurityImpersonation, &hImpToken));
CloseHandle(hToken);
if (SUCCEEDED(hr))
{
hr = BOOL_TO_HRESULT(SetThreadToken(0, hImpToken));
CloseHandle(hImpToken);
if (SUCCEEDED(hr))
{
hr = MapRemoteDrive(local, remote, username, password);
// WNetCancelConnection2W(local, 0, TRUE);
}
}
}
SetThreadToken(0, 0);
}
return hr;
}
with this result full ok
now code for util functions:
HRESULT AdjustDebugPrivilegesToThread()
{
ULONG dwError;
HANDLE hToken;
if (OpenThreadToken(NtCurrentThread(), TOKEN_ADJUST_PRIVILEGES, TRUE, &hToken))
{
static const ::TOKEN_PRIVILEGES tp = { 1, { { { SE_DEBUG_PRIVILEGE } } } };
AdjustTokenPrivileges(hToken, FALSE, const_cast<::PTOKEN_PRIVILEGES>(&tp), 0, 0, 0);
dwError = GetLastError();
CloseHandle(hToken);
}
else
{
dwError = GetLastError();
}
return HRESULT_FROM_WIN32(dwError);
}
and..
NTSTATUS GetSystemToken(PVOID buf)
{
NTSTATUS status;
union {
PVOID pv;
PBYTE pb;
PSYSTEM_PROCESS_INFORMATION pspi;
};
pv = buf;
ULONG NextEntryOffset = 0;
do
{
pb += NextEntryOffset;
HANDLE hProcess, hToken, hNewToken;
CLIENT_ID ClientId = { pspi->UniqueProcessId };
if (ClientId.UniqueProcess)
{
static SECURITY_QUALITY_OF_SERVICE sqos = {
sizeof sqos, SecurityImpersonation, SECURITY_DYNAMIC_TRACKING, FALSE
};
static OBJECT_ATTRIBUTES soa = { sizeof(soa), 0, 0, 0, 0, &sqos };
if (0 <= NtOpenProcess(&hProcess, PROCESS_QUERY_LIMITED_INFORMATION, &zoa, &ClientId))
{
status = NtOpenProcessToken(hProcess, TOKEN_QUERY|TOKEN_DUPLICATE, &hToken);
NtClose(hProcess);
if (0 <= status)
{
ULONG rcb;
TOKEN_STATISTICS ts;
static const LUID SystemLuid = SYSTEM_LUID;
status = -1;
if (0 <= NtQueryInformationToken(hToken, TokenStatistics, &ts, sizeof(ts), &rcb) &&
ts.AuthenticationId.LowPart == SystemLuid.LowPart &&
ts.AuthenticationId.HighPart == SystemLuid.HighPart)
{
status = NtDuplicateToken(hToken, TOKEN_IMPERSONATE,
&soa, FALSE, TokenImpersonation, &hNewToken);
}
NtClose(hToken);
if (0 <= status)
{
status = NtSetInformationThread(NtCurrentThread(), ThreadImpersonationToken, &hNewToken, sizeof(hNewToken));
NtClose(hNewToken);
return status;
}
}
}
}
} while (NextEntryOffset = pspi->NextEntryOffset);
return STATUS_UNSUCCESSFUL;
}
NTSTATUS GetTcbToken(PVOID buf)
{
NTSTATUS status;
union {
PVOID pv;
PBYTE pb;
PSYSTEM_PROCESS_INFORMATION pspi;
};
pv = buf;
ULONG NextEntryOffset = 0;
do
{
pb += NextEntryOffset;
HANDLE hProcess, hToken, hNewToken;
if (pspi->InheritedFromUniqueProcessId && pspi->UniqueProcessId)
{
static SECURITY_QUALITY_OF_SERVICE sqos = {
sizeof sqos, SecurityImpersonation, SECURITY_DYNAMIC_TRACKING, FALSE
};
static OBJECT_ATTRIBUTES soa = { sizeof(soa), 0, 0, 0, 0, &sqos };
CLIENT_ID ClientId = { pspi->UniqueProcessId };
if (0 <= NtOpenProcess(&hProcess, PROCESS_QUERY_LIMITED_INFORMATION, &zoa, &ClientId))
{
status = NtOpenProcessToken(hProcess, TOKEN_DUPLICATE, &hToken);
NtClose(hProcess);
if (0 <= status)
{
status = NtDuplicateToken(hToken, TOKEN_ADJUST_PRIVILEGES|TOKEN_IMPERSONATE,
&soa, FALSE, TokenImpersonation, &hNewToken);
NtClose(hToken);
if (0 <= status)
{
static const TOKEN_PRIVILEGES tp = { 1, { { { SE_DEBUG_PRIVILEGE } } } };
status = NtAdjustPrivilegesToken(hNewToken, FALSE, const_cast<PTOKEN_PRIVILEGES>(&tp), 0, 0, 0);
if (STATUS_SUCCESS == status)
{
status = NtSetInformationThread(NtCurrentThread(), ThreadImpersonationToken, &hNewToken, sizeof(hNewToken));
}
NtClose(hNewToken);
if (STATUS_SUCCESS == status)
{
return STATUS_SUCCESS;
}
}
}
}
}
} while (NextEntryOffset = pspi->NextEntryOffset);
return STATUS_UNSUCCESSFUL;
}
NTSTATUS ImpersonateSystemOrTcbToken(bool bTcb)
{
NTSTATUS status;
ULONG cb = 0x10000;
do
{
status = STATUS_INSUFFICIENT_RESOURCES;
if (PBYTE buf = new BYTE[cb += 0x1000])
{
if (0 <= (status = NtQuerySystemInformation(SystemProcessInformation, buf, cb, &cb)))
{
status = (bTcb ? GetTcbToken : GetSystemToken)(buf);
if (status == STATUS_INFO_LENGTH_MISMATCH)
{
status = STATUS_UNSUCCESSFUL;
}
}
delete [] buf;
}
} while(status == STATUS_INFO_LENGTH_MISMATCH);
return status;
}

Thread32First returning bogus thread id

Thread32First returns true so I know the function isn't failing. However, it keeps returning the same value for the th32ThreadID data member of THREADENTRY32, 0. Doesn't matter what process I try it on, it continues to be the same thread id: 0.
Here's my code:
#include <windows.h>
#include <tlhelp32.h>
#include <stdio.h>
BOOL EnableDebugPriv(HANDLE proc);
int main()
{
HANDLE hSnapshot;
HANDLE hThread;
THREADENTRY32 thread32;
DWORD dwPid = 15404;
EnableDebugPriv(OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid));
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, dwPid);
if(hSnapshot == INVALID_HANDLE_VALUE)
{
printf("Error getting snapshot: %lu\n", GetLastError());
return 1;
}
thread32.dwSize = sizeof(THREADENTRY32);
if(!Thread32First(hSnapshot, &thread32))
{
printf("Error thread32first\n");
return 1;
}
printf("Thread Id: %lu\n", thread32.th32ThreadID);
hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, thread32.th32ThreadID);
if(hThread == NULL)
{
printf("Error getting handle: %lu\n", GetLastError());
return 1;
}
return 0;
}
BOOL EnableDebugPriv(HANDLE proc)
{
HANDLE hToken;
LUID sedebugnameValue;
TOKEN_PRIVILEGES tkp;
// pass our opened process handle
OpenProcessToken(proc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &sedebugnameValue);
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Luid = sedebugnameValue;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if(AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof tkp, NULL, NULL))
{
CloseHandle(hToken);
return TRUE;
}
else
{
MessageBox(NULL,"Bro, you gotta be an admin to set privs like this", "Shit", MB_OK);
CloseHandle(hToken);
return FALSE;
}
}
Note: I am aware that using a static process ID like I am could cause problems. However, I make sure the process is still running with the same process ID. That's not the problem.

Elevating process produces a shell (2 total) but want only 1 total?

Following my previous question here, now I get two shells - one parent (not elevated), and a child shell (elevated). What does one need to do to the code to have instead one shell only that's elevated? E.g. how about somehow closing the parent process?
BOOL IsAppRunningAsAdminMode()
{
BOOL fIsRunAsAdmin = FALSE;
DWORD dwError = ERROR_SUCCESS;
PSID pAdministratorsGroup = NULL;
// Allocate and initialize a SID of the administrators group.
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
if (!AllocateAndInitializeSid(
&NtAuthority,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&pAdministratorsGroup))
{
dwError = GetLastError();
goto Cleanup;
}
// Determine whether the SID of administrators group is enabled in
// the primary access token of the process.
if (!CheckTokenMembership(NULL, pAdministratorsGroup, &fIsRunAsAdmin))
{
dwError = GetLastError();
goto Cleanup;
}
Cleanup:
// Centralized cleanup for all allocated resources.
if (pAdministratorsGroup)
{
FreeSid(pAdministratorsGroup);
pAdministratorsGroup = NULL;
}
// Throw the error if something failed in the function.
if (ERROR_SUCCESS != dwError)
{
throw dwError;
}
return fIsRunAsAdmin;
}
int main() {
bool fIsRunAsAdmin = IsAppRunningAsAdminMode();
if (fIsRunAsAdmin == false)
{
wchar_t szPath[MAX_PATH];
if (GetModuleFileName(NULL, szPath, ARRAYSIZE(szPath)))
{
// Launch itself as admin
SHELLEXECUTEINFO sei = { sizeof(sei) };
sei.lpVerb = L"runas";
sei.lpFile = szPath;
sei.hwnd = NULL;
sei.nShow = SW_NORMAL;
if (!ShellExecuteEx(&sei))
{
DWORD dwError = GetLastError();
if (dwError == ERROR_CANCELLED)
{
// The user refused to allow privileges elevation.
std::cout << "User did not allow elevation" << std::endl;
}
}
}
}
else {
//do nothing since process already elevated
}
//other code following omitted

Do I need SE_SHUTDOWN_NAME privilege to log off a user?

I need to log off a user from a C++ program. I use ExitWindowsEx API for that, but I'm not sure from the documentation if I need any special privileges for that?
You do. Here's an example
bool ShutdownWindows(void)
{
HANDLE hToken = NULL;
TOKEN_PRIVILEGES tkp = {0};
bool bRet = false;
// Get a token for this process.
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
if (LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid)) {
tkp.PrivilegeCount = 1; // one privilege to set
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
// Get the shutdown privilege for this process.
if (AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, 0)) {
::CloseHandle(hToken);
if (ERROR_SUCCESS == GetLastError()) {
DWORD dwFlags = EWX_POWEROFF;
DWORD dwReason = SHTDN_REASON_MAJOR_SYSTEM;
if (ExitWindowsEx(dwFlags, dwReason)) {
bRet = true;
}
}
}
}
}
return bRet;
} // ShutdownWindows