USB-Stick serialnumber and volume letter

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
Pythonboy
User
Beiträge: 11
Registriert: Freitag 27. März 2020, 11:57

Hallo,

kennt ihr vielleicht eine Möglichkeit, oder eine Bibliothek, mit der es möglich ist die Seriennummer (bzw. die eindeutige ID) sowie den Laufwerksbuchstaben der eingebunden Sticks auszulesen?

Gewünscht wäre am Ende ine Auflistung wie folgt z.B.:

Code: Alles auswählen

("F:", "579EF173")
("G:", "878FH189")
("H:", "8G6T9AF8")
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich kenne keine Module dafuer, aber man findet zB https://superuser.com/questions/498083/ ... mmand-line - damit wuerde ich mal rumspielen.
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Pythonboy
User
Beiträge: 11
Registriert: Freitag 27. März 2020, 11:57

Danke für die Hinweise, ich habe es jetzt wie folgt gelöst:

Code: Alles auswählen

from subprocess import Popen, PIPE
import re 

VOLUME_LETTERS = ["A:","B:","C:","D:","E:","F:","G:","H:","I:","J:","K:","L:","M:","N:","O:","P:","Q:","R:","S:","T:","U:","V:","W:","X:","Y:","Z:"]

def get_volume_serial_number_and_letter():
    volume_letter_to_serial_number = {}
    for volume_letter in VOLUME_LETTERS:
        process = Popen(['vol', volume_letter], stdout=PIPE, stderr=PIPE, shell=True)
        stdout, stderr = process.communicate()
        decoded_stdout = stdout.decode('windows-1252')
        search_index_start = re.search("Volumeseriennummer: ", decoded_stdout)
        try:
            volume_letter_to_serial_number[volume_letter] = decoded_stdout[search_index_start.span()[1]:search_index_start.span()[1] + 9] #serial number is 9 chars long
        except AttributeError:
            volume_letter_to_serial_number[volume_letter] = None
    return volume_letter_to_serial_number


print(get_volume_serial_number_and_letter())
Meine Idee ist es, dass man für gewisse Dinge auszuführen kein Passwort braucht, sondern bestimmte USB-Sticks in der richtigen Reihenfolge in ein USB-Hub stecken muss, um die Dinge freizuschalten.

Jetzt muss ich nur noch eine Funktion machen zum initialisieren, die mir die Laufwerksbuchstaben zusammen mit der gehashten Seriennummer in eine Datei auslagert und eine Funktion, die die aktuell ausgelesenen Werte mit dieser Datei abgleicht.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Uh. Da wird mir mein Herz ganz schwer, wenn ich sehe, wie du hier die VOLUME_LETTERS hart hinschreibst.

Das erzeugt man programmatisch, zB so:

Code: Alles auswählen

for i in range(26):
    volume = f"{chr(ord('A') + i)}:"
    print(volume)
Pythonboy
User
Beiträge: 11
Registriert: Freitag 27. März 2020, 11:57

So ähnlich habe ich mir die Liste generiert:

Code: Alles auswählen

import string
print([f"{char}:" for char in string.ascii_uppercase])
--> dann noch ' durch " ersetzt, weil ich dachte dass es so besser lesbar ist.

Auf den Ansatz mit ord und chr wäre ich jetzt nicht gekommen, danke! :)
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

ascii_uppercase ist ja auch fein, wenn nicht sogar besser. Gibt halt viele Wege nach Rom, aber sowas wie aufzaehlen lassen sind Computer recht gut drin, da lasse ich den das machen ;)
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

ascii_uppercase ist schon eine gute Idee, aber nicht zum Erzeugen einer anschließend hartkodierten Liste. Ich würde statt dessen einen Generator bauen:

Code: Alles auswählen

import string

def volumeletters():
    for item in string.ascii_uppercase:
        yield f"{item}:"

for volumeletter in volumeletters():
    print(volumeletter)
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Warum einfach, wenn's auch kompliziert geht :roll: .

Ich hatte Dich ja schon auf das richtige Modul hingewiesen:

Code: Alles auswählen

import wmi

for device in wmi.WMI().Win32_LogicalDisk():
    name = device.wmi_property('Name').value
    serial_number = device.wmi_property('VolumeSerialNumber').value
    print(name, serial_number)
