How to terminate a system process in C++ program? - c++

I want to terminate one process, when I open this process, it built in my account and system.
Then I want to terminate this process using c++ program. My code could only terminate the process in my account, not in system. How could I change to terminate both?
My code is like:
PROCESSENTRY32 entry;
entry.dwSize = sizeof(PROCESSENTRY32);
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (Process32First(snapshot, &entry) == TRUE)
{
while (Process32Next(snapshot, &entry) == TRUE)
{
if (stricmp(entry.szExeFile, "tvnserver.exe") == 0)
{
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, entry.th32ProcessID);
TerminateProcess(hProcess, 1);
CloseHandle(hProcess);
}
}
}
CloseHandle(snapshot);

Unless you have the required privileges to obtain a handle to the process, you can use the administrators special debug privileges to terminate the target process
Assuming you run as administrator you can enable the SE_DEBUG_NAME privilege on the current process, and you'll be able to terminate the target process. Alternatively could also take ownership of the target process as documented here https://social.msdn.microsoft.com/Forums/vstudio/en-US/3fb9cb5d-8891-4ba6-a945-06009be51e40/terminating-a-process-from-system-account-when-privileges-are-not-sufficient?forum=vcgeneral
This uses the first solution, and works for me, when running it as administrator.
#include <stdio.h>
#include <windows.h>
#include <TlHelp32.h>
NTSTATUS EnablePrivilege(wchar_t *privilege)
{
HANDLE token;
TOKEN_PRIVILEGES *tp = NULL;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token))
goto error;
tp = (TOKEN_PRIVILEGES*)new char[offsetof(TOKEN_PRIVILEGES, Privileges[1])];
if (!tp)
goto error;
tp->PrivilegeCount = 1;
tp->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!LookupPrivilegeValueW(0, privilege, &tp->Privileges[0].Luid))
goto error;
if (!AdjustTokenPrivileges(token, 0, tp, 0, 0, 0) || GetLastError() != ERROR_SUCCESS)
goto error;
CloseHandle(token);
return 0x0;
error:
if(tp)
delete[] tp;
return 0xC0000001;
}
int main()
{
PROCESSENTRY32 entry;
entry.dwSize = sizeof(PROCESSENTRY32);
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (Process32First(snapshot, &entry) == TRUE)
{
while (Process32Next(snapshot, &entry) == TRUE)
{
if (wcscmp(entry.szExeFile, L"spoolsv.exe") == 0)
{
NTSTATUS result = EnablePrivilege(SE_DEBUG_NAME);
if (result != 0)
{
printf("could not set SE_DEBUG_NAME Privilege\n");
getchar();
return -1;
}
HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, entry.th32ProcessID);
if (hProcess == NULL)
{
printf("couldn't open process\n");
getchar();
return -1;
}
TerminateProcess(hProcess, 1);
CloseHandle(hProcess);
}
}
}
CloseHandle(snapshot);
printf("success!\n");
getchar();
return 0;
}

Related

A question about Windows process privilege escalation

