├── 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 | 
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 | 
47 | 
48 |
49 | 
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 | 
72 | 
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 | 
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 | 
112 | 
113 | 
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 | 
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 |
--------------------------------------------------------------------------------