GDBデバッグ講座(Part2)
今回はリモートでレジスタの値を書き換え、3DSの挙動を変更してみましょう。
※Part1を終えた前提で話を進めます。まだPart1を見ていない方は、先にPart1を確認してください。
https://cfw-question.hatenablog.com/entry/2020/06/05/211346
準備
前回使用したGDBTestのmain.cを以下のように書き換えてください。
#include <3ds.h> int main(int argc, char* argv[]) { gfxInitDefault(); srvPublishToSubscriber(0x204, 0); // Main loop while (aptMainLoop()) { gspWaitForVBlank(); gfxSwapBuffers(); hidScanInput(); u32 kDown = hidKeysDown(); if (kDown & KEY_START) break; } gfxExit(); return 0; }
今回注目する関数はsrvPublishToSubscriber
です。これは3DSの制御にかかわるサービス通知を送信する関数です。引数にある0x204が通知IDで、これは「HOMEボタンの押下」にあたります。つまりこのプログラムは、「画面の初期化後にHOMEボタンを押したことになる」プログラムです。言葉で説明されてもいまいちよくわからないと思うので、実際にビルドしたciaをインストールして実行してみてください。起動後になぜかHOMEボタンを押したことになっていると思います。今回はGDBで通知IDを0x202(電源ボタンの押下)に書き換え、電源ボタンを押した判定にしてみましょう。
デバッグ
ブレークポイント
まずはPart1と同様にGDBで3DSに接続してください。今回はブレークポイントを、main関数ではなくsrvPublishToSubscriber
に直接行指定してみましょう。上記のコードでは7行目にあるので、b 7
と指定します。指定できたらcontinueでブレークポイントまでを実行します。
逆アセンブル
逆アセンブルを行うことで、処理をより細かく見ることが可能です。disas
というコマンドを実行してみてください。ズラーッと出てきたその中身が逆アセンブルの結果です。矢印で示されている場所が現在の処理位置です。
おそらく矢印は、srvPublishToSubscriberではなくmov r1, #0
を示していると思います。これは引数を先にレジスタに代入しておかないと、srvPublishToSubscriberに分岐した際に引数を参照できないためです。今回は直接レジスタの値を書き換えて通知IDを変更してみましょう。
niコマンド
通知IDが既にレジスタに代入されているという状況を作るため、まずはsrvPublishToSubscriberに分岐するアドレスまでの処理を実行してみましょう。ブレークポイントをかけてもいいのですが、今回はni
コマンドを使用します。これは機械語の命令単位で処理を実行できるコマンドです。では、このni
コマンドを2回実行し、再度disas
コマンドを実行してみてください。今度はsrvPublishToSubscriberと書かれた行を矢印が示していることと思います。
レジスタ書き換え
通知IDはr0に格納されています。set $r0=0x202
と実行し、r0の値を書き換えます。
実行
これで準備は整いました。c
を実行して挙動を確認してみてください。電源ボタンを押したときの挙動になっていることと思います。
最後に
今回はGDBでのレジスタ書き換えについて解説しました。今回使用した関数はほかの通知IDも使用できます。一覧は以下のURLを確認してください。
https://www.3dbrew.org/wiki/Services#Notifications