def winerror(code): global _WINERROR_BY_VALUE if not _WINERROR_BY_VALUE: # Lazy init _WINERROR_BY_VALUE = gdef.FlagMapper(*(getattr(gdef, error) for error in gdef.meta.errors)) val = _WINERROR_BY_VALUE[code] if val is code: # Not found val = _WINERROR_BY_VALUE[code & 0xffff] # Hresult: extract code (https://en.wikipedia.org/wiki/HRESULT) return val
class LogicalDrive(AutoHandle): DRIVE_TYPE = gdef.FlagMapper(DRIVE_UNKNOWN, DRIVE_NO_ROOT_DIR, DRIVE_REMOVABLE, DRIVE_FIXED, DRIVE_REMOTE, DRIVE_CDROM, DRIVE_RAMDISK) def __init__(self, name): self.name = name @property def type(self): """The type of drive, values are: * DRIVE_UNKNOWN(0x0L) * DRIVE_NO_ROOT_DIR(0x1L) * DRIVE_REMOVABLE(0x2L) * DRIVE_FIXED(0x3L) * DRIVE_REMOTE(0x4L) * DRIVE_CDROM(0x5L) * DRIVE_RAMDISK(0x6L) :type: :class:`long` or :class:`int` (or subclass) """ t = winproxy.GetDriveTypeA(self.name) return self.DRIVE_TYPE.get(t,t) @property def path(self): """The target path of the device :type: :class:`str`""" res = query_dos_device(self.name.strip("\\")) if len(res) != 1: raise ValueError("[Unexpected result] query_dos_device(logicaldrive) returned multiple path") return res[0] def query_info(self, info): return windows.utils.query_volume_information(self.handle, info) @property def volume_info(self): return self.query_info(gdef.FileFsVolumeInformation) @property def serial(self): return self.volume_info.VolumeSerialNumber def _get_handle(self): nt_name = windows.utils.dospath_to_ntpath(self.name) handle = windows.winproxy.CreateFileA(nt_name, gdef.GENERIC_READ, gdef.FILE_SHARE_READ, None, gdef.OPEN_EXISTING, gdef.FILE_FLAG_BACKUP_SEMANTICS , None) return handle def __repr__(self): return """<{0} "{1}" ({2})>""".format(type(self).__name__, self.name, self.type.name)
import ctypes import windows from collections import namedtuple from contextlib import contextmanager from windows import utils import windows.generated_def as gdef from windows.generated_def import * SERVICE_TYPE = gdef.FlagMapper(SERVICE_KERNEL_DRIVER, SERVICE_FILE_SYSTEM_DRIVER, SERVICE_WIN32_OWN_PROCESS, SERVICE_WIN32_SHARE_PROCESS, SERVICE_INTERACTIVE_PROCESS) SERVICE_STATE = gdef.FlagMapper(SERVICE_STOPPED, SERVICE_START_PENDING, SERVICE_STOP_PENDING, SERVICE_RUNNING, SERVICE_CONTINUE_PENDING, SERVICE_PAUSE_PENDING, SERVICE_PAUSED) SERVICE_CONTROLE_ACCEPTED = gdef.FlagMapper() SERVICE_FLAGS = gdef.FlagMapper(SERVICE_RUNS_IN_SYSTEM_PROCESS) ServiceStatus = namedtuple("ServiceStatus", ["type", "state", "control_accepted", "flags"]) """ ``type`` might be one of: * ``SERVICE_KERNEL_DRIVER(0x1L)`` * ``SERVICE_FILE_SYSTEM_DRIVER(0x2L)`` * ``SERVICE_WIN32_OWN_PROCESS(0x10L)`` * ``SERVICE_WIN32_SHARE_PROCESS(0x20L)`` * ``SERVICE_INTERACTIVE_PROCESS(0x100L)`` ``state`` might be one of: * ``SERVICE_STOPPED(0x1L)`` * ``SERVICE_START_PENDING(0x2L)``
class System(object): """The state of the current ``Windows`` system ``Python`` is running on""" # Setup these in a fixedproperty ? network = network.Network() """Object of class :class:`windows.winobject.network.Network`""" registry = registry.Registry() """Object of class :class:`windows.winobject.registry.Registry`""" @property def processes(self): """The list of running processes :type: [:class:`~windows.winobject.process.WinProcess`] -- A list of Process """ return self.enumerate_processes() @property def threads(self): """The list of running threads :type: [:class:`~windows.winobject.process.WinThread`] -- A list of Thread """ return self.enumerate_threads_setup_owners() @property def logicaldrives(self): """List of logical drives [C:\, ...] :type: [:class:`~windows.winobject.volume.LogicalDrive`] -- A list of LogicalDrive """ return volume.enum_logical_drive() @utils.fixedpropety def services(self): """The list of services :type: [:class:`~windows.winobject.service.ServiceA`] -- A list of Service""" return service.ServiceManager() @property def handles(self): """The list of system handles :type: [:class:`~windows.winobject.handle.Handle`] -- A list of Hanlde""" return handle.enumerate_handles() @property def modules(self): """The list of system modules :type: [:class:`~windows.winobject.system_module.SystemModule`] -- A list of :class:`~windows.winobject.system_module.SystemModule` or :class:`~windows.winobject.system_module.SystemModuleWow64` """ return system_module.enumerate_kernel_modules() @utils.fixedpropety def bitness(self): """The bitness of the system :type: :class:`int` -- 32 or 64 """ if os.environ["PROCESSOR_ARCHITECTURE"].lower() != "x86": return 64 if "PROCESSOR_ARCHITEW6432" in os.environ: return 64 return 32 @utils.fixedpropety def wmi(self): r"""An object to perform wmi requests to various namespaces :type: :class:`~windows.winobject.wmi.WmiManager`""" return wmi.WmiManager() @utils.fixedpropety def event_log(self): return event_log.EvtlogManager() @utils.fixedpropety def etw(self): return event_trace.EtwManager() @utils.fixedpropety def task_scheduler(self): """An object able to manage scheduled tasks on the local system :type: :class:`~windows.winobject.task_scheduler.TaskService` """ windows.com.init() clsid_task_scheduler = gdef.IID.from_string("0f87369f-a4e5-4cfc-bd3e-73e6154572dd") task_service = task_scheduler.TaskService() # What is non-implemented (WinXP) # Raise (NotImplementedError?) ? Return NotImplemented ? windows.com.create_instance(clsid_task_scheduler, task_service) task_service.connect() return task_service @utils.fixedpropety def object_manager(self): """An object to query the objects in the kernel object manager. :type: :class:`~windows.winobject.object_manager.ObjectManager` """ return windows.winobject.object_manager.ObjectManager() @utils.fixedpropety def bits(self): return bits.create_manager() #TODO: use GetComputerNameExA ? and recover other names ? @utils.fixedpropety def computer_name(self): """The name of the computer :type: :class:`str` """ size = gdef.DWORD(0x1000) # For now I don't know what is best as A vs W APIs... if windows.pycompat.is_py3: buf = ctypes.create_unicode_buffer(size.value) winproxy.GetComputerNameW(buf, ctypes.byref(size)) else: buf = ctypes.create_string_buffer(size.value) winproxy.GetComputerNameA(buf, ctypes.byref(size)) return buf[:size.value] @utils.fixedpropety def version(self): """The version of the system :type: (:class:`int`, :class:`int`) -- (Major, Minor) """ data = self.get_version() result = data.dwMajorVersion, data.dwMinorVersion if result == (6,2): result_str = self.get_file_version("kernel32") result_tup = [int(x) for x in result_str.split(".")] result = tuple(result_tup[:2]) return result @utils.fixedpropety def version_name(self): """The name of the system version, values are: * Windows Server 2016 * Windows 10 * Windows Server 2012 R2 * Windows 8.1 * Windows Server 2012 * Windows 8 * Windows Server 2008 * Windows 7 * Windows Server 2008 * Windows Vista * Windows XP Professional x64 Edition * TODO: version (5.2) + is_workstation + bitness == 32 (don't even know if possible..) * Windows Server 2003 R2 * Windows Server 2003 * Windows XP * Windows 2000 * "Unknow Windows <version={0} | is_workstation={1}>".format(version, is_workstation) :type: :class:`str` """ version = self.version is_workstation = self.product_type == gdef.VER_NT_WORKSTATION if version == (10, 0): return ["Windows Server 2016", "Windows 10"][is_workstation] elif version == (6, 3): return ["Windows Server 2012 R2", "Windows 8.1"][is_workstation] elif version == (6, 2): return ["Windows Server 2012", "Windows 8"][is_workstation] elif version == (6, 1): return ["Windows Server 2008 R2", "Windows 7"][is_workstation] elif version == (6, 0): return ["Windows Server 2008", "Windows Vista"][is_workstation] elif version == (5, 2): metric = winproxy.GetSystemMetrics(gdef.SM_SERVERR2) if is_workstation: if self.bitness == 64: return "Windows XP Professional x64 Edition" else: return "TODO: version (5.2) + is_workstation + bitness == 32" elif metric != 0: return "Windows Server 2003 R2" else: return "Windows Server 2003" elif version == (5, 1): return "Windows XP" elif version == (5, 0): return "Windows 2000" else: return "Unknow Windows <version={0} | is_workstation={1}>".format(version, is_workstation) VERSION_MAPPER = gdef.FlagMapper(gdef.VER_NT_WORKSTATION, gdef.VER_NT_DOMAIN_CONTROLLER, gdef.VER_NT_SERVER) @utils.fixedpropety def product_type(self): """The product type, value might be: * VER_NT_WORKSTATION(0x1L) * VER_NT_DOMAIN_CONTROLLER(0x2L) * VER_NT_SERVER(0x3L) :type: :class:`long` or :class:`int` (or subclass) """ version = self.get_version() return self.VERSION_MAPPER[version.wProductType] EDITION_MAPPER = gdef.FlagMapper(gdef.PRODUCT_UNDEFINED, gdef.PRODUCT_ULTIMATE, gdef.PRODUCT_HOME_BASIC, gdef.PRODUCT_HOME_PREMIUM, gdef.PRODUCT_ENTERPRISE, gdef.PRODUCT_HOME_BASIC_N, gdef.PRODUCT_BUSINESS, gdef.PRODUCT_STANDARD_SERVER, gdef.PRODUCT_DATACENTER_SERVER, gdef.PRODUCT_SMALLBUSINESS_SERVER, gdef.PRODUCT_ENTERPRISE_SERVER, gdef.PRODUCT_STARTER, gdef.PRODUCT_DATACENTER_SERVER_CORE, gdef.PRODUCT_STANDARD_SERVER_CORE, gdef.PRODUCT_ENTERPRISE_SERVER_CORE, gdef.PRODUCT_ENTERPRISE_SERVER_IA64, gdef.PRODUCT_BUSINESS_N, gdef.PRODUCT_WEB_SERVER, gdef.PRODUCT_CLUSTER_SERVER, gdef.PRODUCT_HOME_SERVER, gdef.PRODUCT_STORAGE_EXPRESS_SERVER, gdef.PRODUCT_STORAGE_STANDARD_SERVER, gdef.PRODUCT_STORAGE_WORKGROUP_SERVER, gdef.PRODUCT_STORAGE_ENTERPRISE_SERVER, gdef.PRODUCT_SERVER_FOR_SMALLBUSINESS, gdef.PRODUCT_SMALLBUSINESS_SERVER_PREMIUM, gdef.PRODUCT_HOME_PREMIUM_N, gdef.PRODUCT_ENTERPRISE_N, gdef.PRODUCT_ULTIMATE_N, gdef.PRODUCT_WEB_SERVER_CORE, gdef.PRODUCT_MEDIUMBUSINESS_SERVER_MANAGEMENT, gdef.PRODUCT_MEDIUMBUSINESS_SERVER_SECURITY, gdef.PRODUCT_MEDIUMBUSINESS_SERVER_MESSAGING, gdef.PRODUCT_SERVER_FOUNDATION, gdef.PRODUCT_HOME_PREMIUM_SERVER, gdef.PRODUCT_SERVER_FOR_SMALLBUSINESS_V, gdef.PRODUCT_STANDARD_SERVER_V, gdef.PRODUCT_DATACENTER_SERVER_V, gdef.PRODUCT_ENTERPRISE_SERVER_V, gdef.PRODUCT_DATACENTER_SERVER_CORE_V, gdef.PRODUCT_STANDARD_SERVER_CORE_V, gdef.PRODUCT_ENTERPRISE_SERVER_CORE_V, gdef.PRODUCT_HYPERV, gdef.PRODUCT_STORAGE_EXPRESS_SERVER_CORE, gdef.PRODUCT_STORAGE_STANDARD_SERVER_CORE, gdef.PRODUCT_STORAGE_WORKGROUP_SERVER_CORE, gdef.PRODUCT_STORAGE_ENTERPRISE_SERVER_CORE, gdef.PRODUCT_STARTER_N, gdef.PRODUCT_PROFESSIONAL, gdef.PRODUCT_PROFESSIONAL_N, gdef.PRODUCT_SB_SOLUTION_SERVER, gdef.PRODUCT_SERVER_FOR_SB_SOLUTIONS, gdef.PRODUCT_STANDARD_SERVER_SOLUTIONS, gdef.PRODUCT_STANDARD_SERVER_SOLUTIONS_CORE, gdef.PRODUCT_SB_SOLUTION_SERVER_EM, gdef.PRODUCT_SERVER_FOR_SB_SOLUTIONS_EM, gdef.PRODUCT_SOLUTION_EMBEDDEDSERVER, gdef.PRODUCT_SOLUTION_EMBEDDEDSERVER_CORE, gdef.PRODUCT_SMALLBUSINESS_SERVER_PREMIUM_CORE, gdef.PRODUCT_ESSENTIALBUSINESS_SERVER_MGMT, gdef.PRODUCT_ESSENTIALBUSINESS_SERVER_ADDL, gdef.PRODUCT_ESSENTIALBUSINESS_SERVER_MGMTSVC, gdef.PRODUCT_ESSENTIALBUSINESS_SERVER_ADDLSVC, gdef.PRODUCT_CLUSTER_SERVER_V, gdef.PRODUCT_EMBEDDED, gdef.PRODUCT_STARTER_E, gdef.PRODUCT_HOME_BASIC_E, gdef.PRODUCT_HOME_PREMIUM_E, gdef.PRODUCT_PROFESSIONAL_E, gdef.PRODUCT_ENTERPRISE_E, gdef.PRODUCT_ULTIMATE_E, gdef.PRODUCT_ENTERPRISE_EVALUATION, gdef.PRODUCT_MULTIPOINT_STANDARD_SERVER, gdef.PRODUCT_MULTIPOINT_PREMIUM_SERVER, gdef.PRODUCT_STANDARD_EVALUATION_SERVER, gdef.PRODUCT_DATACENTER_EVALUATION_SERVER, gdef.PRODUCT_ENTERPRISE_N_EVALUATION, gdef.PRODUCT_STORAGE_WORKGROUP_EVALUATION_SERVER, gdef.PRODUCT_STORAGE_STANDARD_EVALUATION_SERVER, gdef.PRODUCT_CORE_ARM, gdef.PRODUCT_CORE_N, gdef.PRODUCT_CORE_COUNTRYSPECIFIC, gdef.PRODUCT_CORE_LANGUAGESPECIFIC, gdef.PRODUCT_CORE, gdef.PRODUCT_PROFESSIONAL_WMC, gdef.PRODUCT_UNLICENSED) @utils.fixedpropety def edition(self): # Find a better name ? version = self.get_version() edition = gdef.DWORD() try: winproxy.GetProductInfo(version.dwMajorVersion, version.dwMinorVersion, version.wServicePackMajor, version.wServicePackMinor, edition) except winproxy.ExportNotFound as e: # Windows XP does not implem GetProductInfo assert version.dwMajorVersion, version.dwMinorVersion == (5,1) return self._edition_windows_xp() return self.EDITION_MAPPER[edition.value] def _edition_windows_xp(self): # Emulate standard response from IsOS(gdef.OS_PROFESSIONAL) if winproxy.IsOS(gdef.OS_PROFESSIONAL): return gdef.PRODUCT_PROFESSIONAL return gdef.PRODUCT_HOME_BASIC @utils.fixedpropety def windir(self): buffer = ctypes.c_buffer(0x100) reslen = winproxy.GetWindowsDirectoryA(buffer) return buffer[:reslen] def get_version(self): data = gdef.OSVERSIONINFOEXA() data.dwOSVersionInfoSize = ctypes.sizeof(data) winproxy.GetVersionExA(ctypes.cast(ctypes.pointer(data), ctypes.POINTER(gdef.OSVERSIONINFOA))) return data def get_file_version(self, name): size = winproxy.GetFileVersionInfoSizeA(name) buf = ctypes.c_buffer(size) winproxy.GetFileVersionInfoA(name, 0, size, buf) bufptr = gdef.PVOID() bufsize = gdef.UINT() winproxy.VerQueryValueA(buf, "\\VarFileInfo\\Translation", ctypes.byref(bufptr), ctypes.byref(bufsize)) bufstr = ctypes.cast(bufptr, gdef.LPCSTR) tup = struct.unpack("<HH", bufstr.value[:4]) req = "{0:04x}{1:04x}".format(*tup) winproxy.VerQueryValueA(buf, "\\StringFileInfo\\{0}\\ProductVersion".format(req), ctypes.byref(bufptr), ctypes.byref(bufsize)) bufstr = ctypes.cast(bufptr, gdef.LPCSTR) return bufstr.value @utils.fixedpropety def build_number(self): # Best effort. use get_file_version if registry code fails try: # Does not works on Win7.. # Missing CurrentMajorVersionNumber/CurrentMinorVersionNumber/UBR # We have CurrentVersion instead # Use this code and get_file_version as a backup ? curver_key = windows.system.registry(r"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion") try: major = curver_key["CurrentMajorVersionNumber"].value minor = curver_key["CurrentMinorVersionNumber"].value except WindowsError as e: version = curver_key["CurrentVersion"].value # May raise ValueError if no "." major, minor = version.split(".") build = curver_key["CurrentBuildNumber"].value # Update Build Revision try: ubr = curver_key["UBR"].value except WindowsError as e: ubr = 0 # Not present on Win7 return "{0}.{1}.{2}.{3}".format(major, minor, build, ubr) except (WindowsError, ValueError): return self.get_file_version("ntdll") @staticmethod def enumerate_processes(): dbgprint("Enumerating processes with CreateToolhelp32Snapshot", "SLOW") process_entry = gdef.PROCESSENTRY32() process_entry.dwSize = ctypes.sizeof(process_entry) snap = winproxy.CreateToolhelp32Snapshot(gdef.TH32CS_SNAPPROCESS, 0) winproxy.Process32First(snap, process_entry) res = [] res.append(process.WinProcess._from_PROCESSENTRY32(process_entry)) while winproxy.Process32Next(snap, process_entry): res.append(process.WinProcess._from_PROCESSENTRY32(process_entry)) winproxy.CloseHandle(snap) return res @staticmethod def enumerate_threads_generator(): # Ptet dangereux, parce que on yield la meme THREADENTRY32 a chaque fois dbgprint("Enumerating threads with CreateToolhelp32Snapshot <generator>", "SLOW") thread_entry = gdef.THREADENTRY32() thread_entry.dwSize = ctypes.sizeof(thread_entry) snap = winproxy.CreateToolhelp32Snapshot(gdef.TH32CS_SNAPTHREAD, 0) dbgprint("New handle CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD) <generator> | {0:#x}".format(snap), "HANDLE") try: winproxy.Thread32First(snap, thread_entry) yield thread_entry while winproxy.Thread32Next(snap, thread_entry): yield thread_entry finally: winproxy.CloseHandle(snap) dbgprint("CLOSE CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD) <generator> | {0:#x}".format(snap), "HANDLE") @staticmethod def enumerate_threads(): return [WinThread._from_THREADENTRY32(th) for th in System.enumerate_threads_generator()] def enumerate_threads_setup_owners(self): # Enumerating threads is a special operation concerning the owner process. # We may not be able to retrieve the name of the owning process by normal way # (as we need to get a handle on the process) # So, this implementation of enumerate_thread also setup the owner with the result of enumerate_processes dbgprint("Enumerating threads with CreateToolhelp32Snapshot and setup owner", "SLOW") # One snap for both enum to be prevent race snap = winproxy.CreateToolhelp32Snapshot(gdef.TH32CS_SNAPTHREAD | gdef.TH32CS_SNAPPROCESS, 0) process_entry = gdef.PROCESSENTRY32() process_entry.dwSize = ctypes.sizeof(process_entry) winproxy.Process32First(snap, process_entry) processes = [] processes.append(process.WinProcess._from_PROCESSENTRY32(process_entry)) while winproxy.Process32Next(snap, process_entry): processes.append(process.WinProcess._from_PROCESSENTRY32(process_entry)) # Forge a dict pid -> process proc_dict = {proc.pid: proc for proc in processes} thread_entry = gdef.THREADENTRY32() thread_entry.dwSize = ctypes.sizeof(thread_entry) threads = [] winproxy.Thread32First(snap, thread_entry) parent = proc_dict[thread_entry.th32OwnerProcessID] threads.append(process.WinThread._from_THREADENTRY32(thread_entry, owner=parent)) while winproxy.Thread32Next(snap, thread_entry): parent = proc_dict[thread_entry.th32OwnerProcessID] threads.append(process.WinThread._from_THREADENTRY32(thread_entry, owner=parent)) winproxy.CloseHandle(snap) return threads
class Token(utils.AutoHandle): """Represent a Windows Token. The attributes only documented by a type are from the :class:`~windows.generated_def.winstructs.TOKEN_INFORMATION_CLASS`, such return values may be improved version of the structure. .. note:: see `[MSDN] TOKEN_INFORMATION_CLASS <https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ne-winnt-_token_information_class>`_ """ def __init__(self, handle): self._handle = handle def _get_required_token_information_size(self, infos_class): cbsize = gdef.DWORD() try: winproxy.GetTokenInformation(self.handle, infos_class, None, 0, ctypes.byref(cbsize)) except winproxy.WinproxyError as e: if not e.winerror in (gdef.ERROR_INSUFFICIENT_BUFFER, gdef.ERROR_BAD_LENGTH): raise return cbsize.value def get_token_infomations(self, infos_class, rtype): required_size = self._get_required_token_information_size(infos_class) requested_size = max(required_size, ctypes.sizeof(rtype)) buffer = utils.BUFFER(rtype, 1)(size=requested_size) cbsize = gdef.DWORD() winproxy.GetTokenInformation(self.handle, infos_class, buffer, buffer.real_size, cbsize) return buffer[0] def set_informations(self, info_type, infos): return winproxy.SetTokenInformation(self.handle, info_type, ctypes.byref(infos), ctypes.sizeof(infos)) craft = meta_craft(get_token_infomations) # https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ne-winnt-_token_information_class TokenUser = craft(gdef.TokenUser, gdef.TOKEN_USER) #: :class:`~windows.generated_def.winstructs.TOKEN_USER` TokenGroups = craft(gdef.TokenGroups , TokenGroupsType) #: :class:`TokenGroups` TokenPrivileges = craft(gdef.TokenPrivileges , TokenPrivilegesType) #: :class:`TokenPrivileges` TokenOwner = craft(gdef.TokenOwner, gdef.TOKEN_OWNER) #: :class:`~windows.generated_def.winstructs.TOKEN_OWNER` TokenPrimaryGroup = craft(gdef.TokenPrimaryGroup, gdef.TOKEN_PRIMARY_GROUP) #: :class:`~windows.generated_def.winstructs.TOKEN_PRIMARY_GROUP` TokenDefaultDacl = craft(gdef.TokenDefaultDacl, gdef.TOKEN_DEFAULT_DACL) #: :class:`~windows.generated_def.winstructs.TOKEN_DEFAULT_DACL` TokenSource = craft(gdef.TokenSource, gdef.TOKEN_SOURCE) #: :class:`~windows.generated_def.winstructs.TOKEN_SOURCE` TokenType = craft(gdef.TokenType, gdef.TOKEN_TYPE) #: :class:`~windows.generated_def.winstructs.TOKEN_TYPE` TokenImpersonationLevel = craft(gdef.TokenImpersonationLevel, gdef.SECURITY_IMPERSONATION_LEVEL) #: :class:`~windows.generated_def.winstructs.SECURITY_IMPERSONATION_LEVEL` TokenStatistics = craft(gdef.TokenStatistics, gdef.TOKEN_STATISTICS) #: :class:`~windows.generated_def.winstructs.TOKEN_STATISTICS` TokenRestrictedSids = craft(gdef.TokenRestrictedSids, TokenGroupsType) #: :class:`~windows.generated_def.winstructs.TokenGroups` TokenSessionId = craft(gdef.TokenSessionId, gdef.DWORD) #: :class:`~windows.generated_def.winstructs.DWORD` TokenGroupsAndPrivileges = craft(gdef.TokenGroupsAndPrivileges, gdef.TOKEN_GROUPS_AND_PRIVILEGES) #: :class:`~windows.generated_def.winstructs.TOKEN_GROUPS_AND_PRIVILEGES` # TokenSessionReference = craft(gdef.TokenSessionReference, ???) # Reserved. TokenSandBoxInert = craft(gdef.TokenSandBoxInert, gdef.DWORD) #: :class:`~windows.generated_def.winstructs.DWORD` # TokenAuditPolicy = craft(gdef.TokenAuditPolicy, ???) # Reserved. TokenOrigin = craft(gdef.TokenOrigin, gdef.TOKEN_ORIGIN) #: :class:`~windows.generated_def.winstructs.TOKEN_ORIGIN` TokenElevationType = craft(gdef.TokenElevationType, gdef.TOKEN_ELEVATION_TYPE) #: :class:`~windows.generated_def.winstructs.TOKEN_ELEVATION_TYPE` TokenLinkedToken = craft(gdef.TokenLinkedToken, gdef.TOKEN_LINKED_TOKEN) #: :class:`~windows.generated_def.winstructs.TOKEN_LINKED_TOKEN` TokenElevation = craft(gdef.TokenElevation, gdef.TOKEN_ELEVATION) #: :class:`~windows.generated_def.winstructs.TOKEN_ELEVATION` TokenHasRestrictions = craft(gdef.TokenHasRestrictions, gdef.DWORD) #: :class:`~windows.generated_def.winstructs.DWORD` TokenAccessInformation = craft(gdef.TokenAccessInformation, gdef.TOKEN_ACCESS_INFORMATION) #: :class:`~windows.generated_def.winstructs.TOKEN_ACCESS_INFORMATION` TokenVirtualizationAllowed = craft(gdef.TokenVirtualizationAllowed, gdef.DWORD) #: :class:`~windows.generated_def.winstructs.DWORD` TokenVirtualizationEnabled = craft(gdef.TokenVirtualizationEnabled, gdef.DWORD) #: :class:`~windows.generated_def.winstructs.DWORD` TokenIntegrityLevel = craft(gdef.TokenIntegrityLevel, gdef.TOKEN_MANDATORY_LABEL) #: :class:`~windows.generated_def.winstructs.TOKEN_MANDATORY_LABEL` TokenUIAccess = craft(gdef.TokenUIAccess, gdef.DWORD) #: :class:`~windows.generated_def.winstructs.DWORD` TokenMandatoryPolicy = craft(gdef.TokenMandatoryPolicy, gdef.TOKEN_MANDATORY_POLICY) #: :class:`~windows.generated_def.winstructs.TOKEN_MANDATORY_POLICY` TokenLogonSid = craft(gdef.TokenLogonSid, TokenGroupsType) #: :class:`TokenGroups` TokenIsAppContainer = craft(gdef.TokenIsAppContainer, gdef.DWORD) #: :class:`~windows.generated_def.winstructs.DWORD` TokenCapabilities = craft(gdef.TokenCapabilities, TokenGroupsType) #: :class:`TokenGroups` TokenAppContainerSid = craft(gdef.TokenAppContainerSid, gdef.TOKEN_APPCONTAINER_INFORMATION) #: :class:`~windows.generated_def.winstructs.TOKEN_APPCONTAINER_INFORMATION` TokenAppContainerNumber = craft(gdef.TokenAppContainerNumber, gdef.DWORD) #: :class:`~windows.generated_def.winstructs.DWORD` TokenUserClaimAttributes = craft(gdef.TokenUserClaimAttributes, gdef.CLAIM_SECURITY_ATTRIBUTES_INFORMATION) #: :class:`~windows.generated_def.winstructs.CLAIM_SECURITY_ATTRIBUTES_INFORMATION` TokenDeviceClaimAttributes = craft(gdef.TokenDeviceClaimAttributes, gdef.CLAIM_SECURITY_ATTRIBUTES_INFORMATION) #: :class:`~windows.generated_def.winstructs.CLAIM_SECURITY_ATTRIBUTES_INFORMATION` # TokenRestrictedUserClaimAttributes = craft(gdef.TokenRestrictedUserClaimAttributes, ???) # Reserved. # TokenRestrictedDeviceClaimAttributes = craft(gdef.TokenRestrictedDeviceClaimAttributes, ???) # Reserved. TokenDeviceGroups = craft(gdef.TokenDeviceGroups, TokenGroupsType) #: :class:`TokenGroups` TokenRestrictedDeviceGroups = craft(gdef.TokenRestrictedDeviceGroups, gdef.TOKEN_GROUPS) #: :class:`~windows.generated_def.winstructs.TOKEN_GROUPS` # Reserved. # Structure found in ntseapi.h (thx internet) TokenSecurityAttributes = craft(gdef.TokenSecurityAttributes, TokenSecurityAttributesInformation) #: :class:`TokenSecurityAttributesInformation` # Help would be appreciated for the structures of the following query type # TokenIsRestricted = craft(gdef.TokenIsRestricted, ???) # Reserved. TokenProcessTrustLevel = craft(gdef.TokenProcessTrustLevel, gdef.PSID) #: :class:`~windows.generated_def.winstructs.PSID` # TokenPrivateNameSpace = craft(gdef.TokenPrivateNameSpace, gdef.ULONG) # Reserved. # TokenSingletonAttributes = craft(gdef.TokenSingletonAttributes, ???) # Reserved. # TokenBnoIsolation = craft(gdef.TokenBnoIsolation, ???) # Reserved. # TokenChildProcessFlags = craft(gdef.TokenChildProcessFlags, ???) # Reserved. # TokenIsLessPrivilegedAppContainer = craft(gdef.TokenIsLessPrivilegedAppContainer, ???) # Reserved. # High level properties @property def user(self): """The user sid of the token :type: :class:`~windows.generated_def.winstructs.PSID` """ return self.TokenUser.User.Sid @property def username(self): """The username of the token :type: :class:`str` """ return self._user_and_computer_name()[1] @property def computername(self): """The computername of the token :type: :class:`str` """ return self._user_and_computer_name()[0] def _user_and_computer_name(self): return windows.utils.lookup_sid(self.user) groups = TokenGroups #: Alias for TokenGroups (type may change in the future for improved struct) @property def owner(self): """The owner sid of the token :type: :class:`~windows.generated_def.winstructs.PSID` """ return self.TokenOwner.Owner @property def primary_group(self): """The sid of the primary group of the token :type: :class:`~windows.generated_def.winstructs.PSID` """ return self.TokenPrimaryGroup.PrimaryGroup @property def default_dacl(self): """The defaul DACL of the token :type: :class:`windows.security.Acl` """ import window.security # Beuk move token.py & in a security/ directory ? return self.get_token_infomations(gdef.TokenDefaultDacl, windows.security.PAcl)[0] # def source(self): (tok.TokenSource) ?? @property def type(self): """The type (Primary / Impersonation) of the token """ return self.TokenType.value @property def impersonation_level(self): """The impersonation level of a ``TokenImpersonation`` token. :raises: :class:`WindowsError` if token is not a ``TokenImpersonation`` :type: :class:`int` -- Enum value from :class:`~windows.generated_def.winstructs.SECURITY_IMPERSONATION_LEVEL` """ try: return self.TokenImpersonationLevel.value except WindowsError as e: if (e.winerror == gdef.ERROR_INVALID_PARAMETER and self.type != gdef.TokenImpersonation): # raise ValueError ? e.strerror += " This Token is not an Impersonation token" raise statistics = TokenStatistics #: Alias for TokenStatistics (type may change in the future for improved struct) @property def id(self): """The TokenId Specifies an unique identifier that identifies this instance of the token object. :type: :class:`int` """ return int(self.TokenStatistics.TokenId) @property def authentication_id(self): """The AuthenticationId Specifies an unique identifier assigned to the session this token represents. There can be many tokens representing a single logon session. :type: :class:`int` """ return int(self.TokenStatistics.AuthenticationId) @property def modified_id(self): """The ModifiedId Specifies an unique identifier that changes each time the token is modified. :type: :class:`int` """ return int(self.TokenStatistics.ModifiedId) restricted_sids = TokenRestrictedSids #: Alias for TokenRestrictedSids (type may change in the future for improved struct) session_id = TokenSessionId #: Alias for TokenSessionId (type may change in the future for improved struct) @property def groups_and_privileges(self): """Alias for TokenGroupsAndPrivileges (type may change in the future for improved struct)""" # Return enhanced 'TOKEN_GROUPS_AND_PRIVILEGES' ? return self.TokenGroupsAndPrivileges @property def privileges(self): """Alias for ``TokenPrivileges`` :type: :class:`TokenPrivileges` """ return self.TokenPrivileges sandbox_inert = TokenSandBoxInert #: Alias for TokenSandBoxInert (type may change in the future for improved struct) # def audit_policy(self): # raise NotImplementedError("Need to find the type of TokenAuditPolicy") @property def origin(self): """The originating logon session of the token. :type: :class:`int` """ origin_logon_session = self.TokenOrigin.OriginatingLogonSession return int(origin_logon_session) # improved LUID implem __int__ :) @property def elevation_type(self): """The elevation type of the token. :type: :class:`int` -- Enum value from :class:`~windows.generated_def.winstructs.TOKEN_ELEVATION_TYPE` """ return self.TokenElevationType.value @property def linked_token(self): """The token linked to our token if present (may raise else) :type: :class:`Token` """ # TODO: return None if not present ? return Token(self.TokenLinkedToken.LinkedToken) @property def elevated(self): """``True`` if token is an elevated token""" return bool(self.TokenElevation.TokenIsElevated) is_elevated = elevated #: Alias for ``elevated`` deprecated and may disapear has_restriction = TokenHasRestrictions #: Alias for TokenHasRestrictions (type may change in the future for improved struct) @property def access_information(self): """Alias for TokenAccessInformation (type may change in the future for improved struct)""" # Return enhanced subclass ? return self.TokenAccessInformation @property def trust_level(self): """The trust level of the process if present else ``None``. :type: :class:`~windows.generated_def.winstructs.PSID` """ tl = self.TokenProcessTrustLevel if not tl: # NULL: return None return tl virtualization_allowed = TokenVirtualizationAllowed #: Alias for TokenVirtualizationAllowed (type may change in the future for improved struct) virtualization_enabled = TokenVirtualizationEnabled #: Alias for TokenVirtualizationEnabled (type may change in the future for improved struct) @property def integrity_level(self): """The integrity level and attributes of the token :type: :class:`windows.generated_def.winstructs.SID_AND_ATTRIBUTES` """ return self.TokenIntegrityLevel.Label # SID_AND_ATTRIBUTES def get_integrity(self): """Return the integrity level of the token :type: :class:`int` """ sid = self.integrity_level.Sid count = winproxy.GetSidSubAuthorityCount(sid) integrity = winproxy.GetSidSubAuthority(sid, count[0] - 1)[0] return KNOW_INTEGRITY_LEVEL[integrity] def set_integrity(self, integrity): """Set the integrity level of a token :param type: :class:`int` """ mandatory_label = gdef.TOKEN_MANDATORY_LABEL() mandatory_label.Label.Attributes = 0x60 # cast integrity to int to accept SECURITY_MANDATORY_LOW_RID & other Flags mandatory_label.Label.Sid = gdef.PSID.from_string("S-1-16-{0}".format(int(integrity))) return self.set_informations(gdef.TokenIntegrityLevel, mandatory_label) _INTEGRITY_PROPERTY_DOC = """The integrity of the token as an int (extracted from integrity PSID) :getter: :func:`get_integrity` :setter: :func:`set_integrity` """ integrity = property(get_integrity, set_integrity, doc=_INTEGRITY_PROPERTY_DOC) ui_access = TokenUIAccess #: Alias for TokenUIAccess (type may change in the future for improved struct) VALID_TOKEN_POLICIES = gdef.FlagMapper( gdef.TOKEN_MANDATORY_POLICY_OFF, gdef.TOKEN_MANDATORY_POLICY_NO_WRITE_UP, gdef.TOKEN_MANDATORY_POLICY_NEW_PROCESS_MIN, gdef.TOKEN_MANDATORY_POLICY_VALID_MASK, ) @property def mandatory_policy(self): """mandatory integrity access policy for the associated token :type: :class:`int` -- see `[MSDN] mandatory policy <https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_token_mandatory_policy>`_ """ return self.VALID_TOKEN_POLICIES[self.TokenMandatoryPolicy.Policy] @property def logon_sid(self): """The logon sid of the token. (Case of multiple logon sid not handled and will raise AssertionError) :type: :class:`windows.generated_def.winstructs.SID_AND_ATTRIBUTES` """ rgroups = self.TokenLogonSid assert rgroups.GroupCount == 1, "More than 1 TokenLogonSid" return rgroups.Groups[0] is_appcontainer = TokenIsAppContainer #: Alias for TokenIsAppContainer (type may change in the future for improved struct) capabilities = TokenCapabilities #: Alias for TokenCapabilities (type may change in the future for improved struct) @property def appcontainer_sid(self): """The sid of the TokenAppContainerSid if present else ``None`` :type: :class:`~windows.generated_def.winstructs.PSID` """ sid = self.TokenAppContainerSid.TokenAppContainer if not sid: # NULL return None return sid appcontainer_number = TokenAppContainerNumber #: Alias for TokenAppContainerNumber (type may change in the future for improved struct) @property def security_attributes(self): """The security attributes of the token :type: [:class:`TokenSecurityAttributeV1`] - A list of token security attributes """ return self.TokenSecurityAttributes.attributes ## Token Methods def duplicate(self, access_rigth=gdef.MAXIMUM_ALLOWED, attributes=None, type=None, impersonation_level=None): """Duplicate the token into a new :class:`Token`. :param type: The type of token: ``TokenPrimary(0x1L)`` or ``TokenImpersonation(0x2L)`` :param impersonation_level: The :class:`~windows.generated_def.winstructs.SECURITY_IMPERSONATION_LEVEL` for a ``TokenImpersonation(0x2L)``: - If ``type`` is ``TokenPrimary(0x1L)`` this parameter is ignored if ``None`` or used as-is. - If ``type`` is ``TokenImpersonation(0x2L)`` and this parameter is None, ``self.impersonation_level`` is used. - If ``type`` is ``TokenImpersonation(0x2L)`` and our Token is a ``TokenPrimary(0x1L)`` this parameter MUST be provided :returns: :class:`Token` - The duplicate token Example: >>> tok <Token TokenId=0x39d6dde5 Type=TokenPrimary(0x1L)> >>> tok.duplicate() <Token TokenId=0x39d7b206 Type=TokenPrimary(0x1L)> >>> tok.duplicate(type=gdef.TokenImpersonation) ... ValueError: Duplicating a PrimaryToken as a TokenImpersonation require explicit <impersonation_level> parameter >>> tok.duplicate(type=gdef.TokenImpersonation, impersonation_level=gdef.SecurityImpersonation) <Token TokenId=0x39dadbf8 Type=TokenImpersonation(0x2L) ImpersonationLevel=SecurityImpersonation(0x2L)> """ newtoken = gdef.HANDLE() if type is None: type = self.type if impersonation_level is None: if self.type == gdef.TokenImpersonation: impersonation_level = self.impersonation_level elif type != gdef.TokenImpersonation: impersonation_level = 0 #: ignored else: raise ValueError("Duplicating a PrimaryToken as a TokenImpersonation require explicit <impersonation_level> parameter") winproxy.DuplicateTokenEx(self.handle, access_rigth, attributes, impersonation_level, type, newtoken) return bltn_type(self)(newtoken.value) def adjust_privileges(self, privileges): """Adjust the token privileges according to ``privileges``. This API is the `complex one` to adjust multiple privileges at once. To simply enable one privilege see :func:`enable_privilege`. :param privileges: :class:`~windows.generated_def.winstructs.TOKEN_PRIVILEGES` (or subclass as :class:`TokenPrivileges`). To easily update your token privileges use the result of :data:`privileges`. Example: >>> tok = windows.current_process.token >>> privs = tok.privileges >>> privs["SeShutdownPrivilege"] = gdef.SE_PRIVILEGE_ENABLED >>> privs["SeUndockPrivilege"] = gdef.SE_PRIVILEGE_ENABLED >>> tok.adjust_privileges(privs) """ buffsize = None if isinstance(privileges, TokenPrivilegesType): # The TokenPrivilegesType should come from a PTR via Improved buffer try: buffsize = privileges._b_base_.real_size except AttributeError as e: pass if buffsize is None: buffsize = ctypes.sizeof(privileges) winproxy.AdjustTokenPrivileges(self.handle, False, privileges, buffsize, None, None) if winproxy.GetLastError() == gdef.ERROR_NOT_ALL_ASSIGNED: # Transform this in a real WindowsError raise WindowsError(gdef.ERROR_NOT_ALL_ASSIGNED, "Failed to adjust all privileges") def enable_privilege(self, name): """Enable privilege ``name`` in the token :raises: :class:`ValueError` if :class:`Token` has no privilege ``name`` """ privs = self.privileges try: privs[name] = gdef.SE_PRIVILEGE_ENABLED except KeyError as e: # Emulate the WindowsError that would be triggered in 'adjust_privileges' ? raise ValueError("{0} has no privilege <{1}>".format(self, name)) return self.adjust_privileges(privs) def __repr__(self): flag_repr = gdef.Flag.__repr__ tid_int = int(self.TokenStatistics.TokenId) toktype = self.type if toktype == gdef.TokenPrimary: return "<{0} TokenId={1:#x} Type={2}>".format(type(self).__name__, tid_int, flag_repr(toktype)) return "<{0} TokenId={1:#x} Type={2} ImpersonationLevel={3}>".format(type(self).__name__, tid_int, flag_repr(toktype), flag_repr(self.impersonation_level))
import ctypes import functools import windows from windows import utils from windows import winproxy import windows.generated_def as gdef bltn_type = type KNOW_INTEGRITY_LEVEL = gdef.FlagMapper( gdef.SECURITY_MANDATORY_UNTRUSTED_RID, gdef.SECURITY_MANDATORY_LOW_RID, gdef.SECURITY_MANDATORY_MEDIUM_RID, gdef.SECURITY_MANDATORY_MEDIUM_PLUS_RID, gdef.SECURITY_MANDATORY_HIGH_RID, gdef.SECURITY_MANDATORY_SYSTEM_RID, gdef.SECURITY_MANDATORY_PROTECTED_PROCESS_RID ) # Voodoo to fix lookup-strangeness in class declaration def meta_craft(x): def partial_applier(infos_class, rtype): return property(functools.partial(x, infos_class=infos_class, rtype=rtype)) return partial_applier class TokenGroups(gdef.TOKEN_GROUPS): @property def _groups(self): return windows.utils.resized_array(self.Groups, self.GroupCount)
import ctypes import sys import windows import windows.generated_def as gdef from windows import winproxy from windows.pycompat import basestring from windows.winobject.token import Token, KNOW_INTEGRITY_LEVEL # Specific access right FILE_ACCESS_RIGHT = gdef.FlagMapper(gdef.FILE_READ_DATA, gdef.FILE_WRITE_DATA, gdef.FILE_APPEND_DATA, gdef.FILE_READ_EA, gdef.FILE_WRITE_EA, gdef.FILE_EXECUTE, gdef.FILE_READ_ATTRIBUTES, gdef.FILE_WRITE_ATTRIBUTES) DIRECTORY_ACCESS_RIGHT = gdef.FlagMapper( gdef.FILE_LIST_DIRECTORY, gdef.FILE_ADD_FILE, gdef.FILE_ADD_SUBDIRECTORY, gdef.FILE_READ_EA, gdef.FILE_WRITE_EA, gdef.FILE_TRAVERSE, gdef.FILE_DELETE_CHILD, gdef.FILE_READ_ATTRIBUTES, gdef.FILE_WRITE_ATTRIBUTES, ) NAMED_PIPE_ACCESS_RIGHT = gdef.FlagMapper( gdef.FILE_READ_DATA,
CERT_E_MALFORMED, CERT_E_UNTRUSTEDROOT, CERT_E_CHAINING, TRUST_E_FAIL, CERT_E_REVOKED, CERT_E_UNTRUSTEDTESTROOT, CERT_E_REVOCATION_FAILURE, CERT_E_CN_NO_MATCH, CERT_E_WRONG_USAGE, TRUST_E_EXPLICIT_DISTRUST, CERT_E_UNTRUSTEDCA, CERT_E_INVALID_POLICY, CERT_E_INVALID_NAME, CRYPT_E_FILE_ERROR, ] wintrust_return_value_mapper = gdef.FlagMapper(*wintrust_know_return_value) def check_signature(filename): """Check if ``filename`` embeds a valid signature. :return: :class:`int`: ``0`` if ``filename`` have a valid signature else the error """ file_data = WINTRUST_FILE_INFO() file_data.cbStruct = ctypes.sizeof(WINTRUST_FILE_INFO) file_data.pcwszFilePath = filename file_data.hFile = None file_data.pgKnownSubject = None WVTPolicyGUID = WINTRUST_ACTION_GENERIC_VERIFY_V2
def get_datalen(self): return self.u1.s1.DataLength datalen = property(get_datalen, set_datalen) """The length of the data""" KNOWN_ALPC_ATTRIBUTES = (gdef.ALPC_MESSAGE_SECURITY_ATTRIBUTE, gdef.ALPC_MESSAGE_VIEW_ATTRIBUTE, gdef.ALPC_MESSAGE_CONTEXT_ATTRIBUTE, gdef.ALPC_MESSAGE_HANDLE_ATTRIBUTE, gdef.ALPC_MESSAGE_TOKEN_ATTRIBUTE, gdef.ALPC_MESSAGE_DIRECT_ATTRIBUTE, gdef.ALPC_MESSAGE_WORK_ON_BEHALF_ATTRIBUTE) KNOWN_ALPC_ATTRIBUTES_MAPPING = gdef.FlagMapper(*KNOWN_ALPC_ATTRIBUTES) class MessageAttribute(gdef.ALPC_MESSAGE_ATTRIBUTES): """The attributes of an ALPC message""" ATTRIBUTE_BY_FLAG = [ (gdef.ALPC_MESSAGE_SECURITY_ATTRIBUTE, gdef.ALPC_SECURITY_ATTR), (gdef.ALPC_MESSAGE_VIEW_ATTRIBUTE, gdef.ALPC_DATA_VIEW_ATTR), (gdef.ALPC_MESSAGE_CONTEXT_ATTRIBUTE, gdef.ALPC_CONTEXT_ATTR), (gdef.ALPC_MESSAGE_HANDLE_ATTRIBUTE, gdef.ALPC_HANDLE_ATTR), (gdef.ALPC_MESSAGE_TOKEN_ATTRIBUTE, gdef.ALPC_TOKEN_ATTR), (gdef.ALPC_MESSAGE_DIRECT_ATTRIBUTE, gdef.ALPC_DIRECT_ATTR), (gdef.ALPC_MESSAGE_WORK_ON_BEHALF_ATTRIBUTE, gdef.ALPC_WORK_ON_BEHALF_ATTR), ]
import ctypes import struct import windows.alpc as alpc import windows.com import windows.generated_def as gdef if windows.pycompat.is_py3: buffer = bytes KNOW_REQUEST_TYPE = gdef.FlagMapper(gdef.RPC_REQUEST_TYPE_CALL, gdef.RPC_REQUEST_TYPE_BIND) KNOW_RESPONSE_TYPE = gdef.FlagMapper(gdef.RPC_RESPONSE_TYPE_FAIL, gdef.RPC_RESPONSE_TYPE_SUCCESS, gdef.RPC_RESPONSE_TYPE_BIND_OK) KNOWN_RPC_ERROR_CODE = gdef.FlagMapper( gdef.ERROR_INVALID_HANDLE, gdef.RPC_X_BAD_STUB_DATA, gdef.RPC_S_UNKNOWN_IF, gdef.RPC_S_PROTOCOL_ERROR, gdef.RPC_S_UNSUPPORTED_TRANS_SYN, gdef.RPC_S_PROCNUM_OUT_OF_RANGE) NOT_USED = 0xBAADF00D class ALPC_RPC_BIND(ctypes.Structure): _pack_ = 1 _fields_ = [ ("request_type", gdef.DWORD), ("UNK1", gdef.DWORD), ("UNK2", gdef.DWORD), ("target", gdef.RPC_IF_ID), ("flags", gdef.DWORD),
CFGMGR32_ERRORS = gdef.FlagMapper( gdef.CR_SUCCESS, gdef.CR_DEFAULT, gdef.CR_OUT_OF_MEMORY, gdef.CR_INVALID_POINTER, gdef.CR_INVALID_FLAG, gdef.CR_INVALID_DEVNODE, gdef.CR_INVALID_DEVINST, gdef.CR_INVALID_RES_DES, gdef.CR_INVALID_LOG_CONF, gdef.CR_INVALID_ARBITRATOR, gdef.CR_INVALID_NODELIST, gdef.CR_DEVNODE_HAS_REQS, gdef.CR_DEVINST_HAS_REQS, gdef.CR_INVALID_RESOURCEID, gdef.CR_DLVXD_NOT_FOUND, gdef.CR_NO_SUCH_DEVNODE, gdef.CR_NO_SUCH_DEVINST, gdef.CR_NO_MORE_LOG_CONF, gdef.CR_NO_MORE_RES_DES, gdef.CR_ALREADY_SUCH_DEVNODE, gdef.CR_ALREADY_SUCH_DEVINST, gdef.CR_INVALID_RANGE_LIST, gdef.CR_INVALID_RANGE, gdef.CR_FAILURE, gdef.CR_NO_SUCH_LOGICAL_DEV, gdef.CR_CREATE_BLOCKED, gdef.CR_NOT_SYSTEM_VM, gdef.CR_REMOVE_VETOED, gdef.CR_APM_VETOED, gdef.CR_INVALID_LOAD_TYPE, gdef.CR_BUFFER_SMALL, gdef.CR_NO_ARBITRATOR, gdef.CR_NO_REGISTRY_HANDLE, gdef.CR_REGISTRY_ERROR, gdef.CR_INVALID_DEVICE_ID, gdef.CR_INVALID_DATA, gdef.CR_INVALID_API, gdef.CR_DEVLOADER_NOT_READY, gdef.CR_NEED_RESTART, gdef.CR_NO_MORE_HW_PROFILES, gdef.CR_DEVICE_NOT_THERE, gdef.CR_NO_SUCH_VALUE, gdef.CR_WRONG_TYPE, gdef.CR_INVALID_PRIORITY, gdef.CR_NOT_DISABLEABLE, gdef.CR_FREE_RESOURCES, gdef.CR_QUERY_VETOED, gdef.CR_CANT_SHARE_IRQ, gdef.CR_NO_DEPENDENT, gdef.CR_SAME_RESOURCES, gdef.CR_NO_SUCH_REGISTRY_KEY, gdef.CR_INVALID_MACHINENAME, gdef.CR_REMOTE_COMM_FAILURE, gdef.CR_MACHINE_UNAVAILABLE, gdef.CR_NO_CM_SERVICES, gdef.CR_ACCESS_DENIED, gdef.CR_CALL_NOT_IMPLEMENTED, gdef.CR_INVALID_PROPERTY, gdef.CR_DEVICE_INTERFACE_ACTIVE, gdef.CR_NO_SUCH_DEVICE_INTERFACE, gdef.CR_INVALID_REFERENCE_STRING, gdef.CR_INVALID_CONFLICT_LIST, gdef.CR_INVALID_INDEX, gdef.CR_INVALID_STRUCTURE_SIZE )
:type: [:class:`ResourceDescriptor`] -- A list of [:class:`ResourceDescriptor`] """ return list(self.get_resources_for_type(gdef.ResType_All)) def __repr__(self): return "<{0}>".format(type(self).__name__) ResType_Mapper = gdef.FlagMapper( gdef.ResType_None, gdef.ResType_Mem, gdef.ResType_IO, gdef.ResType_DMA, gdef.ResType_IRQ, gdef.ResType_BusNumber, gdef.ResType_MemLarge, gdef.ResType_ClassSpecific, gdef.ResType_DevicePrivate, gdef.ResType_MfCardConfig, gdef.ResType_PcCardConfig, ) class ResourceDescriptor(gdef.HANDLE): """Describe a resource allocated or reserved by a device instance. This class is a base class, all resources returned by :class:`LogicalConfiguration` should be one of the following: * :class:`ResourceNoType` * :class:`MemoryResource` * :class:`IoResource`
import ctypes import sys import windows import windows.generated_def as gdef from windows import winproxy from windows.winobject.token import Token # Specific access right FILE_ACCESS_RIGHT = gdef.FlagMapper(gdef.FILE_READ_DATA, gdef.FILE_WRITE_DATA, gdef.FILE_APPEND_DATA, gdef.FILE_READ_EA, gdef.FILE_WRITE_EA, gdef.FILE_EXECUTE, gdef.FILE_READ_ATTRIBUTES, gdef.FILE_WRITE_ATTRIBUTES) DIRECTORY_ACCESS_RIGHT = gdef.FlagMapper( gdef.FILE_LIST_DIRECTORY, gdef.FILE_ADD_FILE, gdef.FILE_ADD_SUBDIRECTORY, gdef.FILE_READ_EA, gdef.FILE_WRITE_EA, gdef.FILE_TRAVERSE, gdef.FILE_DELETE_CHILD, gdef.FILE_READ_ATTRIBUTES, gdef.FILE_WRITE_ATTRIBUTES, ) NAMED_PIPE_ACCESS_RIGHT = gdef.FlagMapper( gdef.FILE_READ_DATA, gdef.FILE_WRITE_DATA,
class Certificate(gdef.CERT_CONTEXT): """Represent a Certificate """ @property def raw_serial(self): """The raw serial number of the certificate. :type: [:class:`int`]: A list of int ``0 <= x <= 255``""" serial_number = self.pCertInfo[0].SerialNumber return [(c & 0xff) for c in serial_number.pbData[:serial_number.cbData][::-1]] @property def serial(self): """The string representation of the certificate's serial. :type: :class:`str` """ serial_bytes = self.raw_serial return " ".join("{:02x}".format(x) for x in serial_bytes) def get_name(self, nametype=gdef.CERT_NAME_SIMPLE_DISPLAY_TYPE, param_type=0, flags=0): """Retrieve the subject or issuer name of the certificate. See `CertGetNameStringA <https://msdn.microsoft.com/en-us/library/windows/desktop/aa376086(v=vs.85).aspx>`_ :returns: :class:`str` """ if nametype == gdef.CERT_NAME_RDN_TYPE: param_type = gdef.DWORD(param_type) param_type = gdef.LPDWORD(param_type) size = winproxy.CertGetNameStringA(self, nametype, flags, param_type, None, 0) namebuff = ctypes.c_buffer(size) size = winproxy.CertGetNameStringA(self, nametype, flags, param_type, namebuff, size) return namebuff[:-1] name = property(get_name) """The name of the certificate. :type: :class:`str`""" def raw_hash(self): size = gdef.DWORD(100) buffer = ctypes.c_buffer(size.value) winproxy.CryptHashCertificate(None, 0, 0, self.pbCertEncoded, self.cbCertEncoded, ctypes.cast(buffer, gdef.LPBYTE), size) return buffer[:size.value] @property def thumbprint(self): """The thumbprint of the certificate (which is the sha1 of the encoded cert). Example: >>> x <Certificate "YOLO2" serial="6f 1d 3e 7d d9 77 59 a9 4c 1c 53 dc 80 db 0c fe"> >>> x.thumbprint 'E2 A2 DB 76 A1 DD 8E 70 0D C6 9F CB 71 CF 29 12 C6 D9 78 97' :type: :class:`str` """ return " ".join("{:02X}".format(x) for x in bytearray(self.raw_hash())) @property def distinguished_name(self): """The distinguished name (DN) of the certificate. Example: >>> x <Certificate "Microsoft Windows Production PCA 2011" serial="61 07 76 56 00 00 00 00 00 08"> >>> x.distinguished_name 'C=US, S=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Windows Production PCA 2011' :type: :class:`str` """ return self.get_name(gdef.CERT_NAME_RDN_TYPE, gdef.CERT_X500_NAME_STR) @property def issuer(self): """The name of the certificate's issuer. :type: :class:`str`""" return self.get_name(flags=gdef.CERT_NAME_ISSUER_FLAG) @property def store(self): """The certificate store that contains the certificate :type: :class:`CertificateStore` """ return CertificateStore(self.hCertStore) def get_raw_certificate_chains(self): # Rename to all_chains ? chain_context = EPCCERT_CHAIN_CONTEXT() enhkey_usage = gdef.CERT_ENHKEY_USAGE() enhkey_usage.cUsageIdentifier = 0 enhkey_usage.rgpszUsageIdentifier = None cert_usage = gdef.CERT_USAGE_MATCH() cert_usage.dwType = gdef.USAGE_MATCH_TYPE_AND cert_usage.Usage = enhkey_usage chain_para = gdef.CERT_CHAIN_PARA() chain_para.cbSize = ctypes.sizeof(chain_para) chain_para.RequestedUsage = cert_usage winproxy.CertGetCertificateChain(None, self, None, self.hCertStore, ctypes.byref(chain_para), 0, None, ctypes.byref(chain_context)) # Lower chains ? # winproxy.CertGetCertificateChain(None, self, None, self[0].hCertStore, ctypes.byref(chain_para), 0x80, None, ctypes.byref(chain_context)) #return CertficateChain(chain_context) return chain_context @property # fixedproperty ? def chains(self): """The list of chain context available for this certificate. Each elements of this list is a list of ``Certificate`` that should go from the ``self`` certificate to a trusted certificate. :type: [[:class:`Certificate`]] -- A list of chain (list) of :class:`Certificate` """ chain_context = self.get_raw_certificate_chains() res = [] for chain in chain_context.chains: chain_res = [elt.cert for elt in chain.elements] res.append(chain_res) return res # API Arround CertSelectCertificateChains ? # https://msdn.microsoft.com/en-us/library/windows/desktop/dd433797(v=vs.85).aspx def duplicate(self): """Duplicate the certificate by incrementing the internal refcount. (see `CertDuplicateCertificateContext <https://msdn.microsoft.com/en-us/library/windows/desktop/aa376045(v=vs.85).aspx>`_) note: The object returned is ``self`` :return: :class:`Certificate` """ res = winproxy.CertDuplicateCertificateContext(self) # Check what the doc says: the pointer returned is actually the PCERT in parameter # Only the refcount is incremented # This postulate allow us to return 'self' directly # https://msdn.microsoft.com/en-us/library/windows/desktop/aa376045(v=vs.85).aspx if not ctypes.addressof(res[0]) == ctypes.addressof(self): raise ValueError( "CertDuplicateCertificateContext did not returned the argument (check doc)" ) return self def view(self, title=None): return windows.winproxy.CryptUIDlgViewContext( gdef.CERT_STORE_CERTIFICATE_CONTEXT, ctypes.byref(self), None, title, 0, None) KNOWN_PROPERTIES_VALUES = gdef.FlagMapper( gdef.CERT_KEY_PROV_HANDLE_PROP_ID, gdef.CERT_KEY_PROV_INFO_PROP_ID, gdef.CERT_SHA1_HASH_PROP_ID, gdef.CERT_MD5_HASH_PROP_ID, gdef.CERT_HASH_PROP_ID, gdef.CERT_KEY_CONTEXT_PROP_ID, gdef.CERT_KEY_SPEC_PROP_ID, gdef.CERT_IE30_RESERVED_PROP_ID, gdef.CERT_PUBKEY_HASH_RESERVED_PROP_ID, gdef.CERT_ENHKEY_USAGE_PROP_ID, gdef.CERT_CTL_USAGE_PROP_ID, gdef.CERT_NEXT_UPDATE_LOCATION_PROP_ID, gdef.CERT_FRIENDLY_NAME_PROP_ID, gdef.CERT_PVK_FILE_PROP_ID, gdef.CERT_DESCRIPTION_PROP_ID, gdef.CERT_ACCESS_STATE_PROP_ID, gdef.CERT_SIGNATURE_HASH_PROP_ID, gdef.CERT_SMART_CARD_DATA_PROP_ID, gdef.CERT_EFS_PROP_ID, gdef.CERT_FORTEZZA_DATA_PROP_ID, gdef.CERT_ARCHIVED_PROP_ID, gdef.CERT_KEY_IDENTIFIER_PROP_ID, gdef.CERT_AUTO_ENROLL_PROP_ID, gdef.CERT_PUBKEY_ALG_PARA_PROP_ID, gdef.CERT_CROSS_CERT_DIST_POINTS_PROP_ID, gdef.CERT_ISSUER_PUBLIC_KEY_MD5_HASH_PROP_ID, gdef.CERT_SUBJECT_PUBLIC_KEY_MD5_HASH_PROP_ID, gdef.CERT_ENROLLMENT_PROP_ID, gdef.CERT_DATE_STAMP_PROP_ID, gdef.CERT_ISSUER_SERIAL_NUMBER_MD5_HASH_PROP_ID, gdef.CERT_SUBJECT_NAME_MD5_HASH_PROP_ID, gdef.CERT_EXTENDED_ERROR_INFO_PROP_ID, gdef.CERT_RENEWAL_PROP_ID, gdef.CERT_ARCHIVED_KEY_HASH_PROP_ID, gdef.CERT_AUTO_ENROLL_RETRY_PROP_ID, gdef.CERT_AIA_URL_RETRIEVED_PROP_ID, gdef.CERT_AUTHORITY_INFO_ACCESS_PROP_ID, gdef.CERT_BACKED_UP_PROP_ID, gdef.CERT_OCSP_RESPONSE_PROP_ID, gdef.CERT_REQUEST_ORIGINATOR_PROP_ID, gdef.CERT_SOURCE_LOCATION_PROP_ID) def enum_properties(self): prop = 0 res = [] while True: prop = winproxy.CertEnumCertificateContextProperties(self, prop) if not prop: return res res.append(self.KNOWN_PROPERTIES_VALUES[prop]) raise RuntimeError("Unreachable code") properties = property(enum_properties) """The properties of the certificate :type: [:class:`int` or :class:`~windows.generated_def.Flag`] -- A list of property ID """ #def get_property(self): # https://msdn.microsoft.com/en-us/library/windows/desktop/aa376079(v=vs.85).aspx # - Usefull: # CERT_SHA1_HASH_PROP_ID def get_property(self, prop): "TODO: DOC :D + auto-type ?" datasize = gdef.DWORD() windows.winproxy.CertGetCertificateContextProperty( self, prop, None, datasize) buf = (gdef.BYTE * datasize.value)() windows.winproxy.CertGetCertificateContextProperty( self, prop, buf, datasize) return bytearray(buf) @property def encoded(self): """The encoded certificate. :type: :class:`bytearray`""" return bytearray(self.pbCertEncoded[:self.cbCertEncoded]) @property def version(self): """The version number of the certificate :type: :class:`int` """ return self.pCertInfo[0].dwVersion @classmethod def from_file(cls, filename): """Create a :class:`Certificate` from the file ``filename`` :return: :class:`Certificate` """ with open(filename, "rb") as f: data = f.read() buf = (ctypes.c_ubyte * len(data))(*bytearray(data)) pcert = windows.winproxy.CertCreateCertificateContext( windows.crypto.DEFAULT_ENCODING, buf, len(data)) return cls.from_pointer(pcert) @classmethod def from_buffer(cls, data): """Create a :class:`Certificate` from the buffer ``data`` :return: :class:`Certificate` """ buf = (ctypes.c_ubyte * len(data))(*bytearray(data)) pcert = windows.winproxy.CertCreateCertificateContext( windows.crypto.DEFAULT_ENCODING, buf, len(data)) return cls.from_pointer(pcert) @classmethod def from_pointer(self, ptr): return ctypes.cast(ptr, ctypes.POINTER(Certificate))[0] def __eq__(self, other): if not isinstance(other, Certificate): return NotImplemented return windows.winproxy.CertCompareCertificate(DEFAULT_ENCODING, self.pCertInfo, other.pCertInfo) def __repr__(self): return '<{0} "{1}" serial="{2}">'.format( type(self).__name__, self.name, self.serial)
CRYPT_OBJECT_FORMAT_TYPE = [ gdef.CERT_QUERY_OBJECT_FILE, gdef.CERT_QUERY_OBJECT_BLOB, gdef.CERT_QUERY_CONTENT_CERT, gdef.CERT_QUERY_CONTENT_CTL, gdef.CERT_QUERY_CONTENT_CRL, gdef.CERT_QUERY_CONTENT_SERIALIZED_STORE, gdef.CERT_QUERY_CONTENT_SERIALIZED_CERT, gdef.CERT_QUERY_CONTENT_SERIALIZED_CTL, gdef.CERT_QUERY_CONTENT_SERIALIZED_CRL, gdef.CERT_QUERY_CONTENT_PKCS7_SIGNED, gdef.CERT_QUERY_CONTENT_PKCS7_UNSIGNED, gdef.CERT_QUERY_CONTENT_PKCS7_SIGNED_EMBED, gdef.CERT_QUERY_CONTENT_PKCS10, gdef.CERT_QUERY_CONTENT_PFX, gdef.CERT_QUERY_CONTENT_CERT_PAIR, gdef.CERT_QUERY_CONTENT_PFX_AND_LOAD ] CRYPT_OBJECT_FORMAT_TYPE_DICT = gdef.FlagMapper(*CRYPT_OBJECT_FORMAT_TYPE) ## Move CryptObject to new .py ? class CryptObject(object): """Extract information from an CryptoAPI object. (see `CryptQueryObject <https://msdn.microsoft.com/en-us/library/windows/desktop/aa380264(v=vs.85).aspx>`_) Current main use is extracting the signers certificates from a PE file. """ MSG_PARAM_KNOW_TYPES = { gdef.CMSG_SIGNER_INFO_PARAM: gdef.CMSG_SIGNER_INFO, gdef.CMSG_SIGNER_COUNT_PARAM: gdef.DWORD, gdef.CMSG_CERT_COUNT_PARAM: gdef.DWORD }
class System(object): """The state of the current ``Windows`` system ``Python`` is running on""" network = network.Network() """Object of class :class:`windows.winobject.network.Network`""" registry = registry.Registry() """Object of class :class:`windows.winobject.registry.Registry`""" @property def processes(self): """The list of running processes :type: [:class:`~windows.winobject.process.WinProcess`] -- A list of Process """ return self.enumerate_processes() @property def threads(self): """The list of running threads :type: [:class:`~windows.winobject.process.WinThread`] -- A list of Thread """ return self.enumerate_threads_setup_owners() @property def logicaldrives(self): """List of logical drives [C:\, ...] :type: [:class:`~windows.winobject.volume.LogicalDrive`] -- A list of LogicalDrive """ return volume.enum_logical_drive() @property def services(self): """The list of services :type: [:class:`~windows.winobject.service.ServiceA`] -- A list of Service""" return service.enumerate_services() @property def handles(self): """The list of system handles :type: [:class:`~windows.winobject.handle.Handle`] -- A list of Hanlde""" return handle.enumerate_handles() @utils.fixedpropety def bitness(self): """The bitness of the system :type: :class:`int` -- 32 or 64 """ if os.environ["PROCESSOR_ARCHITECTURE"].lower() != "x86": return 64 if "PROCESSOR_ARCHITEW6432" in os.environ: return 64 return 32 @utils.fixedpropety def wmi(self): r"""An object to perform wmi requests to various namespaces :type: :class:`~windows.winobject.wmi.WmiManager`""" return wmi.WmiManager() #TODO: use GetComputerNameExA ? and recover other names ? @utils.fixedpropety def computer_name(self): """The name of the computer :type: :class:`str` """ size = DWORD(0x1000) buf = ctypes.c_buffer(size.value) winproxy.GetComputerNameA(buf, ctypes.byref(size)) return buf[:size.value] @utils.fixedpropety def version(self): """The version of the system :type: (:class:`int`, :class:`int`) -- (Major, Minor) """ data = self.get_version() result = data.dwMajorVersion, data.dwMinorVersion if result == (6, 2): result_str = self.get_file_version("kernel32") result_tup = [int(x) for x in result_str.split(".")] result = tuple(result_tup[:2]) return result @utils.fixedpropety def version_name(self): """The name of the system version, values are: * Windows Server 2016 * Windows 10 * Windows Server 2012 R2 * Windows 8.1 * Windows Server 2012 * Windows 8 * Windows Server 2008 * Windows 7 * Windows Server 2008 * Windows Vista * Windows XP Professional x64 Edition * TODO: version (5.2) + is_workstation + bitness == 32 (don't even know if possible..) * Windows Server 2003 R2 * Windows Server 2003 * Windows XP * Windows 2000 * "Unknow Windows <version={0} | is_workstation={1}>".format(version, is_workstation) :type: :class:`str` """ version = self.version is_workstation = self.product_type == VER_NT_WORKSTATION if version == (10, 0): return ["Windows Server 2016", "Windows 10"][is_workstation] elif version == (6, 3): return ["Windows Server 2012 R2", "Windows 8.1"][is_workstation] elif version == (6, 2): return ["Windows Server 2012", "Windows 8"][is_workstation] elif version == (6, 1): return ["Windows Server 2008 R2", "Windows 7"][is_workstation] elif version == (6, 0): return ["Windows Server 2008", "Windows Vista"][is_workstation] elif version == (5, 2): metric = winproxy.GetSystemMetrics(SM_SERVERR2) if is_workstation: if self.bitness == 64: return "Windows XP Professional x64 Edition" else: return "TODO: version (5.2) + is_workstation + bitness == 32" elif metric != 0: return "Windows Server 2003 R2" else: return "Windows Server 2003" elif version == (5, 1): return "Windows XP" elif version == (5, 0): return "Windows 2000" else: return "Unknow Windows <version={0} | is_workstation={1}>".format( version, is_workstation) VERSION_MAPPER = gdef.FlagMapper(VER_NT_WORKSTATION, VER_NT_DOMAIN_CONTROLLER, VER_NT_SERVER) @utils.fixedpropety def product_type(self): """The product type, value might be: * VER_NT_WORKSTATION(0x1L) * VER_NT_DOMAIN_CONTROLLER(0x2L) * VER_NT_SERVER(0x3L) :type: :class:`long` or :class:`int` (or subclass) """ version = self.get_version() return self.VERSION_MAPPER[version.wProductType] @utils.fixedpropety def windir(self): buffer = ctypes.c_buffer(0x100) reslen = winproxy.GetWindowsDirectoryA(buffer) return buffer[:reslen] def get_version(self): data = windows.generated_def.OSVERSIONINFOEXA() data.dwOSVersionInfoSize = ctypes.sizeof(data) winproxy.GetVersionExA( ctypes.cast(ctypes.pointer(data), ctypes.POINTER(windows.generated_def.OSVERSIONINFOA))) return data def get_file_version(self, name): size = winproxy.GetFileVersionInfoSizeA(name) buf = ctypes.c_buffer(size) winproxy.GetFileVersionInfoA(name, 0, size, buf) bufptr = PVOID() bufsize = UINT() winproxy.VerQueryValueA(buf, "\\VarFileInfo\\Translation", ctypes.byref(bufptr), ctypes.byref(bufsize)) bufstr = ctypes.cast(bufptr, LPCSTR) tup = struct.unpack("<HH", bufstr.value[:4]) req = "{0:04x}{1:04x}".format(*tup) winproxy.VerQueryValueA( buf, "\\StringFileInfo\\{0}\\ProductVersion".format(req), ctypes.byref(bufptr), ctypes.byref(bufsize)) bufstr = ctypes.cast(bufptr, LPCSTR) return bufstr.value @utils.fixedpropety def build_number(self): # This returns the last version where ntdll was updated # Should look at HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion # values: CurrentBuild + UBR return self.get_file_version("comctl32") @staticmethod def enumerate_processes(): dbgprint("Enumerating processes with CreateToolhelp32Snapshot", "SLOW") process_entry = PROCESSENTRY32() process_entry.dwSize = ctypes.sizeof(process_entry) snap = winproxy.CreateToolhelp32Snapshot(gdef.TH32CS_SNAPPROCESS, 0) winproxy.Process32First(snap, process_entry) res = [] res.append(process.WinProcess._from_PROCESSENTRY32(process_entry)) while winproxy.Process32Next(snap, process_entry): res.append(process.WinProcess._from_PROCESSENTRY32(process_entry)) winproxy.CloseHandle(snap) return res @staticmethod def enumerate_threads_generator(): # Ptet dangereux, parce que on yield la meme THREADENTRY32 a chaque fois dbgprint( "Enumerating threads with CreateToolhelp32Snapshot <generator>", "SLOW") thread_entry = THREADENTRY32() thread_entry.dwSize = ctypes.sizeof(thread_entry) snap = winproxy.CreateToolhelp32Snapshot(gdef.TH32CS_SNAPTHREAD, 0) dbgprint( "New handle CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD) <generator> | {0:#x}" .format(snap), "HANDLE") try: winproxy.Thread32First(snap, thread_entry) yield thread_entry while winproxy.Thread32Next(snap, thread_entry): yield thread_entry finally: winproxy.CloseHandle(snap) dbgprint( "CLOSE CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD) <generator> | {0:#x}" .format(snap), "HANDLE") @staticmethod def enumerate_threads(): return [ WinThread._from_THREADENTRY32(th) for th in System.enumerate_threads_generator() ] def enumerate_threads_setup_owners(self): # Enumerating threads is a special operation concerning the owner process. # We may not be able to retrieve the name of the owning process by normal way # (as we need to get a handle on the process) # So, this implementation of enumerate_thread also setup the owner with the result of enumerate_processes dbgprint( "Enumerating threads with CreateToolhelp32Snapshot and setup owner", "SLOW") # One snap for both enum to be prevent race snap = winproxy.CreateToolhelp32Snapshot( gdef.TH32CS_SNAPTHREAD | gdef.TH32CS_SNAPPROCESS, 0) process_entry = PROCESSENTRY32() process_entry.dwSize = ctypes.sizeof(process_entry) winproxy.Process32First(snap, process_entry) processes = [] processes.append( process.WinProcess._from_PROCESSENTRY32(process_entry)) while winproxy.Process32Next(snap, process_entry): processes.append( process.WinProcess._from_PROCESSENTRY32(process_entry)) # Forge a dict pid -> process proc_dict = {proc.pid: proc for proc in processes} thread_entry = THREADENTRY32() thread_entry.dwSize = ctypes.sizeof(thread_entry) threads = [] winproxy.Thread32First(snap, thread_entry) parent = proc_dict[thread_entry.th32OwnerProcessID] threads.append( process.WinThread._from_THREADENTRY32(thread_entry, owner=parent)) while winproxy.Thread32Next(snap, thread_entry): parent = proc_dict[thread_entry.th32OwnerProcessID] threads.append( process.WinThread._from_THREADENTRY32(thread_entry, owner=parent)) winproxy.CloseHandle(snap) return threads