Модификация игры ASM/C++ Часть 4.

Тема в разделе "Уроки, Создание читов и трейнеров, исходники", создана пользователем RedYu, 26 июл 2016.

  1. Оффлайн

    RedYu

    На форуме с:
    25 июл 2016
    Сообщения:
    6
    Симпатии:
    4
    Баллы:
    2
    Пол:
    Мужской
    Всем, привет!
    В этой части я хочу рассказать про поиск объектов в игре. А так же про перехват функций.

    И так. Начнёмsmile-z
    Запустим игру и выберем то что хотим перехватить. Например у нас с какой-то частотой падает солнце, запустим Cheat Engine, и попробуем найти массив где находятся солнце в игре.
    Например мы на экране видим 1 солнце в Cheat Engine ищем 1, потом когда солнца будет 2шт, нужно отсеять уже 2, и так далее пока не будет минимальное количество адресов.


    Вот мы нашли 2 адреса в памяти игры, попробуем узнать функцию которая записывает в этот адрес в памяти. Показывать как это найти в Cheat Engine, я не буду, так как в интернете полно видео уроков как пользоваться Cheat Engine.

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


    И видим инкрементирование и декрементирование когда солнце пропадает, очень похоже на то что нам нужно.
    Копируем адрес инструкции инкрементирования, и переходим в IDA Pro. Для того чтобы понять что там происходит. Запустим под отладчиком, и поставим breakpoint чтобы узнать какая функция вызывает sub_476100. Смотрим на скриншот.


    Это функция sub_477CB0. Смотрим где она вызывается.

    [​IMG]

    Пробуем найти где именно, для этого просто выйдем из функции нажав сочетание клавиш Ctrl + F7. И вызвали её из sub_467970. С параметрами

    PHP:
    v18 sub_477CB0(*(_DWORD *)(*(_DWORD *)(v23 2368) + 8), *(_DWORD *)(v4 8), 0.00.00);
    На что очень сомнительно чтобы последние параметры были 0. Попробуем через Cheat Engine, занопить вызов sub_477CB0.

    Код:
    .text:00467BD3 fst     [esp+28h+var_24]                ; float
    .text:00467BD7 fstp    [esp+28h+var_28]                ; float
    .text:00467BDA call    sub_477CB0
    .text:00467BDF mov     esi, eax
    .text:00467BE1 push    ebp
    .text:00467BE2 mov     ecx, esi
    .text:00467BE4 call    sub_4779A0
    И мы увидели что создалось солнце, и через 1-2 сек игра крашнулась. А нам интересен был факт того что солнце создалось, а значит это не та функция что нам нужна.
    Берем второй адрес и точно так же делаем все с ним.


    И видим что здесь немного другие инструкции. Но не важно, выбираем первую когда солнце создается. И переходим в IDA Pro.
    Видим что выполняются они в функции sub_420D90, теперь пробуем найти откуда эта функция вызывается, все в точности как и с прошлым вариантом.
    Нашли sub_40F400, а эта функция вызывается из sub_4163D0. И видим.


    Пробуем сделать выход из функции sub_4163D0, заменив первый байт на retn.


    И видим что солнышко больше никогда не создастся. Ура мы нашли функцию которая создает солнце. А если быть более точным это функция sub_40F400.

    Давайте попробуем проанализировать что это за функция.

    int __thiscall sub_40F400(_DWORD *this, int a2, int a3, int a4, int a5)

    Видим что первый параметр это указатель на объект который управляет созданием солнц в игре. Остальные параметры не понятные, знаем что последний равен 0, а3 равен 60, а параметр а4 принимает значение 4 или 6.

    А второй параметр получается после вызова функции с передачей аргумента 550, и прибавлением к результату 100. Очень похоже на рандомsmile-z но сказать окончательно не могу.

    PHP:
        v3 sub_5FF2A0(550);
        ++*(
    _DWORD *)(a1 21844);
        
    v4 v3 100;
    И так что мы имеем в плане этого класса который управляет солнцами. Некий класс в котором есть функция sub_40F400(int, int, int, int)

    Например класс у нас такой.

    PHP:
    class Creator
    {
    public:
        
    void sub_40F400(intintintint);
    }
    Попробуем реализовать это в нашей DLL.

    И так чтобы работала та функция которая нам нужна нам нужно указать её адрес в памяти. Смотрим код ниже

    PHP:
    void Creator::CreateSun(int a2int a3int a4int a5)
    {
        
    typedef void(__thiscall *t)(Creator*, intintintint);
        
    t f = (t)0x0040F400;
        
    f(thisa2a3a4a5);
    }
    Всё класс объекта создан теперь осталось найти адрес этого объекта в памяти игры. Для этого вернемся в IDA Pro. И найдем какую-то функцию которая использует соглашение о вызове __cdecl, __stdcall.
    Чуть не забыл о соглашениях, подробно можно о них почитать вот здесь

    Почему именно __cdecl, __stdcall, а все потому что эти функции проще всего перехватить. Так как аргументы функции передаются через стек, а не через регистры.
    Хотя я покажу в следующих частях, как перехватить функцию где соглашение о вызове не позволяет это просто сделать. Например __usercall это последствия оптимизации программы/игры.

    Помним что функция sub_40F400 вызывается в sub_4163D0, ищем где вызывается sub_4163D0. В функции sub_4181E0, но здесь все еще __thiscall, идем дальше sub_418600, тоже самое, дальше...
    Код:
    .rdata:006E4D8C                 dd offset sub_549210
    .rdata:006E4D90                 dd offset sub_418600
    .rdata:006E4D94                 dd offset sub_549930
    Видим что функция sub_418600 виртуальная, а значит ищем чуть выше указатель на деструктор, а там где он и будет сам конструкторsmile-z

    Совсем рядом находится начало
    Код:
    .rdata:006E4D38 off_6E4D38      dd offset sub_40AF10    ; DATA XREF: sub_40A3C0+3D
    .rdata:006E4D38                                         ; sub_40AF40+1F
    .rdata:006E4D3C                 dd offset sub_547BB0
    А именно off_6E4D38 ищем где оно применяется. И видим применение его в функции

    PHP:
    int __thiscall sub_40A3C0(_DWORD *thisint a2)

    v2 this;
      
    v51 0;
      
    sub_54BE80(a2);
      
    v58 0;
      *(
    _DWORD *)(a2 160) = &off_6F4000;
      *(
    _DWORD *)a2 = &off_6E4D38//Вот оно
      
    *(_DWORD *)(a2 160) = &off_6E4E50;
      *(
    _DWORD *)(a2 168) = 0;
      *(
    _DWORD *)(a2 172) = 0;
    То есть а2, и есть указатель на наш объект. Но соглашение все еще __thiscall. Идем дальше и видим что функция sub_40A3C0, вызывается в sub_4528B0


    Сразу на будущее хочу сказать что размер нашего объекта равен 0x57D8u, тогда и напишем это в нашем классе


    и видим что CompileTimeSizeCheck(Creator, 0x57D8); не подсвечивается красным, для примера просто измените значение размера.

    Продолжаем поиск __cdecl, __stdcall. И видим что функция sub_4528B0, вызывается из sub_452B30. Идем дальше sub_452820, и дальше smile-z
    Поиск очень долгая и нудная работа. Для того чтобы не писать здесь, я сделал видео о том как я нашел эту функцию.

    Видео

    И так вот мы нашли эту функцию
    int __stdcall sub_40D840(signed int a1)

    Пробуем её заменить на свою, для того чтобы определить объект который создает солнце. Смотрим на код.



    Осталось только перезаписать вызов нашей функции в игре. Находим адрес где вызывается sub_40D840.

    Код:
    .text:00452B47                 push    eax
    .text:00452B48                 call    sub_40D840
    .text:00452B4D                 mov     dword ptr [esi+9A8h], 0
    Делается это в нашей FixGame::Initialize вот так.

    PHP:
    void FixGame::Initialize(void)
    {
        
    BYTE bytes[] = { 0xB10x010x90 };
        
    WriteMemoryBYTES(0x005D14BDbytes3);

        
    WriteInstructionCall(0x00452B48, (UINT)Creator::sub_40D840);
    }
    Теперь мы можем управлять нашим объектом, из любого места в DLL. Например вот так:

    PHP:
        if (g_Creator)
            
    g_Creator->CreateSun(1006040);
    Круто ведь, да?)
    Можем когда угодно создать солнце)
    Например можем по нажатию кнопки, или еще после чего-то) Это уже в пределах вашей фантазии)

    Теперь я покажу как в игре, расширить объект, например создадим переменную(объект, любого другого класса) в которой будет хранится количество солнц, созданные нами с помощью нашей DLL.
    Создадим класс CreatorEx.



    Добавляем вконец переменную CreatorEx.

    PHP:
    class Creator
    {
        
    Creator() {};
    public:
        static 
    int __stdcall sub_40D840(CreatorpCreator);

        
    void CreateSun(int a2int a3int a4int a5);

        
    /* 000 */ virtual ~Creator() {}; // Помним что внутри объекта есть виртуальные функции, а значит по адресу 0000, лежит таблица виртуальных функций
        /* 004 */


        /* 0004 */ 
    unsigned long __uUnkValue0004[0x15F6 1]; // 0x57D8u делим на 4 так как мы используем тип long,
                                                              //а 0x57D8u размер в байтах. -1 потому что первые 4 байта это указатель на таблицу виртуальных функций
        /* 57D8 */ 
    CreatorEx objCreatorEx;
        
    /* 57E0 */
    };

    extern Creatorg_Creator;

    CompileTimeSizeCheck(Creator0x57E0);
    Очевидно что размер изменился, заставим теперь игру выделить память под нашу переменную.
    Вспоминаем место где мы видели размер нашего класса, а это было в функции sub_4528B0.

    Код:
    .text:004528CE                 push    57D8h           ; size_t
    .text:004528D3                 call    [email protected]@Z    ; operator new(uint)
    .text:004528D8                 add     esp, 4
    Вот это место, нам нужно заменить push 57D8h , на свой. Давайте это и сделаем.

    Код:
    004528CE  68 D8 57 00 00 E8 FA 75  24 00 83 C4 04 89 44 24  h+W..ш·u$.Г-.ЙD$
    Нам нужно записать новый размер по адресу 004528CE + 1, потому что 68 - это push, его мы не трогаем.
    Действуем.

    PHP:
    void FixGame::Initialize(void)
    {
        
    BYTE bytes[] = { 0xB10x010x90 };
        
    WriteMemoryBYTES(0x005D14BDbytes3);

        
    WriteMemoryDWORD(0x004528CFsizeof(Creator)); // patch new size
        
    WriteInstructionCall(0x00452B48, (UINT)Creator::sub_40D840);
    }
    Всё теперь мы заставили игру выделить для нас память, и нам осталось лишь вызвать конструктор нашего объекта.

    Поправим инициализацию, объекта.

    PHP:
    int __stdcall Creator::sub_40D840(CreatorpCreator)
    {
        
    typedef int(__stdcall *t)(Creator*);
        
    t f = (t)0x0040D840;

        new (&
    pCreator->objCreatorExCreatorEx();

        
    g_Creator pCreator;

        return 
    f(pCreator);
    }
    Теперь у нас создано и наше расширение, и мы можем вызывает его функции.

    Например вот так.
    PHP:
        DWORD dwCountSuns;
        if (
    g_Creator)
        {
            
    dwCountSuns g_Creator->objCreatorEx.GetCountSuns();
            if (
    dwCountSuns == 0)
            {
                
    g_Creator->CreateSun(1006040);
                
    g_Creator->objCreatorEx.Increment();
            }
        }
    Но нам же так же нужно и вызвать деструктор, это я уже покажу в следующей статьеsmile-z А на этом всеsmile-z

    Жду ваши вопросыsmile-z

    Так же сделал репозиторий с исходным кодом проекта.
    GitHub

    Если нужен исходник:
    PlantsVsZombies.rar
    --- Добавлено, 26 июл 2016, Дата первого сообщения: 26 июл 2016 ---
    https://www.virustotal.com/ru/file/...2a5f5173f80e61f9eaa6e2b1/analysis/1469530739/
     
    Rango нравится это.
  2.  

Поделиться этой страницей

  1. asm c уроки

Уважаемый пользователь!

Мы обнаружили, что вы блокируете показ рекламы на нашем сайте.

Просим внести его в список исключения или отключить AdBlock.

Наши материалы предоставляются БЕСПЛАТНО и единственным доходом является реклама.

Спасибо за понимание!