Debugger.godMode() – デバッガーを使ってJVMアプリケーションをハッキングする
他の言語: English Español 한국어 Português 中文
かつてのコンピューターゲームは異なっていました。 彼らのグラフィックスやメカニクスが進化しただけでなく、 今日のゲームにはあまり見られない特徴がありました:チートコード。
チートコードとは、何か特別なものを得られるキーの連続のことで、 無限の弾薬を得たり、壁を通り抜ける能力を得たりすることができました。 最も一般的で強力なものは「ゴッドモード」で、これによりあなたは無敵になりました。

これがあなたのキャラクターがDoomで
IDDQD
を入力したときに見えるようになるものです。- ゴッドモードのためのキーの組み合わせです。
実際には、この特定のキーのシーケンスは非常に人気があり、それは
ミームになり、ゲーム自体を超えて人気を博しました。
ゴッドモードがかつてのようにゲームで一般的でなく、IDDQDのミームの時代が終わりつつあるようですが、 現代版が存在するかどうか疑問に思うかもしれません。 私自身は、デバッガが僕の現代版のIDDQDであると考えています。 これは必ずしもゲームに関連しているわけではありませんが、超能力を持っているという感覚を呼び起こします。
Space Invaders
僕の主張を説明するための楽しいシナリオを紹介します。 Doomに詳しくない人でも、この より古いゲームであるSpace Invadersを見たことがあるでしょう。 Doomと同様、そのプロットはスペースでの侵略者と戦うというテーマを中心に展開されます。
僕の友人であり同僚であるEugene Nizienkoは、 このゲームをエディターの中でプレイすることができるIntelliJ IDEAプラグインを作成しました。 これは、インデックス作成が完了するのを待つ間に過ごすのに最適な方法です。


このゲームにはゴッドモードはありませんが、私たちが非常に決意しているなら、自分自身で追加することはできますか? デバッガーを使ってプログラムをハッキングする古典的な伝統を取り戻すために、一緒に見てみましょう!
責任を持ってください! 彼のプログラムをいじる前に、ユージンから同意を得ました。 自分自身のコードでないデバッガーを使っている場合は、それを倫理的に行ってください。 そうでなければ、やらないでください。
トゥールを準備する
メタ的な体験を準備しましょう。自身のデバッガーを使ってIntelliJ IDEAをデバッグします。
しかし、小さな問題があります。IntelliJ IDEAをデバッグするとき、IDEは一時停止し、その結果IDEは応答しなくなります。 したがって、我々は機能的な追加のIDEインスタンスが必要で、それは私たちのデバッグツールとして機能します。
複数のIDEインスタンスを管理するために、私はJetBrains Toolbox Appを使用します。 これはあなたのJetBrainsのIDEを整理するためのアプリです。 それにより、一緒に複数のバージョンの同じIDEをインストールしたり、これらを異なるVMオプションのセットで実行するためのショートカットを作成したりすることができます。
以下の2つのIntelliJ IDEAインスタンスをインストールしましょう。


両方のインスタンスで同じIDEバージョンを使用する場合は、特定の システム、設定、ログディレクトリを指定することを確認してください。Tool actions | 設定 (Settings) | Configurationからアクセスできます。 このページでは、便宜上IDEインスタンスに名前を付けることもできます。私はそれらに’Space Invaders’と’Debug’と名付けました。
‘Space Invaders’インスタンスをデバッグできるようにするために、それの近くのTool actionsをクリックしてから、 設定 (Settings) | Edit JVM optionsに進みます。 開いたファイルに以下の行をペーストします:
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005


これにより、ターゲットJVMがデバッグエージェントで起動し、
ポート5005
での
入力デバッガ接続を受け付けるようになります。
ゲームを起動する
’Space Invaders’インスタンスを起動し、ゲームをインストールし、
Space Invadersと
いうアクションを実行して起動します。
アクションを見つけるには、Shiftキーを2回押し、Space Invaders
と入力し始めます。


