feature image

2017年4月17日 | ブログ記事

ゲーム解析入門 ~東方紅魔郷を例に~【新歓ブログリレー2017 14日目】

ゲーム解析入門 ~東方紅魔郷を例に~【新歓ブログリレー2017 14日目】

初めに

こんにちは、long_long_floatです。プログラム担当です。

皆さんは「ゲーム解析」と聞いて何を思い浮かべますか? 認証回避やチートなどグレー(ブラック)な行為を思い浮かべる人もいるかもしれませんが、本記事ではゲームをきちんと購入した上で他人に迷惑をかけない範囲でゲームの内部パラメータ表示などの攻略の補助になるようなハックをしていきます。

なお、ソフトウェアによっては逆アセンブルなどのリバースエンジニアリングを禁止している場合もあるので規約を確認してください(今回対象の東方紅魔郷はそのような記述はなかったです)。また、認証回避やチートなどの行為を推奨しているわけではありません。

また、自分も解析についてはプロではないのであまり高度なトピックは期待しないほうがいいです。

解析対象のゲームについて

今回解析するゲームは「東方紅魔郷」(以下紅魔郷)という同人シューティングです。これは東方シリーズ初のWindows版で、第6作目にあたります。ちなみに第1作目は1996年発表でPC-98向けでした。結構歴史あるんですねー。

やりたいこと

さて、自分はこの紅魔郷を最近始めた訳ですが(今更感すごい)、道中はスイスイ行ってもボスで結構ミスしてしまうわけです。そこで紅魔郷にはクリア済みのステージを遊べるプラクティスモードというものがあるのですが、ご丁寧に道中から遊ばなければいけないのでボスだけを練習したい場合、結構面倒くさくなります。

そこでゲーム内部の値を弄ってボスまで飛ばそうというのが今回の目的です。

準備

以下のツールをダウンロード、インストールしてください。本記事で使ったバージョンも記しておきます(あまり操作感は変わらないと思うので最新で問題ないです)。本記事では日本語化はしないので適宜行ってください。また、本記事ではツールの使い方を一から説明しないので詳しい使い方はREADME等読んでください(うさみみハリケーンは日本語で詳しく書いてあるので必読)。

以下は本文では使いませんが、解析によく使われるツールです。

本編

まず、手始めにスコアを書き換えることで基本的な流れを把握します。というのはスコアは特有の数値になることが多く検索がしやすいからです。

  1. 紅魔郷を起動する。
  2. うさみみハリケーンを起動する。起動時にプロセスを選択するウィンドウが出るので紅魔郷を選ぶ。
  3. スコアを適当に増やす。
  4. 現在のスコアで検索
  5. うさみみハリケーンのメニューから検索
  6. メモリ範囲を指定して検索(64bit Mode)
  1. 検索・比較単位を4Bytes(DWord)にして現在のスコアで通常検索
  1. 恐らく0069BCA0と0069BCA4がヒットします。
  2. 見つかったアドレスに対応する値を書き換える。2つ出てくると思いますがどちらでもいいです(恐らく前者はスコアのアニメーション用のカウント、後者は前者の目標値だと思われます)。

上手くいくと上画像の状態から下の画像のようにありえんスコアになります。Playerが増えているのは増加したスコアによってエクステンドしたからで、Powerが減っているのはスクショをとるときに一回死んだからです…。

では実際に飛ばせるように解析するわけですが、先ほどのスコアのようにどこをどうしたらいいか自明ではないです。そこで以下の仮説(正しいとは言っていない)を立ててそれをもとに解析していきます。

ゲームがループするたびにインクリメントされるカウントが存在していて、そのカウント値によってイベント(ボスが出てくる等)が発生する。そのカウントを書き換えることでボスまで飛ばせるはず。

まず、カウントは一定間隔で増加しているはずです1。そこで変動検索というものを活用します。これはメモリ内の値の変動を検索することができ、カウントは増加しているので増加している値を検索すればカウントが見つかるだろうという寸法です。

  1. 紅魔郷でステージ1開始後、メモリ範囲を指定して検索(64bit Mode)
  2. オプションをCommit-ReadWrite、検索・比較単位を4Bytesに設定
  3. 変動検索の比較用メモリの「確保・記録」をクリック
  1. 紅魔郷を適当に進める
  2. 検索ウィンドウに戻り変動検索実行の「増加」をクリック
  3. 2.に戻る(2回目以降の検索は右のリストの絞り込みになります)
![](/content/images/2017/04/search-count1.png)
何回か繰り返すと600件くらいに収束すると思います。これを†すべて†値を変えて試してみてもいいですが数が多すぎるのでもう少し絞り込みます。

そこで、先ほどのスコアとカウントは近い場所にあると仮定して、スコアのアドレスを調べてみます。スコアの値(0069BCA0にあります)を選んでいる状態で右クリック→表示アドレスを指定をクリックしてください。すると以下のウィンドウが開きます。

