Admin Rights

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Amrotu
User
Beiträge: 7
Registriert: Freitag 12. Juni 2020, 06:33

Hallo zusammen,

ich möchte ein Programm schreiben mit dem mehrere Programme gestartet werden können ohne dass ich ständig nach Erlaubnis gefragt werde. Dazu habe ich im Internet einen Code gefunden, der auch super funktioniert und ich werde nicht individuell nach den rechten abgefragt. Wenn ich das Python-Programm allerdings schließe, versucht Python alle Programme erneut zu öffnen und Windows fragt mich zu jedem wieder individuell, was dann irgendwie den Zweck des Programmes verfehlt.

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 "Unsupported operating system for this module: "# % (os.name,)

def runAsAdmin(cmdLine=None, wait=True):

if os.name != 'nt':
raise "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 "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())


##################################################################
def start():
#Starte Programme
try:
os.startfile(r"C:\ProgramData\Microsoft\Windows\Start Menu\Programs\La")
except:
pass
try:
os.startfile(r"C:\Program Files (x86)\Lu")
except:
pass


Zum ausführen lade ich start().
Benutzeravatar
__blackjack__
User
Beiträge: 14052
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Amrotu: Was man so im Internet findet — sieht manchmal furchtbar aus.

Der Code scheint für Python 2 geschrieben worden zu sein. Es wird `raw_input()` verwendet, ``print`` aber ohne entsprechenden `__future__`-Import so geschrieben als sei es eine Funktion. Das ist verwirrend, wird zu komisch aussehenden Ausgaben führen, und Python 2 ist tot. Bitte nicht mehr verwenden.

Importe gehören an den Anfang des Moduls, damit man leicht die Abhängigkeiten sieht.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase).

Die Hauptfunktion `test()` zu nennen ist komisch.

Man sollte keine kryptischen Abkürzungen in Namen verwenden oder Namen die nur eine kryptisch Abkürzung ist. `rc` ist eine typische Abkürzung für „radio controlled“. Ich bin sicher das ist hier nicht gemeint. Aber was dann‽

Die erste Zuweisung an `rc` in der `test()`-Funktion wird nirgends verwendet, kann also weg.

Wenn man Typprüfung macht, dann nicht mit `type()` und Vergleichen, sondern mit `isinstance()`. Aber man macht keine Typprüfung. Der Vergleich klappt unter Python 3 so auch gar nicht mehr weil das `types`-Modul `TupleType` und `ListType` nicht mehr kennt. Das war auch unter Python 2 schon lange veraltet weil man schon länger `tuple` und `list` direkt als Datentypen verwenden konnte. Zeichenketten als Ausnahme auszulösen ist auch schon seit einer Eeeeeewigkeit nicht mehr erwünscht und führt in Python 3 dann auch zu einem `TypeError`.

`cmdDir` und `obj` werden definiert, aber nirgends verwendet.

Man sollte keine ”nackten” `except`\s ohne konkrete Ausnahmen verwenden. Hier wird anscheinend darauf reagiert, dass die Funktion `IsUserAnAdmin()` in der shell32-DLL nicht existiert oder die DLL selbst nicht geladen werden kann, das wären also ein `AttributeError` oder `OSError`.

Die Konstante `FALSE` ist nirgends definiert, das sollte wohl `False` sein.

Da das alles sowieso nur unter Windows läuft, kann man sich die Tests auf andere Betriebssysteme im Code sparen. Das scheitert dann ja schon an den Importen der windowsspezifischen Module am Anfang.

Die `start()`-Funktion wird nirgends aufgerufen, also ist mir nicht so ganz klar wie die Programme dort zweimal gestartet werden können, wenn sie nicht mal einmal gestartet werden‽

Überarbeitet (ungetestet):

Code: Alles auswählen

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

import win32api
import win32con
import win32event
import win32process
from win32com.shell import shellcon
from win32com.shell.shell import ShellExecuteEx


def start():
    try:
        os.startfile(
            r"C:\ProgramData\Microsoft\Windows\Start Menu\Programs\La"
        )
    except:
        pass
    try:
        os.startfile(r"C:\Program Files (x86)\Lu")
    except:
        pass


def is_user_admin():
    #
    # WARNING: requires Windows XP SP2 or higher!
    #
    try:
        return ctypes.windll.shell32.IsUserAnAdmin()
    except (AttributeError, OSError):
        print("Admin check failed, assuming not an admin.")
        return False


