14

int*私がゲームであり、私のヘルスを含むグローバルがあると仮定しましょう。ゲーム トレーナーの仕事は、ゴッド モードを実現するために、この値を任意の値に変更することです。ゲームトレーナーのチュートリアルを調べて、それらがどのように機能するかを理解しました。一般的なアイデアは、メモリスキャナーを使用して特定の値のアドレスを見つけようとすることです。次に、dll などを注入してこのアドレスを変更します。

しかし、グローバルとそのア​​ドレスがアプリを実行するたびに変化する単純なプログラムを作成したint*ので、ゲームトレーナーがこれらのアドレスをハードコーディングする方法がわかりませんか? それとも私の例は間違っていますか?

私は何が欠けていますか?

4

5 に答える 5

7

これは通常、静的変数から問題の変数を含むヒープ アドレスまでポインター チェーンをトレースすることによって行われます。例えば:

struct CharacterStats
{
    int health;
    // ...
}

class Character
{
public:
    CharacterStats* stats;

    // ...

    void hit(int damage)
    {
        stats->health -= damage;
        if (stats->health <= 0)
            die();
    }
}


class Game
{
public:
    Character* main_character;
    vector<Character*> enemies;
    // ...
}

Game* game;

void main()
{
    game = new Game();
    game->main_character = new Character();
    game->main_character->stats = new CharacterStats;

    // ...

}

この場合、mikek3332002 のアドバイスに従って Character::hit() 関数内にブレークポイントを設定し、減算を無効にすると、敵を含むすべてのキャラクターが無敵になります。解決策は、「ゲーム」変数 (データ セグメントまたは関数のスタックに存在する必要があります) のアドレスを見つけ、ヘルス変数のアドレスが見つかるまですべてのポインターをたどることです。

Cheat Engine などの一部のツールには、これを自動化する機能があり、自分でポインター チェーンを見つけようとします。ただし、より複雑なケースでは、おそらくリバース エンジニアリングに頼る必要があります。

于 2010-05-28T06:15:02.563 に答える
1

編集: 幸運だったようですが、ポインターの最後の 3 つの数字は同じままのようです。おそらく、これは ASLR が作動して、ベース イメージ アドレスか何かを変更しているのでしょうか?

aaahhhh私の悪い、私はprintfに%dを使用してアドレスを印刷し、%pではありませんでした。%p を使用した後、アドレスは同じままでした

#include <stdio.h>

int *something = NULL;

int main()
{
    something = new int;
    *something = 5;

    fprintf(stdout, "Address of something: %p\nValue of something: %d\nPointer Address of something: %p", &something, *something, something);
    getchar();
    return 0;
}
于 2010-05-28T05:19:15.117 に答える
1

Gameshark のコードなどを特定する方法は、アプリケーションのメモリ イメージをダンプし、1 つのことを実行してから、何が変更されたかを調べることでした。いくつかの変更があるかもしれませんが、探すべきパターンがあるはずです。たとえば、メモリをダンプして、撃って、メモリをダンプして、もう一度撃って、メモリをダンプして、リロードします。次に、変更を探して、弾薬がどこにどのように保管されているかを把握します。健康についても同様ですが、さらに多くのことが変わります (少なくとも移動するため)。ただし、「外部効果」を最小限に抑えるときに行うのが最も簡単です。たとえば、多くのことが起こっているため、銃撃戦中にメモリダンプを比較しようとしないでください。その性質の何か。

于 2010-05-28T19:23:21.863 に答える
1

動的に割り当てられた変数の例

見つけたい値は、ライフが 0 になってゲーム オーバーになるのを防ぐためのライフの数です。

  1. ゲームをプレイして、このインスタンスの lifes 変数の場所を検索します。
  2. 見つかったら、逆アセンブラ/デバッガを使用して、その場所の変更を監視します。
  3. 命を失う。
  4. デバッガーは、デクリメントが発生したアドレスを報告する必要があります。
  5. その命令をノーオペレーションに置き換えます

tsearch というプログラムからこのパターンを取得しました


このトピックの調査から見つかったいくつかの関連 Web サイト:

于 2010-05-28T05:52:03.683 に答える
1

アクセス ポインターの検出は非常に面倒であり、静的メモリ値をさまざまなコンパイラやゲーム バージョンに適応させるのは困難です。

malloc()、free() などの API フックでは、ポインターをたどる方法とは異なる方法があります。ディスカバリーは、すべての動的メモリー割り当てを記録し、並行してメモリー検索を行うことから始まります。見つかったヒープ メモリ アドレスは、記録されたメモリ割り当てと逆照合されます。オブジェクトのサイズと、オブジェクト内の値のオフセットがわかります。これをバックトレースで繰り返し、malloc() 呼び出しまたは C++ コンストラクターのジャンプバック コード アドレスを取得します。その情報を使用して、そこから割り当てられるすべてのオブジェクトを追跡および変更できます。オブジェクトをダンプして比較すると、さらに興味深い値が見つかります。たとえば、ユニバーサル エリート ゲーム トレーナー「ugtrain」は Linux でこのように動作します。LD_PRELOAD を使用します。適応は「objdump -D」で動作

参照: http://en.wikipedia.org/wiki/Trainer_%28games%29

Ugtrain のソース: https://github.com/sriemer/ugtrain

malloc() フックは次のようになります。

static __thread bool no_hook = false;

void *malloc (size_t size)
{
    void *mem_addr;
    static void *(*orig_malloc)(size_t size) = NULL;

    /* handle malloc() recursion correctly */
    if (no_hook)
        return orig_malloc(size);

    /* get the libc malloc function */
    no_hook = true;
    if (!orig_malloc)
        *(void **) (&orig_malloc) = dlsym(RTLD_NEXT, "malloc");

    mem_addr = orig_malloc(size);

    /* real magic -> backtrace and send out spied information */
    postprocess_malloc(size, mem_addr);
    no_hook = false;

    return mem_addr;
}

ただし、見つかったメモリ アドレスがメモリ内の実行可能ファイルまたはライブラリ内にある場合は、ASLR が動的な原因である可能性があります。Linux では、ライブラリは PIC (位置に依存しないコード) であり、最新のディストリビューションではすべての実行可能ファイルは PIE (位置に依存しない実行可能ファイル) です。

于 2015-02-19T13:39:37.653 に答える