35: EntityListの特定とクラス再構築

概要

ゲーム上にはプレイヤーや敵、他のプレイヤーなどの多くのアクター(Entity)がいます。 ゲームエンジンでは、これらEntityをまとめた、EntityListという物を使ってEntityを管理しており、これを特定できれば全Entityの情報が取得できます。
今回は、EntityListを特定し、他の全プレイヤーの位置情報を取得する事を目標にやっていきます。
また、その過程でReClass.NETというツールを使ったクラス再構築の方法を紹介します。
今回のチート対象のゲームは、x86でオープンソースのAssault Cube v1.2.0.2を使います。 PwnAdventure3でもEntityListはありますが、Entityの構造体に敵の座標の情報が無かったので、Aimbotの記事とかにこの後繋げるためにこのゲームにしました。

AssaultCube のダウンロード

Assault Cube はオープンソースで無料のFPSゲームで、一人プレイもマルチプレイもできます。 マルチプレイでチートするのは良くないので、チートする時は一人プレイでやりましょう
Assault Cube 1.2.0.2 を ココ からダウンロードしてください。
これより新しいバージョンもありますが、このバージョンの体力や弾数などのオフセットがネット上に公開されていたので、 バージョンをこれにしておくとポインタリストとかを作成する手間が省けます。

AssaultCube の操作方法や設定

簡単な操作方法の説明は以下です。 また、チートする上で便利な設定は以下です。

ReClass.NET のダウンロード

ReClass.NETというツールは、言葉で説明するより使って見た方がわかりやすいですが、クラスを再構築するのにとても便利なツールです。
CheatEngineの Dissect data/structure の機能と同じ事をやってくれるツールですが、ReClass.NETの方が便利です。
ココ から最新バージョンの .rar ファイルをダウンロードして使用してください。
※ .NETとありますが、.NET専用とかじゃないので無視していいです
※ 32bit用と64bit用の実行ファイルがあるので、起動する際はゲームに応じて使い分けてください

EntityListの特定方法の概要

例えばプレイヤーがダメージを受けた場合、ゲームは以下のような処理を行います。
  1. EntityListからプレイヤー(Entity)のアドレスを取得
  2. プレイヤーのアドレス+体力へのオフセットを計算して体力のアドレスを計算
  3. 体力のアドレスを使って体力を減算する
こんな感じで、プレイヤーのメンバの値が変更される処理の前に、EntityListからそのプレイヤーのアドレスを取得している場面があります
なので最初にプレイヤーの名前や体力などのプレイヤークラスにありそうな情報のアドレスを特定し、そのアドレスに書き込みがある命令をさかのぼって探していきます。

EntityListのデータ構造はゲームエンジンによって変わります
Entityへのポインタの配列だったり、リンクリストだったりします。どのゲームエンジンがどのデータ構造なのかは この記事 とかに詳しく載ってます。
Assault Cube の場合、EntityList は Entityへのポインタの配列 で管理されています
チート対象のEntityListの構造が分からない場合は、ゲームのファイルの構成や実行ファイルの文字列情報などからゲームエンジンを特定したり、 いくつかの敵(Entity)のアドレスを特定してその差分を取って予想したりなどして特定します。

プレイヤークラスの再構築

EntityListを特定する始めとして、プレイヤークラスの何かしらのメンバのアドレスを突き止める必要があります。
いつも通りCheatEngineを使ってアドレスを突き止め、ポインタリストを作成すればよいのですが、 AssaultCubeはチート界隈で有名なゲームで、ネット上に既に情報が多くあるので、この記事 を参考にします

記事によると、プレイヤーのアドレスは常に BaseAddress(0x400000) + offsetLocalPlayer(0x10f4f4) = 0x50F4F4 のポインタが指しているアドレスにあるらしいです。 CheatEngine上で、Add Address Manually を押し、以下のように 4ByteHexadecimal を指定する事で、プレイヤーのアドレスが取得できます。 今回自分は、0x281A518 がプレイヤーのアドレスっぽいです。
記事には、その他プレイヤーからこの値のオフセットにこんな値がありますみたいな情報が多く書いてあるので、ここでReClass.NETの出番です。 x86用のReClass.NETを立ち上げ、File > Attach to Process ... でAssaultCubeのプロセス(ac_client.exe)にアタッチさせましょう。 アタッチ後、以下のようしてプレイヤーのアドレス(0x281A518)を入れ、適当なアドレスの上で 右クリック > Add Bytes をしてバイト数をとりあえず増やしましょう。 ちなみに下図の青い四角の所でクラス名を定義できます。 ゲームを動かしながらReClass.NETを見ると、プレイヤークラスの中のメンバの値が今どうなっているかをリアルタイムかつ一気に3つの型の表現で見る事ができます。 試しにまず、視点を変えずに、プレイヤーを動かしたりジャンプしたりしてみると、以下の部分が座標っぽい事がすぐにわかると思います。 座標はx,y,zで並んでいて、よく Vector3 という型で表されますが、ReClass.NETでは Vector3型がデフォルトで用意されています。 上記のオフセット00040034をクリックし、下図のボタンで Vector3型に変換しましょう。 こんな感じでクラスのメンバを突き止めていき、クラスの再構築を行っていくと、後のリバースエンジニアリングとかで役立ちます。
記事によると、体力は 0xF8 のオフセットにあるらしいので、これも下記のように型を I+nt32 にして名前を付けておきましょう。 ReClass.NETを使う上での鉄則は、上の方(オフセットが小さい方)のメンバから型を変更していく点です。 下の方のメンバの型を指定した後、それより上のメンバの型を変える場合、(変更する型の種類によっては)下のメンバで定義した場所のオフセットがズレてしまいます。 これがReClass.NETの残念な点ですが、気を付けてください。
ここまで解析したら、一旦このReClass.NETの状態を Ctrl-S とかで保存しておきましょう。

