Перейти к содержанию
  • записей
    5
  • комментариев
    12
  • просмотр
    2 071

Вызов внутреигровых функций


roma912

1 708 просмотров

Так, давно что-то ничего не писал в блог
Собственно время пришло

Данная статья является примером метода о котором рассказано в данном видео

 

Рассмотрим вызов игровой функции на примере перовой части игры Dishonored 
В данном примере будем восполнять себе количество маны, которая в игре не может регенерироваться полностью после использования череды способностей

Для начала нам необходимо найти само значение маны, благо оно храниться 4мя байтами в памяти и тут нет ничего сложного
bO0W6Yq.png

Теперь когда у нас есть адрес маны или же указатель на данный адрес, нам необходимо найти метод который как раз и перезаписывает значение
Делается это очень просто (F6)
yC4CANI.png

Нас интересует инструкция которая перезаписывает значение 1 раз
Выходим на эту инструкцию в дизассемблере и ставим брейк
suWyKYF.png

Вот мы и нашли инструкцию, далее просто выходим из него (Кнопка Execute till return)
Для того чтобы вызвать метод, необходимо узнать и проанализировать его параметры. Этим и займемся
vDQrTqo.png

 

В метод передается 2 параметра, один из которых находится в ecx (Указатель на базовый класс)
А второй передается в регистре ecx, до инструкции которая перезапишет значение


Данный кусок:
push ecx
mov ecx,esi


Регистр edx содержит все время один и тот же не изменяющийся адрес, т.е он статичен
Распишу параметры чтобы было более нагляднее
push ecx (Значение 50 в данный момент)
mov ecx, esi (В ecx записывается значение из esi (0x0AD0A000))
Теперь если немного проанализировать, то можно понять что 50 - это значение маны, которое будет присвоено игроку после применения способности
А 0x0AD0A000 это указатель на начало структуры игрока, т.е PlayerController* player;

Таким образом метод выглядит так: ManaMethod(PlayerController* player, int ManaOut);

Теперь можно приступать к написанию самого кода на c++, чтобы вызывать этот внутреигровой метод (При этом заранее найдем работающий указатель на класс игрока PlayerController)
Для вызова метода нам необходимо определить его соглашение о вызове, т.е в каком порядке туда передаются аргументы
Посмотреть все соглашения можно тут https://ru.wikipedia.org/wiki/Соглашение_о_вызове
Немного почитав можно увидеть что это соглашение __thiscall

thiscall — соглашение о вызовах, используемое компиляторами для языка C++ при вызове методов классов в объектно-ориентированном программировании.

Аргументы функции передаются через стек, справа налево. Очистку стека производит вызываемая функция. Соглашение thiscall отличается от cdecl соглашения только тем, что указатель на объект, для которого вызывается метод (указатель this), записывается в регистр ecx.


В самом коде объявляем этот метод для его вызова далее
 

//Метод принимает PlayerController* и ManaOut
//0x00AB17C0 - адрес которых хранился в edx и он статичен
typedef void*(__thiscall * _ManaType)(PlayerController* Player, int Mana);
_ManaType ManaFunc = (_ManaType)(0x00AB17C0);

Остается только написать вызов этого метода и на этом дело будет сделано

/*PlayerConroller.h */
struct PlayerController {

public:
	char pad_0000[2656]; //0x0000
	int Mana; //0x0A60


};

/*Метод который вызывается при создании потока из dll*/
void onAttach()
{
	while (true)
	{
		if (GetAsyncKeyState(VK_END))
		{
			PlayerController* Player = reinterpret_cast<PlayerController*>(*(DWORD*)(Base + 0x01052DE8)); //Получаем указатель на игрока PlayerController*
			ManaFunc(Player, 100);
		}

		Sleep(150);
	}
}

Собственно на этом моменте статья закончена
При нажатии на кнопку END внутри игры, мана восполняется до 100 единиц

  • Плюс 2

0 Комментариев


Рекомендуемые комментарии

Комментариев нет

Пожалуйста, войдите, чтобы комментировать

Вы сможете оставить комментарий после входа в



Войти
×
×
  • Создать...

Важная информация

Находясь на нашем сайте, Вы автоматически соглашаетесь соблюдать наши Условия использования.