4

I want to copy a list of files to the Windows system directory (C:\Windows) using a Python function.

I have a function:

import shutil

def copy_list(src_list, dst):
    for file in src_list:
        shutil.copy(file, dst)

And I want to call it like this:

def copy_as_admin():
    #... some code to obtain user elevation ...

    copy_list(files_list, "C:\\Windows\")

How can I achieve this? PS: I'm using Python3, I tried solutions in this thread, How to run python script with elevated privilege on windows but those solutions are for Python version 2.

Community
  • 1
  • 1
lpsandaruwan
  • 790
  • 2
  • 11
  • 27
  • Possible duplicate of [How to run python script with elevated privilege on windows](http://stackoverflow.com/questions/19672352/how-to-run-python-script-with-elevated-privilege-on-windows) – SiHa Jan 25 '17 at 12:44
  • 2
    That solution is for python version2. And it's not working in python 3. – lpsandaruwan Jan 25 '17 at 12:46
  • 1
    OK, then maybe you should mention that in your question. – SiHa Jan 25 '17 at 12:47
  • Thank you. I updated the question. :-) – lpsandaruwan Jan 25 '17 at 12:51
  • Copying files to `%SystemRoot%` is generally a bad practice. Why do you need this? Also, the linked solution just needs extremely trivial porting to Python 3; you can't do that? – Eryk Sun Jan 25 '17 at 18:57
  • 1
    Actually, what I want to do is copy some font files to system font directory using python. even though It is a bad practice I'm interested in learning. :-) I could run the whole script by giving admin privileges, but what I want to do is give privileges to a function while running the program. – lpsandaruwan Jan 25 '17 at 19:54

3 Answers3

2

You cannot change privilege at runtime on windows.

An app needs to have a manifest (not suitable for python) or be ran as a privileged user.

When the app starts and privileges are too low, you can ask the user to run as administrator, or let the app relaunch itself with elevated privileges by calling runas.

import ctypes

if not ctypes.windll.shell32.IsUserAnAdmin():
    print('Not enough priviledge, restarting...')
    import sys
    ctypes.windll.shell32.ShellExecuteW(
        None, 'runas', sys.executable, ' '.join(sys.argv), None, None)
else:
    print('Elevated privilege acquired')
Ben the Coder
  • 539
  • 2
  • 5
  • 21
Cyrbil
  • 6,341
  • 1
  • 24
  • 40
  • 1
    I think `runas` needs to be told what user/permission level you require. This will just run as the default user, wont it? – SiHa Jan 25 '17 at 12:53
  • 1
    runas.exe (i.e. the "seclogon" service) cannot elevate to a user's unrestricted administrator token when UAC is enabled. That requires the `ShellExecute[Ex]` "runas" verb (i.e. the "appinfo" service), which you can call using ctypes or PyWin32. At best runas.exe can logon the Administrator account (RID 500) if you've enabled this account and stored the password in your credential vault. The default UAC policy exempts the Administrator account, which is why this account is disabled by default starting with Windows Vista. – Eryk Sun Jan 25 '17 at 18:48
  • @Cyrbil Your code has an error! You need to remove the `exit(0)` line from your code, or Python will terminate the program before the line `print('Elevated privilege acquired')`. – Ben the Coder Feb 18 '23 at 17:50
  • @Cyrbil Just removed the incorrect code. (Technically, I'm still waiting for my edit to be approved). – Ben the Coder Feb 18 '23 at 17:51
2

The following example builds on Cyrbil's excellent work. In particular, two enumerations are introduced. The first allows for easy specification of how an elevated program is to be opened, and the second helps when errors need to be easily identified. Please note that if you want all command line arguments passed to the new process, sys.argv[0] should probably be replaced with a function call: subprocess.list2cmdline(sys.argv).

#! /usr/bin/env python3
import ctypes
import enum
import sys


# Reference:
# msdn.microsoft.com/en-us/library/windows/desktop/bb762153(v=vs.85).aspx


class SW(enum.IntEnum):

    HIDE = 0
    MAXIMIZE = 3
    MINIMIZE = 6
    RESTORE = 9
    SHOW = 5
    SHOWDEFAULT = 10
    SHOWMAXIMIZED = 3
    SHOWMINIMIZED = 2
    SHOWMINNOACTIVE = 7
    SHOWNA = 8
    SHOWNOACTIVATE = 4
    SHOWNORMAL = 1


class ERROR(enum.IntEnum):

    ZERO = 0
    FILE_NOT_FOUND = 2
    PATH_NOT_FOUND = 3
    BAD_FORMAT = 11
    ACCESS_DENIED = 5
    ASSOC_INCOMPLETE = 27
    DDE_BUSY = 30
    DDE_FAIL = 29
    DDE_TIMEOUT = 28
    DLL_NOT_FOUND = 32
    NO_ASSOC = 31
    OOM = 8
    SHARE = 26


def bootstrap():
    if ctypes.windll.shell32.IsUserAnAdmin():
        main()
    else:
        hinstance = ctypes.windll.shell32.ShellExecuteW(
            None, 'runas', sys.executable, sys.argv[0], None, SW.SHOWNORMAL
        )
        if hinstance <= 32:
            raise RuntimeError(ERROR(hinstance))


def main():
    # Your Code Here
    print(input('Echo: '))


if __name__ == '__main__':
    bootstrap()
Noctis Skytower
  • 21,433
  • 16
  • 79
  • 117
2

You can now use the PyUAC module (for Windows). It works for me (I'm using Python 3). Install it using:

pip install pyuac
pip install pypiwin32

Direct usage of the package is:

import pyuac

def main():
    print("Do stuff here that requires being run as an admin.")
    # The window will disappear as soon as the program exits!
    input("Press enter to close the window. >")

if __name__ == "__main__":
    if not pyuac.isUserAdmin():
        print("Re-launching as admin!")
        pyuac.runAsAdmin()
    else:        
        main()  # Already an admin here.

Or, if you wish to use the decorator:

from pyuac import main_requires_admin

@main_requires_admin
def main():
    print("Do stuff here that requires being run as an admin.")
    # The window will disappear as soon as the program exits!
    input("Press enter to close the window. >")

if __name__ == "__main__":
    main()

The actual code (in the module) is:-

#!/usr/bin/env python
# -*- coding: utf-8; mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
# vim: fileencoding=utf-8 tabstop=4 expandtab shiftwidth=4

# (C) COPYRIGHT © Preston Landers 2010
# Released under the same license as Python 2.6.5

 
import sys, os, traceback, types
 
def isUserAdmin():
   
    if os.name == 'nt':
        import ctypes
        # WARNING: requires Windows XP SP2 or higher!
        try:
            return ctypes.windll.shell32.IsUserAnAdmin()
        except:
            traceback.print_exc()
            print "Admin check failed, assuming not an admin."
            return False
    elif os.name == 'posix':
        # Check for root on Posix
        return os.getuid() == 0
    else:
        raise RuntimeError, "Unsupported operating system for this module: %s" % (os.name,)
   
def runAsAdmin(cmdLine=None, wait=True):
 
    if os.name != 'nt':
        raise RuntimeError, "This function is only implemented on Windows."
   
    import win32api, win32con, win32event, win32process
    from win32com.shell.shell import ShellExecuteEx
    from win32com.shell import shellcon
   
    python_exe = sys.executable
 
    if cmdLine is None:
        cmdLine = [python_exe] + sys.argv
    elif type(cmdLine) not in (types.TupleType,types.ListType):
        raise ValueError, "cmdLine is not a sequence."
    cmd = '"%s"' % (cmdLine[0],)
    # XXX TODO: isn't there a function or something we can call to massage command line params?
    params = " ".join(['"%s"' % (x,) for x in cmdLine[1:]])
    cmdDir = ''
    showCmd = win32con.SW_SHOWNORMAL
    #showCmd = win32con.SW_HIDE
    lpVerb = 'runas'  # causes UAC elevation prompt.
   
    # print "Running", cmd, params
 
    # ShellExecute() doesn't seem to allow us to fetch the PID or handle
    # of the process, so we can't get anything useful from it. Therefore
    # the more complex ShellExecuteEx() must be used.
 
    # procHandle = win32api.ShellExecute(0, lpVerb, cmd, params, cmdDir, showCmd)
 
    procInfo = ShellExecuteEx(nShow=showCmd,
                              fMask=shellcon.SEE_MASK_NOCLOSEPROCESS,
                              lpVerb=lpVerb,
                              lpFile=cmd,
                              lpParameters=params)
 
    if wait:
        procHandle = procInfo['hProcess']    
        obj = win32event.WaitForSingleObject(procHandle, win32event.INFINITE)
        rc = win32process.GetExitCodeProcess(procHandle)
        #print "Process handle %s returned code %s" % (procHandle, rc)
    else:
        rc = None
 
    return rc
 
def test():
    rc = 0
    if not isUserAdmin():
        print "You're not an admin.", os.getpid(), "params: ", sys.argv
        #rc = runAsAdmin(["c:\\Windows\\notepad.exe"])
        rc = runAsAdmin()
    else:
        print "You are an admin!", os.getpid(), "params: ", sys.argv
        rc = 0
    x = raw_input('Press Enter to exit.')
    return rc
 
 
if __name__ == "__main__":
    sys.exit(test())

(from this answer, the same accepted answer in How to run script with elevated privilege on windows)

The Amateur Coder
  • 789
  • 3
  • 11
  • 33