[IPSwitch] 誰でもできるコード開発 #2

はじめに

今回の内容は以下の記事の続きになります。

この記事を読むにあたって必ず目を通して理解しておいてください。

0以外の値に上書きしたい

さて、前回はスペシャルコストを0にするコードについて学びました。

スペシャルコストを決定する関数はVer4.7.0においてはloc_847A0であり、それは以下のアセンブラで与えられました。

loc_847A0
LDR             X1, [SP,#0x6C0+var_660]
ADRP            X2, #aSpecialcost@PAGE ; "SpecialCost"
SUB             X0, X29, #-var_C8
ADD             X2, X2, #aSpecialcost@PAGEOFF ; "SpecialCost"
STR             X19, [SP,#0x6C0+var_468]
BL              sub_19A32AC

このとき、BL sub_19A32ACというのはBLが返り値を持つサブルーチンであり、単にX1レジスタに保存されているアドレスに値を入れればその値がまさにスペシャルコストになりました。

つまり、sub_19A32ACには分岐する必要がなかったのでここの命令を上書きしてしまって良かったわけです。

loc_847A0
LDR             X1, [SP,#0x6C0+var_660]
ADRP            X2, #aSpecialcost@PAGE ; "SpecialCost"
SUB             X0, X29, #-var_C8
ADD             X2, X2, #aSpecialcost@PAGEOFF ; "SpecialCost"
STR             X19, [SP,#0x6C0+var_468]	
STR             WZR, [X1]

「レジスタが持つアドレスが指し示すメモリの値を0にする」という命令は通常は二命令ないと実装できないのですが、ARM64には読み込むと常に0を返すゼロレジスタと呼ばれる便利なものがあるので上のように一行で実装することができました。

では、0ではない別の値にしたい場合はどうすればよいでしょうか?

わざわざスペシャルコストを0以外の別の値にしたがる人はいないと思いますが、ここでは技術的に可能かどうかだけを解説しています。

以下はX1レジスタがもつアドレスのメモリの値を255にするアセンブラです。

MOV             X19, #255
STR             X19, [X1]

これでスペシャルコストの命令を上書きすれば全てのブキのスペシャルコストを255にすることができます。

今回はたまたまX19レジスタを指定していますが、影響がないならどんなレジスタを指定しても構いません。ちなみにARM64はX30までのレジスタが扱えます。

上書きできる命令を探す

アセンブラでこれを実装するのは先程も言ったように必ず二命令が必要になります。

つまり、BL(分岐)命令以外のどれかを更に上書きする必要が発生するということです。

ここで大事なのは「上書きしても動作に問題のない命令はどれか」ということを正しく理解することなのです。

今回の場合はたまたまBL命令の一つ前も上書きしても動作に問題がありませんでした。

なので、スペシャルコストを255にするコードは以下のようになります。

// Special Cost 255 [tkgling]
@disabled
000847B0 F31F80D2
000847B4 330000B9

一行目のコードがの “F31F80D2” がレジスタに255を代入しているので、ここの値を変えればいくらでも好きな値に設定できます。

スペシャルコスト255固定

分岐先命令を上書き

さて、今回は特定の値を代入する命令も高々二行で書くことができたので置き換えても問題がない命令を見つけて目的のコードを書くことができました。

しかし、もしもどの命令も置き換えることができなかったときはどうすればいいのでしょう?

そういうときはBL命令自体を上書きするのではなく、BLで分岐した先の命令を変えてしまえば良いことになります。

LDR             X1, [SP,#0x6C0+var_660]
ADRP            X2, #aSpecialcost@PAGE ; "SpecialCost"
SUB             X0, X29, #-var_C8
ADD             X2, X2, #aSpecialcost@PAGEOFF ; "SpecialCost"
STR             X19, [SP,#0x6C0+var_468]
BL              sub_19A32AC

sub_19A32AC
STR             X21, [SP,#-0x10+var_20]!
STP             X20, X19, [SP,#0x20+var_10]
STP             X29, X30, [SP,#0x20+var_s0]
ADD             X29, SP, #0x20
MOV             X21, X0
ADD             X0, SP, #0x20+var_18
MOV             X20, X2
MOV             X19, X1
BL              sub_19A3B04
ADD             X1, SP, #0x20+var_18
MOV             X0, X21
MOV             X2, X20
BL              sub_19A2CA0
TBZ             W0, #0, loc_19A3320
ADD             X0, SP, #0x20+var_18
BL              sub_19A3B30
AND             W8, W0, #0xFF
CMP             W8, #0xFF
B.EQ            loc_19A3320
ADD             X0, SP, #0x20+var_18
BL              sub_19A3B30
AND             W8, W0, #0xFF
CMP             W8, #0xD1
B.NE            loc_19A3320
ADD             X0, SP, #0x20+var_18
BL              sub_19A3B38
STR             W0, [X19]
MOV             W0, #1
B               loc_19A3324
MOV             W0, WZR
LDP             X29, X30, [SP,#0x20+var_s0]
LDP             X20, X19, [SP,#0x20+var_10]
LDR             X21, [SP+0x20+var_20],#0x30
RET

サブルーチンsub_19A32ACはloc_847A0よりも遥かに長いので、こちらで目的のコードを書く方が合理的だと言えます。

このサブルーチンをスキップしてもバグが発生しないということは、このサブルーチン内の命令は全部NOP(何もしない)にしても構わないということです。

大事なのは最終的な返り値と[X1]に入れる値だけですので、コードを上から順番にちゃんと読んでいけば解読できます。

“本来のスペシャルコストの値から一律して100減らしたい” といったコードのような場合は、sub_19A32ACを編集しなければ実装することはできません。

分岐命令を上書きしてできるのは “何らかの定数を代入する” ということだけだということを覚えておいてください。

できることできないこと
好きな定数を代入する本来の値を参照する
値をゼロクリア条件分岐
関数のスキップ

本来の値を参照することができないということは、例えばサーモンランのクマブキアンロックのためにCoopAdditionの値を変更する必要があるのですが、クマブキだけを開放するといった細かいことはできないということです。

実際には使えないヒーローモードのブキも含めて全てのブキが使用可能になってしまいます。

一般的にExeFSの改造でできるのは大雑把で大胆な変更なので、細かいところを調整したいのであればLFSを利用して直接BPRMファイルを変数してしまうほうが楽だと思います。

ブキやスペシャルの性能をめちゃくちゃにしたりとか、そういう系のチートがこれに該当します。

最後に

今回は0ではない好きな定数で値を上書きするためのコードの書き方について学びました。

第三回ではただの定数の変更ではない、関数内の命令上書き方法について解説したいと思います。