├── requirements.txt
├── MANIFEST.in
├── LICENSE
├── tests
└── testing_script.py
├── setup.py
├── .gitignore
├── README.md
└── ReadWriteMemory
└── __init__.py
/requirements.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include *.txt
2 | include LICENSE
3 | recursive-include tests *.py
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Victor Santiago
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/tests/testing_script.py:
--------------------------------------------------------------------------------
1 | from ReadWriteMemory import ReadWriteMemory
2 | from random import randint
3 |
4 | rwm = ReadWriteMemory()
5 | process = rwm.get_process_by_name('ac_client.exe')
6 | process.open()
7 |
8 | print('\nPrint the Process information.')
9 | print(process.__dict__)
10 |
11 | health_pointer = process.get_pointer(0x004e4dbc, offsets=[0xf4])
12 | ammo_pointer = process.get_pointer(0x004df73c, offsets=[0x378, 0x14, 0x0])
13 | grenade_pointer = process.get_pointer(0x004df73c, offsets=[0x35c, 0x14, 0x0])
14 | print(health_pointer)
15 |
16 | health = process.read(health_pointer)
17 | ammo = process.read(ammo_pointer)
18 | grenade = process.read(grenade_pointer)
19 |
20 | print('\nPrinting the current values.')
21 | print({'Health': health, 'Ammo': ammo, 'Grenade': grenade})
22 |
23 | process.write(health_pointer, randint(1, 100))
24 | process.write(ammo_pointer, randint(1, 20))
25 | process.write(grenade_pointer, randint(1, 5))
26 |
27 | health = process.read(health_pointer)
28 | ammo = process.read(ammo_pointer)
29 | grenade = process.read(grenade_pointer)
30 |
31 | print('\nPrinting the new modified random values.')
32 | print({'Health': health, 'Ammo': ammo, 'Grenade': grenade})
33 |
34 | process.close()
35 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup
2 |
3 | with open('README.md', 'r', encoding='utf-8') as f:
4 | long_description = f.read()
5 |
6 | setup(name='ReadWriteMemory',
7 | packages=['ReadWriteMemory'],
8 | version='0.1.5',
9 | license='MIT',
10 | description='ReadWriteMemory Class to work with Windows process memory and hacking video games.',
11 | long_description=long_description,
12 | long_description_content_type='text/markdown',
13 | author='Victor M Santiago',
14 | author_email='vsantiago113sec@gmail.com',
15 | url='https://github.com/vsantiago113/ReadWriteMemory',
16 | download_url='https://github.com/vsantiago113/ReadWriteMemory/archive/0.1.5.zip',
17 | keywords=['ReadWriteMemory', 'Hacking', 'Cheat Engine'],
18 | python_requires='>=3.4.0',
19 | classifiers=[
20 | 'Development Status :: 5 - Production/Stable',
21 | 'Topic :: Utilities',
22 | 'License :: OSI Approved :: MIT License',
23 | 'Operating System :: MacOS :: MacOS X',
24 | 'Operating System :: Microsoft :: Windows',
25 | 'Operating System :: POSIX :: Linux',
26 | 'Programming Language :: Python',
27 | 'Intended Audience :: Developers',
28 | 'Programming Language :: Python :: 3.4',
29 | 'Programming Language :: Python :: 3.5',
30 | 'Programming Language :: Python :: 3.6',
31 | 'Programming Language :: Python :: 3.7',
32 | 'Programming Language :: Python :: 3.8',
33 | ],
34 | )
35 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | env/
12 | Test/build/
13 | develop-eggs/
14 | dist/
15 | downloads/
16 | eggs/
17 | .eggs/
18 | lib/
19 | lib64/
20 | parts/
21 | sdist/
22 | var/
23 | wheels/
24 | *.egg-info/
25 | .installed.cfg
26 | *.egg
27 |
28 | # PyInstaller
29 | # Usually these files are written by a python script from a template
30 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
31 | *.manifest
32 | *.spec
33 |
34 | # Installer logs
35 | pip-log.txt
36 | pip-delete-this-directory.txt
37 |
38 | # Unit test / coverage reports
39 | htmlcov/
40 | .tox/
41 | .coverage
42 | .coverage.*
43 | .cache
44 | nosetests.xml
45 | coverage.xml
46 | *.cover
47 | .hypothesis/
48 |
49 | # Translations
50 | *.mo
51 | *.pot
52 |
53 | # Django stuff:
54 | *.log
55 | local_settings.py
56 |
57 | # Flask stuff:
58 | instance/
59 | .webassets-cache
60 |
61 | # Scrapy stuff:
62 | .scrapy
63 |
64 | # Sphinx documentation
65 | docs/_build/
66 |
67 | # PyBuilder
68 | target/
69 |
70 | # Jupyter Notebook
71 | .ipynb_checkpoints
72 |
73 | # pyenv
74 | .python-version
75 |
76 | # celery beat schedule file
77 | celerybeat-schedule
78 |
79 | # SageMath parsed files
80 | *.sage.py
81 |
82 | # dotenv
83 | .env
84 |
85 | # virtualenv
86 | .venv
87 | venv/
88 | ENV/
89 |
90 | # Spyder project settings
91 | .spyderproject
92 | .spyproject
93 |
94 | # Rope project settings
95 | .ropeproject
96 |
97 | # mkdocs documentation
98 | /site
99 |
100 | # mypy
101 | .mypy_cache/
102 |
103 | # PyCharm
104 | .idea/
105 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ReadWriteMemory
2 | 
3 | 
4 | 
5 | 
6 | 
7 |
8 | ### Description
9 | The ReadWriteMemory Class is made on Python for reading and writing to the memory of any process. This Class does not depend on any extra modules and only uses standard Python libraries like ctypes.
10 |
11 | ---
12 |
13 | ### [Documentation](https://vsantiago113.github.io/readwritememory.github.io/)
14 |
15 | ---
16 |
17 | ### Requirements
18 | Python 3.4+
19 | OS: Windows 7, 8 and 10
20 |
21 | ---
22 |
23 | #### Windows API’s in this module:
24 | [EnumProcesses](https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-enumprocesses)
25 | [GetProcessImageFileName](https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-getprocessimagefilenamea)
26 | [OpenProcess](https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess)
27 | [Process Security and Access Rights](https://docs.microsoft.com/en-us/windows/win32/procthread/process-security-and-access-rights)
28 | [CloseHandle](https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle)
29 | [GetLastError](https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror)
30 | [ReadProcessMemory](https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-readprocessmemory)
31 | [WriteProcessMemory](https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-writeprocessmemory)
32 |
33 | ---
34 |
35 | ## Usage
36 |
37 | ### Import and instantiate the Class
38 | ```python
39 | from ReadWriteMemory import ReadWriteMemory
40 |
41 | rwm = ReadWriteMemory()
42 | ```
43 |
44 | ### Get a Process by name
45 | ```python
46 | from ReadWriteMemory import ReadWriteMemory
47 |
48 | rwm = ReadWriteMemory()
49 |
50 | process = rwm.get_process_by_name('ac_client.exe')
51 | ```
52 |
53 | ### Get a Process by ID
54 | ```python
55 | from ReadWriteMemory import ReadWriteMemory
56 |
57 | rwm = ReadWriteMemory()
58 |
59 | process = rwm.get_process_by_id(1337)
60 | ```
61 |
62 | ### Get Base Address of the Process
63 | ```py
64 | from ReadWriteMemory import ReadWriteMemory
65 |
66 | rwm = ReadWriteMemory()
67 |
68 | process = rwm.get_process_by_name('ac_client.exe')
69 | # Remember to open the process first in order to get the base address of the process
70 | process.open()
71 | base_address = process.get_base_address()
72 | ```
73 |
74 | ### Get the list of running processes ID's from the current system
75 | ```python
76 | from ReadWriteMemory import ReadWriteMemory
77 |
78 | rwm = ReadWriteMemory()
79 |
80 | processes_ids = rwm.enumerate_processes()
81 | ```
82 |
83 | ### Print the Process information
84 | ```python
85 | from ReadWriteMemory import ReadWriteMemory
86 |
87 | rwm = ReadWriteMemory()
88 |
89 | process = rwm.get_process_by_name('ac_client.exe')
90 | print(process.__dict__)
91 | ```
92 |
93 | ### Print the Process HELP docs
94 | ```python
95 | from ReadWriteMemory import ReadWriteMemory
96 |
97 | rwm = ReadWriteMemory()
98 |
99 | process = rwm.get_process_by_name('ac_client.exe')
100 | help(process)
101 | ```
102 |
103 | ### Exception: ReadWriteMemoryError
104 | ````python
105 | from ReadWriteMemory import ReadWriteMemory
106 | from ReadWriteMemory import ReadWriteMemoryError
107 |
108 | rwm = ReadWriteMemory()
109 | try:
110 | process = rwm.get_process_by_name('ac_client.exe')
111 | except ReadWriteMemoryError as error:
112 | print(error)
113 | ````
114 |
115 | ### Open the Process
116 | To be able to read or write to the process's memory first you need to call the open() method.
117 | ```python
118 | from ReadWriteMemory import ReadWriteMemory
119 |
120 | rwm = ReadWriteMemory()
121 |
122 | process = rwm.get_process_by_name('ac_client.exe')
123 | process.open()
124 | ```
125 |
126 | ### Set the pointers for example: to get health, ammo and grenades
127 | The offsets must be a list in the correct order, if the address does not have any offsets then just pass the address. You need to pass two arguments, first the process address as hex and a list of offsets as hex.
128 | ```python
129 | from ReadWriteMemory import ReadWriteMemory
130 |
131 | rwm = ReadWriteMemory()
132 |
133 | process = rwm.get_process_by_name('ac_client.exe')
134 | process.open()
135 |
136 | health_pointer = process.get_pointer(0x004e4dbc, offsets=[0xf4])
137 | ammo_pointer = process.get_pointer(0x004df73c, offsets=[0x378, 0x14, 0x0])
138 | grenade_pointer = process.get_pointer(0x004df73c, offsets=[0x35c, 0x14, 0x0])
139 | ```
140 |
141 | ### Read the values for the health, ammo and grenades from the Process's memory
142 | ```python
143 | from ReadWriteMemory import ReadWriteMemory
144 |
145 | rwm = ReadWriteMemory()
146 |
147 | process = rwm.get_process_by_name('ac_client.exe')
148 | process.open()
149 |
150 | health_pointer = process.get_pointer(0x004e4dbc, offsets=[0xf4])
151 | ammo_pointer = process.get_pointer(0x004df73c, offsets=[0x378, 0x14, 0x0])
152 | grenade_pointer = process.get_pointer(0x004df73c, offsets=[0x35c, 0x14, 0x0])
153 |
154 | health = process.read(health_pointer)
155 | ammo = process.read(ammo_pointer)
156 | grenade = process.read(grenade_pointer)
157 | ```
158 |
159 | ### Print the health, ammo and grenade values
160 | ```python
161 | from ReadWriteMemory import ReadWriteMemory
162 |
163 | rwm = ReadWriteMemory()
164 |
165 | process = rwm.get_process_by_name('ac_client.exe')
166 | process.open()
167 |
168 | health_pointer = process.get_pointer(0x004e4dbc, offsets=[0xf4])
169 | ammo_pointer = process.get_pointer(0x004df73c, offsets=[0x378, 0x14, 0x0])
170 | grenade_pointer = process.get_pointer(0x004df73c, offsets=[0x35c, 0x14, 0x0])
171 |
172 | health = process.read(health_pointer)
173 | ammo = process.read(ammo_pointer)
174 | grenade = process.read(grenade_pointer)
175 |
176 | print({'Health': health, 'Ammo': ammo, 'Grenade': grenade})
177 | ```
178 |
179 | ### Write some random values for health, ammo and grenade to the Process's memory
180 | ```python
181 | from ReadWriteMemory import ReadWriteMemory
182 | from random import randint
183 |
184 | rwm = ReadWriteMemory()
185 |
186 | process = rwm.get_process_by_name('ac_client.exe')
187 | process.open()
188 |
189 | health_pointer = process.get_pointer(0x004e4dbc, offsets=[0xf4])
190 | ammo_pointer = process.get_pointer(0x004df73c, offsets=[0x378, 0x14, 0x0])
191 | grenade_pointer = process.get_pointer(0x004df73c, offsets=[0x35c, 0x14, 0x0])
192 |
193 | process.write(health_pointer, randint(1, 100))
194 | process.write(ammo_pointer, randint(1, 20))
195 | process.write(grenade_pointer, randint(1, 5))
196 | ```
197 |
198 | ### Close the Process's handle when you are done using it.
199 | ```python
200 | from ReadWriteMemory import ReadWriteMemory
201 |
202 | rwm = ReadWriteMemory()
203 |
204 | process = rwm.get_process_by_name('ac_client.exe')
205 | process.open()
206 |
207 | process.close()
208 | ```
209 |
210 | ### Examples
211 | Check out the code inside the Test folder on the python file named testing_script.py.
212 | The AssaultCube game used for this test is version v1.1.0.4 If you use a different version then you will have to use CheatEngine to find the memory addresses.
213 | [https://github.com/assaultcube/AC/releases/tag/v1.1.0.4](https://github.com/assaultcube/AC/releases/tag/v1.1.0.4)
214 | For more examples check out the AssaultCube game trainer:
215 | [https://github.com/vsantiago113/ACTrainer](https://github.com/vsantiago113/ACTrainer)
216 | ```python
217 | from ReadWriteMemory import ReadWriteMemory
218 | from random import randint
219 |
220 | rwm = ReadWriteMemory()
221 | process = rwm.get_process_by_name('ac_client.exe')
222 | process.open()
223 |
224 | print('\nPrint the Process information.')
225 | print(process.__dict__)
226 |
227 | health_pointer = process.get_pointer(0x004e4dbc, offsets=[0xf4])
228 | ammo_pointer = process.get_pointer(0x004df73c, offsets=[0x378, 0x14, 0x0])
229 | grenade_pointer = process.get_pointer(0x004df73c, offsets=[0x35c, 0x14, 0x0])
230 | print(health_pointer)
231 |
232 | health = process.read(health_pointer)
233 | ammo = process.read(ammo_pointer)
234 | grenade = process.read(grenade_pointer)
235 |
236 | print('\nPrinting the current values.')
237 | print({'Health': health, 'Ammo': ammo, 'Grenade': grenade})
238 |
239 | process.write(health_pointer, randint(1, 100))
240 | process.write(ammo_pointer, randint(1, 20))
241 | process.write(grenade_pointer, randint(1, 5))
242 |
243 | health = process.read(health_pointer)
244 | ammo = process.read(ammo_pointer)
245 | grenade = process.read(grenade_pointer)
246 |
247 | print('\nPrinting the new modified random values.')
248 | print({'Health': health, 'Ammo': ammo, 'Grenade': grenade})
249 |
250 | process.close()
251 |
252 | ```
253 |
--------------------------------------------------------------------------------
/ReadWriteMemory/__init__.py:
--------------------------------------------------------------------------------
1 | from typing import Any, List
2 | import os.path
3 | import ctypes
4 | import ctypes.wintypes
5 |
6 | # Process Permissions
7 | PROCESS_QUERY_INFORMATION = 0x0400
8 | PROCESS_VM_OPERATION = 0x0008
9 | PROCESS_VM_READ = 0x0010
10 | PROCESS_VM_WRITE = 0x0020
11 | PROCESS_ALL_ACCESS = 0x1f0fff
12 |
13 | MAX_PATH = 260
14 |
15 |
16 | class ReadWriteMemoryError(Exception):
17 | pass
18 |
19 |
20 | class Process(object):
21 | """
22 | The Process class holds the information about the requested process.
23 | """
24 | def __init__(self, name: str = '', pid: int = -1, handle: int = -1, error_code: str = None):
25 | """
26 | :param name: The name of the executable file for the specified process.
27 | :param pid: The process ID.
28 | :param handle: The process handle.
29 | :param error_code: The error code from a process failure.
30 | """
31 | self.name = name
32 | self.pid = pid
33 | self.handle = handle
34 | self.error_code = error_code
35 |
36 | def __repr__(self) -> str:
37 | return f'{self.__class__.__name__}: "{self.name}"'
38 |
39 | def open(self):
40 | """
41 | Open the process with the Query, Operation, Read and Write permissions and return the process handle.
42 |
43 | :return: True if the handle exists if not return False
44 | """
45 | dw_desired_access = (PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE)
46 | b_inherit_handle = True
47 | self.handle = ctypes.windll.kernel32.OpenProcess(dw_desired_access, b_inherit_handle, self.pid)
48 | if not self.handle:
49 | raise ReadWriteMemoryError(f'Unable to open process <{self.name}>')
50 |
51 | def close(self) -> int:
52 | """
53 | Closes the handle of the process.
54 |
55 | :return: The last error code from the result after an attempt to close the handle.
56 | """
57 | ctypes.windll.kernel32.CloseHandle(self.handle)
58 | return self.get_last_error()
59 |
60 | def get_all_access_handle(self):
61 | """
62 | Gets full access handle of the process.
63 |
64 | :return: handle of the process
65 | """
66 | b_inherit_handle = True
67 | self.handle = ctypes.windll.kernel32.OpenProcess(PROCESS_ALL_ACCESS, b_inherit_handle, self.pid)
68 |
69 | @staticmethod
70 | def get_last_error() -> int:
71 | """
72 | Get the last error code.
73 |
74 | :return: The last error code.
75 | """
76 | return ctypes.windll.kernel32.GetLastError()
77 |
78 | def get_pointer(self, lp_base_address: hex, offsets: List[hex] = ()) -> int:
79 | """
80 | Get the pointer of a given address.
81 |
82 | :param lp_base_address: The address from where you want to get the pointer.
83 | :param offsets: a list of offets.
84 |
85 | :return: The pointer of a give address.
86 | """
87 | temp_address = self.read(lp_base_address)
88 | pointer = 0x0
89 | if not offsets:
90 | return lp_base_address
91 | else:
92 | for offset in offsets:
93 | pointer = int(str(temp_address), 0) + int(str(offset), 0)
94 | temp_address = self.read(pointer)
95 | return pointer
96 |
97 | def get_modules(self) -> List[int]:
98 | """
99 | Get the process's modules.
100 | :return: A list of the process's modules adresses in decimal.
101 | :return: An empty list if the process is not open.
102 | """
103 | modules = (ctypes.wintypes.HMODULE * MAX_PATH)()
104 | ctypes.windll.psapi.EnumProcessModules(self.handle, modules, ctypes.sizeof(modules), None)
105 | return [x for x in tuple(modules) if x != None]
106 |
107 | def get_base_address(self):
108 | """
109 | Get the base address of the process.
110 | :return: The base address of the process.
111 | """
112 | return self.get_modules()[0]
113 |
114 | def thread(self, address: int):
115 | """
116 | Create a remote thread to the address.
117 | If you don't know what you're doing, the process can crash.
118 | """
119 | ctypes.windll.kernel32.CreateRemoteThread(self.handle, 0, 0, address, 0, 0, 0)
120 | self.close() #the thread stays in the process
121 | self.open() #just for better code understanding
122 |
123 | def read(self, lp_base_address: int) -> Any:
124 | """
125 | Read data from the process's memory.
126 |
127 | :param lp_base_address: The process's pointer
128 |
129 | :return: The data from the process's memory if succeed if not raises an exception.
130 | """
131 | try:
132 | read_buffer = ctypes.c_uint()
133 | lp_buffer = ctypes.byref(read_buffer)
134 | n_size = ctypes.sizeof(read_buffer)
135 | lp_number_of_bytes_read = ctypes.c_ulong(0)
136 | ctypes.windll.kernel32.ReadProcessMemory(self.handle, ctypes.c_void_p(lp_base_address), lp_buffer,
137 | n_size, lp_number_of_bytes_read)
138 | return read_buffer.value
139 | except (BufferError, ValueError, TypeError) as error:
140 | if self.handle:
141 | self.close()
142 | self.error_code = self.get_last_error()
143 | error = {'msg': str(error), 'Handle': self.handle, 'PID': self.pid,
144 | 'Name': self.name, 'ErrorCode': self.error_code}
145 | ReadWriteMemoryError(error)
146 |
147 | def readString(self, lp_base_address: int, length: int) -> Any:
148 | """
149 | Read data from the process's memory.
150 |
151 | :param lp_base_address: The process's pointer
152 | :param length: The length of string
153 |
154 | :return: The data from the process's memory if succeed if not raises an exception.
155 | """
156 | try:
157 | read_buffer = ctypes.create_string_buffer(length)
158 | lp_number_of_bytes_read = ctypes.c_ulong(0)
159 | ctypes.windll.kernel32.ReadProcessMemory(self.handle, lp_base_address, read_buffer, length, lp_number_of_bytes_read)
160 | bufferArray = bytearray(read_buffer)
161 | found_terminator = bufferArray.find(b'\x00')
162 | if found_terminator != -1:
163 | return bufferArray[:found_terminator].decode('utf-8')
164 | print("[ReadMemory/Error]: terminator not found.\naddress: %s" % hex(lp_base_address))
165 | return ""
166 | except (BufferError, ValueError, TypeError) as error:
167 | if self.handle:
168 | self.close()
169 | self.error_code = self.get_last_error()
170 | error = {'msg': str(error), 'Handle': self.handle, 'PID': self.pid,
171 | 'Name': self.name, 'ErrorCode': self.error_code}
172 | ReadWriteMemoryError(error)
173 |
174 | def readByte(self, lp_base_address: int, length: int = 1) -> List[hex]:
175 | """
176 | Read data from the process's memory.
177 | :param lp_base_address: The process's pointer {don't use offsets}
178 | :param length: The length of the bytes to read
179 | :return: The data from the process's memory if succeed if not raises an exception.
180 | """
181 | try:
182 | read_buffer = ctypes.c_ubyte()
183 | lp_buffer = ctypes.byref(read_buffer)
184 | n_size = ctypes.sizeof(read_buffer)
185 | lp_number_of_bytes_read = ctypes.c_ulong(0)
186 | return [hex(read_buffer.value) for x in range(length) if ctypes.windll.kernel32.ReadProcessMemory(self.handle, ctypes.c_void_p(lp_base_address + x), lp_buffer, n_size, lp_number_of_bytes_read)]
187 |
188 | except (BufferError, ValueError, TypeError) as error:
189 | if self.handle:
190 | self.close()
191 | self.error_code = self.get_last_error()
192 | error = {'msg': str(error), 'Handle': self.handle, 'PID': self.pid,
193 | 'Name': self.name, 'ErrorCode': self.error_code}
194 | ReadWriteMemoryError(error)
195 |
196 | def readDouble(self, lp_base_address: int) -> Any:
197 | """
198 | Read data from the process's memory.
199 |
200 | :param lp_base_address: The process's pointer
201 |
202 | :return: The data from the process's memory if succeed if not raises an exception.
203 | """
204 | try:
205 | read_buffer = ctypes.c_double()
206 | lp_buffer = ctypes.byref(read_buffer)
207 | n_size = ctypes.sizeof(read_buffer)
208 | lp_number_of_bytes_read = ctypes.c_ulong(0)
209 | ctypes.windll.kernel32.ReadProcessMemory(self.handle, ctypes.c_void_p(lp_base_address), lp_buffer,
210 | n_size, lp_number_of_bytes_read)
211 | return read_buffer.value
212 | except (BufferError, ValueError, TypeError) as error:
213 | if self.handle:
214 | self.close()
215 | self.error_code = self.get_last_error()
216 | error = {'msg': str(error), 'Handle': self.handle, 'PID': self.pid,
217 | 'Name': self.name, 'ErrorCode': self.error_code}
218 | ReadWriteMemoryError(error)
219 |
220 | def write(self, lp_base_address: int, value: int) -> bool:
221 | """
222 | Write data to the process's memory.
223 |
224 | :param lp_base_address: The process' pointer.
225 | :param value: The data to be written to the process's memory
226 |
227 | :return: It returns True if succeed if not it raises an exception.
228 | """
229 | try:
230 | write_buffer = ctypes.c_uint(value)
231 | lp_buffer = ctypes.byref(write_buffer)
232 | n_size = ctypes.sizeof(write_buffer)
233 | lp_number_of_bytes_written = ctypes.c_ulong(0)
234 | ctypes.windll.kernel32.WriteProcessMemory(self.handle, ctypes.c_void_p(lp_base_address), lp_buffer,
235 | n_size, lp_number_of_bytes_written)
236 | return True
237 | except (BufferError, ValueError, TypeError) as error:
238 | if self.handle:
239 | self.close()
240 | self.error_code = self.get_last_error()
241 | error = {'msg': str(error), 'Handle': self.handle, 'PID': self.pid,
242 | 'Name': self.name, 'ErrorCode': self.error_code}
243 | ReadWriteMemoryError(error)
244 |
245 | def writeDouble(self, lp_base_address: int, value: float) -> bool:
246 | """
247 | Write data to the process's memory.
248 |
249 | :param lp_base_address: The process' pointer.
250 | :param value: The data to be written to the process's memory
251 |
252 | :return: It returns True if succeed if not it raises an exception.
253 | """
254 | try:
255 | write_buffer = ctypes.c_double(value)
256 | lp_buffer = ctypes.byref(write_buffer)
257 | n_size = ctypes.sizeof(write_buffer)
258 | lp_number_of_bytes_written = ctypes.c_ulong(0)
259 | ctypes.windll.kernel32.WriteProcessMemory(self.handle, ctypes.c_void_p(lp_base_address), lp_buffer,
260 | n_size, lp_number_of_bytes_written)
261 | return True
262 | except (BufferError, ValueError, TypeError) as error:
263 | if self.handle:
264 | self.close()
265 | self.error_code = self.get_last_error()
266 | error = {'msg': str(error), 'Handle': self.handle, 'PID': self.pid,
267 | 'Name': self.name, 'ErrorCode': self.error_code}
268 | ReadWriteMemoryError(error)
269 |
270 | def writeString(self, lp_base_address: int, string: str) -> bool:
271 | """
272 | Write data to the process's memory.
273 |
274 | :param lp_base_address: The process' pointer.
275 | :param string: The string to be written to the process's memory
276 |
277 | :return: It returns True if succeed if not it raises an exception.
278 | """
279 | try:
280 | write_buffer = ctypes.create_string_buffer(string.encode())
281 | lp_buffer = ctypes.byref(write_buffer)
282 | n_size = ctypes.sizeof(write_buffer)
283 | lp_number_of_bytes_written = ctypes.c_size_t()
284 | ctypes.windll.kernel32.WriteProcessMemory(self.handle, lp_base_address, lp_buffer,
285 | n_size, lp_number_of_bytes_written)
286 | return True
287 | except (BufferError, ValueError, TypeError) as error:
288 | if self.handle:
289 | self.close()
290 | self.error_code = self.get_last_error()
291 | error = {'msg': str(error), 'Handle': self.handle, 'PID': self.pid,
292 | 'Name': self.name, 'ErrorCode': self.error_code}
293 | ReadWriteMemoryError(error)
294 |
295 | def writeByte(self, lp_base_address: int, bytes: List[hex]) -> bool:
296 | """
297 | Write data to the process's memory.
298 | :param lp_base_address: The process' pointer {don't use offsets}.
299 | :param bytes: The byte(s) to be written to the process's memory
300 | :return: It returns True if succeed if not it raises an exception.
301 | """
302 | try:
303 | for x in range(len(bytes)):
304 | write_buffer = ctypes.c_ubyte(bytes[x])
305 | lp_buffer = ctypes.byref(write_buffer)
306 | n_size = ctypes.sizeof(write_buffer)
307 | lp_number_of_bytes_written = ctypes.c_ulong(0)
308 | ctypes.windll.kernel32.WriteProcessMemory(self.handle, ctypes.c_void_p(lp_base_address + x), lp_buffer,
309 | n_size, lp_number_of_bytes_written)
310 | return True
311 | except (BufferError, ValueError, TypeError) as error:
312 | if self.handle:
313 | self.close()
314 | self.error_code = self.get_last_error()
315 | error = {'msg': str(error), 'Handle': self.handle, 'PID': self.pid,
316 | 'Name': self.name, 'ErrorCode': self.error_code}
317 | ReadWriteMemoryError(error)
318 |
319 | class ReadWriteMemory:
320 | """
321 | The ReadWriteMemory Class is used to read and write to the memory of a running process.
322 | """
323 | def __init__(self):
324 | self.process = Process()
325 |
326 | @staticmethod
327 | def set_privileges():
328 | import win32con
329 | import win32api
330 | import win32security
331 | import ntsecuritycon
332 | from ntsecuritycon import TokenPrivileges
333 |
334 | remote_server = None
335 | token = win32security.OpenProcessToken(win32api.GetCurrentProcess(), win32con.TOKEN_ADJUST_PRIVILEGES | win32con.TOKEN_QUERY)
336 | win32security.AdjustTokenPrivileges(token, False, ((p[0], 2) if p[0] == win32security.LookupPrivilegeValue(remote_server, "SeBackupPrivilege") or p[0] == win32security.LookupPrivilegeValue(remote_server, "SeDebugPrivilege") or p[0] == win32security.LookupPrivilegeValue(remote_server, "SeSecurityPrivilege") else (p[0], p[1]) for p in win32security.GetTokenInformation(token, TokenPrivileges)))
337 |
338 | def get_process_by_name(self, process_name: str) -> "Process":
339 | """
340 | :description: Get the process by the process executabe\'s name and return a Process object.
341 |
342 | :param process_name: The name of the executable file for the specified process for example, my_program.exe.
343 |
344 | :return: A Process object containing the information from the requested Process.
345 | """
346 | if not process_name.endswith('.exe'):
347 | self.process.name = process_name + '.exe'
348 |
349 | process_ids = self.enumerate_processes()
350 |
351 | for process_id in process_ids:
352 | self.process.handle = ctypes.windll.kernel32.OpenProcess(PROCESS_QUERY_INFORMATION, False, process_id)
353 | if self.process.handle:
354 | image_file_name = (ctypes.c_char * MAX_PATH)()
355 | if ctypes.windll.psapi.GetProcessImageFileNameA(self.process.handle, image_file_name, MAX_PATH) > 0:
356 | filename = os.path.basename(image_file_name.value)
357 | if filename.decode('utf-8') == process_name:
358 | self.process.pid = process_id
359 | self.process.name = process_name
360 | return self.process
361 | self.process.close()
362 |
363 | raise ReadWriteMemoryError(f'Process "{self.process.name}" not found!')
364 |
365 | def get_process_by_id(self, process_id: int) -> "Process":
366 | """
367 | :description: Get the process by the process ID and return a Process object.
368 |
369 | :param process_id: The process ID.
370 |
371 | :return: A Process object containing the information from the requested Process.
372 | """
373 |
374 | self.process.handle = ctypes.windll.kernel32.OpenProcess(PROCESS_QUERY_INFORMATION, False, process_id)
375 | if self.process.handle:
376 | image_file_name = (ctypes.c_char * MAX_PATH)()
377 | if ctypes.windll.psapi.GetProcessImageFileNameA(self.process.handle, image_file_name, MAX_PATH) > 0:
378 | filename = os.path.basename(image_file_name.value)
379 | self.process.pid = process_id
380 | self.process.name = filename.decode('utf-8')
381 | self.process.close()
382 | return self.process
383 | else:
384 | raise ReadWriteMemoryError(f'Unable to get the executable\'s name for PID={self.process.pid}!')
385 |
386 | raise ReadWriteMemoryError(f'Process "{self.process.pid}" not found!')
387 |
388 | @staticmethod
389 | def enumerate_processes() -> list:
390 | """
391 | Get the list of running processes ID's from the current system.
392 |
393 | :return: A list of processes ID's
394 | """
395 | count = 32
396 | while True:
397 | process_ids = (ctypes.wintypes.DWORD * count)()
398 | cb = ctypes.sizeof(process_ids)
399 | bytes_returned = ctypes.wintypes.DWORD()
400 | if ctypes.windll.Psapi.EnumProcesses(ctypes.byref(process_ids), cb, ctypes.byref(bytes_returned)):
401 | if bytes_returned.value < cb:
402 | return list(set(process_ids))
403 | else:
404 | count *= 2
405 |
--------------------------------------------------------------------------------