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 の操作方法や設定
簡単な操作方法の説明は以下です。- 移動:
WASD (Spaceでジャンプ)
- 視点移動:
マウス
- 打つ:
マウスクリック
- メニューを開く:
Esc
- リロード:
R
- 現在のランキング:
Tab (自分の名前や敵の名前の取得に使える)
- マップ:
左のAltキー
- 武器切り替え:
1,2,3,4キー
- ウィンドウモード:
Settings > Video settings > FullScreen のチェックを外す
- 名前変更:
Settings > Gameplay settings > Nickname
- ボットの動きを止める/攻撃させない:
戦闘開始中に Singleplayer > Bot settings ... > Idle bots?をチェック + Bots attack?のチェック外す
ReClass.NET のダウンロード
ReClass.NETというツールは、言葉で説明するより使って見た方がわかりやすいですが、クラスを再構築するのにとても便利なツールです。CheatEngineの
Dissect data/structure
の機能と同じ事をやってくれるツールですが、ReClass.NETの方が便利です。ココ から最新バージョンの
.rar
ファイルをダウンロードして使用してください。※ .NETとありますが、.NET専用とかじゃないので無視していいです
※ 32bit用と64bit用の実行ファイルがあるので、起動する際はゲームに応じて使い分けてください
EntityListの特定方法の概要
例えばプレイヤーがダメージを受けた場合、ゲームは以下のような処理を行います。- EntityListからプレイヤー(Entity)のアドレスを取得
プレイヤーのアドレス+体力へのオフセット
を計算して体力のアドレスを計算- 体力のアドレスを使って体力を減算する
なので最初にプレイヤーの名前や体力などのプレイヤークラスにありそうな情報のアドレスを特定し、そのアドレスに書き込みがある命令をさかのぼって探していきます。
EntityListのデータ構造はゲームエンジンによって変わります。
Entityへのポインタの配列だったり、リンクリストだったりします。どのゲームエンジンがどのデータ構造なのかは この記事 とかに詳しく載ってます。
Assault Cube の場合、EntityList は Entityへのポインタの配列 で管理されています。
チート対象のEntityListの構造が分からない場合は、ゲームのファイルの構成や実行ファイルの文字列情報などからゲームエンジンを特定したり、 いくつかの敵(Entity)のアドレスを特定してその差分を取って予想したりなどして特定します。
プレイヤークラスの再構築
EntityListを特定する始めとして、プレイヤークラスの何かしらのメンバのアドレスを突き止める必要があります。いつも通りCheatEngineを使ってアドレスを突き止め、ポインタリストを作成すればよいのですが、 AssaultCubeはチート界隈で有名なゲームで、ネット上に既に情報が多くあるので、この記事 を参考にします。
記事によると、プレイヤーのアドレスは常に
BaseAddress(0x400000) + offsetLocalPlayer(0x10f4f4) = 0x50F4F4
のポインタが指しているアドレスにあるらしいです。
CheatEngine上で、Add Address Manually
を押し、以下のように 4Byte
の Hexadecimal
を指定する事で、プレイヤーのアドレスが取得できます。
今回自分は、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
型がデフォルトで用意されています。
上記のオフセット0004
と0034
をクリックし、下図のボタンで 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が必須の情報となっています。