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

bortolomeo

Стажёры
  • Постов

    17
  • Зарегистрирован

  • Посещение

  • Победитель дней

    2

bortolomeo стал победителем дня 23 апреля 2011

bortolomeo имел наиболее популярный контент!

Репутация

4 Lamer

Информация

  • Пол
    Не определился
  1. ну вот, что-то мне говорит что если ты посмотришь дебаггером, то здесь mov [ecx+78],#101 mov [edi+000000B4],#100 mov dword ptr [eax+6C],#1120404556 ecx, edi, eax будут указывать на один и тот же адрес, и будут являться указателями на объект (или если кому угодно структуру) игрока. Если там посмотреть там на память, то найдешь все что тебе надо.
  2. Так а чего, вроде писал же чел про трутницы скрипт. А если скилов не хватает то что уж тут поделать, для того и форум.
  3. функция 46CAA0 рассудок берется здесь: mov eax, dword_72836C push esi mov esi, ecx mov ecx, [eax+84h] fld dword ptr [ecx+68h] здоровье лежит рядом, 4-мя байтами раньше. тип 32 бит флоат. можно было ставить точку останова на доступ к строку "все впорядке" или "в здравом уме" и смотреть по стеку откуда оно вызывалось
  4. вот никогда не получалось скомпилировать декомпилированные игры :-(
  5. а вот и код: //Определение классов и структур 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 без оптимизации. в других компиляторах с другими параметрами может быть и по-другому
  6. 1) Если вызывается что-нибудь типа someObject->member(); //то это будет move ecx, someObj call member //ну это если наследования нет и всего такого прочего 2) А если что нибудь типа someClass* someObject; function(someObject); //то это будет push someObject call function Между структурами и классами различий не будет, потому как если я ничего не путаю, то структура - это тот же класс, только у нее поля по умолчанию public.
  7. В основном, конечно по вызову. Регистр ECX используется в функции без инициализации. Инициируется перед вызовом функции. Сейчас еще посмотрел откуда берется эта структура/объект. push 8F0h call sub_6BAD9B add esp, 8 mov [ebp+var_14], eax Функция sub_6BAD9B вызывается не один раз из разных мест. В дереве вызовов функции sub_6BAD9B находится функция HeapAlloc. В функцию sub_6BAD9B каждый раз передаются разные числа (и я уверен что это размер). Я очень верю что это какой-то свой оператор new. Потом идет функция которая инициализирует ту память которую мы выделили. И я очень верю что это конструктор. Еще человек использует у себя boost, и я очень не верю что он все пишет на структурах. Но что это функция-член, я конечно подумал когда увидел что в нее передается указатель на что-то в регистре ECX. Как то не встречал чтобы параметры передавались таки образом если не класс
  8. Этот трейнер мне заказал какой-то странный араб. Он очень жаловался на то, что уже месяц играет в казуальную игру, и ему нужно срочно ее пройти и успокоится. Для тех кто в нее играл (а таких, наверное, довольно много), вкратце опишу что она собой представляет, и какая задача передо мной стояла. А представляет она из себя обычный казуальный 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], eax mov 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 и получаю результат! Таймер стоит, побочных эффектов не обнаружено. Конечно, патчить код не так здорово как данные, но разбирать дальше что происходит нет ни желания, ни времени. И так ведь работает :-).
  9. Герои меча и отладчика Сначала небольшая предыстория. 7 лет назад, я вернулся из магазина с диском 4-х героев. Ощущения от них были двоякие: с одной стороны неплохие находки в плане геймплея, интересные кампании. С другой – непривычное графическое оформление, высокие (для моей машины в то время) системные требования, и, конечно же, ошибки. Их было много, но одна была особенно критичной. Если зайти в своем замке в тюрьму, в которой содержатся герои, программа вылетала в систему. Это случилось когда я победил особо сильного соперника с раскачанным героем. Была надежда на то, что в тюрьме пытками и подкупом можно склонить его на свою сторону, ан нет. В общем диск полетел в стол, где был благополучно забыт. Сегодня у выдалось свободное время. Страдая бездельем, я решил разобрать вещи в столе, и выкинуть ненужное. Наткнулся на забытый диск, и уборку пришлось отложить. Забыл о былых обидах, и погрузился в мир Аксеота. Почти прошел первый сценарий кампании, и надо же было опять влезть в тюрьму с чужим героем. Игра закрылась, а у меня появилось занятие. За 7 лет я несколько поумнел, и узнал про существование отладчика. Открыв игру во всеми любимом отладчике OllyDbg, и повторив последовательность действий для выявления ошибки увидел следующую картину: Увидел, что вызывается функция, которая возвращает указатель на какой-то объект. Функция возвращает 0. Далее программа пытается что-то читать, и вызывается исключение нарушения доступа к памяти. Затем я решил просмотреть функцию, которая возвращает указатель. Это метод какого-то класса, куда передается строка. Класс – контейнер. В контейнере находятся объекты со строками. Объекты упорядочены по возрастанию строк. Если упрощенно, то в нем с помощью алгоритма бинарного поиска ищется объект, в котором присутствует заданная аргументом строка. Если элемент найден, то возвращается указатель на него, если нет - 0 (если я ни в чем не ошибся, конечно). Судя по строкам, которые ищут, то это картинки для GUI. Строка на которой все вылетает – «background». На скриншоте отображена часть, которая возвращает значение из функции. Поставил breakpoint на инструкцию xor eax, eax чтобы посмотреть как часто функция возвращает нулевой указатель, и как этот указатель в дальнейшем обрабатывается. В результате я выяснил, что ноль возвращается довольно часто. Просмотрев перекрестные ссылки на эту функцию, обнаружил что чаще всего указатель проверяют. Например таким образом: Места чтобы добавить проверку на ноль и оператор условного перехода не было. Делать DLL Injection ради этого не сильно хотелось. Поэтому попробовал сделать все тупо, быстро и опасно. Места, где идет обращение к этому указателя забить NOP-ами: Получилось как-то не слишком изящно. Зато быстро и ненапряжно. Может и прокатить. Запустил игру и зашел в тюрьму. В систему не вылетает, исключений нет. Пленного героя показывает. А радости-то и никакой. Герой, как и подобает настоящему герою, оказался неподкупным. И тут я сделал то что надо было сделать с самого начала: поискал информацию про тюрьму в гугле. Обнаружилось 2 вещи. Во-первых, тюрьма не нужна и непонятно почему я вообще подумал что там можно подкупить вражеского героя. Во-вторых, есть патч, который исправляет эту ошибку. Зато было интересно, как надеюсь и вам при прочтении этой статьи.
×
×
  • Создать...

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

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