CELua[RU]
    • Категории
    • Последние
    • Метки
    • Популярные
    • Пользователи
    • Группы
    • Зарегистрироваться
    • Войти
    1. Главная
    2. MasterGH
    3. Сообщения
    Не в сети
    • Профиль
    • Подписки 1
    • Подписчики 1
    • Темы 131
    • Сообщения 255
    • Группы 4

    Сообщения

    Последние Лучшие сообщения Спорные
    • Опрос. На чем создаю читы?
      написал в Новости
      MasterGHM
      MasterGH
    • RE: CE Dissect Data Scaner 1.0.2 (4 hardware breakpoints)

      Охота на структуры и удивительное путешествие в мир структур в L4D2

      1. Игру L4D2 в оконный режим

      2. Установка плагина (позже)

      3. Установка VEH отладчика, иначе вылеты

      4. Первая миссия. Ищем адрес патронов. Бряк на адрес. Нашли инструкцию
        4f3f9116-b3d2-4c56-bc7d-bbb2ce839ef2-image.png
        Красным показано, что я не всю структуру проанализирую позже. Долго было ждать. Структура больше 5К байт...

      5. Делаем сигнатуру любой инструкции при работе с патронами на всякий случай, если игра вылетит.

      server.dll,83 BE 14 14 00 00 00 7F 4D
      6. Переходим в код и ищем проскакивающие адреса на инструкции. Там один адрес нашего пистолета. Клик на него и переносим адрес начала будущей структуры в окно Dissect Window. Можно не создавать структуру (отказываемся). Кликаем "Scanner" (он будет на всех окнах dissrct data)
      415be144-f67e-4d9b-8cda-8a0f14a99a45-image.png
      7. Далее работает сканирование.
      3c3caa73-8292-4d83-8267-958f805de5d0-image.png
      В игре можно что-то делать, можно ничего не делать. Долго ждать..

      Меняю опции. Чувствительность как была так и осталась минимальная (это время ожидания прерывания на адресе умноженное на коэфициент чувсвительности и на 100 мс). Размер структуры меняю, до 1100
      7e7ac546-2b8a-4c07-a0fe-5d973df7fa15-image.png
      b005e43b-17ad-4c98-a58d-ed9c8bffd9d4-image.png
      Наконец ~30 секунд дождался без вылетов (иногда бывают, возможно из-за VEH). Появился результат
      c3cf4da1-c639-4471-a4e3-4cd3d372e291-image.png
      Самые интересны это байтовые и float значения. и инструкции чтения. Их определит можно пока только по логам...

      Меняю первый байт на 1 и пистолет стал очень быстро стрелять.
      b111bcbd-d82c-4d72-9822-7bed532b87e7-image.png
      Ради чего это все и делалось, чтобы похожие адреса искать...

      Если сравнить дефолтную расструктуризацию, она слева
      7281f3f1-038c-4901-8a50-7d11f173cb5c-image.png
      Логи (для меня и для желающих) по определению типа по опкодам

      \+0 (2F3B49B0): vtDword 64FC73CB  -  mov edx,[ecx]
      \+28 (2F3B49D8): vtDword 6530433E  -  mov eax,[esi+28]
      \+4C (2F3B49FC): vtDword 651169A3  -  cmp dword ptr [esi+30],00
      \+4F (2F3B49FF): vtDword 651169B6  -  mov eax,[esi+30]
      \+53 (2F3B4A03): vtDword 65116C60  -  mov eax,[ecx+34]
      \+64 (2F3B4A14): vtByte 64F8627D  -  cmp byte ptr [esi+64],00
      \+84 (2F3B4A34): vtDword 64F861F0  -  movss xmm0,[esi+00000084]
      \+88 (2F3B4A38): vtDword 64F86215  -  movss xmm2,[esi+00000088]
      \+8C (2F3B4A3C): vtDword 64F9F9A6  -  movss xmm0,[eax+0000008C]
      \+90 (2F3B4A40): vtDword 64FF2CC6  -  fld dword ptr [eax]
      \+C8 (2F3B4A78): vtDword 64FF2DA6  -  mov ecx,[eax]
      \+CC (2F3B4A7C): vtDword 64FF2DE6  -  mov ecx,[eax]
      \+E0 (2F3B4A90): vtDword 64FF2DA6  -  mov ecx,[eax]
      \+104 (2F3B4AB4): vtDword 651141FA  -  mov eax,[eax]
      \+10C (2F3B4ABC): vtByte 64FF2DC6  -  movzx ecx,byte ptr [eax]
      \+10D (2F3B4ABD): vtByte 64FF2DC6  -  movzx ecx,byte ptr [eax]
      \+10E (2F3B4ABE): vtWord 64FF2D96  -  movsx ecx,word ptr [eax]
      \+110 (2F3B4AC0): vtDword 64FF2DB6  -  mov ecx,[eax]
      \+138 (2F3B4AE8): vtDword 64FBB772  -  or [esi+00000138],edi
      \+171 (2F3B4B21): vtByte 64FBB7D4  -  cmp byte ptr [esi+00000171],00
      \+172 (2F3B4B22): vtByte 64FA6572  -  cmp byte ptr [esi+00000172],03
      \+173 (2F3B4B23): vtByte 64FF2DC6  -  movzx ecx,byte ptr [eax]
      \+174 (2F3B4B24): vtDword 651141FA  -  mov eax,[eax]
      \+178 (2F3B4B28): vtDword 64FBB79D  -  mov eax,[esi+00000178]
      \+17C (2F3B4B2C): vtDword 64FBB7E8  -  mov eax,[esi+0000017C]
      \+180 (2F3B4B30): vtDword 64FDD9A6  -  mov eax,[ecx]
      \+188 (2F3B4B38): vtDword 64FF2D66  -  fld dword ptr [eax]
      \+18C (2F3B4B3C): vtDword 64FF2D6D  -  fld dword ptr [eax+04]
      \+190 (2F3B4B40): vtDword 64FF2D73  -  fld dword ptr [eax+08]
      \+194 (2F3B4B44): vtDword 64FF2D66  -  fld dword ptr [eax]
      \+198 (2F3B4B48): vtDword 64FF2D6D  -  fld dword ptr [eax+04]
      \+19C (2F3B4B4C): vtDword 64FF2D73  -  fld dword ptr [eax+08]
      \+1A0 (2F3B4B50): vtByte 64F9FD30  -  movzx eax,word ptr [ecx+20]
      \+1A1 (2F3B4B51): vtByte 64F9FD30  -  movzx eax,word ptr [ecx+20]
      \+1A2 (2F3B4B52): vtByte 64FDDBB0  -  movzx eax,byte ptr [ecx+22]
      \+1A3 (2F3B4B53): vtByte 64FF2DC6  -  movzx ecx,byte ptr [eax]
      \+1AA (2F3B4B5A): vtByte 64FF2DC6  -  movzx ecx,byte ptr [eax]
      \+1AC (2F3B4B5C): vtDword 64FF2D66  -  fld dword ptr [eax]
      \+1B0 (2F3B4B60): vtDword 64FF2D6D  -  fld dword ptr [eax+04]
      \+1B4 (2F3B4B64): vtDword 64FF2D73  -  fld dword ptr [eax+08]
      \+1B8 (2F3B4B68): vtDword 64FF2D66  -  fld dword ptr [eax]
      \+1BC (2F3B4B6C): vtDword 64FF2D6D  -  fld dword ptr [eax+04]
      \+1C0 (2F3B4B70): vtDword 64FF2D73  -  fld dword ptr [eax+08]
      \+1E0 (2F3B4B90): vtDword 64FF2DA6  -  mov ecx,[eax]
      \+1E4 (2F3B4B94): vtDword 64FF2DA6  -  mov ecx,[eax]
      \+1E8 (2F3B4B98): vtDword 64FF2DA6  -  mov ecx,[eax]
      \+1EC (2F3B4B9C): vtDword 64FF2DB6  -  mov ecx,[eax]
      \+1F0 (2F3B4BA0): vtByte 64FF2DC6  -  movzx ecx,byte ptr [eax]
      \+20C (2F3B4BBC): vtDword 651141FA  -  mov eax,[eax]
      \+210 (2F3B4BC0): vtDword 651141FA  -  mov eax,[eax]
      \+214 (2F3B4BC4): vtDword 64FF2CC6  -  fld dword ptr [eax]
      \+218 (2F3B4BC8): vtDword 64FF2CC6  -  fld dword ptr [eax]
      \+21C (2F3B4BCC): vtDword 64FF2CC6  -  fld dword ptr [eax]
      \+220 (2F3B4BD0): vtDword 64FF2DE6  -  mov ecx,[eax]
      \+22C (2F3B4BDC): vtDword 64FF2CC6  -  fld dword ptr [eax]
      \+238 (2F3B4BE8): vtDword 64FF2DA6  -  mov ecx,[eax]
      \+2B4 (2F3B4C64): vtDword 64FF2CC6  -  fld dword ptr [eax]
      \+2E4 (2F3B4C94): vtByte 64FF2DC6  -  movzx ecx,byte ptr [eax]
      \+2E5 (2F3B4C95): vtByte 64FF2DC6  -  movzx ecx,byte ptr [eax]
      \+2E6 (2F3B4C96): vtByte 64FF2DC6  -  movzx ecx,byte ptr [eax]
      \+2E7 (2F3B4C97): vtByte 64FF2DC6  -  movzx ecx,byte ptr [eax]
      \+2E8 (2F3B4C98): vtByte 64FF2DC6  -  movzx ecx,byte ptr [eax]
      \+2E9 (2F3B4C99): vtByte 64FF2DC6  -  movzx ecx,byte ptr [eax]
      \+2EA (2F3B4C9A): vtByte 64FF2DC6  -  movzx ecx,byte ptr [eax]
      \+2EB (2F3B4C9B): vtByte 64FF2DC6  -  movzx ecx,byte ptr [eax]
      \+2EC (2F3B4C9C): vtByte 64FF2DC6  -  movzx ecx,byte ptr [eax]
      \+374 (2F3B4D24): vtDword 64FA65AC  -  mov edi,[esi+00000374]
      \+378 (2F3B4D28): vtDword 64FF2DE6  -  mov ecx,[eax]
      \+37C (2F3B4D2C): vtDword 64FF2DE6  -  mov ecx,[eax]
      \+380 (2F3B4D30): vtDword 64FF2DE6  -  mov ecx,[eax]
      \+384 (2F3B4D34): vtDword 64FF2DE6  -  mov ecx,[eax]
      \+388 (2F3B4D38): vtDword 64FA65B8  -  movss xmm1,[ecx]
      \+38C (2F3B4D3C): vtDword 64FA660B  -  movss xmm1,[ecx+04]
      \+390 (2F3B4D40): vtDword 64FA6653  -  movss xmm1,[ecx+08]
      \+394 (2F3B4D44): vtDword 64FA68AD  -  movss xmm1,[eax]
      \+398 (2F3B4D48): vtDword 64FA68E1  -  movss xmm2,[eax+04]
      \+39C (2F3B4D4C): vtDword 64FA6904  -  movss xmm2,[eax+08]
      \+3A0 (2F3B4D50): vtDword 64FDEC3A  -  mov ecx,[edi]
      \+3B8 (2F3B4D68): vtDword 6508F54F  -  test [eax+000003B8],edx
      \+42C (2F3B4DDC): vtDword 64F84E81  -  fstp dword ptr [esi+0000042C]
      \+434 (2F3B4DE4): vtDword 64FF2DA6  -  mov ecx,[eax]
      \+438 (2F3B4DE8): vtDword 64FF2D66  -  fld dword ptr [eax]
      \+43C (2F3B4DEC): vtDword 64FF2D6D  -  fld dword ptr [eax+04]
      \+440 (2F3B4DF0): vtDword 64FF2D73  -  fld dword ptr [eax+08]
      \+444 (2F3B4DF4): vtDword 64FF2DA6  -  mov ecx,[eax]
      \+448 (2F3B4DF8): vtDword 64FF2DA6  -  mov ecx,[eax]
      \+44C (2F3B4DFC): vtDword 64FF2DE6  -  mov ecx,[eax]
      

      Плагин выложу позже. Надо еще доработать и потестить.

      Вот к примеру float распознал как Pointer и там где fst тоже по +42C тоже фигня. Это ошибки. Это быстро поправить, но могут быть еще ошибки.
      1a48dcb3-c1d8-4132-aca1-cca4bef03aa4-image.png

      Не менее интересны еще вложенные структурки, которые удается раскрыть (не все). Вот одна из них и усеяна параметрами, которые можно покрутить
      abdc2308-5907-4d18-ae78-24abc521b91a-image.png

      Вложенные структуры пока отдельно можно создавать в окнах, чистить сканером, потом подставлять их в основную структуру по имени. В общем пока рано об этом писать.

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

      написал в Плагин-строй
      MasterGHM
      MasterGH
    • CE Dissect Data Scaner 1.0.2 (4 hardware breakpoints)
      Функции плагина:

      1. Точное определение типа данных на смещениях структуры (те которые срабатывают на аппаратных брейкпоинтах);
      2. Показать в имени элемента структуры один из опкодов чтения или его часть (с регистрами и смещением);
      3. Найти такие смещения, с которыми работают только инструкции чтения за указанное время равное Accuracy * 10мс на байт в структуре. Чем выше Accuracy, тем выше вероятность убрать больше лишний смещений;

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

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

      b385f695-a526-46df-89eb-c82298547404-image.png

      Это тестовая версия, поэтому могут быть ошибки при сканировании. О них можно написать мне в личку с названием игры, сигнатуры инструкции.

      Если плагин вдруг заключил и пишет что-то в консоли, то нажать на кнопку Stop или для остановки ввести Lua команду

      stopDissectDataScanner()
      Установка

      Распаковать в основную директорию с программой. В архиве форма, файл настроек и lua исходник.

      Как работать с плагином

      1. Ищем параметр в игре
      2. Ставим "точку останова на доступ"(брейкпоинт) или "на запись"
      3. Находим одну из инструкций. Например, в игре Кредо Убийцы 3 это инструкция
      AC3SP.exe+11BAEEB - 66 89 4E 5C           - mov [esi+5C],cx
      
      1. Можно пойти двумя способами по переносу базового адреса в окно структур

      4.1 На инструкции смотрим например базовый адрес ESI, он будет началом структуры. Его переносим в окно Dissect Data (открывается в отладчике). Отказываемся от всех диалогов, чтобы не получать структуру, которую создает Cheat Engine.

      4.2 Определить адреса например на "[esi+5c]" в дизассемблере и на любом вызвать контекстное меню и перебросить базовый адрес в Dissect Data

      1. В окне Dissect Data запускаем "Scanner" нажав на эту надпись и вводим имя структуры, размер в hex, и чувствительность поиска от 1 до 10

      2. Идем как можно быстрее в игру, а она уже должна быть активна и в оконном режиме, и не в меню. Ждем пока завершится сканирование, что-то делая в игре или простоя стоим там. Если очень долго, можно уменьшить чувствительность или размер структуры в hex-е

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

      Результаты

      Нет 100% гарантии сразу найти все рабочие адреса, в которых что-то будет интересное. Большая часть может себя не проявить в игре или привести к вылетам. После определенного количества вылетов, вы поймете какие типы данных и как менять и вылетов будет меньше.

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

      Вызов Lua кода после crash-а вернет адрес инструкции

      local sign = 'F3 0F 11 80 EC 00 00 00'
      function GetAddress(signature)
        local t = AOBScan(signature, '+X-C-W',0)
        local s = t[0]
        t.destroy()
        return s
      end
      print(GetAddress(sign))
      

      ...Много зависит от ползания по структуре и внутри структур (об этом в блоге) и соответственно, нужно время.

      Если времени мало. Получаем около 20 параметров структуре байтовых и сингловых. Безопасней всего менять сингловые. Их можно увеличить все в 2 раза и зайти в игру и увидеть, что там происходит. Не забываем перед правками сделать копию структуры и нажать на Lock, чтобы сравнить и откатить изменения. За тем уже правим по байтикам на ноль или на единицу. Редко бывают вылеты. Каждый адрес отмечаем, что его проверяли или удаляем. Если интересные, то меняем название. Сохраняем структуру.

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

      Собрали структуры. Ищем связи между ними. Об этом я скриншотил в блоге (ссылка выше). Опять сохраняем структуры.

      На три игры я находил 1-3 полезных параметра и до 6 каких-то забавных или баговых. Например, я нашел коэффициент скольжения персонажей в структуре с координатами и там же гравитацию. Попадались адреса, которые запрещали двигаться, т.е. можно морозить ботов. Попадались байтовые адреса, меняющие руки с оружием в L4D2.

      Структуры сохраняются в файл таблицы или экспортируются в отдельный файл.

      Далее делаем читы, как обычно. Либо меняем код, либо меняем данные по базовому указателю через скрипты Аutoasembler

      1. Исправлены ошибки с определением смещения и типов данных. Это позволил сделать новый алгоритм, который анализирует все опкоды работающие с адресом и выбирает наиболее подходящие для определения смещения адреса в структуре и для определения типа данных в структуре.

      Выбор лучшего опкода для определения типа данных:

      - опкоды с перезаписываемыми регистрами не выбираются;

      - опкоды работающие с float приоритетнее чем dword;

      - опкоды без квадратных скобок пока пропускаются: всякие repe, movsd и другие

      - опкоды с не правильным дизассемблированием пропускаются (невозможно получить адрес в скобках)

      1. Много новых данных в логах, после формирования структуры. С его помощью можно просмотреть все опкоды, которые сработали на смещении структуры.

      На скриншоте пример структуры, после работы плагина

      c8279a93-2615-4122-bcfe-6d2d8b9a7933-image.png

      написал в Плагин-строй
      MasterGHM
      MasterGH
    • CE Plugin: AA Maker 2.4.2

      AAmaker предназначен для создания создания autoassembler скриптов для Cheat Engine 6.5 или выше

      cd490844-e873-49d0-bfe4-3b914fe35217-image.png

      Предыстория. Изначально нужно было генерировать динамически АА-шаблоны по правилам, не зная регистров и прочего, на инструкции по сигнатуре. Это не было реализовано.

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

      -----------
      Это плагин CE Lua plugin для Cheat Engine 6.5 или выше

      AAmaker предназначен для содания создания autoassembler скриптов.

      1. Установите aamaker.lua в "Autorun" директорию
      2. Запустите Cheat Engine.
      3. Присоедините процесс
      4. Перейдите в "Memory View" окно
      5. Выделите адрес кода
      6. Правой кнопки мышки вызовите контекстное меню
      7. Используйте функции "AA Maker" в контекстном меню
        5b40b07a-28d6-4489-9e76-6d916254187f-image.png
        ----------
        + исправления
        + новый директивы:
        {$AddressInjection}
        {$OriginalCode}
        {$CheatCode}
        {$Nops}
        {$ArrayOfbyte}
        {$ProcessName}
        {$Date}
        {$PrintLog}
        + исправления шаблонов
        + удален aobscan. Изменен aobscanmodule
      8. Исправление ошибок (автор ++METHOS)
      9. Добавление шаблонов (автор ++METHOS)
        Исправление. Совместимость с CE 6.5
      написал в Плагин-строй
      MasterGHM
      MasterGH
    • CE Lua Крестики и нолики

      Крестики и нолики.CT

      c7c664ad-b109-47d7-a1f0-3c8882243941-image.png
      Игра с рандомом. Есть счет.
      Запускается через таблицу в аттаче.
      Здесь мог быть AI на Lua, возвращающий номер клетки от 1 до 9.

      function InputAI()
       -- рандом
       return math.random (1, 9)
      end
      
      player1Symbol = '1'
      player2Symbol = '0'
      whoStep = 0
      player1Score = 0
      player2Score = 0
      
      function InputAI()
       -- рандом
       return math.random (1, 9)
      end
      
      \-- Проверка правил
      function caheckRules(symbol)
      
        -- Комбинации выигрыша
        local tableRules =
        {
          {1,2,3}, {4,5,6}, {7,8,9},  -- горизонтальные клетки
          {1,4,7}, {2,5,8}, {3,6,9},  -- вертикальные клетки
          {1,5,9}, {3,5,7}            -- клетки по диагонали
        }
      
        -- Результат проверки клеток
        local mask = [[
          return UDF1.CEButton%s.Caption == 'symbol' and
                 UDF1.CEButton%s.Caption == 'symbol' and
                 UDF1.CEButton%s.Caption == 'symbol'
        ]]
      
        for i = 1, #tableRules do
      
          local luaStringCode = mask:format(tableRules[i][1],
            tableRules[i][2], tableRules[i][3]):gsub('symbol', symbol)
      
          if loadstring (luaStringCode)() then
            return true
          end
      
        end
      
        return false
      end
      
      function StartGame()
        whoStep = 0
        for i = 1, 9 do
          loadstring ('UDF1.CEButton'..i..'.Caption = ""')()
        end
      end
      
      \-- 1 - ход сделан, 0 - ход не удался
      function Input(indexInput)
      
        if loadstring ('return UDF1.CEButton'..indexInput..'.Caption')() ~= '' then
          return 0
        end
      
        local writeSymbol = player1Symbol
        if whoStep == 0 then
          whoStep = 1
        else
          whoStep = 0
          writeSymbol = player2Symbol
        end
      
        local s = 'UDF1.CEButton'..indexInput..'.Caption = '..writeSymbol
        loadstring (s)()
      
        local somebodyWinner = false
      
        if caheckRules(player1Symbol) then
          player1Score = player1Score + 1
          UDF1.CELabelScore.Caption = player1Score..':'..player2Score
          ShowMessage('Player1 is winner!')
          somebodyWinner = true
      
        elseif caheckRules(player2Symbol) then
          player2Score = player2Score + 1
          UDF1.CELabelScore.Caption = player1Score..':'..player2Score
          ShowMessage('Player2 is winner!')
          somebodyWinner = true
        end
      
        -- Если кто-то выиграл, то очистить поле
        if somebodyWinner then
          StartGame()
          return 1
        end
      
        --  Проверка ничьи
        local countEmpty = 9
        for i = 1, 9 do
          if loadstring ('return UDF1.CEButton'..i..'.Caption ~= ""')() then
            countEmpty = countEmpty - 1
          end
        end
      
        if countEmpty <= 0 then
          UDF1.CELabelScore.Caption = player1Score..':'..player2Score
          ShowMessage('Friendship!')
          StartGame()
          return 1
        end
      
        -- Ходит IA
        if whoStep == 1 then
          ::repeat1::
          local index = InputAI()
          if Input(index) == 0 then
            goto repeat1
          end
        end
      end
      
      
      function CEButtonClick(sender)
        Input(tonumber(sender.name:match('%d')))
      end
      
      function MenuItem1Click(sender)
        player1Score = 0
        player2Score = 0
        UDF1.CELabelScore.Caption = player1Score..':'..player2Score
        StartGame()
        ShowMessage('Restart!')
      end
      
      UDF1.show()
      
      написал в MasterGH
      MasterGHM
      MasterGH
    • CE Stack Viewer

      a50f1946-1900-4fa6-8a0c-31f2f1062c12-image.png

      Информация о файле
      Назначение

      Плагин позволяет в реальном режиме игры наблюдать за стеком на определенной инструкции отладочного кода. Это в свою очередь позволяет в окне расструктуризации данных "Dessect Data Structure" сравнивать старые и текущие структуры стека в реальном режиме игры, наблюдать за появлением красных данных и обращать на них особое внимание. Красные данные показывают чем отличаются стеки при действиях в игре. Например, выстрелив один раз и выстрелив следующий раз можно увидеть отличия красным цветом в стеке в реальном режиме. Это могут быть не только изменения патронов, но и чего-то другого, когда уже стреляем другим оружием. Важно то, что можно видеть изменения в реальном времени, а не по копиям стека, которые приходилось раньше снимать много раз. Мы можем видеть как часто по времени что-то меняется, все в динамике...

      Есть у меня сомнения по поводу постоянного ESP сравнения, при котором происходит "наблюдение" за стеком, но пока оставлю как есть.

      Установка

      Распаковать файлы архива в директорию "autorun". Например,

      C:\Program Files (x86)\Cheat Engine 6.4\autorun
      Краткое руководство

      1. Запускаем игру

      2. Находим адрес параметра

      3. Ставим брейкпоинт на адрес и появляются инструкции (или что-то делаем в игре чтобы инструкции появились)

      4. Выделяем инструкцию, которую требуется исследовать

      5. Останавливаем отладку

      6. Открываем Stack Viewer из окна Дизассемблер-> Tools->* Stack Viewer [Plugin] и переносим в ESP, EIP (или RSP, RIP) в поля окна Stack Viewer. Нажимаем галку "Is Active" и идем в игру, и если надо что-то делаем в игре, потому что инструкция должна выполниться хотя бы один раз чтобы заполнить дамп стека в реальном времени

      7. Открываем окно расструктуризации и пишем метку "MemStackRunTime" (или другую) и делаем расструктуризацию.

      8. Открывает плагин Tiny Dumper и клонируем "MemStackRunTime" в другую метку "StackDump1" (или другую). Теперь у нас в окне две структуры: с разовой копией и постоянно обновляющейся копией стека.

      9. Идем в игру и повторно делаем какие-то действия при этом наблюдаем за окном расструктуризации. Если в окне красные данные не появились, то следует проскролить структуру ниже. Если красные данные появились, то можно повторить действия в игре и пронаблюдать в реальном режиме закономерность изменения красных данных и частоту их изменения. Возможно найдутся интересные зависимости связанные с каким-то действием в игре. Если красных данных не нашлось, то никаких изменений в стеке не было. Попробуйте сделать что-то другое.

      10. Для остановки наблюдения за стеком выключаем галку "Is Active"

      написал в Плагин-строй
      MasterGHM
      MasterGH
    • RE: CE Tiny Dumper

      Пошаговое руководство по Tiny Dumper

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

      1. Перед запуском CE устанавливаем файлы плагина в папку autorun (frmTinyDumper.xml и TinyDumper.lua)

      2. Запускаем игру например "Painkiller - Battle out of Hell 1.64" поставленная в оконный режим через 3DAnalyze.

      Оконный режим ставить обязательно, т.к. будет пошаговая отладка. В противном случае игра зависнет, а с CE рабтать нельзя будет

      1. Запустить CE с установленным плагином и подключиться к игре

      2. Ищем адрес здоровья (или брони, или патронов), тип Double (8 байт)
        0x24571EA8 = 99.2900238037109

      3. Ставим бряк на доступ или на запись на адрес 0x24571EA8

      2bf8d038-de92-4de0-88f6-944b9093adf5-image.png

      1. Идем в игру и встаем под удар персонажем

      Появляются инструкции
      3b2e99be-4a03-455c-a575-28511d347064-image.png
      и там же нажимаем на кнопку Stop, чтобы бряки не мешались в окне бряков

      1. Берем например инструкцию

      1015C194 - 89 53 08 - mov [ebx+08],edx
      6ad80302-80f1-4905-8bac-bdbfbb930bfd-image.png
      По ней идем в дизассемблер и на инструкции жмем F5 - установка бряка на доступ

      2755fb71-f09c-4815-88c7-191fbfdf2dc7-image.png

      1. Открываем окно бряков, выделяем инструкцию и пишем условие на прерывание с остановкой процесса

      EBX==0x24571EA0 (это условие взяли, потому что ebx находится в инструкции, а значение взяли EBX из отладчика)

      2c1e1ed4-0df0-4314-981f-f8e31550959d-image.png
      43e9dfd5-1c49-4bf2-bf1a-b588c308181c-image.png

      Нажимаем Ок, идем в игру
      9. Видим, что произошла остановка игры
      148b600b-4ffe-4e2c-893a-df3330aff53f-image.png
      10. Открываем окно ТиниДампера
      6db64901-ca0e-4d32-8be9-2fcb27894556-image.png

      1. Пишем значение регистра esp и название будущей зарегенной метки
        26586a39-c6f1-40ee-a44b-bd966578e736-image.png

      2. Нажимаем "Dump and Close" и CTRL+U чтобы видеть под рукой зареганные ваши метки

      7f7a0d40-9107-4c19-b13f-2105a57422a5-image.png

      1. Открываем окно расструктуризации CTRL+D и пишем метку в свободное поле
        1104aed5-e0c1-45bd-b880-877433c6c1a1-image.png

      2. Далее следуем действия как на рисунках
        9cac0888-208c-4f0e-9ae2-b53f6c49aeb4-image.png
        7db1307b-9305-4fd6-9b34-e75c2c8cc466-image.png

      3. Наконец результат
        6d506668-7bed-4cae-9c22-47af124c0c16-image.png

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

      1. Сейчас процесс игры остановлен на бряке кода. Чтобы продолжить игру. Удаляем бряк из окна бряков и отпускаем игру по F9.

      2. Теперь снимаем другие дампы на том же адресе для адресов здоровья врагов на предмет отличиях их или для поиска указателей... Во всяком случае сохраненный дамп может дать какие-то подсказки и зацепки. Главное не начать новый уровень игры или не загружать сохранения, т.к. обычно при переходе на новый уровень данные могут измениться... На этом пока все

      -----
      Tiny dumper 1.2 Сравнение структур после перезагрузки. Поиск фильтра свой-чужой-дружественый

      1. Запускаем игру, запускаем CE и присоединяем процесс игры к CE

      2. Сохраняем игровой слот перед тем как искать структуры

      3. Находим три структуры игроков: свой игрок, дружественный, враг

      4. Дампим три структуры из пункта 3 указывая: базовый адрес структуры, уникальную метку

      5. Игру закрываем, снова открываем, загружаем слот, присоединяем процесс игры к CE

      6. В Tiny dumper 1.2 вызываем контекстное меню правой кнопки мышки и вызываем опцию "Rewrite dumps" - дампы из кеша перезаписываются в память в процесса игры

      7. Снова находим три структуры игроков: свой игрой, дружественный, враг

      8. В окне расструктуризации создаем три группы

      Группа1 "Свой игрок":

      -адрес после перезагрузки

      -метка из тини дампера своего игрока до перезагрузки

      Группа2 "Чужой игрок":

      -адрес после перезагрузки

      -метка из тини дампера чужого игрока до перезагрузки

      Группа3 "Дружественный игрок":

      -адрес после перезагрузки

      -метка из тини дампера дружественного игрока до перезагрузки

      1. Если повезет, то находим фильтр - фиолетового цвета строка. Если филетовых строк несколько, то запоминаем их смещения и значения на всякий случай

      2. Пишем АА-скрипт со смещением фильтра. Правильно написанный АА-скрипт не будет крешить игру.

      3. Активируем АА-скрипт и проверяем в игре, что свой и дружественный игроки не получают урон, а вражеские получают урон. Если это не так, то берем другие смещения из пункта 9 и повторяем 10.

      Если фильтр найти не удалось, то ищем 3 многоуровневых указателя до верхего статического адреса для своего, для 2-х врагов. Открываем Structure Spider.

      Указываем в поиске уровень вложенности указателей.

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

      Если не нашлось ничего, то что-то делали не так или отличия между группами игроков нет. Однако графический интерфейс обычно связан только с нашим героем и не связан с другими. Поэтому по связи GUI как минимум фильтр свой-чужой можно сделать всегда. Что касается дружественных игроков, то нужно будет исследовать более углубленно.

      написал в Плагин-строй
      MasterGHM
      MasterGH
    • CE Tiny Dumper

      7a0117d9-fc33-467d-9ef8-3ad6b1049fe0-image.png

      Функции:

      1. Копирование участка виртуальной памяти фиксированной длины

      2. Связывание адреса копированных данных с новой меткой

      3. Возможность сравнивать дампы во время игры с любого доступного адреса (в том числе стека)

      4. Возможность сравнивать сохраненные дампы структур после перезагрузки игры

      Основное назначение - снимать дампы со стека в пошаговой отладке по адресу ESP и сравнивать эти дампы в окне расструктуризации данных (Dessect Data/Structure). Таким образом можно найти сходства или различия в передаваемых аргументах функций, а также в адресах обратного вызова по инструкции ret. Сравнение аргументов может помочь определить указатели, смещения на данные. Сравнение адресов возврата может помочь определить развилки между ветками кода ботов и ветками персонажа за которого играете. Можно начать исследовать эту развилку для определения условий свой/чужой.

      Второе назначение - сравнивать дампы после перезагрузки игры. Сравнения дампов после перезагрузки, загрузки уровней игры, загрузки слотов сохранения, смены оружия, изменения в инвентаре, смены уровня героя и многие другие изменения после перезагрузки игры могут позволить найти отличия или совпадения данных структур от начала базового адреса структуры и обратить на них гораздо большее внимание среди данных в структуре

      Данные для ввода:

      Source Address - адрес с которого будет сниматься дамп

      Size dump - размер участка данных в байтах

      Register label - метка для обращения к адресу

      *Подсказки:

      1. Плагин можно найти в первом окне дизассемблера в меню Tools (Утилиты). Окна-клоны дизассемблера не имеют подключенного подменю.

      2. Зарегистрированные метки, которые вы забыли можно посмотреть и удалить через окно зарегистрированных меток "Symbol Config" (нажать на CTRL+U в окне дизассемблера)

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

      Что нового в версии 1.1
      Размещено 6 марта, 2015

      1. Добавлен список зарегистрированных меток
      2. Добавлено контекстное меню с опциями копирования меток в буфер обмена и удаление меток с освобождением памяти
      3. Кнопка снятия дампа больше не закрывает окно

      Что нового в версии 1.2
      Размещено 6 марта, 2015

      Добавления:

      1. При выделении записи в полях выводятся данные адреса, размер дампа и название зарегистрированной метки
      2. Добавлена опция сохранения дампов
      3. Добавлена опция загрузки дампов
      4. Добавлена опция очистить таблицу
      5. Добавлена опция перезаписи дампов
      6. После закрытия игры дампы можно сохранять
        Исправления:
      7. Исправлено подключение к русской версии
      8. Исправлены название опций
        Описание контекстного меню:
      9. Copy Name - копирует имя выделенной записи
      10. Remove selected - удаляет выделенную запись
      11. Save to file - сохранить все дампы в файл, в указанную директорию
      12. Load from file - загрузить дампы из файла
      13. Ramove all records - удаляет все дампы и зарегистрированные метки
      14. Rewrite dumps - перезаписывает дампы в новую память, регистрирует метки адресов дампов повторно
      написал в Плагин-строй
      MasterGHM
      MasterGH
    • Tool C# Regular Expression

      a159e201-c947-4e08-a6e7-f22fd12524a1-image.png

      Regular Expression.rar

      string allText = richTextBox1.Text;
      string pattern = textBox2.Text;
      string s = string.Empty;
      
      try 
      {
        Regex regex = new Regex(pattern);
        MatchCollection matches = regex.Matches(allText);			
        if (matches.Count > 0)
        {
            foreach (Match match in matches)
                s += match.Value + ";\r\n";
        }
        else
        {
            s = "Совпадений не найдено";
        }				
        textBox3.Text = s;
      } catch (Exception) {
        
        //throw;
      }
      
      написал в MasterGH
      MasterGHM
      MasterGH
    • CE mapbranches
      MapBranches

      Описание

      (Другие записи: https://celua.ru/topic/113/ветвления-кода-связанные-с-адресом-старые-записи/2)

      1. На адрес устанавливается брейкпоинт.
      2. Идем в игру делаем или не делаем что-то
      3. Начинают срабатывать инструкции на брейкпоинте
      4. От каждой инструкции начинается трейслог, подобный тому, который есть у CE, но менее тормозной
      5. Заканчивается трейслог на инструкции, на 100-ом счетчике после ret-а (об этом пункте будет уточнение). Здесь выходим на коневой цикл
      6. Трейслог останавливается и ожидание следующей инструкции, которая сработает на брейкпоинте, она должна быть отличной

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

      По этим данным можно нарисовать схему, где ret-ы меняют направление пути прохождения потоком дизассемблерного кода

      На скриншотах ниже
      Вертикальные линии со кружками -— это ветвь трейслога
      Белый кружок — адрес ret-a, который не повторялся
      Пунктирный кружок — адрес ret-а, который повторялся. Стрелка от такого пунктирного круга будет указывать на белый круг.

      По схеме видим, где ret-ы меняют свое направление поднимаясь по рутине в корневом цикле

      1cbcd3d0-9724-4f79-8586-c53a70388824-image.png

      b9062c23-18da-4e84-8055-4cb2e36a5a94-image.png

      9309d64f-ac96-4919-b7a1-1d67e58590b3-image.png

      По плану по визуальной части:

      1. Клик на кружок — переход на код

      2. Придумать что-то, чтобы стрелки не накладывались друг на друга

      3. Установка брейкпоинтов на кружках — становиться красными

      4. Легенда. Номер ветви, связанная инструкция, состояние брейкпоинтов. Комментарий к ветви

      5. Загрузка/сохранение легенды

      Для чего нужно. Для поиска условий, которые включают и выключают ветви кода. Т.е. чтобы можно было это условие быстро найти и изменив его, повторить выполнение кода ветви, которая "пробьётся" из корневого цикла. Таким образом попытаться повторить действие в игре, а если не получаться повторить, то определить дополнительные условия.

      написал в Плагин-строй
      MasterGHM
      MasterGH
    • RE: Ветвления кода связанные с адресом (старые записи)

      Трассеровка по ретам

      -------
      857b57ff-3395-4d71-9f76-e191f66fc90b-image.png

      На скриншоте 22 ветви трассеровки от адреса патронов не входя в call-ы до корневого цикла. Корневой цикл определяется, когда поиск происходит более трех секунд и не находит ничего. Вполне хватает этого времени. Буду еще повторно тестить и проверять доходит ли он цикла или надо еще что-то придумывать.

      Желтым выделяется точка, вертикальная полоса ветви и связи с такими же узлами. Узел это адрес выхода из ret

      Внизу ряд счетчиков от 00 до 99. Если более 99 то отсчет заного. Показывает дианамику обращения к ветвям после нахождения корневого цикла

      Есть еще несколько функций, которых не видно на скриншоте — в контекстном меню.

      Анализ можно запустить из окна CE из главной таблицы, выделив адрес и опцию либо на чтение, либо на доступ.

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

      Занялся модернизацией этого рисования и оптимизацией. Например, чтобы определить является ли проход по ret, то не нужен дизассемблер. Залез в доки и посмотрел опкоды

      function IsRet(address)
      		local value = readBytes(address,1, false)
      		return value == 0xC3 or value == 0xCB or  value == 0xC2 or value == 0xCA
      	end
      

      Скорость трассеровки должна в несколько раз увеличиться. К сожалению, смогу это узнать, когда перепишу текущий плагин.

      Там были уже сделаны изменения (все еще сырое для публикации плагина, пока не публикую)

      • Нумерация ветвей
      • Клик на круг — переход в дефоттный дизассемблер
      • Подсвечивать выбранный круг
      • Перерасчет позиций, когда поменялся размер окна
      • Счетчики выполнений инструкций срабатывающих к обращению на адрес
      • Изменен способ рисования связей между ветвями
      • Контекстное меню Показать ветку
      • Контекстное меню Скрыть ветку
      • На скрытой ветви не показываются связи упрощая обзор. Код поднимается снизу вверх. Если одна ветвь ниже другой, то можно её скрыть. Ориентируемся по счетчикам хитов на инструкции
      • Контекстное меню Сброс хитов
      • Оптимизация поиска корневого цикла
      • Выделение связей
      • Оптимизация рисования новых свзей
      • Контекстное меню пауза/продолжение
      • Запуск из контекстного меню при выделеии адреса в главной таблице
      • При закрытии окна снимаются все брейкпоинты
      • Возможность ставить логи на запись из контекстного меню и на чтение в главной таблице CE
      написал в MasterGH
      MasterGHM
      MasterGH
    • Ветвления кода связанные с адресом (старые записи)

      В этой записи блога не будет чего-то, что показало бы "вау, это что-то новое и есть результат". Все сырое и результат пока мне только снится — быстрый поиск условий и включение, и выключение ветвей по этим условиям. Мыслю я не инструкциями, не группой инструкций, а ветвлениями кода и условиями, которые их запускают. Проще 20 ветвлений по 5 окон, чем тонна инструкций... Жаль пока теория, практики с результатом нет.

      Рисунок. На нем слева прототип, справа текущий сырой вариант

      1. ставим брейкпоинт на адрес патронов

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

      Кружи означают ret-ы. Клик на круг будет переходлм в дизассемблер

      Стрелки будут показывать входы и выход связанные с соседними рутинами (поиск общих адресов будет стрелками)

      e3ab56bb-c1a6-4626-bead-f41fbb327a0d-image.png
      На экране справа вариант без стрелок, т.к. не успел еще их отладить. 4 ветки трассера по ретам из рутины связанные с адресом патронов, выстрелами.

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

      07232118 - 8B 85 90060000  		- mov eax,[ebp+00000690]
      0721C03F - 0FB7 8E 90060000  	- movzx ecx,word ptr [esi+00000690]
      0722FE69 - 39 86 58030000 		- cmp [esi+00000358],eax
      0722FEA8 - 83 BE 58030000 00 	- cmp dword ptr [esi+00000358],00
      0721C4A0 - 0FB6 96 B0030000  	- movzx edx,byte ptr [esi+000003B0]
      0723155C - 83 BE B0030000 00 	- cmp dword ptr [esi+000003B0],00
      07230357 - 83 BE 90060000 00 	- cmp dword ptr [esi+00000690],00
      0721E00C - 8B BE 90060000  		- mov edi,[esi+00000690]
      0722330C - 01 BE 90060000  		- add [esi+00000690],edi
      0722FFC7 - 83 BE 58030000 00 	- cmp dword ptr [esi+00000358],00
      072306DF - 83 BE 90060000 00 	- cmp dword ptr [esi+00000690],00
      0722E808 - 8B 86 90060000  		- mov eax,[esi+00000690]
      07231150 - 8B 8E 90060000  		- mov ecx,[esi+00000690]
      07231186 - 83 86 90060000 01	- add dword ptr [esi+00000690],01
      072311D4 - 8B 86 90060000 		- mov eax,[esi+00000690]
      0723121A - 3B 96 90060000		- cmp edx,[esi+00000690]
      07231FE9 - 83 BE 90060000 00	- cmp dword ptr [esi+00000690],00
      0721CA71 - 0FB6 86 B0030000		- movzx eax,byte ptr [esi+000003B0]
      0721E9B7 - 83 BE 90060000 00	- cmp dword ptr [esi+00000690],00
      0721EA26 - DA BE 90060000		- fidivr [esi+00000690]
      

      a80c8713-7d3b-48ee-9fd4-bb2ed6058b0c-image.png

      Как видно, довольно все еще сырое, но видно, что все 20 ветвлений по ретам сформировались до корневого цикла.

      Потом можно будет, я надеюсь, увидеть связи между этим ветвлениями, когда я прикручу стрелки. По кружкам кликать и переходить в дизассемблер и смотреть условия выше. На условии искать другие адреса, которое запускает ветвь кода. На адресах опять делать ветвления (новое окно с ветвлениями о ретам)... На практике будет получаться более двух таких окон (для патронов). Нужно будет искать адреса связанные с условиями в новом окне.

      ClassSettings = {}
      ClassSettings.__index = ClassSettings
      function ClassSettings:New(_fileName, _maskFile)
      
      	local obj = {}
      	obj.stringList = createStringlist()
      	obj.fileName = _fileName
      	obj.maskFile = _maskFile
      	
      	obj.directoryPath = getCheatEngineDir()..'autorun'
      	obj.filePath = obj.directoryPath..'\\'..obj.fileName
      	
      	-- Есть ли такой ключ
      	function obj:HasKey(keyString)
      		local stringCount = self.stringList.Count
      		for i=0,stringCount-1 do
      			local items = self:Split(self.stringList[i])
      			if(keyString == items[1]) then
      				return true 
      			end
      		end
      	end
      	
      	-- Получить значение ключа
      	function obj:Get(keyString, defaultValue)
      		if(obj:HasKey(keyString)) then
      			local stringCount = self.stringList.Count
      			for i=0,stringCount-1 do
      				local items = self:Split(self.stringList[i])
      				if(keyString == items[1]) then
      					return  items[2]
      				end
      			end
      		end
      		return defaultValue
      	end
      	
      	-- Записать ключ
      	function obj:Set(keyString, stringOrDigitalValue)
      		-- Искать номер строки
      		local stringCount = self.stringList.Count
      		for i=0,stringCount-1 do
      			local items = self:Split(self.stringList[i])
      			if(keyString == items[1]) then
      				items[2] = stringOrDigitalValue
      				self.stringList.remove(self.stringList[i])
      				break
      			end
      		end
      		
      		self.stringList.add (keyString..' '..stringOrDigitalValue)
      	end
      	
      
      	-- Возвращает числовой вариант
      	function obj:GetDigital(keyString, defaultValue)
      		if(obj:HasKey(keyString)) then
      			return tonumber(obj:Get(keyString))
      		end
      		return defaultValue
      	end
      
      	-- Сохранить все ключи
      	function obj:Save()
      		self.stringList.saveToFile(obj.filePath)
      	end
      
      	function obj:FileExist(directoryPath, pathToFile, mask)
      		local paths = getFileList(directoryPath, mask, false)
      		for i=1,#paths do
      			if(paths[i] == pathToFile) then
      				return true
      			end
      		end
      		return false
      	end
      	
      	function obj:Split(argString)
      		local resultTable = {}
      		for i in string.gmatch(argString, "%S+") do
      			table.insert(resultTable, i)
      		end
      		return resultTable
      	end
      
      	
      	-- Загрузка ключей в память
      	if(obj:FileExist(obj.directoryPath, obj.filePath, obj.maskFile)) then
      		obj.stringList.loadFromFile(obj.filePath)
      	end	
      	setmetatable(obj, self)
      	return obj
      end
      	
      	
      classSettings = ClassSettings:New('userdata2.txt', '*.txt')
      \----------
      function DeleteAllBreakPoints()
      	local tableAddressOnBreakPoint = debug_getBreakpointList()
      	for i =1, #tableAddressOnBreakPoint do
      		debug_removeBreakpoint(tableAddressOnBreakPoint[i])
      	end
      end
      
      
      ClassTraceBranch = {}
      function ClassTraceBranch:New(_dataAddress, _onEnd)
      
      	local obj  = {}
      	obj.address 		= _dataAddress
      	obj.OnEnd 			= _onEnd
      	obj.Is64Bit 		= targetIs64Bit()
      	obj.tableData 		= {}
      
      	--Breakpoint methods: 				bpmInt3=0, bpmDebugRegister=1, bpmException=2
      	--breakpoint continue methods:      co_run=0, co_stepinto=1, co_stepover=2
      	--Breakpoint triggers:				bptExecute=0, bptAccess=1, bptWrite=2
      	--debug_setBreakpoint(self.address, 1, bptExecute, bpmDebugRegister)
      	
      	
      	function obj:OnBreakpoint() 
      	
      	
      		local XIP = 0
      		if(self.Is64Bit) then XIP = RIP else XIP = EIP end
      		
      		local someFind = false
      		local endAction = false
      		
      		for i=1,#self.tableData do
      			--if(self.tableData[i].XIP == XIP) then
      			if(self.tableData[i].XIP == XIP) then
      				self.tableData[i].Count = self.tableData[i].Count + 1
      				someFind = true
      				if(self.tableData[i].Count > 100) then
      					endAction = true
      				end
      				break
      			end
      		end
      		
      		if(endAction) then
      			--table.insert ( self.tableData, {XIP = XIP, Count = 1})
      			--self:PrintData()
      			--print('-->'..string.format('%08X',XIP))
      			DeleteAllBreakPoints()
      			debug_continueFromBreakpoint(co_run)
      			self.OnEnd()
      		else
      			if(not someFind) then
      				local _,opcode,_,_ = splitDisassembledString(disassemble(XIP))
      				local isRet = string.match(opcode,'ret')
      				--if(isRet) then
      				--	print('ret '..string.format('%08X', XIP))
      				--end
      				table.insert ( self.tableData, {XIP = XIP, Count = 1, isRet = isRet})
      				debug_continueFromBreakpoint(co_stepover)
      			end
      		end
      		return 1
      	end
      
      	function obj:PrintData()
      		local s = ''
      		local addressPrevious = getPreviousOpcode(self.address)
      		local _,opcode,_,_ = splitDisassembledString(disassemble(addressPrevious))
      		print(string.format('%08X - %s:\r\n', addressPrevious, opcode))
      		
      		for i=1,#self.tableData do
      			local _,opcode,_,_ = splitDisassembledString(disassemble(self.tableData[i].XIP))
      			s = s..string.format('%08X - %s - %s\r\n', self.tableData[i].XIP, self.tableData[i].Count, opcode)
      		end
      		print(s)
      	end	
      
      	debug_continueFromBreakpoint(co_stepover)
      	
      	setmetatable (obj, self)
      	obj.__index = ClassTraceBranch
      	return obj
      end
      
      ClassManagerBranches = {}
      function ClassManagerBranches:New(mainAddress, sizeBreakPoint)
      
      	local obj  = {}
      
      	
      	obj.tableData 				= {} --XIP = 0, traceBranch = nil
      	obj.mainAddress				= mainAddress
      	obj.sizeBreakPoint 			= sizeBreakPoint
      	obj.currentTraceBranch 		= nil
      	obj.isLogBranch				= false
      	
      	local Is64Bit 				= targetIs64Bit()
      
      	function obj:Containts(XIP)
      		for i=1, #self.tableData do
      			if(self.tableData[i].XIP == XIP) then 
      				return true
      			end
      		end
      		return false
      	end
      	
      	function obj:Stop()
      		
      		DeleteAllBreakPoints()
      		
      		for i = 1, #self.tableData do
      			local addressPrevious = getPreviousOpcode(self.tableData[i].traceBranch.address)
      			local _,opcode,_,_ = splitDisassembledString(disassemble(addressPrevious))
      			print(string.format('%08X %s', addressPrevious, opcode))
      		end
      		
      		print('\r\n')
      		
      		for i = 1, #self.tableData do
      			self.tableData[i].traceBranch:PrintData()
      		end
      		
      		debug_continueFromBreakpoint(co_run)
      	end	
      	
      	function obj:OnBreakpoint()
      	
      		if(self.isLogBranch) then
      			return self.currentTraceBranch:OnBreakpoint() 
      		end
      
      		local XIP = 0
      		if(Is64Bit) then XIP = RIP else XIP = EIP end
      		
      		if(not self:Containts(XIP)) then
      	
      			local hitAddress = getPreviousOpcode(XIP)
      			local _,opcode,_,_ = splitDisassembledString(disassemble(hitAddress))
      			--print(string.format('Start Trace at : %08X %s', hitAddress, opcode))
      			
      			DeleteAllBreakPoints()
      			
      			self.currentTraceBranch = ClassTraceBranch:New
      			(	
      				XIP, 
      				function ()
      					--print('Stop')
      					self.isLogBranch = false
      					debug_setBreakpoint(self.mainAddress, self.sizeBreakPoint, bptAccess, bpmDebugRegister)
      				end
      			)
      
      			table.insert (self.tableData, {XIP = XIP, traceBranch = self.currentTraceBranch})
      			
      			self.isLogBranch = true
      		end
      		
      		return 1
      	end
      	
      	
      	print('Start at: '.. obj.mainAddress)
      	debug_setBreakpoint(obj.mainAddress, obj.sizeBreakPoint, bptAccess, bpmDebugRegister)
      		
      	setmetatable (obj, self)
      	obj.__index = ClassManagerBranches
      	return obj
      end
      
      
      \--if(getOpenedProcessID() == 0) then openProcess('test.exe') end
      
      if(getOpenedProcessID() == 0) then 
      	openProcess('xrengine.exe')
      end
      
      managerBranches = ClassManagerBranches:New('37859970',4)
      
      function debugger_onBreakpoint()
      	return managerBranches:OnBreakpoint()
      end
      
      
      \--managerBranches:Stop()
      
      \-----------------------------
      
      \-- КЛАСС ClassMapBranch
      ClassMapBranch = {}
      ClassMapBranch.__index = ClassMapBranch
      function ClassMapBranch:New(_frmMarkers, _managerBranches)
      
      	local _frmMapBranch = createForm(false) --frmDissassembler -- createForm(false)
      	
      	_frmMapBranch.DoubleBuffered = true
      	_frmMapBranch.Caption 	= 'Map branches'
      	_frmMapBranch.Width 	= classSettings:GetDigital('obj.frmMapBranch.Width', 500)
      	_frmMapBranch.Height 	= classSettings:GetDigital('obj.frmMapBranch.Height', 900)
      	_frmMapBranch.Left		= classSettings:GetDigital('obj.frmMapBranch.Left', 0)
      	_frmMapBranch.Top 		= classSettings:GetDigital('obj.frmMapBranch.Top', 0)
      		
      
      	if(classSettings:Get('obj.frmMapBranch.Visible', '1') == '1') then
      		_frmMapBranch.show()
      	end
      	_frmMapBranch.show()
      		
      	_frmMapBranch.setBorderStyle(bsSizeable)
      	_frmMapBranch.FormStyle = 'fsNormal'
      	
      	
      	local obj = {}
      	
      	
      	obj.classManagerBranches = _managerBranches
      	obj.frmMapBranch 		= _frmMapBranch
      	obj.frmMarkedBranches	= _frmMarkers
      	obj.selectedAddress 	= -1
      	obj.mainCanvas 			= obj.frmMapBranch.Canvas
      	obj.bufferGraphic 		= createBitmap(obj.frmMapBranch.Canvas.Width, obj.frmMapBranch.Canvas.Height)
      	
      	obj.dy 					= 20
      	obj.column0_X 			= 30	-- маркер X
      	obj.column1_X 			= 65
      	obj.column2_X 			= 70 + 50
      	obj.column3_X 			= 70 + 50 + 40
      	obj.extraLineRender_Y	= -20
      	obj.tableVisibleAddress = {}		-- содержит данные рисуемой таблицы (см. свойства инже по коду)
      	
      	-- Можно поставить другие цвета для фона и шрифта
      	obj.colorBackground 	=  classSettings:GetDigital('obj.colorBackground', 0x00525252)
      	obj.colorFont 			=  0x00FFFFFF --classSettings:GetDigital('obj.colorFont', )
      	
      	
      	obj.frmMapBranch.Color 		= obj.colorBackground
      	obj.frmMapBranch.Font.Color = obj.colorFont 
      	
      	obj.isDirtyData = true
      	setProperty(obj.frmMapBranch, 'OnPaint', function () 
      		
      		if(obj.isDirtyData) then
      			obj.isDirtyData = false
      			obj:Draw()
      		end
      
      		obj.mainCanvas.draw(0, 0, obj.bufferGraphic)
      	end)
      	
      	setProperty(obj.frmMapBranch, 'OnResize', function ()
      		if(obj.bufferGraphic.Width ~= obj.mainCanvas.Width or
      			obj.bufferGraphic.Height ~= obj.mainCanvas.Height) then
      			obj.isDirtyResize = true
      		  end
      		end
      	)
      
      	-- Не уничтожать окно при закрытии
      	setProperty(obj.frmMapBranch, 'OnClose', function (sender) 
      		return caHide --Possible options: caHide, caFree, caMinimize, caNone
      	end
      	)		
      
      	
      	obj.checkTimer = createTimer(obj.frmMapBranch)
      	obj.checkTimer.Interval = 50
      	obj.lastTopAddress = 0
      	obj.isDirtyResize = false
      	obj.needUpdateDraw = false
      	
      	obj.checkTimer.OnTimer = function ()
      	
      		obj.needUpdateDraw = true
      		if(	obj.needUpdateDraw) then
      			obj.needUpdateDraw = false
      			obj.frmMapBranch.repaint()
      		end
      
      		if(obj.isDirtyResize) then
      			obj.isDirtyResize = false
      			obj.bufferGraphic.destroy()
      			obj.bufferGraphic = createBitmap(obj.frmMapBranch.Canvas.Width, obj.frmMapBranch.Canvas.Height)
      			obj.bufferGraphic.Canvas.Brush.Color 	= obj.colorBackground
      			obj.bufferGraphic.Canvas.Font.Color 	= obj.colorFont
      			obj.bufferGraphic.Canvas.floodFill(x,y)
      			obj.isDirtyData = true
      			obj.frmMapBranch.repaint()
      		end		
      	end
      	
      	
      	obj.mainAddress = string.format('%08X',obj.classManagerBranches.mainAddress)
      	
      	-- Рисует дизассемблерный код и может рисовать пути
      	function obj:Draw()
      		
      		local bufferGraphicCanvas = obj.bufferGraphic.Canvas
      		
      		bufferGraphicCanvas.Font.Size 		= sizeFont
      		bufferGraphicCanvas.Font.Color 		= obj.colorFont
      		bufferGraphicCanvas.Pen.Style 		= 'psSolid'
      		bufferGraphicCanvas.Brush.Style 	= 'bsClear'
      		bufferGraphicCanvas.Brush.Color 	= obj.colorBackground
      		
      		
      		bufferGraphicCanvas.clear()
      		obj.frmMapBranch.color = 0xFFFFFFFF -- obj.colorBackground
      		bufferGraphicCanvas.Brush.Color = 0xFFFFFFFF
      		bufferGraphicCanvas.Pen.Style   = 0xFFFFFFFF
      		
      		--bufferGraphicCanvas.Brush.Style = 'bsClear'
      		
      		local mainPoint = 
      		{
      			x = obj.frmMapBranch.Width / 2,
      			y =  obj.frmMapBranch.Height  - 50
      		}
      		
      		bufferGraphicCanvas.textOut(mainPoint.x, mainPoint.y, obj.mainAddress)
      		bufferGraphicCanvas.line(mainPoint.x - 300, mainPoint.y - 30, mainPoint.x + 300, mainPoint.y - 30)
      		
      		local tableData = obj.classManagerBranches.tableData
      		for i = 1, #tableData do
      			--local addressPrevious = getPreviousOpcode(tableData[i].traceBranch.address)
      			--local _,opcode,_,_ = splitDisassembledString(disassemble(addressPrevious))
      			--local s = string.format('%08X %s', addressPrevious, opcode)
      			--bufferGraphicCanvas.textOut(100, 100 + i * 20, 'Test: '.. s)
      			local dx = i*40
      			local dy = 20
      			
      			--bufferGraphicCanvas.line
      			--(
      			--	mainPoint.x - 300 + dx,
      			--	mainPoint.y - 30 - dy,
      			--	mainPoint.x - 300 + dx,
      			--	mainPoint.y - 30
      			--)
      			
      			local tableDataFromBranche = tableData[i].traceBranch.tableData
      			local countRets = 0
      			for j = 1, #tableDataFromBranche do
      				--table.insert ( self.tableData, {XIP = XIP, Count = 1, isRet = string.match(opcode,'ret')})
      				--table.insert ( self.tableData, {XIP = XIP, Count = 1, isRet = string.match(opcode,'ret')})
      				if(tableDataFromBranche[j].isRet) then
      					countRets = countRets + 1
      					bufferGraphicCanvas.ellipse
      					(	
      						mainPoint.x - 300 + dx - 5,
      						mainPoint.y - 30 - dy - countRets * 20 - 5,
      						mainPoint.x - 300 + dx + 6,
      						mainPoint.y - 30 - dy - countRets * 20 + 6
      					)
      				end
      			end
      			
      			bufferGraphicCanvas.line
      			(
      				mainPoint.x - 300 + dx,
      				mainPoint.y,
      				mainPoint.x - 300 + dx,
      				mainPoint.y - dy - countRets * 20 - 10
      			)
      		end	
      		
      		--local priority1 = math.random(0,155)
      		--local someColor = byteTableToDword({priority1,priority1,priority1,0})
      		--bufferGraphicCanvas.Font.Color = someColor
      				
      
      			
      
      		obj.mainCanvas.draw(0, 0, obj.bufferGraphic)
      	end	
      	
      
      	function obj:DrawArrow(point1, point2, distance, colorArrow, minY, a1_IsFarTop, a1_IsFarBottom, a2_IsFarTop, a2_IsFarBottom, a1_IsFarTop, a1_IsFarBottom, a2_IsFarTop, a2_IsFarBottom)
      
      		local bufferCanvas = obj.bufferGraphic.Canvas
      		
      		bufferCanvas.Pen.Color = colorArrow
      		bufferCanvas.Pen.Style = 'psDot'	
      		
      		local height = bufferCanvas.Height
      		
      		isDrawSideLine1 = true
      		if(point1.y < minY) 			then	point1.y = minY  isDrawSideLine1 = false
      		elseif (point1.y > height) 	then	point1.y = height isDrawSideLine1 = false
      		end
      		
      		isDrawRow = true
      		if(point2.y < minY) 			then	point2.y = minY	  isDrawRow = false
      		elseif (point2.y > height) 	then	point2.y = height isDrawRow = false
      		end
      		
      		if(isDrawSideLine1) then
      			bufferCanvas.line(point1.x, point1.y, point2.x - distance, point1.y)
      		end
      		bufferCanvas.line(point2.x - distance, point1.y, point2.x - distance, point2.y)
      		
      
      	
      		if(isDrawRow) then
      			bufferCanvas.line(point1.x, point2.y, point2.x - distance, point2.y)
      			obj.bufferGraphic.Canvas.Pen.Style = 'psSolid' --'psSolid'
      			local x1 = point2.x - 5
      			local x2 = point2.x 
      			local y2 = point2.y
      			
      			-- Рисование стрелки костыльным способом
      			bufferCanvas.line(x1,		y2 - 3,			x2,		y2)
      			bufferCanvas.line(x1,		y2 - 7 + 10,	x2,		y2)
      			bufferCanvas.line(x1,		y2 - 3,			x1,		y2 - 7 + 10)
      			bufferCanvas.line(x1 + 1,	y2 - 2,			x1 + 1, y2 - 7 + 9)
      			bufferCanvas.line(x1 + 2,	y2 - 1,			x1 + 2, y2 - 7 + 9)
      			bufferCanvas.line(x1,		y2 - 1,			x1 + 6, y2)
      		end
      
      	end
      				
      		
      	obj.frmMapBranch.OnDestroy = function (sender)
      		obj:SaveParameters()
      	end
      	
      	function obj:SaveParameters()
      	
      		classSettings:Set('obj.frmMapBranch.Width', obj.frmMapBranch.Width)
      		classSettings:Set('obj.frmMapBranch.Height', obj.frmMapBranch.Height)
      		classSettings:Set('obj.frmMapBranch.Left', obj.frmMapBranch.Left)
      		classSettings:Set('obj.frmMapBranch.Top', obj.frmMapBranch.Top)
      		
      		classSettings:Set('obj.colorBackground', obj.colorBackground)
      		
      		if(obj.frmMapBranch.Visible) then
      			classSettings:Set('obj.frmMapBranch.Visible', '1')
      		else
      			classSettings:Set('obj.frmMapBranch.Visible', '0')
      		end
      			
      		classSettings:Save()
      	end			
      		
      	obj:Draw()
      
      	setmetatable(obj, self)
      	return obj
      end	
      
      
      
      mapBranch = ClassMapBranch:New(frmMarkedBranches, managerBranches)
      
      написал в MasterGH
      MasterGHM
      MasterGH
    • RE: CE Dissect Data Scaner 1.0.2 (4 hardware breakpoints)

      Проведено огромное количество опытов над структурами. Много переделок.

      Очень кратко напишу, что поменялось.

      1. Название структуры состоит из адреса, количества адресов в ней и времени в миллисекундах на один байт в структуре.

      Подчеркнуто красным
      23c85a19-21c7-4812-8cf3-b55339a75bdb-image.png

      1. Смещения отбираются только те, на которых за X времени не было обнаружено ни одной инструкции на запись. Чем больше та самая чувствительность, тем точнее результат

      2. В именах структур теперь ценная информация. Это смещение, регистр и тип

      На скриншоте ниже можно посмотреть пункты 2 и 3.

      Также на этом скриншоте я заморозил по соседству адрес (поставил Lock) и пару раз пострелял в игре Сталкере Зов Припяти
      13ebe817-8585-4768-9c47-c6f75c2b1306-image.png
      Выводы, которые я сделал за очень и очень скромное время пользования. За минут 15 и на паре структур оружия в двух разных играх L4D2 и Сталкер Зов Припяти

      1. Если править 4-х байтовые значения, то легко можно нарваться на вылет игры. Но подключившись снова, можно продолжить.

      2. Правки значений с типом в 1 байт могут заблокировать оружие (в двух играх) или же устроить скорострельность на полную катушку в L4D2.

      3. Правки значений с типом float. Можно легко нарваться на вылет в игре, если например поставить нолик. Деление на ноль или какая-то иная причина. Лучше ставить чуть больше нуля, можно положительные или отрицательные значения

      4. Частенько бывают адреса в структуре, на которых включаются инструкции записи только после изменения значения. Тут я пока ничего не смог сделать, просто удалить их из структуры как лишние.

      5. Для сканера используется 1 аппаратный брейкпоинт, остальные три штуки еще не используются, т.к. сложно их прикрутить. Если использовать все 4 аппаратных бряка, то скорость сканера была бы в 4 раза быстрее. Сейчас на 2К байт по 10 мс, у меня уходит где-то 150 секунд. Если я ставил 20мс, то находилось на 10 смещений больше или какие-то другие смещения пропадали.

      Код будущего плагина все еще находится на стадии тестирования. Поэтому пока плагин не выкладываю.

      upd1: инструкции cmp, add, sub, xor, and, not, test, mulss, fsub, fmul, dec, inc, mul; теперь выводятся в имя элемента структуры.

      На скриншоте случайно вышел на координаты UI таймера. Сделал три скана трех структур в новых окнах

      629e6be0-a9da-4ebe-9bb5-011f3108998a-image.png
      upd2: перемещение структуры
      576ed324-e895-4d61-b511-02cac97fbbe2-image.png
      02ba5e5b-763b-4580-a0a0-88b9920270b7-image.png
      4e4dac89-4c6a-41be-a1c5-f59dd987106e-image.png
      Итог перемещения двух структур в первую
      b2bc976c-9111-4466-b3d3-443d9cd21478-image.png

      написал в Плагин-строй
      MasterGHM
      MasterGH
    • [CE Lua Plugin] Способ раскраски значений найденных адресов

      930079fc-c2ee-4bae-9d42-667e8ee06307-image.png

      Пример раскрашивания адресов без каких-либо условий

      MainForm.Foundlist3.OnCustomDrawSubItem =
        function(sender, ListItem, SubItemIndex, state, DefaultDraw)
        if ListItem.Index % 3 == 0 then
          if SubItemIndex % 2 == 0 then
            sender.canvas.font.color = 0xff0000
          else
            sender.canvas.font.color = 0x00ffff
          end
        else
          sender.canvas.font.color = 0xffff00
        end
        return true
      end
      
      1. Из прошлой записи блога нашли "MainForm.Foundlist3" для работы с TListView (компонент от Lazarus среды разработки)

      2. OnCustomDrawSubItem — функция обработчик раскрашивания вложенных элементов в Item. Не поленитесь, зайдите в файл документации (C:\Program Files\Cheat Engine 7.4\celua.txt)

      Также есть OnCustomDrawItem — раскрашивание невложенных элементов.

      source

      Этот способ раскрашивания можно использовать по условиям. Чтобы связать условия с адресами и цветом смотри MemScan Class, FoundList class в celua.txt.

      написал в MasterGH
      MasterGHM
      MasterGH
    • CE Action Logger 1.0 Beta

      551365e9-d214-48a9-809d-dae6f8f79f17-image.png

      Cheat Engine 6.8.3 Cheat Engine Extensions

      Как установить:
      Распаковать файлы в директория Cheat Engine

      Версия: Beta 1.0 (22.018.2019)
          + Добавлены базовые действия в логи
          + Добавлены ссылки на ресурс game hack lab
      
      Файлы:
          autorun->User actions logger.lua
          autorun->GHLLogo.lua        
          autorun->forms->GHLLogo.frm
      
      написал в Плагин-строй
      MasterGHM
      MasterGH
    • RE: CE Tool Lua Regular expressions 2

      Lua поиск элемента до и после строки

      Была задача получить два списка из документа, в котором было с пару десятков тысяч строк. На практике выяснилось, что искать текст после ключевого слова легче чем искать текст до ключевого слова. Об этом и будет дальше

      В утилите "Lua Regular Expressions (v. 1.0)"
      image.png
      Текст во вкладке "gmatch"

      Game1
      gameCompany
      DAU
      53.21k
      -20%
      Game2
      gameCompany
      DAU
      20.35k
      -20%

      Поиск элемента после строки: "DAU "DAU%c%c(.-)%c%c"

      53.21k;20.35k;

      Поиск элемента до строки: "%c%c(.-)gameCompany%c%c"
      (паттерн со двигом скобок для поиска предыдущей фразы)
      не прокатит для вывода списка игр над фразой gameCompany

      ;53.21k
      -20%

      Game2
      ;

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

      Cначала добавим первую пустую строку и видим повторяющиеся фрагменты

      "%c%cGame1%c%cgameCompany%c%c"

      пишем шаблон ".*%c%c(.-)%c%cgameCompany%c%c" и опять мимо

      Game1;53.21k
      -20%

      Game2;

      Потому что текст над Game2 пошел выше Game2. Тогда делаем захват, только первой фразы и дальше не идем "%c%c(%w-)%c%cgameCompany%c%c"

      Game1;Game2;
      И тогда все ок.

      Но это еще не все. Осталась первая пустая строка, которую добавили, если её удалить, тогда "%c%c(%w-)%c%cgameCompany%c%c"

      Game2;
      Не видит Game1.

      Значит мы можем убрать %c%c, и будет "(%w-)%c%cgameCompany"

      Game1;Game2;

      Дальше название игры может быть таким "My Game: my Game". Здесь и пробел и двоеточие. В таком случае текст уже будет

      My Game1: my Game

      gameCompany

      DAU

      53.21k

      -20%

      My Game2: my Game

      gameCompany

      DAU

      20.35k

      -20%

      Пробуем "(%w-)%c%cgameCompany"

      Game;Game;
      Что не верно, т.к. захват одним (%w-)

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

      '([%w%s]-)%c%cgameCompany'

      my Game; my Game;

      Затем ([:%w%s]-)%c%cgameCompany

      My Game1: my Game;

      My Game2: my Game;

      Затем %c?%c?([:%w%s]-)%c%cgameCompany

      My Game1: my Game;
      My Game2: my Game;

      Вот и все. Если попариться один раз, то тексты уже парсить будет гораздо быстрее.

      Так я вывел столбы DAU и названий игр в таблицу, что было в районе 500 строк из пару десятков тысяч строк

      p.s. Текст в консоли Lua отличается %c%c, а %с

      p.s.p.s. Можно раcсплитить текст по "/n/r" или "/n" в таблицу строк и по индексам данных находить предыдущую или последующую фразу. Но мне проще две строки ввести "%c?%c?([:%w%s]-)%c%cgameCompany" и "DAU%c%c(.-)%c%c"

      написал в Плагин-строй
      MasterGHM
      MasterGH
    • CE Tool Lua Regular expressions 2

      CT таблица для составления регулярных выражений 4-х функций

      string.match (s, pattern [, init])
      string.gmatch (s, pattern)
      string.gsub (s, pattern, repl [, n])
      string.find (s, pattern [, init [, plain]])
      

      Функция string.gsub может принимать в аргумент функции "repl" таблицу (тогда будет замена по ключам значений из таблицы ) или в аргумент "repl" может попадать некоторая другая функция с аргументом найденного слова (тогда будет вызов этой другой функции при каждом захвате символа или слова). Более подробное в документации Lua 5.3

      Еще несколько ссылок с практическим руководством

      lua-users wiki: Sample Code
      lua-users wiki: Optimisation Tips
      lua-users wiki: Object Oriented Programming
      lua-users wiki: Tutorial Directory

      9c7cb7f1-e10d-4d07-84c5-6d2ee905e441-image.png

      63a3f6ae-ce5e-4d49-b556-534380157b0f-image.png
      2df0435b-b355-48e3-934a-00e4daa0dcca-image.png

      8f2a6a5e-2b4c-4d9a-b13d-17a80956faa8-image.png

      f23565ca-e4bb-4896-a680-54f00a0a2975-image.png

      написал в Плагин-строй ce plugin
      MasterGHM
      MasterGH
    • CE disassembler

      80185edf-e083-49a5-9ed3-dde77b9d94cf-image.png
      2c49f00f-7aad-453e-8131-196e0c918444-image.png

      Кратко

      1. Добавил сохранение/загрузку параметров. Хранение в текстовом файле в папке autoruns

      2. Добавил настройки графического интерфейса (слайдеры, пункт меню)

      3. Обновления UI

        • Гладкая перерисовка, двойной буфер
        • Стрелки прыжков сдвигаются вправо при увеличении их количества
        • Слайдер яркости фона
        • Слайдер количества адресов в невидимой области от 0 до +1К от верхнего и нижнего адресов. Увеличивает количество стрелок для прыжков.
        • Стрелки в невидимой области обозначаются двумя цветами.
          Один цвет для тех стрелок, у которых адрес прыжка и адрес назначения не выходят в область видимости.
          Второй цвет для тех стрелок, которых адрес прыжка или адрес назначения входят в области видимости.
          Видимые стрелки обозначаются белым.
          На рисуемые стрелки прыжков влияет слайдер яркости.
        • Не показывать стрелки прыжков для инструкций типа 'jmp [...]', 'jne [...]'...
      4. Сохранение и загрузка при заходе и выходе из CE

        • значения слайдеров
        • верхнего адреса
        • выбранного адреса
        • параметры шрифтов и других

      С цветами экспериментировал не понравилось, пока оставляю градацию "черный-белый".

      Основное назначение его показывать приоритетные инструкции более ярким цветом. Инструкции такие как арифметические и логические. Это первый и второй слайдеры. Третий - инструкции на запись, чтение. Четвертый - инструкции ветвления. Пятый слайдер - остальные инструкции. Остальные слайдеры связаны с настройками. Основные из них размер шрифта, яркость фона, вертикальный отступ между инструкциями и другие.

      Также в плагин встроена поддержка работы с окнами трейслогов.
      8de95150-d583-40ce-9bd0-f351c8ae3e6d-image.png

      Происходит постоянный поиск новых и отсутствующих окон трейслогов... Делаем трейслоги. Двойной клик по адресам и переходим по адресу в дизассемблер (если он открыт) и видим в нем раскрашенные пути маркерами. Если окон много, то видим пересечения, расхождения путей и видим инструкции, которые не выполнялись...Встроено контекстное меню установки брейкпоинтов на call-ы в трейслоге. Если его вызывать, то после идем в игру и, не делая того, что исследуем, прерываемся на брейкпоинтах и снимаем их. Когда больше не прерываемся, то делаем в игре то, что нужно и прерываемся на оставшемся брейкпоинте. На нем исследуем условие входа в рутину. Тут как бы можно на страх и риск изменить условие, чтобы ветка кода повторила свое выполнение. Если позволяет, то выполнить поток вызвав call... Но я придерживаюсь варианта изменения данных для того чтобы поток сам повторил рутину. Либо подменить параметры функции или подменить параметры после выхода функции. На данный момент показать что-то на практике нечего. Как будет, сделаю и покажу видео.

      Встроена поддержка маркирования путей на выбранном участке кода. Это специальное окно в меню "утилитах" в окне дизассемблера его можно найти. Указывается верхний адрес и нижний адрес области кода. Например, функции. При старте начинается логирование уникальных прохождения от начального до конечного адреса и раскраска маркерами. Возможных таких путей 8. Т.е. делая что-то в игре (разовые действия) видим уникальную ветвь, она тут же появляется и на этой ветви можно прерваться поставив опцию, и оттрейсить выход из рутины...

      Более подробное описание и скриншоты в закрытом разделе в моем старом блоге. Имеют к нему доступ пользователи со статусом "Разработчики". Новую информацию я буду писать в этой теме.

      Плагин сейчас на стадии чернового варианта, кто хочет пробуйте. Установка с репозитория. Чтобы подключить нужно загрузить .CT таблицу. Знаю что не удобно, но пока так.

      Плагин планируется развивать дальше, сейчас это черновой вариант

      написал в Плагин-строй ce plugin
      MasterGHM
      MasterGH
    • RE: CE Compact View

      Ниже находится обучающий пример плагина для CE Lua с установкой компактного режима применяя парадигму ООП — инкапсуляции.

      Для программирования плагинов на Lua могут пригодится приемы ООП. Это касается сущностей, их поведений и взаимодействий между ними. В данном примере CECompactView — описание класса на основе мета-таблицы и оно является сущностью. Функции класса — uncompact() и compact(), которые реализуют поведения этой сущности. Основной скрипт с описанием сущности можно поместить в отдельный файл .lua и далее можно кратко инициализировать сущность и управлять ей

      Например

      \-- Создание экземпляра класса через new() функцию, где аргументом является состояние компактный или некомпактный
      ce_compact_view = CECompactView:new(true)
      
      \-- Проверить состояние
      \--print(ce_compact_view:get_state() and 'Compact View Mode' or 'Full View Mode')
      
      \-- Установить компактный режим
      \--ce_compact_view:compact()
      
      \-- Установить не компактный режим
      \--ce_compact_view:uncompact()
      

      Сам класс или мета-таблица.

      \-- Класс инкапсулирующий поведения компактного состояния окна CE
      CECompactView = {}
      
      function CECompactView:new(state)
      
      	local obj = {}
      	
      	-- Состояние компактное или нет: true или false
      	obj.compact_mode = state
      	
      	-- Форма CE, где getMainForm — поддерживаемая функция CE и возвращает главную форму CE
      	obj.form_ce = getMainForm()
      	
      	-- Получение элементов контекстного меню главной формы
      	local menu_items = obj.form_ce.menu.items
      	
      	-- Создание нового контекстного меню с названием CompactView и именем ItemCompact
      	obj.menu_item_compact = createMenuItem(menu_items)
      	obj.menu_item_compact.name = 'ItemCompact'
      	obj.menu_item_compact.caption = 'CompactView'
      	
      	-- Добавление контекстного меню на главную форму
      	menu_items.add(obj.menu_item_compact)
      
      	-- Сменить состояние окна CE: компактное или нет
      	function obj:set(state)
      		-- view_components состояния скрытия компонентов
      		obj.compact_mode = state
      		-- видимость компонентов обратна компактному режиму
      		local view_components = not state
      		obj.form_ce.panel1.visible = view_components
      		obj.form_ce.panel4.visible = view_components
      		obj.form_ce.panel5.visible = view_components
      		obj.form_ce.Splitter1.visible = view_components
      		-- Записать имя по состоянию
      		obj.menu_item_compact.caption = state and 'Full View Mode' or 'Compact View Mode' 
      		-- Поставить обработчик по состоянию
      		obj.menu_item_compact.onClick = state and obj.compact or obj.uncompact
      		
      		print(state and 'Compact View Mode' or 'Full View Mode')
      	end
      
      	-- Некопактное окно CE
      	function obj:uncompact()
      		obj:set(true)
      	end
      
      	-- Компактное окно CE
      	function obj:compact()
      		obj:set(false)
      	end
      
      	-- Состояние окна CE: компактное или нет
      	function obj:get_state()
      		return obj.compact_mode
      	end
      
      	-- Связь таблицы (смотрим докуметацию по Lua)
      	setmetatable(obj, self)
      	obj.__index = self
      
      	-- Активировать состояние
      	obj:set(state)
      
      	-- Возвращает ссылку на экземпляр
      	return obj
      end
      \------------
      
      \-- Создание экземпляра класса
      ce_compact_view = CECompactView:new(true)
      
      \-- Проверить состояние
      \--print(ce_compact_view:get_state() and 'Compact View Mode' or 'Full View Mode')
      
      \-- Установить компактный режим
      \--ce_compact_view:compact()
      
      \-- Установить не компактный режим
      \--ce_compact_view:uncompact()
      

      Скрипт можно сохранить в Lua файл и добавить в папку autorun.

      написал в Плагин-строй
      MasterGHM
      MasterGH
    • CE Compact View

      Функции

      1. Контекстное меню. Компактное/ не компактное окно CE
      2. Сохранение и загрузка положения и размера формы CE
      3. Сохранение и загрузка компактного состояния формы или не компактного

      После запуска, CE будет там, где её (CE) закрыли с позицией, размерами и с состоянием "Компактны режим", если последний был включен.

      Параметры сохраняются на жесткий диск в файл "..\autorun\userdata.txt"

      написал в Плагин-строй
      MasterGHM
      MasterGH
    • 1 / 1