How is it possible to lock windows from a windows service? - python

I'm writing a windows service in Python, which at some point needs to lock windows if a specific condition happens (for example the person leaves the pc un-attended for some time).
The solution I found was to use user32.LockWorkStation() using the ctypes module.
ctypes.windll.user32.LockWorkStation()
However, after failing at locking the OS, I noticed the LockWorkstation specifically works in the
interactive [desktop] mode which windows services do not support. it actually wasted quite a bit of my time as it works when debugging! any way,
Quoting from LockWorkStation function:
The LockWorkStation function is callable only by processes running on
the interactive desktop. In addition, the user must be logged on, and
the workstation cannot already be locked.
windows services do have a property where you can allow it to interact with the desktop (on log on tab), however, this feature is if I'm not mistaken, disabled on latest versions of windows and apart from that, is not a good idea to enable it either.
Also quoting from MSDN:
Services do not have message loops, unless they are allowed to
interact with the desktop. If the message loop is not provided by a
hidden form, as in this example, the service must be run under the
local system account, and manual intervention is required to enable
interaction with the desktop. That is, the administrator must manually
check the Allow service to interact with desktop check box on the Log
On tab of the service properties dialog box. In that case, a message
loop is automatically provided. This option is available only when the
service is run under the local system account. Interaction with the
desktop cannot be enabled programmatically.
Therefore I'm looking for other solutions that would allow me to lock the windows from a windows service. How can I achieve this ?

