最初に断っておくことがある。
本サイトの情報によって、利用者または第三者に生じた、いかなる損害による責任を一切負いません。
チート行為を助長する目的はございません。
あくまで個人の趣味の範疇でお楽しみください。
古いゲームの体験版を入手したが、制作会社がなくなっていた。
GoogleMapで住所を調べると、カリフォルニアのどこかの小さな開発会社は中古トラクター販売店に変わっている。ゲームを購入することができなくなっている。一応探してみたが、ニッチなゲームのソースコードはおろか、購入済み製品も出回ってはいない。
仕方がないので、体験版のソフトでもう少し遊べないか考えることにした。
ゲームの制約は下記だ。
- レースゲームなので、タイマーのような時間をカウントしているものがあるが、一定時間経過すると強制的にゲームが終了する
分解すると下記を調べるということだ
- 時間をカウントしている変数を見つけ出し、一定期間が何かを特定する
- 強制終了している処理(関数)を探し出す
- ゲームが終了するまでの時間を伸ばす
- exeファイルにパッチを充てる
うさみみハリケーンをダウンロードした前提から始める。
ゲームを起動する
実行ファイルを起動を押してダイアログを開く
パスを指定して、起動ボタンを押す
ゲームのプロセスを選択して、選択ボタンを押すとメイン画面が起動する
プログラマーじゃない人は、この画面で面食らうかもしれない。
知識をつけてから出直すしかない。
変数を見つける
ゲームというのは数値を扱っている。お金であったりHP、MPなどが変数になっている。変数の変動を探す必要がある。基本的には変数を検索する、値を変動させる、変数を検索する を繰り返していくと、該当するAddressを割り出すことができる。
初回検索したときのアドレスが一覧で分かる。2回目の検索は1回目で検索したメモリから変更があったものが検索がかかり、絞り込み検索を行うことができる。
増加した、減少した、変動していない、などをゲームの操作に合わせて実行していくと、変数の絞り込みができる。
このゲームでは、ゲームが終了すると必ず351の数値になる変数が複数存在した。
これの変数がタイマーに関するものなのか、検証していく。
リアルタイムで強調表示にチェックを入れておくと便利だ。このゲームではゲーム進行とともに、1ずつカウントアップされているので、タイマー用の変数だと予想できる。
すべてのアドレスを、書き換えてどういう挙動になるか、画面に見えている数字が変化するかを見ていく。ゲーム中に下記の画面からメモリの書き込みを行う。
004FC494のアドレスを書き換えると、すぐにゲームが終了した。タイマーに関する変数はこのアドレスで間違いなさそうだ。
変数を参照している関数を見つけ出す
関数を見つけ出すには、ブレークポイントを設定する。
デバッギー操作でアタッチする。変数のアドレスを004FC494を指定し、読み書きで停止するようにする。設定ボタンを押すと、関数をトレースすることができる。
一応ここで、仮説を立てておく必要がある。351で停止するということは、350が処理を停止する境界値になっているのかもしれない。
350の値をてかがりとする。電卓で350と打つと、HEXは15Eになる。351は、15Fだ。
デバッグの状態でゲームを進行させて、004FC494のアドレスを書き換えゲームが終了させる。このときのトレースログを見る。
[Break on HBP1]
Registers:
EAX=00000000 EBX=00493D4F ECX=0000002B EDX=0000015F
ESP=0019F97C EBP=0019FA14 ESI=0019FA7C EDI=000001F9
EIP=004150F6 EFlags=00000283 [ I S C ]
Stack dump(ESP-+20h):
C0350131 007F0000 F9010000 7CFA1900 00007F00 14FA1900 FD4F4100 00000000
00050000 7CFA1900 60DCF575 AD010000 77E04A00 FA010000 5C020000 5D020000
ThreadID:
0000458C
EIP=004150F6が呼び出されたアドレスになる。EDXには351の数字も扱われていることが分かる。
逆アセンブルから修正したいコードを特定
ここからはトレースされた関数を逆アセンブル、改造、動作検証を繰り返して、強制終了している箇所を特定する。
逆アセンブル実行ボタンを押し、EIP=004150F6アドレスに移動する。
アセンブリ言語の知識が必要になるが、TEST で検証した結果、JXXX (アドレスへジャンプ)する命令が分かる。JEはTEST結果が1のき、JNはTEST結果が0のときジャンプされる。JNLEは、Less Equelの条件が付いているので、より小さくなく等しくない条件のときジャンプするという意味となる。
超初歩的には、このジャンプをひとつひとつ潰していけば、どこかで処理される処理されないの分岐を見つけることができる。
右クリックメニューから、全選択列をNOPで埋めるボタンを押す。
NOPに書き換えてしまう。No Operation 処理しない命令に書き換える。この状態でゲーム操作をして、挙動が変更されれば判定処理が特定できる。
[Break on HBP1]
Registers:
EAX=00000001 EBX=00000000 ECX=0000000F EDX=00000001
ESP=0019FA0C EBP=00000032 ESI=00000014 EDI=00000028
EIP=00436404 EFlags=00000202 [ I ]
Stack dump(ESP-+20h):
33000000 09000000 D0070000 2D000000 13000000 2B000000 AB6B4300 13000000
A0000000 14000000 0B5CE600 60DCF575 01000000 7F0E7F02 10424000 0EFFFFFF
ThreadID:
0000458C
最終的には、00436404アドレスがゲームの終了判定を行っていた。
004363FA 813D94C44F00B8880000 CMP DWORD PTR [004FC494H], 0000015EH
00436404 7E06 JLE 0043640CH
CMPは比較のコマンドだ。その後JLEでゲーム内処理を行っている。
NOPにしてジャンプしなくなるとゲームが終了する動きになっている。
DWORD は数値の型、PTR [004FC494H]は変数のポインターを表している。ここで15Eが出てくることに気が付く。もう一度電卓を出すが、数値は350だ。
つまりカウントアップ変数である004FC494アドレスと、350数値を比較して、350以下の場合はジャンプしてゲームを継続、351になって超えるとゲームは終了してしまう処理になっている。
ここで350から35,000に数値を変更したいとする。
35,000は88B8になるから、下記のように書き換える。
【変更前】813D94C44F005E010000
【変更後】813D94C44F00B8880000
アセンブリはリトルエンディアンになるので、数値の指定は2バイトずつ逆順になる。
15E→5E 01 、88B8→B8 88
コードを入力したら、コード修正実行ボタンを押して、ゲームに反映されたか確認する。
修正パッチの作成
修正後のアドレスを右クリックし、全選択列のアドレスと16進数ダンプをコピーする。
改造コード実行画面で、コード実行ボタンを押す。