0

私はリバースエンジニアリングを行っており、DLLを介してゲームのメモリにパッチを適用しています。通常、私は単一または複数の関数ですべてにパッチを適用する同じ古い方法に固執します。しかし、実行する必要のあるメモリ書き込みを定義する構造体配列を使用し、それらすべてを一度にループすることで、より適切に実行できるように感じます。管理がはるかに簡単なIMO。

でも、一定にしたいです。したがって、パッチごとにそのようなものにメモリを動的に割り当てる必要はなく、データは一度に(.rdataに)すべて存在します。これは、「bytesize」データを使用する単純なタスクです。たとえば、次のようになります。

struct struc_patch
{
    BYTE val[8];    // max size of each patch (usually I only use 5 bytes anyway for call and jmp writes)
                    // I can of course increase this if really needed
    void *dest;
    char size;
} patches[] =
{
    // simply write "01 02 03 04" to 0x400000
    {{0x1, 0x2, 0x3, 0x4}, (void*)0x400000, 4},
};
//[...]
for each(struc_patch p in patches)
{
    memcpy(p.dest, p.val, p.size);
}

しかし、タイプをもっと詳しく知りたいときは、バイト配列「90909090」として「0x90909090」のような整数を指定する方法が見つかりません。したがって、これは機能しません。

struct struc_patch
{
    BYTE val[8];    // max size of each patch (usually I only use 5 bytes anyway for call and jmp writes)
                    // I can of course increase this if really needed
    void *dest;
    char size;
} patches[] =
{
    // how to write "jmp MyHook"? Here, the jmp offset will be truncated instead of overlapping in the array. Annoying.
    {{0xE9, (DWORD)&MyHook - 0x400005}, (void*)0x400000, 5},
};

もちろん、大きな問題は、&MyHookをコンパイラーで解決する必要があることです。望ましい結果を得て、それを一定に保つ他の方法はありますか?

正直なところ、STLの経験はほとんどありません。したがって、それを使用した解決策がある場合は、コードを正しく理解するために、詳細に説明する必要があるかもしれません。私は大きなC/C ++ / WinAPI中毒者ですが、似たような性質で書かれたゲーム用なので、ぴったりです。

4

3 に答える 3

2

コンパイル時ではなく、STLの何もこれに役立つとは思いません。マクロで行ったのと同じように、テンプレートで行うための素晴らしい方法があるかもしれません。(バイトを区切るコンマ)

しかし、私はこのような単純なことをすることをお勧めします:

struct jump_insn
{
    unsigned char opcode;
    unsigned long addr;
} jump_insns[] = {
    {0xe9, (unsigned long)&MyHook - 0x400005}
};

struct mem
{
   unsigned char val[8];
} mems[] = {
    {1,2,3,4}
};

struct struc_patch
{
    unsigned char *val;    // max size of each patch (usually I only use 5 bytes anyway for call and jmp writes)
                    // I can of course increase this if really needed
    void *dest;
    char size;
} patches[] =
{
    // simply write "01 02 03 04" to 0x400000
    {(unsigned char*)(&mems[0]), (void*)0x400000, 4},

    // how to write "jmp MyHook"? Here, the jmp offset will be truncated instead of overlapping in the array. Annoying.
    {(unsigned char*)(&jump_insns[0]), (void*)0x400000, 5},
};

すべてをインラインで実行することはできず、さまざまな種類のパッチに新しいタイプが必要になりますが、それらは任意の長さにすることができ(8バイトだけでなく)、すべてが.rodataになります。

于 2012-09-29T00:09:30.053 に答える
0

これを処理するためのより良い方法は、アドレスの差をその場で計算することです。たとえば(ソース):

#define INST_CALL    0xE8

void InterceptLocalCode(BYTE bInst, DWORD pAddr, DWORD pFunc, DWORD dwLen)
{
    BYTE *bCode = new BYTE[dwLen];
    ::memset(bCode, 0x90, dwLen);
    DWORD dwFunc = pFunc - (pAddr + 5);

    bCode[0] = bInst;
    *(DWORD *)&bCode[1] = dwFunc;
    WriteBytes((void*)pAddr, bCode, dwLen);

    delete[] bCode;
}

void PatchCall(DWORD dwAddr, DWORD dwFunc, DWORD dwLen)
{
    InterceptLocalCode(INST_CALL, dwAddr, dwFunc, dwLen);
}

dwAddrは呼び出し命令を入れるアドレス、dwFuncは呼び出す関数、dwLenは置き換える命令の長さです(基本的には、入れるNOPの数を計算するために使用されます)。

于 2012-09-28T23:01:41.340 に答える
0

要約すると、私の解決策(Nicolasの提案に感謝):

#pragma pack(push)
#pragma pack(1)
#define POFF(d,a) (DWORD)d-(a+5)
struct jump_insn
{
    const BYTE opcode = 0xE9;
    DWORD offset;
};

struct jump_short_insn
{
    const BYTE opcode = 0xEB;
    BYTE offset;
};

struct struc_patch
{
    void *data;
    void *dest;
    char size;
};
#pragma pack(pop)

そして使用中:

// Patches
jump_insn JMP_HOOK_LoadButtonTextures = {POFF(&HOOK_LoadButtonTextures, 0x400000)};

struc_patch patches[] =
{
    {&JMP_HOOK_LoadButtonTextures, IntToPtr(0x400000)},
};

クラス メンバーの const を使用すると、すべてをより簡単かつ明確に定義でき、単純にすべて memcpy することができます。もちろん、pack プラグマは、memcpy が BYTE オペコードと DWORD 値の間の 3 つのアライン バイトをコピーしないようにするために必要です。

おかげさまで、パッチ適用方法をより堅牢にすることができました。

于 2012-09-29T02:57:48.770 に答える