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

    Сообщения

    Последние Лучшие сообщения Спорные
    • RE: Собираем ссылки на разные источники

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

      написал в Статьи
      MasterGHM
      MasterGH
    • RE: Телепорт на 10 слотов и на 3 типа данных (float, double, integer)

      Конечно, возможно. Если заглянуть в документацию, то через чтение массива по адресу address, bytecount - количеству байт, и ReturnAsTable возвращаемая таблица.

      readBytes(address,bytecount, ReturnAsTable ) : returns the bytes at the given address. If ReturnAsTable is true it will return a table instead of multiple bytes
        Reads the bytes at the given address and returns a table containing the read out bytes
      
      writeBytes(address, table) : Write the given bytes to the given address from a table
      

      Я думаю, пример можно найти в Интернете. Только у double байт побольше будет чем у других типов в этом скрипте

      написал в Приёмы
      MasterGHM
      MasterGH
    • Различия между разделами "Блоги", "Статьи" и "Личный раздел"

      "Блоги" — это свободный стиль, частные какие-то случаи и задачи, какие-то заметки, видео, файлы и т.п. Какие-то особенности для конкретной игры и решение проблемы. В общем менее важно, т.к. решение задачи в целом возможно, если знать общие приемы из раздела Статьи.

      "Статьи "— это более строгий стиль. Как будто составляется руководство, по которому информация будет нужна часто, для общих случаев или информация, чтобы была под рукой. Могут быть и частные редкие случаи, когда их можно применить ко множеству задач и это может пригодится в целом.
      "Фишки (приемы)" — некоторые специфичные приемы, которые могут пригодится или не пригодиться, по ситуации.
      "Статьи других авторов" — бывают полезные статьи. Если нашлась интересная, то можно опубликовать ссылку на неё и может пару комментариев, что привлекло внимание в этой статье.

      "Личный раздел" — специфичный раздел, который нужен больше вам самому, чем другим. Например, для обсуждения и обратной связи или для заметок. Может кто-то что-то прокомментирует или поделиться информацией.

      "Программирование" — раздел для вдохновения. публикуем там все, что связано с программированием. Пока это связано с программированием на Cheat Engine на autoassmbler, на Lua, на других языках для создания .dll модулей или бинарного кода.
      "Плагин-строй" — расширения для CE
      "Софт" — программы, которые могут пригодится по тематике программирования.
      "Справочники" — тут могут быть как ссылки на руководства по CE, по языкам программирования, по API, по ключевым словам и т.п. Справочник и статья отличаются по тому, что справочник, это конкретное описание функций, ключевых слов, аргументов, директив, макросов и прочее.
      "Обучающие примеры" — в нем могут быть примеры для понимания принципов программирования. Язык не важен. Так же могут быть короткиие примеры.

      написал в Обсуждения и предложения
      MasterGHM
      MasterGH
    • О разделе "Репозитории"

      Раздел создан для обсуждений связанных с репозиториями.

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

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

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

      Как это все может происходить я напишу подробную инструкцию + видео с примерами.
      Если кто желает, пока может сам поискать про репозитории в Интернете. Что такое и как с ними работать.

      написал в Репозитории
      MasterGHM
      MasterGH
    • Заметка. Логи брейкпоинта на структуру размером 0x1000

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

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

      Ниже будет большой кусок опкодов, который работает со структурой.
      Log breakpoint on structure 0x1000.txt

      Можно посмотреть в каком порядке опкоды исполняются и с какими смещениями, насколько далеко они друг от друга. Некоторые смещения рядом с известными смещениями могут заинтересовать. Одинаковые опкоды в определенном порядке, но по разным RIP тоже могут заинтересовать. Switch-case могут заинтересоваь в виде cmp [x]. число. Инструкции чтения из структуры больше интересуют, т.к. они могут более вероятно, на что-то влиять - из них читается и для чего-то это нужно.

      Причем последовательность инструкций идет в порядке вызова, но RIP всегда уникальный (RIP не дублируются). Здесь RIP, т.к. CE 64 разрядный

      Если последовательность к данным структуры примерно сохраняется, то можно обратить внимание на обращение к адресам в структуре рядом со здоровьем по смещению 2с0: до обращения к адресу здоровья, во время и после. Я выделил их стрелками. Эти смещения могут быть связаны со здоровьем
      b19446ca-e6cf-43d2-a4a4-cdf94fc4d2a4-изображение.png
      Интересные моменты могут быть с так называемыми switch-case операциями. Можно пытаться их подменить
      88ab7cab-087d-420e-ab3b-f92450c663f1-изображение.png

      написал в MasterGH
      MasterGHM
      MasterGH
    • Для чего нужны личные разделы

      Личный раздел — это ваш персональный раздел, в котором вы являетесь модератором (пока это экспериментально). Темы и обсуждения связаны с вами. Он нужен лично вам, чтобы создавать темы, оставлять сообщения, заметки, обсуждения, отвечать на вопросы, комментировать и т.п. Важные личные файлы всегда резервируйте на своем личном хостинге (гугл диск, яндекс диск и т.п.), а большие файлы лучше сразу туда скидывать и оставлять ссылку на них.
      Если возникала необходимость в личном разделе, напишите мне запрос в личное сообщение.

      написал в MasterGH
      MasterGHM
      MasterGH
    • RE: CE 6.8 Обзор нового инструмента поиска по структурам

      Пользователь @Pitronic написал в CE 6.8 Обзор нового инструмента поиска по структурам:

      Разрешаю в этой теме всё что в той дополнить

      Спасибо, за твое разрешение. Мне, кажется, это две разные статьи и лучше в новой теме.
      @Pitronic публиковать статьи тоже можешь.. Там еще можно добавлять теги и выбирать раздел.

      написал в Статьи
      MasterGHM
      MasterGH
    • RE: Поиск в региона памяти модуля

      Хм. Ну, да такая функция есть уже 😃
      Я думаю, исходники все равно пригодятся в качестве примера, как такой функционал делать. Поставлю метку "обучалка"

      написал в Приёмы
      MasterGHM
      MasterGH
    • RE: CE 6.8 Обзор нового инструмента поиска по структурам

      Прикольная статья, особенно пример работы функции "поиск совпадений". Что-то не помню ее. Надо будет поробовать как-нибудь.

      А все, там русский перевод 😃 меня сбил. Знаю эту функцию

      написал в Статьи
      MasterGHM
      MasterGH
    • RE: Защита Трейнера

      Странно, у меня без ошибок. Версия ce 7.5 с с сайта CE и там же файл туториала. Посмотрю как будет время

      написал в Вопросы
      MasterGHM
      MasterGH
    • Крафт в StarsOne

      В оригинале, если нет всех компонентов рецепта, то нельзя скрафтить вещь.
      68960b6a-5bec-46ac-aefb-8ad86b84272c-изображение.png
      Цель: скрафтить без компонетов по нажатию кнопки
      Игра на Unity и можно пробовать использовать в dnSpy модифицировать файл Assembly-CSharp.dll
      Открываем файлик и смотрим на классы связанные с крафтом
      Так находим кнопку, которая создаст указанное количество вещей крафта в CreateItem
      d8824a7d-2174-4f57-b45f-c47ddd94856d-изображение.png
      fd633567-857e-4533-b4de-eabf3eb2f481-изображение.png
      Задача скрафтить по имеющемуся рецепту любую вещь. Для этого я добавляю проверку количества вещей и удаляю лишний код. Под сполерами оригинальный и модифицированный код
      77be2bf2-7c7e-42f8-a7d0-c69b75307a00-изображение.png
      Модифицированный код

      using System;
      using System.Collections.Generic;
      using System.Runtime.CompilerServices;
      using UnityEngine;
      
      public partial class Crafting : MonoBehaviour
      {
      	public void CreateItem(int curCraft, int craftCount)
      	{
      		if (this.timeBreak >= 0.3f)
      		{
                  // Добавили одну вещь крафта
      			if (craftCount == 0)
      			{
      				craftCount = 1;
      			}
      
      			if (craftCount != 0)
      			{
      				this.succes = true;
      				CraftRecipe component = this.craftObjects[curCraft].GetComponent<CraftRecipe>();
                    
                      // Здесь удалили код, который должен расходовать вещи из инвентаря
                      // ...
                    
      				if (this.succes)
      				{
      					this.iks = this.craftObjects[curCraft].GetComponent<CraftRecipe>().countCreate * craftCount;
      					for (int m = 0; m < this.craftObjects[curCraft].GetComponent<CraftRecipe>().countCreate * craftCount; m++)
      					{
      						this.emptyCell = Inventory.inv.FindItemForStack(this.craftObjects[curCraft].name);
      						if (this.emptyCell >= 0)
      						{
      							if (Inventory.inv.items[this.emptyCell].stack + this.iks <= Inventory.inv.items[this.emptyCell].maxStack)
      							{
      								Inventory.inv.items[this.emptyCell].stack += this.iks;
      								break;
      							}
      							this.iks -= Inventory.inv.items[this.emptyCell].maxStack - Inventory.inv.items[this.emptyCell].stack;
      							Inventory.inv.items[this.emptyCell].stack = Inventory.inv.items[this.emptyCell].maxStack;
      						}
      						else
      						{
      							this.emptyCell = Inventory.inv.FindEmptyCell();
      							if (this.emptyCell >= 0)
      							{
      								this.cloneItem = Inventory.inv.CreateItem(this.craftObjects[curCraft]);
      								if (this.cloneItem.maxStack >= this.iks)
      								{
      									this.cloneItem.stack = this.iks;
      									Inventory.inv.items[this.emptyCell] = this.cloneItem;
      									break;
      								}
      								this.cloneItem.stack = this.cloneItem.maxStack;
      								this.iks -= this.cloneItem.maxStack;
      								Inventory.inv.items[this.emptyCell] = this.cloneItem;
      							}
      							else
      							{
      								if (this.craftObjects[curCraft].GetComponent<Item>().maxStack >= this.iks)
      								{
      									PlayerNetwork.inst.CallCmdCreateItem(this.craftObjects[curCraft].name, Inventory.inv.transform.position - Vector3.forward * 2f + FloatingOrigin.offset, Quaternion.identity, this.iks, this.craftObjects[curCraft].GetComponent<Item>().health);
      									break;
      								}
      								PlayerNetwork.inst.CallCmdCreateItem(this.craftObjects[curCraft].name, Inventory.inv.transform.position - Vector3.forward * 2f + FloatingOrigin.offset, Quaternion.identity, this.craftObjects[curCraft].GetComponent<Item>().maxStack, this.craftObjects[curCraft].GetComponent<Item>().health);
      								this.iks -= this.craftObjects[curCraft].GetComponent<Item>().maxStack;
      							}
      							new WaitForEndOfFrame();
      						}
      					}
      				}
      			}
      			this.timeBreak = 0f;
      		}
      	}
      }
      

      Оригинальный

      public void CreateItem(int curCraft, int craftCount)
      	{
      		if (this.timeBreak >= 0.3f)
      		{
      			if (craftCount != 0)
      			{
      				this.succes = true;
      				CraftRecipe component = this.craftObjects[curCraft].GetComponent<CraftRecipe>();
      				int[] array = new int[7];
      				int[] array2 = new int[7];
      				for (int i = 0; i < 7; i++)
      				{
      					if (this.succes && component.ingredients[i] != null)
      					{
      						array2[i] = component.countIngredients[i];
      						if (array2[i] <= 0)
      						{
      							array2[i] = 1;
      						}
      						array2[i] *= craftCount;
      						for (int j = 0; j < Inventory.inv.items.Length; j++)
      						{
      							if (Inventory.inv.items[j] != null && component.ingredients[i].name == Inventory.inv.items[j].prefName)
      							{
      								array[i] += Inventory.inv.items[j].stack;
      							}
      						}
      						if (array[i] < array2[i])
      						{
      							this.succes = false;
      						}
      					}
      				}
      				if (this.succes)
      				{
      					for (int k = 0; k < 7; k++)
      					{
      						if (component.ingredients[k] != null)
      						{
      							for (int l = 0; l < Inventory.inv.items.Length; l++)
      							{
      								if (Inventory.inv.items[l] != null && array2[k] > 0 && component.ingredients[k].name == Inventory.inv.items[l].prefName)
      								{
      									this.iks = Inventory.inv.items[l].stack;
      									Inventory.inv.items[l].stack -= array2[k];
      									array2[k] -= this.iks;
      									if (array2[k] <= 0)
      									{
      										break;
      									}
      								}
      							}
      						}
      					}
      					this.iks = this.craftObjects[curCraft].GetComponent<CraftRecipe>().countCreate * craftCount;
      					for (int m = 0; m < this.craftObjects[curCraft].GetComponent<CraftRecipe>().countCreate * craftCount; m++)
      					{
      						this.emptyCell = Inventory.inv.FindItemForStack(this.craftObjects[curCraft].name);
      						if (this.emptyCell >= 0)
      						{
      							if (Inventory.inv.items[this.emptyCell].stack + this.iks <= Inventory.inv.items[this.emptyCell].maxStack)
      							{
      								Inventory.inv.items[this.emptyCell].stack += this.iks;
      								break;
      							}
      							this.iks -= Inventory.inv.items[this.emptyCell].maxStack - Inventory.inv.items[this.emptyCell].stack;
      							Inventory.inv.items[this.emptyCell].stack = Inventory.inv.items[this.emptyCell].maxStack;
      						}
      						else
      						{
      							this.emptyCell = Inventory.inv.FindEmptyCell();
      							if (this.emptyCell >= 0)
      							{
      								this.cloneItem = Inventory.inv.CreateItem(this.craftObjects[curCraft]);
      								if (this.cloneItem.maxStack >= this.iks)
      								{
      									this.cloneItem.stack = this.iks;
      									Inventory.inv.items[this.emptyCell] = this.cloneItem;
      									break;
      								}
      								this.cloneItem.stack = this.cloneItem.maxStack;
      								this.iks -= this.cloneItem.maxStack;
      								Inventory.inv.items[this.emptyCell] = this.cloneItem;
      							}
      							else
      							{
      								if (this.craftObjects[curCraft].GetComponent<Item>().maxStack >= this.iks)
      								{
      									PlayerNetwork.inst.CallCmdCreateItem(this.craftObjects[curCraft].name, Inventory.inv.transform.position - Vector3.forward * 2f + FloatingOrigin.offset, Quaternion.identity, this.iks, this.craftObjects[curCraft].GetComponent<Item>().health);
      									break;
      								}
      								PlayerNetwork.inst.CallCmdCreateItem(this.craftObjects[curCraft].name, Inventory.inv.transform.position - Vector3.forward * 2f + FloatingOrigin.offset, Quaternion.identity, this.craftObjects[curCraft].GetComponent<Item>().maxStack, this.craftObjects[curCraft].GetComponent<Item>().health);
      								this.iks -= this.craftObjects[curCraft].GetComponent<Item>().maxStack;
      							}
      							new WaitForEndOfFrame();
      						}
      					}
      				}
      			}
      			this.timeBreak = 0f;
      		}
      	}
      

      Изменяем весь класс или метов в этом окне
      fc19935c-d36c-4dbf-9f7c-4eb458eab3b0-изображение.png
      Если выводит ошибки при компяляции, то скачиваем IlSpy и его код вставляем в код в dnSpy. Или качаем DnSpy 3.2.0 или ранее
      Изменения сохраняем в модуль, запускаем игру и крафтим.
      Получить все рецепты (не проверял правда, попробуйте если хотите)
      c40b6907-64ba-4ea9-8a60-6ceeb36db575-изображение.png
      Вещи не ломаются. Убрать отнятие "здоровья" у вещи
      8f6337fb-51cc-4e59-a841-84a854fbd53d-изображение.png
      8edb5aba-652b-40ec-8e1f-64414389fd26-изображение.png

      написал в Статьи unity dnspy блог
      MasterGHM
      MasterGH
    • Поиск в региона памяти модуля

      b75ef461-6a8d-4399-b351-1524235b357e-изображение.png

      c = createComboBox(MainForm.gbScanOptions)
      
      c.Style = 'csDropDownList'
      c.Items.add('All')
      c.ItemIndex = 0
      
      c.Align = alTop
      c.BorderSpacing.Left = 6
      c.BorderSpacing.Right = 6
      c.BorderSpacing.Bottom = 2
      
      local modulelist
      
      c.OnDropDown = function(d)
        --fill the list
        while c.Items.Count > 1 do
          c.Items.delete(1)
        end
      
        modulelist = enumModules()
        local i
        for i = 1, #modulelist do
          c.Items.Add(modulelist[i].Name)
        end
      end
      
      c.OnSelect = function(d)
        if c.ItemIndex >= 1 then
          MainForm.FromAddress.Text = string.format("%.16x", modulelist[c.ItemIndex].Address)
          if modulelist[c.ItemIndex].Size == nil then
            modulelist[c.ItemIndex].Size = getModuleSize(modulelist[c.ItemIndex].Name)
          end
          MainForm.ToAddress.Text = string.format("%.16x", modulelist[c.ItemIndex].Address+modulelist[c.ItemIndex].Size)
        else
          MainForm.FromAddress.Text = "0000000000000000"
          MainForm.ToAddress.Text = "7fffffffffffffff"
        end
      end
      
      c.Enabled = false 
      

      Источник
      ModuleListRegioScan.lua (поместить в папку "autorun")

      написал в Приёмы фишки ce plugin обучалка
      MasterGHM
      MasterGH
    • RE: Защита Трейнера

      Tutorial-i386_3.CT

      написал в Вопросы
      MasterGHM
      MasterGH
    • RE: Собираем ссылки на разные источники

      Ну, пока не знаю. Если руки дотянуться до этого.
      У AAMaker сложная история. Во-первых этот инструмент делали двое я и srg. По моей задумке плагин должен был автоматически сам каждый раз ассемблировать по неточной сигнатуре новый АА-код и выполнять его. А сейчас при применении правила он генерируется разого в АА-код вручную. Т.е. это не совсем то, что я хотел. Я его с тех давних пор так и не доделал. Во-вторых нет никакой аналитики и статистики, не на что опереться. В каких случаях неточная сигнатура проходит, а в каких нет. Мне кажется, что пока самый надежный вариант это делать assert сигнатуры на проверку и выводить ошибку. И для каждой новой версии игры делать новую сигнатуру.

      написал в Статьи
      MasterGHM
      MasterGH
    • RE: CE 6.8 Обзор нового инструмента поиска по структурам

      Если есть видео по этой теме, то можно и тут его запостить

      написал в Статьи
      MasterGHM
      MasterGH
    • CE рисование через дополнительный поток

      На видео показано рисование через поток и рисование без потока

      CE Native Thread

      Когда происходит рисование без потока, то окно нельзя подвинуть, не работает кнопка и даже не возможно работать с Cheat Engine

      function FilledWithPixels()
        while true do
         ::begin::
          UDF1.repaint()
          for x=1,UDF1.Canvas.Width do
              for y=1,UDF1.Canvas.Height do
                  local min = math.random(1, 0xFFFF)
                  local max = math.random(0xFFFF, 0x00FFFFFF)
                  UDF1.Canvas.SetPixel(x,y, math.random (min, max))
                  if(needReUpdate) then
                   needReUpdate = false
                   goto begin
                  end
              end
          end
          t.suspend()
        end
      end
      
      t = createNativeThreadSuspended(FilledWithPixels)
      t.name = 'New thread 1'
      needReUpdate = true
      
      UDF1 = createForm()
      UDF1.Width = 400
      UDF1.Height = 200
      btn = createButton(UDF1)
      btn.OnClick = function (sender)
        needReUpdate = true
        t.resume()
      end
      
      написал в Видео ce thread ce lua ce draw ce видео
      MasterGHM
      MasterGH
    • RE: Создать структуру программно

      Решил попробовать написать CE Lua скрипт, который прочесывает структуру и в Dissect data/structures окне создавал бы структуру только со смещениями, с которыми код работает, т.е. читает или пишет.

      Например, мой персонаж прогуливается по городу, а каждые 200 мс ставится брейкпоинт на смещение +1 до гипотетической N границы структуры (например до 4096).

      Я не успел сделать определение типа, но смещения внутри структуры получить я успел.

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

      1. к каким смещением было обращение (адреса по ним мы и будем менять или сравнивать структуры между собой позже)

      2. по виду инструкции и соседнему смещению уже примерно можно определить тип данных и их размер
        a3ea23e7-54e8-46c5-82a5-f3d2a73cc540-изображение.png
        Осталось сделать определение типа, а это не так уж долго сделать и осталось взять код из предыдущих записей в блоге формирования структуры в окне dessect data. Так мы получим структуру только с активными смещениями, (а зачем нам пассивные?) и определим в них тип, я надеюсь определим правильно

      Пример кода, который я использовал

      addressStructure1 = 0x412E0200 --> адрес начала структуры в любой игре
      sizeStructure = 100     --> гипототический размер структуры 100 для быстрых поисков, по умолчанию 4096
      indexStructure = 0      --> индекс внутри структуры, который будет перемещаться вместе с breakPointAddress
      breakPointAddress = 0   --> адрес, на который сейчас постален брейкпоинт
      waitTimeTillBreak = 300 --> частота активности смещеиня
      resultText = ''         --> конкатенация частей текста в этой переменной
      
      \-- Функция пытается поставить брейкпоинт на следующий байт в структуре
      function TryNextSetBreakPointToAddress()
        debug_removeBreakpoint(breakPointAddress)
        indexStructure = indexStructure + 1
        if indexStructure > sizeStructure then
          debugTimer.Interval = 1000
          debugTimer.Enabled = false
          debugTimer.destroy()
          debug_continueFromBreakpoint(co_run)
          print(resultText) --> вывод результата с завершением отладки
          return
        end
        breakPointAddress = addressStructure1 + indexStructure
        debug_setBreakpoint(breakPointAddress, 1, bptAccess, bpmDebugRegister)
        debug_continueFromBreakpoint(co_run)
      end
      
      \-- Любимая функция снятия отладочных данных
      function debugger_onBreakpoint()
        -- проверить обращение к структуре
        prevAddress = getPreviousOpcode(RIP)
        resultText = resultText..string.format('Offset: + %X : %s', indexStructure, disassemble(prevAddress)) .. '\r\n'
        TryNextSetBreakPointToAddress()
        return 1
      end
      
      \-- Простой таймер
      debugTimer = createTimer(nil, false)
      debugTimer.OnTimer = function(timer) TryNextSetBreakPointToAddress() end
      debugTimer.Interval = waitTimeTillBreak
      debugTimer.Enabled = true
      breakPointAddress = addressStructure1 + indexStructure
      debug_setBreakpoint(breakPointAddress, 1, bptAccess, bpmDebugRegister)
      
      написал в Программирование
      MasterGHM
      MasterGH
    • CE 6.8 Обзор нового инструмента поиска по структурам
      1. Открываем trainme Dark Byte.
      2. Проходим первый шаг (там нужно сделать One Hit Kill чит через прогресс бар, количество патронов тоже интересно поломать)
      3. Так вот на Step2 ищем адрес здоровья нашего космического корабля.
        Ставим брейкпоинт на доступ, стреляем.
        Видим, три адреса и вот теперь новые функции в контекстном меню как на скриншоте
        0745f2ad-fbcb-481d-b618-0a79d582e6cf-изображение.png
        На "(1)-(2)" - вызываем контекстное меню выделив адрес своего корабля и добавляем его в группу1. Тоже самое делаем для красных адресов кораблей противников

      На "(2)" вызываем опцию сканирования и видим "(3)"
      На (3) выбираем скан по RAX или по другим значениям. Например, когда по RAX не нашли ничего.
      Дальше видим окно и галку ставим "Only find matching groups" и сканим. Видим результат
      7e026599-701d-46c8-8e87-696877c11119-изображение.png
      Т.е. по +70 и другим смещениям можно сделать фильтр свой - чужой.

      В общем замечательный инструмент для работы со структурами с заданием уровня сканирования по оффестам

      написал в Статьи ce structure блог compare
      MasterGHM
      MasterGH
    • Создать структуру программно

      Создать структуру программно

      Пример, что будет в конце записи
      изображение.png
      Нужен поинтер и процесс.

      Запустим туториал из Cheat Engine из меню Health->Cheat Engine Tutorial.
      Прохождение туториала подробно описано здесь

      Cheat Engine Tutorial Guide (x32) - Cheat Engine
      Подключаемся к процессу идем на 8-мой шаг. В руководстве есть поинтер
      [[[["Tutorial-i386.exe"+XXXXXX]+C]+14]+0]+18
      Вместо XXXXXX может быть любое смещение. Поэтому надо бы поискать

      Нашил адрес.
      Поставили бряк.
      Нашли esi
      Поставили бряк

      Вышли сюда

      0042595E - A1 60D65F00 - mov eax,[005FD660] { [0183E9E8] }
      00425963 - 8B 40 0C - mov eax,[eax+0C]
      00425966 - 8B 40 14 - mov eax,[eax+14]
      00425969 - FF 30 - push [eax]
      //...
      00425F3C - 89 46 18 - mov [esi+18],eax

      // Вот такой получили поинтер
      [[[[005FD660]+C]+14]+0]+18
      Поинтер получили, дальше пойдет Lua.

      Открываем Lua консоль и проверяем поинтер

      Выведем адрес и его значение

      \-- getAddress позволяет получить адрес. Если есть квадратные скобки, то и значение адреса
        local address = getAddress("[[[[005FD660]+C]+14]+0]+18")
        local value = getAddress("[[[[[005FD660]+C]+14]+0]+18]")
        local text = string.format('%X = %s',address,value)
        -- Показать диалог. В defines.lua можно посмотреть переменные mtInformation, mbok
        messageDialog(text, mtInformation, mbok)
        --> Вывод "018250E0 = 3289"
      

      Поинтер верный.

      Другой вариант читать значение поинтера примерно такой

      local address = getAddress("game.exe")
        address = readPointer(address + 0x123)
        address = readPointer(address + 0x456)
        local value = readFloat(address + 0x789)
      

      Есть и такие варианты

      value = readInteger("[[[[[[[[[witcher3.exe + 028F3F60] +0] +18] +20] +40] +40] + 1c0] +10] +28]")    
      value = readFloat("[[[[[[[[[witcher3.exe + 028F3F60] +0] +18] +20] +40] +40] + 1c0] +10] +28]")    
      value = readDouble("[[[[[[[[[witcher3.exe + 028F3F60] +0] +18] +20] +40] +40] + 1c0] +10] +28]")
      

      Теперь самое интересное — создание структур с помощью Lua
      Построим структуру [[[[005FD660]+C]+14]+0]+18. На +18 будет наш адрес.
      Сначала построим один уровень —"005FD660"

      \--Создаем пустую структуру и добавляем её в глобальный список
        myStructure = createStructure('MyStructure')
        myStructure.addToGlobalStructureList()
        -- Автоматически заполняем
        -- Праметры структуры: адрес, смещение и размер
        myStructure.autoGuess('005FD660', 0, 4096)   
        -- Создать окно и внести поле адреса
        local structureFrm = createStructureForm('005FD660')
        -- Выбрать структуру на форме. Через UI клик по индексу последней созданной структуры
        local structureIndex = getStructureCount() - 1
        structureFrm.Menu.Items[2][structureIndex+2].doClick()
      

      Если выполнить скрипт выше, то мы построим структуру одного уровня.
      Построим теперь структуру двух уровней "[005FD660]+C". Второй уровень нужно развернуть
      Для этого после создания структуры создам дочернюю

      myStructure2 = createStructure('MyStructure2')
        myStructure2.autoGuess('[005FD660]', 0, 50)
        -- И поместим её по индексу
        local offset = 0
        myStructure.getElementByOffset(offset).setChildStruct(myStructure2)
        -- Чтобы развернуть список поитеров. К сожалению разворачивается только два уровня
        structureFrm.Menu.Items[1][6].doClick()
      

      Итого получается такой скрипт до второго уровня

      \-- Создаем пустую структуру и добавляем её в глобальный список
        myStructure = createStructure('MyStructure')
        myStructure.addToGlobalStructureList()
        -- Автоматически заполняем
        -- Праметры структуры: адрес, смещение и размер
        myStructure.autoGuess('005FD660', 0, 4096)
      \------------------
        myStructure2 = createStructure('MyStructure2')
        myStructure2.autoGuess('[005FD660]', 0, 50)
      
        local offset = 0
        myStructure.getElementByOffset(offset).setChildStruct(myStructure2)
      \------------------
        -- Создать окно и внести поле адреса
        local structureFrm = createStructureForm('005FD660')
        -- Выбрать структуру на форме. Через UI клик по индексу последней созданной структуры
        local structureIndex = getStructureCount() - 1
        structureFrm.Menu.Items[2][structureIndex+2].doClick()
      
        structureFrm.Menu.Items[1][6].doClick()
      

      Создадим и развернем весь указатель [[[[005FD660]+C]+14]+0]+18
      Не будем делать через цикл, чтобы не усложнять

      myStructure2 = createStructure('MyStructure2')
        myStructure2.autoGuess('[005FD660]', 0, 50)
        local offset = 0x0
        myStructure.getElementByOffset(offset).setChildStruct(myStructure2)
      
        myStructure3 = createStructure('MyStructure3')
        myStructure3.autoGuess('[[005FD660]+C]', 0, 50)
        offset = 0xC
        myStructure2.getElementByOffset(offset).setChildStruct(myStructure3)
      
        myStructure4 = createStructure('MyStructure4')
        myStructure4.autoGuess('[[[005FD660]+C]+14]', 0, 50)
        offset = 0x14
        myStructure3.getElementByOffset(offset).setChildStruct(myStructure4)
      
        myStructure5 = createStructure('MyStructure5')
        myStructure5.autoGuess('[[[[005FD660]+C]+14]+0]', 0, 50)
        offset = 0x0
        myStructure4.getElementByOffset(offset).setChildStruct(myStructure5)
      

      Полный скрипт

      \-- Создаем пустую структуру и добавляем её в глобальный список
        myStructure = createStructure('MyStructure')
        myStructure.addToGlobalStructureList()
        -- Автоматически заполняем
        -- Праметры структуры: адрес, смещение и размер
        myStructure.autoGuess('005FD660', 0, 50)
      \------------------
        myStructure2 = createStructure('MyStructure2')
        myStructure2.autoGuess('[005FD660]', 0, 50)
        local offset = 0x0
        myStructure.getElementByOffset(offset).setChildStruct(myStructure2)
      
        myStructure3 = createStructure('MyStructure3')
        myStructure3.autoGuess('[[005FD660]+C]', 0, 50)
        offset = 0xC
        myStructure2.getElementByOffset(offset).setChildStruct(myStructure3)
      
        myStructure4 = createStructure('MyStructure4')
        myStructure4.autoGuess('[[[005FD660]+C]+14]', 0, 50)
        offset = 0x14
        myStructure3.getElementByOffset(offset).setChildStruct(myStructure4)
      
        myStructure5 = createStructure('MyStructure5')
        myStructure5.autoGuess('[[[[005FD660]+C]+14]+0]', 0, 50)
        offset = 0x0
        myStructure4.getElementByOffset(offset).setChildStruct(myStructure5)
      
        --[[[[005FD660]+C]+14]+0]+18
      \------------------
        -- Создать окно и внести поле адреса
        local structureFrm = createStructureForm('005FD660')
        -- Выбрать структуру на форме. Через UI клик по индексу последней созданной структуры
        local structureIndex = getStructureCount() - 1
        structureFrm.Menu.Items[2][structureIndex+2].doClick()
      
        structureFrm.Menu.Items[1][6].doClick()
      

      Результат
      33e4b026-502e-4ddd-809d-e1ca47ed7159-изображение.png

      Ну и на закуску.

      Допустим мы знаем что по адресу X будет всегда тип float (как X,Y,Z координаты). Но расструктуризация будет показывать другой тип, дизассемблер будет показывать другой тип — 4 байта. Что делать?

      И сразу еще допустим double тип всегда хотим как float тип (в виде комментов в дизассемблере или в расструктуризации)

      Воспользоваться следующей функцией

      -- Может менять тип адреса x в окне дизассемблере в комментариях, когда в инструкции существует адрес
      -- Может менять тип адреса в окне расструктуризации
      -- Изменив тип, будет ображаться значение другого типа
      onAutoGuess(function) :
      Registers an function to be called whenever autoguess is used to predict a variable type
      function override (address, ceguess): Return the variable type you want it to be. If no change, just return ceguess
      Ну и вот два примера

      
          -- Пример замена типа по типу
          function iKnowBetter1(address, ceguess)
              if (ceguess==vtDouble) then
                return vtDword
              else
                return ceguess
              end
            end
      
           -- Пример замена типа по адресу
          function iKnowBetter2(address, ceguess)  
            local someAddress = 0x1424AC6D0
            if (address == someAddress) then
              return vtSingle
            else
              return ceguess
            end
          end
      
          onAutoGuess(iKnowBetter1)
          onAutoGuess(iKnowBetter2)
      
      написал в Программирование ce structure ce lua
      MasterGHM
      MasterGH
    • Как контролировать включение и выключение скриптов в таблице

      Как контролировать включение и выключение скриптов в таблице

      AA или "Autoassembler code" код похожий на язык программирования ассемблера.

      Кратко, он позволяет менять игровой код. Подробнее Cheat Engine:Auto Assembler (http://wiki.cheatengine.org/index.php?title=Cheat_Engine:Auto_Assembler)

      Обычный АА-скрипт состоит из двух директив и добавляется в таблицу CE как запись

       // Код срабатывающий как при активации, так и при деактивации
        [ENABLE]
        // Код активации
        [DISABLE]
        // Код деактивации
      

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

      Есть такие директивы как {$lua} и {$asm}.

      Под {$lua} пишут Lua скрипт, под {$asm} пишут АА-скрипт.
      Мы можем проверить Lua скриптом любое условие и разрешить включать галку или выключать галку. Например, через "syntaxcheck" — проверку синтаксиса или другое условие .
      Если проверка не прошла, то не получится галку включить и не получится выключить, если что-то пойдет не так.

      В Lua строка из двух минусов "--", обозначает комментарий.

      {$lua}
      \-- Расскоментируйте или закоментируйте пару строк ниже
      \-- if syntaxcheck then return end
      \-- error[666] = 666
      \------------------------------ ENABLE ------------------------------
      [ENABLE]
      \-- Расскоментируйте или закоментируйте пару строк ниже
      \-- if syntaxcheck then return end
      \-- error[666] = 666
      \------------------------------ DISABLE ------------------------------
      [DISABLE]
      \-- Расскоментируйте или закоментируйте пару строк ниже
      \-- if syntaxcheck then return end
      \-- error[666] = 666
      

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

      {$lua}
      \-- if syntaxcheck then return end
      \-- некоторый lua код правильный или не правильный
      \-- error[666] = 666
      \------------------------------ ENABLE ------------------------------
      [ENABLE]
      \--if syntaxcheck then return end
      \-- некоторый lua код
      \-- некоторый lua код правильный или не правильный
      \-- error[666] = 666
      \------------------------------ DISABLE ------------------------------
      [DISABLE]
      if syntaxcheck then return end
      \-- некоторый lua код
      \-- некоторый lua код правильный или не правильный
      error[666] = 666
      

      Следующий вариант проверяет открыт ли процесс. Если нет, то покажет сообщение.

      Как узнать, что процесс был закрыт после открытия
      process - зарезервированное переменная, показывает что процесс открыт

      (http://wiki.cheatengine.org/index.php?title=Lua:process)
      В комментариях можно увидеть при каких условиях блокируются включение галки

      {$lua}
        -- code before either enable/disable section runs for both just like with AA code
        if syntaxcheck then return end
        [ENABLE]
        if process == nil  then
         showMessage('Процесс не подключен. Галка не будет включена')
         return
        end
        if process ~= nil and readInteger(process) == nil  then
         showMessage('Процесс был закрыт. Галка не будет включена')
         return
        end
      
        [DISABLE]
        -- Галку можно выключить, но код выключения выполнять если процесс подключен
        if process ~= nil and readInteger(process) ~= nil then
          print('Attached to ' .. process)
        else
         showMessage('The process closed')
        end
      
      написал в Программирование ce lua условие управление
      MasterGHM
      MasterGH
    • 1
    • 2
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 8 / 13