Projekt: HashWert mit PowerShell-Script
Einleitung
Integration von Menschen ist nicht immer einfach, weil sie eine andere Sprache sprechen, die man zuweilen nicht versteht.
Das ist in der Computerwelt zuweilen dasselbe, weshalb der
Programmierer entweder bei seiner Sprache bleibt und dann nur schwer
bis gar nicht die gewünschten Ergebnisse erhält oder offen gegenüber
anderen Computersprachen ist:
Eben das beste aus jeder Welt. Aus der Einsicht heraus,
dass man mit Batches eben keinen Hashwert berechnen kann,
wird dann im folgenden auf ein PowerShell-Script zugegfreiffen,
dass als kleinsten gemeinsamen Nenner die Fähigkeit haben
muss, dass man das Ergebnis als Textdokument
ausgeben kann, um es dann mit der Batch weiterzuverarbeiten.
PowerShell-Script werden nicht kompiliert und liegen quelloffen vor.
Man kann Sie im normalen Editor von Windows mit Drag und Drop öffnen:
Egal ob VBS, Batch, HTA- für eine Oberfläche (Gui) oder nun
PowerShell-Script: ich arbeite immer mit dem einfachen Editor von
Windows: Der ist stabil, man kann reihenweise suchen und ersetzen -
auch eben Sonderzeichen und das reicht.
Zusätzlich schaffe ich mir Bibliotheken mit Code-Schnipseln und
veröffentliche diese auf dieser Webseite: Diese werden dann modifiziert
und erklärt: ich weiß, dass wenn ich jemand anders etwas erklären kann,
es auch selbst verstanden habe.
Es geht also in dieser Abhandlung primär darum, wie man ein Projekt mit
mehreren Programmiersprachen gestaltet, dass müssen sie als Amateur in
einem geringeren Umfang als Profi machen.
1. Projektziel
Es bietet sich an, erstmal ein Ziel zu setzen: Ich will mit Bordmitteln
von Windows 10 einen Hash-Wert von einer Datei ermitteln. Das mit einer
grafischen Oberfläche zum Aussuchen der Datei, von der der Hash-Wert ermittelt wird.
2. Vorüberlegungen zur Erreichung des Ziels
Danach eruiere ich die Grundvoraussetzung - dazu brauche ich inzwischen
keinen Merkzettel oder viel Zeit, weil sich solche Vorgänge
automatisieren:
Windows 10 als Betriebssystem, ich kann relativ gut Batch, da drin kann
ich auch andere Dateien abspeichern und ausführen. Ich brauche
zumindest ein Textdokument mit dem Ergebnis (Hashwert).
Ein Hashwert ist eine Zahlen-Buchstabenkombination, also leicht als String handelbar. Eine Batch ist also geeignet.
Dann kann ich mir überlegen, ob ich noch mit einer HTA oder VBS eine
Benutzeroberfläche für andere User zur Präsentation einrichte. Ich entschied mich für eine Abfrage auf Explorerbasis mit VBS.
Dann brauche ich einen Befehlssatz, der eine Ausgabe des Hash-Wertes ermöglicht. Hier ist das Kernproblem.
3. Recherche
Also gebe ich ergebnisoffen ins Internet und gebe Hash-Wert und Batch,
Hash-Wert und VBS und Hash-Wert und PowerShell ein und werde bei dieser
Kombination fündig:
Stack-Overflow
Mit englischer Sprache sind sie klar im Vorteil: Das ist nicht so
schwer, wenn sie sich längere Zeit mit Programmiersprachen
beschäftigen...
4. Tests:
Ich bin erstmal froh, wenn ich drei Zeile der PowerShell
verstehe, weiß wie man die Datei modifiziert (Die Ausgabe geht
Gottseidank wie bei der CMD mit > oder >> relativ einfach)
Stack-Overflow:
$someFilePath = "C:\foo.txt"
$md5 = New-Object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
$hash = [System.BitConverter]::ToString($md5.ComputeHash([System.IO.File]::ReadAllBytes($someFilePath)))
Das modifizierte der Sternenhimmelstuermer natürlich ein wenig:
Name der Datei: z. B. try.ps1
$someFilePath = "C:\Users\Snappy\Desktop\archiv\vers.pdf"
$md5 = New-Object -TypeName System.Security.Cryptography.SHA256CryptoServiceProvider
$hash = [System.BitConverter]::ToString($md5.ComputeHash([System.IO.File]::ReadAllBytes($someFilePath)))>>test.txt
Der blaue Pfad ist ein beliebiger Pfad zu einem Dokument oder einer Datei,der SHA256 wird in einem Test.txt ausgegeben.
Der Profi sieht es sofort, man kann sämtliche Hash-Werte abfragen, die bei Microsoft gelistet sind:
KeyedHashAlgorithm
MD5
RIPEMD160
SHA1
SHA256
SHA384
SHA512
Setzen sie einfach statt .SHA256 .MD5CryptoServiceProvider
Ach ja, für Skeptiker, die keinen Überblick haben: Die Variable md5 in
Zeile zwei , in der der SHA256 gespeichert wird, hat keine Auswirkung,
weil die Reihe New-Object -TypeName System.Security.Cryptography.SHA256CryptoServiceProvider den Hash-Wert generiert.
Dasselbe kann man auch mit Text machen, einfach einmal die Seite von Stack-Overflow nachschauen...
----
Das kann man dann prima mit einer Batch starten:
Beim o. a. Beispiel ein klassischer Einzeller, wenn die Batch relativ (im selben Ordner wie die try.ps1) liegt:
start powershell.exe -windowstyle hidden "C:\Users\Snappy\Desktop\archiv\try.ps1"
Ich starte die PowerShell versteckt und die arbeitet das Script ab.
---
Erklärung für Anfänger:
Voraussetzung: Sie wissen, wie eine Batch bzw. ein PowerShell-Dokument
*.ps1 aus einem Textdokument *.txt angelegt wird und das durch
Umbenennen zu einer ausführbaren Datei wird.
Aktivierung von PowerShell: PowerShell durch Eingabe von pwershell.exe
ins Windowssuchfeld finden - Mit rechter Maustaste Treffer anklicken
und als Administrator ausführen öffnen
(geht nicht anders!), dann in Konsole eingeben:
Set-ExecutionPolicy Unrestricted
Vorsicht, dann sind alle PowerShell-Scripte erlaubt: Deshalb lesen Sie sich diesen Artikel durch.
Danach wie o. a. die beiden Skripte erstellen, den Pfad zur Datei wie oben beschrieben in die *.PS1 eingeben - fertig.
Durch Doppelklick der Batch wird ein Test.txt Dokument erstellt. Bei
wiederholtem Drücken ein Wert darunter eingefügt (natürlich derselbe,
weil der Pfad ja immer noch gleich ist).
5. Codegerüst aufbauen
Nachdem ich nun meine Codeschnipsel getestet habe, weiß, dass
sie funktionieren, baue ich nun ein Codegerüst auf. Ich verpacke dabei
das Powershell-Script in der Batch und teste wieder, ob das
funktioniert:
Es rentiert sich wirklich, Aufgaben und Blöcke zu schaffen, die
fehlerfrei arbeiten und diese Blöcke dann einzeln zu testen: Gerade bei
umfangreichen Batches kriegen sie sonst Probleme, weil sie zwar mit
einem pause Befehl die
Abarbeitung in der Batch überprüfen können (Die Kommandozeile schließt
sich sonst nach der Ausführung mit Fehlermeldung oder bei erfolgreicher
Ausführung)
Lange Rede kurzer Sinn, ich verpacke nun das Powershellscript in der Batch:
test.bat oder beliebiger Name mit .bat am Ende...
set destination=%~dp0%
>"%destination%vers.txt" Echo Hello world
>"%destination%try.ps1" Echo $someFilePath = "C:\Users\Snappy\Desktop\archiv\vers.txt"
>>"%destination%try.ps1" Echo $md5 = New-Object -TypeName System.Security.Cryptography.SHA256CryptoServiceProvider
>>"%destination%try.ps1"
Echo $hash =
[System.BitConverter]::ToString($md5.ComputeHash([System.IO.File]::ReadAllBytes($someFilePath)))^>^>test.txt
start powershell.exe -windowstyle hidden ""%destination%try.ps1"
Die erste Zeile ermittelt den Pfad zum Verzeichnis bzw. Ordner, wo
die Batch liegt und packt diesen in die Variable: destination.
Danach erstelle ich ein einzeiliges Dokument Namens vers.txt, dass im selben Ordner wie die Batch liegt (>"%destination%vers.txt" Echo), Der Text dieses Dokumentes ist Hello World,
da kann auch jeder beliebige andere Text drinstehen: Nur Sonderzeichen müssen escaped werden!
Die folgenden drei Zeilen sind zur Erstellung der Datei try.psi,
wo unser PowerShell-Script Zeile für Zeile ausgedruckt wird. In der
ersten Zeile eben ein Ausgabezeichen > zur Erstellung und in den
folgenden zwei Zeilen zwei Ausgabezeichen >>, damit in der Datei try.ps1 zwei weitere Zeilen hinzugefükt werden.
Ein Problem sind dann die beiden Ausgabezeichen nach dem Echo in der fünften Zeile der Batch: Die müssen escaped werden: )^>^>test.txt
6. VBS als Oberfläche
Jetzt wird ein wenig Oberfläche hinzugefügt, damit die Eingabe für den User ein wenig leichter wird, Da reicht ein wenig vbs aus:
Die verpacke ich gleich auch in der Batch: irgendeinname.bat
set /p hwertabfrage=Geben sie md5 oder SHA512 ein
set destination=%~dp0%
>"%destination%frage.vbs" Echo Set wShell=CreateObject("WScript.Shell")
>>"%destination%frage.vbs"
Echo Set oExec=wShell.Exec("mshta.exe ""about:<input type=file
id=FILE><script>FILE.click();new
ActiveXObject('Scripting.FileSystemObject').GetStandardStream(1).WriteLine(FILE.value);close();resizeTo(0,0);</script>""")
>>"%destination%frage.vbs" Echo Set FSO = CreateObject("Scripting.FileSystemObject")
>>"%destination%frage.vbs" Echo Set MyFile = FSO.CreateTextFile("%destination%vers.txt", TRUE)
>>"%destination%frage.vbs" Echo MyFile.Close
>>"%destination%frage.vbs" Echo Set MyFile = FSO.OpenTextFile("%destination%vers.txt", 8)
>>"%destination%frage.vbs" Echo MyFile.WriteLine oExec.StdOut.ReadAll
>>"%destination%frage.vbs" Echo MyFile.Close
start /wait frage.vbs
set /p dateipfad=<vers.txt
>"%destination%try.ps1" Echo $someFilePath = "%dateipfad%"
>>"%destination%try.ps1" Echo $md5 = New-Object -TypeName System.Security.Cryptography.%hwertabfrage%CryptoServiceProvider
>>"%destination%try.ps1"
Echo $hash =
[System.BitConverter]::ToString($md5.ComputeHash([System.IO.File]::ReadAllBytes($someFilePath)))^>^>test.txt
start /wait powershell.exe -windowstyle hidden ""%destination%try.ps1"
del frage.vbs
del vers.txt
del try.ps1
Hinweis beim Paste und Copy in den Windows-Text-Editor wird die hier getrennt dargestellte Reihe (new ...ActiveXObject...) wieder eine Zeile, das ist wichtig!!!
Nun ein wenig Erklärung:
In der ersten Zeile Frage ich ab, welcher Wert ermittelt werden soll:
md5 oder sha 256,512, oder wie o. a. Die Abfrage wird in der Variable
Ertabgabfrage gespeichert und wird wie üblich in einer Batch mit zwei
Prozentzeichen dann später eingesetzt, wenn das powershell-script
generiert wird - nichts besonderes also
Dann ermittle ich wieder in der ersten Zeile, wo die Batch liegt: Das ist eine reine Batchzeile...
Zuerst erstelle ich ein reines VBS Dokument, welches in der Batch durch die Zeile >"%destination%frage.vbs" Echo eingeleitet wird und ein VBS-Dokument namens frage.vbs generiert
Das ist zweiteilig: in der ersten Zeile wird in einer Variable ein Objekt kreiert, welches den Dateipfad aufruft. Dieses Objekt enthält in der Adressierungt (oExec.StdOut.ReadAll) den Dateipfad in der langen Zeile aus:
Das muss nun irgendwo wieder in einem Textdokument ausgegeben werden,
weshalb ich ein einfaches Script zur Erstellung eines Textdokumentes
modifizierte:
Ich erstelle also ein FileSystem Objekt (eine Hülle), vergebe mit der Variablen destination den Pfad und nenne das Textdokument vers.txt. In dieses Dokument schreibe ich dann mit der Zeile MyFile.WriteLine oExec.StdOut.ReadAll den erhalten Dateipfad in das Dokument vers.txt.
Anmerkung: Das Escapen entfällt hier bei den Ausgabezeichen aus
unbekannten Gründen: ich kann Ihnen auch nicht sagen warum genau, aber
irgendeine Zeichenkombination hebt das Escapen auf...try and error ist
da die beste Lösungsstrategie...
Mit start /wait frage.vbs starte ich das VBS (ja, auch VBS muss bei dieser Variante auf ihrem PC aktiviert sein).
Die Batch wartet nun auf die Ausführung des VBS: VBS ist schnell, aber ich empfehle immer wait mit zu verwenden, weil sie spätestens beim PowerShell-Script Probleme bekommen.
Danach setze ich eine Variable mit dem Inhalt der ersten und einzigen
Zeile (mehr Zeilen werden bei dieser Technik nicht übernommen set /p dateipfad=<vers.txt).
Dann verpacke ich das PowerShell-Script und führe es aus:
PowerShell-Script braucht einige Zeit und deshalb müssen sie die Zeile:
start /wait powershell.exe -windowstyle hidden ""%destination%try.ps1" verwenden.
Machen sie das nicht, dann können Sie die diversen entstandenen
Dokumente nicht löschen, weil die PowerShell noch am arbeiten ist,
deshalb immer den start /wait Befehl benutzen.
Am Ende steht der berechnete Wert im test.txt.
Benennen sie ihn gerne um.
Anmerkung: Löschen sie ruhig einmal die del - Befehle am Ende, dann bekommen sie die PS1 und das VBS in Klarschrift!!!
7. Das Projekt ist beendet und der Autor stellt ihnen ein Grundgerüst für andere Projekte zur Verfügung.
Z. B. können sie jetzt einen ganzen Ordner mit Dateien abarbeiten:
Dateiliste erstellen und die Werte mit einer for-Schleife einzeln mit
einem call-Befehl übergeben wäre mein Lösungsansatz.
Ich weiß, das ich das kann, aber finde, dass dieses sie ruhig einmal
selbst ausprobieren sollten: vom reinen Abschreiben lernt man nichts!
Ansonsten können Sie in dieser Abhandlung eine Lösung in Powershell für Listen finden, die der Autor dann nur in Powershel schrieb.
Ich habe genug Code vorgegeben und der Rest ist ein reines Batchproblem...
8. Fazit
In dieser kleinen Batch von 21 Zeilen sind drei Programmiersprachen verwendet worden: VBS, PowerShell-Script und Batch.
Für eine Firma sind solche Konstruktionen ungeeignet, da die ja
nicht unbedingt sämtliche Programmiersprachen für
ihre Mitarbeiter freigeben wollen.
Für den ambitionierten Hobbybastler bedeutet so etwas aber auch keine fremde Software auf dem Computer zu haben.
Ich schreibe noch einen Artikel über Base 64 und PowerShell-Script -
PowerShell ist eine mächtige Skriptsprache und von daher sollte man
ruhig sich diese einmal anschauen und zumindest wissen, dass man durch
ein wenig Modifierung mit Ausgabezeichen schnell sich Ergebnisse in die
Batchwelt importieren kann.
Mit ein wenig "multikulti" kann man in der Programmierwelt mehr
erreichen, auch wenn dann verschiedene Programmiersprachen
letztlich in getrennten Umgebungen (Scripten) werkeln.
Hauptsache es kommt am Ende ein Ergebnis raus und man kann von den Ergebnissen einer fremden Sprache dann profitieren.
Das spricht dann doch für eine Integration mehrerer Programme in einer Batch...
9. Nachtrag
Ich entwickelte auch ein Skript, mit dem man einen einzelnen
Hashwert mit einem Dateiexplorer in einer GUI abfragt: Der Hash-Wert
wird dabei in der Textbox ausgegeben: vieeleichte einmal das Script:
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$Form = New-Object System.Windows.Forms.Form
$Form.Size = New-Object System.Drawing.Size(600,400)
############################################## Start functions
function Openfiledialog {
Add-Type -AssemblyName System.Windows.Forms
$OpenFile = New-Object System.Windows.Forms.OpenFileDialog
[void]$OpenFile.ShowDialog()
$ergebnis=$OpenFile.filename
$pfad=$ergebnis
$someFilePath = $pfad
$md5 = New-Object -TypeName System.Security.Cryptography.SHA256CryptoServiceProvider
$hash = [System.BitConverter]::ToString($md5.ComputeHash([System.IO.File]::ReadAllBytes($someFilePath)))
$outputBox.text+=$ergebnis + $hash + "`r`n"
} #end Openfiledialog
############################################## end functions
$outputBox = New-Object System.Windows.Forms.TextBox
$outputBox.Location = New-Object System.Drawing.Size(10,150)
$outputBox.Size = New-Object System.Drawing.Size(565,200)
$outputBox.MultiLine = $True
$outputBox.ScrollBars = "Vertical"
$Form.Controls.Add($outputBox)
############################################## end text fields
############################################## Start buttons
$Button = New-Object System.Windows.Forms.Button
$Button.Location = New-Object System.Drawing.Size(400,30)
$Button.Size = New-Object System.Drawing.Size(110,80)
$Button.Text = "Openfiledialog"
$Button.Add_Click({Openfiledialog})
$Form.Controls.Add($Button)
############################################## end buttons
$Form.Add_Shown({$Form.Activate()})
[void] $Form.ShowDialog()
Das Skript läuft wie gesagt über einen Button, der die Funktion Openfildialog aufruft. Der Name und Pfad der Datei ($ergebnis=$OpenFile.filename) wird in dem Fileexplorer ($OpenFile = New-Object System.Windows.Forms.OpenFileDialog) abgegriffen und das Ergebnis ($ergebnis=$OpenFile.filename) in einer Outbox ($outputBox.text+=$ergebnis + $hash + "`r`n") ausgegeben. Dabei wird jedes weitere Ergebnis durch den Zeilenschalter ($outputBox.text+=$ergebnis + $hash) in der nächsten Zeile hinzugefügt (+ "`r`n").
Sie können also mehrere Dateien nacheinander überprüfen - einfach Button nochmal drücken und es geht in die nächste Runde.
Der Code zur Ermittlung des Hashwertes 256 wird wie gewohnt verwendet. Das können sie gerne ändern.
Ist eine richtige Software auf rudimentäre GUI beschränkt...
Impressum