しままのものづくり

新人エンジニアが趣味プロするところです。RTAの話もします。

RTA 1n Kagawa Onlineで実践した配信セットアップ【ミラー向け?】

こんにちは、Cmaです。

今日のお昼くらいに回ってきたこのツイートをみて、「あーオンラインイベントの配信セットアップとか知りたい人いるかなぁ」とか思ったので、この間やったRTA1nKagawa Onlineのセットアップを共有しようと思います。

はじめに

本題に入る前に。今回の配信セットアップについては、配信管理ボランティアとして参加したRTA in Japan 3 Onlineを大いに参考にしています。 RTA 1n Kagawa Onlineのセットアップを紹介しますが、本イベントのセットアップが特段新規性のあるものではないことは予めご了承ください。

RTA 1n Kagawa Online とは

RTA 1n Kagawa Online」は、2020/03/07と2020/03/08の両日に渡って行われた、RTAのオンラインマラソンイベントです。 運営は私含めて5人、私Cmaは一応主催として色々やってました。技術的なところだと、Miraiさんに多大なるご協力をいただき、自分のやりたいことを実現させてもらっていました。 もちろん私が技術的なところに力を注げたのは他の運営陣のおかげです。本当にありがたかったです。

イベントの特徴としては、

  • ゲームをプレイするRTAプレイヤー(以下「走者」)が自宅から配信している映像、音声をミラー配信する
  • イベントの制約として同時にプレイする走者は2人までとした
  • 走者以外にも解説・ガヤとして話す人(以下「解説」)が任意の人数いる(今回は最大2人でした)
  • 表示する画面比は(当初7種類の予定だったが)9種類。画面比の違いと画面数の違いにそれぞれ対応する
  • セットアップ時間(RTA間の準備時間)には専用の画面を表示しつつ、BGMを流しておく

技術的にフォーカスされる部分はこの辺りかと思います。

この内容を、RTA 1n Kagawa Onlineでは「配信PCを操作して画面・音声を作る人」と「走者の配信やDiscordを予め見聞きしておいて事前準備をしておく人」の2人で回してました。 セットアップ時間は10分でしたが、イベントとしてはオンスケだったので無理なスケジュールではなかったです。運営陣の皆さん負担かけさせてたらごめんなさい。

こんなイベントで、どんな準備をして、どんなセットアップで進行していたかを書ければいいなと思います。

配信環境について

配信PCは、日頃からゼルダRTA交流会等で積極的にミラー配信を行ってくれているMiraiさんのPCをお借りしました。 お借りするとは言っても物理的に拝借するわけではなく、遠隔操作できる仕組みを利用して、私もMiraiさんのPCを使えるようにしただけです。

遠隔操作については、実現方法として2つ候補がありました。

  1. Windows標準のリモートデスクトップ機能
  2. AnyDeskを利用する方法

当初はWindowsリモートデスクトップを利用する方向で進めていたのですが、Pro Editionでは1ユーザーに1人しか同時にログインできない=1人が操作している間は他の人は画面すら見れないということで、リモートデスクトップでの実現は難しいね、という結論に至りました。そのため、RTA 1n Kagawa OnlineではAnyDeskを用いた方法で実現しました。AnyDeskの細かい使い方は各自ググっていただければすぐ出てきます。特別な使い方はしていないのでここでは省略します。

anydesk.com

配信PCの遠隔操作の際には、新規のWindowsユーザーを準備し、そのユーザーで全ての操作を行いました。

これは、Administrator権限を持たせないことでセキュリティ的なリスク(私がMiraiさんのPCを悪用しようとするリスク)を避けること、イベント後にユーザーを消去することで使い捨てが効くことが大きな理由です。 ボランティア等を募って、より利用者が多くなるような場合は特に注意した方が良い点だと思います。

配信レイアウトについて

f:id:cma2819:20200316185324p:plain
配信レイアウト - セットアップ

f:id:cma2819:20200316185158p:plain
配信レイアウト - ゲーム画面

配信レイアウトは、おなじみNodeCGで実現しました。NodeCGについて細かいことは省略しますが、Webの仕組みで動的な配信オーバーレイを実装できるフレームワークです。 今回はデザインをkenさんにお願いして、テキストや画面の位置の配置・配色などもろもろを定義していただき、NodeCG上のWebページにCSSで実装しています。 これに関してはまた別に機会に。気になる方はGitHubソースコードがあるのでご参照ください。

twitter.com

github.com

配信PC上では、NodeCGで作られたオーバーレイをOBS内のブラウザで取り込み、画面全体に表示します。 あとはオーバーレイに空いているゲーム画面のエリアに、走者が配信しているゲームの映像を当てはめることで、映像は完成します。

