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.