I try to use OpenProcess() function to read some information from the "System" process. But I always get the error code 5 which means ERROR_ACCESS_DENIED.
I have used AdjustTokenPrivileges() function to get the debug privilege and it still not works. I just don't know what's wrong with my code. Here is part of my code:
int GetInfo()
{
PROCESSENTRY32 pe32{ sizeof(PROCESSENTRY32) };
THREADENTRY32 th32{ sizeof(THREADENTRY32) };
MODULEENTRY32 md32{ sizeof(MODULEENTRY32) };
PWCHAR Name;
DWORD id = 0;
int err = 0;
HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hProcessSnap == INVALID_HANDLE_VALUE)
{
return -1;
}
BOOL pResult = Process32First(hProcessSnap, &pe32);
while (pResult)
{
Name = pe32.szExeFile;
if (lstrcmpW(Name, L"System") == 0)
{
id = pe32.th32ProcessID;
PrivilegeEscalation();
HANDLE ProcessHandle = OpenProcess(PROCESS_QUERY_INFORMATION, false, pe32.th32ProcessID);
err = GetLastError();
cout << err << endl;
cout << "The thread number of System is : " << pe32.cntThreads << endl;
CloseHandle(ProcessHandle);
break;
}
pResult = Process32Next(hProcessSnap, &pe32);
}
CloseHandle(hProcessSnap);
return 0;
BOOL PrivilegeEscalation()
{
HANDLE hToken;
TOKEN_PRIVILEGES Tp;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
{
return FALSE;
}
Tp.PrivilegeCount = 1;
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &Tp.Privileges[0].Luid);
Tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, FALSE, &Tp, sizeof(Tp), NULL, NULL);
//int err = GetLastError();
CloseHandle(hToken);
return TRUE;
}
The System process is not a real process, it represents the kernel. You can't expect all process related functions to work on it.
If you are cloning something like Process Explorer, call the undocumented NT API like the Windows Task manager has been doing all its life.

WindowsIdentity.GetCurrent().IsSystem in C++

I need to know if the current process is running as system. In C# I use WindowsIdentity.GetCurrent().IsSystem to do that, what is the equivalent in C++?
I'm trying to avoid comparing usernames, because different OS has different usernames for SYSTEM account.
I have created an example based on Eryk's idea, and it works:
BOOL IsSystem()
{
HANDLE hToken = NULL;
BOOL result = false;
TOKEN_USER *tokenUser = NULL;
DWORD dwLength = 0;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken) == 0)
{
DbgPrint("OpenProcessToken(): %d", GetLastError());
goto cleanup;
}
if (GetTokenInformation(hToken, TokenUser, (LPVOID) tokenUser, 0, &dwLength) == 0)
{
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
DbgPrint("GetTokenInformation(): %d", GetLastError());
goto cleanup;
}
tokenUser = (TOKEN_USER *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength);
if (tokenUser == NULL)
{
goto cleanup;
}
if (GetTokenInformation(hToken, TokenUser, (LPVOID) tokenUser, dwLength, &dwLength) == 0)
{
DbgPrint("GetTokenInformation(): %d", GetLastError());
goto cleanup;
}
result = IsWellKnownSid(tokenUser->User.Sid, WinLocalSystemSid);
}
cleanup:
if (tokenUser != NULL)
{
HeapFree(GetProcessHeap(), NULL, tokenUser);
}
return result;
}

OpenProcess() Access_Denied with SeDebugPrivilege

