Process Hacker
support.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * general support functions
4  *
5  * Copyright (C) 2009-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 #include <phgui.h>
24 #include <guisupp.h>
25 #include <winsta.h>
26 #include <md5.h>
27 #include <sha.h>
28 
29 // We may want to change this for debugging purposes.
30 #define PHP_USE_IFILEDIALOG (WINDOWS_HAS_IFILEDIALOG)
31 
32 typedef BOOLEAN (NTAPI *_WinStationQueryInformationW)(
33  _In_opt_ HANDLE ServerHandle,
34  _In_ ULONG LogonId,
35  _In_ WINSTATIONINFOCLASS WinStationInformationClass,
36  _Out_writes_bytes_(WinStationInformationLength) PVOID WinStationInformation,
37  _In_ ULONG WinStationInformationLength,
38  _Out_ PULONG ReturnLength
39  );
40 
41 typedef BOOL (WINAPI *_CreateEnvironmentBlock)(
42  _Out_ LPVOID *lpEnvironment,
43  _In_opt_ HANDLE hToken,
44  _In_ BOOL bInherit
45  );
46 
47 typedef BOOL (WINAPI *_DestroyEnvironmentBlock)(
48  _In_ LPVOID lpEnvironment
49  );
50 
51 DECLSPEC_SELECTANY WCHAR *PhSizeUnitNames[7] = { L"B", L"kB", L"MB", L"GB", L"TB", L"PB", L"EB" };
52 DECLSPEC_SELECTANY ULONG PhMaxSizeUnit = MAXULONG32;
53 
66  _Inout_ PPH_RECTANGLE Rectangle,
67  _In_ PPH_RECTANGLE Bounds
68  )
69 {
70  if (Rectangle->Left + Rectangle->Width > Bounds->Left + Bounds->Width)
71  Rectangle->Left = Bounds->Left + Bounds->Width - Rectangle->Width;
72  if (Rectangle->Top + Rectangle->Height > Bounds->Top + Bounds->Height)
73  Rectangle->Top = Bounds->Top + Bounds->Height - Rectangle->Height;
74 
75  if (Rectangle->Left < Bounds->Left)
76  Rectangle->Left = Bounds->Left;
77  if (Rectangle->Top < Bounds->Top)
78  Rectangle->Top = Bounds->Top;
79 }
80 
89  _Inout_ PPH_RECTANGLE Rectangle,
90  _In_ PPH_RECTANGLE Bounds
91  )
92 {
93  Rectangle->Left = Bounds->Left + (Bounds->Width - Rectangle->Width) / 2;
94  Rectangle->Top = Bounds->Top + (Bounds->Height - Rectangle->Height) / 2;
95 }
96 
105  _In_ HWND hWnd,
106  _Inout_ PPH_RECTANGLE Rectangle
107  )
108 {
109  MONITORINFO monitorInfo = { sizeof(monitorInfo) };
110 
111  if (GetMonitorInfo(
112  MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST),
113  &monitorInfo
114  ))
115  {
116  PH_RECTANGLE bounds;
117 
118  bounds = PhRectToRectangle(monitorInfo.rcWork);
119  PhAdjustRectangleToBounds(Rectangle, &bounds);
120  }
121 }
122 
132  _In_ HWND WindowHandle,
133  _In_opt_ HWND ParentWindowHandle
134  )
135 {
136  if (ParentWindowHandle)
137  {
138  RECT rect, parentRect;
139  PH_RECTANGLE rectangle, parentRectangle;
140 
141  GetWindowRect(WindowHandle, &rect);
142  GetWindowRect(ParentWindowHandle, &parentRect);
143  rectangle = PhRectToRectangle(rect);
144  parentRectangle = PhRectToRectangle(parentRect);
145 
146  PhCenterRectangle(&rectangle, &parentRectangle);
147  PhAdjustRectangleToWorkingArea(WindowHandle, &rectangle);
148  MoveWindow(WindowHandle, rectangle.Left, rectangle.Top,
149  rectangle.Width, rectangle.Height, FALSE);
150  }
151  else
152  {
153  MONITORINFO monitorInfo = { sizeof(monitorInfo) };
154 
155  if (GetMonitorInfo(
156  MonitorFromWindow(WindowHandle, MONITOR_DEFAULTTONEAREST),
157  &monitorInfo
158  ))
159  {
160  RECT rect;
161  PH_RECTANGLE rectangle;
162  PH_RECTANGLE bounds;
163 
164  GetWindowRect(WindowHandle, &rect);
165  rectangle = PhRectToRectangle(rect);
166  bounds = PhRectToRectangle(monitorInfo.rcWork);
167 
168  PhCenterRectangle(&rectangle, &bounds);
169  MoveWindow(WindowHandle, rectangle.Left, rectangle.Top,
170  rectangle.Width, rectangle.Height, FALSE);
171  }
172  }
173 }
174 
182  _In_reads_(NumberOfObjects) PVOID *Objects,
183  _In_ ULONG NumberOfObjects
184  )
185 {
186  ULONG i;
187 
188  for (i = 0; i < NumberOfObjects; i++)
189  PhReferenceObject(Objects[i]);
190 }
191 
199  _In_reads_(NumberOfObjects) PVOID *Objects,
200  _In_ ULONG NumberOfObjects
201  )
202 {
203  ULONG i;
204 
205  for (i = 0; i < NumberOfObjects; i++)
206  PhDereferenceObject(Objects[i]);
207 }
208 
222  _In_ PVOID DllHandle,
223  _In_ ULONG MessageTableId,
224  _In_ ULONG MessageLanguageId,
225  _In_ ULONG MessageId
226  )
227 {
228  NTSTATUS status;
229  PMESSAGE_RESOURCE_ENTRY messageEntry;
230 
231  status = RtlFindMessage(
232  DllHandle,
233  MessageTableId,
234  MessageLanguageId,
235  MessageId,
236  &messageEntry
237  );
238 
239  // Try using the system LANGID.
240  if (!NT_SUCCESS(status))
241  {
242  status = RtlFindMessage(
243  DllHandle,
244  MessageTableId,
245  GetSystemDefaultLangID(),
246  MessageId,
247  &messageEntry
248  );
249  }
250 
251  // Try using U.S. English.
252  if (!NT_SUCCESS(status))
253  {
254  status = RtlFindMessage(
255  DllHandle,
256  MessageTableId,
257  MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
258  MessageId,
259  &messageEntry
260  );
261  }
262 
263  if (!NT_SUCCESS(status))
264  return NULL;
265 
266  if (messageEntry->Flags & MESSAGE_RESOURCE_UNICODE)
267  {
268  return PhCreateStringEx((PWCHAR)messageEntry->Text, messageEntry->Length);
269  }
270  else
271  {
272  return PhConvertMultiByteToUtf16Ex((PCHAR)messageEntry->Text, messageEntry->Length);
273  }
274 }
275 
282  _In_ NTSTATUS Status
283  )
284 {
285  PPH_STRING message;
286 
287  if (!NT_NTWIN32(Status))
288  message = PhGetMessage(GetModuleHandle(L"ntdll.dll"), 0xb, GetUserDefaultLangID(), (ULONG)Status);
289  else
290  message = PhGetWin32Message(WIN32_FROM_NTSTATUS(Status));
291 
292  if (PhIsNullOrEmptyString(message))
293  return message;
294 
296 
297  // Remove any trailing newline.
298  if (message->Length >= 2 * sizeof(WCHAR) &&
299  message->Buffer[message->Length / sizeof(WCHAR) - 2] == '\r' &&
300  message->Buffer[message->Length / sizeof(WCHAR) - 1] == '\n')
301  {
302  PhMoveReference(&message, PhCreateStringEx(message->Buffer, message->Length - 2 * sizeof(WCHAR)));
303  }
304 
305  // Fix those messages which are formatted like:
306  // {Asdf}\r\nAsdf asdf asdf...
307  if (message->Buffer[0] == '{')
308  {
309  PH_STRINGREF titlePart;
310  PH_STRINGREF remainingPart;
311 
312  if (PhSplitStringRefAtChar(&message->sr, '\n', &titlePart, &remainingPart))
313  PhMoveReference(&message, PhCreateString2(&remainingPart));
314  }
315 
316  return message;
317 }
318 
325  _In_ ULONG Result
326  )
327 {
328  PPH_STRING message;
329 
330  message = PhGetMessage(GetModuleHandle(L"kernel32.dll"), 0xb, GetUserDefaultLangID(), Result);
331 
332  if (message)
334 
335  // Remove any trailing newline.
336  if (message && message->Length >= 2 * sizeof(WCHAR) &&
337  message->Buffer[message->Length / sizeof(WCHAR) - 2] == '\r' &&
338  message->Buffer[message->Length / sizeof(WCHAR) - 1] == '\n')
339  {
340  PhMoveReference(&message, PhCreateStringEx(message->Buffer, message->Length - 2 * sizeof(WCHAR)));
341  }
342 
343  return message;
344 }
345 
356  _In_ HWND hWnd,
357  _In_ ULONG Type,
358  _In_ PWSTR Format,
359  ...
360  )
361 {
362  va_list argptr;
363 
364  va_start(argptr, Format);
365 
366  return PhShowMessage_V(hWnd, Type, Format, argptr);
367 }
368 
370  _In_ HWND hWnd,
371  _In_ ULONG Type,
372  _In_ PWSTR Format,
373  _In_ va_list ArgPtr
374  )
375 {
376  INT result;
377  WCHAR message[PH_MAX_MESSAGE_SIZE + 1];
378 
379  result = _vsnwprintf(message, PH_MAX_MESSAGE_SIZE, Format, ArgPtr);
380 
381  if (result == -1)
382  return -1;
383 
384  return MessageBox(hWnd, message, PhApplicationName, Type);
385 }
386 
388  _In_ NTSTATUS Status,
389  _In_opt_ ULONG Win32Result
390  )
391 {
392  if (!Win32Result)
393  {
394  // In some cases we want the simple Win32 messages.
395  if (
396  Status == STATUS_ACCESS_DENIED ||
397  Status == STATUS_ACCESS_VIOLATION
398  )
399  {
400  Win32Result = RtlNtStatusToDosError(Status);
401  }
402  // Process NTSTATUS values with the NT-Win32 facility.
403  else if (NT_NTWIN32(Status))
404  {
405  Win32Result = WIN32_FROM_NTSTATUS(Status);
406  }
407  }
408 
409  if (!Win32Result)
410  return PhGetNtMessage(Status);
411  else
412  return PhGetWin32Message(Win32Result);
413 }
414 
424  _In_ HWND hWnd,
425  _In_opt_ PWSTR Message,
426  _In_ NTSTATUS Status,
427  _In_opt_ ULONG Win32Result
428  )
429 {
430  PPH_STRING statusMessage;
431 
432  statusMessage = PhGetStatusMessage(Status, Win32Result);
433 
434  if (!statusMessage)
435  {
436  if (Message)
437  {
438  PhShowError(hWnd, L"%s.", Message);
439  }
440  else
441  {
442  PhShowError(hWnd, L"Unable to perform the operation.");
443  }
444 
445  return;
446  }
447 
448  if (Message)
449  {
450  PhShowError(hWnd, L"%s: %s", Message, statusMessage->Buffer);
451  }
452  else
453  {
454  PhShowError(hWnd, L"%s", statusMessage->Buffer);
455  }
456 
457  PhDereferenceObject(statusMessage);
458 }
459 
473  _In_ HWND hWnd,
474  _In_opt_ PWSTR Message,
475  _In_ NTSTATUS Status,
476  _In_opt_ ULONG Win32Result
477  )
478 {
479  PPH_STRING statusMessage;
480  INT result;
481 
482  statusMessage = PhGetStatusMessage(Status, Win32Result);
483 
484  if (!statusMessage)
485  {
486  if (Message)
487  {
488  result = PhShowMessage(hWnd, MB_ICONERROR | MB_OKCANCEL, L"%s.", Message);
489  }
490  else
491  {
492  result = PhShowMessage(hWnd, MB_ICONERROR | MB_OKCANCEL, L"Unable to perform the operation.");
493  }
494 
495  return result == IDOK;
496  }
497 
498  if (Message)
499  {
500  result = PhShowMessage(hWnd, MB_ICONERROR | MB_OKCANCEL, L"%s: %s", Message, statusMessage->Buffer);
501  }
502  else
503  {
504  result = PhShowMessage(hWnd, MB_ICONERROR | MB_OKCANCEL, L"%s", statusMessage->Buffer);
505  }
506 
507  PhDereferenceObject(statusMessage);
508 
509  return result == IDOK;
510 }
511 
525  _In_ HWND hWnd,
526  _In_ PWSTR Verb,
527  _In_ PWSTR Object,
528  _In_opt_ PWSTR Message,
529  _In_ BOOLEAN Warning
530  )
531 {
532  PPH_STRING verb;
533  PPH_STRING verbCaps;
534  PPH_STRING action;
535 
536  // Make sure the verb is all lowercase.
537  verb = PhaLowerString(PhaCreateString(Verb));
538 
539  // "terminate" -> "Terminate"
540  verbCaps = PhaDuplicateString(verb);
541  if (verbCaps->Length > 0) verbCaps->Buffer[0] = towupper(verbCaps->Buffer[0]);
542 
543  // "terminate", "the process" -> "terminate the process"
544  action = PhaConcatStrings(3, verb->Buffer, L" ", Object);
545 
547  {
548  TASKDIALOGCONFIG config = { sizeof(config) };
549  TASKDIALOG_BUTTON buttons[2];
550  INT button;
551 
552  config.hwndParent = hWnd;
553  config.hInstance = PhLibImageBase;
554  config.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION;
555  config.pszWindowTitle = PhApplicationName;
556  config.pszMainIcon = Warning ? TD_WARNING_ICON : NULL;
557  config.pszMainInstruction = PhaConcatStrings(3, L"Do you want to ", action->Buffer, L"?")->Buffer;
558 
559  if (Message)
560  config.pszContent = PhaConcatStrings2(Message, L" Are you sure you want to continue?")->Buffer;
561 
562  buttons[0].nButtonID = IDYES;
563  buttons[0].pszButtonText = verbCaps->Buffer;
564  buttons[1].nButtonID = IDNO;
565  buttons[1].pszButtonText = L"Cancel";
566 
567  config.cButtons = 2;
568  config.pButtons = buttons;
569  config.nDefaultButton = IDYES;
570 
572  &config,
573  &button,
574  NULL,
575  NULL
576  ) == S_OK)
577  {
578  return button == IDYES;
579  }
580  else
581  {
582  return FALSE;
583  }
584  }
585  else
586  {
587  return PhShowMessage(
588  hWnd,
589  MB_YESNO | MB_ICONWARNING,
590  L"Are you sure you want to %s?",
591  action->Buffer
592  ) == IDYES;
593  }
594 }
595 
609  _In_ PPH_KEY_VALUE_PAIR KeyValuePairs,
610  _In_ ULONG SizeOfKeyValuePairs,
611  _In_ PWSTR String,
612  _Out_ PULONG Integer
613  )
614 {
615  ULONG i;
616 
617  for (i = 0; i < SizeOfKeyValuePairs / sizeof(PH_KEY_VALUE_PAIR); i++)
618  {
619  if (PhEqualStringZ(KeyValuePairs[i].Key, String, TRUE))
620  {
621  *Integer = (ULONG)KeyValuePairs[i].Value;
622  return TRUE;
623  }
624  }
625 
626  return FALSE;
627 }
628 
640  _In_ PPH_KEY_VALUE_PAIR KeyValuePairs,
641  _In_ ULONG SizeOfKeyValuePairs,
642  _In_ ULONG Integer,
643  _Out_ PWSTR *String
644  )
645 {
646  ULONG i;
647 
648  for (i = 0; i < SizeOfKeyValuePairs / sizeof(PH_KEY_VALUE_PAIR); i++)
649  {
650  if ((ULONG)KeyValuePairs[i].Value == Integer)
651  {
652  *String = (PWSTR)KeyValuePairs[i].Key;
653  return TRUE;
654  }
655  }
656 
657  return FALSE;
658 }
659 
666  _Out_ PGUID Guid
667  )
668 {
669  static ULONG seed = 0;
670  // The top/sign bit is always unusable for RtlRandomEx
671  // (the result is always unsigned), so we'll take the
672  // bottom 24 bits. We need 128 bits in total, so we'll
673  // call the function 6 times.
674  ULONG random[6];
675  ULONG i;
676 
677  for (i = 0; i < 6; i++)
678  random[i] = RtlRandomEx(&seed);
679 
680  // random[0] is usable
681  *(PUSHORT)&Guid->Data1 = (USHORT)random[0];
682  // top byte from random[0] is usable
683  *((PUSHORT)&Guid->Data1 + 1) = (USHORT)((random[0] >> 16) | (random[1] & 0xff));
684  // top 2 bytes from random[1] are usable
685  Guid->Data2 = (SHORT)(random[1] >> 8);
686  // random[2] is usable
687  Guid->Data3 = (SHORT)random[2];
688  // top byte from random[2] is usable
689  *(PUSHORT)&Guid->Data4[0] = (USHORT)((random[2] >> 16) | (random[3] & 0xff));
690  // top 2 bytes from random[3] are usable
691  *(PUSHORT)&Guid->Data4[2] = (USHORT)(random[3] >> 8);
692  // random[4] is usable
693  *(PUSHORT)&Guid->Data4[4] = (USHORT)random[4];
694  // top byte from random[4] is usable
695  *(PUSHORT)&Guid->Data4[6] = (USHORT)((random[4] >> 16) | (random[5] & 0xff));
696 
697  ((PGUID_EX)Guid)->s2.Version = GUID_VERSION_RANDOM;
698  ((PGUID_EX)Guid)->s2.Variant &= ~GUID_VARIANT_STANDARD_MASK;
699  ((PGUID_EX)Guid)->s2.Variant |= GUID_VARIANT_STANDARD;
700 }
701 
702 FORCEINLINE VOID PhpReverseGuid(
703  _Inout_ PGUID Guid
704  )
705 {
706  Guid->Data1 = _byteswap_ulong(Guid->Data1);
707  Guid->Data2 = _byteswap_ushort(Guid->Data2);
708  Guid->Data3 = _byteswap_ushort(Guid->Data3);
709 }
710 
724  _Out_ PGUID Guid,
725  _In_ PGUID Namespace,
726  _In_ PCHAR Name,
727  _In_ ULONG NameLength,
728  _In_ UCHAR Version
729  )
730 {
731  PGUID_EX guid;
732  PUCHAR data;
733  ULONG dataLength;
734  GUID ns;
735  UCHAR hash[20];
736 
737  // Convert the namespace to big endian.
738 
739  ns = *Namespace;
740  PhpReverseGuid(&ns);
741 
742  // Compute the hash of the namespace concatenated with the name.
743 
744  dataLength = 16 + NameLength;
745  data = PhAllocate(dataLength);
746  memcpy(data, &ns, 16);
747  memcpy(&data[16], Name, NameLength);
748 
749  if (Version == GUID_VERSION_MD5)
750  {
751  MD5_CTX context;
752 
753  MD5Init(&context);
754  MD5Update(&context, data, dataLength);
755  MD5Final(&context);
756 
757  memcpy(hash, context.digest, 16);
758  }
759  else
760  {
761  A_SHA_CTX context;
762 
763  A_SHAInit(&context);
764  A_SHAUpdate(&context, data, dataLength);
765  A_SHAFinal(&context, hash);
766 
767  Version = GUID_VERSION_SHA1;
768  }
769 
770  PhFree(data);
771 
772  guid = (PGUID_EX)Guid;
773  memcpy(guid->Data, hash, 16);
774  PhpReverseGuid(&guid->Guid);
775  guid->s2.Version = Version;
778 }
779 
789  _Out_writes_z_(Count) PWSTR Buffer,
790  _In_ ULONG Count
791  )
792 {
793  static ULONG seed = 0;
794  ULONG i;
795 
796  if (Count == 0)
797  return;
798 
799  for (i = 0; i < Count - 1; i++)
800  {
801  Buffer[i] = 'A' + (RtlRandomEx(&seed) % 26);
802  }
803 
804  Buffer[Count - 1] = 0;
805 }
806 
818  _In_ PPH_STRING String,
819  _In_ ULONG DesiredCount
820  )
821 {
822  if (
823  (ULONG)String->Length / 2 <= DesiredCount ||
824  DesiredCount < 3
825  )
826  {
827  return PhReferenceObject(String);
828  }
829  else
830  {
831  PPH_STRING string;
832 
833  string = PhCreateStringEx(NULL, DesiredCount * 2);
834  memcpy(string->Buffer, String->Buffer, (DesiredCount - 3) * 2);
835  memcpy(&string->Buffer[DesiredCount - 3], L"...", 6);
836 
837  return string;
838  }
839 }
840 
853  _In_ PPH_STRING String,
854  _In_ ULONG DesiredCount
855  )
856 {
857  ULONG_PTR secondPartIndex;
858 
859  secondPartIndex = PhFindLastCharInString(String, 0, L'\\');
860 
861  if (secondPartIndex == -1)
862  secondPartIndex = PhFindLastCharInString(String, 0, L'/');
863  if (secondPartIndex == -1)
864  return PhEllipsisString(String, DesiredCount);
865 
866  if (
867  String->Length / 2 <= DesiredCount ||
868  DesiredCount < 3
869  )
870  {
871  return PhReferenceObject(String);
872  }
873  else
874  {
875  PPH_STRING string;
876  ULONG_PTR firstPartCopyLength;
877  ULONG_PTR secondPartCopyLength;
878 
879  string = PhCreateStringEx(NULL, DesiredCount * 2);
880  secondPartCopyLength = String->Length / 2 - secondPartIndex;
881 
882  // Check if we have enough space for the entire second part of the string.
883  if (secondPartCopyLength + 3 <= DesiredCount)
884  {
885  // Yes, copy part of the first part and the entire second part.
886  firstPartCopyLength = DesiredCount - secondPartCopyLength - 3;
887  }
888  else
889  {
890  // No, copy part of both, from the beginning of the first part and
891  // the end of the second part.
892  firstPartCopyLength = (DesiredCount - 3) / 2;
893  secondPartCopyLength = DesiredCount - 3 - firstPartCopyLength;
894  secondPartIndex = String->Length / 2 - secondPartCopyLength;
895  }
896 
897  memcpy(
898  string->Buffer,
899  String->Buffer,
900  firstPartCopyLength * 2
901  );
902  memcpy(
903  &string->Buffer[firstPartCopyLength],
904  L"...",
905  6
906  );
907  memcpy(
908  &string->Buffer[firstPartCopyLength + 3],
909  &String->Buffer[secondPartIndex],
910  secondPartCopyLength * 2
911  );
912 
913  return string;
914  }
915 }
916 
917 FORCEINLINE BOOLEAN PhpMatchWildcards(
918  _In_ PWSTR Pattern,
919  _In_ PWSTR String,
920  _In_ BOOLEAN IgnoreCase
921  )
922 {
923  PWCHAR s, p;
924  BOOLEAN star = FALSE;
925 
926  // Code is from http://xoomer.virgilio.it/acantato/dev/wildcard/wildmatch.html
927 
928 LoopStart:
929  for (s = String, p = Pattern; *s; s++, p++)
930  {
931  switch (*p)
932  {
933  case '?':
934  break;
935  case '*':
936  star = TRUE;
937  String = s;
938  Pattern = p;
939 
940  do
941  {
942  Pattern++;
943  } while (*Pattern == '*');
944 
945  if (!*Pattern) return TRUE;
946 
947  goto LoopStart;
948  default:
949  if (!IgnoreCase)
950  {
951  if (*s != *p)
952  goto StarCheck;
953  }
954  else
955  {
956  if (towupper(*s) != towupper(*p))
957  goto StarCheck;
958  }
959 
960  break;
961  }
962  }
963 
964  while (*p == '*')
965  p++;
966 
967  return (!*p);
968 
969 StarCheck:
970  if (!star)
971  return FALSE;
972 
973  String++;
974  goto LoopStart;
975 }
976 
986  _In_ PWSTR Pattern,
987  _In_ PWSTR String,
988  _In_ BOOLEAN IgnoreCase
989  )
990 {
991  if (!IgnoreCase)
992  return PhpMatchWildcards(Pattern, String, FALSE);
993  else
994  return PhpMatchWildcards(Pattern, String, TRUE);
995 }
996 
1006  _In_ PPH_STRINGREF String
1007  )
1008 {
1009  PH_STRING_BUILDER stringBuilder;
1010  SIZE_T i;
1011  SIZE_T length;
1012  PWCHAR runStart;
1013  SIZE_T runCount;
1014 
1015  length = String->Length / sizeof(WCHAR);
1016  runStart = NULL;
1017 
1018  PhInitializeStringBuilder(&stringBuilder, String->Length);
1019 
1020  for (i = 0; i < length; i++)
1021  {
1022  switch (String->Buffer[i])
1023  {
1024  case '&':
1025  if (runStart)
1026  {
1027  PhAppendStringBuilderEx(&stringBuilder, runStart, runCount * sizeof(WCHAR));
1028  runStart = NULL;
1029  }
1030 
1031  PhAppendStringBuilder2(&stringBuilder, L"&&");
1032 
1033  break;
1034  default:
1035  if (runStart)
1036  {
1037  runCount++;
1038  }
1039  else
1040  {
1041  runStart = &String->Buffer[i];
1042  runCount = 1;
1043  }
1044 
1045  break;
1046  }
1047  }
1048 
1049  if (runStart)
1050  PhAppendStringBuilderEx(&stringBuilder, runStart, runCount * sizeof(WCHAR));
1051 
1052  return PhFinalStringBuilderString(&stringBuilder);
1053 }
1054 
1065  _In_ PWSTR A,
1066  _In_ PWSTR B,
1067  _In_ BOOLEAN IgnoreCase,
1068  _In_ BOOLEAN MatchIfPrefix
1069  )
1070 {
1071  WCHAR t;
1072 
1073  if (!IgnoreCase)
1074  {
1075  while (TRUE)
1076  {
1077  // This takes care of double ampersands as well (they are treated as
1078  // one literal ampersand).
1079  if (*A == '&')
1080  A++;
1081  if (*B == '&')
1082  B++;
1083 
1084  t = *A;
1085 
1086  if (t == 0)
1087  {
1088  if (MatchIfPrefix)
1089  return 0;
1090 
1091  break;
1092  }
1093 
1094  if (t != *B)
1095  break;
1096 
1097  A++;
1098  B++;
1099  }
1100 
1101  return C_2uTo4(t) - C_2uTo4(*B);
1102  }
1103  else
1104  {
1105  while (TRUE)
1106  {
1107  if (*A == '&')
1108  A++;
1109  if (*B == '&')
1110  B++;
1111 
1112  t = *A;
1113 
1114  if (t == 0)
1115  {
1116  if (MatchIfPrefix)
1117  return 0;
1118 
1119  break;
1120  }
1121 
1122  if (towupper(t) != towupper(*B))
1123  break;
1124 
1125  A++;
1126  B++;
1127  }
1128 
1129  return C_2uTo4(t) - C_2uTo4(*B);
1130  }
1131 }
1132 
1141  _In_opt_ PSYSTEMTIME Date,
1142  _In_opt_ PWSTR Format
1143  )
1144 {
1145  PPH_STRING string;
1146  ULONG bufferSize;
1147 
1148  bufferSize = GetDateFormat(LOCALE_USER_DEFAULT, 0, Date, Format, NULL, 0);
1149  string = PhCreateStringEx(NULL, bufferSize * 2);
1150 
1151  if (!GetDateFormat(LOCALE_USER_DEFAULT, 0, Date, Format, string->Buffer, bufferSize))
1152  {
1153  PhDereferenceObject(string);
1154  return NULL;
1155  }
1156 
1158 
1159  return string;
1160 }
1161 
1170  _In_opt_ PSYSTEMTIME Time,
1171  _In_opt_ PWSTR Format
1172  )
1173 {
1174  PPH_STRING string;
1175  ULONG bufferSize;
1176 
1177  bufferSize = GetTimeFormat(LOCALE_USER_DEFAULT, 0, Time, Format, NULL, 0);
1178  string = PhCreateStringEx(NULL, bufferSize * 2);
1179 
1180  if (!GetTimeFormat(LOCALE_USER_DEFAULT, 0, Time, Format, string->Buffer, bufferSize))
1181  {
1182  PhDereferenceObject(string);
1183  return NULL;
1184  }
1185 
1187 
1188  return string;
1189 }
1190 
1199  _In_opt_ PSYSTEMTIME DateTime
1200  )
1201 {
1202  PPH_STRING string;
1203  ULONG timeBufferSize;
1204  ULONG dateBufferSize;
1205  ULONG count;
1206 
1207  timeBufferSize = GetTimeFormat(LOCALE_USER_DEFAULT, 0, DateTime, NULL, NULL, 0);
1208  dateBufferSize = GetDateFormat(LOCALE_USER_DEFAULT, 0, DateTime, NULL, NULL, 0);
1209 
1210  string = PhCreateStringEx(NULL, (timeBufferSize + 1 + dateBufferSize) * 2);
1211 
1212  if (!GetTimeFormat(LOCALE_USER_DEFAULT, 0, DateTime, NULL, &string->Buffer[0], timeBufferSize))
1213  {
1214  PhDereferenceObject(string);
1215  return NULL;
1216  }
1217 
1218  count = (ULONG)PhCountStringZ(string->Buffer);
1219  string->Buffer[count] = ' ';
1220 
1221  if (!GetDateFormat(LOCALE_USER_DEFAULT, 0, DateTime, NULL, &string->Buffer[count + 1], dateBufferSize))
1222  {
1223  PhDereferenceObject(string);
1224  return NULL;
1225  }
1226 
1228 
1229  return string;
1230 }
1231 
1238  _In_ ULONG64 TimeSpan
1239  )
1240 {
1241  PH_AUTO_POOL autoPool;
1242  PPH_STRING string;
1243  DOUBLE days;
1244  DOUBLE weeks;
1245  DOUBLE fortnights;
1246  DOUBLE months;
1247  DOUBLE years;
1248  DOUBLE centuries;
1249 
1250  PhInitializeAutoPool(&autoPool);
1251 
1252  days = (DOUBLE)TimeSpan / PH_TICKS_PER_DAY;
1253  weeks = days / 7;
1254  fortnights = weeks / 2;
1255  years = days / 365.2425;
1256  months = years * 12;
1257  centuries = years / 100;
1258 
1259  if (centuries >= 1)
1260  {
1261  string = PhaFormatString(L"%u %s", (ULONG)centuries, (ULONG)centuries == 1 ? L"century" : L"centuries");
1262  }
1263  else if (years >= 1)
1264  {
1265  string = PhaFormatString(L"%u %s", (ULONG)years, (ULONG)years == 1 ? L"year" : L"years");
1266  }
1267  else if (months >= 1)
1268  {
1269  string = PhaFormatString(L"%u %s", (ULONG)months, (ULONG)months == 1 ? L"month" : L"months");
1270  }
1271  else if (fortnights >= 1)
1272  {
1273  string = PhaFormatString(L"%u %s", (ULONG)fortnights, (ULONG)fortnights == 1 ? L"fortnight" : L"fortnights");
1274  }
1275  else if (weeks >= 1)
1276  {
1277  string = PhaFormatString(L"%u %s", (ULONG)weeks, (ULONG)weeks == 1 ? L"week" : L"weeks");
1278  }
1279  else
1280  {
1281  DOUBLE milliseconds;
1282  DOUBLE seconds;
1283  DOUBLE minutes;
1284  DOUBLE hours;
1285  ULONG secondsPartial;
1286  ULONG minutesPartial;
1287  ULONG hoursPartial;
1288 
1289  milliseconds = (DOUBLE)TimeSpan / PH_TICKS_PER_MS;
1290  seconds = (DOUBLE)TimeSpan / PH_TICKS_PER_SEC;
1291  minutes = (DOUBLE)TimeSpan / PH_TICKS_PER_MIN;
1292  hours = (DOUBLE)TimeSpan / PH_TICKS_PER_HOUR;
1293 
1294  if (days >= 1)
1295  {
1296  string = PhaFormatString(L"%u %s", (ULONG)days, (ULONG)days == 1 ? L"day" : L"days");
1297  hoursPartial = (ULONG)PH_TICKS_PARTIAL_HOURS(TimeSpan);
1298 
1299  if (hoursPartial >= 1)
1300  {
1301  string = PhaFormatString(L"%s and %u %s", string->Buffer, hoursPartial, hoursPartial == 1 ? L"hour" : L"hours");
1302  }
1303  }
1304  else if (hours >= 1)
1305  {
1306  string = PhaFormatString(L"%u %s", (ULONG)hours, (ULONG)hours == 1 ? L"hour" : L"hours");
1307  minutesPartial = (ULONG)PH_TICKS_PARTIAL_MIN(TimeSpan);
1308 
1309  if (minutesPartial >= 1)
1310  {
1311  string = PhaFormatString(L"%s and %u %s", string->Buffer, (ULONG)minutesPartial, (ULONG)minutesPartial == 1 ? L"minute" : L"minutes");
1312  }
1313  }
1314  else if (minutes >= 1)
1315  {
1316  string = PhaFormatString(L"%u %s", (ULONG)minutes, (ULONG)minutes == 1 ? L"minute" : L"minutes");
1317  secondsPartial = (ULONG)PH_TICKS_PARTIAL_SEC(TimeSpan);
1318 
1319  if (secondsPartial >= 1)
1320  {
1321  string = PhaFormatString(L"%s and %u %s", string->Buffer, (ULONG)secondsPartial, (ULONG)secondsPartial == 1 ? L"second" : L"seconds");
1322  }
1323  }
1324  else if (seconds >= 1)
1325  {
1326  string = PhaFormatString(L"%u %s", (ULONG)seconds, (ULONG)seconds == 1 ? L"second" : L"seconds");
1327  }
1328  else if (milliseconds >= 1)
1329  {
1330  string = PhaFormatString(L"%u %s", (ULONG)milliseconds, (ULONG)milliseconds == 1 ? L"millisecond" : L"milliseconds");
1331  }
1332  else
1333  {
1334  string = PhaCreateString(L"a very short time");
1335  }
1336  }
1337 
1338  // Turn 1 into "a", e.g. 1 minute -> a minute
1339  if (PhStartsWithString2(string, L"1 ", FALSE))
1340  {
1341  // Special vowel case: a hour -> an hour
1342  if (string->Buffer[2] != 'h')
1343  string = PhaConcatStrings2(L"a ", &string->Buffer[2]);
1344  else
1345  string = PhaConcatStrings2(L"an ", &string->Buffer[2]);
1346  }
1347 
1348  PhReferenceObject(string);
1349  PhDeleteAutoPool(&autoPool);
1350 
1351  return string;
1352 }
1353 
1361  _In_ ULONG64 Value,
1362  _In_ BOOLEAN GroupDigits
1363  )
1364 {
1365  PH_FORMAT format;
1366 
1367  format.Type = UInt64FormatType | (GroupDigits ? FormatGroupDigits : 0);
1368  format.u.UInt64 = Value;
1369 
1370  return PhFormat(&format, 1, 0);
1371 }
1372 
1374  _In_ PWSTR Value,
1375  _In_ ULONG FractionalDigits,
1376  _In_ BOOLEAN GroupDigits
1377  )
1378 {
1379  static PH_INITONCE initOnce = PH_INITONCE_INIT;
1380  static WCHAR decimalSeparator[4];
1381  static WCHAR thousandSeparator[4];
1382 
1383  PPH_STRING string;
1384  NUMBERFMT format;
1385  ULONG bufferSize;
1386 
1387  if (PhBeginInitOnce(&initOnce))
1388  {
1389  if (!GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, decimalSeparator, 4))
1390  {
1391  decimalSeparator[0] = '.';
1392  decimalSeparator[1] = 0;
1393  }
1394 
1395  if (!GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, thousandSeparator, 4))
1396  {
1397  thousandSeparator[0] = ',';
1398  thousandSeparator[1] = 0;
1399  }
1400 
1401  PhEndInitOnce(&initOnce);
1402  }
1403 
1404  format.NumDigits = FractionalDigits;
1405  format.LeadingZero = 0;
1406  format.Grouping = GroupDigits ? 3 : 0;
1407  format.lpDecimalSep = decimalSeparator;
1408  format.lpThousandSep = thousandSeparator;
1409  format.NegativeOrder = 1;
1410 
1411  bufferSize = GetNumberFormat(LOCALE_USER_DEFAULT, 0, Value, &format, NULL, 0);
1412  string = PhCreateStringEx(NULL, bufferSize * 2);
1413 
1414  if (!GetNumberFormat(LOCALE_USER_DEFAULT, 0, Value, &format, string->Buffer, bufferSize))
1415  {
1416  PhDereferenceObject(string);
1417  return NULL;
1418  }
1419 
1421 
1422  return string;
1423 }
1424 
1440  _In_ ULONG64 Size,
1441  _In_ ULONG MaxSizeUnit
1442  )
1443 {
1444  PH_FORMAT format;
1445 
1446  // PhFormat handles this better than the old method.
1447 
1448  format.Type = SizeFormatType | FormatUseRadix;
1449  format.Radix = (UCHAR)(MaxSizeUnit != -1 ? MaxSizeUnit : PhMaxSizeUnit);
1450  format.u.Size = Size;
1451 
1452  return PhFormat(&format, 1, 0);
1453 }
1454 
1461  _In_ PGUID Guid
1462  )
1463 {
1464  PPH_STRING string;
1465  UNICODE_STRING unicodeString;
1466 
1467  if (!NT_SUCCESS(RtlStringFromGUID(Guid, &unicodeString)))
1468  return NULL;
1469 
1470  string = PhCreateStringFromUnicodeString(&unicodeString);
1471  RtlFreeUnicodeString(&unicodeString);
1472 
1473  return string;
1474 }
1475 
1485  _In_ PWSTR FileName
1486  )
1487 {
1488  ULONG versionInfoSize;
1489  ULONG dummy;
1490  PVOID versionInfo;
1491 
1492  versionInfoSize = GetFileVersionInfoSize(
1493  FileName,
1494  &dummy
1495  );
1496 
1497  if (versionInfoSize)
1498  {
1499  versionInfo = PhAllocate(versionInfoSize);
1500 
1501  if (!GetFileVersionInfo(
1502  FileName,
1503  0,
1504  versionInfoSize,
1505  versionInfo
1506  ))
1507  {
1508  PhFree(versionInfo);
1509 
1510  return NULL;
1511  }
1512  }
1513  else
1514  {
1515  return NULL;
1516  }
1517 
1518  return versionInfo;
1519 }
1520 
1528  _In_ PVOID VersionInfo
1529  )
1530 {
1531  PVOID buffer;
1532  ULONG length;
1533 
1534  if (VerQueryValue(VersionInfo, L"\\VarFileInfo\\Translation", &buffer, &length))
1535  {
1536  // Combine the language ID and code page.
1537  return (*(PUSHORT)buffer << 16) + *((PUSHORT)buffer + 1);
1538  }
1539  else
1540  {
1541  return (MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US) << 16) + 1252;
1542  }
1543 }
1544 
1552  _In_ PVOID VersionInfo,
1553  _In_ PWSTR SubBlock
1554  )
1555 {
1556  PVOID buffer;
1557  ULONG length;
1558 
1559  if (VerQueryValue(VersionInfo, SubBlock, &buffer, &length))
1560  {
1561  PPH_STRING string;
1562 
1563  string = PhCreateStringEx((PWCHAR)buffer, length * sizeof(WCHAR));
1564  // length may include the null terminator.
1566 
1567  return string;
1568  }
1569  else
1570  {
1571  return NULL;
1572  }
1573 }
1574 
1583  _In_ PVOID VersionInfo,
1584  _In_ ULONG LangCodePage,
1585  _In_ PWSTR StringName
1586  )
1587 {
1588  WCHAR subBlock[65];
1589  PH_FORMAT format[4];
1590 
1591  PhInitFormatS(&format[0], L"\\StringFileInfo\\");
1592  PhInitFormatX(&format[1], LangCodePage);
1593  format[1].Type |= FormatPadZeros | FormatUpperCase;
1594  format[1].Width = 8;
1595  PhInitFormatC(&format[2], '\\');
1596  PhInitFormatS(&format[3], StringName);
1597 
1598  if (PhFormatToBuffer(format, 4, subBlock, sizeof(subBlock), NULL))
1599  return PhGetFileVersionInfoString(VersionInfo, subBlock);
1600  else
1601  return NULL;
1602 }
1603 
1605  _Out_ PPH_IMAGE_VERSION_INFO ImageVersionInfo,
1606  _In_ PVOID VersionInfo,
1607  _In_ ULONG LangCodePage
1608  )
1609 {
1610  ImageVersionInfo->CompanyName = PhGetFileVersionInfoString2(VersionInfo, LangCodePage, L"CompanyName");
1611  ImageVersionInfo->FileDescription = PhGetFileVersionInfoString2(VersionInfo, LangCodePage, L"FileDescription");
1612  ImageVersionInfo->ProductName = PhGetFileVersionInfoString2(VersionInfo, LangCodePage, L"ProductName");
1613 }
1614 
1622  _Out_ PPH_IMAGE_VERSION_INFO ImageVersionInfo,
1623  _In_ PWSTR FileName
1624  )
1625 {
1626  PVOID versionInfo;
1627  ULONG langCodePage;
1628  VS_FIXEDFILEINFO *rootBlock;
1629  ULONG rootBlockLength;
1630  PH_FORMAT fileVersionFormat[7];
1631 
1632  versionInfo = PhGetFileVersionInfo(FileName);
1633 
1634  if (!versionInfo)
1635  return FALSE;
1636 
1637  langCodePage = PhGetFileVersionInfoLangCodePage(versionInfo);
1638  PhpGetImageVersionInfoFields(ImageVersionInfo, versionInfo, langCodePage);
1639 
1640  if (!ImageVersionInfo->CompanyName && !ImageVersionInfo->FileDescription && !ImageVersionInfo->ProductName)
1641  {
1642  // Use the windows-1252 code page.
1643  PhpGetImageVersionInfoFields(ImageVersionInfo, versionInfo, (langCodePage & 0xffff0000) + 1252);
1644 
1645  // Use the default language (US English).
1646  if (!ImageVersionInfo->CompanyName && !ImageVersionInfo->FileDescription && !ImageVersionInfo->ProductName)
1647  {
1648  PhpGetImageVersionInfoFields(ImageVersionInfo, versionInfo, (MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US) << 16) + 1252);
1649 
1650  if (!ImageVersionInfo->CompanyName && !ImageVersionInfo->FileDescription && !ImageVersionInfo->ProductName)
1651  PhpGetImageVersionInfoFields(ImageVersionInfo, versionInfo, (MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US) << 16) + 0);
1652  }
1653  }
1654 
1655  // The version information is language-independent and must be read from the root block.
1656  if (VerQueryValue(versionInfo, L"\\", &rootBlock, &rootBlockLength) && rootBlockLength != 0)
1657  {
1658  PhInitFormatU(&fileVersionFormat[0], rootBlock->dwFileVersionMS >> 16);
1659  PhInitFormatC(&fileVersionFormat[1], '.');
1660  PhInitFormatU(&fileVersionFormat[2], rootBlock->dwFileVersionMS & 0xffff);
1661  PhInitFormatC(&fileVersionFormat[3], '.');
1662  PhInitFormatU(&fileVersionFormat[4], rootBlock->dwFileVersionLS >> 16);
1663  PhInitFormatC(&fileVersionFormat[5], '.');
1664  PhInitFormatU(&fileVersionFormat[6], rootBlock->dwFileVersionLS & 0xffff);
1665 
1666  ImageVersionInfo->FileVersion = PhFormat(fileVersionFormat, 7, 30);
1667  }
1668  else
1669  {
1670  ImageVersionInfo->FileVersion = NULL;
1671  }
1672 
1673  PhFree(versionInfo);
1674 
1675  return TRUE;
1676 }
1677 
1685  _Inout_ PPH_IMAGE_VERSION_INFO ImageVersionInfo
1686  )
1687 {
1688  if (ImageVersionInfo->CompanyName) PhDereferenceObject(ImageVersionInfo->CompanyName);
1689  if (ImageVersionInfo->FileDescription) PhDereferenceObject(ImageVersionInfo->FileDescription);
1690  if (ImageVersionInfo->FileVersion) PhDereferenceObject(ImageVersionInfo->FileVersion);
1691  if (ImageVersionInfo->ProductName) PhDereferenceObject(ImageVersionInfo->ProductName);
1692 }
1693 
1695  _In_opt_ PPH_STRING FileName,
1696  _In_ PPH_IMAGE_VERSION_INFO ImageVersionInfo,
1697  _In_opt_ PPH_STRINGREF Indent,
1698  _In_opt_ ULONG LineLimit
1699  )
1700 {
1701  PH_STRING_BUILDER stringBuilder;
1702 
1703  if (LineLimit == 0)
1704  LineLimit = MAXULONG32;
1705 
1706  PhInitializeStringBuilder(&stringBuilder, 40);
1707 
1708  // File name
1709 
1710  if (!PhIsNullOrEmptyString(FileName))
1711  {
1712  PPH_STRING temp;
1713 
1714  if (Indent) PhAppendStringBuilder(&stringBuilder, Indent);
1715 
1716  temp = PhEllipsisStringPath(FileName, LineLimit);
1717  PhAppendStringBuilder(&stringBuilder, &temp->sr);
1718  PhDereferenceObject(temp);
1719  PhAppendCharStringBuilder(&stringBuilder, '\n');
1720  }
1721 
1722  // File description & version
1723 
1724  if (!(
1725  PhIsNullOrEmptyString(ImageVersionInfo->FileDescription) &&
1726  PhIsNullOrEmptyString(ImageVersionInfo->FileVersion)
1727  ))
1728  {
1729  PPH_STRING tempDescription = NULL;
1730  PPH_STRING tempVersion = NULL;
1731  ULONG limitForDescription;
1732  ULONG limitForVersion;
1733 
1734  if (LineLimit != MAXULONG32)
1735  {
1736  limitForVersion = (LineLimit - 1) / 4; // 1/4 space for version (and space character)
1737  limitForDescription = LineLimit - limitForVersion;
1738  }
1739  else
1740  {
1741  limitForDescription = MAXULONG32;
1742  limitForVersion = MAXULONG32;
1743  }
1744 
1745  if (!PhIsNullOrEmptyString(ImageVersionInfo->FileDescription))
1746  {
1747  tempDescription = PhEllipsisString(
1748  ImageVersionInfo->FileDescription,
1749  limitForDescription
1750  );
1751  }
1752 
1753  if (!PhIsNullOrEmptyString(ImageVersionInfo->FileVersion))
1754  {
1755  tempVersion = PhEllipsisString(
1756  ImageVersionInfo->FileVersion,
1757  limitForVersion
1758  );
1759  }
1760 
1761  if (Indent) PhAppendStringBuilder(&stringBuilder, Indent);
1762 
1763  if (tempDescription)
1764  {
1765  PhAppendStringBuilder(&stringBuilder, &tempDescription->sr);
1766 
1767  if (tempVersion)
1768  PhAppendCharStringBuilder(&stringBuilder, ' ');
1769  }
1770 
1771  if (tempVersion)
1772  PhAppendStringBuilder(&stringBuilder, &tempVersion->sr);
1773 
1774  if (tempDescription)
1775  PhDereferenceObject(tempDescription);
1776  if (tempVersion)
1777  PhDereferenceObject(tempVersion);
1778 
1779  PhAppendCharStringBuilder(&stringBuilder, '\n');
1780  }
1781 
1782  // File company
1783 
1784  if (!PhIsNullOrEmptyString(ImageVersionInfo->CompanyName))
1785  {
1786  PPH_STRING temp;
1787 
1788  if (Indent) PhAppendStringBuilder(&stringBuilder, Indent);
1789 
1790  temp = PhEllipsisString(ImageVersionInfo->CompanyName, LineLimit);
1791  PhAppendStringBuilder(&stringBuilder, &temp->sr);
1792  PhDereferenceObject(temp);
1793  PhAppendCharStringBuilder(&stringBuilder, '\n');
1794  }
1795 
1796  // Remove the extra newline.
1797  if (stringBuilder.String->Length != 0)
1798  PhRemoveEndStringBuilder(&stringBuilder, 1);
1799 
1800  return PhFinalStringBuilderString(&stringBuilder);
1801 }
1802 
1812  _In_ PWSTR FileName,
1813  _Out_opt_ PULONG IndexOfFileName
1814  )
1815 {
1816  PPH_STRING fullPath;
1817  ULONG bufferSize;
1818  ULONG returnLength;
1819  PWSTR filePart;
1820 
1821  bufferSize = 0x80;
1822  fullPath = PhCreateStringEx(NULL, bufferSize * 2);
1823 
1824  returnLength = RtlGetFullPathName_U(FileName, bufferSize, fullPath->Buffer, &filePart);
1825 
1826  if (returnLength > bufferSize)
1827  {
1828  PhDereferenceObject(fullPath);
1829  bufferSize = returnLength;
1830  fullPath = PhCreateStringEx(NULL, bufferSize * 2);
1831 
1832  returnLength = RtlGetFullPathName_U(FileName, bufferSize, fullPath->Buffer, &filePart);
1833  }
1834 
1835  if (returnLength == 0)
1836  {
1837  PhDereferenceObject(fullPath);
1838  return NULL;
1839  }
1840 
1841  PhTrimToNullTerminatorString(fullPath);
1842 
1843  if (IndexOfFileName)
1844  {
1845  if (filePart)
1846  {
1847  // The path points to a file.
1848  *IndexOfFileName = (ULONG)(filePart - fullPath->Buffer);
1849  }
1850  else
1851  {
1852  // The path points to a directory.
1853  *IndexOfFileName = -1;
1854  }
1855  }
1856 
1857  return fullPath;
1858 }
1859 
1866  _In_ PPH_STRINGREF String
1867  )
1868 {
1869  NTSTATUS status;
1870  UNICODE_STRING inputString;
1871  UNICODE_STRING outputString;
1872  PPH_STRING string;
1873  ULONG bufferLength;
1874 
1875  if (!PhStringRefToUnicodeString(String, &inputString))
1876  return NULL;
1877 
1878  bufferLength = 0x40;
1879  string = PhCreateStringEx(NULL, bufferLength);
1880  outputString.MaximumLength = (USHORT)bufferLength;
1881  outputString.Length = 0;
1882  outputString.Buffer = string->Buffer;
1883 
1885  NULL,
1886  &inputString,
1887  &outputString,
1888  &bufferLength
1889  );
1890 
1891  if (status == STATUS_BUFFER_TOO_SMALL)
1892  {
1893  PhDereferenceObject(string);
1894  string = PhCreateStringEx(NULL, bufferLength);
1895  outputString.MaximumLength = (USHORT)bufferLength;
1896  outputString.Length = 0;
1897  outputString.Buffer = string->Buffer;
1898 
1900  NULL,
1901  &inputString,
1902  &outputString,
1903  &bufferLength
1904  );
1905  }
1906 
1907  if (!NT_SUCCESS(status))
1908  {
1909  PhDereferenceObject(string);
1910  return NULL;
1911  }
1912 
1913  string->Length = outputString.Length;
1914  string->Buffer[string->Length / 2] = 0; // make sure there is a null terminator
1915 
1916  return string;
1917 }
1918 
1925  _In_ PPH_STRING FileName
1926  )
1927 {
1928  PH_STRINGREF pathPart;
1929  PH_STRINGREF baseNamePart;
1930 
1931  if (!PhSplitStringRefAtLastChar(&FileName->sr, '\\', &pathPart, &baseNamePart))
1932  return PhReferenceObject(FileName);
1933 
1934  return PhCreateString2(&baseNamePart);
1935 }
1936 
1941  VOID
1942  )
1943 {
1944  static PPH_STRING cachedSystemDirectory = NULL;
1945 
1946  PPH_STRING systemDirectory;
1947  ULONG bufferSize;
1948  ULONG returnLength;
1949 
1950  // Use the cached value if possible.
1951 
1952  systemDirectory = cachedSystemDirectory;
1953 
1954  if (systemDirectory)
1955  return PhReferenceObject(systemDirectory);
1956 
1957  bufferSize = 0x40;
1958  systemDirectory = PhCreateStringEx(NULL, bufferSize * 2);
1959 
1960  returnLength = GetSystemDirectory(systemDirectory->Buffer, bufferSize);
1961 
1962  if (returnLength > bufferSize)
1963  {
1964  PhDereferenceObject(systemDirectory);
1965  bufferSize = returnLength;
1966  systemDirectory = PhCreateStringEx(NULL, bufferSize * 2);
1967 
1968  returnLength = GetSystemDirectory(systemDirectory->Buffer, bufferSize);
1969  }
1970 
1971  if (returnLength == 0)
1972  {
1973  PhDereferenceObject(systemDirectory);
1974  return NULL;
1975  }
1976 
1977  PhTrimToNullTerminatorString(systemDirectory);
1978 
1979  // Try to cache the value.
1981  &cachedSystemDirectory,
1982  systemDirectory,
1983  NULL
1984  ) == NULL)
1985  {
1986  // Success, add one more reference for the cache.
1987  PhReferenceObject(systemDirectory);
1988  }
1989 
1990  return systemDirectory;
1991 }
1992 
1997  _Out_ PPH_STRINGREF SystemRoot
1998  )
1999 {
2000  static PH_STRINGREF systemRoot;
2001 
2002  PH_STRINGREF localSystemRoot;
2003  SIZE_T count;
2004 
2005  if (systemRoot.Buffer)
2006  {
2007  *SystemRoot = systemRoot;
2008  return;
2009  }
2010 
2011  localSystemRoot.Buffer = USER_SHARED_DATA->NtSystemRoot;
2012  count = PhCountStringZ(localSystemRoot.Buffer);
2013  localSystemRoot.Length = count * sizeof(WCHAR);
2014 
2015  // Make sure the system root string doesn't have a trailing backslash.
2016  if (localSystemRoot.Buffer[count - 1] == '\\')
2017  localSystemRoot.Length -= sizeof(WCHAR);
2018 
2019  *SystemRoot = localSystemRoot;
2020 
2021  systemRoot.Length = localSystemRoot.Length;
2022  MemoryBarrier();
2023  systemRoot.Buffer = localSystemRoot.Buffer;
2024 }
2025 
2040 PLDR_DATA_TABLE_ENTRY PhFindLoaderEntry(
2041  _In_opt_ PVOID DllBase,
2042  _In_opt_ PPH_STRINGREF FullDllName,
2043  _In_opt_ PPH_STRINGREF BaseDllName
2044  )
2045 {
2046  PLDR_DATA_TABLE_ENTRY result = NULL;
2047  PLDR_DATA_TABLE_ENTRY entry;
2048  PH_STRINGREF fullDllName;
2049  PH_STRINGREF baseDllName;
2050  PLIST_ENTRY listHead;
2051  PLIST_ENTRY listEntry;
2052 
2053  listHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
2054  listEntry = listHead->Flink;
2055 
2056  while (listEntry != listHead)
2057  {
2058  entry = CONTAINING_RECORD(listEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
2059  PhUnicodeStringToStringRef(&entry->FullDllName, &fullDllName);
2060  PhUnicodeStringToStringRef(&entry->BaseDllName, &baseDllName);
2061 
2062  if (
2063  (!DllBase || entry->DllBase == DllBase) &&
2064  (!FullDllName || PhEqualStringRef(&fullDllName, FullDllName, TRUE)) &&
2065  (!BaseDllName || PhEqualStringRef(&baseDllName, BaseDllName, TRUE))
2066  )
2067  {
2068  result = entry;
2069  break;
2070  }
2071 
2072  listEntry = listEntry->Flink;
2073  }
2074 
2075  return result;
2076 }
2077 
2089  _In_ PVOID DllHandle,
2090  _Out_opt_ PULONG IndexOfFileName
2091  )
2092 {
2093  PLDR_DATA_TABLE_ENTRY entry;
2094  PPH_STRING fileName;
2095  PPH_STRING newFileName;
2096  ULONG_PTR indexOfFileName;
2097 
2098  RtlEnterCriticalSection(NtCurrentPeb()->LoaderLock);
2099 
2100  entry = PhFindLoaderEntry(DllHandle, NULL, NULL);
2101 
2102  if (entry)
2103  fileName = PhCreateStringFromUnicodeString(&entry->FullDllName);
2104  else
2105  fileName = NULL;
2106 
2107  RtlLeaveCriticalSection(NtCurrentPeb()->LoaderLock);
2108 
2109  if (!fileName)
2110  return NULL;
2111 
2112  newFileName = PhGetFileName(fileName);
2113  PhDereferenceObject(fileName);
2114  fileName = newFileName;
2115 
2116  if (IndexOfFileName)
2117  {
2118  indexOfFileName = PhFindLastCharInString(fileName, 0, '\\');
2119 
2120  if (indexOfFileName != -1)
2121  indexOfFileName++;
2122  else
2123  indexOfFileName = 0;
2124 
2125  *IndexOfFileName = (ULONG)indexOfFileName;
2126  }
2127 
2128  return fileName;
2129 }
2130 
2135  VOID
2136  )
2137 {
2138  return PhGetDllFileName(NtCurrentPeb()->ImageBaseAddress, NULL);
2139 }
2140 
2145  VOID
2146  )
2147 {
2148  PPH_STRING fileName;
2149  ULONG indexOfFileName;
2150  PPH_STRING path = NULL;
2151 
2152  fileName = PhGetDllFileName(NtCurrentPeb()->ImageBaseAddress, &indexOfFileName);
2153 
2154  if (fileName)
2155  {
2156  if (indexOfFileName != 0)
2157  {
2158  // Remove the file name from the path.
2159  path = PhSubstring(fileName, 0, indexOfFileName);
2160  }
2161 
2162  PhDereferenceObject(fileName);
2163  }
2164 
2165  return path;
2166 }
2167 
2175  _In_ ULONG Folder,
2176  _In_opt_ PWSTR AppendPath
2177  )
2178 {
2179  PPH_STRING path;
2180  SIZE_T appendPathLength;
2181 
2182  if (AppendPath)
2183  appendPathLength = PhCountStringZ(AppendPath) * 2;
2184  else
2185  appendPathLength = 0;
2186 
2187  path = PhCreateStringEx(NULL, MAX_PATH * 2 + appendPathLength);
2188 
2189  if (SUCCEEDED(SHGetFolderPath(
2190  NULL,
2191  Folder,
2192  NULL,
2193  SHGFP_TYPE_CURRENT,
2194  path->Buffer
2195  )))
2196  {
2198 
2199  if (AppendPath)
2200  {
2201  memcpy(&path->Buffer[path->Length / 2], AppendPath, appendPathLength + 2); // +2 for null terminator
2202  path->Length += appendPathLength;
2203  }
2204 
2205  return path;
2206  }
2207 
2208  PhDereferenceObject(path);
2209 
2210  return NULL;
2211 }
2212 
2227  _In_opt_ HWND hWnd,
2228  _In_ ULONG NumberOfHandles,
2229  _In_ PHANDLE Handles,
2230  _In_ ULONG Timeout
2231  )
2232 {
2233  NTSTATUS status;
2234  ULONG startTickCount;
2235  ULONG currentTickCount;
2236  LONG currentTimeout;
2237 
2238  startTickCount = GetTickCount();
2239  currentTimeout = Timeout;
2240 
2241  while (TRUE)
2242  {
2243  status = MsgWaitForMultipleObjects(
2244  NumberOfHandles,
2245  Handles,
2246  FALSE,
2247  (ULONG)currentTimeout,
2248  QS_ALLEVENTS
2249  );
2250 
2251  if (status >= STATUS_WAIT_0 && status < (NTSTATUS)(STATUS_WAIT_0 + NumberOfHandles))
2252  {
2253  return status;
2254  }
2255  else if (status == (STATUS_WAIT_0 + NumberOfHandles))
2256  {
2257  MSG msg;
2258 
2259  // Pump messages
2260 
2261  while (PeekMessage(&msg, hWnd, 0, 0, PM_REMOVE))
2262  {
2263  TranslateMessage(&msg);
2264  DispatchMessage(&msg);
2265  }
2266  }
2267  else
2268  {
2269  return status;
2270  }
2271 
2272  // Recompute the timeout value.
2273 
2274  if (Timeout != INFINITE)
2275  {
2276  currentTickCount = GetTickCount();
2277  currentTimeout = Timeout - (currentTickCount - startTickCount);
2278 
2279  if (currentTimeout < 0)
2280  return STATUS_TIMEOUT;
2281  }
2282  }
2283 }
2284 
2313  _In_ PWSTR FileName,
2314  _In_opt_ PPH_STRINGREF CommandLine,
2315  _In_opt_ PVOID Environment,
2316  _In_opt_ PPH_STRINGREF CurrentDirectory,
2317  _In_opt_ PPH_CREATE_PROCESS_INFO Information,
2318  _In_ ULONG Flags,
2319  _In_opt_ HANDLE ParentProcessHandle,
2320  _Out_opt_ PCLIENT_ID ClientId,
2321  _Out_opt_ PHANDLE ProcessHandle,
2322  _Out_opt_ PHANDLE ThreadHandle
2323  )
2324 {
2325  NTSTATUS status;
2326  RTL_USER_PROCESS_INFORMATION processInfo;
2327  PRTL_USER_PROCESS_PARAMETERS parameters;
2328  UNICODE_STRING fileName;
2329  UNICODE_STRING commandLine;
2330  UNICODE_STRING currentDirectory;
2331  PUNICODE_STRING windowTitle;
2332  PUNICODE_STRING desktopInfo;
2333 
2335  FileName,
2336  &fileName,
2337  NULL,
2338  NULL
2339  ))
2340  return STATUS_OBJECT_NAME_NOT_FOUND;
2341 
2342  if (CommandLine)
2343  {
2344  if (!PhStringRefToUnicodeString(CommandLine, &commandLine))
2345  return STATUS_NAME_TOO_LONG;
2346  }
2347 
2348  if (CurrentDirectory)
2349  {
2350  if (!PhStringRefToUnicodeString(CurrentDirectory, &currentDirectory))
2351  return STATUS_NAME_TOO_LONG;
2352  }
2353 
2354  if (Information)
2355  {
2356  windowTitle = Information->WindowTitle;
2357  desktopInfo = Information->DesktopInfo;
2358  }
2359  else
2360  {
2361  windowTitle = NULL;
2362  desktopInfo = NULL;
2363  }
2364 
2365  if (!windowTitle)
2366  windowTitle = &fileName;
2367 
2368  if (!desktopInfo)
2369  desktopInfo = &NtCurrentPeb()->ProcessParameters->DesktopInfo;
2370 
2371  status = RtlCreateProcessParameters(
2372  &parameters,
2373  &fileName,
2374  Information ? Information->DllPath : NULL,
2375  CurrentDirectory ? &currentDirectory : NULL,
2376  CommandLine ? &commandLine : &fileName,
2377  Environment,
2378  windowTitle,
2379  desktopInfo,
2380  Information ? Information->ShellInfo : NULL,
2381  Information ? Information->RuntimeData : NULL
2382  );
2383 
2384  if (NT_SUCCESS(status))
2385  {
2386  status = RtlCreateUserProcess(
2387  &fileName,
2389  parameters,
2390  NULL,
2391  NULL,
2392  ParentProcessHandle,
2394  NULL,
2395  NULL,
2396  &processInfo
2397  );
2398  RtlDestroyProcessParameters(parameters);
2399  }
2400 
2401  RtlFreeHeap(RtlProcessHeap(), 0, fileName.Buffer);
2402 
2403  if (NT_SUCCESS(status))
2404  {
2405  if (!(Flags & PH_CREATE_PROCESS_SUSPENDED))
2406  NtResumeThread(processInfo.Thread, NULL);
2407 
2408  if (ClientId)
2409  *ClientId = processInfo.ClientId;
2410 
2411  if (ProcessHandle)
2412  *ProcessHandle = processInfo.Process;
2413  else
2414  NtClose(processInfo.Process);
2415 
2416  if (ThreadHandle)
2417  *ThreadHandle = processInfo.Thread;
2418  else
2419  NtClose(processInfo.Thread);
2420  }
2421 
2422  return status;
2423 }
2424 
2442  _In_opt_ PWSTR FileName,
2443  _In_opt_ PWSTR CommandLine,
2444  _In_opt_ PVOID Environment,
2445  _In_opt_ PWSTR CurrentDirectory,
2446  _In_ ULONG Flags,
2447  _In_opt_ HANDLE TokenHandle,
2448  _Out_opt_ PHANDLE ProcessHandle,
2449  _Out_opt_ PHANDLE ThreadHandle
2450  )
2451 {
2452  return PhCreateProcessWin32Ex(
2453  FileName,
2454  CommandLine,
2455  Environment,
2456  CurrentDirectory,
2457  NULL,
2458  Flags,
2459  TokenHandle,
2460  NULL,
2461  ProcessHandle,
2462  ThreadHandle
2463  );
2464 }
2465 
2466 static const PH_FLAG_MAPPING PhpCreateProcessMappings[] =
2467 {
2468  { PH_CREATE_PROCESS_UNICODE_ENVIRONMENT, CREATE_UNICODE_ENVIRONMENT },
2469  { PH_CREATE_PROCESS_SUSPENDED, CREATE_SUSPENDED },
2470  { PH_CREATE_PROCESS_BREAKAWAY_FROM_JOB, CREATE_BREAKAWAY_FROM_JOB },
2471  { PH_CREATE_PROCESS_NEW_CONSOLE, CREATE_NEW_CONSOLE }
2472 };
2473 
2475  _In_ PPROCESS_INFORMATION ProcessInfo,
2476  _Out_opt_ PCLIENT_ID ClientId,
2477  _Out_opt_ PHANDLE ProcessHandle,
2478  _Out_opt_ PHANDLE ThreadHandle
2479  )
2480 {
2481  if (ClientId)
2482  {
2483  ClientId->UniqueProcess = UlongToHandle(ProcessInfo->dwProcessId);
2484  ClientId->UniqueThread = UlongToHandle(ProcessInfo->dwThreadId);
2485  }
2486 
2487  if (ProcessHandle)
2488  *ProcessHandle = ProcessInfo->hProcess;
2489  else
2490  NtClose(ProcessInfo->hProcess);
2491 
2492  if (ThreadHandle)
2493  *ThreadHandle = ProcessInfo->hThread;
2494  else
2495  NtClose(ProcessInfo->hThread);
2496 }
2497 
2519  _In_opt_ PWSTR FileName,
2520  _In_opt_ PWSTR CommandLine,
2521  _In_opt_ PVOID Environment,
2522  _In_opt_ PWSTR CurrentDirectory,
2523  _In_opt_ STARTUPINFO *StartupInfo,
2524  _In_ ULONG Flags,
2525  _In_opt_ HANDLE TokenHandle,
2526  _Out_opt_ PCLIENT_ID ClientId,
2527  _Out_opt_ PHANDLE ProcessHandle,
2528  _Out_opt_ PHANDLE ThreadHandle
2529  )
2530 {
2531  NTSTATUS status;
2532  PPH_STRING commandLine = NULL;
2533  STARTUPINFO startupInfo;
2534  PROCESS_INFORMATION processInfo;
2535  ULONG newFlags;
2536 
2537  if (CommandLine) // duplicate because CreateProcess modifies the string
2538  commandLine = PhCreateString(CommandLine);
2539 
2540  newFlags = 0;
2541  PhMapFlags1(&newFlags, Flags, PhpCreateProcessMappings, sizeof(PhpCreateProcessMappings) / sizeof(PH_FLAG_MAPPING));
2542 
2543  if (StartupInfo)
2544  {
2545  startupInfo = *StartupInfo;
2546  }
2547  else
2548  {
2549  memset(&startupInfo, 0, sizeof(STARTUPINFO));
2550  startupInfo.cb = sizeof(STARTUPINFO);
2551  }
2552 
2553  if (!TokenHandle)
2554  {
2555  if (CreateProcess(
2556  FileName,
2557  PhGetString(commandLine),
2558  NULL,
2559  NULL,
2561  newFlags,
2562  Environment,
2563  CurrentDirectory,
2564  &startupInfo,
2565  &processInfo
2566  ))
2567  status = STATUS_SUCCESS;
2568  else
2569  status = PhGetLastWin32ErrorAsNtStatus();
2570  }
2571  else
2572  {
2573  if (CreateProcessAsUser(
2574  TokenHandle,
2575  FileName,
2576  PhGetString(commandLine),
2577  NULL,
2578  NULL,
2580  newFlags,
2581  Environment,
2582  CurrentDirectory,
2583  &startupInfo,
2584  &processInfo
2585  ))
2586  status = STATUS_SUCCESS;
2587  else
2588  status = PhGetLastWin32ErrorAsNtStatus();
2589  }
2590 
2591  if (commandLine)
2592  PhDereferenceObject(commandLine);
2593 
2594  if (NT_SUCCESS(status))
2595  {
2596  PhpConvertProcessInformation(&processInfo, ClientId, ProcessHandle, ThreadHandle);
2597  }
2598 
2599  return status;
2600 }
2601 
2623  _In_ PPH_CREATE_PROCESS_AS_USER_INFO Information,
2624  _In_ ULONG Flags,
2625  _Out_opt_ PCLIENT_ID ClientId,
2626  _Out_opt_ PHANDLE ProcessHandle,
2627  _Out_opt_ PHANDLE ThreadHandle
2628  )
2629 {
2630  static PH_INITONCE initOnce = PH_INITONCE_INIT;
2631  static _WinStationQueryInformationW WinStationQueryInformationW_I = NULL;
2632  static _CreateEnvironmentBlock CreateEnvironmentBlock_I = NULL;
2633  static _DestroyEnvironmentBlock DestroyEnvironmentBlock_I = NULL;
2634 
2635  NTSTATUS status;
2636  HANDLE tokenHandle;
2637  PVOID defaultEnvironment = NULL;
2638  STARTUPINFO startupInfo = { sizeof(startupInfo) };
2639  BOOLEAN needsDuplicate = FALSE;
2640 
2641  if (PhBeginInitOnce(&initOnce))
2642  {
2643  HMODULE winsta;
2644  HMODULE userEnv;
2645 
2646  winsta = LoadLibrary(L"winsta.dll");
2647  WinStationQueryInformationW_I = (_WinStationQueryInformationW)GetProcAddress(winsta, "WinStationQueryInformationW");
2648 
2649  userEnv = LoadLibrary(L"userenv.dll");
2650  CreateEnvironmentBlock_I = (_CreateEnvironmentBlock)GetProcAddress(userEnv, "CreateEnvironmentBlock");
2651  DestroyEnvironmentBlock_I = (_DestroyEnvironmentBlock)GetProcAddress(userEnv, "DestroyEnvironmentBlock");
2652 
2653  PhEndInitOnce(&initOnce);
2654  }
2655 
2657  return STATUS_INVALID_PARAMETER_2;
2658  if (!Information->ApplicationName && !Information->CommandLine)
2659  return STATUS_INVALID_PARAMETER_MIX;
2660 
2661  startupInfo.lpDesktop = Information->DesktopName;
2662 
2663  // Try to use CreateProcessWithLogonW if we need to load the user profile.
2664  // This isn't compatible with some options.
2665  if (Flags & PH_CREATE_PROCESS_WITH_PROFILE)
2666  {
2667  BOOLEAN useWithLogon;
2668 
2669  useWithLogon = TRUE;
2670 
2671  if (Flags & (PH_CREATE_PROCESS_USE_PROCESS_TOKEN | PH_CREATE_PROCESS_USE_SESSION_TOKEN))
2672  useWithLogon = FALSE;
2673 
2675  useWithLogon = FALSE;
2676 
2678  {
2679  if (Information->SessionId != NtCurrentPeb()->SessionId)
2680  useWithLogon = FALSE;
2681  }
2682 
2683  if (Information->LogonType && Information->LogonType != LOGON32_LOGON_INTERACTIVE)
2684  useWithLogon = FALSE;
2685 
2686  if (useWithLogon)
2687  {
2688  PPH_STRING commandLine;
2689  PROCESS_INFORMATION processInfo;
2690  ULONG newFlags;
2691 
2692  if (Information->CommandLine) // duplicate because CreateProcess modifies the string
2693  commandLine = PhCreateString(Information->CommandLine);
2694  else
2695  commandLine = NULL;
2696 
2697  if (!Information->Environment)
2699 
2700  newFlags = 0;
2701  PhMapFlags1(&newFlags, Flags, PhpCreateProcessMappings, sizeof(PhpCreateProcessMappings) / sizeof(PH_FLAG_MAPPING));
2702 
2703  if (CreateProcessWithLogonW(
2704  Information->UserName,
2705  Information->DomainName,
2706  Information->Password,
2707  LOGON_WITH_PROFILE,
2708  Information->ApplicationName,
2709  PhGetString(commandLine),
2710  newFlags,
2711  Information->Environment,
2712  Information->CurrentDirectory,
2713  &startupInfo,
2714  &processInfo
2715  ))
2716  status = STATUS_SUCCESS;
2717  else
2718  status = PhGetLastWin32ErrorAsNtStatus();
2719 
2720  if (commandLine)
2721  PhDereferenceObject(commandLine);
2722 
2723  if (NT_SUCCESS(status))
2724  {
2725  PhpConvertProcessInformation(&processInfo, ClientId, ProcessHandle, ThreadHandle);
2726  }
2727 
2728  return status;
2729  }
2730  }
2731 
2732  // Get the token handle. Various methods are supported.
2733 
2734  if (Flags & PH_CREATE_PROCESS_USE_PROCESS_TOKEN)
2735  {
2736  HANDLE processHandle;
2737 
2738  if (!NT_SUCCESS(status = PhOpenProcess(
2739  &processHandle,
2741  Information->ProcessIdWithToken
2742  )))
2743  return status;
2744 
2745  status = PhOpenProcessToken(
2746  &tokenHandle,
2747  TOKEN_ALL_ACCESS,
2748  processHandle
2749  );
2750  NtClose(processHandle);
2751 
2752  if (!NT_SUCCESS(status))
2753  return status;
2754 
2756  needsDuplicate = TRUE; // can't set the session ID of a token in use by a process
2757  }
2758  else if (Flags & PH_CREATE_PROCESS_USE_SESSION_TOKEN)
2759  {
2760  WINSTATIONUSERTOKEN userToken;
2761  ULONG returnLength;
2762 
2763  if (!WinStationQueryInformationW_I)
2764  return STATUS_PROCEDURE_NOT_FOUND;
2765 
2766  if (!WinStationQueryInformationW_I(
2767  NULL,
2768  Information->SessionIdWithToken,
2770  &userToken,
2771  sizeof(WINSTATIONUSERTOKEN),
2772  &returnLength
2773  ))
2774  {
2776  }
2777 
2778  tokenHandle = userToken.UserToken;
2779 
2781  needsDuplicate = TRUE; // not sure if this is necessary
2782  }
2783  else
2784  {
2785  ULONG logonType;
2786 
2787  if (Information->LogonType)
2788  {
2789  logonType = Information->LogonType;
2790  }
2791  else
2792  {
2793  logonType = LOGON32_LOGON_INTERACTIVE;
2794 
2795  // Check if this is a service logon.
2796  if (PhEqualStringZ(Information->DomainName, L"NT AUTHORITY", TRUE))
2797  {
2798  if (PhEqualStringZ(Information->UserName, L"SYSTEM", TRUE))
2799  {
2801  logonType = LOGON32_LOGON_SERVICE;
2802  else
2803  logonType = LOGON32_LOGON_NEW_CREDENTIALS; // HACK
2804  }
2805 
2806  if (PhEqualStringZ(Information->UserName, L"LOCAL SERVICE", TRUE) ||
2807  PhEqualStringZ(Information->UserName, L"NETWORK SERVICE", TRUE))
2808  {
2809  logonType = LOGON32_LOGON_SERVICE;
2810  }
2811  }
2812  }
2813 
2814  if (!LogonUser(
2815  Information->UserName,
2816  Information->DomainName,
2817  Information->Password,
2818  logonType,
2819  LOGON32_PROVIDER_DEFAULT,
2820  &tokenHandle
2821  ))
2823  }
2824 
2826  {
2827  HANDLE linkedTokenHandle;
2828  TOKEN_TYPE tokenType;
2829  ULONG returnLength;
2830 
2831  // NtQueryInformationToken normally returns an impersonation token with SecurityIdentification,
2832  // but if the process is running with SeTcbPrivilege, it returns a primary token. We can never
2833  // duplicate a SecurityIdentification impersonation token to make it a primary token, so we just
2834  // check if the token is primary before using it.
2835 
2836  if (NT_SUCCESS(PhGetTokenLinkedToken(tokenHandle, &linkedTokenHandle)))
2837  {
2839  linkedTokenHandle,
2840  TokenType,
2841  &tokenType,
2842  sizeof(TOKEN_TYPE),
2843  &returnLength
2844  )) && tokenType == TokenPrimary)
2845  {
2846  NtClose(tokenHandle);
2847  tokenHandle = linkedTokenHandle;
2848  needsDuplicate = FALSE; // the linked token that is returned is always a copy, so no need to duplicate
2849  }
2850  else
2851  {
2852  NtClose(linkedTokenHandle);
2853  }
2854  }
2855  }
2856 
2857  if (needsDuplicate)
2858  {
2859  HANDLE newTokenHandle;
2860  OBJECT_ATTRIBUTES objectAttributes;
2861 
2863  &objectAttributes,
2864  NULL,
2865  0,
2866  NULL,
2867  NULL
2868  );
2869 
2870  status = NtDuplicateToken(
2871  tokenHandle,
2872  TOKEN_ALL_ACCESS,
2873  &objectAttributes,
2874  FALSE,
2875  TokenPrimary,
2876  &newTokenHandle
2877  );
2878  NtClose(tokenHandle);
2879 
2880  if (!NT_SUCCESS(status))
2881  return status;
2882 
2883  tokenHandle = newTokenHandle;
2884  }
2885 
2886  // Set the session ID if needed.
2887 
2889  {
2890  if (!NT_SUCCESS(status = PhSetTokenSessionId(
2891  tokenHandle,
2892  Information->SessionId
2893  )))
2894  {
2895  NtClose(tokenHandle);
2896  return status;
2897  }
2898  }
2899 
2900  if (!Information->Environment)
2901  {
2902  if (CreateEnvironmentBlock_I)
2903  {
2904  CreateEnvironmentBlock_I(&defaultEnvironment, tokenHandle, FALSE);
2905 
2906  if (defaultEnvironment)
2908  }
2909  }
2910 
2911  status = PhCreateProcessWin32Ex(
2912  Information->ApplicationName,
2913  Information->CommandLine,
2914  Information->Environment ? Information->Environment : defaultEnvironment,
2915  Information->CurrentDirectory,
2916  &startupInfo,
2917  Flags,
2918  tokenHandle,
2919  ClientId,
2920  ProcessHandle,
2921  ThreadHandle
2922  );
2923 
2924  if (defaultEnvironment)
2925  {
2926  if (DestroyEnvironmentBlock_I)
2927  DestroyEnvironmentBlock_I(defaultEnvironment);
2928  }
2929 
2930  NtClose(tokenHandle);
2931 
2932  return status;
2933 }
2934 
2936  _In_ PSID AccountSid,
2937  _Out_ PTOKEN_PRIVILEGES *Privileges
2938  )
2939 {
2940  NTSTATUS status;
2941  LSA_HANDLE accountHandle;
2942  PPRIVILEGE_SET accountPrivileges;
2943  PTOKEN_PRIVILEGES privileges;
2944 
2945  status = LsaOpenAccount(PhGetLookupPolicyHandle(), AccountSid, ACCOUNT_VIEW, &accountHandle);
2946 
2947  if (!NT_SUCCESS(status))
2948  return status;
2949 
2950  status = LsaEnumeratePrivilegesOfAccount(accountHandle, &accountPrivileges);
2951  LsaClose(accountHandle);
2952 
2953  if (!NT_SUCCESS(status))
2954  return status;
2955 
2956  privileges = PhAllocate(FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges) + sizeof(LUID_AND_ATTRIBUTES) * accountPrivileges->PrivilegeCount);
2957  privileges->PrivilegeCount = accountPrivileges->PrivilegeCount;
2958  memcpy(privileges->Privileges, accountPrivileges->Privilege, sizeof(LUID_AND_ATTRIBUTES) * accountPrivileges->PrivilegeCount);
2959 
2960  LsaFreeMemory(accountPrivileges);
2961 
2962  *Privileges = privileges;
2963 
2964  return status;
2965 }
2966 
2977  _In_ HANDLE TokenHandle,
2978  _Out_ PHANDLE NewTokenHandle
2979  )
2980 {
2981  static SID_IDENTIFIER_AUTHORITY ntAuthority = SECURITY_NT_AUTHORITY;
2982  static SID_IDENTIFIER_AUTHORITY mandatoryLabelAuthority = SECURITY_MANDATORY_LABEL_AUTHORITY;
2983  static LUID_AND_ATTRIBUTES defaultAllowedPrivileges[] =
2984  {
2985  { { SE_SHUTDOWN_PRIVILEGE, 0 }, 0 },
2986  { { SE_CHANGE_NOTIFY_PRIVILEGE, 0 }, 0 },
2987  { { SE_UNDOCK_PRIVILEGE, 0 }, 0 },
2988  { { SE_INC_WORKING_SET_PRIVILEGE, 0 }, 0 },
2989  { { SE_TIME_ZONE_PRIVILEGE, 0 }, 0 }
2990  };
2991 
2992  NTSTATUS status;
2993  UCHAR administratorsSidBuffer[FIELD_OFFSET(SID, SubAuthority) + sizeof(ULONG) * 2];
2994  PSID administratorsSid;
2995  UCHAR usersSidBuffer[FIELD_OFFSET(SID, SubAuthority) + sizeof(ULONG) * 2];
2996  PSID usersSid;
2997  UCHAR sidsToDisableBuffer[FIELD_OFFSET(TOKEN_GROUPS, Groups) + sizeof(SID_AND_ATTRIBUTES)];
2998  PTOKEN_GROUPS sidsToDisable;
2999  ULONG i;
3000  ULONG j;
3001  ULONG deleteIndex;
3002  BOOLEAN found;
3003  PTOKEN_PRIVILEGES privilegesOfToken;
3004  PTOKEN_PRIVILEGES privilegesOfUsers;
3005  PTOKEN_PRIVILEGES privilegesToDelete;
3006  UCHAR lowMandatoryLevelSidBuffer[FIELD_OFFSET(SID, SubAuthority) + sizeof(ULONG)];
3007  PSID lowMandatoryLevelSid;
3008  TOKEN_MANDATORY_LABEL mandatoryLabel;
3009  PSECURITY_DESCRIPTOR currentSecurityDescriptor;
3010  BOOLEAN currentDaclPresent;
3011  BOOLEAN currentDaclDefaulted;
3012  PACL currentDacl;
3013  PTOKEN_USER currentUser;
3014  PACE_HEADER currentAce;
3015  ULONG newDaclLength;
3016  PACL newDacl;
3017  SECURITY_DESCRIPTOR newSecurityDescriptor;
3018  TOKEN_DEFAULT_DACL newDefaultDacl;
3019  HANDLE newTokenHandle;
3020 
3021  // Set up the SIDs to Disable structure.
3022 
3023  // Initialize the Administrators SID.
3024  administratorsSid = (PSID)administratorsSidBuffer;
3025  RtlInitializeSid(administratorsSid, &ntAuthority, 2);
3026  *RtlSubAuthoritySid(administratorsSid, 0) = SECURITY_BUILTIN_DOMAIN_RID;
3027  *RtlSubAuthoritySid(administratorsSid, 1) = DOMAIN_ALIAS_RID_ADMINS;
3028 
3029  // Initialize the Users SID.
3030  usersSid = (PSID)usersSidBuffer;
3031  RtlInitializeSid(usersSid, &ntAuthority, 2);
3032  *RtlSubAuthoritySid(usersSid, 0) = SECURITY_BUILTIN_DOMAIN_RID;
3033  *RtlSubAuthoritySid(usersSid, 1) = DOMAIN_ALIAS_RID_USERS;
3034 
3035  sidsToDisable = (PTOKEN_GROUPS)sidsToDisableBuffer;
3036  sidsToDisable->GroupCount = 1;
3037  sidsToDisable->Groups[0].Sid = administratorsSid;
3038  sidsToDisable->Groups[0].Attributes = 0;
3039 
3040  // Set up the Privileges to Delete structure.
3041 
3042  // Get the privileges that the input token contains.
3043  if (!NT_SUCCESS(status = PhGetTokenPrivileges(TokenHandle, &privilegesOfToken)))
3044  return status;
3045 
3046  // Get the privileges of the Users group - the privileges that we are going to allow.
3047  if (!NT_SUCCESS(PhpGetAccountPrivileges(usersSid, &privilegesOfUsers)))
3048  {
3049  // Unsuccessful, so use the default set of privileges.
3050  privilegesOfUsers = PhAllocate(FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges) + sizeof(defaultAllowedPrivileges));
3051  privilegesOfUsers->PrivilegeCount = sizeof(defaultAllowedPrivileges) / sizeof(LUID_AND_ATTRIBUTES);
3052  memcpy(privilegesOfUsers->Privileges, defaultAllowedPrivileges, sizeof(defaultAllowedPrivileges));
3053  }
3054 
3055  // Allocate storage for the privileges we need to delete. The worst case scenario is that
3056  // all privileges in the token need to be deleted.
3057  privilegesToDelete = PhAllocate(FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges) + sizeof(LUID_AND_ATTRIBUTES) * privilegesOfToken->PrivilegeCount);
3058  deleteIndex = 0;
3059 
3060  // Compute the privileges that we need to delete.
3061  for (i = 0; i < privilegesOfToken->PrivilegeCount; i++)
3062  {
3063  found = FALSE;
3064 
3065  // Is the privilege allowed?
3066  for (j = 0; j < privilegesOfUsers->PrivilegeCount; j++)
3067  {
3068  if (RtlIsEqualLuid(&privilegesOfToken->Privileges[i].Luid, &privilegesOfUsers->Privileges[j].Luid))
3069  {
3070  found = TRUE;
3071  break;
3072  }
3073  }
3074 
3075  if (!found)
3076  {
3077  // This privilege needs to be deleted.
3078  privilegesToDelete->Privileges[deleteIndex].Attributes = 0;
3079  privilegesToDelete->Privileges[deleteIndex].Luid = privilegesOfToken->Privileges[i].Luid;
3080  deleteIndex++;
3081  }
3082  }
3083 
3084  privilegesToDelete->PrivilegeCount = deleteIndex;
3085 
3086  // Filter the token.
3087 
3088  status = NtFilterToken(
3089  TokenHandle,
3090  0,
3091  sidsToDisable,
3092  privilegesToDelete,
3093  NULL,
3094  &newTokenHandle
3095  );
3096  PhFree(privilegesToDelete);
3097  PhFree(privilegesOfUsers);
3098  PhFree(privilegesOfToken);
3099 
3100  if (!NT_SUCCESS(status))
3101  return status;
3102 
3103  // Set the integrity level to Low if we're on Vista and above.
3104  if (WINDOWS_HAS_UAC)
3105  {
3106  lowMandatoryLevelSid = (PSID)lowMandatoryLevelSidBuffer;
3107  RtlInitializeSid(lowMandatoryLevelSid, &mandatoryLabelAuthority, 1);
3108  *RtlSubAuthoritySid(lowMandatoryLevelSid, 0) = SECURITY_MANDATORY_LOW_RID;
3109 
3110  mandatoryLabel.Label.Sid = lowMandatoryLevelSid;
3111  mandatoryLabel.Label.Attributes = SE_GROUP_INTEGRITY;
3112 
3113  NtSetInformationToken(newTokenHandle, TokenIntegrityLevel, &mandatoryLabel, sizeof(TOKEN_MANDATORY_LABEL));
3114  }
3115 
3116  // Fix up the security descriptor and default DACL.
3117  if (NT_SUCCESS(PhGetObjectSecurity(newTokenHandle, DACL_SECURITY_INFORMATION, &currentSecurityDescriptor)))
3118  {
3119  if (NT_SUCCESS(PhGetTokenUser(TokenHandle, &currentUser)))
3120  {
3122  currentSecurityDescriptor,
3123  &currentDaclPresent,
3124  &currentDacl,
3125  &currentDaclDefaulted
3126  )))
3127  {
3128  currentDaclPresent = FALSE;
3129  }
3130 
3131  newDaclLength = sizeof(ACL) + FIELD_OFFSET(ACCESS_ALLOWED_ACE, SidStart) + RtlLengthSid(currentUser->User.Sid);
3132 
3133  if (currentDaclPresent)
3134  newDaclLength += currentDacl->AclSize - sizeof(ACL);
3135 
3136  newDacl = PhAllocate(newDaclLength);
3137  RtlCreateAcl(newDacl, newDaclLength, ACL_REVISION);
3138 
3139  // Add the existing DACL entries.
3140  if (currentDaclPresent)
3141  {
3142  for (i = 0; i < currentDacl->AceCount; i++)
3143  {
3144  if (NT_SUCCESS(RtlGetAce(currentDacl, i, &currentAce)))
3145  RtlAddAce(newDacl, ACL_REVISION, MAXULONG32, currentAce, currentAce->AceSize);
3146  }
3147  }
3148 
3149  // Allow access for the current user.
3150  RtlAddAccessAllowedAce(newDacl, ACL_REVISION, GENERIC_ALL, currentUser->User.Sid);
3151 
3152  // Set the security descriptor of the new token.
3153 
3154  RtlCreateSecurityDescriptor(&newSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION);
3155 
3156  if (NT_SUCCESS(RtlSetDaclSecurityDescriptor(&newSecurityDescriptor, TRUE, newDacl, FALSE)))
3157  PhSetObjectSecurity(newTokenHandle, DACL_SECURITY_INFORMATION, &newSecurityDescriptor);
3158 
3159  // Set the default DACL.
3160 
3161  newDefaultDacl.DefaultDacl = newDacl;
3162  NtSetInformationToken(newTokenHandle, TokenDefaultDacl, &newDefaultDacl, sizeof(TOKEN_DEFAULT_DACL));
3163 
3164  PhFree(newDacl);
3165 
3166  PhFree(currentUser);
3167  }
3168 
3169  PhFree(currentSecurityDescriptor);
3170  }
3171 
3172  *NewTokenHandle = newTokenHandle;
3173 
3174  return STATUS_SUCCESS;
3175 }
3176 
3185  _In_ HWND hWnd,
3186  _In_ PWSTR FileName,
3187  _In_opt_ PWSTR Parameters
3188  )
3189 {
3190  SHELLEXECUTEINFO info = { sizeof(info) };
3191 
3192  info.lpFile = FileName;
3193  info.lpParameters = Parameters;
3194  info.nShow = SW_SHOW;
3195  info.hwnd = hWnd;
3196 
3197  if (!ShellExecuteEx(&info))
3198  {
3199  // It already displays error messages by itself.
3200  //PhShowStatus(hWnd, L"Unable to execute the program", 0, GetLastError());
3201  }
3202 }
3203 
3220  _In_opt_ HWND hWnd,
3221  _In_ PWSTR FileName,
3222  _In_opt_ PWSTR Parameters,
3223  _In_ ULONG ShowWindowType,
3224  _In_ ULONG Flags,
3225  _In_opt_ ULONG Timeout,
3226  _Out_opt_ PHANDLE ProcessHandle
3227  )
3228 {
3229  SHELLEXECUTEINFO info = { sizeof(info) };
3230 
3231  info.lpFile = FileName;
3232  info.lpParameters = Parameters;
3233  info.fMask = SEE_MASK_NOCLOSEPROCESS;
3234  info.nShow = ShowWindowType;
3235  info.hwnd = hWnd;
3236 
3237  if ((Flags & PH_SHELL_EXECUTE_ADMIN) && WINDOWS_HAS_UAC)
3238  info.lpVerb = L"runas";
3239 
3240  if (ShellExecuteEx(&info))
3241  {
3242  if (Timeout)
3243  {
3244  if (!(Flags & PH_SHELL_EXECUTE_PUMP_MESSAGES))
3245  {
3246  LARGE_INTEGER timeout;
3247 
3248  NtWaitForSingleObject(info.hProcess, FALSE, PhTimeoutFromMilliseconds(&timeout, Timeout));
3249  }
3250  else
3251  {
3252  PhWaitForMultipleObjectsAndPump(NULL, 1, &info.hProcess, Timeout);
3253  }
3254  }
3255 
3256  if (ProcessHandle)
3257  *ProcessHandle = info.hProcess;
3258  else
3259  NtClose(info.hProcess);
3260 
3261  return TRUE;
3262  }
3263  else
3264  {
3265  return FALSE;
3266  }
3267 }
3268 
3276  _In_ HWND hWnd,
3277  _In_ PWSTR FileName
3278  )
3279 {
3281  {
3282  LPITEMIDLIST item;
3283  SFGAOF attributes;
3284 
3285  if (SUCCEEDED(SHParseDisplayName_I(FileName, NULL, &item, 0, &attributes)))
3286  {
3287  SHOpenFolderAndSelectItems_I(item, 0, NULL, 0);
3288  CoTaskMemFree(item);
3289  }
3290  else
3291  {
3292  PhShowError(hWnd, L"The location \"%s\" could not be found.", FileName);
3293  }
3294  }
3295  else
3296  {
3297  PPH_STRING selectFileName;
3298 
3299  selectFileName = PhConcatStrings2(L"/select,", FileName);
3300  PhShellExecute(hWnd, L"explorer.exe", selectFileName->Buffer);
3301  PhDereferenceObject(selectFileName);
3302  }
3303 }
3304 
3312  _In_ HWND hWnd,
3313  _In_ PWSTR FileName
3314  )
3315 {
3316  SHELLEXECUTEINFO info = { sizeof(info) };
3317 
3318  info.lpFile = FileName;
3319  info.nShow = SW_SHOW;
3320  info.fMask = SEE_MASK_INVOKEIDLIST;
3321  info.lpVerb = L"properties";
3322  info.hwnd = hWnd;
3323 
3324  if (!ShellExecuteEx(&info))
3325  {
3326  // It already displays error messages by itself.
3327  //PhShowStatus(hWnd, L"Unable to execute the program", 0, GetLastError());
3328  }
3329 }
3330 
3339  _In_ PPH_STRING KeyName,
3340  _In_ BOOLEAN Computer
3341  )
3342 {
3343  PPH_STRING keyName;
3344  PPH_STRING tempString;
3345 
3346  if (PhStartsWithString2(KeyName, L"HKCU", TRUE))
3347  {
3348  keyName = PhConcatStrings2(L"HKEY_CURRENT_USER", &KeyName->Buffer[4]);
3349  }
3350  else if (PhStartsWithString2(KeyName, L"HKU", TRUE))
3351  {
3352  keyName = PhConcatStrings2(L"HKEY_USERS", &KeyName->Buffer[3]);
3353  }
3354  else if (PhStartsWithString2(KeyName, L"HKCR", TRUE))
3355  {
3356  keyName = PhConcatStrings2(L"HKEY_CLASSES_ROOT", &KeyName->Buffer[4]);
3357  }
3358  else if (PhStartsWithString2(KeyName, L"HKLM", TRUE))
3359  {
3360  keyName = PhConcatStrings2(L"HKEY_LOCAL_MACHINE", &KeyName->Buffer[4]);
3361  }
3362  else
3363  {
3364  PhSetReference(&keyName, KeyName);
3365  }
3366 
3367  if (Computer)
3368  {
3370  tempString = PhConcatStrings2(L"Computer\\", keyName->Buffer);
3371  else
3372  tempString = PhConcatStrings2(L"My Computer\\", keyName->Buffer);
3373 
3374  PhDereferenceObject(keyName);
3375  keyName = tempString;
3376  }
3377 
3378  return keyName;
3379 }
3380 
3388  _In_ HWND hWnd,
3389  _In_ PPH_STRING KeyName
3390  )
3391 {
3392  static PH_STRINGREF regeditKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows\\CurrentVersion\\Applets\\Regedit");
3393 
3394  PPH_STRING lastKey;
3395  HANDLE regeditKeyHandle;
3396  UNICODE_STRING valueName;
3397  PPH_STRING regeditFileName;
3398 
3399  if (!NT_SUCCESS(PhCreateKey(
3400  &regeditKeyHandle,
3401  KEY_WRITE,
3403  &regeditKeyName,
3404  0,
3405  0,
3406  NULL
3407  )))
3408  return;
3409 
3410  RtlInitUnicodeString(&valueName, L"LastKey");
3411  lastKey = PhExpandKeyName(KeyName, TRUE);
3412  NtSetValueKey(regeditKeyHandle, &valueName, 0, REG_SZ, lastKey->Buffer, (ULONG)lastKey->Length + 2);
3413  PhDereferenceObject(lastKey);
3414 
3415  NtClose(regeditKeyHandle);
3416 
3417  // Start regedit.
3418  // If we aren't elevated, request that regedit be elevated.
3419  // This is so we can get the consent dialog in the center of
3420  // the specified window.
3421 
3422  regeditFileName = PhGetKnownLocation(CSIDL_WINDOWS, L"\\regedit.exe");
3423 
3424  if (!regeditFileName)
3425  regeditFileName = PhCreateString(L"regedit.exe");
3426 
3427  if (!PhElevated)
3428  {
3429  PhShellExecuteEx(hWnd, regeditFileName->Buffer, L"", SW_NORMAL, PH_SHELL_EXECUTE_ADMIN, 0, NULL);
3430  }
3431  else
3432  {
3433  PhShellExecute(hWnd, regeditFileName->Buffer, L"");
3434  }
3435 
3436  PhDereferenceObject(regeditFileName);
3437 }
3438 
3451  _In_ HANDLE KeyHandle,
3452  _In_opt_ PWSTR ValueName
3453  )
3454 {
3455  NTSTATUS status;
3456  UNICODE_STRING valueName;
3458  ULONG bufferSize;
3459  ULONG attempts = 16;
3460 
3461  RtlInitUnicodeString(&valueName, ValueName);
3462 
3463  bufferSize = 0x100;
3464  buffer = PhAllocate(bufferSize);
3465 
3466  do
3467  {
3468  status = NtQueryValueKey(
3469  KeyHandle,
3470  &valueName,
3472  buffer,
3473  bufferSize,
3474  &bufferSize
3475  );
3476 
3477  if (NT_SUCCESS(status))
3478  break;
3479 
3480  if (status == STATUS_BUFFER_OVERFLOW)
3481  {
3482  PhFree(buffer);
3483  buffer = PhAllocate(bufferSize);
3484  }
3485  else
3486  {
3487  PhFree(buffer);
3488  return NULL;
3489  }
3490  } while (--attempts);
3491 
3492  return buffer;
3493 }
3494 
3507  _In_ HANDLE KeyHandle,
3508  _In_opt_ PWSTR ValueName
3509  )
3510 {
3511  PPH_STRING string = NULL;
3513 
3514  buffer = PhQueryRegistryValue(KeyHandle, ValueName);
3515 
3516  if (buffer)
3517  {
3518  if (
3519  buffer->Type == REG_SZ ||
3520  buffer->Type == REG_MULTI_SZ ||
3521  buffer->Type == REG_EXPAND_SZ
3522  )
3523  {
3524  if (buffer->DataLength >= sizeof(WCHAR))
3525  string = PhCreateStringEx((PWCHAR)buffer->Data, buffer->DataLength - sizeof(WCHAR));
3526  else
3527  string = PhReferenceEmptyString();
3528  }
3529 
3530  PhFree(buffer);
3531  }
3532 
3533  return string;
3534 }
3535 
3537  _Inout_ PULONG Value2,
3538  _In_ ULONG Value1,
3539  _In_ const PH_FLAG_MAPPING *Mappings,
3540  _In_ ULONG NumberOfMappings
3541  )
3542 {
3543  ULONG i;
3544  ULONG value2;
3545 
3546  value2 = *Value2;
3547 
3548  if (value2 != 0)
3549  {
3550  // There are existing flags. Map the flags
3551  // we know about by clearing/setting them. The flags
3552  // we don't know about won't be affected.
3553 
3554  for (i = 0; i < NumberOfMappings; i++)
3555  {
3556  if (Value1 & Mappings[i].Flag1)
3557  value2 |= Mappings[i].Flag2;
3558  else
3559  value2 &= ~Mappings[i].Flag2;
3560  }
3561  }
3562  else
3563  {
3564  // There are no existing flags, which means
3565  // we can build the value from scratch, with no
3566  // clearing needed.
3567 
3568  for (i = 0; i < NumberOfMappings; i++)
3569  {
3570  if (Value1 & Mappings[i].Flag1)
3571  value2 |= Mappings[i].Flag2;
3572  }
3573  }
3574 
3575  *Value2 = value2;
3576 }
3577 
3579  _Inout_ PULONG Value1,
3580  _In_ ULONG Value2,
3581  _In_ const PH_FLAG_MAPPING *Mappings,
3582  _In_ ULONG NumberOfMappings
3583  )
3584 {
3585  ULONG i;
3586  ULONG value1;
3587 
3588  value1 = *Value1;
3589 
3590  if (value1 != 0)
3591  {
3592  for (i = 0; i < NumberOfMappings; i++)
3593  {
3594  if (Value2 & Mappings[i].Flag2)
3595  value1 |= Mappings[i].Flag1;
3596  else
3597  value1 &= ~Mappings[i].Flag1;
3598  }
3599  }
3600  else
3601  {
3602  for (i = 0; i < NumberOfMappings; i++)
3603  {
3604  if (Value2 & Mappings[i].Flag2)
3605  value1 |= Mappings[i].Flag1;
3606  }
3607  }
3608 
3609  *Value1 = value1;
3610 }
3611 
3612 UINT_PTR CALLBACK PhpOpenFileNameHookProc(
3613  _In_ HWND hdlg,
3614  _In_ UINT uiMsg,
3615  _In_ WPARAM wParam,
3616  _In_ LPARAM lParam
3617  )
3618 {
3619  switch (uiMsg)
3620  {
3621  case WM_NOTIFY:
3622  {
3623  LPOFNOTIFY header = (LPOFNOTIFY)lParam;
3624 
3625  // We can't use CDN_FILEOK because it's not sent if the buffer is too small,
3626  // defeating the entire purpose of this callback function.
3627 
3628  switch (header->hdr.code)
3629  {
3630  case CDN_SELCHANGE:
3631  {
3632  ULONG returnLength;
3633 
3634  returnLength = CommDlg_OpenSave_GetFilePath(
3635  header->hdr.hwndFrom,
3636  header->lpOFN->lpstrFile,
3637  header->lpOFN->nMaxFile
3638  );
3639 
3640  if ((LONG)returnLength > 0 && returnLength > header->lpOFN->nMaxFile)
3641  {
3642  PhFree(header->lpOFN->lpstrFile);
3643  header->lpOFN->nMaxFile = returnLength + 0x200; // pre-allocate some more
3644  header->lpOFN->lpstrFile = PhAllocate(header->lpOFN->nMaxFile * 2);
3645 
3646  returnLength = CommDlg_OpenSave_GetFilePath(
3647  header->hdr.hwndFrom,
3648  header->lpOFN->lpstrFile,
3649  header->lpOFN->nMaxFile
3650  );
3651  }
3652  }
3653  break;
3654  }
3655  }
3656  break;
3657  }
3658 
3659  return FALSE;
3660 }
3661 
3663  VOID
3664  )
3665 {
3666  OPENFILENAME *ofn;
3667 
3668  ofn = PhAllocate(sizeof(OPENFILENAME));
3669  memset(ofn, 0, sizeof(OPENFILENAME));
3670 
3671  ofn->lStructSize = sizeof(OPENFILENAME);
3672  ofn->nMaxFile = 0x400;
3673  ofn->lpstrFile = PhAllocate(ofn->nMaxFile * 2);
3674  ofn->lpstrFileTitle = NULL;
3675  ofn->Flags = OFN_ENABLEHOOK | OFN_EXPLORER;
3676  ofn->lpfnHook = PhpOpenFileNameHookProc;
3677 
3678  ofn->lpstrFile[0] = 0;
3679 
3680  return ofn;
3681 }
3682 
3684  _In_ OPENFILENAME *OpenFileName
3685  )
3686 {
3687  if (OpenFileName->lpstrFilter) PhFree((PVOID)OpenFileName->lpstrFilter);
3688  if (OpenFileName->lpstrFile) PhFree((PVOID)OpenFileName->lpstrFile);
3689 
3690  PhFree(OpenFileName);
3691 }
3692 
3693 typedef struct _PHP_FILE_DIALOG
3694 {
3695  BOOLEAN UseIFileDialog;
3696  BOOLEAN Save;
3697  union
3698  {
3699  OPENFILENAME *OpenFileName;
3700  IFileDialog *FileDialog;
3701  } u;
3703 
3704 PPHP_FILE_DIALOG PhpCreateFileDialog(
3705  _In_ BOOLEAN Save,
3706  _In_opt_ OPENFILENAME *OpenFileName,
3707  _In_opt_ IFileDialog *FileDialog
3708  )
3709 {
3710  PPHP_FILE_DIALOG fileDialog;
3711 
3712  assert(!!OpenFileName != !!FileDialog);
3713  fileDialog = PhAllocate(sizeof(PHP_FILE_DIALOG));
3714  fileDialog->Save = Save;
3715 
3716  if (OpenFileName)
3717  {
3718  fileDialog->UseIFileDialog = FALSE;
3719  fileDialog->u.OpenFileName = OpenFileName;
3720  }
3721  else if (FileDialog)
3722  {
3723  fileDialog->UseIFileDialog = TRUE;
3724  fileDialog->u.FileDialog = FileDialog;
3725  }
3726  else
3727  {
3728  PhRaiseStatus(STATUS_INVALID_PARAMETER);
3729  }
3730 
3731  return fileDialog;
3732 }
3733 
3743  VOID
3744  )
3745 {
3746  OPENFILENAME *ofn;
3747  PVOID ofnFileDialog;
3748 
3749  if (PHP_USE_IFILEDIALOG)
3750  {
3751  IFileDialog *fileDialog;
3752 
3753  if (SUCCEEDED(CoCreateInstance(
3754  &CLSID_FileOpenDialog,
3755  NULL,
3756  CLSCTX_INPROC_SERVER,
3757  &IID_IFileDialog,
3758  &fileDialog
3759  )))
3760  {
3761  // The default options are fine.
3762  return PhpCreateFileDialog(FALSE, NULL, fileDialog);
3763  }
3764  }
3765 
3766  ofn = PhpCreateOpenFileName();
3767  ofnFileDialog = PhpCreateFileDialog(FALSE, ofn, NULL);
3769 
3770  return ofnFileDialog;
3771 }
3772 
3782  VOID
3783  )
3784 {
3785  OPENFILENAME *ofn;
3786  PVOID ofnFileDialog;
3787 
3788  if (PHP_USE_IFILEDIALOG)
3789  {
3790  IFileDialog *fileDialog;
3791 
3792  if (SUCCEEDED(CoCreateInstance(
3793  &CLSID_FileSaveDialog,
3794  NULL,
3795  CLSCTX_INPROC_SERVER,
3796  &IID_IFileDialog,
3797  &fileDialog
3798  )))
3799  {
3800  // The default options are fine.
3801  return PhpCreateFileDialog(TRUE, NULL, fileDialog);
3802  }
3803  }
3804 
3805  ofn = PhpCreateOpenFileName();
3806  ofnFileDialog = PhpCreateFileDialog(TRUE, ofn, NULL);
3808 
3809  return ofnFileDialog;
3810 }
3811 
3818  _In_ PVOID FileDialog
3819  )
3820 {
3821  PPHP_FILE_DIALOG fileDialog = FileDialog;
3822 
3823  if (fileDialog->UseIFileDialog)
3824  {
3825  IFileDialog_Release(fileDialog->u.FileDialog);
3826  }
3827  else
3828  {
3829  PhpFreeOpenFileName(fileDialog->u.OpenFileName);
3830  }
3831 
3832  PhFree(fileDialog);
3833 }
3834 
3846  _In_ HWND hWnd,
3847  _In_ PVOID FileDialog
3848  )
3849 {
3850  PPHP_FILE_DIALOG fileDialog = FileDialog;
3851 
3852  if (fileDialog->UseIFileDialog)
3853  {
3854  // Set a blank default extension. This will have an effect when the user
3855  // selects a different file type.
3856  IFileDialog_SetDefaultExtension(fileDialog->u.FileDialog, L"");
3857 
3858  return SUCCEEDED(IFileDialog_Show(fileDialog->u.FileDialog, hWnd));
3859  }
3860  else
3861  {
3862  OPENFILENAME *ofn = fileDialog->u.OpenFileName;
3863 
3864  ofn->hwndOwner = hWnd;
3865 
3866  // Determine whether the structure represents a open or save dialog and call the appropriate function.
3867  if (!fileDialog->Save)
3868  {
3869  return GetOpenFileName(ofn);
3870  }
3871  else
3872  {
3873  return GetSaveFileName(ofn);
3874  }
3875  }
3876 }
3877 
3878 static const PH_FLAG_MAPPING PhpFileDialogIfdMappings[] =
3879 {
3880  { PH_FILEDIALOG_CREATEPROMPT, FOS_CREATEPROMPT },
3881  { PH_FILEDIALOG_PATHMUSTEXIST, FOS_PATHMUSTEXIST },
3882  { PH_FILEDIALOG_FILEMUSTEXIST, FOS_FILEMUSTEXIST },
3883  { PH_FILEDIALOG_SHOWHIDDEN, FOS_FORCESHOWHIDDEN },
3884  { PH_FILEDIALOG_NODEREFERENCELINKS, FOS_NODEREFERENCELINKS },
3885  { PH_FILEDIALOG_OVERWRITEPROMPT, FOS_OVERWRITEPROMPT },
3886  { PH_FILEDIALOG_DEFAULTEXPANDED, FOS_DEFAULTNOMINIMODE },
3887  { PH_FILEDIALOG_STRICTFILETYPES, FOS_STRICTFILETYPES },
3888  { PH_FILEDIALOG_PICKFOLDERS, FOS_PICKFOLDERS }
3889 };
3890 
3891 static const PH_FLAG_MAPPING PhpFileDialogOfnMappings[] =
3892 {
3893  { PH_FILEDIALOG_CREATEPROMPT, OFN_CREATEPROMPT },
3894  { PH_FILEDIALOG_PATHMUSTEXIST, OFN_PATHMUSTEXIST },
3895  { PH_FILEDIALOG_FILEMUSTEXIST, OFN_FILEMUSTEXIST },
3896  { PH_FILEDIALOG_SHOWHIDDEN, OFN_FORCESHOWHIDDEN },
3897  { PH_FILEDIALOG_NODEREFERENCELINKS, OFN_NODEREFERENCELINKS },
3898  { PH_FILEDIALOG_OVERWRITEPROMPT, OFN_OVERWRITEPROMPT }
3899 };
3900 
3910  _In_ PVOID FileDialog
3911  )
3912 {
3913  PPHP_FILE_DIALOG fileDialog = FileDialog;
3914 
3915  if (fileDialog->UseIFileDialog)
3916  {
3917  FILEOPENDIALOGOPTIONS dialogOptions;
3918  ULONG options;
3919 
3920  if (SUCCEEDED(IFileDialog_GetOptions(fileDialog->u.FileDialog, &dialogOptions)))
3921  {
3922  options = 0;
3923 
3924  PhMapFlags2(
3925  &options,
3926  dialogOptions,
3927  PhpFileDialogIfdMappings,
3928  sizeof(PhpFileDialogIfdMappings) / sizeof(PH_FLAG_MAPPING)
3929  );
3930 
3931  return options;
3932  }
3933  else
3934  {
3935  return 0;
3936  }
3937  }
3938  else
3939  {
3940  OPENFILENAME *ofn = fileDialog->u.OpenFileName;
3941  ULONG options;
3942 
3943  options = 0;
3944 
3945  PhMapFlags2(
3946  &options,
3947  ofn->Flags,
3948  PhpFileDialogOfnMappings,
3949  sizeof(PhpFileDialogOfnMappings) / sizeof(PH_FLAG_MAPPING)
3950  );
3951 
3952  return options;
3953  }
3954 }
3955 
3980  _In_ PVOID FileDialog,
3981  _In_ ULONG Options
3982  )
3983 {
3984  PPHP_FILE_DIALOG fileDialog = FileDialog;
3985 
3986  if (fileDialog->UseIFileDialog)
3987  {
3988  FILEOPENDIALOGOPTIONS dialogOptions;
3989 
3990  if (SUCCEEDED(IFileDialog_GetOptions(fileDialog->u.FileDialog, &dialogOptions)))
3991  {
3992  PhMapFlags1(
3993  &dialogOptions,
3994  Options,
3995  PhpFileDialogIfdMappings,
3996  sizeof(PhpFileDialogIfdMappings) / sizeof(PH_FLAG_MAPPING)
3997  );
3998 
3999  IFileDialog_SetOptions(fileDialog->u.FileDialog, dialogOptions);
4000  }
4001  }
4002  else
4003  {
4004  OPENFILENAME *ofn = fileDialog->u.OpenFileName;
4005 
4006  PhMapFlags1(
4007  &ofn->Flags,
4008  Options,
4009  PhpFileDialogOfnMappings,
4010  sizeof(PhpFileDialogOfnMappings) / sizeof(PH_FLAG_MAPPING)
4011  );
4012  }
4013 }
4014 
4025  _In_ PVOID FileDialog
4026  )
4027 {
4028  PPHP_FILE_DIALOG fileDialog = FileDialog;
4029 
4030  if (fileDialog->UseIFileDialog)
4031  {
4032  ULONG index;
4033 
4034  if (SUCCEEDED(IFileDialog_GetFileTypeIndex(fileDialog->u.FileDialog, &index)))
4035  {
4036  return index;
4037  }
4038  else
4039  {
4040  return 0;
4041  }
4042  }
4043  else
4044  {
4045  OPENFILENAME *ofn = fileDialog->u.OpenFileName;
4046 
4047  return ofn->nFilterIndex;
4048  }
4049 }
4050 
4060  _In_ PVOID FileDialog,
4061  _In_ PPH_FILETYPE_FILTER Filters,
4062  _In_ ULONG NumberOfFilters
4063  )
4064 {
4065  PPHP_FILE_DIALOG fileDialog = FileDialog;
4066 
4067  if (fileDialog->UseIFileDialog)
4068  {
4069  IFileDialog_SetFileTypes(
4070  fileDialog->u.FileDialog,
4071  NumberOfFilters,
4072  (COMDLG_FILTERSPEC *)Filters
4073  );
4074  }
4075  else
4076  {
4077  OPENFILENAME *ofn = fileDialog->u.OpenFileName;
4078  PPH_STRING filterString;
4079  PH_STRING_BUILDER filterBuilder;
4080  ULONG i;
4081 
4082  PhInitializeStringBuilder(&filterBuilder, 10);
4083 
4084  for (i = 0; i < NumberOfFilters; i++)
4085  {
4086  PhAppendStringBuilder2(&filterBuilder, Filters[i].Name);
4087  PhAppendCharStringBuilder(&filterBuilder, 0);
4088  PhAppendStringBuilder2(&filterBuilder, Filters[i].Filter);
4089  PhAppendCharStringBuilder(&filterBuilder, 0);
4090  }
4091 
4092  filterString = PhFinalStringBuilderString(&filterBuilder);
4093 
4094  if (ofn->lpstrFilter)
4095  PhFree((PVOID)ofn->lpstrFilter);
4096 
4097  ofn->lpstrFilter = PhAllocateCopy(filterString->Buffer, filterString->Length + 2);
4098  PhDereferenceObject(filterString);
4099  }
4100 }
4101 
4113  _In_ PVOID FileDialog
4114  )
4115 {
4116  PPHP_FILE_DIALOG fileDialog = FileDialog;
4117 
4118  if (fileDialog->UseIFileDialog)
4119  {
4120  IShellItem *result;
4121  PPH_STRING fileName = NULL;
4122 
4123  if (SUCCEEDED(IFileDialog_GetResult(fileDialog->u.FileDialog, &result)))
4124  {
4125  PWSTR name;
4126 
4127  if (SUCCEEDED(IShellItem_GetDisplayName(result, SIGDN_FILESYSPATH, &name)))
4128  {
4129  fileName = PhCreateString(name);
4130  CoTaskMemFree(name);
4131  }
4132 
4133  IShellItem_Release(result);
4134  }
4135 
4136  if (!fileName)
4137  {
4138  PWSTR name;
4139 
4140  if (SUCCEEDED(IFileDialog_GetFileName(fileDialog->u.FileDialog, &name)))
4141  {
4142  fileName = PhCreateString(name);
4143  CoTaskMemFree(name);
4144  }
4145  }
4146 
4147  return fileName;
4148  }
4149  else
4150  {
4151  return PhCreateString(fileDialog->u.OpenFileName->lpstrFile);
4152  }
4153 }
4154 
4162  _In_ PVOID FileDialog,
4163  _In_ PWSTR FileName
4164  )
4165 {
4166  PPHP_FILE_DIALOG fileDialog = FileDialog;
4167  PH_STRINGREF fileName;
4168 
4169  PhInitializeStringRefLongHint(&fileName, FileName);
4170 
4171  if (fileDialog->UseIFileDialog)
4172  {
4173  IShellItem *shellItem = NULL;
4174  PH_STRINGREF pathNamePart;
4175  PH_STRINGREF baseNamePart;
4176 
4177  if (PhSplitStringRefAtLastChar(&fileName, '\\', &pathNamePart, &baseNamePart) &&
4179  {
4180  LPITEMIDLIST item;
4181  SFGAOF attributes;
4182  PPH_STRING pathName;
4183 
4184  pathName = PhCreateString2(&pathNamePart);
4185 
4186  if (SUCCEEDED(SHParseDisplayName_I(pathName->Buffer, NULL, &item, 0, &attributes)))
4187  {
4188  SHCreateShellItem_I(NULL, NULL, item, &shellItem);
4189  CoTaskMemFree(item);
4190  }
4191 
4192  PhDereferenceObject(pathName);
4193  }
4194 
4195  if (shellItem)
4196  {
4197  IFileDialog_SetFolder(fileDialog->u.FileDialog, shellItem);
4198  IFileDialog_SetFileName(fileDialog->u.FileDialog, baseNamePart.Buffer);
4199  IShellItem_Release(shellItem);
4200  }
4201  else
4202  {
4203  IFileDialog_SetFileName(fileDialog->u.FileDialog, FileName);
4204  }
4205  }
4206  else
4207  {
4208  OPENFILENAME *ofn = fileDialog->u.OpenFileName;
4209 
4210  if (PhFindCharInStringRef(&fileName, '/', FALSE) != -1 || PhFindCharInStringRef(&fileName, '\"', FALSE) != -1)
4211  {
4212  // It refuses to take any filenames with a slash or quotation mark.
4213  return;
4214  }
4215 
4216  PhFree(ofn->lpstrFile);
4217 
4218  ofn->nMaxFile = (ULONG)max(fileName.Length / sizeof(WCHAR) + 1, 0x400);
4219  ofn->lpstrFile = PhAllocate(ofn->nMaxFile * 2);
4220  memcpy(ofn->lpstrFile, fileName.Buffer, fileName.Length + sizeof(WCHAR));
4221  }
4222 }
4223 
4236  _In_ PWSTR FileName,
4237  _Out_ PBOOLEAN IsPacked,
4238  _Out_opt_ PULONG NumberOfModules,
4239  _Out_opt_ PULONG NumberOfFunctions
4240  )
4241 {
4242  // An image is packed if:
4243  //
4244  // 1. It references fewer than 3 modules, and
4245  // 2. It imports fewer than 5 functions, and
4246  // 3. It does not use the Native subsystem.
4247  //
4248  // Or:
4249  //
4250  // 1. The function-to-module ratio is lower than 3
4251  // (on average fewer than 3 functions are imported
4252  // from each module), and
4253  // 2. It references more than 2 modules but fewer than
4254  // 6 modules.
4255  //
4256  // Or:
4257  //
4258  // 1. The function-to-module ratio is lower than 2
4259  // (on average fewer than 2 functions are imported
4260  // from each module), and
4261  // 2. It references more than 5 modules but fewer than
4262  // 31 modules.
4263  //
4264  // Or:
4265  //
4266  // 1. It does not have a section named ".text".
4267  //
4268  // An image is not considered to be packed if it has only
4269  // one import from a module named "mscoree.dll".
4270 
4271  NTSTATUS status;
4272  PH_MAPPED_IMAGE mappedImage;
4273  PH_MAPPED_IMAGE_IMPORTS imports;
4274  PH_MAPPED_IMAGE_IMPORT_DLL importDll;
4275  ULONG i;
4276  //ULONG limitNumberOfSections;
4277  ULONG limitNumberOfModules;
4278  ULONG numberOfModules;
4279  ULONG numberOfFunctions = 0;
4280  BOOLEAN hasTextSection = FALSE;
4281  BOOLEAN isModuleMscoree = FALSE;
4282  BOOLEAN isPacked;
4283 
4284  status = PhLoadMappedImage(
4285  FileName,
4286  NULL,
4287  TRUE,
4288  &mappedImage
4289  );
4290 
4291  if (!NT_SUCCESS(status))
4292  return status;
4293 
4294  // Go through the sections and look for the ".text" section.
4295 
4296  // This rule is currently disabled.
4297  hasTextSection = TRUE;
4298 
4299  //limitNumberOfSections = min(mappedImage.NumberOfSections, 64);
4300 
4301  //for (i = 0; i < limitNumberOfSections; i++)
4302  //{
4303  // CHAR sectionName[IMAGE_SIZEOF_SHORT_NAME + 1];
4304 
4305  // if (PhGetMappedImageSectionName(
4306  // &mappedImage.Sections[i],
4307  // sectionName,
4308  // IMAGE_SIZEOF_SHORT_NAME + 1,
4309  // NULL
4310  // ))
4311  // {
4312  // if (STR_IEQUAL(sectionName, ".text"))
4313  // {
4314  // hasTextSection = TRUE;
4315  // break;
4316  // }
4317  // }
4318  //}
4319 
4320  status = PhGetMappedImageImports(
4321  &imports,
4322  &mappedImage
4323  );
4324 
4325  if (!NT_SUCCESS(status))
4326  goto CleanupExit;
4327 
4328  // Get the module and function totals.
4329 
4330  numberOfModules = imports.NumberOfDlls;
4331  limitNumberOfModules = min(numberOfModules, 64);
4332 
4333  for (i = 0; i < limitNumberOfModules; i++)
4334  {
4335  if (!NT_SUCCESS(status = PhGetMappedImageImportDll(
4336  &imports,
4337  i,
4338  &importDll
4339  )))
4340  goto CleanupExit;
4341 
4342  if (PhEqualBytesZ(importDll.Name, "mscoree.dll", TRUE))
4343  isModuleMscoree = TRUE;
4344 
4345  numberOfFunctions += importDll.NumberOfEntries;
4346  }
4347 
4348  // Determine if the image is packed.
4349 
4350  if (
4351  numberOfModules != 0 &&
4352  (
4353  // Rule 1
4354  (numberOfModules < 3 && numberOfFunctions < 5 &&
4355  mappedImage.NtHeaders->OptionalHeader.Subsystem != IMAGE_SUBSYSTEM_NATIVE) ||
4356  // Rule 2
4357  ((numberOfFunctions / numberOfModules) < 3 &&
4358  numberOfModules > 2 && numberOfModules < 5) ||
4359  // Rule 3
4360  ((numberOfFunctions / numberOfModules) < 2 &&
4361  numberOfModules > 4 && numberOfModules < 31) ||
4362  // Rule 4
4363  !hasTextSection
4364  ) &&
4365  // Additional .NET rule
4366  !(numberOfModules == 1 && numberOfFunctions == 1 && isModuleMscoree)
4367  )
4368  {
4369  isPacked = TRUE;
4370  }
4371  else
4372  {
4373  isPacked = FALSE;
4374  }
4375 
4376  *IsPacked = isPacked;
4377 
4378  if (NumberOfModules)
4379  *NumberOfModules = numberOfModules;
4380  if (NumberOfFunctions)
4381  *NumberOfFunctions = numberOfFunctions;
4382 
4383 CleanupExit:
4384  PhUnloadMappedImage(&mappedImage);
4385 
4386  return status;
4387 }
4388 
4389 ULONG PhCrc32(
4390  _In_ ULONG Crc,
4391  _In_reads_(Length) PCHAR Buffer,
4392  _In_ SIZE_T Length
4393  )
4394 {
4395  Crc ^= 0xffffffff;
4396 
4397  while (Length--)
4398  Crc = (Crc >> 8) ^ PhCrc32Table[(Crc ^ *Buffer++) & 0xff];
4399 
4400  return Crc ^ 0xffffffff;
4401 }
4402 
4403 C_ASSERT(RTL_FIELD_SIZE(PH_HASH_CONTEXT, Context) >= sizeof(MD5_CTX));
4404 C_ASSERT(RTL_FIELD_SIZE(PH_HASH_CONTEXT, Context) >= sizeof(A_SHA_CTX));
4405 
4416  _Out_ PPH_HASH_CONTEXT Context,
4417  _In_ PH_HASH_ALGORITHM Algorithm
4418  )
4419 {
4420  Context->Algorithm = Algorithm;
4421 
4422  switch (Algorithm)
4423  {
4424  case Md5HashAlgorithm:
4425  MD5Init((MD5_CTX *)Context->Context);
4426  break;
4427  case Sha1HashAlgorithm:
4428  A_SHAInit((A_SHA_CTX *)Context->Context);
4429  break;
4430  case Crc32HashAlgorithm:
4431  Context->Context[0] = 0;
4432  break;
4433  default:
4434  PhRaiseStatus(STATUS_INVALID_PARAMETER_2);
4435  break;
4436  }
4437 }
4438 
4447  _Inout_ PPH_HASH_CONTEXT Context,
4448  _In_reads_bytes_(Length) PVOID Buffer,
4449  _In_ ULONG Length
4450  )
4451 {
4452  switch (Context->Algorithm)
4453  {
4454  case Md5HashAlgorithm:
4455  MD5Update((MD5_CTX *)Context->Context, (PUCHAR)Buffer, Length);
4456  break;
4457  case Sha1HashAlgorithm:
4458  A_SHAUpdate((A_SHA_CTX *)Context->Context, (PUCHAR)Buffer, Length);
4459  break;
4460  case Crc32HashAlgorithm:
4461  Context->Context[0] = PhCrc32(Context->Context[0], (PUCHAR)Buffer, Length);
4462  break;
4463  default:
4464  PhRaiseStatus(STATUS_INVALID_PARAMETER);
4465  }
4466 }
4467 
4477 BOOLEAN PhFinalHash(
4478  _Inout_ PPH_HASH_CONTEXT Context,
4479  _Out_writes_bytes_(HashLength) PVOID Hash,
4480  _In_ ULONG HashLength,
4481  _Out_opt_ PULONG ReturnLength
4482  )
4483 {
4484  BOOLEAN result;
4485  ULONG returnLength;
4486 
4487  result = FALSE;
4488 
4489  switch (Context->Algorithm)
4490  {
4491  case Md5HashAlgorithm:
4492  if (HashLength >= 16)
4493  {
4494  MD5Final((MD5_CTX *)Context->Context);
4495  memcpy(Hash, ((MD5_CTX *)Context->Context)->digest, 16);
4496  result = TRUE;
4497  }
4498 
4499  returnLength = 16;
4500 
4501  break;
4502  case Sha1HashAlgorithm:
4503  if (HashLength >= 20)
4504  {
4505  A_SHAFinal((A_SHA_CTX *)Context->Context, (PUCHAR)Hash);
4506  result = TRUE;
4507  }
4508 
4509  returnLength = 20;
4510 
4511  break;
4512  case Crc32HashAlgorithm:
4513  if (HashLength >= 4)
4514  {
4515  *(PULONG)Hash = Context->Context[0];
4516  result = TRUE;
4517  }
4518 
4519  returnLength = 4;
4520 
4521  break;
4522  default:
4523  PhRaiseStatus(STATUS_INVALID_PARAMETER);
4524  }
4525 
4526  if (ReturnLength)
4527  *ReturnLength = returnLength;
4528 
4529  return result;
4530 }
4531 
4542  _In_ PPH_STRINGREF CommandLine,
4543  _Inout_ PULONG_PTR Index
4544  )
4545 {
4546  PH_STRING_BUILDER stringBuilder;
4547  SIZE_T length;
4548  SIZE_T i;
4549 
4550  ULONG numberOfBackslashes;
4551  BOOLEAN inQuote;
4552  BOOLEAN endOfValue;
4553 
4554  length = CommandLine->Length / 2;
4555  i = *Index;
4556 
4557  // This function follows the rules used by CommandLineToArgvW:
4558  //
4559  // * 2n backslashes and a quotation mark produces n backslashes and a quotation mark (non-literal).
4560  // * 2n + 1 backslashes and a quotation mark produces n and a quotation mark (literal).
4561  // * n backslashes and no quotation mark produces n backslashes.
4562 
4563  PhInitializeStringBuilder(&stringBuilder, 10);
4564  numberOfBackslashes = 0;
4565  inQuote = FALSE;
4566  endOfValue = FALSE;
4567 
4568  for (; i < length; i++)
4569  {
4570  switch (CommandLine->Buffer[i])
4571  {
4572  case '\\':
4573  numberOfBackslashes++;
4574  break;
4575  case '\"':
4576  if (numberOfBackslashes != 0)
4577  {
4578  if (numberOfBackslashes & 1)
4579  {
4580  numberOfBackslashes /= 2;
4581 
4582  if (numberOfBackslashes != 0)
4583  {
4584  PhAppendCharStringBuilder2(&stringBuilder, '\\', numberOfBackslashes);
4585  numberOfBackslashes = 0;
4586  }
4587 
4588  PhAppendCharStringBuilder(&stringBuilder, '\"');
4589 
4590  break;
4591  }
4592  else
4593  {
4594  numberOfBackslashes /= 2;
4595  PhAppendCharStringBuilder2(&stringBuilder, '\\', numberOfBackslashes);
4596  numberOfBackslashes = 0;
4597  }
4598  }
4599 
4600  if (!inQuote)
4601  inQuote = TRUE;
4602  else
4603  inQuote = FALSE;
4604 
4605  break;
4606  default:
4607  if (numberOfBackslashes != 0)
4608  {
4609  PhAppendCharStringBuilder2(&stringBuilder, '\\', numberOfBackslashes);
4610  numberOfBackslashes = 0;
4611  }
4612 
4613  if (CommandLine->Buffer[i] == ' ' && !inQuote)
4614  {
4615  endOfValue = TRUE;
4616  }
4617  else
4618  {
4619  PhAppendCharStringBuilder(&stringBuilder, CommandLine->Buffer[i]);
4620  }
4621 
4622  break;
4623  }
4624 
4625  if (endOfValue)
4626  break;
4627  }
4628 
4629  *Index = i;
4630 
4631  return PhFinalStringBuilderString(&stringBuilder);
4632 }
4633 
4651  _In_ PPH_STRINGREF CommandLine,
4652  _In_opt_ PPH_COMMAND_LINE_OPTION Options,
4653  _In_ ULONG NumberOfOptions,
4654  _In_ ULONG Flags,
4655  _In_ PPH_COMMAND_LINE_CALLBACK Callback,
4656  _In_opt_ PVOID Context
4657  )
4658 {
4659  SIZE_T i;
4660  SIZE_T j;
4661  SIZE_T length;
4662  BOOLEAN cont;
4663  BOOLEAN wasFirst;
4664 
4665  PH_STRINGREF optionName;
4666  PPH_COMMAND_LINE_OPTION option = NULL;
4667  PPH_STRING optionValue;
4668 
4669  if (CommandLine->Length == 0)
4670  return TRUE;
4671 
4672  i = 0;
4673  length = CommandLine->Length / 2;
4674  wasFirst = TRUE;
4675 
4676  while (TRUE)
4677  {
4678  // Skip spaces.
4679  while (i < length && CommandLine->Buffer[i] == ' ')
4680  i++;
4681 
4682  if (i >= length)
4683  break;
4684 
4685  if (option &&
4686  (option->Type == MandatoryArgumentType ||
4687  (option->Type == OptionalArgumentType && CommandLine->Buffer[i] != '-')))
4688  {
4689  // Read the value and execute the callback function.
4690 
4691  optionValue = PhParseCommandLinePart(CommandLine, &i);
4692  cont = Callback(option, optionValue, Context);
4693  PhDereferenceObject(optionValue);
4694 
4695  if (!cont)
4696  break;
4697 
4698  option = NULL;
4699  }
4700  else if (CommandLine->Buffer[i] == '-')
4701  {
4702  ULONG_PTR originalIndex;
4703  SIZE_T optionNameLength;
4704 
4705  // Read the option (only alphanumeric characters allowed).
4706 
4707  // Skip the dash.
4708  i++;
4709 
4710  originalIndex = i;
4711 
4712  for (; i < length; i++)
4713  {
4714  if (!iswalnum(CommandLine->Buffer[i]) && CommandLine->Buffer[i] != '-')
4715  break;
4716  }
4717 
4718  optionNameLength = i - originalIndex;
4719 
4720  optionName.Buffer = &CommandLine->Buffer[originalIndex];
4721  optionName.Length = optionNameLength * 2;
4722 
4723  // Find the option descriptor.
4724 
4725  option = NULL;
4726 
4727  for (j = 0; j < NumberOfOptions; j++)
4728  {
4729  if (PhEqualStringRef2(&optionName, Options[j].Name, FALSE))
4730  {
4731  option = &Options[j];
4732  break;
4733  }
4734  }
4735 
4736  if (!option && !(Flags & PH_COMMAND_LINE_IGNORE_UNKNOWN_OPTIONS))
4737  return FALSE;
4738 
4739  if (option && option->Type == NoArgumentType)
4740  {
4741  cont = Callback(option, NULL, Context);
4742 
4743  if (!cont)
4744  break;
4745 
4746  option = NULL;
4747  }
4748 
4749  wasFirst = FALSE;
4750  }
4751  else
4752  {
4753  PPH_STRING value;
4754 
4755  value = PhParseCommandLinePart(CommandLine, &i);
4756 
4757  if ((Flags & PH_COMMAND_LINE_IGNORE_FIRST_PART) && wasFirst)
4758  {
4759  PhDereferenceObject(value);
4760  value = NULL;
4761  }
4762 
4763  if (value)
4764  {
4765  cont = Callback(NULL, value, Context);
4766  PhDereferenceObject(value);
4767 
4768  if (!cont)
4769  break;
4770  }
4771 
4772  wasFirst = FALSE;
4773  }
4774  }
4775 
4776  return TRUE;
4777 }
4778 
4789  _In_ PPH_STRINGREF String
4790  )
4791 {
4792  static PH_STRINGREF backslashAndQuote = PH_STRINGREF_INIT(L"\\\"");
4793 
4794  PH_STRING_BUILDER stringBuilder;
4795  ULONG length;
4796  ULONG i;
4797 
4798  ULONG numberOfBackslashes;
4799 
4800  length = (ULONG)String->Length / 2;
4801  PhInitializeStringBuilder(&stringBuilder, String->Length / 2 * 3);
4802  numberOfBackslashes = 0;
4803 
4804  // Simply replacing " with \" won't work here. See PhParseCommandLinePart
4805  // for the quoting rules.
4806 
4807  for (i = 0; i < length; i++)
4808  {
4809  switch (String->Buffer[i])
4810  {
4811  case '\\':
4812  numberOfBackslashes++;
4813  break;
4814  case '\"':
4815  if (numberOfBackslashes != 0)
4816  {
4817  PhAppendCharStringBuilder2(&stringBuilder, '\\', numberOfBackslashes * 2);
4818  numberOfBackslashes = 0;
4819  }
4820 
4821  PhAppendStringBuilder(&stringBuilder, &backslashAndQuote);
4822 
4823  break;
4824  default:
4825  if (numberOfBackslashes != 0)
4826  {
4827  PhAppendCharStringBuilder2(&stringBuilder, '\\', numberOfBackslashes);
4828  numberOfBackslashes = 0;
4829  }
4830 
4831  PhAppendCharStringBuilder(&stringBuilder, String->Buffer[i]);
4832 
4833  break;
4834  }
4835  }
4836 
4837  return PhFinalStringBuilderString(&stringBuilder);
4838 }
4839 
4841  _In_ PWSTR FileName,
4842  _In_opt_ PWSTR Extension,
4843  _Out_writes_(MAX_PATH) PWSTR Buffer
4844  )
4845 {
4846  NTSTATUS status;
4847  ULONG result;
4848  UNICODE_STRING fileName;
4849  OBJECT_ATTRIBUTES objectAttributes;
4850  FILE_BASIC_INFORMATION basicInfo;
4851 
4852  result = SearchPath(
4853  NULL,
4854  FileName,
4855  Extension,
4856  MAX_PATH,
4857  Buffer,
4858  NULL
4859  );
4860 
4861  if (result == 0 || result >= MAX_PATH)
4862  return FALSE;
4863 
4864  // Make sure this is not a directory.
4865 
4867  Buffer,
4868  &fileName,
4869  NULL,
4870  NULL
4871  )))
4872  return FALSE;
4873 
4875  &objectAttributes,
4876  &fileName,
4878  NULL,
4879  NULL
4880  );
4881 
4882  status = NtQueryAttributesFile(&objectAttributes, &basicInfo);
4883  RtlFreeHeap(RtlProcessHeap(), 0, fileName.Buffer);
4884 
4885  if (!NT_SUCCESS(status))
4886  return FALSE;
4887  if (basicInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
4888  return FALSE;
4889 
4890  return TRUE;
4891 }
4892 
4907  _In_ PPH_STRINGREF CommandLine,
4908  _Out_ PPH_STRINGREF FileName,
4909  _Out_ PPH_STRINGREF Arguments,
4910  _Out_opt_ PPH_STRING *FullFileName
4911  )
4912 {
4913  static PH_STRINGREF whitespace = PH_STRINGREF_INIT(L" \t");
4914 
4915  PH_STRINGREF commandLine;
4916  PH_STRINGREF temp;
4917  PH_STRINGREF currentPart;
4918  PH_STRINGREF remainingPart;
4919  WCHAR buffer[MAX_PATH];
4920  WCHAR originalChar;
4921 
4922  commandLine = *CommandLine;
4923  PhTrimStringRef(&commandLine, &whitespace, 0);
4924 
4925  if (commandLine.Length == 0)
4926  {
4927  PhInitializeEmptyStringRef(FileName);
4928  PhInitializeEmptyStringRef(Arguments);
4929 
4930  if (FullFileName)
4931  *FullFileName = NULL;
4932 
4933  return FALSE;
4934  }
4935 
4936  if (*commandLine.Buffer == '"')
4937  {
4938  PH_STRINGREF arguments;
4939 
4940  PhSkipStringRef(&commandLine, sizeof(WCHAR));
4941 
4942  // Find the matching quote character and we have our file name.
4943 
4944  if (!PhSplitStringRefAtChar(&commandLine, '"', &commandLine, &arguments))
4945  {
4946  PhSkipStringRef(&commandLine, -(LONG_PTR)sizeof(WCHAR)); // Unskip the initial quote character
4947  *FileName = commandLine;
4948  PhInitializeEmptyStringRef(Arguments);
4949 
4950  if (FullFileName)
4951  *FullFileName = NULL;
4952 
4953  return FALSE;
4954  }
4955 
4956  PhTrimStringRef(&arguments, &whitespace, PH_TRIM_START_ONLY);
4957  *FileName = commandLine;
4958  *Arguments = arguments;
4959 
4960  if (FullFileName)
4961  {
4962  PPH_STRING tempCommandLine;
4963 
4964  tempCommandLine = PhCreateString2(&commandLine);
4965 
4966  if (PhpSearchFilePath(tempCommandLine->Buffer, L".exe", buffer))
4967  {
4968  *FullFileName = PhCreateString(buffer);
4969  }
4970  else
4971  {
4972  *FullFileName = NULL;
4973  }
4974 
4975  PhDereferenceObject(tempCommandLine);
4976  }
4977 
4978  return TRUE;
4979  }
4980 
4981  // Try to find an existing executable file, starting with the first part of the
4982  // command line and successively restoring the rest of the command line.
4983  // For example, in "C:\Program Files\Internet Explorer\iexplore", we try
4984  // to match:
4985  // * "C:\Program"
4986  // * "C:\Program Files\Internet"
4987  // * "C:\Program Files\Internet "
4988  // * "C:\Program Files\Internet "
4989  // * "C:\Program Files\Internet Explorer\iexplore"
4990  //
4991  // Note that we do not trim whitespace in each part because filenames can contain
4992  // trailing whitespace before the extension (e.g. "Internet .exe").
4993 
4994  temp.Buffer = PhAllocate(commandLine.Length + sizeof(WCHAR));
4995  memcpy(temp.Buffer, commandLine.Buffer, commandLine.Length);
4996  temp.Buffer[commandLine.Length / sizeof(WCHAR)] = 0;
4997  temp.Length = commandLine.Length;
4998  remainingPart = temp;
4999 
5000  while (remainingPart.Length != 0)
5001  {
5002  BOOLEAN found;
5003  BOOLEAN result;
5004 
5005  found = PhSplitStringRefAtChar(&remainingPart, ' ', &currentPart, &remainingPart);
5006 
5007  if (found)
5008  {
5009  originalChar = *(remainingPart.Buffer - 1);
5010  *(remainingPart.Buffer - 1) = 0;
5011  }
5012 
5013  result = PhpSearchFilePath(temp.Buffer, L".exe", buffer);
5014 
5015  if (found)
5016  {
5017  *(remainingPart.Buffer - 1) = originalChar;
5018  }
5019 
5020  if (result)
5021  {
5022  FileName->Buffer = commandLine.Buffer;
5023  FileName->Length = ((PCHAR)currentPart.Buffer - (PCHAR)temp.Buffer) + currentPart.Length;
5024 
5025  PhTrimStringRef(&remainingPart, &whitespace, PH_TRIM_START_ONLY);
5026  *Arguments = remainingPart;
5027 
5028  if (FullFileName)
5029  *FullFileName = PhCreateString(buffer);
5030 
5031  PhFree(temp.Buffer);
5032 
5033  return TRUE;
5034  }
5035  }
5036 
5037  PhFree(temp.Buffer);
5038 
5039  *FileName = *CommandLine;
5040  PhInitializeEmptyStringRef(Arguments);
5041 
5042  if (FullFileName)
5043  *FullFileName = NULL;
5044 
5045  return FALSE;
5046 }