🗒️
【Ruby】パケットのバイナリデータを扱うときにチラ見したいチートシート
表題のようなものが欲しくて整理してみました。データを各種形式に変換する際の変換方法を中心に整理しました。
いつもバイナリデータの処理のコードを実装する際、都度検索していて、エンコードやら○進数やら、こんがらがっているので。
概要図
サンプルのデータの準備
まず、サンプルのバイナリデータは以下のスニペットを実行して、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つ目の参考リンクが分かりやすい)
参考
Discussion