def run_as_admin(arguments=None, wait=True):
    if arguments is None:
        arguments = [sys.executable, *sys.argv]
    #
    # TODO Isn't there a function or something we can call to massage command
    # line params?
    #
    command, *parameters = (f'"{argument}"' for argument in arguments)
    #
    # 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.
    #
    process_info = ShellExecuteEx(
        nShow=win32con.SW_SHOWNORMAL,
        fMask=shellcon.SEE_MASK_NOCLOSEPROCESS,
        lpVerb="runas",  # causes UAC elevation prompt.
        lpFile=command,
        lpParameters=" ".join(parameters),
    )

    if wait:
        process_handle = process_info["hProcess"]
        win32event.WaitForSingleObject(process_handle, win32event.INFINITE)
        exit_code = win32process.GetExitCodeProcess(process_handle)
    else:
        exit_code = None

    return exit_code


def main():
    if not is_user_admin():
        print("You're not an admin.", os.getpid(), "params: ", sys.argv)
        exit_code = run_as_admin()
    else:
        print("You are an admin!", os.getpid(), "params: ", sys.argv)
        exit_code = 0
    input("Press Enter to exit.")
    return exit_code


if __name__ == "__main__":
    sys.exit(main())
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Amrotu
User
Beiträge: 7
Registriert: Freitag 12. Juni 2020, 06:33

Hey blackjack,

erst einmal vielen Dank für die schnelle Antwort.Ich habe noch ein zweites Programm geschrieben, dass start() ausführt. Dass das main Programm die test Funktion ist, habe ich nicht erkannt, funktioniert aber. Ich habe soviel rumprobiert, dass ich leider den abgeänderten Code hier reinkopiert habe. Im Original hatte ich am Anfang von

def start():
isUserAdmin()

stehen. Aber auch wenn ich

def start()
main()
...
schreibe, und ich start() später aufrufe, werden die Programme doppelt geladen; einmal mit Admin rights, und nach schließen noch einmal mit Abfrage der Admin Rechte. Ich habe in meiner Freizeit erst ein paar Grundlagen gelernt, deswegen konnte/ kann ich selbst mit dem Code nicht all zu viel anfangen.
Sirius3
User
Beiträge: 18272
Registriert: Sonntag 21. Oktober 2012, 17:20

Also das, was Du hier gezeigt hast, ist nicht das, was Du eigentlich ausgeführt hast.
Dann zeig doch mal dieses zweite Programm.
Amrotu
User
Beiträge: 7
Registriert: Freitag 12. Juni 2020, 06:33

Das erste Programm heißt admin und dieses heißt Randy und war bisher immer lieb :D

Code: Alles auswählen

from tkinter import *
import admin
import random
import os

admin.start()

#Randy
root = Tk()
root.title("Randy")
hr = 200
wr = 1450
root.geometry(str(wr)+"x"+str(hr))

def b1():
  r=random.randrange(0,100)
  if r<10:
    Ausgabe.config(text = "Yes", background = "green")
  else:
    Ausgabe.config(text = "No", background = "red")

def b2():
  r=random.randrange(0,100)
  if r<25:
    Ausgabe.config(text = "Yes", background = "green")
  else:
    Ausgabe.config(text = "No", background = "red")

def b3():
  r=random.randrange(0,100)
  if r<50:
    Ausgabe.config(text = "Yes", background = "green")
  else:
    Ausgabe.config(text = "No", background = "red")

def b4():
  r=random.randrange(0,100)
  if r<75:
    Ausgabe.config(text = "Yes", background = "green")
  else:
    Ausgabe.config(text = "No", background = "red")

def b5():
  r=random.randrange(0,100)
  if r<90:
    Ausgabe.config(text = "Yes", background = "green")
  else:
    Ausgabe.config(text = "No", background = "red")

h = 4
w = 40
Ausgabe = Label(root,width = 200, height = h, text="Thinking...")
Ausgabe.pack()

bb1 = Button(root,width = w, height = h, text = "10", command = b1, background = "cyan")
bb1.pack(side = LEFT)

bb2 = Button(root,width = w, height = h, text = "25", command = b2, background = "blue")
bb2.pack(side = LEFT)

bb3 = Button(root,width = w, height = h, text = "50", command = b3, background = "darkgreen")
bb3.pack(side = LEFT)

bb4 = Button(root,width = w, height = h, text = "75", command = b4, background = "gold")
bb4.pack(side = LEFT)

bb5 = Button(root,width = w, height = h, text = "90", command = b5, background = "darkorange")
bb5.pack(side = LEFT)

root.mainloop()
Benutzeravatar
sparrow
User
Beiträge: 4538
Registriert: Freitag 17. April 2009, 10:28

Das Durchnummerieren von Funktionen und GUI-Elementen im Code ist immer ein Hinweis darauf, dass man eigentlich eine Datenstruktur wie eine Liste benutzen möchte.

