#前回の記事の続き
前回はdllinjectで簡単なゲームハッキングをつくりましたが、今回はdllinjectの一番いいところ、関数の呼び出しを書いていきます。
#関数がいくつかあるプロジェクトを作る
#include <iostream>
#include <string>
#include <Windows.h>
void Function1() {
std::cout << "Function1 called" << std::endl;
}
void Function2(std::string Str) {
std::cout << Str << std::endl;
}
int Function3(int a, int b) {
return a + b;
}
int main()
{
std::cout << "Base Address:" << GetModuleHandle(NULL) << std::endl;//ベースアドレス
std::cout << "Function1 Address:" << "base address + " << (uintptr_t)&Function1 - (uintptr_t)GetModuleHandle(NULL) << std::endl;//Function1のアドレス
std::cout << "Function2 Address:" << "base address + " << (uintptr_t)&Function2 - (uintptr_t)GetModuleHandle(NULL) << std::endl;//Function2のアドレス
std::cout << "Function3 Address:" << "base address + " << (uintptr_t)&Function3 - (uintptr_t)GetModuleHandle(NULL) << std::endl;//Function2のアドレス
while (true)
{
if (GetAsyncKeyState(VK_ESCAPE)) //Escが押されたら終了
{
break;
};
Sleep(100);
}
}
適当に書いてみました。このコードが今回のターゲットです、、、かわいそうに、、
上からじゅんに
- ただの関数
- 引数がある関数
- 返り値がある関数
です。わざわざアドレスを探すのは面倒なので、それぞれのアドレスを出力させておきます。
#Dllinject用のDll
#include <Windows.h>
#include <TlHelp32.h>
#include <Psapi.h>
#include <iostream>
uintptr_t GetProcId(const wchar_t* procname) {
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 Loaded() {
std::cout << "Dll injected" << std::endl;
DWORD procId = GetProcId(L"target.exe");
uintptr_t BaseAddress = GetModuleBaseAddress(procId, L"target.exe");
using Function1 = void(__thiscall*)();
static Function1 _Function1 = reinterpret_cast<Function1>((uintptr_t)(BaseAddress + 69847));
_Function1();
using Function2 = void(__thiscall*)(std::string);
static Function2 _Function2 = reinterpret_cast<Function2>((uintptr_t)(BaseAddress + 69742));
_Function2("Hi from dll");
using Function3 = int(__thiscall*)(int a, int b);
static Function3 _Function3 = reinterpret_cast<Function3>((uintptr_t)(BaseAddress + 70462));
int ret = _Function3(10, 4);
std::cout << ret << std::endl;
return 0;
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)Loaded, hModule, 0, nullptr);
//FreeLibraryAndExitThread(hModule, 1);
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
初めのGetProcIdとGetModuleBaseAddressはインジェクトした先のプロセスのベースアドレスを取得するための関数です。
それではLoadedという関数の中を見ていきましょう
std::cout << "Dll injected" << std::endl;
でインジェクトしたよーという確認ログ。
uintptr_t BaseAddress = GetModuleBaseAddress(procId, L"target.exe");
でベースアドレス取得。
ここからが重要です!
using Function1 = void(__thiscall*)();
このコードで未知の関数を定義します。
そして、、
static Function1 _Function1 = reinterpret_cast<Function1>((uintptr_t)(BaseAddress + 69847));
で無理やりキャスト。アドレスを間違えないようにしましょう。
アドレスは、今回ターゲットにしているプロジェクトに出力させています。人によって違う場合があるので、絶対にコピペをしてコードを実行しようとはしないでください。
さてさてこれで関数を定義して無理やりキャストしたので、_Function1という変数はターゲットとなるプロジェクトのFunction1と同じものとなりますね。
あとは呼び出すだけ
_Function1();
うまく動いているなら、ターゲットの関数がしっかり仕事をして、Function1 calledと出力されているはずです。
引数がある場合も全く同じですが、引数を定義するときに入れておくだけです。
using Function2 = void(__thiscall*)(std::string);
返り値がある場合も全く同じようにできます。
#一応injectの仕方
お好きなインジェクターを使えばいいです。
cheat engineとかウサミミハリケーンなどはウィルスがなさそうで安心なので重宝しています。
#最後に
Dllinjectって最高ですよね。
次回は、virtual tableによる関数の呼び出しか、プロセスの関数のフッキングのどちらかを書きます。