• Категории
  • Последние
  • Метки
  • Популярные
  • Пользователи
  • Группы
  • Зарегистрироваться
  • Войти
GameHackLab[RU]
  • Категории
  • Последние
  • Метки
  • Популярные
  • Пользователи
  • Группы
  • Зарегистрироваться
  • Войти

Как правильно сделать call в своём скрипте?

Запланировано Прикреплена Закрыта Перенесена Взлом игр (начинающим)
8 Сообщения 3 Posters 252 Просмотры
Загружаем больше сообщений
  • Сначала старые
  • Сначала новые
  • По количеству голосов
Ответить
  • Ответить, создав новую тему
Авторизуйтесь, чтобы ответить
Эта тема была удалена. Только пользователи с правом управления темами могут её видеть.
  • P
    paracetamol
    отредактировано 22 мая 2024 г., 08:28

    Откопал в игре код:

    StartNightImmediately::UsePower - cmp dword ptr [050310B8],01
    StartNightImmediately::UsePower+7- jne StartNightImmediately::UsePower+A
    StartNightImmediately::UsePower+9- ret 
    StartNightImmediately::UsePower+A- xor ecx,ecx
    StartNightImmediately::UsePower+C- xor edx,edx
    StartNightImmediately::UsePower+E- call Terraria.Main::SkipToTime
    StartNightImmediately::UsePower+13- ret 
    

    Есть ещё аналогичные вызовы дня, полдня, вечера... все отличаются только регистрами ECX и EDX перед CALL

    Есть способ какой-то в свой скрипт добавить вызов этого метода?

    Пока пробовал только такой вариант (символ debug_flag для одноразового срабатывания):

      cmp byte ptr [debug_flag],0
      je @f
      mov byte ptr [debug_flag],0
      push ecx
      push edx
      xor ecx,ecx
      xor edx,edx
      call Terraria.Main::SkipToTime
      pop edx
      pop ecx
    @@:
    

    Но игра вылетает :(

    G 1 ответ Последний ответ 23 мая 2024 г., 07:33 Ответить Цитировать 0
    • P
      paracetamol
      отредактировано 22 мая 2024 г., 08:50

      Вариант с передачей в стек не отличается результатом :(

        cmp byte ptr [debug_flag],0
        je @f
        mov byte ptr [debug_flag],0
        push 00000000
        push 00000000
        call Terraria.Main::SkipToTime
      @@:
      
      1 ответ Последний ответ Ответить Цитировать 0
      • G
        garik66 @paracetamol
        отредактировано 23 мая 2024 г., 07:33

        @paracetamol попробуй ret после call добавить - нет возврата из функции, поэтому вылет.

        P 1 ответ Последний ответ 23 мая 2024 г., 13:57 Ответить Цитировать 0
        • P
          paracetamol @garik66
          отредактировано 23 мая 2024 г., 13:57

          @garik66 Я не совсем понимаю.
          ret - это возврат из текущей функции к коду, который её вызвал. Место где я делаю call - это не функция. Просто место кода, где я делаю всякие свои штуки.
          Намереваюсь вместе с ними при некоторых условиях (взведён debug_flag) сделать так, чтобы отработала функция, находящаяся по адресу после call.
          Функция которую я хочу вызвать - SkipToTime

          Terraria.Main::SkipToTime - push ebp
          Terraria.Main::SkipToTime+1- mov ebp,esp
          Terraria.Main::SkipToTime+3- push edi
          Terraria.Main::SkipToTime+4- push esi
          Terraria.Main::SkipToTime+5- sub esp,08
          Terraria.Main::SkipToTime+8- xor eax,eax
          Terraria.Main::SkipToTime+A- mov [ebp-0C],eax
          Terraria.Main::SkipToTime+D- mov edi,ecx
          Terraria.Main::SkipToTime+F- movzx esi,dl
          Terraria.Main::SkipToTime+12- movzx eax,byte ptr [058C128D]
          Terraria.Main::SkipToTime+19- cmp esi,eax
          Terraria.Main::SkipToTime+1B- je Terraria.Main::SkipToTime+4E
          Terraria.Main::SkipToTime+1D- call dword ptr [07F45530]
          Terraria.Main::SkipToTime+23- mov [ebp-0C],eax
          Terraria.Main::SkipToTime+26- cmp byte ptr [058C128D],00
          Terraria.Main::SkipToTime+2D- je Terraria.Main::SkipToTime+3A
          Terraria.Main::SkipToTime+2F- lea ecx,[ebp-0C]
          Terraria.Main::SkipToTime+32- call dword ptr [07F4553C]
          Terraria.Main::SkipToTime+38- jmp Terraria.Main::SkipToTime+43
          Terraria.Main::SkipToTime+3A- lea ecx,[ebp-0C]
          Terraria.Main::SkipToTime+3D- call dword ptr [07F45548]
          Terraria.Main::SkipToTime+43- movzx eax,byte ptr [058C128D]
          Terraria.Main::SkipToTime+4A- cmp esi,eax
          Terraria.Main::SkipToTime+4C- jne Terraria.Main::SkipToTime+1D
          Terraria.Main::SkipToTime+4E- mov [ebp-10],edi
          Terraria.Main::SkipToTime+51- fild dword ptr [ebp-10]
          Terraria.Main::SkipToTime+54- fstp qword ptr [058C0CE8]
          Terraria.Main::SkipToTime+5A- cmp dword ptr [058C10B8],02
          Terraria.Main::SkipToTime+61- jne Terraria.Main::SkipToTime+82
          Terraria.Main::SkipToTime+63- push -01
          Terraria.Main::SkipToTime+65- push 00
          Terraria.Main::SkipToTime+67- push 00
          Terraria.Main::SkipToTime+69- push 00
          Terraria.Main::SkipToTime+6B- push 00
          Terraria.Main::SkipToTime+6D- push 00
          Terraria.Main::SkipToTime+6F- push 00
          Terraria.Main::SkipToTime+71- push 00
          Terraria.Main::SkipToTime+73- push 00
          Terraria.Main::SkipToTime+75- mov ecx,00000007
          Terraria.Main::SkipToTime+7A- lea edx,[ecx-08]
          Terraria.Main::SkipToTime+7D- call Terraria.NetMessage::TrySendData
          Terraria.Main::SkipToTime+82- lea esp,[ebp-08]
          Terraria.Main::SkipToTime+85- pop esi
          Terraria.Main::SkipToTime+86- pop edi
          Terraria.Main::SkipToTime+87- pop ebp
          Terraria.Main::SkipToTime+88- ret 
          

          ret в ней есть к слову
          Чтобы понять как правильно её вызывать (у неё есть параметры)
          ff99a16d-351d-4137-9a55-2794fd25a539-image.png
          Я проводил искал код её вызывающий и нашёл 4 похожих друг на друга класса
          2a1397ac-746f-49cf-9280-203d9b24c862-image.png
          Каждый из которых отличается в коде только формированием регистров ECX и EDX (гипотеза моя в том, что ECX - первый параметр, а EDX - второй)
          Не мудрствуя лукаво решил просто сделать вызов так, как это делает один из этих классов своим методом UsePower:

          StartNightImmediately::UsePower - cmp dword ptr [058C10B8],01
          StartNightImmediately::UsePower+7- jne StartNightImmediately::UsePower+A
          StartNightImmediately::UsePower+9- ret 
          StartNightImmediately::UsePower+A- xor ecx,ecx
          StartNightImmediately::UsePower+C- xor edx,edx
          StartNightImmediately::UsePower+E- call Terraria.Main::SkipToTime
          StartNightImmediately::UsePower+13- ret 
          StartNightImmediately::UsePower+14- add [eax],al
          

          Т.е. такие 2 xor, затем call

          Полагаю мой провал связан с тем, что помимо формирования корректного состояния регистров мне ещё нужно было озаботиться тем, чтобы также правильно сформировать стек. Всё дело в стеке? Может кто подсказать как изучить стек в момент перед вызовом call'а и как его повторить в своём коде?

          1 ответ Последний ответ Ответить Цитировать 0
          • P
            paracetamol
            отредактировано 23 мая 2024 г., 19:11

            Я нашёл решение и теперь у меня получилось успешно вызвать функцию - эффект от неё применился и никаких крашей! :blush:

            Рассказываю и читающему, если ему вдруг тоже интересно

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

            Причины предыдущих неудач
            Игра крашилась у меня не из-за того, что я что-то не так передавал, а из-за того, что вызываемая функция (SkipToTime, см.выше код) почему то:

            1. уничтожает значения регистров edi и esi
            Terraria.Main::SkipToTime+3- push edi
            Terraria.Main::SkipToTime+4- push esi
            Terraria.Main::SkipToTime+5- sub esp,08
            
            1. сразу за этим безобразием без бэкапов обнуляет eax
            Terraria.Main::SkipToTime+8- xor eax,eax
            

            Ну и соответственно после отработки функции и возвращения в исходный код начинали твориться "чудеса" из-за сбоя состояния регистров.

            Найденное решение:

            1. бэкап всех регистров общего назначения, а также edi и esi (понятное дело ebp и esp бэкапить не нужно)
              push eax
              push ebx
              push ecx
              push edx
              push edi
              push esi
            
            1. далее заполняем регистры необходимыми значениями (в моём случае это заполненные нулями ecx и edx)
              xor ecx,ecx
              xor edx,edx
            
            1. собственно сам вызов
            call Terraria.Main::SkipToTime
            
            1. ну и восстанавливаем состояние регистров (естественно в обратном бэкапу порядке)
              pop esi
              pop edi
              pop edx
              pop ecx
              pop ebx
              pop eax
            

            Всё.

            В Террарии наступила ночь, часики перемотались на 7:30 вечера, игра не вылетела, я счастлив, мир прекрасен!

            StoneWeaverS 1 ответ Последний ответ 25 мая 2024 г., 04:55 Ответить Цитировать 1
            • StoneWeaverS
              StoneWeaver administrators @paracetamol
              отредактировано 25 мая 2024 г., 04:55

              @paracetamol Ты прошёл по пути самурая, и пришёл к решению самостоятельно. Это круто! От себя добавлю - если бы ты сразу попробовал выяснить, какой тип вызовов в этой игре используется, возможно, твой путь к правильному решению был бы намного короче :)

              P 1 ответ Последний ответ 25 мая 2024 г., 10:08 Ответить Цитировать 0
              • P
                paracetamol @StoneWeaver
                отредактировано 25 мая 2024 г., 10:08

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

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

                1 ответ Последний ответ Ответить Цитировать 1
                • P
                  paracetamol
                  отредактировано 25 мая 2024 г., 10:19

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

                    // начало скрипта (flgRain выведен в таблицу и назначена горячая клавиша на установку значения в 1)
                    cmp byte ptr [flgRain],0              // проверяем не было ли команды на смену дождя
                    je endRain                            // если нет - завершаем скрипт
                    mov byte ptr [flgRain],0              // если была, то сначала сбрасываем флаг обратно (нам не нужно беспрерывно менять дождь туда-сюда, нужно выполнить код лишь единожды)
                    pushad                                // сохраняем все регистры в стек
                    call Terraria.Main::get_IsItRaining   // проверяем идёт ли сейчас дождь (результат вернётся в регистр EAX)
                    test eax,eax                          // 0 - не идёт, 1 - идёт
                    je @f                                 // прыжок к секции начала дождя (пропустить остановку дождя) если EAX==0
                    call Terraria.Main::StopRain          // останавливаем дождь
                    popad                                 // восстанавливаем регистры из стека
                    jmp endRain                           // выход из скрипта (не нужно запускать дождь)
                  @@:
                    call Terraria.Main::StartRain         // запустить дождь
                    popad                                 // восстановить регистры из стека
                  endRain:
                  

                  За идею использования инструкций pushad/popad хочу поблагодарить пользователя "youneuoy" с Discord канала GamehackLab[RU].

                  1 ответ Последний ответ Ответить Цитировать 1
                  1 из 8
                  • Первое сообщение
                    1/8
                    Последнее сообщение