Pythonboy
User
Beiträge: 11
Registriert: Freitag 27. März 2020, 11:57

Hallo Siruis,

deine Version sieht wirklich etwas lesbarer aus. Diese habe ich nun übernommen, mein aktueller Entwurf sieht so aus, damit bin ich aber noch nicht zufrieden.

Habt ihr Vorschläge wie ich es verbessern könnte?

Code: Alles auswählen

import wmi
import argon2


def get_volume_serial_number_and_letter_wmi():
        volume_letter_to_serial_number = dict()
        for volume in wmi.WMI().Win32_LogicalDisk():
            name = volume.wmi_property('Name').value
            serial_number = volume.wmi_property('VolumeSerialNumber').value
            volume_letter_to_serial_number[name] = serial_number
        return volume_letter_to_serial_number

def get_difference_of_dicts(dict1, dict2):
    difference = {}
    for key in dict1.keys():
        if key not in dict2.keys():
            difference[key] = dict1[key]
    return difference  
     


def main():
      while True:
        print("Was möchtest du tun?: ")
        choice = int(input("1 - Einstellungen setzen \n2 - Einstellungen prüfen\n\n> "))
        if choice == 1: 
            input("Entferne alle USB-Sticks aus dem USB-Hub. Drücke [Enter], sobald alle USB-Sticks entfernt sind\n")
            volume_letters_and_serial_without_usb_sticks = get_volume_serial_number_and_letter_wmi()
            input("Stecke nun die USB-Sticks in der gewünschten Reihenfolge in den USB-Hub und drücke anschließend [Enter]\n")
            volume_letters_and_serial_with_usb_sticks = get_volume_serial_number_and_letter_wmi()
            difference = get_difference_of_dicts(volume_letters_and_serial_with_usb_sticks, volume_letters_and_serial_without_usb_sticks)
            for key, value in difference.items():
                difference[key] = argon2.PasswordHasher().hash(value)
            print(difference)
        if choice == 2:
            volume_letters_and_serial_with_usb_sticks = get_volume_serial_number_and_letter_wmi()
            difference2 = get_difference_of_dicts(volume_letters_and_serial_with_usb_sticks, volume_letters_and_serial_without_usb_sticks)
            if difference2:
                for key, value in difference2.items():
                    if key in difference.keys():
                        if argon2.PasswordHasher().verify(difference[key],difference2[key]):
                            print("YES")
                        else:
                            print("NO")
                    else:
                        print("NO")
            else:
                print("NO")

main()




Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Was gefällt Dir daran nicht?

In `get_volume_serial_number_and_letter_wmi` ist die Einrückung falsch.
`get_difference_of_dicts` läßt sich deutlich eleganter schreiben:

Code: Alles auswählen

def get_difference_of_dicts(input, keys_to_be_removed):
    return {key: input[key] for key in set(input) - set(keys_to_be_removed)}
Vor dem `main`-Aufruf sollte `if __name__ == "__main__":` stehen.

Im choice == 2-Zweig werden `difference` und `volume_letters_and_serial_without_usb_sticks` nicht gesetzt, was zu einem Fehler führen kann.
Wenn Dich nur die USB-Sticks interessieren, würde ich ja auf WMI-Property DeviceType == 2 prüfen, oder die Description auswerten. Ich denke, es ist ziemlich umständlich erst alle USB-Sticks zu ziehen, um sie danach wieder einzustecken.
Ist denn wichtig, ob das Laufwerk als Q: eingebunden wird oder als V:?
Und wenn Dich eh nur die USB-Devices interessieren, warum fragst Du dann nicht die USB-DeviceID ab, statt Dich hier mit einem VolumeSerialNumber herumzuschlagen?

Volume-IDs sind keine Passwörter, mit argon2 darauf loszugehen ist vielleicht etwas übertrieben. Was soll das ganze Programm überhaupt tun?
Pythonboy
User
Beiträge: 11
Registriert: Freitag 27. März 2020, 11:57

Meine Grundidee ist es, bestimmte Funktionen nur freizuschalten, sofern die richtigen USB-Sticks in der richtigen Reihenfolge in das USB-Hub gesteckt werden.

