見出し画像

完全に理解した()内部解析

笑 すらも省略した形はなんらかのメソッドの呼び出しに見えてしまいます。
ある種「これは冗談だからね」と定義されたfunctionとも言えなくはないです。引数がデフォルトで '笑' なんでしょう。

本記事は内部解析をするにあたっての紆余曲折を日記のように綴っている内容です。0から1へと切り替わる瞬間とはどんなものなのかといったところに焦点を置いてくださればと思います。
内部解析の教科書的内容ではないのでご容赦ください。

3行であらすじ

・筆者はRTA走者
・なんか新技はねぇのか
・じゃぁ内部解析するしかねぇ

「そもそも」や概念の理解

筆者は内部解析知識ゼロからのスタートです。高級言語でのプログラム経験こそあれ、です。最初の理解が一番苦労するのは経験済みなので気長にやっていこうという気持ちでした。気合いを入れてくじけたらその先持ち直すのが大変なので。

データと命令

PS1ソフトをpsxfinというエミュレータで起動して
メモリの中身を直接書き換えてゲーム画面という結果が変わることは体験して理解しました。このときの筆者の理解は
メモリとは、メモリアドレスという添え字が振られた大きな配列なんだ
というものでした。そこまで踏み外してはいないと思います。
ただ、メモリアドレスによってその特性がまちまちなので呼び出された先での使い方には差があるんだなぁとぼんやり理解していました。

メモリアドレスによっての特性~値が0のときの挙動~
・アイテムの個数=>1
・装備の修正値=>0
・召喚獣呼び出しフラグ=>立っていない

まぁこの辺りはプログラマであれば特に詰まる内容ではありません。自分で作ったプログラム配下では自分でルールが決められるわけですから。
ここまでがデータとしての振る舞い。

問題は命令としての振る舞いです。
前述のデータとしての理解しかなかったので、この大きなメモリの中にどうやって命令文が書かれているのか?配列的なデータをどうやって取り出してどうすることで使えているのか?そこが分からなかったのです。
PS1のCPUであるR3000の命令一覧表とかは見つかってもその辺りの概念というものが飲み込めていませんでした。
なんかめちゃくちゃ細かーく説明しているところでは16進数で命令を書いている風味なことを説明していましたがよく分かりませんでした。それくらい、記事の内容が読み解けないほどに知識が無かったのです。

漏れ出る知識に焦点を当てる

命令文のことが分からないままなのでデータを格納しているメモリを書き換えて、どのアドレスがどの領域なのかをチェックしたり値とIDを調べたりということが続きましたがとある記事によって転機が訪れました。

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

https://cma2819.hateblo.jp/entry/2019/12/04/003438

外国の方による内部解析の解説動画を翻訳された記事です。記事の趣旨としては「解析に明るくない人にも分かる内容で説明する」というものでした。なので細かい説明は省いたり、必要な知識は結構細かく説明されたりと非常に丁寧な記事でした。
しかし
途中から急に、何の前振りもなく「レジスタ」という単語が割り込んできました。レジスタとは即席の変数のようなものですね。筆者としては直近で調べたものだったから分かったものの一般層はこのあたりでスワイプ速度が速くなることでしょう。
だが
これこそが筆者の求めていた情報だったのです。
おおよそこの手の記事というのは「分からない人向けに嚙み砕いて表現するか」「分かる者同士の情報共有として専門書のようになるか」のどちらかです。大概二極化します。紹介した記事はもちろん前者です。とはいえ記事を書かれている方は解析に明るい方なのでしょう。知識を広めたり共有することにも余念がないことでしょう楽しいことでしょう。実際筆者も得意分野の話となるとついつい高揚しがちなので気持ちはとてもよく分かります。例えば今とか。
さて
「分からない人向けに簡潔に説明している」最中、咄嗟に出てきた専門用語や知識。先ほど例に挙げた二極化の話でいくなればその中間に位置するのがこの記事だといえる、と筆者は感じました。新しい知識を仕入れて一歩踏み出したい、けれど嚙み砕きすぎだと結局のところ中身はなんなの?とつまずくし専門書のようなものは難解すぎて読み解くことができない。そんな初級の方にこそこの手の『中間』に位置する記事は知られるべきだと思います。
筆者も記事を読み進めるにつれて少しずつ理解できたのです。16進数とメモリを使ったデータと命令文の関係が。