NodeCGのブラウザソースは、以下のような設定で取り込みます。

f:id:cma2819:20200316193849p:plain
ブラウザソース - NodeCG

「カスタムCSS」は、意図しないスタイルが効くのを避けるためにデフォルトのものを消去しておきます。また、表示していない画面のブラウザが動くことで配信PCやOBSに負荷がかかるのを避けるために「表示されていないときにソースをシャットダウン」を有効にします。

逆に「シーンがアクティブになったときにブラウザの表示を更新」はチェックを外しておきます。最初はチェックした状態で進めていたのですが、OBSでトランジションした際に画面の読み込みが走ってしまうので解除しました。これをNodeCGの画面数分用意しますが、今回はOBSの1シーンに全てのNodeCGソースを作成しておきました。

f:id:cma2819:20200316200943p:plain
ブラウザソースだけを保存したOBSシーン

上述した設定を反映したNodeCGソースを、とりあえず1つのシーンに集めておきます。実際にレイアウトを作るシーンは、このソースを「参照」するようにします。 各NodeCG画面ごとにシーンは作成しますが、仮に配信用のシーンを消してしまった場合も、元々のブラウザソースは残っているので、リカバリが容易かつ設定忘れを防ぐことができます。

f:id:cma2819:20200316202312p:plain
OBS上のシーン・ソース

ゲーム画面(映像)

続いて、走者が配信するゲーム画面の取り込みです。

走者の皆さまには、配信サイト「Mixer」での配信をお願いしました。チャンネルは4つ用意し、スケジュール順に各走者に割り振ります。4チャンネル用意したのは、イベントの同時プレイ数が最大2つであるためレースが連続しても事前に準備ができるようにするためです。同時画面数がより多い場合はチャンネル数を増やすか、スケジュールで前後の画面数を調整する必要があります。

mixer.com

オンラインイベントではよく使われるMixerですが、MixerにはFTL(Faster than Light)という配信プロトコルがあり、これによって1秒未満のラグで配信することができます。詳しくは後述しますが、ラグをできるだけ少ない状態で配信したい場合には、このFTL方式での配信を行うようにしました。

ただしFTL方式は配信側への負荷が大きいので、配信テストを行った上で問題なく配信できるかを確かめておいた方が良いです。

OBSでの取り込みですが、NodeCGソースと同様にOBS内のブラウザを利用します。Mixer取り込み時のブラウザソースの設定は以下の通りです。

f:id:cma2819:20200316205402p:plain
ブラウザソース - Mixer

今回はカスタムCSSは特に変更していません。代わりに「OBSに音声を再ルーティングする」にチェックを入れています。 これによって、各ブラウザの音声ソースがOBSの音声ミキサーに割り当てられます。各ゲーム画面の音声をOBSで操作するためのものです。詳細は後述します。

このままではブラウザにMixerのチャンネルページが表示されているだけなので、ゲーム画面のみを取り込むようにします。OBSのブラウザを操作するために、ブラウザソース上で右クリックして「対話」ウィンドウを開きます。

f:id:cma2819:20200316211026p:plain
OBSの対話ウィンドウ

このウィンドウで配信を最大化すると、OBSのブラウザでゲーム画面のみを表示することができます。音量もブラウザで変更する必要がないように、最大にしておきます。

f:id:cma2819:20200316211704p:plain

ゲーム画面の整形ですが、余分な外枠はOBSの「クロップ/パッド」フィルタで取り除きます。これでまず大本のゲーム画面のクロップを行います。

f:id:cma2819:20200316212917p:plain

通常のゲームであればこれをうまいことNodeCGオーバーレイの裏に配置すればOKです。このクロップフィルタは元ソースに残るので、ゲーム切り替えの際にリセットするのをお忘れなく。

DS等の複数画面を表示するゲームでは、同じ画面ソースを複製・それぞれに変換でクロップを行うなどが必要になります。うまいことやってNodeCGの画面エリアにはめ込んで映像の準備は完了です。

音声について

セットアップ中に配信に乗せずにできるだけ準備し、音声が調整された状態でゲーム画面に切り替えられるように、音声については極力OBSから独立させます。

管理したい音声は、 - OBSから出力されるゲーム音 - Discordから出力される通話音 - Spotifyから出力されるBGM

の3つです。

音声の管理には、「Voicemeeter banana」を利用しました。 PC上で動かせる仮想ミキサ―で、PCに入力されている物理デバイスのほか、2つの仮想デバイスを設定することができます。ただし、今回管理したい音声は全てPC内のソフトウェアからの音声だったので、仮想デバイス2つでは足りません。ソフトウェアの音声出力を仮想の物理デバイスに見立てるために、「VB-Cable」を利用します。

www.vb-audio.com

