Binary Daten lesen

  • 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:

    Code
    _BinParse(binary, expression)


    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:

    Code
    byte
    byte
    word
    <-word
    array^word


    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:

    Code
    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!)

    Code
    \n


    Stellt einen Zeilenumbruch dar. (CR & LF)

    Code
    byte


    Liest ein byte.

    Code
    word


    Liest ein word (2 byte).

    Code
    dword


    Liest ein dword (2 word's).

    Code
    qword


    Liest ein qword (2 dword's).

    Code
    array^x


    Liest ein byte-array mit der Länge x (Für x kann eine beliebige Zahl eingesetzt werden!).

    Code
    array^word


    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).

    Code
    array^byte(x)


    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)

    Code
    /* comment */


    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>

    [/autoit][autoit][/autoit][autoit]

    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)

    [/autoit][autoit][/autoit][autoit]

    ; 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)

    [/autoit]


    Spoiler anzeigen
    [autoit]

    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

    [/autoit] [autoit][/autoit] [autoit]

    If StringLeft($aSplit[$iMain], 2) = '<-' Then
    $aSplit[$iMain] = StringTrimLeft($aSplit[$iMain], 2)
    $fInvert = True
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    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

    [/autoit] [autoit][/autoit] [autoit]

    $aReturn[0] += 1
    EndIf
    ExitLoop
    EndIf
    Next
    Next
    Return $aReturn
    EndFunc ;==>Parse

    [/autoit] [autoit][/autoit] [autoit]

    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

    [/autoit] [autoit][/autoit] [autoit]

    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

    [/autoit] [autoit][/autoit] [autoit]

    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

    [/autoit]

    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 :)