Cheat Engineでゴッドイーター3をチートしよう! スクリプトでアドレスを自動検出する

Cheat Engineでよくあるのが、体力や弾丸のアドレスを見つけたのにゲームを再起動すると使えないといった*問題です。これは動的に確保されたメモリ領域の場合に起こる現象です。今回はスクリプトを使ってアドレスを自動検出することで、その問題を解決してみようと思います。これをしておくと、後々ポインタースキャンをする時やその他の作業をするのが楽になります。
なお、この記事の内容はゴッドイーター3だけでなく、他のゲームにも応用できることなのでぜひ読んでみてください。


※ここでは"問題"と表現していますが、これはCheat Engineに問題があるわけではなく、プログラムとはそういう使用だということです。詳細はこちらの記事の「動的アドレス」をご参照ください。


どの命令にスクリプトを組むか決めよう


HPのアドレスを見つけた状態から始めていきます。



まず「Find out what accesses this address」を使用して、HPのアドレスにアクセスしている命令を見つけにいきます。



デバッガーでge3.exeにアタッチするのでYesをクリックします。



アクセスしている命令を見つけました。
左のCountにある数字がすごい勢いでカウントアップされているのを確認できます。これはその命令が実行されている回数を表していて、プログラム内部ではループ命令で継続的に実行されていることを意味しています。
もうデバッグは必要ないので右下のStopをクリックして中止しましょう。



スクリプトを組む命令を決めます。
どの命令でもできると思いますが一番上の命令に組むことにします。 ミッション開始と同時に実行される命令にスクリプトを組めば、スクリプトをオンにした瞬間に狙いの動作を実現できます。
スクリプトを組む命令を選択し、Show disassemblerをクリックして次に進みましょう。



逆アセンブラウィンドウが表示されました。
対象としている命令はアドレス 7FF74CB28FDA の movss xmm1, [rbx+10] です。 このアドレスは皆さんと異なるかもしれません。 同じゲームでもバージョンによってアドレスがずれている可能性があるからです。



対象の命令を右クリックして「Find out what addresses this instruction accesses」を選択します。この命令がアクセスしているアドレスを見つけてくれます。
大変便利な機能で使う頻度も多いです。



Stopをクリックしてデバッグを中止します。
画像のように3つのアドレスにアクセスしているのを確認できます。 上からプレイヤーのHPのアドレス、hugoのHPのアドレス、zekeのHPのアドレスになっています。


実はこの命令、ブレークポイントを設置してみればわかるのですが、プレイヤー、hugo、zekeのように順々にアクセスしながらループしているため、このままスクリプトを組んでもうまく動作しません。 検出したいのはあくまでプレイヤーのアドレスだけです。


したがって、自分と味方1、味方2を区別できる値を見つける必要があります。


オブジェクトを区別する値を見つける


プレイヤーのアドレスを選択後、右クリックから「Find commonalities between addresses」、「Mark selection as group 1」を選択します。
プレイヤーのアドレスをグループ1にその他をグループ2に設定しています。グループ1を設定すればその他は自動的にグループ2になります。



再度右クリックから「Find commonalities between addresses」、「Scan for commonalities」を選択します。



逆アセンブラ画面に戻り確認するとわかりますが、この命令ではRBXにベースアドレスが格納されているので、RBXの行をダブルクリックします。



Scanをクリックします。



画像のようにGroup 1とGroup 2が保持している値の相違を一目で確認できるようになりました。 アドレス:値という表記になっており、一番左の黒字の数字がオフセットです。
ここでは自分と味方1、味方2を区別できるような値をスクロールしながら探します。
そうするとオフセット150にそれらしい値がありました。



プレイヤーが0、hugoが1、zekeが2になっています。 ここで間違いないでしょう。


この「Find commonalities between addresses」は、例えば銃の打ち合いをするゲームで青チームが1、赤チームが2といった具合で共通する値を探すときにも使用します。
Cheat Engineの機能「Dissect data structure」でも同じことができます。


これでスクリプトを作る準備ができました。


スクリプトを作ろう!


まずスクリプトを組む命令を左クリックで選択します。
その状態でメニューバーのToolsからAuto Assembleを選択しましょう。



TemplateからAOB Injectionと進みます。



AOB Injectionのテンプレートが挿入されました。
ここから新たにコードを追加していきます。



// <- の行が変更を加えた箇所です。


//// --------------------  Main Section  ---------------------
[ENABLE]
//// --------------------  Enable Section  ---------------------
[DISABLE]
//// --------------------  Disable Section  --------------------

Cheat EngineのAuto Assemblerスクリプトには3つのセクションがあります。


Mainセクションに書いたコードはスクリプトがオンになった時、オフになった時の両方で実行されます。