今回はメインの出力音声であるOBSからのゲーム音を仮想の物理デバイスとして、Voicemeeter bananaに入力しました。OBSの音声の「モニタリングデバイス」を、VB-Cableで追加した「Cable Input」にします。

f:id:cma2819:20200316220817p:plain

Mixerからの出力をOBSからの出力として利用するために、OBSのオーディオ設定を変更します。Mixerソースが配信画面に乗っている状態で、「オーディオの詳細プロパティ」から設定を変更します。

「音声モニタリング」を全て「モニターのみ(出力はミュート)」とします。モニターとして音声が再生されるので、Mixerからの音声はVoicemeeter bananaに入ります。配信への出力はミュートされるので、ブラウザソースから直接OBSを介して配信に乗るようなことはありません。レース時のゲーム音声のミュート/ミュート解除や、ゲーム音声同士のバランス調整はOBSで行いつつ、全体のバランスをVoicemeeter bananaで調整する、といった形です。

f:id:cma2819:20200316221310p:plain

あとは、複数人の走者や解説とのDiscord通話の音声を適宜調整します。走者のプレイと音声、また解説にラグが起きないようにMixerのFTL配信を利用して、自然に通話ができ、配信に乗るようにしてあげます。

複数人の声を配信に乗せる場合はDiscordを利用するので、上記のようなセットアップになります。走者の音声のみの場合はもっと単純で、Mixerに走者の音声を乗せてしまいます。Mixer内であらかじめゲーム音とのバランスを調整しておき、Voicemeeter bananaでの調整は行いません。この場合はMixer配信はFTLである必要はありません。RTA 1n Kagawa Onlineでも、走者のみの音声であればFTLを避けるようにしました。

懸念点

今回のセットアップでの懸念点もあります。

OBSからMixerの音声を取ってくるには、配信にMixer画面が乗っている状態でないといけないことです。そのため、ゲーム画面に配信を切り替える前にゲーム音を聞くことができませんでした。

これに気づいたのは実は最初のゲームの直前だったのですが、対応としては、ゲーム画面にトランジションした後、Voicemeeter bananaのゲーム音量を少しずつ上げていくことでうまいところを探すようにしました。Discordを使わない場合は事前にMixer単体で確認できるのでそこまで大きな運用変更ではありませんでしたが、本当なら予めMixerの音声を聞いておきたいところでした。

他には、Discord通話を行う走者の配信では、MixerにDiscord音声を乗せないように対応してもらう必要があったことです。これに関しては私の伝達不足で、ハード的に難しいという方もいらっしゃいました。これは他の運営の方の起点で、Mixer配信にDiscordの音声を集めて予め調整をしっかりしてもらう等で対応してもらいました。配信テストの段階で、この辺りは周知できるとより良いと思います。

終わりに

今のところ思いつくのがこの程度なので、ここまでとしたいと思います。もし気になることがあればコメントなりTwitterでご連絡いただければ適宜追記したいと思います。

元ネタがオンラインイベントなので、今回の話はオンラインイベントが対象でした。オンラインイベントは恐らく皆さんが思っているより手軽なものなので、これを参考に運営してみてはいかがでしょう。

また、今回はオンラインイベントの話でしたが、オフラインイベントの技術的な話が気になる方は、Hoishinさんの「RTA in Japanを支える技術 ~オーディオ編~」が参考になると思います。併せてどうぞ。

hoishin.hatenablog.com

以上です。ではまたどこかで。

【翻訳記事】時のオカリナにおける任意コード実行について

はじめに

本記事は、先日実現された「ゼルダの伝説時のオカリナ」における任意コード実行(Arbitrary Code Execution, 通称ACE)についての解説記事です。 本記事の内容はFig氏による解説動画をもとにしています。元動画と異なる点についてはご指摘いただけると幸いです。

youtu.be

任意コード実行という性質上ゲーム内部のシステムに関わる内容が含まれ、専門的な知識が必要となる場面もありますが、できるだけわかりやすく解説できればと思います。

「メモリ」と「アドレス」について

前提知識として、任意コード実行において頻出となる単語「メモリ」と「アドレス」について説明します。

メモリ

広義にはコンピュータにおいてデータを格納するためのものですが、ここではニンテンドー64内の、ゲームデータを格納する入れ物を指します。

ゲームが動作するには、ゲームソフト(ROM)の中のデータを読み込み、ゲーム本体で使えるように保持しておく必要があります。 ここでいう「データ」は、リンクや敵キャラのステータスはもちろん、画面に表示されているグラフィックス自体、ゲームが動作するための処理プログラムやそのための数値など様々です。

