-
Notifications
You must be signed in to change notification settings - Fork 37
開発する
最初に、Java11 (11.0.5以上)
をインストールしておきます。
次に、GitHubからPacketProxyのソースコードをcloneします。
$ git clone https://github.com/DeNA/PacketProxy.git
cloneできたら、下記の3種類の方法のうち、いずれかの方法でビルドし実行します。
- コマンドラインからビルドし実行
- Eclipseでビルドし実行
- IntelliJ IDEAでビルドし実行
それぞれの方法の具体的な手順を下記に示します。
- gradlewコマンドを実行します。
$ ./gradlew assemble
- PacketProxyを実行します。
$ ./gradlew run
-
インポートを選択します
- Eclipseを起動後
import projects...
を選択します。
- Eclipseを起動後
-
Gradleプロジェクトを選択します
- インポートプロジェクトダイアログで
Gradle
->Existing Gradle Project
を選択します。
- インポートプロジェクトダイアログで
-
cloneしたプロジェクトを読み込みます
- プロジェクト選択ダイアログで、cloneしたPacketProxyプロジェクトを読み込みます。
-
PacketProxyをビルドし実行します
-
Gradle Tasks
タブからPacketProxy
->application
->run
を選択します。
-
※ もしGradleプロジェクトよりもEclipseプロジェクトで開発したい場合には ./gradlew eclipse
を実行した上で、Eclipseプロジェクトとしてインポートしてください。
-
プロジェクトを読み込みます
- IntelliJ IDEAを起動後
import Project
を選択し、cloneしたPacketProxyプロジェクトを読み込みます。
- IntelliJ IDEAを起動後
-
Gradleプロジェクトを選択します
- インポートプロジェクトダイアログで
Import project from external model
->Gradle
を選択します。
- インポートプロジェクトダイアログで
-
PacketProxyをビルドし実行します。
- プロジェクトツリーから
src/main/java/core/packetproxy/PacketProxy.java
を右クリックしプロパティメニューからRun PacketProxy.main()
を選択します。
- プロジェクトツリーから
HTTPやHTTPS等の主要な通信プロトコルは、PacketProxyにビルトインされていますが、それ以外のプロトコル(例:ゲームの独自の暗号通信プロトコルなど)は実装されていません。
そのため、ゲームの通信をみたい場合は、ゲーム用プロトコル用の拡張(エンコードモジュール)を開発することになります。
エンコードモジュールを開発することで、PacketProxyのヒストリ画面に自動的にパケットを復号・整形(デコード)して表示できたり、再送時に自動的に暗号化(エンコード)して送信できたりするので、より脆弱性診断が実施しやすくなります。
エンコードモジュールは、端的に言えば下図の デコード
と エンコード
と記載された4つの処理を実行するクラスのことです。
※ 画面表示
と ユーザが書き換え
という処理はエンコードモジュールの処理ではありませんが、エンコードモジュールの処理をわかりやすくするため、記載しました。
もっとも簡単なエンコードモジュールのコードを示します(実際には、他にも定義が必要なメソッドがありますが、後述します)。
public class EncodeSample extends Encoder {
public EncodeSample() {
}
public EncodeSample(String ALPN) throws Exception {
super(ALPN);
}
@Override
public byte[] decodeClientRequest(byte[] input_data) throws Exception {
return input_data;
}
@Override
public byte[] encodeClientRequest(byte[] input_data) throws Exception {
return input_data;
}
@Override
public byte[] decodeServerResponse(byte[] input_data) throws Exception {
return input_data;
}
@Override
public byte[] encodeServerResponse(byte[] input_data) throws Exception {
return input_data;
}
}
ちなみに、このエンコードモジュールはエンコード処理とデコード処理において、何もしない(そのままのデータを返す)コードになっています。
さらに別の例を示します。
このエンコードモジュールは、小文字で入力されたパケットデータを大文字に変換します。
このエンコードモジュールを使うことによって、PacketProxy上ではパケットデータを大文字で扱うことができるようになります。PacketProxyから外に出るときには、小文字に戻すという処理が行われます。
public class EncodeSampleUpperCase extends Encoder {
public EncodeSampleUpperCase() {
}
public EncodeSampleUpperCase(String ALPN) throws Exception {
super(ALPN);
}
@Override
public byte[] decodeClientRequest(byte[] input_data) throws Exception {
return new String(input_data).toUpperCase().getBytes();
}
@Override
public byte[] encodeClientRequest(byte[] input_data) throws Exception {
return new String(input_data).toLowerCase().getBytes();
}
@Override
public byte[] decodeServerResponse(byte[] input_data) throws Exception {
return new String(input_data).toUpperCase().getBytes();
}
@Override
public byte[] encodeServerResponse(byte[] input_data) throws Exception {
return new String(input_data).toLowerCase().getBytes();
}
}
ところで実は、エンコードモジュールには、今まで説明してきた 4 つのメソッド以外にも、定義しなければならないメソッドが 2 つあります。
1つ目は、 String getName()
というメソッドで、エンコードモジュールの名前を定義します。この名前は、PacketProxyの設定画面で利用するエンコードモジュールを選択するときにラベルとして表示されます。
2つ目は、分かりにくいのですが、 int checkDelimiter(byte[] input_data)
というメソッドです。このメソッドは、入力されたバイト列において、パケットの区切りが何バイト目になるのかを返すメソッドです。バイナリデータが到着するたびに呼び出されます。
この checkDelimiter
メソッドは、次々と到着するバイナリデータから、パケットを切り出す為に利用されます。もし、まだパケットとして切り出すだけのバイナリデータが到着していない場合には、 -1 を返すようにしてください。
エンコードモジュールの完全な例は、次のようになります。
public class EncodeSample extends Encoder {
public EncodeSample() {
}
public EncodeSample(String ALPN) throws Exception {
super(ALPN);
}
@Override
public String getName() {
return "Sample";
}
@Override
public int checkDelimiter(byte[] input_data) throws Exception {
// 到着したデータの塊をそのままパケットとして扱う
return input_data.length;
}
@Override
public byte[] decodeClientRequest(byte[] input_data) throws Exception {
return input_data;
}
@Override
public byte[] encodeClientRequest(byte[] input_data) throws Exception {
return input_data;
}
@Override
public byte[] decodeServerResponse(byte[] input_data) throws Exception {
return input_data;
}
@Override
public byte[] encodeServerResponse(byte[] input_data) throws Exception {
return input_data;
}
}
HTTPプロトコルベースのエンコードモジュールを開発するとき、Encoderクラスを継承して開発すると、各メソッド引数の入力バイナリデータをHTTPデータとしてパースしなければならず面倒です。
利便性のため、入力バイナリデータをHTTPデータとしてパースしてから引数として渡すEncodeHTTPBaseクラスを用意しました。下記のように、EncodeHTTPBaseクラスを継承することで、より簡単にHTTPベースのエンコードモジュールを開発できます。
public class EncodeSampleHTTP extends EncodeHTTPBase
{
public EncodeSampleHTTP() {
}
public EncodeSampleHTTP(String ALPN) throws Exception {
super(ALPN);
}
@Override
public String getName() {
return "SampleHTTP";
}
@Override
protected Http decodeClientRequestHttp(Http inputHttp) throws Exception {
byte[] body = inputHttp.getBody();
// ... ここでbodyを操作
inputHttp.setBody(body);
return inputHttp;
}
@Override
protected Http encodeClientRequestHttp(Http inputHttp) throws Exception {
return inputHttp;
}
@Override
protected Http decodeServerResponseHttp(Http inputHttp) throws Exception {
return inputHttp;
}
@Override
protected Http encodeServerResponseHttp(Http inputHttp) throws Exception {
return inputHttp;
}
}
実際に実装を開始する方法は2種類あります。
-
1つ目の方法は、PacketProxyをcloneし、PacketProxyのソースコードツリーに直接エンコードモジュールを実装する方法です。
-
2つ目の方法は、PacketProxyPlugin(エンコードモジュール開発用のプロジェクト)をcloneして、エンコードモジュールを開発する方法です。
- ビルドすると
PacketProxyPlugin-VERSION-all.jar
として出力されるので、$HOME/.packetproxy/plugins/
に配置します。すると、PacketProxyの起動時に自動的に読み込まれるようになります。詳しくは PacketProxyPlugin リポジトリのREADMEをみてください。
- ビルドすると
PacketProxy本体を更新した場合には、新しいリリースファイル(インストーラ含む)を、コマンドラインから生成することができます。 Mac版のリリースファイルを生成するために、署名に使う鍵や公証(notary)に使うアカウントとパスワードが必要になります。リリースファイルを生成する前に、build.gradleに設定しておいてください。またMac版のリリースファイルはjpackageを利用して生成しているため、あらかじめjpackageのインストールが必要です。
$ ./gradlew release
[プロジェクトルート]/build/distributions/
フォルダに、下記リリースファイルが生成されます。
- Windows 64ビット版
win64/PacketProxy-[Version]-Installer-Win64.exe
- MacOS
mac/PacketProxy-[Version]-Installer-Mac-Signed.dmg