[ENABLE] セクションに書いたコードはスクリプトがオンになった時に実行されます。
[DISABLE] セクションに書いたコードはスクリプトがオフになった時に実行されます。

Auto Assembler Commands


aobscanmodule  -> プロセス内から指定された16進数コードを探し、そのアドレスを取得する。
label      -> アドレスにラベル名をつける。
                  値が1ならcode1:、2ならcode2:にジャンプするなどの条件分岐する場合に使用。
alloc      -> allocateの略でメモリを確保する。
dealloc  -> 確保したメモリを開放する。
registersymbol      -> アドレスをシンボルに登録してスクリプト外でも使えるようにする。
unregistersymbol  -> 登録したシンボルを解除する。
db   -> declare bytesの略で、指定されたアドレスにバイト配列を書き込む。
           上のスクリプトだとINJECTに F3 0F 10 4B 10 の計5バイトを書き込んでいる。
           dbの他に dw , dd , dq もありそれぞれ2バイト、4バイト、8バイトに対応している。

Assembler Commands


cmp   ->  compareの略で値を比較する。
jmp   ->  指定されたアドレスに無条件にジャンプする。
jne    ->  jump not equalの略で比較した値が同じでなければ指定されたアドレスにジャンプする。
              cmpとセットで使われることが多い。
mov   ->  値をコピーする。


参考リンク
List of all the Auto Assembler commands
Assembler Commands


Cheat Engine7.1からの変更点
dealloc (newmem)
dealloc (healthBase)
と従来2行にわたって書かなければならなかったのが
dealloc (newmem healthBase)
と間にスペースを入れることで1行で書けるようになりました。
Cheat Engine7.1未満をお使いの方は各所変換しながらコードを書いてください。


スクリプトの詳細な説明はこちらから



メニューバーのFileからAssign to current cheat tableを選択します。



Cheat Engineのメインウインドウに戻ると Auto Assemble script というスクリプトが追加されているので、画像の赤枠部分をクリックしてスクリプトをオンにしましょう。
スクリプトを再編集したいときは、<script>のあたりをダブルクリックします。


Cheat Engineメインウインドウの右下にある「Add Address Manually」をクリックして手動でアドレスを設定します。



Pointerにチェックを入れます。



スクリプトでシンボルに登録したhealthBaseとオフセットの10を入力し、
TypeをFloatに変え、DescriptionはHPにしときます。



チートテーブルを見ると無事にHPのアドレスを検出できています。


チートテーブルを整理する

ここからする設定は見栄えの問題で必須ではありませんが、やっておくとチートテーブルがすっきりします。



HPのアドレスの行を左クリックしたままスクリプトの行に重ねるようにドラッグ&ドロップします。



スクリプトの行の上で右クリックから、「Group config」、「Hide children when deactivated」を選択します。 これでスクリプトをオフにするとHPのアドレスも自動で隠れるようになります。
Group configには他にも便利な設定があるのでいろいろ試してみてください。


OPのアドレスも検出する


HPとOPのアドレスを見てみるとアドレスが似ていることに気付きます。これは2つのアドレスの距離が近いことを示しています。


OPのアドレスからHPのアドレスを減算してみると
1AA73AC6340 - 1AA73AC6424 = E4
になり、HPのアドレスからOPのアドレスまでの距離はE4であることが分かりました。これを利用してついでにOPのアドレスも自動で検出できるように設定します。



HPのアドレスの行を選択し、Ctrl+C、Ctril+Vでアドレスを複製します。
右クリックからコピー、張り付けすることもできます。



Pasteをクリックします。



矢印のあたりをダブルクリックしてアドレスを編集します。



オフセットの10に+E4を付け足し、DescriptionをOPに変更します。



これでHP、OPと、2つのアドレスを検出できるようになりました。
もちろん値を固定することもできます。
ゲームを再起動したり、ミッションを変えても無事検出できることを確認できたら完成です。


スクリプト補足説明

ge3.exe -> 7ff74c4f0000 メインモジュールのベースアドレス
ge3.exe+638FDA -> 7FF74CB28FDA  スクリプトを組んだ命令
ge3.exe+638FDF -> 7FF74CB28FDF  スクリプトを組んだ命令の次の命令


以下のコードはコピペできます。

[ENABLE]

aobscanmodule(INJECT,ge3.exe,F3 0F 10 4B 10 F3 0F 10)  // ge3.exeプロセス内から指定された16進数コードを探し、そのアドレスを取得する。
                                                       // ラベル名はINJECT
alloc(newmem,$1000,"ge3.exe"+638FDA)  // スクリプト用に0x1000バイトのメモリを確保する。
                                      // ラベル名はnewmem
alloc(healthBase, 8)        // 8バイトのメモリを確保する