メモリにはデータを格納できる量が決まっており、必要に応じてデータをメモリに格納したり、メモリからデータを解放したりしてうまくやり繰りしています。 想定されていないゲームの動作によってメモリの限界量を超えるデータを読み込もうとすると、フリーズしてしまいます。

ニンテンドー64の中にゲームのデータを入れるための大きな箱があり、必要なものを都度出し入れしているのをイメージしていただいてよいかと思います。

アドレス

メモリはデータを詰めるための大きな箱ですが、ゲーム自体はメモリの中のどこに欲しいデータがあるかを理解する必要があります。

そこでメモリには細かく区間分けがされており、区間ごとに住所のような一意の数値が割り振られています。この住所が「アドレス」と呼ばれるものです。 ゲームはメモリの中のデータを読み込む際に、このアドレスを使ってデータの位置を特定します。かなりシンプルな例を挙げると、リンクの残りハート数をアドレス12に入れておくと決めておけば、ダメージを受けたり回復するたびにアドレス12の値を変更するような処理が動き、リンクの残りハートを管理することができます。

今回の任意コード実行においては、このアドレスを利用する場面が多く出てきます。メモリ内のデータの場所を指す番号・住所とご理解ください。

また、あるアドレスのデータをゲーム側の処理で使っていることを、「アドレスを参照する」といった言い方で表現します。

静的なメモリと動的なメモリ

本ゲームで利用しているメモリ空間として、静的なメモリと動的なメモリというものがあります。

静的なメモリというのは、格納するデータが予め定められているメモリ空間です。イメージとしては、例えばリンクのハート数等のいかなる状況でも利用するものは静的なメモリに保持されるでしょう。

対して動的なメモリというのは、ゲームの状況に応じて必要になったデータを随時保持していくメモリ空間です。例えばダンジョン内のギミックやオブジェクトはリンクがその部屋に入ったときにのみ必要になるため、動的なメモリに必要になったときにだけ保持されます。

ファイルネーム設定

f:id:cma2819:20191118005206p:plain
ファイルネーム設定

動画冒頭ではまず、ファイルネームの設定を行っています。設定したファイルネームが今後の任意コード実行に重要な役割を持つのですが、ここではファイルネームを設定する先のアドレスその値だけ理解していただければOKです。

動画下に表示されている0x8011A5F4 08 01 28 50 - A3 B6 B6 3Dは64に保存されているメモリを可視化したもので、アドレス8011A5F40xは16進数であることを表しており、数値としての意味はありません)で、08 01 28 50 - A3 B6 B6 3Dです。ゲーム上、どのようなデータもメモリには16進数で格納されます。重要なのは、この数値をプレイヤーが自由に操作できるということです。

SRM(Stale Reference Manipulation)について概要

f:id:cma2819:20191118010700p:plain
Stale Reference Manipulation

続いて、Stale Reference Manipulation(以下SRM)というバグ技について触れています。SRMも最近見つかったバグ技で、任意コード実行を実現する大きな要素となりました。

SRMについては以下のドキュメントで解説していますので、ご参照いただければと思いますが、動画と同様に概要を解説いたします。

https://gist.github.com/cma2819/34c9b00f12573316f9b6becc2c0ae948

アクター

f:id:cma2819:20191118011414p:plain
ツボを運ぶ

時のオカリナを構成する様々なオブジェクトは、「アクター」と呼ばれています。それぞれのアクターの情報はゲームにロードされたときに動的メモリに保持され、位置情報やそのアクターの状態がデータとして格納されています。動画中でリンクがツボを持ち上げるとメモリ内のデータが書き換わりますが、リンクがツボを動かすことでツボのアクター情報が書き換わっているためです。

リンクがアクターを持ち上げる際、ゲーム内ではどのアクターを持ち上げているかをアドレスで管理しています。リンクが動くのに合わせて参照先のアドレスの位置情報のデータを変更することで、リンクに合わせてアクターも動く、というわけです。

f:id:cma2819:20191118014631p:plain
アクターの参照

参照の保持

ダンジョンは複数の部屋で構成されており、その部屋はロードゾーンで仕切られています。リンクが何かを持っている状態でロードゾーンを跨いでも、アクターごと次の部屋に保持されます。

しかし、バグ技を利用したり少し工夫をすることで、アクターは次の部屋に保持することなく、リンクがアクターを持っているという状態だけをキープすることができます。

その状態では、リンクはまだアクターを持っていることになっているので、元々持っていたアクターのアドレスのデータを常に更新しつづけます。そのアドレスに入っていた、元々持っていたアクターは動的メモリから解放され、次の部屋のアクターが新たにロードされます。すると、新たにロードされた別のアクターの情報を書き換えることができます。

f:id:cma2819:20191118021619p:plain
参照を保持することで他のアクターのデータを書き換える

