メモリハックの作り方(Minecraft Windows 10)

#はじめに
簡単なメモリハックの仕方について書いていきます。動機などありません。試す方は自己責任でやってください。
今回は一番簡単であるズーム機能を実装する方法を書いていこうと思います(Visual Studio)
#メモリハックとは
Windows上のプロセスの仮想メモリに無理やり書き込み&読み込みをすることで、プロセスの挙動を変えることです。
簡単ですね。それでは具体的にコードを書いていきましょう。
#ベースアドレス＆プロセスハンドルを取得しよう
Windows上ではプロセスを実行するとプロセスidというのが割り当てられます。
このプロセスidを使ってベースアドレスとプロセスハンドルを取得できます。

```cpp
#include <iostream>
#include <Windows.h>
#include <vector>
#include <TlHelp32.h>

uintptr_t GetProcId(const wchar_t* procname) //プロセスID取得
{
    DWORD ProcId = 0;
    HANDLE hSnap = (CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
    if (hSnap != INVALID_HANDLE_VALUE)
    {
        PROCESSENTRY32 procentry;
        procentry.dwSize = sizeof(procentry);

        if (Process32First(hSnap, &procentry))
        {

            do
            {
                if (!_wcsicmp(procentry.szExeFile, procname))
                {
                    ProcId = procentry.th32ProcessID;
                    break;
                }
            } while (Process32Next(hSnap, &procentry));
        }
    }
    CloseHandle(hSnap);
    return ProcId;
}
uintptr_t GetModuleBaseAddress(DWORD procId, const wchar_t* modName)//ベースアドレス取得
{
    uintptr_t modBaseAddr = 0;
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, procId);
    if (hSnap != INVALID_HANDLE_VALUE)
    {
        MODULEENTRY32 modEntry;
        modEntry.dwSize = sizeof(modEntry);
        if (Module32First(hSnap, &modEntry))
        {
            do
            {
                if (!_wcsicmp(modEntry.szModule, modName))
                {
                    modBaseAddr = (uintptr_t)modEntry.modBaseAddr;
                    break;
                }
            } while (Module32Next(hSnap, &modEntry));
        }
    }
    CloseHandle(hSnap);
    return modBaseAddr;
}
int main()
{
    DWORD procId = GetProcId(L"Minecraft.Windows.exe");//実行中のMinecraft.Windows.exeのプロセスID
    uintptr_t BaseAddress = GetModuleBaseAddress(procId, L"Minecraft.Windows.exe");//ベースアドレスの取得
    if (BaseAddress != NULL) {
        HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, NULL, procId);//プロセスハンドル取得
    }
    return 0;
}
```
プロセスID、ベースアドレス、プロセスハンドルはこの後も使うので取っておきましょう
#グローバルキーフックの仕方
一番簡単な方法でやります。