label(code)                 // アドレスにラベル名をつける
label(return)               // 同上

newmem:
  cmp [rbx+150], 0          // プレイヤーのアドレスかそれ以外かを比較
  jne code                  // プレイヤーのアドレスでなければcode:へジャンプする
  mov [healthBase], rbx     // healthBaseにプレイヤーのアドレスをコピーする
code:
  movss xmm1,[rbx+10]       // プレイヤーのアドレス以外ならここにジャンプしてきてコードが実行される
  jmp return                // ↓にあるreturn:にジャンプする

INJECT:                     // INJECT:はアドレスge3.exe+638FDAを示している
  jmp newmem                // ge3.exe+638FDAのmovss xmm1, [rbx+10] をjmp newmemに書き換える
return:                     // ge3.exe+638FDAの次のコードge3.exe+638FDFにリターンする
registersymbol(INJECT healthBase)      // INJECTとhealthBaseをシンボルに登録する

[DISABLE]

INJECT:                    
  db F3 0F 10 4B 10        // 上で書き換えたge3.exe+638FDAのjmp newmemをmovss xmm1, [rbx+10]に戻す 
                           // movss xmm1, [rbx+10]は16進数コードだとF3 0F 10 4B 10になる
                            
unregistersymbol(INJECT healthBase)    // INJECTとhealthBaseをシンボルから解除する
dealloc(newmem healthBase)             // 確保したメモリを開放する(newmemとhealthBase)



関連記事

8 Comments

-

名無し

スクリプトについてなのですが、キーを押した時だけ実行するようにすることは可能でしょうか?
スクリプト自体のオンオフではなく、キー…仮にF1としますが、F1を押した時だけその部分のスクリプトを実行する…みたいな

例としては、下記のCode1部分をキーを押した時だけ有効にしたい

newmem:
mov [pickup],rdx
code1:
mov byte ptr [rdx+59],63
code2:
mov eax,[rdx+44]
add eax,FFFFFE95
jmp return

  • 2020/12/13 (Sun) 23:33
  • REPLY
ちーたー

なんでもチート

To -さん

コメントありがとうございます。
flag用のメモリを新たに確保し、1がセットされていれば改造コードを実行
0がセットされていれば元のコードを実行というふうにすれば上手くいきます。

[ENABLE]
alloc(flag, 4)
registersymbol(flag)

flag:
dd 0

newmem:
cmp [flag], 1
je code1
jmp originalcode

code1:
//改造コード
originalcode:
//元のコード

[DISABLE]
unregistersymbol(flag)
dealloc(flag)

さらに下記のようなスクリプトをチートテーブルに追加して
そのスクリプトに対して右クリックからホットキーを設定して完了です。

[ENABLE]
flag:
dd 1
[DISABLE]
flag:
dd 0


  • 2020/12/14 (Mon) 05:33
  • REPLY

名無し

To なんでもチートさん

詳しくありがとうございます
チートテーブルというのはLuaスクリプトでいいんですよね?
そのまま入力してみたのですが
[string "[ENABLE] ..."]:1: unexpected symbol near '['
というエラーが出て実行できませんでした
何が原因化分かりますでしょうか?

  • 2020/12/14 (Mon) 10:55
  • REPLY

名無し

ああ、すいません普通にスクリプトで良かったのですね
おかげさまで出来ました

ちなみに、"押している間だけ"実行というのも可能でしょうか?

  • 2020/12/14 (Mon) 11:23
  • REPLY
ちーたー

なんでもチート

To 名無しさん

出来たようで良かったです!
luaスクリプトにisKeyPressed関数とtimerクラスがあるので、それらを使うと実装できるかもしれません。
自分もluaはほとんど触れたことがないので具体的にどうすればいいかまではわからないです。
https://forum.cheatengine.org/index.php
上のサイトのサイト内検索からisKeyPressedと検索をかけて、各記事を参考にしてみてはいかがでしょうか。

  • 2020/12/16 (Wed) 08:36
  • REPLY

名無し

To なんでもチートさん

ありがとうございます
そちらのサイトで調べてみます

  • 2020/12/16 (Wed) 09:57
  • REPLY

-

主人公の見た目を他キャラに変更する事は可能でしょうか?

  • 2021/01/04 (Mon) 16:21
  • REPLY
ちーたー

なんでもチート

コメントありがとうございます。
少し調べてみましたが見つかりませんでした。
髪型や服などを変更するチートはありそうです。
今そういったCTやトレーナーが出回ってないのであれば自分で改造するしかないのですが、中には高度な知識を要求されるものもあるので、海外勢に質問してみるのも1つの手かもしれません。
お力になれず申し訳ないです。

  • 2021/01/06 (Wed) 20:00
  • REPLY