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

bortolomeo

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

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

  • Посещение

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

    2

Сообщения, опубликованные bortolomeo

  1. Вот доказательство того, что я сам взломал то, что смог:

    ну вот, что-то мне говорит что если ты посмотришь дебаггером, то здесь


    mov [ecx+78],#101

    mov [edi+000000B4],#100

    mov dword ptr [eax+6C],#1120404556

    ecx, edi, eax будут указывать на один и тот же адрес, и будут являться указателями на объект (или если кому угодно структуру) игрока. Если там посмотреть там на память, то найдешь все что тебе надо.

  2. Chucky, ты, похоже, сам даже не пытаешься взломать игру, а лишь выпрашиваешь скрипты у тех, кто это делает. И неясно, с какой именно целью ты это делаешь.

    Всем остальным: Отныне и впредь раскрытые скрипты во избежание их "воровоства" публикуем исключительно в разделе, доступ к которому имеют только участники группы "Разработчики".

    Так а чего, вроде писал же чел про трутницы скрипт. А если скилов не хватает то что уж тут поделать, для того и форум.

  3. Спасибо большое! :) А "Рассудок" ты взломал?

    функция 46CAA0

    рассудок берется здесь:


    mov eax, dword_72836C
    push esi
    mov esi, ecx
    mov ecx, [eax+84h]
    fld dword ptr [ecx+68h]

    здоровье лежит рядом, 4-мя байтами раньше. тип 32 бит флоат.

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

  4. Я бы и сам бы проверил написав тестируемый код. Да вот интересно, кто как думает, а то обычно по таким вещам (связанные с ООП и дизассемблированием) только мне одному доводится заморачиваться.

    а вот и код:



    //Определение классов и структур
    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
  5. хм... интересно.

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

    Если дизассемблерный код функции работает с указателем на 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.

  6. 1. Почему ты считаешь что это "объект", а не структура? Какие отличия объекта от структуры? (каверзный вопрос с прицелом на второй)

    В основном, конечно по вызову. Регистр ECX используется в функции без инициализации. Инициируется перед вызовом функции. Сейчас еще посмотрел откуда берется эта структура/объект.


    push 8F0h
    call sub_6BAD9B
    add esp, 8
    mov [ebp+var_14], eax

    Функция sub_6BAD9B вызывается не один раз из разных мест. В дереве вызовов функции sub_6BAD9B находится функция HeapAlloc. В функцию sub_6BAD9B каждый раз передаются разные числа (и я уверен что это размер).

    Я очень верю что это какой-то свой оператор new.

    Потом идет функция которая инициализирует ту память которую мы выделили. И я очень верю что это конструктор.

    Еще человек использует у себя boost, и я очень не верю что он все пишет на структурах.

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

  7. Этот трейнер мне заказал какой-то странный араб. Он очень жаловался на то, что уже месяц играет в казуальную игру, и ему нужно срочно ее пройти и успокоится.

    Для тех кто в нее играл (а таких, наверное, довольно много), вкратце опишу что она собой представляет, и какая задача передо мной стояла. А представляет она из себя обычный казуальный Match3 с двумя экранами: карта Рима, на которой можно строить различные строения и собственно match3 игра. Из отличий от остальных казуальных игр - на match3 экране есть бонусы, они становятся доступными после постройки определенных строений. У каждого бонуса есть энергия. Она пополняется, если соединить фишки соответствующие этому бонусу. Кроме того есть режим турнира, в котором нужно очистить ировое поле за заданное време. Вот на этом то режиме араб и застрял.

    Итак, тренер должен:

    1) устанавливать заданное количество ресурсов,

    2) автоматически пополнять энергию у бонусов,

    3) останавливать время в режиме турнира.

    littlegame.png

    С чего бы начать? С поиска какого-нибудь простого значения с помощью Art of Money. Идеально для поиска подходит количество ресурсов (неважно каких). Они скорее всего хранятся в двойном беззнаковом слове. Их легко изменить в игре, чтобы отсеять мусор. Благодаря повериям казуальщиков, это достаточно большое число, и мусора будет не слишком много.

    Ищу и отсеиваю. Отсеиваю и ищу. После второй же итерации, остаются 3 ячейки памяти, в которых ничего не меняется.

    artmoney.png

    Теперь в дело вступает OllyDbg. Устанвливаю точку останова на чтение всех трех найденных ячеек. На какой первой остановится, там и здорово. Подкидываю золотишка. Остановилось. Здорово. Смотрю на код.

    ollydbgcondstop.png

    А в коде происходит следующее: сначала считывают текущее количество золота(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;
    }

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

    ollydbgafterstack.png

    Тут небольшая последовательность присвоений.

    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-ами. Игра не единожды падает, но наконец-то я нахожу нужный участок кода.

    ollydbgtimestop.png

    Заменяю JNZ на JMP и получаю результат! Таймер стоит, побочных эффектов не обнаружено. Конечно, патчить код не так здорово как данные, но разбирать дальше что происходит нет ни желания, ни времени. И так ведь работает :-).

    • Плюс 1
  8. Герои меча и отладчика

    Сначала небольшая предыстория. 7 лет назад, я вернулся из магазина с диском 4-х героев. Ощущения от них были двоякие: с одной стороны неплохие находки в плане геймплея, интересные кампании. С другой – непривычное графическое оформление, высокие (для моей машины в то время) системные требования, и, конечно же, ошибки. Их было много, но одна была особенно критичной.

    cover.jpg

    Если зайти в своем замке в тюрьму, в которой содержатся герои, программа вылетала в систему. Это случилось когда я победил особо сильного соперника с раскачанным героем. Была надежда на то, что в тюрьме пытками и подкупом можно склонить его на свою сторону, ан нет. В общем диск полетел в стол, где был благополучно забыт.

    Сегодня у выдалось свободное время. Страдая бездельем, я решил разобрать вещи в столе, и выкинуть ненужное. Наткнулся на забытый диск, и уборку пришлось отложить. Забыл о былых обидах, и погрузился в мир Аксеота. Почти прошел первый сценарий кампании, и надо же было опять влезть в тюрьму с чужим героем. Игра закрылась, а у меня появилось занятие.

    За 7 лет я несколько поумнел, и узнал про существование отладчика. Открыв игру во всеми любимом отладчике OllyDbg, и повторив последовательность действий для выявления ошибки увидел следующую картину:

    ollycrash.png

    Увидел, что вызывается функция, которая возвращает указатель на какой-то объект. Функция возвращает 0. Далее программа пытается что-то читать, и вызывается исключение нарушения доступа к памяти.

    Затем я решил просмотреть функцию, которая возвращает указатель. Это метод какого-то класса, куда передается строка. Класс – контейнер. В контейнере находятся объекты со строками. Объекты упорядочены по возрастанию строк. Если упрощенно, то в нем с помощью алгоритма бинарного поиска ищется объект, в котором присутствует заданная аргументом строка. Если элемент найден, то возвращается указатель на него, если нет - 0 (если я ни в чем не ошибся, конечно). Судя по строкам, которые ищут, то это картинки для GUI. Строка на которой все вылетает – «background».

    list2.png

    На скриншоте отображена часть, которая возвращает значение из функции. Поставил breakpoint на инструкцию xor eax, eax чтобы посмотреть как часто функция возвращает нулевой указатель, и как этот указатель в дальнейшем обрабатывается. В результате я выяснил, что ноль возвращается довольно часто. Просмотрев перекрестные ссылки на эту функцию, обнаружил что чаще всего указатель проверяют. Например таким образом:

    list3.png

    Места чтобы добавить проверку на ноль и оператор условного перехода не было. Делать DLL Injection ради этого не сильно хотелось. Поэтому попробовал сделать все тупо, быстро и опасно. Места, где идет обращение к этому указателя забить NOP-ами:

    list4.png

    Получилось как-то не слишком изящно. Зато быстро и ненапряжно. Может и прокатить. Запустил игру и зашел в тюрьму. В систему не вылетает, исключений нет. Пленного героя показывает. А радости-то и никакой. Герой, как и подобает настоящему герою, оказался неподкупным. И тут я сделал то что надо было сделать с самого начала: поискал информацию про тюрьму в гугле. Обнаружилось 2 вещи. Во-первых, тюрьма не нужна и непонятно почему я вообще подумал что там можно подкупить вражеского героя. Во-вторых, есть патч, который исправляет эту ошибку. Зато было интересно, как надеюсь и вам при прочтении этой статьи.

    list5.png

    • Плюс 2
×
×
  • Создать...

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

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