3DS研究所

homebrew・CFW全般

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と同様にGDB3DSに接続してください。今回はブレークポイントを、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