Das mit dem deutlich eleganter habe ich versucht, bin aber leider kläglich gescheitert. Danke für das Snippet.

Die Seriennummer habe ich genommen, damit wirklich nur die USB-Sticks dafür genutzt werden können, die ich hier vor mir liegen habe und keine anderen. Ist die DeviceID auch immer gleich, oder ist die bei unterschiedlichen Systemen unterschiedlich?

Das mit dem USB-Sticks vorher entfernen und wieder reinstecken hat mich auch sehr beim Testen genervt, aber da ich verbundene Netzlaufwerke habe, dachte ich das wäre Notwendig damit wirklich nur die USB-Sticks beachtet werden. Das mit der WMI-Property DeviceTyp == 2 habe ich noch nicht gewusst, danke auch von meinen USB-Sockets für diesen Vorschlag, weil ich diese nun nicht mehr so penetrieren muss.

Was wür ein Hashalgorhymus würdest du empfehlen?
Benutzeravatar
__blackjack__
User
Beiträge: 13117
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Pythonboy: `pathlib` und `json` werden importiert, aber nirgends verwendet.

Man könnte verhindern das `main()` aufgerufen wird wenn man das als Modul importiert.

Die Benutzereingaben sollte man gegen Fehleingaben absichern.

Das Programm kommt auch nicht damit klar wenn der Benutzer zuerst 2 auswählt. Das darf er sinnvollerweise erst dann machen wenn mindestens einmal der erste Punkt gewählt wurde. Da es nur zwei Punkte gibt, braucht man dem Benutzer am Anfang gar keine Auswahl anbieten, sondern kann gleich die Einstellungen abfragen. Am besten steckt man die beiden Aktionen sowieso in eigene Funktionen, dann ist das auch kein Problem vor der Schleife einmal bedingungslos die Funktion für die Einstellungen aufzurufen.

Beim Überprüfen gibt es eine Schleife über `items()` aber `value` aus dieser Schleife wird dann gar nicht verwendet.

Man sollte nicht `key` und `value` als Namen verwenden, wenn man dafür einen passenderen hat, wie `letter` und `serial` beispielsweise.

`difference` und `difference2` sind auch keine guten Namen.

