Перейти к содержанию

ООП в играх и в обмане игр. Стоит ли его применять в ....


Рекомендуемые сообщения

ООП состоит на основе трёх концепций: инкапсуляция, наследование и полиморфизм. Но модель ООП на этом не заканчивается. В каждом отдельном языке программирования добавлены свои "прибамбасы" относительно наследования и полиморфизма, что формирует модель ООП соответствующую языку программирования. Из всех языков которых я знаю или сталкивался С++ и его продолжения наиболее сильные в ООП.

О концепциях ООП

Класс - это тип или описание объекта. На основе этого описания выделятеся память и строится объект. Когда один объект строит другой возникает цепочка указателей. Всегда есть статический адрес начального объекта, по которому можно добраться через цепочку указателей до данных вложенных объектов.

Также существуют:

vftable - таблица виртуальных функций

vbtable (virtual base class table) - таблица базового виртуального класса

Обе таблицы находятся в статической памяти и могут служить описанием работы с данными и методами как для одного так и нескольких объектов.

Инкапсуляция характеризует вложенные в объект данные, например, это - жизнь, здоровье, опыт и т.п. Напрямую извне к этим данным в понятии инкапсуляции обращаться нельзя. К ним можно обращаться только через его методы и свойства - нам это только в пользу. Можно например сделать чит-код "убийство" с одного удара внедрением отладочного кода в метод класса игрока. Этот метод работает как для вашего героя так и для ботов и всё это благодаря тому что разработчик игры использовал ООП в программировании игры.

Наследование характеризует ситуацию, когда некоторый объект был создан на основе своего описания и описания наследуемого родительского класса. В языке С++ наследовать можно как один класс так и несколько. При простом наследовании, где наследование происходит от одного класса, возможно наследование виртуальных функций, а при множественном наследование возможно наследование виртуальных классов. Существует малая вероятность что в играх сразу встретится наследование виртуальных функций и классов. Обычно, что-то одно и чаще наследование виртуальных функций.

Сложное наследование некоторых современных стратегиях это головная боль тех кто умеет внедрять чит-коды.

Полиморфизм существует при наследовании объектов при определении виртуальных методов или классов. Также стоит обратить внимание на два специфичных метода. Конструктор - создаёт объект и деструктор - разрушает его для того чтобы тот не занимал места в памяти операционной системы.

Насчёт "прибамбасов" таких как перегрузка методов, перегрузка операторов, шаблоны(в С++)... и т.д. Об этом я, может быть, напишу, на примере конкретной игры. Например, в СТАЛКЕРЕ управление объектами осуществятся, скорее всего, через описание шаблона и итератора...

ООП в играх да и в целом позволяет строить модель программы в удобном виде. В книгах пишут, что если код размеров от 10000-100000 строк, то в нём невозможно ориентироваться без применения ООП чего не говорить уже о большем размере кода. Дело в том не понятно к какой абстракции принадлежит та или иная функция, тот или иной метод. Если программа размером например не более 1000 строк, то вполне можно обойтись без ООП - программа будет работать быстрее, если будет правильно написана, но иногда быстрее что-то написать с ОПП - и получится 400 строк вместо 1000.

В современной игре в которой очень много объектов обязательно применяется ООП. В этом случае в ней может быть множество объектов разных типов, но лишь один метод (типа B или C) будет работать со здоровьем всех игроков или урона зданий, или даже уменьшение и увеличения опыта, или даже чего-нибудь другого. Может быть, даже не уменьшение, а просто копирование уменьшенного параметра.

