AMQP.ps1
# This file contains functions for AMQP and relay messaging # Parses Bus message from the given byte array function Parse-BusMessage { [cmdletbinding()] Param( [Parameter(Mandatory=$True)] [byte[]]$Bytes ) Process { # Check the message type if($Bytes[0] -eq 0x41 -and $Bytes[1] -eq 0x4D -and $Bytes[2] -eq 0x51 -and $Bytes[3] -eq 0x50) { # This is version negotiation message # Construct the message object $message = New-Object PSObject switch($Bytes[4]) { 0 { $type = "AMQP"} 1 { $type = "AMQP"} 2 { $type = "TLS"} 3 { $type = "SASL"} } $message | Add-Member -NotePropertyName "Type" -NotePropertyValue "Protocol $type" $message | Add-Member -NotePropertyName "Protocol" -NotePropertyValue $Bytes[4] $message | Add-Member -NotePropertyName "Major" -NotePropertyValue $Bytes[5] $message | Add-Member -NotePropertyName "Minor" -NotePropertyValue $Bytes[6] $message | Add-Member -NotePropertyName "Revision" -NotePropertyValue $Bytes[7] } elseif($Bytes[0] -eq 0x00 -and $Bytes[1] -eq 0x53 -and $Bytes[2] -eq 0x75 -and $Bytes[3] -eq 0xb0) { # This is a OnewaySend message $message = Parse-RelayMessage -Bytes $Bytes } else { # This is an AMQP frame $message = Parse-AMQPFrame -Bytes $Bytes } return $message } } # Parses AMQP Frame error # Mar 12th 2020 function Parse-AMQPError { [cmdletbinding()] Param( [Parameter(Mandatory=$False)] [PSObject]$Error ) Process { $retVal = $Error # If the error is not $null, let's try to get the actual error message if($Error -ne $null) { $enum = $Error.getEnumerator() if($enum.MoveNext()) { $retVal = $enum.Value[1] } } return $retVal } } # Parses AMQP Frame from the given byte array # Mar 10th 2020 function Parse-AMQPFrame { [cmdletbinding()] Param( [Parameter(Mandatory=$True)] [byte[]]$Bytes ) Process { # Parse the header $Size = ([BitConverter]::ToUInt32($Bytes[3..0],0)) $DOff = $Bytes[4] $ExtendedHeader = $Bytes[8..$($DOff * 4)] # Construct the message $message = New-Object PSObject $message | Add-Member -NotePropertyName "Size" -NotePropertyValue $Size $message | Add-Member -NotePropertyName "DOFF" -NotePropertyValue $DOff $message | Add-Member -NotePropertyName "Extended Header" -NotePropertyValue $ExtendedHeader # Data position $pos = $DOff * 4 + 2 if($Bytes[5] -eq 0x00) # Parse AQMP Frame { $message | Add-Member -NotePropertyName "Type" -NotePropertyValue "AQMP" # Channel $channel = ([BitConverter]::ToUInt16($Bytes[7..6],0)) $message | Add-Member -NotePropertyName "Channel" -NotePropertyValue $channel switch($Bytes[$pos++]) { 0x10 { $message.Type = "AQMP Open" $content = Parse-AMQPItem -Bytes $Bytes -Pos ([ref]$pos) $message | Add-Member -NotePropertyName "ContainerId" -NotePropertyValue $content[0] $message | Add-Member -NotePropertyName "HostName" -NotePropertyValue $content[1] $message | Add-Member -NotePropertyName "MaxFrameSize" -NotePropertyValue $content[2] $message | Add-Member -NotePropertyName "ChannelMax" -NotePropertyValue $content[3] $message | Add-Member -NotePropertyName "IdleTimeOut" -NotePropertyValue $content[4] $message | Add-Member -NotePropertyName "OutgoingLocales" -NotePropertyValue $content[5] $message | Add-Member -NotePropertyName "IncomingLocales" -NotePropertyValue $content[6] $message | Add-Member -NotePropertyName "OfferedCapabilities" -NotePropertyValue $content[7] $message | Add-Member -NotePropertyName "DesiredCapabilities" -NotePropertyValue $content[8] $message | Add-Member -NotePropertyName "Properties" -NotePropertyValue $content[9] } 0x11 { $message.Type = "AQMP Begin" $content = Parse-AMQPItem -Bytes $Bytes -Pos ([ref]$pos) $message | Add-Member -NotePropertyName "RemoteChannel" -NotePropertyValue $content[0] $message | Add-Member -NotePropertyName "NextOutgoingId" -NotePropertyValue $content[1] $message | Add-Member -NotePropertyName "IncomingWindow" -NotePropertyValue $content[2] $message | Add-Member -NotePropertyName "OutgoingWindow" -NotePropertyValue $content[3] $message | Add-Member -NotePropertyName "HandleMax" -NotePropertyValue $content[4] $message | Add-Member -NotePropertyName "OfferedCapabilities" -NotePropertyValue $content[5] $message | Add-Member -NotePropertyName "DesiredCapabilities" -NotePropertyValue $content[6] $message | Add-Member -NotePropertyName "Properties" -NotePropertyValue $content[7] } 0x12 { $message.Type = "AQMP Attach" $content = Parse-AMQPItem -Bytes $Bytes -Pos ([ref]$pos) $message | Add-Member -NotePropertyName "Name" -NotePropertyValue $content[0] $message | Add-Member -NotePropertyName "Handle" -NotePropertyValue $content[1] $message | Add-Member -NotePropertyName "Direction" -NotePropertyValue "out" if($content[2] -eq "True"){ $message.Direction = "in"; $targetPos=-1} # Target $enum=$content[(6+$targetPos)].Values.GetEnumerator() if($enum.MoveNext()) { $message | Add-Member -NotePropertyName "Target" -NotePropertyValue ($enum.Value[0]) } # Tracking id $enum=$content[13].Values.GetEnumerator() if($enum.MoveNext()) { $message | Add-Member -NotePropertyName "TrackingId" -NotePropertyValue ($enum.Value) } } 0x13 { $message.Type = "AQMP Flow" $content = Parse-AMQPItem -Bytes $Bytes -Pos ([ref]$pos) $message | Add-Member -NotePropertyName "NextIncomingId" -NotePropertyValue $content[0] $message | Add-Member -NotePropertyName "IncomingWindow" -NotePropertyValue $content[1] $message | Add-Member -NotePropertyName "NextOutgoingId" -NotePropertyValue $content[2] $message | Add-Member -NotePropertyName "OutgoingWindow" -NotePropertyValue $content[3] $message | Add-Member -NotePropertyName "Handle" -NotePropertyValue $content[4] $message | Add-Member -NotePropertyName "DeliveryCount" -NotePropertyValue $content[5] $message | Add-Member -NotePropertyName "LinkCredit" -NotePropertyValue $content[6] $message | Add-Member -NotePropertyName "Available" -NotePropertyValue $content[7] $message | Add-Member -NotePropertyName "Drain" -NotePropertyValue $content[8] $message | Add-Member -NotePropertyName "Echo" -NotePropertyValue $content[9] $message | Add-Member -NotePropertyName "Properties" -NotePropertyValue $content[10] } 0x14 { $message.Type = "AQMP Transfer" $content = Parse-AMQPItem -Bytes $Bytes -Pos ([ref]$pos) $message | Add-Member -NotePropertyName "Handle" -NotePropertyValue $content[0] $message | Add-Member -NotePropertyName "DeliveryId" -NotePropertyValue $content[1] $message | Add-Member -NotePropertyName "DeliveryTag" -NotePropertyValue $content[2] $message | Add-Member -NotePropertyName "MessageFormat" -NotePropertyValue $content[3] $message | Add-Member -NotePropertyName "Settled" -NotePropertyValue $content[4] $message | Add-Member -NotePropertyName "More" -NotePropertyValue $content[5] $message | Add-Member -NotePropertyName "RcvSettleMode" -NotePropertyValue $content[6] $message | Add-Member -NotePropertyName "State" -NotePropertyValue $content[7] $message | Add-Member -NotePropertyName "Resume" -NotePropertyValue $content[8] $message | Add-Member -NotePropertyName "Aborted" -NotePropertyValue $content[9] $message | Add-Member -NotePropertyName "Batchable" -NotePropertyValue $content[10] } 0x16 { $message.Type = "AQMP Detach" $content = Parse-AMQPItem -Bytes $Bytes -Pos ([ref]$pos) $message | Add-Member -NotePropertyName "Handle" -NotePropertyValue $content[0] $message | Add-Member -NotePropertyName "Closed" -NotePropertyValue $content[1] $message | Add-Member -NotePropertyName "Error" -NotePropertyValue (Parse-AMQPError -Error $content[2]) } 0x17 { $message.Type = "AQMP End" $content = Parse-AMQPItem -Bytes $Bytes -Pos ([ref]$pos) if($content -ne $null) { $message | Add-Member -NotePropertyName "Error" -NotePropertyValue (Parse-AMQPError -Error $content[0]) } else { $message | Add-Member -NotePropertyName "Error" -NotePropertyValue $null } } 0x18 { $message.Type = "AQMP Close" if($content -ne $null) { $message | Add-Member -NotePropertyName "Error" -NotePropertyValue (Parse-AMQPError -Error $content[0]) } } } } else # Parse SASL Frame { switch($Bytes[$pos++]) { 0x40 { # sasl-server-mechanisms = list $message | Add-Member -NotePropertyName "Type" -NotePropertyValue "SASL Mechanisms" $content = Parse-AMQPList -Bytes $Bytes -Pos ([ref]$pos) $message | Add-Member -NotePropertyName "Content" -NotePropertyValue $content } 0x44 { # sasl-outcome = list $message | Add-Member -NotePropertyName "Type" -NotePropertyValue "SASL Outcome" $content = Parse-AMQPList -Bytes $Bytes -Pos ([ref]$pos) # Status $statusCodes = @("ok","auth","sys","sys-perm","sys-temp") $status = $statusCodes[$content[0]] # Message $text = [text.encoding]::ASCII.GetString( [convert]::FromBase64String($content[1])) $message | Add-Member -NotePropertyName "Status" -NotePropertyValue $status $message | Add-Member -NotePropertyName "Message" -NotePropertyValue $text } } } return $message } } # Parses an AMQP item from the given byte array # Mar 10th 2020 function Parse-AMQPItem { [cmdletbinding()] Param( [Parameter(Mandatory=$True)] [byte[]]$Bytes, [Parameter(Mandatory=$True)] [ref]$Pos ) Process { $p=$Pos.Value $retVal = $null # Check the item type switch($Bytes[$p++]) { # descriptor constructor 0x00 { $descriptor = Parse-AMQPItem -Bytes $Bytes -Pos([ref]$p) $value = Parse-AMQPItem -Bytes $Bytes -Pos([ref]$p) $retVal = @{ $descriptor = $value } } # null 0x40 { $pos.Value = $p return $null } # true 0x41 { $retVal = $True } # false 0x42 { $retVal = $False } # uint 0 0x43 { $retVal = 0 } # ulong 0 0x44 { $retVal = 0 } # empty list 0x45 { $retVal = @() } # boolean 0x56 { $boolean = $Bytes[$p++] $retVal = $boolean -eq 0x01 # 0x01 = true } # ubyte 0x50 { $retVal = [byte]$Bytes[$p++] } # byte 0x51 { $retVal = [byte]$Bytes[$p++] } # smalluint 0x52 { $retVal = [byte]$Bytes[$p++] } # smallulong 0x53 { $retVal = [byte]$Bytes[$p++] } # smallint 0x54 { $retVal = [int]$Bytes[$p++] } # smalllong 0x55 { $retVal = [long]$Bytes[$p++] } # ushort 0x60 { $retVal = [BitConverter]::ToUInt16($Bytes[$($p+1)..$($p)],0) $p+=2 } # short 0x61 { $retVal = [BitConverter]::ToInt16($Bytes[$($p+1)..$($p)],0) $p+=2 } # uint 0x70 { $retVal = [BitConverter]::ToUInt32($Bytes[$($p+3)..$($p)],0) $p+=4 } # int 0x71 { $retVal = [BitConverter]::ToUInt32($Bytes[$($p+3)..$($p)],0) $p+=4 } # float 0x72 { $retVal = [float][BitConverter]::ToInt32($Bytes[$($p+3)..$($p)],0) $p+=4 } # char 0x73 { $retVal = [text.encoding]::UTF32.GetChars($Bytes[$($p+3)..$($p)]) $p+=4 } # decimal32 0x74 { # Do nothing $p+=4 } # ulong 0x80 { $retVal = [BitConverter]::ToUInt64($Bytes[$($p+7)..$($p)],0) $p+=8 } # long 0x81 { $retVal = [BitConverter]::ToInt64($Bytes[$($p+7)..$($p)],0) $p+=8 } # double 0x82 { $retVal = [BitConverter]::ToDouble($Bytes[$($p+7)..$($p)],0) $p+=8 } # timestamp 0x82 { $timeStamp = [BitConverter]::ToUint($Bytes[$($p+7)..$($p)],0) $retVal = $epoch.AddSeconds($timeStamp) $p+=8 } # decimal64 0x84 { # Do nothing $p+=8 } # decimal128 0x94 { # Do nothing $p+=16 } # UUID 0x98 { $retVal = [guid][BitConverter]::ToUint($Bytes[$($p+15)..$($p)],0) $p+=16 } # Binary 0xa0 { $size = $Bytes[$p++] $retVal = [convert]::ToBase64String($Bytes[$p..$($p+$size)]) $p += $size } # String 0xa1 { $size = $Bytes[$p++] $retVal = [text.encoding]::UTF8.GetString($Bytes[$p..$($p+$size-1)]) $p += $size } # symbol 0xa3 { $size = $Bytes[$p++] $retVal = [text.encoding]::ASCII.GetString($Bytes[$p..$($p+$size-1)]) $p += $size } # Binary 0xb0 { $size = [BitConverter]::ToUInt32($Bytes[$($p+3)..$($p)],0) $p+=4 $retVal = [convert]::ToBase64String($Bytes[$p..$($p+$size)]) $p += $size } # String 0xb1 { $size = [BitConverter]::ToUInt32($Bytes[$($p+3)..$($p)],0) $p+=4 $retVal = [text.encoding]::UTF8.GetString($Bytes[$p..$($p+$size)]) $p += $size } # Symbol 0xb3 { $size = [BitConverter]::ToUInt32($Bytes[$($p+3)..$($p)],0) $p+=4 $retVal = [text.encoding]::ASCII.GetString($Bytes[$p..$($p+$size)]) $p += $size } # List 0xC0 { #$p-- $retVal = Parse-AMQPList -Bytes $Bytes -Pos ([ref]$p) } # List 0xD0 { #$p-- $retVal = Parse-AMQPList -Bytes $Bytes -Pos ([ref]$p) } # Map 0xC1 { #$p-- $retVal = Parse-AMQPMap -Bytes $Bytes -Pos ([ref]$p) } # Map 0xD1 { #$p-- $retVal = Parse-AMQPMap -Bytes $Bytes -Pos ([ref]$p) } # Array 0xE0 { $retVal = Parse-AMQPArray -Bytes $Bytes -Pos ([ref]$p) } # Array 0xF0 { $retVal = Parse-AMQPArray -Bytes $Bytes -Pos ([ref]$p) } } $Pos.Value = $p #if($retVal -ne $null) #{ return $retVal #} } } # Parses a AMQP list from the given byte array # Mar 10th 2020 function Parse-AMQPList { [cmdletbinding()] Param( [Parameter(Mandatory=$True)] [byte[]]$Bytes, [Parameter(Mandatory=$True)] [ref]$Pos ) Process { $p=$Pos.Value $p-- # Check the list type switch($Bytes[$p++]) { 0x45 { # The empty list $size = 0 } 0xC0 { $size = $Bytes[$p++] $intSize = 1 } 0xD0 { $size = [BitConverter]::ToUInt16($bytes[$($p+3)..$($p)],0) $p += 4 $intSize = 4 } } $max = $p + $size # Next int indicates the number of the items so increase position by the size of the int $p += $intSize $retVal = @() # Loop through the items while($p -lt $max) { $retVal += Parse-AMQPItem -Bytes $Bytes -Pos ([ref]$p) } $Pos.Value=$p return $retVal } } # Parses a AMQP list from the given byte array # Mar 10th 2020 function Parse-AMQPArray { [cmdletbinding()] Param( [Parameter(Mandatory=$True)] [byte[]]$Bytes, [Parameter(Mandatory=$True)] [ref]$Pos ) Process { $p=$Pos.Value $retVal = @() # Size $size = $Bytes[$p++] # Number of elements $elements = $Bytes[$p++] # Type $type = $Bytes[$p++] for($a = 0 ; $a -lt $elements ; $a++) { # Array elements does not have type (except for the first one) $p-- $Bytes[$p]=$type $retVal += Parse-AMQPItem -Bytes $Bytes -Pos ([ref]$p) } $Pos.Value = $p return $retVal } } # Parses a AMQP list from the given byte array # Mar 12th 2020 function Parse-AMQPMap { [cmdletbinding()] Param( [Parameter(Mandatory=$True)] [byte[]]$Bytes, [Parameter(Mandatory=$True)] [ref]$Pos ) Process { $p=$Pos.Value $p-- # Check the list type switch($Bytes[$p++]) { 0xC1 { $size = $Bytes[$p++] $intSize = 1 } 0xD1 { $size = [BitConverter]::ToUInt16($bytes[$($p+3)..$($p)],0) $p += 4 $intSize = 4 } } $max = $p + $size # Next int indicates the number of the items so increase position by the size of the int $p += $intSize $retVal = @() # Loop through the items while($p -lt $max) { $key = Parse-AMQPItem -Bytes $Bytes -Pos ([ref]$p) $value = Parse-AMQPItem -Bytes $Bytes -Pos ([ref]$p) $retVal = @{ $key = $value } } $Pos.Value=$p return $retVal } } # Returns a SASL Init message # Mar 10th 2020 function New-SASLInit { [cmdletbinding()] Param( [Parameter(Mandatory=$False)] [ValidateSet('EXTERNAL','MSSBCBS','PLAIN','ANONYMOUS')] [String]$Mechanics="EXTERNAL" ) Process { # Get the ascii bytes of the selected mechanics $mechBytes = [text.encoding]::ASCII.getBytes($Mechanics) $array = @( 0xC0, # Array [byte]($mechBytes.length + 5), # Length of the Array 0x03, # Number of elements 0xA3, # Symbol [byte]$mechBytes.length) # Length of the mechanics string $array += $mechBytes # The mechanics string $array += @( 0x40, # The initial response ($null) 0x40) # The hostname ($null) # Construct the message $message = @( # The length of the whole message [BitConverter]::GetBytes([Uint32]($mechBytes.length+18))[3..0]) $message += @( 0x02, # DOFF = 2 0x01, # Message type = SASL 0x00, # 0x00, # 0x00, # 0x53, # SmallULong 0x41) # SASL Init $message += $array # The array return $message } } # Returns an AMQP Open message # Mar 10th 2020 function New-AMQPOpen { [cmdletbinding()] Param( [Parameter(Mandatory=$True)] [String]$ContainerId, [Parameter(Mandatory=$True)] [String]$HostName ) Process { # Get the ascii bytes of the strings $idByt = [text.encoding]::ASCII.getBytes($ContainerId) $idS=$idByt.Length $hostByt = [text.encoding]::ASCII.getBytes($HostName) $hostS=$hostByt.Length $array = @( 0xC0, # Array [byte]($ids+$hostS + 19), # Length of the Array 0x0A, # Number of elements 0xA1, # UTF-8 [byte]$ids) # Length of the ContainerId string $array += $idByt # The ContainerId string $array += @( 0xA1, # UTF-8 [byte]$hostS) # Length of the ContainerId string $array += $hostByt # The ContainerId string $array += @( 0x70, # UINT 32 bit 0x00,0x01,0x00, 0x00, # Max Frame Size = 65536 0x60, # UShort 16 bit 0x1F, 0xFF, # Channel Max = 8191 0x40, # Idle timeout in millis. ($null) 0x40, # Outgoing locales ($null) 0x40, # Incoming locales ($null) 0x40, # Offered capabilities ($null) 0x40, # Desired capabilities ($null) 0x40) # Properties ($null) # Construct the message $message = @( # The length of the whole message [BitConverter]::GetBytes([Uint32]($array[1]+13))[3..0]) $message += @( 0x02, # DOFF = 2 0x00, # Message type = AMQP 0x00, # 0x00, # 0x00, # 0x53, # SmallULong 0x10) # AMQP Open $message += $array # The array return $message } } # Returns an AMQP Open message # Mar 10th 2020 function New-AMQPBegin { [cmdletbinding()] Param() Process { # Construct the message $message = [byte[]]@( 0x00, 0x00, 0x00, 0x23, # Length of the message 0x02, # DOFF = 2 0x00, # Message type = AMQP 0x00, # 0x00, # 0x00, # 0x53, # SmallULong 0x11, # AMQP Begin 0xC0, # Array 0x16, # Array length 0x08, # Array items 0x40, # Remote Channel ($null) 0x52, # Small Uint 0x01, # Next outgoing Id 0x70, # UInt 32 0x00, 0x00, 0x13, 0x88, # Incoming Window = 5000 0x70, # UInt 32 0x00, 0x00, 0x13, 0x88, # Outgoing Window = 5000 0x70, # UInt 32 0x00, 0x03, 0xFF, 0xFF, # Handle max = 262143 0x40, # Offered capabilities ($null) 0x40, # Desired capabilities ($null) 0x40) # Properties ($null) return $message } } # Returns an AMQP Attach message # Mar 11th 2020 function New-AMQPAttach { [cmdletbinding()] Param( [Parameter(Mandatory=$True)] [byte]$Handle, [Parameter(Mandatory=$True)] [PSObject]$BootStrap, [Parameter(Mandatory=$True)] [String]$RelayLinkGuid, [Parameter(Mandatory=$True)] [ValidateSet('in','out')] [String]$Direction, [Parameter(Mandatory=$True)] [String]$TrackingID ) Process { # Define variables $name = "RelayLink_$($RelayLinkGuid):$($Direction)" $url = "://$($BootStrap.Namespace).servicebus.windows.net/$($BootStrap.ServicePath)/" $target = "sb$url" # Calculate SAS Token $sasUrl = "http$url" $SASToken = Get-SASToken -Url $sasUrl -Key $BootStrap.SharedAccessKey -KeyName $BootStrap.SharedAccessKeyName # Get the bytes of the strings $bName= [text.encoding]::UTF8.GetBytes($name) $bTarget= [text.encoding]::UTF8.GetBytes($target) $bSwt= [text.encoding]::ASCII.GetBytes("com.microsoft:swt") $bSAS= [text.encoding]::UTF8.GetBytes($SASToken) $bClientAgent= [text.encoding]::ASCII.GetBytes("com.microsoft:client-agent") $bClientAgentString= [text.encoding]::UTF8.GetBytes("ServiceBus/3.0.51093.14;") $bDynamicRelay= [text.encoding]::ASCII.GetBytes("com.microsoft:dynamic-relay") $bListenerType= [text.encoding]::ASCII.GetBytes("com.microsoft:listener-type") $bRelayedConnection= [text.encoding]::UTF8.GetBytes("RelayedConnection") $bTrackingId= [text.encoding]::ASCII.GetBytes("com.microsoft:tracking-id") $bTrackingIdString = [text.encoding]::UTF8.GetBytes($TrackingId.ToString()) # Calculate the combined length $strLen = $bName.length + $bTarget.length + $bSwt.length + $bSAS.length + $bClientAgent.length + ` $bClientAgentString.length + $bDynamicRelay.length + $bListenerType.length + ` $bRelayedConnection.length + $bTrackingId.length + $bTrackingIdString.length # Set the handle if($Handle -gt 0) { $bHandle=(0x52, # smallUint $Handle) # handle value } else { $bHandle+=@(0x43) # Handle = 0, UInt 8 } # Set the role if($Direction -eq "in") { $bRole=0x41 } else { $bRole=0x42 } # Construct the message $message = [byte[]]@(([BitConverter]::GetBytes([uint32]($strLen + 91 +($bHandle.Length - 1))))[3..0]) # Length of the frame $message+=@(0x02, # DOFF = 2 0x00, # Message type = AMQP 0x00, # 0x00, # 0x00, # 0x53, # SmallULong 0x12, # AMQP Attach 0xD0) # List $message+=@(([BitConverter]::GetBytes([uint32]($strLen + 75 +($bHandle.Length - 1))))[3..0] 0x00, 0x00, 0x00, 0x0E, # Number of elements 0xA1, # String $bName.length) # String length $message+=@($bName) # Name $message+=@($bHandle) # Handle $message+=@($bRole, # Role (False) = Sender 0x40, # snd-settle-mode ($null) 0x40, # rcv-settle-mode ($null) 0x00, # Source (0x00) 0x53, # SmallULong 0x28) # (0x28 = 40) if($Direction -eq "in") { $message+=@(0xC0, # List ($bTarget.length + 13), # Array Length 0x0B, # ? 0xA1, # String $bTarget.length) # Length $message+=@($bTarget) # Target $message+=@(0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40) # ? (belongs to the array) $message+=@(0x00, # ? 0x53, # SmallULong 0x29, # (0x29 = 41) 0xC0, # List 0x08, # List Length 0x07, # List items 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40) # ? } else { $message+=@(0xC0, # List 0x0C, # List Length (0x0c = 12) 0x0b, # List elements 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, # 0x53, # SmallULong 0x29, # (0x29 = 41) 0xC0, # Array ($bTarget.length + 9), # Array Length 0x07, # ? 0xA1, # String $bTarget.length) # Length $message+=@($bTarget) # Target $message+=@(0x40, 0x40, 0x40, 0x40, 0x40, 0x40) # ? (belongs to the array) } $message+=@(0x40, 0x40, 0x40, 0x40, 0x40, 0x40, # ? 0xD1) # Map (unsettled) # Length of the map: $message+=@(([BitConverter]::GetBytes([uint32]($strLen - $bName.length - $bTarget.length + 23)))[3..0]) $message+=@(0x00, 0x00, 0x00, 0x0A # ? 0xA3, # Symbol $bSwt.length) # Length $message+=@($bSwt) # com.microsoft:swt $message+=@(0xA1, # String $bSAS.length) # Length $message+=@($bSAS) # SASToken $message+=@(0xA3, # Symbol $bClientAgent.length) # Length $message+=@($bClientAgent) # com.microsoft:client-agent $message+=@(0xA1, # String $bClientAgentString.length) # Length $message+=@($bClientAgentString) # ServiceBus/3.0.51093.14 $message+=@(0xA3, # Symbol $bDynamicRelay.length) # Length $message+=@($bDynamicRelay) # com.microsoft:dynamic-relay $message+=@(0x42) # False $message+=@(0xA3, # Symbol $bListenerType.length) # Length $message+=@($bListenerType) # com.microsoft:client-agent $message+=@(0xA1, # String $bRelayedConnection.length) # Length $message+=@($bRelayedConnection) # RelayedConnection $message+=@(0xA3, # Symbol $bTrackingId.length) # Length $message+=@($bTrackingId) # com.microsoft:tracking-id $message+=@(0xA1, # String $bTrackingIdString.length) # Length $message+=@($bTrackingIdString) # GUID return [byte[]]$message } } # Returns an AMQP Open message # Mar 11th 2020 function New-AMQPFlow { [cmdletbinding()] Param() Process { # Construct the message $message = [byte[]]@( 0x00, 0x00, 0x00, 0x28, # Length of the message 0x02, # DOFF = 2 0x00, # Message type = AMQP 0x00, # 0x00, # 0x00, # 0x53, # SmallULong 0x13, # AMQP Flow 0xC0, # Array 0x1B, # Array length 0x0B, # Array items 0x52, # Small Uint 0x01, # Next incoming Id 0x70, # UInt 32 0x00, 0x00, 0x13, 0x88, # Incoming Window = 5000 0x52, # Small Uint 0x01, # Next outgoing Id 0x70, # UInt 32 0x00, 0x00, 0x13, 0x88, # Outgoing Window = 5000 0x52, # Small Uint 0x01, # Handle (=1) 0x43, # UInt Delivery count = 0 0x70, # UInt 32 0x00, 0x00, 0x03, 0xE8, # Link credit = 1000 0x43, # UInt Available count = 0 0x40, # Drain ($null) 0x42, # Echo ($false) 0x40) # Properties ($null) return $message } } # Returns an AMQP Detach message # Mar 11th 2020 function New-AMQPDetach { [cmdletbinding()] Param( [Parameter(Mandatory=$True)] [byte]$Handle ) Process { # Set the handle if($Handle -gt 0) { $bHandle=(0x52, # smallUint $Handle) # handle value } else { $bHandle+=@(0x43) # Handle = 0, UInt 8 } # Construct the message $message= @(0x00, 0x00, 0x00, (17+($bHandle.Length - 1)), # Length of the frame (will be set later) 0x02, # DOFF = 2 0x00, # Message type = AMQP 0x00, # 0x00, # 0x00, # 0x53, # SmallULong 0x16, # AMQP Detach 0xC0, # List (0x04+($bHandle.Length - 1)),# Size 0x03) # Elements $message+=@($bHandle) $message+=@(0x41, # Closed ($true) 0x40) # Error ($null) return [byte[]]$message } } # Returns an AMQP End message # Mar 11th 2020 function New-AMQPEnd { [cmdletbinding()] Param() Process { # Construct the message $message= @(0x00, 0x00, 0x00, 0x0F, # Length of the frame (will be set later) 0x02, # DOFF = 2 0x00, # Message type = AMQP 0x00, # 0x00, # 0x00, # 0x53, # SmallULong 0x17, # AMQP End 0xC0, # List 0x02, # Size 0x01, # Elements 0x40) # Error ($null) return [byte[]]$message } } # Returns an AMQP Close message # Mar 11th 2020 function New-AMQPClose { [cmdletbinding()] Param() Process { # Construct the message $message= @(0x00, 0x00, 0x00, 0x0F, # Length of the frame 0x02, # DOFF = 2 0x00, # Message type = AMQP 0x00, # 0x00, # 0x00, # 0x53, # SmallULong 0x18, # AMQP Close 0xC0, # List 0x02, # Size 0x01, # Elements 0x40) # Error ($null) return [byte[]]$message } } # Returns an AMQP Disposition message # Mar 11th 2020 function New-AMQPDisposition { [cmdletbinding()] Param() Process { # Construct the message $message= @(0x00, 0x00, 0x00, 0x17, # Length of the frame 0x02, # DOFF = 2 0x00, # Message type = AMQP 0x00, # 0x00, # 0x00, # 0x53, # SmallULong 0x15, # AMQP Disposition 0xC0, # List 0x0A, # Size 0x06, # Elements 0x41, # Role ($true = Receiver) 0x43, # First (0) 0x40, # Last ($null), 0x41, # Settled ($true), 0x00, 0x53, 0x24, # State (0x24) 0x45, # State value (empty list) 0x40) # Batchable ($null) return [byte[]]$message } } # "Parses" the relay messages # Mar 16th 2020 function Parse-RelayMessage { [cmdletbinding()] Param( [Parameter(Mandatory=$True)] [byte[]]$Bytes #[Parameter(Mandatory=$True)] #[ref]$Pos ) Process { $p=0 $content = @() # First parse the strings and guids while($p -lt $Bytes.Length) { $curByte=$Bytes[$p++] # Something #if($curByte -eq 0x56) #{ # $length=$Bytes[$p] # $p+=$length #} # Strings if(($curByte -eq 0x99 -and $bytes[$p] -lt 0xC8) -or $curByte -eq 0x3F -or ($curByte -eq 0x40 -and $bytes[$p] -lt 0x80) -or ($curByte -eq 0x02 -and $bytes[$p] -eq 0x02))# -or ($curByte -eq 0x42 -and ($bytes[$p]+$p -lt $bytes.Length -and $bytes[$p] -gt 20))) { # If 0x02, move to next one if($Bytes[$($p-1)] -eq 0x02 -and $curByte -eq 0x02){$p++} # Get the string length and bytes $strLen = $Bytes[$p++] $stringBytes = $Bytes[$p..$($p+$strLen-1)] # Convert to string $str=[text.encoding]::UTF8.GetString($stringBytes) $content+=$str $p+=$strLen # OnewaySend message hack: Get the relay ip address if($str -eq "HttpsAddress") { $ip="$($Bytes[$p+1]).$($Bytes[$p+2]).$($Bytes[$p+3]).$($Bytes[$p+4])" $content += $ip } } # Guid elseif($curByte -eq 0xAD) { [byte[]]$bGuid = $Bytes[$p..$($p+15)] $content += ([guid]$bGuid).ToString() $p+=16 } } # Construct the message $message = New-Object PSObject $message | Add-Member -NotePropertyName "Size" -NotePropertyValue $Bytes.Length $message | Add-Member -NotePropertyName "Type" -NotePropertyValue "Relay" if($content.Count -gt 16 -and $content[0] -eq "OnewaySend") { # This is a OnewaySend message $message.Type="OnewaySend" $message | Add-Member -NotePropertyName "RelayName" -NotePropertyValue $content[2] $message | Add-Member -NotePropertyName "Container" -NotePropertyValue $content[3] $message | Add-Member -NotePropertyName "RelayId" -NotePropertyValue $content[8] $message | Add-Member -NotePropertyName "RelayIp" -NotePropertyValue $content[14] $message | Add-Member -NotePropertyName "RelayAddress" -NotePropertyValue $content[17] if($message.RelayAddress -eq $null) { $message.RelayAddress = $content[16] } } elseif($Bytes.Length -eq 4 -and $Bytes[0] -eq 0x98) { $message.type="Relay ConnectReply" } elseif($content.Count -eq 1) { if($content[0] -eq "RelayedAcceptReply") { $message.type="Relay AcceptReply" } elseif($content[0].StartsWith("sb://")) { $message.type="Relay Name" $message | Add-Member -NotePropertyName "Relay" -NotePropertyValue $content[0] } } elseif($content.Count -eq 2) { if($content[0] -eq "Ping") { $message.type="Relay Ping" } else { $message.type="Relay NetRemoteReply" $message | Add-Member -NotePropertyName "Id" -NotePropertyValue $content[0] } } elseif($content.Count -eq 3) { $message.type="Relay Ids" $message | Add-Member -NotePropertyName "SequenceId" -NotePropertyValue $content[2] $message | Add-Member -NotePropertyName "Relay" -NotePropertyValue $content[1] $message | Add-Member -NotePropertyName "SomeId" -NotePropertyValue $content[0] } elseif($content.Count -gt 10) { $message.type="Relay ProxyConnect" $message | Add-Member -NotePropertyName "ProxyUrl" -NotePropertyValue $content[8] $message | Add-Member -NotePropertyName "ProxyId" -NotePropertyValue $content[10] $message | Add-Member -NotePropertyName "SomeId2" -NotePropertyValue $content[1] $message | Add-Member -NotePropertyName "ConId" -NotePropertyValue $content[0] $message | Add-Member -NotePropertyName "ConnectionId" -NotePropertyValue $content[12] } return $message } } # Returns a Relay Connect message # Mar 17th 2020 function New-RelayConnect { [cmdletbinding()] Param() Process { # Construct the message $message= @(0x1E, 0x01, 0x00, 0x00) return [byte[]]$message } } # Returns a Relay Accept message # Mar 17th 2020 function New-RelayAccept { [cmdletbinding()] Param( [Parameter(Mandatory=$True)] [String]$Id ) Process { # Construct the message $message = @(0x56, 0x02, 0x0B, 0x01, 0x73, 0x04, 0x0B, 0x01, 0x61, 0x06, 0x56, 0x08, 0x44, 0x0A, 0x1E, 0x00, 0x82, 0x99, 0x0D, 0x52, 0x65, 0x6C, 0x61, 0x79, 0x65, 0x64, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x44, 0x0C, 0x1E, 0x00, 0x82, 0x99, 0x46, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x73, 0x2E, 0x6D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x2E, 0x63, 0x6F, 0x6D, 0x2F, 0x32, 0x30, 0x30, 0x35, 0x2F, 0x31, 0x32, 0x2F, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4D, 0x6F, 0x64, 0x65, 0x6C, 0x2F, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6E, 0x67, 0x2F, 0x41, 0x6E, 0x6F, 0x6E, 0x79, 0x6D, 0x6F, 0x75, 0x73, 0x01, 0x56, 0x0E, 0x40, 0x0D, 0x52, 0x65, 0x6C, 0x61, 0x79, 0x65, 0x64, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x08, 0x43, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x73, 0x2E, 0x6D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x2E, 0x63, 0x6F, 0x6D, 0x2F, 0x6E, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2F, 0x32, 0x30, 0x30, 0x39, 0x2F, 0x30, 0x35, 0x2F, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x62, 0x75, 0x73, 0x2F, 0x63, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, 0x09, 0x01, 0x69, 0x29, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x77, 0x77, 0x77, 0x2E, 0x77, 0x33, 0x2E, 0x6F, 0x72, 0x67, 0x2F, 0x32, 0x30, 0x30, 0x31, 0x2F, 0x58, 0x4D, 0x4C, 0x53, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x2D, 0x69, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x63, 0x65, 0x40, 0x02, 0x49, 0x64, 0x99, 0x24) $message += [text.encoding]::UTF8.getBytes($id) $message += @(0x01, 0x01, 0x01) return [byte[]]$message } } # Returns a Relay Ids Reply message # Mar 17th 2020 function New-RelayNameReply { [cmdletbinding()] Param() Process { # Construct the message $message = @(0x0B) return [byte[]]$message } } # Returns an Relay Name reply message # Mar 17th 2020 function New-RelayIdsReply { [cmdletbinding()] Param( [Parameter(Mandatory=$True)] [guid]$SomeId, [Parameter(Mandatory=$True)] [guid]$ConnectionId, [Parameter(Mandatory=$True)] [String]$Relay ) Process { $bRelay = [text.encoding]::UTF8.GetBytes($Relay) # Construct the message $message = @(0x06,($bRelay.length + 89)) $message+=@(0x01, 0x00, 0x56, 0x02, 0x0B, 0x01, 0x73, 0x04, 0x0B, 0x01, 0x61, 0x06, 0x56, 0x08, 0x44, 0x0A, 0x1E, 0x00, 0x82, 0xAB, 0xA0, 0x05, 0x44, 0x12, 0xAD) $message+=$someId.ToByteArray() $message+=@(0x44, 0x0C, 0x1E, 0x00, 0x82, 0xAB, 0x14, 0x01, 0x56, 0x0E, 0x42, 0x9E, 0x05, 0x0A, 0x20, 0x42, 0x1E, 0xAD) $message+=$ConnectionId.ToByteArray() $message+=@(0x42, 0x96, 0x05, 0x42, 0x94, 0x05, 0x44, 0x2A, 0x99) $message+=$bRelay.length $message+=$bRelay $message+=@(0x01, 0x01, 0x01, 0x01, 0x01) return [byte[]]$message } } # Returns an Relay Net Remote message # Mar 17th 2020 function New-RelayNetRemote { [cmdletbinding()] Param( [Parameter(Mandatory=$True)] [guid]$ConnectionId ) Process { # Construct the message $message = @(0x06, 0x55, 0x00, 0x56, 0x02, 0x0B, 0x01, 0x73, 0x04, 0x0B, 0x01, 0x72, 0x20, 0x0B, 0x01, 0x61, 0x06, 0x56, 0x08, 0x55, 0x2E, 0x55, 0x1E, 0xAD) $message += $ConnectionId.ToByteArray() $message += @(0x55, 0x30, 0x06, 0x34, 0x82, 0x06, 0x32, 0x82, 0x01, 0x43, 0x05, 0x6E, 0x65, 0x74, 0x72, 0x6D, 0x36, 0x0B, 0x05, 0x6E, 0x65, 0x74, 0x72, 0x6D, 0x38, 0x89, 0x08, 0x01, 0x44, 0x0A, 0x1E, 0x00, 0x82, 0xAB, 0x3A, 0x44, 0x0C, 0x1E, 0x00, 0x82, 0xAB, 0x14, 0x01, 0x56, 0x0E, 0x01, 0x01) return [byte[]]$message } } # Returns an Relay Proxy Connect Reply message # Mar 17th 2020 function New-RelayProxyConnectReply { [cmdletbinding()] Param( [Parameter(Mandatory=$True)] [guid]$SequenceId, [Parameter(Mandatory=$True)] [guid]$SomeId2, [Parameter(Mandatory=$False)] [guid]$ConnectionId = (New-Guid) ) Process { # Construct the message [byte[]]$message = @(0x06, 0xCC, 0x03, 0xA6, 0x02, 0x45, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x74, 0x65, 0x6D, 0x70, 0x75, 0x72, 0x69, 0x2E, 0x6F, 0x72, 0x67, 0x2F, 0x49, 0x43, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x53, 0x69, 0x67, 0x6E, 0x61, 0x6C, 0x69, 0x6E, 0x67, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2F, 0x53, 0x69, 0x67, 0x6E, 0x61, 0x6C, 0x43, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6F, 0x6E, 0x73, 0x65, 0x17, 0x53, 0x69, 0x67, 0x6E, 0x61, 0x6C, 0x43, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6F, 0x6E, 0x73, 0x65, 0x13, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x74, 0x65, 0x6D, 0x70, 0x75, 0x72, 0x69, 0x2E, 0x6F, 0x72, 0x67, 0x2F, 0x15, 0x53, 0x69, 0x67, 0x6E, 0x61, 0x6C, 0x43, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x52, 0x65, 0x73, 0x75, 0x6C, 0x74, 0x5C, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x73, 0x2E, 0x64, 0x61, 0x74, 0x61, 0x63, 0x6F, 0x6E, 0x74, 0x72, 0x61, 0x63, 0x74, 0x2E, 0x6F, 0x72, 0x67, 0x2F, 0x32, 0x30, 0x30, 0x34, 0x2F, 0x30, 0x37, 0x2F, 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x2E, 0x41, 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x50, 0x72, 0x6F, 0x78, 0x79, 0x2E, 0x43, 0x6F, 0x6D, 0x6D, 0x6F, 0x6E, 0x2E, 0x53, 0x69, 0x67, 0x6E, 0x61, 0x6C, 0x69, 0x6E, 0x67, 0x44, 0x61, 0x74, 0x61, 0x4D, 0x6F, 0x64, 0x65, 0x6C, 0x29, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x77, 0x77, 0x77, 0x2E, 0x77, 0x33, 0x2E, 0x6F, 0x72, 0x67, 0x2F, 0x32, 0x30, 0x30, 0x31, 0x2F, 0x58, 0x4D, 0x4C, 0x53, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x2D, 0x69, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x63, 0x65, 0x0A, 0x41, 0x63, 0x6B, 0x4C, 0x61, 0x74, 0x65, 0x6E, 0x63, 0x79, 0x0B, 0x43, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x49, 0x64, 0x56, 0x02, 0x0B, 0x01, 0x73, 0x04, 0x0B, 0x01, 0x72, 0x20, 0x0B, 0x01, 0x61, 0x06, 0x56, 0x08, 0x55, 0x90, 0x05, 0x55, 0x1E, 0xAD) $message += $SequenceId.ToByteArray() $message += @(0x01, 0x55, 0x3E, 0x1E, 0x00, 0x82, 0x55, 0x1E, 0xAD) $message += $SequenceId.ToByteArray() $message += @(0x55, 0x40, 0x83, 0x01, 0x44, 0x0A, 0x1E, 0x00, 0x82, 0xAB, 0x01, 0x44, 0x12, 0xAD) $message += $SomeId2.ToByteArray() $message += @(0x44, 0x0C, 0x1E, 0x00, 0x82, 0xAB, 0x14, 0x01, 0x56, 0x0E, 0x42, 0x03, 0x0A, 0x05, 0x42, 0x07, 0x0B, 0x01, 0x62, 0x09, 0x0B, 0x01, 0x69, 0x0B, 0x45, 0x0D, 0x81, 0x45, 0x0F, 0x99, 0x24) $message += [text.encoding]::UTF8.GetBytes($ConnectionId.ToString()) $message += @(0x01, 0x01, 0x01, 0x01) return [byte[]]$message } } #$bytes=@(0x00, 0x00, 0x00, 0x3F, 0x02, 0x01, 0x00, 0x00, 0x00, 0x53, 0x40, 0xC0, 0x32, 0x01, 0xE0, 0x2F, 0x04, 0xB3, 0x00, 0x00, 0x00, 0x07, 0x4D, 0x53, 0x53, 0x42, 0x43, 0x42, 0x53, 0x00, 0x00, 0x00, 0x05, 0x50, 0x4C, 0x41, 0x49, 0x4E, 0x00, 0x00, 0x00, 0x09, 0x41, 0x4E, 0x4F, 0x4E, 0x59, 0x4D, 0x4F, 0x55, 0x53, 0x00, 0x00, 0x00, 0x08, 0x45, 0x58, 0x54, 0x45, 0x52, 0x4E, 0x41, 0x4C) #$bytes=@(0x98,0,0,0) #$pos=0 #$bytes = get-content .\b22.bin -Encoding Byte #$message = Parse-RelayMessage -Bytes $bytes -Verbose #Remove-Variable bytes #$message |