```cpp
    while (true)
    {
        if (GetAsyncKeyState(67)) {//仮想キーコードを入れる今回はC

        }
    }
```
#ポインターからアドレス取得
ポインターとは毎回プロセスが起動するとアドレスが変わるので、アドレスを計算するために必要なものです。
ポインターを取得する方法は次の記事で詳しく書きます。[ここ](https://qiita.com/360tetsu360/items/0f93e097f0cf58836e57)
ここではそのポインターからアドレスを導き出す関数を書きます。

```cpp
uintptr_t FindDMAAddy(HANDLE hProc, uintptr_t ptr, std::vector<unsigned int> offsets)
{
    uintptr_t addr = ptr;
    for (unsigned int i = 0; i < offsets.size(); ++i)
    {
        ReadProcessMemory(hProc, (BYTE*)addr, &addr, sizeof(addr), 0);
        addr += offsets[i];
    }
    return addr;
}
```
関数はこれだけです。使い方は

```cpp
uintptr_t fovdirectadd = 0x0369BD40;
vector<unsigned int> fovoffset = { 0x28,0x130,0x1E8 };
int main()
{
    DWORD procId = GetProcId(L"Minecraft.Windows.exe");//実行中のMinecraft.Windows.exeのプロセスID
    uintptr_t BaseAddress = GetModuleBaseAddress(procId, L"Minecraft.Windows.exe");//ベースアドレスの取得
    if (BaseAddress != NULL) {
        HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, NULL, procId);//プロセスハンドル取得
        uintptr_t fovaddr = FindDMAAddy(hProcess, BaseAddress + fovdirectadd, fovoffset);//これが欲しいアドレスです
    }
    return 0;
}
```

#書き込み
準備はできましたそれでは書き込みをしてみましょう

```cpp
WriteProcessMemory(hProcess, (BYTE*)Address, &value, sizeof(value), nullptr);
```
これが書き込むためのAPIです。
``Address``がアドレスで、``value``には入れたい値のポインター、``sizeof(value)``は入れる値の長さです。
#コード全体
```cpp
#include <iostream>
#include <Windows.h>
#include <vector>
#include <TlHelp32.h>

using namespace std;

uintptr_t GetProcId(const wchar_t* procname) //プロセスID取得
{
    DWORD ProcId = 0;
    HANDLE hSnap = (CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
    if (hSnap != INVALID_HANDLE_VALUE)
    {
        PROCESSENTRY32 procentry;
        procentry.dwSize = sizeof(procentry);

        if (Process32First(hSnap, &procentry))
        {

            do
            {
                if (!_wcsicmp(procentry.szExeFile, procname))
                {
                    ProcId = procentry.th32ProcessID;
                    break;
                }
            } while (Process32Next(hSnap, &procentry));
        }
    }
    CloseHandle(hSnap);
    return ProcId;
}
uintptr_t GetModuleBaseAddress(DWORD procId, const wchar_t* modName)//ベースアドレス取得
{
    uintptr_t modBaseAddr = 0;
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, procId);
    if (hSnap != INVALID_HANDLE_VALUE)
    {
        MODULEENTRY32 modEntry;
        modEntry.dwSize = sizeof(modEntry);
        if (Module32First(hSnap, &modEntry))
        {
            do
            {
                if (!_wcsicmp(modEntry.szModule, modName))
                {
                    modBaseAddr = (uintptr_t)modEntry.modBaseAddr;
                    break;
                }
            } while (Module32Next(hSnap, &modEntry));
        }
    }
    CloseHandle(hSnap);
    return modBaseAddr;
}
uintptr_t fovdirectadd = 0x0369BD40;
vector<unsigned int> fovoffset = { 0x28,0x130,0x1E8 };
uintptr_t FindDMAAddy(HANDLE hProc, uintptr_t ptr, std::vector<unsigned int> offsets)
{
    uintptr_t addr = ptr;
    for (unsigned int i = 0; i < offsets.size(); ++i)
    {
        ReadProcessMemory(hProc, (BYTE*)addr, &addr, sizeof(addr), 0);
        addr += offsets[i];
    }
    return addr;
}
int main()
{
    DWORD procId = GetProcId(L"Minecraft.Windows.exe");//実行中のMinecraft.Windows.exeのプロセスID
    uintptr_t BaseAddress = GetModuleBaseAddress(procId, L"Minecraft.Windows.exe");//ベースアドレスの取得
    if (BaseAddress != NULL) {
        HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, NULL, procId);//プロセスハンドル取得
        uintptr_t fovaddr = FindDMAAddy(hProcess, BaseAddress + fovdirectadd, fovoffset);//これが欲しいアドレスです
        float value = 0.0f;
        while (true)
        {
            if (GetAsyncKeyState(67)) {
                value = 30.0f;
                WriteProcessMemory(hProcess, (BYTE*)fovaddr, &value, sizeof(value), nullptr);
            }
            else {
                value = 110.0f;
                WriteProcessMemory(hProcess, (BYTE*)fovaddr, &value, sizeof(value), nullptr);
            }
        }
    }
    return 0;
}
```
できましたね。これはCキーを押すとズームをするコードです。
ビルドするときはx64を選びましょう。
![Minecraft 2021_03_02 9_51_19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626228/43233076-6202-e0b5-fcae-740bc1ee1055.png)
![Minecraft 2021_03_02 9_51_23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626228/3e5d3d9b-8006-f491-33f1-4de2f454193f.png)
ちゃんとCキーを押すとズームされるようになってますね!

#最後に
この技術を使えばほぼ何でもできてしまいます。悪質なハックも簡単に作れてしまいます。
なので絶対に悪用はしないでください。
ポインタースキャンの仕方は次回やります
[次回](https://qiita.com/360tetsu360/items/0f93e097f0cf58836e57)
#参考
[Guided hacking](https://guidedhacking.com/threads/finddmaaddy-c-multilevel-pointer-function.6292/)
[GetAsyncKetState](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getasynckeystate)