#はじめに
簡単なメモリハックの仕方について書いていきます。動機などありません。試す方は自己責任でやってください。
今回は一番簡単であるズーム機能を実装する方法を書いていこうと思います(Visual Studio)
#メモリハックとは
Windows上のプロセスの仮想メモリに無理やり書き込み&読み込みをすることで、プロセスの挙動を変えることです。
簡単ですね。それでは具体的にコードを書いていきましょう。
#ベースアドレス&プロセスハンドルを取得しよう
Windows上ではプロセスを実行するとプロセスidというのが割り当てられます。
このプロセスidを使ってベースアドレスとプロセスハンドルを取得できます。
#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、ベースアドレス、プロセスハンドルはこの後も使うので取っておきましょう
#グローバルキーフックの仕方
一番簡単な方法でやります。
while (true)
{
if (GetAsyncKeyState(67)) {//仮想キーコードを入れる今回はC
}
}
#ポインターからアドレス取得
ポインターとは毎回プロセスが起動するとアドレスが変わるので、アドレスを計算するために必要なものです。
ポインターを取得する方法は次の記事で詳しく書きます。ここ
ここではそのポインターからアドレスを導き出す関数を書きます。
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;
}
関数はこれだけです。使い方は
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;
}
#書き込み
準備はできましたそれでは書き込みをしてみましょう
WriteProcessMemory(hProcess, (BYTE*)Address, &value, sizeof(value), nullptr);
これが書き込むためのAPIです。
Address
がアドレスで、value
には入れたい値のポインター、sizeof(value)
は入れる値の長さです。
#コード全体
#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を選びましょう。
ちゃんとCキーを押すとズームされるようになってますね!
#最後に
この技術を使えばほぼ何でもできてしまいます。悪質なハックも簡単に作れてしまいます。
なので絶対に悪用はしないでください。
ポインタースキャンの仕方は次回やります
次回
#参考
Guided hacking
GetAsyncKetState