しばらくプレイして、修正したい動作を確認してみましょう。 敵のミサイルが宇宙船に当たると、左上のヘルスバーからヘルスが減ります。
アタッチとサスペンド
デバッグの旅は、‘Debug’のIDEインスタンスを開き、新しいKotlinプロジェクトを設定することから始まります。 このプロジェクトが必要な主な理由は、プロジェクトがなければデバッガーを起動することはできないからです。
さらに、IntelliJ IDEAは新規プロジェクトテンプレートにJava/Kotlin標準ライブラリを含めていますが、 これを後で使用するかもしれません。標準ライブラリの使用法については、後続の章で説明します。
プロジェクトを作成した後、メインメニューに移動し、実行 (Run) | プロセスにアタッチ (Attach to Process)を選択します。 これにより、デバッガーのアタッチリクエストを待機しているローカルのJVMのリストが表示されます。 リストから他の実行中のIDEを選択します。


以下のような メッセージがコンソールに表示され、デバッガが正常にターゲットVMにアタッチされたことが確認できます。
Connected to the target VM, address: 'localhost:5005', transport: 'socket'
これで興味深い部分に入ります。どうやってアプリケーションを一時停止させますか?
通常であれば、アプリケーションのコードにブレークポイントを設定しますが、この場合、 私たちはIntelliJ IDEAとSpace Invadersプラグインの両方のソースを持っていません。 これにより、ブレークポイントを設定することができないだけでなく、プログラムの動作を理解するのも複雑になります。 一見すると、調査したりステップを進めたりするものは何もないように見えます。
幸いなことに、IntelliJ IDEAには時停止 (Pause Program)という機能があります。 これを使うと、対応するコードの行を指定することなく、任意のタイミングでプログラムを一時停止することができます。 これはデバッガーツールバーまたはメインメニューで見つけることができます。 実行 (Run) | デバッグアクション (Debugging Actions) | 時停止 (Pause Program)。


アプリケーションが一時停止し、デバッグの開始点が得られます。
時停止 (Pause Program)は非常に強力な手法であり、特にいくつかの高度なシナリオで特に役立ちます。 詳細を学ぶために、関連する記事をチェックしてみてください:
関連するオブジェクトを見つける
私たちの目標をプログラミングの用語で見ると、それは宇宙船のヘルスが下がらないようにすることを意味します。 対応する状態を保持しているオブジェクトを見つけましょう。
プラグインコードについて何も知らないので、 私たちはIntelliJ IDEAデバッガーのメモリ (Memory)ビューを使ってヒープを直接調査することができます。


この機能は、現在生きているすべてのオブジェクトに関する情報を提供します。
invaders
と入力して何を見つけることができるか見てみましょう。


どうやらプラグインのクラスは com.github.nizienko.spaceinvaders
というパッケージに所属しているようです。
このパッケージの中には GameState
というクラスがあり、それにはいくつかのライブインスタンスがあります。
一見するとこれが必要なものに見えます。
GameState
をダブルクリックすると、このクラスのすべてのインスタンスが表示されます。


どうやらそれはenumで、これは私たちが探しているものではありません。
探し続けると、私たちは1つの Game
のインスタンスに出くわします。
ノードを展開すると、そのインスタンスのフィールドを調査することができます。


health
プロパティがここで関心を持つべきもののようです。
そのフィールドの中には _value
というものがあります。
私の場合、その値は 100
で、これは
私がゲームを一時停止したときにヘルスバーが満タンであることと相関しています。
したがって、これは考慮すべき正しいフィールドであり、その値は 0
から 100
の範囲にあると思われます。
この仮説をテストしてみましょう。
_value
を右クリックし、値の設定 (Set Value)を選択します。
現在の値とは異なる値を選択します。例えば、私は 50
を選びます。


この工程では、一時中止アクションの後にメソッドを評価できません (Cannot evaluate methods after Pause action)というエラーに遭遇します。


問題は、私たちがブレークポイントの代わりに時停止 (Pause Program)を使用し、この機能はいくつかの制限を伴うために発生します。 しかし、これを回避するための小さなトリックを使うことができます。
私は時停止 (Pause Program)アクションの概要を説明した以前の投稿の一つでこれを説明しました。 もしあなたがそれを見逃したなら、ここが何をするべきかです:一度アプリケーションが停止したら、ステップイン (Step Into)やステップオーバー (Step Over)のようなステッピングアクションを実行します。 すると、値の設定 (Set Value)や式の評価 (Evaluate Expression)のような高度な機能を利用できるようになります。
今度は health
の値を設定できるはずです。
値を変更してアプリケーションを再開し、ヘルスバーに変更が表示されるかどうか確認してみてください。
したがって、関連する状態を持つオブジェクトを見つけることができました。 最低限、私たちは定期的に健康バーを手動で補充することができます。 まだ完全に抜け出すことはできないかもしれませんが、着実にそこに近づいています。
ラベルと式
今や私たちは注目するべきオブジェクトを特定したわけですから、それをマークすると便利です。 デバッグラベルに馴染みがない場合、マークされたオブジェクトは以下のように見えます:


