World of tanks бот своїми руками
Давним-давно з'явилася MMO World of tanks. І описувати її не бачу сенсу, як мінімум Ви про неї чули.
Сам почав в неї грати, як тільки відкрився ОБТ. Але любов до розкопок в чужому коді не давала спокою. Настав день і IDA (найпотужніший дизассемблер) була запущена.
Мої особисті переживання навряд чи кого зацікавлять, так що перейду до справи.
Для успішного написання бота необхідно володіти інформацією по:
- асемблеру
- мови C / C ++
- мови Python (движок гри працює на ньому)
- Python Embedded
- технології COM
- технології Direct3D
- впровадженню в чужій процес
- перехоплення функцій - хуках
- мови на якому пишеться бот (будь-який, наприклад: C #)
Матеріал пишеться за версією 0.9.7 бета тесту. Надалі статті так само будуть писатися під актуальні версії, будуть зауваження при наявності принципових змін.
У грі використовується движок BigWorld.
Python - його основа, що тільки на ньому не робиться. Python реалізований не сторонньої dll, але вшитий в сам клієнт, від куди нам і доведеться його запускати.
Чому саме запускати Python? У всіх іграх вся ігрова інформація зберігається в структурах певних особливостями мови на якому вона написана, і цю інформацію можна прочитати з процесу. У нашому випадку основа Python, використовується структура PyObject і її спадкоємці.
Знову ж таки, чому саме запускати Python? Для отримання інформації шляхом читання пам'яті процесу, доведеться писати свій інтерпретатор мови. Немає сенсу винаходити велосипед. Суть зводиться до виконання функцій Python безпосередньо в клієнті через запроваджену бібліотеку або машинний код. Є пара варіантів роботи з даними гри. Кожен раз запускати рядок з кодом на Python. Або довантажити модуль, який буде кешувати дані і з мінімальними витратами видавати в бот.
Отримання актуальних даних, стан яких не зміниться в процесі вилучення, краще проводити в кінці відтворення сцени (функція Direct3D EndScene). У цей момент картинка сформована повністю, але не виведена на екран, дані ще не встигли оновитися і знаходяться як би в замороженому стані. Цю функцію і необхідно відловити, тобто поставити на неї хук.
В поточна реалізація движка побудована на Direct3D дев'ятої версії, але є нюанс. Движок може використовувати один з інтерфейсів або IDirect3D9 (стандартний), або IDirect3D9Ex (розширений), в залежності від можливостей комп'ютера / операційної системи. За допомогою цих інтерфейсів створюють так само різні пристрої Direct3D (IDirect3DDevice9 / IDirect3DDevice9Ex), у яких точки входу в функцію закінчення відтворення сцени відрізняються. У поширеній схемі перехоплення відбувається наступне: завантажується бібліотека d3d9.dll, створюється IDirect3D9, створюється IDirect3DDevice9, впроваджується хук в EndScene. У нас виникає проблема, перехопивши EndScene від IDirect3DDevice9, ми можемо не отримати управління, якщо буде створено IDirect3DDevice9Ex. Не бачу сенсу в перехопленні обох функцій, тільки одна буде працювати.
Тому, простіше дочекатися створення пристрою, а потім поставити хук на тут версію, яку створить двигун. В клієнті є структура (не бійтеся НЕ Python), що відповідає за рендер (назвемо її RenderContext), яка містить пару полів з Direct3D і Direct3DDevice. Наше завдання полягає в очікуванні значень відмінних від нуля в цих полях. Як тільки вони будуть заповнені, ми будемо знати, що сам Direct3D і пристрій створені, і можна вішати хук.
А як же відмінності IDirect3D9 і IDirect3D9Ex? Відмінності полягають в більшій кількості функцій у IDirect3D9Ex, фактично інтерфейс IDirect3D9Ex є спадкоємцем IDirect3D9, так що функції IDirect3D9 мають ті ж індекси в IDirect3D9Ex, але вони мають різні точки входу, тому що їх представляють різні класи. Теж стосується і IDirect3DDevice9 / IDirect3DDevice9Ex. EndScene має один індекс в обох інтерфейсах, але різні точки входу. Підсумок: нам не важливий тип створеного пристрою Direct3DDevice, нам важлива точка входу. Знаючи покажчик на екземпляр пристрою (він в RenderContext), ми отримаємо необхідну точку входу.
На сьогодні все. У наступній статті будемо шукати RenderContext, зміщення покажчика на екземпляр Direct3D. Запасіться дизассемблером IDA.