このようにして、動的メモリを操作して、参照の保持を利用して関係ないアクターの情報を書き換えてしまうのがSRMというバグ技です。

実践デモ

動画内では6分ごろになりますが、ここから任意コード実行の実践になります。記事投稿時点では、任意コード実行はゴロンシティでのみ実現しています。

SRMを発動させ、ツボのアクターを配置する

f:id:cma2819:20191118214501p:plain
ボムチュウを利用して異空間をホバリング

任意コード実行の準備として、さっそくSRMを発動させます。ネールの愛やバクダン、ボムチュウを利用して異空間を動き回っていますが、これはSRMのためのセットアップです。記事投稿時点ではこの方法も改善されているため詳細なやり方は割愛します。

まず異空間から再度中央の部屋に戻ってくることで中央の部屋のアクターをロードしなおすことで、持ち運び可能なアクターであるツボが読み込まれるアドレスを操作しています。ツボを利用してダルニアの部屋にスーパースライドし、ダルニアの部屋でスライドを解除するとツボの参照を保持したままツボのアクターを解放します。この状態では、リンクが持ち上げているツボのグラフィックが表示されなくなります。

さらに持ち上げたツボの参照を保持した後にも、岩迷路の部屋に入ってから中央の部屋に戻ることで再度部屋の情報をロードしなおし、動的メモリの内容を操作します。最終的にツボの角度情報のアドレスが、SRM後に任意コード実行に利用可能なアドレスになるように操作することがセットアップの目的です。

結果的にはアドレス801FA0B0の中にツボの角度情報が保持されるように調整します。アドレス801FA0B0の値は80 1F 51 50 - 80 1F 8A 08となり、そのうち51 50の部分はツボの角度情報です。

f:id:cma2819:20191118223049p:plain
SRMしたツボの角度を操作した結果

パチンコアクターの調整(パチンコ玉の発射角度)

続いて、パチンコのアクターを利用した調整を行います。パチンコのアクターも動的なメモリ空間を使うものですが、どのアドレスにどのアクターが展開されるかはここまでのセットアップで固定されています。 そのため、パチンコのアクターに含まれる様々なデータもどのアドレスに展開されるかが確定します。今回使うデータはアドレス801F5150に格納されるパチンコ玉の発射角度です。

f:id:cma2819:20191120200346p:plain
パチンコの発射角度を調整

パチンコ玉を発射するX角度とY角度を調整して、アドレス801F5150の1~8桁目を08 07 21 2Dに調整します。 動画ではメモリの実値をみながらなのでちょうどよい角度に調整できていますが、非常に細かい単位での調整です。このパチンコアクターの情報は、リンクがパチンコを再度使うまで維持されるので、次にパチンコを使うまでこのアドレスの値は変わりません。

3Pコントローラの入力

f:id:cma2819:20191120202320p:plain
3Pコントローラの入力を利用

ここまではアクター情報を操作していましたが、次に調整する値は特殊です。

時のオカリナは1Pコントローラの入力を利用するゲームですが、ゲーム内部では3Pコントローラの入力も受け付けています。任意コード実行のためのメモリ操作として、3Pコントローラの入力を記録するメモリを利用します。

3Pコントローラの入力値はアドレス801C84E4に記録されます。スティックの入力も固定しなければいけない上に、通常のコントローラでは届かない範囲までスティック入力をしなければいけません。 動画ではエミュレータを使って、ゲーム内の数値単位に入力しています。スティックはX軸105、Y軸125、加えて十字キー上とCボタン下を入力した状態にしておきます。

3Pコントローラの入力によって、アドレス80AC84E4の値を08 04 69 7D - 00 00 08 04に調整します。

任意コードの実行

ここまでで任意コード実行の準備が整いました。ダルニアとの会話を引き金に、準備した任意コードを実行します。

f:id:cma2819:20191120204429p:plain
フックショットを対象とした任意コード実行

今回実行する任意コードは、「所持しているフックショットを消す」というコードです。ここではまだポーズ画面にフックショットがあることがわかります。フックショットの所持状態は動画でも表示されているアドレス8011A64Dの1, 2桁目のメモリに記録されています。

任意コードの実行には、剣を取り出している状態で、Z注目しながらダルニアに話しかけます。ダルニアに話しかけると、先ほどまで0Aだった値がFFとなります。任意コード実行の副作用として、リンクやナビィの状態が変化します。これは操作キャラであるリンクの内部ステータスがダークリンクのものになっており、自分をZ注目できたり、ナビィの見た目がおかしかったりします。

実行したコードはフックショットを消すという実にシンプルなものであり、副作用もありますが、ゲーム内の操作で任意のコードを実行することができました。

f:id:cma2819:20191120215034p:plain
任意コード実行によって消滅したフックショット

