├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .gitmodules ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Documentation ├── AndroidMemoryToolCLIDOCS.md ├── AndroidMemoryToolExtensiveDocumentation.md └── MemoryProfilerDOCS.md ├── ERRORS.md ├── LICENSE.md ├── README.md ├── SECURITY.md ├── Tests ├── Auto-Installation-Tools.py ├── Cross-Platform-Tests-For-Tool.py ├── Some-Tools-Test-Usage.py ├── custom_profiler.py └── memory_leakage_program_for_profiler_testing.py ├── amt.py ├── androidMemoryTool ├── CommonAPI │ ├── __init__.py │ └── cross_platform_memory_profiler.py ├── LinuxAPI │ ├── AndroidMemoryTool-Tests.py │ ├── DataClasses.py │ ├── ThreadingController.py │ ├── __init__.py │ ├── additional_features.py │ ├── android_memory_tool_linux.py │ ├── libs_read_writers.py │ ├── mapping.py │ ├── search_and_readers.py │ └── search_and_writers.py ├── WindowsAPI │ ├── DataClasses.py │ ├── ThreadingController.py │ ├── __init__.py │ ├── android_memory_tool_windows.py │ └── complex_api_for_all.py ├── __init__.py ├── __main__.py ├── androidMemoryTool.py ├── cli_for_tool.py └── errors_class.py ├── assets └── android_memory_tool.jpg ├── pyproject.toml ├── setup.cfg └── setup.py /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.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 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Android-Py-Cheats-Script"] 2 | path = Android-Py-Cheats-Script 3 | url = https://github.com/Anonym0usWork1221/Android-Py-Cheats-Script 4 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | Email. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Welcome to GitHub docs contributing guide 2 | 3 | Thank you for investing your time in contributing to our project! Any contribution you make will be reflected on [docs.github.com](https://docs.github.com/en) :sparkles:. 4 | 5 | Read our [Code of Conduct](./CODE_OF_CONDUCT.md) to keep our community approachable and respectable. 6 | 7 | In this guide you will get an overview of the contribution workflow from opening an issue, creating a PR, reviewing, and merging the PR. 8 | 9 | Use the table of contents icon on the top left corner of this document to get to a specific section of this guide quickly. 10 | -------------------------------------------------------------------------------- /Documentation/AndroidMemoryToolCLIDOCS.md: -------------------------------------------------------------------------------- 1 | AndroidMemoryToolCLIDOCS 2 | ----- 3 | 4 | The Android Memory Tool CLI is a command-line interface for the Android Memory Tool. It provides various commands to interact with memory in Android applications. 5 | > Supported Platforms -> Linux, Android and Windows (Required Administer Permission) 6 | 7 | ### Usage 8 | * For Linux: 9 | ``` 10 | python3 -m androidMemoryTool [options] 11 | ``` 12 | * For Android: 13 | Execute the tool with root privileges using `sudo`: 14 | ``` 15 | sudo python3 -m androidMemoryTool [options] 16 | ``` 17 | * For Windows (Required Administer Permission CMD): 18 | ``` 19 | python -m androidMemoryTool [options] 20 | ``` 21 | 22 | * If you added the bin path of python libraries to environment variable then you can execute it directly 23 | ```` 24 | amt [options] 25 | ```` 26 | **and use sudo for android** 27 | 28 | ### Available Commands 29 | * `read_value`: Read a value from memory. 30 | * `read_write_value`: Read and write a value in memory. 31 | * `write_lib`: Write a value to a library. 32 | * `read_lib`: Read a value from a library. 33 | * `refiner_address`: Refine a list of addresses. 34 | * `get_module_base_address`: Get the base address of a module. 35 | * `raw_dump`: Dump a library as raw binary. 36 | * `find_hex_pattern`: Find a hexadecimal pattern in memory. 37 | * `find_and_replace_hex_pattern`: Find and replace a hexadecimal pattern in memory. 38 | * `dump_maps`: Dump memory maps. 39 | * `get_pid`: Return the PID of a process. 40 | * `help`: Display help information. 41 | 42 | ### Command-line Data Types 43 | Pass them with just name as given below 44 | * `DWORD` 45 | * `FLOAT` 46 | * `DOUBLE` 47 | * `WORD` 48 | * `BYTE` 49 | * `QWORD` 50 | * `XOR` 51 | * `UTF_8` 52 | * `UTF_16LE` 53 | 54 | ### Command Details 55 | You can get detailed information about each command and its usage by running: 56 | ``` 57 | python3 -m androidMemoryTool help 58 | ``` 59 | For example, to get help for the read_value command, run: 60 | ``` 61 | python3 -m androidMemoryTool help read_value 62 | ``` 63 | 64 | ### Examples 65 | * Read a value from memory: 66 | ```` 67 | python3 -m androidMemoryTool read_value 68 | ```` 69 | Replace , , , , and with the appropriate values. 70 | 71 | * Read and write a value in memory: 72 | ```` 73 | python3 -m androidMemoryTool read_write_value 74 | ```` 75 | Replace , , , , , and with the appropriate values. 76 | 77 | * Write a value to a library: 78 | ```` 79 | python3 -m androidMemoryTool write_lib 80 | ```` 81 | Replace , , , , and with the appropriate values. 82 | 83 | * Read a value from a library: 84 | ```` 85 | python3 -m androidMemoryTool read_lib [--value ] 86 | ```` 87 | Replace , , , , and with the appropriate values. 88 | 89 | * Refine a list of addresses: 90 | ```` 91 | python3 -m androidMemoryTool refiner_address 92 | ```` 93 | Replace , , , , , and with the appropriate values. 94 | 95 | * Get the base address of a module: 96 | ```` 97 | python3 -m androidMemoryTool get_module_base_address 98 | ```` 99 | Replace and with the appropriate values. 100 | 101 | * Dump a library as raw binary: 102 | ```` 103 | python3 -m androidMemoryTool raw_dump [] 104 | ```` 105 | Replace , , and with the appropriate values. The argument is optional and defaults to the current directory. 106 | 107 | * Find a hexadecimal pattern in memory: 108 | ```` 109 | python3 -m androidMemoryTool find_hex_pattern 110 | ```` 111 | Replace , , , , and with the appropriate values. 112 | 113 | * Find and replace a hexadecimal pattern in memory: 114 | ```` 115 | python3 -m androidMemoryTool find_and_replace_hex_pattern 116 | ```` 117 | Replace , , , , , and with the appropriate values. 118 | 119 | * Dump memory maps: 120 | ```` 121 | python3 -m androidMemoryTool dump_maps [--path ] 122 | ```` 123 | Replace and with the appropriate values. The argument is optional and defaults to the current directory. 124 | 125 | * Return the PID of a process: 126 | ```` 127 | python3 -m androidMemoryTool get_pid 128 | ```` 129 | Replace with the appropriate package name. 130 | 131 | ### Version 132 | To get the version of the Android Memory Tool, use the following command: 133 | ```` 134 | python3 -m androidMemoryTool -v 135 | ```` 136 | ### Help 137 | To display general help information or help for a specific command, use the help command: 138 | ```` 139 | python3 -m androidMemoryTool help [command] 140 | ```` 141 | Replace `[command]` with the desired command to get help for that command. If no command is provided, general help information will be displayed. 142 | -------------------------------------------------------------------------------- /Documentation/MemoryProfilerDOCS.md: -------------------------------------------------------------------------------- 1 | # Memory Profiler Documentation 2 | 3 | ### Introduction 4 | 5 | This documentation outlines the steps to create and use a custom memory profiler using the `androidMemoryTool` library 6 | and the `MemoryProfiler` class provided in the code. The memory profiler is designed to monitor the memory usage of a 7 | specified process and log the data to a file or display it in the console. 8 | 9 | ### Prerequisites 10 | Before using the memory profiler, make sure you have the following: 11 | * Python 3.6 or greater installed on your system. 12 | * The `androidMemoryTool` library, which contains the MemoryProfiler class. 13 | * Knowledge of the Process ID (PID) or Name of the target process you want to profile. 14 | 15 | ### Basic Usage 16 | If you want to use the MemoryProfiler class directly without the custom functions, 17 | you can do so by following these steps: 18 | 19 | * Create a MemoryProfiler instance and Start Profiling: 20 | ```python 21 | from androidMemoryTool import AndroidMemoryTool 22 | tool = AndroidMemoryTool(PKG='ac_client') 23 | profiler = tool.get_memory_profiler(logging_file_path='memory_log.txt') 24 | profiler.start_profiling(threshold_in_mb=20.0, # Look for 20mb threshold in target program 25 | verbose=True, # Show output on screen 26 | update_interval_delay=1.0, # Recheck the memory of target program for next threshold 27 | logging=True # Save logs continuously in file after given interval 28 | ) 29 | ``` 30 | 31 | ## Custom Memory Profiler 32 | ### Setting Up the Environment 33 | * Import the necessary libraries and classes: 34 | ```python 35 | from androidMemoryTool import AndroidMemoryTool 36 | from time import sleep 37 | from os import system 38 | ``` 39 | * Initialize an instance of `AndroidMemoryTool` with the package name (PKG) of the target process: 40 | ```python 41 | tool = AndroidMemoryTool(PKG="ac_client") 42 | ``` 43 | 44 | * Obtain an instance of the MemoryProfiler class from the AndroidMemoryTool instance: 45 | ```python 46 | profile_instance = tool.get_memory_profiler() 47 | ``` 48 | 49 | * Define the custom log file where memory data will be stored: 50 | ```python 51 | custom_log_file = "memory_dump.txt" 52 | ``` 53 | 54 | ### Custom Memory Logging Functions 55 | Two custom functions are provided to handle memory data logging and printing: 56 | * `custom_log_memory_data(data: list[dict])`: This function logs memory data to the specified file. 57 | * `custom_print_function(data: list[dict])`: This function prints memory data to the console. 58 | Make sure to modify these functions according to your requirements. 59 | 60 | Example: 61 | ```python 62 | def custom_log_memory_data(data: list[dict]) -> None: 63 | with open(custom_log_file, 'w') as log_file: 64 | log_file.write(f"{'Time':<10}{'Process RSS (BYTES)':<40}{'Memory Leak':<15}{'Memory Churn':<15}\n") 65 | for entry in data: 66 | log_file.write(f"{entry['time']:<10}{entry['process_memory']:<40}{entry['leak']:<15}{entry['churn']:<15}\n") 67 | 68 | 69 | def custom_print_function(data: list[dict]): 70 | if AndroidMemoryTool.get_platform() == 'Windows': 71 | system("CLS") 72 | else: 73 | system("clear") 74 | formatted_status_print_header = f"{'Time':<10}{'Process RSS (MB)':<30}{'Memory Leak':<15}{'Memory Churn':<15}\n" 75 | current_body = formatted_status_print_header 76 | for entry in data: 77 | current_body += f"{entry['time']:<10}{entry['process_memory'] / (1024 * 1024):<30}{entry['leak']:<15}" \ 78 | f"{entry['churn']:<15}\n" 79 | print(current_body) 80 | ``` 81 | 82 | ### Creating and Starting the Custom Profiler 83 | * Define a function to start the custom memory profiler: 84 | ```python 85 | def start_custom_profiler(): 86 | threshold_in_mb: float = 20.0 87 | update_interval_delay: float = 1.0 88 | while True: 89 | try: 90 | # Retrieve memory data and store it 91 | profile_instance.current_memory_data(threshold_in_mb=threshold_in_mb) 92 | profile_instance.create_data_from_files() 93 | data = profile_instance.get_current_data() 94 | 95 | # Log memory data 96 | custom_log_memory_data(data=data) 97 | 98 | # Print memory data 99 | custom_print_function(data=data) 100 | 101 | sleep(update_interval_delay) 102 | except KeyboardInterrupt: 103 | print('Closing Program. Please wait.') 104 | break 105 | ``` 106 | * Call the `start_custom_profiler` function to begin memory profiling: 107 | ```python 108 | start_custom_profiler() 109 | ``` 110 | 111 | ### Customizing Memory Profiling 112 | You can customize various aspects of the memory profiling process: 113 | * Adjust the `threshold_in_mb` variable to set the memory usage threshold for detecting memory leaks and churn. 114 | * Modify the `update_interval_delay` variable to control how often memory data is collected and logged. 115 | 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /ERRORS.md: -------------------------------------------------------------------------------- 1 | # Known Errors 2 | 3 | 1. **__ERROR [No 5]: input/output error__** 4 | Source [Detail Error](https://linuxpip.org/errno-5-input-output-error/) 5 | 6 | ```` 7 | Input/output error is a general error message that happens all the time under different situation. It indicates a problem in filesystem level, more specifically, the operating system cannot access a certain part of the disk drive (or virtual disk drive). 8 | In this article, we will explain the possible reasons why the “errno 5 input/output error” message happens and a few solutions that might help solving it. 9 | 10 | “[Errno 5] Input/output error” causes 11 | 12 | Before we get into any further, let’s make it clear that the error indicates a problem happens with the disk while the operating system is writing or reading from it. The error is specific to Linux machines. 13 | Sometimes, especially in situation where you’re running Linux as a virtual machine, the cause of “[Errno 5] Input/output error” might be related to the hypervisor. Try updating VMware or VirtualBox to see if the problem goes away. 14 | Windows is currently under heavy development with changes are made every few months, that makes running a virtual machine more complex than ever. On Windows machines, you have to pay attention to Hyper-V to see if it plays nicely with VirtualBox or VMware. If Hyper-V causes the problem, you would have no choice but update VMware or VirtualBox (or reinstall Windows, of course). 15 | 16 | “OSError: error no 5 input/output error” with Python 17 | 18 | It doesn’t really matter that you are using Django, Odoo, PyTorch or low-level libraries like pexpect or shutil, if there’s something wrong while reading/writing data to the disk, “OSError: errno 5 input/output error” might be the first error you will see. 19 | There’s a couple of things you can try, depends on your specific scenario : 20 | 21 | 1. Check the disk for errors. On Windows, you can run chkdsk. 22 | 2. On Linux, there is fsck. If there are recoverable errors, they’ll be fixed. 23 | 3. After that, your Python program may run without any issue. 24 | 4. Carefully inspect the permissions of the folder/directory you’re working in. It should include appropriate read/write permission. 25 | 5. Replace the disk drive to see if the problem goes away. If it does, then your disk drive is faulty. 26 | 27 | ```` 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | AndroidMemoryTool 2 | ==== 3 | ![PyPI - Version](https://img.shields.io/pypi/v/androidMemoryTool?label=pypi%20package) 4 | ![PyPI - Downloads](https://img.shields.io/pypi/dm/androidMemoryTool) 5 | [![GitHub stars](https://img.shields.io/github/stars/Anonym0usWork1221/android-memorytool.svg)](https://github.com/Anonym0usWork1221/android-memorytool/stargazers) 6 | [![GitHub forks](https://img.shields.io/github/forks/Anonym0usWork1221/android-memorytool.svg)](https://github.com/Anonym0usWork1221/android-memorytool/network/members) 7 | [![GitHub issues](https://img.shields.io/github/issues/Anonym0usWork1221/android-memorytool.svg)](https://github.com/Anonym0usWork1221/android-memorytool/issues) 8 | [![GitHub watchers](https://img.shields.io/github/watchers/Anonym0usWork1221/android-memorytool.svg)](https://github.com/Anonym0usWork1221/android-memorytool/watchers) 9 | [![Python](https://img.shields.io/badge/language-Python%203-blue.svg)](https://www.python.org) 10 | [![GPT_LICENSE](https://img.shields.io/badge/license-GPL-red.svg)](https://opensource.org/licenses/) 11 | ![code size](https://img.shields.io/github/languages/code-size/Anonym0usWork1221/android-memorytool) 12 | 13 | 14 |
15 |
16 |

17 | 18 |

