19: 水平打ちとビーム

概要

16回の柱打ちの記事では、一度に一発しか打てない炎魔法を、柱状にして打つ事ができました。 その一方で、17回 でプレイヤーの向きの情報を取得する方法と、向きの情報からプレイヤーの前後左右上下ベクトルを算出する方法を知り、18回 で、それを実際にPlayerクラス等に実装しました。
今回は、向きの情報から得た前後左右ベクトルを使用して、柱状ではなく、水平状とビーム状に炎を打つ事を目標にやっていきます。
チート対象のゲームは、x86でチートの練習用に作られた、Pwn Adventure 3を使います。
Windows対象で、C++を言語として用いる。ソースコードはまとめてページの一番下にある。
また、この記事は18回の記事で作成したヘッダーファイルを使用し、 dllmain.cppの方は16回の記事のコードを元に作成します。
※ 今回は dllmain.cpp の方しか変更しません。

ビーム

先にビームの方を実装します。
柱打ちでは、ループ毎にgoup関数でプレイヤーを上げていましたが、ビームの場合は上げるのではなく前に進ませれば良いだけです。
dllmain.cpp以外を18回から、dllmain.cppは16回からコピーして持ってきた後、dllmain.cppに以下の関数を追加します。 続いて、以下のgoupだった所を上記のgoforwardに変えればなんと終了です。 それでは、実際にビルドして動かしてみましょう。下図のようになるはずです。

水平打ち

水平打ちは、一回左に半分ずらしてから、右に移動して打つループをさせないといけないので、一手間かかりまが、dllmain.cppだけ変更する事には代わり無く、追加するコードも少しです。 まず、下図のように必要な関数を用意します。 そして、インラインフックで埋め込むコードの方は以下のようにします。 これで、ビルドして動かしてみると、以下のようになるはずです。

ソースコード

ビームのdllmain.cpp

#include "pch.h"
#include "general-cheats.h"
#include "player.h"

using namespace std;

Player* player;


Vector3 curLoc;
void __stdcall SetCurrentLoc() {
    curLoc = player->GetCurrentLocation();
}
void __stdcall RestoreLoc() {
    player->SetLocation(curLoc, { 1, 1, 1 });
}

void __stdcall goup() {
    Vector3 value;
    value.x = 0; value.y = 100.0; value.z = 0;
    player->MoveLocation(value, { 0, 1, 0 });
}

void __stdcall goforward() {
    Vector3 fv = player->GetCurrentForwardVector();
    player->MoveLocation(fv, 300.0, { 1, 1, 1 });
}


DWORD jmpBackAddr;
void __declspec(naked) cheats() {
    __asm
    {
        push ebx
        pushad
        call SetCurrentLoc
        popad
        mov ebx, 0x20
        push ebx
        mov eax, [ebp + 0x8]
        mov ecx, [ebp + 0xc]
        lea edx, [eax + 0x70]
        push edx
        mov eax, [ecx]
        call[eax + 0x80]
        call goforward
        pop ebx
        dec ebx
        jne $ - 0x1a
        pushad
        call RestoreLoc
        popad
        pop ebx
        mov ecx, jmpBackAddr
        jmp ecx
    }
}

// cheatsに飛ばす命令を作成して返す
//   B9 xx xx xx xx  -  mov ecx, cheatsの先頭アドレス
//   FF E1           -  jmp ecx
BYTE* initJmpToCheats(DWORD size) {
    BYTE* jmpToCheats = (BYTE*)malloc(size);
    memset(jmpToCheats, 0x90, size);

    BYTE buf1[] = { 0xB9 };
    memcpy(jmpToCheats, buf1, sizeof(buf1));

    DWORD* cheatsAddr = (DWORD*)cheats;
    memcpy(&jmpToCheats[sizeof(buf1)], &cheatsAddr, sizeof(DWORD));

    BYTE buf2[] = { 0xFF, 0xE1 };
    memcpy(&jmpToCheats[5], buf2, sizeof(buf2));

    return jmpToCheats;
}

