• Ну и что вы думаете? Мы взяли и создали свой собственный чат, с блекджеком и шлюхами, теперь все легенды в одном месте: даже те 1000 человек, которых мы забанили в старом чате 🫡 Будем публиковать там очень интересные подробности нового дизайна форума, oh yeah

    Вступай и становись легендой, пока это не стало поздно: жмякай на меня, ток не сильно(

Гайд Dota 2 External Python [Zoomhack + Weather changer]

Начинающий
Статус
Оффлайн
Регистрация
12 Мар 2025
Сообщения
74
Реакции[?]
8
Поинты[?]
8K
Приветствую !
В этом гайде я расскажу как сделать рабочий External на путончике пока что только zoomhack + weather changer.
Полагаю что у каждого есть опыт с Cheat Engine, и в этом гайде я не буду углубляться в его изучение.

И так в доте есть уже встроенные команды для реализации Zoomhack, но в матче конечно мы их менять не можем, но можем писать в адреса значений этих команд.
Нам понадобиться найти несколько адресов:
r_farz (Грубо говоря это лимит как бы на обзор , он должен увеличиваться пропорционально с дистанцией камеры чтобы не было черных нон-рендер зон)
dota_camera_distance (сама команда для изменение дальности камеры) - Кстати адрес дальности камеры статична, с этим заморачиваться не будем.
fog_enable (Включение/Выключение тумана, в >1400 дистанции камеры уже туман будет сильно мешать, а >2000 уже все будет покрыто под белый туман и поэтому надо его автоматически отключить при запуске)
cl_weather 0-9 (на выбор 10 погод, так же легко меняется через запись в память)


В итоге получаем 4 адреса которые нам надо заполучить.
Начинаем по порядку - r_farz:

Заходим в пробу героя, смотрим r_farz, в начале он всегда -1 но так как значений -1 много чтобы не крашнуло CE ставим другое желаемое
например 100000 и потом резко отсеиваем до 1 чтобы отсеять весь прочий мусор. И да r_farz это float не ошибайтесь.

Как только мы нашли нужный адрес смотрим, пробуем его менять через CE, попробуйте изменять его на 0, если обзор пропадет то значит все ок!
Дальше мы делаем Pointerscan на найденный нами адрес, клацаем по Pointerscan of this address и выставляем желаемые значения от 3 до 7. Желательно 3, больше и не надо, подождите пару минут и в результате сканирования выберите адрес с минимальным количеством смещений, для удобства.
Выходим из игры - Заходим обратно, открываем еще раз дотку на CEшке и заново ищем r_farz , найденный адрес вбиваем в результат сканирования, нажимаете
Pointer scanner - Rescan Memory removes pointers not pointing to the right address.
В появившемся окне на поле ввода адреса вставляем новый адрес r_farz и отсеиваем по новой.
В новых результатах ищем стабильный адрес желательно с одним смещением .И обязательно модуль должен быть client.dll для всех адресов которые мы будем искать, вам могут попасться tier0.dll, engine2.dll, server.dll -их не трогаем они опасны или не стабильны.
После того как нашли записывайте себе в конфиги чита (например у меня offsets.json):
[client.dll, базовый адрес, смещение] .
Принцип поиска для остальных такой же, ищете в консоли команду - меняется, отсеиваете находите адрес делаете Pointerscan, перезаходите и еще раз так по кругу и записываем в конфиги свои. Только учтите что у cl_weather не float а integer (4 bytes).
а у fog_enabled (0, 1) - byte

Приступим к коду

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


Начнем с самого главного - запись в память:
Записывать будем через библиотеку Pymem.

memory_manager.py:
# Импортируем необходимые модули
import ctypes  # Для работы с Windows API (чтение/запись памяти)
import psutil  # Для получения информации о процессах
import struct  # Для упаковки/распаковки данных

# Класс для управления процессом и памятью
class MemoryManager:
    # Константа для полного доступа к процессу
    PROCESS_ALL_ACCESS = 0x1F0FFF

    def [B]init[/B](self, process_name):
        # Сохраняем имя процесса
        self.process_name = process_name
        # Получаем PID процесса по его имени
        self.pid = self.get_pid_by_name(process_name)
        # Если процесс не найден — кидаем исключение
        if not self.pid:
            raise Exception(f"Процесс '{process_name}' не найден!")
        print(f"[INFO] PID процесса '{process_name}': {self.pid}")

        # Открываем процесс с полным доступом
        self.process = ctypes.windll.kernel32.OpenProcess(self.PROCESS_ALL_ACCESS, False, self.pid)
        if not self.process:
            raise Exception("Не удалось открыть процесс!")

    @staticmethod
    def get_pid_by_name(name):
        """
        Ищет PID процесса по его имени.
        """
        for proc in psutil.process_iter(['pid', 'name']):
            if proc.info['name'].lower() == name.lower():
                return proc.info['pid']
        return None

    def list_modules(self):
        """
        Возвращает список модулей (DLL) загруженных процессом.
        """
        h_module_snap = ctypes.windll.kernel32.CreateToolhelp32Snapshot(0x00000008, self.pid)
        if h_module_snap == -1:
            raise Exception("Ошибка создания снимка модулей!")

        # Структура для хранения информации о модуле
        class MODULEENTRY32(ctypes.Structure):
            [I]fields[/I] = [
                ("dwSize", ctypes.c_ulong),
                ("th32ModuleID", ctypes.c_ulong),
                ("th32ProcessID", ctypes.c_ulong),
                ("GlblcntUsage", ctypes.c_ulong),
                ("ProccntUsage", ctypes.c_ulong),
                ("modBaseAddr", ctypes.POINTER(ctypes.c_ubyte)),
                ("modBaseSize", ctypes.c_ulong),
                ("hModule", ctypes.c_void_p),
                ("szModule", ctypes.c_char * 256),
                ("szExePath", ctypes.c_char * 260)
            ]

        # Создаем экземпляр структуры и устанавливаем ее размер
        me32 = MODULEENTRY32()
        me32.dwSize = ctypes.sizeof(MODULEENTRY32)
        modules = []

        # Перебираем модули процесса
        if ctypes.windll.kernel32.Module32First(h_module_snap, ctypes.byref(me32)):
            while True:
                module_name = me32.szModule.decode()
                module_base = ctypes.addressof(me32.modBaseAddr.contents)
                modules.append((module_name, module_base))
                if not ctypes.windll.kernel32.Module32Next(h_module_snap, ctypes.byref(me32)):
                    break

        # Закрываем снимок модулей
        ctypes.windll.kernel32.CloseHandle(h_module_snap)
        return modules

    def read_pointer(self, address):
        """
        Чтение указателя по адресу.
        """
        buffer = ctypes.create_string_buffer(8)
        bytes_read = ctypes.c_size_t()
        if ctypes.windll.kernel32.ReadProcessMemory(self.process, ctypes.c_void_p(address), buffer, 8, ctypes.byref(bytes_read)):
            return int.from_bytes(buffer.raw, "little")
        return None

    def calculate_pointer(self, base_address, offsets):
        """
        Рассчитываем адрес с учетом цепочки указателей.
        """
        address = base_address
        for offset in offsets:
            address = self.read_pointer(address)
            if address is None:
                raise Exception("Ошибка чтения указателя")
            address += offset
        return address

    def read_memory(self, address, data_type):
        """
        Чтение значения из памяти по адресу.
        """
        size = {"byte": 1, "int": 4, "float": 4, "double": 8, "str": 256}.get(data_type, 4)
        buffer = ctypes.create_string_buffer(size)
        bytes_read = ctypes.c_size_t()

        if ctypes.windll.kernel32.ReadProcessMemory(self.process, ctypes.c_void_p(address), buffer, size, ctypes.byref(bytes_read)):
            if data_type == "byte":
                return buffer.raw[0]
            elif data_type == "int":
                return int.from_bytes(buffer.raw, "little")
            elif data_type == "float":
                return struct.unpack('f', buffer.raw[:4])[0]
            elif data_type == "double":
                return struct.unpack('d', buffer.raw[:8])[0]
            elif data_type == "str":
                return buffer.raw.decode(errors="ignore").rstrip('\x00')
        return None

    def close(self):
        """
        Закрываем хендл процесса.
        """
        ctypes.windll.kernel32.CloseHandle(self.process)
        print(f"[INFO] Процесс '{self.process_name}' закрыт.")

# Пример использования с поддержкой смещения
if [B]name[/B] == "[B]main[/B]":
    try:
        # Создаем менеджер памяти для процесса notepad.exe
        mm = MemoryManager("dota2.exe")

        # Получаем список модулей и находим базовый адрес модуля notepad.exe
        modules = mm.list_modules()
        base_address = None
        for module_name, module_base in modules:
            if "dota2.exe" in module_name.lower():
                base_address = module_base
                print(f"[INFO] Модуль '{module_name}' найден по базовому адресу: {hex(base_address)}")
                break

        if not base_address:
            raise Exception("Базовый адрес модуля не найден!")

        #Оффсеты (пример, замени на свой) Так же можешь из конфига своего загрузить
        offset = 0x10

        final_address = base_address + offset
        print(f"[INFO] Конечный адрес после смещения: {hex(final_address)}")

        value = mm.read_memory(final_address, "float")
        print(f"[INFO] Значение по адресу {hex(final_address)}: {value}")

        # Закрываем процесс
        mm.close()

    except Exception as e:
        print(f"[ERROR] {str(e)}")
Код для записи вы будете реализовывать в интерфейсе ну если вы не ленивые можно ин на отдельном файле. В коде я привел пример использования.
События могут так же быть разные - прокрутка мыши, ползунка , по нажатии кнопки и т.п, но принцип одинаковый:
Event >mm.write_memory(cam_distance_address, cam_distance, "float")
если mm.read_memory(fog_enable_address, "byte") == 1: mm.write_memory(fog_enable_address, 0, "byte")
Event >mm.write_memory(r_farz_address, cam_distance*2, "float") - r_farz должен быть выше дистанции




Вам не обязательно копировать весь мой код, все можно сделать гораздо компактнее, но все то сделано для удобства, вы же можете игнорировать методы как calculate_pointer, read_pointer они вам вряд ли понадобиться . Ну окошко думаю тут уже по вашему вкусу, можете на tkinter, можете на PyQt5. Тут уже исходя только от вашей фантазии. Ну если вы прям хотите вы скажите и я вам отправлю код моей гуишки.
На этом все. Конечный результат выглядит так:
 
Начинающий
Статус
Оффлайн
Регистрация
12 Мар 2025
Сообщения
74
Реакции[?]
8
Поинты[?]
8K
И да, просто писать базовый адрес и оффсет не самая лучшая идея, но для ознакомления и для базового понятие сойдет, потому-что при каждом обновлении игры чаще всего меняются базовые адреса наших значений. Советую сделать поиск через сигнатуры
 
Начинающий
Статус
Оффлайн
Регистрация
11 Мар 2024
Сообщения
3
Реакции[?]
0
Поинты[?]
0
Слушай я ничего не понял ибо на этом форуме только 2 дня, я повторял за тобой ,но у меня ничего не получилось на моменте с pointer scan. Можешь поподробнее рассказать что делать? Я пробовал с r_farz.
 
Начинающий
Статус
Оффлайн
Регистрация
12 Мар 2025
Сообщения
74
Реакции[?]
8
Поинты[?]
8K
Слушай я ничего не понял ибо на этом форуме только 2 дня, я повторял за тобой ,но у меня ничего не получилось на моменте с pointer scan. Можешь поподробнее рассказать что делать? Я пробовал с r_farz.
Смори когда ты находишь условно адрес r_farz делаешь по нему Pointerscan (правая кнопка мыши Pointerscan of this address) и дальше параметры тебе менять ненадо , нажимаешь ок или там скан не помню и ждешь пока зеленный прогрессбар не завершится . Потом ты увидешь все результаты, и у тебя с верху будет Offset 0, Offset 1, Offset 2 и т.д ты должен кликнуть по самому последнему чтобы отфильтровать по убыванию. Ты должен взять адрес который имеет лишь одно смещение. Пример:

client.dll+12345 - 0x2C (Одно смещение)
client.dll+12abc - 0x5C - 0x10 (Два смещения)
тебе нужен тот который будет с одним смещением и кликнуть дважды чтобы он добавился в список адресов.
Дважды кликнув на него там ты увидешь модуль (client.dll) и смещение относительно модуля (client.dll + 0x123abc) и сверху таблицу смещений, но так как мы взяли адреса с одним смещением там будет одно смещение. И его ты будешь использовать в коде.


Будешь брать базовый адрес модуля и к нему прибавлять смещение относительно модуля, а после уже добавишь третье смещение
 
Начинающий
Статус
Оффлайн
Регистрация
11 Мар 2024
Сообщения
3
Реакции[?]
0
Поинты[?]
0
Спасибо за помощь у меня был ещё 1 вопрос. Вот fog_enable 1 там как найти если значений очень много, я пробовал ,но ничего не получилось.
 
Начинающий
Статус
Оффлайн
Регистрация
11 Мар 2024
Сообщения
3
Реакции[?]
0
Поинты[?]
0
Смори когда ты находишь условно адрес r_farz делаешь по нему Pointerscan (правая кнопка мыши Pointerscan of this address) и дальше параметры тебе менять ненадо , нажимаешь ок или там скан не помню и ждешь пока зеленный прогрессбар не завершится . Потом ты увидешь все результаты, и у тебя с верху будет Offset 0, Offset 1, Offset 2 и т.д ты должен кликнуть по самому последнему чтобы отфильтровать по убыванию. Ты должен взять адрес который имеет лишь одно смещение. Пример:

client.dll+12345 - 0x2C (Одно смещение)
client.dll+12abc - 0x5C - 0x10 (Два смещения)
тебе нужен тот который будет с одним смещением и кликнуть дважды чтобы он добавился в список адресов.
Дважды кликнув на него там ты увидешь модуль (client.dll) и смещение относительно модуля (client.dll + 0x123abc) и сверху таблицу смещений, но так как мы взяли адреса с одним смещением там будет одно смещение. И его ты будешь использовать в коде.


Будешь брать базовый адрес модуля и к нему прибавлять смещение относительно модуля, а после уже добавишь третье смещение
Ещё я не понял с ресканом я сделал его но ничего не поменялось у меня "client.dll"+051C9880 40 там ниже так же только Offset 0 меняется
2 раза нажимаю открываю адресс и там 40 просто
 
Начинающий
Статус
Оффлайн
Регистрация
12 Мар 2025
Сообщения
74
Реакции[?]
8
Поинты[?]
8K
Спасибо за помощь у меня был ещё 1 вопрос. Вот fog_enable 1 там как найти если значений очень много, я пробовал ,но ничего не получилось.
fog_enable байт (0/1) по этому да у тебя будет очень много совпадений так то, ну если будешь отсекать достаточно долго то получиться и да еще советую отсекать со способом "Unchanged Value", допустим ты ставишь fog_enable 0, пишешь в CE 0 делаешь next scan но следующий ты делаешь не 4 bytes а unchanged value, то есть не измененное значение и 5-10 раз нажимаешь на него таким образом отсечешь множество ненужных адресов.
Ещё я не понял с ресканом я сделал его но ничего не поменялось у меня "client.dll"+051C9880 40 там ниже так же только Offset 0 меняется
2 раза нажимаю открываю адресс и там 40 просто
Ещё я не понял с ресканом я сделал его но ничего не поменялось у меня "client.dll"+051C9880 40 там ниже так же только Offset 0 меняется
2 раза нажимаю открываю адресс и там 40 просто
Если некоторые адреса те же это +, бери один из этих адресов и используй, если стоит client.dll+051c9880 40 то бери его , в pointerscan не обязательно должно быть один адрес, бери первый или любой другой но только client.dll, добавь в список адресов и попробуй изменить значение и если в игре тоже оно меняется тогда ты его смело можешь использовать (ну да конечно же ты должен быть уверен что он статичен)
 
Zodiak 1love
Пользователь
Статус
Оффлайн
Регистрация
19 Авг 2019
Сообщения
279
Реакции[?]
35
Поинты[?]
0
Начинающий
Статус
Оффлайн
Регистрация
12 Мар 2025
Сообщения
74
Реакции[?]
8
Поинты[?]
8K
А все другое например погоду можно? Вак не забанит за запись в память?
Нет ваку вообще похуй на такую мелочь, ну насчет камеры да без хуманайзера на демке палиться но и то не факт что тебя за это забанят даже если тебя зарепортят, короче супер легит тема
 
Начинающий
Статус
Оффлайн
Регистрация
12 Мар 2025
Сообщения
17
Реакции[?]
4
Поинты[?]
4K
Камера на 1400 вполне безопасна. Максимум — по репортам на реплее можно заметить, но по овервотчу не банят.
Ну а гайд, конечно, мусорный. Всё это, кроме камеры, легко делается через конвары.
Камеру тоже супер-просто сделать. Базовые навыки читенжина.

C++:
class CCvarNode {
public:
    optional<string> name() const {
        if (auto name_ptr = Memory::read_memory<uintptr_t>(this))
            return Memory::read_string(*name_ptr);
        return nullopt;
    }

    template <typename T>
    T get() const {
        return Memory::read_memory<T>(this + 0x40);
    }

    template <typename T>
    void set(const T value) {
        Memory::write_memory<T>(this + 0x40, value);
    }
};

class CCvarNodes {
public:
    CCvarNode* node_by_index(const size_t index) const {
        return Memory::read_memory<CCvarNode*>(this + index * 8).value_or(nullptr);
    }
};

class CCvar {
public:
    CCvarNodes* node() const {
        return Memory::read_memory<CCvarNodes*>(this + 0x40).value_or(nullptr);
    }
};
 
Последнее редактирование:
Начинающий
Статус
Оффлайн
Регистрация
12 Мар 2025
Сообщения
74
Реакции[?]
8
Поинты[?]
8K
Камера на 1400 вполне безопасна. Максимум — по репортам на реплее можно заметить, но по овервотчу не банят.
Ну а гайд, конечно, мусорный. Всё это, кроме камеры, легко делается через конвары.
Камеру тоже супер-просто сделать. Базовые навыки читенжина.

C++:
class CCvarNode {
public:
    optional<string> name() const {
        if (auto name_ptr = Memory::read_memory<uintptr_t>(this))
            return Memory::read_string(*name_ptr);
        return nullopt;
    }

    template <typename T>
    T get() const {
        return Memory::read_memory<T>(this + 0x40);
    }

    template <typename T>
    void set(const T value) {
        Memory::write_memory<T>(this + 0x40, value);
    }
};

class CCvarNodes {
public:
    CCvarNode* node_by_index(const size_t index) const {
        return Memory::read_memory<CCvarNode*>(this + index * 8).value_or(nullptr);
    }
};

class CCvar {
public:
    CCvarNodes* node() const {
        return Memory::read_memory<CCvarNodes*>(this + 0x40).value_or(nullptr);
    }
};
Гайд создан для новичков, чтобы не запугивать начинающих страшным кодом и непонятными словами которые ты щас написал.
 
Начинающий
Статус
Оффлайн
Регистрация
12 Мар 2025
Сообщения
17
Реакции[?]
4
Поинты[?]
4K
Гайд создан для новичков, чтобы не запугивать начинающих страшным кодом и непонятными словами которые ты щас написал.
Так может лучше сразу учить делать по уму, а не делать "гайд" о том, как использовать читенжин?

Камера и r_farz кстати делаются по одной сиге.
C++:
    void set_distance(int distance) {
        Memory::write_memory(this, static_cast<float>(distance));
    }

    void set_r_farz(int r_farz) {
        Memory::write_memory(this + 0x14, static_cast<float>(r_farz));
    }
 
Начинающий
Статус
Оффлайн
Регистрация
23 Янв 2022
Сообщения
34
Реакции[?]
10
Поинты[?]
9K
Гайд создан для новичков, чтобы не запугивать начинающих страшным кодом и непонятными словами которые ты щас написал.
Я бы сказал так Гайд Создан чтобы люди пастили себе мини читы :roflanEbalo: не хватает еще как сделать партиклы в тумане войны
 
Сверху Снизу