This program enumerate all handles and get their names.
For pID 4 OpenProcess gets error 5 with SeDebugPrivilege.
UAC off. Running from Admin.
Enable SeDebugPrivilege
BOOL EnableDebugPrivilege(BOOL bEnable)
{
HANDLE hToken = nullptr;
LUID luid;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) return FALSE;
if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) return FALSE;
TOKEN_PRIVILEGES tokenPriv;
tokenPriv.PrivilegeCount = 1;
tokenPriv.Privileges[0].Luid = luid;
tokenPriv.Privileges[0].Attributes = bEnable ? SE_PRIVILEGE_ENABLED : 0;
if (!AdjustTokenPrivileges(hToken, FALSE, &tokenPriv, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) return FALSE;
_tprintf(_T("Privileges error: %d\n", GetLastError()));
return TRUE;
}
Enumerate handles
DWORD EnumerateFileHandles(ULONG pid)
{
HINSTANCE hNtDll = LoadLibrary(_T("ntdll.dll"));
assert(hNtDll != NULL);
PFN_NTQUERYSYSTEMINFORMATION NtQuerySystemInformation =
(PFN_NTQUERYSYSTEMINFORMATION)GetProcAddress(hNtDll,
"NtQuerySystemInformation");
assert(NtQuerySystemInformation != NULL);
PFN_NTQUERYINFORMATIONFILE NtQueryInformationFile =
(PFN_NTQUERYINFORMATIONFILE)GetProcAddress(hNtDll,
"NtQueryInformationFile");
DWORD nSize = 4096, nReturn;
PSYSTEM_HANDLE_INFORMATION pSysHandleInfo = (PSYSTEM_HANDLE_INFORMATION)
HeapAlloc(GetProcessHeap(), 0, nSize);
while (NtQuerySystemInformation(SystemExtendedHandleInformation, pSysHandleInfo,
nSize, &nReturn) == STATUS_INFO_LENGTH_MISMATCH)
{
HeapFree(GetProcessHeap(), 0, pSysHandleInfo);
nSize += 4096;
pSysHandleInfo = (SYSTEM_HANDLE_INFORMATION*)HeapAlloc(
GetProcessHeap(), 0, nSize);
}
DWORD dwFiles = 0;
_tprintf(_T("Handles Number: %d\n"), pSysHandleInfo->NumberOfHandles);
for (ULONG i = 0; i < pSysHandleInfo->NumberOfHandles; i++)
{
PSYSTEM_HANDLE pHandle = &(pSysHandleInfo->Handles[i]);
if (pHandle->ProcessId == 4)
{
HANDLE hProcess = OpenProcess(
PROCESS_DUP_HANDLE, FALSE, pHandle->ProcessId);
if (hProcess == NULL)
{
_tprintf(_T("OpenProcess failed w/err 0x%08lx\n"), GetLastError());
continue;
}
HANDLE hCopy;
if (!DuplicateHandle(hProcess, (HANDLE)pHandle->Handle,
GetCurrentProcess(), &hCopy, MAXIMUM_ALLOWED, FALSE, 0))
continue;
TCHAR buf[MAX_PATH];
if (GetFinalPathNameByHandle(hCopy, buf, sizeof(buf), VOLUME_NAME_DOS))
wprintf(L"p%d:h%d:t%d:\t%s\n", pHandle->ProcessId, pHandle->Handle, pHandle->ObjectTypeNumber, buf);
CloseHandle(hProcess);
CloseHandle(hCopy);
}
}
HeapFree(GetProcessHeap(), 0, pSysHandleInfo);
return dwFiles;
}
On windows 7 x64 it's work fine.
But on Windows 10 x64 OpenProcess returns error 5 with SeDebugPrivilege.
How open system process(pID 4) on windows 10.
You can't open a handle for it as the documentation for OpenProcess specifically says it'll fail:
If the specified process is the Idle process or one of the CSRSS
processes, this function fails and the last error code is
ERROR_ACCESS_DENIED because their access restrictions prevent
user-level code from opening them.
If you want to get system process names, you could try to use CreateToolhelp32Snapshot() to get the snapshot of the process, then use Process32First() and Process32Next() to enumerate the all process.
Here is an example:
#include <iostream>
#include <stdio.h>
#include <windows.h>
#include <string>
#include <TlHelp32.h>
using namespace std;
int main()
{
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(pe32);
HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);//get the snapshot
if (hProcessSnap == INVALID_HANDLE_VALUE)
{
cout << "CreateToolhelp32Snapshot Error!" << endl;
return false;
}
BOOL bResult = Process32First(hProcessSnap, &pe32);
int num(0);
while(bResult)
{
cout << "[" << ++num << "] : " << "Process Name:"<< pe32.szExeFile << " " << "ProcessID:" << pe32.th32ProcessID << endl;
bResult = Process32Next(hProcessSnap, &pe32);
}
CloseHandle(hProcessSnap);
}
Hope it could help you!

Get full path from a process in Windows