19 | 20 | ----------- 21 | 22 | __*AndroidMemoryTool*__: A Powerful Memory Reader and Writer Tool for Android, Linux, and Windows Operating Systems 23 | 24 | AndroidMemoryTool is a sophisticated memory manipulation tool meticulously crafted for use on Android, Linux, and 25 | Windows operating systems. This versatile tool is meticulously coded in Python, utilizing ctypes and struct datatypes, 26 | ensuring efficiency comparable to C. 27 | 28 | Our commitment to excellence extends to our support system. If you encounter any bugs or non-functional features, 29 | please do not hesitate to contact us. Your feedback is invaluable in our pursuit of continuous improvement. 30 | 31 | * Date : 2023/09/30 32 | * Author : **__Abdul Moez__** 33 | * Version : 0.6.3 (Linux Pid Bug Fixed) 34 | * Study : UnderGraduate in GCU Lahore, Pakistan 35 | * Repository : [Main Branch](https://github.com/Anonym0usWork1221/android-memorytool) 36 | * Documentation: [AndroidMemoryToolExtensiveDocumentation](https://github.com/Anonym0usWork1221/android-memorytool/tree/main/Documentation) 37 | 38 | 39 | GNU General Public License 40 | Copyright (c) 2023 AbdulMoez 41 | 42 | ----------- 43 | 44 | # Note 45 | 1. This documentation is for 0.6 version (UPDATED) 46 | 2. You can find old version on pypi if you want to use them 47 | 3. For Linux and Android Users use 0.6.3 Build other wise you will get PID error 48 | 4. Fow now please calculate the windows pointer addresses manually this tool does not have feature to calculate pointer for windows yet. 49 | 50 | ----------- 51 | 52 | # Version 0.6.3 53 | 54 | ```` 55 | -----------------------------------------MODIFICATION LOG-------------------------------------------------- 56 | 57 | 1. Added Support for Windows OS (while maintaining compatibility with existing API calls). 58 | 2. Introduced a Robust Memory Profiler capable of threshold-based memory leak and churn detection using process IDs. 59 | 3. Customizability of the Memory Profiler has been enhanced (Please refer to the documentation for details). 60 | 4. Introduced several static methods, including: 61 | - get_developer 62 | - get_version_code 63 | - get_cpu_counts 64 | - get_platform 65 | - is_root_acquired 66 | 5. We are pleased to announce the addition of group search support with a new parameter, "is_grouped," 67 | which can be set to True. This enhancement allows users to perform grouped searches effectively and efficiently. 68 | By default, the value of the range is set to 512, aligning with the capabilities of Game Guardian. 69 | 70 | 6. We have recently introduced two new error classes to enhance the functionality of our memory tool: 71 | WINAPIException and PIDException. These additions further bolster our product's robustness and error-handling 72 | capabilities, ensuring a more seamless and reliable user experience. 73 | 7. We've meticulously updated and expanded our documentation to ensure that it's more informative, user-friendly, 74 | and grammatically impeccable than ever before. 75 | --------------------------------------------TO-DO LIST---------------------------------------------------- 76 | 77 | 1. FIXME: Resolve the speed mode bug on Windows OS. 78 | 2. TODO: Implement Reverse Engineering Support for offline binaries using renowned disassemblers such as Capstone, 79 | Keystone, and R2pipe. 80 | 3. TODO: Add Assembly support for runtime memory reading and writing. 81 | 4. TODO: Incorporate wildcard support in Windows API. 82 | 5. TODO: Expand the functionality with additional API handling methods. 83 | 84 | ----------------------------------------SUGGESTIONS------------------------------------------------------- 85 | 86 | Your valuable suggestions are welcome through either direct messages or our Discord server. 87 | 88 | -------------------------------------------NOTICE-------------------------------------------------------- 89 | 90 | This update has significantly increased the complexity of the Memory Tool, making it increasingly challenging for a 91 | single individual to manage its development. Therefore, we warmly welcome contributions from anyone interested in 92 | collaborating on its further enhancement. 93 | ```` 94 | 95 | 96 | ----------- 97 | Supported Platforms 98 | ----------- 99 | * Windows Support (Started from 0.6 Version) 100 | * Linux Support (Started From 0.2 Version) 101 | * Android Support (Started From 0.1 Version) 102 | 103 | ----------- 104 | Supported ByteOrders 105 | ----------- 106 | * Little-Endian 107 | * Big-Endian 108 | 109 | ----------- 110 | Tested Platforms 111 | ----------- 112 | > Our tool has been rigorously tested and proven to run seamlessly on the following platforms: 113 | 114 | * Windows 11 (64-bit) 115 | * Linux - Kali Linux (64-bit) 116 | * Android - Xiaomi 11T (Termux, 64-bit, Android 13) 117 | 118 | > Rest assured, our commitment to compatibility ensures a smooth and efficient user experience across these platforms. 119 | 120 | ----------- 121 | Requirements 122 | ----------- 123 | 124 | * Python 3.5+ 125 | * Android Requirements: Rooted Device Required 126 | * Linux Requirements: Root access may be necessary on certain Linux platforms. 127 | * Windows Requirements: Administrator permissions required 128 | 129 | 130 | ----------- 131 | Dependencies 132 | ----------- 133 | * Pip Dependencies (Automatically Installed in Requirements): `psutil` 134 | 135 | ----------- 136 | Installation 137 | ---------------------------------------- 138 | 1. **Installation via Pip for Easy Integration into Your Project** 139 | To effortlessly incorporate the Android Memory Tool into your project, execute the following pip command: 140 | > pip install androidMemoryTool==0.6.3 141 | 142 | 2. **Installation by Cloning the Repository and Running Commands** 143 | Alternatively, you can acquire the Android Memory Tool by cloning the GitHub repository and executing the 144 | subsequent commands: 145 | > git clone https://github.com/Anonym0usWork1221/android-memorytool/tree/main 146 | cd android-memorytool 147 | pip install . 148 | 149 | 3. **Project live at** 150 | [PyPi-0.6.3](https://pypi.org/project/androidMemoryTool/0.6.3/) 151 | 152 | 153 | Utilize our cutting-edge Memory Tool, replete with intricate examples, readily accessible within the designated folder. 154 | [Access Android-Py-Cheats-Script @ 723ca6b](https://github.com/Anonym0usWork1221/Android-Py-Cheats-Script/tree/723ca6b11de40d1031f210a12174b9c6b08e7b04). 155 | 156 | ----------- 157 | Video Demo - 0.6 158 | ----------- 159 | [![Video Demo](https://img.youtube.com/vi/5jV1haoEyWQ/0.jpg)](https://www.youtube.com/watch?v=5jV1haoEyWQ) 160 | 161 | ----------- 162 | 163 | ## Documentation 164 | 165 | * **Getting Process ID** 166 | To obtain the Process ID (PID) of a target process, you can use the following code snippet: 167 | 168 | ```py 169 | from androidMemoryTool import AndroidMemoryTool 170 | # Initialize the tool and set the speed_mode to off for Windows in this version only. 171 | tool = AndroidMemoryTool(PKG="ac_client") 172 | pid = tool.get_pid() 173 | print(pid) 174 | ``` 175 | * **Getting Module Base** 176 | To retrieve the base address of a specific module in the target process, you can use the following code snippet: 177 | 178 | ```py 179 | from androidMemoryTool import AndroidMemoryTool 180 | 181 | tool = AndroidMemoryTool(PKG="ac_client") 182 | base_addr = tool.get_module_base_address("client.so") 183 | print(base_addr) 184 | ``` 185 | 186 | * **Searching and Reading Process Memory** 187 | To search for a specific value in the process memory and read the results, use the following code: 188 | 189 | ```py 190 | from androidMemoryTool import AndroidMemoryTool, DataTypes, PMAP 191 | 192 | # Initialize the tool and set the speed_mode to off for Windows in this version only. 193 | tool = AndroidMemoryTool(PKG="Tutorial-x86_64.exe", 194 | SPEED_MODE=False, 195 | TYPE=DataTypes.DWORD, 196 | WORKERS=AndroidMemoryTool.get_cpu_counts(fraction=2), 197 | pMAP=PMAP(ALL=True) 198 | ) 199 | # Search for a value in the entire memory. 200 | values = tool.read_value(100) 201 | founded_offsets = values[0] 202 | founded_values = values[1] 203 | print(founded_values) 204 | print(founded_offsets) 205 | ``` 206 | 207 | * **Searching and Writing Process Memory** 208 | You can search for a specific value in the process memory and replace it with a new value using the following code: 209 | 210 | ```py 211 | from androidMemoryTool import AndroidMemoryTool, DataTypes, PMAP 212 | 213 | # Initialize the tool and set the speed_mode to off for Windows in this version only. 214 | tool = AndroidMemoryTool(PKG="Tutorial-x86_64.exe", 215 | SPEED_MODE=False, 216 | TYPE=DataTypes.DWORD, 217 | WORKERS=AndroidMemoryTool.get_cpu_counts(fraction=2), 218 | pMAP=PMAP(ALL=True) 219 | ) 220 | # Search for all values and replace them with a new value. 221 | values1 = tool.read_write_value(100, 10) 222 | print(values1) 223 | ``` 224 | 225 | * **Reading Address Value** 226 | To read the value at a specific memory address, use the following code: 227 | 228 | ```py 229 | from androidMemoryTool import AndroidMemoryTool, DataTypes 230 | 231 | tool = AndroidMemoryTool(PKG="ac_client", 232 | TYPE=DataTypes.DWORD 233 | ) 234 | base_addr = tool.get_module_base_address("client.so") 235 | values1 = tool.read_lib(base_addr, '0xfff150d') 236 | print(values1) 237 | 238 | ``` 239 | 240 | * **Writing Address Value** 241 | To write a value to a specific memory address, use the following code: 242 | 243 | ```py 244 | from androidMemoryTool import AndroidMemoryTool, DataTypes 245 | 246 | tool = AndroidMemoryTool(PKG="ac_client", TYPE=DataTypes.DWORD) 247 | base_addr = tool.get_module_base_address("client.so") 248 | values1 = tool.write_lib(base_addr, '0xfff150d', 58) 249 | print(values1) 250 | ``` 251 | 252 | * **Raw Dump Process Memory** 253 | You can dump the memory of a process using the following code: 254 | 255 | ```py 256 | from androidMemoryTool import AndroidMemoryTool 257 | 258 | tool = AndroidMemoryTool(PKG="ac_client") 259 | dump = tool.raw_dump(lib_name='client.so', path='./') 260 | print(dump) 261 | ``` 262 | 263 | * **Address Refiner** 264 | To refine addresses based on a value, use the following code: 265 | 266 | ```py 267 | from androidMemoryTool import AndroidMemoryTool, DataTypes, PMAP 268 | 269 | tool = AndroidMemoryTool(PKG="Tutorial-x86_64.exe", 270 | SPEED_MODE=False, 271 | TYPE=DataTypes.DWORD, 272 | WORKERS=AndroidMemoryTool.get_cpu_counts(fraction=2), 273 | pMAP=PMAP(ALL=True) 274 | ) 275 | values = tool.read_value(100) 276 | founded_offsets = values[0] 277 | refined_address = tool.refiner_address(list_address=founded_offsets, value_to_refine=50) 278 | print(refined_address) 279 | 280 | ``` 281 | 282 | * **Finding Hex Patterns (Linux and Android only)** 283 | To locate hex patterns in memory, use the following code (Linux and Android only): 284 | 285 | ```python 286 | from androidMemoryTool import AndroidMemoryTool, DataTypes, PMAP 287 | 288 | tool = AndroidMemoryTool(PKG="Tutorial-x86_64.exe", 289 | SPEED_MODE=False, 290 | TYPE=DataTypes.DWORD, 291 | WORKERS=AndroidMemoryTool.get_cpu_counts(fraction=2), 292 | pMAP=PMAP(ALL=True) 293 | ) 294 | found_pattern = tool.find_hex_pattern("87 ?? 2B") 295 | for index in range(0, len(found_pattern[0])): 296 | print(f"{found_pattern[0][index]}: {found_pattern[2][index]}") 297 | print(f"Total Pattern found: {found_pattern[1]}") 298 | 299 | ``` 300 | 301 | * **Finding and Replacing Hex Patterns (Linux and Android only)** 302 | To find and replace hex patterns in memory, use the following code (Linux and Android only): 303 | 304 | ```python 305 | from androidMemoryTool import AndroidMemoryTool, DataTypes, PMAP 306 | 307 | tool = AndroidMemoryTool(PKG="Tutorial-x86_64.exe", 308 | SPEED_MODE=False, 309 | TYPE=DataTypes.DWORD, 310 | WORKERS=AndroidMemoryTool.get_cpu_counts(fraction=2), 311 | pMAP=PMAP(ALL=True) 312 | ) 313 | found_pattern = tool.find_and_replace_hex_pattern("87 ?? 2B", "87 1D 2B") 314 | for index in range(0, len(found_pattern[0])): 315 | print(f"{found_pattern[0][index]}: {found_pattern[2][index]}") 316 | print(f"Total Pattern found and replaced: {found_pattern[1]}") 317 | ``` 318 | 319 | * **Dumping Memory Maps** 320 | You can dump the memory maps of a process using the following code: 321 | 322 | ```python 323 | from androidMemoryTool import AndroidMemoryTool 324 | 325 | tool = AndroidMemoryTool(PKG="ac_client") 326 | is_dumped = tool.dump_maps(path="./") 327 | print(is_dumped) 328 | ``` 329 | 330 | * **Group Search** 331 | Perform a group search to read and modify multiple values at once in specific range: 332 | 333 | ```python 334 | from androidMemoryTool import AndroidMemoryTool, DataTypes, PMAP 335 | 336 | tool = AndroidMemoryTool(PKG="Tutorial-x86_64.exe", 337 | SPEED_MODE=False, 338 | TYPE=DataTypes.DWORD, 339 | WORKERS=AndroidMemoryTool.get_cpu_counts(fraction=3), 340 | pMAP=PMAP(ALL=True) 341 | ) 342 | values = tool.read_value(read=[1000, 100], is_grouped=True, range_val=510) 343 | for value in values[0]: 344 | tool.write_lib(value, '0x0', 1000) 345 | print(f"Total Values Modified: {values[1]}") 346 | ``` 347 | 348 | * **Prebuilt Memory Profiler** 349 | Utilize the prebuilt Memory Profiler to analyze memory usage: 350 | 351 | ```python 352 | from androidMemoryTool import AndroidMemoryTool 353 | 354 | tool = AndroidMemoryTool(PKG="Tutorial-x86_64.exe") 355 | memory_profiler = tool.get_memory_profiler() 356 | memory_profiler.start_profiling(logging=False) 357 | ``` 358 | 359 | * **Static Methods** 360 | The AndroidMemoryTool also provides static methods for various functionalities: 361 | 362 | ```python 363 | from androidMemoryTool import AndroidMemoryTool 364 | 365 | # Get the name of the developer 366 | print(AndroidMemoryTool.get_developer()) 367 | 368 | # Get the version code of the tool 369 | print(AndroidMemoryTool.get_version_code()) 370 | 371 | # Get the number of CPU cores available on your device (can specify a fraction) 372 | print(AndroidMemoryTool.get_cpu_counts()) 373 | 374 | # Get the platform the script is running on (Linux, Android, Windows) 375 | print(AndroidMemoryTool.get_platform(verbose=True)) 376 | 377 | # Check if the script is running on a rooted terminal or non-rooted 378 | print(AndroidMemoryTool.is_root_acquired()) 379 | ``` 380 | 381 | ----------- 382 | ## Error Handling Enhancements 383 | > We have introduced new error handling classes to enhance the robustness of our code. 384 | > These error handling classes will help you better manage and troubleshoot issues that may arise during the 385 | > execution of your code. 386 | 387 | * **PIDException**: 388 | The `PIDException` is raised when there is an issue with connecting to the specified process. 389 | This error message provides valuable information to help you diagnose and resolve the problem efficiently. 390 | 391 | ```python 392 | try: 393 | from androidMemoryTool import AndroidMemoryTool, PIDException 394 | tool = AndroidMemoryTool(PKG="ac_client") 395 | except PIDException as e: 396 | print(f"An error occurred while trying to connect to the process: {e}") 397 | ``` 398 | 399 | * **WINAPIException (Only occur on Windows)**: 400 | The `WINAPIException` is specific to Windows environments and is raised when an error occurs during a read operation. 401 | This error message provides detailed information about the issue encountered during the reading process, making it 402 | easier for you to pinpoint and rectify the problem. 403 | 404 | ```python 405 | try: 406 | from androidMemoryTool import AndroidMemoryTool, WINAPIException 407 | tool = AndroidMemoryTool(PKG="ac_client") 408 | tool.read_value(read="some_wrong_value") 409 | except WINAPIException as e: 410 | print(f"An error occurred while reading a value: {e}") 411 | 412 | ``` 413 | 414 | ----------- 415 | ## Android Memory Tool CLI Documentation 416 | > CLI Documentation Relocated: The CLI documentation has been relocated to the Documentation folder. 417 | > You can access it by visiting the [AndroidMemoryToolCLIDOCS](https://github.com/Anonym0usWork1221/android-memorytool/tree/main/Documentation) on GitHub. 418 | 419 | ----------- 420 | ## Custom Android Memory Profiling Documentation 421 | > You can make custom Profiling tools by going to [MemoryProfilerDOCS](https://github.com/Anonym0usWork1221/android-memorytool/tree/main/Documentation) on GitHub. 422 | 423 | ----------- 424 | # Comprehensive Documentation 425 | > For in-depth and comprehensive documentation, please refer to the following link: 426 | [Comprehensive Documentation](https://github.com/Anonym0usWork1221/android-memorytool/tree/main/Documentation) 427 | ----------- 428 | 429 | # Troubleshooting Errors 430 | * **Windows ERROR ON `SPEED_MODE`** 431 | An issue has been identified on Windows systems related to the SPEED_MODE option, 432 | which may result in the application getting stuck in a thread indefinitely. As a temporary solution, 433 | we recommend disabling the SPEED_MODE for Windows. 434 | 435 | > Some other known errors and their solutions can be found [here](https://github.com/Anonym0usWork1221/android-memorytool/blob/main/ERRORS.md) 436 | 437 | ----------- 438 | 439 | Supported Data Types (For all Linux, Android and Windows) 440 | ------------------- 441 | 442 | All data types are signed. 443 | 444 | | **Range** | **Name** | **Type** | **Bytes** | 445 | |---------------------------------------------------------|----------|------------------|-----------| 446 | | -2,147,483,648 to 2,147,483,647 | DWORD | signed int | 4 | 447 | | 3.4E +/- 38 (7 digits) | FLOAT | float | 4 | 448 | | 1.7E +/- 308 (15 digits) | DOUBLE | double | 8 | 449 | | -32,768 to 32,767 | WORD | signed short int | 2 | 450 | | -128 to 127 | BYTE | signed char | 1 | 451 | | -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 | QWORD | signed long long | 8 | 452 | | -2,147,483,648 to 2,147,483,647 | XOR | signed long | 4 | 453 | | Random | UTF_8 | char[] | Random | 454 | | Random | UTF_16LE | char[] | Random | 455 | 456 | 457 | 458 | Supported Map Ranges (Linux and Android only) 459 | -------------------- 460 | | **Script Name** | **Name** | **Description** | 461 | |-----------------|--------------|----------------------------------------| 462 | | ALL | Whole Memory | Whole Memory of current process (slow) | 463 | | C_ALLOC | C++ alloc | RAM c++ Allocated memory | 464 | | A_ANONYMOUS | Anonymous | Range with r-w access only | 465 | | CODE_APP | Code App | shared libs memory (dangerous) | 466 | | JAVA_HEAP | Java Heap | Java heap | 467 | | C_HEAP | C++ Heap | Heap memory of cpp | 468 | | C_DATA | C++ .data | .Data Memory | 469 | | C_BSS | C++ .bss | .bss section memory | 470 | | J_Java | Java | Java memory section | 471 | | STACK | Stack | Stack Memory | 472 | | ASHMEM | Ashmen | Ashmen Memory | 473 | | V_video | Video | Video memory range | 474 | | B_Bad | Bad | Bad Memory (dangerous) | 475 | | CODE_SYSTEM | Code system | Code system memory (dangerous) | 476 | 477 | ----------- 478 | 479 | # Contributor 480 | 481 | 482 | 483 | 484 | 485 | ----------- 486 | Support and Contact Information 487 | ---------- 488 | > If you require any assistance or have questions, please feel free to reach out to me through the following channels: 489 | * **Email**: `abdulmoez123456789@gmail.com` 490 | 491 | > I have also established a dedicated Discord group for more interactive communication: 492 | * **Discord Server**: `https://discord.gg/RMNcqzmt9f` 493 | 494 | 495 | ----------- 496 | 497 | Buy Me a coffee 498 | -------------- 499 | __If you'd like to show your support and appreciation for my work, you can buy me a coffee using the 500 | following payment option:__ 501 | 502 | **Payoneer**: `abdulmoez123456789@gmail.com` 503 | 504 | > Your support is greatly appreciated and helps me continue providing valuable assistance and resources. 505 | Thank you for your consideration. 506 | 507 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | | Version | Supported | 6 | | ------- | ------------------ | 7 | | 6.0.3.x | :white_check_mark: | 8 | 9 | ## Reporting a Vulnerability 10 | 11 | If you discover a security vulnerability in the AndroidMemoryTool, please help us protect our users by responsibly disclosing the issue. You can report security vulnerabilities to us by following these steps: 12 | 13 | 1. Contact us through one of the following methods: 14 | - Email: abdulmoez123456789@gmail.com 15 | - Discord: [Discord Server](https://discord.gg/RMNcqzmt9f) 16 | 17 | 2. Please provide us with a detailed report, including: 18 | - A clear description of the vulnerability. 19 | - Steps to reproduce the vulnerability. 20 | - Information about the affected versions. 21 | 22 | 3. We will acknowledge your report and work to address the issue promptly. 23 | 24 | 4. Once the vulnerability is confirmed and fixed, we will release security updates as per our version support policy. 25 | 26 | Please note that we appreciate responsible disclosure and will not take legal action against security researchers who follow responsible disclosure practices. 27 | -------------------------------------------------------------------------------- /Tests/Auto-Installation-Tools.py: -------------------------------------------------------------------------------- 1 | 2 | import platform 3 | import subprocess 4 | import sys 5 | 6 | 7 | def install_radare2(): 8 | current_platform = platform.system() 9 | 10 | if current_platform == 'Linux': 11 | print(f"Platform Detected as {current_platform}: Installing Additional tools as sudo." 12 | f" By using apt-get method.") 13 | subprocess.check_call(['sudo', 'apt-get', 'update']) 14 | subprocess.check_call(['sudo', 'apt-get', 'install', 'radare2']) 15 | 16 | elif current_platform == 'Windows': 17 | install_path = 'C:\\Radare2\\' 18 | print(f"Platform Detected as {current_platform}: Installing Additional tools. Using Default directory as: " 19 | f"{install_path}") 20 | subprocess.check_call(['powershell', 'Invoke-WebRequest', '-Uri', 21 | 'https://github.com/radareorg/radare2/releases/latest/download/radare2_windows.zip', 22 | '-OutFile', 'radare2.zip']) 23 | subprocess.check_call( 24 | ['powershell', 'Expand-Archive', '-Path', 'radare2.zip', '-DestinationPath', install_path]) 25 | subprocess.check_call(['del', 'radare2.zip'], shell=True) 26 | subprocess.check_call(['setx', 'PATH', f'%PATH%;{install_path}']) 27 | 28 | elif current_platform == 'Android': 29 | print(f"Platform Detected as {current_platform}: Installing Additional tools. Assuming the tool is termux.") 30 | subprocess.check_call(['pkg', 'install', 'radare2']) 31 | 32 | else: 33 | print(f"Platform Detected as {current_platform}: Auto-installation of Additional tools is unsupported.") 34 | print("Please install Radare2 manually. And it is to environment variable") 35 | sys.exit(1) 36 | 37 | 38 | try: 39 | import r2pipe 40 | except ImportError: 41 | print("r2pipe not found. Installing Radare2...") 42 | install_radare2() 43 | try: 44 | import r2pipe 45 | except ImportError: 46 | print("Additional tools not found installing automatically...") 47 | sys.exit(1) 48 | 49 | print("Radare2 and r2pipe successfully installed!") 50 | -------------------------------------------------------------------------------- /Tests/Cross-Platform-Tests-For-Tool.py: -------------------------------------------------------------------------------- 1 | # In this update you need to load PMAP and DataTypes separately from androidMemoryTool 2 | # PMAP only work with Linux and Android. 3 | # (Work on all Linux, Android and Windows) 4 | from androidMemoryTool import AndroidMemoryTool, DataTypes, PMAP 5 | 6 | # Initialize the tool set the speed_mode to off for working on Windows in this version only. 7 | # (Work on all Linux, Android and Windows) 8 | tool = AndroidMemoryTool(PKG="Tutorial-x86_64.exe", 9 | SPEED_MODE=False, 10 | TYPE=DataTypes.DWORD, 11 | # half of cores available in operating system 12 | WORKERS=AndroidMemoryTool.get_cpu_counts(fraction=2), 13 | pMAP=PMAP(ALL=True) # This will not affect windows search rage 14 | ) 15 | # Don't use speed mode on group search on WindowsAPI there is bug in threads in Windows api 16 | # Group search get value list which contain list of addresses and total number of addresses in it 17 | # (Work on all Linux, Android and Windows) 18 | tool.read_value(read=[1000, 100], is_grouped=True, range_val=510) # by default range_val=512 (adjust as you like) 19 | 20 | # Find hex pattern in memory (Not available for widows in this update (0.6) 21 | tool.find_hex_pattern(search_pattern='8A ?? 2B') 22 | # Find hex pattern and replace it with new pattern in memory (Not available for widows in this update (0.6) 23 | tool.find_and_replace_hex_pattern(search_pattern='8A ?? 3B', replace_pattern='88 22 3B') 24 | # Search for some value in whole memory (Work on all Linux, Android and Windows) 25 | tool.read_value(read=100) 26 | # Get base address of some dll in windows and lib in linux and android (Work on all Linux, Android and Windows) 27 | tool.get_module_base_address(module_name="ntdll.dll") 28 | # Read the address value of some offset (Work on all Linux, Android and Windows) 29 | tool.read_lib(base_address='015E24F8', offset='0x0') 30 | # Write the address value of some offset (Work on all Linux, Android and Windows) 31 | tool.write_lib(base_address='015E24F8', offset='0x0', write_value=1000) 32 | # Dump the memory of some dlls or libs (Work on all Linux, Android and Windows) 33 | tool.raw_dump(lib_name="ntdll.dll") 34 | # Refine address and get new value from old address (Work on all Linux, Android and Windows) 35 | tool.refiner_address(list_address=[], value_to_refine=97) 36 | # Just Search all values and replace all with new value (Work on all Linux, Android and Windows) 37 | tool.read_write_value(read=100, write=200) 38 | # Dump the memory maps (Work on all Linux, Android and Windows) 39 | tool.dump_maps() 40 | # Get the pid of process by their name (Work on all Linux, Android and Windows) 41 | tool.get_pid() 42 | 43 | # --- Static methods (From 0.6 version to onward) --- 44 | # Name of developer 45 | print(AndroidMemoryTool.get_developer()) 46 | # Version of Tool 47 | print(AndroidMemoryTool.get_version_code()) 48 | # Number of cores available on your device 49 | print(AndroidMemoryTool.get_cpu_counts()) 50 | # Known Platform = ['Android', 'Linux', 'Windows'] (Get Non-supported message if platform is 51 | # not supported by script on verbose=True) 52 | print(AndroidMemoryTool.get_platform(verbose=True)) 53 | # Check if the script running on rooted terminal or non-rooted 54 | print(AndroidMemoryTool.is_root_acquired()) 55 | 56 | 57 | # Get the Memory profiler (you can customize it for that read the Profiler Documentation) 58 | # (Work on all Linux, Android and Windows) 59 | memory_profiler = tool.get_memory_profiler() 60 | # Run the profiler by default configurations 61 | memory_profiler.start_profiling(logging=False) # Logging will disable saving output in file 62 | 63 | 64 | -------------------------------------------------------------------------------- /Tests/Some-Tools-Test-Usage.py: -------------------------------------------------------------------------------- 1 | import r2pipe 2 | 3 | 4 | def disassemble_binary(binary_path): 5 | r2 = r2pipe.open(binary_path) 6 | r2.cmd("aaa") # Analyze all functions 7 | 8 | functions = r2.cmdj("aflj") # Get a list of functions 9 | 10 | pseudocode = "" 11 | for function in functions: 12 | r2.cmd("aaa") 13 | address = function['offset'] 14 | r2.cmd(f"s {address}") 15 | pseudocode += r2.cmd(f"pdc\n") # Generate pseudocode for each function 16 | 17 | r2.quit() 18 | return pseudocode 19 | 20 | 21 | binary_path = "./c" 22 | pseudocode = disassemble_binary(binary_path) 23 | print(pseudocode) 24 | -------------------------------------------------------------------------------- /Tests/custom_profiler.py: -------------------------------------------------------------------------------- 1 | from androidMemoryTool import AndroidMemoryTool 2 | from time import sleep 3 | from os import system 4 | 5 | tool = AndroidMemoryTool(PKG="Tutorial-x86_64.exe") 6 | profile_instance = tool.get_memory_profiler() 7 | 8 | custom_log_file = "memory_dump.txt" 9 | 10 | 11 | def custom_log_memory_data(data: list[dict]) -> None: 12 | with open(custom_log_file, 'w') as log_file: 13 | log_file.write(f"{'Time':<10}{'Process RSS (BYTES)':<40}{'Memory Leak':<15}{'Memory Churn':<15}\n") 14 | for entry in data: 15 | log_file.write(f"{entry['time']:<10}{entry['process_memory']:<40}{entry['leak']:<15}{entry['churn']:<15}\n") 16 | 17 | 18 | def custom_print_function(data: list[dict]): 19 | if AndroidMemoryTool.get_platform() == 'Windows': 20 | system("CLS") 21 | else: 22 | system("clear") 23 | formatted_status_print_header = f"{'Time':<10}{'Process RSS (MB)':<30}{'Memory Leak':<15}{'Memory Churn':<15}\n" 24 | current_body = formatted_status_print_header 25 | for entry in data: 26 | current_body += f"{entry['time']:<10}{entry['process_memory'] / (1024 * 1024):<30}{entry['leak']:<15}" \ 27 | f"{entry['churn']:<15}\n" 28 | print(current_body) 29 | 30 | 31 | def start_custom_profiler(): 32 | threshold_in_mb: float = 20.0 33 | update_interval_delay: float = 1.0 34 | while True: 35 | try: 36 | # This function will store memory in public variables 37 | profile_instance.current_memory_data(threshold_in_mb=threshold_in_mb) 38 | profile_instance.create_data_from_files() # Create the data and store in self._data which we can get latter 39 | data = profile_instance.get_current_data() # return the data generated by profiler 40 | custom_log_memory_data(data=data) 41 | custom_print_function(data=data) 42 | """ 43 | Data will get these values 44 | time, ram, cores, process_memory, leak, churn, pid 45 | """ 46 | sleep(update_interval_delay) # Delay for some time 47 | except KeyboardInterrupt: 48 | print('Closing Program please wait') 49 | break 50 | 51 | 52 | start_custom_profiler() 53 | -------------------------------------------------------------------------------- /Tests/memory_leakage_program_for_profiler_testing.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | 4 | 5 | class MemoryLeak: 6 | def __init__(self): 7 | self.data = [] 8 | 9 | def leak_memory(self): 10 | while True: 11 | self.data.extend([1] * 1000000) # Allocate more memory 12 | time.sleep(0.1) 13 | 14 | 15 | if __name__ == "__main__": 16 | memory_leak = MemoryLeak() 17 | print(f"PID: {os.getpid()}") 18 | print("Running memory leak...") 19 | memory_leak.leak_memory() 20 | 21 | -------------------------------------------------------------------------------- /amt.py: -------------------------------------------------------------------------------- 1 | """ 2 | /* 3 | * Date : 2023/09/30 4 | * Version : 0.6 5 | * Author : Abdul Moez 6 | * Email : abdulmoez123456789@gmail.com 7 | * Affiliation : Undergraduate at Government College University (GCU) Lahore, Pakistan 8 | * GitHub : https://github.com/Anonym0usWork1221/android-memorytool 9 | * 10 | * Description: 11 | * This code is governed by the GNU General Public License, version 3 or later. 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program. If not, see . 14 | */ 15 | """ 16 | 17 | # !/usr/bin/env python 18 | from androidMemoryTool import AndroidMemoryToolCLI, AndroidMemoryTool 19 | 20 | 21 | def execute_cli(): 22 | cli_tool = AndroidMemoryToolCLI(AndroidMemoryTool) 23 | cli_tool.cli_handler() 24 | 25 | 26 | if __name__ == '__main__': 27 | execute_cli() 28 | -------------------------------------------------------------------------------- /androidMemoryTool/CommonAPI/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anonym0usWork1221/android-memorytool/d777a7231100eed31cf9af36d1b73de63961da06/androidMemoryTool/CommonAPI/__init__.py -------------------------------------------------------------------------------- /androidMemoryTool/CommonAPI/cross_platform_memory_profiler.py: -------------------------------------------------------------------------------- 1 | """ 2 | /* 3 | * Date : 2023/09/30 4 | * Version : 0.6 5 | * Author : Abdul Moez 6 | * Email : abdulmoez123456789@gmail.com 7 | * Affiliation : Undergraduate at Government College University (GCU) Lahore, Pakistan 8 | * GitHub : https://github.com/Anonym0usWork1221/android-memorytool 9 | * 10 | * Description: 11 | * This code is governed by the GNU General Public License, version 3 or later. 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program. If not, see . 14 | */ 15 | """ 16 | 17 | from psutil import cpu_count, virtual_memory, Process, NoSuchProcess 18 | from ..errors_class import PIDException 19 | from threading import Event, Thread 20 | from time import sleep, strftime 21 | from os import system 22 | import platform 23 | 24 | if platform.system() == "Windows": 25 | _CLEAR_CMD = "cls" 26 | else: 27 | _CLEAR_CMD = "clear" 28 | 29 | 30 | class MemoryProfiler: 31 | def __init__(self, pid: int, logging_file_path: str = "memory_log.txt") -> None: 32 | # Private variables 33 | self._logging_file: str = logging_file_path 34 | self._break_profile_loop = Event() 35 | self._pid: int = pid 36 | self._data = [] 37 | 38 | # Accessible variables 39 | self.highest_memory_usage = self.get_memory_usage() 40 | self.churn_flag = Event() 41 | self.leak_flag = Event() 42 | self.memory_history = [] 43 | 44 | def create_data_from_files(self) -> None: 45 | system_info = self.get_system_info() 46 | self._data.append({ 47 | 'time': strftime("%H:%M:%S"), 48 | 'ram': system_info['RAM'], 49 | 'cores': system_info['Cores'], 50 | 'process_memory': self.get_memory_usage() / (1024 ** 2), 51 | 'leak': "Yes" if self.leak_flag.is_set() else "No", 52 | 'churn': "Yes" if self.churn_flag.is_set() else "No", 53 | 'pid': self._pid 54 | }) 55 | 56 | def clean_current_status_print_string(self) -> str: 57 | self.create_data_from_files() 58 | formatted_status_print_header = f"{'Time':<10}{'RAM (GB)':<10}{'Cores':<10}{'Process RSS (MB)':<30}" \ 59 | f"{'Memory Leak':<15}{'Memory Churn':<15}{'PID':<6}\n" 60 | formatted_status_print_string = formatted_status_print_header 61 | for entry in self._data: 62 | current_body = f"{entry['time']:<10}{entry['ram']:<10}{entry['cores']:<10}" \ 63 | f"{entry['process_memory'] / (1024 * 1024):<30}{entry['leak']:<15}" \ 64 | f"{entry['churn']:<15}{entry['pid']:<6}\n" 65 | formatted_status_print_string += current_body 66 | 67 | return formatted_status_print_string 68 | 69 | @staticmethod 70 | def get_system_info() -> dict: 71 | system_info = { 72 | 'RAM': virtual_memory().total // (1024 ** 3), 73 | 'Cores': cpu_count(logical=False), 74 | } 75 | return system_info 76 | 77 | def get_memory_usage(self) -> any: 78 | try: 79 | process = Process(self._pid) 80 | memory_info = process.memory_info() 81 | memory_usage_bytes = memory_info.rss 82 | return memory_usage_bytes 83 | except NoSuchProcess: 84 | raise PIDException(f"Processes with PID {self._pid} is not running in memory either " 85 | f"it crashed or stoped working restart process.") 86 | 87 | def stop_profiling(self) -> None: 88 | self._break_profile_loop.set() # Signal the monitoring thread to stop 89 | 90 | def current_memory_data(self, threshold_in_mb: float = 10.0) -> None: 91 | memory_usage = self.get_memory_usage() 92 | self.memory_history.append(memory_usage) 93 | 94 | if memory_usage > self.highest_memory_usage + int(threshold_in_mb * 1024.0 * 1024.0): 95 | self.leak_flag.set() 96 | self.churn_flag.clear() # Clear churn flag if leakage occurs 97 | elif memory_usage > self.highest_memory_usage: 98 | if not self.leak_flag.is_set(): 99 | self.churn_flag.set() # Set churn flag if memory usage increases 100 | self.highest_memory_usage = memory_usage 101 | else: 102 | self.leak_flag.clear() 103 | self.churn_flag.clear() 104 | 105 | def monitor_memory(self, threshold_in_mb: float = 10.0, update_interval_delay: float = 1.0) -> None: 106 | while not self._break_profile_loop.is_set(): 107 | self.current_memory_data(threshold_in_mb=threshold_in_mb) 108 | sleep(update_interval_delay) # Update interval 109 | 110 | def log_memory_data(self) -> None: 111 | with open(self._logging_file, 'w') as log_file: 112 | log_file.write(f"{'Time':<10}{'RAM (GB)':<10}" 113 | f"{'Cores':<10}{'Process RSS (BYTES)':<40}{'Memory Leak':<15}" 114 | f"{'Memory Churn':<15}{'PID':<10}\n") 115 | for entry in self._data: 116 | log_file.write( 117 | f"{entry['time']:<10}{entry['ram']:<10}" 118 | f"{entry['cores']:<10}{entry['process_memory']:<40}{entry['leak']:<15}" 119 | f"{entry['churn']:<15}{entry['pid']:<10}\n" 120 | ) 121 | 122 | def get_current_data(self) -> list: 123 | return self._data 124 | 125 | def start_profiling(self, 126 | verbose: bool = True, 127 | threshold_in_mb: float = 10.0, 128 | update_interval_delay: float = 1.0, 129 | logging: bool = True) -> None: 130 | monitor_thread = Thread(target=self.monitor_memory, args=(threshold_in_mb, update_interval_delay)) 131 | monitor_thread.daemon = True 132 | monitor_thread.start() 133 | 134 | try: 135 | while True: 136 | if logging: 137 | self.log_memory_data() 138 | if verbose: 139 | global _CLEAR_CMD 140 | system(_CLEAR_CMD) 141 | print(self.clean_current_status_print_string()) 142 | 143 | sleep(update_interval_delay) 144 | except KeyboardInterrupt: 145 | self.stop_profiling() # Stop thread properly 146 | self.log_memory_data() 147 | 148 | 149 | # if __name__ == "__main__": 150 | # target_pid = int(input("Enter the PID of the process to profile: ")) 151 | # profiler = MemoryProfiler(target_pid) 152 | # profiler.start_profiling() 153 | -------------------------------------------------------------------------------- /androidMemoryTool/LinuxAPI/AndroidMemoryTool-Tests.py: -------------------------------------------------------------------------------- 1 | # from androidMemoryTool import AndroidMemoryTool, DataTypes, PMAP 2 | 3 | # initialize tool. 4 | # tool = AndroidMemoryTool(PKG="ac_client", TYPE=DataTypes.DWORD, SPEED_MODE=True, WORKERS=55, 5 | # pMAP=PMAP(ALL=True)) 6 | # values = tool.read_value([20, 40], is_grouped=True, range_val=512) 7 | 8 | # print(tool.get_pid("ac_client")) 9 | # values = tool.read_value(100) 10 | # founded_offsets = values[0] 11 | 12 | # refined_address = tool.refiner_address(list_address=founded_offsets, value_to_refine=50) 13 | # tool.read_lib('0x0', '0x100') 14 | # tool.write_lib('0x0', '0x0', 10) 15 | 16 | # pid = AndroidMemoryTool.get_pid("714") 17 | # print(pid) 18 | 19 | # if you are reading you will get tuple of two values offset list and total values found 20 | 21 | # values = tool.read_value(100) 22 | # founded_offsets = values[0] 23 | # founded_values = values[1] 24 | # print(founded_values) 25 | # print(founded_offsets) 26 | 27 | 28 | # if you are writing only return total value wrote 29 | # values1 = tool.read_write_value(100, 10) 30 | # print(values1) 31 | 32 | 33 | # if you are reading lib offset only get the value at that place 34 | # values1 = tool.read_lib(0x0, 0x100) 35 | # print(values1) 36 | 37 | 38 | # if you are writing lib offset only get the true/false at that place 39 | # values1 = tool.write_lib(0x0, 0x100, 20) 40 | # print(values1) 41 | 42 | # dump = tool.raw_dump('client.so', '/home/kali/Documents/') 43 | # print(dump) 44 | 45 | # print(tool.read_value("19h")) 46 | 47 | # Manually entered pid 48 | # tool = AndroidMemoryTool(PKG=714, TYPE=AndroidMemoryTool.DataTypes.DWORD, SPEED_MODE=True, WORKERS=55, 49 | # pMAP=AndroidMemoryTool.PMAP(ALL=True)) 50 | # test_value = tool.read_value(42) 51 | # print(test_value) 52 | 53 | # to dump maps 54 | # is_dumped = tool.dump_maps(path="./") 55 | # print(is_dumped) 56 | 57 | # utf8 lib tests 58 | # tool = AndroidMemoryTool(PKG=662, TYPE=AndroidMemoryTool.DataTypes.UTF_8, SPEED_MODE=True, WORKERS=55, 59 | # pMAP=AndroidMemoryTool.PMAP(ALL=True)) 60 | # off = tool.read_value("hi") 61 | # print(off) 62 | 63 | # off = tool.read_lib(0x5590fb61d8e2, 0x0, "hi") 64 | # print(off) 65 | 66 | # Hex pattern 67 | # found_pattern = tool.find_hex_pattern("87 ?? 2B") 68 | # for index in range(0, len(found_pattern[0])): 69 | # print(f"{found_pattern[0][index]}: {found_pattern[2][index]}") 70 | # print(f"Total Pattern found: {found_pattern[1]}") 71 | 72 | # Read Write Hex pattern 73 | # found_pattern = tool.find_and_replace_hex_pattern("87 ?? 2B", "87 1D 2D") 74 | # for index in range(0, len(found_pattern[0])): 75 | # print(f"{found_pattern[0][index]}: {found_pattern[2][index]}") 76 | # print(f"Total Pattern found and replaced: {found_pattern[1]}") 77 | -------------------------------------------------------------------------------- /androidMemoryTool/LinuxAPI/DataClasses.py: -------------------------------------------------------------------------------- 1 | """ 2 | /* 3 | * Date : 2023/09/30 4 | * Version : 0.6 5 | * Author : Abdul Moez 6 | * Email : abdulmoez123456789@gmail.com 7 | * Affiliation : Undergraduate at Government College University (GCU) Lahore, Pakistan 8 | * GitHub : https://github.com/Anonym0usWork1221/android-memorytool 9 | * 10 | * Description: 11 | * This code is governed by the GNU General Public License, version 3 or later. 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program. If not, see . 14 | */ 15 | """ 16 | 17 | from subprocess import check_output, CalledProcessError 18 | from ..errors_class import PIDException 19 | from dataclasses import dataclass 20 | from struct import pack, unpack 21 | from binascii import unhexlify 22 | from .mapping import Mapping 23 | from sys import byteorder 24 | 25 | 26 | class DataClasses: 27 | _PKG_NAME = "" 28 | _DATA_TYPES = "" 29 | _IS_SPEED_MODE = False 30 | _TOTAL_WORKERS = 55 31 | 32 | _MAPS_ADDR = [] 33 | _DATA_BYTE = 0 34 | _PID = "" 35 | 36 | @dataclass() 37 | class PMAP: 38 | """ 39 | Data class that defines different options for memory mapping. 40 | """ 41 | 42 | ALL: bool = True 43 | B_BAD: bool = False 44 | C_ALLOC: bool = False 45 | C_BSS: bool = False 46 | C_DATA: bool = False 47 | C_HEAP: bool = False 48 | JAVA_HEAP: bool = False 49 | A_ANONYMOUS: bool = False 50 | CODE_SYSTEM: bool = False 51 | STACK: bool = False 52 | ASHMEM: bool = False 53 | J_Java: bool = False 54 | CODE_APP: bool = False 55 | V_video: bool = False 56 | 57 | @dataclass() 58 | class DataTypes(object): 59 | """ 60 | Data class that defines different data types used in the code. 61 | """ 62 | 63 | DWORD: str = "DWORD" 64 | FLOAT: str = "FLOAT" 65 | DOUBLE: str = "DOUBLE" 66 | WORD: str = "WORD" 67 | BYTE: str = "BYTE" 68 | QWORD: str = "QWORD" 69 | XOR: str = "XOR" 70 | UTF_8: str = "UTF-8" 71 | UTF_16LE: str = "UTF-16LE" 72 | 73 | def __init__(self): 74 | ... 75 | 76 | @staticmethod 77 | def get_pid(pkg: any((str, int))) -> str: 78 | """ 79 | Retrieves the process ID (PID) for the given package name or PID. 80 | Args: 81 | pkg: The package name or PID. 82 | Returns: 83 | The process ID (PID) as a string. 84 | """ 85 | 86 | pkg = str(pkg) 87 | # if the pkg is numeric string 88 | if pkg.isnumeric(): 89 | try: 90 | pid_id = check_output(["ps", "-q", pkg, "-o", "cmd="]) 91 | return pkg if pid_id.decode().strip() is not None else "" 92 | except CalledProcessError: 93 | raise PIDException("Processes is not running in memory try to restart process and script") 94 | else: 95 | try: 96 | pid_id = check_output(["pidof", "-s", pkg]) # gets the first id if multiple ids found 97 | process_ids = pid_id.decode().strip() 98 | return str(process_ids) 99 | except CalledProcessError: 100 | raise PIDException("Processes is not running in memory try to restart process and script") 101 | 102 | def init_setup(self, PKG: any((str, int)), TYPE: str = DataTypes.DWORD, SPEED_MODE=False, WORKERS=55) -> None: 103 | """ 104 | Initializes the setup with the specified package, data type, speed mode, and number of workers. 105 | 106 | Args: 107 | PKG: The package name or PID. 108 | TYPE: The data type. 109 | SPEED_MODE: Flag indicating whether speed mode is enabled or not. 110 | WORKERS: The number of workers. 111 | 112 | Returns: 113 | None. 114 | """ 115 | 116 | self._DATA_TYPES = TYPE 117 | self._PKG_NAME = PKG 118 | self._IS_SPEED_MODE = SPEED_MODE 119 | self._TOTAL_WORKERS = WORKERS 120 | 121 | def identify_dict(self, P=PMAP()) -> list: 122 | """ 123 | Identifies the memory addresses based on the given memory mapping options. 124 | 125 | Args: 126 | P: The memory mapping options. 127 | 128 | Returns: 129 | A list of identified memory addresses. 130 | """ 131 | mapping_functions = { 132 | "ALL": Mapping.mapping_all, 133 | "B_BAD": Mapping.mapping_b, 134 | "C_ALLOC": Mapping.mapping_ca, 135 | "C_BSS": Mapping.mapping_cb, 136 | "C_DATA": Mapping.mapping_cd, 137 | "C_HEAP": Mapping.mapping_ch, 138 | "JAVA_HEAP": Mapping.mapping_jh, 139 | "A_ANONYMOUS": Mapping.mapping_a, 140 | "CODE_SYSTEM": Mapping.mapping_xs, 141 | "STACK": Mapping.mapping_s, 142 | "ASHMEM": Mapping.mapping_as, 143 | "J_Java": Mapping.mapping_j, 144 | "CODE_APP": Mapping.mapping_xa, 145 | "V_video": Mapping.mapping_v 146 | } 147 | 148 | wanted_ranges = [k for k, v in P.__dict__.items() if v] 149 | address_set = set() 150 | 151 | for map_name in wanted_ranges: 152 | if map_name in mapping_functions: 153 | address_set.update(mapping_functions[map_name](self.get_pid(self._PKG_NAME))["address"]) 154 | if map_name == "ALL": 155 | break 156 | 157 | return list(address_set) 158 | 159 | def data_type_encoding(self, read: any, write=None) -> any: 160 | """ 161 | Encodes the data values based on the specified data type for searching or writing in memory. 162 | 163 | Args: 164 | read: The value to be encoded for searching in memory. 165 | write: The value to be encoded for writing in memory. 166 | 167 | Returns: 168 | The encoded search value and replace value (if provided). 169 | """ 170 | if self._DATA_TYPES == "DWORD": 171 | if write: 172 | if "h" in str(read).lower(): 173 | read = int(str(read).lower().replace("h", ""), 16) 174 | if "h" in str(write).lower(): 175 | write = int(str(write).lower().replace("h", ""), 16) 176 | 177 | if byteorder == 'little': 178 | search_value = pack('i', read) 182 | replace_value = pack('>i', write) 183 | 184 | return search_value, replace_value 185 | 186 | else: 187 | if "h" in str(read).lower(): 188 | read = int(str(read).lower().replace("h", ""), 16) 189 | 190 | if byteorder == 'little': 191 | search_value = pack('i', read) 194 | return search_value 195 | 196 | elif self._DATA_TYPES == "FLOAT": 197 | if write: 198 | if "h" in str(read).lower(): 199 | read = unpack('!f', bytes.fromhex(str(read).lower().replace("h", "")))[0] 200 | 201 | if "h" in str(write).lower(): 202 | write = unpack('!f', bytes.fromhex(str(write).lower().replace("h", "")))[0] 203 | 204 | if byteorder == 'little': 205 | search_value = pack('f', read) 209 | replace_value = pack('>f', write) 210 | 211 | return search_value, replace_value 212 | 213 | else: 214 | if "h" in str(read).lower(): 215 | read = unpack('!f', bytes.fromhex(str(read).lower().replace("h", "")))[0] 216 | 217 | if byteorder == 'little': 218 | search_value = pack('f', read) 221 | return search_value 222 | 223 | elif self._DATA_TYPES == "DOUBLE": 224 | if write: 225 | if "h" in str(read).lower(): 226 | read = unpack('d', unhexlify(str(read).lower().replace("h", "")))[0] 227 | 228 | if "h" in str(write).lower(): 229 | write = unpack('d', unhexlify(str(write).lower().replace("h", "")))[0] 230 | 231 | if byteorder == 'little': 232 | search_value = pack('d', read) 236 | replace_value = pack('>d', write) 237 | 238 | return search_value, replace_value 239 | 240 | else: 241 | if "h" in str(read).lower(): 242 | read = unpack('d', unhexlify(str(read).lower().replace("h", "")))[0] 243 | 244 | if byteorder == 'little': 245 | search_value = pack('d', read) 248 | return search_value 249 | 250 | elif self._DATA_TYPES == "WORD": 251 | if write: 252 | 253 | if byteorder == 'little': 254 | search_value = pack('h', read) 258 | replace_value = pack('>h', write) 259 | 260 | return search_value, replace_value 261 | 262 | else: 263 | if byteorder == 'little': 264 | search_value = pack('i', read) 267 | return search_value 268 | 269 | elif self._DATA_TYPES == "BYTE": 270 | if write: 271 | if byteorder == 'little': 272 | search_value = pack('b', read) 276 | replace_value = pack('>b', write) 277 | 278 | return search_value, replace_value 279 | 280 | else: 281 | if byteorder == 'little': 282 | search_value = pack('b', read) 285 | return search_value 286 | 287 | elif self._DATA_TYPES == "QWORD": 288 | if write: 289 | if byteorder == 'little': 290 | search_value = pack('q', read) 294 | replace_value = pack('>q', write) 295 | 296 | return search_value, replace_value 297 | 298 | else: 299 | if byteorder == 'little': 300 | search_value = pack('q', read) 303 | return search_value 304 | 305 | elif self._DATA_TYPES == "XOR": 306 | if write: 307 | if byteorder == 'little': 308 | search_value = pack('l', read) 312 | replace_value = pack('>l', write) 313 | 314 | return search_value, replace_value 315 | 316 | else: 317 | if byteorder == 'little': 318 | search_value = pack('l', read) 321 | return search_value 322 | 323 | elif self._DATA_TYPES == "UTF-8": 324 | if write: 325 | search_value = read.encode('utf-8') 326 | replace_value = write.encode('utf-8') 327 | 328 | return search_value, replace_value 329 | 330 | else: 331 | search_value = read.encode('utf-8') 332 | return search_value 333 | 334 | elif self._DATA_TYPES == "UTF-16LE": 335 | if write: 336 | search_value = read.encode('utf-16LE') 337 | replace_value = write.encode('utf-16LE') 338 | 339 | return search_value, replace_value 340 | 341 | else: 342 | search_value = read.encode('utf-16LE') 343 | return search_value 344 | 345 | else: 346 | return None 347 | 348 | def data_type_decoding(self, read: any, write=None) -> any: 349 | """ 350 | Decodes the data values based on the specified data type after reading from memory. 351 | 352 | Args: 353 | read: The value to be decoded after reading from memory. 354 | write: The value to be decoded after writing to memory. 355 | 356 | Returns: 357 | The decoded search value and replace value (if provided). 358 | """ 359 | 360 | if write: 361 | decode_func = unpack 362 | search_value = replace_value = None 363 | decode_format = "" 364 | if self._DATA_TYPES == "DWORD": 365 | decode_format = 'i' 366 | elif self._DATA_TYPES == "FLOAT": 367 | decode_format = 'f' 368 | elif self._DATA_TYPES == "DOUBLE": 369 | decode_format = 'd' 370 | elif self._DATA_TYPES == "WORD": 371 | decode_format = 'h' 372 | elif self._DATA_TYPES == "BYTE": 373 | decode_format = 'b' 374 | elif self._DATA_TYPES == "QWORD": 375 | decode_format = 'q' 376 | elif self._DATA_TYPES == "XOR": 377 | decode_format = 'l' 378 | elif self._DATA_TYPES == "UTF-8": 379 | search_value = read.encode('utf-8') 380 | replace_value = write.encode('utf-8') 381 | elif self._DATA_TYPES == "UTF-16LE": 382 | search_value = read.encode('utf-16LE') 383 | replace_value = write.encode('utf-16LE') 384 | else: 385 | return None 386 | 387 | if search_value is None: 388 | search_value = decode_func(decode_format, read) 389 | if replace_value is None: 390 | replace_value = decode_func(decode_format, write) 391 | 392 | return search_value, replace_value 393 | else: 394 | decode_func = unpack 395 | decode_format = None 396 | search_value = None 397 | 398 | if self._DATA_TYPES == "DWORD": 399 | decode_format = 'i' 400 | elif self._DATA_TYPES == "FLOAT": 401 | decode_format = 'f' 402 | elif self._DATA_TYPES == "DOUBLE": 403 | decode_format = 'd' 404 | elif self._DATA_TYPES == "WORD": 405 | decode_format = 'h' 406 | elif self._DATA_TYPES == "BYTE": 407 | decode_format = 'b' 408 | elif self._DATA_TYPES == "QWORD": 409 | decode_format = 'q' 410 | elif self._DATA_TYPES == "XOR": 411 | decode_format = 'l' 412 | elif self._DATA_TYPES == "UTF-8": 413 | search_value = read.encode('utf-8') 414 | elif self._DATA_TYPES == "UTF-16LE": 415 | search_value = read.encode('utf-16LE') 416 | else: 417 | return None 418 | 419 | if decode_format: 420 | search_value = decode_func(decode_format, read) 421 | 422 | return search_value 423 | 424 | def data_type_bytes(self) -> int: 425 | """ 426 | Returns the number of bytes occupied by the current data type. 427 | 428 | Returns: 429 | The number of bytes occupied by the current data type. 430 | """ 431 | data_type_sizes = { 432 | "DWORD": 4, 433 | "FLOAT": 4, 434 | "DOUBLE": 8, 435 | "WORD": 2, 436 | "BYTE": 1, 437 | "QWORD": 8, 438 | "XOR": 4, 439 | "UTF-8": 0, 440 | "UTF-16LE": 0 441 | } 442 | 443 | return data_type_sizes.get(self._DATA_TYPES, -1) 444 | 445 | def init_tool(self, pMAP=PMAP()) -> None: 446 | """ 447 | Initializes the memory tool with the specified memory mapping options. 448 | 449 | Args: 450 | pMAP: The memory mapping options. 451 | 452 | Returns: 453 | None. 454 | """ 455 | self._MAPS_ADDR = self.identify_dict(pMAP) 456 | self._DATA_BYTE = self.data_type_bytes() 457 | self._PID = self.get_pid(self._PKG_NAME) 458 | 459 | def get_variables(self, is_speed=False, is_pkg=False, is_data_type=False, is_workers=False, is_map_addr=False, 460 | is_data_byte=False, is_pid=False) -> any: 461 | """ 462 | Retrieves the specified variable based on the provided flags. 463 | 464 | Args: 465 | is_speed: Flag indicating whether to retrieve the speed mode value. 466 | is_pkg: Flag indicating whether to retrieve the package name value. 467 | is_data_type: Flag indicating whether to retrieve the data type value. 468 | is_workers: Flag indicating whether to retrieve the number of workers value. 469 | is_map_addr: Flag indicating whether to retrieve the memory map addresses value. 470 | is_data_byte: Flag indicating whether to retrieve the data type bytes value. 471 | is_pid: Flag indicating whether to retrieve the process ID (PID) value. 472 | 473 | Returns: 474 | The requested variable value. 475 | """ 476 | variables = { 477 | is_speed: self._IS_SPEED_MODE, 478 | is_pkg: self._PKG_NAME, 479 | is_workers: self._TOTAL_WORKERS, 480 | is_data_type: self._DATA_TYPES, 481 | is_map_addr: self._MAPS_ADDR, 482 | is_data_byte: self._DATA_BYTE, 483 | is_pid: self._PID 484 | } 485 | 486 | return variables.get(True, None) 487 | -------------------------------------------------------------------------------- /androidMemoryTool/LinuxAPI/ThreadingController.py: -------------------------------------------------------------------------------- 1 | """ 2 | /* 3 | * Date : 2023/09/30 4 | * Version : 0.6 5 | * Author : Abdul Moez 6 | * Email : abdulmoez123456789@gmail.com 7 | * Affiliation : Undergraduate at Government College University (GCU) Lahore, Pakistan 8 | * GitHub : https://github.com/Anonym0usWork1221/android-memorytool 9 | * 10 | * Description: 11 | * This code is governed by the GNU General Public License, version 3 or later. 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program. If not, see . 14 | */ 15 | """ 16 | 17 | from .additional_features import AdditionalFeatures 18 | from .search_and_writers import SearchAndWrite 19 | from .search_and_readers import SearchAndRead 20 | from .DataClasses import DataClasses 21 | from concurrent.futures import ThreadPoolExecutor 22 | 23 | 24 | class FastSearchAlgo(DataClasses): 25 | """A class that implements fast search algorithms for text, values, and pattern finding.""" 26 | 27 | _SearchAndWriteObject = SearchAndWrite() 28 | _SearchAndReadObject = SearchAndRead() 29 | _AdditionalFeaturesObject = AdditionalFeatures() 30 | 31 | def __init__(self): 32 | super(FastSearchAlgo, self).__init__() 33 | 34 | def fast_search_algorithms_text(self, pid: str, addr: list, read: any, write=None) -> None: 35 | """ 36 | Performs a fast search for text in the specified memory address range of a process. 37 | 38 | Args: 39 | pid (str): The process ID. 40 | addr (list): A list of memory addresses to search in. 41 | read (any): The text to search for. 42 | write (Optional): The replacement text for writing to memory (default: None). 43 | 44 | Returns: 45 | None 46 | """ 47 | if write: 48 | with ThreadPoolExecutor(max_workers=super().get_variables(is_workers=True)) as executor: 49 | executor.submit(self._SearchAndWriteObject.speed_search_and_write_text, pid, addr, read, write) 50 | else: 51 | with ThreadPoolExecutor(max_workers=super().get_variables(is_workers=True)) as executor: 52 | executor.submit(self._SearchAndReadObject.speed_search_and_read_text, pid, addr, read) 53 | 54 | def fast_search_algorithms_value(self, pid: str, addr: list, read: any, buf: int, write=None) -> None: 55 | """ 56 | Performs a fast search for values in the specified memory address range of a process. 57 | 58 | Args: 59 | pid (str): The process ID. 60 | addr (list): A list of memory addresses to search in. 61 | read (any): The value to search for. 62 | buf (int): The buffer size for reading memory. 63 | write (Optional): The replacement value for writing to memory (default: None). 64 | 65 | Returns: 66 | None 67 | """ 68 | if write: 69 | with ThreadPoolExecutor(max_workers=super().get_variables(is_workers=True)) as executor: 70 | executor.submit(self._SearchAndWriteObject.speed_search_and_write, pid, addr, buf, read, write) 71 | else: 72 | with ThreadPoolExecutor(max_workers=super().get_variables(is_workers=True)) as executor: 73 | executor.submit(self._SearchAndReadObject.speed_search_and_read, pid, addr, buf, read) 74 | 75 | def fast_group_search_algorithms_value(self, pid: str, address_list: list, buf: int, 76 | read_list: list, range_val: int, write=None) -> None: 77 | """ 78 | Performs a fast group search for values in the specified memory address range of a process. 79 | 80 | Args: 81 | pid (str): The process ID. 82 | address_list (list): List of memory address ranges to search and write. 83 | buf (int): The number of bytes to read at once. 84 | read_list (list): List of binary values to search for. 85 | range_val (int): Maximum distance between the group search values. 86 | write (any) (optional): The binary value to write. 87 | 88 | Returns: 89 | None 90 | """ 91 | 92 | if write: 93 | with ThreadPoolExecutor(max_workers=super().get_variables(is_workers=True)) as executor: 94 | executor.submit(self._SearchAndWriteObject.speed_group_search_and_write(pid, address_list, buf, 95 | read_list, write, range_val)) 96 | else: 97 | with ThreadPoolExecutor(max_workers=super().get_variables(is_workers=True)) as executor: 98 | executor.submit(self._SearchAndReadObject.speed_group_search_and_read(pid, address_list, buf, 99 | read_list, range_val)) 100 | 101 | def fast_search_algorithms_pattern_finding(self, pid: str, addr: list, buf: int, hex_pattern: str, 102 | replace_pattern: str = None) -> None: 103 | """ 104 | Executes fast search algorithms for finding a hexadecimal pattern in memory addresses. 105 | 106 | Args: 107 | pid (str): The process ID. 108 | addr (list): The list of memory addresses to search. 109 | buf (int): The buffer size for reading memory. 110 | hex_pattern (str): The hexadecimal pattern to search for. 111 | replace_pattern (str, optional): The hexadecimal pattern to replace the found pattern with. 112 | Defaults to None. 113 | 114 | Returns: 115 | None. 116 | """ 117 | 118 | if replace_pattern: 119 | with ThreadPoolExecutor(max_workers=super().get_variables(is_workers=True)) as executor: 120 | executor.submit(self._AdditionalFeaturesObject.speed_find_and_replace_hexadecimal_pattern, pid, 121 | addr, buf, hex_pattern, replace_pattern) 122 | else: 123 | with ThreadPoolExecutor(max_workers=super().get_variables(is_workers=True)) as executor: 124 | executor.submit(self._AdditionalFeaturesObject.speed_find_hexadecimal_pattern, pid, 125 | addr, buf, hex_pattern) 126 | -------------------------------------------------------------------------------- /androidMemoryTool/LinuxAPI/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anonym0usWork1221/android-memorytool/d777a7231100eed31cf9af36d1b73de63961da06/androidMemoryTool/LinuxAPI/__init__.py -------------------------------------------------------------------------------- /androidMemoryTool/LinuxAPI/additional_features.py: -------------------------------------------------------------------------------- 1 | """ 2 | /* 3 | * Date : 2023/09/30 4 | * Version : 0.6 5 | * Author : Abdul Moez 6 | * Email : abdulmoez123456789@gmail.com 7 | * Affiliation : Undergraduate at Government College University (GCU) Lahore, Pakistan 8 | * GitHub : https://github.com/Anonym0usWork1221/android-memorytool 9 | * 10 | * Description: 11 | * This code is governed by the GNU General Public License, version 3 or later. 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program. If not, see . 14 | */ 15 | """ 16 | 17 | # ! /usr/bin/env python 18 | 19 | from functools import wraps 20 | from queue import Queue 21 | from re import search 22 | 23 | 24 | def store_in_queue(function) -> any: 25 | """ 26 | Decorator that stores the return value of a function in a queue. 27 | 28 | Args: 29 | function: The function to be decorated. 30 | 31 | Returns: 32 | The decorated function. 33 | """ 34 | 35 | @wraps(function) 36 | def wrapper(self, *args): 37 | self._returned_values.put(function(self, *args)) 38 | 39 | return wrapper 40 | 41 | 42 | class AdditionalFeatures: 43 | """ 44 | Class for additional features of memory manipulation. 45 | 46 | Methods: 47 | get_pattern_finder_values() -> list: 48 | Retrieves the values from the pattern finder operation. 49 | 50 | reset_queue() -> None: 51 | Resets the queue of returned values. 52 | 53 | speed_find_hexadecimal_pattern(pid: str, address_list: list, buf: int, hex_pattern: str) -> list: 54 | Searches for a hexadecimal pattern in memory addresses using speed optimization. 55 | 56 | find_hexadecimal_pattern(pid: str, address_list: list, buf: int, hex_pattern: str) -> list: 57 | Searches for a hexadecimal pattern in memory addresses. 58 | """ 59 | 60 | def __init__(self): 61 | pass 62 | 63 | _returned_values = Queue() 64 | 65 | def get_pattern_finder_values(self) -> list: 66 | """ 67 | Retrieves the values from the pattern finder operation. 68 | 69 | Returns: 70 | The values from the pattern finder operation. 71 | """ 72 | return self._returned_values.get() 73 | 74 | def reset_queue(self) -> None: 75 | """ 76 | Resets the queue of returned values. 77 | """ 78 | self._returned_values.queue.clear() 79 | 80 | @store_in_queue 81 | def speed_find_hexadecimal_pattern(self, pid: str, address_list: list, buf: int, hex_pattern: str) -> list: 82 | """ 83 | Searches for a hexadecimal pattern in memory addresses using speed optimization. 84 | 85 | Args: 86 | pid (str): The process ID. 87 | address_list (list): The list of memory addresses to search. 88 | buf (int): The buffer size for reading memory. 89 | hex_pattern (str): The hexadecimal pattern to search for. 90 | 91 | Returns: 92 | list: A list containing the offsets, total values found, and founded patterns. 93 | """ 94 | 95 | offsets = [] 96 | founded_patterns = [] 97 | total_values_found = 0 98 | 99 | with open(f"/proc/{pid}/mem", "rb+") as mem_file: 100 | for address in address_list: 101 | partitions = address.split("-") 102 | start_addr = int(partitions[0], 16) 103 | end_addr = int(partitions[1], 16) 104 | mem_file.seek(start_addr) 105 | total_bytes = end_addr - start_addr 106 | current_bytes = 0 107 | try: 108 | while current_bytes < total_bytes + 1: 109 | current_bytes += buf 110 | read_bytes = mem_file.read(buf) 111 | read_hex = read_bytes.hex().upper() 112 | # print(f"{read_hex}\t{hex_pattern}") 113 | if search(hex_pattern, read_hex): 114 | founded_patterns.append(read_hex) 115 | current_pos = mem_file.tell() - buf 116 | mem_file.seek(current_pos) 117 | mem_file.read(buf) 118 | offsets.append(hex(current_pos)) 119 | total_values_found += 1 120 | except Exception as e: 121 | print("[*] Exception ", e) 122 | 123 | return [offsets, total_values_found, founded_patterns] 124 | 125 | @staticmethod 126 | def find_hexadecimal_pattern(pid: str, address_list: list, buf: int, hex_pattern: str) -> list: 127 | """ 128 | Searches for a hexadecimal pattern in memory addresses. 129 | 130 | Args: 131 | pid (str): The process ID. 132 | address_list (list): The list of memory addresses to search. 133 | buf (int): The buffer size for reading memory. 134 | hex_pattern (str): The hexadecimal pattern to search for. 135 | 136 | Returns: 137 | list: A list containing the offsets, total values found, and founded patterns. 138 | """ 139 | 140 | offsets = [] 141 | founded_patterns = [] 142 | total_values_found = 0 143 | 144 | with open(f"/proc/{pid}/mem", "rb+") as mem_file: 145 | for address in address_list: 146 | partitions = address.split("-") 147 | start_addr = int(partitions[0], 16) 148 | end_addr = int(partitions[1], 16) 149 | mem_file.seek(start_addr) 150 | total_bytes = end_addr - start_addr 151 | current_bytes = 0 152 | try: 153 | while current_bytes < total_bytes + 1: 154 | current_bytes += buf 155 | read_bytes = mem_file.read(buf) 156 | read_hex = read_bytes.hex().upper() 157 | if search(hex_pattern, read_hex): 158 | founded_patterns.append(read_hex) 159 | current_pos = mem_file.tell() - buf 160 | mem_file.seek(current_pos) 161 | mem_file.read(buf) 162 | offsets.append(hex(current_pos)) 163 | total_values_found += 1 164 | except Exception as e: 165 | print("[*] Exception ", e) 166 | 167 | return [offsets, total_values_found, founded_patterns] 168 | 169 | @store_in_queue 170 | def speed_find_and_replace_hexadecimal_pattern(self, pid: str, address_list: list, buf: int, 171 | search_pattern: str, replace_pattern: str) -> list: 172 | """ 173 | Searches for a hexadecimal pattern in memory addresses and replaces it with a new pattern using speed optimization. 174 | 175 | Args: 176 | pid (str): The process ID. 177 | address_list (list): The list of memory addresses to search and replace. 178 | buf (int): The buffer size for reading memory. 179 | search_pattern (str): The hexadecimal pattern to search for. 180 | replace_pattern (str): The hexadecimal pattern to replace the found pattern with. 181 | 182 | Returns: 183 | list: A list containing the offsets, total values found, and founded patterns. 184 | """ 185 | 186 | offsets = [] 187 | founded_patterns = [] 188 | total_values_found = 0 189 | 190 | with open(f"/proc/{pid}/mem", "rb+") as mem_file: 191 | for address in address_list: 192 | partitions = address.split("-") 193 | start_addr = int(partitions[0], 16) 194 | end_addr = int(partitions[1], 16) 195 | mem_file.seek(start_addr) 196 | total_bytes = end_addr - start_addr 197 | current_bytes = 0 198 | try: 199 | while current_bytes < total_bytes + 1: 200 | current_bytes += buf 201 | read_bytes = mem_file.read(buf) 202 | read_hex = read_bytes.hex().upper() 203 | if search(search_pattern, read_hex): 204 | founded_patterns.append(read_hex) 205 | current_pos = mem_file.tell() - buf 206 | mem_file.seek(current_pos) 207 | byte_array_conversion = bytearray.fromhex(replace_pattern) 208 | mem_file.write(byte_array_conversion) 209 | offsets.append(hex(current_pos)) 210 | total_values_found += 1 211 | except Exception as e: 212 | print("[*] Exception ", e) 213 | 214 | return [offsets, total_values_found, founded_patterns] 215 | 216 | @staticmethod 217 | def find_and_replace_hexadecimal_pattern(pid: str, address_list: list, buf: int, 218 | search_pattern: str, replace_pattern: str) -> list: 219 | """ 220 | Searches for a hexadecimal pattern in memory addresses and replaces it with a new pattern. 221 | 222 | Args: 223 | pid (str): The process ID. 224 | address_list (list): The list of memory addresses to search and replace. 225 | buf (int): The buffer size for reading memory. 226 | search_pattern (str): The hexadecimal pattern to search for. 227 | replace_pattern (str): The hexadecimal pattern to replace the found pattern with. 228 | 229 | Returns: 230 | list: A list containing the offsets, total values found, and founded patterns. 231 | """ 232 | offsets = [] 233 | founded_patterns = [] 234 | total_values_found = 0 235 | 236 | with open(f"/proc/{pid}/mem", "rb+") as mem_file: 237 | for address in address_list: 238 | partitions = address.split("-") 239 | start_addr = int(partitions[0], 16) 240 | end_addr = int(partitions[1], 16) 241 | mem_file.seek(start_addr) 242 | total_bytes = end_addr - start_addr 243 | current_bytes = 0 244 | try: 245 | while current_bytes < total_bytes + 1: 246 | current_bytes += buf 247 | read_bytes = mem_file.read(buf) 248 | read_hex = read_bytes.hex().upper() 249 | if search(search_pattern, read_hex): 250 | founded_patterns.append(read_hex) 251 | current_pos = mem_file.tell() - buf 252 | mem_file.seek(current_pos) 253 | byte_array_conversion = bytearray.fromhex(replace_pattern) 254 | mem_file.write(byte_array_conversion) 255 | offsets.append(hex(current_pos)) 256 | total_values_found += 1 257 | except Exception as e: 258 | print("[*] Exception ", e) 259 | 260 | return [offsets, total_values_found, founded_patterns] 261 | -------------------------------------------------------------------------------- /androidMemoryTool/LinuxAPI/libs_read_writers.py: -------------------------------------------------------------------------------- 1 | """ 2 | /* 3 | * Date : 2023/09/30 4 | * Version : 0.6 5 | * Author : Abdul Moez 6 | * Email : abdulmoez123456789@gmail.com 7 | * Affiliation : Undergraduate at Government College University (GCU) Lahore, Pakistan 8 | * GitHub : https://github.com/Anonym0usWork1221/android-memorytool 9 | * 10 | * Description: 11 | * This code is governed by the GNU General Public License, version 3 or later. 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program. If not, see . 14 | */ 15 | """ 16 | 17 | 18 | # ! /usr/bin/env python 19 | class LibsControllers: 20 | """ 21 | Class for controlling memory operations on a process. 22 | 23 | Methods: 24 | write_lib_offsets(pid: str, base_address: str, offset: str, value: any) -> bool: 25 | Writes a value to a specific memory address in the memory of a process. 26 | 27 | read_lib_offsets(pid: str, base_address: str, offset: str, buf: int) -> any: 28 | Reads the value from a specific memory address in the memory of a process. 29 | 30 | address_refiners(pid: str, list_address: list, buf: int, changed_value: any) -> list: 31 | Refines a list of memory addresses by checking if the value at each address matches a specified value. 32 | 33 | raw_dumper(pid: str, address: list) -> bytes: 34 | Dumps a range of memory addresses from a process. 35 | """ 36 | 37 | def __init__(self): 38 | pass 39 | 40 | @staticmethod 41 | def write_lib_offsets(pid: str, base_address: str, offset: str, value: any) -> bool: 42 | """ 43 | Writes a value to a specific memory address in the memory of a process. 44 | 45 | Args: 46 | pid (str): The process ID. 47 | base_address (str): The base memory address in hexadecimal format. 48 | offset (str): The offset from the base address in hexadecimal format. 49 | value (any): The value to be written to the memory address. 50 | 51 | Returns: 52 | bool: True if the write operation is successful, False otherwise. 53 | """ 54 | 55 | write_address = int(base_address, 16) + int(offset, 16) 56 | mem_file = open(f"/proc/{pid}/mem", "rb+") 57 | try: 58 | mem_file.seek(write_address) 59 | mem_file.write(value) 60 | mem_file.close() 61 | return True 62 | except IOError as e: 63 | if not isinstance(base_address, str) or not isinstance(offset, str): 64 | print(f"[-] Exception: {e}\n[-] This error mostly occur due to invalid type of parameters," 65 | f" pass the base_address and offset as a hex string e.g: '0x5645c973ad44'") 66 | return False 67 | 68 | @staticmethod 69 | def read_lib_offsets(pid: str, base_address: str, offset: str, buf: int) -> any: 70 | """ 71 | Reads the value from a specific memory address in the memory of a process. 72 | 73 | Args: 74 | pid (str): The process ID. 75 | base_address (str): The base memory address in hexadecimal format. 76 | offset (str): The offset from the base address in hexadecimal format. 77 | buf (int): The number of bytes to read from the memory address. 78 | 79 | Returns: 80 | any: The value read from the memory address. 81 | """ 82 | 83 | read_address = int(base_address, 16) + int(offset, 16) 84 | mem_file = open(f"/proc/{pid}/mem", "rb+") 85 | try: 86 | mem_file.seek(read_address) 87 | founded_value_on_address = mem_file.read(buf) 88 | mem_file.close() 89 | return founded_value_on_address 90 | except IOError as e: 91 | if not isinstance(base_address, str) or not isinstance(offset, str): 92 | print(f"[-] Exception: {e}\n[-] This error mostly occur due to invalid type of parameters," 93 | f" pass the base_address and offset as a hex string e.g: '0x5645c973ad44'") 94 | return None 95 | 96 | def address_refiners(self, pid: str, list_address: list, buf: int, changed_value: any) -> list: 97 | """ 98 | Refines a list of memory addresses by checking if the value at each address matches a specified value. 99 | 100 | Args: 101 | pid (str): The process ID. 102 | list_address (list): List of memory addresses to be refined. 103 | buf (int): The number of bytes to read from each memory address. 104 | changed_value (any): The value to compare against the read values. 105 | 106 | Returns: 107 | list: List of refined memory addresses where the read value matches the changed value. 108 | """ 109 | 110 | refined_address = [] 111 | for address in list_address: 112 | read_value = self.read_lib_offsets(pid, '0x0', address, buf) 113 | if read_value == changed_value: 114 | refined_address.append(address) 115 | return refined_address 116 | 117 | @staticmethod 118 | def raw_dumper(pid: str, address: list) -> bytes: 119 | """ 120 | Dumps a range of memory addresses from a process. 121 | 122 | Args: 123 | pid (str): The process ID. 124 | address (list): List of memory address ranges to dump. 125 | 126 | Returns: 127 | bytes: The dumped bytes from the specified memory address range. 128 | """ 129 | 130 | start_addr = int(address[0][0], 16) 131 | end_addr = int(address[len(address) - 1][1], 16) 132 | mem_file = open(f"/proc/{pid}/mem", "rb+") 133 | mem_file.seek(start_addr) 134 | total_bytes = end_addr - start_addr 135 | byte_strings = mem_file.read(total_bytes) 136 | return byte_strings 137 | -------------------------------------------------------------------------------- /androidMemoryTool/LinuxAPI/mapping.py: -------------------------------------------------------------------------------- 1 | """ 2 | /* 3 | * Date : 2023/09/30 4 | * Version : 0.6 5 | * Author : Abdul Moez 6 | * Email : abdulmoez123456789@gmail.com 7 | * Affiliation : Undergraduate at Government College University (GCU) Lahore, Pakistan 8 | * GitHub : https://github.com/Anonym0usWork1221/android-memorytool 9 | * 10 | * Description: 11 | * This code is governed by the GNU General Public License, version 3 or later. 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program. If not, see . 14 | */ 15 | """ 16 | 17 | """ 18 | Mapping Structure: 19 | 20 | The mapping structure provides information about the memory regions allocated to a process, including their 21 | permissions, offsets, devices, inodes, and pathnames. 22 | 23 | Mapping Format: 24 | [address] [perms] [offset] [device] [inode] [pathname] 25 | 26 | Mapping Fields: 27 | - address: The range of memory addresses covered by the mapping. 28 | - perms: The permissions of the memory region (e.g., read, write, execute). 29 | - offset: The offset of the mapping. 30 | - device: The device associated with the mapping. 31 | - inode: The inode of the file associated with the mapping (if applicable). 32 | - pathname: The pathname of the file associated with the mapping (if applicable). 33 | 34 | Mapping Tags: 35 | - O->others:others: Anonymous mappings without specific tags (e.g., --p, anon, system/framework, etc.). 36 | - Jh->:rw-p, anon:dalvik-main space: Dalvik heap mappings in the Java space. 37 | - Xs->system: --xp, lib, .so, system: System libraries and shared objects mappings. 38 | - J->Java: rw-p, javalib: Java library mappings. 39 | - ch -> c++heap: C++ heap mappings. 40 | - ca -> c++ alloc: Anonymous C++ allocations mappings. 41 | - cd -> c++ data: C++ data mappings. 42 | - cb -> c++ bss: C++ BSS (Block Started by Symbol) mappings. 43 | - PS -> PPSSPP: PPSSPP emulator mappings. 44 | - A -> Anonymous: Anonymous mappings with read-write permissions only. 45 | - S -> Stack: Stack mappings. 46 | - As -> Ashmem: Ashmem (Anonymous Shared Memory) mappings. 47 | - V -> Video: Video mappings. 48 | - B -> Bad(dang): Bad or dangerous mappings. 49 | - Xa -> code app:r-xp, lib, .so, .jar: Application code mappings (read-executable). 50 | 51 | Order of Fields in map.txt: 52 | ['address', 'perms', 'offset', 'device', 'inode', 'pathname'] 53 | 54 | Example: 55 | ['76a01ae000-76a01af000', 'r--p', '00000000', '00:00', '26', '[anon:atexit handler]'] 56 | 57 | The mapping above represents an anonymous mapping with read-only permissions, no offset, device '00:00', 58 | inode '26', and no associated file pathname. 59 | 60 | """ 61 | 62 | # order 63 | ''' 64 | address perms offset device inode pathname 65 | 0 1 2 3 4 5 66 | ['76a01ae000-76a01af000', 'r--p', '00000000', '00:00', '26', '[anon:atexit handler]'] 67 | 68 | A -> 4[] 69 | ''' 70 | 71 | 72 | # ! /usr/bin/env python 73 | class Mapping: 74 | """Class for extracting memory mappings of a process using /proc/[pid]/maps file.""" 75 | 76 | def __init__(self): 77 | pass 78 | 79 | @staticmethod 80 | def _read_map_file(pid: str): 81 | """ 82 | Reads the mapping file for the specified process ID (pid). 83 | Args: 84 | pid (str): The process ID. 85 | 86 | Returns: 87 | list: A list of strings representing the lines read from the mapping file. 88 | """ 89 | 90 | with open(f"/proc/{pid}/maps", "r") as map_file: 91 | return map_file.readlines() 92 | 93 | @staticmethod 94 | def _parse_mapping_line(line: str): 95 | """ 96 | Parses a mapping line and extracts relevant information. 97 | Args: 98 | line (str): A line from the mapping file. 99 | 100 | Returns: 101 | tuple: A tuple containing temporary details, region, and permissions extracted from the line. 102 | """ 103 | temp_details = line.split(" ", 5) 104 | region = temp_details[-1] 105 | perm = temp_details[1] 106 | return temp_details, region, perm 107 | 108 | @staticmethod 109 | def _filter_mappings(lines: list, condition: callable): 110 | """ 111 | Filters the mappings based on a given condition. 112 | Args: 113 | lines (list): A list of strings representing the lines read from the mapping file. 114 | condition (callable): A lambda function that takes temporary details, region, and permissions as input 115 | and returns True or False based on the condition. 116 | 117 | Returns: 118 | dict: A dictionary containing filtered mapping details with keys 'address', 'permissions', 119 | and 'allocated'. 120 | """ 121 | 122 | details = {"address": [], "permissions": [], "allocated": []} 123 | for line in lines: 124 | temp_details, region, perm = Mapping._parse_mapping_line(line) 125 | if condition(temp_details, region, perm): 126 | details["address"].append(temp_details[0]) 127 | details["permissions"].append(temp_details[1]) 128 | details["allocated"].append(region) 129 | return details 130 | 131 | @staticmethod 132 | def mapping_xa(pid: str) -> dict: 133 | """ 134 | Returns the mappings with specific conditions related to libraries in the system. 135 | 136 | Args: 137 | pid (str): The process ID. 138 | 139 | Returns: 140 | dict: A dictionary containing mapping details for libraries with specific conditions. 141 | """ 142 | 143 | lines = Mapping._read_map_file(pid) 144 | condition = lambda temp_details, region, perm: \ 145 | ("lib" in region) and ("rw" in perm) and (".so" in region) and ("system" in region) 146 | return Mapping._filter_mappings(lines, condition) 147 | 148 | @staticmethod 149 | def mapping_dump_libs(pid: str, lib_name: str) -> list: 150 | """ 151 | Returns the addresses of pages belonging to a specific library in the process. 152 | 153 | Args: 154 | pid (str): The process ID. 155 | lib_name (str): The name of the library. 156 | 157 | Returns: 158 | list: A list of addresses belonging to the specified library. 159 | """ 160 | 161 | lines = Mapping._read_map_file(pid) 162 | address = [] 163 | for line in lines: 164 | page = line.split() 165 | module = page[-1] 166 | if lib_name in module: 167 | address.append(page[0].split('-')) 168 | return address 169 | 170 | @staticmethod 171 | def mapping_ca(pid: str) -> dict: 172 | """ 173 | Returns the mappings with specific conditions related to libc_malloc and anonymous regions. 174 | 175 | Args: 176 | pid (str): The process ID. 177 | 178 | Returns: 179 | dict: A dictionary containing mapping details for libc_malloc and anonymous regions. 180 | """ 181 | 182 | lines = Mapping._read_map_file(pid) 183 | condition = lambda temp_details, region, perm: \ 184 | ("anon" in region) and ("rw" in perm) and ("libc_malloc" in region) 185 | return Mapping._filter_mappings(lines, condition) 186 | 187 | @staticmethod 188 | def mapping_a(pid: str) -> dict: 189 | """ 190 | Returns the mappings with specific conditions related to regions with fewer than 46 details and 191 | read-write permissions. 192 | 193 | Args: 194 | pid (str): The process ID. 195 | 196 | Returns: 197 | dict: A dictionary containing mapping details for regions with specific conditions. 198 | """ 199 | 200 | lines = Mapping._read_map_file(pid) 201 | condition = lambda temp_details, region, perm: (len(temp_details) < 46) and ("rw" in perm) 202 | return Mapping._filter_mappings(lines, condition) 203 | 204 | @staticmethod 205 | def mapping_b(pid: str) -> dict: 206 | """ 207 | Returns the mappings with specific conditions related to regions containing 'fonts' in the name 208 | and read-write permissions. 209 | 210 | Args: 211 | pid (str): The process ID. 212 | 213 | Returns: 214 | dict: A dictionary containing mapping details for regions with specific conditions. 215 | """ 216 | 217 | lines = Mapping._read_map_file(pid) 218 | condition = lambda temp_details, region, perm: ("fonts" in region) and ("rw" in perm) 219 | return Mapping._filter_mappings(lines, condition) 220 | 221 | @staticmethod 222 | def mapping_cb(pid: str) -> dict: 223 | """ 224 | Returns the mappings with specific conditions related to the '[anon:.bss]' region and 225 | read-write permissions. 226 | 227 | Args: 228 | pid (str): The process ID. 229 | 230 | Returns: 231 | dict: A dictionary containing mapping details for the specified region. 232 | """ 233 | 234 | lines = Mapping._read_map_file(pid) 235 | condition = lambda temp_details, region, perm: ("[anon:.bss]" in region) and ("rw" in perm) 236 | return Mapping._filter_mappings(lines, condition) 237 | 238 | @staticmethod 239 | def mapping_cd(pid: str) -> dict: 240 | """ 241 | Returns the mappings with specific conditions related to regions starting with '/data/app/' and 242 | read-write permissions. 243 | 244 | Args: 245 | pid (str): The process ID. 246 | 247 | Returns: 248 | dict: A dictionary containing mapping details for the specified regions. 249 | """ 250 | 251 | lines = Mapping._read_map_file(pid) 252 | condition = lambda temp_details, region, perm: ("/data/app/" in region) and ("rw" in perm) 253 | return Mapping._filter_mappings(lines, condition) 254 | 255 | @staticmethod 256 | def mapping_ch(pid: str) -> dict: 257 | """ 258 | Returns the mappings with specific conditions related to the '[heap]' region and 259 | read-write permissions. 260 | 261 | Args: 262 | pid (str): The process ID. 263 | 264 | Returns: 265 | dict: A dictionary containing mapping details for the specified region. 266 | """ 267 | 268 | lines = Mapping._read_map_file(pid) 269 | condition = lambda temp_details, region, perm: ("[heap]" in region) and ("rw" in perm) 270 | return Mapping._filter_mappings(lines, condition) 271 | 272 | @staticmethod 273 | def mapping_jh(pid: str) -> dict: 274 | """ 275 | Returns the mappings with specific conditions related to regions starting with '/dev/ashmem/' and 276 | read-write permissions. 277 | 278 | Args: 279 | pid (str): The process ID. 280 | 281 | Returns: 282 | dict: A dictionary containing mapping details for the specified regions. 283 | """ 284 | 285 | lines = Mapping._read_map_file(pid) 286 | condition = lambda temp_details, region, perm: ("/dev/ashmem/" in region) and ("rw" in perm) 287 | return Mapping._filter_mappings(lines, condition) 288 | 289 | @staticmethod 290 | def mapping_xs(pid: str) -> dict: 291 | """ 292 | Returns the mappings with specific conditions related to regions starting with '/system' and 293 | read-write permissions. 294 | 295 | Args: 296 | pid (str): The process ID. 297 | 298 | Returns: 299 | dict: A dictionary containing mapping details for the specified regions. 300 | """ 301 | 302 | lines = Mapping._read_map_file(pid) 303 | condition = lambda temp_details, region, perm: ("/system" in region) and ("rw" in perm) 304 | return Mapping._filter_mappings(lines, condition) 305 | 306 | @staticmethod 307 | def mapping_s(pid: str) -> dict: 308 | """ 309 | Returns the mappings with specific conditions related to the '[stack]' region and 310 | read-write permissions. 311 | 312 | Args: 313 | pid (str): The process ID. 314 | 315 | Returns: 316 | dict: A dictionary containing mapping details for the specified region. 317 | """ 318 | 319 | lines = Mapping._read_map_file(pid) 320 | condition = lambda temp_details, region, perm: ("[stack]" in region) and ("rw" in perm) 321 | return Mapping._filter_mappings(lines, condition) 322 | 323 | @staticmethod 324 | def mapping_as(pid: str) -> dict: 325 | """ 326 | Returns the mappings with specific conditions related to regions starting with '/dev/ashmem/' and 327 | containing 'dalvik' in the name and read-write permissions. 328 | 329 | Args: 330 | pid (str): The process ID. 331 | 332 | Returns: 333 | dict: A dictionary containing mapping details for the specified regions. 334 | """ 335 | 336 | lines = Mapping._read_map_file(pid) 337 | condition = lambda temp_details, region, perm: \ 338 | ("/dev/ashmem/" in region and "dalvik" in region) and ("rw" in perm) 339 | return Mapping._filter_mappings(lines, condition) 340 | 341 | @staticmethod 342 | def mapping_j(pid: str) -> dict: 343 | """ 344 | Returns the mappings with specific conditions related to regions containing 'javalib' and 345 | 'dalvik' in the name and read-write permissions. 346 | 347 | Args: 348 | pid (str): The process ID. 349 | 350 | Returns: 351 | dict: A dictionary containing mapping details for the specified regions. 352 | """ 353 | 354 | lines = Mapping._read_map_file(pid) 355 | condition = lambda temp_details, region, perm: \ 356 | ("javalib" in region and "dalvik" in region) and ("rw" in perm) 357 | return Mapping._filter_mappings(lines, condition) 358 | 359 | @staticmethod 360 | def mapping_v(pid: str) -> dict: 361 | """ 362 | Returns the mappings with specific conditions related to regions containing 'kgsl-3d0' in the name 363 | and read-write permissions. 364 | 365 | Args: 366 | pid (str): The process ID. 367 | 368 | Returns: 369 | dict: A dictionary containing mapping details for the specified regions. 370 | """ 371 | 372 | lines = Mapping._read_map_file(pid) 373 | condition = lambda temp_details, region, perm: ("kgsl-3d0" in region) and ("rw" in perm) 374 | return Mapping._filter_mappings(lines, condition) 375 | 376 | @staticmethod 377 | def mapping_all(pid: str) -> dict: 378 | """ 379 | Returns all the mappings with read-write permissions. 380 | 381 | Args: 382 | pid (str): The process ID. 383 | 384 | Returns: 385 | dict: A dictionary containing all mapping details with keys 'address', 'permissions', 386 | and 'allocated'. 387 | """ 388 | 389 | lines = Mapping._read_map_file(pid) 390 | condition = lambda temp_details, region, perm: "rw" in perm 391 | return Mapping._filter_mappings(lines, condition) 392 | 393 | 394 | # if __name__ == '__main__': 395 | # print(Mapping().mapping_a(pid="9628")) 396 | -------------------------------------------------------------------------------- /androidMemoryTool/LinuxAPI/search_and_readers.py: -------------------------------------------------------------------------------- 1 | """ 2 | /* 3 | * Date : 2023/09/30 4 | * Version : 0.6 5 | * Author : Abdul Moez 6 | * Email : abdulmoez123456789@gmail.com 7 | * Affiliation : Undergraduate at Government College University (GCU) Lahore, Pakistan 8 | * GitHub : https://github.com/Anonym0usWork1221/android-memorytool 9 | * 10 | * Description: 11 | * This code is governed by the GNU General Public License, version 3 or later. 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program. If not, see . 14 | */ 15 | """ 16 | 17 | # ! /usr/bin/env python 18 | from functools import wraps 19 | from queue import Queue 20 | from re import finditer 21 | 22 | 23 | def store_in_queue(function) -> any: 24 | """ 25 | Decorator function that stores the return value of the decorated function in a queue. 26 | 27 | Args: 28 | function: The function to be decorated. 29 | 30 | Returns: 31 | any: The return value of the decorated function. 32 | """ 33 | 34 | @wraps(function) 35 | def wrapper(self, *args): 36 | self._returned_v.put(function(self, *args)) 37 | 38 | return wrapper 39 | 40 | 41 | class SearchAndRead: 42 | """ 43 | Class for searching and reading memory values in a process. 44 | 45 | Methods: 46 | get_readers_values() -> list: 47 | Retrieves the values stored in the queue. 48 | 49 | reset_queue() -> None: 50 | Resets the queue to remove all stored values. 51 | 52 | speed_search_and_read_text(pid: str, address_list: list, read: any) -> tuple[list[str], int]: 53 | Searches and reads text values from memory addresses in a process (optimized). 54 | 55 | speed_search_and_read(pid: str, address_list: list, buf: int, read: any) -> tuple[list[str], int]: 56 | Searches and reads binary values from memory addresses in a process (optimized). 57 | 58 | search_and_read(pid: str, address_list: list, buf: int, read: any) -> list: 59 | Searches and reads binary values from memory addresses in a process. 60 | 61 | search_and_read_text(pid: str, address_list: list, read: any) -> list: 62 | Searches and reads text values from memory addresses in a process. 63 | """ 64 | 65 | def __init__(self): 66 | pass 67 | 68 | _returned_v = Queue() 69 | 70 | def get_readers_values(self) -> list: 71 | """ 72 | Retrieves the values stored in the queue. 73 | 74 | Returns: 75 | list: List of stored values. 76 | """ 77 | 78 | return self._returned_v.get() 79 | 80 | def reset_queue(self) -> None: 81 | """ 82 | Resets the queue to remove all stored values. 83 | """ 84 | 85 | self._returned_v.queue.clear() 86 | 87 | @store_in_queue 88 | def speed_search_and_read_text(self, pid: str, address_list: list, read: any) -> tuple[list[str], int]: 89 | """ 90 | Searches and reads text values from memory addresses in a process (optimized). 91 | 92 | Args: 93 | pid (str): The process ID. 94 | address_list (list): List of memory address ranges to search and read. 95 | read (any): The text value to search for. 96 | 97 | Returns: 98 | tuple[list[str], int]: Tuple containing a list of found offsets and the total number of values found. 99 | """ 100 | 101 | mem_file = open(f"/proc/{pid}/mem", "rb+") 102 | total_values_found = 0 103 | offsets = [] 104 | 105 | for address in address_list: 106 | partitions = address.split("-") 107 | start_addr = int(partitions[0], 16) 108 | end_addr = int(partitions[1], 16) 109 | mem_file.seek(start_addr) 110 | total_bytes = end_addr - start_addr 111 | byte_strings = mem_file.read(total_bytes) 112 | occurrence = [_.start() for _ in finditer(read, byte_strings)] 113 | 114 | for offset in occurrence: 115 | offsets.append(hex(offset)) # fixed illegal calculation of hex value [Version-0.5] 116 | total_values_found += 1 117 | 118 | mem_file.close() 119 | return offsets, total_values_found 120 | 121 | @store_in_queue 122 | def speed_search_and_read(self, pid: str, address_list: list, buf: int, read: any) -> tuple[list[str], int]: 123 | """ 124 | Searches and reads binary values from memory addresses in a process (optimized). 125 | 126 | Args: 127 | pid (str): The process ID. 128 | address_list (list): List of memory address ranges to search and read. 129 | buf (int): The number of bytes to read at once. 130 | read (any): The binary value to search for. 131 | 132 | Returns: 133 | tuple[list[str], int]: Tuple containing a list of found offsets and the total number of values found. 134 | """ 135 | 136 | mem_file = open(f"/proc/{pid}/mem", "rb+") 137 | 138 | offsets = [] 139 | total_values_found = 0 140 | 141 | for address in address_list: 142 | partitions = address.split("-") 143 | start_addr = int(partitions[0], 16) 144 | end_addr = int(partitions[1], 16) 145 | mem_file.seek(start_addr) 146 | total_bytes = end_addr - start_addr 147 | current_bytes = 0 148 | while current_bytes < total_bytes + 1: 149 | try: 150 | current_bytes += buf 151 | read_byte = mem_file.read(buf) 152 | if read == read_byte: 153 | address_list = mem_file.seek(-buf, 1) 154 | mem_file.read(buf) 155 | offsets.append(hex(address_list)) # fixed illegal calculation of hex value [Version-0.5] 156 | total_values_found += 1 157 | except IOError: 158 | ... 159 | 160 | mem_file.close() 161 | return offsets, total_values_found 162 | 163 | @store_in_queue 164 | def speed_group_search_and_read(self, pid: str, address_list: list, buf: int, read_list: list, range_val: int)\ 165 | -> tuple[list[str], int]: 166 | """ 167 | Searches and read binary values in memory addresses of a process with a specified range. 168 | 169 | Args: 170 | pid (str): The process ID. 171 | address_list (list): List of memory address ranges to search and write. 172 | buf (int): The number of bytes to read at once. 173 | read_list (list): List of binary values to search for. 174 | range_val (int): Maximum distance between the group search values. 175 | 176 | Returns: 177 | tuple[list[str], int]: Tuple containing a list of found offsets and the total number of values found. 178 | """ 179 | 180 | mem_file = open(f"/proc/{pid}/mem", "rb+") 181 | 182 | offsets = [] 183 | total_values_found = 0 184 | 185 | for address in address_list: 186 | partitions = address.split("-") 187 | start_addr = int(partitions[0], 16) 188 | end_addr = int(partitions[1], 16) 189 | mem_file.seek(start_addr) 190 | total_bytes = end_addr - start_addr 191 | current_bytes = 0 192 | found_indexes = [] 193 | while current_bytes < total_bytes + 1: 194 | try: 195 | current_bytes += buf 196 | read_byte = mem_file.read(buf) 197 | if read_byte in read_list: 198 | found_indexes.append(mem_file.tell() - buf) 199 | except IOError: 200 | ... 201 | 202 | # Perform range check on found indexes 203 | for ranged_index in range(len(found_indexes) - 1): 204 | # Calculation from GameGuardian Post: 205 | # https://gameguardian.net/forum/topic/8149-gameguadian-group-search/ 206 | 207 | if found_indexes[ranged_index + 1] - found_indexes[ranged_index] <= range_val * buf: 208 | ranged_offset = mem_file.seek(found_indexes[ranged_index]) 209 | offsets.append(hex(ranged_offset)) 210 | mem_file.read(buf) 211 | total_values_found += 1 212 | 213 | mem_file.close() 214 | return offsets, total_values_found 215 | 216 | @staticmethod 217 | def search_and_read(pid: str, address_list: list, buf: int, read: any) -> list: 218 | """ 219 | Searches and reads binary values from memory addresses in a process. 220 | 221 | Args: 222 | pid (str): The process ID. 223 | address_list (list): List of memory address ranges to search and read. 224 | buf (int): The number of bytes to read at once. 225 | read (any): The binary value to search for. 226 | 227 | Returns: 228 | list: List containing a list of found offsets and the total number of values found. 229 | """ 230 | 231 | mem_file = open(f"/proc/{pid}/mem", "rb+") 232 | 233 | offsets = [] 234 | total_values_found = 0 235 | 236 | for address in address_list: 237 | partitions = address.split("-") 238 | start_addr = int(partitions[0], 16) 239 | end_addr = int(partitions[1], 16) 240 | mem_file.seek(start_addr) 241 | total_bytes = end_addr - start_addr 242 | current_bytes = 0 243 | while current_bytes < total_bytes + 1: 244 | try: 245 | current_bytes += buf 246 | read_byte = mem_file.read(buf) 247 | if read == read_byte: 248 | address_list = mem_file.seek(-buf, 1) 249 | mem_file.read(buf) 250 | offsets.append(hex(address_list)) # fixed illegal calculation of hex value [Version-0.5] 251 | total_values_found += 1 252 | except IOError: 253 | ... 254 | 255 | mem_file.close() 256 | return list([offsets, total_values_found]) 257 | 258 | @staticmethod 259 | def group_search_and_read(pid: str, address_list: list, buf: int, read_list: list, range_val: int)\ 260 | -> tuple[list[str], int]: 261 | """ 262 | Searches and read binary values in memory addresses of a process with a specified range. 263 | 264 | Args: 265 | pid (str): The process ID. 266 | address_list (list): List of memory address ranges to search and write. 267 | buf (int): The number of bytes to read at once. 268 | read_list (list): List of binary values to search for. 269 | range_val (int): Maximum distance between the group search values. 270 | 271 | Returns: 272 | tuple[list[str], int]: Tuple containing a list of found offsets and the total number of values found. 273 | """ 274 | 275 | mem_file = open(f"/proc/{pid}/mem", "rb+") 276 | 277 | offsets = [] 278 | total_values_found = 0 279 | 280 | for address in address_list: 281 | partitions = address.split("-") 282 | start_addr = int(partitions[0], 16) 283 | end_addr = int(partitions[1], 16) 284 | mem_file.seek(start_addr) 285 | total_bytes = end_addr - start_addr 286 | current_bytes = 0 287 | found_indexes = [] 288 | while current_bytes < total_bytes + 1: 289 | try: 290 | current_bytes += buf 291 | read_byte = mem_file.read(buf) 292 | if read_byte in read_list: 293 | found_indexes.append(mem_file.tell() - buf) 294 | except IOError: 295 | ... 296 | 297 | # Perform range check on found indexes 298 | for ranged_index in range(len(found_indexes) - 1): 299 | # Calculation from GameGuardian Post: 300 | # https://gameguardian.net/forum/topic/8149-gameguadian-group-search/ 301 | 302 | if found_indexes[ranged_index + 1] - found_indexes[ranged_index] <= range_val * buf: 303 | ranged_offset = mem_file.seek(found_indexes[ranged_index]) 304 | offsets.append(hex(ranged_offset)) 305 | mem_file.read(buf) 306 | total_values_found += 1 307 | 308 | mem_file.close() 309 | return offsets, total_values_found 310 | 311 | @staticmethod 312 | def search_and_read_text(pid: str, address_list: list, read: any) -> list: 313 | """ 314 | Searches and reads text values from memory addresses in a process. 315 | 316 | Args: 317 | pid (str): The process ID. 318 | address_list (list): List of memory address ranges to search and read. 319 | read (any): The text value to search for. 320 | 321 | Returns: 322 | list: List containing a list of found offsets and the total number of values found. 323 | """ 324 | 325 | mem_file = open(f"/proc/{pid}/mem", "rb+") 326 | total_values_found = 0 327 | offsets = [] 328 | 329 | for address in address_list: 330 | partitions = address.split("-") 331 | start_addr = int(partitions[0], 16) 332 | end_addr = int(partitions[1], 16) 333 | mem_file.seek(start_addr) 334 | total_bytes = end_addr - start_addr 335 | byte_strings = mem_file.read(total_bytes) 336 | occurrence = [_.start() for _ in finditer(read, byte_strings)] 337 | 338 | for offset in occurrence: 339 | offsets.append(hex(offset)) # fixed illegal calculation of hex value [Version-0.5] 340 | total_values_found += 1 341 | 342 | mem_file.close() 343 | return list([offsets, total_values_found]) 344 | -------------------------------------------------------------------------------- /androidMemoryTool/LinuxAPI/search_and_writers.py: -------------------------------------------------------------------------------- 1 | """ 2 | /* 3 | * Date : 2023/09/30 4 | * Version : 0.6 5 | * Author : Abdul Moez 6 | * Email : abdulmoez123456789@gmail.com 7 | * Affiliation : Undergraduate at Government College University (GCU) Lahore, Pakistan 8 | * GitHub : https://github.com/Anonym0usWork1221/android-memorytool 9 | * 10 | * Description: 11 | * This code is governed by the GNU General Public License, version 3 or later. 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program. If not, see . 14 | */ 15 | """ 16 | 17 | # ! /usr/bin/env python 18 | from functools import wraps 19 | from queue import Queue 20 | from re import finditer 21 | 22 | 23 | def store_in_queue(function) -> any: 24 | """ 25 | Decorator function that stores the return value of the decorated function in a queue. 26 | 27 | Args: 28 | function: The function to be decorated. 29 | 30 | Returns: 31 | any: The return value of the decorated function. 32 | """ 33 | 34 | @wraps(function) 35 | def wrapper(self, *args): 36 | self._returned_values.put(function(self, *args)) 37 | 38 | return wrapper 39 | 40 | 41 | class SearchAndWrite: 42 | """ 43 | Class for searching and writing memory values in a process. 44 | 45 | Methods: 46 | get_writer_values() -> list: 47 | Retrieves the values stored in the queue. 48 | 49 | reset_queue() -> None: 50 | Resets the queue to remove all stored values. 51 | 52 | speed_search_and_write(pid: str, address_list: list, buf: int, read: any, write: any) -> int: 53 | Searches and writes binary values in memory addresses of a process (optimized). 54 | 55 | speed_search_and_write_text(pid: str, address_list: list, read: any, write: any) -> int: 56 | Searches and writes text values in memory addresses of a process (optimized). 57 | 58 | search_and_write(pid: str, address_list: list, buf: int, read: any, write: any) -> int: 59 | Searches and writes binary values in memory addresses of a process. 60 | 61 | search_and_write_text(pid: str, address_list: list, read: any, write: any) -> int: 62 | Searches and writes text values in memory addresses of a process. 63 | """ 64 | 65 | def __init__(self): 66 | pass 67 | 68 | _returned_values = Queue() 69 | 70 | def get_writer_values(self) -> list: 71 | """ 72 | Retrieves the values stored in the queue. 73 | 74 | Returns: 75 | list: List of stored values. 76 | """ 77 | return self._returned_values.get() 78 | 79 | def reset_queue(self) -> None: 80 | """ 81 | Resets the queue to remove all stored values. 82 | """ 83 | self._returned_values.queue.clear() 84 | 85 | @store_in_queue 86 | def speed_search_and_write(self, pid: str, address_list: list, buf: int, read: any, write: any) -> int: 87 | """ 88 | Searches and writes binary values in memory addresses of a process (optimized). 89 | 90 | Args: 91 | pid (str): The process ID. 92 | address_list (list): List of memory address ranges to search and write. 93 | buf (int): The number of bytes to read at once. 94 | read (any): The binary value to search for. 95 | write (any): The binary value to write. 96 | 97 | Returns: 98 | int: The total number of values replaced. 99 | """ 100 | 101 | mem_file = open(f"/proc/{pid}/mem", "rb+") 102 | total_values_replaced = 0 103 | replaced_addresses = [] 104 | for address in address_list: 105 | partitions = address.split("-") 106 | start_addr = int(partitions[0], 16) 107 | end_addr = int(partitions[1], 16) 108 | mem_file.seek(start_addr) 109 | total_bytes = end_addr - start_addr 110 | current_bytes = 0 111 | while current_bytes < total_bytes + 1: 112 | try: 113 | current_bytes += buf 114 | read_byte = mem_file.read(buf) 115 | if read == read_byte: 116 | addr = mem_file.seek(-buf, 1) 117 | mem_file.write(write) 118 | replaced_addresses.append(hex(addr)) 119 | total_values_replaced += 1 120 | except IOError: 121 | ... 122 | 123 | mem_file.close() 124 | return total_values_replaced 125 | 126 | @store_in_queue 127 | def speed_search_and_write_text(self, pid: str, address_list: list, read: any, write: any) -> int: 128 | """ 129 | Searches and writes text values in memory addresses of a process (optimized). 130 | 131 | Args: 132 | pid (str): The process ID. 133 | address_list (list): List of memory address ranges to search and write. 134 | read (any): The text value to search for. 135 | write (any): The text value to write. 136 | 137 | Returns: 138 | int: The total number of values replaced. 139 | """ 140 | 141 | mem_file = open(f"/proc/{pid}/mem", "rb+") 142 | total_values_replaced = 0 143 | for address in address_list: 144 | partitions = address.split("-") 145 | start_addr = int(partitions[0], 16) 146 | end_addr = int(partitions[1], 16) 147 | mem_file.seek(start_addr) 148 | total_bytes = end_addr - start_addr 149 | byte_strings = mem_file.read(total_bytes) 150 | occurrence = [_.start() for _ in finditer(read, byte_strings)] 151 | 152 | for offset in occurrence: 153 | mem_file.seek(start_addr + offset) 154 | mem_file.write(write) 155 | total_values_replaced += 1 156 | 157 | mem_file.close() 158 | return total_values_replaced 159 | 160 | @store_in_queue 161 | def speed_group_search_and_write(self, pid: str, address_list: list, buf: int, read_list: list, write: any, 162 | range_val: int) -> int: 163 | """ 164 | Searches and writes binary values in memory addresses of a process with a specified range. 165 | 166 | Args: 167 | pid (str): The process ID. 168 | address_list (list): List of memory address ranges to search and write. 169 | buf (int): The number of bytes to read at once. 170 | read_list (list): List of binary values to search for. 171 | write (any): The binary value to write. 172 | range_val (int): Maximum distance between the group search values. 173 | 174 | Returns: 175 | int: The total number of values replaced. 176 | """ 177 | mem_file = open(f"/proc/{pid}/mem", "rb+") 178 | total_values_replaced = 0 179 | 180 | for address in address_list: 181 | partitions = address.split("-") 182 | start_addr = int(partitions[0], 16) 183 | end_addr = int(partitions[1], 16) 184 | mem_file.seek(start_addr) 185 | total_bytes = end_addr - start_addr 186 | current_bytes = 0 187 | found_indexes = [] 188 | while current_bytes < total_bytes + 1: 189 | try: 190 | current_bytes += buf 191 | read_byte = mem_file.read(buf) 192 | if read_byte in read_list: 193 | found_indexes.append(mem_file.tell() - buf) 194 | except IOError: 195 | ... 196 | 197 | # Perform range check on found indexes 198 | for ranged_index in range(len(found_indexes) - 1): 199 | # Calculation from GameGuardian Post: 200 | # https://gameguardian.net/forum/topic/8149-gameguadian-group-search/ 201 | 202 | if found_indexes[ranged_index + 1] - found_indexes[ranged_index] <= range_val * buf: 203 | mem_file.seek(found_indexes[ranged_index]) 204 | mem_file.write(write) 205 | total_values_replaced += 1 206 | 207 | mem_file.close() 208 | 209 | return total_values_replaced 210 | 211 | @staticmethod 212 | def group_search_and_write(pid: str, address_list: list, buf: int, read_list: list, write: any, 213 | range_val: int) -> int: 214 | """ 215 | Searches and writes binary values in memory addresses of a process with a specified range. 216 | 217 | Args: 218 | pid (str): The process ID. 219 | address_list (list): List of memory address ranges to search and write. 220 | buf (int): The number of bytes to read at once. 221 | read_list (list): List of binary values to search for. 222 | write (any): The binary value to write. 223 | range_val (int): Maximum distance between the group search values. 224 | 225 | Returns: 226 | int: The total number of values replaced. 227 | """ 228 | mem_file = open(f"/proc/{pid}/mem", "rb+") 229 | total_values_replaced = 0 230 | 231 | for address in address_list: 232 | partitions = address.split("-") 233 | start_addr = int(partitions[0], 16) 234 | end_addr = int(partitions[1], 16) 235 | mem_file.seek(start_addr) 236 | total_bytes = end_addr - start_addr 237 | current_bytes = 0 238 | found_indexes = [] 239 | while current_bytes < total_bytes + 1: 240 | try: 241 | current_bytes += buf 242 | read_byte = mem_file.read(buf) 243 | if read_byte in read_list: 244 | found_indexes.append(mem_file.tell() - buf) 245 | except IOError: 246 | ... 247 | 248 | # Perform range check on found indexes 249 | for ranged_index in range(len(found_indexes) - 1): 250 | # Calculation from GameGuardian Post: 251 | # https://gameguardian.net/forum/topic/8149-gameguadian-group-search/ 252 | 253 | if found_indexes[ranged_index + 1] - found_indexes[ranged_index] <= range_val * buf: 254 | mem_file.seek(found_indexes[ranged_index]) 255 | mem_file.write(write) 256 | total_values_replaced += 1 257 | 258 | mem_file.close() 259 | 260 | return total_values_replaced 261 | 262 | @staticmethod 263 | def search_and_write(pid: str, address_list: list, buf: int, read: any, write: any) -> int: 264 | """ 265 | Searches and writes binary values in memory addresses of a process. 266 | 267 | Args: 268 | pid (str): The process ID. 269 | address_list (list): List of memory address ranges to search and write. 270 | buf (int): The number of bytes to read at once. 271 | read (any): The binary value to search for. 272 | write (any): The binary value to write. 273 | 274 | Returns: 275 | int: The total number of values replaced. 276 | """ 277 | 278 | mem_file = open(f"/proc/{pid}/mem", "rb+") 279 | total_values_replaced = 0 280 | 281 | for address in address_list: 282 | partitions = address.split("-") 283 | start_addr = int(partitions[0], 16) 284 | end_addr = int(partitions[1], 16) 285 | mem_file.seek(start_addr) 286 | total_bytes = end_addr - start_addr 287 | current_bytes = 0 288 | while current_bytes < total_bytes + 1: 289 | try: 290 | current_bytes += buf 291 | read_byte = mem_file.read(buf) 292 | if read == read_byte: 293 | mem_file.seek(-buf, 1) 294 | mem_file.write(write) 295 | total_values_replaced += 1 296 | except IOError: 297 | ... 298 | 299 | mem_file.close() 300 | 301 | return total_values_replaced 302 | 303 | @staticmethod 304 | def search_and_write_text(pid: str, address_list: list, read: any, write: any) -> int: 305 | """ 306 | Searches and writes text values in memory addresses of a process. 307 | 308 | Args: 309 | pid (str): The process ID. 310 | address_list (list): List of memory address ranges to search and write. 311 | read (any): The text value to search for. 312 | write (any): The text value to write. 313 | 314 | Returns: 315 | int: The total number of values replaced. 316 | """ 317 | 318 | mem_file = open(f"/proc/{pid}/mem", "rb+") 319 | total_values_replaced = 0 320 | for address in address_list: 321 | partitions = address.split("-") 322 | start_addr = int(partitions[0], 16) 323 | end_addr = int(partitions[1], 16) 324 | mem_file.seek(start_addr) 325 | total_bytes = end_addr - start_addr 326 | byte_strings = mem_file.read(total_bytes) 327 | occurrence = [_.start() for _ in finditer(read, byte_strings)] 328 | 329 | for offset in occurrence: 330 | mem_file.seek(start_addr + offset) 331 | mem_file.write(write) 332 | total_values_replaced += 1 333 | 334 | mem_file.close() 335 | return total_values_replaced 336 | -------------------------------------------------------------------------------- /androidMemoryTool/WindowsAPI/DataClasses.py: -------------------------------------------------------------------------------- 1 | """ 2 | /* 3 | * Date : 2023/09/30 4 | * Version : 0.6 5 | * Author : Abdul Moez 6 | * Email : abdulmoez123456789@gmail.com 7 | * Affiliation : Undergraduate at Government College University (GCU) Lahore, Pakistan 8 | * GitHub : https://github.com/Anonym0usWork1221/android-memorytool 9 | * 10 | * Description: 11 | * This code is governed by the GNU General Public License, version 3 or later. 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program. If not, see . 14 | */ 15 | """ 16 | 17 | from ..errors_class import PIDException 18 | from dataclasses import dataclass 19 | import ctypes 20 | import psutil 21 | 22 | 23 | class DataClasses: 24 | 25 | @dataclass() 26 | class DataTypes(object): 27 | """ 28 | Data class that defines different data types used in the code. 29 | """ 30 | 31 | DWORD: str = "DWORD" 32 | FLOAT: str = "FLOAT" 33 | DOUBLE: str = "DOUBLE" 34 | WORD: str = "WORD" 35 | BYTE: str = "BYTE" 36 | QWORD: str = "QWORD" 37 | XOR: str = "XOR" 38 | UTF_8: str = "UTF-8" 39 | UTF_16LE: str = "UTF-16LE" 40 | 41 | def __init__(self): 42 | """ 43 | Initialize a new instance of the SearchAndRead class. 44 | This constructor does not take any parameters. 45 | """ 46 | ... 47 | 48 | @staticmethod 49 | def get_pid(pkg: any((str, int))) -> int: 50 | """ 51 | Retrieves the process ID (PID) for the given package name or PID. 52 | Args: 53 | pkg (str or int): The package name or PID. 54 | Returns: 55 | int: The process ID (PID). 56 | Raises: 57 | PIDException: If the process is not running in memory or if an error occurs during retrieval. 58 | """ 59 | 60 | pkg = str(pkg) 61 | if pkg.isnumeric(): # If the input is already a PID 62 | pid = int(pkg) 63 | if psutil.pid_exists(pid): 64 | return int(pid) 65 | else: 66 | raise PIDException("Process is not running in memory. Try to restart the process and script.") 67 | else: # If the input is a package name 68 | try: 69 | for proc in psutil.process_iter(attrs=['pid', 'name']): 70 | if pkg.lower() in proc.info['name'].lower(): 71 | return int(proc.info['pid']) 72 | raise PIDException("Process is not running in memory. Try to restart the process and script.") 73 | except Exception: 74 | raise PIDException("Error occurred while retrieving PID for the process.") 75 | 76 | @staticmethod 77 | def data_type_buffer(data_type: str, read_value: any = None, 78 | write: any = None, read_string: str = "", write_string: str = ""): 79 | """ 80 | Create ctypes buffer objects for the specified data type. 81 | 82 | Args: 83 | data_type (str): The data type for which to create a buffer. 84 | write (any): Optional. The value to be used for creating the write buffer if provided. 85 | read_string (str): Optional. The string value to be used for creating the read buffer if provided. 86 | write_string (str): Optional. The string value to be used for creating the write buffer if provided. 87 | 88 | Returns: 89 | ctypes.Array: The read and write buffer objects. 90 | 91 | Raises: 92 | ValueError: If an unsupported data type is provided. 93 | """ 94 | 95 | if data_type == "DWORD": 96 | if write: 97 | if read_value: 98 | read_buffer = ctypes.c_int32(read_value) 99 | else: 100 | read_buffer = ctypes.c_int32() 101 | write_buffer = ctypes.c_int32(write) 102 | return read_buffer, write_buffer 103 | if read_value: 104 | return ctypes.c_int32(read_value) 105 | return ctypes.c_int32() 106 | elif data_type == "FLOAT": 107 | if write: 108 | if read_value: 109 | read_buffer = ctypes.c_float(read_value) 110 | else: 111 | read_buffer = ctypes.c_float() 112 | write_buffer = ctypes.c_float(write) 113 | return read_buffer, write_buffer 114 | if read_value: 115 | return ctypes.c_float(read_value) 116 | return ctypes.c_float() 117 | elif data_type == "DOUBLE": 118 | if write: 119 | if read_value: 120 | read_buffer = ctypes.c_double(read_value) 121 | else: 122 | read_buffer = ctypes.c_double() 123 | write_buffer = ctypes.c_double(write) 124 | return read_buffer, write_buffer 125 | if read_value: 126 | return ctypes.c_double(read_value) 127 | return ctypes.c_double() 128 | elif data_type == "WORD": 129 | if write: 130 | if read_value: 131 | read_buffer = ctypes.c_short(read_value) 132 | else: 133 | read_buffer = ctypes.c_short() 134 | write_buffer = ctypes.c_short(write) 135 | return read_buffer, write_buffer 136 | if read_value: 137 | return ctypes.c_short(read_value) 138 | return ctypes.c_short() 139 | elif data_type == "BYTE": 140 | if write: 141 | if read_value: 142 | read_buffer = ctypes.c_byte(read_value) 143 | else: 144 | read_buffer = ctypes.c_byte() 145 | write_buffer = ctypes.c_byte(write) 146 | return read_buffer, write_buffer 147 | if read_value: 148 | return ctypes.c_byte(read_value) 149 | return ctypes.c_byte() 150 | elif data_type == "QWORD": 151 | if write: 152 | if read_value: 153 | read_buffer = ctypes.c_longlong(read_value) 154 | else: 155 | read_buffer = ctypes.c_longlong() 156 | write_buffer = ctypes.c_longlong(write) 157 | return read_buffer, write_buffer 158 | if read_value: 159 | return ctypes.c_longlong(read_value) 160 | return ctypes.c_longlong() 161 | elif data_type == "XOR": 162 | if write: 163 | if read_value: 164 | read_buffer = ctypes.c_long(read_value) 165 | else: 166 | read_buffer = ctypes.c_long() 167 | write_buffer = ctypes.c_long(write) 168 | return read_buffer, write_buffer 169 | if read_value: 170 | return ctypes.c_long(read_value) # Signed long (XOR) 171 | return ctypes.c_long() 172 | elif data_type == "UTF-8": 173 | if write_string: 174 | read_buffer = ctypes.create_string_buffer(read_string.encode('utf-8')) 175 | write_buffer = ctypes.create_string_buffer(write_string.encode('utf-8')) 176 | return read_buffer, write_buffer 177 | return ctypes.create_string_buffer(read_string.encode('utf-8')) 178 | elif data_type == "UTF-16LE": 179 | if write_string: 180 | read_buffer = ctypes.create_unicode_buffer(read_string) 181 | write_buffer = ctypes.create_unicode_buffer(write_string) 182 | return read_buffer, write_buffer 183 | return ctypes.create_unicode_buffer(read_string) 184 | else: 185 | raise ValueError(f"Unsupported data type: {data_type}") 186 | -------------------------------------------------------------------------------- /androidMemoryTool/WindowsAPI/ThreadingController.py: -------------------------------------------------------------------------------- 1 | """ 2 | /* 3 | * Date : 2023/09/30 4 | * Version : 0.6 5 | * Author : Abdul Moez 6 | * Email : abdulmoez123456789@gmail.com 7 | * Affiliation : Undergraduate at Government College University (GCU) Lahore, Pakistan 8 | * GitHub : https://github.com/Anonym0usWork1221/android-memorytool 9 | * 10 | * Description: 11 | * This code is governed by the GNU General Public License, version 3 or later. 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program. If not, see . 14 | */ 15 | """ 16 | 17 | from .complex_api_for_all import ComplexApiController 18 | from concurrent.futures import ThreadPoolExecutor 19 | 20 | 21 | class FastSearchAlgo(object): 22 | 23 | def __init__(self, workers: int = 2): 24 | super(FastSearchAlgo, self).__init__() 25 | self._workers = workers 26 | self.main_controller = ComplexApiController() 27 | 28 | def fast_search_algorithms_value(self, handle, buf: any, string: bool = False, 29 | write_buf=None) -> None: 30 | if write_buf: 31 | with ThreadPoolExecutor(max_workers=self._workers) as executor: 32 | executor.submit(self.main_controller.search_and_write_in_memory, handle, buf, write_buf, string) 33 | else: 34 | with ThreadPoolExecutor(max_workers=self._workers) as executor: 35 | executor.submit(self.main_controller.speed_search_and_read_in_memory, handle, buf) 36 | 37 | def fast_search_algorithms_group_values(self, handle, buf: any, range_val: int, string: bool = False, 38 | write_buf=None) -> None: 39 | if write_buf: 40 | with ThreadPoolExecutor(max_workers=self._workers) as executor: 41 | executor.submit(self.main_controller.speed_group_search_and_write, handle, buf, write_buf, range_val, 42 | string) 43 | else: 44 | with ThreadPoolExecutor(max_workers=self._workers) as executor: 45 | executor.submit(self.main_controller.speed_group_search_and_read, handle, buf, range_val) 46 | 47 | def fast_search_algorithms_hex_patterns(self, handle, search_pattern: str, replacement_pattern: str = None) -> None: 48 | if replacement_pattern: 49 | with ThreadPoolExecutor(max_workers=self._workers) as executor: 50 | executor.submit(self.main_controller.speed_find_and_replace_hex_pattern_in_memory, handle, 51 | search_pattern, replacement_pattern) 52 | else: 53 | with ThreadPoolExecutor(max_workers=self._workers) as executor: 54 | executor.submit(self.main_controller.speed_find_hex_pattern_in_memory, handle, search_pattern) 55 | -------------------------------------------------------------------------------- /androidMemoryTool/WindowsAPI/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anonym0usWork1221/android-memorytool/d777a7231100eed31cf9af36d1b73de63961da06/androidMemoryTool/WindowsAPI/__init__.py -------------------------------------------------------------------------------- /androidMemoryTool/WindowsAPI/android_memory_tool_windows.py: -------------------------------------------------------------------------------- 1 | """ 2 | /* 3 | * Date : 2023/09/30 4 | * Version : 0.6 5 | * Author : Abdul Moez 6 | * Email : abdulmoez123456789@gmail.com 7 | * Affiliation : Undergraduate at Government College University (GCU) Lahore, Pakistan 8 | * GitHub : https://github.com/Anonym0usWork1221/android-memorytool 9 | * 10 | * Description: 11 | * This code is governed by the GNU General Public License, version 3 or later. 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program. If not, see . 14 | */ 15 | """ 16 | 17 | # RES: https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-readprocessmemory?redirectedfrom=MSDN 18 | 19 | # ! /usr/bin/env python 20 | from .ThreadingController import FastSearchAlgo 21 | from .DataClasses import DataClasses 22 | from ..errors_class import WINAPIException, PIDException 23 | from os import cpu_count 24 | import ctypes 25 | 26 | # Process Permissions 27 | PROCESS_QUERY_INFORMATION = 0x0400 28 | PROCESS_VM_OPERATION = 0x0008 29 | PROCESS_ALL_ACCESS = 0x1f0fff 30 | PROCESS_VM_WRITE = 0x0020 31 | PROCESS_VM_READ = 0x0010 32 | MAX_PATH = 260 33 | 34 | 35 | class AndroidMemoryToolWindows(FastSearchAlgo): 36 | """ 37 | AndroidMemoryToolWindows is a class for interacting with the memory of Android processes on Windows. 38 | 39 | Args: 40 | PKG (Union[str, int]): The package name or process ID of the Android application. 41 | TYPE (str, optional): The data type to use when reading and writing memory. Defaults to "DWORD". 42 | SPEED_MODE (bool, optional): If True, use a fast search algorithm; if False, use a slower but more 43 | precise method. Defaults to True. 44 | WORKERS (int, optional): The number of worker threads to use for memory operations. Defaults to half of the 45 | available CPU cores. 46 | 47 | Attributes: 48 | _pid (int): The process ID of the Android application. 49 | _handle (int): The handle to the process. 50 | _string_data_types (list): List of supported string data types. 51 | _speed_mode (bool): Flag indicating whether to use fast search algorithms. 52 | _data_type (str): The data type to use for memory operations. 53 | _search_and_read (SearchAndRead): An instance of the SearchAndRead class for memory operations. 54 | 55 | Methods: 56 | _get_process_by_id(self): 57 | Private method to obtain a handle to the Android process based on its process ID. 58 | 59 | _open_handle(self): 60 | Private method to open a handle to the Android process with specific access rights. 61 | 62 | _open_handler_on_all_access(self): 63 | Private method to open a handle to the Android process with full access. 64 | 65 | _close_handle(self) -> int: 66 | Private method to close the handle to the Android process and return the last error code. 67 | 68 | read_value(self, read: Any, is_grouped: bool = False, range_val: int = 512) -> Any: 69 | Read a value from the Android process's memory. 70 | 71 | read_write_value(self, read: Any, write: Any, is_grouped: bool = False, range_val: int = 512) -> Any: 72 | Read and write a value in the Android process's memory. 73 | 74 | write_lib(self, base_address: str, offset: str, write_value: Any) -> bool: 75 | Write a value to a specified memory address within a loaded library in the Android process. 76 | 77 | read_lib(self, base_address: str, offset: str, value: Union[str, int, None] = None) -> Any: 78 | Read a value from a specified memory address within a loaded library in the Android process. 79 | 80 | refiner_address(self, list_address: List[int], value_to_refine: Any) -> Any: 81 | Refine a list of memory addresses based on a specified value. 82 | 83 | get_module_base_address(self, module_name: str) -> Any: 84 | Get the base address of a loaded module (DLL) in the Android process's memory. 85 | 86 | raw_dump(self, lib_name: str, path: str = './') -> bool: 87 | Dump the memory of a specified loaded library in the Android process to a binary file. 88 | 89 | find_hex_pattern(search_pattern: str) -> Any: 90 | [Not implemented in this version] Find a hexadecimal pattern in the Android process's memory. 91 | 92 | find_and_replace_hex_pattern(search_pattern: str, replace_pattern: str) -> Any: 93 | [Not implemented in this version] Find and replace a hexadecimal pattern in the Android process's memory. 94 | 95 | dump_maps(self, path: str = "./") -> bool: 96 | Dump the memory maps (address ranges) of the Android process to a text file. 97 | """ 98 | 99 | def __init__(self, 100 | PKG: any((str, int)), 101 | TYPE: str = DataClasses.DataTypes.DWORD, 102 | SPEED_MODE: bool = True, 103 | WORKERS: int = int(cpu_count() / 2), # half of cores available in operating system 104 | ) -> None: 105 | """ 106 | Initializes an instance of AndroidMemoryToolWindows. 107 | 108 | Args: 109 | PKG (str or int): The package name or process ID of the Android application. 110 | TYPE (str, optional): The data type to use for memory operations (default is DataClasses.DataTypes.DWORD). 111 | SPEED_MODE (bool, optional): Whether to use speed optimization mode (default is True). 112 | WORKERS (int, optional): Number of worker threads to use for memory operations 113 | (default is half of CPU cores). 114 | """ 115 | 116 | super(AndroidMemoryToolWindows, self).__init__(workers=WORKERS) 117 | self._pid = DataClasses.get_pid(pkg=PKG) 118 | self._handle = self._get_process_by_id() 119 | self._close_handle() 120 | self._string_data_types = [DataClasses.DataTypes.UTF_8, DataClasses.DataTypes.UTF_16LE] 121 | self._speed_mode = SPEED_MODE 122 | self._data_type = TYPE 123 | 124 | def get_process_id(self) -> str: 125 | return str(self._pid) 126 | 127 | def _get_process_by_id(self): 128 | """ 129 | Get the handle of the Android application's process by its process ID. 130 | Returns: 131 | int: The process handle. 132 | Raises: 133 | PIDException: If the process with the specified PID is not found. 134 | """ 135 | 136 | handle = ctypes.windll.kernel32.OpenProcess(PROCESS_QUERY_INFORMATION, False, self._pid) 137 | if handle: 138 | image_file_name = (ctypes.c_char * MAX_PATH)() 139 | if ctypes.windll.psapi.GetProcessImageFileNameA(handle, image_file_name, MAX_PATH) > 0: 140 | return handle 141 | else: 142 | raise PIDException(f'Unable to get the executable\'s name for PID={self._pid}!') 143 | 144 | raise PIDException(f'Process "{self._pid}" not found!') 145 | 146 | def _open_handle(self): 147 | """ 148 | Open the process handle with specific access rights. 149 | 150 | Raises: 151 | WINAPIException: If unable to open the process handle. 152 | """ 153 | 154 | dw_desired_access = (PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE) 155 | b_inherit_handle = True 156 | self._handle = ctypes.windll.kernel32.OpenProcess(dw_desired_access, b_inherit_handle, self._pid) 157 | if not self._handle: 158 | raise WINAPIException(f'Unable to open process') 159 | 160 | def _open_handler_on_all_access(self): 161 | """ 162 | Open the process handle with all access rights. 163 | 164 | Raises: 165 | WINAPIException: If unable to open the process handle. 166 | """ 167 | b_inherit_handle = True 168 | self._handle = ctypes.windll.kernel32.OpenProcess(PROCESS_ALL_ACCESS, b_inherit_handle, self._pid) 169 | if not self._handle: 170 | raise WINAPIException(f'Unable to open process') 171 | 172 | def _close_handle(self) -> int: 173 | """ 174 | Close the process handle. 175 | 176 | Returns: 177 | int: The error code if the handle cannot be closed. 178 | """ 179 | 180 | ctypes.windll.kernel32.CloseHandle(self._handle) 181 | return ctypes.windll.kernel32.GetLastError() 182 | 183 | def read_value(self, read: any, is_grouped: bool = False, range_val: int = 512) -> any: 184 | """ 185 | Read a value from the memory of the Android application. 186 | 187 | Args: 188 | read: The value to read from memory. 189 | is_grouped (bool, optional): Whether the read operation is grouped (default is False). 190 | range_val (int, optional): The range of memory to search for the value (default is 512). 191 | 192 | Returns: 193 | any: The read value. 194 | """ 195 | self._open_handler_on_all_access() 196 | if not is_grouped: 197 | data_types = DataClasses.data_type_buffer(data_type=self._data_type, read_value=read) 198 | else: 199 | data_types = [DataClasses.data_type_buffer(data_type=self._data_type, read_value=val) for val in read] 200 | # If data type is valid continue 201 | if not is_grouped: 202 | # Handle both string and on ASCII chars 203 | if self._speed_mode: 204 | super().fast_search_algorithms_value(handle=self._handle, buf=data_types) 205 | r_value = self.main_controller.get_readers_values() 206 | self.main_controller.reset_queue() 207 | self._close_handle() 208 | return r_value 209 | r_value = self.main_controller.search_and_read_in_memory(handle=self._handle, buf=data_types) 210 | self._close_handle() 211 | return r_value 212 | elif is_grouped and self._data_type: 213 | if self._speed_mode: 214 | # FIXME: Bug-Threads are not terminating and runnning forever. 215 | super().fast_search_algorithms_group_values(handle=self._handle, buf=data_types, range_val=range_val) 216 | r_value = self.main_controller.get_readers_values() 217 | self.main_controller.reset_queue() 218 | self._close_handle() 219 | return r_value 220 | r_value = self.main_controller.group_search_and_read(handle=self._handle, buf=data_types, 221 | range_val=range_val) 222 | self._close_handle() 223 | return r_value 224 | 225 | return None 226 | 227 | def read_write_value(self, read: any, write: any, is_grouped: bool = False, range_val: int = 512) -> any: 228 | """ 229 | Read and write a value to the memory of the Android application. 230 | 231 | Args: 232 | read: The value to read from memory. 233 | write: The value to write to memory. 234 | is_grouped (bool, optional): Whether the read and write operations are grouped (default is False). 235 | range_val (int, optional): The range of memory to search for the value (default is 512). 236 | 237 | Returns: 238 | any: The read value. 239 | """ 240 | 241 | self._open_handler_on_all_access() 242 | read_buffer, write_buffer = DataClasses.data_type_buffer(data_type=self._data_type, read_value=read, 243 | write=write, read_string=read, write_string=write) 244 | # If data type is valid continue 245 | if self._data_type not in self._string_data_types and not is_grouped: 246 | if self._speed_mode: 247 | super().fast_search_algorithms_value(handle=self._handle, buf=read_buffer, write_buf=write_buffer) 248 | r_value = self.main_controller.get_readers_values() 249 | self.main_controller.reset_queue() 250 | self._close_handle() 251 | return r_value 252 | r_value = self.main_controller.search_and_write_in_memory(handle=self._handle, read_buf=read_buffer, 253 | write_buff=write_buffer) 254 | self._close_handle() 255 | return r_value 256 | 257 | elif is_grouped and self._data_type: 258 | if self._speed_mode: 259 | if self._data_type in self._string_data_types: 260 | super().fast_search_algorithms_group_values(handle=self._handle, 261 | buf=read_buffer, range_val=range_val, 262 | write_buf=write_buffer, string=True) 263 | else: 264 | super().fast_search_algorithms_group_values(handle=self._handle, 265 | buf=read_buffer, range_val=range_val, 266 | write_buf=write_buffer) 267 | 268 | r_value = self.main_controller.get_readers_values() 269 | self.main_controller.reset_queue() 270 | self._close_handle() 271 | return r_value 272 | if self._data_type in self._string_data_types: 273 | r_value = self.main_controller.group_search_and_write(handle=self._handle, 274 | read_buf=read_buffer, 275 | write_buff=write_buffer, 276 | range_val=range_val, string=True) 277 | else: 278 | r_value = self.main_controller.group_search_and_write(handle=self._handle, 279 | read_buf=read_buffer, 280 | write_buff=write_buffer, 281 | range_val=range_val) 282 | self._close_handle() 283 | return r_value 284 | else: 285 | # string to search 286 | if self._speed_mode: 287 | super().fast_search_algorithms_value(handle=self._handle, buf=read_buffer, write_buf=write_buffer, 288 | string=True) 289 | r_value = self.main_controller.get_readers_values() 290 | self.main_controller.reset_queue() 291 | self._close_handle() 292 | return r_value 293 | r_value = self.main_controller.search_and_write_in_memory(handle=self._handle, read_buf=read_buffer, 294 | write_buff=write_buffer, string=True) 295 | self._close_handle() 296 | return r_value 297 | 298 | def write_lib(self, base_address: str, offset: str, write_value: any) -> bool: 299 | """ 300 | Write a value to a specified memory location within a loaded library. 301 | 302 | Args: 303 | base_address (str): The base address of the loaded library. 304 | offset (str): The offset within the library where the value should be written. 305 | write_value: The value to write to memory. 306 | 307 | Returns: 308 | bool: True if the write operation was successful, False otherwise. 309 | """ 310 | 311 | self._open_handle() 312 | _, write_buf = DataClasses.data_type_buffer(data_type=self._data_type, write=write_value, 313 | write_string=write_value) 314 | base_address = str(base_address) 315 | offset = str(offset) 316 | is_string = True if self._data_type in self._string_data_types else False 317 | r_value = self.main_controller.write_lib_offsets(handle=self._handle, 318 | base_address=base_address, offset=offset, buf=write_buf, 319 | string=is_string) 320 | self._close_handle() 321 | return r_value 322 | 323 | def read_lib(self, base_address: str, offset: str, value: any((str, int, None)) = None) -> any: 324 | """ 325 | Read a value from a specified memory location within a loaded library. 326 | Args: 327 | base_address (str): The base address of the loaded library. 328 | offset (str): The offset within the library where the value should be read from. 329 | value (str, int, None, optional): The expected value to compare when reading (default is None). 330 | Returns: 331 | any: The read value. 332 | """ 333 | 334 | self._open_handle() 335 | read_buffer = DataClasses.data_type_buffer(data_type=self._data_type, read_string=value) 336 | base_address = str(base_address) 337 | offset = str(offset) 338 | if self._data_type not in self._string_data_types: 339 | r_value = self.main_controller.read_lib_offsets(handle=self._handle, 340 | base_address=base_address, 341 | offset=offset, buf=read_buffer) 342 | self._close_handle() 343 | return r_value 344 | else: 345 | is_utf_16 = True if self._data_type == DataClasses.DataTypes.UTF_16LE else False 346 | length_of_read = len(value) 347 | r_value = self.main_controller.read_lib_offsets(handle=self._handle, 348 | base_address=base_address, 349 | offset=offset, buf=read_buffer, string=True, 350 | length=length_of_read, utf_16=is_utf_16) 351 | self._close_handle() 352 | return r_value 353 | 354 | def refiner_address(self, list_address: list, value_to_refine: any) -> any: 355 | """ 356 | Refine a list of memory addresses based on a value to match. 357 | 358 | Args: 359 | list_address (list): List of memory addresses to refine. 360 | value_to_refine: The value to refine the addresses. 361 | 362 | Returns: 363 | any: The refined list of memory addresses. 364 | """ 365 | 366 | self._open_handle() 367 | read_buffer = DataClasses.data_type_buffer(data_type=self._data_type, read_string=value_to_refine) 368 | if self._data_type not in self._string_data_types: 369 | r_value = self.main_controller.address_refiners(handle=self._handle, 370 | list_address=list_address, buf=read_buffer, 371 | changed_value=value_to_refine) 372 | self._close_handle() 373 | return r_value 374 | else: 375 | is_utf_16 = True if self._data_type == DataClasses.DataTypes.UTF_16LE else False 376 | length_of_read = len(value_to_refine) 377 | r_value = self.main_controller.address_refiners(handle=self._handle, 378 | list_address=list_address, buf=read_buffer, 379 | changed_value=value_to_refine, string=True, 380 | length=length_of_read, utf_16=is_utf_16) 381 | self._close_handle() 382 | return r_value 383 | 384 | def get_module_base_address(self, module_name: str) -> any: 385 | """ 386 | Get the base address of a loaded module (DLL) by its name. 387 | 388 | Args: 389 | module_name (str): The name of the loaded module. 390 | 391 | Returns: 392 | any: The base address of the module. 393 | """ 394 | 395 | self._open_handle() 396 | base_address, _ = self.main_controller.find_dll_addresses(handle=self._handle, dll_name=module_name) 397 | self._close_handle() 398 | return base_address 399 | 400 | def raw_dump(self, lib_name: str, path='./') -> bool: 401 | """ 402 | Dump the memory of a loaded library to a binary file. 403 | 404 | Args: 405 | lib_name (str): The name of the loaded library to dump. 406 | path (str, optional): The path where the binary dump file should be saved (default is './'). 407 | 408 | Returns: 409 | bool: True if the dump was successful, False otherwise. 410 | """ 411 | self._open_handle() 412 | bytes_of_process, address = self.main_controller.raw_dumper(handle=self._handle, dll_name=lib_name) 413 | with open(f"{path}{address[0]}-{address[1]}-{lib_name.replace('.dll', '')}.bin", "wb") as byte_file: 414 | byte_file.write(bytes_of_process) 415 | byte_file.close() 416 | self._close_handle() 417 | return True 418 | 419 | @staticmethod 420 | def find_hex_pattern(search_pattern: str) -> any: 421 | """ 422 | Finds the specified hexadecimal pattern in the memory. 423 | 424 | Args: 425 | search_pattern: The hexadecimal pattern to search for. 426 | 427 | Returns: 428 | The addresses where the pattern was found. 429 | """ 430 | print('[DEBUG] This function is not available in current version. Please wait for next update') 431 | return None 432 | 433 | @staticmethod 434 | def find_and_replace_hex_pattern(search_pattern: str, replace_pattern: str) -> any: 435 | """ 436 | Search for and replace a hexadecimal pattern in the memory of the Android application. 437 | 438 | Args: 439 | search_pattern (str): The hexadecimal pattern to search for. 440 | replace_pattern (str): The hexadecimal pattern to replace with. 441 | 442 | Returns: 443 | any: The result of the search 444 | and replace operation. 445 | """ 446 | print('[DEBUG] This function is not available in current version. Please wait for next update') 447 | return None 448 | 449 | def dump_maps(self, path="./") -> bool: 450 | """ 451 | Dump the memory mappings of the Android application to a text file. 452 | 453 | Args: 454 | path (str, optional): The path where the text file should be saved (default is './'). 455 | 456 | Returns: 457 | bool: True if the dump was successful, False otherwise. 458 | """ 459 | 460 | self._open_handle() 461 | memory_maps = self.main_controller.get_memory_mappings(handle=self._handle) 462 | self._close_handle() 463 | if memory_maps: 464 | with open(f"{path}maps.txt", "w") as map_file: 465 | for memory_map in memory_maps: 466 | map_file.write(f"{memory_map['BaseAddress']}-{memory_map['EndingAddress']:<40}" 467 | f"{memory_map['SizeOfImage']:<30}{memory_map['ModuleName']:<70}\n") 468 | map_file.close() 469 | return True 470 | return False 471 | -------------------------------------------------------------------------------- /androidMemoryTool/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | /* 4 | * Date : 2023/09/30 5 | * Version : 0.6 6 | * Author : Abdul Moez 7 | * Email : abdulmoez123456789@gmail.com 8 | * Affiliation : Undergraduate at Government College University (GCU) Lahore, Pakistan 9 | * GitHub : https://github.com/Anonym0usWork1221/android-memorytool 10 | * 11 | * Description: 12 | * This code is governed by the GNU General Public License, version 3 or later. 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program. If not, see . 15 | */ 16 | """ 17 | 18 | # Tool APIS 19 | from .androidMemoryTool import AndroidMemoryTool, DataTypes, PMAP 20 | from .cli_for_tool import AndroidMemoryToolCLI 21 | 22 | # ERROR APIS 23 | from .errors_class import PIDException, WINAPIException 24 | 25 | # Common APIS 26 | from .CommonAPI.cross_platform_memory_profiler import MemoryProfiler 27 | 28 | # Linux APIS 29 | from .LinuxAPI.android_memory_tool_linux import AndroidMemoryToolLinux 30 | from .LinuxAPI.ThreadingController import FastSearchAlgo 31 | from .LinuxAPI.search_and_writers import SearchAndWrite 32 | from .LinuxAPI.search_and_readers import SearchAndRead 33 | from .LinuxAPI.DataClasses import DataClasses 34 | from .LinuxAPI.mapping import Mapping 35 | 36 | # Windows APIS 37 | from .WindowsAPI.android_memory_tool_windows import AndroidMemoryToolWindows 38 | from .WindowsAPI.complex_api_for_all import ComplexApiController 39 | from .WindowsAPI.ThreadingController import FastSearchAlgo 40 | from .WindowsAPI.DataClasses import DataClasses 41 | -------------------------------------------------------------------------------- /androidMemoryTool/__main__.py: -------------------------------------------------------------------------------- 1 | """ 2 | /* 3 | * Date : 2023/09/30 4 | * Version : 0.6 5 | * Author : Abdul Moez 6 | * Email : abdulmoez123456789@gmail.com 7 | * Affiliation : Undergraduate at Government College University (GCU) Lahore, Pakistan 8 | * GitHub : https://github.com/Anonym0usWork1221/android-memorytool 9 | * 10 | * Description: 11 | * This code is governed by the GNU General Public License, version 3 or later. 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program. If not, see . 14 | */ 15 | """ 16 | 17 | from androidMemoryTool import AndroidMemoryToolCLI, AndroidMemoryTool 18 | 19 | 20 | def execute_cli(): 21 | cli_tool = AndroidMemoryToolCLI(AndroidMemoryTool) 22 | cli_tool.cli_handler() 23 | 24 | 25 | if __name__ == '__main__': 26 | execute_cli() 27 | -------------------------------------------------------------------------------- /androidMemoryTool/androidMemoryTool.py: -------------------------------------------------------------------------------- 1 | """ 2 | /* 3 | * Date : 2023/09/30 4 | * Version : 0.6 5 | * Author : Abdul Moez 6 | * Email : abdulmoez123456789@gmail.com 7 | * Affiliation : Undergraduate at Government College University (GCU) Lahore, Pakistan 8 | * GitHub : https://github.com/Anonym0usWork1221/android-memorytool 9 | * 10 | * Description: 11 | * This code is governed by the GNU General Public License, version 3 or later. 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program. If not, see . 14 | */ 15 | """ 16 | 17 | from .WindowsAPI.android_memory_tool_windows import AndroidMemoryToolWindows 18 | from .LinuxAPI.android_memory_tool_linux import AndroidMemoryToolLinux 19 | from .CommonAPI.cross_platform_memory_profiler import MemoryProfiler 20 | from .LinuxAPI.DataClasses import DataClasses 21 | from subprocess import check_output 22 | from dataclasses import dataclass 23 | import platform 24 | import psutil 25 | 26 | if platform.system() == 'Windows': 27 | _WIN_PLATFORM = True 28 | else: 29 | # Assuming that the platforms are in list[Linux, Android] other platform will give errors. 30 | _WIN_PLATFORM = False 31 | 32 | 33 | @dataclass() 34 | class PMAP(DataClasses.PMAP): 35 | """ 36 | Data class that defines different options for memory mapping. 37 | 38 | Inherited-Attributes: 39 | ALL (bool, optional): True to search for memory attributes in all regions, False to search in MAIN only. 40 | B_BAD (bool, optional): True to search for BAD regions in memory. 41 | C_ALLOC (bool, optional): True to search for C_ALLOC regions in memory. 42 | C_BSS (bool, optional): True to search for C_BSS regions in memory. 43 | C_DATA (bool, optional): True to search for C_DATA regions in memory. 44 | C_HEAP (bool, optional): True to search for C_HEAP regions in memory. 45 | JAVA_HEAP (bool, optional): True to search for JAVA_HEAP regions in memory. 46 | A_ANONYMOUS (bool, optional): True to search for A_ANONYMOUS regions in memory. 47 | CODE_SYSTEM (bool, optional): True to search for CODE_SYSTEM regions in memory. 48 | STACK (bool, optional): True to search for STACK regions in memory. 49 | ASHMEM (bool, optional): True to search for ASHMEM regions in memory. 50 | J_Java (bool, optional): True to search for J_Java regions in memory. 51 | CODE_APP (bool, optional): True to search for CODE_APP regions in memory. 52 | V_video (bool, optional): True to search for V_video regions in memory. 53 | """ 54 | ... 55 | 56 | 57 | @dataclass() 58 | class DataTypes(DataClasses.DataTypes): 59 | """ 60 | Data class that defines different data types used in the code. 61 | 62 | Inherited-Attributes: 63 | DWORD (str): 32-bit data type. 64 | FLOAT (str): 32-bit floating-point data type. 65 | DOUBLE (str): 64-bit floating-point data type. 66 | WORD (str): 16-bit data type. 67 | BYTE (str): 8-bit data type. 68 | QWORD (str): 64-bit data type. 69 | XOR (str): XOR data type. 70 | UTF_8 (str): UTF-8 character encoding. 71 | UTF_16LE (str): UTF-16LE character encoding. 72 | """ 73 | ... 74 | 75 | 76 | class AndroidMemoryTool(object): 77 | """ 78 | Android Memory Tool for reading, writing, and analyzing memory of Android applications and processes. 79 | 80 | Attributes: 81 | VERSION_CODE (float): The version code of the AndroidMemoryTool. 82 | DEVELOPER (str): The name of the developer of AndroidMemoryTool. 83 | PLATFORM (str): The platform the script is running on (Linux, Android, Windows). 84 | 85 | Args: 86 | PKG (str or int): The package name or PID of the target Android application. 87 | TYPE (str): The data type for memory operations (default is DWORD). 88 | SPEED_MODE (bool): Enable speed mode for memory operations (default is False). 89 | WORKERS (int): The number of worker processes for parallel memory operations (default is half of CPU cores). 90 | pMAP (PMAP): Process Memory Attributes object to specify memory search attributes (default is empty PMAP). 91 | 92 | Raises: 93 | PIDException: If the specified process is not running in memory. 94 | 95 | Example: 96 | To initialize AndroidMemoryTool and obtain the Process ID (PID) of a target process: 97 | ```python 98 | from androidMemoryTool import AndroidMemoryTool 99 | tool = AndroidMemoryTool(PKG="ac_client") 100 | pid = tool.get_pid() 101 | print(pid) 102 | ``` 103 | 104 | """ 105 | VERSION_CODE = 0.6 106 | DEVELOPER = "Abdul Moez" 107 | PLATFORM = platform.system() 108 | 109 | def __init__(self, PKG: any((str, int)), 110 | TYPE: str = DataTypes.DWORD, 111 | SPEED_MODE: bool = False, 112 | WORKERS: int = int(psutil.cpu_count() / 2), # half of cores available in operating system 113 | pMAP: PMAP = PMAP() 114 | ) -> None: 115 | """ 116 | Initialize AndroidMemoryTool with specified parameters. 117 | 118 | Args: 119 | PKG (str or int): The package name or PID of the target Android application. 120 | TYPE (str): The data type for memory operations (default is DWORD). 121 | SPEED_MODE (bool): Enable speed mode for memory operations (default is False). 122 | WORKERS (int): The number of worker processes for parallel memory operations (default is half of CPU cores). 123 | pMAP (PMAP): Process Memory Attributes object to specify memory search attributes (default is empty PMAP). 124 | 125 | Returns: 126 | None 127 | 128 | Example: 129 | To initialize AndroidMemoryTool with custom parameters: 130 | ```python 131 | tool = AndroidMemoryTool(PKG="ac_client", TYPE=DataTypes.WORD, SPEED_MODE=True, WORKERS=4) 132 | ``` 133 | """ 134 | 135 | self._pkg_name = PKG 136 | if _WIN_PLATFORM: 137 | # PMAP is not supported in Windows API 138 | self._current_instance = AndroidMemoryToolWindows(PKG=PKG, TYPE=TYPE, SPEED_MODE=SPEED_MODE, 139 | WORKERS=WORKERS) 140 | else: 141 | self._current_instance = AndroidMemoryToolLinux(PKG=PKG, 142 | TYPE=TYPE, 143 | SPEED_MODE=SPEED_MODE, 144 | WORKERS=WORKERS, 145 | pMAP=pMAP) 146 | 147 | def __repr__(self) -> str: 148 | """ 149 | Return a string representation of the AndroidMemoryTool instance. 150 | 151 | Returns: 152 | str: A string containing the class name and the target package name or PID. 153 | 154 | Example: 155 | ```python 156 | tool = AndroidMemoryTool(PKG="ac_client") 157 | print(tool) # Output: "AndroidMemoryTool: 'ac_client'" 158 | ``` 159 | """ 160 | 161 | return f'{self.__class__.__name__}: "{self._pkg_name}"' 162 | 163 | @staticmethod 164 | def get_version_code() -> float: 165 | """ 166 | Get the version code of AndroidMemoryTool. 167 | 168 | Returns: 169 | float: The version code. 170 | 171 | Example: 172 | ```python 173 | version = AndroidMemoryTool.get_version_code() 174 | print(version) 175 | ``` 176 | """ 177 | return AndroidMemoryTool.VERSION_CODE 178 | 179 | @staticmethod 180 | def get_cpu_counts(fraction: int = None) -> int: 181 | """ 182 | Get the number of CPU cores available on the device. 183 | 184 | Args: 185 | fraction (int, optional): If specified, returns a fraction of available CPU cores (default is None). 186 | 187 | Returns: 188 | int: The number of CPU cores. 189 | 190 | Example: 191 | ```python 192 | cores = AndroidMemoryTool.get_cpu_counts() 193 | print(cores) 194 | 195 | # Get half of available CPU cores 196 | half_cores = AndroidMemoryTool.get_cpu_counts(fraction=2) 197 | print(half_cores) 198 | ``` 199 | """ 200 | 201 | if fraction: 202 | return int(psutil.cpu_count() / fraction) 203 | return psutil.cpu_count() 204 | 205 | @staticmethod 206 | def get_developer() -> str: 207 | """ 208 | Get the name of the developer of AndroidMemoryTool. 209 | 210 | Returns: 211 | str: The developer's name. 212 | 213 | Example: 214 | ```python 215 | developer = AndroidMemoryTool.get_developer() 216 | print(developer) 217 | ``` 218 | """ 219 | 220 | return AndroidMemoryTool.DEVELOPER 221 | 222 | @staticmethod 223 | def get_platform(verbose: bool = False) -> str: 224 | """ 225 | Get the platform the script is running on (Linux, Android, Windows). 226 | 227 | Args: 228 | verbose (bool, optional): If True, print a message for unsupported platforms (default is False). 229 | 230 | Returns: 231 | str: The platform name. 232 | 233 | Example: 234 | ```python 235 | platform_name = AndroidMemoryTool.get_platform(verbose=True) 236 | print(platform_name) 237 | ``` 238 | """ 239 | 240 | if platform.system() == 'Windows': 241 | return "Windows" 242 | elif platform.system() == 'Linux': 243 | return 'Linux' 244 | elif check_output(['uname', '-o']).strip() == b'Android': 245 | return 'Android' 246 | else: 247 | if verbose: 248 | print('[DEBUG] This platform is not currently supported by AndroidMemoryTool') 249 | return platform.system() 250 | 251 | @staticmethod 252 | def is_root_acquired() -> bool: 253 | """ 254 | Check if the script is running with root/administrator privileges. 255 | 256 | Returns: 257 | bool: True if running with root/admin privileges, False otherwise. 258 | 259 | Example: 260 | ```python 261 | is_root = AndroidMemoryTool.is_root_acquired() 262 | print(is_root) 263 | ``` 264 | """ 265 | 266 | if _WIN_PLATFORM: 267 | import ctypes 268 | return True if ctypes.windll.shell32.IsUserAnAdmin() == 1 else False 269 | from os import getuid 270 | return True if getuid() == 0 else False 271 | 272 | def read_value(self, read: any, is_grouped: bool = False, range_val: int = 512) -> any: 273 | """ 274 | Search and read a value or values from memory. 275 | 276 | Args: 277 | read (any): The value or values to search for in memory. 278 | is_grouped (bool, optional): True to group search results, False to return individual results 279 | (default is False). 280 | range_val (int, optional): The maximum range for memory search (default is 512). 281 | 282 | Returns: 283 | any: The value or values found in memory. 284 | 285 | Example: 286 | ```python 287 | values = tool.read_value(100) 288 | founded_offsets = values[0] 289 | founded_values = values[1] 290 | print(founded_values) 291 | print(founded_offsets) 292 | ``` 293 | """ 294 | 295 | # Note: Group search is not available for string data-types 296 | return self._current_instance.read_value(read=read, is_grouped=is_grouped, range_val=range_val) 297 | 298 | def read_write_value(self, read: any, write: any, is_grouped: bool = False, range_val: int = 552) -> any: 299 | """ 300 | Search, read, and optionally write a value or values in memory. 301 | 302 | Args: 303 | read (any): The value or values to search for in memory. 304 | write (any): The value to replace the found value(s) with. 305 | is_grouped (bool, optional): True to group search results, False to return individual results 306 | (default is False). 307 | range_val (int, optional): The maximum range for memory search (default is 552). 308 | 309 | Returns: 310 | any: The value or values found in memory. 311 | 312 | Example: 313 | ```python 314 | values1 = tool.read_write_value(100, 10) 315 | print(values1) 316 | ``` 317 | """ 318 | 319 | # Note: Group search is not available for string data-types 320 | return self._current_instance.read_write_value(read=read, write=write, is_grouped=is_grouped, 321 | range_val=range_val) 322 | 323 | def write_lib(self, base_address: str, offset: str, write_value: any) -> any: 324 | """ 325 | Write a value to a specific memory address. 326 | 327 | Args: 328 | base_address (str): The base address in hexadecimal format. 329 | offset (str): The offset in hexadecimal format. 330 | write_value (any): The value to write to the specified memory address. 331 | 332 | Returns: 333 | any: The result of the write operation. 334 | 335 | Example: 336 | ```python 337 | values1 = tool.write_lib(base_address='0x12345678', offset='0x100', write_value=42) 338 | print(values1) 339 | ``` 340 | """ 341 | 342 | return self._current_instance.write_lib(base_address=base_address, offset=offset, write_value=write_value) 343 | 344 | def read_lib(self, base_address: str, offset: str, value: any((str, int, None)) = None) -> any: 345 | """ 346 | Read a value from a specific memory address. 347 | 348 | Args: 349 | base_address (str): The base address in hexadecimal format. 350 | offset (str): The offset in hexadecimal format. 351 | value (any, optional): A value to compare against the read value (default is None). 352 | 353 | Returns: 354 | any: The value read from the specified memory address. 355 | 356 | Example: 357 | ```python 358 | values1 = tool.read_lib(base_address='0x12345678', offset='0x100', value=None) 359 | print(values1) 360 | ``` 361 | """ 362 | 363 | return self._current_instance.read_lib(base_address=base_address, offset=offset, value=value) 364 | 365 | def refiner_address(self, list_address: list, value_to_refine: any) -> any: 366 | """ 367 | Refine a list of memory addresses based on a specific value. 368 | 369 | Args: 370 | list_address (list): A list of memory addresses. 371 | value_to_refine (any): The value to refine the addresses against. 372 | 373 | Returns: 374 | any: The refined list of memory addresses. 375 | 376 | Example: 377 | ```python 378 | values = tool.read_value(100) 379 | founded_offsets = values[0] 380 | refined_address = tool.refiner_address(list_address=founded_offsets, value_to_refine=50) 381 | print(refined_address) 382 | ``` 383 | """ 384 | 385 | return self._current_instance.refiner_address(list_address=list_address, value_to_refine=value_to_refine) 386 | 387 | def get_module_base_address(self, module_name: str) -> any: 388 | """ 389 | Get the base address of a specific module in the target process. 390 | 391 | Args: 392 | module_name (str): The name of the module to retrieve the base address of. 393 | 394 | Returns: 395 | any: The base address of the module. 396 | 397 | Example: 398 | ```python 399 | base_addr = tool.get_module_base_address("client.so") 400 | print(base_addr) 401 | ``` 402 | """ 403 | 404 | return self._current_instance.get_module_base_address(module_name=module_name) 405 | 406 | def raw_dump(self, lib_name: str, path='./') -> bool: 407 | """ 408 | Dump the memory of a process to a file. 409 | 410 | Args: 411 | lib_name (str): The name of the library to dump. 412 | path (str, optional): The path to save the dump file (default is './'). 413 | 414 | Returns: 415 | bool: True if the dump was successful, False otherwise. 416 | 417 | Example: 418 | ```python 419 | dump = tool.raw_dump(lib_name='client.so', path='./') 420 | print(dump) 421 | ``` 422 | """ 423 | 424 | return self._current_instance.raw_dump(lib_name=lib_name, path=path) 425 | 426 | def find_hex_pattern(self, search_pattern: str) -> any: 427 | """ 428 | Find hex patterns in memory (Linux and Android only). 429 | 430 | Args: 431 | search_pattern (str): The hex pattern to search for. 432 | 433 | Returns: 434 | any: A tuple containing found addresses, total patterns found, and found values. 435 | 436 | Example: 437 | ```python 438 | found_pattern = tool.find_hex_pattern("87 ?? 2B") 439 | for index in range(0, len(found_pattern[0])): 440 | print(f"{found_pattern[0][index]}: {found_pattern[2][index]}") 441 | print(f"Total Pattern found: {found_pattern[1]}") 442 | ``` 443 | """ 444 | 445 | # Note: Not available for Windows API in this version 446 | return self._current_instance.find_hex_pattern(search_pattern=search_pattern) 447 | 448 | def find_and_replace_hex_pattern(self, search_pattern: str, replace_pattern: str) -> any: 449 | """ 450 | Find and replace hex patterns in memory (Linux and Android only). 451 | 452 | Args: 453 | search_pattern (str): The hex pattern to search for. 454 | replace_pattern (str): The hex pattern to replace with. 455 | 456 | Returns: 457 | any: A tuple containing found addresses, total patterns found, and found values. 458 | 459 | Example: 460 | ```python 461 | found_pattern = tool.find_and_replace_hex_pattern("87 ?? 2B", "87 1D 2B") 462 | for index in range(0, len(found_pattern[0])): 463 | print(f"{found_pattern[0][index]}: {found_pattern[2][index]}") 464 | print(f"Total Pattern found and replaced: {found_pattern[1]}") 465 | ``` 466 | """ 467 | 468 | # Note: Not available for Windows API in this version 469 | return self._current_instance.find_and_replace_hex_pattern(search_pattern=search_pattern, 470 | replace_pattern=replace_pattern) 471 | 472 | def dump_maps(self, path="./") -> bool: 473 | """ 474 | Dump the memory maps of a process to a file. 475 | 476 | Args: 477 | path (str, optional): The path to save the memory maps dump file (default is './'). 478 | 479 | Returns: 480 | bool: True if the dump was successful, False otherwise. 481 | 482 | Example: 483 | ```python 484 | is_dumped = tool.dump_maps(path="./") 485 | print(is_dumped) 486 | ``` 487 | """ 488 | 489 | return self._current_instance.dump_maps(path=path) 490 | 491 | def get_pid(self) -> int: 492 | """ 493 | Get the Process ID (PID) of the target process. 494 | 495 | Returns: 496 | int: The PID of the process. 497 | 498 | Raises: 499 | PIDException: If the specified process is not running in memory. 500 | 501 | Example: 502 | ```python 503 | pid = tool.get_pid() 504 | print(pid) 505 | ``` 506 | """ 507 | return int(self._current_instance.get_process_id()) 508 | 509 | def get_memory_profiler(self, logging_file_path: str = "memory_log.txt") -> MemoryProfiler: 510 | """ 511 | Get a MemoryProfiler instance to analyze memory usage. 512 | 513 | Args: 514 | logging_file_path (str, optional): The path to the memory profiling log file (default is "memory_log.txt"). 515 | 516 | Returns: 517 | MemoryProfiler: A MemoryProfiler instance. 518 | 519 | Example: 520 | ```python 521 | memory_profiler = tool.get_memory_profiler(logging_file_path="memory_log.txt") 522 | memory_profiler.start_profiling(logging=False) 523 | ``` 524 | """ 525 | # Only needed PID (Initialize the MemoryTool with pid or name only) 526 | pid = self.get_pid() 527 | return MemoryProfiler(pid=pid, logging_file_path=logging_file_path) 528 | -------------------------------------------------------------------------------- /androidMemoryTool/errors_class.py: -------------------------------------------------------------------------------- 1 | """ 2 | /* 3 | * Date : 2023/09/30 4 | * Version : 0.6 5 | * Author : Abdul Moez 6 | * Email : abdulmoez123456789@gmail.com 7 | * Affiliation : Undergraduate at Government College University (GCU) Lahore, Pakistan 8 | * GitHub : https://github.com/Anonym0usWork1221/android-memorytool 9 | * 10 | * Description: 11 | * This code is governed by the GNU General Public License, version 3 or later. 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program. If not, see . 14 | */ 15 | """ 16 | 17 | 18 | class PIDException(Exception): 19 | def __init__(self, message): 20 | super().__init__(message) 21 | 22 | 23 | class WINAPIException(Exception): 24 | ... 25 | 26 | 27 | -------------------------------------------------------------------------------- /assets/android_memory_tool.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anonym0usWork1221/android-memorytool/d777a7231100eed31cf9af36d1b73de63961da06/assets/android_memory_tool.jpg -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools", "wheel"] 3 | build-backend = "setuptools.build_meta" 4 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file=README.md 3 | license_files=LICENSE.md 4 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """ 2 | /* 3 | * Date : 2023/09/30 4 | * Version : 0.6 5 | * Author : Abdul Moez 6 | * Email : abdulmoez123456789@gmail.com 7 | * Affiliation : Undergraduate at Government College University (GCU) Lahore, Pakistan 8 | * GitHub : https://github.com/Anonym0usWork1221/android-memorytool 9 | * 10 | * Description: 11 | * This code is governed by the GNU General Public License, version 3 or later. 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program. If not, see . 14 | */ 15 | """ 16 | 17 | from setuptools import setup 18 | from pathlib import Path 19 | 20 | this_directory = Path(__file__).parent 21 | long_description = (this_directory / "README.md").read_text() 22 | license = (this_directory / "LICENSE.md").read_text() 23 | # with open("./requirements.txt", "r") as requirement_file: 24 | # tool_requirements = requirement_file.readlines() 25 | # requirement_file.close() 26 | 27 | setup( 28 | name='androidMemoryTool', 29 | version='0.6.3', 30 | packages=["androidMemoryTool", "androidMemoryTool.WindowsAPI", "androidMemoryTool.LinuxAPI", 31 | "androidMemoryTool.CommonAPI"], 32 | license="GPL-3.0 license", 33 | author='Abdul Moez', 34 | author_email='abdulmoez123456789@gmail.com', 35 | description='Proficiently Managing Runtime RAM Memory for Windows, Android, and Linux Platforms', 36 | url='https://github.com/Anonym0usWork1221/android-memorytool', 37 | zip_safe=False, 38 | long_description=long_description, 39 | long_description_content_type='text/markdown', 40 | data_files=[ 41 | ('', ['LICENSE.md']), 42 | ], 43 | install_requires=['psutil'], 44 | entry_points={ 45 | 'console_scripts': ['amt=androidMemoryTool.__main__:execute_cli'] 46 | }, 47 | keywords=[ 48 | 'ps', 'top', 'kill', 'free', 'lsof', 'netstat', 'nice', 'tty', 49 | 'ionice', 'uptime', 'taskmgr', 'process', 'df', 'iotop', 'iostat', 50 | 'ifconfig', 'taskset', 'who', 'pidof', 'pmap', 'smem', 'pstree', 51 | 'monitoring', 'ulimit', 'prlimit', 'smem', 'performance', 52 | 'metrics', 'agent', 'observability', 'Memory Profiler', 'Memory Tool', 'Memory Leak Detection', 53 | 'Cross-Platform Memory Analysis', 'Windows Memory Profiling', 'Linux Memory Monitoring', 54 | 'Android Memory Analyzer', 'Memory Performance Metrics', 'Memory Optimization', 'Software Development Tool', 55 | 'Memory Management', 'Memory Churn Analysis', 'Memory Profiling Library', 'Memory Debugging', 56 | 'Resource Monitoring', 'Performance Metrics', 'Memory Analysis Software', 'Cross-Platform Development', 57 | 'Memory Efficiency', 'Memory Improvement', 58 | ], 59 | python_requires=">=3.6", 60 | classifiers=[ 61 | "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)", 62 | "Operating System :: OS Independent", 63 | "Environment :: Console", 64 | "Intended Audience :: Developers", 65 | "Programming Language :: Python", 66 | "Programming Language :: Python :: 3", 67 | "Operating System :: Microsoft :: Windows :: Windows 10", 68 | "Operating System :: Microsoft :: Windows :: Windows 8", 69 | "Operating System :: Microsoft :: Windows :: Windows 7", 70 | "Topic :: Software Development :: Libraries :: Python Modules", 71 | "Topic :: Software Development :: Quality Assurance", 72 | "Topic :: Utilities", 73 | "Topic :: Software Development :: Embedded Systems", 74 | "Topic :: System :: Operating System", 75 | "Topic :: System :: Systems Administration", 76 | "Topic :: Software Development :: Disassemblers", 77 | "Topic :: Games/Entertainment", 78 | "Topic :: Software Development :: Debuggers", 79 | "Topic :: Software Development :: Libraries :: Python Modules", 80 | "Environment :: Console", 81 | ], 82 | ) 83 | --------------------------------------------------------------------------------