Всем, привет!
В предыдущих частях мы подготовили нашу 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 по одной довольно тупой причине
Когда статической функции указать соглашение о вызове __thiscall, она подсвечивается как ошибка, что мне не нравится но при этом код компилируется
Но я люблю когда в коде нет красных полос
При __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, после чего будем создавать солнце
[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]
Попробуем скомпилировать и запустить
И видим что солнце автоматически подбирается, вот видео где можно это посмотреть
Видео
Но таким способом мы не можем сделать например следующее, если нам нужно подобрать когда солнце будет на земле.
Поэтому я буду в следующей части показывать как сделать это с помощью функции.
Так же у предмета есть функция которая возвращает время сколько предмет будет валяться на полу.
[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();
Жду ваши вопросы
Так же сделал репозиторий с исходным кодом проекта.
GitHub
Если нужен исходник:
PlantsVsZombies.rar
1 часть
2 часть
3 часть
4 часть
Пересмотрел все возможные предметы, начиная от 7, лень было переименовывать
В предыдущих частях мы подготовили нашу 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 по одной довольно тупой причине
Когда статической функции указать соглашение о вызове __thiscall, она подсвечивается как ошибка, что мне не нравится но при этом код компилируется
Но я люблю когда в коде нет красных полос
При __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, после чего будем создавать солнце
[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]
Попробуем скомпилировать и запустить
И видим что солнце автоматически подбирается, вот видео где можно это посмотреть
Видео
Но таким способом мы не можем сделать например следующее, если нам нужно подобрать когда солнце будет на земле.
Поэтому я буду в следующей части показывать как сделать это с помощью функции.
Так же у предмета есть функция которая возвращает время сколько предмет будет валяться на полу.
[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();
Жду ваши вопросы
Так же сделал репозиторий с исходным кодом проекта.
GitHub
Если нужен исходник:
PlantsVsZombies.rar
1 часть
2 часть
3 часть
4 часть
Пересмотрел все возможные предметы, начиная от 7, лень было переименовывать