Mir erschließt sich der Zusammenhang zwischen den Programmen nicht. Wozu?
Das Programm macht ja mit admin nichts, außer ds aufzurufen und danach gefärbte Knöpfe anzuzeigen.
Ist das der laienhafte Versuch jemanden Admin-Rechte zu entlocken, eine seltsame GUI anzuzeigen und im Hintergrund Schadsoftware auszuführen?
Amrotu
User
Beiträge: 7
Registriert: Freitag 12. Juni 2020, 06:33

Hey sparrow,

eine Schadsoftware wollte ich hier nicht schreiben :DD. Also eigentlich soll das Programm ein paar andere Programme mit admin rights öffnen (deswegen admin.start()) und dann wird mir noch eine Art Randomizer geladen^^

"Das Durchnummerieren von Funktionen und GUI-Elementen im Code ist immer ein Hinweis darauf, dass man eigentlich eine Datenstruktur wie eine Liste benutzen möchte." Ja das macht auch Sinn, war aber das erste Mal, dass ich mit tkinter gearbeitet habe, deswegen ist das wahrscheinlich auch nicht der schönste Code^^
Benutzeravatar
__blackjack__
User
Beiträge: 14052
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Amrotu: Jetzt wissen wir aber immer noch nicht was Du eigentlich machst, denn selbst wenn am Anfang von `admin.start()` ein sinnfreies ``isUserAdmin()`` steht, werden da ja nirgends Adminrechte erlangt oder `test` ausgeführt.

Zum neuen Quelltext: `os` wird importiert, aber nirgends verwendet.

Eingerückt wird mit vier Leerzeichen pro Ebene. Die Leerzeichensetzung weicht neben der Einrückung auch anderweitig in fast jeder Zeile vom Style Guide for Python Code ab.

Sternchen-Importe sind Böse™. Gerade bei `tkinter` holt man sich so fast 200 Namen ins Modul von denen nur ein kleiner Bruchteil tatsächlich verwendet wird. Und nicht nur Namen die das `tkinter`-Modul definiert, sondern auch alles was das `tkinter`-Modul seinerseits aus anderen Modulen importiert. Da verliert man schnell die Übersicht welcher Name eigentlich wo her kommt. Zudem besteht die Gefahr von Namenskollisionen.

Auf Modulebene gehört nur Code der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst. Hauptprogramm und Funktionsdefinitionen auf Modulebene zu mischen ist dann noch mal unübersichtlicher.

Alles was Funktionen und Methoden ausser Konstanten benötigen sollte als Argument(e) übergeben werden. Bei GUI-Code braucht man deshalb für jede nicht-triviale GUI objektorientierte Progammierung, oder mindestens Closures.

Die Fenstergeometrie darf man hier nicht fest vorgeben. Warum machst Du das? Bei mir auf dem Rechner ist beispielsweise der 90-Button dadurch überhaupt nicht sichtbar.

Das die GUI nicht komisch angezeigt wird ist mehr oder weniger Zufall, denn wie sich `pack()` verhält wenn innerhalb eines Containerwidgets verschiedene `side`-Werte verwendet werden ist schwer vorhersehbar. Die Buttons gehören in einen eigenen Frame.

Zeichenketten und Werte mit `str()` und ``+`` zusammenstückeln ist eher BASIC denn Python. In Python gibt es dafür Zeichenkettenformatierung mit der `format()`-Methode auf Zeichenketten.

Namen sollten keine kryptischen Abkürzungen enthalten oder gar nur aus kryptischen Abkürzungen bestehen. Einbuchstabige Namen sind selten gute Namen.

Was soll das `r` bei `hr` und `wr` bedeuten? Der Leser kann vielleicht noch raten das `h` für `height` steht und `w` für `width`, aber wenn die tatsächlich so heissen würden, müsste er nicht raten.

Aber soll `bb1` bedeuten? Das eine `b` steht wahrscheinlich für `button`, aber das andere?

`Ausgabe` müsste `ausgabe` heissen, denn das ist ja keine Klasse.

Wie sparrow schon geschrieben hat nummeriert man keine Namen. Die ganzen Rückruffunktionen sind fast identisch und auch der Code der die Buttons dafür erstellt. Da zieht man die Gemeinsamkeiten in eine Liste heraus und schreibt dann eine Schleife über diese Liste. In der Schleife werden dann die Buttons erstellt und mit *einer* Rückruffunktion verbunden, die entsprechend parametrisiert ist.

Code: Alles auswählen

#!/usr/bin/env python3
import random
import tkinter as tk
from functools import partial

import admin


