Взламываем игры с помощью Python

от автора

В стародавние времена, когда по земле ходили мамонты, а я был в два раза моложе, среди игрового сообщества пользовалась популярностью компьютерная программа для «взлома» игр под названием ArtMoney. С помощью этой софтины можно было не только облегчить себе жизнь в прохождении хардкорного приключения, модифицировав значения ресурсов в игре, но и просто поразвлечься, изучив полюбившийся проект с разных сторон.

А на днях мне вдруг захотелось вспомнить молодость и поиграть в бумерский диаблойд под названием Titan Quest, выпущенный аж в 2006 году. Да вот только времени на беготню, прокачку, и вот это вот всё, у меня нет. И ArtMoney нет. Зато есть определенные знания программирования. Вот я и решил совместить приятное с полезным, написав аналог ArtMoney на Python, а заодно стать супербогатым, хотя бы в Titan Quest.

Для этого дела понадобились только Python и библиотека Pymem, с помощью которой можно взламывать процессы Windows и манипулировать памятью (читать и записывать).

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

Класс MemoryEditor

class MemoryEditor:     def __init__(self, process_name: str) -> None:         self.pm = pymem.Pymem(process_name)         self.process_base = pymem.process.module_from_name(self.pm.process_handle, process_name).lpBaseOfDll

Конструктор класса принимает имя процесса (process_name), которое нужно открыть (например, process.exe).

  • pymem.Pymem(process_name) — открывает процесс и позволяет взаимодействовать с его памятью.

  • process_base — это базовый адрес основного модуля процесса (обычно самого .exe файла).

Метод search_value

def search_value(self, value: int) -> list:         search_results = []         memory_size = 0x7FFFFFFF  # Размер памяти для сканирования (большой диапазон)         chunk_size = 0x1000  # Размер блока чтения         search_bytes = ctypes.c_uint32(value).value.to_bytes(4, byteorder='little')                  offset = 0         while offset < memory_size:             current_address = self.process_base + offset                          if self.is_memory_readable(current_address):                 try:                     buffer = self.pm.read_bytes(current_address, chunk_size)                 except pymem.exception.MemoryReadError:                     offset += chunk_size                     continue                                  chunk_offset = 0                 while True:                     chunk_offset = buffer.find(search_bytes, chunk_offset)                     if chunk_offset == -1:                         break                      # Сохранение адреса найденного значения                     address = current_address + chunk_offset                     search_results.append(address)                                          chunk_offset += len(search_bytes)                          offset += chunk_size          return search_results

Эта функция выполняет поиск заданного значения (value) в памяти процесса.

  • memory_size определяет область памяти, в которой будет производиться поиск.

  • chunk_size определяет, какой объем данных будет считываться за раз (в данном случае 4KB).

  • search_bytes преобразует значение в байтовую строку для поиска в памяти.

  • Цикл while offset < memory_size: проходит по всей указанной области памяти, проверяя каждую часть на наличие нужного значения.

  • self.is_memory_readable(current_address) проверяет, доступна ли память для чтения.

  • Если значение найдено в текущем блоке памяти, его адрес сохраняется в search_results.

Метод is_memory_readable

def is_memory_readable(self, address) -> bool:         mbi = pymem.memory.virtual_query(self.pm.process_handle, address)         if mbi.Protect & 0xF != 0x0 and mbi.State == 0x1000 and mbi.Protect & 0x100 == 0:             return True         return False

Эта функция проверяет, доступна ли память по указанному адресу для чтения.

  • Использует функцию virtual_query из библиотеки pymem, которая возвращает информацию о состоянии и защите памяти.

  • Проверяет, что память доступна, не защищена и не имеет флага PAGE_GUARD.

Метод search_next_value

def search_next_value(self, addresses: list, next_value: int) -> list:         search_results = []         search_bytes = ctypes.c_uint32(next_value).value.to_bytes(4, byteorder='little')                  for address in addresses:             if self.is_memory_readable(address):                 try:                     buffer = self.pm.read_bytes(address, 4)                 except pymem.exception.MemoryReadError:                     continue                                  if buffer == search_bytes:                     search_results.append(address)                  return search_results

Эта функция ищет новое значение (next_value) только среди адресов, найденных на предыдущем этапе поиска.

  • Функция принимает список адресов (addresses) и значение для поиска (next_value).

  • Если новое значение найдено по одному из адресов, этот адрес добавляется в список search_results.

И метод replace_value

def replace_value(self, addresses: list, new_value: int) -> None:         replace_bytes = ctypes.c_uint32(new_value).value.to_bytes(4, byteorder='little')         for address in addresses:             self.pm.write_bytes(address, replace_bytes, 4)             print(f"Замена значения по адресу: {hex(address)} на {new_value}")

Функция заменяет значения по указанным адресам (addresses) на новое значение (new_value).

  • replace_bytes — это новое значение в виде байтовой строки.

  • self.pm.write_bytes(address, replace_bytes, 4) записывает новое значение в память по указанному адресу.

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

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

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

Кто желает воспользоваться программой или дополнить её: репозиторий PyMoney.

Благодарю за внимание!


ссылка на оригинал статьи https://habr.com/ru/articles/835212/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *