🗒️

【Ruby】パケットのバイナリデータを扱うときにチラ見したいチートシート

2021/08/18に公開

表題のようなものが欲しくて整理してみました。データを各種形式に変換する際の変換方法を中心に整理しました。

いつもバイナリデータの処理のコードを実装する際、都度検索していて、エンコードやら○進数やら、こんがらがっているので。

概要図

サンプルのデータの準備

まず、サンプルのバイナリデータは以下のスニペットを実行して、Pingを受信して受け取って準備します。
IPADDR_OF_INTERFACE の部分はインタフェースのIPアドレスに置き換えます)

IPヘッダとICMPヘッダのバイナリデータが mesg 変数に格納されます。文字列リテラルでエンコードは <Encoding:ASCII-8BIT> となります。

require "socket"

socket = Socket.open(
        Socket::AF_INET,
        Socket::SOCK_RAW,
        Socket::IPPROTO_ICMP
      )
socket.bind(Socket.sockaddr_in(nil, "IPADDR_OF_INTERFACE"))
mesg, _ = socket.recvfrom(1500)
# 出力結果は適当にマスクしているので実際の値とは異なります
pp mesg # "E\x00\x000\xED\x16@\x00\xFE\x01\xFCE\x12\xB2\xC8\x11\xAC\x1F\f\x8B\b\x00\xA8y-\xB4\x00\x01O\xCA\ea\x00\x00\x00\x00\x8D\x81\a\x00\x00\x00\x00\x00\x10\x11\x12\x13"
pp mesg.encoding # <Encoding:ASCII-8BIT>

今回受信データ中のIPヘッダの宛先IPアドレスの値をサンプルデータとして利用します。
IPヘッダ(20Byte)のうち、宛先IPアドレスは最後の4Byteです。

値としては 172.31.12.139 を利用します。バイナリデータは "\xAC\x1F!\xAA" が該当しますので、以下チートシートではこのデータを利用します。
文字列をバイナリデータとするには String#b を利用すればいいので、以下のように準備すれば簡単です。

dst_addr = "\xAC\x1F!\xAA".b

チートシート

上記「概要図」の内容を実際のコードで動作を整理していきます。

String(バイナリ) ⇔ Array[Integer]

pp dst_addr.unpack("C*") # [172, 31, 33, 170]
pp dst_addr.bytes # [172, 31, 33, 170]

pp [172, 31, 33, 170].pack("C*") # "\xAC\x1F!\xAA"

String(バイナリ) ⇔ Array[String(16進数)]

pp dst_addr.unpack("H2 H2 H2 H2") # ["ac", "1f", "21", "aa"]
pp dst_addr.unpack("H*")[0].scan(/.{1,#{2}}/) # ["ac", "1f", "21", "aa"]

pp ["ac", "1f", "21", "aa"].pack("H2 H2 H2 H2") # "\xAC\x1F!\xAA"

String(バイナリ) ⇔ String(2進数)

pp dst_addr.unpack("B*")[0] # 10101100000111110010000110101010

pp ["10101100000111110010000110101010"].pack("B*") # "\xAC\x1F!\xAA"

String(バイナリ) ⇔ Array[String(バイナリ)]

pp dst_addr.chars # ["\x84", "\xAC", "\x1F", "!"] 

pp ["\x84".b, "\xAC".b, "\x1F".b, "!".b].join # "\xAC\x1F!\xAA"

Array[Integer] ⇔ Array[String(16進数)]

addr_arr = dst_addr.bytes # [172, 31, 33, 170]
pp addr_arr.map { |s| s.to_s(16) } # ["ac", "1f", "21", "aa"]

pp ["ac", "1f", "21", "aa"].map { |s| s.to_i(16) } # [172, 31, 33, 170]

おまけ:覚えておきたいWireSharkの見方

WireShark のデータ部の左側は16進表記。
右側は上記でいうところのバイナリデータ。バイナリデータの中身は1Byte(0-255)分格納されるが、ASCIIコード的には7bit分しか割り当てが無い。また、7bitの中には文字以外(各種制御コードとか)のものもあるので、そういったものは表示ができないため、「.」で表示される。(下記2つ目の参考リンクが分かりやすい)

参考

https://www.k-cube.co.jp/wakaba/server/ascii_code.html

https://techracho.bpsinc.jp/hachi8833/2018_07_02/58408

https://amzn.to/3yYVA1Q

Discussion