Skip to content

開発する

funa-tk edited this page Aug 5, 2021 · 11 revisions

開発環境を整える

最初に、Java11 (11.0.5以上) をインストールしておきます。

次に、GitHubからPacketProxyのソースコードをcloneします。

$ git clone https://github.com/DeNA/PacketProxy.git

cloneできたら、下記の3種類の方法のうち、いずれかの方法でビルドし実行します。

  • コマンドラインからビルドし実行
  • Eclipseでビルドし実行
  • IntelliJ IDEAでビルドし実行

それぞれの方法の具体的な手順を下記に示します。

コマンドラインからビルドし実行

  1. gradlewコマンドを実行します。
    $ ./gradlew assemble
  2. PacketProxyを実行します。
    $ ./gradlew run

Eclipseでビルドし実行

  1. インポートを選択します
    • Eclipseを起動後 import projects... を選択します。
  2. Gradleプロジェクトを選択します
    • インポートプロジェクトダイアログで Gradle -> Existing Gradle Project を選択します。
  3. cloneしたプロジェクトを読み込みます
    • プロジェクト選択ダイアログで、cloneしたPacketProxyプロジェクトを読み込みます。
  4. PacketProxyをビルドし実行します
    • Gradle Tasks タブから PacketProxy -> application -> run を選択します。

※ もしGradleプロジェクトよりもEclipseプロジェクトで開発したい場合には ./gradlew eclipse を実行した上で、Eclipseプロジェクトとしてインポートしてください。

IntelliJ IDEAでビルドし実行

  1. プロジェクトを読み込みます
    • IntelliJ IDEAを起動後 import Project を選択し、cloneしたPacketProxyプロジェクトを読み込みます。
  2. Gradleプロジェクトを選択します
    • インポートプロジェクトダイアログで Import project from external model -> Gradle を選択します。
  3. PacketProxyをビルドし実行します
    • プロジェクトツリーから src/main/java/core/packetproxy/PacketProxy.java を右クリックしプロパティメニューから Run PacketProxy.main() を選択します。

エンコードモジュールを開発する

エンコードモジュールとは

HTTPやHTTPS等の主要な通信プロトコルは、PacketProxyにビルトインされていますが、それ以外のプロトコル(例:ゲームの独自の暗号通信プロトコルなど)は実装されていません。

そのため、ゲームの通信をみたい場合は、ゲーム用プロトコル用の拡張(エンコードモジュール)を開発することになります。

エンコードモジュールを開発することで、PacketProxyのヒストリ画面に自動的にパケットを復号・整形(デコード)して表示できたり、再送時に自動的に暗号化(エンコード)して送信できたりするので、より脆弱性診断が実施しやすくなります。

エンコードモジュールの動作のしくみ

エンコードモジュールは、端的に言えば下図の デコードエンコード と記載された4つの処理を実行するクラスのことです。

エンコードモジュール概略図

画面表示ユーザが書き換え という処理はエンコードモジュールの処理ではありませんが、エンコードモジュールの処理をわかりやすくするため、記載しました。

エンコードモジュールのコード1

もっとも簡単なエンコードモジュールのコードを示します(実際には、他にも定義が必要なメソッドがありますが、後述します)。

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;
	}
}

ちなみに、このエンコードモジュールはエンコード処理とデコード処理において、何もしない(そのままのデータを返す)コードになっています。

エンコードモジュールのコード2

さらに別の例を示します。

このエンコードモジュールは、小文字で入力されたパケットデータを大文字に変換します。

このエンコードモジュールを使うことによって、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;
	}
}

エンコードモジュールのコード3

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