there's something wrong with this api. i already check return function. no error. but output nothing.
HANDLE hSnapProcess = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
PROCESSENTRY32 process;
process.dwSize = sizeof(PROCESSENTRY32);
Process32First(hSnapProcess, &process);
do
{
if (process.th32ProcessID != 0)
{
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, process.th32ProcessID);
if (hProcess != NULL)
{
wchar_t filePath[MAX_PATH];
if (!GetModuleFileNameExW(hProcess, NULL, filePath, MAX_PATH))
{
std::wcout << filePath << std::endl;
}
}
CloseHandle(hProcess);
}
} while (Process32Next(hSnapProcess, &process));
CloseHandle(hSnapProcess);
Two clear mistakes can be seen in your code, both can be understood by reading the documentation.
Firstly, in GetModuleFileNameEx:
The handle must have the PROCESS_QUERY_INFORMATION and PROCESS_VM_READ access rights.
Your handle only has PROCESS_QUERY_INFORMATION.
Secondly, again in GetModuleFileNameEx:
If the function succeeds, the return value specifies the length of the string copied to the buffer.
If the function fails, the return value is zero.
Your logic is back-to-front. Replace:
if (!GetModuleFileNameExW(hProcess, NULL, filePath, MAX_PATH))
with
if (GetModuleFileNameExW(hProcess, NULL, filePath, MAX_PATH))
Nothing is wrong with this API, the only thing that's wrong here is your code.
The documentation clearly states that the return value of GetModuleFileNameExW is the length of the string copied to the buffer.
If the return value is 0, the function has failed.
So you simply need to write this:
...
if (GetModuleFileNameExW(hProcess, NULL, filePath, MAX_PATH) != 0)
{
// handle "success" case
}
...
BTW CloseHandle(hProcess); should be inside the if (hProcess != NULL) block.
Full working example with error checks
#include <iostream>
#include <windows.h>
#include <tlhelp32.h>
#include <psapi.h>
int main()
{
HANDLE hSnapProcess = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (hSnapProcess != INVALID_HANDLE_VALUE)
{
PROCESSENTRY32 process;
process.dwSize = sizeof(PROCESSENTRY32);
Process32First(hSnapProcess, &process);
do
{
if (process.th32ProcessID != 0)
{
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, process.th32ProcessID);
if (hProcess != NULL)
{
wchar_t filePath[MAX_PATH];
if (GetModuleFileNameExW(hProcess, NULL, filePath, MAX_PATH))
{
std::wcout << filePath << std::endl;
}
else
{
std::wcout << L"GetModuleFileNameExW failed with error" << GetLastError() << std::endl;
}
CloseHandle(hProcess);
}
}
} while (Process32Next(hSnapProcess, &process));
CloseHandle(hSnapProcess);
}
else
{
std::wcout << L"CreateToolhelp32Snapshot failed with error" << GetLastError() << std::endl;
}
}

Is there a workaround for CreateProcessWithTokenW's cmdline argument's max length?