Пару слов думаю, стоит сказать про скрипты LUA и другие. Они позволяют задавать новые свойства и поведения объектов без перекпомпляции классов, методов, всего кода. Если вы знаете как построить запрос LUA-интерпритатору, то вы можете написать достаточно полезные чит-коды - тоже самое бессмертие или добавление ресурсов. Также снова напомню, что скриптовые моды, например могут не позволить, например, сделать стрельбу без перезарядки и в некоторых случаях нельзя сделать кого-то бессмертным, может быть и можно, но понять как это сделать не просто - копаться в распакованных ресурсов игры в надежде найти параметр бессмертия или нужно очень хорошо уметь пользоваться дизассемблерером - перехватывать запросы к интерпритатору и анализировать структуры на входе и на выходе из интерпретатора.

О том как обманывать игры с сложным ООП программированием будет в моих статьях, я надеюсь =) Пока не все игры мне поддаются, просто на это я выделяю мало времени. Сначала надо разобраться как выглядит виртуальные таблицы при виртуальном наследовании классов.

Что касается применения ООП в написании трейнера. Могу сказать, что это дело вкуса. Есть люди которые от и до пишут трейнер на ассемблере приследуя цель вылизанной от и до программы меньшего размера. Только это не драйвер, который требует лучшей производительности, потраченный труд уходит в пустую.

Как бы там ни было, для мозгов человека удобнее воспринимать наиболее структурированную информацию - где чит-код является классом иди структурой. А ими управляет специальный класс. С этой стороны трейнеры лучше писать с применением ООП, чем на ассемблере или процедурном с WinApi. Однако, если бы трейнер был очень нужной полезной программой-драйвером, то его без сомнений лучше писать на ассемблере, в "тяжелом" случае на С.

По поводу языка на котором лучше писать. Это дело вкуса, но если интересно моё мнение, то писать нужно на языке С++ с применением ООП. С++ это системный язык. Дельфи, Байсик больше прикладного характера, но системные программы можно также писать и они будут также работать. Использовать ли визуальное оформление, писать ли на VC++? Да, можно, это не критично. Вам не придётся писать дополнительного кода.

В завершении статьи. Трейнеры для синголовых игр на сегодняшний день ещё не использовали весь возможный потенциал в обмане игр:

1. Нет универсальности трейнеров для одного вида игры с разными патчами и локализациями.

2. Думаю, что нет ни одного трейнера с инжектом игрового скрипта или заданием скрипта для игрового скрипт-интерпритатора в играх где он используется - я довольно много дизассемблировал трейнеров и этого не видел (другое дело моды). Предпосылки к этому есть у MHS и CE - и там и там можно создавать удалённый поток и подгружать библиотеки, которые будут своего рода входным вводом задания скриптов для игрового скрит-интерпретатора.

Если вы с жадностью хотите ещё прочитать информацию, то читайте её по ссылкам: презентация, более подробная версия реверсинга С++

Прочтение этой статьи позволит ориентироваться в связях структур игровых объектов – позволит осмысленно подходить к поиску многоуровневых указателей, а также внедрять чит-коды с использованием фильтров.

Часть1. Как выглядит объект в памяти. Первое знакомство

Класс можно представить так:

Название класса:

Начало:

Описание данных класса;

Описание методов класса;

Конструктор;

Деструктор;

Конец описания класса.

Начало и конец в дальнейшим обозначается фигурными скобками {…}

