string CalculateHMACSHA256(byte[] bytes) { using (var hmacsha256 = new HMACSHA256(key)) { var bs = hmacsha256.ComputeHash(bytes); return BitConverter.ToString(bs).ToLower().Replace("-", ""); } } // 出力例 // b248bb2b861e5efe1e5bea3722b19c65ec73f77a64deaf76906cc513f40db534
Photonの生パケットを読む
Ⅰ. はじめに
1. Photon とは
クロスプラットフォームで動作する「ネットワークエンジン」です。
ドイツのベンチャー企業「Exit Games」が開発しています。
「クラウド(SaaS)」と「オンプレミス」が選択できます。
アジアリージョンのクラウドはGMOが独占して提供しています。
サーバは Windows Server のみで動作します
2. 仕組み
Photon には「ネームサーバ」と「マスタサーバ」と「ゲームサーバ」が存在します。
※この記事では「ネームサーバ」について触れません。
「マスタサーバ」は「ゲームサーバ」のインスタンスの状態を全て監視します。
負荷状態等をもとにユーザーをどの「ゲームサーバ」に割り当てて通信させるか最適な選択を行います。
Ⅱ. マスタサーバの生パケットを読む
詳しいことはこちらに書いてあるようです。
英語
https://doc.photonengine.com/en-us/onpremise/current/reference/binary-protocol
日本語
https://doc.photonengine.com/ja-jp/onpremise/current/reference/binary-protocol
重要な部分だけをメモ。
1. 赤色の枠で囲んでいる部分を見つける
画像の場合 0x04 0xF3 0x02 です。
# | 意味 |
---|---|
1バイト目 | データ送信(受信)回数 |
2バイト目 | 0xF3固定 |
3バイト目 | 不明。0x01~0x10の場合が多い |
2. 緑色の枠で囲んでいる部分を見つける
画像の場合 0xE1 です。
この部分は 「OperationCode」 を意味しています。
0xE1 を 10進数表記すると 225 です。
以下のリファレンスから 225 は OperationCode.JoinRandomGame であることが解ります。
https://doc-api.photonengine.com/en/dotnet/current/class_exit_games_1_1_client_1_1_photon_1_1_load_balancing_1_1_operation_code.html
3. オレンジ色の枠で囲んでいる部分を見つける
画像の場合 0x00 0x04 です。
この部分は「パラメータが何個あるか」を意味しています。
0x00 0x04の場合4個です。
4. 水色の枠で囲んでいる部分を見つける
画像の場合 0xD5 です。
この部分は「ParameterCode」を意味しています。
0xD5 を 10進数表記すると 213 です。
以下のリファレンスから 213 は ParameterCode.LobbyName であることが解ります。
https://doc-api.photonengine.com/en/dotnet/current/class_exit_games_1_1_client_1_1_photon_1_1_load_balancing_1_1_parameter_code.html
5. ピンク色の枠で囲んでいる部分を見つける
画像の場合 0x73 です。
この部分は「GpType」を意味しています。
0x73 を 10進数表記すると 115 です。
GpType は 逆コンパイラを用いて調べます。
以下の表から 115 は GpType.String であることが解ります。
GpType | 値(10進数) |
---|---|
Array | 121 |
Boolean | 111 |
Byte | 98 |
ByteArray | 120 |
ObjectArray | 122 |
Short | 107 |
Float | 102 |
Dictionary | 68 |
Double | 100 |
Hashtable | 104 |
Integer | 105 |
IntegerArray | 110 |
Long | 108 |
String | 115 |
StringArray | 97 |
Custom | 99 |
Null | 42 |
EventData | 101 |
OperationRequest | 113 |
OperationResponse | 112 |
6. 茶色の枠で囲んでいる部分を見つける
画像の場合 0x00 0x01 です。
この部分は「長さ」を意味しています。
0x00 0x01 の場合1バイトです。
5 の項目が GpType.String だったので、String型で1バイトということが解ります。
7. 紫色の枠で囲んでいる部分を見つける
画像の場合 0x31 です。
この部分は「データの値」を意味しています。
5 の項目が GpType.String だったので、0x31はASCII文字コード表より'A'であることが解ります。
8. これ以降は同様にします。
留意点
・ParameterCode の Enum に含まれてない値が出てきた。
Photon マスタサーバに対する独自の拡張です。
Photon Server (On-Premises) を利用している場合は独自の拡張が可能となります。
・GpType.String は最大 0xFFFF バイトです。
・GpType.ByteArrayは最大 0xFFFFFFFF バイトです。
Ⅲ. ゲームサーバの生パケットを読む
こちらを使います。
Wireshark で Lua スクリプを読み込むだけです。
https://github.com/AltspaceVR/wireshark-photon-dissector
C#で bzip2 の圧縮、展開を行う方法
Ⅱ. SharpZipLib をインストールする
SharpZipLib というライブラリを使います。
1. NuGet から SharpZipLib で検索し、インストールする。
※.NET Standard 版もありますが、作成者の名前が異なるので非公式リリースのような気がします。
(深く調べていないので詳細は不明です。)
Ⅲ. サンプル
1. 圧縮する
private byte[] BZip2Compress(byte[] bytes) { var outStream = new MemoryStream(); BZip2.Compress(new MemoryStream(bytes), outStream, true, 9); return outStream.ToArray(); }
2. 展開する
private byte[] BZip2Decompress(byte[] bytes) { var outStream = new MemoryStream(); BZip2.Decompress(new MemoryStream(bytes), outStream, true); return outStream.ToArray(); }
Ⅳ. その他
isStreamOwner を true にすると自動的に stream を close してくれます。
level は 0-9 までの数値です。0が圧縮率が最低で、9が圧縮率が最高です。
リバースエンジニアリングを楽にする単語のメモ
Ⅰ. はじめに
タイトルの通り「リバースエンジニアリングを楽にする単語のメモ」です。
随時追加します。
WebSocket4Net C# .NET Standard対応 WebSocket クライアントライブラリ
Ⅰ. はじめに
WebSocket4Net は2017/05/11 時点でベータ段階ですが .NET Standard 対応の WebSocket ライブラリです。
Ⅱ. 使い方
1. 「WebSocket4Net」で検索し、インストールする
2. サンプルプログラム
using System; using WebSocket4Net; namespace WebSocketSample { class Program { static void Main(string[] args) { var websocket = new WebSocket("ws://example.com:81/..."); // var proxy = new HttpConnectProxy(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8008)); // websocket.Proxy = proxy; websocket.Opened += Websocket_Opened; websocket.MessageReceived += Websocket_MessageReceived; websocket.Closed += Websocket_Closed; websocket.DataReceived += Websocket_DataReceived; websocket.Error += Websocket_Error; websocket.AutoSendPingInterval = 30; websocket.EnableAutoSendPing = true; websocket.Open(); Console.ReadKey(); } private static void Websocket_Opened(object sender, EventArgs e) { Console.WriteLine("Websocket_Opened"); } private static void Websocket_Error(object sender, SuperSocket.ClientEngine.ErrorEventArgs e) { Console.WriteLine("Websocket_Error"); } private static void Websocket_DataReceived(object sender, WebSocket4Net.DataReceivedEventArgs e) { Console.WriteLine($"Websocket_DataReceived { e.Data }"); } private static void Websocket_Closed(object sender, EventArgs e) { Console.WriteLine("closed"); } private static void Websocket_MessageReceived(object sender, MessageReceivedEventArgs e) { Console.WriteLine($"Websocket_MessageReceived { e.Message }"); } } }