おまけですが、ReClass.NETには現状のクラスの定義をC++やC#の形で表現してくれる機能があります。 Project > Generate C++ Code ... を押せば、以下のようなプレイヤークラスの定義が出てくると思います。

EntityList の特定

(この内容は、この動画 を参考にしています)
ReClass.NETを眺めていればわかりますが、プレイヤーのアドレス(0x281A518)からオフセット 0x225 にプレイヤー名があります。 今回はプレイヤー名にアクセスしている命令からさかのぼってEntityListを見つけます。
プレイヤー名のアドレス 0x281A518+0x225=0x281A73D をCheatEngineの Add Address Manually で追加して置きます。 そして下図のように、このアドレスにアクセスしている命令を見ます。 これで以下のように複数の場所からのアクセスがあると思います。 一つ一つ Show disassembler で周辺のコードを見ていき、EntityListからのプレイヤーのアドレスの取得っぽいコードを見つけます。 Entityへのポインタの配列 の構造を取っているEntityListへのアクセスは以下のようなコードになっているはずです。
void* player = *(EntityList[index]); // C/C++
void* eax    = *(edi[esi]);
mov eax, [edi+esi*4]; // asm (4はx86なのでアドレスが32bit=4Byteだから)
上図で選択している一番アクセス数が多い命令の近くに、EntityListへのアクセスっぽいコードがありました。 上図の赤く囲ったところの、edi が EntityList のアドレスと思われるので、ブレークポイントを打って実行してこの値を読み取り、 CheatEngineの Add Address Manually で値を下図のように保存しておきます。 これで、EntityListの(と思われる)アドレスの取得ができました

これが本当にEntityListのアドレスかどうか確かめてみます。
EntityListから適当なオフセットでEntityのアドレスを取得し、その名前を見てみましょう。 例として *(EntityList[1]) を見てみます。上記の結果でEntityListが自分の所では 0x2A65330 であることがわかったので、0x2A65330+0x4 のアドレスが指しているアドレスをCheatEngineの Add Address Manually で確認します。
これで、EntityListのインデックス1のポインタが指しているEntityのアドレスが 0x1AC80758 であることがわかりました。
このアドレスをReClass.NETに以下のように入れてみましょう。
そして、0x225のオフセットを見てみると、他のプレイヤーの名前がある事がわかります。
EntityListから計算して他のプレイヤーのアドレスを取得できたので、これは確かにEntityListのアドレスであることが確認できます

EntityList のポインタリストを手動で取得

EntityListは特定できましたが、ゲーム再起動時にアドレスが変ってしまうので、ポインタリストが欲しいです。
ポインタリストをCheatEngineで自動で取得する方法しか紹介していませんが、EntityListのような、いかにもグローバル変数として宣言されていそうな値なら、 手動で簡単にポインタリストを見つける事ができたりします
先程 Show disassembler で見た、EntityListへのアクセスのコードをもう一度見てみます(下に画像を再添付します)。 上図の edi がEntityListだと判明しましたが、そのediは二つ上の命令 mov edi,[ac_client.exe+10F4F8] によって取得されています。これで実はポインタリストがあっさり判明しました。 ac_client.exeのアドレスは、GetModule("ac_client.exe")でアドレスを取得でき、それに0x10F4F8を加算したアドレスに、 毎回EntityListのアドレスが入っているという事です。
このように、リバースエンジニアリングによってポインタリストを手動で求める事ができます

EntityList の用途

EntityListを取得すると、全プレイヤー(Entity)の情報が取得出来る事になります。
もちろんですが、各Entityのクラスは同じなので、自分のプレイヤーで体力がオフセット0xF8にあれば、 他のプレイヤーの体力のオフセットも0xF8にあります。
先程、座標の情報がプレイヤークラスのメンバの中にある事を確認しました。 この情報を用いることにより、AimbotやESPチートが作れるようになります。 このように、多くの "よくあるチート" には、EntityListが必須の情報となっています。