4

I've been trying to puzzle out how to run a bunch of applications which all require elevated permissions. Applications like DameWare, MSC.exe, PowerShell.exe, and the SCCM Manager Console, which are all used in my daily work routine.

I am running Win7 right now, with plans to move to Win10 eventually. Every day I run these programs and it is time consuming to run them one by one and type in name/password for each. I figured I'd just 'automate the boring stuff' and let Python do it.

Over on this question (How to run python script with elevated privilege on windows) the answer is there and the code for an old module called 'admin' was posted. However it was written in Python 2+ and doesn't work so well with Python 3.5+. I've done what I know to do with my limited python knowledge but I keep getting errors when it attempts to run

Traceback (most recent call last):
  File "<pyshell#1>", line 1, in <module>
    runAsAdmin('cmd.exe')
  File "I:\Scripting\Python\admin.py", line 41, in runAsAdmin
    elif type(cmdLine) not in (types.TupleType,types.ListType):
AttributeError: module 'types' has no attribute 'TupleType'

I've done some research and all I can find is the Python 2 documentation or examples, but not a Python 3 conversion/equivalent.

Here is the admin.py source, I've done what I can to bring it up to Python 3.5+. Any help you can offer will be appreciated!

#!/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 = input('Press Enter to exit.')
    return rc


if __name__ == "__main__":
    sys.exit(test())
Community
  • 1
  • 1
Ryan Barnes
  • 107
  • 1
  • 10
  • You haven't actually shown an error you're getting. Just an uninteresting part of stack trace. Please include **complete** stack trace. – Łukasz Rogalski Jan 27 '17 at 14:57
  • Edited OP to include complete stack trace. – Ryan Barnes Jan 27 '17 at 15:01
  • `types.TupleType` _does_ exist in the Python 2.7.13 version of the `types` module, so the error makes no sense. You could try substituting `type(())` or the more readable `type(tuple())` for this particular problem. – martineau Jan 27 '17 at 15:16
  • Try `elif type(cmdLine) not in (tuple, list)` – Jared Goguen Jan 27 '17 at 15:17
  • @martineau I think you can just use the keyword raw because `type(tuple()) == tuple` – Jared Goguen Jan 27 '17 at 15:19
  • @Jared: You are correct and your suggestion would be a better workaround than mine. Interestingly, the help for the `types` module in Python 3 says "Define names for built-in types that aren't directly accessible as a builtin" whereas the one in Python 2 says "Define names for all type symbols known in the standard interpreter"—so the scope of names in it was reduced. Regardless, `isinstance(, tuple)` works in both versions. – martineau Jan 27 '17 at 16:56

2 Answers2

2

It looks like types.TupleType and types.ListType do not exist in Python 3. Try the following instead:

elif type(cmdLine) not in (tuple, list)

The value error after saying that "cmdLine is not a sequence" is not exactly accurate because strings are sequences, but should indeed raise a ValueError. I might reword it to "cmdLine should be a non-empty tuple or list, or None." You could update it to more broadly check whether cmdLine is a non-string iterable, but that might be overkill.

Jared Goguen
  • 8,772
  • 2
  • 18
  • 36
  • Okay, that removed the stack trace bug! Thanks! I'll probably modify the script a bit to check if the string is valid a different way. I don't know what the original author was trying to accomplish there. – Ryan Barnes Jan 27 '17 at 16:37
  • @RyanBarnes The author is just trying to verify that `cmdLine` is a tuple or a list so that the indexing operations in the following lines work as expected. If `cmdLine` was a string, those indexing operations would still work, but would produce unexpected results. – Jared Goguen Jan 27 '17 at 17:00
  • 1
    @Ryan: I think `elif isinstance(cmdLine, collections.Sequence) and not isinstance(cmdLine, str):` would accomplish the same thing in Python 3. – martineau Jan 27 '17 at 17:17
  • @JaredGoguen thank you for explaining that. I was wondering why it needed to even check for other items in the index, but I think that it's looking to see if it's trying to open "notepad.exe file.txt". – Ryan Barnes Jan 30 '17 at 04:37
2

The following example demonstrates a simple way to have a program run with elevated privileges on Windows. The enumerations are meant to simplify some values needed when interacting with the operating system. 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
  • I believe the accepted answer solves the problem, but this answer is the one that actually solves the broader title question. – code11 Mar 14 '17 at 14:51