任意コード実行に関しての詳細

それでは、ゲーム内部ではどのような処理が行われて任意コード実行が可能になっているのかをご紹介していきます。

動画で表示されているウィンドウについて

f:id:cma2819:20191201200033p:plain
ゲーム内部のメモリを表示するウィンドウ

内部処理の解説のために、ツールを利用してゲーム内のメモリや記憶領域の情報を表示しています。画面左下の表では、左側のアドレスに対して実際に格納されている値が右側に表示されています。ここまでで解説しているアドレス〇〇に値△△が格納されている、といった情報が一覧表示されています。

中央に表示されているウィンドウは、64内部で保持されている、CPU(コンピュータの構成要素のうち、計算を司る部分)への命令を一覧表示したものです。ゲーム内での様々な処理は、「命令」という単位で実現されています。この命令は、あるメモリに値を足すであったり、処理するアドレスをいくつに飛ばすのような単純なものです。これらの命令を瞬間的・連続的に一気に実行することでゲームの動きが実現されています。

これらの命令の内容も専用のメモリ領域に保持されています。命令の内容は他のメモリ領域と同様に16進数で表現できる数値ですが、64はこの数値を決まったルールに従って解釈することで命令を理解し、その命令を実行しています。中央のウィンドウでは、メモリ内の数値を同じルールに従って解釈し、より人にとって読みやすい形で表示しています。Command列では命令の種類(加算、ジャンプ、分岐など)、Parameters列は命令に渡す数値やアドレス情報になっています。

右部に表示されているCPU General Purpose Registersは、CPUが命令を処理する際に利用する数値情報を保存する領域ですが、今までのメモリとは異なるものです。CPUの処理を行うための専用領域と考えていただいてOKです。ATT0のような固有の番号で管理、参照します。

SRMが命令の呼び出し先を捻じ曲げる

ここからは実際の動きに関する解説です。

ダルニアに話しかける際、ゲーム内部ではどのようなアイテムを手に持っているか(あるいは何も持っていないか)によって、異なるアドレスの命令を実行しようとします。剣を手に持っている場合、アドレス801FA0B0に格納されている値80 1F 8A 08を実行しようとします。

