1""" 2Windows specific utility functions, this module should be imported in a try, 3except block because it is only applicable on Windows platforms. 4 5 6Much of what is here was adapted from the following: 7 8 https://stackoverflow.com/a/43233332 9 http://stackoverflow.com/questions/29566330 10""" 11 12import collections 13import ctypes 14import logging 15import os 16from ctypes import wintypes 17 18import ntsecuritycon 19import psutil 20import win32api 21import win32con 22import win32process 23import win32security 24import win32service 25 26# Set up logging 27log = logging.getLogger(__name__) 28 29ntdll = ctypes.WinDLL("ntdll") 30secur32 = ctypes.WinDLL("secur32") 31kernel32 = ctypes.WinDLL("kernel32", use_last_error=True) 32advapi32 = ctypes.WinDLL("advapi32", use_last_error=True) 33userenv = ctypes.WinDLL("userenv", use_last_error=True) 34 35SYSTEM_SID = "S-1-5-18" 36LOCAL_SRV_SID = "S-1-5-19" 37NETWORK_SRV_SID = "S-1-5-19" 38 39LOGON_WITH_PROFILE = 0x00000001 40 41WINSTA_ALL = ( 42 win32con.WINSTA_ACCESSCLIPBOARD 43 | win32con.WINSTA_ACCESSGLOBALATOMS 44 | win32con.WINSTA_CREATEDESKTOP 45 | win32con.WINSTA_ENUMDESKTOPS 46 | win32con.WINSTA_ENUMERATE 47 | win32con.WINSTA_EXITWINDOWS 48 | win32con.WINSTA_READATTRIBUTES 49 | win32con.WINSTA_READSCREEN 50 | win32con.WINSTA_WRITEATTRIBUTES 51 | win32con.DELETE 52 | win32con.READ_CONTROL 53 | win32con.WRITE_DAC 54 | win32con.WRITE_OWNER 55) 56 57DESKTOP_ALL = ( 58 win32con.DESKTOP_CREATEMENU 59 | win32con.DESKTOP_CREATEWINDOW 60 | win32con.DESKTOP_ENUMERATE 61 | win32con.DESKTOP_HOOKCONTROL 62 | win32con.DESKTOP_JOURNALPLAYBACK 63 | win32con.DESKTOP_JOURNALRECORD 64 | win32con.DESKTOP_READOBJECTS 65 | win32con.DESKTOP_SWITCHDESKTOP 66 | win32con.DESKTOP_WRITEOBJECTS 67 | win32con.DELETE 68 | win32con.READ_CONTROL 69 | win32con.WRITE_DAC 70 | win32con.WRITE_OWNER 71) 72 73MAX_COMPUTER_NAME_LENGTH = 15 74 75SECURITY_LOGON_TYPE = wintypes.ULONG 76Interactive = 2 77Network = 3 78Batch = 4 79Service = 5 80 81LOGON_SUBMIT_TYPE = wintypes.ULONG 82PROFILE_BUFFER_TYPE = wintypes.ULONG 83 84MsV1_0InteractiveLogon = 2 85MsV1_0Lm20Logon = 3 86MsV1_0NetworkLogon = 4 87MsV1_0WorkstationUnlockLogon = 7 88MsV1_0S4ULogon = 12 89MsV1_0NoElevationLogon = 82 90 91KerbInteractiveLogon = 2 92KerbWorkstationUnlockLogon = 7 93KerbS4ULogon = 12 94 95MSV1_0_S4U_LOGON_FLAG_CHECK_LOGONHOURS = 0x2 96 97KERB_S4U_LOGON_FLAG_CHECK_LOGONHOURS = 0x2 98KERB_S4U_LOGON_FLAG_IDENTITY = 0x8 99 100TOKEN_SOURCE_LENGTH = 8 101 102NEGOTIATE_PACKAGE_NAME = b"Negotiate" 103MICROSOFT_KERBEROS_NAME = b"Kerberos" 104MSV1_0_PACKAGE_NAME = b"MICROSOFT_AUTHENTICATION_PACKAGE_V1_0" 105 106DELETE = 0x00010000 107READ_CONTROL = 0x00020000 108WRITE_DAC = 0x00040000 109WRITE_OWNER = 0x00080000 110 111STANDARD_RIGHTS_REQUIRED = DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER 112 113TOKEN_ASSIGN_PRIMARY = 0x0001 114TOKEN_DUPLICATE = 0x0002 115TOKEN_IMPERSONATE = 0x0004 116TOKEN_QUERY = 0x0008 117TOKEN_QUERY_SOURCE = 0x0010 118TOKEN_ADJUST_PRIVILEGES = 0x0020 119TOKEN_ADJUST_GROUPS = 0x0040 120TOKEN_ADJUST_DEFAULT = 0x0080 121TOKEN_ADJUST_SESSIONID = 0x0100 122 123TOKEN_ALL_ACCESS = ( 124 STANDARD_RIGHTS_REQUIRED 125 | TOKEN_ASSIGN_PRIMARY 126 | TOKEN_DUPLICATE 127 | TOKEN_IMPERSONATE 128 | TOKEN_QUERY 129 | TOKEN_QUERY_SOURCE 130 | TOKEN_ADJUST_PRIVILEGES 131 | TOKEN_ADJUST_GROUPS 132 | TOKEN_ADJUST_DEFAULT 133 | TOKEN_ADJUST_SESSIONID 134) 135 136DUPLICATE_CLOSE_SOURCE = 0x00000001 137DUPLICATE_SAME_ACCESS = 0x00000002 138 139TOKEN_TYPE = wintypes.ULONG 140TokenPrimary = 1 141TokenImpersonation = 2 142 143SECURITY_IMPERSONATION_LEVEL = wintypes.ULONG 144SecurityAnonymous = 0 145SecurityIdentification = 1 146SecurityImpersonation = 2 147SecurityDelegation = 3 148 149 150class NTSTATUS(wintypes.LONG): 151 def to_error(self): 152 return ntdll.RtlNtStatusToDosError(self) 153 154 def __repr__(self): 155 name = self.__class__.__name__ 156 status = wintypes.ULONG.from_buffer(self) 157 return "{}({})".format(name, status.value) 158 159 160PNTSTATUS = ctypes.POINTER(NTSTATUS) 161 162 163class BOOL(wintypes.BOOL): 164 def __repr__(self): 165 name = self.__class__.__name__ 166 return "{}({})".format(name, bool(self)) 167 168 169class HANDLE(wintypes.HANDLE): 170 __slots__ = ("closed",) 171 172 def __int__(self): 173 return self.value or 0 174 175 def Detach(self): 176 if not getattr(self, "closed", False): 177 self.closed = True 178 value = int(self) 179 self.value = None 180 return value 181 raise ValueError("already closed") 182 183 def Close(self, CloseHandle=kernel32.CloseHandle): 184 if self and not getattr(self, "closed", False): 185 CloseHandle(self.Detach()) 186 187 __del__ = Close 188 189 def __repr__(self): 190 return "{}({})".format(self.__class__.__name__, int(self)) 191 192 193class LARGE_INTEGER(wintypes.LARGE_INTEGER): 194 # https://msdn.microsoft.com/en-us/library/ff553204 195 ntdll.RtlSecondsSince1970ToTime.restype = None 196 _unix_epoch = wintypes.LARGE_INTEGER() 197 ntdll.RtlSecondsSince1970ToTime(0, ctypes.byref(_unix_epoch)) 198 _unix_epoch = _unix_epoch.value 199 200 def __int__(self): 201 return self.value 202 203 def __repr__(self): 204 name = self.__class__.__name__ 205 return "{}({})".format(name, self.value) 206 207 def as_time(self): 208 time100ns = self.value - self._unix_epoch 209 if time100ns >= 0: 210 return time100ns / 1e7 211 raise ValueError("value predates the Unix epoch") 212 213 @classmethod 214 def from_time(cls, t): 215 time100ns = int(t * 10 ** 7) 216 return cls(time100ns + cls._unix_epoch) 217 218 219CHAR = ctypes.c_char 220WCHAR = ctypes.c_wchar 221PCHAR = ctypes.POINTER(CHAR) 222PWCHAR = ctypes.POINTER(WCHAR) 223 224 225class STRING(ctypes.Structure): 226 _fields_ = ( 227 ("Length", wintypes.USHORT), 228 ("MaximumLength", wintypes.USHORT), 229 ("Buffer", PCHAR), 230 ) 231 232 233LPSTRING = ctypes.POINTER(STRING) 234 235 236class UNICODE_STRING(ctypes.Structure): 237 _fields_ = ( 238 ("Length", wintypes.USHORT), 239 ("MaximumLength", wintypes.USHORT), 240 ("Buffer", PWCHAR), 241 ) 242 243 244LPUNICODE_STRING = ctypes.POINTER(UNICODE_STRING) 245 246 247class LUID(ctypes.Structure): 248 _fields_ = ( 249 ("LowPart", wintypes.DWORD), 250 ("HighPart", wintypes.LONG), 251 ) 252 253 def __new__(cls, value=0): 254 return cls.from_buffer_copy(ctypes.c_ulonglong(value)) 255 256 def __int__(self): 257 return ctypes.c_ulonglong.from_buffer(self).value 258 259 def __repr__(self): 260 name = self.__class__.__name__ 261 return "{}({})".format(name, int(self)) 262 263 264LPLUID = ctypes.POINTER(LUID) 265PSID = wintypes.LPVOID 266 267 268class SID_AND_ATTRIBUTES(ctypes.Structure): 269 _fields_ = ( 270 ("Sid", PSID), 271 ("Attributes", wintypes.DWORD), 272 ) 273 274 275LPSID_AND_ATTRIBUTES = ctypes.POINTER(SID_AND_ATTRIBUTES) 276 277 278class TOKEN_GROUPS(ctypes.Structure): 279 _fields_ = ( 280 ("GroupCount", wintypes.DWORD), 281 ("Groups", SID_AND_ATTRIBUTES * 1), 282 ) 283 284 285LPTOKEN_GROUPS = ctypes.POINTER(TOKEN_GROUPS) 286 287 288class TOKEN_SOURCE(ctypes.Structure): 289 _fields_ = ( 290 ("SourceName", CHAR * TOKEN_SOURCE_LENGTH), 291 ("SourceIdentifier", LUID), 292 ) 293 294 def __init__(self, SourceName=None, SourceIdentifier=None): 295 super().__init__() 296 if SourceName is not None: 297 if not isinstance(SourceName, bytes): 298 SourceName = SourceName.encode("mbcs") 299 self.SourceName = SourceName 300 if SourceIdentifier is None: 301 # pylint: disable=access-member-before-definition 302 luid = self.SourceIdentifier 303 # pylint: enable=access-member-before-definition 304 ntdll.NtAllocateLocallyUniqueId(ctypes.byref(luid)) 305 else: 306 self.SourceIdentifier = SourceIdentifier 307 308 309LPTOKEN_SOURCE = ctypes.POINTER(TOKEN_SOURCE) 310py_source_context = TOKEN_SOURCE(b"PYTHON ") 311py_origin_name = __name__.encode() 312py_logon_process_name = "{}-{}".format(py_origin_name, os.getpid()) 313SIZE_T = ctypes.c_size_t 314 315 316class QUOTA_LIMITS(ctypes.Structure): 317 _fields_ = ( 318 ("PagedPoolLimit", SIZE_T), 319 ("NonPagedPoolLimit", SIZE_T), 320 ("MinimumWorkingSetSize", SIZE_T), 321 ("MaximumWorkingSetSize", SIZE_T), 322 ("PagefileLimit", SIZE_T), 323 ("TimeLimit", wintypes.LARGE_INTEGER), 324 ) 325 326 327LPQUOTA_LIMITS = ctypes.POINTER(QUOTA_LIMITS) 328LPULONG = ctypes.POINTER(wintypes.ULONG) 329LSA_OPERATIONAL_MODE = wintypes.ULONG 330LPLSA_OPERATIONAL_MODE = LPULONG 331LPHANDLE = ctypes.POINTER(wintypes.HANDLE) 332LPLPVOID = ctypes.POINTER(wintypes.LPVOID) 333LPDWORD = ctypes.POINTER(wintypes.DWORD) 334 335 336class ContiguousUnicode(ctypes.Structure): 337 # _string_names_: sequence matched to underscore-prefixed fields 338 def __init__(self, *args, **kwargs): # pylint: disable=useless-super-delegation 339 super().__init__(*args, **kwargs) 340 341 def _get_unicode_string(self, name): 342 wchar_size = ctypes.sizeof(WCHAR) 343 s = getattr(self, "_{}".format(name)) 344 length = s.Length // wchar_size 345 buf = s.Buffer 346 if buf: 347 return buf[:length] 348 return None 349 350 def _set_unicode_buffer(self, values): 351 cls = type(self) 352 wchar_size = ctypes.sizeof(WCHAR) 353 bufsize = (len("\x00".join(values)) + 1) * wchar_size 354 ctypes.resize(self, ctypes.sizeof(cls) + bufsize) 355 addr = ctypes.addressof(self) + ctypes.sizeof(cls) 356 for value in values: 357 bufsize = (len(value) + 1) * wchar_size 358 ctypes.memmove(addr, value, bufsize) 359 addr += bufsize 360 361 def _set_unicode_string(self, name, value): 362 values = [] 363 for n in self._string_names_: 364 if n == name: 365 values.append(value or "") 366 else: 367 values.append(getattr(self, n) or "") 368 self._set_unicode_buffer(values) 369 370 cls = type(self) 371 wchar_size = ctypes.sizeof(WCHAR) 372 addr = ctypes.addressof(self) + ctypes.sizeof(cls) 373 for n, v in zip(self._string_names_, values): 374 ptr = ctypes.cast(addr, PWCHAR) 375 ustr = getattr(self, "_{}".format(n)) 376 length = ustr.Length = len(v) * wchar_size 377 full_length = length + wchar_size 378 if (n == name and value is None) or ( 379 n != name and not (length or ustr.Buffer) 380 ): 381 ustr.Buffer = None 382 ustr.MaximumLength = 0 383 else: 384 ustr.Buffer = ptr 385 ustr.MaximumLength = full_length 386 addr += full_length 387 388 def __getattr__(self, name): 389 if name not in self._string_names_: 390 raise AttributeError 391 return self._get_unicode_string(name) 392 393 def __setattr__(self, name, value): 394 if name in self._string_names_: 395 self._set_unicode_string(name, value) 396 else: 397 super().__setattr__(name, value) 398 399 @classmethod 400 def from_address_copy(cls, address, size=None): 401 x = ctypes.Structure.__new__(cls) 402 if size is not None: 403 ctypes.resize(x, size) 404 ctypes.memmove(ctypes.byref(x), address, ctypes.sizeof(x)) 405 delta = ctypes.addressof(x) - address 406 for n in cls._string_names_: 407 ustr = getattr(x, "_{}".format(n)) 408 addr = ctypes.c_void_p.from_buffer(ustr.Buffer) 409 if addr: 410 addr.value += delta 411 return x 412 413 414class AuthInfo(ContiguousUnicode): 415 # _message_type_: from a logon-submit-type enumeration 416 def __init__(self): 417 super().__init__() 418 self.MessageType = self._message_type_ 419 420 421class MSV1_0_INTERACTIVE_LOGON(AuthInfo): 422 _message_type_ = MsV1_0InteractiveLogon 423 _string_names_ = "LogonDomainName", "UserName", "Password" 424 425 _fields_ = ( 426 ("MessageType", LOGON_SUBMIT_TYPE), 427 ("_LogonDomainName", UNICODE_STRING), 428 ("_UserName", UNICODE_STRING), 429 ("_Password", UNICODE_STRING), 430 ) 431 432 def __init__(self, UserName=None, Password=None, LogonDomainName=None): 433 super().__init__() 434 if LogonDomainName is not None: 435 self.LogonDomainName = LogonDomainName 436 if UserName is not None: 437 self.UserName = UserName 438 if Password is not None: 439 self.Password = Password 440 441 442class S4ULogon(AuthInfo): 443 _string_names_ = "UserPrincipalName", "DomainName" 444 445 _fields_ = ( 446 ("MessageType", LOGON_SUBMIT_TYPE), 447 ("Flags", wintypes.ULONG), 448 ("_UserPrincipalName", UNICODE_STRING), 449 ("_DomainName", UNICODE_STRING), 450 ) 451 452 def __init__(self, UserPrincipalName=None, DomainName=None, Flags=0): 453 super().__init__() 454 self.Flags = Flags 455 if UserPrincipalName is not None: 456 self.UserPrincipalName = UserPrincipalName 457 if DomainName is not None: 458 self.DomainName = DomainName 459 460 461class MSV1_0_S4U_LOGON(S4ULogon): 462 _message_type_ = MsV1_0S4ULogon 463 464 465class KERB_S4U_LOGON(S4ULogon): 466 _message_type_ = KerbS4ULogon 467 468 469PMSV1_0_S4U_LOGON = ctypes.POINTER(MSV1_0_S4U_LOGON) 470PKERB_S4U_LOGON = ctypes.POINTER(KERB_S4U_LOGON) 471 472 473class ProfileBuffer(ContiguousUnicode): 474 # _message_type_ 475 def __init__(self): 476 super().__init__() 477 self.MessageType = self._message_type_ 478 479 480class MSV1_0_INTERACTIVE_PROFILE(ProfileBuffer): 481 _message_type_ = MsV1_0InteractiveLogon 482 _string_names_ = ( 483 "LogonScript", 484 "HomeDirectory", 485 "FullName", 486 "ProfilePath", 487 "HomeDirectoryDrive", 488 "LogonServer", 489 ) 490 _fields_ = ( 491 ("MessageType", PROFILE_BUFFER_TYPE), 492 ("LogonCount", wintypes.USHORT), 493 ("BadPasswordCount", wintypes.USHORT), 494 ("LogonTime", LARGE_INTEGER), 495 ("LogoffTime", LARGE_INTEGER), 496 ("KickOffTime", LARGE_INTEGER), 497 ("PasswordLastSet", LARGE_INTEGER), 498 ("PasswordCanChange", LARGE_INTEGER), 499 ("PasswordMustChange", LARGE_INTEGER), 500 ("_LogonScript", UNICODE_STRING), 501 ("_HomeDirectory", UNICODE_STRING), 502 ("_FullName", UNICODE_STRING), 503 ("_ProfilePath", UNICODE_STRING), 504 ("_HomeDirectoryDrive", UNICODE_STRING), 505 ("_LogonServer", UNICODE_STRING), 506 ("UserFlags", wintypes.ULONG), 507 ) 508 509 510def _check_status(result, func, args): 511 if result.value < 0: 512 raise ctypes.WinError(result.to_error()) 513 return args 514 515 516def _check_bool(result, func, args): 517 if not result: 518 raise ctypes.WinError(ctypes.get_last_error()) 519 return args 520 521 522INVALID_HANDLE_VALUE = wintypes.HANDLE(-1).value 523INVALID_DWORD_VALUE = wintypes.DWORD(-1).value # ~WinAPI 524INFINITE = INVALID_DWORD_VALUE 525STD_INPUT_HANDLE = wintypes.DWORD(-10).value 526STD_OUTPUT_HANDLE = wintypes.DWORD(-11).value 527STD_ERROR_HANDLE = wintypes.DWORD(-12).value 528 529 530class SECURITY_ATTRIBUTES(ctypes.Structure): 531 _fields_ = ( 532 ("nLength", wintypes.DWORD), 533 ("lpSecurityDescriptor", wintypes.LPVOID), 534 ("bInheritHandle", wintypes.BOOL), 535 ) 536 537 def __init__(self, **kwds): 538 self.nLength = ctypes.sizeof(self) 539 super().__init__(**kwds) 540 541 542LPSECURITY_ATTRIBUTES = ctypes.POINTER(SECURITY_ATTRIBUTES) 543LPBYTE = ctypes.POINTER(wintypes.BYTE) 544LPHANDLE = PHANDLE = ctypes.POINTER(ctypes.c_void_p) 545LPDWORD = ctypes.POINTER(ctypes.c_ulong) 546 547 548class STARTUPINFO(ctypes.Structure): 549 """https://msdn.microsoft.com/en-us/library/ms686331""" 550 551 _fields_ = ( 552 ("cb", wintypes.DWORD), 553 ("lpReserved", wintypes.LPWSTR), 554 ("lpDesktop", wintypes.LPWSTR), 555 ("lpTitle", wintypes.LPWSTR), 556 ("dwX", wintypes.DWORD), 557 ("dwY", wintypes.DWORD), 558 ("dwXSize", wintypes.DWORD), 559 ("dwYSize", wintypes.DWORD), 560 ("dwXCountChars", wintypes.DWORD), 561 ("dwYCountChars", wintypes.DWORD), 562 ("dwFillAttribute", wintypes.DWORD), 563 ("dwFlags", wintypes.DWORD), 564 ("wShowWindow", wintypes.WORD), 565 ("cbReserved2", wintypes.WORD), 566 ("lpReserved2", LPBYTE), 567 ("hStdInput", wintypes.HANDLE), 568 ("hStdOutput", wintypes.HANDLE), 569 ("hStdError", wintypes.HANDLE), 570 ) 571 572 def __init__(self, **kwds): 573 self.cb = ctypes.sizeof(self) 574 super().__init__(**kwds) 575 576 577LPSTARTUPINFO = ctypes.POINTER(STARTUPINFO) 578 579 580class PROC_THREAD_ATTRIBUTE_LIST(ctypes.Structure): 581 pass 582 583 584PPROC_THREAD_ATTRIBUTE_LIST = ctypes.POINTER(PROC_THREAD_ATTRIBUTE_LIST) 585 586 587class STARTUPINFOEX(STARTUPINFO): 588 _fields_ = (("lpAttributeList", PPROC_THREAD_ATTRIBUTE_LIST),) 589 590 591LPSTARTUPINFOEX = ctypes.POINTER(STARTUPINFOEX) 592 593 594class PROCESS_INFORMATION(ctypes.Structure): 595 """https://msdn.microsoft.com/en-us/library/ms684873""" 596 597 _fields_ = ( 598 ("hProcess", wintypes.HANDLE), 599 ("hThread", wintypes.HANDLE), 600 ("dwProcessId", wintypes.DWORD), 601 ("dwThreadId", wintypes.DWORD), 602 ) 603 604 605LPPROCESS_INFORMATION = ctypes.POINTER(PROCESS_INFORMATION) 606 607 608class HANDLE_IHV(wintypes.HANDLE): 609 pass 610 611 612def errcheck_ihv(result, func, args): 613 if result.value == INVALID_HANDLE_VALUE: 614 raise ctypes.WinError(ctypes.get_last_error()) 615 return result.value 616 617 618class DWORD_IDV(wintypes.DWORD): 619 pass 620 621 622def errcheck_idv(result, func, args): 623 if result.value == INVALID_DWORD_VALUE: 624 raise ctypes.WinError(ctypes.get_last_error()) 625 return result.value 626 627 628def errcheck_bool(result, func, args): 629 if not result: 630 raise ctypes.WinError(ctypes.get_last_error()) 631 return args 632 633 634def _win(func, restype, *argtypes): 635 func.restype = restype 636 func.argtypes = argtypes 637 if issubclass(restype, NTSTATUS): 638 func.errcheck = _check_status 639 elif issubclass(restype, BOOL): 640 func.errcheck = _check_bool 641 elif issubclass(restype, HANDLE_IHV): 642 func.errcheck = errcheck_ihv 643 elif issubclass(restype, DWORD_IDV): 644 func.errcheck = errcheck_idv 645 else: 646 func.errcheck = errcheck_bool 647 648 649# https://msdn.microsoft.com/en-us/library/ms683231 650_win(kernel32.GetStdHandle, HANDLE_IHV, wintypes.DWORD) # _In_ nStdHandle 651 652 653# https://msdn.microsoft.com/en-us/library/ms724211 654_win(kernel32.CloseHandle, wintypes.BOOL, wintypes.HANDLE) # _In_ hObject 655 656 657# https://msdn.microsoft.com/en-us/library/ms724935 658_win( 659 kernel32.SetHandleInformation, 660 wintypes.BOOL, 661 wintypes.HANDLE, # _In_ hObject 662 wintypes.DWORD, # _In_ dwMask 663 wintypes.DWORD, 664) # _In_ dwFlags 665 666 667# https://msdn.microsoft.com/en-us/library/ms724251 668_win( 669 kernel32.DuplicateHandle, 670 wintypes.BOOL, 671 wintypes.HANDLE, # _In_ hSourceProcessHandle, 672 wintypes.HANDLE, # _In_ hSourceHandle, 673 wintypes.HANDLE, # _In_ hTargetProcessHandle, 674 LPHANDLE, # _Out_ lpTargetHandle, 675 wintypes.DWORD, # _In_ dwDesiredAccess, 676 wintypes.BOOL, # _In_ bInheritHandle, 677 wintypes.DWORD, 678) # _In_ dwOptions 679 680 681# https://msdn.microsoft.com/en-us/library/ms683179 682_win(kernel32.GetCurrentProcess, wintypes.HANDLE) 683 684 685# https://msdn.microsoft.com/en-us/library/ms683189 686_win( 687 kernel32.GetExitCodeProcess, 688 wintypes.BOOL, 689 wintypes.HANDLE, # _In_ hProcess, 690 LPDWORD, 691) # _Out_ lpExitCode 692 693 694# https://msdn.microsoft.com/en-us/library/aa365152 695_win( 696 kernel32.CreatePipe, 697 wintypes.BOOL, 698 PHANDLE, # _Out_ hReadPipe, 699 PHANDLE, # _Out_ hWritePipe, 700 LPSECURITY_ATTRIBUTES, # _In_opt_ lpPipeAttributes, 701 wintypes.DWORD, 702) # _In_ nSize 703 704 705# https://msdn.microsoft.com/en-us/library/ms682431 706# _win(advapi32.CreateProcessWithTokenW, wintypes.BOOL, 707# PHANDLE, # _In_ lpUsername 708# wintypes.DWORD, # _In_ dwLogonFlags 709# wintypes.LPCWSTR, # _In_opt_ lpApplicationName 710# wintypes.LPWSTR, # _Inout_opt_ lpCommandLine 711# wintypes.DWORD, # _In_ dwCreationFlags 712# wintypes.LPVOID, # _In_opt_ lpEnvironment 713# wintypes.LPCWSTR, # _In_opt_ lpCurrentDirectory 714# LPSTARTUPINFO, # _In_ lpStartupInfo 715# LPPROCESS_INFORMATION) # _Out_ lpProcessInformation 716 717 718# https://msdn.microsoft.com/en-us/library/ms682431 719_win( 720 advapi32.CreateProcessWithLogonW, 721 wintypes.BOOL, 722 wintypes.LPCWSTR, # _In_ lpUsername 723 wintypes.LPCWSTR, # _In_opt_ lpDomain 724 wintypes.LPCWSTR, # _In_ lpPassword 725 wintypes.DWORD, # _In_ dwLogonFlags 726 wintypes.LPCWSTR, # _In_opt_ lpApplicationName 727 wintypes.LPWSTR, # _Inout_opt_ lpCommandLine 728 wintypes.DWORD, # _In_ dwCreationFlags 729 wintypes.LPCWSTR, # _In_opt_ lpEnvironment 730 wintypes.LPCWSTR, # _In_opt_ lpCurrentDirectory 731 LPSTARTUPINFO, # _In_ lpStartupInfo 732 LPPROCESS_INFORMATION, 733) # _Out_ lpProcessInformation 734 735 736# https://msdn.microsoft.com/en-us/library/ms683179 737_win(kernel32.GetCurrentProcess, wintypes.HANDLE) 738 739 740# https://msdn.microsoft.com/en-us/library/ms724251 741_win( 742 kernel32.DuplicateHandle, 743 BOOL, 744 wintypes.HANDLE, # _In_ hSourceProcessHandle 745 wintypes.HANDLE, # _In_ hSourceHandle 746 wintypes.HANDLE, # _In_ hTargetProcessHandle 747 LPHANDLE, # _Out_ lpTargetHandle 748 wintypes.DWORD, # _In_ dwDesiredAccess 749 wintypes.BOOL, # _In_ bInheritHandle 750 wintypes.DWORD, 751) # _In_ dwOptions 752 753 754# https://msdn.microsoft.com/en-us/library/ms724295 755_win( 756 kernel32.GetComputerNameW, BOOL, wintypes.LPWSTR, LPDWORD # _Out_ lpBuffer 757) # _Inout_ lpnSize 758 759 760# https://msdn.microsoft.com/en-us/library/aa379295 761_win( 762 advapi32.OpenProcessToken, 763 BOOL, 764 wintypes.HANDLE, # _In_ ProcessHandle 765 wintypes.DWORD, # _In_ DesiredAccess 766 LPHANDLE, 767) # _Out_ TokenHandle 768 769 770# https://msdn.microsoft.com/en-us/library/aa446617 771_win( 772 advapi32.DuplicateTokenEx, 773 BOOL, 774 wintypes.HANDLE, # _In_ hExistingToken 775 wintypes.DWORD, # _In_ dwDesiredAccess 776 LPSECURITY_ATTRIBUTES, # _In_opt_ lpTokenAttributes 777 SECURITY_IMPERSONATION_LEVEL, # _In_ ImpersonationLevel 778 TOKEN_TYPE, # _In_ TokenType 779 LPHANDLE, 780) # _Out_ phNewToken 781 782 783# https://msdn.microsoft.com/en-us/library/ff566415 784_win(ntdll.NtAllocateLocallyUniqueId, NTSTATUS, LPLUID) # _Out_ LUID 785 786 787# https://msdn.microsoft.com/en-us/library/aa378279 788_win( 789 secur32.LsaFreeReturnBuffer, 790 NTSTATUS, 791 wintypes.LPVOID, 792) # _In_ Buffer 793 794 795# https://msdn.microsoft.com/en-us/library/aa378265 796_win( 797 secur32.LsaConnectUntrusted, 798 NTSTATUS, 799 LPHANDLE, 800) # _Out_ LsaHandle 801 802 803# https://msdn.microsoft.com/en-us/library/aa378318 804_win( 805 secur32.LsaRegisterLogonProcess, 806 NTSTATUS, 807 LPSTRING, # _In_ LogonProcessName 808 LPHANDLE, # _Out_ LsaHandle 809 LPLSA_OPERATIONAL_MODE, 810) # _Out_ SecurityMode 811 812 813# https://msdn.microsoft.com/en-us/library/aa378269 814_win(secur32.LsaDeregisterLogonProcess, NTSTATUS, wintypes.HANDLE) # _In_ LsaHandle 815 816 817# https://msdn.microsoft.com/en-us/library/aa378297 818_win( 819 secur32.LsaLookupAuthenticationPackage, 820 NTSTATUS, 821 wintypes.HANDLE, # _In_ LsaHandle 822 LPSTRING, # _In_ PackageName 823 LPULONG, 824) # _Out_ AuthenticationPackage 825 826 827# https://msdn.microsoft.com/en-us/library/aa378292 828_win( 829 secur32.LsaLogonUser, 830 NTSTATUS, 831 wintypes.HANDLE, # _In_ LsaHandle 832 LPSTRING, # _In_ OriginName 833 SECURITY_LOGON_TYPE, # _In_ LogonType 834 wintypes.ULONG, # _In_ AuthenticationPackage 835 wintypes.LPVOID, # _In_ AuthenticationInformation 836 wintypes.ULONG, # _In_ AuthenticationInformationLength 837 LPTOKEN_GROUPS, # _In_opt_ LocalGroups 838 LPTOKEN_SOURCE, # _In_ SourceContext 839 LPLPVOID, # _Out_ ProfileBuffer 840 LPULONG, # _Out_ ProfileBufferLength 841 LPLUID, # _Out_ LogonId 842 LPHANDLE, # _Out_ Token 843 LPQUOTA_LIMITS, # _Out_ Quotas 844 PNTSTATUS, 845) # _Out_ SubStatus 846 847 848def duplicate_token( 849 source_token=None, 850 access=TOKEN_ALL_ACCESS, 851 impersonation_level=SecurityImpersonation, 852 token_type=TokenPrimary, 853 attributes=None, 854): 855 close_source = False 856 if source_token is None: 857 close_source = True 858 source_token = HANDLE() 859 advapi32.OpenProcessToken( 860 kernel32.GetCurrentProcess(), TOKEN_ALL_ACCESS, ctypes.byref(source_token) 861 ) 862 token = HANDLE() 863 try: 864 advapi32.DuplicateTokenEx( 865 source_token, 866 access, 867 attributes, 868 impersonation_level, 869 token_type, 870 ctypes.byref(token), 871 ) 872 finally: 873 if close_source: 874 source_token.Close() 875 return token 876 877 878def lsa_connect_untrusted(): 879 handle = wintypes.HANDLE() 880 secur32.LsaConnectUntrusted(ctypes.byref(handle)) 881 return handle.value 882 883 884def lsa_register_logon_process(logon_process_name): 885 if not isinstance(logon_process_name, bytes): 886 logon_process_name = logon_process_name.encode("mbcs") 887 logon_process_name = logon_process_name[:127] 888 buf = ctypes.create_string_buffer(logon_process_name, 128) 889 name = STRING(len(logon_process_name), len(buf), buf) 890 handle = wintypes.HANDLE() 891 mode = LSA_OPERATIONAL_MODE() 892 secur32.LsaRegisterLogonProcess( 893 ctypes.byref(name), ctypes.byref(handle), ctypes.byref(mode) 894 ) 895 return handle.value 896 897 898def lsa_lookup_authentication_package(lsa_handle, package_name): 899 if not isinstance(package_name, bytes): 900 package_name = package_name.encode("mbcs") 901 package_name = package_name[:127] 902 buf = ctypes.create_string_buffer(package_name) 903 name = STRING(len(package_name), len(buf), buf) 904 package = wintypes.ULONG() 905 secur32.LsaLookupAuthenticationPackage( 906 lsa_handle, ctypes.byref(name), ctypes.byref(package) 907 ) 908 return package.value 909 910 911LOGONINFO = collections.namedtuple( 912 "LOGONINFO", ("Token", "LogonId", "Profile", "Quotas") 913) 914 915 916def lsa_logon_user( 917 auth_info, 918 local_groups=None, 919 origin_name=py_origin_name, 920 source_context=None, 921 auth_package=None, 922 logon_type=None, 923 lsa_handle=None, 924): 925 if local_groups is None: 926 plocal_groups = LPTOKEN_GROUPS() 927 else: 928 plocal_groups = ctypes.byref(local_groups) 929 if source_context is None: 930 source_context = py_source_context 931 if not isinstance(origin_name, bytes): 932 origin_name = origin_name.encode("mbcs") 933 buf = ctypes.create_string_buffer(origin_name) 934 origin_name = STRING(len(origin_name), len(buf), buf) 935 if auth_package is None: 936 if isinstance(auth_info, MSV1_0_S4U_LOGON): 937 auth_package = NEGOTIATE_PACKAGE_NAME 938 elif isinstance(auth_info, KERB_S4U_LOGON): 939 auth_package = MICROSOFT_KERBEROS_NAME 940 else: 941 auth_package = MSV1_0_PACKAGE_NAME 942 if logon_type is None: 943 if isinstance(auth_info, S4ULogon): 944 logon_type = win32con.LOGON32_LOGON_NETWORK 945 else: 946 logon_type = Interactive 947 profile_buffer = wintypes.LPVOID() 948 profile_buffer_length = wintypes.ULONG() 949 profile = None 950 logonid = LUID() 951 htoken = HANDLE() 952 quotas = QUOTA_LIMITS() 953 substatus = NTSTATUS() 954 deregister = False 955 if lsa_handle is None: 956 lsa_handle = lsa_connect_untrusted() 957 deregister = True 958 try: 959 if isinstance(auth_package, (str, bytes)): 960 auth_package = lsa_lookup_authentication_package(lsa_handle, auth_package) 961 try: 962 secur32.LsaLogonUser( 963 lsa_handle, 964 ctypes.byref(origin_name), 965 logon_type, 966 auth_package, 967 ctypes.byref(auth_info), 968 ctypes.sizeof(auth_info), 969 plocal_groups, 970 ctypes.byref(source_context), 971 ctypes.byref(profile_buffer), 972 ctypes.byref(profile_buffer_length), 973 ctypes.byref(logonid), 974 ctypes.byref(htoken), 975 ctypes.byref(quotas), 976 ctypes.byref(substatus), 977 ) 978 except OSError: 979 if substatus.value: 980 raise ctypes.WinError(substatus.to_error()) 981 raise 982 finally: 983 if profile_buffer: 984 address = profile_buffer.value 985 buftype = PROFILE_BUFFER_TYPE.from_address(address).value 986 if buftype == MsV1_0InteractiveLogon: 987 profile = MSV1_0_INTERACTIVE_PROFILE.from_address_copy( 988 address, profile_buffer_length.value 989 ) 990 secur32.LsaFreeReturnBuffer(address) 991 finally: 992 if deregister: 993 secur32.LsaDeregisterLogonProcess(lsa_handle) 994 return LOGONINFO(htoken, logonid, profile, quotas) 995 996 997def logon_msv1( 998 name, 999 password, 1000 domain=None, 1001 local_groups=None, 1002 origin_name=py_origin_name, 1003 source_context=None, 1004): 1005 return lsa_logon_user( 1006 MSV1_0_INTERACTIVE_LOGON(name, password, domain), 1007 local_groups, 1008 origin_name, 1009 source_context, 1010 ) 1011 1012 1013def logon_msv1_s4u( 1014 name, local_groups=None, origin_name=py_origin_name, source_context=None 1015): 1016 domain = ctypes.create_unicode_buffer(MAX_COMPUTER_NAME_LENGTH + 1) 1017 length = wintypes.DWORD(len(domain)) 1018 kernel32.GetComputerNameW(domain, ctypes.byref(length)) 1019 return lsa_logon_user( 1020 MSV1_0_S4U_LOGON(name, domain.value), local_groups, origin_name, source_context 1021 ) 1022 1023 1024def logon_kerb_s4u( 1025 name, 1026 realm=None, 1027 local_groups=None, 1028 origin_name=py_origin_name, 1029 source_context=None, 1030 logon_process_name=py_logon_process_name, 1031): 1032 lsa_handle = lsa_register_logon_process(logon_process_name) 1033 try: 1034 return lsa_logon_user( 1035 KERB_S4U_LOGON(name, realm), 1036 local_groups, 1037 origin_name, 1038 source_context, 1039 lsa_handle=lsa_handle, 1040 ) 1041 finally: 1042 secur32.LsaDeregisterLogonProcess(lsa_handle) 1043 1044 1045def DuplicateHandle( 1046 hsrc=kernel32.GetCurrentProcess(), 1047 srchandle=kernel32.GetCurrentProcess(), 1048 htgt=kernel32.GetCurrentProcess(), 1049 access=0, 1050 inherit=False, 1051 options=win32con.DUPLICATE_SAME_ACCESS, 1052): 1053 tgthandle = wintypes.HANDLE() 1054 kernel32.DuplicateHandle( 1055 hsrc, srchandle, htgt, ctypes.byref(tgthandle), access, inherit, options 1056 ) 1057 return tgthandle.value 1058 1059 1060def CreatePipe(inherit_read=False, inherit_write=False): 1061 read, write = wintypes.HANDLE(), wintypes.HANDLE() 1062 kernel32.CreatePipe(ctypes.byref(read), ctypes.byref(write), None, 0) 1063 if inherit_read: 1064 kernel32.SetHandleInformation( 1065 read, win32con.HANDLE_FLAG_INHERIT, win32con.HANDLE_FLAG_INHERIT 1066 ) 1067 if inherit_write: 1068 kernel32.SetHandleInformation( 1069 write, win32con.HANDLE_FLAG_INHERIT, win32con.HANDLE_FLAG_INHERIT 1070 ) 1071 return read.value, write.value 1072 1073 1074def set_user_perm(obj, perm, sid): 1075 """ 1076 Set an object permission for the given user sid 1077 """ 1078 info = ( 1079 win32security.OWNER_SECURITY_INFORMATION 1080 | win32security.GROUP_SECURITY_INFORMATION 1081 | win32security.DACL_SECURITY_INFORMATION 1082 ) 1083 sd = win32security.GetUserObjectSecurity(obj, info) 1084 dacl = sd.GetSecurityDescriptorDacl() 1085 ace_cnt = dacl.GetAceCount() 1086 found = False 1087 for idx in range(0, ace_cnt): 1088 (aceType, aceFlags), ace_mask, ace_sid = dacl.GetAce(idx) 1089 ace_exists = ( 1090 aceType == ntsecuritycon.ACCESS_ALLOWED_ACE_TYPE 1091 and ace_mask == perm 1092 and ace_sid == sid 1093 ) 1094 if ace_exists: 1095 # If the ace already exists, do nothing 1096 break 1097 else: 1098 dacl.AddAccessAllowedAce(dacl.GetAclRevision(), perm, sid) 1099 sd.SetSecurityDescriptorDacl(1, dacl, 0) 1100 win32security.SetUserObjectSecurity(obj, info, sd) 1101 1102 1103def grant_winsta_and_desktop(th): 1104 """ 1105 Grant the token's user access to the current process's window station and 1106 desktop. 1107 """ 1108 current_sid = win32security.GetTokenInformation(th, win32security.TokenUser)[0] 1109 # Add permissions for the sid to the current windows station and thread id. 1110 # This prevents windows error 0xC0000142. 1111 winsta = win32process.GetProcessWindowStation() 1112 set_user_perm(winsta, WINSTA_ALL, current_sid) 1113 desktop = win32service.GetThreadDesktop(win32api.GetCurrentThreadId()) 1114 set_user_perm(desktop, DESKTOP_ALL, current_sid) 1115 1116 1117def environment_string(env): 1118 senv = "" 1119 for k, v in env.items(): 1120 senv += k + "=" + v + "\0" 1121 senv += "\0" 1122 return ctypes.create_unicode_buffer(senv) 1123 1124 1125def CreateProcessWithTokenW( 1126 token, 1127 logonflags=0, 1128 applicationname=None, 1129 commandline=None, 1130 creationflags=0, 1131 environment=None, 1132 currentdirectory=None, 1133 startupinfo=None, 1134): 1135 creationflags |= win32con.CREATE_UNICODE_ENVIRONMENT 1136 if commandline is not None: 1137 commandline = ctypes.create_unicode_buffer(commandline) 1138 if startupinfo is None: 1139 startupinfo = STARTUPINFO() 1140 if currentdirectory is not None: 1141 currentdirectory = ctypes.create_unicode_buffer(currentdirectory) 1142 if environment is not None: 1143 environment = ctypes.pointer(environment_string(environment)) 1144 process_info = PROCESS_INFORMATION() 1145 ret = advapi32.CreateProcessWithTokenW( 1146 token, 1147 logonflags, 1148 applicationname, 1149 commandline, 1150 creationflags, 1151 environment, 1152 currentdirectory, 1153 ctypes.byref(startupinfo), 1154 ctypes.byref(process_info), 1155 ) 1156 if ret == 0: 1157 winerr = win32api.GetLastError() 1158 exc = OSError(win32api.FormatMessage(winerr)) 1159 exc.winerror = winerr 1160 raise exc 1161 return process_info 1162 1163 1164def enumerate_tokens(sid=None, session_id=None, privs=None): 1165 """ 1166 Enumerate tokens from any existing processes that can be accessed. 1167 Optionally filter by sid. 1168 """ 1169 for p in psutil.process_iter(): 1170 if p.pid == 0: 1171 continue 1172 try: 1173 ph = win32api.OpenProcess(win32con.PROCESS_ALL_ACCESS, 0, p.pid) 1174 except win32api.error as exc: 1175 if exc.winerror == 5: 1176 log.debug("Unable to OpenProcess pid=%d name=%s", p.pid, p.name()) 1177 continue 1178 raise exc 1179 try: 1180 access = ( 1181 win32security.TOKEN_DUPLICATE 1182 | win32security.TOKEN_QUERY 1183 | win32security.TOKEN_IMPERSONATE 1184 | win32security.TOKEN_ASSIGN_PRIMARY 1185 ) 1186 th = win32security.OpenProcessToken(ph, access) 1187 except Exception as exc: # pylint: disable=broad-except 1188 log.debug( 1189 "OpenProcessToken failed pid=%d name=%s user%s", 1190 p.pid, 1191 p.name(), 1192 p.username(), 1193 ) 1194 continue 1195 try: 1196 process_sid = win32security.GetTokenInformation( 1197 th, win32security.TokenUser 1198 )[0] 1199 except Exception as exc: # pylint: disable=broad-except 1200 log.exception( 1201 "GetTokenInformation pid=%d name=%s user%s", 1202 p.pid, 1203 p.name(), 1204 p.username(), 1205 ) 1206 continue 1207 1208 proc_sid = win32security.ConvertSidToStringSid(process_sid) 1209 if sid and sid != proc_sid: 1210 log.debug("Token for pid does not match user sid: %s", sid) 1211 continue 1212 1213 if ( 1214 session_id 1215 and win32security.GetTokenInformation(th, win32security.TokenSessionId) 1216 != session_id 1217 ): 1218 continue 1219 1220 def has_priv(tok, priv): 1221 luid = win32security.LookupPrivilegeValue(None, priv) 1222 for priv_luid, flags in win32security.GetTokenInformation( 1223 tok, win32security.TokenPrivileges 1224 ): 1225 if priv_luid == luid: 1226 return True 1227 return False 1228 1229 if privs: 1230 has_all = True 1231 for name in privs: 1232 if not has_priv(th, name): 1233 has_all = False 1234 if not has_all: 1235 continue 1236 yield dup_token(th) 1237 1238 1239def impersonate_sid(sid, session_id=None, privs=None): 1240 """ 1241 Find an existing process token for the given sid and impersonate the token. 1242 """ 1243 for tok in enumerate_tokens(sid, session_id, privs): 1244 tok = dup_token(tok) 1245 elevate_token(tok) 1246 if win32security.ImpersonateLoggedOnUser(tok) == 0: 1247 raise OSError("Impersonation failure") 1248 return tok 1249 raise OSError("Impersonation failure") 1250 1251 1252def dup_token(th): 1253 """ 1254 duplicate the access token 1255 """ 1256 # TODO: is `duplicate_token` the same? 1257 sec_attr = win32security.SECURITY_ATTRIBUTES() 1258 sec_attr.bInheritHandle = True 1259 return win32security.DuplicateTokenEx( 1260 th, 1261 win32security.SecurityImpersonation, 1262 win32con.MAXIMUM_ALLOWED, 1263 win32security.TokenPrimary, 1264 sec_attr, 1265 ) 1266 1267 1268def elevate_token(th): 1269 """ 1270 Set all token privileges to enabled 1271 """ 1272 # Get list of privileges this token contains 1273 privileges = win32security.GetTokenInformation(th, win32security.TokenPrivileges) 1274 1275 # Create a set of all privileges to be enabled 1276 enable_privs = set() 1277 for luid, flags in privileges: 1278 enable_privs.add((luid, win32con.SE_PRIVILEGE_ENABLED)) 1279 1280 # Enable the privileges 1281 if win32security.AdjustTokenPrivileges(th, 0, enable_privs) == 0: 1282 raise OSError(win32api.FormatMessage(win32api.GetLastError())) 1283 1284 1285def make_inheritable(token): 1286 """Create an inheritable handle""" 1287 return win32api.DuplicateHandle( 1288 win32api.GetCurrentProcess(), 1289 token, 1290 win32api.GetCurrentProcess(), 1291 0, 1292 1, 1293 win32con.DUPLICATE_SAME_ACCESS, 1294 ) 1295 1296 1297def CreateProcessWithLogonW( 1298 username=None, 1299 domain=None, 1300 password=None, 1301 logonflags=0, 1302 applicationname=None, 1303 commandline=None, 1304 creationflags=0, 1305 environment=None, 1306 currentdirectory=None, 1307 startupinfo=None, 1308): 1309 creationflags |= win32con.CREATE_UNICODE_ENVIRONMENT 1310 if commandline is not None: 1311 commandline = ctypes.create_unicode_buffer(commandline) 1312 if startupinfo is None: 1313 startupinfo = STARTUPINFO() 1314 if environment is not None: 1315 environment = ctypes.pointer(environment_string(environment)) 1316 process_info = PROCESS_INFORMATION() 1317 advapi32.CreateProcessWithLogonW( 1318 username, 1319 domain, 1320 password, 1321 logonflags, 1322 applicationname, 1323 commandline, 1324 creationflags, 1325 environment, 1326 currentdirectory, 1327 ctypes.byref(startupinfo), 1328 ctypes.byref(process_info), 1329 ) 1330 return process_info 1331