この記事はグラブル7周年イベント”STAY MOON”調査記録を解読する(1)の続きです。
まだ読んでない方はそちらを先に読んでいただけると助かります。
データの考察
前回解読したトップページの文章をクリックすると次のようなデータが表示されます。
このデータも日本語版と英語版で少し内容が異なりますが、前回同様ひとまず日本語版で進めていきます。
文字を読める状態にする
前回作った対応表を使って対応するアルファベットと数字を書き込んでみました。
画像の状態では扱いづらいのでテキストに書き起こします。
(一度画像に書き込んでおかないとどこまで書いたかわからなくなるのでこうしています。)
F5 6D 65 73 73 61 67 65 5F 5F 5F 5F 5F 5F 74 6F 41 6C
6C 00 00 00 00 00 00 00 00 00 00 00 57 73 74 61 79 3A 610F
5473 306F 300C 7559 307E 308B 3053 3068 300D 3002 307B
304B 3001 300C 6EDE 5728 300D 300C 505C 6EDE 300D 300C
5EF6 671F 300D 300C 96E2 308C 306A 3044 300D 300C 5C45
7D9A 3051 308B 300D 306A 3069 3002 20 CA F5 5F 5F 5F
6E 61 63 6B 5F 5F 5F 5F 5F 74 6F 4D 6F 6F 6E 00 00 00
00 00 00 00 00 00 00 00 00 08 06 F5 5F 72 65 73 65 6E
64 5F 5F 5F 5F 5F 5F 74 6F 41 6C 6C 00 00 00 00 00 00 00
00 00 00 00 0D 73 74 61 79 3A 96E2 308C 306A 3044 0D 55
F5 5F 5F 5F 6E 61 63 6B 5F 5F 5F 5F 5F 74 6F 4D 6F
6F 6E 00 00 00 00 00 00 00 00 00 00 00 00 08 06 F5 5F 72
65 73 65 6E 64 5F 5F 5F 5F 5F 5F 74 6F 41 6C 6C 00 00
00 00 00 00 00 00 00 00 00 0D 73 74 61 79 3A 96E2 308C 06A
3044 0D 55 F5 5F 5F 5F 6E 61 63 6B 5F 5F 5F 5F 5F 74
6F 4D 6F 6F 6E 00 00 00 00 00 00 00 00 00 00 00 00 08 06
この作業が一番大変でした💢
(打ち間違いなどあったらごめんなさい。)
何のデータなのかを考える
「正体不明の通信を傍受」「暗号解読班が調査中」とあるので、おそらくこのデータが傍受した通信のデータだと考えられます。
前回解読したトップページの文章が通信プロトコルの説明だと考えられるので、それを元にデータを解析していきます。
データを解析する
察しのいい方ならもうお気づきだと思いますが、このデータは16進数表記のバイナリデータと考えられます。
これを前回解読してわかった通信プロトコルを元に解析していきます。
DISPATCH GUIDELINES
PACKET CONSISTS OF THE FOLLOWING
3 BLOCKS:
「発信ガイドライン: パッケージには以下の3ブロックが含まれています。」
らしいので各ブロックごとに確認していきます。
ヘッダ
HEADER:
MESSAGE FLAG 0XF5 (1 BYTE) + DATA CLASS (7
BYTES) + DATA ADDRESS (11 BYTES) + DATA
LENGTH (12 BYTES) IN SEQUENCE TOTALS
31 BYTES.
ヘッダは
- Message Flag 0xF5 (1 Byte)
- Data Class (7 Bytes)
- Data Address (11 Bytes)
- Data Length (12 Bytes)
の合計31バイトのデータから構成されているようです。
先ほど書き起こしたデータから初めの31バイト分を確認してみます。
F5 6D 65 73 73 61 67 65 5F 5F 5F 5F 5F 5F 74 6F
41 6C 6C 00 00 00 00 00 00 00 00 00 00 00 57
ここから更に分解して人間が読める状態にしていきます。
詳しい説明は省きますが下記のようなJavaScriptを使って16進数のデータを文字列に直しています。
function hexToText(hexText) {
return String.fromCharCode(
...hexText
.split(/[\n\s]/)
.map(n => parseInt(n, 16))
);
}
6D 65 73 73 61 67 65
文字にするとmessage
になります。
5F 5F 5F 5F 5F 5F 76 6F 41 6C 6C
文字にすると______toAll
になります。
00 00 00 00 00 00 00 00 00 00 00 57
10進数にすると87
になります。
なのでこのデータは
- 種類:
message
- 宛先:
______toAll
- 長さ:
87
バイト
となります!
本文
DATA:
MESSAGES OF VARIABLE CLASS,
NUANCE, AND LENGTH. BYTE RANGE MATCHES
DATA LENGTH IN HEADER.
通信の本文にあたる部分は長さが可変で、ヘッダのData Length
の長さになっているとのことです。
なので32バイト目から87バイト分が本文になります。
73 74 61 79 3A 610F
5473 306F 300C 7559 307E 308B 3053 3068 300D 3002 307B
304B 3001 300C 6EDE 5728 300D 300C 505C 6EDE 300D 300C
5EF6 671F 300D 300C 96E2 308C 306A 3044 300D 300C 5C45
7D9A 3051 308B 300D 306A 3069 3002
これを文字にすると
stay:意味は「留まること」。ほか、「滞在」「停滞」「延期」「離れない」「居続ける」など。
になります。
フッター
FOOTER:
2 BYTE CHECKSUM ADDED TO EACH BYTE
IN HEADER AND DATA.
2バイトのチェックサムだそうです。
該当部分は
20 CA
になります。
これは誤り検出の一種でデータが正しく受信されているか確認するために使います。
ヘッダと本文のデータ列の和が0x20CA
になるか確認してみます。
function checksum(dataText) {
return dataText
.replace(/[\n\s]/g, "")// スペースと改行を取り除く
.match(/.{2}/g)// 2文字ごとに分割する(1バイト分)
.map(n => parseInt(n, 16))// 10進数に直す
.reduce((sum, n) => sum + n)// 全て足す
.toString(16);// 16進数にする
}
上記のJavaScriptで確認したところ0x2121
になりました。
違うじゃん…。
これは見なかったことにしましょう。
(英語版やこれ以降の通信データはchecksumと一致していました。)
まとめ
以上の解析方法で今回のデータをすべて解析するとこうなります!
F5
6D 65 73 73 61 67 65
5F 5F 5F 5F 5F 5F 74 6F 41 6C
6C 00 00 00 00 00 00 00 00 00 00 00 57
73 74 61 79 3A
610F 5473 306F 300C 7559 307E 308B 3053
3068 300D 3002 307B 304B 3001 300C 6EDE
5728 300D 300C 505C 6EDE 300D 300C 5EF6
671F 300D 300C 96E2 308C 306A 3044 300D
300C 5C45 7D9A 3051 308B 300D 306A 3069 3002
20 CA
F5
5F 5F 5F 6E 61 63 6B
5F 5F 5F 5F 5F 74 6F 4D 6F 6F 6E
00 00 00 00 00 00 00 00 00 00 00 00
08 06
F5
5F 72 65 73 65 6E 64
5F 5F 5F 5F 5F 5F 74 6F 41 6C 6C
00 00 00 00 00 00 00 00 00 00 00 0D
73 74 61 79 3A 96E2 308C 306A 3044
0D 55
F5
5F 5F 5F 6E 61 63 6B
5F 5F 5F 5F 5F 74 6F 4D 6F 6F 6E
00 00 00 00 00 00 00 00 00 00 00 00
08 06
F5
5F 72 65 73 65 6E 64
5F 5F 5F 5F 5F 5F 74 6F 41 6C 6C
00 00 00 00 00 00 00 00 00 00 00 0D
73 74 61 79 3A 96E2 308C 06A 3044
0D 55
F5
5F 5F 5F 6E 61 63 6B
5F 5F 5F 5F 5F 74 6F 4D 6F 6F 6E
00 00 00 00 00 00 00 00 00 00 00 00
08 06
(わかりやすいように改行をいれています)
message
______toAll
stay:意味は「留まること」。
ほか、「滞在」「停滞」「延期」「離れない」「居続ける」など。
___nack
_____toMoon
_resend
______toAll
stay:離れない
___nack
_____toMoon
_resend
______toAll
stay:離れjい
___nack
_____toMoon
「離れjい」はData Lengthを見る限り誤字だと思われます。
ヘッダのData Class
にあたる部分をみると
message
メッセージresend
再送信nack
否定応答(Wikipedia)
といった感じなので月からの「stay」というメッセージが受信側に届かず何回も再送信している…といったやり取りに見えます。
一体誰に対しての通信を傍受したのか…気になりますね。
めちゃくちゃしっかり作りこまれていて面白かったです。
(一部の誤字やデータの改行がぐちゃぐちゃなのはすごく気になりますが…)
誤字脱字などあれば@totoraj_gameまで知らせていただけるとありがたいです。
よければ筆者のツイートを拡散していただけると嬉しいです。
解説の続きを書きました!
— totoraj(ととらj) (@totoraj_game) February 24, 2021
解析方法などを詳しく書いたのでぜひ読んでみてください☺️
グラブル7周年イベント"STAY MOON"調査記録を解読する(2) | totorajの開発日記 https://t.co/eikUhS3Gg1 pic.twitter.com/35sz57dKHc
長くなりましたがここまで読んでいただきありがとうございました!!!
(内容は同じですが英語版の解析結果も載せておきます。)
英語版の解析結果
F5
6D 65 73 73 61 67 65
5F 5F 5F 5F 5F 5F 74 6F 41 6C 6C
00 00 00 00 00 00 00 00 00 00 00 5C
73 74 61 79 3A 20 64 65 66 2E 20 74 6F 20 73 74
6F 70 20 73 6F 6D 65 77 68 65 72 65 2E 20 53 79
6E 2E 20 5B 73 6F 6A 6F 75 72 6E 5D 2C 20 5B 64
65 6C 61 79 5D 2C 20 5B 70 6F 73 74 70 6F 6E 65
5D 2C 20 5B 72 65 6D 61 69 6E 5D 2C 20 5B 70 65
72 73 69 73 74 5D 2C 20 65 74 63 2E
29 2D
F5
5F 5F 5F 6E 61 63 6B
5F 5F 5F 5F 5F 74 6F 4D 6F 6F 6E
00 00 00 00 00 00 00 00 00 00 00 00
08 06
F5
5F 72 65 73 65 6E 64
5F 5F 5F 5F 5F 5F 74 6F 41 6C 6C
00 00 00 00 00 00 00 00 00 00 00 0D
73 74 61 79 3A 20 72 65 6D 61 69 6E 2E
0C DD
F5
5F 5F 5F 6E 61 63 6B
5F 5F 5F 5F 5F 74 6F 4D 6F 6F 6E
00 00 00 00 00 00 00 00 00 00 00 00
08 06
F5
5F 72 65 73 65 6E 64
5F 5F 5F 5F 5F 5F 74 6F 41 6C 6C
00 00 00 00 00 00 00 00 00 00 00 0D
73 74 61 79 3A 20 72 65 6D 61 69 6E 2E
0C DD
message
______toAll
stay: def. to stop somewhere.
Syn. [sojourn], [delay], [postpone], [remain], [persist], etc.
___nack
_____toMoon
_resend
______toAll
stay: remain.
___nack
_____toMoon
_resend
______toAll
stay: remain.
___nack
_____toMoon