ラベルは多くの面で有益です。 本記事の文脈においては、関連するオブジェクトをマークすることで、現在の実行コンテキストに依存せずにそれを式の評価 (Evaluate Expression)などの機能で直接使用できるようになります。
残念ながら、 _value
を直接マークすることはできませんが、それを含むオブジェクトはマークできます。
これを行うには、 health
を右クリックし、オブジェクトのマーク (Mark Object)を選択し、それに名前を付けます。


これで、ラベルが他の場所でどのように機能するかをテストできます。
式の評価 (Evaluate Expression)ダイアログを開き、 health_object_DebugLabel
を式として入力します。
見ての通り、オブジェクトは体験の開始 (Evaluate)ダイアログを通じてプログラムのどこからでもアクセスできます:


では、宇宙船の健康状態を体験の開始 (Evaluate)から変更するにはどうでしょうか?
health_object_DebugLabel._value = 100
は動作しません。
同時に、 _value
はKotlinプロパティのバッキングフィールドのように見えます。
これが真実であれば、Kotlinは対応するゲッターを生成しなければならないはずです:
health_object_DebugLabel.getValue()
体験の開始 (Evaluate)ダイアログはこれが有効なコードではないと考えていますが、とにかく試してみましょう:


この表現は現在の宇宙船の健康状態を返すので、このアプローチは機能します! 予想通り、セッターも機能します:
health_object_DebugLabel.setValue(100)
セッターを評価した後、アプリケーションを再開し、変更が反映されたことを確認しましょう。 ええと - フルヘルスバーを見ています!
式をフックする
目標に到達するための最後の一歩は、状態の変更を自動化して、 ヘルスリフィルが裏で行われ、ゲームプレイを中断なく楽しむことができるようにすることです。
これは非中断ブレークポイントを使用して行うことができます。 このタイプのブレークポイントは一般的にログ作成に使用されますが、 ログ表現は必ずしもピュアである必要はありません。 したがって、私たちはログ表現の中に望ましい副作用を導入することができます。 しかし、ソースコードがないため、このブレークポイントを設定する場所がないように見えます。
私が以前に述べたように、Java/Kotlinの標準ライブラリのソースを使用することもあるかもしれませんか? ここがアイデアです:IntelliJ IDEAとそのプラグインはJava/Kotlinで書かれており、UIフレームワークとしてSwingを使用しています。 したがって、スペースインベーダーズは確実にこれらの依存関係からコードを呼び出しています。 つまり、私たちはこれらのソースを使用してブレークポイントを設定することができます。
簡単のために、私たちはJDKのバージョンを指定しませんでした。 代わりに、IntelliJ IDEAが提案したバージョンでプロジェクトを初期化しました。 しかし、最高の結果を得るためには、あなたのプログラムを実行するのに使用されるバージョンに近いソースを使用することをお勧めします。
ブレークポイントを設定するには数多くの適切な場所があります。
私は、キーを押すたびに副作用を引き起こす java.awt.event.KeyListener::keyPressed
のメソッドブレークポイントを設定することにしました:


式を持つブレークポイントを ホットコードに設定すると、ターゲットアプリケーションの速度が大幅に低下する可能性があります。
では、自家製のIDDQDがどのように機能するか、スペースインベーダーズに戻って確認しましょう。それは機能します!

結論
この記事では、デバッガを使用してアプリケーションがどのように動作するかを見つけ、そのメモリをナビゲートし、その機能性を変更することができました、 それらをすべてアプリケーションのソースに触れることなく行いました! 私はデバッガをIDDQDと比較したことがあまりにも大胆でないことを願っています、そして、あなたがデバッガの挑戦に立ち向かうためのいくつかのテクニックを学んだことを願っています。
私は、Eugene Nizienkoにスペースインベーダーズプラグインを作成してもらい、Egor Ushakovにデバッグとプログラミングの一貫したインスピレーションを提供してもらったことに感謝します。 彼らのような人々と一緒だと、コンピュータは2倍楽しいです。
もし、次の投稿で私が取り組むべきデバッグの課題があれば、教えてください!
楽しくハッキングしましょう!