Process Hacker
runas.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * run as dialog
4  *
5  * Copyright (C) 2010-2013 wj32
6  *
7  * This file is part of Process Hacker.
8  *
9  * Process Hacker is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * Process Hacker is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 /*
24  * The run-as mechanism has three stages:
25  * 1. The user enters the information into the dialog box. Here it is decided
26  * whether the run-as service is needed. If it is not, PhCreateProcessAsUser
27  * is called directly. Otherwise, PhExecuteRunAsCommand2 is called for
28  * stage 2.
29  * 2. PhExecuteRunAsCommand2 creates a random service name and tries to create
30  * the service and execute it (using PhExecuteRunAsCommand). If the process
31  * has insufficient permissions, an elevated instance of phsvc is started
32  * and PhSvcCallExecuteRunAsCommand is called.
33  * 3. The service is started, and sets up an instance of phsvc with the same
34  * random service name as its port name. Either the original or elevated
35  * Process Hacker instance then calls PhSvcCallInvokeRunAsService to complete
36  * the operation.
37  *
38  * ProcessHacker.exe (user, limited privileges)
39  * * | ^
40  * | | | phsvc API (LPC)
41  * | | |
42  * | v |
43  * ProcessHacker.exe (user, full privileges)
44  * | ^ | ^
45  * | | SCM API (RPC) | |
46  * | | | |
47  * v | | | phsvc API (LPC)
48  * services.exe | |
49  * * | |
50  * | | |
51  * | | |
52  * | v |
53  * ProcessHacker.exe (NT AUTHORITY\SYSTEM)
54  * *
55  * |
56  * |
57  * |
58  * program.exe
59  */
60 
61 #include <phapp.h>
62 #include <phsvc.h>
63 #include <phsvccl.h>
64 #include <settings.h>
65 #include <emenu.h>
66 #include <shlwapi.h>
67 #include <winsta.h>
68 #include <windowsx.h>
69 
70 typedef struct _RUNAS_DIALOG_CONTEXT
71 {
72  HANDLE ProcessId;
73  PPH_LIST DesktopList;
74  PPH_STRING CurrentWinStaName;
76 
77 INT_PTR CALLBACK PhpRunAsDlgProc(
78  _In_ HWND hwndDlg,
79  _In_ UINT uMsg,
80  _In_ WPARAM wParam,
81  _In_ LPARAM lParam
82  );
83 
85  VOID
86  );
87 
89  _In_ PWSTR UserName,
90  _Out_ PPH_STRING *DomainPart,
91  _Out_ PPH_STRING *UserPart
92  );
93 
94 #define SIP(String, Integer) { (String), (PVOID)(Integer) }
95 
96 static PH_KEY_VALUE_PAIR PhpLogonTypePairs[] =
97 {
98  SIP(L"Batch", LOGON32_LOGON_BATCH),
99  SIP(L"Interactive", LOGON32_LOGON_INTERACTIVE),
100  SIP(L"Network", LOGON32_LOGON_NETWORK),
101  SIP(L"New credentials", LOGON32_LOGON_NEW_CREDENTIALS),
102  SIP(L"Service", LOGON32_LOGON_SERVICE)
103 };
104 
105 static WCHAR RunAsOldServiceName[32] = L"";
106 static PH_QUEUED_LOCK RunAsOldServiceLock = PH_QUEUED_LOCK_INIT;
107 
108 static PPH_STRING RunAsServiceName;
109 static SERVICE_STATUS_HANDLE RunAsServiceStatusHandle;
110 static PHSVC_STOP RunAsServiceStop;
111 
113  _In_ HWND ParentWindowHandle,
114  _In_opt_ HANDLE ProcessId
115  )
116 {
117  RUNAS_DIALOG_CONTEXT context;
118 
119  context.ProcessId = ProcessId;
120  context.DesktopList = NULL;
121 
122  DialogBoxParam(
124  MAKEINTRESOURCE(IDD_RUNAS),
125  ParentWindowHandle,
127  (LPARAM)&context
128  );
129 }
130 
131 static VOID PhpAddAccountsToComboBox(
132  _In_ HWND ComboBoxHandle
133  )
134 {
135  LSA_HANDLE policyHandle;
136  LSA_ENUMERATION_HANDLE enumerationContext = 0;
137  PLSA_ENUMERATION_INFORMATION buffer;
138  ULONG count;
139  ULONG i;
140  PPH_STRING name;
141  SID_NAME_USE nameUse;
142 
143  if (NT_SUCCESS(PhOpenLsaPolicy(&policyHandle, POLICY_VIEW_LOCAL_INFORMATION, NULL)))
144  {
146  policyHandle,
147  &enumerationContext,
148  &buffer,
149  0x100,
150  &count
151  )))
152  {
153  for (i = 0; i < count; i++)
154  {
155  name = PhGetSidFullName(buffer[i].Sid, TRUE, &nameUse);
156 
157  if (name)
158  {
159  if (nameUse == SidTypeUser)
160  ComboBox_AddString(ComboBoxHandle, name->Buffer);
161 
162  PhDereferenceObject(name);
163  }
164  }
165 
166  LsaFreeMemory(buffer);
167  }
168 
169  LsaClose(policyHandle);
170  }
171 }
172 
173 static BOOLEAN IsServiceAccount(
174  _In_ PPH_STRING UserName
175  )
176 {
177  if (
178  PhEqualString2(UserName, L"NT AUTHORITY\\LOCAL SERVICE", TRUE) ||
179  PhEqualString2(UserName, L"NT AUTHORITY\\NETWORK SERVICE", TRUE) ||
180  PhEqualString2(UserName, L"NT AUTHORITY\\SYSTEM", TRUE)
181  )
182  {
183  return TRUE;
184  }
185  else
186  {
187  return FALSE;
188  }
189 }
190 
191 static PPH_STRING GetCurrentWinStaName(
192  VOID
193  )
194 {
195  PPH_STRING string;
196 
197  string = PhCreateStringEx(NULL, 0x200);
198 
199  if (GetUserObjectInformation(
200  GetProcessWindowStation(),
201  UOI_NAME,
202  string->Buffer,
203  (ULONG)string->Length + 2,
204  NULL
205  ))
206  {
208  return string;
209  }
210  else
211  {
212  PhDereferenceObject(string);
213  return PhCreateString(L"WinSta0"); // assume the current window station is WinSta0
214  }
215 }
216 
217 static BOOL CALLBACK EnumDesktopsCallback(
218  _In_ PWSTR DesktopName,
219  _In_ LPARAM Context
220  )
221 {
222  PRUNAS_DIALOG_CONTEXT context = (PRUNAS_DIALOG_CONTEXT)Context;
223 
224  PhAddItemList(context->DesktopList, PhConcatStrings(
225  3,
226  context->CurrentWinStaName->Buffer,
227  L"\\",
228  DesktopName
229  ));
230 
231  return TRUE;
232 }
233 
234 INT_PTR CALLBACK PhpRunAsDlgProc(
235  _In_ HWND hwndDlg,
236  _In_ UINT uMsg,
237  _In_ WPARAM wParam,
238  _In_ LPARAM lParam
239  )
240 {
241  PRUNAS_DIALOG_CONTEXT context;
242 
243  if (uMsg != WM_INITDIALOG)
244  {
245  context = (PRUNAS_DIALOG_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom());
246  }
247  else
248  {
249  context = (PRUNAS_DIALOG_CONTEXT)lParam;
250  SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context);
251  }
252 
253  if (!context)
254  return FALSE;
255 
256  switch (uMsg)
257  {
258  case WM_INITDIALOG:
259  {
260  HWND typeComboBoxHandle = GetDlgItem(hwndDlg, IDC_TYPE);
261  HWND userNameComboBoxHandle = GetDlgItem(hwndDlg, IDC_USERNAME);
262  ULONG sessionId;
263 
264  PhCenterWindow(hwndDlg, GetParent(hwndDlg));
265 
266  if (SHAutoComplete_I)
267  {
269  GetDlgItem(hwndDlg, IDC_PROGRAM),
270  SHACF_AUTOAPPEND_FORCE_ON | SHACF_AUTOSUGGEST_FORCE_ON | SHACF_FILESYS_ONLY
271  );
272  }
273 
274  ComboBox_AddString(typeComboBoxHandle, L"Batch");
275  ComboBox_AddString(typeComboBoxHandle, L"Interactive");
276  ComboBox_AddString(typeComboBoxHandle, L"Network");
277  ComboBox_AddString(typeComboBoxHandle, L"New credentials");
278  ComboBox_AddString(typeComboBoxHandle, L"Service");
279  PhSelectComboBoxString(typeComboBoxHandle, L"Interactive", FALSE);
280 
281  ComboBox_AddString(userNameComboBoxHandle, L"NT AUTHORITY\\SYSTEM");
282  ComboBox_AddString(userNameComboBoxHandle, L"NT AUTHORITY\\LOCAL SERVICE");
283  ComboBox_AddString(userNameComboBoxHandle, L"NT AUTHORITY\\NETWORK SERVICE");
284 
285  PhpAddAccountsToComboBox(userNameComboBoxHandle);
286 
287  if (NT_SUCCESS(PhGetProcessSessionId(NtCurrentProcess(), &sessionId)))
288  SetDlgItemInt(hwndDlg, IDC_SESSIONID, sessionId, FALSE);
289 
290  SetDlgItemText(hwndDlg, IDC_DESKTOP, L"WinSta0\\Default");
291 
292  SetDlgItemText(hwndDlg, IDC_PROGRAM,
293  ((PPH_STRING)PhAutoDereferenceObject(PhGetStringSetting(L"RunAsProgram")))->Buffer);
294 
295  if (!context->ProcessId)
296  {
297  SetDlgItemText(hwndDlg, IDC_USERNAME,
298  ((PPH_STRING)PhAutoDereferenceObject(PhGetStringSetting(L"RunAsUserName")))->Buffer);
299 
300  // Fire the user name changed event so we can fix the logon type.
301  SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_USERNAME, CBN_EDITCHANGE), 0);
302  }
303  else
304  {
305  HANDLE processHandle;
306  HANDLE tokenHandle;
307  PTOKEN_USER user;
308  PPH_STRING userName;
309 
311  &processHandle,
313  context->ProcessId
314  )))
315  {
317  &tokenHandle,
318  TOKEN_QUERY,
319  processHandle
320  )))
321  {
322  if (NT_SUCCESS(PhGetTokenUser(tokenHandle, &user)))
323  {
324  if (userName = PhGetSidFullName(user->User.Sid, TRUE, NULL))
325  {
326  SetDlgItemText(hwndDlg, IDC_USERNAME, userName->Buffer);
327  PhDereferenceObject(userName);
328  }
329 
330  PhFree(user);
331  }
332 
333  NtClose(tokenHandle);
334  }
335 
336  NtClose(processHandle);
337  }
338 
339  EnableWindow(GetDlgItem(hwndDlg, IDC_USERNAME), FALSE);
340  EnableWindow(GetDlgItem(hwndDlg, IDC_PASSWORD), FALSE);
341  EnableWindow(GetDlgItem(hwndDlg, IDC_TYPE), FALSE);
342  }
343 
344  SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_PROGRAM), TRUE);
345  Edit_SetSel(GetDlgItem(hwndDlg, IDC_PROGRAM), 0, -1);
346 
347  //if (!PhElevated)
348  // SendMessage(GetDlgItem(hwndDlg, IDOK), BCM_SETSHIELD, 0, TRUE);
349 
350  if (!WINDOWS_HAS_UAC)
351  ShowWindow(GetDlgItem(hwndDlg, IDC_TOGGLEELEVATION), SW_HIDE);
352  }
353  break;
354  case WM_DESTROY:
355  {
356  if (context->DesktopList)
357  PhDereferenceObject(context->DesktopList);
358 
359  RemoveProp(hwndDlg, PhMakeContextAtom());
360  }
361  break;
362  case WM_COMMAND:
363  {
364  switch (LOWORD(wParam))
365  {
366  case IDCANCEL:
367  EndDialog(hwndDlg, IDCANCEL);
368  break;
369  case IDOK:
370  {
371  NTSTATUS status;
372  PPH_STRING program;
373  PPH_STRING userName;
374  PPH_STRING password;
375  PPH_STRING logonTypeString;
376  ULONG logonType;
377  ULONG sessionId;
378  PPH_STRING desktopName;
379  BOOLEAN useLinkedToken;
380 
381  program = PhaGetDlgItemText(hwndDlg, IDC_PROGRAM);
382  userName = PhaGetDlgItemText(hwndDlg, IDC_USERNAME);
383  logonTypeString = PhaGetDlgItemText(hwndDlg, IDC_TYPE);
384 
385  // Fix up the user name if it doesn't have a domain.
386  if (PhFindCharInString(userName, 0, '\\') == -1)
387  {
388  PSID sid;
389  PPH_STRING newUserName;
390 
391  if (NT_SUCCESS(PhLookupName(&userName->sr, &sid, NULL, NULL)))
392  {
393  newUserName = PhGetSidFullName(sid, TRUE, NULL);
394 
395  if (newUserName)
396  {
397  PhAutoDereferenceObject(newUserName);
398  userName = newUserName;
399  }
400 
401  PhFree(sid);
402  }
403  }
404 
405  if (!IsServiceAccount(userName))
406  password = PhGetWindowText(GetDlgItem(hwndDlg, IDC_PASSWORD));
407  else
408  password = NULL;
409 
410  sessionId = GetDlgItemInt(hwndDlg, IDC_SESSIONID, NULL, FALSE);
411  desktopName = PhaGetDlgItemText(hwndDlg, IDC_DESKTOP);
412 
413  if (WINDOWS_HAS_UAC)
414  useLinkedToken = Button_GetCheck(GetDlgItem(hwndDlg, IDC_TOGGLEELEVATION)) == BST_CHECKED;
415  else
416  useLinkedToken = FALSE;
417 
419  PhpLogonTypePairs,
420  sizeof(PhpLogonTypePairs),
421  logonTypeString->Buffer,
422  &logonType
423  ))
424  {
425  if (
426  logonType == LOGON32_LOGON_INTERACTIVE &&
427  !context->ProcessId &&
428  sessionId == NtCurrentPeb()->SessionId &&
429  !useLinkedToken
430  )
431  {
432  // We are eligible to load the user profile.
433  // This must be done here, not in the service, because
434  // we need to be in the target session.
435 
437  PPH_STRING domainPart;
438  PPH_STRING userPart;
439 
440  PhpSplitUserName(userName->Buffer, &domainPart, &userPart);
441 
442  memset(&createInfo, 0, sizeof(PH_CREATE_PROCESS_AS_USER_INFO));
443  createInfo.CommandLine = program->Buffer;
444  createInfo.UserName = userPart->Buffer;
445  createInfo.DomainName = domainPart->Buffer;
446  createInfo.Password = PhGetStringOrEmpty(password);
447 
448  // Whenever we can, try not to set the desktop name; it breaks a lot of things.
449  // Note that on XP we must set it, otherwise the program doesn't display correctly.
450  if (WindowsVersion < WINDOWS_VISTA || (desktopName->Length != 0 && !PhEqualString2(desktopName, L"WinSta0\\Default", TRUE)))
451  createInfo.DesktopName = desktopName->Buffer;
452 
454 
455  status = PhCreateProcessAsUser(
456  &createInfo,
458  NULL,
459  NULL,
460  NULL
461  );
462 
463  if (domainPart) PhDereferenceObject(domainPart);
464  if (userPart) PhDereferenceObject(userPart);
465  }
466  else
467  {
468  status = PhExecuteRunAsCommand2(
469  hwndDlg,
470  program->Buffer,
471  userName->Buffer,
472  PhGetStringOrEmpty(password),
473  logonType,
474  context->ProcessId,
475  sessionId,
476  desktopName->Buffer,
477  useLinkedToken
478  );
479  }
480  }
481  else
482  {
483  status = STATUS_INVALID_PARAMETER;
484  }
485 
486  if (password)
487  {
488  RtlSecureZeroMemory(password->Buffer, password->Length);
489  PhDereferenceObject(password);
490  }
491 
492  if (!NT_SUCCESS(status))
493  {
494  if (status != STATUS_CANCELLED)
495  PhShowStatus(hwndDlg, L"Unable to start the program", status, 0);
496  }
497  else if (status != STATUS_TIMEOUT)
498  {
499  PhSetStringSetting2(L"RunAsProgram", &program->sr);
500  PhSetStringSetting2(L"RunAsUserName", &userName->sr);
501  EndDialog(hwndDlg, IDOK);
502  }
503  }
504  break;
505  case IDC_BROWSE:
506  {
507  static PH_FILETYPE_FILTER filters[] =
508  {
509  { L"Programs (*.exe;*.pif;*.com;*.bat)", L"*.exe;*.pif;*.com;*.bat" },
510  { L"All files (*.*)", L"*.*" }
511  };
512  PVOID fileDialog;
513 
514  fileDialog = PhCreateOpenFileDialog();
515  PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER));
516  PhSetFileDialogFileName(fileDialog, PhaGetDlgItemText(hwndDlg, IDC_PROGRAM)->Buffer);
517 
518  if (PhShowFileDialog(hwndDlg, fileDialog))
519  {
520  PPH_STRING fileName;
521 
522  fileName = PhGetFileDialogFileName(fileDialog);
523  SetDlgItemText(hwndDlg, IDC_PROGRAM, fileName->Buffer);
524  PhDereferenceObject(fileName);
525  }
526 
527  PhFreeFileDialog(fileDialog);
528  }
529  break;
530  case IDC_USERNAME:
531  {
532  PPH_STRING userName = NULL;
533 
534  if (!context->ProcessId && HIWORD(wParam) == CBN_SELCHANGE)
535  {
536  userName = PhAutoDereferenceObject(PhGetComboBoxString(GetDlgItem(hwndDlg, IDC_USERNAME), -1));
537  }
538  else if (!context->ProcessId && (
539  HIWORD(wParam) == CBN_EDITCHANGE ||
540  HIWORD(wParam) == CBN_CLOSEUP
541  ))
542  {
543  userName = PhaGetDlgItemText(hwndDlg, IDC_USERNAME);
544  }
545 
546  if (userName)
547  {
548  if (IsServiceAccount(userName))
549  {
550  EnableWindow(GetDlgItem(hwndDlg, IDC_PASSWORD), FALSE);
551 
552  // Hack for Windows XP
553  if (
554  PhEqualString2(userName, L"NT AUTHORITY\\SYSTEM", TRUE) &&
556  )
557  {
558  PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_TYPE), L"New credentials", FALSE);
559  }
560  else
561  {
562  PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_TYPE), L"Service", FALSE);
563  }
564  }
565  else
566  {
567  EnableWindow(GetDlgItem(hwndDlg, IDC_PASSWORD), TRUE);
568  PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_TYPE), L"Interactive", FALSE);
569  }
570  }
571  }
572  break;
573  case IDC_SESSIONS:
574  {
575  PPH_EMENU sessionsMenu;
576  PSESSIONIDW sessions;
577  ULONG numberOfSessions;
578  ULONG i;
579  RECT buttonRect;
580  PPH_EMENU_ITEM selectedItem;
581 
582  sessionsMenu = PhCreateEMenu();
583 
584  if (WinStationEnumerateW(NULL, &sessions, &numberOfSessions))
585  {
586  for (i = 0; i < numberOfSessions; i++)
587  {
588  PPH_STRING menuString;
589  WINSTATIONINFORMATION winStationInfo;
590  ULONG returnLength;
591 
593  NULL,
594  sessions[i].SessionId,
596  &winStationInfo,
597  sizeof(WINSTATIONINFORMATION),
598  &returnLength
599  ))
600  {
601  winStationInfo.Domain[0] = 0;
602  winStationInfo.UserName[0] = 0;
603  }
604 
605  if (
606  winStationInfo.UserName[0] != 0 &&
607  sessions[i].WinStationName[0] != 0
608  )
609  {
610  menuString = PhFormatString(
611  L"%u: %s (%s\\%s)",
612  sessions[i].SessionId,
613  sessions[i].WinStationName,
614  winStationInfo.Domain,
615  winStationInfo.UserName
616  );
617  }
618  else if (winStationInfo.UserName[0] != 0)
619  {
620  menuString = PhFormatString(
621  L"%u: %s\\%s",
622  sessions[i].SessionId,
623  winStationInfo.Domain,
624  winStationInfo.UserName
625  );
626  }
627  else if (sessions[i].WinStationName[0] != 0)
628  {
629  menuString = PhFormatString(
630  L"%u: %s",
631  sessions[i].SessionId,
632  sessions[i].WinStationName
633  );
634  }
635  else
636  {
637  menuString = PhFormatString(L"%u", sessions[i].SessionId);
638  }
639 
640  PhInsertEMenuItem(sessionsMenu,
641  PhCreateEMenuItem(0, 0, menuString->Buffer, NULL, (PVOID)sessions[i].SessionId), -1);
642  PhAutoDereferenceObject(menuString);
643  }
644 
645  WinStationFreeMemory(sessions);
646 
647  GetWindowRect(GetDlgItem(hwndDlg, IDC_SESSIONS), &buttonRect);
648 
649  selectedItem = PhShowEMenu(
650  sessionsMenu,
651  hwndDlg,
654  buttonRect.right,
655  buttonRect.top
656  );
657 
658  if (selectedItem)
659  {
660  SetDlgItemInt(
661  hwndDlg,
663  (ULONG)selectedItem->Context,
664  FALSE
665  );
666  }
667 
668  PhDestroyEMenu(sessionsMenu);
669  }
670  }
671  break;
672  case IDC_DESKTOPS:
673  {
674  PPH_EMENU desktopsMenu;
675  ULONG i;
676  RECT buttonRect;
677  PPH_EMENU_ITEM selectedItem;
678 
679  desktopsMenu = PhCreateEMenu();
680 
681  if (!context->DesktopList)
682  context->DesktopList = PhCreateList(10);
683 
684  context->CurrentWinStaName = GetCurrentWinStaName();
685 
686  EnumDesktops(GetProcessWindowStation(), EnumDesktopsCallback, (LPARAM)context);
687 
688  for (i = 0; i < context->DesktopList->Count; i++)
689  {
691  desktopsMenu,
692  PhCreateEMenuItem(0, 0, ((PPH_STRING)context->DesktopList->Items[i])->Buffer, NULL, NULL),
693  -1
694  );
695  }
696 
697  GetWindowRect(GetDlgItem(hwndDlg, IDC_DESKTOPS), &buttonRect);
698 
699  selectedItem = PhShowEMenu(
700  desktopsMenu,
701  hwndDlg,
704  buttonRect.right,
705  buttonRect.top
706  );
707 
708  if (selectedItem)
709  {
710  SetDlgItemText(
711  hwndDlg,
712  IDC_DESKTOP,
713  selectedItem->Text
714  );
715  }
716 
717  for (i = 0; i < context->DesktopList->Count; i++)
718  PhDereferenceObject(context->DesktopList->Items[i]);
719 
720  PhClearList(context->DesktopList);
721  PhDereferenceObject(context->CurrentWinStaName);
722  PhDestroyEMenu(desktopsMenu);
723  }
724  break;
725  }
726  }
727  break;
728  }
729 
730  return FALSE;
731 }
732 
738  VOID
739  )
740 {
741  static SID_IDENTIFIER_AUTHORITY appPackageAuthority = SECURITY_APP_PACKAGE_AUTHORITY;
742 
743  HWINSTA wsHandle;
744  HDESK desktopHandle;
745  ULONG allocationLength;
746  PSECURITY_DESCRIPTOR securityDescriptor;
747  PACL dacl;
748  CHAR allAppPackagesSidBuffer[FIELD_OFFSET(SID, SubAuthority) + sizeof(ULONG) * 2];
749  PSID allAppPackagesSid;
750 
751  // TODO: Set security on the correct window station and desktop.
752 
753  allAppPackagesSid = (PISID)allAppPackagesSidBuffer;
754  RtlInitializeSid(allAppPackagesSid, &appPackageAuthority, SECURITY_BUILTIN_APP_PACKAGE_RID_COUNT);
755  *RtlSubAuthoritySid(allAppPackagesSid, 0) = SECURITY_APP_PACKAGE_BASE_RID;
756  *RtlSubAuthoritySid(allAppPackagesSid, 1) = SECURITY_BUILTIN_PACKAGE_ANY_PACKAGE;
757 
758  // We create a DACL that allows everyone to access everything.
759 
760  allocationLength = SECURITY_DESCRIPTOR_MIN_LENGTH +
761  (ULONG)sizeof(ACL) +
762  (ULONG)sizeof(ACCESS_ALLOWED_ACE) +
764  (ULONG)sizeof(ACCESS_ALLOWED_ACE) +
765  RtlLengthSid(allAppPackagesSid);
766  securityDescriptor = PhAllocate(allocationLength);
767  dacl = (PACL)((PCHAR)securityDescriptor + SECURITY_DESCRIPTOR_MIN_LENGTH);
768 
769  RtlCreateSecurityDescriptor(securityDescriptor, SECURITY_DESCRIPTOR_REVISION);
770 
771  RtlCreateAcl(dacl, allocationLength - SECURITY_DESCRIPTOR_MIN_LENGTH, ACL_REVISION);
772  RtlAddAccessAllowedAce(dacl, ACL_REVISION, GENERIC_ALL, &PhSeEveryoneSid);
773 
774  if (WindowsVersion >= WINDOWS_8)
775  {
776  RtlAddAccessAllowedAce(dacl, ACL_REVISION, GENERIC_ALL, allAppPackagesSid);
777  }
778 
779  RtlSetDaclSecurityDescriptor(securityDescriptor, TRUE, dacl, FALSE);
780 
781  if (wsHandle = OpenWindowStation(
782  L"WinSta0",
783  FALSE,
784  WRITE_DAC
785  ))
786  {
787  PhSetObjectSecurity(wsHandle, DACL_SECURITY_INFORMATION, securityDescriptor);
788  CloseWindowStation(wsHandle);
789  }
790 
791  if (desktopHandle = OpenDesktop(
792  L"Default",
793  0,
794  FALSE,
795  WRITE_DAC | DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS
796  ))
797  {
798  PhSetObjectSecurity(desktopHandle, DACL_SECURITY_INFORMATION, securityDescriptor);
799  CloseDesktop(desktopHandle);
800  }
801 
802  PhFree(securityDescriptor);
803 }
804 
813  _In_ PPH_RUNAS_SERVICE_PARAMETERS Parameters
814  )
815 {
816  NTSTATUS status;
817  ULONG win32Result;
818  PPH_STRING commandLine;
819  SC_HANDLE scManagerHandle;
820  SC_HANDLE serviceHandle;
821  PPH_STRING portName;
822  UNICODE_STRING portNameUs;
823  ULONG attempts;
824  LARGE_INTEGER interval;
825 
826  if (!(scManagerHandle = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE)))
828 
829  commandLine = PhFormatString(L"\"%s\" -ras \"%s\"", PhApplicationFileName->Buffer, Parameters->ServiceName);
830 
831  serviceHandle = CreateService(
832  scManagerHandle,
833  Parameters->ServiceName,
834  Parameters->ServiceName,
835  SERVICE_ALL_ACCESS,
836  SERVICE_WIN32_OWN_PROCESS,
837  SERVICE_DEMAND_START,
838  SERVICE_ERROR_IGNORE,
839  commandLine->Buffer,
840  NULL,
841  NULL,
842  NULL,
843  L"LocalSystem",
844  L""
845  );
846  win32Result = GetLastError();
847 
848  PhDereferenceObject(commandLine);
849 
850  CloseServiceHandle(scManagerHandle);
851 
852  if (!serviceHandle)
853  {
854  return NTSTATUS_FROM_WIN32(win32Result);
855  }
856 
858 
859  StartService(serviceHandle, 0, NULL);
860  DeleteService(serviceHandle);
861 
862  portName = PhConcatStrings2(L"\\BaseNamedObjects\\", Parameters->ServiceName);
863  PhStringRefToUnicodeString(&portName->sr, &portNameUs);
864  attempts = 10;
865 
866  // Try to connect several times because the server may take
867  // a while to initialize.
868  do
869  {
870  status = PhSvcConnectToServer(&portNameUs, 0);
871 
872  if (NT_SUCCESS(status))
873  break;
874 
875  interval.QuadPart = -50 * PH_TIMEOUT_MS;
876  NtDelayExecution(FALSE, &interval);
877  } while (--attempts != 0);
878 
879  PhDereferenceObject(portName);
880 
881  if (NT_SUCCESS(status))
882  {
883  status = PhSvcCallInvokeRunAsService(Parameters);
885  }
886 
887  if (serviceHandle)
888  CloseServiceHandle(serviceHandle);
889 
890  return status;
891 }
892 
922  _In_ HWND hWnd,
923  _In_ PWSTR Program,
924  _In_opt_ PWSTR UserName,
925  _In_opt_ PWSTR Password,
926  _In_opt_ ULONG LogonType,
927  _In_opt_ HANDLE ProcessIdWithToken,
928  _In_ ULONG SessionId,
929  _In_ PWSTR DesktopName,
930  _In_ BOOLEAN UseLinkedToken
931  )
932 {
933  NTSTATUS status = STATUS_SUCCESS;
934  PH_RUNAS_SERVICE_PARAMETERS parameters;
935  WCHAR serviceName[32];
936  PPH_STRING portName;
937  UNICODE_STRING portNameUs;
938 
939  memset(&parameters, 0, sizeof(PH_RUNAS_SERVICE_PARAMETERS));
940  parameters.ProcessId = (ULONG)ProcessIdWithToken;
941  parameters.UserName = UserName;
942  parameters.Password = Password;
943  parameters.LogonType = LogonType;
944  parameters.SessionId = SessionId;
945  parameters.CommandLine = Program;
946  parameters.DesktopName = DesktopName;
947  parameters.UseLinkedToken = UseLinkedToken;
948 
949  // Try to use an existing instance of the service if possible.
950  if (RunAsOldServiceName[0] != 0)
951  {
952  PhAcquireQueuedLockExclusive(&RunAsOldServiceLock);
953 
954  portName = PhConcatStrings2(L"\\BaseNamedObjects\\", RunAsOldServiceName);
955  PhStringRefToUnicodeString(&portName->sr, &portNameUs);
956 
957  if (NT_SUCCESS(PhSvcConnectToServer(&portNameUs, 0)))
958  {
959  parameters.ServiceName = RunAsOldServiceName;
960  status = PhSvcCallInvokeRunAsService(&parameters);
962 
963  PhDereferenceObject(portName);
964  PhReleaseQueuedLockExclusive(&RunAsOldServiceLock);
965 
966  return status;
967  }
968 
969  PhDereferenceObject(portName);
970  PhReleaseQueuedLockExclusive(&RunAsOldServiceLock);
971  }
972 
973  // An existing instance was not available. Proceed normally.
974 
975  memcpy(serviceName, L"ProcessHacker", 13 * sizeof(WCHAR));
976  PhGenerateRandomAlphaString(&serviceName[13], 16);
977  PhAcquireQueuedLockExclusive(&RunAsOldServiceLock);
978  memcpy(RunAsOldServiceName, serviceName, sizeof(serviceName));
979  PhReleaseQueuedLockExclusive(&RunAsOldServiceLock);
980 
981  parameters.ServiceName = serviceName;
982 
983  if (PhElevated)
984  {
985  status = PhExecuteRunAsCommand(&parameters);
986  }
987  else
988  {
989  if (PhUiConnectToPhSvc(hWnd, FALSE))
990  {
991  status = PhSvcCallExecuteRunAsCommand(&parameters);
993  }
994  else
995  {
996  status = STATUS_CANCELLED;
997  }
998  }
999 
1000  return status;
1001 }
1002 
1004  _In_ PWSTR UserName,
1005  _Out_ PPH_STRING *DomainPart,
1006  _Out_ PPH_STRING *UserPart
1007  )
1008 {
1009  PH_STRINGREF userName;
1010  PH_STRINGREF domainPart;
1011  PH_STRINGREF userPart;
1012 
1013  PhInitializeStringRefLongHint(&userName, UserName);
1014 
1015  if (PhSplitStringRefAtChar(&userName, '\\', &domainPart, &userPart))
1016  {
1017  *DomainPart = PhCreateString2(&domainPart);
1018  *UserPart = PhCreateString2(&userPart);
1019  }
1020  else
1021  {
1022  *DomainPart = NULL;
1023  *UserPart = PhCreateString2(&userName);
1024  }
1025 }
1026 
1027 static VOID SetRunAsServiceStatus(
1028  _In_ ULONG State
1029  )
1030 {
1031  SERVICE_STATUS status;
1032 
1033  memset(&status, 0, sizeof(SERVICE_STATUS));
1034  status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
1035  status.dwCurrentState = State;
1036  status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
1037 
1038  SetServiceStatus(RunAsServiceStatusHandle, &status);
1039 }
1040 
1041 static DWORD WINAPI RunAsServiceHandlerEx(
1042  _In_ DWORD dwControl,
1043  _In_ DWORD dwEventType,
1044  _In_ LPVOID lpEventData,
1045  _In_ LPVOID lpContext
1046  )
1047 {
1048  switch (dwControl)
1049  {
1050  case SERVICE_CONTROL_STOP:
1051  PhSvcStop(&RunAsServiceStop);
1052  return NO_ERROR;
1053  case SERVICE_CONTROL_INTERROGATE:
1054  return NO_ERROR;
1055  }
1056 
1057  return ERROR_CALL_NOT_IMPLEMENTED;
1058 }
1059 
1060 static VOID WINAPI RunAsServiceMain(
1061  _In_ DWORD dwArgc,
1062  _In_ LPTSTR *lpszArgv
1063  )
1064 {
1065  PPH_STRING portName;
1066  UNICODE_STRING portNameUs;
1067  LARGE_INTEGER timeout;
1068 
1069  memset(&RunAsServiceStop, 0, sizeof(PHSVC_STOP));
1070  RunAsServiceStatusHandle = RegisterServiceCtrlHandlerEx(RunAsServiceName->Buffer, RunAsServiceHandlerEx, NULL);
1071  SetRunAsServiceStatus(SERVICE_RUNNING);
1072 
1073  portName = PhConcatStrings2(L"\\BaseNamedObjects\\", RunAsServiceName->Buffer);
1074  PhStringRefToUnicodeString(&portName->sr, &portNameUs);
1075  // Use a shorter timeout value to reduce the time spent running as SYSTEM.
1076  timeout.QuadPart = -5 * PH_TIMEOUT_SEC;
1077 
1078  PhSvcMain(&portNameUs, &timeout, &RunAsServiceStop);
1079 
1080  SetRunAsServiceStatus(SERVICE_STOPPED);
1081 }
1082 
1084  _In_ PPH_STRING ServiceName
1085  )
1086 {
1087  HANDLE tokenHandle;
1088  SERVICE_TABLE_ENTRY entry;
1089 
1090  // Enable some required privileges.
1091 
1093  &tokenHandle,
1094  TOKEN_ADJUST_PRIVILEGES,
1095  NtCurrentProcess()
1096  )))
1097  {
1098  PhSetTokenPrivilege(tokenHandle, L"SeAssignPrimaryTokenPrivilege", NULL, SE_PRIVILEGE_ENABLED);
1099  PhSetTokenPrivilege(tokenHandle, L"SeBackupPrivilege", NULL, SE_PRIVILEGE_ENABLED);
1100  PhSetTokenPrivilege(tokenHandle, L"SeImpersonatePrivilege", NULL, SE_PRIVILEGE_ENABLED);
1101  PhSetTokenPrivilege(tokenHandle, L"SeIncreaseQuotaPrivilege", NULL, SE_PRIVILEGE_ENABLED);
1102  PhSetTokenPrivilege(tokenHandle, L"SeRestorePrivilege", NULL, SE_PRIVILEGE_ENABLED);
1103  NtClose(tokenHandle);
1104  }
1105 
1106  RunAsServiceName = ServiceName;
1107 
1108  entry.lpServiceName = ServiceName->Buffer;
1109  entry.lpServiceProc = RunAsServiceMain;
1110 
1111  StartServiceCtrlDispatcher(&entry);
1112 
1113  return STATUS_SUCCESS;
1114 }
1115 
1117  _In_ PPH_RUNAS_SERVICE_PARAMETERS Parameters
1118  )
1119 {
1120  NTSTATUS status;
1121  PPH_STRING domainName;
1122  PPH_STRING userName;
1123  PH_CREATE_PROCESS_AS_USER_INFO createInfo;
1124  ULONG flags;
1125 
1126  if (Parameters->UserName)
1127  {
1128  PhpSplitUserName(Parameters->UserName, &domainName, &userName);
1129  }
1130  else
1131  {
1132  domainName = NULL;
1133  userName = NULL;
1134  }
1135 
1136  memset(&createInfo, 0, sizeof(PH_CREATE_PROCESS_AS_USER_INFO));
1137  createInfo.ApplicationName = Parameters->FileName;
1138  createInfo.CommandLine = Parameters->CommandLine;
1139  createInfo.CurrentDirectory = Parameters->CurrentDirectory;
1140  createInfo.DomainName = PhGetString(domainName);
1141  createInfo.UserName = PhGetString(userName);
1142  createInfo.Password = Parameters->Password;
1143  createInfo.LogonType = Parameters->LogonType;
1144  createInfo.SessionId = Parameters->SessionId;
1145  createInfo.DesktopName = Parameters->DesktopName;
1146 
1148 
1149  if (Parameters->ProcessId)
1150  {
1151  createInfo.ProcessIdWithToken = UlongToHandle(Parameters->ProcessId);
1153  }
1154 
1155  if (Parameters->UseLinkedToken)
1157 
1158  status = PhCreateProcessAsUser(
1159  &createInfo,
1160  flags,
1161  NULL,
1162  NULL,
1163  NULL
1164  );
1165 
1166  if (domainName) PhDereferenceObject(domainName);
1167  if (userName) PhDereferenceObject(userName);
1168 
1169  return status;
1170 }