bortolomeo Опубликовано 22 апреля, 2011 Поделиться Опубликовано 22 апреля, 2011 Этот трейнер мне заказал какой-то странный араб. Он очень жаловался на то, что уже месяц играет в казуальную игру, и ему нужно срочно ее пройти и успокоится. Для тех кто в нее играл (а таких, наверное, довольно много), вкратце опишу что она собой представляет, и какая задача передо мной стояла. А представляет она из себя обычный казуальный Match3 с двумя экранами: карта Рима, на которой можно строить различные строения и собственно match3 игра. Из отличий от остальных казуальных игр - на match3 экране есть бонусы, они становятся доступными после постройки определенных строений. У каждого бонуса есть энергия. Она пополняется, если соединить фишки соответствующие этому бонусу. Кроме того есть режим турнира, в котором нужно очистить ировое поле за заданное време. Вот на этом то режиме араб и застрял.Итак, тренер должен: 1) устанавливать заданное количество ресурсов, 2) автоматически пополнять энергию у бонусов, 3) останавливать время в режиме турнира.С чего бы начать? С поиска какого-нибудь простого значения с помощью Art of Money. Идеально для поиска подходит количество ресурсов (неважно каких). Они скорее всего хранятся в двойном беззнаковом слове. Их легко изменить в игре, чтобы отсеять мусор. Благодаря повериям казуальщиков, это достаточно большое число, и мусора будет не слишком много. Ищу и отсеиваю. Отсеиваю и ищу. После второй же итерации, остаются 3 ячейки памяти, в которых ничего не меняется. Теперь в дело вступает OllyDbg. Устанвливаю точку останова на чтение всех трех найденных ячеек. На какой первой остановится, там и здорово. Подкидываю золотишка. Остановилось. Здорово. Смотрю на код.А в коде происходит следующее: сначала считывают текущее количество золота(eax) из массива(edx) по заданному индексу (ecx). Затем прибавляют аргумент функции ([ebp+0xc]), и полученное значение записывают обратно. Смотрю откуда берется массив. Массив берется из [ecx+0x7F0]. Смотрим что такое ecx (кроме того что регистр, конечно :-)). А ecx у нас берется из локальной переменной (ebp-78), в которую он сохраняется в самом начеле функции. Значит функция - член класса. Ecx - указатель на this. Для лучшего понимая происходящего пишу псевдокод:void Game::AddResources (DWORD increment, DWORD resourceIndex){ DWORD currentResource; currentResource = this->resources[resourceIndex]; currentResource += increment; this->resources[resourceIndex] = currentResource;}Теперь нужно найти где же хранится указатель на объект. Открывают стек вызовов и поднимаюсь на уровень выше, в функцию откуда вызвали добавление ресурсов.Тут небольшая последовательность присвоений. mov edx, [0xA5ABB0]mov eax, [edx]mov [ebp-0xC4], eaxmov ecx, [ebp-0xC4]И получается отлично! Указатель на игру хранится в глобальной переменной, а значит я в любой момент времени могу его вычислить. Опять пишу псевдокод, что делать когда у меня будет указатель.DWORD* GlobalPointer = (DWORD*) 0xA5ABB0;BYTE* GamePointer = GlobalPointer[0];// это чтобы со смещением не путатьсяDWORD* ResourcesPointer = GamePointer[0x7F0];ResourcesPointer[resourcesId] = newResource;Добавляю поочередно ресурсы. Смотрю на изменение значений в массиве (дамп OllyDbg). Составляю табличку смещений массива: int MONEY_OFFSET = 1; //Золотоint WOOD_OFFSET = 2; //Ресурсыint int FOOD_OFFSET = 3; //Едаint SCORE_OFFSET = 4; //ОчкиПопутно отмечаю, что рядом в дампе меняются и другие значения. После пары-тройки несложных экспериментов выясняю что: int LIFES_OFFSET = 5; // жизниint LEVEL_OFFSET = 6; // уровеньОбращаю внимание на ряд единичек и нолей. Они обязательно должны что-то означать. Создаю новый профиль в игре, начинаю заново. И точно, означают. Доступен или не доступен бонус. Значит где-то здесь есть и заряд бонусов. Есть. Такой же ряд байт, минимум 0 (разряжен), максимум 0xC4 (заряжен). Странный выбор максимума, кстати. Пишу таблицу для бонусов. int HAMMER = 0x0;int BOMB = 0x1;int FIREBALL = 0x2;int HOURGLASS = 0x3;int LIGHTNING = 0x4;int MEGABOMB = 0x5;int MIXER = 0x6;int STAR = 0x7; //Всякие бонусыint BONUSAMOUNT_OFFSET = 0x4C; //Смещение для зарядаint BONUSENABLED_OFFSET = 0x7A; //Смещение для индикатора доступностиС этим понятно. Перехожу в режим турнира. Смотрю на память в дампе, и нахожу бонусы для турнира.int TOURNBONUSAMOUNT_OFFSET = 0x164; //Смещение для заряда в турниреint TOURNBONUSENABLED_OFFSET = 0x192; //Смещение для индикатора доступности в турниреПоловина пути пройдено. Остается найти как остановить таймер в турнире, и спасти несчастного араба от игровой зависимости. Ищу стандартные функции для получения времени, ставлю на них точку останова, и обнуляю возвращаемый результат. Добиваюсь того, что нашел нужную функцию, после которой игра полностью замирает. Двигаюсь после нее по коду, заменяя отдельные функции и условные переходы Nop-ами. Игра не единожды падает, но наконец-то я нахожу нужный участок кода.Заменяю JNZ на JMP и получаю результат! Таймер стоит, побочных эффектов не обнаружено. Конечно, патчить код не так здорово как данные, но разбирать дальше что происходит нет ни желания, ни времени. И так ведь работает :-). 1 Ссылка на комментарий Поделиться на другие сайты Поделиться
MasterGH Опубликовано 22 апреля, 2011 Поделиться Опубликовано 22 апреля, 2011 ....Смотрю откуда берется массив. Массив берется из [ecx+0x7F0]. Смотрим что такое ecx (кроме того что регистр, конечно :-)). А ecx у нас берется из локальной переменной (ebp-78), в которую он сохраняется в самом начеле функции. Значит функция - член класса. Ecx - указатель на this. Для лучшего понимая происходящего пишу псевдокод:void Game::AddResources (DWORD increment, DWORD resourceIndex){ DWORD currentResource; currentResource = this->resources[resourceIndex]; currentResource += increment; this->resources[resourceIndex] = currentResource;}Теперь нужно найти где же хранится указатель на объект. Открывают стек вызовов и поднимаюсь на уровень выше, в функцию откуда вызвали добавление ресурсов.1. Почему ты считаешь что это "объект", а не структура? Какие отличия объекта от структуры? (каверзный вопрос с прицелом на второй)2. У тебя есть утверждение из цитаты выше:А ecx у нас берется из локальной переменной (ebp-78), в которую он сохраняется в самом начеле функции. Значит функция - член класса. Ecx - указатель на this.Как ты узнаёшь что функция является членом класса? Точнее как ты пришёл к такому выводу?Если ты определял это по тому что функция работает с ecx ведь это ещё не значит что эта функция член класса (кстати о каком классе идёт речь, да и потом может быть там класса вообще нет а ecx указатель на структуру)......Другое дело писать псевдокод по метаданным (правда я эту часть не исследовал, знаю что эти метаданные вшиваются компилятором). Просто подразумевается что ты по каким-то соображением построил псевдокод "Game::AddResources"void Game::AddResources (...)Но про метаданные ты ничего не упоминаешь и никто не понимает как ты пришёл к выводу что функция является членом класса. Что ты работаешь именно с объектом...На С++ можно написать функции которые никак не связаны с классом и экземпляром класса и эти функции без метаданных нельзя относить к членам класса.Если я не прав, прошу поправить Ссылка на комментарий Поделиться на другие сайты Поделиться
bortolomeo Опубликовано 22 апреля, 2011 Автор Поделиться Опубликовано 22 апреля, 2011 1. Почему ты считаешь что это "объект", а не структура? Какие отличия объекта от структуры? (каверзный вопрос с прицелом на второй)В основном, конечно по вызову. Регистр ECX используется в функции без инициализации. Инициируется перед вызовом функции. Сейчас еще посмотрел откуда берется эта структура/объект. push 8F0hcall sub_6BAD9Badd esp, 8mov [ebp+var_14], eaxФункция sub_6BAD9B вызывается не один раз из разных мест. В дереве вызовов функции sub_6BAD9B находится функция HeapAlloc. В функцию sub_6BAD9B каждый раз передаются разные числа (и я уверен что это размер).Я очень верю что это какой-то свой оператор new. Потом идет функция которая инициализирует ту память которую мы выделили. И я очень верю что это конструктор. Еще человек использует у себя boost, и я очень не верю что он все пишет на структурах. Но что это функция-член, я конечно подумал когда увидел что в нее передается указатель на что-то в регистре ECX. Как то не встречал чтобы параметры передавались таки образом если не класс Ссылка на комментарий Поделиться на другие сайты Поделиться
MasterGH Опубликовано 22 апреля, 2011 Поделиться Опубликовано 22 апреля, 2011 хм... интересно.Далее считаю объект - объектом, а структуру структурой (не объекта). Объект - экземпляр класса. Пусть у рассматриваемого объекта нет таблиц виртуальных функций и нет таблиц виртуальных классов. Нет метаданных подсказывающих про классы и т.п.Если дизассемблерный код функции работает с указателем на ecx это ещё не значит, что ecx указатель на объект (но я не уверен, хотя всем известно что компилятор C++ по ecx передаёт указатель на объект). Если у объекта есть конструктор, то и у структуры данных может быть своя функция похожая на конструктор... По поводу "boost" я к не в курсе что это за зверь. Программист мог хорошо взяться за оптимизацию и отказаться где-то от ООП заменив объект простыми структурами.Интересно узнать следующее.1) Как будет передаваться указатель на объект в функцию член в дизассемблере.2) Как будет передаваться указатель на обычную структуру в функцию не принадлежащую никакому классу.3) Будут ли какие-то отличияЯ бы и сам бы проверил написав тестируемый код. Да вот интересно, кто как думает, а то обычно по таким вещам (связанные с ООП и дизассемблированием) только мне одному доводится заморачиваться. Ссылка на комментарий Поделиться на другие сайты Поделиться
bortolomeo Опубликовано 22 апреля, 2011 Автор Поделиться Опубликовано 22 апреля, 2011 хм... интересно.Далее считаю объект - объектом, а структуру структурой (не объекта). Объект - экземпляр класса. Пусть у рассматриваемого объекта нет таблиц виртуальных функций и нет таблиц виртуальных классов. Нет метаданных подсказывающих про классы и т.п.Если дизассемблерный код функции работает с указателем на ecx это ещё не значит, что ecx указатель на объект (но я не уверен, хотя всем известно что компилятор C++ по ecx передаёт указатель на объект). Если у объекта есть конструктор, то и у структуры данных может быть своя функция похожая на конструктор... По поводу "boost" я к не в курсе что это за зверь. Программист мог хорошо взяться за оптимизацию и отказаться где-то от ООП заменив объект простыми структурами.Интересно узнать следующее.1) Как будет передаваться указатель на объект в функцию член в дизассемблере.2) Как будет передаваться указатель на обычную структуру в функцию не принадлежащую никакому классу.3) Будут ли какие-то отличияЯ бы и сам бы проверил написав тестируемый код. Да вот интересно, кто как думает, а то обычно по таким вещам (связанные с ООП и дизассемблированием) только мне одному доводится заморачиваться.1) Если вызывается что-нибудь типа someObject->member();//то это будет move ecx, someObj call member //ну это если наследования нет и всего такого прочего2) А если что нибудь типа someClass* someObject; function(someObject);//то это будет push someObject call functionМежду структурами и классами различий не будет, потому как если я ничего не путаю, то структура - это тот же класс, только у нее поля по умолчанию public. Ссылка на комментарий Поделиться на другие сайты Поделиться
bortolomeo Опубликовано 22 апреля, 2011 Автор Поделиться Опубликовано 22 апреля, 2011 Я бы и сам бы проверил написав тестируемый код. Да вот интересно, кто как думает, а то обычно по таким вещам (связанные с ООП и дизассемблированием) только мне одному доводится заморачиваться.а вот и код://Определение классов и структурstruct SomeStruct{ int classData; void structMethod();};class SomeClass{ int structData;public: void classMethod();};void SomeStruct::structMethod(){}void SomeClass::classMethod(){}void StructFunction(SomeStruct* testStruct){}void ClassFunction(SomeClass* testObject){};//Дизассемблер testStruct->structMethod();0040168A 8B 4D F4 mov ecx,dword ptr [ebp-0Ch] 0040168D E8 4E FE FF FF call SomeStruct::structMethod (4014E0h) testObject->classMethod();00401692 8B 4D E8 mov ecx,dword ptr [ebp-18h] 00401695 E8 76 FE FF FF call SomeClass::classMethod (401510h) StructFunction(testStruct);0040169A 8B 45 F4 mov eax,dword ptr [ebp-0Ch] 0040169D 50 push eax 0040169E E8 9D FE FF FF call StructFunction (401540h) 004016A3 83 C4 04 add esp,4 ClassFunction(testObject);004016A6 8B 45 E8 mov eax,dword ptr [ebp-18h] 004016A9 50 push eax 004016AA E8 C1 FE FF FF call ClassFunction (401570h) 004016AF 83 C4 04 add esp,4 Как видим, экземпляры классов и структур не отличаются ни в дизассемблере, ни в исходнике. (не считая ключевого слова public в определении класса).это в MSVS 2008 без оптимизации. в других компиляторах с другими параметрами может быть и по-другому 1 Ссылка на комментарий Поделиться на другие сайты Поделиться
MasterGH Опубликовано 23 апреля, 2011 Поделиться Опубликовано 23 апреля, 2011 Спасибо за интересное исследование Добавил +1 к посту выше.P.S. Просто напоминаю. Повторюсь о том что писал в личном сообщении. Если картинки с хостинга пропадут, то эту статью сложно будет понять. Картинки на хостинге вряд ли хранятся неограниченный срок. Дело конечно твоё и решать тебе Ссылка на комментарий Поделиться на другие сайты Поделиться
Рекомендуемые сообщения