为了从 Python 请求 UAC 提升权限,当调用外部程序时,您可以这样做
ctypes.windll.shell32.ShellExecuteW(None, "runas", my_binary_file_path, "", None, 1)
但是,假设您的 Python 脚本正在 管理员权限下执行,您如何没有管理员权限调用外部程序?
最佳答案
我更喜欢的一种方法是以 shell 用户身份运行。首先打开 shell 进程并复制其 token 。您可以通过调用 GetShellWindow 然后调用 GetWindowThreadProcessId 来获取 shell 进程 PID。通常这是资源管理器。
默认情况下,管理员帐户没有 SeAssignPrimaryTokenPrivilege,在这种情况下您不能直接调用 CreateProcessAsUser。您必须请求一个更有特权的进程来代表您调用电话。 CreateProcessWithTokenW 通过对辅助登录服务进行远程过程调用来为您执行此操作。
PyWin32 不包装 GetShellWindow 和 CreateProcessWithTokenW,因此您需要使用 ctypes 来调用它们。
很少有 Windows 系统可能在没有常规 shell 的情况下运行,或者使用 shell 时无法通过 SetShellWindow[Ex] 注册其窗口。在这种情况下,GetShellWindow 返回 NULL。作为这种情况的回退,您可以使用一种有点可疑(但有效)的方法来获取 session 用户的 token 并调用 CreateProcessAsUser。
首先获取 session 的 Windows 子系统进程 csrss.exe 的 PID。最简单的方法是调用未记录(但稳定)的函数 CsrGetProcessId。启用 SeDebugPrivilege 以使用有限的查询访问权限打开此进程。然后打开它的Token,复制,模拟。现在您拥有通过 WTSQueryUserToken 获取 session 用户 token 所需的 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 - 在没有提升权限的情况下调用二进制文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48051283/
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时
我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题
对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl
我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚
我好像记得Lua有类似Ruby的method_missing的东西。还是我记错了? 最佳答案 表的metatable的__index和__newindex可以用于与Ruby的method_missing相同的效果。 关于ruby-难道Lua没有和Ruby的method_missing相媲美的东西吗?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/7732154/
使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta
我有一个奇怪的问题:我在rvm上安装了rubyonrails。一切正常,我可以创建项目。但是在我输入“railsnew”时重新启动后,我有“程序'rails'当前未安装。”。SystemUbuntu12.04ruby-v"1.9.3p194"gemlistactionmailer(3.2.5)actionpack(3.2.5)activemodel(3.2.5)activerecord(3.2.5)activeresource(3.2.5)activesupport(3.2.5)arel(3.0.2)builder(3.0.0)bundler(1.1.4)coffee-rails(