3: ポインタリストを使ってアドレスランダム化に対応

概要

CheatEngineでお金の値が格納されているアドレスが特定できたとして、ゲーム再起動時にまたそのアドレスにお金の値が格納されているとは限りません。 こういったアドレスランダム化に対応するために、お金の値が格納されている絶対アドレスを覚えておくのではなく、あるPEファイル(ゲームの実行ファイル自体など)の先頭アドレスからの相対アドレスを使うという方法があります。 この相対アドレスは、厳密にはファイルの先頭から何番目のオフセットにあるアドレスが指しているアドレス...のように複数のポインタを連結した形(マルチレベルポインタ)で表現されています。
今回は、CheatEngineで特定したゲーム上のお金の値が入っているアドレスの、ポインタリストを作成することを目標とする。
Unityで作ったゲームを対象にやっていき、ゲームのサンプルはココからダウンロードできる。

アドレスランダム化のざっくり仕組み

お金の値が格納されている絶対アドレスを覚えておくのではなく、あるPEファイル(ゲームの実行ファイル自体など)の先頭アドレスからの相対アドレスを使う
と書いたが、なんで相対アドレスにするとランダム化を回避できるのかという点が気になると思う。
実は、アドレスランダム化は完全にただ不規則にアドレスがランダムになっているのでは無い。 例えばDLLファイル(動的にリンクされる実行ファイルのライブラリ)なら、そのファイルがロードされるアドレスだけランダムであって、DLL内部のコードの配置はランダム化されていないので、DLLの先頭アドレスからのオフセットは各々変わらない。 あるいは、スタック領域(関数呼び出し時の引数やリターンアドレスなどが置かれる領域)も、どのアドレスからをスタック領域とする、というのはランダムだが、スタックの開始アドレスからの相対的なアドレスは不変になっている。
図で表せば以下のようなイメージである。 ちなみに、先頭アドレスの事をBase Addressと言う。
よって、Base Addressからのオフセットでポインタをマルチレベルで辿っていくという表現方法でアドレスを表すとランダム化に対応できる。

とりあえずポインタリストを作成してみる

ポインタリストの作成はCheatEngineが自動化してやってくれるので、まずはやってみる。
前回の記事などを参考に、お金のアドレスを特定できたとします。
そしたら下図のように、AddressListにある特定したお金のアドレスを右クリックし、Pointer scan for this addressを選択します。 そうすると、下図のように色々オプションを指定できるウィンドウが出てくると思いますが、今回はデフォルトのままでOKを押しちゃいましょう。 押すと、ポインターマップ(要はスキャン結果)をどこに保存するかが出てくるので、適当な所を指定しておきましょう (結構一杯ファイルが出てくる場合があるので、専用のフォルダの中に作るといいと思います)。 ポインタスキャンは、色んなアドレスから総当たりでこのお金のアドレスにたどり着くための候補を探すので、場合によってはかなり時間がかかります。 それなので、どれくらい深く探索するかやどこからどこまでのアドレスを開始アドレスとして探索するか などのオプションを細かく指定できるようになっており、そのためのウィンドウが上記画像です。
スキャンが終わると、下図のように多くのお金のアドレスへのポインタリストが出てるかと思います。 これでポインタリストが出来たのですが、実はこれだけだと不十分です。

これはあくまで自動で総当たりで見つけてくれる機能なので、今回たまたま、この経路でお金のアドレスにたどり着いただけで、実際はゲーム起動毎にポインタの値が意図するアドレスを指していなかったりすることがあります。
それなので、一回CheatEngineをそのままにしたまま、ゲームだけ再起動して、下図のようにまたCheatEngineをアタッチします。 OKを押すと、アドレスなどを保持しますか? という下図のような確認画面が出てきますが、ここではYesを押しましょう。 これで、少しゲームを動かしてお金を20Gにした後、先ほど作成したポインタリストを見てみると、下図のようにちゃんとお金を指しているポインタリストと、そうではないポインタリストがあると思います。 これらを整理するため、再スキャンを行います。
まずは下図のように、Pointer Scanner > Rescan memoryを押しましょう。 次に、またポップが出てくると思いますが、今回のゲームではちゃんとお金の値を指せているポインタリストの表示(251B7FB94A0=20)からわかる通り、お金のアドレスは0x251B7FB94A0であることがわかります。 よって、下図のようにこのアドレスを出てきたウィンドウに打ち込みます。 そうすると再びファイルエクスプローラーが表示されますが、これも先ほどの.PTRファイルに上書き保存しちゃいましょう。
これで、下図のように候補が半分以下に絞られました。 これを後1~2回ぐらい繰り返して精度を上げていきましょう。手順の概略は以下です。
  1. CheatEngineつけっぱなしで、ゲームだけ再起動
  2. CheatEngineをアタッチして、ゲーム内でお金を拾う
  3. お金の値をちゃんと指せているポインタリストを探し、今回のお金のアドレスを見つける
  4. Pointer Scanner > Rescan memoryで再び今回のお金のアドレスでスキャンして絞る

繰り返したら、下記画像のようにダブルクリックするとAddressListの方に追加されるので、いくつか追加しておきましょう。 そして、ゲームを再起動して、AddressListに追加したポインタを見てみましょう。
うまく行ってれば、お金の値をちゃんと見つけられているはずです! これを書き換えれば、ちゃんとお金の値を改竄できます。

ポインタリストの読み方

ポインタリストのあの表をどうやって読むのかを理解することは、後にチートコードを書くときに必要となる。
もうポインタスキャンのウィンドウを閉じてしまったという場合は、AddressListにある適当なアドレスを右クリックして再びPointer scan for this addressを押してから、 こんどは出てきたウィンドウでキャンセルを押せば、ポインタスキャンのウィンドウを再び出すことができ、下図のようにFile > Openでポインタリストを作成する時に保存しておいた.PTRファイルを開く。 今回は下図の意味を紹介します。 これは、以下のような感じになっています。
a = (mono.dllの先頭アドレス + 0x00264110) のアドレスに入ってる値
b = (a + 0x310) のアドレスに入ってる値
c = (b + 0x38) のアドレスに入ってる値
(c + 0x4A0) のアドレスに入ってる値 = 0x192FC0694A0
実際にコーディングする際は、mono.dllの先頭アドレス(BaseAddress)を取得する必要があります。