def update(ausgabe_label, probability):
    if random.randrange(0, 100) < probability:
        ausgabe_label.config(text="Yes", background="green")
    else:
        ausgabe_label.config(text="No", background="red")


def main():
    admin.start()
    root = tk.Tk()
    root.title("Randy")

    width, height = 20, 3

    ausgabe_label = tk.Label(root, height=height, text="Thinking...")
    ausgabe_label.pack(fill=tk.X)

    frame = tk.Frame()
    
    for probability, color in [
        (10, "cyan"),
        (25, "blue"),
        (50, "darkgreen"),
        (75, "gold"),
        (90, "darkorange"),
    ]:
        tk.Button(
            frame,
            width=width,
            height=height,
            text=probability,
            command=partial(update, ausgabe_label, probability),
            background=color,
        ).pack(side=tk.LEFT)

    frame.pack()

    root.mainloop()


if __name__ == "__main__":
    main()
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Amrotu
User
Beiträge: 7
Registriert: Freitag 12. Juni 2020, 06:33

Hey,
erst einmal nochmal danke für die Rückmeldung. Ich hatte ursprünglich nicht vor jemanden den Code zu zeigen, aber trotzdem danke für die Verbesserungsvorschläge. Das war mein erstes kleines Projekt und weil die Definitionen zu den Buttons schon b1, etc. hießen, habe ich die entsprechenden Buttons bb1 genannt. Ist in der letzten Version vor mir natürlich sehr viel eleganter gelöst ;). Dazu: Was genau macht "if __name__ == "__main__":"?
"Jetzt wissen wir aber immer noch nicht was Du eigentlich machst, denn selbst wenn am Anfang von `admin.start()` ein sinnfreies ``isUserAdmin()`` steht, werden da ja nirgends Adminrechte erlangt oder `test` ausgeführt." also sinnfrei ist das nicht, denn die Programme laden so, ohne dass Admin rechte abgefragt werden. Wenn ich Randy aber schließe, werden die Programme halt wie schon erklärt erneut geöffnet.
Amrotu
User
Beiträge: 7
Registriert: Freitag 12. Juni 2020, 06:33

Hey,
erst einmal nochmal danke für die Rückmeldung. Ich hatte ursprünglich nicht vor jemanden den Code zu zeigen, aber trotzdem danke für die Verbesserungsvorschläge. Das war mein erstes kleines Projekt und weil die Definitionen zu den Buttons schon b1, etc. hießen, habe ich die entsprechenden Buttons bb1 genannt. Ist in der letzten Version vor mir natürlich sehr viel eleganter gelöst ;). Dazu: Was genau macht "if __name__ == "__main__":"?
"Jetzt wissen wir aber immer noch nicht was Du eigentlich machst, denn selbst wenn am Anfang von `admin.start()` ein sinnfreies ``isUserAdmin()`` steht, werden da ja nirgends Adminrechte erlangt oder `test` ausgeführt." also sinnfrei ist das nicht, denn die Programme laden so, ohne dass Admin rechte abgefragt werden. Wenn ich Randy aber schließe, werden die Programme halt wie schon erklärt erneut geöffnet.
Benutzeravatar
__blackjack__
User
Beiträge: 14052
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Amrotu: Das mag sein das die Programme starten, aber nicht wegen dem Modul oder der `isUserAdmin()`-Funktion, in sofern ist das schon ziemlich sinnlos. Es kann aber auch nicht sein, dass am Ende die Programme noch mal starten solange Du da nicht noch irgendetwas anderes unterschlagen hast.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Amrotu
User
Beiträge: 7
Registriert: Freitag 12. Juni 2020, 06:33

Ihr habt leider alle Informationen. Aber vielleicht frage ich noch einmal anders: Wie könnte ich denn jetzt Python dann als Administrator ausführen?
Benutzeravatar
sparrow
User
Beiträge: 4538
Registriert: Freitag 17. April 2009, 10:28

Das halte ich für unwahrscheinlich, denn Code und Beschreibung passen nicht zusammen.

Unter Windows führt man ein Programm als Administrator aus indem man es mit entsprechenden Rechten startet.
Rechtsklick drauf, "als Administrator ausführen"

Aber es gilt: Man sollte Programme gar nicht als Administrator ausführen müssen. Die Ausnahmen sind wenige.
Sirius3
User
Beiträge: 18272
Registriert: Sonntag 21. Oktober 2012, 17:20

@sparrow: oder eben mit `runas`. Das benutzt der OP aber gar nicht.

@Amrotu: so wie Du den Code zeigst, wird nie etwas als Admin ausgeführt, und Du machst noch etwas anderes, denn im Code wird nie etwas doppelt ausgeführt.
Antworten