在没有提升权限的情况下调用二进制文件
Call binary without elevated privilege
为了从 Python 请求 UAC 提升权限,当调用外部程序时,您可以这样做
ctypes.windll.shell32.ShellExecuteW(None, "runas", my_binary_file_path, "", None, 1)
然而,假设您的 Python 脚本正在 以 管理员权限执行,您如何在没有 管理员权限的情况下调用外部程序 权利?
您可以使用类似的东西:
import os
os.system('calc.exe')
OR 如果你想使用 ctypes 调用 CreateProcess API函数
from ctypes.wintypes import *
from ctypes import *
kernel32 = WinDLL('kernel32', use_last_error=True)
PVOID = c_void_p
LPVOID = PVOID
LPTSTR = c_void_p
LPBYTE = c_char_p
LPSECURITY_ATTRIBUTES = LPVOID
# Specifies the window station, desktop, standard handles, and appearance of the main window for a process at creation time.
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms686331(v=vs.85).aspx
class STARTUPINFO(Structure): # typedef struct _STARTUPINFO
_fields_ = [ # {
('cb', DWORD), # DWORD cb;
('lpReserved', LPTSTR), # LPTSTR lpReserved;
('lpDesktop', LPTSTR), # LPTSTR lpDesktop;
('lpTitle', LPTSTR), # LPTSTR lpTitle;
('dwX', DWORD), # DWORD dwX;
('dwY', DWORD), # DWORD dwY;
('dwXSize', DWORD), # DWORD dwXSize;
('dwYSize', DWORD), # DWORD dwYSize;
('dwXCountChars', DWORD), # DWORD dwXCountChars;
('dwYCountChars', DWORD), # DWORD dwYCountChars;
('dwFillAttribute', DWORD), # DWORD dwFillAttribute;
('dwFlags', DWORD), # DWORD dwFlags;
('wShowWindow', WORD), # WORD wShowWindow;
('cbReserved2', WORD), # WORD cbReserved2;
('lpReserved2', LPBYTE), # LPBYTE lpReserved2;
('hStdInput', HANDLE), # HANDLE hStdInput;
('hStdOutput', HANDLE), # HANDLE hStdOutput;
('hStdError', HANDLE) # HANDLE hStdError;
] # }
# Contains information about a newly created process and its primary thread. It is used with the CreateProcess, CreateProcessAsUser, CreateProcessWithLogonW, or CreateProcessWithTokenW function.
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms684873(v=vs.85).aspx
class PROCESS_INFORMATION(Structure): # typedef struct _PROCESS_INFORMATION
_fields_ = [ # {
("hProcess", HANDLE), # HANDLE hProcess;
("hThread", HANDLE), # HANDLE hThread;
("dwProcessId", DWORD), # DWORD dwProcessId;
("dwThreadId", DWORD) # DWORD dwThreadId;
] # }
# Creates a new process and its primary thread. The new process runs in the security context of the calling process.
CreateProcess = kernel32.CreateProcessW # https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425(v=vs.85).aspx
CreateProcess.restype = BOOL # BOOL WINAPI CreateProcess
CreateProcess.argtypes = [ # (
LPCWSTR, # LPCTSTR lpApplicationName,
LPWSTR, # LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES, # LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES, # LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL, # BOOL bInheritHandles,
DWORD, # DWORD dwCreationFlags,
LPVOID, # LPVOID lpEnvironment,
LPCWSTR, # LPCTSTR lpCurrentDirectory,
POINTER(STARTUPINFO), # LPSTARTUPINFO lpStartupInfo,
POINTER(PROCESS_INFORMATION) # LPPROCESS_INFORMATION lpProcessInformation
]
# Process creation flags | https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863(v=vs.85).aspx
CREATE_NEW_CONSOLE = 0x00000010 # The new process has a new console, instead of inheriting its parent's console (the default).
EXTENDED_STARTUPINFO_PRESENT = 0x00080000 # The process is created with extended startup information; the lpStartupInfo parameter specifies a STARTUPINFOEX structure.
# Where lpApplicationName is the path of the executable
CreateProcess(
None, # _In_opt_ lpApplicationName The lpApplicationName parameter can be NULL. In that case, the module name must be the first white space–delimited token in the lpCommandLine string.
u"C:\Windows\System32\cmd.exe", # _Inout_opt lpCommandLine The command line to be executed
None, # _In_opt_ pProcessAttributes A pointer to a SECURITY_ATTRIBUTES structure that determines whether the returned handle to the new process object can be inherited by child processes. If lpProcessAttributes is NULL, the handle cannot be inherited.
None, # _In_opt_ lpThreadAttributes A pointer to a SECURITY_ATTRIBUTES structure that determines whether the returned handle to the new thread object can be inherited by child processes. If lpThreadAttributes is NULL, the handle cannot be inherited.
0, # _In_ bInheritHandles If this parameter is TRUE, each inheritable handle in the calling process is inherited by the new process. If the parameter is FALSE, the handles are not inherited. Note that inherited handles have the same value and access rights as the original handles.
(CREATE_NEW_CONSOLE | EXTENDED_STARTUPINFO_PRESENT), #_In_dwCreationFlags The flags that control the priority class and the creation of the process # To specify these attributes when creating a process, specify EXTENDED_STARTUPINFO_PRESENT in the dwCreationFlag parameter and a STARTUPINFOEX structure in the lpStartupInfo parameter
None, # _In_opt_ lpEnvironment A pointer to the environment block for the new process. If this parameter is NULL, the new process uses the environment of the calling process.
None, # _In_opt_ lpCurrentDirectory The full path to the current directory for the process. If this parameter is NULL, the new process will have the same current drive and directory as the calling process
byref(lpStartupInfo), # _In_ lpStartupInfo A pointer to a STARTUPINFO or STARTUPINFOEX structure.To set extended attributes, use a STARTUPINFOEX structure and specify EXTENDED_STARTUPINFO_PRESENT in the dwCreationFlags parameter.
byref(lpProcessInformation)) # _Out_ lpProcessInformation A pointer to a PROCESS_INFORMATION structure that receives identification information about the new process.
我更喜欢的一种方法是 运行 作为 shell 用户。首先打开 shell 进程并复制其令牌。您可以通过调用 GetShellWindow
然后调用 GetWindowThreadProcessId
来获取 shell 进程 PID。通常这是资源管理器。
默认情况下,管理员帐户没有SeAssignPrimaryTokenPrivilege,在这种情况下您不能直接调用CreateProcessAsUser
。您必须请求一个更有特权的进程来代表您拨打电话。 CreateProcessWithTokenW
通过对辅助登录服务进行远程过程调用来为您完成此操作。
PyWin32 不包装 GetShellWindow
和 CreateProcessWithTokenW
,因此您需要使用 ctypes 来调用它们。
很少 Windows 系统可能 运行ning 没有常规 shell,或者 shell 无法通过 [注册其 window =19=]。在这种情况下,GetShellWindow
returns NULL
。作为这种情况的回退,您可以使用一种有点可疑(但有效)的方法来获取会话用户的令牌并调用 CreateProcessAsUser
。
首先获取会话 Windows 子系统进程 csrss.exe 的 PID。最简单的方法是调用未记录(但稳定)的函数 CsrGetProcessId
。启用 SeDebugPrivilege 以使用有限的查询访问权限打开此进程。然后打开它的Token,复制,模拟。现在您拥有通过 WTSQueryUserToken
获取会话用户令牌所需的 SeTcbPrivilege,并且您还拥有能够调用 CreateProcessAsUser
.
的 SeAssignPrimaryTokenPrivilege
导入和 ctypes 定义
import os
import contextlib
import win32con
import winerror
import win32api
import win32process
import win32security
import win32ts
import pywintypes
import ctypes
from ctypes import wintypes
ntdll = ctypes.WinDLL('ntdll')
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
advapi32 = ctypes.WinDLL('advapi32', use_last_error=True)
user32 = ctypes.WinDLL('user32', use_last_error=True)
TOKEN_ADJUST_SESSIONID = 0x0100
PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
LPBYTE = ctypes.POINTER(wintypes.BYTE)
class STARTUPINFO(ctypes.Structure):
"""https://msdn.microsoft.com/en-us/library/ms686331"""
__slots__ = ()
_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)
LPSTARTUPINFO = ctypes.POINTER(STARTUPINFO)
class PROCESS_INFORMATION(ctypes.Structure):
"""https://msdn.microsoft.com/en-us/library/ms684873"""
__slots__ = ()
_fields_ = (('hProcess', wintypes.HANDLE),
('hThread', wintypes.HANDLE),
('dwProcessId', wintypes.DWORD),
('dwThreadId', wintypes.DWORD))
LPPROCESS_INFORMATION = ctypes.POINTER(PROCESS_INFORMATION)
kernel32.CloseHandle.argtypes = (wintypes.HANDLE,)
# https://msdn.microsoft.com/en-us/library/ms682434
advapi32.CreateProcessWithTokenW.argtypes = (
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/ms633512
user32.GetShellWindow.restype = wintypes.HWND
辅助函数
def adjust_token_privileges(htoken, state):
prev_state = win32security.AdjustTokenPrivileges(htoken, False, state)
error = win32api.GetLastError()
if error == winerror.ERROR_NOT_ALL_ASSIGNED:
raise pywintypes.error(
error, 'AdjustTokenPrivileges',
win32api.FormatMessageW(error))
return prev_state
def enable_token_privileges(htoken, *privilege_names):
state = []
for name in privilege_names:
state.append((win32security.LookupPrivilegeValue(None, name),
win32con.SE_PRIVILEGE_ENABLED))
return adjust_token_privileges(htoken, state)
@contextlib.contextmanager
def open_effective_token(access, open_as_self=True):
hthread = win32api.GetCurrentThread()
impersonated_self = False
try:
htoken = win32security.OpenThreadToken(
hthread, access, open_as_self)
except pywintypes.error as e:
if e.winerror != winerror.ERROR_NO_TOKEN:
raise
win32security.ImpersonateSelf(win32security.SecurityImpersonation)
impersonated_self = True
htoken = win32security.OpenThreadToken(
hthread, access, open_as_self)
try:
yield htoken
finally:
if impersonated_self:
win32security.SetThreadToken(None, None)
@contextlib.contextmanager
def enable_privileges(*privilege_names):
"""Enable a set of privileges for the current thread."""
prev_state = ()
with open_effective_token(
win32con.TOKEN_QUERY |
win32con.TOKEN_ADJUST_PRIVILEGES) as htoken:
prev_state = enable_token_privileges(htoken, *privilege_names)
try:
yield
finally:
if prev_state:
adjust_token_privileges(htoken, prev_state)
def duplicate_shell_token():
hWndShell = user32.GetShellWindow()
if not hWndShell:
raise pywintypes.error(
winerror.ERROR_FILE_NOT_FOUND,
'GetShellWindow', 'no shell window')
tid, pid = win32process.GetWindowThreadProcessId(hWndShell)
hProcShell = win32api.OpenProcess(
win32con.PROCESS_QUERY_INFORMATION, False, pid)
hTokenShell = win32security.OpenProcessToken(
hProcShell, win32con.TOKEN_DUPLICATE)
# Contrary to MSDN, CreateProcessWithTokenW also requires
# TOKEN_ADJUST_DEFAULT and TOKEN_ADJUST_SESSIONID
return win32security.DuplicateTokenEx(
hTokenShell,
win32security.SecurityImpersonation,
win32con.TOKEN_ASSIGN_PRIMARY |
win32con.TOKEN_DUPLICATE |
win32con.TOKEN_QUERY |
win32con.TOKEN_ADJUST_DEFAULT |
TOKEN_ADJUST_SESSIONID,
win32security.TokenPrimary, None)
@contextlib.contextmanager
def impersonate_system():
with enable_privileges(win32security.SE_DEBUG_NAME):
pid_csr = ntdll.CsrGetProcessId()
hprocess_csr = win32api.OpenProcess(
PROCESS_QUERY_LIMITED_INFORMATION, False, pid_csr)
htoken_csr = win32security.OpenProcessToken(
hprocess_csr, win32con.TOKEN_DUPLICATE)
htoken = win32security.DuplicateTokenEx(
htoken_csr, win32security.SecurityImpersonation,
win32con.TOKEN_QUERY |
win32con.TOKEN_IMPERSONATE |
win32con.TOKEN_ADJUST_PRIVILEGES,
win32security.TokenImpersonation)
enable_token_privileges(
htoken,
win32security.SE_TCB_NAME,
win32security.SE_INCREASE_QUOTA_NAME,
win32security.SE_ASSIGNPRIMARYTOKEN_NAME)
try:
htoken_prev = win32security.OpenThreadToken(
win32api.GetCurrentThread(), win32con.TOKEN_IMPERSONATE, True)
except pywintypes.error as e:
if e.winerror != winerror.ERROR_NO_TOKEN:
raise
htoken_prev = None
win32security.SetThreadToken(None, htoken)
try:
yield
finally:
win32security.SetThreadToken(None, htoken_prev)
def startupinfo_update(si_src, si_dst):
for name in ('lpDesktop', 'lpTitle', 'dwX', 'dwY', 'dwXSize',
'dwYSize', 'dwXCountChars', 'dwYCountChars',
'dwFillAttribute', 'dwFlags', 'wShowWindow',
'hStdInput', 'hStdOutput', 'hStdError'):
try:
setattr(si_dst, name, getattr(si_src, name))
except AttributeError:
pass
主要功能
def runas_session_user(cmd, executable=None, creationflags=0, cwd=None,
startupinfo=None, return_handles=False):
if not creationflags & win32con.DETACHED_PROCESS:
creationflags |= win32con.CREATE_NEW_CONSOLE
if cwd is None:
cwd = os.getcwd()
si = win32process.STARTUPINFO()
if startupinfo:
startupinfo_update(startupinfo, si)
with impersonate_system():
htoken_user = win32ts.WTSQueryUserToken(
win32ts.WTS_CURRENT_SESSION)
hProcess, hThread, dwProcessId, dwThreadId = (
win32process.CreateProcessAsUser(
htoken_user, executable, cmd, None, None, False,
creationflags, None, cwd, si))
if return_handles:
return hProcess, hThread
return dwProcessId, dwThreadId
def runas_shell_user(cmd, executable=None, creationflags=0, cwd=None,
startupinfo=None, return_handles=False):
if not creationflags & win32con.DETACHED_PROCESS:
creationflags |= win32con.CREATE_NEW_CONSOLE
if cwd is None:
cwd = os.getcwd()
si = STARTUPINFO()
if startupinfo:
startupinfo_update(startupinfo, si)
pi = PROCESS_INFORMATION()
try:
htoken = duplicate_shell_token()
except pywintypes.error as e:
if e.winerror != winerror.ERROR_FILE_NOT_FOUND:
raise
return runas_session_user(cmd, executable, creationflags, cwd,
startupinfo, return_handles)
with enable_privileges(win32security.SE_IMPERSONATE_NAME):
if not advapi32.CreateProcessWithTokenW(
int(htoken), 0, executable, cmd, creationflags, None,
cwd, ctypes.byref(si), ctypes.byref(pi)):
error = ctypes.get_last_error()
raise pywintypes.error(
error, 'CreateProcessWithTokenW',
win32api.FormatMessageW(error))
hProcess = pywintypes.HANDLE(pi.hProcess)
hThread = pywintypes.HANDLE(pi.hThread)
if return_handles:
return hProcess, hThread
return pi.dwProcessId, pi.dwThreadId
为了从 Python 请求 UAC 提升权限,当调用外部程序时,您可以这样做
ctypes.windll.shell32.ShellExecuteW(None, "runas", my_binary_file_path, "", None, 1)
然而,假设您的 Python 脚本正在 以 管理员权限执行,您如何在没有 管理员权限的情况下调用外部程序 权利?
您可以使用类似的东西:
import os
os.system('calc.exe')
OR 如果你想使用 ctypes 调用 CreateProcess API函数
from ctypes.wintypes import *
from ctypes import *
kernel32 = WinDLL('kernel32', use_last_error=True)
PVOID = c_void_p
LPVOID = PVOID
LPTSTR = c_void_p
LPBYTE = c_char_p
LPSECURITY_ATTRIBUTES = LPVOID
# Specifies the window station, desktop, standard handles, and appearance of the main window for a process at creation time.
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms686331(v=vs.85).aspx
class STARTUPINFO(Structure): # typedef struct _STARTUPINFO
_fields_ = [ # {
('cb', DWORD), # DWORD cb;
('lpReserved', LPTSTR), # LPTSTR lpReserved;
('lpDesktop', LPTSTR), # LPTSTR lpDesktop;
('lpTitle', LPTSTR), # LPTSTR lpTitle;
('dwX', DWORD), # DWORD dwX;
('dwY', DWORD), # DWORD dwY;
('dwXSize', DWORD), # DWORD dwXSize;
('dwYSize', DWORD), # DWORD dwYSize;
('dwXCountChars', DWORD), # DWORD dwXCountChars;
('dwYCountChars', DWORD), # DWORD dwYCountChars;
('dwFillAttribute', DWORD), # DWORD dwFillAttribute;
('dwFlags', DWORD), # DWORD dwFlags;
('wShowWindow', WORD), # WORD wShowWindow;
('cbReserved2', WORD), # WORD cbReserved2;
('lpReserved2', LPBYTE), # LPBYTE lpReserved2;
('hStdInput', HANDLE), # HANDLE hStdInput;
('hStdOutput', HANDLE), # HANDLE hStdOutput;
('hStdError', HANDLE) # HANDLE hStdError;
] # }
# Contains information about a newly created process and its primary thread. It is used with the CreateProcess, CreateProcessAsUser, CreateProcessWithLogonW, or CreateProcessWithTokenW function.
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms684873(v=vs.85).aspx
class PROCESS_INFORMATION(Structure): # typedef struct _PROCESS_INFORMATION
_fields_ = [ # {
("hProcess", HANDLE), # HANDLE hProcess;
("hThread", HANDLE), # HANDLE hThread;
("dwProcessId", DWORD), # DWORD dwProcessId;
("dwThreadId", DWORD) # DWORD dwThreadId;
] # }
# Creates a new process and its primary thread. The new process runs in the security context of the calling process.
CreateProcess = kernel32.CreateProcessW # https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425(v=vs.85).aspx
CreateProcess.restype = BOOL # BOOL WINAPI CreateProcess
CreateProcess.argtypes = [ # (
LPCWSTR, # LPCTSTR lpApplicationName,
LPWSTR, # LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES, # LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES, # LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL, # BOOL bInheritHandles,
DWORD, # DWORD dwCreationFlags,
LPVOID, # LPVOID lpEnvironment,
LPCWSTR, # LPCTSTR lpCurrentDirectory,
POINTER(STARTUPINFO), # LPSTARTUPINFO lpStartupInfo,
POINTER(PROCESS_INFORMATION) # LPPROCESS_INFORMATION lpProcessInformation
]
# Process creation flags | https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863(v=vs.85).aspx
CREATE_NEW_CONSOLE = 0x00000010 # The new process has a new console, instead of inheriting its parent's console (the default).
EXTENDED_STARTUPINFO_PRESENT = 0x00080000 # The process is created with extended startup information; the lpStartupInfo parameter specifies a STARTUPINFOEX structure.
# Where lpApplicationName is the path of the executable
CreateProcess(
None, # _In_opt_ lpApplicationName The lpApplicationName parameter can be NULL. In that case, the module name must be the first white space–delimited token in the lpCommandLine string.
u"C:\Windows\System32\cmd.exe", # _Inout_opt lpCommandLine The command line to be executed
None, # _In_opt_ pProcessAttributes A pointer to a SECURITY_ATTRIBUTES structure that determines whether the returned handle to the new process object can be inherited by child processes. If lpProcessAttributes is NULL, the handle cannot be inherited.
None, # _In_opt_ lpThreadAttributes A pointer to a SECURITY_ATTRIBUTES structure that determines whether the returned handle to the new thread object can be inherited by child processes. If lpThreadAttributes is NULL, the handle cannot be inherited.
0, # _In_ bInheritHandles If this parameter is TRUE, each inheritable handle in the calling process is inherited by the new process. If the parameter is FALSE, the handles are not inherited. Note that inherited handles have the same value and access rights as the original handles.
(CREATE_NEW_CONSOLE | EXTENDED_STARTUPINFO_PRESENT), #_In_dwCreationFlags The flags that control the priority class and the creation of the process # To specify these attributes when creating a process, specify EXTENDED_STARTUPINFO_PRESENT in the dwCreationFlag parameter and a STARTUPINFOEX structure in the lpStartupInfo parameter
None, # _In_opt_ lpEnvironment A pointer to the environment block for the new process. If this parameter is NULL, the new process uses the environment of the calling process.
None, # _In_opt_ lpCurrentDirectory The full path to the current directory for the process. If this parameter is NULL, the new process will have the same current drive and directory as the calling process
byref(lpStartupInfo), # _In_ lpStartupInfo A pointer to a STARTUPINFO or STARTUPINFOEX structure.To set extended attributes, use a STARTUPINFOEX structure and specify EXTENDED_STARTUPINFO_PRESENT in the dwCreationFlags parameter.
byref(lpProcessInformation)) # _Out_ lpProcessInformation A pointer to a PROCESS_INFORMATION structure that receives identification information about the new process.
我更喜欢的一种方法是 运行 作为 shell 用户。首先打开 shell 进程并复制其令牌。您可以通过调用 GetShellWindow
然后调用 GetWindowThreadProcessId
来获取 shell 进程 PID。通常这是资源管理器。
默认情况下,管理员帐户没有SeAssignPrimaryTokenPrivilege,在这种情况下您不能直接调用CreateProcessAsUser
。您必须请求一个更有特权的进程来代表您拨打电话。 CreateProcessWithTokenW
通过对辅助登录服务进行远程过程调用来为您完成此操作。
PyWin32 不包装 GetShellWindow
和 CreateProcessWithTokenW
,因此您需要使用 ctypes 来调用它们。
很少 Windows 系统可能 运行ning 没有常规 shell,或者 shell 无法通过 [注册其 window =19=]。在这种情况下,GetShellWindow
returns NULL
。作为这种情况的回退,您可以使用一种有点可疑(但有效)的方法来获取会话用户的令牌并调用 CreateProcessAsUser
。
首先获取会话 Windows 子系统进程 csrss.exe 的 PID。最简单的方法是调用未记录(但稳定)的函数 CsrGetProcessId
。启用 SeDebugPrivilege 以使用有限的查询访问权限打开此进程。然后打开它的Token,复制,模拟。现在您拥有通过 WTSQueryUserToken
获取会话用户令牌所需的 SeTcbPrivilege,并且您还拥有能够调用 CreateProcessAsUser
.
导入和 ctypes 定义
import os
import contextlib
import win32con
import winerror
import win32api
import win32process
import win32security
import win32ts
import pywintypes
import ctypes
from ctypes import wintypes
ntdll = ctypes.WinDLL('ntdll')
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
advapi32 = ctypes.WinDLL('advapi32', use_last_error=True)
user32 = ctypes.WinDLL('user32', use_last_error=True)
TOKEN_ADJUST_SESSIONID = 0x0100
PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
LPBYTE = ctypes.POINTER(wintypes.BYTE)
class STARTUPINFO(ctypes.Structure):
"""https://msdn.microsoft.com/en-us/library/ms686331"""
__slots__ = ()
_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)
LPSTARTUPINFO = ctypes.POINTER(STARTUPINFO)
class PROCESS_INFORMATION(ctypes.Structure):
"""https://msdn.microsoft.com/en-us/library/ms684873"""
__slots__ = ()
_fields_ = (('hProcess', wintypes.HANDLE),
('hThread', wintypes.HANDLE),
('dwProcessId', wintypes.DWORD),
('dwThreadId', wintypes.DWORD))
LPPROCESS_INFORMATION = ctypes.POINTER(PROCESS_INFORMATION)
kernel32.CloseHandle.argtypes = (wintypes.HANDLE,)
# https://msdn.microsoft.com/en-us/library/ms682434
advapi32.CreateProcessWithTokenW.argtypes = (
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/ms633512
user32.GetShellWindow.restype = wintypes.HWND
辅助函数
def adjust_token_privileges(htoken, state):
prev_state = win32security.AdjustTokenPrivileges(htoken, False, state)
error = win32api.GetLastError()
if error == winerror.ERROR_NOT_ALL_ASSIGNED:
raise pywintypes.error(
error, 'AdjustTokenPrivileges',
win32api.FormatMessageW(error))
return prev_state
def enable_token_privileges(htoken, *privilege_names):
state = []
for name in privilege_names:
state.append((win32security.LookupPrivilegeValue(None, name),
win32con.SE_PRIVILEGE_ENABLED))
return adjust_token_privileges(htoken, state)
@contextlib.contextmanager
def open_effective_token(access, open_as_self=True):
hthread = win32api.GetCurrentThread()
impersonated_self = False
try:
htoken = win32security.OpenThreadToken(
hthread, access, open_as_self)
except pywintypes.error as e:
if e.winerror != winerror.ERROR_NO_TOKEN:
raise
win32security.ImpersonateSelf(win32security.SecurityImpersonation)
impersonated_self = True
htoken = win32security.OpenThreadToken(
hthread, access, open_as_self)
try:
yield htoken
finally:
if impersonated_self:
win32security.SetThreadToken(None, None)
@contextlib.contextmanager
def enable_privileges(*privilege_names):
"""Enable a set of privileges for the current thread."""
prev_state = ()
with open_effective_token(
win32con.TOKEN_QUERY |
win32con.TOKEN_ADJUST_PRIVILEGES) as htoken:
prev_state = enable_token_privileges(htoken, *privilege_names)
try:
yield
finally:
if prev_state:
adjust_token_privileges(htoken, prev_state)
def duplicate_shell_token():
hWndShell = user32.GetShellWindow()
if not hWndShell:
raise pywintypes.error(
winerror.ERROR_FILE_NOT_FOUND,
'GetShellWindow', 'no shell window')
tid, pid = win32process.GetWindowThreadProcessId(hWndShell)
hProcShell = win32api.OpenProcess(
win32con.PROCESS_QUERY_INFORMATION, False, pid)
hTokenShell = win32security.OpenProcessToken(
hProcShell, win32con.TOKEN_DUPLICATE)
# Contrary to MSDN, CreateProcessWithTokenW also requires
# TOKEN_ADJUST_DEFAULT and TOKEN_ADJUST_SESSIONID
return win32security.DuplicateTokenEx(
hTokenShell,
win32security.SecurityImpersonation,
win32con.TOKEN_ASSIGN_PRIMARY |
win32con.TOKEN_DUPLICATE |
win32con.TOKEN_QUERY |
win32con.TOKEN_ADJUST_DEFAULT |
TOKEN_ADJUST_SESSIONID,
win32security.TokenPrimary, None)
@contextlib.contextmanager
def impersonate_system():
with enable_privileges(win32security.SE_DEBUG_NAME):
pid_csr = ntdll.CsrGetProcessId()
hprocess_csr = win32api.OpenProcess(
PROCESS_QUERY_LIMITED_INFORMATION, False, pid_csr)
htoken_csr = win32security.OpenProcessToken(
hprocess_csr, win32con.TOKEN_DUPLICATE)
htoken = win32security.DuplicateTokenEx(
htoken_csr, win32security.SecurityImpersonation,
win32con.TOKEN_QUERY |
win32con.TOKEN_IMPERSONATE |
win32con.TOKEN_ADJUST_PRIVILEGES,
win32security.TokenImpersonation)
enable_token_privileges(
htoken,
win32security.SE_TCB_NAME,
win32security.SE_INCREASE_QUOTA_NAME,
win32security.SE_ASSIGNPRIMARYTOKEN_NAME)
try:
htoken_prev = win32security.OpenThreadToken(
win32api.GetCurrentThread(), win32con.TOKEN_IMPERSONATE, True)
except pywintypes.error as e:
if e.winerror != winerror.ERROR_NO_TOKEN:
raise
htoken_prev = None
win32security.SetThreadToken(None, htoken)
try:
yield
finally:
win32security.SetThreadToken(None, htoken_prev)
def startupinfo_update(si_src, si_dst):
for name in ('lpDesktop', 'lpTitle', 'dwX', 'dwY', 'dwXSize',
'dwYSize', 'dwXCountChars', 'dwYCountChars',
'dwFillAttribute', 'dwFlags', 'wShowWindow',
'hStdInput', 'hStdOutput', 'hStdError'):
try:
setattr(si_dst, name, getattr(si_src, name))
except AttributeError:
pass
主要功能
def runas_session_user(cmd, executable=None, creationflags=0, cwd=None,
startupinfo=None, return_handles=False):
if not creationflags & win32con.DETACHED_PROCESS:
creationflags |= win32con.CREATE_NEW_CONSOLE
if cwd is None:
cwd = os.getcwd()
si = win32process.STARTUPINFO()
if startupinfo:
startupinfo_update(startupinfo, si)
with impersonate_system():
htoken_user = win32ts.WTSQueryUserToken(
win32ts.WTS_CURRENT_SESSION)
hProcess, hThread, dwProcessId, dwThreadId = (
win32process.CreateProcessAsUser(
htoken_user, executable, cmd, None, None, False,
creationflags, None, cwd, si))
if return_handles:
return hProcess, hThread
return dwProcessId, dwThreadId
def runas_shell_user(cmd, executable=None, creationflags=0, cwd=None,
startupinfo=None, return_handles=False):
if not creationflags & win32con.DETACHED_PROCESS:
creationflags |= win32con.CREATE_NEW_CONSOLE
if cwd is None:
cwd = os.getcwd()
si = STARTUPINFO()
if startupinfo:
startupinfo_update(startupinfo, si)
pi = PROCESS_INFORMATION()
try:
htoken = duplicate_shell_token()
except pywintypes.error as e:
if e.winerror != winerror.ERROR_FILE_NOT_FOUND:
raise
return runas_session_user(cmd, executable, creationflags, cwd,
startupinfo, return_handles)
with enable_privileges(win32security.SE_IMPERSONATE_NAME):
if not advapi32.CreateProcessWithTokenW(
int(htoken), 0, executable, cmd, creationflags, None,
cwd, ctypes.byref(si), ctypes.byref(pi)):
error = ctypes.get_last_error()
raise pywintypes.error(
error, 'CreateProcessWithTokenW',
win32api.FormatMessageW(error))
hProcess = pywintypes.HANDLE(pi.hProcess)
hThread = pywintypes.HANDLE(pi.hThread)
if return_handles:
return hProcess, hThread
return pi.dwProcessId, pi.dwThreadId