Summary
In order to interact with user session in a service, you first need to use a user session id. Basically you'll need to use WTSGetActiveConsoleSessionId, WTSGetActiveConsoleSessionId and CreateEnvironmentBlock prior to calling CreateProcessAsUser. Here is the snippet that does the trick:
import win32process
import win32con
import win32ts
console_session_id = win32ts.WTSGetActiveConsoleSessionId()
console_user_token = win32ts.WTSQueryUserToken(console_session_id)
startup = win32process.STARTUPINFO()
priority = win32con.NORMAL_PRIORITY_CLASS
environment = win32profile.CreateEnvironmentBlock(console_user_token, False)
handle, thread_id ,pid, tid = win32process.CreateProcessAsUser(console_user_token, None, "rundll32.exe user32.dll,LockWorkStation", None, None, True, priority, environment, None, startup)
If you need to call a specific application you may call this like this:
win32process.CreateProcessAsUser(console_user_token, your_app_exe, app_args, None, None, True, priority, environment, None, startup)
This is actually how services in windows interact with user sessions. Using this method, you no longer need the user credentials.
Long Explanation:
When it comes to Windows services to access/interact with user session (session >0). It's usually recommended to use CreateProcessAsUser(). one would go on and do something like this (ref):
user = "username"
pword = "123456"
domain = "." # means current domain
logontype = win32con.LOGON32_LOGON_INTERACTIVE
# some may suggest to use BATCH mode instead in case you fail! but this doesn't work either!
# logontype = win32con.LOGON32_LOGON_BATCH
provider = win32con.LOGON32_PROVIDER_WINNT50
token = win32security.LogonUser(user, domain, pword, logontype, provider)
startup = win32process.STARTUPINFO()
process_information = PROCESS_INFORMATION()
cwd = os.path.dirname(__file__)
lock_file = os.path.join(cwd,'system_locker.exe')
appname = lock_file
priority = win32con.NORMAL_PRIORITY_CLASS
result = win32process.CreateProcessAsUser(token, appname, None, None, None, True, priority, None, None, startup)
but if you go this way, You'll face the error :
(1314, 'CreateProcessAsUser', 'A required privilege is not held by the client.')
There are many suggestions to get rid of this issue, such as disabling UAC, etc. but none will work and if you look at the MSDN documentation about CreateProcessAsUser, you'll see :
Typically, the process that calls the CreateProcessAsUser function
must have the SE_INCREASE_QUOTA_NAME privilege and may require the
SE_ASSIGNPRIMARYTOKEN_NAME privilege if the token is not assignable.
If this function fails with ERROR_PRIVILEGE_NOT_HELD (1314), use the
CreateProcessWithLogonW function instead. CreateProcessWithLogonW
requires no special privileges, but the specified user account must be
allowed to log on interactively. Generally, it is best to use
CreateProcessWithLogonW to create a process with alternate
credentials.
which means to use CreateProcessWithLogonW. If one goes and tries this for example like this :
from ctypes import *
from ctypes.wintypes import *
INVALID_HANDLE_VALUE = -1
CREATE_UNICODE_ENVIRONMENT = 0x00000400
CData = Array.__base__
LPBYTE = POINTER(BYTE)
class PROCESS_INFORMATION(Structure):
'''http://msdn.microsoft.com/en-us/library/ms684873'''
_fields_ = [
('hProcess', HANDLE),
('hThread', HANDLE),
('dwProcessId', DWORD),
('dwThreadId', DWORD),
]
LPPROCESS_INFORMATION = POINTER(PROCESS_INFORMATION)
class STARTUPINFOW(Structure):
'http://msdn.microsoft.com/en-us/library/ms686331'
_fields_ = [
('cb', DWORD),
('lpReserved', LPWSTR),
('lpDesktop', LPWSTR),
('lpTitle', LPWSTR),
('dwX', DWORD),
('dwY', DWORD),
('dwXSize', DWORD),
('dwYSize', DWORD),
('dwXCountChars', DWORD),
('dwYCountChars', DWORD),
('dwFillAttribute', DWORD),
('dwFlags', DWORD),
('wShowWindow', WORD),
('cbReserved2', WORD),
('lpReserved2', LPBYTE),
('hStdInput', HANDLE),
('hStdOutput', HANDLE),
('hStdError', HANDLE),
]
LPSTARTUPINFOW = POINTER(STARTUPINFOW)
# http://msdn.microsoft.com/en-us/library/ms682431
windll.advapi32.CreateProcessWithLogonW.restype = BOOL
windll.advapi32.CreateProcessWithLogonW.argtypes = [
LPCWSTR, # lpUsername
LPCWSTR, # lpDomain
LPCWSTR, # lpPassword
DWORD, # dwLogonFlags
LPCWSTR, # lpApplicationName
LPWSTR, # lpCommandLine (inout)
DWORD, # dwCreationFlags
LPCWSTR, # lpEnvironment (force Unicode)
LPCWSTR, # lpCurrentDirectory
LPSTARTUPINFOW, # lpStartupInfo
LPPROCESS_INFORMATION, # lpProcessInfo (out)
]
def CreateProcessWithLogonW(
lpUsername=None,
lpDomain=None,
lpPassword=None,
dwLogonFlags=0,
lpApplicationName=None,
lpCommandLine=None,
dwCreationFlags=0,
lpEnvironment=None,
lpCurrentDirectory=None,
startupInfo=None
):
if (lpCommandLine is not None and
not isinstance(lpCommandLine, CData)
):
lpCommandLine = create_unicode_buffer(lpCommandLine)
dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT
if startupInfo is None:
startupInfo = STARTUPINFOW(sizeof(STARTUPINFOW))
processInformation = PROCESS_INFORMATION(
INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE)
success = windll.advapi32.CreateProcessWithLogonW(
lpUsername, lpDomain, lpPassword, dwLogonFlags, lpApplicationName,
lpCommandLine, dwCreationFlags, lpEnvironment, lpCurrentDirectory,
byref(startupInfo), byref(processInformation))
if not success:
raise WinError()
return processInformation
....
result = CreateProcessWithLogonW(user, domain, pword, 0, None, "rundll32.exe user32.dll,LockWorkStation")
He/she will face the error :
(13, 'Access is denied.', None, 5)
Another similar implementation that fails is as follows (taken from):
from ctypes import wintypes
from subprocess import PIPE
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
advapi32 = ctypes.WinDLL('advapi32', use_last_error=True)
ERROR_INVALID_HANDLE = 0x0006
INVALID_HANDLE_VALUE = wintypes.HANDLE(-1).value
INVALID_DWORD_VALUE = wintypes.DWORD(-1).value
DEBUG_PROCESS = 0x00000001
DEBUG_ONLY_THIS_PROCESS = 0x00000002
CREATE_SUSPENDED = 0x00000004
DETACHED_PROCESS = 0x00000008
CREATE_NEW_CONSOLE = 0x00000010
CREATE_NEW_PROCESS_GROUP = 0x00000200
CREATE_UNICODE_ENVIRONMENT = 0x00000400
CREATE_SEPARATE_WOW_VDM = 0x00000800
CREATE_SHARED_WOW_VDM = 0x00001000
INHERIT_PARENT_AFFINITY = 0x00010000
CREATE_PROTECTED_PROCESS = 0x00040000
EXTENDED_STARTUPINFO_PRESENT = 0x00080000
CREATE_BREAKAWAY_FROM_JOB = 0x01000000
CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000
CREATE_DEFAULT_ERROR_MODE = 0x04000000
CREATE_NO_WINDOW = 0x08000000
STARTF_USESHOWWINDOW = 0x00000001
STARTF_USESIZE = 0x00000002
STARTF_USEPOSITION = 0x00000004
STARTF_USECOUNTCHARS = 0x00000008
STARTF_USEFILLATTRIBUTE = 0x00000010
STARTF_RUNFULLSCREEN = 0x00000020
STARTF_FORCEONFEEDBACK = 0x00000040
STARTF_FORCEOFFFEEDBACK = 0x00000080
STARTF_USESTDHANDLES = 0x00000100
STARTF_USEHOTKEY = 0x00000200
STARTF_TITLEISLINKNAME = 0x00000800
STARTF_TITLEISAPPID = 0x00001000
STARTF_PREVENTPINNING = 0x00002000
SW_HIDE = 0
SW_SHOWNORMAL = 1
SW_SHOWMINIMIZED = 2
SW_SHOWMAXIMIZED = 3
SW_SHOWNOACTIVATE = 4
SW_SHOW = 5
SW_MINIMIZE = 6
SW_SHOWMINNOACTIVE = 7
SW_SHOWNA = 8
SW_RESTORE = 9
SW_SHOWDEFAULT = 10 # ~STARTUPINFO
SW_FORCEMINIMIZE = 11
LOGON_WITH_PROFILE = 0x00000001
LOGON_NETCREDENTIALS_ONLY = 0x00000002
STD_INPUT_HANDLE = wintypes.DWORD(-10).value
STD_OUTPUT_HANDLE = wintypes.DWORD(-11).value
STD_ERROR_HANDLE = wintypes.DWORD(-12).value
class HANDLE(wintypes.HANDLE):
__slots__ = 'closed',
def __int__(self):
return self.value or 0
def Detach(self):
if not getattr(self, 'closed', False):
self.closed = True
value = int(self)
self.value = None
return value
raise ValueError("already closed")
def Close(self, CloseHandle=kernel32.CloseHandle):
if self and not getattr(self, 'closed', False):
CloseHandle(self.Detach())
__del__ = Close
def __repr__(self):
return "%s(%d)" % (self.__class__.__name__, int(self))
class PROCESS_INFORMATION(ctypes.Structure):
"""https://msdn.microsoft.com/en-us/library/ms684873"""
__slots__ = '_cached_hProcess', '_cached_hThread'
_fields_ = (('_hProcess', HANDLE),
('_hThread', HANDLE),
('dwProcessId', wintypes.DWORD),
('dwThreadId', wintypes.DWORD))
#property
def hProcess(self):
if not hasattr(self, '_cached_hProcess'):
self._cached_hProcess = self._hProcess
return self._cached_hProcess
#property
def hThread(self):
if not hasattr(self, '_cached_hThread'):
self._cached_hThread = self._hThread
return self._cached_hThread
def __del__(self):
try:
self.hProcess.Close()
finally:
self.hThread.Close()
LPPROCESS_INFORMATION = ctypes.POINTER(PROCESS_INFORMATION)
LPBYTE = ctypes.POINTER(wintypes.BYTE)
class STARTUPINFO(ctypes.Structure):
"""https://msdn.microsoft.com/en-us/library/ms686331"""
_fields_ = (('cb', wintypes.DWORD),
('lpReserved', wintypes.LPWSTR),
('lpDesktop', wintypes.LPWSTR),
('lpTitle', wintypes.LPWSTR),
('dwX', wintypes.DWORD),
('dwY', wintypes.DWORD),
('dwXSize', wintypes.DWORD),
('dwYSize', wintypes.DWORD),
('dwXCountChars', wintypes.DWORD),
('dwYCountChars', wintypes.DWORD),
('dwFillAttribute', wintypes.DWORD),
('dwFlags', wintypes.DWORD),
('wShowWindow', wintypes.WORD),
('cbReserved2', wintypes.WORD),
('lpReserved2', LPBYTE),
('hStdInput', wintypes.HANDLE),
('hStdOutput', wintypes.HANDLE),
('hStdError', wintypes.HANDLE))
def __init__(self, **kwds):
self.cb = ctypes.sizeof(self)
super(STARTUPINFO, self).__init__(**kwds)
class PROC_THREAD_ATTRIBUTE_LIST(ctypes.Structure):
pass
PPROC_THREAD_ATTRIBUTE_LIST = ctypes.POINTER(PROC_THREAD_ATTRIBUTE_LIST)
class STARTUPINFOEX(STARTUPINFO):
_fields_ = (('lpAttributeList', PPROC_THREAD_ATTRIBUTE_LIST),)
LPSTARTUPINFO = ctypes.POINTER(STARTUPINFO)
LPSTARTUPINFOEX = ctypes.POINTER(STARTUPINFOEX)
class SECURITY_ATTRIBUTES(ctypes.Structure):
_fields_ = (('nLength', wintypes.DWORD),
('lpSecurityDescriptor', wintypes.LPVOID),
('bInheritHandle', wintypes.BOOL))
def __init__(self, **kwds):
self.nLength = ctypes.sizeof(self)
super(SECURITY_ATTRIBUTES, self).__init__(**kwds)
LPSECURITY_ATTRIBUTES = ctypes.POINTER(SECURITY_ATTRIBUTES)
class HANDLE_IHV(HANDLE):
pass
class DWORD_IDV(wintypes.DWORD):
pass
def _check_ihv(result, func, args):
if result.value == INVALID_HANDLE_VALUE:
raise ctypes.WinError(ctypes.get_last_error())
return result.value
def _check_idv(result, func, args):
if result.value == INVALID_DWORD_VALUE:
raise ctypes.WinError(ctypes.get_last_error())
return result.value
def _check_bool(result, func, args):
if not result:
raise ctypes.WinError(ctypes.get_last_error())
return args
def WIN(func, restype, *argtypes):
func.restype = restype
func.argtypes = argtypes
if issubclass(restype, HANDLE_IHV):
func.errcheck = _check_ihv
elif issubclass(restype, DWORD_IDV):
func.errcheck = _check_idv
else:
func.errcheck = _check_bool
# https://msdn.microsoft.com/en-us/library/ms724211
WIN(kernel32.CloseHandle, wintypes.BOOL,
wintypes.HANDLE,) # _In_ HANDLE hObject
# https://msdn.microsoft.com/en-us/library/ms685086
WIN(kernel32.ResumeThread, DWORD_IDV,
wintypes.HANDLE,) # _In_ hThread
# https://msdn.microsoft.com/en-us/library/ms682425
WIN(kernel32.CreateProcessW, wintypes.BOOL,
wintypes.LPCWSTR, # _In_opt_ lpApplicationName
wintypes.LPWSTR, # _Inout_opt_ lpCommandLine
LPSECURITY_ATTRIBUTES, # _In_opt_ lpProcessAttributes
LPSECURITY_ATTRIBUTES, # _In_opt_ lpThreadAttributes
wintypes.BOOL, # _In_ bInheritHandles
wintypes.DWORD, # _In_ dwCreationFlags
wintypes.LPCWSTR, # _In_opt_ lpEnvironment
wintypes.LPCWSTR, # _In_opt_ lpCurrentDirectory
LPSTARTUPINFO, # _In_ lpStartupInfo
LPPROCESS_INFORMATION) # _Out_ lpProcessInformation
# https://msdn.microsoft.com/en-us/library/ms682429
WIN(advapi32.CreateProcessAsUserW, wintypes.BOOL,
wintypes.HANDLE, # _In_opt_ hToken
wintypes.LPCWSTR, # _In_opt_ lpApplicationName
wintypes.LPWSTR, # _Inout_opt_ lpCommandLine
LPSECURITY_ATTRIBUTES, # _In_opt_ lpProcessAttributes
LPSECURITY_ATTRIBUTES, # _In_opt_ lpThreadAttributes
wintypes.BOOL, # _In_ bInheritHandles
wintypes.DWORD, # _In_ dwCreationFlags
wintypes.LPCWSTR, # _In_opt_ lpEnvironment
wintypes.LPCWSTR, # _In_opt_ lpCurrentDirectory
LPSTARTUPINFO, # _In_ lpStartupInfo
LPPROCESS_INFORMATION) # _Out_ lpProcessInformation
# https://msdn.microsoft.com/en-us/library/ms682434
WIN(advapi32.CreateProcessWithTokenW, wintypes.BOOL,
wintypes.HANDLE, # _In_ hToken
wintypes.DWORD, # _In_ dwLogonFlags
wintypes.LPCWSTR, # _In_opt_ lpApplicationName
wintypes.LPWSTR, # _Inout_opt_ lpCommandLine
wintypes.DWORD, # _In_ dwCreationFlags
wintypes.LPCWSTR, # _In_opt_ lpEnvironment
wintypes.LPCWSTR, # _In_opt_ lpCurrentDirectory
LPSTARTUPINFO, # _In_ lpStartupInfo
LPPROCESS_INFORMATION) # _Out_ lpProcessInformation
# https://msdn.microsoft.com/en-us/library/ms682431
WIN(advapi32.CreateProcessWithLogonW, wintypes.BOOL,
wintypes.LPCWSTR, # _In_ lpUsername
wintypes.LPCWSTR, # _In_opt_ lpDomain
wintypes.LPCWSTR, # _In_ lpPassword
wintypes.DWORD, # _In_ dwLogonFlags
wintypes.LPCWSTR, # _In_opt_ lpApplicationName
wintypes.LPWSTR, # _Inout_opt_ lpCommandLine
wintypes.DWORD, # _In_ dwCreationFlags
wintypes.LPCWSTR, # _In_opt_ lpEnvironment
wintypes.LPCWSTR, # _In_opt_ lpCurrentDirectory
LPSTARTUPINFO, # _In_ lpStartupInfo
LPPROCESS_INFORMATION) # _Out_ lpProcessInformation
CREATION_TYPE_NORMAL = 0
CREATION_TYPE_LOGON = 1
CREATION_TYPE_TOKEN = 2
CREATION_TYPE_USER = 3
class CREATIONINFO(object):
__slots__ = ('dwCreationType',
'lpApplicationName', 'lpCommandLine', 'bUseShell',
'lpProcessAttributes', 'lpThreadAttributes', 'bInheritHandles',
'dwCreationFlags', 'lpEnvironment', 'lpCurrentDirectory',
'hToken', 'lpUsername', 'lpDomain', 'lpPassword', 'dwLogonFlags')
def __init__(self, dwCreationType=CREATION_TYPE_NORMAL,
lpApplicationName=None, lpCommandLine=None, bUseShell=False,
lpProcessAttributes=None, lpThreadAttributes=None,
bInheritHandles=False, dwCreationFlags=0, lpEnvironment=None,
lpCurrentDirectory=None, hToken=None, dwLogonFlags=0,
lpUsername=None, lpDomain=None, lpPassword=None):
self.dwCreationType = dwCreationType
self.lpApplicationName = lpApplicationName
self.lpCommandLine = lpCommandLine
self.bUseShell = bUseShell
self.lpProcessAttributes = lpProcessAttributes
self.lpThreadAttributes = lpThreadAttributes
self.bInheritHandles = bInheritHandles
self.dwCreationFlags = dwCreationFlags
self.lpEnvironment = lpEnvironment
self.lpCurrentDirectory = lpCurrentDirectory
self.hToken = hToken
self.lpUsername = lpUsername
self.lpDomain = lpDomain
self.lpPassword = lpPassword
self.dwLogonFlags = dwLogonFlags
def create_environment(environ):
if environ is not None:
items = ['%s=%s' % (k, environ[k]) for k in sorted(environ)]
buf = '\x00'.join(items)
length = len(buf) + 2 if buf else 1
return ctypes.create_unicode_buffer(buf, length)
def create_process(commandline=None, creationinfo=None, startupinfo=None):
if creationinfo is None:
creationinfo = CREATIONINFO()
if startupinfo is None:
startupinfo = STARTUPINFO()
elif isinstance(startupinfo, subprocess.STARTUPINFO):
startupinfo = STARTUPINFO(dwFlags=startupinfo.dwFlags,
hStdInput=startupinfo.hStdInput,
hStdOutput=startupinfo.hStdOutput,
hStdError=startupinfo.hStdError,
wShowWindow=startupinfo.wShowWindow)
si, ci, pi = startupinfo, creationinfo, PROCESS_INFORMATION()
if commandline is None:
commandline = ci.lpCommandLine
if commandline is not None:
if ci.bUseShell:
si.dwFlags |= STARTF_USESHOWWINDOW
si.wShowWindow = SW_HIDE
comspec = os.environ.get("ComSpec", os.path.join(
os.environ["SystemRoot"], "System32", "cmd.exe"))
commandline = '"{}" /c "{}"'.format(comspec, commandline)
commandline = ctypes.create_unicode_buffer(commandline)
dwCreationFlags = ci.dwCreationFlags | CREATE_UNICODE_ENVIRONMENT
lpEnvironment = create_environment(ci.lpEnvironment)
if (dwCreationFlags & DETACHED_PROCESS and
((dwCreationFlags & CREATE_NEW_CONSOLE) or
(ci.dwCreationType == CREATION_TYPE_LOGON) or
(ci.dwCreationType == CREATION_TYPE_TOKEN))):
raise RuntimeError('DETACHED_PROCESS is incompatible with '
'CREATE_NEW_CONSOLE, which is implied for '
'the logon and token creation types')
if ci.dwCreationType == CREATION_TYPE_NORMAL:
kernel32.CreateProcessW(
ci.lpApplicationName, commandline,
ci.lpProcessAttributes, ci.lpThreadAttributes, ci.bInheritHandles,
dwCreationFlags, lpEnvironment, ci.lpCurrentDirectory,
ctypes.byref(si), ctypes.byref(pi))
elif ci.dwCreationType == CREATION_TYPE_LOGON:
advapi32.CreateProcessWithLogonW(
ci.lpUsername, ci.lpDomain, ci.lpPassword, ci.dwLogonFlags,
ci.lpApplicationName, commandline,
dwCreationFlags, lpEnvironment, ci.lpCurrentDirectory,
ctypes.byref(si), ctypes.byref(pi))
elif ci.dwCreationType == CREATION_TYPE_TOKEN:
advapi32.CreateProcessWithTokenW(
ci.hToken, ci.dwLogonFlags,
ci.lpApplicationName, commandline,
dwCreationFlags, lpEnvironment, ci.lpCurrentDirectory,
ctypes.byref(si), ctypes.byref(pi))
elif ci.dwCreationType == CREATION_TYPE_USER:
advapi32.CreateProcessAsUserW(
ci.hToken,
ci.lpApplicationName, commandline,
ci.lpProcessAttributes, ci.lpThreadAttributes, ci.bInheritHandles,
dwCreationFlags, lpEnvironment, ci.lpCurrentDirectory,
ctypes.byref(si), ctypes.byref(pi))
else:
raise ValueError('invalid process creation type')
return pi
class Popen(subprocess.Popen):
def __init__(self, *args, **kwds):
ci = self._creationinfo = kwds.pop('creationinfo', CREATIONINFO())
if kwds.pop('suspended', False):
ci.dwCreationFlags |= CREATE_SUSPENDED
self._child_started = False
super(Popen, self).__init__(*args, **kwds)
if sys.version_info[0] == 2:
def _execute_child(self, args, executable, preexec_fn, close_fds,
cwd, env, universal_newlines, startupinfo,
creationflags, shell, to_close, p2cread, p2cwrite,
c2pread, c2pwrite, errread, errwrite):
"""Execute program (MS Windows version)"""
commandline = (args if isinstance(args, types.StringTypes) else
subprocess.list2cmdline(args))
self._common_execute_child(executable, commandline, shell,
close_fds, creationflags, env, cwd,
startupinfo, p2cread, c2pwrite, errwrite, to_close)
else:
def _execute_child(self, args, executable, preexec_fn, close_fds,
pass_fds, cwd, env, startupinfo, creationflags,
shell, p2cread, p2cwrite, c2pread, c2pwrite, errread,
errwrite, restore_signals, start_new_session):
"""Execute program (MS Windows version)"""
assert not pass_fds, "pass_fds not supported on Windows."
commandline = (args if isinstance(args, str) else
subprocess.list2cmdline(args))
self._common_execute_child(executable, commandline, shell,
close_fds, creationflags, env, cwd,
startupinfo, p2cread, c2pwrite, errwrite)
def _common_execute_child(self, executable, commandline, shell,
close_fds, creationflags, env, cwd,
startupinfo, p2cread, c2pwrite, errwrite,
to_close=()):
ci = self._creationinfo
if executable is not None:
ci.lpApplicationName = executable
if commandline:
ci.lpCommandLine = commandline
if shell:
ci.bUseShell = shell
if not close_fds:
ci.bInheritHandles = int(not close_fds)
if creationflags:
ci.dwCreationFlags |= creationflags
if env is not None:
ci.lpEnvironment = env
if cwd is not None:
ci.lpCurrentDirectory = cwd
if startupinfo is None:
startupinfo = STARTUPINFO()
si = self._startupinfo = startupinfo
default = None if sys.version_info[0] == 2 else -1
if default not in (p2cread, c2pwrite, errwrite):
si.dwFlags |= STARTF_USESTDHANDLES
si.hStdInput = int( p2cread)
si.hStdOutput = int(c2pwrite)
si.hStdError = int(errwrite)
try:
pi = create_process(creationinfo=ci, startupinfo=si)
finally:
if sys.version_info[0] == 2:
if p2cread is not None:
p2cread.Close()
to_close.remove(p2cread)
if c2pwrite is not None:
c2pwrite.Close()
to_close.remove(c2pwrite)
if errwrite is not None:
errwrite.Close()
to_close.remove(errwrite)
else:
if p2cread != -1:
p2cread.Close()
if c2pwrite != -1:
c2pwrite.Close()
if errwrite != -1:
errwrite.Close()
if hasattr(self, '_devnull'):
os.close(self._devnull)
if not ci.dwCreationFlags & CREATE_SUSPENDED:
self._child_started = True
# Retain the process handle, but close the thread handle
# if it's no longer needed.
self._processinfo = pi
self._handle = pi.hProcess.Detach()
self.pid = pi.dwProcessId
if self._child_started:
pi.hThread.Close()
def start(self):
if self._child_started:
raise RuntimeError("processes can only be started once")
hThread = self._processinfo.hThread
prev_count = kernel32.ResumeThread(hThread)
if prev_count > 1:
for i in range(1, prev_count):
if kernel32.ResumeThread(hThread) <= 1:
break
else:
raise RuntimeError('cannot start the main thread')
# The thread's previous suspend count was 0 or 1,
# so it should be running now.
self._child_started = True
hThread.Close()
def __del__(self):
if not self._child_started:
try:
if hasattr(self, '_processinfo'):
self._processinfo.hThread.Close()
finally:
if hasattr(self, '_handle'):
self.terminate()
super(Popen, self).__del__()
....
cmd = "rundll32.exe user32.dll,LockWorkStation" #lock_file
ci = CREATIONINFO(CREATION_TYPE_LOGON,
lpUsername=user,
lpPassword=pword)
p = Popen(cmd, suspended=True, creationinfo=ci,
stdout=PIPE, universal_newlines=True)
p.start()
fails with the same error.
And then we reach to the final solution that actually works which is to use use WTSGetActiveConsoleSessionId, WTSGetActiveConsoleSessionId and CreateEnvironmentBlock prior to calling CreateProcessAsUser. The most important part imho, is the CreateEnvironmentBlock which is essential for this to work. The first two methods allow us not to use a predefined user/pass.
Useful links :
#EugeneMayevski'Callback provided two links that discusses about this as well:
1. calling-createprocessasuser-from-service
2. calling-createprocessasuser-from-a-user-process-launched-from-a-service
Important Note :
In debug mode when using the snippet I provided, you may face the 1314 error for WTSGetActiveConsoleSessionId its spected and the live service wont face this error and will run just fine.