«Описание данных класса» - это описание переменных определенного типа. Например, переменная "int а" имеет тип int (это целое число 4 байта со знаком (подробнее о типах см. в Интернете)

Методы класса представляют собой функции, которые возвращают или не возвращают значения. Например,

void fun(){printf("Hello World!“}. В данном примере присутствие void, говорит о том, что функция не возвращает значения.

Привязка данных и методов к классу называется инкапсуляцией.

Существует такое понятие как ограничение видимости при инкапсуляции. Ограничение описывается основными директивами: private, protected, public:

private – доступ к данным только изнутри объекта,

protected – доступ к данным изнутри объекта и из производных классов.

public – доступ к данным из любого места.

Давайте посмотрим наконец как выглядит класс в С++.


int c; //по умолчанию доступ будет как private
public:
void pc(){printf("Method C %dn",c);}
};
class C{

Этот класс имеет описание закрытой переменной с и открытую функцию pc, которая выводит на «досовский» экран значение переменной с.

Напомню, что это всего лишь описание будущего объекта. Любой объект инициализируется «конструктором» и разрушается «деструктором». В примере выше конструктор и деструктор не объявлены, но они вызываются по умолчанию.

Давайте посмотрим наш пример с конструктором, который инициализирует данные с равной единице.


int c; //тут может быть здоровья игрока к примеру
public:
C(int aa=100){c=aa;} // конструктор
void pc(){printf("Method C %dn",c);}
};
class C{

Конструктор, принимает по умолчанию входной параметр аа равный 1 и присваивает его с.

Пару слов о пользе конструкторов. Конструктор в играх может создавать объект по входным параметрам. В читинге конструктор может пригодиться при принудительном создании нового объекта или создании объекта с изменёнными свойствами. Ну, например, когда игра загружается вы, скорее всего, можете подменить скин своего персонажа, или изменить его характиристику - что он как бы является торговцем, которого убить невозможно. Ну, ладно... идём дальше, т.к. это отдельная тема.

Деструктор выглядел бы так:

~C(…);

Более подробно см. в Интернете.

Подведём итог. Мы знаем, как выглядят и работают класс, конструктор, деструктор, данные, методы.

Создадим проект в VC++ проект консольного варианта:



class Player{
int NameID,Health,Ammo;
public:
void set(int a, int b, int c){NameID=a; Health=b; Ammo=c;}
void getInfoX(){
printf("NameID= %dn",NameID);
printf("Health= %dn",Health);
printf("Ammo= %dn",Ammo);
}
};

void main()
{
Player *obj=new(Player);

obj->set(1,100,20);
obj->getInfoX();

delete(obj);
};
#include 

Оператор –> применяется для обращения к функциям внутри объекта, который был создан в выделнной памяти.

Результат работы программы:

post-3-1294595717,35_thumb.gif

Рис.1

Данные объекта были построены таким образом

post-3-1294595711,21_thumb.gif

Рис.2

А вот что мы увидим в отладчике VC++

-----

#include

class Player{

int NameID,Health,Ammo;

public:

void set(int a, int b, int c){NameID=a;Health=b;Ammo=c;}

void getInfoX(){

printf("NameID= %dn",NameID);

printf("Health= %dn",Health);

printf("Ammo= %dn",Ammo);

}

};

void main()

{

00401000 push esi

Player *obj=new(Player);

00401001 push 0Ch//0Сh – кол-во байт выделенной памяти.

00401003 call operator new (401189h)

00401008 mov esi,eax

obj->set(1,100,20);

0040100A xor eax,eax

0040100C inc eax

obj->getInfoX();

0040100D push eax

0040100E push offset string "NameID= %dn" (40BB08h)

00401013 mov dword ptr [esi],eax //esi – адрес объекта

00401015 mov dword ptr [esi+4],64h

0040101C mov dword ptr [esi+8],14h

00401023 call printf (40104Fh)

00401028 push dword ptr [esi+4]

0040102B push offset string "Health= %dn" (40BB14h)

00401030 call printf (40104Fh)

00401035 push dword ptr [esi+8]

00401038 push offset string "Ammo= %dn" (40BB20h)

0040103D call printf (40104Fh)

delete(obj);

00401042 push esi

00401043 call operator delete (401114h)

00401048 add esp,20h

};

0040104B xor eax,eax

0040104D pop esi

0040104E ret

---

Тоже самое в IDA

post-3-1294595722,6_thumb.gif

рис. 3

А вот так выглядит в псевдокоде

post-3-1294595725,98_thumb.gif

рис. 4

При обмане игр у нас не будет исходника С++ и нам нужно научится понимать псевдокод.

Сделаем немного понятнее псевдокод.

Нажмём shift+F9 и опишем структуру класса Player с 3*4 байт

post-3-1294595729,35_thumb.gif

рис. 5

Затем в псевдокоде нажмём на Y и определим тип структуры:

post-3-1294595736,06_thumb.gif

рис.6

И получим более лучшее представление псевдокода

post-3-1294595740,62_thumb.gif

Рис.7

И ещё давайте посмотрим, что будет если мы не иницилизируем некоторые данные объекта.

post-3-1294595747,32_thumb.gif

рис 8

Результат такой:

post-3-1294595751,49_thumb.gif

рис 9

Переменные x1.x2.x3 не инициализировались и остался мусорный код выделенной памяти. Когда выделяется память, то она не «подчищена», если вы не знали.

Подведём итог. Познакомились с С++ (если не были знакомы) увидели на простом примере как оформляется структура объекта в С++ и как она выглядит в памяти и в псевдокоде. Увидели как псевдокод облегчает анализ игровых объектов.

Главное научиться проводить аналогии между псевдокодом и дизассемблерным кодом для того чтобы представить как устроен объект.

Если у вас возникли трудности в С++ то существует много информации в Интернете, которую вы можете легко найти самостоятельно.

Часть2. Дальнейшее исследования игрового объекта в памяти

Наследование.

В этой части рассмотрим как выглядит структура объекта:

  • При наследовании базового класса
  • При иерархии наследования классов
  • При иерархии наследования класса с инкапсулированным объектом
2.1 Структура объекта при простом наследовании базового класса.
post-3-1294595763,84_thumb.gif
Рис. 1
Посмотрим дизассемблерный код...
post-3-1294595767,6_thumb.gif
Рис. 2
Псевдокод будет таким
post-3-1294595772,02_thumb.gif
Рис. 3
2.2 Структура объекта при иерархии наследования классов.
Ниже представлен пример классов «вложеннных» друг в друга по принципу «матрёшки».
Базовый класс «стремиться» вверх структуры
post-3-1294595776,53_thumb.gif
Рис. 4
Псевдокод для этого кода приведён ниже.
post-3-1294595781_thumb.jpg
рис. 5
По аналогии можно представить как будут располагаться данные при различных наследованиях.
2.3 Структура объекта при иерархии наследования классов с инкапсулированным объектом.
Тут всё просто. Структура одного объекта должна содержать указатель на созданный объект. Красная стрелка это указатель на созданный инкапсулированный объект. Получается что один объект, содержит указатель на другой объект.
post-3-1294595785,27_thumb.jpg
рис. 6
Ниже находится псевдокод (к сожалению картинка сдохла на сервере, а у меня нет оригинала, но осталась рука - пока не знаю как она сюда попала :grin: ).
image007a.jpg
рис. 7
Ниже находится дизассемблерный код.
post-3-1294597053,63_thumb.jpg
рис. 8
Подведём итог вы узнали:
как выглядит структура инкапсулирующая объект,
как выглядит структура при наследовании и при иерархии наследования.Часть 3. Дальнейшее исследования игрового объекта в памяти
В этой части мы рассмотрим.

  • Виртуальные функции
  • Виртуальные классы и множественное наследование

3.1 Виртуальные функции.

Если вы не сталкивались с полиморфизмом ни разу и вообще с ООП знакомы мало, то понять эту тему будет, скорее всего, сложно. Виртуальные функции это одинаково объявленные функции в иерархии наследования, которые позволяют работать с данными объекта в контексте нужного класса. Объяснил как смог.. =)

Очень важно понять, как это работает для исследования игровых объектов в дальнейшем, т.к. виртуальные функции попадаются сплошь и рядом.

Разберём пример «на пальцах», показывающий выгоду виртуальных функции перед похожими, но не виртуальными одинаковыми функциями.

Программа перемещает графические объекты по нажатым клавишам клавиатуры, но писать мне её некогда, поэтому разберу основное.


{
int x,y;
public:
Dot() {x=100; y=100;} // просто зададим координаты по умолчанию
void show(){сделать пиксел белым};
void hide() {сделать пиксел черным};
void move (int dx,dy){ hide(); x+=dx; y+=dy; show()};
};
class Dot

Мы создали класс Точки, у которой описаны координаты положения и описаны функции перемещения с угасанием и отображения точки. Ключевое место здесь в том, что в функции перемещения присутствуют локальные вызовы угасания и отображения точки. Именно из-за локальных вызовов появится проблема в производном классе, т.к. в базовом классе будут вызываться свои локальные функции с тем же именем. Выходом будет определение виртуальных функций. Рассмотрим эту проблему.


{
int radius;
public:
Ring () {radius=200;}; //зададим радиус круга по умолчанию
void show(){сделать окружность белой};
void hide(){сделать окружность чёрной};
};
class Ring: public Dot

Видим, что Круг наследует функцию перемещения, в которой происходят локальные вызовы hide и show. Т.е. если мы зададим движение Кругу, то двигаться будет Точка, т.к. вызываются локальные функции в контексте класса Точки.

Ring *ring=new(Ring);

ring->mov(50,60); // вызовет перемещение только точки,а не круга

Чтобы решить эту проблему был введен механизм виртуальных функций. Вызов той или иной функции задается смещением (генерируемым компилятором), по которому в созданной таблице виртуальных методов (vftable) выбирается нужная функция, которая и вызовется.


{
int x,y;
public:
Dot() {x=100; y=100;} // просто зададим координаты по умлочанию
virtual void show(){сделать пиксел белым};
virtual void hide() {сделать пиксел черным};
void move (int dx,dy){ hide(); x+=dx; y+=dy; show()};
};

class Ring: public Dot
{
int radius;
public:
Ring () {radius=200;}; // также просто зададим радиус круга
void show(){сделать окружность белой};
void hide(){сделать окружность чёрной};
};
class Dot

Теперь


ring->mov(50,60); // вызовет перемещение только круга в заданные координаты 50 и 60
ring->Dot::mov(50,60); // этот метод в принципе бесполезный, т.к. координаты точки уже были равны 50 и 60
Ring *ring=new(Ring);

Вызывать метод перемещения точки было не обязательно, главное то, что круг наследовал координаты x и y и можно и нужно перемещать только круг. Однако, если вы вызовете ring->Dot::mov(100,100), то это распространится и на круге... т.е. и точка и круг взаимосвязаны и буквально слеплены в одно целое как в симбиозе (кто биологию хорошо знает? ^_^ )

Как мы увидели механизм «виртуальных функций (полиморфизм)» нужен для правильной работы с данными объекта в требуемых контекстах его описывающих классов. Чаще всего виртуальные функции объявляются без входных параметров, таким образом, их применение наиболее удобно и более расположено к полиморфизму.

При полиморфизме в статичной области кода возникает таблица виртуальных функций vftable. Указатель на таблицу виртуальных функций находится по первому адресу объекта (см. рис. 1)

post-3-1294595796,26_thumb.png

рис. 1

Следует отметить, что vftable характерна только для определённого типа класса. Если вы найдёте в памяти игры адрес таблицы виртуальных функций (vftable) некоторого объекта и затем в сканере памяти найдёте 6 указателей на ту же vftable, то можете считать что вы «вышли» ещё на 6 объектов, которые наследовали vftable класса или некоторой иерархии классов. Соответственно по указателю на vftable в чит-кодах можно делать фильтры по изменению данных игровых объектов построенных по описанию конкретного класса или по описанию классовой иерархии.

Учитывая всё выше известное, напишем программу с игровыми объектами и проанализируем формирование объектов с vftable.

Результат программы будет таким.

post-3-1294595799,9_thumb.png

рис. 2. (Координат нет, это вы поймете на рис 6 )

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

Сначала посмотрим расположение данных двух объектов, после того как мы их заполнили. Так мы увидим данные после того как CE сама расструктуризовала их.

post-3-1294595804,3_thumb.png

рис. 3

Как мы видим, структуры объектов заполнены непонятными данными по смещениям 0x18-2С, 4C-0x64, 0x68-0x6C. Адреса по этим смещениям рассмотрим позже. Сейчас важно понять, как устроены таблицы на рисунке по смещениям 0x0, 0x30 и как они работают.

Они выглядят вот так (так как и расструктуризавала CE)

post-3-1294595809,18_thumb.png

рис. 4

Для удобства переведём эти значения в hex- вид

post-3-1294595813,26_thumb.png

рис. 5

Я пока не знаю, правильно ли я определил, что по смещению 0хС значение 0х48 указывает на конец таблицы виртуальных функций в контексте всей иерархии классов...

00401017 – это указатель на функцию Player:: GetInfo

0040102B – это указатель на функцию Building::GetInfo

Обращение к указателям компилируется в коде (вы это можете посмотреть в дизассемблерном листинге ниже), т.е. в объектах не содержится данных о том к какой именно функции, в каком описании класса в какой момент обращаться.

Залезем в псевдокод в функцию main, т.к. именно по нему нам надо учиться обманывать игры и смотрим рис 5 и рис.6

В псевдокоде смещения в структурах объектов десятичные,но там не сложные числа можно перевести в 16-ричную систему в уме для того чтобы соотнести смещения структур на рис. 5

post-3-1294595818,45_thumb.png

рис. 6

GetInfo в контексте класса control не сработала, из-за виртуальных функций, так и должно быть.

Псевдокод показывает всё что необходимо и это гораздо нагляднее, чем смотреть дизассемблерный код в отладке. Для интересующихся (маньяков) привожу дизассемблерный код. Комментировать не буду, т.к. основное всё разобрал. Скопируйте его в отдельное место и можете тщательно разобраться как что и куда.

class abstr{

virtual void GetInfo()=0;

};

class control: abstr{

protected:

int x,y,z,ID;

public:

control(){x=y=z=0;}

void move(int dx,int dy, int dz) {x+=dx;y+=dy;x+=dz;GetInfo();}

void GetInfo(){printf("x= %d y= %d z= %dn",x,y,z);}

00401000 push dword ptr [ecx+0Ch]

00401003 push dword ptr [ecx+8]

00401006 push dword ptr [ecx+4]

00401009 push offset string "x= %d y= %d z= %dn" (40BB0Ch)

0040100E call printf (4010EAh)

00401013 add esp,10h

00401016 ret

};

class Player: public control{

protected:

int Health;

public:

void Set(int id, int health){ID=id; Health=health; GetInfo();}

void GetInfo(){printf("PlayerID= %d Health= %dn",ID,Health);

00401017 push dword ptr [ecx+14h]

0040101A push dword ptr [ecx+10h]

0040101D push offset string "PlayerID= %d Health= %dn" (40BB20h)

00401022 call printf (4010EAh)

00401027 add esp,0Ch

}

0040102A ret

};

class Building: public Player{

int SpeedOfC;

public:

void Set(int id, int health, int Speed)

{ID=id; Health=health;SpeedOfC=Speed; GetInfo();}

void GetInfo(){printf

("Building ID= %d Health= %d SpeedOfC= %dn",ID,Health,SpeedOfC);}

0040102B push dword ptr [ecx+18h]

0040102E push dword ptr [ecx+14h]

00401031 push dword ptr [ecx+10h]

00401034 push offset string "Building ID= %d Health= %d Speed"... (40BB3Ch)

00401039 call printf (4010EAh)

0040103E add esp,10h

00401041 ret

};

void main()

{

00401042 push ebx

00401043 push esi

00401044 push edi

Player *objP=new(Player);

00401045 push 18h

00401047 call operator new (401255h)

0040104C xor esi,esi

0040104E pop ecx

0040104F cmp eax,esi

00401051 je main+24h (401066h)

00401053 mov dword ptr [eax+0Ch],esi

00401056 mov dword ptr [eax+8],esi

00401059 mov dword ptr [eax+4],esi

0040105C mov dword ptr [eax],offset Player::`vftable' (40BB6Ch)

00401062 mov edi,eax

00401064 jmp main+26h (401068h)

00401066 xor edi,edi

Building *objB=new(Building);

00401068 push 1Ch

0040106A call operator new (401255h)

0040106F pop ecx

00401070 cmp eax,esi

00401072 je main+43h (401085h)

00401074 mov dword ptr [eax+0Ch],esi

00401077 mov dword ptr [eax+8],esi

0040107A mov dword ptr [eax+4],esi

0040107D mov dword ptr [eax],offset Building::`vftable' (40BB74h)

00401083 mov esi,eax

objP->Set(1,200);

00401085 mov eax,dword ptr [edi]

00401087 xor ebx,ebx

00401089 inc ebx

0040108A mov ecx,edi

0040108C mov dword ptr [edi+10h],ebx

0040108F mov dword ptr [edi+14h],0C8h

00401096 call dword ptr [eax]

objB->Set(1,1000,5);

00401098 mov eax,dword ptr [esi]

0040109A push 5

0040109C mov dword ptr [esi+10h],ebx

0040109F pop ebx

004010A0 mov ecx,esi

004010A2 mov dword ptr [esi+14h],3E8h

004010A9 mov dword ptr [esi+18h],ebx

004010AC call dword ptr [eax]

objP->move(5,5,0);

004010AE mov eax,dword ptr [edi]

004010B0 add dword ptr [edi+8],ebx

004010B3 add dword ptr [edi+4],ebx

004010B6 mov ecx,edi

004010B8 call dword ptr [eax]

objB->move(6,6,0);

004010BA mov eax,dword ptr [esi]

004010BC add dword ptr [esi+8],6

004010C0 add dword ptr [esi+4],6

004010C4 mov ecx,esi

004010C6 call dword ptr [eax]

objP->control::GetInfo();

004010C8 mov ecx,edi

004010CA call control::GetInfo (401000h)

objB->control::GetInfo();

004010CF mov ecx,esi

004010D1 call control::GetInfo (401000h)

delete(objP);

004010D6 push edi

004010D7 call operator delete (4011AFh)

delete(objB);

004010DC push esi

004010DD call operator delete (4011AFh)

004010E2 pop ecx

004010E3 pop ecx

004010E4 pop edi

004010E5 pop esi

};

004010E6 xor eax,eax

004010E8 pop ebx

004010E9 ret

С виртуальными функциями более менее познакомились. Возможно, в следующих статья разберём ещё более тяжёлые случаи (адские :ninja: )

Продолжение следует. В следующей частях возможно будет рассмотрено.

Множественное наследование.

Виртуальные функции при множественном наследовании.

Виртуальное наследование классов.

Виртуальные функции при виртуальном наследовании классов.

Статичные данные и функции в объектах. Дружественные функции в объектах.

Массивы объектов.

Объекты по шаблону. Объекты по стандартному шаблону .

Сообщения. Методы дизассемблирования и отладки.

Объекты совместно с lua – интерпритатором.

Создаём объекты через конструкторы и команды lua.

Обход антиотладочных ловушек xlive.

Распаковка игровых ресурсов.

Обман flash игр.

Обман .NET игр.

Обман JAVA игр.

Обман эмуляторных игр, поиск нулевого адреса.

  • Плюс 2
Ссылка на комментарий
Поделиться на другие сайты

  • 2 года спустя...
  • 5 месяцев спустя...

Эти книги читать не советую.

Советую читать эти. Искать в книгах методы применения структурного программирования так и ООП... и конечно изучать в дизассемблере примеры.

Ссылка на комментарий
Поделиться на другие сайты

  • 2 года спустя...
  • 9 месяцев спустя...
×
×
  • Создать...

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

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