実際の命令の動きを見てみると、

  1. ダルニアに話しかけた後801F8970の命令でレジスタT680 1F 8A 08が保存される
  2. 801F8974の命令でレジスタT6に保存されているアドレスへのジャンプが行われる
  3. 命令が`80 1F 8A 08'に飛ぶ となっているのがわかります。これが通常の動きです。

SRMを利用してメモリを書き換えると事情が変わります。今回はツボを利用したSRMで、アドレス801FA0B0の値を書き換えています。通常80 1F 8A 08だった値は80 1F 51 50となっているため、処理は以下のように変わります。

  1. ダルニアに話しかけた後801F8970の命令でレジスタT680 1F 51 50が保存される
  2. 801F8974の命令でレジスタT6に保存されているアドレスへのジャンプが行われる
  3. 命令が80 1F 51 50に飛ぶ

SRMによって、呼び出す命令の参照先が変化したことがわかります。これが、SRMが任意コード実行を可能にした所以です。

f:id:cma2819:20191201204748p:plain
SRMによってジャンプ先アドレスが変わっている

ファイルネームの値を「任意コード」として読み込むまで

命令を操作することで801F5150というアドレスを命令として読み込ませることができたので、ここから任意コードの実行を目指します。SRMによって書き換えることができたのはアドレスの後半部分51 50のみであるため、ここから任意コード実行を行うまでにも様々なメモリ操作が必要になります。

ジャンプした先のアドレス801F5150付近の情報は、ゲームにプログラミングされている命令コードではなく、ゲーム内のアクターデータを保持する動的メモリ空間です。通常はこれらの領域を命令として読み込むことはありません。しかし前述したとおり、命令コードであろうと何であろうとメモリの中には16進数の値が入っているだけなので、ゲームはそれぞれの値を命令として解釈しようとします。

そしてアドレス801F5150は、セットアップ内でパチンコアクターのXY角度を格納したメモリです。パチンコ弾の発射角度を利用して、アドレス801F5150の値は08 07 21 2Dになっています。

この08 07 21 2Dを命令として解釈すると、「アドレス801C84B4にジャンプ」という命令になります。ジャンプ命令は次の命令を処理した後に指定されたアドレスの命令に飛ばしますが、今回の801F5150の次の命令801F5154でリンクがダークリンク状態になる副作用が発生しています。

アドレス801F5154の命令はレジスタS2の値のアドレス(801DAA30)にレジスタF13の値をコピーする」というものです。これによって、操作キャラであるリンクのアクター情報のうち「アクタータイプ」と呼ばれているものが書き換わり、操作キャラのリンクがさもダークリンクであるような状態になってしまいます。この状態ではマップを移動するロードゾーンが無効化されてしまうため、セーブリセットによってこの状態をリセットすることになります。

f:id:cma2819:20191201214058p:plain
パチンコ弾で決めたアドレスへのジャンプ後

ジャンプ命令によって、アドレス801C84B4に飛びます。このアドレスのメモリはコントローラによる入力を記憶するメモリ空間です。

801C84B4801C84B8の値は、ダルニアに話しかける際の1Pコントローラの入力が影響しています。ダルニアに話しかける際の入力はZ(L)トリガーとAボタンが入力されている状態でしたが、この入力値を命令として解釈すると、「レジスタR0の値下2桁を特定のメモリに代入」「レジスタS4レジスタR0の値(0)+レジスタR0の値(0)にする」となっています。これらはゲームの処理に影響しない無害な命令のため、問題なく命令が進みます。

その後はNOP「なにもしない」命令が続きますが、3Pコントローラの入力値のアドレス801C84E4、のメモリに到達します。3Pコントローラの入力を細かく調整したことで、アドレス801C84E4の値が08 04 69 7Dになっています。これは命令として8011A5F4にジャンプ」に解釈されます。次の命令もコントローラの入力によるものですが、これも無害な命令のため問題なくジャンプが行われます。

さて、ここでジャンプする先の8011A5F4ですが、動画冒頭に設定したファイルネームの値が格納されているメモリがまさしく8011A5F4です。ファイルネームは2命令分の値が格納できる長さを持っているため、ファイルネームで2つの命令を自由に設定できるということです。

ファイルネームによる2つの命令

f:id:cma2819:20191201221632p:plain
ファイルネームがそのまま命令コードになる

ファイルネームによって設定したメモリの値は以下の2つです。

  • 8011A5F408 01 28 50(81まば)
  • 8011A5F8A3 B6 B6 3D(べLLっ)

この2つを命令として解釈すると以下のようになります。

  • 8011A5F4:「アドレス8004A140にジャンプ」
  • 8011A5F8:「レジスタS6の値下2桁(FF)を所持アイテムのフックショット情報のメモリに代入」

1つめのジャンプ命令は、任意コード実行後に、その先に続くメモリを読み込ませないための処理です。これによって、フリーズを回避して処理を戻すことができる命令のアドレスにジャンプさせます。

2つめの命令が、今回実現した「フックショットを消す」コード本体です。ファイルネームの後半部分が任意コードの実際の中身となっています。

後は今まで通り、2つめの命令を実行したあとにアドレス8004A140にジャンプして、任意コード自体は完了します。

後片付け

ここまで散々命令コードを操作してきましたが、正常に処理を戻してあげないとゲームが予期せぬ動作によってフリーズしてしまいます。ここからは、正常にゲームを再開させるための後処理になります。

ジャンプ命令で飛んだ先のアドレス8004A140には、想定と異なる動作をしたゲームの処理を復帰させるコードを呼び出す処理が定義されています。これは、任意コード等ではなく、ゲームにプログラミングされているものです。これを利用して、SRMで書き換わった命令コードのデータも元に戻した上で元々の命令呼び出しに復帰させ、ゲームをフリーズさせることなく任意コード実行を終了させます。

f:id:cma2819:20191201231038p:plain
全体イメージ(動画本編で見ると非常にわかりやすい)

おまけ

動画では、フックショット消去以外にも複数の任意コード実行を紹介しています。

  • 81まばELLサ:「オカリナ」画面欄がすべて埋まる
  • 81まばべEee:言語が海外版仕様になる(Aアクションもよく見たら英語)
  • 81まばプELら:リンクの服が青色+ホバーブーツみたいに滑る+盾が上下逆さま
  • 81まばべHLり:ミラーシールド装備
  • 81まばべLLら:リンクの服が黒、リンクが一切落下しない(Figさん大好きモード)

おわりに

今回はFigさんによる解説動画ですが、SRMの発展、任意コード実行には多くのプレイヤーの尽力がありました。また、将来これがRTAで利用できるかどうかはまだわかりませんが、時オカの歴史において非常に大きな出来事であったことには変わりありません。

日本の方向けということで日本語解説を書かせていただきましたが、特にMIPS関連では不足している知識もあり、理解できていない部分があるかもしれません。お気軽にこちらへのコメントやTwitter(@cma2819)でご指摘、ご質問いただければ幸いです。

2019年はゼルダRTAが爆発的に進化した年になりました。来年にも期待ですね。

以上

2018年の振り返り

明けましておめでとうございます。Cmaです。

2018年も色々ありましたね。僕にとっても色々なことがあった良い一年でした。せっかくなのでまとめてみたいと思います。

時オカRTAの話

一年間ずっと時オカしてました。ブレないところは自信があります。 日頃の記録狙いはそこまで語らないとしても、今年は初めて100%RTAをやりましたね。時オカバグ有の濃度で4時間越えのカテゴリはかなり重かったです。 平日通す時間なんてないので、土日のどっちかを一日潰して通し、ダメだったら翌週、また通してダメだったら翌週・・・みたいな生活がしばらく続いていました。 正直あの期間はだいぶしんどかったですが、当時世界5位(現6位)の記録が出せたのは良かったなと。そして色々あって、100%からは離れるわけですが。

そして時は10月、とよまなさんに声をかけていただき、「RTA解体ショー」という企画でお話する機会を設けていただきました。 RTAを知らない人に向けて、時オカRTAの魅力・面白さを伝えるということで、結構気合い入れて頑張った記憶はあります。 その時にとよまなさんと色々話をして、RTAを自己満足的にやるだけでなく、パフォーマンス的な側面やイベント映えみたいなものを意識するようになりました。 2019年はイベントにおけるRTAを先導していけるように動いていけたらいいなと思っています。

2018年で一番大きな成果といえば、AllDungeonsでWRを取ることができて、初めてメインカテゴリで世界記録保持者になることができました。 今でもあの短い期間で、元世界に1分半以上差つけて記録を出せたのはスゲェなと思います。いやーびっくりびっくり。

ニコニコに解説付きで動画上げているので、興味ある方は見てくださいね。

イベントの話(NodeCGとか)

興味はあったものの動くことができていなかった、配信レイアウトの作成にようやく手を出すことができました。

本当に1ミリも知らないところから、Hoishinさんに色々ご教示いただいて「NodeCG」というものを知り、お勉強し始めました。 NodeCGを知ったのはこのタイミングですが、プログラミング自体の知識はあったことと、Node.jsも触っていた(Electronとか)ので、割ととっつきやすかったです。 作ったモノを活かしてもらってるのは、隔週くらいでやってる「ゼルダ交流会」と、とよまなさん主催の「C4RUN RTAリレー」ですかね。 もっと色んなところでNodeCG使ってほしいなと思うので、RTAイベントの主催の方とかから声かけてもらえたらいいなとか思ってます。

前述のC4RUN RTAリレーでは、C4LANというLANパーティ(大きな会場にゲーマーが集まって各々ゲームをプレイしたり、ステージでパフォーマンスがあったりする)イベントの一角でRTAをやってみました。 RTA in Japanでも一度同じような試みがあったのですが、今回はとよまなさんの主催です。

NodeCGの操作もあったので、若干運営寄りの立ち回りをしていましたが、かなり楽しかったです。あんな感じで、少人数でも集まってリレーするのは楽しいですね。 ちなみにC4LANの時に初めてHoishinさんとお話ししました。配信レイアウト褒めてもらって嬉しかったです。精進します。

RTA in Japan3の話

あまり多くは語るべきじゃないですが、申請していた時オカ100%は落選しました。どんまい。

解説するものも特になかったので、今回は完全に観戦勢でした。 現地で色々見てましたが楽しかったですね。内藤九段や15分チャレンジトーナメントとか、RTACheckerの宣伝ツイートが配信画面に載るとか、色々ありました。 プレイヤーの人ともお話しして、次回は一緒に出ましょう!とか、そんな話をしました。全力は尽くします。

次回の話ですが、出るとしたら時オカです。実は今回の15分チャレンジにお声かけはいただいていて、お断りしちゃった背景があるのですが、見ててすごく楽しそうだったので次に企画枠の話があったら参加しようとは思いますが、走者としての申請は時オカ以外考えてません。 というか時オカ100%をあの場でやりたい。観衆の皆様に見せたいというのが消えないので、時オカ100%で出れるまでは申請し続けたいと思います。 (まぁそれでまた落ちて凹んでモチベーション持っていかれてもアレなので、あまりこだわりすぎないのも必要ですね)

なんにせよ、この一年でRTAを「魅せる」ことにかなり魅力を感じているので、RTA in Japanに限らずイベントに出れる機会があればしゃしゃり出たいと思ってます。 お声かけも待ってます。

まとめ

書いてるうちにまとまらなくなってきましたが、人に恵まれ、機会に恵まれ、色々なことができた一年だったと思います。特に下半期が怒涛ですね。

2019年はさらに目立つようなことが増えると思います。具体的には、ゼルダRTAのオフラインイベントが直近で動かせそうです。

あと今年は、ブログで色々発信していこうと思います。。。(一年くらい使ってなかったよここ)

なんか語り忘れたことがあったら追記します。 2019年もよろしくお願いいたします。