結局のところ16進数による命令とは

つまりなんなんだと。00で最小値、ffで最大値を取るデータ群の顔しておいてなんで時には命令になるんだよと。
完全に理解した()筆者から言わせると予約語です。
プログラムを知らない人向けに言うなれば電話を掛ける際のドアタマ184です。

拝啓なら敬具のようなお約束

184をアタマにつけて電話番号を入力すると電話相手に対して、発信者の電話番号を伏せる非通知で電話を掛けることができます。そういうお約束です。だから070,080,090などの電話番号が枯渇したとしても184から始まる電話番号は新たに発行されたりしません。非通知発信との区別がつかないから。これは電話という土俵に設けられたルールなのです。
同様のことが16進数で書かれたメモリについても言えます。
16進数2桁1組が、4組だか8組だか揃ったときにフォーメーションAだったら作戦Aを実行する!みたいなノリだと思います。たぶん、きっと、おそらく。

命令文の置換は自動


まぁ便利なことにその16進数英数字のフォーメーションを読み取って作戦単位(命令文として)表示してくれるツールなんかもあるんですね。先人の知識や技術、かがくのちからってのは本当にすげー!です。
解析ツールとか逆アセンブルと呼ばれるものですね。筆者はps2disというものを使用しています。
一応逆アセンブルして目を通したことこそありましたが本当にその命令というのが組みあがっているのか?装備のデータが格納されているところにも命令が書かれてるけど?ここも実行するの?とやっぱり分からないことだらけだったんですね。とはいえ知識を得たらそれ(解釈)が合っているのか試してみたくなるものです。答え合わせでありその先へ進む為の足掛かりですからとても重要なことです。建前としてはそうですが趣味のような側面もあるので単純な知的好奇心で動いている、というのが本音でしょう。

命令を読み解いて自分のものにする

「なかなかどうして」という言葉を使ってみよう、などと新しい言葉を覚えて実際に使ってみる->失敗(経験)する->覚えるというサイクルがあるように実践するのは大切です。

「簡単そう」且つ「明らかなアドレス」

「何もしない」は例外ですがおおよその命令は何かを参照しています。【味方HPを○○】とか【△番目のアイテムを〇〇】とか。今回は命令、つまり例示したものでいう○○の部分は勿論【 】内の文章を読み解くわけです。【A2710004】みたいな英数字の羅列から。そうなると重要なのは、なるべく分かっている情報で解読内容を埋めることです。まずは概念とか読み方そのものを理解するのにわざわざ難しいところから手を着けることはありません。そういう姿勢はちっとも格好良いことありません。効率が悪いです。
簡単なものから攻めて、自分のものにし、今まで難解だったものが分かるようになる。新たな知識はこうしたことの積み上げです。
今までエミュレータを触ってきてどのアドレスに何のデータが格納されているかはなんとなく分かります。そこから「それらの内いずれかのデータを使った」「簡単そうな処理」に目をつけて解読していきます。

具体的に

・タイトルはチョコボの不思議なダンジョン2
・ターゲットにしたメモリアドレスは0x00103304 , チョコボが現在居る階層が何階なのか?が格納されている箇所
・ps2disの機能を使って、このアドレスを参照している命令を探す
・探した命令の大まかな機能を調べる
・階層が進むときの処理っぽいのを見つける(インクリメントしてるとか)
・命令内容を細かく読み解く
・前後の命令との関係性も見てみる

命令の大まかな機能

・addiu
加算や減算の命令。実際に逆解析ででてきたものは
addiu a1,a1,$0004(__00103304)
PHP風にいうなれば
$a1 = $a1 + 0004; // => 00103304
正確なことを言えばintの振る舞いで加算しつつ出来上がるのは00を補った8文字のstringとかまぁその辺の話は置いておいて。
要は(変数チックな)a1レジスタに、加算で生成した'00103304'という値(メモリアドレス)を格納しているということ。
合ってるのか?という一抹の不安を抱えつつもプログラムが読めている気はする。しかし単にアドレスを格納しているだけだと直接的な処理とは言いづらい。もっと的を射るようなクリティカルな処理を見つけて「ゲーム内の処理」を命令として見たいので今回はこの手の命令は無視しておこう。