Zwischenstand (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python3
import argon2
import wmi


def get_property(volume, name):
    return volume.wmi_property(name).value


def get_volume_serial_numbers():
    return dict(
        (
            get_property(volume, "Name"),
            get_property(volume, "VolumeSerialNumber"),
        )
        for volume in wmi.WMI().Win32_LogicalDisk()
    )


def subtract_mappings(mapping_a, mapping_b):
    return {key: mapping_a[key] for key in mapping_a.keys() - mapping_b.keys()}


def configure():
    input(
        "Entferne alle USB-Sticks aus dem USB-Hub. Drücke [Enter], sobald"
        " alle USB-Sticks entfernt sind\n"
    )
    volume_letters_and_serial_without_usb_sticks = get_volume_serial_numbers()
    input(
        "Stecke nun die USB-Sticks in der gewünschten Reihenfolge in den"
        " USB-Hub und drücke anschließend [Enter]\n"
    )
    volume_letters_and_serial_with_usb_sticks = get_volume_serial_numbers()
    difference = {
        letter: argon2.PasswordHasher().hash(serial)
        for letter, serial in subtract_mappings(
            volume_letters_and_serial_with_usb_sticks,
            volume_letters_and_serial_without_usb_sticks,
        ).items()
    }
    print(difference)
    return volume_letters_and_serial_without_usb_sticks, difference


def verify(volume_letters_and_serial_without_usb_sticks, difference):
    volume_letters_and_serial_with_usb_sticks = get_volume_serial_numbers()
    difference2 = subtract_mappings(
        volume_letters_and_serial_with_usb_sticks,
        volume_letters_and_serial_without_usb_sticks,
    )
    if difference2:
        for letter, serial in difference2.items():
            if letter in difference:
                if argon2.PasswordHasher().verify(difference[letter], serial):
                    print("YES")
                else:
                    print("NO")
            else:
                print("NO")
    else:
        print("NO")


def main():
    volume_letters_and_serial_without_usb_sticks, difference = configure()

    while True:
        print("Was möchtest du tun?")
        while True:
            try:
                choice = int(
                    input(
                        "1 - Einstellungen setzen\n"
                        "2 - Einstellungen prüfen\n\n> "
                    )
                )
                break
            except ValueError:
                print("Fehler: Bitte eine ganze Zahl eingeben!")

        if choice == 1:
            (
                volume_letters_and_serial_without_usb_sticks,
                difference,
            ) = configure()

        elif choice == 2:
            verify(volume_letters_and_serial_without_usb_sticks, difference)

        else:
            print("Fehler: Ungültige Auswahl!")


if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Pythonboy
User
Beiträge: 11
Registriert: Freitag 27. März 2020, 11:57

Danke __blackjack__ für die ganzen Verbesserungen, bzgl. "`pathlib` und `json` werden importiert, aber nirgends verwendet." ist mir im letzten Moment noch selber aufgefallen, daher habe ich es noch hier nachträglich bearbeitet (nicht dass sich jemand wundert dass es in meinem obigem Code nicht vorkommt), da hattest du aber wahrscheinlich schon auf Antworten geklickt (Man, seid ihr schnell!)
Pythonboy
User
Beiträge: 11
Registriert: Freitag 27. März 2020, 11:57

Hallo,

ich habe versucht die Verbesserungen umzusetzen, jetzt bin ich hier gelandet:

Code: Alles auswählen

import wmi
import argon2
import json
import pathlib

CONFIGURATION_FILE = pathlib.Path("TEST.txt")

def get_property(volume, name):
    return volume.wmi_property(name).value

def get_usb_serial_and_drive_letter():
    return dict((get_property(volume, "Name"),get_property(volume, "VolumeSerialNumber")) for volume in wmi.WMI().Win32_LogicalDisk() if volume.DriveType == 2)

def write_to_config_file(configuration):
    with open (CONFIGURATION_FILE, 'w', encoding='UTF-8') as output_file:
        output_file.write(json.dumps(configuration))

def read_config_file():
    if CONFIGURATION_FILE.exists():
        with open (CONFIGURATION_FILE, 'r', encoding='UTF-8') as input_file:
            return json.loads(input_file.read())
    else:
        return 

    
def verify_config(saved_config, current_connected_usb_volumes):
    try:
        results = [argon2.PasswordHasher().verify(saved_config[letter], current_connected_usb_volumes[letter]) for letter in saved_config.keys()]
    except:
        return False
    if results:
        return all(results)
    else:
        return False

def main():
    while True:
        print("Was möchtest du tun?")
        while True:
            try:
                choice = int(input("1 - Einstellungen setzen\n2 - Einstellungen prüfen\n\n> "))
                break
            except ValueError:
                print("Fehler: Bitte eine ganze Zahl eingeben!")
        if choice == 1:
            hashed_current_connected_usb_volumes = {letter: argon2.PasswordHasher().hash(serial) for letter, serial in get_usb_serial_and_drive_letter().items()}
            if hashed_current_connected_usb_volumes:
                write_to_config_file(hashed_current_connected_usb_volumes)
            else:
                print("Keine USB-Stick gefunden. Vergewissere dich, dass USB-Sticks eingesteckt sind und versuche es erneut.")
        if choice == 2:
            saved_config = read_config_file()
            if saved_config:
                current_connected_usb_volumes = get_usb_serial_and_drive_letter()
                is_verified = verify_config(saved_config, current_connected_usb_volumes)
                if is_verified:
                    print("Die USB-Sticks sind in der richtigen Reihenfolge eingesteckt")
                else:
                    print("Die USB-Sticks sind nicht in der richtigen Reihenfolge eingesteckt")
            else:
                print("Es wurde keine Konfigurationsdatei gefunden.")
                
if __name__ == "__main__":
    main()

#edit: Fehler in Zeile 28 korrigiert von

Code: Alles auswählen

results = [argon2.PasswordHasher().verify(saved_config[letter], current_connected_usb_volumes[letter]) for letter in current_connected_usb_volumes.keys()]
zu

Code: Alles auswählen

results = [argon2.PasswordHasher().verify(saved_config[letter], current_connected_usb_volumes[letter]) for letter in saved_config.keys()]
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Statt des dict-Aufrufs würde man heutzutage Dict-Comprehension verwenden:

Code: Alles auswählen

def get_usb_serial_and_drive_letter():
    return {
        get_property(volume, "Name"): get_property(volume, "VolumeSerialNumber")
        for volume in wmi.WMI().Win32_LogicalDisk()
        if volume.DriveType == 2
    }
Man darf gerne ein paar Zeilenumbrüche einfügen, um die Lesbarkeit zu erhöhen.

Dass `read_config_file` mal explizit ein Wörterbuch und mal implizit None zurückgibt, sollte nicht sein. Wenn keine Datei gefunden wird, dann ist das ein Fall für eine Exception.
Es darf keine nackten excepts geben, wie in `verify_config`. Was für Fehler können wirklich auftreten?
Benutzeravatar
__blackjack__
User
Beiträge: 13117
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Sirius: Den `dict()`-Aufruf hatte ich weil das hier blöd aussieht:

Code: Alles auswählen

def get_usb_serial_and_drive_letter():
    return {
        get_property(volume, "Name"): get_property(
            volume, "VolumeSerialNumber"
        )
        for volume in wmi.WMI().Win32_LogicalDisk()
        if volume.DriveType == 2
    }
Hat halt Vor- und Nachteile wenn man ein Werkzeug zum formatieren verwendet das so gut wie keine Einstellungen hat. 😇
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Pythonboy
User
Beiträge: 11
Registriert: Freitag 27. März 2020, 11:57

@Sirius: Ich habe die Funktion read_config_file() jetzt geändert, sodass immer ein dict zurück gegeben wird und bei verify_config() wird jetzt die genaue Exception abgefangen:

Code: Alles auswählen

import wmi
import argon2
import json


CONFIGURATION_FILE = "TEST.txt"

def get_property(volume, name):
    return volume.wmi_property(name).value

def get_usb_serial_and_drive_letter():
    return {get_property(volume, "Name") : get_property(volume, "VolumeSerialNumber") for volume in wmi.WMI().Win32_LogicalDisk() if volume.DriveType == 2}

def write_to_config_file(configuration):
    with open (CONFIGURATION_FILE, 'w', encoding='UTF-8') as output_file:
        output_file.write(json.dumps(configuration))

def read_config_file():
    try:
        with open (CONFIGURATION_FILE, 'r', encoding='UTF-8') as input_file:
            return json.loads(input_file.read())
    except FileNotFoundError:
        return {}
    except json.JSONDecodeError:
        return {}

    
def verify_config(saved_config, current_connected_usb_volumes):
    try:
        results = [argon2.PasswordHasher().verify(saved_config[letter], current_connected_usb_volumes[letter]) for letter in saved_config.keys()]
        if results:
            return all(results)
        else:
            return False
    except argon2.exceptions.VerifyMismatchError:
        return False
    except argon2.exceptions.VerificationError:
        return False
    
def main():
    while True:
        while True:
            print("Was möchtest du tun?")
            try:
                choice = int(input("1 - Einstellungen setzen\n2 - Einstellungen prüfen\n\n> "))
                break
            except ValueError:
                print("Fehler: Bitte eine ganze Zahl eingeben!\n")
        if choice == 1:
            hashed_current_connected_usb_volumes = {letter : argon2.PasswordHasher().hash(serial) for letter, serial in get_usb_serial_and_drive_letter().items()}
            if hashed_current_connected_usb_volumes:
                write_to_config_file(hashed_current_connected_usb_volumes)
            else:
                print("Keine USB-Stick gefunden. Vergewissere dich, dass USB-Sticks eingesteckt sind und versuche es erneut.\n")
        if choice == 2:
            saved_config = read_config_file()
            if saved_config:
                current_connected_usb_volumes = get_usb_serial_and_drive_letter()
                is_verified = verify_config(saved_config, current_connected_usb_volumes)
                if is_verified:
                    print("Verifikation erfolgreich.\n")
                else:
                    print("Verifikation fehlgeschlagen\n")
            else:
                print("Es wurde keine Konfigurationsdatei gefunden, oder die Konfigurationsdatei ist fehlerhaft.\n")
                
if __name__ == "__main__":
    main()

Antworten