Всем, привет!
В предыдущих частях мы подготовили нашу DLL и она уже может создавать солнце
Но нам нужно сделать конструктор и деструктор нашего объекта который создает солнце.
Для этого найдем конструктор, и перенесем инициализацию из функции sub_40D840 в настоящий конструктор.
На самом деле это функция другого класса, в которой инициализируеться наш объект.
К примеру это выглядит вот так
[SRC="c++"]
int __thiscall sub_4528B0(_DWORD *this)
{
v1 = this;
sub_452640((int)this);
v2 = operator new(0x57D8u);
if ( v2 )
v3 = sub_40A3C0(v1, (int)v2);
else
v3 = 0;
v4 = v1[49];
v5 = v1[48];
v1[538] = v3;
(*(void (__thiscall **)(int, _DWORD, _DWORD, int, int))(*(_DWORD *)v3 + 160))(v3, 0, 0, v5, v4);
(*(void (__stdcall **)(_DWORD))(*(_DWORD *)v1[200] + 12))(v1[538]);
(*(void (__stdcall **)(_DWORD))(*(_DWORD *)v1[200] + 48))(v1[538]);
return (*(int (__stdcall **)(_DWORD))(*(_DWORD *)v1[200] + 32))(v1[538]);
}
class FirstClass
{
/* .... */
/* 0A82 */ Creator* obj;
/* 0A86 */
public:
void func2(Creator*){};
void func3(Creator*){};
void func4(Creator*){};
void InitObj()
{
obj = new Creator;
obj->func1(0, 0, v5, v4);
func2(obj);
func3(obj);
func4(obj);
}
};
[/SRC]
И нам нужно перехватить конструктор вот этот
sub_40A3C0(v1, (int)v2);
Реализуем это в DLL.
[SRC="c++"]
LPVOID __fastcall Creator::Constructor(LPVOID lpPtr, LPVOID /* edx */, Creator* pCreator)
{
typedef Creator*(__thiscall *t)(LPVOID, Creator*);
t f = (t)0x0040A3C0;
g_Creator = pCreator;
Creator* pReturn = f(lpPtr, pCreator);
new (&pCreator->objCreatorEx) CreatorEx;
return pReturn;
}
[/SRC]
Несмотря на то что используется соглашение о вызове __thiscall, я использую __fastcall по одной довольно тупой причине![Smile Smile](https://forum.zone-game.info/images/smilies/smile.png)
Когда статической функции указать соглашение о вызове __thiscall, она подсвечивается как ошибка, что мне не нравится
но при этом код компилируется![Smile Smile](https://forum.zone-game.info/images/smilies/smile.png)
Но я люблю когда в коде нет красных полос![Smile Smile](https://forum.zone-game.info/images/smilies/smile.png)
При __thiscall первый аргумент передается функции с помощью регистра ECX, все остальное через стек.
А __fastcall два первых аргумента передаются через регистры это тот же ECX, и еще EDX, все остальное через стек.
Если посмотреть у меня в коде EDX просто не используется.
Теперь реализуем деструктор.
[SRC="c++"]
void *__thiscall sub_40AF10(void *this, char a2)
{
void *v2; // esi@1
v2 = this;
sub_40AF40((int)this);
if ( a2 & 1 )
operator delete(v2);
return v2;
}
[/SRC]
Тоже самое как и с конструктором.
[SRC="c++"]
Creator* __fastcall Creator:
estructor(Creator* pCreator, LPVOID /* edx */, bool bIsMemoryFreeUsed)
{
if (g_Creator == pCreator)
g_Creator = NULL;
pCreator->objCreatorEx.~CreatorEx();
typedef Creator*(__thiscall *t)(Creator*, bool);
t f = (t)0x0040AF10;
return f(pCreator, bIsMemoryFreeUsed);
}[/SRC]
Так же я еще добавил функцию рандома.
[SRC="c++"]
int __cdecl Creator::GameRandom(int nRange)
{
typedef unsigned int(__cdecl *t)(int);
t f = (t)0x005FF2A0;
return f(nRange);
}[/SRC]
А функция void CreateSun(int a2, int a3, int a4, int a5);
Изменилась на
[SRC="c++"]
enum ItemType : long {
Sun25 = 4, Sun15, Sun50
};
enum ItemSpeed : long {
NORMAL = 0, SLOW
};
void CreateItem(int posX, int posY, ItemType item, ItemSpeed speed);[/SRC]
Вот функция которая создает солнце, и она же сразу же помещает его в массив
я очень долго пытался понять что это за функция, предполагал что это std::array<CSun, 0x400>. Но больше всего это std::map<const char*, void*>;
Вот так место в памяти выделяется
Знаю точно что v1 = *(_DWORD *)(a1 + 0xC); номер места в памяти где будет расположен следующий элемент, ++*(_DWORD *)(a1 + 0x10); количество итемов на текущий момент, v2 = *(_DWORD *)(a1 + 4); максимальное количество итемов(которое было)
Предположительно массив вот такой
[SRC="c++"]
struct Item
{
/* 0000 */ unsigned long __uUnkValue0000[54];
/* 00D8 */
};
struct ArrayItems
{
/* 0000 */ Item* arrSuns;
/* 0004 */ unsigned long dwMaxCount;
/* 0008 */ unsigned long dwCapacity;
/* 000C */ unsigned long dwNextPosition;
/* 0010 */ unsigned long dwCount;
/* 0014 */ char* szName = "coins";
/* 0018 */
ArraySuns()
{
dwCapacity = 0x400;
dwCount = 0xDF69; // Не понимаю почему
dwMaxCount = 0;
dwNextPosition = 0;
arrSuns = new Item[0x400];
}
~ArraySuns()
{
delete[] arrSuns;
}
};[/SRC]
Но нам интересен не сам массив, а то какой элемент создается, в нашем случае это Item
Мне удалось понять некоторые переменные внутри него.
Теперь попробуем перехватить конструктор создания предмета. Можно перехватить вот здесь на моменте выделения памяти int __usercall sub_420D90@<eax>(int a1@<edi>)
Но это не конструктор, и мы не сможем поменять тогда значения на свои. А лишь получить адрес на объект.
Вот настоящий конструктор int __userpurge sub_432C60@<eax>(int a1@<eax>, int a2@<ecx>, Item* pItem, signed int X, signed int Y)
Обратите внимание на соглашение о вызове, это так же последствия оптимизации. Через EAX, передаётся тип предмета. А через ECX скорость движения нашего предмета.
Единственное что можно тут сделать так это написать функцию naked. Смотрим на код
Записываем свой naked
WriteInstructionJmp(0x0040F443, (UINT)ItemNakedConstructor);
Теперь напишем функцию где будем отслеживать нажатие кнопки F5, после чего будем создавать солнце![Smile Smile](https://forum.zone-game.info/images/smilies/smile.png)
[SRC="c++"]
DWORD WINAPI ThreadProc(LPVOID lpPtr)
{
while (true)
{
if (::GetAsyncKeyState(VK_F5) != 0)
{
if (g_Creator)
{
::Sleep(50);
g_Creator->objCreatorEx.PushSun();
}
}
}
return 0;
}
[/SRC]
Попробуем скомпилировать и запустить![Smile Smile](https://forum.zone-game.info/images/smilies/smile.png)
И видим что солнце автоматически подбирается, вот видео где можно это посмотреть![Smile Smile](https://forum.zone-game.info/images/smilies/smile.png)
Видео
Но таким способом мы не можем сделать например следующее, если нам нужно подобрать когда солнце будет на земле.
Поэтому я буду в следующей части показывать как сделать это с помощью функции.
Так же у предмета есть функция которая возвращает время сколько предмет будет валяться на полу.
[SRC="c++"]unsigned int Item::GetMaxWaitTime(void)
{
typedef unsigned int(__thiscall *t)(Item*);
t f = (t)0x00435DA0;
return f(this);
}[/SRC]
Тем самым мы можем сделать так чтобы солнце пропадало сразу же после того как коснётся земли.
Сделать это можно вот так в конструкторе:
pItem->dwTimeWait = pItem->GetMaxWaitTime();
Жду ваши вопросы![Smile Smile](https://forum.zone-game.info/images/smilies/smile.png)
Так же сделал репозиторий с исходным кодом проекта.
GitHub
Если нужен исходник:
PlantsVsZombies.rar
1 часть
2 часть
3 часть
4 часть
Пересмотрел все возможные предметы, начиная от 7, лень было переименовывать![Smile Smile](https://forum.zone-game.info/images/smilies/smile.png)
В предыдущих частях мы подготовили нашу DLL и она уже может создавать солнце
![Smile Smile](https://forum.zone-game.info/images/smilies/smile.png)
Но нам нужно сделать конструктор и деструктор нашего объекта который создает солнце.
Для этого найдем конструктор, и перенесем инициализацию из функции sub_40D840 в настоящий конструктор.
На самом деле это функция другого класса, в которой инициализируеться наш объект.
К примеру это выглядит вот так
[SRC="c++"]
int __thiscall sub_4528B0(_DWORD *this)
{
v1 = this;
sub_452640((int)this);
v2 = operator new(0x57D8u);
if ( v2 )
v3 = sub_40A3C0(v1, (int)v2);
else
v3 = 0;
v4 = v1[49];
v5 = v1[48];
v1[538] = v3;
(*(void (__thiscall **)(int, _DWORD, _DWORD, int, int))(*(_DWORD *)v3 + 160))(v3, 0, 0, v5, v4);
(*(void (__stdcall **)(_DWORD))(*(_DWORD *)v1[200] + 12))(v1[538]);
(*(void (__stdcall **)(_DWORD))(*(_DWORD *)v1[200] + 48))(v1[538]);
return (*(int (__stdcall **)(_DWORD))(*(_DWORD *)v1[200] + 32))(v1[538]);
}
class FirstClass
{
/* .... */
/* 0A82 */ Creator* obj;
/* 0A86 */
public:
void func2(Creator*){};
void func3(Creator*){};
void func4(Creator*){};
void InitObj()
{
obj = new Creator;
obj->func1(0, 0, v5, v4);
func2(obj);
func3(obj);
func4(obj);
}
};
[/SRC]
И нам нужно перехватить конструктор вот этот
sub_40A3C0(v1, (int)v2);
Реализуем это в DLL.
[SRC="c++"]
LPVOID __fastcall Creator::Constructor(LPVOID lpPtr, LPVOID /* edx */, Creator* pCreator)
{
typedef Creator*(__thiscall *t)(LPVOID, Creator*);
t f = (t)0x0040A3C0;
g_Creator = pCreator;
Creator* pReturn = f(lpPtr, pCreator);
new (&pCreator->objCreatorEx) CreatorEx;
return pReturn;
}
[/SRC]
Несмотря на то что используется соглашение о вызове __thiscall, я использую __fastcall по одной довольно тупой причине
![Smile Smile](https://forum.zone-game.info/images/smilies/smile.png)
Когда статической функции указать соглашение о вызове __thiscall, она подсвечивается как ошибка, что мне не нравится
![Smile Smile](https://forum.zone-game.info/images/smilies/smile.png)
![Smile Smile](https://forum.zone-game.info/images/smilies/smile.png)
Но я люблю когда в коде нет красных полос
![Smile Smile](https://forum.zone-game.info/images/smilies/smile.png)
При __thiscall первый аргумент передается функции с помощью регистра ECX, все остальное через стек.
А __fastcall два первых аргумента передаются через регистры это тот же ECX, и еще EDX, все остальное через стек.
Если посмотреть у меня в коде EDX просто не используется.
Теперь реализуем деструктор.
[SRC="c++"]
void *__thiscall sub_40AF10(void *this, char a2)
{
void *v2; // esi@1
v2 = this;
sub_40AF40((int)this);
if ( a2 & 1 )
operator delete(v2);
return v2;
}
[/SRC]
Тоже самое как и с конструктором.
[SRC="c++"]
Creator* __fastcall Creator:
![Big Grin Big Grin](https://forum.zone-game.info/images/smilies/biggrin.png)
{
if (g_Creator == pCreator)
g_Creator = NULL;
pCreator->objCreatorEx.~CreatorEx();
typedef Creator*(__thiscall *t)(Creator*, bool);
t f = (t)0x0040AF10;
return f(pCreator, bIsMemoryFreeUsed);
}[/SRC]
Так же я еще добавил функцию рандома.
[SRC="c++"]
int __cdecl Creator::GameRandom(int nRange)
{
typedef unsigned int(__cdecl *t)(int);
t f = (t)0x005FF2A0;
return f(nRange);
}[/SRC]
А функция void CreateSun(int a2, int a3, int a4, int a5);
Изменилась на
[SRC="c++"]
enum ItemType : long {
Sun25 = 4, Sun15, Sun50
};
enum ItemSpeed : long {
NORMAL = 0, SLOW
};
void CreateItem(int posX, int posY, ItemType item, ItemSpeed speed);[/SRC]
Вот функция которая создает солнце, и она же сразу же помещает его в массив
я очень долго пытался понять что это за функция, предполагал что это std::array<CSun, 0x400>. Но больше всего это std::map<const char*, void*>;
Вот так место в памяти выделяется
Знаю точно что v1 = *(_DWORD *)(a1 + 0xC); номер места в памяти где будет расположен следующий элемент, ++*(_DWORD *)(a1 + 0x10); количество итемов на текущий момент, v2 = *(_DWORD *)(a1 + 4); максимальное количество итемов(которое было)
Предположительно массив вот такой
[SRC="c++"]
struct Item
{
/* 0000 */ unsigned long __uUnkValue0000[54];
/* 00D8 */
};
struct ArrayItems
{
/* 0000 */ Item* arrSuns;
/* 0004 */ unsigned long dwMaxCount;
/* 0008 */ unsigned long dwCapacity;
/* 000C */ unsigned long dwNextPosition;
/* 0010 */ unsigned long dwCount;
/* 0014 */ char* szName = "coins";
/* 0018 */
ArraySuns()
{
dwCapacity = 0x400;
dwCount = 0xDF69; // Не понимаю почему
dwMaxCount = 0;
dwNextPosition = 0;
arrSuns = new Item[0x400];
}
~ArraySuns()
{
delete[] arrSuns;
}
};[/SRC]
Но нам интересен не сам массив, а то какой элемент создается, в нашем случае это Item
Мне удалось понять некоторые переменные внутри него.
Теперь попробуем перехватить конструктор создания предмета. Можно перехватить вот здесь на моменте выделения памяти int __usercall sub_420D90@<eax>(int a1@<edi>)
Но это не конструктор, и мы не сможем поменять тогда значения на свои. А лишь получить адрес на объект.
Вот настоящий конструктор int __userpurge sub_432C60@<eax>(int a1@<eax>, int a2@<ecx>, Item* pItem, signed int X, signed int Y)
Обратите внимание на соглашение о вызове, это так же последствия оптимизации. Через EAX, передаётся тип предмета. А через ECX скорость движения нашего предмета.
Единственное что можно тут сделать так это написать функцию naked. Смотрим на код
Записываем свой naked
WriteInstructionJmp(0x0040F443, (UINT)ItemNakedConstructor);
Теперь напишем функцию где будем отслеживать нажатие кнопки F5, после чего будем создавать солнце
![Smile Smile](https://forum.zone-game.info/images/smilies/smile.png)
[SRC="c++"]
DWORD WINAPI ThreadProc(LPVOID lpPtr)
{
while (true)
{
if (::GetAsyncKeyState(VK_F5) != 0)
{
if (g_Creator)
{
::Sleep(50);
g_Creator->objCreatorEx.PushSun();
}
}
}
return 0;
}
[/SRC]
Попробуем скомпилировать и запустить
![Smile Smile](https://forum.zone-game.info/images/smilies/smile.png)
И видим что солнце автоматически подбирается, вот видео где можно это посмотреть
![Smile Smile](https://forum.zone-game.info/images/smilies/smile.png)
Видео
Но таким способом мы не можем сделать например следующее, если нам нужно подобрать когда солнце будет на земле.
Поэтому я буду в следующей части показывать как сделать это с помощью функции.
Так же у предмета есть функция которая возвращает время сколько предмет будет валяться на полу.
[SRC="c++"]unsigned int Item::GetMaxWaitTime(void)
{
typedef unsigned int(__thiscall *t)(Item*);
t f = (t)0x00435DA0;
return f(this);
}[/SRC]
Тем самым мы можем сделать так чтобы солнце пропадало сразу же после того как коснётся земли.
Сделать это можно вот так в конструкторе:
pItem->dwTimeWait = pItem->GetMaxWaitTime();
Жду ваши вопросы
![Smile Smile](https://forum.zone-game.info/images/smilies/smile.png)
Так же сделал репозиторий с исходным кодом проекта.
GitHub
Если нужен исходник:
PlantsVsZombies.rar
1 часть
2 часть
3 часть
4 часть
Пересмотрел все возможные предметы, начиная от 7, лень было переименовывать
![Smile Smile](https://forum.zone-game.info/images/smilies/smile.png)