Пользователь
Регистрация: 14.07.2013
Адрес: Киев
Возраст: 29
Сообщений: 17
Отблагодарили 18 раз(а)
Рейтинг мнений:
|
Модификация игры ASM/C++ Часть 2.
Всем, привет!
Сразу же продолжу тему про модификацию игры Plants vs Zombies. Так как в прошлой части ничего интересного не было, а лишь только то что мы создали DLL, и подключили её к игре.
Это не очень интересно так как наша DLL пока еще ничего не делает полезного.
И так первое что нам понадобится это инициализация (запуск) нашей DLL в игре.
Сначала нужно подготовить наш проект, а именно нужно написать функции для работы с памятью игры. Чтобы не изобретать велосипед я нашел хороший пример в интернете.
Свернуть ↑
PHP код:
#pragma once
#define MSG_BUFFER_SIZE 8192
void WriteMemoryBYTES(unsigned int uAddress, void* bytes, unsigned int len);
void WriteMemoryQWORD(unsigned int uAddress, unsigned __int64 value);
void WriteMemoryDWORD(unsigned int uAddress, unsigned int value);
void WriteMemoryWORD(unsigned int uAddress, unsigned short value);
void WriteMemoryBYTE(unsigned int uAddress, unsigned char value);
void NOPMemory(unsigned int uAddress, unsigned int len);
void NULLMemory(unsigned int uAddress, unsigned int len);
void WriteInstruction(unsigned int uAddress, unsigned int uDestination, unsigned char uFirstByte);
void WriteInstructionCallJmpEax(unsigned int uAddress, unsigned int uDestination, unsigned int uNopEnd = NULL);
void WriteInstructionCall(unsigned int uAddress, unsigned int uDestination, unsigned int uNopEnd = NULL);
void WriteInstructionJmp(unsigned int uAddress, unsigned int uDestination, unsigned int uNopEnd = NULL);
void Msg(const char* title, const char* format, ...);
void Msg(const wchar_t* title, const wchar_t* format, ...);
template <typename Type, int Size, int RequiredSize>
struct TypeSizeCheck;
template <typename Type, int Size>
struct TypeSizeCheck<Type, Size, Size>
{};
#define UniqueSizeCheckName2(c) TypeSizeCheck__##c
#define UniqueSizeCheckName1(c) UniqueSizeCheckName2(c)
#define UniqueSizeCheckName UniqueSizeCheckName1(__COUNTER__)
#define CompileTimeSizeCheck(Type, Size) \
struct UniqueSizeCheckName : TypeSizeCheck<Type, sizeof(Type), Size> \
{}
template <typename Type, typename Member, int Offset, int RequiredOffset>
struct MemberOffsetCheck;
template <typename Type, typename Member, int Offset>
struct MemberOffsetCheck<Type, Member, Offset, Offset>
{};
#define CompileTimeOffsetCheck(Type, Member, Offset) \
typedef size_t Type##__##Member##__; \
struct Type##__##Member##__OffsetCheck : \
MemberOffsetCheck<Type, Type##__##Member##__, offsetof(Type, Member), Offset> \
{}
Свернуть ↑Развернуть ↓
Свернуть ↑
PHP код:
#include "stdafx.h"
#include "Utils.h"
HANDLE hGame = NULL;
void WriteMemoryBYTES(unsigned int uAddress, void* bytes, unsigned int len)
{
DWORD flOldProtect;
SIZE_T uNumberOfBytesWritten;
HANDLE hProcess;
if ((hProcess = hGame) && len)
{
VirtualProtectEx(hProcess, (LPVOID)uAddress, len, PAGE_WRITECOPY, &flOldProtect);
WriteProcessMemory(hProcess, (LPVOID)uAddress, bytes, len, &uNumberOfBytesWritten);
FlushInstructionCache(hProcess, (LPVOID)uAddress, len);
VirtualProtectEx(hProcess, (LPVOID)uAddress, len, flOldProtect, &flOldProtect);
}
}
void WriteMemoryQWORD(unsigned int uAddress, unsigned __int64 value) { WriteMemoryBYTES(uAddress, &value, sizeof(unsigned __int64)); }
void WriteMemoryDWORD(unsigned int uAddress, unsigned int value) { WriteMemoryBYTES(uAddress, &value, sizeof(unsigned int)); }
void WriteMemoryWORD(unsigned int uAddress, unsigned short value) { WriteMemoryBYTES(uAddress, &value, sizeof(unsigned short)); }
void WriteMemoryBYTE(unsigned int uAddress, unsigned char value) { WriteMemoryBYTES(uAddress, &value, sizeof(unsigned char)); }
void NOPMemory(unsigned int uAddress, unsigned int len)
{
unsigned int dword_count = (len / 4), byte_count = (len % 4);
unsigned char Byte = 0x90;
unsigned int Dword = 0x90666666;
DWORD flOldProtect;
SIZE_T uNumberOfBytesWritten;
HANDLE hProcess;
if ((hProcess = hGame) && len)
{
VirtualProtectEx(hProcess, (LPVOID)uAddress, len, PAGE_WRITECOPY, &flOldProtect);
while (dword_count != NULL)
{
WriteProcessMemory(hProcess, (LPVOID)uAddress, &Dword, sizeof(unsigned int), &uNumberOfBytesWritten);
uAddress += sizeof(unsigned int);
dword_count--;
}
while (byte_count != NULL)
{
WriteProcessMemory(hProcess, (LPVOID)uAddress, &Byte, sizeof(unsigned char), &uNumberOfBytesWritten);
uAddress += sizeof(unsigned char);
byte_count--;
}
FlushInstructionCache(hProcess, (LPVOID)uAddress, len);
VirtualProtectEx(hProcess, (LPVOID)uAddress, len, flOldProtect, &flOldProtect);
}
}
void NULLMemory(unsigned int uAddress, unsigned int len)
{
unsigned int dword_count = (len / 4), byte_count = (len % 4);
unsigned char Byte = 0x00;
unsigned int Dword = 0x00000000;
DWORD flOldProtect;
SIZE_T uNumberOfBytesWritten;
HANDLE hProcess;
if ((hProcess = hGame) && len)
{
VirtualProtectEx(hProcess, (LPVOID)uAddress, len, PAGE_WRITECOPY, &flOldProtect);
while (dword_count != NULL)
{
WriteProcessMemory(hProcess, (LPVOID)uAddress, &Dword, sizeof(unsigned int), &uNumberOfBytesWritten);
uAddress += sizeof(unsigned int);
dword_count--;
}
while (byte_count != NULL)
{
WriteProcessMemory(hProcess, (LPVOID)uAddress, &Byte, sizeof(unsigned char), &uNumberOfBytesWritten);
uAddress += sizeof(unsigned char);
byte_count--;
}
FlushInstructionCache(hProcess, (LPVOID)uAddress, len);
VirtualProtectEx(hProcess, (LPVOID)uAddress, len, flOldProtect, &flOldProtect);
}
}
void WriteInstruction(unsigned int uAddress, unsigned int uDestination, unsigned char uFirstByte)
{
unsigned char ExecLine[5];
ExecLine[0] = uFirstByte;
*((int*)(ExecLine + 1)) = (((int)uDestination) - (((int)uAddress) + 5));
WriteMemoryBYTES(uAddress, ExecLine, 5);
}
void WriteInstructionCallJmpEax(unsigned int uAddress, unsigned int uDestination, unsigned int uNopEnd)
{
unsigned char ExecLine[7];
ExecLine[0] = 0xE8;
*((int*)(ExecLine + 1)) = (((int)uDestination) - (((int)uAddress) + 5));
*((unsigned short*)(ExecLine + 5)) = 0xE0FF;
WriteMemoryBYTES(uAddress, ExecLine, 7);
if (uNopEnd && uNopEnd > (uAddress + 7))
NOPMemory((uAddress + 7), (uNopEnd - (uAddress + 7)));
}
void WriteInstructionCall(unsigned int uAddress, unsigned int uDestination, unsigned int uNopEnd)
{
unsigned char ExecLine[5];
ExecLine[0] = 0xE8;
*((int*)(ExecLine + 1)) = (((int)uDestination) - (((int)uAddress) + 5));
WriteMemoryBYTES(uAddress, ExecLine, 5);
if (uNopEnd && uNopEnd > (uAddress + 5))
NOPMemory((uAddress + 5), (uNopEnd - (uAddress + 5)));
}
void WriteInstructionJmp(unsigned int uAddress, unsigned int uDestination, unsigned int uNopEnd)
{
unsigned char ExecLine[5];
ExecLine[0] = 0xE9;
*((int*)(ExecLine + 1)) = (((int)uDestination) - (((int)uAddress) + 5));
WriteMemoryBYTES(uAddress, ExecLine, 5);
if (uNopEnd && uNopEnd > (uAddress + 5))
NOPMemory((uAddress + 5), (uNopEnd - (uAddress + 5)));
}
void Msg(const char* title, const char* format, ...)
{
char pBuff[MSG_BUFFER_SIZE];
va_list va;
va_start(va, format);
UINT32 ret_size = (UINT32)vsprintf_s(pBuff, MSG_BUFFER_SIZE, format, va);
if (ret_size < MSG_BUFFER_SIZE)
MessageBoxA(NULL, pBuff, title, MB_OK);
va_end(va);
}
void Msg(const wchar_t* title, const wchar_t* format, ...)
{
wchar_t pBuff[MSG_BUFFER_SIZE];
va_list va;
va_start(va, format);
UINT32 ret_size = (UINT32)vswprintf_s(pBuff, MSG_BUFFER_SIZE, format, va);
if (ret_size < MSG_BUFFER_SIZE)
MessageBoxW(NULL, pBuff, title, MB_OK);
va_end(va);
}
Свернуть ↑Развернуть ↓
Это мне понравилось не только тем что позволяет писать в память, а еще есть дефайны для проверки офстета и размера объекта. Что очень полезно при правильном создании классов игры.
CompileTimeSizeCheck
CompileTimeOffsetCheck
Так же создадим файлик Global.h
В котором напишем следующее
Свернуть ↑
PHP код:
#pragma once
// Вспомогательные дефайны для определения где произошла ошибка
#define WIDEN2(x) L##x
#define WIDEN(x) WIDEN2(x)
#define __WFILE__ WIDEN(__FILE__)
#define __WDATE__ WIDEN(__DATE__)
#define __WTIME__ WIDEN(__TIME__)
#define __WFUNCTION__ WIDEN(__FUNCTION__)
#define __WFUNCDNAME__ WIDEN(__FUNCDNAME__)
#define __WFUNCSIG__ WIDEN(__FUNCSIG__)
#include <windows.h>
#include <process.h>
#include <tlhelp32.h>
#include <new>
#include <iostream>
#include <time.h>
#include <tchar.h>
#include <vector>
#include <map>
using namespace std;
#define QWORD unsigned __int64
#include "Enumerations.h"
#include "Structures.h"
#include "Utils.h"
extern HANDLE hGame;
Свернуть ↑Развернуть ↓
Инклуды
PHP код:
#include "Enumerations.h"
#include "Structures.h"
Вспомогательные внутри их мы будем писать найденные в игре структуры и перечисления, для глобального доступа по всему проекту.
Подключим наш файлик в stdafx.h
PHP код:
#pragma once
#include "Global.h"
На этом подготовка проекта закончена.
Теперь приступим к инициализации нашей DLL в игре.
Для того чтобы проверить к какой игре подключена DLL. Мы в IDA Pro перейдем по адресу 0x401000 и скопируем первые 8 байт нашей игры.
Код:
00401000 53 55 57 8B CE E8 76 AE 14 00 8B 44 24 10 C7 06 SUWЛ+шvо..ЛD$.¦.
53 55 57 8B CE E8 76 AE - в памяти эти байты будут размещены в обратном порядке то есть вот так AE 76 E8 CE 8B 57 55 53
Теперь напишем функцию инициализации нашей библиотеки.
Свернуть ↑
PHP код:
void DllInitializer(HMODULE hDllModule)
{
// Проверка разположения нашей библиотеки
if (((UINT64)hDllModule) < 0x7E000000 && ((UINT64)(&hGame)) < 0x7F000000)
{
// Провека игры PlantsVsZombies
if (*((UINT64*)0x401000) == 0xAE76E8CE8B575553)
{
// Открываем просесс игры
if ((hGame = OpenProcess(PROCESS_ALL_ACCESS | PROCESS_VM_READ | PROCESS_VM_WRITE, FALSE, GetCurrentProcessId())))
{
// Вот здесь мы можем делать что угодно с нашей игрой:)
// Но мы просто отобразим окошко о успешном подключении нашей DLL.
MessageBox(NULL, _T("DLL PROCESS ATTACH"), _T("DLL"), MB_OK | MB_ICONINFORMATION);
CloseHandle(hGame);
hGame = NULL;
}
else
{
Msg(_T("Ошибка при загрузке"), _T("[%s]\n не был выдан доступ к игре"), __WFILE__);
ExitProcess(1);
}
}
else
{
Msg(_T("Ошибка при загрузке"), _T("[%s]\n это не игра PlantsVsZombies\nИли DLL не совместима с этой версией игры"), __WFILE__);
ExitProcess(1);
}
}
else
{
Msg(_T("Ошибка при загрузке"), _T("[%s]\n Плохое размещение DLL в памяти"), __WFILE__);
ExitProcess(1);
}
}
Свернуть ↑Развернуть ↓
Не забудьте запустить нашу функцию в DllMain
PHP код:
case DLL_PROCESS_ATTACH:
DllInitializer(hModule);
break;
Компилируем и убеждаемся что это работает с игрой. То есть процесс открыт для работы с ним.
Для примера, в первой части мы помним что патчили функцию __fptrap.
Давайте вернем её прежнее состояние, мы заменили 2 байтика 6A 02 на C3 90.
Код:
.text:006B5B17 nullsub_4 proc near ; DATA XREF: .data:off_718908
.text:006B5B17 ; .data:off_71890C ...
.text:006B5B17 retn
.text:006B5B17 nullsub_4 endp
.text:006B5B17
.text:006B5B17 ; ---------------------------------------------------------------------------
.text:006B5B18 db 2
.text:006B5B19 ; ---------------------------------------------------------------------------
.text:006B5B19 call __amsg_exit
.text:006B5B1E pop ecx
.text:006B5B1F retn
Для того чтобы изменить их через нашу DLL нам нужно узнать адрес в памяти игры. Помним что она была размещена по адресу 006B5B17. Почему мы здесь выбрали этот адрес, а не 002B4F17 смещение, спросите вы, а всё потому что мы будем работать с виртуальным адресом, а не с самим файлом игры. То было смещение именно в файле, а здесь это уже адрес в памяти.
То есть приложение запуститься и адреса уже будут виртуальные.
Теперь вернёмся к коду.
Для того чтобы записать 2 байта в память будем использовать функцию WriteMemoryWORD(...), так как WORD занимает 2 байта, это то что нам нужно.
Вместо функции
PHP код:
MessageBox(NULL, _T("DLL PROCESS ATTACH"), _T("DLL"), MB_OK | MB_ICONINFORMATION);
напишем
PHP код:
WriteMemoryWORD(0x006B5B17, 0x026A);
Скомпилируем и убедимся что при завершении игры вернулась наша ошибка
Runtime error R6002 floating point not loaded
Хотя если мы откроем EXE файл в WinHex и посмотрим что в нем ничего не изменилось. Мы видим байты C3 90. Магия? Теперь объясню что произошло.
Когда начала запускаться игра, наша библиотека в памяти игры изменила байтики, а не в EXE файле.
Мы могли бы пропатчить нашу программу и без WinHex. Это уже кому как удобней.
На этом конец этой части. Следующая часть будет еще интересней этой
Если нужен исходник:
PlantsVsZombies.rar
1 часть
|