I'm trying to create an unelevated process from an elevated process on Windows 7-10.
I used the following as a reference:
FAQ: How do I start a program as the desktop user from an elevated app?
Now, this method works wonderfully, however due to a possible legacy check, it seems that CreateProcessWithTokenW() only allows the cmdline argument to be less than or equal to 1024 characters.
The cmdline I'm required to pass through is far more than that, which causes an E_INVALIDARG error.
Has anyone run into the same issue as me? If so, how did you work around this absolutely ridiculous 1024 character limit?
for exec not elevated process from our elevated (in same session) we need do next:
create restricted token from our elevated token by
CreateRestrictedToken with LUA_TOKEN
set medium intergity level in new token
call CreateProcessAsUser - note that
If hToken is a restricted version of the caller's primary token, the
SE_ASSIGNPRIMARYTOKEN_NAME privilege is not required.
ULONG LowExec(PCWSTR lpApplicationName, PWSTR lpCommandLine)
{
HANDLE hToken, hLowToken;
ULONG cb = GetSidLengthRequired(1);
TOKEN_MANDATORY_LABEL tml = { { (PSID)alloca(cb) } };
ULONG dwError = NOERROR;
if (CreateWellKnownSid(WinMediumLabelSid, 0, tml.Label.Sid, &cb) &&
OpenProcessToken(NtCurrentProcess(), TOKEN_DUPLICATE | TOKEN_QUERY |
TOKEN_ADJUST_DEFAULT | TOKEN_ASSIGN_PRIMARY, &hToken))
{
BOOL fOk = CreateRestrictedToken(hToken, LUA_TOKEN, 0, 0, 0, 0, 0, 0, &hLowToken);
if (!fOk)
{
dwError = GetLastError();
}
CloseHandle(hToken);
if (fOk)
{
if (SetTokenInformation(hLowToken, ::TokenIntegrityLevel, &tml, sizeof(tml)))
{
STARTUPINFOW si = { sizeof(si)};
PROCESS_INFORMATION pi;
if (CreateProcessAsUser(hLowToken, lpApplicationName, lpCommandLine, 0, 0, TRUE, 0, 0, 0, &si, &pi))
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
else
{
dwError = GetLastError();
}
}
else
{
dwError = GetLastError();
}
CloseHandle(hLowToken);
}
}
else
{
dwError = GetLastError();
}
return dwError;
}
Maybe try using CreateProcessAsUserW() instead? I know it requires special privileges, but if your process is running as administrator, there is a way to overcome it.
The idea is to impersonate a powerful token before calling the API. A powerful token is defined as a token that possesses all desired privileges.
First, enumerate process ID via API like NtQuerySystemInformation(). For each process ID, open the process token and use GetTokenInformation(hToken, TokenPrivileges, ...) to get information of what privileges are possessed by the token. If the token possesses all privileges we want, we may duplicate an impersonation token out from it by DuplicateTokenEx(..., TokenImpersonation, &hTokenImp) (and stop enumerating of course).
Now that we have the powerful token, we shall examine the desired privileges of it, see if they are all enabled. Check status with PrivilegeCheck() and enable it with AdjustTokenPrivileges(). Finally, let current thread impersonate the token by SetThreadToken(NULL, hTokenImp), call the CreateProcessAsUserW() however you like, and stop impersonation by SetThreadToken(NULL, NULL).
As for the token for CreateProcessAsUserW(), I would recommend you to use primary token of current user session by WTSQueryUserToken(), because it would work as expected even if you do OTS elevation. Last time I tried to use Linked Token as described here, and found it would not work with OTS elevation.
Here is a test code in ANSI C. When running as administrator, it will run another instance of itself un-elevated and show the command line length. And yes, CreateProcessAsUserW() supports command-line longer than 1024. :)
#include <stdio.h>
#include <Windows.h>
#include <objbase.h>
#include "EnumProcessesId.h"
#define MY_LuidEqual(a, b) \
( ((a)->HighPart == (b)->HighPart) && ((a)->LowPart == (b)->LowPart) )
static LPVOID MyAllocZero(SIZE_T cb)
{
LPVOID ptr = CoTaskMemAlloc(cb);
if (ptr) { ZeroMemory(ptr, cb); }
return ptr;
}
static void MyFree(LPVOID ptr)
{
CoTaskMemFree(ptr);
}
static DWORD MyWTSGetActiveConsoleSessionId(void)
{
typedef DWORD(WINAPI *fn_t)(void);
fn_t fn = (fn_t)GetProcAddress(LoadLibraryA("kernel32"),
"WTSGetActiveConsoleSessionId");
if (fn) { return fn(); }
return 0;
}
static BOOL MyWTSQueryUserToken(DWORD sessId, HANDLE *phToken)
{
typedef BOOL(WINAPI *fn_t)(DWORD, HANDLE*);
fn_t fn = (fn_t)GetProcAddress(LoadLibraryA("wtsapi32"),
"WTSQueryUserToken");
if (fn) {
return fn(sessId, phToken);
}
return FALSE;
}
static BOOL MyPrivIsEnabled(HANDLE hToken, LUID const *pPrivLuid)
{
BOOL isEnabled = FALSE;
PRIVILEGE_SET ps = { 0 };
ps.PrivilegeCount = 1;
ps.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT;
PrivilegeCheck(hToken, &ps, &isEnabled);
if (!isEnabled) {
ps.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
PrivilegeCheck(hToken, &ps, &isEnabled);
}
return isEnabled;
}
static HRESULT MyEnablePriv(HANDLE hToken, LUID const *pPrivLuid, BOOL enable)
{
BOOL ok = FALSE;
TOKEN_PRIVILEGES tp = { 0 };
tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0;
tp.Privileges[0].Luid = *pPrivLuid;
ok = AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
return ok ? S_OK : HRESULT_FROM_WIN32(GetLastError());
}
typedef struct {
/* in : */
LUID const *pLuidAPT;
LUID const *pLuidIQN;
LUID const *pLuidTCB;
/* out : */
HANDLE hptPowerful;
} MyEnumPowerfulTokenData_t;
static BOOL CALLBACK MyEnumPowerfulTokenProc(DWORD pid, void * user)
{
DWORD const ProcessQueryLimitedInfo = 0x1000;
MyEnumPowerfulTokenData_t *pData = user;
BOOL ok = FALSE, wantContinue = TRUE;
HANDLE hProc = NULL;
HANDLE hProcToken = NULL;
DWORD i = 0, cbTP = 0;
TOKEN_PRIVILEGES *pTP = NULL;
BOOL gotAPT = FALSE, gotIQN = FALSE, gotTCB = FALSE;
/* Get process token */
hProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
if (!hProc) {
hProc = OpenProcess(ProcessQueryLimitedInfo, FALSE, pid);
}
if (!hProc) goto eof;
ok = OpenProcessToken(hProc, TOKEN_QUERY | TOKEN_DUPLICATE, &hProcToken);
if (!ok) goto eof;
/* Check if token possesses desired privileges */
GetTokenInformation(hProcToken, TokenPrivileges, NULL, 0, &cbTP);
if (!cbTP) goto eof;
pTP = MyAllocZero(cbTP);
if (!pTP) goto eof;
ok = GetTokenInformation(hProcToken, TokenPrivileges, pTP, cbTP, &cbTP);
if (!ok) goto eof;
for (i = 0; i < pTP->PrivilegeCount; ++i)
{
LUID const *pThat = &(pTP->Privileges[i].Luid);
if (gotAPT && gotIQN && gotTCB) {
wantContinue = FALSE;
pData->hptPowerful = hProcToken;
hProcToken = NULL; /* to avoid eof CloseHandle() */
break;
}
else if (!gotAPT && MY_LuidEqual(pThat, pData->pLuidAPT)) {
gotAPT = TRUE;
}
else if (!gotIQN && MY_LuidEqual(pThat, pData->pLuidIQN)) {
gotIQN = TRUE;
}
else if (!gotTCB && MY_LuidEqual(pThat, pData->pLuidTCB)) {
gotTCB = TRUE;
}
}
eof:
if (pTP) { MyFree(pTP); }
if (hProcToken) { CloseHandle(hProcToken); }
if (hProc) { CloseHandle(hProc); }
return wantContinue;
}
static HRESULT MyCreateProcess(LPWSTR szCmdLine)
{
HRESULT hr = 0;
BOOL ok = FALSE;
LUID luidAPT = { 0 }; /* SE_ASSIGNPRIMARYTOKEN_NAME */
LUID luidIQN = { 0 }; /* SE_INCREASE_QUOTA_NAME */
LUID luidTCB = { 0 }; /* SE_TCB_NAME */
MyEnumPowerfulTokenData_t enumData = { 0 };
HANDLE hptPowerful = NULL; /* primary/process token */
HANDLE hitPowerful = NULL; /* impersonation token */
HANDLE hptCurrSessUser = NULL;
DWORD dwCurrSessId = 0;
STARTUPINFOW si = { sizeof(si) };
PROCESS_INFORMATION pi = { 0 };
ok = LookupPrivilegeValue(NULL, SE_ASSIGNPRIMARYTOKEN_NAME, &luidAPT)
&& LookupPrivilegeValue(NULL, SE_INCREASE_QUOTA_NAME, &luidIQN)
&& LookupPrivilegeValue(NULL, SE_TCB_NAME, &luidTCB);
if (!ok) {
hr = HRESULT_FROM_WIN32(GetLastError()); goto eof;
}
enumData.pLuidAPT = &luidAPT;
enumData.pLuidIQN = &luidIQN;
enumData.pLuidTCB = &luidTCB;
hr = EnumProcessesId_WinNT(MyEnumPowerfulTokenProc, &enumData);
if (FAILED(hr)) goto eof;
hptPowerful = enumData.hptPowerful;
if (!hptPowerful) {
hr = E_UNEXPECTED; goto eof;
}
ok = DuplicateTokenEx(hptPowerful, TOKEN_ADJUST_PRIVILEGES | TOKEN_IMPERSONATE,
NULL, SecurityImpersonation, TokenImpersonation, &hitPowerful);
if (!ok) {
hr = HRESULT_FROM_WIN32(GetLastError()); goto eof;
}
if (!MyPrivIsEnabled(hitPowerful, &luidAPT))
{
hr = MyEnablePriv(hitPowerful, &luidAPT, TRUE);
if (FAILED(hr)) goto eof;
}
if (!MyPrivIsEnabled(hitPowerful, &luidIQN))
{
hr = MyEnablePriv(hitPowerful, &luidIQN, TRUE);
if (FAILED(hr)) goto eof;
}
if (!MyPrivIsEnabled(hitPowerful, &luidTCB))
{
hr = MyEnablePriv(hitPowerful, &luidTCB, TRUE);
if (FAILED(hr)) goto eof;
}
ok = SetThreadToken(NULL, hitPowerful);
if (!ok) {
hr = HRESULT_FROM_WIN32(GetLastError()); goto eof;
}
dwCurrSessId = MyWTSGetActiveConsoleSessionId();
ok = MyWTSQueryUserToken(dwCurrSessId, &hptCurrSessUser);
if (!ok) {
hr = HRESULT_FROM_WIN32(GetLastError()); goto eof;
}
ok = CreateProcessAsUserW(hptCurrSessUser, NULL, szCmdLine, 0, 0, 0, 0, 0, 0, &si, &pi);
if (!ok) {
hr = HRESULT_FROM_WIN32(GetLastError()); goto eof;
}
eof:
SetThreadToken(NULL, NULL);
if (hptCurrSessUser) { CloseHandle(hptCurrSessUser); }
if (hitPowerful) { CloseHandle(hitPowerful); }
if (hptPowerful) { CloseHandle(hptPowerful); }
if (FAILED(hr)) {
printf("HRESULT = 0x%.8X \n", hr);
}
return 0;
}
int main(int argc, char **argv)
{
if (argc > 1)
{
WCHAR szMsg[999] = {0};
wsprintfW(szMsg,
L"lstrlenW(GetCommandLineW()) = %i \n",
lstrlenW(GetCommandLineW()));
MessageBoxW(NULL, szMsg, L"Test", MB_ICONINFORMATION);
}
else
{
WCHAR szMyExePath[MAX_PATH] = {0};
WCHAR szCmdLine[9999] = {0}, *p;
GetModuleFileNameW(NULL, szMyExePath, MAX_PATH);
wsprintfW(szCmdLine, L"\"%s\" ", szMyExePath);
for (p = szCmdLine; *p; ++p);
while (p < (szCmdLine + 9999 - 1))
{
*p++ = 'a';
}
MyCreateProcess(szCmdLine);
}
return 0;
}
I will leave the implementation of EnumProcessesId_WinNT() to you. The prototype of the function is:
/* return TRUE to continue */
typedef BOOL(CALLBACK *EnumProcessesId_Callback_t)
(DWORD pid, void * user);
EXTERN_C
HRESULT __stdcall
EnumProcessesId_WinNT(
EnumProcessesId_Callback_t fnCallback,
void *user
);