・lbu
ロードバイトだからlbなのかな?指定したメモリアドレス内の値をレジスタに格納するというもの。
lbu v0,$0004(a0)(__00103304)
PHP風で
$v0 = $00103304;
変数名が数字だけで成立するの?とかは置いておいて。
メモリアドレスも16進数、その中に入っている値も16進数、命令文の書き方も16進数。混乱する原因はここにある。
今回の命令はメモリアドレスの中に入っている値をレジスタに格納するもの。現在の階層をレジスタに格納、だけならまだ処理の途中のような雰囲気。別のを探ろう。

・sb
ストアバイト、かな。ストアというのは書き込みを意味する。レジスタの値を指定アドレスへ書き込む。
sb s1,$0004(s3)(__00103304)
例のごとくPHP風で
$00103304 = $s1;
これではないか?チョコボの現在階層を示すアドレスの中身を書き換えているワケだ。ようやくそれらしい処理を見つけた。階層を進める処理があるとしてその処理の終端近辺の命令と言えるだろう。
懸念点としては似たような命令がもう一つあること。なんらかの例外処理なのかチョコボ用と復活ダンジョン用とで分かれているのか今一つ解にはたどりつけなかったものの、目当ての簡単そう且つアドレスのはたらきが明確なところに対する処理を見つけられた。少しこの周辺にアタリをつけて探っていこう。

目当ての命令から周辺の命令へ

先に述べたsb命令が目当てのものだった。すぐ下、すなわちsb命令を実行直後、次に実行する命令に目を付けるとほぼ同じ命令が書かれていた。
sb s1,$0005(s3)(__00103305)
アドレス00103305にはチョコボが今までの冒険で踏み入った最深階の値が格納されている。そこに対して何らかの値を書き込もうとしている。現在階層の書き込みの直後に。これはもう当たりと言って良いだろう。つまりこれは「まだ行ったことのない階層に踏み入ったとき、最深階を更新しつつ現在階も更新している処理だ」と確信した。
しかし
読み進めるにつれてその確信は早計だったと知る。早い話が条件分岐によって最深階の処理については行わない場合があるということ。ifや{  }で仕切っているのではなく
この条件を満たしたら次に実行する命令はアドレス何番からですよ
というgotoチックな書き方だったのだ。命令すらも16進数で書いているのだから当然と言えば当然か。とはいえaddiu , lbu , sbなどといった命令を記事という教科書を基に読み解くことができたので条件分岐の命令やその他周辺命令も少しずつ解読できた。やはり訳の分からない英数字の羅列から読み解こうとすると難儀だが「この辺りは階層を進めるときの処理だ」と理解した上で(身近な処理と捉えて)読み解こうとするのと進捗が全然違う。事実、「分からない」から「ほんの少し読み解くことができた」に進むことができたのだから。

喜びのツイート

https://twitter.com/141_80_call/status/1633501462063099904?s=20

最後に

推敲の為に読み直すとやっぱり自分の思いをただただ吐露しただけのものだなぁと実感。まぁ知識を与える記事ではなく変遷や過程をお届けするつもりで書いているのでそういうものとして捉えて頂ければ幸いなワケで。

ようやくここからスタートを切れそうな雰囲気がしてきた。とはいえ処理途中のレジスタの値を一々追いかけるのは大変なのでブレイクポイントを作れるエミュレータの導入なんかを最近は考えている。
あくまで内部解析を進めているのは何らかのグリッチ(RTA技、バグ技)を見つける為の手段だ。最終的な目的の為アドレスの参照先やレジスタの値をどうにか想定外のものにできないか模索を続けていく。今後はより具体的にアプローチを掛けていけることだろう。
と、信じたい。これもまた早計かもしれないからね。

この記事が気に入ったらサポートをしてみませんか?