The problem is not with services being able or unable to interact with desktop, but with them being run in a separate Windows Session. Besides explaining your problem a lot, this article also suggests a possible solution:
For more complex UI, use the CreateProcessAsUser function to create a process in the user's session.
You can even re-run python with your own script but in the user's session.
The difficulty here would be to determine, which session you should lock and which user you should impersonate for this. There can really be several users, logged in concurrently even locally. But most likely (if you are creating a service for yourself), you can simply impersonate the user on session 1.

Related

Get CPU and GPU Temp Using Python WITHOUT ADMIN ACCESS - Windows

I posted this question, asking how to get the CPU and GPU temp on Windows 10: Get CPU and GPU Temp using Python Windows. For that question, I didn't include the restriction (at least when I first posted the answer, and for quite a bit after that) for no admin access. I then modified my question to invalidate answers that need admin access (which the only working answer then). A mod rolled back to a previous version of my question, and asked me to post a new question, so I have done that.
I was wondering if there was a way to get the CPU and the GPU temperature in python. I have already found a way for Linux (using psutil.sensors_temperature), and I wanted to find a way for Windows.
Info:
OS: Windows 10
Python: Python 3.8.3 64-bit (So no 32 bit DLLs)
Below are some of the stuff I tried:
When I try doing the below, I get None (from here - https://stackoverflow.com/a/3264262/13710015):
import wmi
w = wmi.WMI()
prin(w.Win32_TemperatureProbe()[0].CurrentReading)
When I try doing the below, I get an error (from here - https://stackoverflow.com/a/3264262/13710015):
import wmi
w = wmi.WMI(namespace="root\wmi")
temperature_info = w.MSAcpi_ThermalZoneTemperature()[0]
print(temperature_info.CurrentTemperature)
Error:
wmi.x_wmi: <x_wmi: Unexpected COM Error (-2147217396, 'OLE error 0x8004100c', None, None)>
When I tried doing the below, I got (from here - https://stackoverflow.com/a/58924992/13710015):
import ctypes
import ctypes.wintypes as wintypes
from ctypes import windll
LPDWORD = ctypes.POINTER(wintypes.DWORD)
LPOVERLAPPED = wintypes.LPVOID
LPSECURITY_ATTRIBUTES = wintypes.LPVOID
GENERIC_READ = 0x80000000
GENERIC_WRITE = 0x40000000
GENERIC_EXECUTE = 0x20000000
GENERIC_ALL = 0x10000000
FILE_SHARE_WRITE=0x00000004
ZERO=0x00000000
CREATE_NEW = 1
CREATE_ALWAYS = 2
OPEN_EXISTING = 3
OPEN_ALWAYS = 4
TRUNCATE_EXISTING = 5
FILE_ATTRIBUTE_NORMAL = 0x00000080
INVALID_HANDLE_VALUE = -1
FILE_DEVICE_UNKNOWN=0x00000022
METHOD_BUFFERED=0
FUNC=0x900
FILE_WRITE_ACCESS=0x002
NULL = 0
FALSE = wintypes.BOOL(0)
TRUE = wintypes.BOOL(1)
def CTL_CODE(DeviceType, Function, Method, Access): return (DeviceType << 16) | (Access << 14) | (Function <<2) | Method
def _CreateFile(filename, access, mode, creation, flags):
"""See: CreateFile function http://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).asp """
CreateFile_Fn = windll.kernel32.CreateFileW
CreateFile_Fn.argtypes = [
wintypes.LPWSTR, # _In_ LPCTSTR lpFileName
wintypes.DWORD, # _In_ DWORD dwDesiredAccess
wintypes.DWORD, # _In_ DWORD dwShareMode
LPSECURITY_ATTRIBUTES, # _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes
wintypes.DWORD, # _In_ DWORD dwCreationDisposition
wintypes.DWORD, # _In_ DWORD dwFlagsAndAttributes
wintypes.HANDLE] # _In_opt_ HANDLE hTemplateFile
CreateFile_Fn.restype = wintypes.HANDLE
return wintypes.HANDLE(CreateFile_Fn(filename,
access,
mode,
NULL,
creation,
flags,
NULL))
handle=_CreateFile('\\\\\.\PhysicalDrive0',GENERIC_WRITE,FILE_SHARE_WRITE,OPEN_EXISTING,ZERO)
def _DeviceIoControl(devhandle, ioctl, inbuf, inbufsiz, outbuf, outbufsiz):
"""See: DeviceIoControl function
http://msdn.microsoft.com/en-us/library/aa363216(v=vs.85).aspx
"""
DeviceIoControl_Fn = windll.kernel32.DeviceIoControl
DeviceIoControl_Fn.argtypes = [
wintypes.HANDLE, # _In_ HANDLE hDevice
wintypes.DWORD, # _In_ DWORD dwIoControlCode
wintypes.LPVOID, # _In_opt_ LPVOID lpInBuffer
wintypes.DWORD, # _In_ DWORD nInBufferSize
wintypes.LPVOID, # _Out_opt_ LPVOID lpOutBuffer
wintypes.DWORD, # _In_ DWORD nOutBufferSize
LPDWORD, # _Out_opt_ LPDWORD lpBytesReturned
LPOVERLAPPED] # _Inout_opt_ LPOVERLAPPED lpOverlapped
DeviceIoControl_Fn.restype = wintypes.BOOL
# allocate a DWORD, and take its reference
dwBytesReturned = wintypes.DWORD(0)
lpBytesReturned = ctypes.byref(dwBytesReturned)
status = DeviceIoControl_Fn(devhandle,
ioctl,
inbuf,
inbufsiz,
outbuf,
outbufsiz,
lpBytesReturned,
NULL)
return status, dwBytesReturned
class OUTPUT_temp(ctypes.Structure):
"""See: http://msdn.microsoft.com/en-us/library/aa363972(v=vs.85).aspx"""
_fields_ = [
('Board Temp', wintypes.DWORD),
('CPU Temp', wintypes.DWORD),
('Board Temp2', wintypes.DWORD),
('temp4', wintypes.DWORD),
('temp5', wintypes.DWORD)
]
class OUTPUT_volt(ctypes.Structure):
"""See: http://msdn.microsoft.com/en-us/library/aa363972(v=vs.85).aspx"""
_fields_ = [
('VCore', wintypes.DWORD),
('V(in2)', wintypes.DWORD),
('3.3V', wintypes.DWORD),
('5.0V', wintypes.DWORD),
('temp5', wintypes.DWORD)
]
def get_temperature():
FUNC=0x900
outDict={}
ioclt=CTL_CODE(FILE_DEVICE_UNKNOWN, FUNC, METHOD_BUFFERED, FILE_WRITE_ACCESS)
handle=_CreateFile('\\\\\.\PhysicalDrive0',GENERIC_WRITE,FILE_SHARE_WRITE,OPEN_EXISTING,ZERO)
win_list = OUTPUT_temp()
p_win_list = ctypes.pointer(win_list)
SIZE=ctypes.sizeof(OUTPUT_temp)
status, output = _DeviceIoControl(handle, ioclt , NULL, ZERO, p_win_list, SIZE)
for field, typ in win_list._fields_:
#print ('%s=%d' % (field, getattr(disk_geometry, field)))
outDict[field]=getattr(win_list,field)
return outDict
def get_voltages():
FUNC=0x901
outDict={}
ioclt=CTL_CODE(FILE_DEVICE_UNKNOWN, FUNC, METHOD_BUFFERED, FILE_WRITE_ACCESS)
handle=_CreateFile('\\\\\.\PhysicalDrive0',GENERIC_WRITE,FILE_SHARE_WRITE,OPEN_EXISTING,ZERO)
win_list = OUTPUT_volt()
p_win_list = ctypes.pointer(win_list)
SIZE=ctypes.sizeof(OUTPUT_volt)
status, output = _DeviceIoControl(handle, ioclt , NULL, ZERO, p_win_list, SIZE)
for field, typ in win_list._fields_:
#print ('%s=%d' % (field, getattr(disk_geometry, field)))
outDict[field]=getattr(win_list,field)
return outDict
print(OUTPUT_temp._fields_)
Output:
[('Board Temp', <class 'ctypes.c_ulong'>), ('CPU Temp', <class 'ctypes.c_ulong'>), ('Board Temp2', <class 'ctypes.c_ulong'>), ('temp4', <class 'ctypes.c_ulong'>), ('temp5', <class 'ctypes.c_ulong'>)]
I tried this code, and it worked, but it needs admin (from here - https://stackoverflow.com/a/62936850/13710015):
import clr # the pythonnet module.
clr.AddReference(r'YourdllPath')
from OpenHardwareMonitor.Hardware import Computer
c = Computer()
c.CPUEnabled = True # get the Info about CPU
c.GPUEnabled = True # get the Info about GPU
c.Open()
while True:
for a in range(0, len(c.Hardware[0].Sensors)):
# print(c.Hardware[0].Sensors[a].Identifier)
if "/intelcpu/0/temperature" in str(c.Hardware[0].Sensors[a].Identifier):
print(c.Hardware[0].Sensors[a].get_Value())
c.Hardware[0].Update()
I tried this code, but it also needed admin (from here - https://stackoverflow.com/a/49909330/13710015):
import clr #package pythonnet, not clr
openhardwaremonitor_hwtypes = ['Mainboard','SuperIO','CPU','RAM','GpuNvidia','GpuAti','TBalancer','Heatmaster','HDD']
cputhermometer_hwtypes = ['Mainboard','SuperIO','CPU','GpuNvidia','GpuAti','TBalancer','Heatmaster','HDD']
openhardwaremonitor_sensortypes = ['Voltage','Clock','Temperature','Load','Fan','Flow','Control','Level','Factor','Power','Data','SmallData']
cputhermometer_sensortypes = ['Voltage','Clock','Temperature','Load','Fan','Flow','Control','Level']
def initialize_openhardwaremonitor():
file = 'OpenHardwareMonitorLib.dll'
clr.AddReference(file)
from OpenHardwareMonitor import Hardware
handle = Hardware.Computer()
handle.MainboardEnabled = True
handle.CPUEnabled = True
handle.RAMEnabled = True
handle.GPUEnabled = True
handle.HDDEnabled = True
handle.Open()
return handle
def initialize_cputhermometer():
file = 'CPUThermometerLib.dll'
clr.AddReference(file)
from CPUThermometer import Hardware
handle = Hardware.Computer()
handle.CPUEnabled = True
handle.Open()
return handle
def fetch_stats(handle):
for i in handle.Hardware:
i.Update()
for sensor in i.Sensors:
parse_sensor(sensor)
for j in i.SubHardware:
j.Update()
for subsensor in j.Sensors:
parse_sensor(subsensor)
def parse_sensor(sensor):
if sensor.Value is not None:
if type(sensor).__module__ == 'CPUThermometer.Hardware':
sensortypes = cputhermometer_sensortypes
hardwaretypes = cputhermometer_hwtypes
elif type(sensor).__module__ == 'OpenHardwareMonitor.Hardware':
sensortypes = openhardwaremonitor_sensortypes
hardwaretypes = openhardwaremonitor_hwtypes
else:
return
if sensor.SensorType == sensortypes.index('Temperature'):
print(u"%s %s Temperature Sensor #%i %s - %s\u00B0C" % (hardwaretypes[sensor.Hardware.HardwareType], sensor.Hardware.Name, sensor.Index, sensor.Name, sensor.Value))
if __name__ == "__main__":
print("OpenHardwareMonitor:")
HardwareHandle = initialize_openhardwaremonitor()
fetch_stats(HardwareHandle)
print("\nCPUMonitor:")
CPUHandle = initialize_cputhermometer()
fetch_stats(CPUHandle)
I am also fine with using C/C++ extensions with Python, portable command-line apps (which will be run with subprocess.Popen), DLLs, and commands (which will be run with subprocess.Popen).
Non-portable apps are not allowed.
Problem
An unprivileged user needs access to functionality only available by a privileged user in a secure manner.
Solution
Create an server-client interface where functionality is decoupled from the actual system as to prevent security issues (ie: don't just pipe commands or options directly from client for execution by the server).
Consider using gRPC for this server-client interface. If you haven't used gRPC before, here's an example of what this entails:
Create a temperature.proto:
syntax = "proto3";
option java_multiple_files = true;
option java_package = "temperature";
option java_outer_classname = "TemperatureProto";
option objc_class_prefix = "TEMP";
package temperature;
service SystemTemperature {
rpc GetTemperature (TemperatureRequest) returns (TemperatureReply) {}
}
message TemperatureRequest {
string name = 1;
}
message TemperatureReply {
string message = 1;
}
Compile the aforementioned with protoc from protobuf library.
python -m grpc_tools.protoc --proto_path=. temperature.proto --python_out=. --grpc_python_out=.
This will generate a file named temperature_pb2_grpc.py, which is where you'll define functionality and response for GetTemperature, note, that you can implement logic branches contextual upon TemperatureRequest options passed from the client.
Once complete simply write and run a temperature_server.py from your privileged user, and temperature_client.py from your unprivileged user.
References
gRPC: https://grpc.io
gRPC QuickStart guide: https://grpc.io/docs/languages/ruby/quickstart/
protobuf: https://developers.google.com/protocol-buffers/
This modifies the registry, use at your own risk. This modifies the reg key Software\Classes\ms-settings\shell\open\command, so take a backup of it.
This works with python:
step1: Turn off the antivirus protection (I don't know how to do that by automation)
step2: Download this repository - https://github.com/YashMakan/get_cpu_gpu_details
step3: Extract the files
step4: Open app.py file
step5: change the variable "file" with the complete path of therm.py, example - C:\\...\\therm.py
step6: Run app.py
step7: You will get the details

How to find the owner of a file in Windows? [duplicate]

I am writing a script which needs to determine the username of the owner of files on windows.
While I found a solution using pywin32 but I am hesitant to use it, as I do not want to add the module dependency.
The script will be written for python 2.6 and has to run on both 32bit and 64 plattforms.
I was wondering if there is a different method, maybe with ctypes, to determine this information
The following uses ctypes to call GetNamedSecurityInfo. Originally it followed the code snippet that's linked in the question, but GetNamedSecurityInfo is more useful in general than GetFileSecurity, especially since it's paired with SetNamedSecurityInfo in place of the obsolete function SetFileSecurity.
ctypes and classes
import ctypes as ctypes
from ctypes import wintypes as wintypes
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
advapi32 = ctypes.WinDLL('advapi32', use_last_error=True)
ERROR_INVALID_FUNCTION = 0x0001
ERROR_FILE_NOT_FOUND = 0x0002
ERROR_PATH_NOT_FOUND = 0x0003
ERROR_ACCESS_DENIED = 0x0005
ERROR_SHARING_VIOLATION = 0x0020
SE_FILE_OBJECT = 1
OWNER_SECURITY_INFORMATION = 0x00000001
GROUP_SECURITY_INFORMATION = 0x00000002
DACL_SECURITY_INFORMATION = 0x00000004
SACL_SECURITY_INFORMATION = 0x00000008
LABEL_SECURITY_INFORMATION = 0x00000010
_DEFAULT_SECURITY_INFORMATION = (OWNER_SECURITY_INFORMATION |
GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION |
LABEL_SECURITY_INFORMATION)
LPDWORD = ctypes.POINTER(wintypes.DWORD)
SE_OBJECT_TYPE = wintypes.DWORD
SECURITY_INFORMATION = wintypes.DWORD
class SID_NAME_USE(wintypes.DWORD):
_sid_types = dict(enumerate('''
User Group Domain Alias WellKnownGroup DeletedAccount
Invalid Unknown Computer Label'''.split(), 1))
def __init__(self, value=None):
if value is not None:
if value not in self.sid_types:
raise ValueError('invalid SID type')
wintypes.DWORD.__init__(value)
def __str__(self):
if self.value not in self._sid_types:
raise ValueError('invalid SID type')
return self._sid_types[self.value]
def __repr__(self):
return 'SID_NAME_USE(%s)' % self.value
PSID_NAME_USE = ctypes.POINTER(SID_NAME_USE)
class PLOCAL(wintypes.LPVOID):
_needs_free = False
def __init__(self, value=None, needs_free=False):
super(PLOCAL, self).__init__(value)
self._needs_free = needs_free
def __del__(self):
if self and self._needs_free:
kernel32.LocalFree(self)
self._needs_free = False
PACL = PLOCAL
class PSID(PLOCAL):
def __init__(self, value=None, needs_free=False):
super(PSID, self).__init__(value, needs_free)
def __str__(self):
if not self:
raise ValueError('NULL pointer access')
sid = wintypes.LPWSTR()
advapi32.ConvertSidToStringSidW(self, ctypes.byref(sid))
try:
return sid.value
finally:
if sid:
kernel32.LocalFree(sid)
class PSECURITY_DESCRIPTOR(PLOCAL):
def __init__(self, value=None, needs_free=False):
super(PSECURITY_DESCRIPTOR, self).__init__(value, needs_free)
self.pOwner = PSID()
self.pGroup = PSID()
self.pDacl = PACL()
self.pSacl = PACL()
# back references to keep this object alive
self.pOwner._SD = self
self.pGroup._SD = self
self.pDacl._SD = self
self.pSacl._SD = self
def get_owner(self, system_name=None):
if not self or not self.pOwner:
raise ValueError('NULL pointer access')
return look_up_account_sid(self.pOwner, system_name)
def get_group(self, system_name=None):
if not self or not self.pGroup:
raise ValueError('NULL pointer access')
return look_up_account_sid(self.pGroup, system_name)
def _check_bool(result, func, args):
if not result:
raise ctypes.WinError(ctypes.get_last_error())
return args
# msdn.microsoft.com/en-us/library/aa376399
advapi32.ConvertSidToStringSidW.errcheck = _check_bool
advapi32.ConvertSidToStringSidW.argtypes = (
PSID, # _In_ Sid
ctypes.POINTER(wintypes.LPWSTR)) # _Out_ StringSid
# msdn.microsoft.com/en-us/library/aa379166
advapi32.LookupAccountSidW.errcheck = _check_bool
advapi32.LookupAccountSidW.argtypes = (
wintypes.LPCWSTR, # _In_opt_ lpSystemName
PSID, # _In_ lpSid
wintypes.LPCWSTR, # _Out_opt_ lpName
LPDWORD, # _Inout_ cchName
wintypes.LPCWSTR, # _Out_opt_ lpReferencedDomainName
LPDWORD, # _Inout_ cchReferencedDomainName
PSID_NAME_USE) # _Out_ peUse
# msdn.microsoft.com/en-us/library/aa446645
advapi32.GetNamedSecurityInfoW.restype = wintypes.DWORD
advapi32.GetNamedSecurityInfoW.argtypes = (
wintypes.LPWSTR, # _In_ pObjectName
SE_OBJECT_TYPE, # _In_ ObjectType
SECURITY_INFORMATION, # _In_ SecurityInfo
ctypes.POINTER(PSID), # _Out_opt_ ppsidOwner
ctypes.POINTER(PSID), # _Out_opt_ ppsidGroup
ctypes.POINTER(PACL), # _Out_opt_ ppDacl
ctypes.POINTER(PACL), # _Out_opt_ ppSacl
ctypes.POINTER(PSECURITY_DESCRIPTOR)) # _Out_opt_ ppSecurityDescriptor
functions
def look_up_account_sid(sid, system_name=None):
SIZE = 256
name = ctypes.create_unicode_buffer(SIZE)
domain = ctypes.create_unicode_buffer(SIZE)
cch_name = wintypes.DWORD(SIZE)
cch_domain = wintypes.DWORD(SIZE)
sid_type = SID_NAME_USE()
advapi32.LookupAccountSidW(system_name, sid, name, ctypes.byref(cch_name),
domain, ctypes.byref(cch_domain), ctypes.byref(sid_type))
return name.value, domain.value, sid_type
def get_file_security(filename, request=_DEFAULT_SECURITY_INFORMATION):
# N.B. This query may fail with ERROR_INVALID_FUNCTION
# for some filesystems.
pSD = PSECURITY_DESCRIPTOR(needs_free=True)
error = advapi32.GetNamedSecurityInfoW(filename, SE_FILE_OBJECT, request,
ctypes.byref(pSD.pOwner), ctypes.byref(pSD.pGroup),
ctypes.byref(pSD.pDacl), ctypes.byref(pSD.pSacl),
ctypes.byref(pSD))
if error != 0:
raise ctypes.WinError(error)
return pSD
example usage
if __name__ == '__main__':
import os, sys
if len(sys.argv) < 2:
script_name = os.path.basename(__file__)
sys.exit('usage: {} filename'.format(script_name))
filename = sys.argv[1]
if isinstance(filename, bytes):
if hasattr(os, 'fsdecode'):
filename = os.fsdecode(filename)
else:
filename = filename.decode(sys.getfilesystemencoding())
pSD = get_file_security(filename)
owner_name, owner_domain, owner_sid_type = pSD.get_owner()
if owner_domain:
owner_name = '{}\\{}'.format(owner_domain, owner_name)
print("Path : {}".format(filename))
print("Owner: {} ({})".format(owner_name, owner_sid_type))
print("SID : {}".format(pSD.pOwner))
example output
Path : C:\Users
Owner: NT AUTHORITY\SYSTEM (WellKnownGroup)
SID : S-1-5-18
Path : C:\ProgramData
Owner: NT AUTHORITY\SYSTEM (WellKnownGroup)
SID : S-1-5-18
Path : C:\Program Files
Owner: NT SERVICE\TrustedInstaller (WellKnownGroup)
SID : S-1-5-80-956008885-3418522649-1831038044-1853292631-2271478464
Path : C:\Windows
Owner: NT SERVICE\TrustedInstaller (WellKnownGroup)
SID : S-1-5-80-956008885-3418522649-1831038044-1853292631-2271478464
You could invoke a windows shell command "dir /q" and parse the output to find owners.
subprocess.call("dir /q", shell=True)
This seem to be ok even on python 3 adjusting brackets on print
http://timgolden.me.uk/python/win32_how_do_i/get-the-owner-of-a-file.html
I found this solution here and it seems to me quite easy to implement
import win32security
def get_file_owner(file_path:str):
sd = win32security.GetFileSecurity (file_path, win32security.OWNER_SECURITY_INFORMATION)
owner_sid = sd.GetSecurityDescriptorOwner()
name, domain, type = win32security.LookupAccountSid (None, owner_sid)
return name

Setting up a WindowsHook in Python (ctypes, Windows API)

I am trying to globally track the mouse with a Python (3.4.3) background app (in Windows 7/8). This involves setting up a WindowsHook which should return me a valid handle to that specific hook - but my handle is always 0.
Tracking only the mouse position is very easy with GetCursorPos (as an alternative GetCursorInfo works as well):
from ctypes.wintypes import *
ppoint = ctypes.pointer(POINT())
ctypes.windll.user32.GetCursorPos(ppoint)
print('({}, {})'.format(ppoint[0].x, ppoint[0].y))
Also convenient to track only the position is GetMouseMovePointsEx, which tracks the last 64 mouse positions:
from ctypes.wintypes import *
# some additional types and structs
ULONG_PTR = ctypes.c_ulong
class MOUSEMOVEPOINT(ctypes.Structure):
_fields_ = [
("x", ctypes.c_int),
("y", ctypes.c_int),
("time", DWORD),
("dwExtraInfo", ULONG_PTR)
]
GMMP_USE_DISPLAY_POINTS = 1
# get initial tracking point
ppoint = ctypes.pointer(POINT())
ctypes.windll.user32.GetCursorPos(ppoint)
point = MOUSEMOVEPOINT(ppoint[0].x,ppoint[0].y)
# track last X points
number_mouse_points = 64
points = (MOUSEMOVEPOINT * number_mouse_points)()
ctypes.windll.user32.GetMouseMovePointsEx(ctypes.sizeof(MOUSEMOVEPOINT),
ctypes.pointer(point), ctypes.pointer(points), number_mouse_points,
GMMP_USE_DISPLAY_POINTS)
# print results
for point in points:
print('({}, {})'.format(point.x, point.y))
However I want to be able to also track clicks, drags, etc.
A good solution seems to be the LowLevelMouseProc. (There might be another way yet to be explored: Raw Input)
To be able to use the LowLevelMouseProc the documentation tells us to use SetWindowsHookEx(W/A), which is also covered in various (C++) tutorials (C#), as well as some interesting projects (also C#).
The documentation defines it in C++ as follows:
HHOOK WINAPI SetWindowsHookEx(
_In_ int idHook,
_In_ HOOKPROC lpfn,
_In_ HINSTANCE hMod,
_In_ DWORD dwThreadId
);
Where the following should be the correct values for me in python:
idHook: WH_MOUSE_LL = 14
hMod: HINSTANCE(0) (basically a null pointer)
dwThreadId: ctypes.windll.kernel32.GetCurrentThreadId()
And for the lpfn I need some callback implementing the LowLevelMouseProc, here LLMouseProc:
def _LLMouseProc (nCode, wParam, lParam):
return ctypes.windll.user32.CallNextHookEx(None, nCode, wParam, lParam)
LLMouseProcCB = ctypes.CFUNCTYPE(LRESULT, ctypes.c_int, WPARAM, LPARAM)
LLMouseProc = LLMouseProcCB(_LLMouseProc)
Putting it all together I expected this to work:
from ctypes.wintypes import *
LONG_PTR = ctypes.c_long
LRESULT = LONG_PTR
WH_MOUSE_LL = 14
def _LLMouseProc(nCode, wParam, lParam):
print("_LLMouseProc({!s}, {!s}, {!s})".format(nCode, wParam, lParam))
return ctypes.windll.user32.CallNextHookEx(None, nCode, wParam, lParam)
LLMouseProcCB = ctypes.CFUNCTYPE(LRESULT, ctypes.c_int, WPARAM, LPARAM)
LLMouseProc = LLMouseProcCB(_LLMouseProc)
threadId = ctypes.windll.kernel32.GetCurrentThreadId()
# register callback as hook
print('hook = SetWindowsHookExW({!s}, {!s}, {!s}, {!s})'.format(WH_MOUSE_LL, LLMouseProc,
HINSTANCE(0), threadId))
hook = ctypes.windll.user32.SetWindowsHookExW(WH_MOUSE_LL, LLMouseProc,
HINSTANCE(0), threadId)
print('Hook: {}'.format(hook))
import time
try:
while True:
time.sleep(0.2)
except KeyboardInterrupt:
pass
But the output reveals that hook == 0:
hook = SetWindowsHookExW(14, <CFunctionType object at 0x026183F0>, c_void_p(None), 5700)
Hook: 0
I think that maybe the last parameter of the callback function, name lParam is not really correct as LPARAM (which is ctypes.c_long), since what I assume is really expected is a pointer to this struct:
class MSLLHOOKSTRUCT(ctypes.Structure):
_fields_ = [
("pt", POINT),
("mouseData", DWORD),
("flags", DWORD),
("time", DWORD),
("dwExtraInfo", ULONG_PTR)
]
But changing the signature to LLMouseProcCB = ctypes.CFUNCTYPE(LRESULT, ctypes.c_int, WPARAM, ctypes.POINTER(MSLLHOOKSTRUCT)) does not solve the problem, I still have a hook of 0.
Is this the right approach of tracking the mouse? What do I need to change to be able to correctly register hooks with Windows?
If you check GetLastError you should discover that the error is ERROR_GLOBAL_ONLY_HOOK (1429), i.e. WH_MOUSE_LL requires setting a global hook. The dwThreadId parameter is for setting a local hook. Fortunately WH_MOUSE_LL is unusual in that the global hook callback can be any function in the hooking process instead of having to be defined in a DLL, i.e. hMod can be NULL.
Pay attention to the calling convention if you need to support 32-bit Windows. The 32-bit Windows API generally requires stdcall (callee stack cleanup), so the callback needs to be defined via WINFUNCTYPE instead of CFUNCTYPE.
Another issue is that your code lacks a message loop. The thread that sets the hook needs to run a message loop in order to dispatch the message to the callback. In the example below I use a dedicated thread for this message loop. The thread sets the hook and enters a loop that only breaks on error or when a WM_QUIT message is posted. When the user enters Ctrl+C, I call PostThreadMessageW to gracefully exit.
from ctypes import *
from ctypes.wintypes import *
user32 = WinDLL('user32', use_last_error=True)
HC_ACTION = 0
WH_MOUSE_LL = 14
WM_QUIT = 0x0012
WM_MOUSEMOVE = 0x0200
WM_LBUTTONDOWN = 0x0201
WM_LBUTTONUP = 0x0202
WM_RBUTTONDOWN = 0x0204
WM_RBUTTONUP = 0x0205
WM_MBUTTONDOWN = 0x0207
WM_MBUTTONUP = 0x0208
WM_MOUSEWHEEL = 0x020A
WM_MOUSEHWHEEL = 0x020E
MSG_TEXT = {WM_MOUSEMOVE: 'WM_MOUSEMOVE',
WM_LBUTTONDOWN: 'WM_LBUTTONDOWN',
WM_LBUTTONUP: 'WM_LBUTTONUP',
WM_RBUTTONDOWN: 'WM_RBUTTONDOWN',
WM_RBUTTONUP: 'WM_RBUTTONUP',
WM_MBUTTONDOWN: 'WM_MBUTTONDOWN',
WM_MBUTTONUP: 'WM_MBUTTONUP',
WM_MOUSEWHEEL: 'WM_MOUSEWHEEL',
WM_MOUSEHWHEEL: 'WM_MOUSEHWHEEL'}
ULONG_PTR = WPARAM
LRESULT = LPARAM
LPMSG = POINTER(MSG)
HOOKPROC = WINFUNCTYPE(LRESULT, c_int, WPARAM, LPARAM)
LowLevelMouseProc = HOOKPROC
class MSLLHOOKSTRUCT(Structure):
_fields_ = (('pt', POINT),
('mouseData', DWORD),
('flags', DWORD),
('time', DWORD),
('dwExtraInfo', ULONG_PTR))
LPMSLLHOOKSTRUCT = POINTER(MSLLHOOKSTRUCT)
def errcheck_bool(result, func, args):
if not result:
raise WinError(get_last_error())
return args
user32.SetWindowsHookExW.errcheck = errcheck_bool
user32.SetWindowsHookExW.restype = HHOOK
user32.SetWindowsHookExW.argtypes = (c_int, # _In_ idHook
HOOKPROC, # _In_ lpfn
HINSTANCE, # _In_ hMod
DWORD) # _In_ dwThreadId
user32.CallNextHookEx.restype = LRESULT
user32.CallNextHookEx.argtypes = (HHOOK, # _In_opt_ hhk
c_int, # _In_ nCode
WPARAM, # _In_ wParam
LPARAM) # _In_ lParam
user32.GetMessageW.argtypes = (LPMSG, # _Out_ lpMsg
HWND, # _In_opt_ hWnd
UINT, # _In_ wMsgFilterMin
UINT) # _In_ wMsgFilterMax
user32.TranslateMessage.argtypes = (LPMSG,)
user32.DispatchMessageW.argtypes = (LPMSG,)
#LowLevelMouseProc
def LLMouseProc(nCode, wParam, lParam):
msg = cast(lParam, LPMSLLHOOKSTRUCT)[0]
if nCode == HC_ACTION:
msgid = MSG_TEXT.get(wParam, str(wParam))
msg = ((msg.pt.x, msg.pt.y),
msg.mouseData, msg.flags,
msg.time, msg.dwExtraInfo)
print('{:15s}: {}'.format(msgid, msg))
return user32.CallNextHookEx(None, nCode, wParam, lParam)
def mouse_msg_loop():
hHook = user32.SetWindowsHookExW(WH_MOUSE_LL, LLMouseProc, None, 0)
msg = MSG()
while True:
bRet = user32.GetMessageW(byref(msg), None, 0, 0)
if not bRet:
break
if bRet == -1:
raise WinError(get_last_error())
user32.TranslateMessage(byref(msg))
user32.DispatchMessageW(byref(msg))
if __name__ == '__main__':
import time
import threading
t = threading.Thread(target=mouse_msg_loop)
t.start()
while True:
try:
time.sleep(1)
except KeyboardInterrupt:
user32.PostThreadMessageW(t.ident, WM_QUIT, 0, 0)
break
A simplified version of the accepted answer
Note: pip install pywin32 first.
# Created by BaiJiFeiLong#gmail.com at 2022/2/10 22:27
from ctypes import WINFUNCTYPE, c_int, Structure, cast, POINTER, windll
from ctypes.wintypes import LPARAM, WPARAM, DWORD, PULONG, LONG
import win32con
import win32gui
def genStruct(name="Structure", **kwargs):
return type(name, (Structure,), dict(
_fields_=list(kwargs.items()),
__str__=lambda self: "%s(%s)" % (name, ",".join("%s=%s" % (k, getattr(self, k)) for k in kwargs))
))
#WINFUNCTYPE(LPARAM, c_int, WPARAM, LPARAM)
def hookProc(nCode, wParam, lParam):
msg = cast(lParam, POINTER(HookStruct))[0]
print(msgDict[wParam], msg)
return windll.user32.CallNextHookEx(None, nCode, WPARAM(wParam), LPARAM(lParam))
HookStruct = genStruct(
"Hook", pt=genStruct("Point", x=LONG, y=LONG), mouseData=DWORD, flags=DWORD, time=DWORD, dwExtraInfo=PULONG)
msgDict = {v: k for k, v in win32con.__dict__.items() if k.startswith("WM_")}
windll.user32.SetWindowsHookExW(win32con.WH_MOUSE_LL, hookProc, None, 0)
win32gui.PumpMessages()
Sample Output
WM_MOUSEMOVE Hook(pt=Point(x=50,y=702),mouseData=0,flags=0,time=343134468,dwExtraInfo=<ctypes.wintypes.LP_c_ulong object at 0x000001A466CDF8C8>)
WM_MOUSEMOVE Hook(pt=Point(x=49,y=704),mouseData=0,flags=0,time=343134484,dwExtraInfo=<ctypes.wintypes.LP_c_ulong object at 0x000001A466CDF8C8>)
WM_MOUSEMOVE Hook(pt=Point(x=49,y=705),mouseData=0,flags=0,time=343134484,dwExtraInfo=<ctypes.wintypes.LP_c_ulong object at 0x000001A466CDF8C8>)
WM_MOUSEMOVE Hook(pt=Point(x=49,y=705),mouseData=0,flags=0,time=343134500,dwExtraInfo=<ctypes.wintypes.LP_c_ulong object at 0x000001A466CDF8C8>)
WM_MOUSEMOVE Hook(pt=Point(x=49,y=706),mouseData=0,flags=0,time=343134500,dwExtraInfo=<ctypes.wintypes.LP_c_ulong object at 0x000001A466CDF8C8>)
WM_MOUSEMOVE Hook(pt=Point(x=48,y=707),mouseData=0,flags=0,time=343134515,dwExtraInfo=<ctypes.wintypes.LP_c_ulong object at 0x000001A466CDF8C8>)
WM_LBUTTONDOWN Hook(pt=Point(x=48,y=707),mouseData=0,flags=0,time=343134593,dwExtraInfo=<ctypes.wintypes.LP_c_ulong object at 0x000001A466CDF8C8>)
WM_LBUTTONUP Hook(pt=Point(x=48,y=707),mouseData=0,flags=0,time=343134671,dwExtraInfo=<ctypes.wintypes.LP_c_ulong object at 0x000001A466CDF8C8>)

CreateProcessWithLogonW, to create process as another user on different desktop, fails with browser

First of all it's hard to find a sophisticated title, because the whole situation is complicated and confusing.
I'm trying to create a Process as a user with restricted rights (user is part of the "Users"-group). The process should spawn on another desktop(let's name it "test"), not on the "default"-desktop. The process which uses the CreateProcessWithLogonW function is running with admin rights. While a normal application (notepad, vlc, winrar) runs without a problem, browser applications like IE, Firefox, Chrome seem to have a problem. The were started but they're instantly closing without an errorcode. If I'm using a user with administration rights every application runs without an error.
I'm adding ACEs to the winstation and desktop's DACL. Also if I start cmd.exe like this and try to run firefoxthe same problem occurs. I'm using python 2.7 with ctypes and win32api:
from _ctypes import Array, POINTER, sizeof, byref
from ctypes import wintypes, windll, WinError
import ctypes
from ctypes.wintypes import BYTE, HANDLE, DWORD, BOOL, LPCWSTR, LPWSTR, LPVOID
from win32con import CREATE_NEW_CONSOLE, READ_CONTROL, WRITE_DAC, \
GROUP_SECURITY_INFORMATION, DESKTOP_WRITEOBJECTS, DESKTOP_READOBJECTS
import win32con
from win32security import ACL_REVISION_DS
import win32security
import win32service
WINSTA_ALL = (win32con.WINSTA_ACCESSCLIPBOARD | win32con.WINSTA_ACCESSGLOBALATOMS | \
win32con.WINSTA_CREATEDESKTOP | win32con.WINSTA_ENUMDESKTOPS |\
win32con.WINSTA_ENUMERATE | win32con.WINSTA_EXITWINDOWS |\
win32con.WINSTA_READATTRIBUTES | win32con.WINSTA_READSCREEN |\
win32con.WINSTA_WRITEATTRIBUTES | win32con.DELETE |\
win32con.READ_CONTROL | win32con.WRITE_DAC |\
win32con.WRITE_OWNER)
DESKTOP_ALL = (win32con.DESKTOP_CREATEMENU | win32con.DESKTOP_CREATEWINDOW |\
win32con.DESKTOP_ENUMERATE | win32con.DESKTOP_HOOKCONTROL |\
win32con.DESKTOP_JOURNALPLAYBACK | win32con.DESKTOP_JOURNALRECORD |\
win32con.DESKTOP_READOBJECTS | win32con.DESKTOP_SWITCHDESKTOP |\
win32con.DESKTOP_WRITEOBJECTS | win32con.DELETE |\
win32con.READ_CONTROL | win32con.WRITE_DAC |\
win32con.WRITE_OWNER)
GENERIC_ACCESS = (win32con.GENERIC_READ | win32con.GENERIC_WRITE |\
win32con.GENERIC_EXECUTE | win32con.GENERIC_ALL)
INVALID_HANDLE_VALUE = -1
CREATE_UNICODE_ENVIRONMENT = 0x00000400
CData = Array.__base__
LPBYTE = POINTER(BYTE)
class PROCESS_INFORMATION(ctypes.Structure):
_fields_ = [
('hProcess', HANDLE),
('hThread', HANDLE),
('dwProcessId', DWORD),
('dwThreadId', DWORD),
]
LPPROCESS_INFORMATION = POINTER(PROCESS_INFORMATION)
class STARTUPINFOW(ctypes.Structure):
_fields_ = (('cb', wintypes.DWORD),
('lpReserved', wintypes.LPWSTR),
('lpDesktop', wintypes.LPWSTR),
('lpTitle', wintypes.LPWSTR),
('dwX', wintypes.DWORD),
('dwY', wintypes.DWORD),
('dwXSize', wintypes.DWORD),
('dwYSize', wintypes.DWORD),
('dwXCountChars', wintypes.DWORD),
('dwYCountChars', wintypes.DWORD),
('dwFillAttribute', wintypes.DWORD),
('dwFlags', wintypes.DWORD),
('wShowWindow', wintypes.WORD),
('cbReserved2', wintypes.WORD),
('lpReserved2', LPBYTE),
('hStdInput', wintypes.HANDLE),
('hStdOutput', wintypes.HANDLE),
('hStdError', wintypes.HANDLE))
LPSTARTUPINFOW = POINTER(STARTUPINFOW)
windll.advapi32.CreateProcessWithLogonW.restype = BOOL
windll.advapi32.CreateProcessWithLogonW.argtypes = [
LPCWSTR, # lpUsername
LPCWSTR, # lpDomain
LPCWSTR, # lpPassword
DWORD, # dwLogonFlags
LPCWSTR, # lpApplicationName
LPWSTR, # lpCommandLine (inout)
DWORD, # dwCreationFlags
LPVOID, # lpEnvironment (force Unicode)
LPCWSTR, # lpCurrentDirectory
LPSTARTUPINFOW, # lpStartupInfo
LPPROCESS_INFORMATION, # lpProcessInfo (out)
]
def CreateProcessWithLogonW(
lpUsername=None,
lpDomain=None,
lpPassword=None,
dwLogonFlags=0,
lpApplicationName=0,
lpCommandLine=None,
dwCreationFlags=0,
lpEnvironment=None,
lpCurrentDirectory=None,
startupInfo=None):
if (lpCommandLine is not None and not isinstance(lpCommandLine, CData)):
lpCommandLine = ctypes.create_unicode_buffer(lpCommandLine)
dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT
if startupInfo is None:
startupInfo = STARTUPINFOW(sizeof(STARTUPINFOW))
processInformation = PROCESS_INFORMATION(INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE)
success = windll.advapi32.CreateProcessWithLogonW(
lpUsername, lpDomain, lpPassword, dwLogonFlags, lpApplicationName,
lpCommandLine, dwCreationFlags, lpEnvironment, lpCurrentDirectory,
byref(startupInfo), byref(processInformation))
if not success:
raise WinError()
return processInformation
if __name__ == '__main__':
user_group_sid = win32security.LookupAccountName('','Users')[0]
#
#Adding rights for user group "users" to winsta0
#
#getting winsta0 handle
hwinsta = win32service.OpenWindowStation("winsta0",False,READ_CONTROL | WRITE_DAC )
#getting security descriptor by winsta0-handle
sec_desc_winsta = win32security.GetUserObjectSecurity(hwinsta,win32security.OWNER_SECURITY_INFORMATION |win32security.DACL_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION)
#getting DACL from security descriptor
dacl_winsta = sec_desc_winsta.GetSecurityDescriptorDacl()
if dacl_winsta is None:
#create DACL if not exisiting
dacl_winsta=win32security.ACL()
#Adding ACEs to DACL for specific user group
dacl_winsta.AddAccessAllowedAce(ACL_REVISION_DS,GENERIC_ACCESS,user_group_sid)
dacl_winsta.AddAccessAllowedAce(ACL_REVISION_DS,WINSTA_ALL,user_group_sid)
#setting modified DACL for winsta0
win32security.SetSecurityInfo(hwinsta, win32security.SE_WINDOW_OBJECT,win32security.DACL_SECURITY_INFORMATION,None,None,dacl_winsta,None)
#
#Adding rights for user group "users" to desktop
#
#getting handle of different Desktop
hdesk = win32service.OpenDesktop("test",0 ,False,READ_CONTROL | WRITE_DAC |DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS)
#getting security descriptor by desktop-handle
sec_desc_desk = win32security.GetUserObjectSecurity(hdesk,win32security.OWNER_SECURITY_INFORMATION |win32security.DACL_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION )
#getting DACL from security descriptor
dacl_desk = sec_desc_desk.GetSecurityDescriptorDacl()
if dacl_desk is None:
#create DACL if not exisiting
dacl_desk=win32security.ACL()
#Adding ACEs to DACL for specific user group
dacl_desk.AddAccessAllowedAce(ACL_REVISION_DS,GENERIC_ACCESS,user_group_sid)
dacl_desk.AddAccessAllowedAce(ACL_REVISION_DS,DESKTOP_ALL,user_group_sid)
#setting modified DACL for desktop
win32security.SetSecurityInfo(hdesk, win32security.SE_WINDOW_OBJECT,win32security.DACL_SECURITY_INFORMATION,None,None,dacl_desk,None)
firefox = "C:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe"
notepad = "C:\\windows\\notepad.exe"
si = STARTUPINFOW()
si.lpDesktop = "winsta0\\test"
pi = CreateProcessWithLogonW("test",
".",
"password",
0x00000001,
None,
firefox,
CREATE_NEW_CONSOLE,
startupInfo=si)
The weird thing is, if I'm switching to the "test"-desktop and using ShellRunas (shift+right-click -> run as different user) it works perfectly. So it seems like I'm missing to grant my user some rights, but I don't know which rights.
So here are my questions:
Do I use CreateProccesWithLogonW correctly for my purpose?
What's the difference between CreateProccesWithLogonW and Shellrunas?
How does Shellrunas works behind the scene?

Python Ctypes Read/WriteProcessMemory() - Error 5/998 Help!

Please don't get scared but the following code, if you are familiar with ctypes or C it should be easy to read.
I have been trying to get my ReadProcessMemory() and WriteProcessMemory() functions to be working for so long and have tried almost every possibility but the right one.
It launches the target program, returns its PID and handle just fine. But I always get a error code of 5 - ERROR_ACCESS_DENIED. When I run the read function(forget the write for now). I am launching this program as what I believe to be a CHILD process with PROCESS_ALL_ACCESS or CREATE_PRESERVE_CODE_AUTHZ_LEVEL.
I have also tried PROCESS_ALL_ACCESS and PROCESS_VM_READ when I open the handle.
I can also say that it is a valid memory location because I can find it on the running program with CheatEngine.
As for VirtualQuery() I get an error code of 998 - ERROR_NOACCESS which further confirms my suspicion of it being some security/privilege problem.
Any help or ideas would be very appreciated, again, it's my whole program so far, don't let it scare you =P.
from ctypes import *
from ctypes.wintypes import BOOL
import binascii
BYTE = c_ubyte
WORD = c_ushort
DWORD = c_ulong
LPBYTE = POINTER(c_ubyte)
LPTSTR = POINTER(c_char)
HANDLE = c_void_p
PVOID = c_void_p
LPVOID = c_void_p
UNIT_PTR = c_ulong
SIZE_T = c_ulong
class STARTUPINFO(Structure):
_fields_ = [("cb", DWORD),
("lpReserved", LPTSTR),
("lpDesktop", LPTSTR),
("lpTitle", LPTSTR),
("dwX", DWORD),
("dwY", DWORD),
("dwXSize", DWORD),
("dwYSize", DWORD),
("dwXCountChars", DWORD),
("dwYCountChars", DWORD),
("dwFillAttribute",DWORD),
("dwFlags", DWORD),
("wShowWindow", WORD),
("cbReserved2", WORD),
("lpReserved2", LPBYTE),
("hStdInput", HANDLE),
("hStdOutput", HANDLE),
("hStdError", HANDLE),]
class PROCESS_INFORMATION(Structure):
_fields_ = [("hProcess", HANDLE),
("hThread", HANDLE),
("dwProcessId", DWORD),
("dwThreadId", DWORD),]
class MEMORY_BASIC_INFORMATION(Structure):
_fields_ = [("BaseAddress", PVOID),
("AllocationBase", PVOID),
("AllocationProtect", DWORD),
("RegionSize", SIZE_T),
("State", DWORD),
("Protect", DWORD),
("Type", DWORD),]
class SECURITY_ATTRIBUTES(Structure):
_fields_ = [("Length", DWORD),
("SecDescriptor", LPVOID),
("InheritHandle", BOOL)]
class Main():
def __init__(self):
self.h_process = None
self.pid = None
def launch(self, path_to_exe):
CREATE_NEW_CONSOLE = 0x00000010
CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000
startupinfo = STARTUPINFO()
process_information = PROCESS_INFORMATION()
security_attributes = SECURITY_ATTRIBUTES()
startupinfo.dwFlags = 0x1
startupinfo.wShowWindow = 0x0
startupinfo.cb = sizeof(startupinfo)
security_attributes.Length = sizeof(security_attributes)
security_attributes.SecDescriptior = None
security_attributes.InheritHandle = True
if windll.kernel32.CreateProcessA(path_to_exe,
None,
byref(security_attributes),
byref(security_attributes),
True,
CREATE_PRESERVE_CODE_AUTHZ_LEVEL,
None,
None,
byref(startupinfo),
byref(process_information)):
self.pid = process_information.dwProcessId
print "Success: CreateProcess - ", path_to_exe
else:
print "Failed: Create Process - Error code: ", windll.kernel32.GetLastError()
def get_handle(self, pid):
PROCESS_ALL_ACCESS = 0x001F0FFF
PROCESS_VM_READ = 0x0010
self.h_process = windll.kernel32.OpenProcess(PROCESS_VM_READ, False, pid)
if self.h_process:
print "Success: Got Handle - PID:", self.pid
else:
print "Failed: Get Handle - Error code: ", windll.kernel32.GetLastError()
windll.kernel32.SetLastError(10000)
def read_memory(self, address):
buffer = c_char_p("The data goes here")
bufferSize = len(buffer.value)
bytesRead = c_ulong(0)
if windll.kernel32.ReadProcessMemory(self.h_process, address, buffer, bufferSize, byref(bytesRead)):
print "Success: Read Memory - ", buffer.value
else:
print "Failed: Read Memory - Error Code: ", windll.kernel32.GetLastError()
windll.kernel32.CloseHandle(self.h_process)
windll.kernel32.SetLastError(10000)
def write_memory(self, address, data):
count = c_ulong(0)
length = len(data)
c_data = c_char_p(data[count.value:])
null = c_int(0)
if not windll.kernel32.WriteProcessMemory(self.h_process, address, c_data, length, byref(count)):
print "Failed: Write Memory - Error Code: ", windll.kernel32.GetLastError()
windll.kernel32.SetLastError(10000)
else:
return False
def virtual_query(self, address):
basic_memory_info = MEMORY_BASIC_INFORMATION()
windll.kernel32.SetLastError(10000)
result = windll.kernel32.VirtualQuery(address, byref(basic_memory_info), byref(basic_memory_info))
if result:
return True
else:
print "Failed: Virtual Query - Error Code: ", windll.kernel32.GetLastError()
main = Main()
address = None
main.launch("C:\Program Files\ProgramFolder\Program.exe")
main.get_handle(main.pid)
#main.write_memory(address, "\x61")
while 1:
print '1 to enter an address'
print '2 to virtual query address'
print '3 to read address'
choice = raw_input('Choice: ')
if choice == '1':
address = raw_input('Enter and address: ')
if choice == '2':
main.virtual_query(address)
if choice == '3':
main.read_memory(address)
Thanks!
You should try to set debugging privileges to your process.
Use the following code once before you try to Open / Create a process.
class TOKEN_PRIVILEGES( Structure ):
_fields_ = [
('PrivilegeCount', c_uint),
('Luid', LUID),
('Attributes', c_uint) ]
OpenProcessToken = windll.advapi32.OpenProcessToken
OpenProcessToken.argtypes = [
c_int, # HANDLE ProcessHandle
c_uint, # DWORD DesiredAccess
c_void_p ] # PHANDLE TokenHandle
OpenProcessToken.restype = ErrorIfZero
AdjustTokenPrivileges = windll.advapi32.AdjustTokenPrivileges
AdjustTokenPrivileges.argtypes = [
c_int, # HANDLE TokenHandle
c_int, # BOOL DisableAllPrivileges
c_void_p, # PTOKEN_PRIVILEGES NewState
c_uint, # DWORD BufferLength
c_void_p, # PTOKEN_PRIVILEGES PreviousState
c_void_p ] # PDWORD ReturnLength
AdjustTokenPrivileges.restype = ErrorIfZero
LookupPrivilegeValue = windll.advapi32.LookupPrivilegeValueA
LookupPrivilegeValue.argtypes = [
c_char_p, # LPCTSTR lpSystemName
c_char_p, # LPCTSTR lpName
c_void_p ] # PLUID lpLuid
LookupPrivilegeValue.restype = ErrorIfZero
access_token = c_int(0)
privileges = TOKEN_PRIVILEGES()
OpenProcessToken( GetCurrentProcess(), win32con.TOKEN_QUERY | win32con.TOKEN_ADJUST_PRIVILEGES, byref(access_token) )
access_token = access_token.value
LookupPrivilegeValue( None, "SeDebugPrivilege", byref(privileges.Luid) )
privileges.PrivilegeCount = 1
privileges.Attributes = 2
AdjustTokenPrivileges(
access_token,
0,
byref(privileges),
0,
None,
None )
CloseHandle( access_token )
Maybe this will help you: Creating a Security Descriptor for a New Object in C++
One possible reason for your access denied error is that the user under which you run WriteProcessMemory runs needs to have DEBUG privilege.
Starting with Vista, this privilege is only activated for Administrators, and only when running the application with "Run as Admin".
You can add the privilege to any account.
I see several problems with your code, and it's difficult to know which one is the underlying cause of your exact problem. For example, the line:
address = raw_input('Enter and address: ')
Should probably be something like:
address = long(raw_input('Enter and address: '), 0)
As the code stands, every time you pass address to a function via ctypes what you are actually doing is creating a temporary buffer which contains exactly the string typed by the user and passing in the address of that buffer in the Python process. Definitely not what you want. If I fix that issue, then your program seems to work most of the time.
From my limited testing, most (all?) of the rest of the failures can be fixed by setting the correct argtypes for ReadProcessMemory. This is the single biggest issue I see with ctypes code, a problem exacerbated by handling ctypes.c_voidp as int in Python. If argtypes is not specified, then all arguments are considered to be ctypes.c_int. Anything outside of the range of signed integer -- a pointer or handle with high bit set, for example -- is silently truncated.
Not the cause of your bugs but suboptimal are the lines:
buffer = c_char_p("The data goes here")
bufferSize = len(buffer.value)
The ctypes module provides functions for creating buffers:
bufferSize = 32
buffer = ctypes.create_string_buffer(bufferSize)
Hopefully this will get you down the right path.
PROCESS_VM_READ is not enough: Try use both PROCESS_VM_WRITE + PROCESS_VM_OPERATION. I also received an error violation but the process memory still changed. Add try catch to keep your program alive.
PROCESS_VM_READ = 0x0010
PROCESS_VM_WRITE = 0x0020
PROCESS_VM_OPERATION = 0x0008
PROCESS_ALL_ACCESS = 0x1F0FFF
For me PROCESS_VM_WRITE was not enough, I needed to add PROCESS_VM_OPERATION as well.

Categories