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