mirror of
https://github.com/mirror/processhacker
synced 2026-06-08 16:03:24 +00:00
f4ad8a85da
git-svn-id: svn://svn.code.sf.net/p/processhacker/code@6179 21ef857c-d57f-4fe0-8362-d861dc6d29cd
1171 lines
39 KiB
C
1171 lines
39 KiB
C
/*
|
|
* Process Hacker -
|
|
* run as dialog
|
|
*
|
|
* Copyright (C) 2010-2013 wj32
|
|
*
|
|
* This file is part of Process Hacker.
|
|
*
|
|
* Process Hacker is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Process Hacker is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/*
|
|
* The run-as mechanism has three stages:
|
|
* 1. The user enters the information into the dialog box. Here it is decided
|
|
* whether the run-as service is needed. If it is not, PhCreateProcessAsUser
|
|
* is called directly. Otherwise, PhExecuteRunAsCommand2 is called for
|
|
* stage 2.
|
|
* 2. PhExecuteRunAsCommand2 creates a random service name and tries to create
|
|
* the service and execute it (using PhExecuteRunAsCommand). If the process
|
|
* has insufficient permissions, an elevated instance of phsvc is started
|
|
* and PhSvcCallExecuteRunAsCommand is called.
|
|
* 3. The service is started, and sets up an instance of phsvc with the same
|
|
* random service name as its port name. Either the original or elevated
|
|
* Process Hacker instance then calls PhSvcCallInvokeRunAsService to complete
|
|
* the operation.
|
|
*
|
|
* ProcessHacker.exe (user, limited privileges)
|
|
* * | ^
|
|
* | | | phsvc API (LPC)
|
|
* | | |
|
|
* | v |
|
|
* ProcessHacker.exe (user, full privileges)
|
|
* | ^ | ^
|
|
* | | SCM API (RPC) | |
|
|
* | | | |
|
|
* v | | | phsvc API (LPC)
|
|
* services.exe | |
|
|
* * | |
|
|
* | | |
|
|
* | | |
|
|
* | v |
|
|
* ProcessHacker.exe (NT AUTHORITY\SYSTEM)
|
|
* *
|
|
* |
|
|
* |
|
|
* |
|
|
* program.exe
|
|
*/
|
|
|
|
#include <phapp.h>
|
|
#include <phsvc.h>
|
|
#include <phsvccl.h>
|
|
#include <settings.h>
|
|
#include <emenu.h>
|
|
#include <shlwapi.h>
|
|
#include <winsta.h>
|
|
#include <windowsx.h>
|
|
|
|
typedef struct _RUNAS_DIALOG_CONTEXT
|
|
{
|
|
HANDLE ProcessId;
|
|
PPH_LIST DesktopList;
|
|
PPH_STRING CurrentWinStaName;
|
|
} RUNAS_DIALOG_CONTEXT, *PRUNAS_DIALOG_CONTEXT;
|
|
|
|
INT_PTR CALLBACK PhpRunAsDlgProc(
|
|
_In_ HWND hwndDlg,
|
|
_In_ UINT uMsg,
|
|
_In_ WPARAM wParam,
|
|
_In_ LPARAM lParam
|
|
);
|
|
|
|
VOID PhSetDesktopWinStaAccess(
|
|
VOID
|
|
);
|
|
|
|
VOID PhpSplitUserName(
|
|
_In_ PWSTR UserName,
|
|
_Out_ PPH_STRING *DomainPart,
|
|
_Out_ PPH_STRING *UserPart
|
|
);
|
|
|
|
#define SIP(String, Integer) { (String), (PVOID)(Integer) }
|
|
|
|
static PH_KEY_VALUE_PAIR PhpLogonTypePairs[] =
|
|
{
|
|
SIP(L"Batch", LOGON32_LOGON_BATCH),
|
|
SIP(L"Interactive", LOGON32_LOGON_INTERACTIVE),
|
|
SIP(L"Network", LOGON32_LOGON_NETWORK),
|
|
SIP(L"New credentials", LOGON32_LOGON_NEW_CREDENTIALS),
|
|
SIP(L"Service", LOGON32_LOGON_SERVICE)
|
|
};
|
|
|
|
static WCHAR RunAsOldServiceName[32] = L"";
|
|
static PH_QUEUED_LOCK RunAsOldServiceLock = PH_QUEUED_LOCK_INIT;
|
|
|
|
static PPH_STRING RunAsServiceName;
|
|
static SERVICE_STATUS_HANDLE RunAsServiceStatusHandle;
|
|
static PHSVC_STOP RunAsServiceStop;
|
|
|
|
VOID PhShowRunAsDialog(
|
|
_In_ HWND ParentWindowHandle,
|
|
_In_opt_ HANDLE ProcessId
|
|
)
|
|
{
|
|
RUNAS_DIALOG_CONTEXT context;
|
|
|
|
context.ProcessId = ProcessId;
|
|
context.DesktopList = NULL;
|
|
|
|
DialogBoxParam(
|
|
PhInstanceHandle,
|
|
MAKEINTRESOURCE(IDD_RUNAS),
|
|
ParentWindowHandle,
|
|
PhpRunAsDlgProc,
|
|
(LPARAM)&context
|
|
);
|
|
}
|
|
|
|
static VOID PhpAddAccountsToComboBox(
|
|
_In_ HWND ComboBoxHandle
|
|
)
|
|
{
|
|
LSA_HANDLE policyHandle;
|
|
LSA_ENUMERATION_HANDLE enumerationContext = 0;
|
|
PLSA_ENUMERATION_INFORMATION buffer;
|
|
ULONG count;
|
|
ULONG i;
|
|
PPH_STRING name;
|
|
SID_NAME_USE nameUse;
|
|
|
|
if (NT_SUCCESS(PhOpenLsaPolicy(&policyHandle, POLICY_VIEW_LOCAL_INFORMATION, NULL)))
|
|
{
|
|
while (NT_SUCCESS(LsaEnumerateAccounts(
|
|
policyHandle,
|
|
&enumerationContext,
|
|
&buffer,
|
|
0x100,
|
|
&count
|
|
)))
|
|
{
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
name = PhGetSidFullName(buffer[i].Sid, TRUE, &nameUse);
|
|
|
|
if (name)
|
|
{
|
|
if (nameUse == SidTypeUser)
|
|
ComboBox_AddString(ComboBoxHandle, name->Buffer);
|
|
|
|
PhDereferenceObject(name);
|
|
}
|
|
}
|
|
|
|
LsaFreeMemory(buffer);
|
|
}
|
|
|
|
LsaClose(policyHandle);
|
|
}
|
|
}
|
|
|
|
static BOOLEAN IsServiceAccount(
|
|
_In_ PPH_STRING UserName
|
|
)
|
|
{
|
|
if (
|
|
PhEqualString2(UserName, L"NT AUTHORITY\\LOCAL SERVICE", TRUE) ||
|
|
PhEqualString2(UserName, L"NT AUTHORITY\\NETWORK SERVICE", TRUE) ||
|
|
PhEqualString2(UserName, L"NT AUTHORITY\\SYSTEM", TRUE)
|
|
)
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static PPH_STRING GetCurrentWinStaName(
|
|
VOID
|
|
)
|
|
{
|
|
PPH_STRING string;
|
|
|
|
string = PhCreateStringEx(NULL, 0x200);
|
|
|
|
if (GetUserObjectInformation(
|
|
GetProcessWindowStation(),
|
|
UOI_NAME,
|
|
string->Buffer,
|
|
(ULONG)string->Length + 2,
|
|
NULL
|
|
))
|
|
{
|
|
PhTrimToNullTerminatorString(string);
|
|
return string;
|
|
}
|
|
else
|
|
{
|
|
PhDereferenceObject(string);
|
|
return PhCreateString(L"WinSta0"); // assume the current window station is WinSta0
|
|
}
|
|
}
|
|
|
|
static BOOL CALLBACK EnumDesktopsCallback(
|
|
_In_ PWSTR DesktopName,
|
|
_In_ LPARAM Context
|
|
)
|
|
{
|
|
PRUNAS_DIALOG_CONTEXT context = (PRUNAS_DIALOG_CONTEXT)Context;
|
|
|
|
PhAddItemList(context->DesktopList, PhConcatStrings(
|
|
3,
|
|
context->CurrentWinStaName->Buffer,
|
|
L"\\",
|
|
DesktopName
|
|
));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
INT_PTR CALLBACK PhpRunAsDlgProc(
|
|
_In_ HWND hwndDlg,
|
|
_In_ UINT uMsg,
|
|
_In_ WPARAM wParam,
|
|
_In_ LPARAM lParam
|
|
)
|
|
{
|
|
PRUNAS_DIALOG_CONTEXT context;
|
|
|
|
if (uMsg != WM_INITDIALOG)
|
|
{
|
|
context = (PRUNAS_DIALOG_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom());
|
|
}
|
|
else
|
|
{
|
|
context = (PRUNAS_DIALOG_CONTEXT)lParam;
|
|
SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context);
|
|
}
|
|
|
|
if (!context)
|
|
return FALSE;
|
|
|
|
switch (uMsg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
HWND typeComboBoxHandle = GetDlgItem(hwndDlg, IDC_TYPE);
|
|
HWND userNameComboBoxHandle = GetDlgItem(hwndDlg, IDC_USERNAME);
|
|
ULONG sessionId;
|
|
|
|
PhCenterWindow(hwndDlg, GetParent(hwndDlg));
|
|
|
|
if (SHAutoComplete_I)
|
|
{
|
|
SHAutoComplete_I(
|
|
GetDlgItem(hwndDlg, IDC_PROGRAM),
|
|
SHACF_AUTOAPPEND_FORCE_ON | SHACF_AUTOSUGGEST_FORCE_ON | SHACF_FILESYS_ONLY
|
|
);
|
|
}
|
|
|
|
ComboBox_AddString(typeComboBoxHandle, L"Batch");
|
|
ComboBox_AddString(typeComboBoxHandle, L"Interactive");
|
|
ComboBox_AddString(typeComboBoxHandle, L"Network");
|
|
ComboBox_AddString(typeComboBoxHandle, L"New credentials");
|
|
ComboBox_AddString(typeComboBoxHandle, L"Service");
|
|
PhSelectComboBoxString(typeComboBoxHandle, L"Interactive", FALSE);
|
|
|
|
ComboBox_AddString(userNameComboBoxHandle, L"NT AUTHORITY\\SYSTEM");
|
|
ComboBox_AddString(userNameComboBoxHandle, L"NT AUTHORITY\\LOCAL SERVICE");
|
|
ComboBox_AddString(userNameComboBoxHandle, L"NT AUTHORITY\\NETWORK SERVICE");
|
|
|
|
PhpAddAccountsToComboBox(userNameComboBoxHandle);
|
|
|
|
if (NT_SUCCESS(PhGetProcessSessionId(NtCurrentProcess(), &sessionId)))
|
|
SetDlgItemInt(hwndDlg, IDC_SESSIONID, sessionId, FALSE);
|
|
|
|
SetDlgItemText(hwndDlg, IDC_DESKTOP, L"WinSta0\\Default");
|
|
|
|
SetDlgItemText(hwndDlg, IDC_PROGRAM,
|
|
((PPH_STRING)PhAutoDereferenceObject(PhGetStringSetting(L"RunAsProgram")))->Buffer);
|
|
|
|
if (!context->ProcessId)
|
|
{
|
|
SetDlgItemText(hwndDlg, IDC_USERNAME,
|
|
((PPH_STRING)PhAutoDereferenceObject(PhGetStringSetting(L"RunAsUserName")))->Buffer);
|
|
|
|
// Fire the user name changed event so we can fix the logon type.
|
|
SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_USERNAME, CBN_EDITCHANGE), 0);
|
|
}
|
|
else
|
|
{
|
|
HANDLE processHandle;
|
|
HANDLE tokenHandle;
|
|
PTOKEN_USER user;
|
|
PPH_STRING userName;
|
|
|
|
if (NT_SUCCESS(PhOpenProcess(
|
|
&processHandle,
|
|
ProcessQueryAccess,
|
|
context->ProcessId
|
|
)))
|
|
{
|
|
if (NT_SUCCESS(PhOpenProcessToken(
|
|
&tokenHandle,
|
|
TOKEN_QUERY,
|
|
processHandle
|
|
)))
|
|
{
|
|
if (NT_SUCCESS(PhGetTokenUser(tokenHandle, &user)))
|
|
{
|
|
if (userName = PhGetSidFullName(user->User.Sid, TRUE, NULL))
|
|
{
|
|
SetDlgItemText(hwndDlg, IDC_USERNAME, userName->Buffer);
|
|
PhDereferenceObject(userName);
|
|
}
|
|
|
|
PhFree(user);
|
|
}
|
|
|
|
NtClose(tokenHandle);
|
|
}
|
|
|
|
NtClose(processHandle);
|
|
}
|
|
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_USERNAME), FALSE);
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_PASSWORD), FALSE);
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_TYPE), FALSE);
|
|
}
|
|
|
|
SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_PROGRAM), TRUE);
|
|
Edit_SetSel(GetDlgItem(hwndDlg, IDC_PROGRAM), 0, -1);
|
|
|
|
//if (!PhElevated)
|
|
// SendMessage(GetDlgItem(hwndDlg, IDOK), BCM_SETSHIELD, 0, TRUE);
|
|
|
|
if (!WINDOWS_HAS_UAC)
|
|
ShowWindow(GetDlgItem(hwndDlg, IDC_TOGGLEELEVATION), SW_HIDE);
|
|
}
|
|
break;
|
|
case WM_DESTROY:
|
|
{
|
|
if (context->DesktopList)
|
|
PhDereferenceObject(context->DesktopList);
|
|
|
|
RemoveProp(hwndDlg, PhMakeContextAtom());
|
|
}
|
|
break;
|
|
case WM_COMMAND:
|
|
{
|
|
switch (LOWORD(wParam))
|
|
{
|
|
case IDCANCEL:
|
|
EndDialog(hwndDlg, IDCANCEL);
|
|
break;
|
|
case IDOK:
|
|
{
|
|
NTSTATUS status;
|
|
PPH_STRING program;
|
|
PPH_STRING userName;
|
|
PPH_STRING password;
|
|
PPH_STRING logonTypeString;
|
|
ULONG logonType;
|
|
ULONG sessionId;
|
|
PPH_STRING desktopName;
|
|
BOOLEAN useLinkedToken;
|
|
|
|
program = PhaGetDlgItemText(hwndDlg, IDC_PROGRAM);
|
|
userName = PhaGetDlgItemText(hwndDlg, IDC_USERNAME);
|
|
logonTypeString = PhaGetDlgItemText(hwndDlg, IDC_TYPE);
|
|
|
|
// Fix up the user name if it doesn't have a domain.
|
|
if (PhFindCharInString(userName, 0, '\\') == -1)
|
|
{
|
|
PSID sid;
|
|
PPH_STRING newUserName;
|
|
|
|
if (NT_SUCCESS(PhLookupName(&userName->sr, &sid, NULL, NULL)))
|
|
{
|
|
newUserName = PhGetSidFullName(sid, TRUE, NULL);
|
|
|
|
if (newUserName)
|
|
{
|
|
PhAutoDereferenceObject(newUserName);
|
|
userName = newUserName;
|
|
}
|
|
|
|
PhFree(sid);
|
|
}
|
|
}
|
|
|
|
if (!IsServiceAccount(userName))
|
|
password = PhGetWindowText(GetDlgItem(hwndDlg, IDC_PASSWORD));
|
|
else
|
|
password = NULL;
|
|
|
|
sessionId = GetDlgItemInt(hwndDlg, IDC_SESSIONID, NULL, FALSE);
|
|
desktopName = PhaGetDlgItemText(hwndDlg, IDC_DESKTOP);
|
|
|
|
if (WINDOWS_HAS_UAC)
|
|
useLinkedToken = Button_GetCheck(GetDlgItem(hwndDlg, IDC_TOGGLEELEVATION)) == BST_CHECKED;
|
|
else
|
|
useLinkedToken = FALSE;
|
|
|
|
if (PhFindIntegerSiKeyValuePairs(
|
|
PhpLogonTypePairs,
|
|
sizeof(PhpLogonTypePairs),
|
|
logonTypeString->Buffer,
|
|
&logonType
|
|
))
|
|
{
|
|
if (
|
|
logonType == LOGON32_LOGON_INTERACTIVE &&
|
|
!context->ProcessId &&
|
|
sessionId == NtCurrentPeb()->SessionId &&
|
|
!useLinkedToken
|
|
)
|
|
{
|
|
// We are eligible to load the user profile.
|
|
// This must be done here, not in the service, because
|
|
// we need to be in the target session.
|
|
|
|
PH_CREATE_PROCESS_AS_USER_INFO createInfo;
|
|
PPH_STRING domainPart;
|
|
PPH_STRING userPart;
|
|
|
|
PhpSplitUserName(userName->Buffer, &domainPart, &userPart);
|
|
|
|
memset(&createInfo, 0, sizeof(PH_CREATE_PROCESS_AS_USER_INFO));
|
|
createInfo.CommandLine = program->Buffer;
|
|
createInfo.UserName = userPart->Buffer;
|
|
createInfo.DomainName = domainPart->Buffer;
|
|
createInfo.Password = PhGetStringOrEmpty(password);
|
|
|
|
// Whenever we can, try not to set the desktop name; it breaks a lot of things.
|
|
// Note that on XP we must set it, otherwise the program doesn't display correctly.
|
|
if (WindowsVersion < WINDOWS_VISTA || (desktopName->Length != 0 && !PhEqualString2(desktopName, L"WinSta0\\Default", TRUE)))
|
|
createInfo.DesktopName = desktopName->Buffer;
|
|
|
|
PhSetDesktopWinStaAccess();
|
|
|
|
status = PhCreateProcessAsUser(
|
|
&createInfo,
|
|
PH_CREATE_PROCESS_WITH_PROFILE,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if (domainPart) PhDereferenceObject(domainPart);
|
|
if (userPart) PhDereferenceObject(userPart);
|
|
}
|
|
else
|
|
{
|
|
status = PhExecuteRunAsCommand2(
|
|
hwndDlg,
|
|
program->Buffer,
|
|
userName->Buffer,
|
|
PhGetStringOrEmpty(password),
|
|
logonType,
|
|
context->ProcessId,
|
|
sessionId,
|
|
desktopName->Buffer,
|
|
useLinkedToken
|
|
);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (password)
|
|
{
|
|
RtlSecureZeroMemory(password->Buffer, password->Length);
|
|
PhDereferenceObject(password);
|
|
}
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
if (status != STATUS_CANCELLED)
|
|
PhShowStatus(hwndDlg, L"Unable to start the program", status, 0);
|
|
}
|
|
else if (status != STATUS_TIMEOUT)
|
|
{
|
|
PhSetStringSetting2(L"RunAsProgram", &program->sr);
|
|
PhSetStringSetting2(L"RunAsUserName", &userName->sr);
|
|
EndDialog(hwndDlg, IDOK);
|
|
}
|
|
}
|
|
break;
|
|
case IDC_BROWSE:
|
|
{
|
|
static PH_FILETYPE_FILTER filters[] =
|
|
{
|
|
{ L"Programs (*.exe;*.pif;*.com;*.bat)", L"*.exe;*.pif;*.com;*.bat" },
|
|
{ L"All files (*.*)", L"*.*" }
|
|
};
|
|
PVOID fileDialog;
|
|
|
|
fileDialog = PhCreateOpenFileDialog();
|
|
PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER));
|
|
PhSetFileDialogFileName(fileDialog, PhaGetDlgItemText(hwndDlg, IDC_PROGRAM)->Buffer);
|
|
|
|
if (PhShowFileDialog(hwndDlg, fileDialog))
|
|
{
|
|
PPH_STRING fileName;
|
|
|
|
fileName = PhGetFileDialogFileName(fileDialog);
|
|
SetDlgItemText(hwndDlg, IDC_PROGRAM, fileName->Buffer);
|
|
PhDereferenceObject(fileName);
|
|
}
|
|
|
|
PhFreeFileDialog(fileDialog);
|
|
}
|
|
break;
|
|
case IDC_USERNAME:
|
|
{
|
|
PPH_STRING userName = NULL;
|
|
|
|
if (!context->ProcessId && HIWORD(wParam) == CBN_SELCHANGE)
|
|
{
|
|
userName = PhAutoDereferenceObject(PhGetComboBoxString(GetDlgItem(hwndDlg, IDC_USERNAME), -1));
|
|
}
|
|
else if (!context->ProcessId && (
|
|
HIWORD(wParam) == CBN_EDITCHANGE ||
|
|
HIWORD(wParam) == CBN_CLOSEUP
|
|
))
|
|
{
|
|
userName = PhaGetDlgItemText(hwndDlg, IDC_USERNAME);
|
|
}
|
|
|
|
if (userName)
|
|
{
|
|
if (IsServiceAccount(userName))
|
|
{
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_PASSWORD), FALSE);
|
|
|
|
// Hack for Windows XP
|
|
if (
|
|
PhEqualString2(userName, L"NT AUTHORITY\\SYSTEM", TRUE) &&
|
|
WindowsVersion <= WINDOWS_XP
|
|
)
|
|
{
|
|
PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_TYPE), L"New credentials", FALSE);
|
|
}
|
|
else
|
|
{
|
|
PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_TYPE), L"Service", FALSE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_PASSWORD), TRUE);
|
|
PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_TYPE), L"Interactive", FALSE);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case IDC_SESSIONS:
|
|
{
|
|
PPH_EMENU sessionsMenu;
|
|
PSESSIONIDW sessions;
|
|
ULONG numberOfSessions;
|
|
ULONG i;
|
|
RECT buttonRect;
|
|
PPH_EMENU_ITEM selectedItem;
|
|
|
|
sessionsMenu = PhCreateEMenu();
|
|
|
|
if (WinStationEnumerateW(NULL, &sessions, &numberOfSessions))
|
|
{
|
|
for (i = 0; i < numberOfSessions; i++)
|
|
{
|
|
PPH_STRING menuString;
|
|
WINSTATIONINFORMATION winStationInfo;
|
|
ULONG returnLength;
|
|
|
|
if (!WinStationQueryInformationW(
|
|
NULL,
|
|
sessions[i].SessionId,
|
|
WinStationInformation,
|
|
&winStationInfo,
|
|
sizeof(WINSTATIONINFORMATION),
|
|
&returnLength
|
|
))
|
|
{
|
|
winStationInfo.Domain[0] = 0;
|
|
winStationInfo.UserName[0] = 0;
|
|
}
|
|
|
|
if (
|
|
winStationInfo.UserName[0] != 0 &&
|
|
sessions[i].WinStationName[0] != 0
|
|
)
|
|
{
|
|
menuString = PhFormatString(
|
|
L"%u: %s (%s\\%s)",
|
|
sessions[i].SessionId,
|
|
sessions[i].WinStationName,
|
|
winStationInfo.Domain,
|
|
winStationInfo.UserName
|
|
);
|
|
}
|
|
else if (winStationInfo.UserName[0] != 0)
|
|
{
|
|
menuString = PhFormatString(
|
|
L"%u: %s\\%s",
|
|
sessions[i].SessionId,
|
|
winStationInfo.Domain,
|
|
winStationInfo.UserName
|
|
);
|
|
}
|
|
else if (sessions[i].WinStationName[0] != 0)
|
|
{
|
|
menuString = PhFormatString(
|
|
L"%u: %s",
|
|
sessions[i].SessionId,
|
|
sessions[i].WinStationName
|
|
);
|
|
}
|
|
else
|
|
{
|
|
menuString = PhFormatString(L"%u", sessions[i].SessionId);
|
|
}
|
|
|
|
PhInsertEMenuItem(sessionsMenu,
|
|
PhCreateEMenuItem(0, 0, menuString->Buffer, NULL, UlongToPtr(sessions[i].SessionId)), -1);
|
|
PhAutoDereferenceObject(menuString);
|
|
}
|
|
|
|
WinStationFreeMemory(sessions);
|
|
|
|
GetWindowRect(GetDlgItem(hwndDlg, IDC_SESSIONS), &buttonRect);
|
|
|
|
selectedItem = PhShowEMenu(
|
|
sessionsMenu,
|
|
hwndDlg,
|
|
PH_EMENU_SHOW_LEFTRIGHT,
|
|
PH_ALIGN_LEFT | PH_ALIGN_TOP,
|
|
buttonRect.right,
|
|
buttonRect.top
|
|
);
|
|
|
|
if (selectedItem)
|
|
{
|
|
SetDlgItemInt(
|
|
hwndDlg,
|
|
IDC_SESSIONID,
|
|
PtrToUlong(selectedItem->Context),
|
|
FALSE
|
|
);
|
|
}
|
|
|
|
PhDestroyEMenu(sessionsMenu);
|
|
}
|
|
}
|
|
break;
|
|
case IDC_DESKTOPS:
|
|
{
|
|
PPH_EMENU desktopsMenu;
|
|
ULONG i;
|
|
RECT buttonRect;
|
|
PPH_EMENU_ITEM selectedItem;
|
|
|
|
desktopsMenu = PhCreateEMenu();
|
|
|
|
if (!context->DesktopList)
|
|
context->DesktopList = PhCreateList(10);
|
|
|
|
context->CurrentWinStaName = GetCurrentWinStaName();
|
|
|
|
EnumDesktops(GetProcessWindowStation(), EnumDesktopsCallback, (LPARAM)context);
|
|
|
|
for (i = 0; i < context->DesktopList->Count; i++)
|
|
{
|
|
PhInsertEMenuItem(
|
|
desktopsMenu,
|
|
PhCreateEMenuItem(0, 0, ((PPH_STRING)context->DesktopList->Items[i])->Buffer, NULL, NULL),
|
|
-1
|
|
);
|
|
}
|
|
|
|
GetWindowRect(GetDlgItem(hwndDlg, IDC_DESKTOPS), &buttonRect);
|
|
|
|
selectedItem = PhShowEMenu(
|
|
desktopsMenu,
|
|
hwndDlg,
|
|
PH_EMENU_SHOW_LEFTRIGHT,
|
|
PH_ALIGN_LEFT | PH_ALIGN_TOP,
|
|
buttonRect.right,
|
|
buttonRect.top
|
|
);
|
|
|
|
if (selectedItem)
|
|
{
|
|
SetDlgItemText(
|
|
hwndDlg,
|
|
IDC_DESKTOP,
|
|
selectedItem->Text
|
|
);
|
|
}
|
|
|
|
for (i = 0; i < context->DesktopList->Count; i++)
|
|
PhDereferenceObject(context->DesktopList->Items[i]);
|
|
|
|
PhClearList(context->DesktopList);
|
|
PhDereferenceObject(context->CurrentWinStaName);
|
|
PhDestroyEMenu(desktopsMenu);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* Sets the access control lists of the current window station
|
|
* and desktop to allow all access.
|
|
*/
|
|
VOID PhSetDesktopWinStaAccess(
|
|
VOID
|
|
)
|
|
{
|
|
static SID_IDENTIFIER_AUTHORITY appPackageAuthority = SECURITY_APP_PACKAGE_AUTHORITY;
|
|
|
|
HWINSTA wsHandle;
|
|
HDESK desktopHandle;
|
|
ULONG allocationLength;
|
|
PSECURITY_DESCRIPTOR securityDescriptor;
|
|
PACL dacl;
|
|
CHAR allAppPackagesSidBuffer[FIELD_OFFSET(SID, SubAuthority) + sizeof(ULONG) * 2];
|
|
PSID allAppPackagesSid;
|
|
|
|
// TODO: Set security on the correct window station and desktop.
|
|
|
|
allAppPackagesSid = (PISID)allAppPackagesSidBuffer;
|
|
RtlInitializeSid(allAppPackagesSid, &appPackageAuthority, SECURITY_BUILTIN_APP_PACKAGE_RID_COUNT);
|
|
*RtlSubAuthoritySid(allAppPackagesSid, 0) = SECURITY_APP_PACKAGE_BASE_RID;
|
|
*RtlSubAuthoritySid(allAppPackagesSid, 1) = SECURITY_BUILTIN_PACKAGE_ANY_PACKAGE;
|
|
|
|
// We create a DACL that allows everyone to access everything.
|
|
|
|
allocationLength = SECURITY_DESCRIPTOR_MIN_LENGTH +
|
|
(ULONG)sizeof(ACL) +
|
|
(ULONG)sizeof(ACCESS_ALLOWED_ACE) +
|
|
RtlLengthSid(&PhSeEveryoneSid) +
|
|
(ULONG)sizeof(ACCESS_ALLOWED_ACE) +
|
|
RtlLengthSid(allAppPackagesSid);
|
|
securityDescriptor = PhAllocate(allocationLength);
|
|
dacl = (PACL)((PCHAR)securityDescriptor + SECURITY_DESCRIPTOR_MIN_LENGTH);
|
|
|
|
RtlCreateSecurityDescriptor(securityDescriptor, SECURITY_DESCRIPTOR_REVISION);
|
|
|
|
RtlCreateAcl(dacl, allocationLength - SECURITY_DESCRIPTOR_MIN_LENGTH, ACL_REVISION);
|
|
RtlAddAccessAllowedAce(dacl, ACL_REVISION, GENERIC_ALL, &PhSeEveryoneSid);
|
|
|
|
if (WindowsVersion >= WINDOWS_8)
|
|
{
|
|
RtlAddAccessAllowedAce(dacl, ACL_REVISION, GENERIC_ALL, allAppPackagesSid);
|
|
}
|
|
|
|
RtlSetDaclSecurityDescriptor(securityDescriptor, TRUE, dacl, FALSE);
|
|
|
|
if (wsHandle = OpenWindowStation(
|
|
L"WinSta0",
|
|
FALSE,
|
|
WRITE_DAC
|
|
))
|
|
{
|
|
PhSetObjectSecurity(wsHandle, DACL_SECURITY_INFORMATION, securityDescriptor);
|
|
CloseWindowStation(wsHandle);
|
|
}
|
|
|
|
if (desktopHandle = OpenDesktop(
|
|
L"Default",
|
|
0,
|
|
FALSE,
|
|
WRITE_DAC | DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS
|
|
))
|
|
{
|
|
PhSetObjectSecurity(desktopHandle, DACL_SECURITY_INFORMATION, securityDescriptor);
|
|
CloseDesktop(desktopHandle);
|
|
}
|
|
|
|
PhFree(securityDescriptor);
|
|
}
|
|
|
|
/**
|
|
* Executes the run-as service.
|
|
*
|
|
* \param Parameters The run-as parameters.
|
|
*
|
|
* \remarks This function requires administrator-level access.
|
|
*/
|
|
NTSTATUS PhExecuteRunAsCommand(
|
|
_In_ PPH_RUNAS_SERVICE_PARAMETERS Parameters
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
ULONG win32Result;
|
|
PPH_STRING commandLine;
|
|
SC_HANDLE scManagerHandle;
|
|
SC_HANDLE serviceHandle;
|
|
PPH_STRING portName;
|
|
UNICODE_STRING portNameUs;
|
|
ULONG attempts;
|
|
LARGE_INTEGER interval;
|
|
|
|
if (!(scManagerHandle = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE)))
|
|
return PhGetLastWin32ErrorAsNtStatus();
|
|
|
|
commandLine = PhFormatString(L"\"%s\" -ras \"%s\"", PhApplicationFileName->Buffer, Parameters->ServiceName);
|
|
|
|
serviceHandle = CreateService(
|
|
scManagerHandle,
|
|
Parameters->ServiceName,
|
|
Parameters->ServiceName,
|
|
SERVICE_ALL_ACCESS,
|
|
SERVICE_WIN32_OWN_PROCESS,
|
|
SERVICE_DEMAND_START,
|
|
SERVICE_ERROR_IGNORE,
|
|
commandLine->Buffer,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
L"LocalSystem",
|
|
L""
|
|
);
|
|
win32Result = GetLastError();
|
|
|
|
PhDereferenceObject(commandLine);
|
|
|
|
CloseServiceHandle(scManagerHandle);
|
|
|
|
if (!serviceHandle)
|
|
{
|
|
return NTSTATUS_FROM_WIN32(win32Result);
|
|
}
|
|
|
|
PhSetDesktopWinStaAccess();
|
|
|
|
StartService(serviceHandle, 0, NULL);
|
|
DeleteService(serviceHandle);
|
|
|
|
portName = PhConcatStrings2(L"\\BaseNamedObjects\\", Parameters->ServiceName);
|
|
PhStringRefToUnicodeString(&portName->sr, &portNameUs);
|
|
attempts = 10;
|
|
|
|
// Try to connect several times because the server may take
|
|
// a while to initialize.
|
|
do
|
|
{
|
|
status = PhSvcConnectToServer(&portNameUs, 0);
|
|
|
|
if (NT_SUCCESS(status))
|
|
break;
|
|
|
|
interval.QuadPart = -50 * PH_TIMEOUT_MS;
|
|
NtDelayExecution(FALSE, &interval);
|
|
} while (--attempts != 0);
|
|
|
|
PhDereferenceObject(portName);
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
status = PhSvcCallInvokeRunAsService(Parameters);
|
|
PhSvcDisconnectFromServer();
|
|
}
|
|
|
|
if (serviceHandle)
|
|
CloseServiceHandle(serviceHandle);
|
|
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* Starts a program as another user.
|
|
*
|
|
* \param hWnd A handle to the parent window.
|
|
* \param Program The command line of the program to start.
|
|
* \param UserName The user to start the program as. The user
|
|
* name should be specified as: domain\\name. This parameter
|
|
* can be NULL if \a ProcessIdWithToken is specified.
|
|
* \param Password The password for the specified user. If there
|
|
* is no password, specify an empty string. This parameter
|
|
* can be NULL if \a ProcessIdWithToken is specified.
|
|
* \param LogonType The logon type for the specified user. This
|
|
* parameter can be 0 if \a ProcessIdWithToken is specified.
|
|
* \param ProcessIdWithToken The ID of a process from which
|
|
* to duplicate the token.
|
|
* \param SessionId The ID of the session to run the program
|
|
* under.
|
|
* \param DesktopName The window station and desktop to run the
|
|
* program under.
|
|
* \param UseLinkedToken Uses the linked token if possible.
|
|
*
|
|
* \retval STATUS_CANCELLED The user cancelled the operation.
|
|
*
|
|
* \remarks This function will cause another instance of
|
|
* Process Hacker to be executed if the current security context
|
|
* does not have sufficient system access. This is done
|
|
* through a UAC elevation prompt.
|
|
*/
|
|
NTSTATUS PhExecuteRunAsCommand2(
|
|
_In_ HWND hWnd,
|
|
_In_ PWSTR Program,
|
|
_In_opt_ PWSTR UserName,
|
|
_In_opt_ PWSTR Password,
|
|
_In_opt_ ULONG LogonType,
|
|
_In_opt_ HANDLE ProcessIdWithToken,
|
|
_In_ ULONG SessionId,
|
|
_In_ PWSTR DesktopName,
|
|
_In_ BOOLEAN UseLinkedToken
|
|
)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PH_RUNAS_SERVICE_PARAMETERS parameters;
|
|
WCHAR serviceName[32];
|
|
PPH_STRING portName;
|
|
UNICODE_STRING portNameUs;
|
|
|
|
memset(¶meters, 0, sizeof(PH_RUNAS_SERVICE_PARAMETERS));
|
|
parameters.ProcessId = HandleToUlong(ProcessIdWithToken);
|
|
parameters.UserName = UserName;
|
|
parameters.Password = Password;
|
|
parameters.LogonType = LogonType;
|
|
parameters.SessionId = SessionId;
|
|
parameters.CommandLine = Program;
|
|
parameters.DesktopName = DesktopName;
|
|
parameters.UseLinkedToken = UseLinkedToken;
|
|
|
|
// Try to use an existing instance of the service if possible.
|
|
if (RunAsOldServiceName[0] != 0)
|
|
{
|
|
PhAcquireQueuedLockExclusive(&RunAsOldServiceLock);
|
|
|
|
portName = PhConcatStrings2(L"\\BaseNamedObjects\\", RunAsOldServiceName);
|
|
PhStringRefToUnicodeString(&portName->sr, &portNameUs);
|
|
|
|
if (NT_SUCCESS(PhSvcConnectToServer(&portNameUs, 0)))
|
|
{
|
|
parameters.ServiceName = RunAsOldServiceName;
|
|
status = PhSvcCallInvokeRunAsService(¶meters);
|
|
PhSvcDisconnectFromServer();
|
|
|
|
PhDereferenceObject(portName);
|
|
PhReleaseQueuedLockExclusive(&RunAsOldServiceLock);
|
|
|
|
return status;
|
|
}
|
|
|
|
PhDereferenceObject(portName);
|
|
PhReleaseQueuedLockExclusive(&RunAsOldServiceLock);
|
|
}
|
|
|
|
// An existing instance was not available. Proceed normally.
|
|
|
|
memcpy(serviceName, L"ProcessHacker", 13 * sizeof(WCHAR));
|
|
PhGenerateRandomAlphaString(&serviceName[13], 16);
|
|
PhAcquireQueuedLockExclusive(&RunAsOldServiceLock);
|
|
memcpy(RunAsOldServiceName, serviceName, sizeof(serviceName));
|
|
PhReleaseQueuedLockExclusive(&RunAsOldServiceLock);
|
|
|
|
parameters.ServiceName = serviceName;
|
|
|
|
if (PhElevated)
|
|
{
|
|
status = PhExecuteRunAsCommand(¶meters);
|
|
}
|
|
else
|
|
{
|
|
if (PhUiConnectToPhSvc(hWnd, FALSE))
|
|
{
|
|
status = PhSvcCallExecuteRunAsCommand(¶meters);
|
|
PhUiDisconnectFromPhSvc();
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_CANCELLED;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
static VOID PhpSplitUserName(
|
|
_In_ PWSTR UserName,
|
|
_Out_ PPH_STRING *DomainPart,
|
|
_Out_ PPH_STRING *UserPart
|
|
)
|
|
{
|
|
PH_STRINGREF userName;
|
|
PH_STRINGREF domainPart;
|
|
PH_STRINGREF userPart;
|
|
|
|
PhInitializeStringRefLongHint(&userName, UserName);
|
|
|
|
if (PhSplitStringRefAtChar(&userName, '\\', &domainPart, &userPart))
|
|
{
|
|
*DomainPart = PhCreateString2(&domainPart);
|
|
*UserPart = PhCreateString2(&userPart);
|
|
}
|
|
else
|
|
{
|
|
*DomainPart = NULL;
|
|
*UserPart = PhCreateString2(&userName);
|
|
}
|
|
}
|
|
|
|
static VOID SetRunAsServiceStatus(
|
|
_In_ ULONG State
|
|
)
|
|
{
|
|
SERVICE_STATUS status;
|
|
|
|
memset(&status, 0, sizeof(SERVICE_STATUS));
|
|
status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
|
|
status.dwCurrentState = State;
|
|
status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
|
|
|
|
SetServiceStatus(RunAsServiceStatusHandle, &status);
|
|
}
|
|
|
|
static DWORD WINAPI RunAsServiceHandlerEx(
|
|
_In_ DWORD dwControl,
|
|
_In_ DWORD dwEventType,
|
|
_In_ LPVOID lpEventData,
|
|
_In_ LPVOID lpContext
|
|
)
|
|
{
|
|
switch (dwControl)
|
|
{
|
|
case SERVICE_CONTROL_STOP:
|
|
PhSvcStop(&RunAsServiceStop);
|
|
return NO_ERROR;
|
|
case SERVICE_CONTROL_INTERROGATE:
|
|
return NO_ERROR;
|
|
}
|
|
|
|
return ERROR_CALL_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
static VOID WINAPI RunAsServiceMain(
|
|
_In_ DWORD dwArgc,
|
|
_In_ LPTSTR *lpszArgv
|
|
)
|
|
{
|
|
PPH_STRING portName;
|
|
UNICODE_STRING portNameUs;
|
|
LARGE_INTEGER timeout;
|
|
|
|
memset(&RunAsServiceStop, 0, sizeof(PHSVC_STOP));
|
|
RunAsServiceStatusHandle = RegisterServiceCtrlHandlerEx(RunAsServiceName->Buffer, RunAsServiceHandlerEx, NULL);
|
|
SetRunAsServiceStatus(SERVICE_RUNNING);
|
|
|
|
portName = PhConcatStrings2(L"\\BaseNamedObjects\\", RunAsServiceName->Buffer);
|
|
PhStringRefToUnicodeString(&portName->sr, &portNameUs);
|
|
// Use a shorter timeout value to reduce the time spent running as SYSTEM.
|
|
timeout.QuadPart = -5 * PH_TIMEOUT_SEC;
|
|
|
|
PhSvcMain(&portNameUs, &timeout, &RunAsServiceStop);
|
|
|
|
SetRunAsServiceStatus(SERVICE_STOPPED);
|
|
}
|
|
|
|
NTSTATUS PhRunAsServiceStart(
|
|
_In_ PPH_STRING ServiceName
|
|
)
|
|
{
|
|
HANDLE tokenHandle;
|
|
SERVICE_TABLE_ENTRY entry;
|
|
|
|
// Enable some required privileges.
|
|
|
|
if (NT_SUCCESS(PhOpenProcessToken(
|
|
&tokenHandle,
|
|
TOKEN_ADJUST_PRIVILEGES,
|
|
NtCurrentProcess()
|
|
)))
|
|
{
|
|
PhSetTokenPrivilege(tokenHandle, L"SeAssignPrimaryTokenPrivilege", NULL, SE_PRIVILEGE_ENABLED);
|
|
PhSetTokenPrivilege(tokenHandle, L"SeBackupPrivilege", NULL, SE_PRIVILEGE_ENABLED);
|
|
PhSetTokenPrivilege(tokenHandle, L"SeImpersonatePrivilege", NULL, SE_PRIVILEGE_ENABLED);
|
|
PhSetTokenPrivilege(tokenHandle, L"SeIncreaseQuotaPrivilege", NULL, SE_PRIVILEGE_ENABLED);
|
|
PhSetTokenPrivilege(tokenHandle, L"SeRestorePrivilege", NULL, SE_PRIVILEGE_ENABLED);
|
|
NtClose(tokenHandle);
|
|
}
|
|
|
|
RunAsServiceName = ServiceName;
|
|
|
|
entry.lpServiceName = ServiceName->Buffer;
|
|
entry.lpServiceProc = RunAsServiceMain;
|
|
|
|
StartServiceCtrlDispatcher(&entry);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS PhInvokeRunAsService(
|
|
_In_ PPH_RUNAS_SERVICE_PARAMETERS Parameters
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PPH_STRING domainName;
|
|
PPH_STRING userName;
|
|
PH_CREATE_PROCESS_AS_USER_INFO createInfo;
|
|
ULONG flags;
|
|
|
|
if (Parameters->UserName)
|
|
{
|
|
PhpSplitUserName(Parameters->UserName, &domainName, &userName);
|
|
}
|
|
else
|
|
{
|
|
domainName = NULL;
|
|
userName = NULL;
|
|
}
|
|
|
|
memset(&createInfo, 0, sizeof(PH_CREATE_PROCESS_AS_USER_INFO));
|
|
createInfo.ApplicationName = Parameters->FileName;
|
|
createInfo.CommandLine = Parameters->CommandLine;
|
|
createInfo.CurrentDirectory = Parameters->CurrentDirectory;
|
|
createInfo.DomainName = PhGetString(domainName);
|
|
createInfo.UserName = PhGetString(userName);
|
|
createInfo.Password = Parameters->Password;
|
|
createInfo.LogonType = Parameters->LogonType;
|
|
createInfo.SessionId = Parameters->SessionId;
|
|
createInfo.DesktopName = Parameters->DesktopName;
|
|
|
|
flags = PH_CREATE_PROCESS_SET_SESSION_ID;
|
|
|
|
if (Parameters->ProcessId)
|
|
{
|
|
createInfo.ProcessIdWithToken = UlongToHandle(Parameters->ProcessId);
|
|
flags |= PH_CREATE_PROCESS_USE_PROCESS_TOKEN;
|
|
}
|
|
|
|
if (Parameters->UseLinkedToken)
|
|
flags |= PH_CREATE_PROCESS_USE_LINKED_TOKEN;
|
|
|
|
status = PhCreateProcessAsUser(
|
|
&createInfo,
|
|
flags,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if (domainName) PhDereferenceObject(domainName);
|
|
if (userName) PhDereferenceObject(userName);
|
|
|
|
return status;
|
|
}
|