DWORD WINAPI ThreadMain(LPVOID params) {
    DWORD baseAddr = (DWORD)GetModuleHandle("GameLogic.dll");
    DWORD* target = (DWORD*)(baseAddr + 0x3b2ba);
    DWORD size = 15;
    // 8b 4d 0c            MOV  ECX, [EBP+0xC]   <--  target
    // 8d 50 70            LEA  EDX, [EAX+0x70]
    // 52                  PUSH EDX
    // 8b 01               MOV  EAX, [ECX]
    // ff 90 80 00 00 00   CALL [EAX+0x80]

    jmpBackAddr = (DWORD)target + size;
    BYTE* jmpToCheats = initJmpToCheats(size);

    player = new Player();

    DWORD curProtection;
    VirtualProtect(target, size, PAGE_EXECUTE_READWRITE, &curProtection);
    memcpy(target, jmpToCheats, size);
    DWORD temp;
    VirtualProtect(target, size, curProtection, &temp);

    return 0;
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
    if (ul_reason_for_call == DLL_PROCESS_ATTACH) {
        CreateThread(0, 0, ThreadMain, hModule, 0, 0);
    }
    return TRUE;
}

水平打ちのdllmain.cpp

#include "pch.h"
#include "general-cheats.h"
#include "player.h"

using namespace std;

Player* player;


Vector3 curLoc;
void __stdcall SetCurrentLoc() {
    curLoc = player->GetCurrentLocation();
}
void __stdcall RestoreLoc() {
    player->SetLocation(curLoc, { 1, 1, 1 });
}

void __stdcall goup() {
    Vector3 value;
    value.x = 0; value.y = 100.0; value.z = 0;
    player->MoveLocation(value, { 0, 1, 0 });
}
void __stdcall goforward() {
    Vector3 fv = player->GetCurrentForwardVector();
    player->MoveLocation(fv, 300.0, { 1, 1, 1 });
}
void __stdcall goleft() {
    Vector3 lv = player->GetCurrentLeftVector();
    player->MoveLocation(lv, 100.0, { 1, 0, 1 });
}
void __stdcall goright() {
    Vector3 rv = Invert(player->GetCurrentLeftVector());
    player->MoveLocation(rv, 100.0, { 1, 0, 1 });
}

void __stdcall SetHalfLeft() {
    int i;
    for (i = 0; i < 0x4; i++)
        goleft();
}

DWORD jmpBackAddr;
void __declspec(naked) cheats() {
    __asm
    {
        push ebx
        pushad
        call SetCurrentLoc
        call SetHalfLeft
        popad
        mov ebx, 0x8
        push ebx
        mov eax, [ebp + 0x8]
        mov ecx, [ebp + 0xc]
        lea edx, [eax + 0x70]
        push edx
        mov eax, [ecx]
        call[eax + 0x80]
        call goright
        pop ebx
        dec ebx
        jne $ - 0x1a
        pushad
        call RestoreLoc
        popad
        pop ebx
        mov ecx, jmpBackAddr
        jmp ecx
    }
}

// cheatsに飛ばす命令を作成して返す
//   B9 xx xx xx xx  -  mov ecx, cheatsの先頭アドレス
//   FF E1           -  jmp ecx
BYTE* initJmpToCheats(DWORD size) {
    BYTE* jmpToCheats = (BYTE*)malloc(size);
    memset(jmpToCheats, 0x90, size);

    BYTE buf1[] = { 0xB9 };
    memcpy(jmpToCheats, buf1, sizeof(buf1));

    DWORD* cheatsAddr = (DWORD*)cheats;
    memcpy(&jmpToCheats[sizeof(buf1)], &cheatsAddr, sizeof(DWORD));

    BYTE buf2[] = { 0xFF, 0xE1 };
    memcpy(&jmpToCheats[5], buf2, sizeof(buf2));

    return jmpToCheats;
}

DWORD WINAPI ThreadMain(LPVOID params) {
    DWORD baseAddr = (DWORD)GetModuleHandle("GameLogic.dll");
    DWORD* target = (DWORD*)(baseAddr + 0x3b2ba);
    DWORD size = 15;
    // 8b 4d 0c            MOV  ECX, [EBP+0xC]   <--  target
    // 8d 50 70            LEA  EDX, [EAX+0x70]
    // 52                  PUSH EDX
    // 8b 01               MOV  EAX, [ECX]
    // ff 90 80 00 00 00   CALL [EAX+0x80]

    jmpBackAddr = (DWORD)target + size;
    BYTE* jmpToCheats = initJmpToCheats(size);

    player = new Player();

    DWORD curProtection;
    VirtualProtect(target, size, PAGE_EXECUTE_READWRITE, &curProtection);
    memcpy(target, jmpToCheats, size);
    DWORD temp;
    VirtualProtect(target, size, curProtection, &temp);

    return 0;
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
    if (ul_reason_for_call == DLL_PROCESS_ATTACH) {
        CreateThread(0, 0, ThreadMain, hModule, 0, 0);
    }
    return TRUE;
}