В стародавние времена, когда по земле ходили мамонты, а я был в два раза моложе, среди игрового сообщества пользовалась популярностью компьютерная программа для "взлома" игр под названием 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.
Благодарю за внимание!
Комментарии (27)
A1t0r
10.08.2024 16:49+3Забавно, недавно расковырял игру, чтобы написать утилиту на плюсах для сброса очков умений и характеристик. У tq такая механика, что если сбросить все очки вложенные в мастерство, то можно сменить класс) За статью +
Andrey_Solomatin
10.08.2024 16:49+4ArtMoney была крутая, там можно было профили сохранять, чтобы потом не искать второй раз. До неё я Cheat-O-Matic пользовался.
alpha_man
10.08.2024 16:49Помню в детстве себе сходу накручивал деньги в сталкере первом, чтобы игралось легче) отличное было время...
christener
10.08.2024 16:49+1Уже долгое время существует и совершенствуется Cheat Engine, в которой можно не просто искать и заменять значения по адресам, но и использовать для этого скрипты. Во многих современных играх недостаточно просто найти и заменить значение. Это зачастую не работает.
Причем, готовые таблицы для игр можно легко найти в сети, где основные значения найдены за тебя и понятно подписаны: какое значение/скрипт за какие параметры отвечает.remzalp
10.08.2024 16:49А еще можно внедрить в память процесса ассемблерный код, так что патроны могут стать действительно бесконечными, а если быть недостаточно аккуратным - будут бесконечными даже у врагов :)
Cheat Engine уже давно содержит отладчик и (слабенькую) среду разработки
Dolios
10.08.2024 16:49А на днях мне вдруг захотелось вспомнить молодость и поиграть в бумерский диаблойд под названием Titan Quest, выпущенный аж в 2006 году. Да вот только времени на беготню, прокачку, и вот это вот всё, у меня нет.
Гораздо проще было скачать картинку с текстом: "Поздравляем, вы победили". Никогда не понимал пользователей читов и артмани. Игра, это же процесс, зачем его портить и делать неинтересным? Если тебе изначально не хочется играть, можно же, например, сериал посмотреть или книжку почитать. Хотя, если задача именно в том, чтобы расковырять игру и сделать красивые чиселки в и нвентаре, то это вполне себе понятно, но игра, собственно, тут вторична и не принципиальна.
DaneSoul
10.08.2024 16:49+4У игры есть разные механики и не всегда есть интерес использовать их все.
Например как-то давно пробовал "The SIMs", конкретно хотелось поиграться с редактором домов, для чего стартовых ресурсов явно не достаточно. Надо было потратить кучу часов на нудное зарабатывание виртуальных монеток когда можно было просто ввести чит код и приступить сразу к интересной для меня части?Dolios
10.08.2024 16:49Для меня это звучит так, как будто вы купили мороженное, обильно намазанное сверху навозом и потом этот навоз палочкой счищали. Мой ответ: надо было взять игру, которая нравится. Такое мороженное я не куплю. Тем более, что у меня, например, очередь лет на 5 из таких игр, если я всё брошу и буду только играть. На пенсии обязательно пройду...
randomsimplenumber
10.08.2024 16:49+3Мой ответ: надо было взять игру, которая нравится.
А как узнать, какая игра нравится, о мудрый филин?
DaneSoul
10.08.2024 16:49+3Ну да, а если уж купил мороженное, то надо и упаковку сожрать - за все уплачено!
Если меня угостили тортом у которого сверху украшение из малины, которая мне не нравится, это не значит что я выкину весь торт или буду кривится от малины - я уберу малину и с удовольствием съем остальное.
konst90
10.08.2024 16:49+4Мой ответ: надо было взять игру, которая нравится.
Так он и взял игру, которая нравится.
Но в TQ девять разных классов, игрок может выбрать любые два, то есть сочетаний 9*8. И все их хочется попробовать в полной прокачке, причём с разными вариантами выбора умений. А проходить игру 72 раза всё-таки не очень интересно.
Alkhonor
10.08.2024 16:49Минутка духоты. Там 11 классов на данный момент, но т.к. 1 + 2 и 2 + 1 -тот же класс, то с учетом этого выходит 66 уникальный классов. Ни сколько не меняет посыл вашего сообщения, просто уточнил
newintellimouse
10.08.2024 16:49Не во всех играх есть Story mod, а там, где он есть, в нём может отсутствовать часть сюжета.
imba
10.08.2024 16:49+1Каждыи играет в игру как хочет, если это не сетевой кооп, то 'читы' можно добавлять по своему вкусу. Да и написание читов, это тоже своего рода игра и доставляете не менее чем сама игра, считаи прошел на скрый класс мага_зазеркалья без гаидов))
Derfirm
10.08.2024 16:49+4Читы это не только механизм "быстрой" победы, но и возможность дебага/профилирования приложения(игры). Мне иногда просто интересно пропустить часть гринда и пройти весь сторилайн, иногда поковырять как устроен файл сохранения и какие штуки вшиты в движок. Также читами могут быть всякие переключатели погоды, окраски скинов или сами скины. Как иначе, если внутри игры не реализовано условное "зеркало", позволяющее сменить лицо персонажа после создания? Сделать респек?
Безусловно это может сломать игру и она будет сильно отходить от задуманного дизайна. А может и стать лучше, не попробуешь не узнаешь точно :)
christener
10.08.2024 16:49Мы с другом в школьные времена обожали в GTA: Vice City вводить чит на оружие и просто развлекаться, соревнуясь в том, кто дольше проживет, играя по очереди. Было очень весело.
falseshepard
10.08.2024 16:49Сейчас для этих дел используется cheat engine, там функций сверх artmoney навалено ого-го.
zartarn
10.08.2024 16:49В смысле сейчас? И тогда тоже. Cheat engine вышел в 2000 году.
Ну и не сказать что прям сильно много функций навалено. Есть немного чуть более узкоспециализированных функций, но та же старая добрая ollydbg в целом даст фору)
П.С.: вот тут немного TQ ковыряли, вдруг кому пригодится
Hidden text
kate_caffeine
10.08.2024 16:49+1это не слезы, это ностальгия в глаз попала...
спасибо за статью! никогда не задумывалась о том, как работает арт мани, теперь интересно самой попробовать)) пойду взламывать DMC 4))
QuantumBoy
10.08.2024 16:49Мне вот не понятно. Поправьте, если не прав.
Тут ищется число в 4 байта, при этом для того, чтобы его найти, искомая память разбивается на блоки по 4 байта. Как будто это не надёжно, потому что по факту оно может лежать между блоков, два байта в конце одного блока и два байта в начале другого. Или три в конце одного, один в начале следующего.
Как будто тут просто "повезло", что перед искомым числом хлама оказалось аккурат кратно 4 байтам.
Andrey_Solomatin
10.08.2024 16:49Может быть выравнивание применяется? И на самом деле оно лежит вообще как 8 байтов с нулями впереди?
it_police Автор
10.08.2024 16:49Просто логично было начать поиск с размера, кратного размеру данных и надеяться, что чанки выравнены. Вероятно сработало бы и на 8 байт, и был бы бонус к скорости поиска. Но я не являюсь экспертом по работе с памятью, всё получилось с первого раза, дальше экспериментировать не стал. Поэтому да, повезло.
Ambreon
Интересная статья
Было интересно)