Hallo zusammen,
Da ich in letzter Zeit öfters mal mit Netzwerkprotokollen und Binary Daten zu tun hatte, habe ich mir zur Hilfe folgende Funktion geschrieben:
Mit Hilfe von dieser Funktion (Vielleicht hat jemand ja nen besseren Namen) kann man Binary-daten einfach und schnell in die einzelnen Bestandteile zerlegen... Das funktioniert so:
Man übergibt der Funktion die Binary-daten als ersten Parameter.
Danach erstellt man den Aufbau der Binary-daten und übergibt ihn als 2. Parameter, hierzu ein Beispiel:
In der ersten Zeile wird ein byte gelesen
In der zweiten Zeile wird erneut ein byte gelesen
Darauf folgt ein word (2 bytes)
Darauf folgt erneut ein word, dieses wird aber durch den Pfeil (<-) umgedreht, da in manchen Protokollen (Bspl. OSCAR, dort wird die byte-reihenfolge als typisches Hexadecimal angegeben) oder auch Dateiheadern Zahlen nicht als korrektes Endianness definiert sind, was für eine Konvertierung zu einer Zahl, bei dieser Funktion allerdings notwendig ist!
Zu letzt folgt ein byte-array (Ein Datenfeld) mit der Länge des vorherigen word's(^word)
Wenn diese Funktion erfolgreich ausgeführt wurde, gibt sie folgendes array zurück:
array[0] = 5
array[1] = byte
array[2] = byte
array[3] = word
array[4] = <-word
array[5] = array^word
array[0] ist hierbei immer die Anzahl der Elemente im Array!
Hier noch ein paar Erklärungen zur Syntax der Parse-Expressions:
Wichtig zu Beginn: Nach jedem Ausdruck muss ein Zeilenumbruch erfolgen! (Carriage return + Line feed!)
Stellt einen Zeilenumbruch dar. (CR & LF)
Liest ein byte.
Liest ein word (2 byte).
Liest ein dword (2 word's).
Liest ein qword (2 dword's).
Liest ein byte-array mit der Länge x (Für x kann eine beliebige Zahl eingesetzt werden!).
Liest ein byte-array mit der länge des letzten gelesenen word's. (alternativ können anstatt word auch noch: byte, dword und qword genutzt werden).
Liest ein byte-array mit der länge des x. byte, das gelesen wurde. (alternativ können anstatt byte auch noch: word, dword und qword genutzt werden)
Ein Kommentar, welches vom Parser zu Beginn aus dem Code gelöscht wird.
Ein praktisches Beispiel am OSCAR Protokoll:
[autoit]; anpassen: #include <binparse.au3>
#include <array.au3>
Global $data = Binary('0x2a02123400080001000455667788') ; OSCAR Packet example
; Den packet header parsen
Dim $parse = _BinParse($data, _
'byte\n' & _ ; OSCAR packet mark
'byte\n' & _ ; Channel
'word\n' & _ ; sequence
'<-word\n' & _ ; length
'array^word') ; packet data
If IsArray($parse) Then _ArrayDisplay($parse)
; Den TLV Anhang parsen
Dim $parse2 = _BinParse($parse[$parse[0]], _
'word\n' & _ ; TLV id
'<-word\n' & _ ; TLV length
'array^word') ; TLV data
If IsArray($parse2) Then _ArrayDisplay($parse2)
Spoiler anzeigen
Func _BinParse($bBinary, $sTempExp)
Local $bRead = Binary(''), $iMain, $iSub, $aReturn[1] = [0]
Local $sExpression = _FormatParseExpression($sTempExp)
Local $aSplit = StringSplit($sExpression, @CRLF, 1)
Local $aType[6][3] = [[5],['byte', 1],['word', 2],['dword', 4],['qword', 8],['array', Default]]
For $iMain = 1 To $aSplit[0]
Local $fInvert = False
If StringLeft($aSplit[$iMain], 2) = '<-' Then
$aSplit[$iMain] = StringTrimLeft($aSplit[$iMain], 2)
$fInvert = True
EndIf
For $iSub = 1 To $aType[0][0]
If StringCompare(StringLeft($aSplit[$iMain], StringLen($aType[$iSub][0])), $aType[$iSub][0]) = 0 Then
Local $vAdd = Binary('')
If $aType[$iSub][1] = Default Then
Local $sTemp = StringLeft($aSplit[$iMain], StringLen($aType[$iSub][0])), $iType = 1
For $iType = 1 To $aType[0][0]
If $aSplit[$iMain] = $sTemp & '^' & $aType[$iType][0] Then
Local $iTemp
For $iTemp = UBound($aType, 2) - 1 To 2 Step -1
If $aType[$iType][$iTemp] <> '' Then
Local $bTemp = _ParseByte($bBinary, Number($aType[$iType][$iTemp]))
If $fInvert = True Then
$bTemp = _BinInvert($bTemp)
EndIf
$vAdd = $bTemp
ExitLoop
EndIf
Next
Else
If StringRegExp($aSplit[$iMain], $sTemp & '\^' & $aType[$iType][0] & '\(\d+\)') Then
Local $aTemp = StringRegExp($aSplit[$iMain], $sTemp & '\^' & $aType[$iType][0] & '\((\d+)\)', 3)
If UBound($aType, 2) >= $aTemp[0] + 1 Then
If $aTemp[0] > 0 Then
Local $bTemp = _ParseByte($bBinary, Number($aType[$iType][$aTemp[0] + 1]))
If $fInvert = True Then
$bTemp = _BinInvert($bTemp)
EndIf
$vAdd = $bTemp
ExitLoop
EndIf
EndIf
EndIf
EndIf
Next
Else
Local $bTemp = _ParseByte($bBinary, $aType[$iSub][1])
If $fInvert = True Then
$bTemp = _BinInvert($bTemp)
EndIf
If BinaryLen($bTemp) = $aType[$iSub][1] Then
Local $fDone = False, $iTemp = 2
For $iTemp = 2 To UBound($aType, 2) - 1
If $aType[$iSub][$iTemp] = '' Then
$aType[$iSub][$iTemp] = $bTemp
$fDone = True
ExitLoop
EndIf
Next
If $fDone = False Then
ReDim $aType[UBound($aType, 1)][UBound($aType, 2) + 1]
$aType[$iSub][UBound($aType, 2) - 1] = $bTemp
EndIf
$vAdd = $bTemp
EndIf
EndIf
If IsBinary($vAdd) And BinaryLen($vAdd) > 0 Then
ReDim $aReturn[UBound($aReturn, 1) + 1]
$aReturn[UBound($aReturn, 1) - 1] = $vAdd
$aReturn[0] += 1
EndIf
ExitLoop
EndIf
Next
Next
Return $aReturn
EndFunc ;==>Parse
Func _BinInvert($Binary)
Local $Invert = Binary(''), $Index = 1
For $Index = 1 To BinaryLen($Binary)
$Invert = BinaryMid($Binary, $Index, 1) & $Invert
Next
Return $Invert
EndFunc ;==>InvertByte
Func _ParseByte(ByRef $Binary, $Count = 1)
If BinaryLen($Binary) < $Count Then Return False
If $Count = 0 Then Return False
Local $Byte = BinaryMid($Binary, 1, $Count)
$Binary = BinaryMid($Binary, $Count + 1)
Return $Byte
EndFunc ;==>ParseByte
Func _FormatParseExpression($Expression)
Local $Breaks = StringReplace($Expression, '\n', @CRLF)
Local $Comments = StringRegExpReplace($Breaks, '/\*[^\*/]+\*/', '')
Local $Unallowed = StringRegExpReplace($Comments, '[^abcdefghijklmnopqrstuvwxyz\(\)1234567890\r\n\^<\-]+', '')
Local $Return = $Unallowed
If StringRight($Return, 2) = @CRLF Then $Return = StringTrimRight($Return, 2)
Return $Return
EndFunc ;==>FormatExpression
Ich habe keine Fehlercodes eingebaut, da die Funktion anfangs nur für private Zwecke gedacht war.
MfG
Darknoop
PS: Ich wette, das das Ganze noch viel simpler und umfangreicher zu lösen ist, wenn jemand Zeit und Lust hat, kann er sich ja mal dran versuchen! Ich wäre sogar sehr dankbar drum