├── README.md └── pymemoryapi.py /README.md: -------------------------------------------------------------------------------- 1 | # pymemoryapi 2 | Простая быстрая библиотека для работы с оперативной памятью процесса. 3 | 4 | ### Примечание 5 | Библиотека содержит самые базовые и необходимые для работы с память функции и классы, такие как: чтение и запись памяти, сканер паттернов, получение информации о модулях процесса и трамплин хуки. Библиотека не имеет внешних зависимостей, для её подключения достаточно стандарных модулей Python, работает сильно быстрее Pymem. 6 | 7 | ### Требования 8 | Python ^3.8
9 | ОС: Windows 7, 8, 10 и 11 (x64) 10 | 11 | ### Установка 12 | ```python 13 | pip install pymemoryapi 14 | ``` 15 | 16 | ### Примеры 17 | Подключение к процессу и получение информации о нем 18 | ```python 19 | import pymemoryapi 20 | 21 | terraria_process = pymemoryapi.Process(process_name="Terraria.exe") 22 | print(f"handle: {terraria_process.handle}") 23 | print(f"name: {terraria_process.name}") 24 | print(f"pid: {terraria_process.pid}\n") 25 | 26 | discord_process = pymemoryapi.Process(pid=11064) 27 | print(f"handle: {discord_process.handle}") 28 | print(f"name: {discord_process.name}") 29 | print(f"pid: {discord_process.pid}") 30 | 31 | ``` 32 | ![Alt Image](https://media.discordapp.net/attachments/770327730570133524/999818711030562976/unknown.png) 33 | 34 | Чтение и запись памяти процесса 35 | ```python 36 | import pymemoryapi 37 | 38 | process = pymemoryapi.Process(process_name="notepad++.exe") 39 | 40 | some_address = 0x06B7D5A8 41 | print(f"address value: {process.read_float(some_address)}") 42 | process.write_float(some_address, 1337.228) 43 | print(f"new address value: {process.read_float(some_address)}") 44 | 45 | ``` 46 | ![Alt Image](https://cdn.discordapp.com/attachments/770327730570133524/999824405347713034/unknown.png) 47 | ![Alt Image](https://media.discordapp.net/attachments/770327730570133524/999824443176124456/unknown.png) 48 | 49 | ![Alt Image](https://media.discordapp.net/attachments/770327730570133524/999825134632304680/unknown.png) 50 | 51 | Сканер паттернов 52 | ```python 53 | from time import time 54 | import pymemoryapi 55 | 56 | process = pymemoryapi.Process(process_name="notepad++.exe") 57 | 58 | # ?? - случайный байт 59 | # Если нужно вернуть первый попавшийся адрес можно передать доп. аргумент - return_first_found = True 60 | # метод raw_patter_scan(start_address, end_address, pattern) делает тоже самое, только работает с rb'байты', а не с b'байты' 61 | start_time = time() 62 | addresses = process.pattern_scan(0, 0x100000000, "00 00 A0 ?? 80 EF") 63 | stop_time = time() 64 | 65 | for address in addresses: 66 | print('found address:', hex(address)) 67 | 68 | print(f'pattern scan time: {start_time - start_time} sec.') 69 | 70 | ``` 71 | ![Alt Image](https://cdn.discordapp.com/attachments/770327730570133524/999831073750003753/unknown.png) 72 | ![Alt Image](https://cdn.discordapp.com/attachments/770327730570133524/999831231808143450/unknown.png) 73 | 74 | Получение информации о модулях процесса 75 | ```python 76 | import pymemoryapi 77 | 78 | process = pymemoryapi.Process(process_name="notepad++.exe") 79 | kernel_module = process.get_module_info("KERNEL32.dll") 80 | 81 | print(f"KERNEL32.dll BaseAddress: {hex(kernel_module.BaseAddress)}") 82 | print(f"KERNEL32.dll SizeOfImage: {hex(kernel_module.SizeOfImage)}") 83 | print(f"KERNEL32.dll EntryPoint: {hex(kernel_module.EntryPoint)}") 84 | 85 | ``` 86 | ![Alt Image](https://cdn.discordapp.com/attachments/770327730570133524/999823378401738772/unknown.png) 87 | 88 | Трамплин хуки 89 | ```python 90 | # Бесконечные хп в Terraria 91 | 92 | import pymemoryapi 93 | 94 | process = pymemoryapi.Process(process_name="Terraria.exe") 95 | 96 | # Ищем инструкцию, которая записывает значение хп в цикле -> fild dword ptr [esi + 000003E4] 97 | health_instruction = process.raw_pattern_scan(0, 0xF0000000, "DB 86 E4 03 00 00 D9 5D F8 D9 45 F8", return_first_found=True) 98 | 99 | # Устанавливаем трамплин хук на инструкцию, длина инструкции - не менее 7 байтов с use_x64_registers=False, не менее 12 с use_x64_registers=True 100 | # mov eax,
101 | # jmp eax 102 | # use_x64_registers = False - использование eax (представлено выше) для хранения адреса аллока, use_x64_registers = True - использование rax 103 | # Если игра не умеет обращаться с rax регисторм - используйте use_x64_registers = False 104 | health_hook = pymemoryapi.TrampolineHook(process, health_instruction, 18, 4096, use_x64_registers=False) 105 | 106 | # C7 86 E4 03 00 00 39 05 00 00 -> mov [esi + 000003E4], (int)1337 107 | # Для перевода байтов в строчный паттерн можно использовать метод pymemoryapi.heximate_bytes(bytes) 108 | health_hook.insert_bytecode("C7 86 E4 03 00 00 39 05 00 00") 109 | 110 | ``` 111 | ![Alt Image](https://media.discordapp.net/attachments/770327730570133524/1000012062447120414/before_hook.png) 112 | ![Alt Image](https://media.discordapp.net/attachments/770327730570133524/1000011528830992465/after_hook.png?width=1440&height=339) 113 | ![Alt Image](https://media.discordapp.net/attachments/770327730570133524/1000012151727067156/hook.png) 114 | 115 | Прочие функции 116 | ```python 117 | import pymemoryapi 118 | 119 | process = pymemoryapi.Process("notepad++.exe") 120 | 121 | print(pymemoryapi.list_processes_ids()) 122 | # [0, 2560, ..., 11276] 123 | print(pymemoryapi.list_processes_names()) 124 | # ['opera.exe', 'NVIDIA Share.exe', ..., 'Code.exe'] 125 | print(pymemoryapi.list_processes()) 126 | # [('opera.exe', 8704), ('NVIDIA Share.exe', 15880), ..., ('Code.exe', 18988)] 127 | print(pymemoryapi.list_modules(process.handle)) 128 | # ['notepad++.exe', 'WINTRUST.dll', ..., 'ntdll.dll'] 129 | 130 | print(pymemoryapi.heximate_bytes(pymemoryapi.mov_difference(0x02280016 - 0x27C52878))) 131 | # 9E D7 62 DA 132 | print(pymemoryapi.is_64_bit(process.handle)) 133 | # True 134 | print(pymemoryapi.table_memory(process, 0x017995DC, 8, 36)) 135 | # 17995DC | 55 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 136 | # 1799600 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 EA DA 3E 84 41 EA 00 08 80 A5 84 CF 137 | # 1799624 | 80 A5 F4 CF 80 A5 54 CE 80 A5 24 CE 80 A5 E4 CF 50 D0 B4 18 50 D0 B4 18 50 D0 B4 18 50 D0 B4 18 50 D0 B4 18 138 | # 1799648 | 50 D0 B4 18 50 D0 B4 18 50 D0 B4 18 50 D0 B4 18 50 D0 B4 18 50 D0 B4 18 50 D0 B4 18 50 D0 B4 18 50 D0 B4 18 139 | # 179966C | 50 D0 B4 18 50 D0 B4 18 50 D0 B4 18 50 D0 B4 18 50 D0 B4 18 50 D0 B4 18 50 D0 B4 18 50 D0 B4 18 50 D0 B4 18 140 | # 1799690 | 50 D0 B4 18 50 D0 B4 18 50 D0 B4 18 50 D0 B4 18 DC DA 3E B2 5F EA 00 08 41 63 4D 67 03 00 00 00 00 00 00 00 141 | # 17996B4 | 5C 00 57 00 49 00 4E 00 00 00 AC 01 60 D6 15 76 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 142 | # 17996D8 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 143 | 144 | print(hex(pymemoryapi.virtual_alloc_ex(process.handle, 4096))) 145 | # 0x15d0000 146 | memory_info = pymemoryapi.virtual_query_ex(process.handle, 0x15d0000) 147 | # Возвращает структуру 148 | # class MEMORY_BASIC_INFORMATION(ctypes.Structure): 149 | 150 | # _fields_ = [ 151 | # ("BaseAddress", ctypes.c_ulonglong), 152 | # ("AllocationBase", ctypes.c_ulonglong), 153 | # ("AllocationProtect", ctypes.c_ulong), 154 | # ("align1", ctypes.c_ulong), 155 | # ("RegionSize", ctypes.c_ulonglong), 156 | # ("State", ctypes.c_ulong), 157 | # ("Protect", ctypes.c_ulong), 158 | # ("Type", ctypes.c_ulong), 159 | # ("align2", ctypes.c_ulong), 160 | # ] 161 | print(hex(memory_info.BaseAddress)) 162 | print(memory_info.RegionSize) 163 | # 0x15d0000 164 | # 4096 165 | 166 | ``` 167 | ### Сравнение производительности с Pymem 168 | ```python 169 | import pymemoryapi 170 | import pymem 171 | import time 172 | 173 | pymemoryapi_process = pymemoryapi.Process(process_name="Notepad++.exe") 174 | pymem_process = pymem.Pymem("Notepad++.exe") 175 | 176 | address = 0x06F667DC 177 | 178 | # Чтение (3кк итераций) 179 | start = time.time() 180 | for _ in range(3000000): 181 | pymemoryapi_process.read_float(address) 182 | stop = time.time() 183 | print(f'pymemoryapi reading: {stop - start} sec') 184 | 185 | start = time.time() 186 | for _ in range(3000000): 187 | pymem_process.read_float(address) 188 | stop = time.time() 189 | print(f'pymem reading: {stop - start} sec\n') 190 | 191 | # Запись (100к итераций) 192 | start = time.time() 193 | for _ in range(100000): 194 | pymemoryapi_process.write_float(address, 20.0) 195 | stop = time.time() 196 | print(f'pymemoryapi writing: {stop - start} sec') 197 | 198 | start = time.time() 199 | for _ in range(100000): 200 | pymem_process.write_float(address, 20.0) 201 | stop = time.time() 202 | print(f'pymem writing: {stop - start} sec\n') 203 | 204 | # Сканер паттернов 205 | start = time.time() 206 | pymemoryapi_process.pattern_scan(0, 0x90000000, "00 00 A0 41 00 00 00 00 D5 FF") 207 | stop = time.time() 208 | print(f'pymemoryapi pattern scanning: {stop - start} sec') 209 | 210 | start = time.time() 211 | scan_region = 0 212 | while scan_region < 0x90000000: 213 | scan_region, founded_addresses = pymem.pattern.scan_pattern_page(pymem_process.process_handle, scan_region, b'\x00\x00\xA0\x41\x00\x00\x00\x00\xd5\xff') 214 | stop = time.time() 215 | print(f'pymem pattern scanning: {stop - start} sec') 216 | 217 | ``` 218 | ![Alt Image](https://media.discordapp.net/attachments/770327730570133524/1000025656211537970/unknown.png) 219 | -------------------------------------------------------------------------------- /pymemoryapi.py: -------------------------------------------------------------------------------- 1 | from ctypes.wintypes import * 2 | from typing import Any 3 | import struct 4 | import ctypes 5 | import locale 6 | import os 7 | import re 8 | 9 | # Coded by Xenely 10 | 11 | # WinAPI флаги 12 | PROCESS_ALL_ACCESS = 0x001F0FFF 13 | PROCESS_VM_OPERATION = 0x0008 14 | LIST_MODULES_ALL = 0x003 15 | PROCESS_VM_READ = 0x0010 16 | PROCESS_VM_WRITE = 0x0020 17 | 18 | MEM_COMMIT = 0x00001000 19 | 20 | PAGE_EXECUTE_READ = 0x0020 21 | PAGE_EXECUTE_READWRITE = 0x0040 22 | PAGE_READONLY = 0x002 23 | PAGE_READWRITE = 0x004 24 | 25 | MAX_PATH = 260 26 | 27 | # WinAPI обозначения типов 28 | ctypes.windll.kernel32.OpenProcess.restype = HANDLE 29 | ctypes.windll.kernel32.VirtualAllocEx.restype = LPVOID 30 | 31 | 32 | def heximate_bytes(bytearray: bytes) -> str: 33 | r"""Возвращает массив байтов в ввиде строчного паттерна в стиле Cheat Engine. 34 | 35 | Return byte array as a string pattern like in Cheat Engine. 36 | 37 | >>> from pymemoryapi import * 38 | >>> heximate_bytes(b"\xBA\xFF\x05\xCC") 39 | "BA FF 05 CC" 40 | """ 41 | 42 | heximate_bytes = bytearray.hex() 43 | return (" ".join([heximate_bytes[i: i + 2] for i in range(0, len(heximate_bytes), 2)])).strip().upper() 44 | 45 | 46 | def mov_difference(offset: int) -> bytes: 47 | r"""Возвращает ассемблерное представление mov оффсета. 48 | 49 | Return assembly bytes of mov offset. 50 | 51 | >>> from pymemoryapi import * 52 | >>> # 0x64FF000 = to_address 53 | >>> # 0x4246724 = from_address 54 | >>> mov_difference(to_add - from_add) 55 | b'\xdc\x88+\x02' 56 | """ 57 | 58 | hex_offset = '' 59 | jump_bytecode = b'' 60 | 61 | if offset < 0: 62 | hex_offset = hex(0xFFFF_FFFF_FFFF_FFFF + offset + 1)[10:] 63 | while len(hex_offset) != 8: 64 | hex_offset = '0' + hex_offset 65 | hex_offset = "".join(reversed([hex_offset[i: i + 2] for i in range(0, len(hex_offset), 2)])) 66 | 67 | else: 68 | hex_offset = hex(offset)[2:] 69 | while len(hex_offset) != 8: 70 | hex_offset = '0' + hex_offset 71 | hex_offset = "".join(reversed([hex_offset[i: i + 2] for i in range(0, len(hex_offset), 2)])) 72 | 73 | jump_bytecode += bytes.fromhex(hex_offset) 74 | return jump_bytecode 75 | 76 | 77 | def list_processes_ids() -> list: 78 | """Возвращает список ID всех активных процессов. 79 | 80 | Return ID list of all active processes. 81 | 82 | >>> from pymemoryapi import * 83 | >>> list_processes_ids() 84 | [0, 2560, 4, ..., 4088] 85 | """ 86 | 87 | quantity = 32 88 | while True: 89 | process_ids = (DWORD * quantity)() 90 | quantity_buffer = ctypes.sizeof(process_ids) 91 | bytes_returned = DWORD() 92 | if ctypes.windll.Psapi.EnumProcesses(ctypes.byref(process_ids), quantity_buffer, ctypes.byref(bytes_returned)): 93 | if bytes_returned.value < quantity_buffer: 94 | return list(set(process_ids)) 95 | else: 96 | quantity *= 2 97 | 98 | 99 | def list_processes_names() -> list: 100 | """Возвращает список имен всех активных процессов. 101 | 102 | Return name list of all active processes. 103 | 104 | >>> from pymemoryapi import * 105 | >>> list_processes_names() 106 | ['opera.exe', 'svchost.exe', 'Discord.exe', ..., 'RuntimeBroker.exe'] 107 | """ 108 | 109 | quantity = 32 110 | processes_ids_list = [] 111 | while True: 112 | process_ids = (DWORD * quantity)() 113 | quantity_buffer = ctypes.sizeof(process_ids) 114 | bytes_returned = DWORD() 115 | if ctypes.windll.Psapi.EnumProcesses(ctypes.byref(process_ids), quantity_buffer, ctypes.byref(bytes_returned)): 116 | if bytes_returned.value < quantity_buffer: 117 | processes_ids_list = list(set(process_ids)) 118 | break 119 | else: 120 | quantity *= 2 121 | 122 | processes_names = [] 123 | for process_id in processes_ids_list: 124 | image_file_name = (ctypes.c_char * MAX_PATH)() 125 | process_handle = ctypes.windll.kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, process_id) 126 | if ctypes.windll.psapi.GetProcessImageFileNameA(process_handle, image_file_name, MAX_PATH) > 0: 127 | filename = os.path.basename(image_file_name.value).decode() 128 | processes_names.append(filename) 129 | 130 | return processes_names 131 | 132 | 133 | def list_processes() -> list: 134 | """Возвращает список ID и имен всех активных процессов. 135 | 136 | Return ID's and names list of all active processes. 137 | 138 | >>> from pymemoryapi import * 139 | >>> list_processes() 140 | [('opera.exe', 20484), ('svchost.exe', 13832), ..., ('RuntimeBroker.exe', 33264)] 141 | """ 142 | 143 | quantity = 32 144 | processes_ids_list = [] 145 | while True: 146 | process_ids = (DWORD * quantity)() 147 | quantity_buffer = ctypes.sizeof(process_ids) 148 | bytes_returned = DWORD() 149 | if ctypes.windll.Psapi.EnumProcesses(ctypes.byref(process_ids), quantity_buffer, ctypes.byref(bytes_returned)): 150 | if bytes_returned.value < quantity_buffer: 151 | processes_ids_list = list(set(process_ids)) 152 | break 153 | else: 154 | quantity *= 2 155 | 156 | processes = [] 157 | for process_id in processes_ids_list: 158 | try: 159 | image_file_name = (ctypes.c_char * MAX_PATH)() 160 | process_handle = ctypes.windll.kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, process_id) 161 | if ctypes.windll.psapi.GetProcessImageFileNameA(process_handle, image_file_name, MAX_PATH) > 0: 162 | filename = os.path.basename(image_file_name.value).decode() 163 | processes.append((filename, process_id)) 164 | except Exception: 165 | pass 166 | 167 | return processes 168 | 169 | 170 | def is_64bit(process_handle: int) -> bool: 171 | """Проверяет является ли процесс 64-битным. Возращает булево значение. 172 | 173 | Check if process is 64 bit. Retun bool value. 174 | 175 | >>> from pymemoryapi import * 176 | >>> process = Process("Discord.exe") 177 | >>> is_64bit(process.handle) 178 | True 179 | """ 180 | 181 | wow_64 = ctypes.c_long() 182 | ctypes.windll.kernel32.IsWow64Process(process_handle, ctypes.byref(wow_64)) 183 | return bool(wow_64) 184 | 185 | 186 | def table_memory(process: object, address: int, rows: int, row_length: int = 24) -> None: 187 | """Строит байт таблицу как в Cheat Engine по заданому адресу. 188 | 189 | Print table of bytecode like in Cheat Engine by given address. 190 | """ 191 | 192 | try: 193 | for i in range(rows): 194 | row_bytes = process.read_bytes(address + (i * row_length), row_length) 195 | print(hex(address + (i * row_length))[2:len(hex(address + (i * row_length)))].upper() + ' | ' + heximate_bytes(row_bytes)) 196 | except Exception: 197 | MemoryAPIException("Невозможно построить таблицу по заданому адресу.") 198 | 199 | 200 | def list_modules(process_handle: int) -> list: 201 | """Возвращает список всех подключенных модулей к процессу. 202 | 203 | Return list of connected to process modules. 204 | 205 | >>> from pymemoryapi import * 206 | >>> process = Process("Discord.exe") 207 | >>> list_modules(process.handle) 208 | ['Discord.exe', 'ntdll.dll', 'KERNEL32.DLL', ..., 'wow64cpu.dll'] 209 | """ 210 | 211 | hModules = (ctypes.c_void_p * 1024)() 212 | process_module_success = ctypes.windll.psapi.EnumProcessModulesEx(process_handle, ctypes.byref(hModules), ctypes.sizeof(hModules), ctypes.byref(ctypes.c_ulong()), LIST_MODULES_ALL) 213 | if process_module_success: 214 | modules = [] 215 | hModules = iter(m for m in hModules if m) 216 | for hModule in hModules: 217 | module_info = MODULE(process_handle) 218 | ctypes.windll.psapi.GetModuleInformation(process_handle, ctypes.c_void_p(hModule), ctypes.byref(module_info), ctypes.sizeof(module_info)) 219 | modules.append(module_info.name) 220 | return modules 221 | 222 | 223 | def virtual_alloc_ex(process_handle: int, bytes_length: int) -> int: 224 | """Выделяет указаное количество байт в памяти процесса. 225 | 226 | Allocate memory in selected process by given byte length. 227 | 228 | >>> from pymemoryapi import * 229 | >>> process = Process("Discord.exe") 230 | >>> virtual_alloc_ex(process.handle, 4096) 231 | 0xdef0000 232 | """ 233 | 234 | allocation_address = ctypes.windll.kernel32.VirtualAllocEx(process_handle, 0, bytes_length, MEM_COMMIT, PAGE_EXECUTE_READWRITE) 235 | if not allocation_address: 236 | raise MemoryAPIException("Handle процесса передан неверно, или размер буфера некорректен.") 237 | else: 238 | return allocation_address 239 | 240 | 241 | def virtual_query_ex(process_handle: int, address: int) -> object: 242 | """Возвращает объект MEMORY_BASIC_INFORMATION, содержащий информацию о регионе памяти по адресу. 243 | 244 | Return MEMORY_BASIC_INFORMATION object, containing information about memory region by given address. 245 | 246 | >>> from pymemoryapi import * 247 | >>> process = Process("Notepad++.exe") 248 | >>> region_info = virtual_query_ex(process.handle, 0x80FCC8) 249 | >>> base, size = region_info.BaseAddress, region_info.RegionSize 250 | (0x80f000, 4096) 251 | """ 252 | 253 | memory_information = MEMORY_BASIC_INFORMATION() 254 | ctypes.windll.kernel32.VirtualQueryEx(process_handle, ctypes.cast(address, ctypes.c_char_p), ctypes.byref(memory_information), ctypes.sizeof(memory_information)) 255 | return memory_information 256 | 257 | 258 | class MemoryAPIException(Exception): 259 | """Базовый класс ошибок MemoryAPI. 260 | 261 | Basic MemoryAPI exception class. 262 | """ 263 | pass 264 | 265 | 266 | class MEMORY_BASIC_INFORMATION(ctypes.Structure): 267 | 268 | _fields_ = [ 269 | ("BaseAddress", ctypes.c_ulonglong), 270 | ("AllocationBase", ctypes.c_ulonglong), 271 | ("AllocationProtect", ctypes.c_ulong), 272 | ("align1", ctypes.c_ulong), 273 | ("RegionSize", ctypes.c_ulonglong), 274 | ("State", ctypes.c_ulong), 275 | ("Protect", ctypes.c_ulong), 276 | ("Type", ctypes.c_ulong), 277 | ("align2", ctypes.c_ulong), 278 | ] 279 | 280 | 281 | class MODULE(ctypes.Structure): 282 | 283 | _fields_ = [ 284 | ("BaseAddress", ctypes.c_void_p), 285 | ("SizeOfImage", ctypes.c_ulong), 286 | ("EntryPoint", ctypes.c_void_p), 287 | ] 288 | 289 | def __init__(self, handle): 290 | self.process_handle = handle 291 | 292 | @property 293 | def name(self): 294 | modname = ctypes.c_buffer(MAX_PATH) 295 | ctypes.windll.psapi.GetModuleBaseNameA(self.process_handle, ctypes.c_void_p(self.BaseAddress), modname, ctypes.sizeof(modname)) 296 | return modname.value.decode(locale.getpreferredencoding()) 297 | 298 | @property 299 | def filename(self): 300 | _filename = ctypes.c_buffer(MAX_PATH) 301 | ctypes.windll.psapi.GetModuleFileNameExA(self.process_handle, ctypes.c_void_p(self.BaseAddress), _filename, ctypes.sizeof(_filename)) 302 | return _filename.value.decode(locale.getpreferredencoding()) 303 | 304 | 305 | class TrampolineHook: 306 | """Класс cодержащий методы, необходимые для создание хуков. 307 | 308 | Class containing methods required to set hooks. 309 | 310 | >>> from pymemoryapi import * 311 | >>> process = Process(process_name="Terraria.exe") 312 | >>> health_instruction = process.raw_pattern_scan(0, 0xF0000000, "DB 86 E4 03 00 00 D9 5D F8 D9 45 F8", return_first_found=True) 313 | >>> health_hook = pymemoryapi.TrampolineHook(process, health_instruction, 18, 4096, use_x64_registers=False) 314 | """ 315 | 316 | def unhook(self) -> None: 317 | """Убирает хук и удаляет объект. 318 | 319 | Remove hook and hook object. 320 | """ 321 | 322 | self.__process.write_bytes(self.__instruction_address, self.original_code) 323 | 324 | def clear(self) -> None: 325 | """Очищает байткод, оставляя только оригинальный код. 326 | 327 | Clear bytecode, leaving only original code. 328 | """ 329 | 330 | self.hook_bytecode = self.original_code + self.__backward__jump 331 | while len(self.hook_bytecode) != self.__alloc_buffer_size: 332 | self.hook_bytecode += b'\x00' 333 | else: 334 | self.__process.write_bytes(self.__instruction_address, self.hook_bytecode) 335 | 336 | def insert_bytecode(self, bytecode: str) -> None: 337 | """Подставляет данный байткод перед оригинальным кодом. 338 | 339 | Instern given bytecode before the original code. 340 | 341 | >>> # Бесконечные хп в Terraria 342 | >>> # Infinite HP in Terraria 343 | >>> from pymemoryapi import * 344 | >>> process = Process(process_name="Terraria.exe") 345 | >>> health_instruction = process.raw_pattern_scan(0, 0xF0000000, "DB 86 E4 03 00 00 D9 5D F8 D9 45 F8", return_first_found=True) 346 | >>> health_hook = pymemoryapi.TrampolineHook(process, health_instruction, 18, 4096, use_x64_registers=False) 347 | >>> # C7 86 E4 03 00 00 39 05 00 00 -> mov [esi + 000003E4], (int)1337 348 | >>> health_hook.insert_bytecode("C7 86 E4 03 00 00 39 05 00 00") 349 | 350 | """ 351 | insert_bytecode = bytecode.strip().split(" ") 352 | insert_bytecode = bytes.fromhex("".join(insert_bytecode)) 353 | self.hook_bytecode = insert_bytecode + self.original_code + self.__backward__jump 354 | self.__process.write_bytes(self.alloc, self.hook_bytecode) 355 | 356 | def __init__(self, process: object, instruction_address: int, instruction_lenght: int, alloc_buffer_size: int, use_x64_registers: bool = True) -> None: 357 | 358 | if use_x64_registers and instruction_lenght < 12: 359 | raise MemoryAPIException("Длина инструкции должна быть не менее 12.") 360 | elif not use_x64_registers and instruction_lenght < 7: 361 | raise MemoryAPIException("Длина инструкции должна быть не менее 7.") 362 | 363 | self.__process = process 364 | self.__instruction_address = instruction_address 365 | self.__alloc_buffer_size = alloc_buffer_size 366 | self.__use_x64 = use_x64_registers 367 | 368 | self.hook_bytecode = b"" 369 | 370 | self.alloc = virtual_alloc_ex(process.handle, alloc_buffer_size) 371 | 372 | self.original_code = process.read_bytes(instruction_address, instruction_lenght) 373 | self.hook_bytecode += self.original_code 374 | 375 | if self.__use_x64: 376 | 377 | self.__backward = hex(instruction_address + instruction_lenght)[2:] 378 | while len(self.__backward) != 16: 379 | self.__backward = '0' + self.__backward 380 | 381 | self.__backward = "".join(reversed([self.__backward[i: i + 2] for i in range(0, len(self.__backward), 2)])) 382 | # 48 B8 -> mov rax, address 383 | # FF E0 - jmp rax 384 | self.__backward__jump = b'\x48\xb8' + bytes.fromhex(self.__backward) + b'\xff\xe0' 385 | self.hook_bytecode += self.__backward__jump 386 | 387 | self.__forward = hex(self.alloc)[2:] 388 | while len(self.__forward) != 16: 389 | self.__forward = '0' + self.__forward 390 | 391 | self.__forward = "".join(reversed([self.__forward[i: i + 2] for i in range(0, len(self.__forward), 2)])) 392 | # 48 B8 -> mov rax, address 393 | # FF E0 - jmp rax 394 | self.__forward_jump = b'\x48\xb8' + bytes.fromhex(self.__forward) + b'\xff\xe0' 395 | 396 | while len(self.__forward_jump) != instruction_lenght: 397 | self.__forward_jump += b'\x90' 398 | 399 | process.write_bytes(self.alloc, self.hook_bytecode) 400 | process.write_bytes(instruction_address, self.__forward_jump) 401 | 402 | else: 403 | 404 | self.__backward = hex(instruction_address + instruction_lenght)[2:] 405 | while len(self.__backward) != 8: 406 | self.__backward = '0' + self.__backward 407 | 408 | self.__backward = "".join(reversed([self.__backward[i: i + 2] for i in range(0, len(self.__backward), 2)])) 409 | # B8 -> mov eax, address 410 | # FF E0 - jmp eax 411 | self.__backward__jump = b'\xb8' + bytes.fromhex(self.__backward) + b'\xff\xe0' 412 | self.hook_bytecode += self.__backward__jump 413 | 414 | self.__forward = hex(self.alloc)[2:] 415 | while len(self.__forward) != 8: 416 | self.__forward = '0' + self.__forward 417 | 418 | self.__forward = "".join(reversed([self.__forward[i: i + 2] for i in range(0, len(self.__forward), 2)])) 419 | # B8 -> mov eax, address 420 | # FF E0 - jmp eax 421 | self.__forward_jump = b'\xb8' + bytes.fromhex(self.__forward) + b'\xff\xe0' 422 | 423 | while len(self.__forward_jump) != instruction_lenght: 424 | self.__forward_jump += b'\x90' 425 | 426 | process.write_bytes(self.alloc, self.hook_bytecode) 427 | process.write_bytes(instruction_address, self.__forward_jump) 428 | 429 | 430 | class Process: 431 | """Базовый класс MemoryAPI. Содержит методы, необходимые для работы с ОЗУ процесса. 432 | 433 | Basic MemoryAPI class, containing methods required to work with RAM of process. 434 | 435 | >>>from pymemoryapi import * 436 | >>> process = Process(process_name="Terraria.exe") 437 | >>> process = Process(pid=2204) 438 | >>> # Можно создать без аргументов 439 | >>> # Can create without args 440 | >>> process = Process() 441 | """ 442 | 443 | def __init__(self, process_name: str = None, pid: int = None) -> None: 444 | 445 | if not process_name and not pid: 446 | raise MemoryAPIException("Процесс должен быть подключен при помощи имени или ID.") 447 | 448 | # Подключение по названию процесса 449 | if process_name and not pid: 450 | 451 | introduced_process_name = process_name 452 | if not introduced_process_name.endswith('.exe'): 453 | introduced_process_name += ".exe" 454 | 455 | processes = list_processes() 456 | for process_name_iter, process_id_iter in processes: 457 | if process_name_iter.lower() == introduced_process_name.lower(): 458 | self.pid = process_id_iter 459 | self.name = process_name_iter 460 | self.handle = ctypes.windll.kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, process_id_iter) 461 | break 462 | else: 463 | raise MemoryAPIException("Процесс с данным именем не найден.") 464 | 465 | # Подключение по ID процесса 466 | if pid and not process_name: 467 | 468 | process_id = pid 469 | 470 | processes = list_processes() 471 | for process_name_iter, process_id_iter in processes: 472 | if process_id == process_id_iter: 473 | self.pid = process_id_iter 474 | self.name = process_name_iter 475 | self.handle = ctypes.windll.kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, process_id_iter) 476 | break 477 | else: 478 | raise MemoryAPIException("Процесс с данным ID не найден.") 479 | 480 | if process_name and pid: 481 | raise MemoryAPIException("Класс принимает в конструктор только process_name или только pid.") 482 | 483 | def get_module_info(self, module_name: str) -> object: 484 | """Возвращает объект MODULE, содержащий информацию о модуле. 485 | 486 | return MODULE object, containing informations aboute given module. 487 | 488 | >>> from pymemoryapi import * 489 | >>> process = Process("Discord.exe") 490 | >>> kernel_module = process.get_module_info("kernel32.dll") 491 | >>> kernel_info = (kernel_module.BaseAddress, kernel_module.SizeOfImage, kernel_module.EntryPoint) 492 | (0x757b0000, 0xf0000, 0x757c77c0) 493 | """ 494 | 495 | hModules = (ctypes.c_void_p * 1024)() 496 | process_module_success = ctypes.windll.psapi.EnumProcessModulesEx(self.handle, ctypes.byref(hModules), ctypes.sizeof(hModules), ctypes.byref(ctypes.c_ulong()), LIST_MODULES_ALL) 497 | if process_module_success: 498 | hModules = iter(i for i in hModules if i) 499 | for hModule in hModules: 500 | module_info = MODULE(self.handle) 501 | ctypes.windll.psapi.GetModuleInformation(self.handle, ctypes.c_void_p(hModule), ctypes.byref(module_info), ctypes.sizeof(module_info)) 502 | if module_name.lower() == module_info.name.lower(): 503 | return module_info 504 | 505 | raise MemoryAPIException("Модуль с указаным именем не найден.") 506 | 507 | # Сканер сигнатур 508 | def pattern_scan(self, start_address: int, end_address: int, pattern: str, return_first_found: bool = False) -> Any: 509 | """Ищет адреса с заданными байтам в заданном диапазоне. 510 | 511 | Search address with given bytes in given region. 512 | 513 | >>> # Получение списка адресов 514 | >>> # Для получения первого подходящего адреса передаем аргумент return_first_found=True 515 | >>> # В случае с одиночным поиском возвращает int или None вместо списка 516 | >>> # Getting list of addresses 517 | >>> # For getting first suitable address give extra argument return_first_found=True 518 | >>> # Return int or None if work with single address search 519 | >>> from pymemoryapi import * 520 | >>> process = Process(process_name="Notepad++.exe") 521 | >>> found_addresses = process.pattern_scan(0, 0xFFFFFF, "14 00 00 00 FF FF") 522 | [0x80fcc8, 0x80fdac] 523 | """ 524 | 525 | scan_region = start_address 526 | self.__bytes_pattern = pattern.strip().split(" ") 527 | 528 | if not pattern.count("?"): 529 | self.__bytes_pattern = bytes.fromhex("".join(self.__bytes_pattern)) 530 | else: 531 | temp_pattern = b"" 532 | for byte in self.__bytes_pattern: 533 | if byte == "??": 534 | temp_pattern += b'.' 535 | else: 536 | temp_pattern += bytes.fromhex(byte) 537 | self.__bytes_pattern = temp_pattern 538 | 539 | self.__scan_sections = [] 540 | allowed_protections = [PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_READWRITE, PAGE_READONLY] 541 | while scan_region < end_address: 542 | memory_region_information = virtual_query_ex(self.handle, scan_region) 543 | scan_region = memory_region_information.BaseAddress + memory_region_information.RegionSize 544 | if not (memory_region_information.State != MEM_COMMIT or memory_region_information.Protect not in allowed_protections): 545 | self.__scan_sections.append((memory_region_information.BaseAddress, memory_region_information.RegionSize)) 546 | 547 | addresses = [] 548 | for section in self.__scan_sections: 549 | try: 550 | self.__page = self.read_bytes(section[0], section[1]) 551 | if not return_first_found: 552 | for match in re.finditer(self.__bytes_pattern, self.__page, re.DOTALL): 553 | addresses.append(section[0] + match.span()[0]) 554 | else: 555 | for match in re.finditer(self.__bytes_pattern, self.__page, re.DOTALL): 556 | return section[0] + match.span()[0] 557 | except Exception: 558 | pass 559 | if return_first_found: 560 | return None 561 | else: 562 | return addresses 563 | 564 | def raw_pattern_scan(self, start_address: int, end_address: int, pattern: str, return_first_found: bool = False) -> Any: 565 | """Ищет адреса с заданными байтам в заданном диапазоне. Работает с rb"bytes" вместо b"bytes". 566 | 567 | Search address with given bytes in given region. Work with rb"bytes" instead of b"bytes". 568 | 569 | >>> # Получение списка адресов 570 | >>> # Для получения первого подходящего адреса передаем аргумент return_first_found=True 571 | >>> # В случае с одиночным поиском возвращает int или None вместо списка 572 | >>> # Getting list of addresses 573 | >>> # For getting first suitable address give extra argument return_first_found=True 574 | >>> # Return int or None if work with single address search 575 | >>> from pymemoryapi import * 576 | >>> process = Process(process_name="Notepad++.exe") 577 | >>> found_addresses = process.raw_pattern_scan(0, 0xFFFFFF, "14 00 00 00 FF FF") 578 | [0x80fcc8, 0x80fdac] 579 | """ 580 | 581 | scan_region = start_address 582 | 583 | self.__bytes_pattern = pattern.strip().split(" ") 584 | 585 | if not pattern.count("?"): 586 | self.__bytes_pattern = bytes.fromhex("".join(self.__bytes_pattern)) 587 | else: 588 | temp_pattern = b"" 589 | for byte in self.__bytes_pattern: 590 | if byte == "??": 591 | temp_pattern += b'.' 592 | else: 593 | temp_pattern += b'\\' + 'x'.encode() + byte.encode() 594 | self.__bytes_pattern = temp_pattern 595 | 596 | self.__scan_sections = [] 597 | allowed_protections = [PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_READWRITE, PAGE_READONLY] 598 | while scan_region < end_address: 599 | memory_region_information = virtual_query_ex(self.handle, scan_region) 600 | scan_region = memory_region_information.BaseAddress + memory_region_information.RegionSize 601 | if not (memory_region_information.State != MEM_COMMIT or memory_region_information.Protect not in allowed_protections): 602 | self.__scan_sections.append((memory_region_information.BaseAddress, memory_region_information.RegionSize)) 603 | 604 | addresses = [] 605 | for section in self.__scan_sections: 606 | try: 607 | self.__page = self.read_bytes(section[0], section[1]) 608 | if not return_first_found: 609 | for match in re.finditer(self.__bytes_pattern, self.__page, re.DOTALL): 610 | addresses.append(section[0] + match.span()[0]) 611 | else: 612 | for match in re.finditer(self.__bytes_pattern, self.__page, re.DOTALL): 613 | return section[0] + match.span()[0] 614 | except Exception: 615 | pass 616 | if return_first_found: 617 | return None 618 | else: 619 | return addresses 620 | 621 | # Чтение памяти 622 | def read_bytes(self, address: int, length: int) -> bytes: 623 | r"""Читает заданое количество байт по данному адресу. 624 | 625 | Read given length of bytes by given address. 626 | 627 | >>> from pymemoryapi import * 628 | >>> process = Process(process_name="Notepad++.exe") 629 | >>> value = process.read_bytes(0x80FCC8, 4) 630 | b'\x14\x00\x00\x00' 631 | """ 632 | 633 | buffer = ctypes.create_string_buffer(length) 634 | bytes_read = ctypes.c_size_t() 635 | ctypes.windll.kernel32.ReadProcessMemory(self.handle, ctypes.c_void_p(address), ctypes.byref(buffer), length, ctypes.byref(bytes_read)) 636 | return buffer.raw 637 | 638 | def read_short(self, address: int) -> int: 639 | """Читает 2 байта по данному адресу. 640 | 641 | Read 2 bytes by given address. 642 | 643 | >>> from pymemoryapi import * 644 | >>> process = Process(process_name="Notepad++.exe") 645 | >>> value = process.read_short(0x80FCC8) 646 | 20 647 | """ 648 | 649 | buffer = ctypes.create_string_buffer(2) 650 | bytes_read = ctypes.c_size_t() 651 | ctypes.windll.kernel32.ReadProcessMemory(self.handle, ctypes.c_void_p(address), ctypes.byref(buffer), 2, ctypes.byref(bytes_read)) 652 | return struct.unpack(' int: 655 | """Читает 2 байта по данному адресу. 656 | 657 | Read 2 bytes by given address. 658 | 659 | >>> from pymemoryapi import * 660 | >>> process = Process(process_name="Notepad++.exe") 661 | >>> value = process.read_ushort(0x80FCC8) 662 | 20 663 | """ 664 | 665 | buffer = ctypes.create_string_buffer(2) 666 | bytes_read = ctypes.c_size_t() 667 | ctypes.windll.kernel32.ReadProcessMemory(self.handle, ctypes.c_void_p(address), ctypes.byref(buffer), 2, ctypes.byref(bytes_read)) 668 | return struct.unpack(' int: 671 | """Читает 4 байта по данному адресу. 672 | 673 | Read 4 bytes by given address. 674 | 675 | >>> from pymemoryapi import * 676 | >>> process = Process(process_name="Notepad++.exe") 677 | >>> value = process.read_int(0x80FCC8) 678 | 20 679 | """ 680 | 681 | buffer = ctypes.create_string_buffer(4) 682 | bytes_read = ctypes.c_size_t() 683 | ctypes.windll.kernel32.ReadProcessMemory(self.handle, ctypes.c_void_p(address), ctypes.byref(buffer), 4, ctypes.byref(bytes_read)) 684 | return struct.unpack(' int: 687 | """Читает 4 байта по данному адресу. 688 | 689 | Read 4 bytes by given address. 690 | 691 | >>> from pymemoryapi import * 692 | >>> process = Process(process_name="Notepad++.exe") 693 | >>> value = process.read_uint(0x80FCC8) 694 | 20 695 | """ 696 | 697 | buffer = ctypes.create_string_buffer(4) 698 | bytes_read = ctypes.c_size_t() 699 | ctypes.windll.kernel32.ReadProcessMemory(self.handle, ctypes.c_void_p(address), ctypes.byref(buffer), 4, ctypes.byref(bytes_read)) 700 | return struct.unpack(' int: 703 | """Читает 4 байта по данному адресу. 704 | 705 | Read 4 bytes by given address. 706 | 707 | >>> from pymemoryapi import * 708 | >>> process = Process(process_name="Notepad++.exe") 709 | >>> value = process.read_long(0x80FCC8) 710 | 20 711 | """ 712 | 713 | buffer = ctypes.create_string_buffer(4) 714 | bytes_read = ctypes.c_size_t() 715 | ctypes.windll.kernel32.ReadProcessMemory(self.handle, ctypes.c_void_p(address), ctypes.byref(buffer), 4, ctypes.byref(bytes_read)) 716 | return struct.unpack(' int: 719 | """Читает 4 байта по данному адресу. 720 | 721 | Read 4 bytes by given address. 722 | 723 | >>> from pymemoryapi import * 724 | >>> process = Process(process_name="Notepad++.exe") 725 | >>> value = process.read_ulong(0x80FCC8) 726 | 20 727 | """ 728 | 729 | buffer = ctypes.create_string_buffer(4) 730 | bytes_read = ctypes.c_size_t() 731 | ctypes.windll.kernel32.ReadProcessMemory(self.handle, ctypes.c_void_p(address), ctypes.byref(buffer), 4, ctypes.byref(bytes_read)) 732 | return struct.unpack(' int: 735 | """Читает 8 байтов по данному адресу. 736 | 737 | Read 8 bytes by given address. 738 | 739 | >>> from pymemoryapi import * 740 | >>> process = Process(process_name="Notepad++.exe") 741 | >>> value = process.read_longlong(0x80FCC8) 742 | -4294967276 743 | """ 744 | 745 | buffer = ctypes.create_string_buffer(8) 746 | bytes_read = ctypes.c_size_t() 747 | ctypes.windll.kernel32.ReadProcessMemory(self.handle, ctypes.c_void_p(address), ctypes.byref(buffer), 8, ctypes.byref(bytes_read)) 748 | return struct.unpack(' int: 751 | """Читает 8 байтов по данному адресу. 752 | 753 | Read 8 bytes by given address. 754 | 755 | >>> from pymemoryapi import * 756 | >>> process = Process(process_name="Notepad++.exe") 757 | >>> value = process.read_ulonglong(0x80FCC8) 758 | 18446744069414584340 759 | """ 760 | 761 | buffer = ctypes.create_string_buffer(8) 762 | bytes_read = ctypes.c_size_t() 763 | ctypes.windll.kernel32.ReadProcessMemory(self.handle, ctypes.c_void_p(address), ctypes.byref(buffer), 8, ctypes.byref(bytes_read)) 764 | return struct.unpack(' float: 767 | """Читает 4 байта по данному адресу. 768 | 769 | Read 4 bytes by given address. 770 | >>> from pymemoryapi import * 771 | >>> process = Process(process_name="Notepad++.exe") 772 | >>> value = process.read_float(0xCF6928) 773 | 3.140625 774 | """ 775 | 776 | buffer = ctypes.create_string_buffer(4) 777 | bytes_read = ctypes.c_size_t() 778 | ctypes.windll.kernel32.ReadProcessMemory(self.handle, ctypes.c_void_p(address), ctypes.byref(buffer), 4, ctypes.byref(bytes_read)) 779 | return struct.unpack(' float: 782 | """Читает 8 байтов по данному адресу. 783 | 784 | Read 8 bytes by given address. 785 | 786 | >>> from pymemoryapi import * 787 | >>> process = Process(process_name="Notepad++.exe") 788 | >>> value = process.read_double(0x80F288) 789 | 10.5 790 | """ 791 | 792 | buffer = ctypes.create_string_buffer(8) 793 | bytes_read = ctypes.c_size_t() 794 | ctypes.windll.kernel32.ReadProcessMemory(self.handle, ctypes.c_void_p(address), ctypes.byref(buffer), 8, ctypes.byref(bytes_read)) 795 | return struct.unpack(' None: 799 | r"""Записывает данные байты по данному адресу. 800 | 801 | Write given length of bytes by given address. 802 | 803 | >>> from pymemoryapi import * 804 | >>> process = Process(process_name="Notepad++.exe") 805 | >>> process.write_bytes(0x3BB6490, b"\x00\x00\x00\x00\x00\x00\x39\x40") 806 | """ 807 | 808 | address_to_write = ctypes.cast(address, ctypes.c_char_p) 809 | ctypes.windll.kernel32.WriteProcessMemory(self.handle, address_to_write, value, len(value), 0x0) 810 | 811 | def write_short(self, address: int, value: int) -> None: 812 | """Записывает 2 байта по данному адресу. 813 | 814 | Write 2 bytes by given address. 815 | 816 | >>> from pymemoryapi import * 817 | >>> process = Process(process_name="Notepad++.exe") 818 | >>> process.write_short(0x3BB6490, 1337) 819 | """ 820 | 821 | address_to_write = ctypes.cast(address, ctypes.c_char_p) 822 | ctypes.windll.kernel32.WriteProcessMemory(self.handle, address_to_write, struct.pack('h', value), 2, 0x0) 823 | 824 | def write_ushort(self, address: int, value: int) -> None: 825 | """Записывает 2 байта по данному адресу. 826 | 827 | Write 2 bytes by given address. 828 | 829 | >>> from pymemoryapi import * 830 | >>> process = Process(process_name="Notepad++.exe") 831 | >>> process.write_ushort(0x3BB6490, 1337) 832 | """ 833 | 834 | address_to_write = ctypes.cast(address, ctypes.c_char_p) 835 | ctypes.windll.kernel32.WriteProcessMemory(self.handle, address_to_write, struct.pack('H', value), 2, 0x0) 836 | 837 | def write_int(self, address: int, value: int) -> None: 838 | """Записывает 4 байта по данному адресу. 839 | 840 | Write 4 bytes by given address. 841 | 842 | >>> from pymemoryapi import * 843 | >>> process = Process(process_name="Notepad++.exe") 844 | >>> process.write_int(0x3BB6490, 1337) 845 | """ 846 | 847 | address_to_write = ctypes.cast(address, ctypes.c_char_p) 848 | ctypes.windll.kernel32.WriteProcessMemory(self.handle, address_to_write, struct.pack('i', value), 4, 0x0) 849 | 850 | def write_uint(self, address: int, value: int) -> None: 851 | """Записывает 4 байта по данному адресу. 852 | 853 | Write 4 bytes by given address. 854 | 855 | >>> from pymemoryapi import * 856 | >>> process = Process(process_name="Notepad++.exe") 857 | >>> process.write_uint(0x3BB6490, 1337) 858 | """ 859 | 860 | address_to_write = ctypes.cast(address, ctypes.c_char_p) 861 | ctypes.windll.kernel32.WriteProcessMemory(self.handle, address_to_write, struct.pack('I', value), 4, 0x0) 862 | 863 | def write_long(self, address: int, value: int) -> None: 864 | """Записывает 4 байта по данному адресу. 865 | 866 | Write 4 bytes by given address. 867 | 868 | >>> from pymemoryapi import * 869 | >>> process = Process(process_name="Notepad++.exe") 870 | >>> process.write_long(0x3BB6490, 1337) 871 | """ 872 | 873 | address_to_write = ctypes.cast(address, ctypes.c_char_p) 874 | ctypes.windll.kernel32.WriteProcessMemory(self.handle, address_to_write, struct.pack('l', value), 4, 0x0) 875 | 876 | def write_ulong(self, address: int, value: int) -> None: 877 | """Записывает 4 байта по данному адресу. 878 | 879 | Write 4 bytes by given address. 880 | 881 | >>> from pymemoryapi import * 882 | >>> process = Process(process_name="Notepad++.exe") 883 | >>> process.write_ulong(0x3BB6490, 1337) 884 | """ 885 | 886 | address_to_write = ctypes.cast(address, ctypes.c_char_p) 887 | ctypes.windll.kernel32.WriteProcessMemory(self.handle, address_to_write, struct.pack('L', value), 4, 0x0) 888 | 889 | def write_longlong(self, address: int, value: int) -> None: 890 | """Записывает 8 байтов по данному адресу. 891 | 892 | Write 8 bytes by given address. 893 | 894 | >>> from pymemoryapi import * 895 | >>> process = Process(process_name="Notepad++.exe") 896 | >>> process.write_longlong(0x3BB6490, 1337) 897 | """ 898 | 899 | address_to_write = ctypes.cast(address, ctypes.c_char_p) 900 | ctypes.windll.kernel32.WriteProcessMemory(self.handle, address_to_write, struct.pack('q', value), 8, 0x0) 901 | 902 | def write_ulonglong(self, address: int, value: int) -> None: 903 | """Записывает 8 байтов по данному адресу. 904 | 905 | Write 8 bytes by given address. 906 | 907 | >>> from pymemoryapi import * 908 | >>> process = Process(process_name="Notepad++.exe") 909 | >>> process.write_ulonglong(0x3BB6490, 1337) 910 | """ 911 | 912 | address_to_write = ctypes.cast(address, ctypes.c_char_p) 913 | ctypes.windll.kernel32.WriteProcessMemory(self.handle, address_to_write, struct.pack('Q', value), 8, 0x0) 914 | 915 | def write_float(self, address: int, value: float) -> None: 916 | """Записывает 4 байта по данному адресу. 917 | 918 | Write 4 bytes by given address. 919 | 920 | >>> from pymemoryapi import * 921 | >>> process = Process(process_name="Notepad++.exe") 922 | >>> process.write_float(0x3BB6490, 3.14) 923 | """ 924 | 925 | address_to_write = ctypes.cast(address, ctypes.c_char_p) 926 | ctypes.windll.kernel32.WriteProcessMemory(self.handle, address_to_write, struct.pack('f', value), 4, 0x0) 927 | 928 | def write_double(self, address: int, value: float) -> None: 929 | """Записывает 8 байтов по данному адресу. 930 | 931 | Write 8 bytes by given address. 932 | 933 | >>> from pymemoryapi import * 934 | >>> process = Process(process_name="Notepad++.exe") 935 | >>> process.write_double(0x3BB6490, 3.14) 936 | """ 937 | 938 | address_to_write = ctypes.cast(address, ctypes.c_char_p) 939 | ctypes.windll.kernel32.WriteProcessMemory(self.handle, address_to_write, struct.pack('d', value), 8, 0x0) 940 | --------------------------------------------------------------------------------