![](/content/images/2017/04/memory.png)
で、アドレスとサイズを考慮すると左の赤線を引いているブロックにスコアがあることがわかります。属性が-RW-となっているので読み書きできることもわかります(R, WはそれぞれRead, Writeの頭文字)。で、右の赤線を引いている所(スコアのアドレスに対応している)を見ると.dataとなっています。これはC言語とかにおける、いわゆるグローバル変数が置かれる領域となっています。つまり、スコアはグローバル変数(ここではどうでもいいですが型はint)として定義されていることがわかります。

先の仮定通りならカウントもスコアと同じ領域(00479000 ~ 00479000 + 00270000)にあるはずです。カウントのような様々なところから参照される変数はグローバル変数として定義されているのが自然です。

先ほどの範囲検索で変動検索の範囲を指定すると15件に絞るこむことができました。

![](/content/images/2017/04/search-count2.png)
1件ずつ値を変えて反応を見ていくと、005A5FB0の値を変えたときに明らかにステージの進度が変わっています。そこで、ボスまで進めてその時点でのカウンタの値を控えておきます。ここで少し値を減らしておくのがポイントです。ステージ1だと9014が丁度よさそうです。
![](/content/images/2017/04/skip1.png)
そして、再びステージを開始しカウンタの値を控えたものに書き換えると…
![](/content/images/2017/04/skip2.png)
上手くいったようです。

どうせなのでうさみみハリケーンで使える改造コード2を作ってみました。作り方は簡単でアドレス-上書きしたい値のフォーマットに沿えばうさみみハリケーンで改造コードとして使えます。そのまま飛ばしてしまうとPowerがそのままで練習にならないのでPowerも書き換えるようにしました(スコアと同じ方法でできます)。

stage1

; ボスまで飛ばす
005A5FB0-9014
; Power 64
0069D4B0-40

stage2

; ボスまで飛ばす
005A5FB0-5017
; Power MAX
0069D4B0-80
![](/content/images/2017/04/skip3.png)
stage3
005A5FB0-4016

自動化する

ここまでの方法だと飛ばすためにはいちいち改造コードを実行する必要があり、なかなかに面倒です。そこでDLLインジェクションを用いて既存の関数を上書きすることでステージ開始時に飛ばすことができます。早速実装をしていきたいところですが…間に合わなかったので今回はここで終わりにします。代わりに参考になりそうなリンクを張っておきます。

また、関数のアドレスを知るのにはプログラム自体を解析しないといけなく、OllydbgやIDA Proのお世話になるので使えそうなtipsをいくつか書いておきます。IDA Proは管理者権限で実行しないとエラーが出て解析できないので気を付けてください。

例外を無視する(Ollydbg)

Ollydbgでアタッチしてから実行するとプログラムが頻繁に例外で中断されます。これはかなり鬱陶しいので頻繁に発生する例外を無視してしまいましょう。Options→Exceptions→Ignored exceptionsで無視する例外を指定できます(Add currentで今の例外を追加できます)。

![](/content/images/2017/04/ignore-exception.png)

メモリにブレークポイント(Ollydbg)

あるアドレスにアクセスする命令を突き止めたいときにこれは使えます。読み込み、書き込み、実行をフックにできます。

![](/content/images/2017/04/memory-breakpoint.png)

アセンブリをグラフ表示(IDA Pro)

アセンブリを表示しているときに右クリック→GraphViewをクリックするとアセンブリをグラフ表示できます。

![](/content/images/2017/04/graph.png)

終わりに

うさみみハリケーンを用いたゲームの解析を解説しました。自分が初めてやった時の感想としては思ったより簡単なところ(ツールの使い方。今までめんどくさそうだなぁと避けていた)と難しいところ(目的の値を探すところ。手掛かりがないと手さぐりになってしまう)があるなと。でも、手掛かりがない時に実際のプログラムはどう作られているかを想像しながら解析すると手掛かりが見つかったりします。本格的なゲームでも結局はいつも作っているプログラムと同じなんだなぁと実感しました。

明日はkaz, Durunの記事です。

参考


  1. 当たり前だよなぁ? と思われるかもしれませんがこの方針が決まるまで2日ぐらい見当はずれなことをしていました。↩
  2. 改造コードについては http://cheater.seesaa.net/article/233298107.html 参照。↩
long_long_float icon
この記事を書いた人
long_long_float

プログラミングしてます

この記事をシェア

このエントリーをはてなブックマークに追加
共有

関連する記事

2017年4月20日
学部1年の数学、特にε-δ論法に殺されないために【新歓ブログリレー2017 17日目】
hihumi icon hihumi
2017年4月12日
あたらしいWebAssemblyのはなし【新歓ブログリレー2017 9日目】
Double_oxygeN icon Double_oxygeN
2017年4月11日
できるかなってDistortion
clk icon clk
2017年4月9日
Pythonで数学の課題を解こう【新歓ブログリレー2017 6日目】
hukuda222 icon hukuda222
2017年4月8日
気軽にお絵かき! traP1draw【新歓ブログリレー2017 6日目】
Humming icon Humming
2017年4月7日
ゲームを作ろう! traP3jam【新歓ブログリレー2017 4日目】
Ark icon Ark
記事一覧 タグ一覧 Google アナリティクスについて 特定商取引法に基づく表記