Related
As discussed in WTSQueryUserToken throws error 1008, even when running under LocalSystem, I'm having trouble getting my Windows service to launch an interactive process on a particular user's desktop as soon as they log in.
The proposed solution there was to handle the SERVICE_CONTROL_SESSIONCHANGE control code and use the passed dwSessionId. Here's all of the code (apologies that it's quite lengthy, but I was told to post it here anyway):
// These headers just contain system header #include's function prototypes
// and global variable declarations. If a variable below seems like it is
// undefined, rest assured that it *is* defined in one of these headers.
#include "events.h"
#include "main.h"
int __cdecl _tmain(int argc, LPTSTR argv[]) {
sysStart = system_clock::now();
LogInit();
// If command-line parameter is "install", install the service.
// Otherwise, the service is probably being started by the SCM
if (lstrcmpi(argv[1], L"install") == 0) {
return SvcInstall();
}
SERVICE_TABLE_ENTRY dispatchTable[] = {
{ &svcName[0], (LPSERVICE_MAIN_FUNCTION)SvcMain },
{ nullptr, nullptr }
};
// This call returns when the service has stopped. The
// process should simply terminate when the call returns
if (!StartServiceCtrlDispatcher(dispatchTable)) {
ReportSvcEvent("StartServiceCtrlDispatcher");
}
return ERROR_SUCCESS;
}
char* WINAPI GetTimestamp(string& buf) {
int ms = (high_resolution_clock::now().
time_since_epoch().count() / 1000000) % 1000;
auto tt = system_clock::to_time_t(
system_clock::now());
tm time;
localtime_s(&time, &tt);
strftime(&buf[0], 21, "[%d-%m-%Y %T", &time);
snprintf(&buf[0], 26, "%s.%03d] ", &buf[0], ms);
buf[25] = ' ';
return &buf[0];
}
bool WINAPI LaunchDebugger(void) {
// Get System directory, typically C:\Windows\System32
wstring systemDir(MAX_PATH + 1, '\0');
UINT nChars = GetSystemDirectory(&systemDir[0], systemDir.length());
if (nChars == 0) {
return false; // failed to get system directory
}
systemDir.resize(nChars);
// Get process ID and create the command line
// wostringstream ss;
// ss << systemDir << L"\\vsjitdebugger.exe -p " << GetCurrentProcessId();
wstring cmdLine = L"";
// Start debugger process
STARTUPINFOW si;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(pi));
if (!CreateProcess(nullptr, &cmdLine[0], nullptr,
nullptr, false, 0, nullptr, nullptr, &si, &pi)) {
return false;
}
// Close debugger process handles to eliminate resource leaks
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
// Wait for the debugger to attach
while (!IsDebuggerPresent()) {
Sleep(100);
}
// Stop execution so the debugger can take over
DebugBreak();
return true;
}
VOID WINAPI LogActiveTime(void) {
// The computer is shutting down - write an entry to logFile to reflect
// this, prefixed with a null byte to mark the current file position
// (used for parsing in the timestamp on the next system boot)
logFile << '\0';
LogMessage("User action", "System shutting down after being "
"active for " + DurationString(system_clock::now() - sysStart));
logFile.close();
// If the log file contains > 40 lines (10 boot/shutdown cycles),
// remove the first 4 lines (the earliest boot/shutdown cycle).
// This stops the file from getting too long to read easily
ifstream inFile(logFilePath);
string line;
auto count = 0;
while (getline(inFile, line)) {
count++;
}
}
DWORD WINAPI LogError(const string& funcName) {
auto err = 0;
LogMessage(funcName, system_category(
).message(err = GetLastError()), true);
return err;
}
DWORD WINAPI LogInactiveTime(void) {
// Create a new log file to be used as the input on the next run
LogInit("temp");
// Open the existing log file for reading and find the last shutdown
// log entry by copying its contents to the new file until a null byte
// or EOF is found (see LogActiveTime() for more info)
ifstream inFile(logFilePath);
if (!inFile) {
return LogError("LogInactiveTime");
}
char ch = inFile.get();
while (ch != '\0' && !inFile.eof()) {
logFile << ch;
ch = inFile.get();
}
if (inFile.eof()) {
// No shutdown log entry was found, i.e. this is probably the first
// time the service has run on the current instance of our log file.
// Close the temp file and re-open the original log file before
// returning, otherwise future messages won't make it to the file!
LogInit();
return ERROR_SUCCESS;
}
// At this point we can be sure that a valid shutdown log entry
// exists, so we now need to parse it into a chrono::time_point.
// Also save the entry's starting position in pos for later use
auto pos = inFile.tellg();
auto tt = system_clock::to_time_t(sysStart);
tm start, end = { 0 };
localtime_s(&start, &tt);
inFile >> get_time(&end, "[%d-%m-%Y %T");
if (inFile.fail() || inFile.bad()) {
return LogError("LogInactiveTime");
}
// Ensure that both time_points refer to
// the correct time, regardless of DST
end.tm_isdst = start.tm_isdst;
sysEnd = system_clock::from_time_t(mktime(&end));
// Go back to the *actual* start of the shutdown
// log entry so we can copy it into the new file
inFile.seekg(pos);
// Finish copying over the rest of our existing log
// file, then close it and replace it with the new one
ch = inFile.get();
while (!inFile.eof()) {
logFile << ch;
ch = inFile.get();
}
inFile.close();
remove(logFilePath.c_str());
logFile.close();
rename("temp", logFilePath.c_str());
// Finally, do what we *actually* came here to do!
LogMessage("User action", "System booting after being "
"inactive for " + DurationString(sysStart - sysEnd));
return ERROR_SUCCESS;
}
VOID WINAPI LogInit(const string& filePath) {
setlocale(LC_ALL, "en_US.UTF8");
if (logFile.is_open()) {
logFile.close();
}
logFile.open(filePath == "" ?
logFilePath : filePath, ios::app);
if (!logFile) {
exit(GetLastError());
}
}
VOID WINAPI LogMessage(const string& funcName,
const string& msg, bool isError) {
if (!logFile.is_open()) {
LogInit();
}
string buf(52, '\0');
snprintf(&buf[0], 52, "%s%-6s %-18s ", GetTimestamp(buf),
isError ? "ERROR:" : "INFO:", &(funcName + ':')[0]);
buf[51] = ' ';
logFile << buf << msg << endl;
}
VOID WINAPI ReportSvcEvent(const string& funcName) {
HANDLE eventSrc = RegisterEventSource(nullptr, &svcName[0]);
if (eventSrc != nullptr) {
LPCSTR errParams[2] = { "WinUtilities" };
char buf[MAX_PATH];
StringCchPrintfA(buf, MAX_PATH, "Function '%s' failed: %s",
funcName.c_str(), system_category().message(GetLastError(
)).c_str());
errParams[1] = buf;
ReportEventA(eventSrc, // event log handle
EVENTLOG_ERROR_TYPE, // event type
0, // event category
SVC_ERROR, // event identifier
nullptr, // no security identifier
2, // size of lpszStrings array
0, // no binary data
errParams, // array of strings
nullptr); // no binary data
DeregisterEventSource(eventSrc);
}
}
VOID WINAPI ReportSvcStatus(DWORD newState,
DWORD exitCode, DWORD waitHint) {
static DWORD dwCheckPoint = 1;
static unordered_map<int, string> svcStates;
if (svcStates.empty()) {
// Initialise mapping from service state codes to readable strings
svcStates.insert({ SERVICE_STOPPED, "Stopped" });
svcStates.insert({ SERVICE_START_PENDING, "Start Pending" });
svcStates.insert({ SERVICE_STOP_PENDING, "Stop Pending" });
svcStates.insert({ SERVICE_RUNNING, "Running" });
svcStates.insert({ SERVICE_CONTINUE_PENDING, "Continue Pending" });
svcStates.insert({ SERVICE_PAUSE_PENDING, "Pause Pending" });
svcStates.insert({ SERVICE_PAUSED, "Paused" });
}
// Update the SERVICE_STATUS structure with the new passed-in values
svcStatus.dwCurrentState = newState;
svcStatus.dwWin32ExitCode = exitCode;
svcStatus.dwWaitHint = waitHint;
if (newState == SERVICE_START_PENDING) {
svcStatus.dwControlsAccepted = 0;
} else {
svcStatus.dwControlsAccepted =
SERVICE_ACCEPT_SESSIONCHANGE |
SERVICE_ACCEPT_STOP |
SERVICE_ACCEPT_PRESHUTDOWN;
}
if (newState == SERVICE_RUNNING ||
newState == SERVICE_STOPPED) {
svcStatus.dwCheckPoint = 0;
} else {
svcStatus.dwCheckPoint = dwCheckPoint++;
}
// Report the status of the service to the SCM and our log file
if (!SetServiceStatus(statusHandle, &svcStatus)) {
LogError("SetServiceStatus");
} else {
LogMessage("SetServiceStatus", "Service status " \
"updated to '" + svcStates[newState] + "'.");
}
}
DWORD WINAPI SvcCtrlHandler(DWORD ctrlCode, DWORD
eventType, LPVOID eventData, LPVOID context) {
switch (ctrlCode) {
case SERVICE_CONTROL_SESSIONCHANGE: {
auto sessionId = ((WTSSESSION_NOTIFICATION*
)eventData)->dwSessionId;
switch (eventType) {
case WTS_SESSION_LOGON: {
string userName;
DWORD size;
WTSQuerySessionInformationA(WTS_CURRENT_SERVER_HANDLE, sessionId,
WTS_INFO_CLASS::WTSUserName, (LPSTR*)&userName[0], &size);
ReportSvcEvent("log on");
// A user has successfully logged on to the PC. Now we can start
// an interactive worker process under that user's account which
// will perform the actual work that we want to do
STARTUPINFO si = { 0 };
si.cb = sizeof(si);
si.wShowWindow = true;
HANDLE hToken;
if (!WTSQueryUserToken(sessionId, &hToken)) {
LogError("WTSQueryUserToken");
return ERROR_CALL_NOT_IMPLEMENTED;
}
wstring cmdLine = L"C:\\Path\\to\\my\\app.exe";
if (!CreateProcessAsUser(hToken, &cmdLine[0], nullptr, nullptr, nullptr,
false, CREATE_NO_WINDOW, nullptr, nullptr, &si, &workerProc)) {
LogError("CreateProcessAsUser");
return ERROR_CALL_NOT_IMPLEMENTED;
}
CloseHandle(hToken);
break;
} default: {
break;
}
}
break;
} case SERVICE_CONTROL_STOP: {
// Signal the service to stop
ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);
SetEvent(svcStopEvent);
break;
} case SERVICE_CONTROL_PRESHUTDOWN: {
LogActiveTime();
break;
} default: {
return ERROR_CALL_NOT_IMPLEMENTED;
}
}
return NO_ERROR;
}
VOID WINAPI SvcInit(DWORD argc, LPTSTR argv[]) {
// Get the time at which the last shutdown occurred, and
// log the duration for which the system was inactive
if (LogInactiveTime() > 0) {
return;
}
// Create an event. The control handler function (SvcCtrlHandler)
// signals this event when it receives the stop control code
svcStopEvent = CreateEvent(
nullptr, // default security attributes
TRUE, // manual reset event
FALSE, // not signaled
nullptr); // no name
if (svcStopEvent == nullptr) {
LogError("CreateEvent");
ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
return;
}
// Report running status when initialisation is complete
ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0);
// Wait until our stop event has been signalled
WaitForSingleObject(svcStopEvent, INFINITE);
// Code execution won't reach here until the service has been
// fully stopped. Report this to the SCM when it happens, then
// terminate the worker process and clean up its handles
ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
if (workerProc.hProcess) {
TerminateProcess(workerProc.hProcess, 0);
CloseHandle(workerProc.hProcess);
CloseHandle(workerProc.hThread);
}
}
DWORD WINAPI SvcInstall(void) {
TCHAR path[MAX_PATH];
if (!GetModuleFileName(nullptr, path, MAX_PATH)) {
return LogError("GetModuleFileName");
}
// Get a handle to the SCM database
auto scm = OpenSCManager(
nullptr, // local computer
nullptr, // ServicesActive database
SC_MANAGER_ALL_ACCESS); // full access rights
if (scm == nullptr) {
return LogError("OpenSCManager");
}
// Create the service
auto svc = CreateService(
scm, // SCM database
&svcName[0], // name of service
L"Windows Utilities", // service name to display
SERVICE_ALL_ACCESS, // desired access
SERVICE_WIN32_OWN_PROCESS, // service type
SERVICE_AUTO_START, // start type
SERVICE_ERROR_NORMAL, // error control type
path, // path to service's binary
nullptr, // no load ordering group
nullptr, // no tag identifier
nullptr, // no dependencies
nullptr, // LocalSystem account
nullptr); // no password
if (svc == nullptr) {
CloseServiceHandle(scm);
return LogError("CreateService");
}
SERVICE_DESCRIPTION sd;
sd.lpDescription = const_cast<LPTSTR>(L"Logs system "
"shutdown events to a text file on the desktop. "
"Also creates a system-wide hot key to perform "
"internet searches on any selected text.");
if (!ChangeServiceConfig2(
svc, // handle to service
SERVICE_CONFIG_DESCRIPTION, // change: description
&sd)) // new description
{
CloseServiceHandle(svc);
CloseServiceHandle(scm);
return LogError("ChangeServiceConfig2");
}
CloseServiceHandle(svc);
CloseServiceHandle(scm);
LogMessage("SvcInstall", "Service installed successfully.");
return ERROR_SUCCESS;
}
VOID WINAPI SvcMain(DWORD argc, LPTSTR argv[]) {
// Register the handler function for the service
statusHandle = RegisterServiceCtrlHandlerEx(
&svcName[0], SvcCtrlHandler, 0);
if (!statusHandle) {
ReportSvcEvent("RegisterServiceCtrlHandlerEx");
return;
}
// These SERVICE_STATUS members remain as set here
svcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
svcStatus.dwServiceSpecificExitCode = 0;
// Report initial status to the SCM
ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 3000);
// Perform service-specific initialization and work
SvcInit(argc, argv);
}
The part that doesn't work is in the SvcCtrlHandler() function, where I'm trying to catch the aforementioned control code.
I've even gone so far as to rewrite this whole thing in C# (which is the language that I should have used in the first place since my code is soooooooooo much cleaner and clearer now) and guess what? I still have the exact same problem with the OnSessionChange() method!
When I cold boot the computer and allow my PC to autologin to my single user account, nothing happens (i.e. no app.exe started). But if I then log out and back in again, I get the results I'm looking for.
So it seems as though my service is one of the last few to load and this is stopping it from properly catching the SERVICE_CONTROL_SESSIONCHANGE control code. How can I fix this? MTIA! :D
I'm trying to edit memory in a program. For the most part the code works, but when I try to initiate a handle on the process, it returns NULL.
#include <iostream>
#include <windows.h>
using namespace std;
int main() {
int playerTotalRam = 761;
HWND hwnd = FindWindowA(NULL, "generic game"); // specifies the window to act
upon
// error message if the window isn't found
if (hwnd == NULL) {
cout << "window not found!\n";
system("PAUSE");
} else {
DWORD processID;
// stores the process id of the window
GetWindowThreadProcessId(hwnd, &processID);
// gets the process id of the window
HANDLE handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processID);
// opens the process with full access
if (!handle) {
cout << "couldnt initiate a handle on the process!\n";
system("PAUSE");
}
// error message if the process ID isn't found
if (processID == NULL) {
cout << "cannot find process!\n";
system("PAUSE");
} else {
WriteProcessMemory(handle, (LPVOID)0x044A52C8, &playerTotalRam,
4, 0); // this writes the new value to the listed address
}
}
return 0;
}
The program outputs "couldnt initiate a handle on the process!"
I have this same error with other programs.
What i want to know is what am I doing wrong, and how can I fix this.
My system is windows 10 home.
Either FindWindowA is failing, resulting in an incorrect process id because you have the wrong window title text or you're not running your program as administrator. You need to run as administrator to get PROCESS_ALL_ACCESS permissions and make sure your title text is correct. This will solve your problem.
I'm writing a very simple debugger and I defined a class called BREAKPOINT_INFO that contains information about breakpoints set.
class BREAKPOINT_INFO
{
public:
HANDLE hProcess;
PCHAR lpBreakPoint;
CHAR instr;
BOOL justCalled;
//Set default values
BREAKPOINT_INFO()
{
hProcess = NULL;
lpBreakPoint = NULL;
instr = 0x00;
justCalled = FALSE;
}
//Destructor
~BREAKPOINT_INFO()
{
//Let me know the destructor is being called
MessageBox(NULL, "Destructor called", NULL, MB_OK);
DWORD dwError = 0;
LPCSTR szErrorRest = (LPCSTR)"Error restoring original instruction: ";
LPCSTR szErrorHanlde = (LPCSTR)"Error closing process handle: ";
std::ostringstream oss;
if(hProcess != NULL && lpBreakPoint != NULL)
{
//write back the original instruction stored in instr
if(!WriteProcessMemory(hProcess, lpBreakPoint, &instr, sizeof(CHAR), NULL))
{
dwError = GetLastError();
oss << szErrorRest << dwError;
MessageBox(NULL, oss.str().c_str(), "ERROR", MB_OK|MB_ICONERROR);
}
}
}
};
I need the destructor to clean up any breakpoints set however the deconstructor is never called and I'm not quite sure why that is in my particular case.
Here's main.cpp:
BREAKPOINT_INFO instrMov;
//GetProcModuleHandle is a function I made to get the handle of a
//of a module in a remote process
LPVOID lpServerDll = (LPVOID)GetProcModuleHandle(dwPid, szServerDll);
//the instructions address is relative to the starting address of the server dll. Hence the offset.
PCHAR lpInstr = (PCHAR)((DWORD)lpServerDll+instr_offset);
hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, dwPid);
//sets the breakpoint
instrMov.InitializeBreakPoint(hProcess, lpInstr);
while(1)
{
if(!instrMov.justCalled)
{
instrMov.SetBreakPoint();
}
if(instrMov.justCalled)
{
instrMov.justCalled = FALSE;
}
if(WaitForDebugEvent(&dbgEvent, 0))
{
ProcessDebugEvent(&dbgEvent, lpBreakPoints, 3);
ContinueDebugEvent(dbgEvent.dwProcessId, dbgEvent.dwThreadId, DBG_CONTINUE);
}
}
return 0; //<---never reaches return
It's a never ending loop so the program, at the moment, never actually reaches the return. It has to be terminated with either Ctrl+C or by closing the terminal. Not sure if this could be causing the destructor to not be called or not.
Any information, solutions, etc would be greatly appreciated. Thank you for your time.
you have to handle SIGINT signal, otherwise program is terminated abnormally and dtors are not called.
What I want to know is it possible to try an open a file (and when it fails because it's opened with another process with sharing off) to figure out which process is using said file?
The reason I am wanting to know this information is because I am making a little application that will "fix" malicious files.
For example, some malicious/adware etc set the file security descriptor so the user can't delete the file, etc. My application just resets the security descriptor allowing the user to regain control.
I have also seen a file open up its child process with for example (CreateFile) and have Shared Mode turned off so the file can't be touched, then the application would execute the childprocess from memory.
Yes, you can in general just use the openfiles command, after having enabled collection of this information via, it appears, openfiles /local on.
In Windows NT up to and including (it seems) Windows XP there was a similar Resource Kit command named oh, short for open handles.
An alternative to both is to use SysInternal's Process Explorer.
Note: In some cases openfiles will fail to list some handle. This happens for me when Windows refuses to unmount an USB disk, claiming that some process is using a file on that disk. No such process ever shows up.
I have developed a function to locate such process, kill it and delete the locked file.
bool ForceDeleteFile(LPWSTR FileName);
Here is the full source code:
bool KillFileProcess(LPWSTR FileName)
{
HANDLE hProcessSnap;
HANDLE hProcess;
PROCESSENTRY32 pe32;
DWORD dwPriorityClass;
bool result = false;
// Take a snapshot of all processes in the system.
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hProcessSnap == INVALID_HANDLE_VALUE)
{
//printError(TEXT("CreateToolhelp32Snapshot (of processes)"));
return(FALSE);
}
// Set the size of the structure before using it.
pe32.dwSize = sizeof(PROCESSENTRY32);
// Retrieve information about the first process,
// and exit if unsuccessful
if (!Process32First(hProcessSnap, &pe32))
{
//printError(TEXT("Process32First")); // show cause of failure
CloseHandle(hProcessSnap); // clean the snapshot object
return(FALSE);
}
// Now walk the snapshot of processes, and
// display information about each process in turn
do
{
// Retrieve the priority class.
dwPriorityClass = 0;
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID);
if (hProcess == NULL)
{
//printError(TEXT("OpenProcess"));
}
else
{
dwPriorityClass = GetPriorityClass(hProcess);
if (!dwPriorityClass)
{
//printError(TEXT("GetPriorityClass"));
}
CloseHandle(hProcess);
if (HANDLE hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pe32.th32ProcessID))
{
WCHAR filename[MAX_PATH] = {};
if (GetModuleFileNameEx(hProcess, NULL, filename, MAX_PATH))
{
if (_wcsicmp((const wchar_t *)FileName, (const wchar_t *)filename) == NULL)
{
if (TerminateProcess(pe32.th32ProcessID, 0))
{
_tprintf(L"Found: Process full killed\nKILLED!\n");
result = true;
}
else
{
_tprintf(L"Found: Process full \nFailed to terminate\n");
DoRun(((CString)L"taskkill /F /IM " + (CString)pe32.szExeFile).GetBuffer());
result = false;
}
}
}
else
{
// handle error
}
CloseHandle(hProcess);
}
}
} while (Process32Next(hProcessSnap, &pe32));
CloseHandle(hProcessSnap);
return(result);
}
bool ForceDeleteFile(LPWSTR FileName)
{
bool result = DeleteFile(FileName);
if (!result)
{
_tprintf(L"Can't delete file. using DeleteFile(). Trying to locate process and kill it\n");
result = KillFileProcess(FileName);
if (!result)
_tprintf(L"Couldn't find the process\n");
else
{
Sleep(1000);
result = DeleteFile(FileName);
if (result)
_tprintf(L"DeleteFile success");
else
_tprintf(L"DeleteFile ============== failed ===============");
}
}
return result;
}
BOOL TerminateProcess(DWORD dwProcessId, UINT uExitCode)
{
DWORD dwDesiredAccess = PROCESS_TERMINATE;
BOOL bInheritHandle = FALSE;
HANDLE hProcess = OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId);
if (hProcess == NULL)
return FALSE;
BOOL result = TerminateProcess(hProcess, uExitCode);
CloseHandle(hProcess);
return result;
}
Is it possible to launch process in system context from a parent process thats running under administrator account with elevation(say a command prompt). The problem is similar to what psexec does but more of how it actually implements this.
I was thinking opening the crss.exe/winlogon.exe process duplicating the token and launching a new process using that process token. But I fail to even open the process handle (Getlasterror return 5). Can someone let me know if this is the right approach or the process should be launched differently ?
HANDLE hWinLogonProcess;
for(const auto& ps : running_processes)
{
if(ps.id == GetCurrentProcessId() ||
0 != ps.short_name.CompareNoCase(L"winlogon.exe"))
{
continue;
}
DWORD dwWinLogonSessionId(0);
if(FALSE == ProcessIdToSessionId(GetCurrentProcessId(), &dwWinLogonSessionId))
{
std::wcerr<<"Could not get Winlogon process session id"<<std::endl;
continue;
}
if(dwWinLogonSessionId != dwCurSessionId)
{
continue;
}
hWinLogonProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, ps.id);
if(FALSE == hWinLogonProcess)
{
std::wcerr<<"Failed to get winlogon process handle"<<std::endl;
return;
}
else
{
std::wcout<<"Able to open process "<<ps.short_name.GetString()<<" handle"<<std::endl;
break;
}
}
I am sure its possible as there is a working tool (psexec) but I couldnt find any reference online to do this.
Also this is similar to question, but posting separately as there was details on how it had to be achieved.
Yes, this is possible (without any service help).
But I fail to even open the process handle
Does your process have the SE_DEBUG_PRIVILEGE privilege enabled?
With this privilege, you can open a system process with all access if it is not protected (smss.exe, csrss.exe, services.exe), and use that handle in CreateProcessAsUser(), or with UpdateProcThreadAttribute(PROC_THREAD_ATTRIBUTE_PARENT_PROCESS) if you also have SE_ASSIGNPRIMARYTOKEN_PRIVILEGE and SE_TCB_PRIVILEGE privileges enabled (for setting the token's SessionId to 0), which you can get in 2 ways:
open a thread from an unprotected system process and impersonate it, then open your own thread token and adjust privileges on it.
open a token from any system process (this works even for protected processes), duplicate the token, adjust privileges on it, and then impersonate with this token.
To "launch a process in the system context", if you want to run the process:
with the LocalSystem token.
in the System terminal session (0)
Both, as I say, are possible. And all you need is SE_DEBUG_PRIVILEGE.
more simply - open some system process with PROCESS_CREATE_PROCESS access right. Use this handle with UpdateProcThreadAttribute(PROC_THREAD_ATTRIBUTE_PARENT_PROCESS). As a result, your started process inherits a token from the system process. This will be not work on XP, but there it is possible to hook NtCreateProcess/Ex() to replace HANDLE ParentProcess with your opened handle.
Another way is to use CreateProcessAsUser(). Before creating the process, you will be need SE_ASSIGNPRIMARYTOKEN_PRIVILEGE and SE_TCB_PRIVILEGE privileges to set the token's TokenSessionId (if you want to run in session 0).
Thanks to RbMm answer I figured a way to accomplish this task.
For any of you who did not succeed, I leave below something that might help:
//First we need to add debug privilege to this process
HANDLE hToken;
if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&hToken))
{
std::cout << "OpenProcessToken failed: " << GetLastError();
return 0;
}
TOKEN_PRIVILEGES tk;
tk.PrivilegeCount = 1;
tk.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if(!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tk.Privileges[0].Luid))
{
std::cout << "LookupPrivilegeValue failed: " << GetLastError();
return 0;
}
AdjustTokenPrivileges(hToken, FALSE, &tk, 0, NULL, 0);
if((DWORD res = GetLastError()) != ERROR_SUCCESS)
{
std::cout << "AdjustTokenPrivileges failed: " << res;
}
CloseHandle(hToken);
//Now we need a handle to a process that already runs as SYSTEM.
//You can choose any process that is not protected (if OpenProcess fails try with other process)
//pid of chosen process (you can get this by opening task manager and go to
//Details tab or by enumerating all processes and extract that one you need)
DWORD pid;
HANDLE hProcess = OpenProcess(PROCESS_CREATE_PROCESS, FALSE, pid);
if (!hProcess)
{
std::cout << "OpenProcess with pid " << pid << "failed: " << GetLastError();
return 0
}
//We need to initialize a list that contains PROC_THREAD_ATTRIBUTE_PARENT_PROCESS
//to specify that parent process of the process we are going to start is the
//process we opened earlier (this will make the child process inherit the system context).
//This list will be specified in a STARTUPINFOEX object that CreateProcess will get
STARTUPINFOEX siex = { sizeof(STARTUPINFOEX) };
siex.StartupInfo.cb = sizeof(STARTUPINFOEXW);
//We need to initialize our list. To do this we call InitializeProcThreadAttributeList
//with a NULL list to get how big our list needs to be to store all attributes
//we want to specify, then we allocate our list with the size we got from first call
//and we call again the function to initialize the list.
SIZE_T cbAttributeListSize = 0;
if(!InitializeProcThreadAttributeList(NULL, 1, 0, &cbAttributeListSize))
{
std::cout << "InitializeProcThreadAttributeList failed: " << GetLastError();
return 0
}
siex.lpAttributeList = reinterpret_cast<PPROC_THREAD_ATTRIBUTE_LIST>(HeapAlloc(GetProcessHeap(), 0, cbAttributeListSize));
if(!InitializeProcThreadAttributeList(siex.lpAttributeList, 1, 0, &cbAttributeListSize))
{
std::cout << "InitializeProcThreadAttributeList failed: " << GetLastError();
return 0
}
if(!UpdateProcThreadAttribute(siex.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &hProcess, sizeof(hProcess), NULL, NULL))
{
std::cout << "UpdateProcThreadAttribute failed: " << GetLastError();
return 0
}
//path to program we want to run in system context
LPWSTR szCmdline = _wcsdup(TEXT("C:\\Windows\\System32\\notepad.exe"));
PROCESS_INFORMATION pi = { 0 };
if(!CreateProcess(NULL, szCmdline, nullptr, nullptr, FALSE, EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, reinterpret_cast<LPSTARTUPINFOW>(&siex), &pi))
{
std::cout << "CreateProcess failed: " << GetLastError();
return 0
}