Showing preview only (487K chars total). Download the full file or copy to clipboard to get everything.
Repository: firerpa/lamda
Branch: 9.0
Commit: 1b22a228541c
Files: 96
Total size: 460.3 KB
Directory structure:
gitextract_h60805my/
├── .gitignore
├── CHANGELOG.txt
├── DISCLAIMER.TXT
├── LICENSE
├── README.md
├── SECURITY.md
├── all-llms.txt
├── examples/
│ ├── README.md
│ ├── activity_jump.py
│ └── search_in_taobao.py
├── extensions/
│ ├── README.md
│ ├── example_http_extension.py
│ ├── example_mcp_extension.py
│ ├── firerpa.py
│ ├── mcp_return_types.py
│ └── mcp_sms_reader.py
├── lamda/
│ ├── __init__.py
│ ├── client.py
│ ├── const.py
│ ├── exceptions.py
│ ├── google/
│ │ └── protobuf/
│ │ ├── any.proto
│ │ ├── api.proto
│ │ ├── compiler/
│ │ │ └── plugin.proto
│ │ ├── descriptor.proto
│ │ ├── duration.proto
│ │ ├── empty.proto
│ │ ├── field_mask.proto
│ │ ├── source_context.proto
│ │ ├── struct.proto
│ │ ├── timestamp.proto
│ │ ├── type.proto
│ │ └── wrappers.proto
│ ├── rpc/
│ │ ├── application.proto
│ │ ├── debug.proto
│ │ ├── file.proto
│ │ ├── policy.proto
│ │ ├── proxy.proto
│ │ ├── services.proto
│ │ ├── settings.proto
│ │ ├── shell.proto
│ │ ├── status.proto
│ │ ├── storage.proto
│ │ ├── types.proto
│ │ ├── uiautomator.proto
│ │ ├── util.proto
│ │ └── wifi.proto
│ └── types.py
├── properties.local.example
├── scripts/
│ ├── disable_flag_secure.yaml
│ └── disable_ssl_pinning_simple.yaml
├── setup.py
└── tools/
├── README.md
├── adb_pubkey.py
├── cert.py
├── debugimage.py
├── discover.py
├── firerpa.yml
├── frida_script_generate.py
├── fridarpc.py
├── globalmitm/
│ ├── DNS2SOCKS.c
│ ├── Dockerfile
│ └── entry
├── id_rsa
├── ida.py
├── magisk/
│ ├── META-INF/
│ │ └── com/
│ │ └── google/
│ │ └── android/
│ │ ├── update-binary
│ │ └── updater-script
│ ├── common/
│ │ ├── adb_keys
│ │ ├── properties.local
│ │ ├── server/
│ │ │ └── .keep
│ │ └── service.sh
│ ├── install.sh
│ ├── module.prop
│ └── uninstall.sh
├── objection-1.11.0-command-patch.diff
├── openvpn/
│ ├── Dockerfile
│ ├── config.ovpn
│ ├── entry
│ ├── ovpn-client-new
│ ├── ovpn-client-profile
│ ├── ovpn-client-renew
│ ├── ovpn-client-revoke
│ ├── ovpn-server-new
│ └── vars
├── paddle_ocr_http_api.py
├── requirements.txt
├── root.crt
├── root.key
├── rsync.sh
├── scp.sh
├── socks5/
│ ├── Dockerfile
│ └── entry
├── ssh.sh
├── startmitm.py
├── startmitm.spec
├── test-fridarpc.js
└── test.pem
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
================================================
FILE: CHANGELOG.txt
================================================
CHANGELOG
Version 9.20
-----------
* TOP Bridge now uniformly uses SAPI requests.
* MQTT connection adds max_inflight_messages and session_expiry_interval parameters.
* Added control tasks for system restart, file download, command execution, software update, etc.
* Added OpenAI task executor supporting semantic task execution.
* Integrated MCP extensions (API path /mcp/)
* Updated built-in Frida version.
BREAKING CHANGES
=================================================================
Version 9.20 includes some breaking changes, which only affect users who utilize hub and hub-bridge. The main issue is an adjustment made to the response data format of hub-bridge (this adjustment does not affect the underlying database). This will cause versions prior to 9.15 to fail to connect correctly to the new version of hub-bridge. However, the new version of lamda-server remains compatible with the old version of hub-bridge.
The solutions are as follows:
1. Use the new version of hub and hub-bridge (v3, which supports connecting to both local and remote devices), but all devices must be upgraded to version 9.20 or later.
2. Continue using the old version of hub and hub-bridge, which can normally connect to all versions around 9.20 and earlier.
=================================================================
Version 9.9
-----------
* Task and event system logic adjustments.
Version 9.8
-----------
* Added Python MMKV read/write library.
* Fixed auto exit of top bridge after removing device from hub.
* Improved stealth performance.
Version 9.5
-----------
* Fixed functional abnormalities on some device models.
* Fixed illegal instruction issue with Frida.
Version 9.4
-----------
* Added support for terminating all running tasks.
* Optimized memory usage.
Version 9.3
-----------
* Other optimizations and fixes.
Version 9.2
-----------
* Fixed stack overflow issue on some devices.
* Other optimizations and fixes.
Version 9.0
-----------
* Optimized network performance of the service.
* Switched MCP transport protocol to streamable-http with support for notifications and progress.
* Proxy now supports HTTPS and Shadowsocks protocols.
* Proxy now supports IPv6 and UDP protocols.
* Added P2P Bridge (peer-to-peer connectivity) support.
* Built-in distributed task system.
* Other optimizations and fixes.
Version 8.45
-----------
* Updated Frida version.
* Other compatibility fixes.
Version 8.44
-----------
* Optimized underlying Python compatibility.
* Added interface for playing WAV audio.
* Disabled H.264 screen projection by default.
Version 8.40
-----------
* Fixed incomplete /data mounting issue.
* Improved stability of enhanced automation.
Version 8.38
-----------
* Enhanced automation features.
* Fixed compatibility issues with some Samsung models.
* Optimized audio real-time performance.
Version 8.35
-----------
* Remote desktop now supports real-time audio streaming.
* Fixed segment fault in hex_patch.
Version 8.30
-----------
* Added binary patching interface.
* Added support for using child and sibling in Selector.
* Added support for viewing XML tree layout in remote desktop.
* Updated Frida to fix some issues.
Version 8.28
-----------
* Fixed install_local_file.
* Fixed Frida reporting ID increment issue.
* Improved built-in TensorFlow inference performance.
* Updated some third-party modules.
Version 8.25
-----------
* Added hexedit command.
* Permanently fixed Permission Loophole (maybe).
* Added on-device AI framework (tflite-runtime).
* Updated Frida for better stealth.
Version 8.22
-----------
* Switched to a new SQLite version.
* Remote desktop inspector now displays current coordinates and RGB values.
* Added plugin setup logic.
Version 8.20
-----------
* Added official MCP plugin.
* Improved Frida compatibility.
* Optimized MCP protocol implementation.
* Fixed self-recovery logic.
Version 8.18
-----------
* Reverted problematic Frida version.
* Added support for MCP and HTTP extension plugins.
Version 8.15
-----------
* Fixed service unavailability issue.
* Added support for calling exported scripts via jsonrpc.
* Fixed SSH user directory issue.
* Updated some submodules.
Version 8.12
-----------
* Fixed touch abnormality.
* Added some utility scripts.
* Enhanced stability.
Version 8.10
-----------
* Optimized self-recovery logic.
* Optimized touch compatibility.
Version 8.9
-----------
* Fixed parsing error.
Version 8.8
-----------
* Frida data reporting now supports AMQP.
* Fixed certificate issue caused by upstream library change in cert.py.
* Fixed resource release issue on service restart.
Version 8.5
-----------
* Optimized clipboard sharing logic.
* Added Frida script crash logs.
* Added support for Android 15.
Version 8.0
-----------
* Interfaces now fully support multi-instance applications.
* Remote desktop now supports shared clipboard.
* Added fix configuration for device models that cannot open apps.
* Added Yaml Frida script persistence.
* Fixed compatibility with lower version systems like Android 6.0.
* Fixed automation-related abnormalities on higher version systems.
* Removed/renamed some methods.
* Updated underlying implementation.
Version 7.90
-----------
* Persistent scripts now support spawn mode.
* Added support for output logs from persistent scripts.
* Fixed dump_window_hierarchy.
* Fixed Frida instance retrieval logic error.
Version 7.85
-----------
* Added support for mDNS service broadcasting.
* Added support for enumerating all elements selected by a selector.
* Client now includes an automatic retry mechanism.
* Fixed Bound comparison logic error.
* Allowed loading certificates remotely.
Version 7.80
-----------
* Optimized real-time screen projection smoothness.
* Added support for persistent hook scripts.
* Added Hook RPC support.
* Added data reporting support.
Version 7.76
-----------
* Fixed tool version dependencies.
* Fixed Python version matching issue.
* Updated some submodules.
Version 7.75
-----------
* Added OCR recognition interface.
* Added get_application_by_name.
* Updated some submodules and dependency versions.
Version 7.73
-----------
* Fixed white screen issue for some applications.
Version 7.72
-----------
* Updated some submodules.
* Fixed known issues.
Version 7.71
-----------
* Fixed Permission Loophole #95.
* Fixed enumerate_all_pkg_names.
Version 7.70
-----------
* Updated some submodules.
* Fixed known issues.
Version 7.68
-----------
* Optimized H.264 real-time screen projection.
Version 7.67
-----------
* Removed some invalid program logic.
* Fixed excessively long auto-recovery time.
* Other optimizations and fixes.
Version 7.65
-----------
* Removed IDA-related tools and interfaces.
* Fixed startup failure in some cases.
* Other optimizations and fixes.
* Added enhanced stealth mode.
Version 7.60
-----------
* Optimized image search speed.
* Added support for area screenshot in remote desktop.
* Fixed some remote desktop issues.
Version 7.57
-----------
* Added client interfaces for feature and template-based image search.
* Other optimizations and fixes.
Version 7.55
-----------
* Fixed abnormal display on screen rotation.
* Fixed disconnection on initial remote desktop connection.
* Fixed element existence check.
* Added Meta key definition.
* Other optimizations and fixes.
Version 7.52
-----------
* Fixed Magisk version Leidian emulator compatibility.
* Fixed service not exiting properly.
Version 7.50
-----------
* Permanently fixed Nox emulator compatibility.
* Fixed zombie process caused by logic error.
* New network subscription service, enabling networking without Frp/OpenVPN.
* Fixed multi-resolution system issue.
* Optimized system certificate injection logic for Android 13/14.
* Added support for multi-instance applications (user).
* OpenVPN now supports IPv6.
Version 7.30
-----------
* Fixed Leidian/Nox emulator compatibility.
* Minor adjustments.
Version 7.28
-----------
* Added show_toast interface.
* Built-in proxy now supports DNS traffic.
* startmitm now supports DNS via upstream proxy.
* Fixed Android 10+ Frida spawn.
Version 7.25
-----------
* Fixed scheduled task execution failure.
* Fixed startup failure from Termux.
* Updated built-in Frida version.
Version 7.22
-----------
* Added automatic system time synchronization.
* Updated some built-in modules.
* Minor fixes.
Version 7.20
-----------
* Reduced detection likelihood.
* Optimized lock mechanism, allowing locking for all API resources.
* Fixed emulator compatibility.
* Other minor modifications and fixes.
Version 7.15
-----------
* Added support for Android 14 (SDK 34).
* Fixed monitor registration exception.
* Improved remote desktop compatibility (theoretically supports all devices).
* Fixed scroll_from_bottom_to_top_to_end anomaly. Thanks ThanhPham.
* Fixed code errors in drag_to, long_click.
* Built-in OpenVPN now supports user/password login.
* Remote desktop now supports up to 60 FPS.
* Updated DISCLAIMER.TXT.
* Other minor modifications and fixes.
Version 5.6
-----------
* Fixed incomplete layout export. Thanks ThanhPham.
Version 5.5
-----------
* Fixed file corruption with adb push.
* Added install_local_file interface.
* Optimized code structure.
Version 5.3
-----------
* Added support for custom remote desktop password after using certificate.
* Fixed port multiplexing unsupported on some devices. Thanks alex han.
* Fixed issues with Magisk installation script.
* Fixed Debian launcher compatibility.
Version 5.2
-----------
* Fixed Selector containing False value being invalid. Thanks ThanhPham.
* LAMDA can now be used with other accessibility services simultaneously (Android >= 8.0 only).
Version 5.0
-----------
* Fixed a series of issues related to login certificates.
* Fixed Magisk module configuration reading strategy.
* Remote desktop and RPC now fully support TLS.
* Built-in Debian module can launch a Debian subsystem.
* Fixed remote desktop bugs and made simple layout adjustments.
* Adjusted service internal permissions and related directories.
* Improved server-side stability.
* Adjusted service installation method.
* Proxy service nameserver now supports specifying port.
* Added internal storage (memory configuration) read/write interfaces.
* A series of other updates and fixes.
=================================================================
Note: Version 5.0 client is not fully compatible with version 3.0. Please update both simultaneously.
=================================================================
Version 3.157
-----------
* Added UI element highlighting in inspector.
* Added support for system crash count.
Version 3.155
-----------
* Added Tab key traversal for UI elements.
* Added English character input support in remote desktop.
* Added remote desktop touch support.
Version 3.153
-----------
* Fixed screenshot failure in some scenarios.
* Minor changes.
Version 3.152
-----------
* Minor UI style adjustments.
Version 3.151
-----------
* Fixed high-DPI screen projection stretching issue #41.
Version 3.150
-----------
* Modified scheduled task reload logic.
* Fixed Scapy routing issue.
* Improved compatibility with some Xiaomi devices.
* Fixed Android 11 interface compatibility (Thanks Kate Swan).
* Added support for using 4G as proxy while connected to WiFi.
* Added new UI controls.
Version 3.138
-----------
* Fixed gRPC dependency issue.
* Added retrieval of last system toast.
Version 3.135
-----------
* Fixed remote desktop loading issue.
* Completely fixed race condition in protocol.
* Fixed Windows Python 3.10 compatibility.
* Allowed cross-site calls for HTTP interfaces.
* Added some missing modules.
* Added service status indicator to remote desktop.
* Added remote desktop responsive layout.
* Pre-release next version.
Version 3.123
-----------
* Fixed incomplete retrieval of recent activities.
Version 3.120-1
-----------
* LAMDA can now act as a proxy itself.
* Added interface to get system's recent activities.
* Fixed a potential race condition in protocol.
* Added some commands, removed SQLite db view.
* Experimental H.264 screen projection.
Version 3.108
-----------
* Optimized network disconnection handling.
* Added Redroid (remote android) support.
* Partial compatibility with uiautomator2.
* Added folder upload support.
Version 3.102
-----------
* Fixed file descriptor leak.
* Added support for loading startup config from remote file server.
* Now provides armeabi-v7a server build.
* Fixed root certificate installation failure under Magisk.
* Fixed configuration parsing error.
* Minor UI adjustments.
Version 3.98
-----------
* Added crontab, vi commands.
Version 3.95
-----------
* Fixed build process issues.
* Minor changes.
Version 3.93
-----------
* Added Android constant definitions.
Version 3.90
-----------
* Removed unused libraries to reduce size.
* Removed macOS-incompatible command line history feature from client.
* Updated DISCLAIMER.TXT.
* Updated some dependency versions.
Version 3.83
-----------
* Added WSA support #24 @aimardcr.
* Fixed black screen on note7pro MIUI10 @yu1chaofan.
* Minor changes.
Version 3.80
-----------
* Fixed SSH disconnection issue.
* Reduced package size.
Version 3.78
-----------
* Fixed issue #21 @yu1chaofan.
* Updated frida-server.
Version 3.76
-----------
* Default built-in shell changed to bash.
* Fixed remote desktop touch unusable after network disconnect.
* Fixed OpenVPN zombie process issue.
* startmitm.py now supports specifying adb serial number.
* Added Magisk auto-start support.
Version 3.0.59
-----------
* All UI prompts now in English.
* Fixed an unauthenticated web interface issue.
* Fixed compatibility with older versions.
Version 3.0.55
-----------
* Fixed crash caused by wide-character request headers.
* Merged mitmweb into startmitm process.
* Minor Docker image modifications.
* Added layout inspection support.
Version 3.0.50
-----------
* Added child, sibling selector support.
Version 3.0.48
-----------
* Added portable Windows startmitm command.
* Added support for uploading/downloading files from/to memory.
* Added screenshot() alias.
Version 3.0.47
-----------
* Simplified globalmitm, added HTTP, SOCKS5 proxy support.
* Enhanced webview node finding.
Version 3.0.46
-----------
* Added two-finger zoom support.
* Simplified startmitm DNS man-in-the-middle operations.
Version 3.0.45
-----------
* Added custom server port support (--port).
* Fixed hang on special files during directory index file type detection.
* globalmitm now checks DNS service availability.
* startmitm.py selected wrong network interface when multiple networks present.
* Client communication no longer automatically uses system proxy.
Version 3.0.35
-----------
* Improved built-in ADB performance.
* OpenVPN service now supports auth parameter (default SHA1).
* Fixed issue with scrcpy via built-in ADB.
================================================
FILE: DISCLAIMER.TXT
================================================
DISCLAIMER
To use this service, you (hereinafter referred to as "the user") must agree to all terms of this agreement and complete the entire application process as prompted on the page. You can find DISCLAIMER.TXT in the source code or the released program, or view the copy below.
To download and use the LAMDA software (hereinafter referred to as "this service"), developed by firerpa (address: github.com/firerpa, email: lamda.devel@gmail.com, hereinafter referred to as "the developer"), you must carefully read and agree to all terms in this agreement. Please ensure you have fully understood and agreed to the following content before downloading, installing, or using this software.
You are not authorized to download, install, or use this software and its related services until you have fully read and accepted the terms of this agreement. Once you download, install, or use this software, you are deemed to have read and agreed to all terms of this agreement and are willing to be bound by them.
Risk Disclosure:
This service requires the device to have root access to run, and the default communication protocols and related certificate files are open information, which may increase the risk of your device being compromised.
This service may contain unknown logical errors that could lead to potential risks such as data loss, system crashes, etc. The user decides whether to download and use this service at their own risk.
1. The purpose of this service is to improve the work efficiency of security analysis and testing personnel for tasks such as application behavior and compliance analysis. The related tools provided are for legitimate and compliant app testing, analysis, and mock scenarios.
This service itself does not provide any functionality to intrude, modify, or capture memory and network data of other applications. It integrates services provided by various major open-source frameworks for users to choose from, to facilitate security analysts and to reduce users' repetitive work and management costs.
This service is not for profit. Users can download and use it according to their own needs, and no fees will be charged during the download or use process.
2. This service respects and protects the user's personal privacy and will not steal any information from the user's device. The startup of this service and any rights to read, store, or transmit device data are entirely under the user's control.
3. Users must use this service on a virtual device or a dedicated device with no private data. When using this service, users must comply with the laws and regulations of the People's Republic of China or the user's country or region of residence.
This service must not be used for any illegal purposes, nor for any activities detrimental to others.
4. Users may only use this service for legitimate study, research, or legally authorized application analysis, testing, and other similar activities. If a user violates the above principles and causes losses to a third party while using the software service, the user shall bear all responsibility.
5. The developer shall not be held legally responsible for any accident, negligence, contract breach, defamation, copyright or intellectual property infringement, or any resulting losses (including but not limited to direct, indirect, incidental, or consequential losses) incurred by any entity or individual as a result of downloading or using this service.
6. You may use this service for commercial purposes, but only for extending functionality or developing products based on the features, interfaces, or related services provided by this service. You agree not to use this service, its related services, or interfaces for any purpose that violates local laws and regulations, or to engage in activities that harm the interests of others.
7. The user explicitly agrees to all the contents listed in the terms of this agreement and shall solely bear any potential risks and consequences arising from the use of this service. The developer assumes no legal liability.
8. The developer has the right to unilaterally change, interrupt, or terminate part or all of this service and the terms of this disclaimer and its attachments at any time. Such changes will be announced via message push, web announcements, or other means and will take effect immediately upon publication without separate notification. If you continue to use the service after the disclaimer has been changed, it indicates that you have fully read, understood, and accepted the modified content.
9. If any part of this disclaimer is deemed invalid or unenforceable, that part shall be amended in a manner consistent with applicable law to reflect, as closely as possible, the developer's original intent, and the remaining portions shall remain in full force and effect. The unenforceability of any part of this disclaimer does not constitute a waiver by the developer of the right to enforce that provision.
10. Reservation of Rights: All other rights not expressly granted herein are reserved by the developer.
Please confirm that you have read and accepted all the terms of this agreement. Otherwise, you are not authorized to download, install, or use this software and its related services.
================================================
FILE: LICENSE
================================================
Copyright (c) 2021 - present, firerpa
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================
# **FIRERPA Android** | AI-Powered Automation
<img src="image/logo.svg" alt="FIRERPA" width="200" align="right" />
<p>
<img src="https://img.shields.io/badge/python-3.6+-blue.svg?logo=python&labelColor=yellow" />
<img src="https://img.shields.io/badge/android-6.0+-blue.svg?logo=android&labelColor=white" />
<img src="https://img.shields.io/badge/root%20require-red.svg?logo=android&labelColor=black" />
<img src="https://img.shields.io/github/downloads/rev1si0n/lamda/total" />
<img src="https://img.shields.io/badge/Built--in%20MCP-000.svg?logo=anthropic&labelColor=black" />
</p>
<h6>An all-in-one next-gen Android automation framework blending robust on-device services with AI-ready agents and extensible tool-calling capabilities.</h6>
<p align="left"><a href="https://device-farm.com/docs/en/">Documentation</a> | <a href="https://device-farm.com/docs/zh/">使用文档</a> | <a href="https://t.me/lamda_dev">TELEGRAM</a> | <a href="https://qm.qq.com/q/zDaX2a594I">QQ Group</a> | <a href="https://device-farm.com/llms.txt">llms.txt</a> | <a href="https://device-farm.com/llms-full.txt">llms-full.txt</a></p>
<h3>Core Capabilities</h3>
FIRERPA is a lightweight, on-device Android automation stack with no external dependencies. It unifies low-latency remote desktop with 160+ APIs for device discovery/status/logs, system and app control, UI automation, OCR/image matching, file I/O, storage, scheduling, and shell execution, plus distributed deployment via Hub/FRP/OpenVPN. Built-in ADB/SSH/SCP, logging, API locking, certificates, script encryption, proxy/VPN/MITM utilities, and reverse tooling (Frida persistence/export/report, IDA debugging, binary patching) round out production workflows, alongside MCP/Agent tool-calls and extensions.
<p align="center">
<img src="https://raw.githubusercontent.com/wiki/firerpa/lamda/images/banner.gif" alt="MCP" width="100%">
</p>
<h3>Deployment & Stability</h3>
FIRERPA delivers stable automation across Android 6.0 to 16, including emulators and cloud phones. Built to run non-intrusively, it requires no complex configuration and supports rooted, long-running services with clean install/upgrade/uninstall flows and repeatable lifecycle control.
<h3>160+ APIs & Python SDK</h3>
FIRERPA offers extensive APIs covering system config, app/process control, UI selectors/gestures, file I/O, encrypted key-value storage, OCR, and image matching. The Python SDK mirrors the full surface and provides higher-level helpers to build reliable automation quickly.
<p align="center">
<img src="image/inspect.png" alt="demo" width="100%">
</p>
<h3>Remote Desktop & Diagnostics</h3>
Monitor and control devices visually with minimal setup. Includes file upload/download, WebSocket video (MJPEG/H.264), touch streaming, UI inspection, and event monitoring for validation and real-time diagnostics.
<p align="center">
<img src="image/demo.gif" alt="demo" width="100%">
</p>
---
Our project is not entirely open source to prevent increased countermeasure costs. We remain committed to keeping it free for the community. We guarantee no malicious code; you are welcome to reverse-engineer and analyze its security. See our [Privacy Policy](https://device-farm.com/privacy) or [Contact Us](https://device-farm.com/contact) for enterprise support.
================================================
FILE: SECURITY.md
================================================
# Security Policy
## Supported Versions
| Version | Supported |
| ------- | ------------------ |
| 9.x | :white_check_mark: |
## Reporting a Vulnerability
mailto:lamda.devel@gmail.com
================================================
FILE: all-llms.txt
================================================
FIRERPA (FIRERPAλ) - COMPLETE DOCUMENTATION
========================================
TABLE OF CONTENTS
========================================
1. OVERVIEW
2. SETUP & INSTALLATION
- Requirements
- Pre-installation Checklist
- Server Installation Methods
- Client Installation
- Network Configuration
- Starting/Stopping/Uninstalling
3. BASIC USAGE
- Preparation for Use
- Connecting via Python
4. REMOTE DESKTOP
- Access & Connection
- Security Configuration
- File Management
- Customization Options
- Limitations
5. DEVICE DISCOVERY (mDNS)
6. SCHEDULED TASKS
7. BUILT-IN TERMINAL
8. VIRTUAL DEBIAN ENVIRONMENT
9. PROGRAMMING INTERFACE (API)
- Basic Operations
- Device Information
- Screen Control
- Input Operations
10. ADVANCED INTERFACE OPERATIONS
11. IMAGE MATCHING OPERATIONS
12. TEXT RECOGNITION (OCR)
13. INTERFACE MONITOR
14. BUILT-IN FRIDA
- Three Usage Methods
- Persistent Frida Scripts
- Frida Data Reporting
- Frida Exported Interfaces (RPC)
15. BINARY PATCH
16. BUILT-IN ADB MANAGEMENT
17. CONNECTIVITY
- SSH Connection
- File Transfer (SCP)
18. ONE-CLICK PACKET CAPTURE
19. ENCRYPTION CERTIFICATES
20. DISTRIBUTED DEPLOYMENT
21. DEPLOYING OPENVPN SERVICE
22. INTERFACE LOCKING
23. VERSION HISTORY
========================================
1. OVERVIEW
========================================
FIRERPAλ is an Android device management and automation system that requires rooted devices.
It provides:
- Remote desktop access via browser
- Comprehensive automation APIs (160+ interfaces)
- Advanced debugging capabilities
- Built-in Frida with anti-detection
- Packet capture and network manipulation
- Device farm management
ARCHITECTURE:
- Server component: Runs on rooted Android devices
- Client library: Python 3.6-3.12
- Default port: 65000
- Browser-based remote desktop (Chrome 95+)
PHILOSOPHY:
The documentation emphasizes sequential reading from first chapter. Users should read
documentation when encountering issues before seeking community support.
========================================
2. SETUP & INSTALLATION
========================================
REQUIREMENTS
------------
- Android phone with root privileges
- Minimum: 2GB+ RAM, 1GB+ available storage
- Python 3.6-3.12 for client
- Recommended emulators: NoxPlayer, LDPlayer, AVD
- Chrome 95+ browser for remote desktop
PRE-INSTALLATION CHECKLIST
---------------------------
CRITICAL - Must complete before installation:
1. Turn off Magisk Hide
2. Turn off frida-server
3. Restart the device after confirmation
4. Disable or uninstall all accessibility services (talkback, autojs, etc.)
5. Configure timezone and time correctly
- Settings > Date and Time
- Verify timezone matches location
- If automatic fails, set manually
Skipping these steps may cause compatibility issues with FIRERPA interfaces and
remote desktop functionality.
NETWORK CONFIGURATION
---------------------
Real Devices:
- Ensure computer and phone share the same network
Emulators:
- Android x86 (VMWare): Set bridge mode in virtual machine settings
- LDPlayer/NoxPlayer: Install drivers, enable bridge mode, restart
- Android Studio AVD: Execute `adb forward tcp:65000 tcp:65000`, use localhost to connect
- WSA (Windows Subsystem Android):
* Minimum version: 2210.40000 (rooted)
* Settings: Continuous subsystem resources, disable Advanced Networking
* Enable Developer Mode and "Support single machine UI automation"
* Restart subsystem
- Redroid (Docker-based Android):
* For Ubuntu 20.04:
apt install linux-modules-extra-`uname -r`
* Edit /etc/modules, add: mac80211_hwsim, binder_linux, ashmem_linux
* Restart host machine
* Launch container mapping port 65001 to container's 65000
* Access via: http://127.0.0.1:65001
Note: Use localhost instead of 127.0.0.1 for tools requiring USB-based detection.
SERVER INSTALLATION METHODS
----------------------------
METHOD 1: APP Installation (Easiest)
-------------------------------------
"This installation method is the easiest and doesn't require you to do any extra operations."
Steps:
1. Download lamda-autorun.apk from the provided link
2. Install and grant root privileges within the app
3. Enable auto-start switch
4. Add app to auto-start whitelist if device settings restrict auto-launching
5. Restart the device
6. Wait 1-2 minutes after restart
7. Find device's IP address in WIFI settings (example: 192.168.1.8)
8. Navigate to http://192.168.1.8:65000 in browser to access remote desktop
Troubleshooting:
- If connection fails, check network connectivity and simulator bridging
- Manual startup: adb shell su root sh /data/server/bin/launch.sh
METHOD 2: Magisk Module Installation
-------------------------------------
For devices running Magisk 20.4 or newer:
1. Download lamda-magisk-module.zip from releases page
2. Use Magisk App → Modules → Install from local
3. Restart device
The system waits 30 seconds before launching to prevent crashes.
Auto-starts at boot.
METHOD 3: Manual Installation
------------------------------
For devices without Magisk support:
Step 1: Get Device Architecture
getprop ro.product.cpu.abi
Common outputs: arm64-v8a, x86, x86_64, armeabi-v7a
Step 2: Push Files
adb push lamda-server-[arch].tar.gz /data/local/tmp
adb push busybox-[arch] /data/local/tmp
Step 3: Extract and Configure
After entering `adb shell` and running `su`:
chmod 755 /data/local/tmp/busybox-[arch]
/data/local/tmp/busybox-[arch] tar -C /data -xzf /data/local/tmp/lamda-server-[arch].tar.gz
rm /data/local/tmp/lamda-server-[arch].tar.gz
rm /data/local/tmp/busybox-[arch]
IMPORTANT CONFIGURATION NOTES:
- Directory Location: /data/usr serves as FIRERPA's user data directory
- Must be manually created before first startup if pre-configuring
- Startup Configuration: Use properties.local file for automatic connections to OpenVPN,
proxies, and port forwarding
- Security Warning: "FIRERPA installed by default does not enable any authentication"
Requires trusted network usage unless encryption certificates are enabled
CLIENT INSTALLATION
-------------------
Requirements: Python 3.6-3.12
Installation:
pip3 install -U lamda[full]
Alternative (if above fails):
pip3 install -U 'lamda[full]'
Tip: Add -i parameter to specify nearby PIP mirror for faster installation
Verification:
python3 -m lamda.client
COMMON ISSUES AND SOLUTIONS:
1. UnicodeEncodeError with Chinese characters:
- Occurs when system paths contain non-ASCII characters
- gRPC cannot process correctly
- Verify: import sys; print(sys.path)
- Remove Chinese paths from environment variables like PYTHONPATH
2. ImportError problems:
pip3 install -U --force-reinstall 'lamda[full]'
3. Persistent issues:
- Use a virtual environment (uv recommended based on user settings)
Post-installation:
Update third-party Frida-dependent libraries (frida-tools, objection, etc.)
to prevent runtime anomalies.
STARTING THE SERVER
-------------------
Auto-starts if installed via Magisk or auto-start APP after device restart.
Manual installations require:
sh /data/server/bin/launch.sh
Steps:
1. Enter adb shell and switch to root: su
2. Run launch command
3. Wait for process completion
4. Success message: "llllaamDaa started"
5. Service operates in background mode
TROUBLESHOOTING ERROR MESSAGES:
Error | Resolution
-----------------------|--------------------------------------------------
already running | Service is currently active
invalid TZ area | System timezone requires configuration
not run as root | Command must execute with root privileges
unsupported sdk | Android version incompatibility detected
abi not match | Incorrect architecture package installed
file broken | Reinstall required due to corruption
Notes:
- Initial remote desktop access may experience loading delays; device restart may help
- After successful startup, close terminal and proceed with other tasks
- Failed startups require troubleshooting based on displayed error messages
EXITING THE SERVER
------------------
"FIRERPAλ is intended for continuous 24/7 operation; frequent starts/stops are not recommended"
METHOD 1: Command Line (Primary)
kill -SIGUSR2 $(cat /data/usr/lamda.pid)
METHOD 2: Interface-Based
Use "Shutdown and Restart" section in documentation
IMPORTANT WARNINGS:
- Service Design: Intended for continuous operation; frequent cycling not recommended
- Exit Duration: Complete shutdown may require more than 10 seconds
- Command Caution: Execute kill command only once; repeating consecutively can cause instability
- Risk Advisory: Frequent service cycling can easily introduce system instability
UNINSTALLING THE SERVER
------------------------
Prerequisites:
Follow "Exiting the Server" and wait at least 30 seconds to ensure service exits normally.
STANDARD INSTALLATION:
rm -rf /data/server /data/usr
MAGISK INSTALLATION:
Simply remove from Magisk modules—no additional commands needed.
WARNING:
"Please be cautious when removing the /data/usr directory, as this directory contains
relevant data for your use of the FIRERPA service."
After uninstallation:
"FIRERPA will be completely removed from your device without leaving any other files."
Recommend restarting device afterward to ensure clean completion.
========================================
3. BASIC USAGE
========================================
PREPARATION FOR USE
-------------------
Before continuing with automation and API usage:
1. Ensure FIRERPA server on mobile device has been started
2. Default port: 65000
3. Obtain device IP address from WLAN settings
4. Port specification not necessary unless modified from default
5. NAT Considerations: Some devices behind NAT; refer to Installation Preparation if
IP identification problematic
Example: Documentation assumes reference device IP of 192.168.0.2
CONNECTING VIA PYTHON
----------------------
Basic connection:
from lamda.client import *
d = Device("192.168.0.2")
With encryption certificate:
from lamda.client import *
d = Device("192.168.0.2", certificate="/path/to/lamda.pem")
WORKING WITH API RESPONSES:
Many interfaces return native proto classes. Access values through output properties:
result = status.get_battery_info()
print(result.batt_temperature) # Access specific field
SIMPLE EXAMPLES:
Display message on device:
d.show_toast("Hello from Lamda!")
Make device beep (useful for locating devices):
d.beep()
========================================
4. REMOTE DESKTOP
========================================
ACCESS & CONNECTION
-------------------
Network-based device control via browser.
Access: http://192.168.0.2:65000
Requirements: Chrome 95+ browsers
Mode: Single active operator - only first connected user can control device
Subsequent connections display in view-only mode
SECURITY CONFIGURATION
----------------------
When launching server with encryption certificates via --certificate:
- HTTPS becomes mandatory
- Convert URL to https://
- Authenticate with certificate's embedded password (line one of certificate file)
- Custom credentials in properties.local: ssl-web-credential=12345
FILE MANAGEMENT
---------------
Upload Capability:
- Drag files/folders directly onto right-side terminal
- Supports up to 2,000 simultaneous uploads
- 256MB per-file limit
- All uploaded content receives 644 permissions
- Default storage: /data/usr/uploads (configurable)
Download Method:
- Access http://192.168.0.2:65000/fs/ to browse device files in index format
- Or use folder icon in remote desktop's top-right corner
CUSTOMIZATION OPTIONS
----------------------
Settings gear (upper-right) provides adjustments for:
- Upload directory
- Frame rate
- Resolution scaling
- Image quality
- Clipboard sharing
Video Encoding Options:
- H.264: Reduces bandwidth
- Hardware acceleration: Select "System" backend
- "Default": Uses software encoding
LIMITATIONS
-----------
- Platform doesn't support Chinese character input—only standard English text works
- For enhanced keyboard functionality, explore "Connecting to Built-in ADB" documentation
- Tools like Genymobile/scrcpy recommended for advanced input
========================================
5. DEVICE DISCOVERY (mDNS)
========================================
OVERVIEW
--------
mDNS-based discovery of FIRERPA devices on local networks.
Requirements: FIRERPA version 7.85 or higher
Enables: Locate devices and access services via domain names like {ro.serialno}.local
DEVICE DISCOVERY METHODS
------------------------
Using mdns-beacon Tool:
1. Install: pip install mdns-beacon
2. Execute: mdns-beacon listen --service _lamda._tcp.local.
Displays all devices running FIRERPA on current network.
Note: Results may vary based on network configuration and device model compatibility.
Accessing via Domain Names:
Format: {android_id}.local:65000
Retrieve Android device ID: adb shell settings get secure android_id
Programmatic Discovery:
Use zeroconf library (python-zeroconf) for automated device discovery and
Android device information enumeration.
CONFIGURATION OPTIONS
---------------------
Customize via properties.local configuration file:
mdns.meta=true - Enable broadcasting of device information (ID, ABI, Android version, model)
mdns.service - Modify mDNS service name from default "lamda"
mdns.name - Assign fixed, unique server names per device for enhanced privacy
========================================
6. SCHEDULED TASKS
========================================
OVERVIEW
--------
Enable periodic execution of scripts following standard Linux Crontab syntax.
"All rules will be executed with root privileges."
REQUIREMENTS:
- Access via FIRERPA's remote desktop terminal, built-in ADB terminal, or SSH terminal
- Basic crontab knowledge required
- Device should remain powered on for reliable task execution
IMPLEMENTATION STEPS
--------------------
1. Enter Edit Mode: crontab -e
2. Write Rules: Press 'i' (English input mode), then add crontab expressions
3. Save: Press ESC, then SHIFT+:, type 'wq' and press Enter
COMMON CRONTAB EXAMPLES
-----------------------
Schedule | Command
----------------------------------|------------------------------------------------
System startup | @reboot echo Execute when framework starts
Hourly | 0 */1 * * * echo Execute every hour
Every minute | * * * * * echo Execute every minute
Daily at 8 AM | 0 8 * * * echo Execute at 8 o'clock daily
OUTPUT REDIRECTION:
To save task output:
* * * * * echo hello >/data/usr/script.log 2>&1
IMPORTANT NOTE:
"Due to Android's sleep mechanism, scheduled tasks may not run at the time you expect
after the screen is turned off."
VALIDATION:
Use an online Crontab verification website to confirm rule accuracy before deployment.
========================================
7. BUILT-IN TERMINAL
========================================
OVERVIEW
--------
Key FIRERPA feature allowing real-time command execution.
Access: Through remote desktop, SSH, or ADB connections
Note: "The terminal supports command completion but not parameter completion."
AVAILABLE ALIASES
-----------------
l → ls
ll → ls -l
la → ls -la
py → python
.. → Navigate parent directory
... → Navigate parent directories
t → Go to /data/local/tmp
p → Return to previous directory
BUILT-IN COMMANDS
-----------------
The system includes numerous pre-installed tools:
Development & Analysis:
Python, strace, ltrace, frida tools suite, capstone, keystone, unicorn
Networking:
curl, scapy, tcpdump, socat, stunnel, redir, iperf3
System Tools:
busybox, ncdu, nano, vi, sqlite3, MemDumper, fsmon
PYTHON LIBRARIES
----------------
Pre-installed third-party modules:
Cryptography:
Crypto, OpenSSL, bcrypt, cryptography
Image Processing:
PIL, cv2
Data Handling:
protobuf, msgpack, ujson
Networking:
requests, websocket
Specialized Tools:
frida, numpy, redis, peewee
CRITICAL LIMITATION:
"You cannot install additional libraries through PIP or APT in the built-in terminal environment."
Users requiring additional dependencies should utilize the Virtual Debian Environment.
========================================
8. VIRTUAL DEBIAN ENVIRONMENT
========================================
OVERVIEW
--------
Complete Debian environment within Android devices, comparable to Termux or androdeb.
Allows: apt package installation, software compilation, BPF program development on Android.
INSTALLATION STEPS
------------------
1. Download Package:
Obtain lamda-mod-debian-arm64-v8a.tar.gz from project's release page
(select architecture-appropriate version)
2. Upload via Remote Desktop:
Use remote desktop interface to drag and upload file
Default location: /data/usr/uploads
3. Extract Installation (one-time command):
tar -C /data/usr/modules -xzf /data/usr/uploads/lamda-mod-debian-arm64-v8a.tar.gz
BASIC USAGE
-----------
Entering Interactive Terminal:
debian /bin/bash
Executing Single Commands:
debian /bin/bash -c id
IMPORTANT LIMITATION:
"Only one instance can enter the virtual environment at a time. After you execute
debian /bin/bash and keep using it, if you continue to execute this command in other
terminals, it will return an error, unless you exit the first started debian /bin/bash."
ADVANCED SETUP (SSH + Python)
------------------------------
1. Enter environment and install packages
2. Configure SSH settings in sshd_config
3. Set root password
4. Start SSH daemon: debian /usr/sbin/sshd -D -e
5. Automate via scheduled tasks:
@reboot debian /usr/sbin/sshd -D -e >/data/usr/sshd.log 2>&1
6. Connect remotely: ssh root@[device-ip]
Default password: lamda
========================================
9. PROGRAMMING INTERFACE (API)
========================================
OVERVIEW
--------
"Up to 160 programming API interfaces, allowing you to manage and operate Android
devices meticulously."
Coverage: Command execution, system settings, application management, automation,
proxy services, file operations.
BASIC OPERATIONS
----------------
Display Message:
d.show_toast("message")
Beep (useful for locating devices):
d.beep()
DEVICE INFORMATION
------------------
Get Device Details:
d.device_info()
Returns: productName, sdkInt, displayHeight, displaySizeDpX, displaySizeDpY,
displayWidth, screenOn, naturalOrientation, currentPackageName
Server Information:
d.server_info()
Returns: uniqueId, version, architecture, uptime, secure metrics
Screen Status Checks:
d.is_screen_on() - Determines if display is active
d.is_screen_locked() - Verifies lock state
SCREEN CONTROL
--------------
d.sleep() - Deactivates display (power button equivalent)
d.wake_up() - Activates display
d.screenshot(60) - Captures with quality parameter
d.screenshot(60, Bound(top, left, right, bottom)) - Regional cropping
INPUT OPERATIONS
----------------
Touch Actions:
d.click(Point(x=100, y=100)) - Single tap
d.drag(Point_A, Point_B) - Press and drag motion
d.swipe(Point_A, Point_B) - Swipe gesture
d.swipe_points(p1, p2, p3) - Multi-point swipe path
Physical Keys:
d.press_key(Keys.KEY_BACK) - Supports KEY_BACK, KEY_HOME, KEY_VOLUME_UP,
KEY_POWER, and others
d.press_keycode(code) - Additional Android KeyEvent codes
SYSTEM INTERFACE
----------------
d.get_clipboard() - Retrieve clipboard content (Android 9 and below)
d.set_clipboard("text") - Write clipboard data
d.dump_window_hierarchy() - Extract current page XML layout
d.wait_for_idle(5000) - Pause until UI stabilizes (milliseconds)
d.get_last_toast() - Retrieve latest system toast
SETTINGS & NOTIFICATIONS
------------------------
d.open_quick_settings() - Display settings panel (half-open state)
d.open_notification() - Expand notification drawer
========================================
10. ADVANCED INTERFACE OPERATIONS
========================================
OVERVIEW
--------
Deep automation interfaces for detailed operations on mobile device interfaces.
GETTING ELEMENTS
----------------
Elements located via selectors.
Important: "The element you click directly on the left interface may not be the actual
element" due to overlapping components.
Selection Methods:
element = d(text="同意")
element = d(resourceId="com.tencent.news:id/btm_first_agree")
ELEMENT OPERATIONS
------------------
Click Operations:
element.click() - Standard click
element.click_exists(corner=Corner.COR_TOPLEFT) - Click with position specification
element.long_click() - Extended press
element.click_exists() - Conditional click without exception throwing
Information Retrieval:
element.exists() - Existence check
element.info() - Detailed metadata (bounds, className, text)
element.count() - Quantity of matching elements
element.get(3) - Retrieve specific nth element
Coordinate Access:
info.bounds.center() - Center point
info.bounds.corner("top-left") - Corner coordinates
info.bounds.width, info.bounds.height - Dimensions
TEXT INPUT
----------
Important: "When getting input box elements, your input method must be in a popped-up
state before finding relevant elements."
Operations:
element.set_text("你好世界") - Input text
element.get_text() - Retrieve current content
element.clear_text_field() - Clear field
NAVIGATION
----------
Normal Swipe:
d().swipe(direction=Direction.DIR_UP, step=32)
Fast Swipe:
d().fling_from_top_to_bottom()
d().fling_from_bottom_to_top()
d().fling_from_left_to_right()
d().fling_from_right_to_left()
Scroll (mechanical swiping):
d().scroll_from_top_to_bottom(step)
d().scroll_from_bottom_to_top(step)
d().scroll_from_left_to_right(step)
d().scroll_from_right_to_left(step)
ADVANCED FEATURES
-----------------
element.wait_for_exists(10*1000) - Wait for appearance (milliseconds)
element.wait_until_gone(10*1000) - Wait for disappearance
element.screenshot(quality=60) - Capture element-level screenshots
element.child(index=1) - Locate child elements
element.sibling() - Find sibling elements
element.drag_to(Selector(...)) - Drag operations
========================================
11. IMAGE MATCHING OPERATIONS
========================================
OVERVIEW
--------
Locate and interact with UI elements through image matching.
Two primary methods: Template matching and Feature point matching (SIFT)
KEY CONCEPTS:
- Template matching: Better performance, consistent resolutions
- Feature point matching: Adapts to varying screen resolutions, requires threshold tuning
- Processing: Server-side (preserves local resources)
- Limitation: Mobile device performance may limit efficiency
MAIN FUNCTION SYNTAX
--------------------
d.find_similar_image(
data,
threshold=0.0,
distance=250,
scale=1.0,
area=FindImageArea.FIA_WHOLE_SCREEN,
method=FindImageMethod.FIM_TEMPLATE
)
PARAMETERS
----------
Parameter | Purpose
-----------|------------------------------------------------------------------
data | Image byte data for matching
threshold | Similarity filter level
distance | Maximum feature point tolerance
scale | Performance optimization ratio
area | Screen region restriction
method | Algorithm selection
MATCHING METHODS
----------------
FIM_TEMPLATE:
- Fast, texture-dependent
- Limited rotation/scale tolerance
FIM_FEATURE:
- Robust against transformations
- Suits complex scenes
MATCHING AREAS
--------------
Nine predefined regions optimize performance:
- Full-screen
- Halves: left, right, top, bottom
- Corners: top-left, top-right, bottom-left, bottom-right
========================================
12. TEXT RECOGNITION (OCR)
========================================
OVERVIEW
--------
Leverage OCR technology for interface automation when conventional selectors don't work.
Particularly useful in gaming applications.
OCR BACKEND SETUP
-----------------
PaddleOCR with GPU:
d.setup_ocr_backend(
"paddleocr",
quality=80,
use_gpu=True,
drop_score=0.85,
use_space_char=True
)
EasyOCR for Chinese and English:
d.setup_ocr_backend("easyocr", ["ch_sim", "en"], quality=80)
Custom HTTP Backend:
For systems managing multiple devices, implement custom backend as HTTP service
to avoid excessive resource consumption from repeated initialization.
OCR SELECTOR TYPES
------------------
Method | Purpose
------------------------|--------------------------------------------------
text="Mine" | Exact text matching
textContains="Mine" | Partial text matching
textMatches=".*?Mine" | Regex pattern matching
SUPPORTED OPERATIONS
--------------------
- click() - Activate matched element
- click_exists() - Conditional activation
- exists() - Verify presence
- screenshot() - Capture element image
- info() - Retrieve OCR metadata
KEY LIMITATION:
"OCR recognition methods only support checking if elements exist, clicking, taking
screenshots, and other operations."
More complex interactions aren't available through this interface.
========================================
13. INTERFACE MONITOR
========================================
OVERVIEW
--------
Real-time listener for UI changes that performs automatic actions when conditions match.
Functions similarly to ad-skipping automation tools.
Caution: Performs automatic clicks and key presses that may interfere with manual tasks.
CORE FUNCTIONS
--------------
Enable Monitoring:
d.set_watcher_loop_enabled(True)
Check Status:
d.get_watcher_loop_enabled()
Disable Monitoring:
d.set_watcher_loop_enabled(False)
Clear All Events:
d.remove_all_watchers()
EVENT REGISTRATION TYPES
------------------------
Click Events - Auto-clicks matching UI elements:
d.register_click_target_selector_watcher("EventName", [Selector conditions], target_selector)
Key Press Events - Triggers keyboard actions:
d.register_press_key_watcher("EventName", [Selector conditions], Keys.KEY_CODE)
Count Events - Tallies element appearances:
d.register_none_op_watcher("EventName", [Selector conditions])
Retrieve counts:
d.get_watcher_triggered_count("EventName")
EVENT MANAGEMENT
----------------
Enable: d.set_watcher_enabled("name", True)
Disable: d.set_watcher_enabled("name", False)
Remove: d.remove_watcher(name)
Check status: d.get_watcher_enabled(name)
CRITICAL NOTE:
"Any matching event operations that occur on the interface before enabling the monitor
or enabling monitor events will not be processed!"
========================================
14. BUILT-IN FRIDA
========================================
OVERVIEW
--------
FIRERPA includes integrated, latest-version Frida with:
- Built-in anti-detection patches
- Custom hiding features
- No separate frida-server setup required
IMPORTANT: "Since FIRERPA version 7.18, the built-in FRIDA requires a token parameter
to connect."
THREE USAGE METHODS
-------------------
METHOD 1: Code-Based Integration
---------------------------------
Access Frida through FIRERPA client API:
conn = d.frida
conn.enumerate_processes()
Alternative approach using direct token retrieval:
token = d._get_session_token()
manager = frida.get_device_manager()
conn = manager.add_remote_device("192.168.0.2:65000", token=token)
METHOD 2: Command-Line Usage
-----------------------------
Documentation emphasizes using frida commands through remote desktop for simplicity
(no additional parameters needed there).
For local machine usage, command structure requires:
- Host parameter: -H 192.168.0.2:65000 (not -U)
- Token parameter: --token xxxxxxxxxxxxxxxx
- Optional certificate: --certificate /path/to/lamda.pem (if encryption enabled)
Example command:
frida -H 192.168.0.2:65000 -f com.android.settings --token xxxxxxxxxxxxxxxx
METHOD 3: Objection Tool Integration
-------------------------------------
Patched version available at provided GitHub repository.
Usage pattern:
objection -N -h 192.168.0.2 -p 65000 --token xxxxxxxxxxxxxxxx explore
CRITICAL DETAILS:
- Default FIRERPA port is 65000 (not Frida's 27042)
- Tokens are fixed 16-character strings
- Encryption certificates require additional command parameters for security
========================================
PERSISTENT FRIDA SCRIPTS
========================================
OVERVIEW
--------
FIRERPA enables persistent Frida script injection with automatic management.
Scripts remain active even after app crashes or process exits.
Automatic reinitiation upon app restart.
Feature launched in version 7.80.
INSTALLING SCRIPTS
------------------
Deploy scripts to applications:
app = d.application("com.android.settings")
app.attach_script(script, runtime=ScriptRuntime.RUNTIME_QJS, standup=5)
IMPORTANT LIMITATION:
"The script manager only allows one script to be injected for each APP at a time."
Parameters:
- script: Frida script content (supports bytecode)
- runtime: Execution environment (default: qjs)
- standup: Delay in seconds (1-300) to prevent early injection issues
- spawn: Optional mode for automatic app restart on failure
UNINSTALLING SCRIPTS
--------------------
Remove injected scripts and halt monitoring:
app = d.application("com.android.settings")
app.detach_script()
STATUS VERIFICATION
-------------------
Two diagnostic methods:
Installation check:
app.is_attached_script()
Injection verification (determines if script actively runs in process):
app.is_script_alive()
SCRIPT LOGGING
--------------
Monitor console output and errors by filtering logs:
grep SCRIPT /data/local/tmp/server.log
OFFLINE PERSISTENCE
-------------------
Configure scripts as YAML files in /data/usr/modules/script/ for automatic loading
without API calls.
Configuration template:
enable: true
application: "com.android.settings"
version: "2.10"
runtime: "qjs"
script: !!binary "[base64-encoded content]"
standup: 10
spawn: false
The system monitors this directory for real-time updates, additions, and deletions.
========================================
FRIDA DATA REPORTING
========================================
OVERVIEW
--------
Enable automatic capture of method call data through custom scripts.
Automated submission to Redis, HTTP interfaces, or RabbitMQ queues.
Supports data compression via zlib for improved network performance.
WRITING REPORTING SCRIPTS
--------------------------
Scripts use emit(name, content) method to submit intercepted data.
Parameters:
- name: Identifies data type (e.g., "product_info")
- content: Actual data as string or byte array
Example structure:
Java.perform function intercepts okhttp traffic, extracts URL and body information,
then emits data as JSON string.
DATA REPORTING DESTINATIONS
----------------------------
Three supported options:
- Redis queues (simplest, no metadata)
- HTTP interfaces (includes metadata via query parameters)
- RabbitMQ queues (metadata in headers)
METADATA FIELDS:
Reports include: application name, device ID, encoding type, data name, script ID,
sequence number, timestamp, multi-instance application ID.
Note: Redis reporting excludes metadata due to protocol limitations.
IMPLEMENTATION
--------------
Script Injection:
app = d.application("com.android.settings")
app.attach_script(script, emit="redis://192.168.1.10/0")
Alternative HTTP endpoint:
app.attach_script(script, emit="http://192.168.1.10/dataReport")
Compression Option:
app.attach_script(..., encode=DataEncode.DATA_ENCODE_ZLIB)
Decompression:
import zlib
zlib.decompress(data)
Removal:
app.detach_script()
KEY REQUIREMENTS:
- HTTP services must return "OK" or "SUCCESS" with status 200
- RabbitMQ queues require manual creation
- Only standalone Redis is supported (no clusters)
- HTTP requests are multi-threaded and may arrive out-of-sequence
========================================
FRIDA EXPORTED INTERFACES (RPC)
========================================
OVERVIEW
--------
Enables developers to extend FIRERPA functionality by implementing custom Frida RPC methods.
Users write Hook code for ultimate app control.
Requires familiarity with Frida scripting.
WRITING EXPORT SCRIPTS
-----------------------
Scripts must follow specific formatting requirements:
- Function names use camelCase with lowercase first letter
- Avoid all-caps naming (e.g., use sendHttpRequest not sendHTTPRequest)
- Wrap code in Java.perform() blocks
- Use mandatory return performRpc patterns
THREE EXECUTION CONTEXTS:
performRpcJVMCall:
"Execute on JVM thread" - supports Java operations like Java.use()
performRpcJVMCallOnMain:
"Execute on UI thread" - necessary for UI operations; avoid blocking main thread
performRpcCall:
Basic JavaScript only - no Java layer logic
INJECTING SCRIPTS
-----------------
Inject using persistent Frida script method:
app = d.application("com.android.settings")
app.attach_script(script, runtime=ScriptRuntime.RUNTIME_QJS, standup=5)
Verify injection status:
app.is_script_alive()
CALLING EXPORTED METHODS
-------------------------
Direct API calls:
app = d.application("com.android.settings")
app.exampleFunc1("FIRE", "RPA")
Method names support both camelCase and snake_case variations:
exampleFunc1 equals example_func1
Multi-instance applications: Specify UID parameter to target specific instances
HTTP INTERFACE CALLS
--------------------
Starting version 8.15, supports JSON-RPC 2.0 protocol:
import jsonrpclib
server = jsonrpclib.Server('http://192.168.0.2:65000/script/com.android.settings/0')
server.example_func1("FIRE", "RPA")
Legacy protocol also supported via POST requests with JSON-serialized arguments.
For secured servers:
- Include X-Token header with certificate password
- Use HTTPS
HTTP STATUS CODES:
Code | Meaning
-----|--------------------------------------------------------------
200 | Success
410 | Script not injected/installed
500 | Script/parameter error
400 | Invalid parameters
TROUBLESHOOTING:
Hangs or timeouts typically occur when apps run backgrounded and enter sleep state.
Keep apps in foreground during testing.
========================================
15. BINARY PATCH
========================================
OVERVIEW
--------
Modify device files using hexadecimal search-and-replace operations with wildcard support.
WILDCARD SYNTAX
---------------
System supports flexible pattern matching:
- ?? matches any byte
- B? matches bytes starting with B (like BA, B1, B9)
- Example: 49 BA ?? ?C matches 49 BA [any] [any ending in C]
BASIC USAGE
-----------
Standard replacement:
d.hex_patch("AA BB CC D?", "AA BB CC DD", "/data/test.bin")
Returns object containing:
- count: number of replacements made
- replaces[].offset: byte position of each replacement
PARAMETERS
----------
maxreplace - Limits replacement count (default: all matches replaced):
d.hex_patch("AA BB CC D?", "AA BB CC DD", "/data/test.bin", maxreplace=2)
dryrun - Test mode that identifies matches without modifying files:
d.hex_patch("AA BB ?? ??", "AA BB 00 00", "/data/test.bin", dryrun=True)
FILE PATH WILDCARDS
-------------------
Paths support glob patterns:
/data/app/*/test.bin matches test.bin in any first-level subdirectory under /data/app
REQUIREMENTS:
Patterns require "at least two valid matching digits" for functionality.
========================================
16. BUILT-IN ADB MANAGEMENT
========================================
OVERVIEW
--------
FIRERPA system includes independent ADB service that operates separately from system's
native ADB implementation.
Key feature: "Connect to the highest privilege ADB without enabling developer mode"
through wireless connectivity.
IMPORTANT LIMITATION:
Built-in ADB service does not currently support JDWP debugging functions
(conflicts with system's native implementation).
INSTALLING ADB PUBLIC KEYS
---------------------------
Key Location:
- Linux/Mac: ~/.android/ directory
- Windows: C:\Users\[username]\.android\ directory
- Filename: adbkey.pub
If only adbkey exists, generate public key:
adb pubkey adbkey >adbkey.pub
Installation Process:
d.install_adb_pubkey("/path/to/adbkey.pub")
After successful installation, connect wirelessly:
adb connect 192.168.0.2:65000
REMOVING ADB PUBLIC KEYS
-------------------------
Uninstall public key from built-in ADB service:
d.uninstall_adb_pubkey("/path/to/adbkey.pub")
Note: System-authorized key from developer mode settings is incompatible with
built-in ADB service.
========================================
17. CONNECTIVITY
========================================
CONNECTING TO BUILT-IN ADB
---------------------------
Install Key:
python3 -u adb_pubkey.py install 192.168.1.2
Connect:
adb connect 192.168.1.2:65000
Uninstall Key:
python3 -u adb_pubkey.py uninstall 192.168.1.2
CONNECTING TO BUILT-IN SSH
---------------------------
Connection command:
bash ssh.sh 192.168.1.2
COPYING FILES VIA SCP
---------------------
Retrieve from device:
bash scp.sh 192.168.1.2:/sdcard/DCIM .
Send to device:
bash scp.sh test/ 192.168.1.2:/sdcard
Technical Details:
- Tool uses wrapper script: scp.sh
- Operations target device IP addresses
- File paths follow standard SCP syntax with colon-separated device locations
- Supports both directory and individual file transfers
========================================
18. ONE-CLICK PACKET CAPTURE
========================================
OVERVIEW
--------
Automated man-in-the-middle packet capture for Android 6.0-14.
No manual certificate or proxy configuration required.
Supports real-time packet modification.
Guarantees: "No packets cannot be captured (except certificate pinning)."
KEY REQUIREMENTS
----------------
- Computer and device on same network or USB-connected
- mitmproxy installed (mitmdump command available)
- Network firewall temporarily disabled
- Python 3 (or use pre-packaged startmitm.exe)
BASIC COMMANDS
--------------
Simple packet capture:
python3 -u startmitm.py 192.168.0.2
Shared analysis (colleagues view via browser):
python3 -u startmitm.py 192.168.0.2 --web-port 7890 --web-host 0.0.0.0
Specific application only:
python3 -u startmitm.py 192.168.0.2:com.some.package
With real-time modification script:
python3 -u startmitm.py 192.168.0.2 -s http_flow_hook.py
NETWORK SCENARIOS
-----------------
USB ADB connection:
python3 -u startmitm.py localhost
python3 -u startmitm.py localhost --serial [device_serial]
International/Upstream proxy:
python3 -u startmitm.py 192.168.0.2 --upstream http://127.0.0.1:7890 --dns https://dns.google/dns-query
DNS packet capture:
python3 -u startmitm.py 192.168.0.2 --dns 114.114.114.114
CRITICAL NOTES
--------------
- Close target applications completely before starting capture (not just via taskbar)
- Exit with CONTROL+C once only
- SSL pinning requires Frida bypass scripts
- QUIC auto-downgrades but may cause temporary lag
========================================
19. ENCRYPTION CERTIFICATES
========================================
OVERVIEW
--------
Create encryption certificates for FIRERPA to secure communication between users
and service.
"This certificate is used to encrypt the communication traffic between you and the
FIRERPA service, preventing unauthorized access."
KEY DISTINCTION:
Certificate discussed here differs from those used in packet capture operations.
Purpose: Protecting remote desktop access and service communication rather than
intercepting network traffic.
GENERATION PROCESS
------------------
Location: cert.py script resides in project's tools directory.
Command:
python3 cert.py mydevice.local
Prerequisites: Install necessary environment and dependencies beforehand.
Regular updates recommended to avoid compatibility issues.
GENERATED FILES
---------------
- mydevice.local.pem - The encryption certificate
- root.crt and root.key - Root certificates (require secure storage but aren't directly used)
CERTIFICATE FORMAT
------------------
Output contains RSA private key and certificate blocks in PEM format.
Includes default remote desktop password embedded as parameter: PASSWD=...
IMPLEMENTATION EFFECTS
----------------------
Once applied to FIRERPA:
- Remote desktop access requires HTTPS protocol
- Authorization demands password from certificate
- SSH login also requires certificate-based authentication
IMPORTANT: Users shouldn't manually edit certificate file.
========================================
20. DISTRIBUTED DEPLOYMENT
========================================
OVERVIEW
--------
Enable remote access to devices located elsewhere using distributed deployment methods.
Three primary approaches: FRP forwarding, OpenVPN networking, Xinghuo Platform integration.
FRP-BASED SOLUTIONS
-------------------
Internal Port Forwarding:
Download FRP server from fatedier/frp repository (v0.45.0+).
Launch with parameters controlling token authentication, bind addresses, allowed port
ranges (10000-15000).
Configuration - properties.local variables:
fwd.enable=true
fwd.host=your-server.com # server-address
fwd.port=6009 # server-port
fwd.rport=12345 # remote-port
fwd.token=your-token
fwd.protocol=tcp
Connecting via FRP:
Device("127.0.0.1", port=12345)
Or browser access:
http://[frp-server-ip]:12345
https://[frp-server-ip]:12345 (with certificate)
PUBLIC NETWORK ACCESS WARNING:
Documentation advises against exposing devices directly to public networks.
If necessary:
- FIRERPA must use encryption certificates
- Proxy bind address must change from localhost to 0.0.0.0
- Without certificates: "anyone will be able to access it, which is extremely dangerous"
OPENVPN APPROACH
----------------
Setup involves deploying OpenVPN server (detailed in separate documentation).
Connect personal devices to same virtual network.
XINGHUO PLATFORM
-----------------
Simplified alternative requiring free access code from Device Farm WeChat account.
========================================
21. DEPLOYING OPENVPN SERVICE
========================================
OVERVIEW
--------
Deploy OpenVPN service using Docker on Linux systems (tested on Debian 9).
Default port: 1190/UDP
SERVER PREPARATION
------------------
Enable IP forwarding:
echo net.ipv4.ip_forward=1 >>/etc/sysctl.conf
sysctl -p
For UFW firewall systems:
Add to /etc/ufw/before.rules before *filter rules:
*nat
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -s 172.27.27.0/24 -o eth0 -j MASQUERADE
COMMIT
Change /etc/default/ufw:
DEFAULT_FORWARD_POLICY="ACCEPT"
Then reload:
ufw reload
For systems without UFW:
iptables -P FORWARD ACCEPT
CONFIGURATION SETUP
-------------------
Create directory and initialize:
mkdir -p ~/lamda-openvpn-server
docker run -it --rm --privileged --net host -v ~/lamda-openvpn-server:/etc/openvpn rev1si0n/openvpn ovpn-server-new
Key configurable fields in config.ovpn:
- VPN network: server 172.27.27.0 255.255.255.0
- Port: port 1190
- DNS: push "dhcp-option DNS 114.114.114.114"
CLIENT MANAGEMENT
-----------------
Create client credentials:
docker run -it --rm --privileged --net host -v ~/lamda-openvpn-server:/etc/openvpn rev1si0n/openvpn ovpn-client-new myname
Generate client profile:
docker run -it --rm --privileged --net host -v ~/lamda-openvpn-server:/etc/openvpn rev1si0n/openvpn ovpn-client-profile ovpn myname >myname.ovpn
Revoke client access:
docker run -it --rm --privileged --net host -v ~/lamda-openvpn-server:/etc/openvpn rev1si0n/openvpn ovpn-client-revoke myname
SERVICE OPERATION
-----------------
Foreground mode (testing):
docker run -it --rm --name openvpn-server --privileged --net host -v ~/lamda-openvpn-server:/etc/openvpn rev1si0n/openvpn run
Background mode (production):
docker run -d --rm --name openvpn-server --privileged --net host -v ~/lamda-openvpn-server:/etc/openvpn rev1si0n/openvpn run
========================================
22. INTERFACE LOCKING
========================================
OVERVIEW
--------
Control API access through interface locking mechanisms.
Allows device instances to restrict API usage to prevent unauthorized access.
KEY CONCEPT:
"The interfaces in this chapter are used to lock all API interfaces, allowing you to
restrict interfaces to only be used by the current Device instance."
Recommended approach: Acquire lock, maintain through periodic refreshes, release when done.
THREE MAIN FUNCTIONS
--------------------
1. Acquire Lock
Command: d._acquire_lock(leaseTime=60)
- Automatically releases after 60 seconds by default
- Reentrant capability (subsequent calls refresh existing lock)
- Warning: Setting excessively high lease times risks permanent device lockout if scripts crash
2. Release Lock
Command: d._release_lock()
- Manually relinquishes API lock
- Allows other clients to acquire access
3. Refresh Lock
Command: d._refresh_lock(leaseTime=60)
- Extends lock duration by resetting expiration time
- Intended for periodic calls to maintain continuous access
BEST PRACTICE:
Secure sequence:
1. Acquire lock
2. Spawn thread to refresh periodically
3. Release when finished
This approach prevents unexpected disconnections while maintaining safety against
script failures.
========================================
23. VERSION HISTORY
========================================
CURRENT VERSION: 8.28
---------------------
Latest release includes:
- Fixes for local file installation
- Improvements to Frida ID increment reporting
- Enhanced TensorFlow inference performance
- Updated third-party modules
MAJOR VERSION HIGHLIGHTS
------------------------
Version 8.25:
- hexedit command functionality
- Permission loophole fixes
- On-device AI framework support with tflite-runtime
Version 8.0:
- Full interface support for multiple applications
- Shared clipboard functionality in remote desktop
- Improved compatibility with Android 6.0 through 15
Version 7.50:
- Complete NoxPlayer emulator compatibility
- Network subscription service eliminating FRP/OpenVPN requirements
- Multi-instance app support
Version 5.0:
- Critical security vulnerabilities addressed
- TLS support for remote desktop and RPC
- Python 3.11 client support
FEATURE EVOLUTION
-----------------
Early versions (3.0 series):
Established core functionality including ADB integration, SSH support, file operations.
5.x series:
Introduced significant security improvements and certificate management.
Version 7.x:
Focused on stability, compatibility with various Android versions and emulators,
advanced features like Frida integration.
Recent 8.x versions:
Emphasize AI capabilities and enhanced stealth features.
DEVELOPMENT TRAJECTORY:
Consistent attention to security, compatibility across diverse Android devices and
emulators, progressive feature expansion for automation and system manipulation capabilities.
========================================
BASIC KNOWLEDGE - AUTOMATION FUNDAMENTALS
========================================
OVERVIEW
--------
Android automation fundamentals using FIRERPA.
Emphasized as superior to alternatives like AutoJS, Appium, and uiautomator2 for
enterprise-scale automation projects.
KEY AUTOMATION CONCEPTS
-----------------------
Mobile vs. Web Automation:
"Mobile automation requires you to have both a mobile phone and a computer, while web
automation can be done just on your own computer."
Both use similar principles (element location, clicking, screenshots) but employ
different tools and locating methods.
TOOL COMPARISON
---------------
AutoJS:
Self-controlling, APK-based, suitable for beginners but lacks enterprise scalability.
Appium:
Cross-platform but "bulky and bloated, very unsuitable for large-scale deployment"
uiautomator2:
Streamlined but unstable in multi-device scenarios.
BASIC AUTOMATION WORKFLOW
--------------------------
Three primary approaches:
1. Man-in-the-Middle Capture:
Using certificate installation and proxies for HTTP/HTTPS interception
2. Hook Capture:
Requires reverse engineering knowledge
Uses Frida scripts to intercept function calls
3. Automation Code:
Implements UI interaction logic through selectors
TECHNICAL COMPONENTS
--------------------
Interface Selectors:
Elements located using parameters:
- resourceId
- text
- textContains
- description
- clickable
- scrollable
Example syntax: d(text="Agree").click()
Coordinate System:
- Origin point (0,0) at top-left
- Extends right and down
- Regions defined using Bound with parameters: top, left, bottom, right
- All measured from axis origins
Layout Inspection:
- Remote desktop provides eye-icon toggle for interactive element inspection
- Displays properties usable as selector parameters
- Press CTRL + R to refresh layouts
ANDROID DATA MANAGEMENT
------------------------
Application Data Location:
/data/user/0/[package-name]
Contains:
- Databases directory: SQLite files (mmssms.db for SMS)
- Shared preferences: XML configuration files
- Media/files: Additional application assets
Database Support:
Platform supports reading encrypted databases:
- WeChat: sqlcipher
- Enterprise WeChat: aes-128
- Alipay: sqlcrypto
(Requires obtaining decryption keys)
ADVANCED ASSISTANCE
-------------------
For non-selector-compatible interfaces (games, real-time rendered content):
FIRERPA provides OCR and image matching solutions using SIFT template matching.
========================================
ADDITIONAL NOTES AND BEST PRACTICES
========================================
SECURITY CONSIDERATIONS
-----------------------
- Default FIRERPA installation has no authentication
- Use encryption certificates for any non-trusted network
- Never expose to public internet without proper security
- Keep software updated to latest version
- Protect /data/usr directory (contains configuration and user data)
PERFORMANCE OPTIMIZATION
------------------------
- Use image matching scale parameter to optimize performance
- Server-side OCR/image processing preserves client resources
- Mobile device performance may limit complex operations
- Consider custom HTTP backends for multi-device OCR to avoid resource duplication
AUTOMATION BEST PRACTICES
--------------------------
- Always refresh layouts (CTRL+R) before element inspection
- Ensure input method is visible before text input operations
- Keep monitored apps in foreground during testing
- Close apps completely (not just taskbar) before packet capture
- Use dryrun parameter to test binary patches before applying
- Validate crontab rules with online verification tools
DEBUGGING TIPS
--------------
- Monitor logs: grep SCRIPT /data/local/tmp/server.log
- Check server.log for troubleshooting
- Verify server is running before API calls
- Ensure proper root permissions
- Check timezone configuration for scheduled tasks
- Verify network connectivity for distributed deployments
LIMITATIONS TO REMEMBER
-----------------------
- Remote desktop: English input only (no Chinese characters)
- Built-in terminal: Cannot install additional PIP/APT packages
- Virtual Debian: Only one instance at a time
- Built-in ADB: No JDWP debugging support
- OCR: Limited to basic operations (exists, click, screenshot)
- Persistent Frida: One script per app at a time
- Scheduled tasks: May not run as expected when screen off
RECOMMENDED WORKFLOW
--------------------
1. Read documentation sequentially from first chapter
2. Keep FIRERPA and client libraries updated
3. Use virtual environment for client installation
4. Test configurations in safe environment first
5. Implement proper error handling in automation scripts
6. Use interface locking for exclusive device access
7. Monitor script execution through logging
8. Regular backups of /data/usr directory
========================================
END OF DOCUMENTATION
========================================
This comprehensive documentation covers all major aspects of FIRERPAλ for Android
device management, automation, debugging, and security testing.
For latest updates and additional information, refer to:
https://device-farm.com/doc/en/
Copyright © 2021-present, firerpa
================================================
FILE: examples/README.md
================================================
We’re not targeting any specific application; we’re just using it as a convenient example for the demo.
API Document: https://device-farm.com/doc/
================================================
FILE: examples/activity_jump.py
================================================
# Copyright 2025 rev1si0n (lamda.devel@gmail.com). All rights reserved.
#encoding=utf-8
from lamda.client import *
import time
d = Device("localhost")
app = d.application("com.taobao.taobao")
app.start()
while True:
goodsid = input("Please input a taobao goods id (item_id) (eg. 123456): ")
if goodsid.isdigit():
intent["package"] = "com.taobao.taobao"
intent["action"] = "android.intent.action.VIEW"
intent["component"] = "com.taobao.taobao/com.taobao.android.detail.alittdetail.TTDetailActivity"
intent["data"] = f"http://internal.tt.detail.taobao.com/detail/index.html?id={goodsid}"
d.start_activity(**intent)
time.sleep(2)
================================================
FILE: examples/search_in_taobao.py
================================================
# Copyright 2025 rev1si0n (lamda.devel@gmail.com). All rights reserved.
#encoding=utf-8
from lamda.client import *
import time
"""
This is a simple demo for performing keyword searches on Taobao.
"""
d = Device("localhost")
app = d.application("com.taobao.taobao")
if not app.is_installed():
print ("taobao app is not installed")
exit (1)
if app.info().versionName != "10.48.0":
print ("please intall taaobao 10.48.0")
exit (1)
# ensure the app is stopped
app.stop()
time.sleep(1.5)
app.start()
time.sleep(10) # wait for app fully started
if not d(description="我的淘宝").exists():
print ("is taobao home page?")
exit (1)
# click to activate input
d(description="搜索栏").click()
# wait for search input activated
d(resourceId="com.taobao.taobao:id/searchbtn").wait_for_exists(15*1000)
# input search keyword: 苹果手机
d(resourceId="com.taobao.taobao:id/searchEdit").set_text("苹果手机")
# click "Search"
d(resourceId="com.taobao.taobao:id/searchbtn").click()
# wait for goods showsup
d(description="筛选").wait_for_exists(15*1000)
# do a simple swipe
d().swipe()
# ...
================================================
FILE: extensions/README.md
================================================
API Document: https://device-farm.com/doc/
================================================
FILE: extensions/example_http_extension.py
================================================
# Copyright 2025 rev1si0n (lamda.devel@gmail.com). All rights reserved.
#
# Distributed under MIT license.
# See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
from lamda.extensions import *
# In order to accommodate users of various technical levels and avoid impacting the operation of the firerpa service, all HTTP rewrite methods must be implemented synchronously. These methods will run within threads and will not block the overall service.
# BaseHttpExtension is based on tornado.web.RequestHandler. If you need to use or override other methods such as set_header, initialize, etc., please refer to the official Tornado documentation.
# It is strongly discouraged to override Tornado-related methods like prepare and initialize.
# If you encounter the There is no current event loop in thread XXX exception, please call self.prepare_loop() in your overridden http_xxx method.
# 为了兼容各种技术层级的使用者以及不影响到 firerpa 服务的运行,所有 HTTP 重写方法均须为同步写法,方法将在线程内运行,不会阻塞整体服务。
# BaseHttpExtension 基于 tornado.web.RequestHandler,如需使用或重写其他方法如 set_header、initialize 等,请参照 tornado 官方文档。
# 我们不建议您重写 tornado 相关 prepare、initialize 方法。
# 如果您遇到 There is no current event loop in thread XXX 异常,请在您重写的 http_xxx 方法中调用 `self.prepare_loop()`
# REF: https://www.tornadoweb.org/en/stable/web.html
class ExampleHttpExtension(BaseHttpExtension):
route = "/api/v1/hello-world" # API route
def http_get(self, *args, **kwargs):
""" GET Method Handler """
self.write("Hello World")
def http_post(self, *args, **kwargs):
""" POST Method Handler """
self.write("Hello World")
def http_put(self, *args, **kwargs):
""" PUT Method Handler """
self.write("Hello World")
def http_delete(self, *args, **kwargs):
""" DELETE Method Handler """
self.write("Hello World")
def http_patch(self, *args, **kwargs):
""" PATCH Method Handler """
self.write("Hello World")
================================================
FILE: extensions/example_mcp_extension.py
================================================
# Copyright 2025 rev1si0n (lamda.devel@gmail.com). All rights reserved.
#
# Distributed under MIT license.
# See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
import base64
from lamda.utils import getprop
from lamda.extensions import BaseMcpExtension
from lamda.mcp import mcp, Annotated, TextContent, BlobResourceContents
class ExampleMcpExtension(BaseMcpExtension):
route = "/model-context-protocol/mcp/"
name = "example-mcp-extension"
version = "1.0.0"
@mcp("tool", description="Send a greeting to others.")
def greeting(self, ctx, msg: Annotated[str, "Greeting message"],
to: Annotated[str, "Greeting to who"] = "John"):
return TextContent(text=f"mcp greeting! {msg}, {to}!")
@mcp("tool", description="Read android system property by name.")
def getprop(self, ctx, name: Annotated[str, "Android system property name."]):
return TextContent(text=getprop(name) or "")
@mcp("resource", uri="file://{absolute_path}")
def get_file(self, ctx, absolute_path: Annotated[str, "Absolute file path"]):
""" Read file content on the device by full path """
blob = base64.b64encode(open(absolute_path, "rb").read()).decode()
return BlobResourceContents(blob=blob, uri=f"file://{absolute_path}",
mimeType="text/plain")
================================================
FILE: extensions/firerpa.py
================================================
# Copyright 2025 rev1si0n (lamda.devel@gmail.com). All rights reserved.
#
# Distributed under MIT license.
# See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
#
# ===================================================================
# Official FireRPA MCP extension, turning your phone into an AI AGENT
# ===================================================================
#
import json
import xml.etree.ElementTree as etree
from lamda.mcp import *
from lamda.client import *
from lamda.utils import getprop
from lamda.extensions import BaseMcpExtension, to_json_string
from google.protobuf.json_format import MessageToDict
from xmltodict import parse as xml2dict
prompt = """
# Android Automation Guidelines
You are an expert in Android automation, capable of using specialized tools to accurately complete user-requested operations. You understand and can precisely execute each step in the automation process.
## Quality Assurance
- Prioritize using layout information for identifying operational elements. You should always use non-repeating criteria for judgment.
- Resource-id may be duplicated, and duplicate ids should not be used.
- Each operation should have a certain interval; otherwise, the page may not be fully loaded.
- Never use screenshot to detect coordinates.
## Communication
- Follow the user's language preferences"""
class FireRpaMcpExtension(BaseMcpExtension):
"""Your primary task is to help users automate Android device control using AI through this MCP service."""
route = "/firerpa/mcp/"
name = "firerpa"
version = "1.0"
@mcp("tool", description="Dumps android window's layout hierarchy as JSON string.")
def dump_window_hierarchy(self, ctx, compressed: Annotated[bool, "Enables or disables layout hierarchy compression, default true."] = True):
data = self.device.dump_window_hierarchy(compressed).getvalue()
return self.remove_attrs_and_empty(data)
@mcp("tool", description="Perform a click at arbitrary coordinates on the display.")
def click(self, ctx, pointX: Annotated[int, "X coordinate."], pointY: Annotated[int, "Y coordinate."]):
result = self.device.click(Point(x=pointX, y=pointY))
return str(result).lower()
@mcp("tool", description="Perform a swipe between two points.")
def swipe(self, ctx, fromX: Annotated[int, "Swipe-from X coordinate."], fromY: Annotated[int, "Swipe-from Y coordinate."], toX: Annotated[int, "Swipe-to X coordinate."], toY: Annotated[int, "Swipe-to Y coordinate."], step: Annotated[int, "Step to inject between two points"] = 32):
result = self.device.swipe(Point(x=fromX, y=fromY), Point(x=toX, y=toY), step=step)
return str(result).lower()
@mcp("tool", description="Perform a drag between two points.")
def drag(self, ctx, fromX: Annotated[int, "Drag-from X coordinate."], fromY: Annotated[int, "Drag-from Y coordinate."], toX: Annotated[int, "Drag-to X coordinate."], toY: Annotated[int, "Drag-to Y coordinate."]):
result = self.device.drag(Point(x=fromX, y=fromY), Point(x=toX, y=toY))
return str(result).lower()
@mcp("tool", description="Get device information such as screen width, height, brand, etc.")
def get_deviec_info(self, ctx):
info = self.device.device_info()
return to_json_string(MessageToDict(info))
@mcp("tool", description="Display a toast message on the screen.")
def show_toast(self, ctx, message: Annotated[str, "The toast message."]):
result = self.device.show_toast(message)
return str(result).lower()
@mcp("tool", description="Execute script in the device's shell foreground.")
def execute_shell_script_foreground(self, ctx, scrip: Annotated[str, "Shell script content."]):
result = self.device.execute_script(scrip)
return to_json_string(MessageToDict(result))
@mcp("tool", description="Wake up the device.")
def wake_up(self, ctx):
result = self.device.wake_up()
return str(result).lower()
@mcp("tool", description="Turn off the device screen.")
def sleep(self, ctx):
result = self.device.sleep()
return str(result).lower()
@mcp("tool", description="Check if the device screen is lit up.")
def is_screen_on(self, ctx):
result = self.device.is_screen_on()
return str(result).lower()
@mcp("tool", description="Check is the device screen locked.")
def is_screen_locked(self, ctx):
result = self.device.is_screen_locked()
return str(result).lower()
@mcp("tool", description="Get the device clipboard content.")
def get_clipboard_text(self, ctx):
result = self.device.get_clipboard()
return result
@mcp("tool", description="Set the device clipboard content.")
def set_clipboard_text(self, ctx, text: Annotated[str, "The text to set."]):
result = self.device.set_clipboard(text)
return str(result).lower()
@mcp("tool", description="Simulates a short press using a key code.")
def press_key_code(self, ctx, key_code: Annotated[int, "The Android's KeyEvent keycode."]):
result = self.device.press_keycode(key_code)
return str(result).lower()
@mcp("tool", description="Get the last displayed toast on the system.")
def get_last_toast(self, ctx):
result = self.device.get_last_toast()
return to_json_string(MessageToDict(result))
@mcp("tool", description="Read android system property by name.")
def getprop(self, ctx, name: Annotated[str, "Android system property name."]):
return getprop(name) or ""
@mcp("tool", description="Use full text matching to click on an element.")
def click_by_text(self, ctx, text: Annotated[str, "The full text field."]):
result = self.device(text=text).click_exists()
return str(result).lower()
@mcp("tool", description="Use text contains matching to click on an element.")
def click_by_text_contains(self, ctx, substring: Annotated[str, "The substring to be matched."]):
result = self.device(textContains=substring).click_exists()
return str(result).lower()
@mcp("tool", description="Use text regex matching to click on an element.")
def click_by_text_matches(self, ctx, regex: Annotated[str, "The string matching the element's text."]):
result = self.device(textMatches=regex).click_exists()
return str(result).lower()
@mcp("tool", description="Use full description matching to click on an element.")
def click_by_description(self, ctx, text: Annotated[str, "The full description field."]):
result = self.device(description=text).click_exists()
return str(result).lower()
@mcp("tool", description="Use description contains matching to click on an element.")
def click_by_description_contains(self, ctx, substring: Annotated[str, "The substring to be matched."]):
result = self.device(descriptionContains=substring).click_exists()
return str(result).lower()
@mcp("tool", description="Use description regex matching to click on an element.")
def click_by_description_matches(self, ctx, regex: Annotated[str, "The string matching the element's description."]):
result = self.device(descriptionMatches=regex).click_exists()
return str(result).lower()
@mcp("tool", description="Use resourceId to click on an element, if the resource-id is duplicated, it cannot be used.")
def click_by_resource_id(self, ctx, resource_id: Annotated[str, "Elements's resourceId (resourceName)."]):
result = self.device(resourceId=resource_id).click_exists()
return str(result).lower()
@mcp("tool", description="Use resourceId to input text into an input element, if the resource-id is duplicated, it cannot be used.")
def set_text_by_resource_id(self, ctx, resource_id: Annotated[str, "Input elements's resourceId (resourceName)."], text: Annotated[str, "The input text."]):
result = self.device(resourceId=resource_id).set_text(text)
return str(result).lower()
@mcp("tool", description="Use className to input text into an input element.")
def set_text_by_class_name(self, ctx, class_name: Annotated[str, "Input elements's className. eg: android.widget.EditText"], text: Annotated[str, "The input text."]):
result = self.device(className=class_name).set_text(text)
return str(result).lower()
@mcp("tool", description="Get information about the currently running foreground application.")
def current_top_application_info(self, ctx):
result = self.device.current_application().info()
return to_json_string(MessageToDict(result))
@mcp("tool", description="Use the package name to launch an Android app.")
def start_application_by_id(self, ctx, package_name: Annotated[str, "The package name, such as com.android.settings."]):
result = self.device.application(package_name).start()
return str(result).lower()
@mcp("tool", description="Use the package name to close an Android app.")
def stop_application_by_id(self, ctx, package_name: Annotated[str, "The package name, such as com.android.settings."]):
result = self.device.application(package_name).stop()
return str(result).lower()
@mcp("tool", description="Use the package name to check if the application is installed.")
def is_application_installed(self, ctx, package_name: Annotated[str, "The package name, such as com.android.settings."]):
result = self.device.application(package_name).is_installed()
return str(result).lower()
@mcp("tool", description="Check if the application is running in the foreground using the package name.")
def is_application_running_foreground(self, ctx, package_name: Annotated[str, "The package name, such as com.android.settings."]):
result = self.device.application(package_name).is_foreground()
return str(result).lower()
@mcp("tool", description="Get all manifest permissions of the application using the package name.")
def list_application_permissions(self, ctx, package_name: Annotated[str, "The package name, such as com.android.settings."]):
result = self.device.application(package_name).permissions()
return to_json_string(result)
@mcp("tool", description="Grant the application runtime permissions.")
def grant_application_permission(self, ctx, package_name: Annotated[str, "The package name, such as com.android.settings."], permission: Annotated[str, "The android runtime permissions."]):
result = self.device.application(package_name).grant(permission)
return str(result).lower()
@mcp("tool", description="Revoke the application's runtime permissions.")
def revoke_application_permission(self, ctx, package_name: Annotated[str, "The package name, such as com.android.settings."], permission: Annotated[str, "The android runtime permissions."]):
result = self.device.application(package_name).revoke(permission)
return str(result).lower()
@mcp("tool", description="Check if the application has been granted runtime permissions.")
def is_permission_granted(self, ctx, package_name: Annotated[str, "The package name, such as com.android.settings."], permission: Annotated[str, "The android runtime permissions."]):
result = self.device.application(package_name).is_permission_granted(permission)
return str(result).lower()
@mcp("prompt")
def agent(self, ctx):
return PromptMessage(role="user", content=TextContent(text=prompt))
def remove_attrs_and_empty(self, xml):
""" remove unnecessery node info to reduce token usage """
specified_attrs = {"clickable", "scrollable", "checkable", "enabled",
"focusable", "long-clickable", "password", "visible-to-user",
"drawing-order", "hint", "display-id", "package", "focused"}
def clean_element(element, parent=None):
if element.attrib.get("package") == "com.android.systemui" and parent is not None:
parent.remove(element)
return True
attrs_to_remove = {attr for attr, value in element.attrib.items()
if attr in specified_attrs or not value.strip()}
for attr in attrs_to_remove:
del element.attrib[attr]
for i in range(len(element) - 1, -1, -1):
clean_element(element[i], element)
root = etree.fromstring(xml)
clean_element(root)
return json.dumps(xml2dict(etree.tostring(root, encoding="utf-8").decode(),
attr_prefix=""), ensure_ascii=False, separators=(",", ":"))
================================================
FILE: extensions/mcp_return_types.py
================================================
#!/usr/bin/env python3.9
# Copyright 2025 rev1si0n (lamda.devel@gmail.com). All rights reserved.
#
# Distributed under MIT license.
# See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
#
# THIS IS THE OFFICIAL MCP TYPES equivalent made by firerpa authors (FUCKOFF pydantic)
# github.com/modelcontextprotocol/python-sdk/blob/v1.13.1/src/mcp/types.py
# WE ONLY LIST THE TYPES THAT YOU CAN USE
import msgspec
from msgspec import Struct, Meta
from typing import Dict, Any, Literal, List, Union, Annotated
Role = Literal["user", "assistant"]
LoggingLevel = Literal["debug", "info", "notice", "warning", "error", "critical", "alert", "emergency"]
class BaseModel(Struct, omit_defaults=False):
def validate(self):
return msgspec.json.decode(msgspec.json.encode(self),
type=type(self))
class BaseStructuredModel(BaseModel):
""" StructuredModel """
class Result(BaseModel):
"""Base class for JSON-RPC results."""
class Annotations(BaseModel):
audience: Union[list[Role], None] = None
priority: Union[Annotated[float, Meta(ge=0.0, le=1.0)], None] = None
class ResourceContents(BaseModel):
"""The contents of a specific resource or sub-resource."""
uri: Annotated[str, Meta(min_length=5, max_length=2**16,
pattern="^[a-z0-9A-Z_-]+://.*$")]
"""The URI of this resource."""
mimeType: Union[str, None] = None
"""The MIME type of this resource, if known."""
class TextResourceContents(ResourceContents, kw_only=True):
"""Text contents of a resource."""
text: str
"""The text of the item."""
class BlobResourceContents(ResourceContents, kw_only=True):
"""Binary contents of a resource."""
blob: str
"""A base64-encoded string representing the binary data of the item."""
class TextContent(BaseModel, kw_only=True):
"""Text content for a message."""
type: Literal["text"] = "text"
text: str
"""The text content of the message."""
annotations: Union[Annotations, None] = None
class ImageContent(BaseModel, kw_only=True):
"""Image content for a message."""
type: Literal["image"] = "image"
data: str
"""The base64-encoded image data."""
mimeType: str
"""The MIME type of the image."""
annotations: Union[Annotations, None] = None
class EmbeddedResource(BaseModel, kw_only=True):
"""
The contents of a resource, embedded into a prompt or tool call result.
It is up to the client how best to render embedded resources for the benefit
of the LLM and/or the user.
"""
type: Literal["resource"] = "resource"
resource: Union[TextResourceContents, BlobResourceContents]
annotations: Union[Annotations, None] = None
class PromptMessage(BaseModel):
"""Describes a message returned as part of a prompt."""
role: Role
content: Union[TextContent, ImageContent, EmbeddedResource]
class GetPromptResult(Result, kw_only=True):
"""The server's response to a prompts/get request from the client."""
description: Union[str, None] = ""
"""An optional description for the prompt."""
messages: list[PromptMessage]
class EmptyResult(Result):
"""A response that indicates success but carries no data."""
class CallToolResult(Result):
"""The server's response to a tool call."""
content: list[Union[TextContent, ImageContent, EmbeddedResource]]
structuredContent: Union[Dict[str, Any], None] = None
isError: bool = False
class ReadResourceResult(Result):
"""The server's response to a resources/read request from the client."""
contents: List[Union[TextResourceContents, BlobResourceContents]]
================================================
FILE: extensions/mcp_sms_reader.py
================================================
# Copyright 2025 rev1si0n (lamda.devel@gmail.com). All rights reserved.
#
# Distributed under MIT license.
# See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
#
# ===================================================================
# MCP for reading local SMS messages. 用于读取本机短信的 MCP 扩展
# ===================================================================
#
import json
import sqlite3
from lamda.mcp import mcp, Annotated, TextContent
from lamda.extensions import BaseMcpExtension
db_path = "/data/data/com.android.providers.telephony/databases/mmssms.db"
class SmsMcpExtension(BaseMcpExtension):
route = "/sms/mcp/"
name = "sms-reader-extension"
version = "1.0"
@mcp("tool", description="""Reads the SMS database using SQL statements in SQLite syntax; read-only, no write operations allowed.
The database is standard android mmssms.db, you should always learn the tables or table structure if needed.""")
def read_sms_database_by_sql(self, ctx, sql: Annotated[str, "A raw SQL (SQLite) query string for read-only operations."]):
db = sqlite3.connect(db_path)
db.row_factory = sqlite3.Row
db.execute("PRAGMA query_only")
try:
items = db.execute(sql)
results = json.dumps([dict(row) for row in items.fetchall()])
finally:
db.close()
return TextContent(text=results)
================================================
FILE: lamda/__init__.py
================================================
# Copyright 2022 rev1si0n (lamda.devel@gmail.com). All rights reserved.
#
# Distributed under MIT license.
# See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
__version__ = "9.20"
================================================
FILE: lamda/client.py
================================================
# Copyright 2022 rev1si0n (https://github.com/rev1si0n). All rights reserved.
#
# Distributed under MIT license.
# See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
import os
import io
import re
import sys
import copy
import time
import uuid
import json
import base64
import hashlib
import platform
import warnings
import builtins
import logging
import msgpack
# fix protobuf>=4.0/win32, #10158
if sys.platform == "win32":
os.environ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION"] = "python"
import grpc
import pem as Pem
import collections.abc
# fix pyreadline, py310, Windows
collections.Callable = collections.abc.Callable
from urllib.parse import quote
from collections import defaultdict
from cryptography.fernet import Fernet
from os.path import basename, dirname, expanduser, join as joinpath
from google.protobuf.json_format import MessageToDict, MessageToJson
from grpc_interceptor import ClientInterceptor
from google.protobuf.message import Message
from asn1crypto import pem, x509
try:
import frida
_frida_dma = frida.get_device_manager()
except (ImportError, AttributeError):
_frida_dma = None
from . import __version__
from . types import AttributeDict, BytesIO
from . exceptions import (UnHandledException, DuplicateEntryError,
InvalidArgumentError, UiObjectNotFoundException,
IllegalStateException)
from . import exceptions
handler = logging.StreamHandler()
logger = logging.getLogger("lamda.client")
formatter = logging.Formatter("%(asctime)s %(process)d %(levelname)7s@%(module)s:%(funcName)s - %(message)s")
handler.setFormatter(formatter)
logger.addHandler(handler)
sys.path.append(joinpath(dirname(__file__)))
sys.path.append(joinpath(dirname(__file__), "rpc"))
# use native resolver to support mDNS
os.environ["GRPC_DNS_RESOLVER"] = "native"
protos, services = grpc.protos_and_services("services.proto")
__all__ = [
"Corner",
"Direction",
"GproxyType",
"GrantType",
"Group",
"CustomOcrBackend",
"OcrEngine",
"Key",
"Keys",
"KeyCode",
"KeyCodes",
"MetaKeyCode",
"MetaKeyCodes",
"BaseCryptor",
"FernetCryptor",
"OpenVPNAuth",
"OpenVPNEncryption",
"OpenVPNKeyDirection",
"FindImageMethod",
"FindImageArea",
"ToastDuration",
"OpenVPNCipher",
"OpenVPNProto",
"Orientation",
"OpenVPNProfile",
"GproxyProfile",
"TouchBuilder",
"ScriptRuntime",
"DataEncode",
"AudioStreamType",
"PlayAudioProfile",
"ApplicationInfo",
"Selector",
"TouchWait",
"TouchMove",
"TouchDown",
"TouchUp",
"TouchAction",
"TouchSequence",
"Point",
"Bound",
"load_proto",
"to_dict",
"Device",
"logger",
]
def getXY(p):
return p.x, p.y
def checkDupEntry(a, entries):
if a in entries:
raise DuplicateEntryError(a)
def checkArgumentTyp(a, types):
if not isinstance(a, types):
raise InvalidArgumentError(a)
def touchSequenceSave(s, fpath):
return BytesIO(s.SerializeToString()).save(fpath)
def touchSequenceLoad(s, fpath):
return s.FromString(BytesIO.load(fpath).getvalue())
def touchSequenceIndexer(s, index):
return s.sequence[index]
def touchSequenceIter(s):
yield from s.sequence
def touchSequenceAppendAction(s, **kwargs):
action = TouchAction(**kwargs)
s.sequence.append(action)
def touchSequenceAppendDown(s, **kwargs):
touchSequenceAppendAction(s, down=TouchDown(**kwargs))
def touchSequenceAppendMove(s, **kwargs):
touchSequenceAppendAction(s, move=TouchMove(**kwargs))
def touchSequenceAppendWait(s, **kwargs):
touchSequenceAppendAction(s, wait=TouchWait(**kwargs))
def touchSequenceAppendUp(s, **kwargs):
touchSequenceAppendAction(s, up=TouchUp(**kwargs))
def touchActionRealAction(a):
return getattr(a, a.type)
def touchActionType(a):
return a.WhichOneof("action")
def touchMoveShiftX(a, offset):
a.x = a.x + offset
return a.x
def touchMoveShiftY(a, offset):
a.y = a.y + offset
return a.y
def touchWaitShift(w, offset):
w.wait = w.wait + offset
return w.wait
def applicationInfoSet(application, app):
application.CopyFrom(app.info())
def height(b):
return b.bottom - b.top
def width(b):
return b.right - b.left
def center(b):
x = int(b.left + (b.right - b.left)/2)
y = int(b.top + (b.bottom - b.top)/2)
return Point(x=x, y=y)
def contain(a, b):
return all([b.top >= a.top,
b.left >= a.left,
b.bottom <= a.bottom,
b.right <= a.right])
def equal(a, b):
if not isinstance(b, protos.Bound):
return False
return all([b.top == a.top,
b.left == a.left,
b.bottom == a.bottom,
b.right == a.right])
def corner(b, position):
ca, cb = position.split("-")
return Point(x=getattr(b, cb),
y=getattr(b, ca))
# enum types
Corner = protos.Corner
Direction = protos.Direction
GproxyType = protos.GproxyType
GrantType = protos.GrantType
ScriptRuntime = protos.ScriptRuntime
DataEncode = protos.DataEncode
Group = protos.Group
Key = protos.Key
Keys = protos.Key # make an alias
KeyCode = protos.KeyCode
KeyCodes = protos.KeyCode # make an alias
MetaKeyCode = protos.MetaKeyCode
MetaKeyCodes = protos.MetaKeyCode # make an alias
OpenVPNAuth = protos.OpenVPNAuth
OpenVPNEncryption = protos.OpenVPNEncryption
OpenVPNKeyDirection = protos.OpenVPNKeyDirection
OpenVPNCipher = protos.OpenVPNCipher
OpenVPNProto = protos.OpenVPNProto
ToastDuration = protos.ToastDuration
Orientation = protos.Orientation
AudioStreamType = protos.AudioStreamType
PlayAudioProfile = protos.PlayAudioRequest
# proxy request alias
OpenVPNProfile = protos.OpenVPNConfigRequest
GproxyProfile = protos.GproxyConfigRequest
# multitouch
TouchMove = protos.TouchMove
TouchWait = protos.TouchWait
TouchDown = protos.TouchDown
TouchUp = protos.TouchUp
TouchSequence = protos.TouchSequence
TouchAction = protos.TouchAction
ApplicationInfo = protos.ApplicationInfo
# uiautomator types
_Selector = protos.Selector
Bound = protos.Bound
Point = protos.Point
Point.getXY = getXY
ApplicationInfo.set = applicationInfoSet
TouchWait.shift = touchWaitShift
TouchMove.shiftX = touchMoveShiftX
TouchMove.shiftY = touchMoveShiftY
TouchDown.shiftX = touchMoveShiftX
TouchDown.shiftY = touchMoveShiftY
TouchAction.type = property(touchActionType)
TouchAction.action = property(touchActionRealAction)
TouchSequence.load = classmethod(touchSequenceLoad)
TouchSequence.save = touchSequenceSave
TouchSequence.appendAction = touchSequenceAppendAction
TouchSequence.appendDown = touchSequenceAppendDown
TouchSequence.appendMove = touchSequenceAppendMove
TouchSequence.appendWait = touchSequenceAppendWait
TouchSequence.appendUp = touchSequenceAppendUp
TouchSequence.__getitem__ = touchSequenceIndexer
TouchSequence.__iter__ = touchSequenceIter
HookRpcRequest = protos.HookRpcRequest
HookRpcResponse = protos.HookRpcResponse
Bound.width = property(width)
Bound.height = property(height)
FindImageMethod = protos.FindImageMethod
FindImageArea = protos.FindImageArea
Bound.center = center
Bound.corner = corner
Bound.__contains__ = contain
Bound.__eq__ = equal
def load_proto(name):
""" 载入包下面的相关 proto 文件 """
return grpc.protos_and_services(name)
def to_dict(prot):
""" 将 proto 返回值转换为字典 """
r = MessageToJson(prot, preserving_proto_field_name=True)
return json.loads(r)
def Selector(**kwargs):
""" Selector wrapper """
kwargs.pop("fields", None)
sel = _Selector(**kwargs, fields=kwargs.keys())
return sel
def child_sibling(s, name, **selector):
s = copy.deepcopy(s)
s.childOrSibling.append(name)
s.childOrSiblingSelector.append(Selector(**selector))
return s
def child(s, **selector):
return child_sibling(s, "child", **selector)
def sibling(s, **selector):
return child_sibling(s, "sibling", **selector)
# bind Selector level child sibling
_Selector.child = child
_Selector.sibling = sibling
class CustomOcrBackend(object):
def __init__(self, *args, **kwargs):
raise NotImplementedError
def ocr(self, image):
raise NotImplementedError
class BaseCryptor(object):
def encrypt(self, data):
return data
def decrypt(self, data):
return data
class BaseServiceStub(object):
def __init__(self, stub):
self.stub = stub
class FernetCryptor(BaseCryptor):
def __init__(self, key=None):
key = self._get_key(key)
self.encoder = Fernet(key)
def encrypt(self, data):
return self.encoder.encrypt(data)
def decrypt(self, data):
return self.encoder.decrypt(data)
def _get_key(self, key):
key = (key or "").encode()
key = hashlib.sha256(key).digest()
key = base64.b64encode(key)
return key
class TouchBuilder(object):
def __init__(self):
self.s = TouchSequence()
def down(self, x, y, pressure=50, track=0):
self.s.appendDown(tid=track, x=x, y=y,
pressure=pressure)
return self
def move(self, x, y, pressure=50, track=0):
self.s.appendMove(tid=track, x=x, y=y,
pressure=pressure)
return self
def up(self, track=0):
self.s.appendUp(tid=track)
return self
def wait(self, mills):
self.s.appendWait(wait=mills)
return self
def build(self):
sequence = TouchSequence()
sequence.CopyFrom(self.s)
return sequence
class ClientLoggingInterceptor(ClientInterceptor):
def truncate_string(self, s):
return "{:.1024}...".format(s) if len(s) > 1024 else s
def intercept(self, function, request, details):
"""
日志记录各个接口的调用及参数
"""
displayable = isinstance(request, Message)
args = MessageToDict(request) if displayable else "-"
args = json.dumps(args, ensure_ascii=False, separators=(",", ":"))
args = self.truncate_string(args)
logger.debug("rpc {} {}".format(details.method, args))
res = function(request, details)
return res
class ClientSessionMetadataInterceptor(ClientInterceptor):
def __init__(self, session):
super(ClientSessionMetadataInterceptor, self).__init__()
self.session = session
def intercept(self, function, request, details):
metadata = {}
metadata["version"] = __version__
default = (self.session, platform.node())
session, name = self.session() if callable(self.session) else default
metadata["instance"] = session
metadata["hostname"] = quote(name)
details = details._replace(metadata=metadata.items())
return function(request, details)
class GrpcRemoteExceptionInterceptor(ClientInterceptor):
def intercept(self, function, request, details):
"""
处理远程调用中发生的异常并抛出本地异常
"""
res = function(request, details)
self.raise_remote_exception(res)
return res
def remote_exception(self, exception):
exc = json.loads(exception)
name, args = exc["name"], exc["args"]
default = lambda *p: UnHandledException(name, *p)
clazz = getattr(builtins, name, default)
clazz = getattr(exceptions, name, clazz)
return clazz(*args)
def raise_remote_exception(self, res):
metadata = dict(res.initial_metadata() or [])
exception = metadata.get("exception", None)
if exception != None:
raise self.remote_exception(exception)
class ObjectUiAutomatorOpStub:
def __init__(self, caller, selector):
"""
UiAutomator 子接口,用来模拟出实例的意味
"""
self._selector = selector
self.selector = Selector(**selector)
self.stub = caller.stub
self.caller = caller
def __str__(self):
selector = ", ".join(["{}={}".format(k, v) \
for k, v in self._selector.items()])
return "Object: {}".format(selector)
__repr__ = __str__
def child(self, **selector):
"""
匹配选择器里面的子节点
"""
selector = self.selector.child(**selector)
s = MessageToDict(selector, preserving_proto_field_name=True)
return self.__class__(self.caller, s)
def sibling(self, **selector):
"""
匹配选择器的同级节点
"""
selector = self.selector.sibling(**selector)
s = MessageToDict(selector, preserving_proto_field_name=True)
return self.__class__(self.caller, s)
def take_screenshot(self, quality=100):
"""
对选择器选中元素进行截图
"""
req = protos.SelectorTakeScreenshotRequest(selector=self.selector,
quality=quality)
r = self.stub.selectorTakeScreenshot(req)
return BytesIO(r.value)
def screenshot(self, quality=100):
return self.take_screenshot(quality=quality)
def get_text(self):
"""
获取选择器选中输入控件中的文本
"""
req = protos.SelectorOnlyRequest(selector=self.selector)
r = self.stub.selectorGetText(req)
return r.value
def clear_text_field(self):
"""
清空选择器选中输入控件中的文本
"""
req = protos.SelectorOnlyRequest(selector=self.selector)
r = self.stub.selectorClearTextField(req)
return r.value
def set_text(self, text):
"""
向选择器选中输入控件中填入文本
"""
req = protos.SelectorSetTextRequest(selector=self.selector,
text=text)
r = self.stub.selectorSetText(req)
return r.value
def click(self, corner=Corner.COR_CENTER):
"""
点击选择器选中的控件
"""
req = protos.SelectorClickRequest(selector=self.selector,
corner=corner)
r = self.stub.selectorClick(req)
return r.value
def click_exists(self, corner=Corner.COR_CENTER):
"""
点击选择器选中的控件(不存在将不会产生异常)
"""
req = protos.SelectorClickRequest(selector=self.selector,
corner=corner)
r = self.stub.selectorClickExists(req)
return r.value
def long_click(self, corner=Corner.COR_CENTER):
"""
长按选择器选中的控件
"""
req = protos.SelectorClickRequest(selector=self.selector,
corner=corner)
r = self.stub.selectorLongClick(req)
return r.value
def exists(self):
"""
是否存在选择器选中的控件
"""
req = protos.SelectorOnlyRequest(selector=self.selector)
r = self.stub.selectorExists(req)
return r.value
def info(self):
"""
获取选择器选中控件的信息
"""
req = protos.SelectorOnlyRequest(selector=self.selector)
return self.stub.selectorObjInfo(req)
def _new_object(self, **kwargs):
selector = copy.deepcopy(self._selector)
child_sibling = selector.get("childOrSiblingSelector")
target = child_sibling[-1] if child_sibling else selector
target.update(**kwargs)
return self.caller(**selector)
def text(self, txt):
return self._new_object(text=txt)
def resourceId(self, name):
return self._new_object(resourceId=name)
def description(self, desc):
return self._new_object(description=desc)
def packageName(self, name):
return self._new_object(packageName=name)
def className(self, name):
return self._new_object(className=name)
def textContains(self, needle):
return self._new_object(textContains=needle)
def descriptionContains(self, needle):
return self._new_object(descriptionContains=needle)
def textStartsWith(self, needle):
return self._new_object(textStartsWith=needle)
def descriptionStartsWith(self, needle):
return self._new_object(descriptionStartsWith=needle)
def textMatches(self, match):
return self._new_object(textMatches=match)
def descriptionMatches(self, match):
return self._new_object(descriptionMatches=match)
def resourceIdMatches(self, match):
return self._new_object(resourceIdMatches=match)
def packageNameMatches(self, match):
return self._new_object(packageNameMatches=match)
def classNameMatches(self, match):
return self._new_object(classNameMatches=match)
def checkable(self, value):
return self._new_object(checkable=value)
def clickable(self, value):
return self._new_object(clickable=value)
def focusable(self, value):
return self._new_object(focusable=value)
def scrollable(self, value):
return self._new_object(scrollable=value)
def longClickable(self, value):
return self._new_object(longClickable=value)
def enabled(self, value):
return self._new_object(enabled=value)
def checked(self, value):
return self._new_object(checked=value)
def focused(self, value):
return self._new_object(focused=value)
def selected(self, value):
return self._new_object(selected=value)
def index(self, idx):
return self._new_object(index=idx)
def instance(self, idx):
return self._new_object(instance=idx)
def get(self, idx):
"""
获取匹配的第 N 个索引的元素
"""
return self.instance(idx)
def __iter__(self):
"""
遍历所有符合选择器条件的元素实例
"""
yield from [self.instance(i) for i in \
range(self.count())]
def count(self):
"""
获取选择器选中控件的数量
"""
req = protos.SelectorOnlyRequest(selector=self.selector)
r = self.stub.selectorCount(req)
return r.value
def _set_target_Point(self, req, target):
req.point.CopyFrom(target)
def _set_target_Selector(self, req, target):
req.target.CopyFrom(target)
def drag_to(self, target, step=32):
"""
将选择器选中的控件拖动到另一个选择器或者点上
"""
checkArgumentTyp(target, (Point, _Selector))
func = "_set_target_{}".format(target.DESCRIPTOR.name)
req = protos.SelectorDragToRequest(selector=self.selector,
step=step)
getattr(self, func)(req, target)
r = self.stub.selectorDragTo(req)
return r.value
def wait_for_exists(self, timeout):
"""
等待选择器选中控件出现
"""
req = protos.SelectorWaitRequest(selector=self.selector,
timeout=timeout)
r = self.stub.selectorWaitForExists(req)
return r.value
def wait_until_gone(self, timeout):
"""
等待选择器选中控件消失
"""
req = protos.SelectorWaitRequest(selector=self.selector,
timeout=timeout)
r = self.stub.selectorWaitUntilGone(req)
return r.value
def swipe(self, direction=Direction.DIR_UP, step=32):
"""
在选择器选中的元素上进行滑动操作
"""
req = protos.SelectorSwipeRequest(selector=self.selector,
direction=direction,
step=step)
r = self.stub.selectorSwipe(req)
return r.value
def pinch_in(self, percent, step=16):
"""
双指捏紧(缩小)
"""
req = protos.SelectorPinchRequest(selector=self.selector,
percent=percent, step=step)
r = self.stub.selectorPinchIn(req)
return r.value
def pinch_out(self, percent, step=16):
"""
双指放开(放大)
"""
req = protos.SelectorPinchRequest(selector=self.selector,
percent=percent, step=step)
r = self.stub.selectorPinchOut(req)
return r.value
def scroll_to(self, target, is_vertical=True):
"""
滚动 scrollable 直到匹配目标元素的选择器
"""
checkArgumentTyp(target, _Selector)
req = protos.SelectorScrollRequest(selector=self.selector,
vertical=is_vertical,
target=target)
r = self.stub.selectorScrollTo(req)
return r.value
def _fling_forward(self, is_vertical=True):
req = protos.SelectorFlingRequest(selector=self.selector,
vertical=is_vertical)
r = self.stub.selectorFlingForward(req)
return r.value
def _fling_backward(self, is_vertical=True):
req = protos.SelectorFlingRequest(selector=self.selector,
vertical=is_vertical)
r = self.stub.selectorFlingBackward(req)
return r.value
def _fling_to_end(self, max_swipes, is_vertical=True):
req = protos.SelectorFlingRequest(selector=self.selector,
maxSwipes=max_swipes,
vertical=is_vertical)
r = self.stub.selectorFlingToEnd(req)
return r.value
def _fling_to_beginning(self, max_swipes, is_vertical=True):
req = protos.SelectorFlingRequest(selector=self.selector,
maxSwipes=max_swipes,
vertical=is_vertical)
r = self.stub.selectorFlingToBeginning(req)
return r.value
def fling_from_top_to_bottom(self):
"""
在选择器选中元素上进行从上至下阅读式滑动(单次)
"""
return self._fling_backward(is_vertical=True)
def fling_from_bottom_to_top(self):
"""
在选择器选中元素上进行从下至上阅读式滑动(单次)
"""
return self._fling_forward(is_vertical=True)
def fling_from_left_to_right(self):
"""
在选择器选中元素上进行从左至右阅读式滑动(单次)
"""
return self._fling_backward(is_vertical=False)
def fling_from_right_to_left(self):
"""
在选择器选中元素上进行从右至左阅读式滑动(单次)
"""
return self._fling_forward(is_vertical=False)
def fling_from_top_to_bottom_to_end(self, max_swipes):
"""
在选择器选中元素上进行从上至下阅读式滑动直至无法滑动或达到 max_swipes 次
"""
return self._fling_to_beginning(max_swipes, is_vertical=True)
def fling_from_bottom_to_top_to_end(self, max_swipes):
"""
在选择器选中元素上进行从下至上阅读式滑动直至无法滑动或达到 max_swipes 次
"""
return self._fling_to_end(max_swipes, is_vertical=True)
def fling_from_left_to_right_to_end(self, max_swipes):
"""
在选择器选中元素上进行从左至右阅读式滑动直至无法滑动或达到 max_swipes 次
"""
return self._fling_to_beginning(max_swipes, is_vertical=False)
def fling_from_right_to_left_to_end(self, max_swipes):
"""
在选择器选中元素上进行从右至左阅读式滑动直至无法滑动或达到 max_swipes 次
"""
return self._fling_to_end(max_swipes, is_vertical=False)
def _scroll_forward(self, step, is_vertical=True):
req = protos.SelectorScrollRequest(selector=self.selector,
vertical=is_vertical,
step=step)
r = self.stub.selectorScrollForward(req)
return r.value
def _scroll_backward(self, step, is_vertical=True):
req = protos.SelectorScrollRequest(selector=self.selector,
vertical=is_vertical,
step=step)
r = self.stub.selectorScrollBackward(req)
return r.value
def _scroll_to_end(self, max_swipes, step, is_vertical=True):
req = protos.SelectorScrollRequest(selector=self.selector,
maxSwipes=max_swipes,
vertical=is_vertical,
step=step)
r = self.stub.selectorScrollToEnd(req)
return r.value
def _scroll_to_beginning(self, max_swipes, step, is_vertical=True):
req = protos.SelectorScrollRequest(selector=self.selector,
maxSwipes=max_swipes,
vertical=is_vertical,
step=step)
r = self.stub.selectorScrollToBeginning(req)
return r.value
def scroll_from_top_to_bottom(self, step):
"""
在选择器选中元素上进行从上至下普通滑动
"""
return self._scroll_backward(step, is_vertical=True)
def scroll_from_bottom_to_top(self, step):
"""
在选择器选中元素上进行从下至上普通滑动
"""
return self._scroll_forward(step, is_vertical=True)
def scroll_from_left_to_right(self, step):
"""
在选择器选中元素上进行从左至右普通滑动
"""
return self._scroll_backward(step, is_vertical=False)
def scroll_from_right_to_left(self, step):
"""
在选择器选中元素上进行从右至左普通滑动
"""
return self._scroll_forward(step, is_vertical=False)
def scroll_from_top_to_bottom_to_end(self, max_swipes, step):
"""
在选择器选中元素上进行从上至下普通滑动直至无法滑动或达到 max_swipes 次
"""
return self._scroll_to_beginning(max_swipes, step, is_vertical=True)
def scroll_from_bottom_to_top_to_end(self, max_swipes, step):
"""
在选择器选中元素上进行从下至上普通滑动直至无法滑动或达到 max_swipes 次
"""
return self._scroll_to_end(max_swipes, step, is_vertical=True)
def scroll_from_left_to_right_to_end(self, max_swipes, step):
"""
在选择器选中元素上进行从左至右普通滑动直至无法滑动或达到 max_swipes 次
"""
return self._scroll_to_beginning(max_swipes, step, is_vertical=False)
def scroll_from_right_to_left_to_end(self, max_swipes, step):
"""
在选择器选中元素上进行从右至左普通滑动直至无法滑动或达到 max_swipes 次
"""
return self._scroll_to_end(max_swipes, step, is_vertical=False)
class UiAutomatorStub(BaseServiceStub):
def __init__(self, *args, **kwargs):
super(UiAutomatorStub, self).__init__(*args, **kwargs)
self.watchers = defaultdict(dict)
def device_info(self):
"""
获取设备基本/分辨率等信息
"""
r = self.stub.deviceInfo(protos.Empty())
return r
def set_watcher_loop_enabled(self, enabled):
"""
设置是否启用设备上的 watcher UI 检测
"""
req = protos.Boolean(value=enabled)
r = self.stub.setWatcherLoopEnabled(req)
return r.value
def get_watcher_loop_enabled(self):
"""
获取是否启用设备上的 watcher UI 检测
"""
r = self.stub.getWatcherLoopEnabled(protos.Empty())
return r.value
def get_watcher_triggered_count(self, name):
"""
获取这个 watcher 被触发的次数
"""
req = protos.String(value=name)
r = self.stub.getWatcherTriggeredCount(req)
return r.value
def reset_watcher_triggered_count(self, name):
"""
重置这个 watcher 的触发次数为 0
"""
req = protos.String(value=name)
r = self.stub.resetWatcherTriggeredCount(req)
return r.value
def get_applied_watchers(self):
"""
获取已经在系统应用的 watcher 名称列表
"""
r = self.stub.getAppliedWatchers(protos.Empty())
return r.watchers
# 注意:下面这些 watcher 实现不是安全的
# 注册时都是统一存储到本地实例的变量中,直至 enable 时才会应用至服务端
# 这样做的原因是让你知道你都干了什么,过多的 watcher 会影响性能
def remove_all_watchers(self):
"""
移除所有应用/未应用的 watcher
"""
for name in list(self.get_applied_watchers()):
self.remove_watcher(name)
for name in list(self.watchers.keys()):
self.remove_watcher(name)
def register_click_target_selector_watcher(self, name, conditions,
target):
"""
注册一个满足条件点击 selector 的 watcher
"""
checkDupEntry(name, self.watchers)
req = protos.WatcherRegistRequest(name=name, selectors=conditions,
target=target)
self.watchers[name]["enabled"] = False
func = lambda: self.stub.registerClickUiObjectWatcher(req).value
self.watchers[name]["enable"] = func
def register_press_key_watcher(self, name, conditions, key):
"""
注册一个满足条件点击 key 的 watcher
"""
checkDupEntry(name, self.watchers)
req = protos.WatcherRegistRequest(name=name, selectors=conditions,
key=key)
self.watchers[name]["enabled"] = False
func = lambda: self.stub.registerPressKeysWatcher(req).value
self.watchers[name]["enable"] = func
def register_none_op_watcher(self, name, conditions):
"""
注册一个满足条件无操作的 watcher(用来检测是否出现过某个场景)
"""
checkDupEntry(name, self.watchers)
req = protos.WatcherRegistRequest(name=name, selectors=conditions)
self.watchers[name]["enabled"] = False
func = lambda: self.stub.registerNoneOpWatcher(req).value
self.watchers[name]["enable"] = func
def _remove_watcher(self, name):
return self.stub.removeWatcher(protos.String(value=name)).value
def set_watcher_enabled(self, name, enable):
"""
设置是否启用此 watcher
"""
if name not in self.watchers:
return False
self.watchers[name]["enabled"] = enable
if self.watchers[name]["enabled"]:
return self.watchers[name]["enable"]()
return self._remove_watcher(name)
def get_watcher_enabled(self, name):
"""
获取此 watcher 是否启用
"""
return self.watchers.get(name, {}).get("enable")
def get_last_toast(self):
"""
获取系统中最后一个 toast 消息
"""
r = self.stub.getLastToast(protos.Empty())
return r
def remove_watcher(self, name):
"""
移除一个 watcher
"""
self.watchers.pop(name, None)
return self._remove_watcher(name)
def click(self, point):
"""
点击屏幕中的某个点(Point)
"""
req = protos.ClickPointRequest(point=point)
r = self.stub.click(req)
return r.value
def drag(self, A, B, step=32):
"""
从点(Point) A 拖动到点(Point) B
"""
req = protos.DragPointRequest(A=A, B=B, step=step)
r = self.stub.drag(req)
return r.value
def swipe(self, A, B, step=32):
"""
从点(Point) A 滑动到点(Point) B
"""
req = protos.SwipePointRequest(A=A, B=B, step=step)
r = self.stub.swipe(req)
return r.value
def swipe_points(self, *points, step=32):
"""
滑动一个点(Point)序列(超过两个点)
"""
req = protos.SwipePointsRequest(points=points, step=step)
r = self.stub.swipePoints(req)
return r.value
def open_notification(self):
"""
打开通知栏(状态栏)
"""
r = self.stub.openNotification(protos.Empty())
return r.value
def open_quick_settings(self):
"""
打开设置栏(状态栏)
"""
r = self.stub.openQuickSettings(protos.Empty())
return r.value
def wake_up(self):
"""
唤醒设备(点亮屏幕)
"""
r = self.stub.wakeUp(protos.Empty())
return r.value
def sleep(self):
"""
关闭设备(熄灭屏幕)
"""
r = self.stub.sleep(protos.Empty())
return r.value
def is_screen_on(self):
"""
设备是否处于唤醒状态
"""
r = self.stub.isScreenOn(protos.Empty())
return r.value
def is_screen_locked(self):
"""
设备屏幕是否已经锁定
"""
r = self.stub.isScreenLocked(protos.Empty())
return r.value
def set_clipboard(self, text):
"""
设置剪切板文字
"""
req = protos.ClipboardRequest(ID=str(uuid.uuid4()), value=text)
r = self.stub.setClipboard(req)
return r.value
def get_clipboard(self):
"""
获取剪切板文字(小于 Android10)
"""
r = self.stub.getClipboard(protos.Empty())
return r.value
def _set_target_Area(self, req, area):
req.area = area
def _set_target_Bound(self, req, bound):
req.bound.CopyFrom(bound)
def find_similar_image(self, data, threshold=0.0, distance=250,
scale=1.0, area=FindImageArea.FIA_WHOLE_SCREEN,
method=FindImageMethod.FIM_TEMPLATE):
"""
根据提供的目标图片从屏幕中获取相似图片位置
"""
req = protos.FindImageRequest()
checkArgumentTyp(area, (Bound, int))
name = getattr(getattr(area, "DESCRIPTOR", None),
"name", "Area")
func = "_set_target_{}".format(name)
getattr(self, func)(req, area)
req.method = method
req.distance = distance
req.threshold = threshold
req.scale = scale
req.partial = data
r = self.stub.findSimilarImage(req)
return r.bounds
def freeze_rotation(self, freeze=True):
"""
锁定屏幕旋转
"""
r = self.stub.freezeRotation(protos.Boolean(value=freeze))
return r.value
def set_orientation(self, orien=Orientation.ORIEN_NATURE):
"""
设置屏幕旋转方向
"""
req = protos.OrientationRequest(orientation=orien)
r = self.stub.setOrientation(req)
return r.value
def press_key(self, key):
"""
按下设备物理按键(HOME/VOLUME/BACK)
"""
req = protos.PressKeyRequest(key=key)
r = self.stub.pressKey(req)
return r.value
def press_keycode(self, code, meta=0):
"""
通过 Keycode(整数)按下未定义的按键
ref: https://developer.android.com/reference/android/view/KeyEvent
"""
req = protos.PressKeyRequest(code=code, meta=meta)
r = self.stub.pressKeyCode(req)
return r.value
def take_screenshot(self, quality, bound=None):
"""
截取全屏幕截图
"""
req = protos.TakeScreenshotRequest(quality=quality,
bound=bound)
r = self.stub.takeScreenshot(req)
return BytesIO(r.value)
def screenshot(self, quality, bound=None):
return self.take_screenshot(quality, bound=bound)
def dump_window_hierarchy(self, compressed=False):
"""
获取屏幕界面布局 XML 文档
"""
req = protos.Boolean(value=compressed)
r = self.stub.dumpWindowHierarchy(req)
return BytesIO(r.value)
def wait_for_idle(self, timeout):
"""
等待当前屏幕处于闲置状态(无频繁活动切换)
"""
r = self.stub.waitForIdle(protos.Integer(value=timeout))
return r.value
def __call__(self, **kwargs):
return ObjectUiAutomatorOpStub(self, kwargs)
class AppScriptRpcInterface(object):
def __init__(self, stub, application,
name):
self.application = application
self.stub = stub
self.name = name
def __str__(self):
return "{}:Script:{}".format(self.application,
self.name)
__repr__ = __str__
def __call__(self, *args):
call_args = dict()
call_args["method"] = self.name
call_args["args"] = args
req = HookRpcRequest()
req.package = self.application.applicationId
req.user = self.application.user
req.callinfo = json.dumps(call_args)
result = self.stub.callScript(req)
data = json.loads(result.callresult)
return data
class ApplicationOpStub:
def __init__(self, stub, applicationId, user=0):
"""
Application 子接口,用来模拟出实例的意味
"""
self.user = user
self.applicationId = applicationId
self.stub = stub
def __str__(self):
return "Application:{}@{}".format(self.applicationId,
self.user)
__repr__ = __str__
def is_foreground(self):
"""
应用是否正处于前台运行
"""
req = protos.ApplicationRequest(name=self.applicationId)
req.user = self.user
r = self.stub.isForeground(req)
return r.value
def permissions(self):
"""
获取应用的所有权限列表
"""
req = protos.ApplicationRequest(name=self.applicationId)
req.user = self.user
r = self.stub.getPermissions(req)
return r.permissions
def grant(self, permission, mode=GrantType.GRANT_ALLOW):
"""
授予应用某个权限(应用需要运行时获取的权限)
"""
req = protos.ApplicationRequest(name=self.applicationId,
permission=permission,
mode=mode)
req.user = self.user
r = self.stub.grantPermission(req)
return r.value
def revoke(self, permission):
"""
撤销授予应用的权限(应用需要运行时获取的权限)
"""
req = protos.ApplicationRequest(name=self.applicationId,
permission=permission)
req.user = self.user
r = self.stub.revokePermission(req)
return r.value
def query_launch_activity(self):
"""
获取应用的启动 activity 信息
"""
req = protos.ApplicationRequest(name=self.applicationId)
req.user = self.user
r = self.stub.queryLaunchActivity(req)
return to_dict(r)
def is_permission_granted(self, permission):
"""
检查是否已经授予应用某权限(应用需要运行时获取的权限)
"""
req = protos.ApplicationRequest(name=self.applicationId,
permission=permission)
req.user = self.user
r = self.stub.isPermissionGranted(req)
return r.value
def clear_cache(self):
"""
清空应用的缓存数据(非数据仅缓存)
"""
req = protos.ApplicationRequest(name=self.applicationId)
req.user = self.user
r = self.stub.deleteApplicationCache(req)
return r.value
def reset(self):
"""
清空应用的所有数据
"""
req = protos.ApplicationRequest(name=self.applicationId)
req.user = self.user
r = self.stub.resetApplicationData(req)
return r.value
def start(self):
"""
启动应用
"""
req = protos.ApplicationRequest(name=self.applicationId)
req.user = self.user
r = self.stub.startApplication(req)
return r.value
def stop(self):
"""
停止应用
"""
req = protos.ApplicationRequest(name=self.applicationId)
req.user = self.user
r = self.stub.stopApplication(req)
return r.value
def info(self):
"""
获取应用信息
"""
req = protos.ApplicationRequest(name=self.applicationId)
req.user = self.user
r = self.stub.applicationInfo(req)
return r
def uninstall(self):
"""
卸载应用 (always return true)
"""
req = protos.ApplicationRequest(name=self.applicationId)
req.user = self.user
r = self.stub.uninstallApplication(req)
return r.value
def enable(self):
"""
启用应用
"""
req = protos.ApplicationRequest(name=self.applicationId)
req.user = self.user
r = self.stub.enableApplication(req)
return r.value
def disable(self):
"""
禁用应用(这将使应用从启动器消失)
"""
req = protos.ApplicationRequest(name=self.applicationId)
req.user = self.user
r = self.stub.disableApplication(req)
return r.value
def add_to_doze_mode_whitelist(self):
"""
将APP加入省电白名单(可以一直运行,可能不会覆盖所有系统)
"""
req = protos.ApplicationRequest(name=self.applicationId)
req.user = self.user
r = self.stub.addToDozeModeWhiteList(req)
return True
def remove_from_doze_mode_whitelist(self):
"""
将APP移除省电白名单 (always return true)
"""
req = protos.ApplicationRequest(name=self.applicationId)
req.user = self.user
r = self.stub.removeFromDozeModeWhiteList(req)
return True
def is_installed(self):
"""
检查应用是否已经安装
"""
req = protos.ApplicationRequest(name=self.applicationId)
req.user = self.user
r = self.stub.isInstalled(req)
return r.value
def attach_script(self, script, runtime=ScriptRuntime.RUNTIME_QJS,
emit="",
encode=DataEncode.DATA_ENCODE_NONE,
spawn=False,
standup=5):
"""
向应用注入持久化 Hook 脚本
"""
s = isinstance(script, str)
script = script.encode() if s else script
req = protos.HookRequest()
req.package = self.applicationId
req.script = script
req.runtime = runtime
req.standup = standup
req.spawn = spawn
req.destination = emit
req.encode = encode
req.user = self.user
r = self.stub.attachScript(req)
return r.value
def detach_script(self):
"""
移除注入应用的 Hook 脚本
"""
req = protos.HookRequest()
req.package = self.applicationId
req.user = self.user
r = self.stub.detachScript(req)
return r.value
def is_attached_script(self):
"""
检查使用在此应用注入了 Hook 脚本
"""
req = protos.HookRequest()
req.package = self.applicationId
req.user = self.user
r = self.stub.isScriptAttached(req)
return r.value
def is_script_alive(self):
"""
检查应用中的 Hook 脚本是否正常
"""
req = protos.HookRequest()
req.package = self.applicationId
req.user = self.user
r = self.stub.isScriptAlive(req)
return r.value
def __getattr__(self, name):
"""
调用注入应用 Hook 脚本的导出方法
"""
return AppScriptRpcInterface(self.stub, self,
name)
class ApplicationStub(BaseServiceStub):
def current_application(self):
"""
获取当前处于前台的应用的信息
"""
top = self.stub.currentApplication(protos.Empty())
app = self.__call__(top.packageName, user=top.user)
app.activity = top.activity
return app
def get_application_by_name(self, name, user=0):
req = protos.String(value=name)
r = self.stub.getIdentifierByLabel(req)
app = self.__call__(r.value, user=user)
return app
def enumerate_running_processes(self):
"""
列出设备上所有正在运行的安卓应用进程
"""
r = self.stub.enumerateRunningProcesses(protos.Empty())
return r.processes
def enumerate_all_pkg_names(self):
"""
列出所有已安装的应用的 applicationId
"""
r = self.stub.enumerateAllPkgNames(protos.Empty())
return r.names
def get_last_activities(self, count=3):
"""
获取系统中最后一个活动的详细信息
"""
req = protos.Integer(value=count)
r = self.stub.getLastActivities(req).activities
return list(map(to_dict, r))
def start_activity(self, **activity):
"""
启动 activity(总是返回 True)
"""
activity.setdefault("extras", {})
extras = activity.pop("extras")
req = protos.ApplicationActivityRequest(**activity)
req.extras.update(extras)
r = self.stub.startActivity(req)
return r.value
def install_local_file(self, fpath, user=0):
"""
安装设备上的 apk 文件(注意此路径为设备上的 apk 路径)
"""
req = protos.ApplicationRequest(path=fpath)
req.user = user
r = self.stub.installFromLocalFile(req)
return r
def __call__(self, applicationId, user=0):
return ApplicationOpStub(self.stub, applicationId, user)
class StorageOpStub:
# 用于容器值序列化的方法
def _decrypt(self, data):
return self.cryptor.decrypt(data)
def _encrypt(self, data):
return self.cryptor.encrypt(data)
def _unpack(self, value):
return msgpack.loads(self._decrypt(value))
def _pack(self, value):
return self._encrypt(msgpack.dumps(value))
# 注意:此接口可能并不是跨语言通用
def __init__(self, stub, name, cryptor=None):
self.cryptor = cryptor
self.name = name
self.stub = stub
def delete(self, key):
"""
删除一个 KEY
"""
req = protos.StorageRequest(key=key)
req.container = self.name
res = self.stub.delete(req)
return res.value
def exists(self, key):
"""
检查一个 KEY 是否存在
"""
req = protos.StorageRequest(key=key)
req.container = self.name
res = self.stub.exists(req)
return res.value
def get(self, key, default=None):
"""
获取 KEY 对应的键值
"""
req = protos.StorageRequest(key=key)
req.container = self.name
val = self.stub.get(req).value
res = self._unpack(val) if val else default
return res
def set(self, key, value):
"""
设置 KEY 对应的键值
"""
value = self._pack(value)
req = protos.StorageRequest(key=key, value=value)
req.container = self.name
res = self.stub.set(req)
return res.value
def setex(self, key, value, ttl):
"""
设置 KEY 对应的键值,该 KEY 在 TTL 秒后自动删除
"""
value = self._pack(value)
req = protos.StorageRequest(key=key, value=value)
req.container = self.name
req.ttl = ttl
res = self.stub.setex(req)
return res.value
def setnx(self, key, value):
"""
设置 KEY 对应的键值 (仅当该键不存在时)
"""
value = self._pack(value)
req = protos.StorageRequest(key=key, value=value)
req.container = self.name
res = self.stub.setnx(req)
return res.value
def expire(self, key, ttl):
"""
设置 KEY 在 TTL 秒后过期
"""
req = protos.StorageRequest(key=key, ttl=ttl)
req.container = self.name
res = self.stub.expire(req)
return res.value
def ttl(self, key):
"""
获取 KEY 的 TTL (过期时间)
"""
req = protos.StorageRequest(key=key)
req.container = self.name
res = self.stub.ttl(req)
return res.value
class StorageStub(BaseServiceStub):
def clear(self):
"""
删除所有 Storage 容器
"""
r = self.stub.clearAll(protos.Empty())
return r.value
def use(self, name, cryptor=BaseCryptor, **kwargs):
"""
使用一个 Storage 容器
"""
return StorageOpStub(self.stub, name, cryptor(**kwargs))
def remove(self, name):
"""
删除一个 Storage 容器
"""
req = protos.String(value=name)
r = self.stub.clearContainer(req)
return r.value
class UtilStub(BaseServiceStub):
def _get_file_content(self, certfile):
with open(certfile, "rb") as fd:
return fd.read()
def is_ca_certificate_installed(self, certfile):
"""
安装系统证书(用于 MITM)
"""
data = self._get_file_content(certfile)
req = protos.CertifiRequest(cert=data)
r = self.stub.isCACertificateInstalled(req)
return r.value
def install_ca_certificate(self, certfile):
"""
安装系统证书(用于 MITM)
"""
data = self._get_file_content(certfile)
req = protos.CertifiRequest(cert=data)
r = self.stub.installCACertificate(req)
return r.value
def uninstall_ca_certificate(self, certfile):
"""
移除系统证书(用于 MITM)
"""
data = self._get_file_content(certfile)
req = protos.CertifiRequest(cert=data)
r = self.stub.uninstallCACertificate(req)
return r.value
def record_touch(self):
"""
录制滑动轨迹
"""
r = self.stub.recordTouch(protos.Empty())
return r
def perform_touch(self, tas, wait=True):
"""
在设备上进行真实滑动(重放录制的滑动轨迹)
"""
checkArgumentTyp(tas, TouchSequence)
req = protos.PerformTouchRequest(sequence=tas, wait=wait)
r = self.stub.performTouch(req)
return r.value
def reboot(self):
"""
重启系统(宿主设备)
"""
r = self.stub.reboot(protos.Empty())
return r.value
def shutdown(self):
"""
关闭系统(宿主设备)
"""
r = self.stub.shutdown(protos.Empty())
return r.value
def reload(self, clean=False):
"""
重载设备上运行的服务端
"""
req = protos.Boolean(value=clean)
r = self.stub.reload(req)
return r.value
def exit(self):
"""
退出设备上运行的服务端
"""
r = self.stub.exit(protos.Empty())
return r.value
def beep(self):
"""
播放一声蜂鸣(物理查找)
"""
r = self.stub.beepBeep(protos.Empty())
return r.value
def play_audio(self, file, type=AudioStreamType.AST_SYSTEM,
loop=1, interval=0):
"""
播放 wav 音频
"""
profile = PlayAudioProfile()
profile.file = file
profile.type = type
profile.loop = loop
profile.interval = interval
r = self.stub.playAudio(profile)
return r.value
def show_toast(self, text, duration=ToastDuration.TD_SHORT):
"""
在系统界面底部显示一个 Toast 消息
"""
req = protos.ShowToastRequest(text=text, duration=duration)
r = self.stub.showToast(req)
return r.value
def setprop(self, name, value):
"""
设置系统属性(aka: setprop,支持设置 ro.xx 只读属性)
"""
req = protos.SetPropRequest(name=name, value=value)
r = self.stub.setProp(req)
return r.value
def getprop(self, name):
"""
获取系统属性(aka: getprop)
"""
req = protos.String(value=name)
r = self.stub.getProp(req)
return r.value
def server_info(self):
"""
获取服务端ID、版本等信息
"""
r = self.stub.serverInfo(protos.Empty())
return r
def hex_patch(self, pattern, replacement, path,
maxreplace=-1,
dryrun=False):
"""
对设备上的文件进行十六进制字节替换
"""
req = protos.HexPatchRequest()
req.pattern = pattern
req.replacement = replacement
req.path = path
req.maxreplace = maxreplace
req.dryrun = dryrun
return self.stub.hexPatch(req)
class DebugStub(BaseServiceStub):
def _read_pubkey(self, pubkey):
with open(pubkey, "rb") as fd:
return fd.read()
def install_adb_pubkey(self, pubkey):
"""
给内置 adb 服务添加公钥
"""
req = protos.ADBDConfigRequest()
req.adb_pubkey = self._read_pubkey(pubkey)
r = self.stub.installADBPubKey(req)
return r.value
def uninstall_adb_pubkey(self, pubkey):
"""
从内置 adb 服务移除公钥
"""
req = protos.ADBDConfigRequest()
req.adb_pubkey = self._read_pubkey(pubkey)
r = self.stub.uninstallADBPubKey(req)
return r.value
def is_android_debug_bridge_running(self):
"""
远端 adb daemon 是否在运行
"""
r = self.stub.isAndroidDebugBridgeRunning(protos.Empty())
return r.value
def start_android_debug_bridge(self):
"""
启动内置 adbd (默认随框架启动)
"""
r = self.stub.startAndroidDebugBridge(protos.Empty())
return r.value
def stop_android_debug_bridge(self):
"""
停止内置 adb daemon
"""
r = self.stub.stopAndroidDebugBridge(protos.Empty())
return r.value
class SettingsStub(BaseServiceStub):
def _put(self, group, name, value):
req = protos.SettingsRequest(group=group, name=name,
value=value)
r = self.stub.putSettings(req)
return r.value
def _get(self, group, name):
req = protos.SettingsRequest(group=group,name=name)
r = self.stub.getSettings(req)
return r.value
def get_system(self, name):
"""
等价于 settings get system xxxx
"""
return self._get(Group.GROUP_SYSTEM, name)
def put_system(self, name, value):
"""
等价于 settings put system xxxx xxxx
"""
return self._put(Group.GROUP_SYSTEM, name, value)
def get_global(self, name):
"""
等价于 settings get global xxxx
"""
return self._get(Group.GROUP_GLOBAL, name)
def put_global(self, name, value):
"""
等价于 settings put global xxxx xxxx
"""
return self._put(Group.GROUP_GLOBAL, name, value)
def get_secure(self, name):
"""
等价于 settings get secure xxxx
"""
return self._get(Group.GROUP_SECURE, name)
def put_secure(self, name, value):
"""
等价于 settings put secure xxxx xxxx
"""
return self._put(Group.GROUP_SECURE, name, value)
class ShellStub(BaseServiceStub):
def execute_script(self, script, alias=None,
timeout=60):
"""
前台执行一段脚本(支持标准的多行脚本)
"""
req = protos.ShellRequest(name=alias, script=script,
timeout=timeout)
r = self.stub.executeForeground(req)
return r
def execute_background_script(self, script, alias=None):
"""
后台执行一段脚本(支持标准的多行脚本)
"""
req = protos.ShellRequest(name=alias, script=script)
r = self.stub.executeBackground(req)
return r.tid
def is_background_script_finished(self, tid):
"""
后台脚本是否已经结束
"""
req = protos.ShellTask(tid=tid)
r = self.stub.isBackgroundFinished(req)
return r.value
def kill_background_script(self, tid):
"""
强行停止后台脚本
"""
req = protos.ShellTask(tid=tid)
r = self.stub.killBackground(req)
return r.value
class StatusStub(BaseServiceStub):
def get_boot_time(self):
"""
获取设备启动时间 Unix 时间戳
"""
r = self.stub.getBootTime(protos.Empty())
return r.value
def get_disk_usage(self, mountpoint="/data"):
"""
获取分区数据使用情况
"""
req = protos.String(value=mountpoint)
r = self.stub.getDiskUsage(req)
return r
def get_battery_info(self):
"""
获取电池信息
"""
r = self.stub.getBatteryInfo(protos.Empty())
return r
def get_cpu_info(self):
"""
获取 CPU 用量等信息
"""
r = self.stub.getCpuInfo(protos.Empty())
return r
def get_overall_disk_io_info(self):
"""
获取全局的设备磁盘读写状况
"""
r = self.stub.getOverallDiskIOInfo(protos.Empty())
return r
def get_overall_net_io_info(self):
"""
获取全局的设备网络收发状况
"""
r = self.stub.getOverallNetIOInfo(protos.Empty())
return r
def get_userdata_disk_io_info(self):
"""
获取用户数据设备磁盘读写状况
"""
r = self.stub.getUserDataDiskIOInfo(protos.Empty())
return r
def get_net_io_info(self, interface):
"""
获取特定接口的网络收发状况
"""
req = protos.String(value=interface)
r = self.stub.getNetIOInfo(req)
return r
def get_mem_info(self):
"""
获取设备内存状况
"""
r = self.stub.getMemInfo(protos.Empty())
return r
class ProxyStub(BaseServiceStub):
def is_openvpn_running(self):
"""
检查 OPENVPN 是否正在运行
"""
r = self.stub.isOpenVPNRunning(protos.Empty())
return r.value
def is_gproxy_running(self):
"""
检查 GPROXY 是否正在运行
"""
r = self.stub.isGproxyRunning(protos.Empty())
return r.value
def start_openvpn(self, profile):
"""
启动 OPENVPN
"""
checkArgumentTyp(profile, OpenVPNProfile)
r = self.stub.startOpenVPN(profile)
return r.value
def start_gproxy(self, profile):
"""
启动 GPROXY
"""
checkArgumentTyp(profile, GproxyProfile)
r = self.stub.startGproxy(profile)
return r.value
def stop_openvpn(self):
"""
停止 OPENVPN
"""
r = self.stub.stopOpenVPN(protos.Empty())
return r.value
def stop_gproxy(self):
"""
停止 GPROXY
"""
r = self.stub.stopGproxy(protos.Empty())
return r.value
class SelinuxPolicyStub(BaseServiceStub):
def allow(self, source, target, tclass, action):
"""
selinux allow
"""
req = protos.SelinuxPolicyRequest(source=source, target=target,
tclass=tclass, action=action)
r = self.stub.policySetAllow(req)
return r.value
def disallow(self, source, target, tclass, action):
"""
selinux disallow
"""
req = protos.SelinuxPolicyRequest(source=source, target=target,
tclass=tclass, action=action)
r = self.stub.policySetDisallow(req)
return r.value
def get_enforce(self):
"""
获取当前 selinux enforce 状态
"""
r = self.stub.getEnforce(protos.Empty())
return r.value
def set_enforce(self, enforced=True):
"""
设置当前 selinux enforce 状态 (aka: setenforce 0/1)
"""
req = protos.Boolean(value=enforced)
r = self.stub.setEnforce(req)
return r.value
def enabled(self):
"""
获取设备上的 selinux 是否已经启用
"""
r = self.stub.isEnabled(protos.Empty())
return r.value
def enforce(self, name):
"""
设置一个域为 enforce
"""
req = protos.String(value=name)
r = self.stub.policySetEnforce(req)
return r.value
def permissive(self, name):
"""
设置一个域为 permissive
"""
req = protos.String(value=name)
r = self.stub.policySetPermissive(req)
return r.value
def create_domain(self, name):
"""
新建一个 selinux 域
"""
req = protos.String(value=name)
r = self.stub.policyCreateDomain(req)
return r.value
class FileStub(BaseServiceStub):
def _fd_stream_read(self, fd, chunksize):
for chunk in iter(lambda: fd.read(chunksize), bytes()):
yield chunk
def _fd_streaming_send(self, fd, dest, chunksize):
yield protos.FileRequest(path=dest)
for chunk in self._fd_stream_read(fd, chunksize):
yield protos.FileRequest(payload=chunk)
def _fd_streaming_recv(self, fd, iterator):
for chunk in iterator:
fd.write(chunk.payload)
def download_fd(self, fpath, fd):
"""
从设备下载文件到文件描述符
"""
req = protos.FileRequest(path=fpath)
iterator = self.stub.downloadFile(req)
self._fd_streaming_recv(fd, iterator)
st = self.file_stat(fpath)
return st
def upload_fd(self, fd, dest):
"""
上传文件描述符至设备
"""
chunksize = 1024*1024*1
streaming = self._fd_streaming_send(fd, dest,
chunksize)
self.stub.uploadFile(streaming)
st = self.file_stat(dest)
return st
def download_file(self, fpath, dest):
"""
从设备下载文件到本地
"""
with io.open(dest, mode="wb") as fd:
return self.download_fd(fpath, fd)
def upload_file(self, fpath, dest):
"""
上传本地文件至设备
"""
with io.open(fpath, mode="rb") as fd:
return self.upload_fd(fd, dest)
def delete_file(self, fpath):
"""
删除设备上的文件
"""
req = protos.FileRequest(path=fpath)
r = self.stub.deleteFile(req)
return r.value
def file_chmod(self, fpath, mode=0o644):
"""
更改设备上文件的权限
"""
req = protos.FileRequest(path=fpath, mode=mode)
r = self.stub.fileChmod(req)
return r
def file_stat(self, fpath):
"""
获取设备上文件的信息
"""
req = protos.FileRequest(path=fpath)
r = self.stub.fileStat(req)
return r
class LockStub(BaseServiceStub):
def acquire_lock(self, leaseTime=60):
"""
获取用于控制设备的锁,成功返回 true,被占用则会引发异常提示
"""
req = protos.Integer(value=leaseTime)
r = self.stub.acquireLock(req)
return r.value
def get_session_token(self):
"""
获取当前会话的动态令牌
"""
r = self.stub.getSessionToken(protos.Empty())
return r.value
def refresh_lock(self, leaseTime=60):
"""
刷新用于控制设备的锁,应该在定时任务每60s内调用以保持会话
"""
req = protos.Integer(value=leaseTime)
r = self.stub.refreshLock(req)
return r.value
def release_lock(self):
"""
释放控制设备的锁,释放后该设备可被其他客户端控制
"""
r = self.stub.releaseLock(protos.Empty())
return r.value
class WifiStub(BaseServiceStub):
def status(self):
"""
获取当前已连接 WIFI 的信息
"""
r = self.stub.status(protos.Empty())
return r
def blacklist_add(self, bssid):
"""
将 BSSID 加入 WIFI BSSID 黑名单(将不会在WIFI列表显示)
"""
r = self.stub.blacklistAdd(protos.String(value=bssid))
return r.value
def blacklist_clear(self):
"""
清空 WIFI BSSID 黑名单
"""
r = self.stub.blacklistClear(protos.Empty())
return r.value
def blacklist_get_all(self):
"""
获取在 WIFI BSSID 黑名单中的所有 BSSID
"""
r = self.stub.blacklistAll(protos.Empty())
return r.bssids
def scan(self):
"""
请求扫描附近 WIFI
"""
r = self.stub.scan(protos.Empty())
return r.value
def scan_results(self):
"""
获取已扫描到的附近 WIFI
"""
r = self.stub.scanResults(protos.Empty())
return r.stations
def get_mac_addr(self):
"""
获取当前 WIFI 的 MAC 地址
"""
r = self.stub.getMacAddr(protos.Empty())
return r.value
def signal_poll(self):
"""
获取当前已连接 WIFI 的信号强度等信息
"""
r = self.stub.signalPoll(protos.Empty())
return r
def list_networks(self):
"""
列出已连接过的 WIFI 网络
"""
r = self.stub.listNetworks(protos.Empty())
return r.networks
def select_network(self, networkId):
raise NotImplementedError
def enable_network(self, networkId):
raise NotImplementedError
def disable_network(self, networkId):
raise NotImplementedError
def add_network(self):
raise NotImplementedError
def remove_network(self, networkId):
raise NotImplementedError
def set_network_config(self, networkId, name, value):
raise NotImplementedError
def get_network_config(self, networkId, name):
raise NotImplementedError
def disconnect(self):
"""
断开 WIFI 连接
"""
r = self.stub.disconnect(protos.Empty())
return r.value
def reconnect(self):
"""
重连 WIFI
"""
r = self.stub.reconnect(protos.Empty())
return r.value
def set_config(self, name, value):
raise NotImplementedError
def set_auto_connect(self, auto=True):
raise NotImplementedError
def save_config(self):
raise NotImplementedError
class OcrOperator(object):
def __init__(self, device, elements=None,
**kwargs):
self.elements = elements
self.index = kwargs.pop("index", 0)
self.func, self.rule = kwargs.popitem()
self.match = getattr(self, self.func)
self.device = device
def text(self, item):
return self.rule == item["text"]
def textMatches(self, item):
return bool(re.match(self.rule, item["text"],
re.DOTALL))
def textContains(self, item):
return self.rule in item["text"]
def find_target_item(self):
m = [e for e in self.elements \
if self.match(e)]
o = (m and len(m) > self.index) != True
return None if o else m[self.index]
def find_item_or_throw(self):
item = self.find_target_item()
msg = "OcrSelector[{}={}]".format(self.func, self.rule)
item or self.throw(UiObjectNotFoundException, msg)
return item
def find_cb(self, func, ret, *args):
item = self.find_target_item()
return func(item, *args) if item else ret
def find_or_throw_cb(self, func, *args):
item = self.find_item_or_throw()
return func(item, *args)
def throw(self, exception, *args):
raise exception(*args)
def _screenshot(self, item, quality):
return self.device.screenshot(quality,
bound=item["bound"])
def _click(self, item):
point = item["bound"].center()
return self.device.click(point)
def __str__(self):
return "Ocr: {}={}".format(self.func, self.rule)
__repr__ = __str__
def exists(self):
"""
OCR - 检查元素是否存在
"""
return bool(self.find_target_item())
def click(self):
"""
OCR - 点击元素(不存在则报错)
"""
return self.find_or_throw_cb(self._click)
def click_exists(self):
"""
OCR - 点击元素(不存在将不会产生异常)
"""
return self.find_cb(self._click, False)
def screenshot(self, quality=100):
"""
OCR - 对元素进行截图
"""
return self.find_or_throw_cb(self._screenshot,
quality)
def take_screenshot(self, quality=100):
"""
OCR - 对元素进行截图
"""
return self.screenshot(quality)
def info(self):
"""
OCR - 获取匹配元素的信息
"""
item = self.find_item_or_throw()
return item
class OcrEngine(object):
def __init__(self, service, *args,
**kwargs):
args = list(args)
if type(service) == type:
args.insert(0, service)
service = "custom"
func = getattr(self, "init_{}".format(service))
func(*args, **kwargs)
def init_paddleocr(self, *args, **kwargs):
from paddleocr import PaddleOCR
self._service = PaddleOCR(*args, **kwargs)
self._ocr = self.ocr_paddleocr
def init_easyocr(self, *args, **kwargs):
from easyocr import Reader
self._service = Reader(*args, **kwargs)
self._ocr = self.ocr_easyocr
def init_custom(self, service, *args, **kwargs):
self._service = service(*args, **kwargs)
self._ocr = self.ocr_custom
def ocr_custom(self, image):
result = self._service.ocr(image)
return result
def ocr_paddleocr(self, image):
r = self._service.ocr(image)
n = bool(r and r[0] and type(r[0][-1])==float)
result = (r if n else r[0]) or []
output = [[n[0], n[1][0], n[1][1]] for n in result]
return output
def ocr_easyocr(self, image):
result = self._service.readtext(image)
return result
def ocr(self, screenshot):
img = screenshot.getvalue()
result = self._ocr(img) or []
output = [self.format(*n) for n in result]
return output
def format(self, box, text, confidence):
bound = Bound()
bound.left = int(min(p[0] for p in box))
bound.top = int(min(p[1] for p in box))
bound.bottom = int(max(p[1] for p in box))
bound.right = int(max(p[0] for p in box))
info = dict(text=text, confidence=confidence,
bound=bound)
return info
class Device(object):
def __init__(self, host, port=65000,
certificate=None,
session=None):
self.certificate = certificate
self.server = "{0}:{1}".format(host, port)
policy = dict()
policy["maxAttempts"] = 5
policy["retryableStatusCodes"] = ["UNAVAILABLE"]
policy["backoffMultiplier"] = 2
policy["initialBackoff"] = "0.5s"
policy["maxBackoff"] = "15s"
config = json.dumps(dict(methodConfig=[{"name": [{}],
"retryPolicy": policy,}]))
option = dict()
option["grpc.max_send_message_length"] = 64*1024*1024
option["grpc.max_receive_message_length"] = 128*1024*1024
option["grpc.keepalive_time_ms"] = 30*1000
option["grpc.keepalive_timeout_ms"] = 15*1000
option["grpc.keepalive_permit_without_calls"] = True
option["grpc.max_pings_without_data"] = 0
option["grpc.service_config"] = config
option["grpc.enable_http_proxy"] = 0
if certificate is not None:
with open(certificate, "rb") as fd:
key, crt, ca = self._parse_certdata(fd.read())
creds = grpc.ssl_channel_credentials(root_certificates=ca,
certificate_chain=crt,
private_key=key)
self._chan = grpc.secure_channel(self.server, creds,
options=(("grpc.ssl_target_name_override",
self._parse_cname(crt)),
*tuple(option.items()),))
else:
self._chan = grpc.insecure_channel(self.server,
options=(*tuple(option.items()),)
)
session = session or uuid.uuid4().hex
interceptors = [ClientSessionMetadataInterceptor(session),
GrpcRemoteExceptionInterceptor(),
ClientLoggingInterceptor()]
self._ocr = None
self._ocr_img_quality = 75
self.channel = grpc.intercept_channel(self._chan,
*interceptors)
self.session = session
@property
def frida(self):
if _frida_dma is None:
raise ModuleNotFoundError("frida")
try:
device = _frida_dma.get_device_matching(
lambda d: d.name==self.server)
# make a call to check server connectivity
device.query_system_parameters()
return device
except:
""" No-op """
kwargs = {}
if self.certificate is not None:
kwargs["certificate"] = self.certificate
if self._get_session_token():
kwargs["token"] = self._get_session_token()
try:
_frida_dma.remove_remote_device(self.server)
except frida.InvalidArgumentError:
""" No-op """
device = _frida_dma.add_remote_device(self.server,
**kwargs)
return device
def __str__(self):
return "Device@{}".format(self.server)
__repr__ = __str__
def _parse_certdata(self, data):
key, crt, ca = Pem.parse(data)
ca = ca.as_bytes()
crt = crt.as_bytes()
key = key.as_bytes()
return key, crt, ca
def _parse_cname(self, crt):
_, _, der = pem.unarmor(crt)
subject = x509.Certificate.load(der).subject
return subject.native["common_name"]
def _get_service_stub(self, module):
stub = getattr(services, "{0}Stub".format(module))
return stub(self.channel)
def stub(self, module):
modu = sys.modules[__name__]
stub = self._get_service_stub(module)
wrap = getattr(modu, "{0}Stub".format(module))
inst = getattr(self, module, wrap(stub))
self.__setattr__(module, inst)
return inst
# 快速调用: File
def download_fd(self, fpath, fd):
return self.stub("File").download_fd(fpath, fd)
def upload_fd(self, fd, dest):
return self.stub("File").upload_fd(fd, dest)
def download_file(self, fpath, dest):
return self.stub("File").download_file(fpath, dest)
def upload_file(self, fpath, dest):
return self.stub("File").upload_file(fpath, dest)
def delete_file(self, fpath):
return self.stub("File").delete_file(fpath)
def file_chmod(self, fpath, mode=0o644):
return self.stub("File").file_chmod(fpath, mode=mode)
def file_stat(self, fpath):
return self.stub("File").file_stat(fpath)
# 快速调用: Application
def install_local_file(self, rpath, user=0):
return self.stub("Application").install_local_file(rpath, user=user)
def current_application(self):
return self.stub("Application").current_application()
def enumerate_all_pkg_names(self):
return self.stub("Application").enumerate_all_pkg_names()
def enumerate_running_processes(self):
return self.stub("Application").enumerate_running_processes()
def get_last_activities(self, count=3):
return self.stub("Application").get_last_activities(count=count)
def start_activity(self, **activity):
return self.stub("Application").start_activity(**activity)
def get_application_by_name(self, name):
return self.stub("Application").get_application_by_name(name)
def application(self, applicationId, user=0):
return self.stub("Application")(applicationId, user=user)
# 快速调用: Util
def record_touch(self):
return self.stub("Util").record_touch()
def perform_touch(self, sequence, wait=True):
return self.stub("Util").perform_touch(sequence, wait=wait)
def show_toast(self, text, duration=ToastDuration.TD_SHORT):
return self.stub("Util").show_toast(text, duration=duration)
def is_ca_certificate_installed(self, certdata):
return self.stub("Util").is_ca_certificate_installed(certdata)
def uninstall_ca_certificate(self, certfile):
return self.stub("Util").uninstall_ca_certificate(certfile)
def install_ca_certificate(self, certfile):
return self.stub("Util").install_ca_certificate(certfile)
def reboot(self):
return self.stub("Util").reboot()
def shutdown(self):
return self.stub("Util").shutdown()
def exit(self):
return self.stub("Util").exit()
def reload(self, clean=False):
return self.stub("Util").reload(clean)
def beep(self):
return self.stub("Util").beep()
def play_audio(self, file, type=AudioStreamType.AST_SYSTEM,
loop=1, interval=0):
return self.stub("Util").play_audio(file, type=type, loop=loop,
interval=interval)
def setprop(self, name, value):
return self.stub("Util").setprop(name, value)
def getprop(self, name):
return self.stub("Util").getprop(name)
def hex_patch(self, pattern, replacement, path,
maxreplace=-1, dryrun=False):
return self.stub("Util").hex_patch(pattern, replacement, path,
maxreplace=maxreplace,
dryrun=dryrun)
# 快速调用: Debug
def install_adb_pubkey(self, pubkey):
return self.stub("Debug").install_adb_pubkey(pubkey)
def uninstall_adb_pubkey(self, pubkey):
return self.stub("Debug").uninstall_adb_pubkey(pubkey)
def start_android_debug_bridge(self):
return self.stub("Debug").start_android_debug_bridge()
def is_android_debug_bridge_running(self):
return self.stub("Debug").is_android_debug_bridge_running()
def stop_android_debug_bridge(self):
return self.stub("Debug").stop_android_debug_bridge()
# 快速调用: Proxy
def is_openvpn_running(self):
return self.stub("Proxy").is_openvpn_running()
def is_gproxy_running(self):
return self.stub("Proxy").is_gproxy_running()
def start_openvpn(self, profile):
return self.stub("Proxy").start_openvpn(profile)
def start_gproxy(self, profile):
return self.stub("Proxy").start_gproxy(profile)
def stop_openvpn(self):
return self.stub("Proxy").stop_openvpn()
def stop_gproxy(self):
return self.stub("Proxy").stop_gproxy()
# 快速调用: Shell
def execute_script(self, script, alias=None, timeout=60):
return self.stub("Shell").execute_script(script, alias=alias,
timeout=timeout)
def execute_background_script(self, script, alias=None):
return self.stub("Shell").execute_background_script(script, alias=alias)
def is_background_script_finished(self, tid):
return self.stub("Shell").is_background_script_finished(tid)
def kill_background_script(self, tid):
return self.stub("Shell").kill_background_script(tid)
# 快速调用: UiAutomator
def click(self, point):
return self.stub("UiAutomator").click(point)
def drag(self, A, B, step=32):
return self.stub("UiAutomator").drag(A, B, step=step)
def swipe(self, A, B, step=32):
return self.stub("UiAutomator").swipe(A, B, step=step)
def swipe_points(self, *points, step=32):
return self.stub("UiAutomator").swipe_points(*points, step=step)
def open_notification(self):
return self.stub("UiAutomator").open_notification()
def open_quick_settings(self):
return self.stub("UiAutomator").open_quick_settings()
def wake_up(self):
return self.stub("UiAutomator").wake_up()
def sleep(self):
return self.stub("UiAutomator").sleep()
def is_screen_on(self):
return self.stub("UiAutomator").is_screen_on()
def is_screen_locked(self):
return self.stub("UiAutomator").is_screen_locked()
def set_clipboard(self, text):
return self.stub("UiAutomator").set_clipboard(text)
def get_clipboard(self):
return self.stub("UiAutomator").get_clipboard()
def freeze_rotation(self, freeze=True):
return self.stub("UiAutomator").freeze_rotation(freeze=freeze)
def set_orientation(self, orien=Orientation.ORIEN_NATURE):
return self.stub("UiAutomator").set_orientation(orien)
def press_key(self, key):
return self.stub("UiAutomator").press_key(key)
def press_keycode(self, code, meta=0):
return self.stub("UiAutomator").press_keycode(code, meta)
def take_screenshot(self, quality=100, bound=None):
return self.stub("UiAutomator").take_screenshot(quality, bound=bound)
def screenshot(self, quality=100, bound=None):
return self.stub("UiAutomator").screenshot(quality, bound=bound)
def dump_window_hierarchy(self, compressed=False):
return self.stub("UiAutomator").dump_window_hierarchy(compressed=compressed)
def wait_for_idle(self, timeout):
return self.stub("UiAutomator").wait_for_idle(timeout)
def get_last_toast(self):
return self.stub("UiAutomator").get_last_toast()
def find_similar_image(self, data, threshold=0.0, distance=250,
scale=1.0, area=FindImageArea.FIA_WHOLE_SCREEN,
method=FindImageMethod.FIM_TEMPLATE):
return self.stub("UiAutomator").find_similar_image(data, threshold=threshold,
distance=distance, scale=scale,
area=area, method=method)
# watcher
def remove_all_watchers(self):
return self.stub("UiAutomator").remove_all_watchers()
def set_watcher_loop_enabled(self, enabled):
return self.stub("UiAutomator").set_watcher_loop_enabled(enabled)
def get_watcher_loop_enabled(self):
return self.stub("UiAutomator").get_watcher_loop_enabled()
def get_watcher_triggered_count(self, name):
return self.stub("UiAutomator").get_watcher_triggered_count(name)
def reset_watcher_triggered_count(self, name):
return self.stub("UiAutomator").reset_watcher_triggered_count(name)
def get_applied_watchers(self):
return self.stub("UiAutomator").get_applied_watchers()
def register_click_target_selector_watcher(self, name, conditions,
target):
return self.stub("UiAutomator").register_click_target_selector_watcher(
name, conditions, target
)
def register_press_key_watcher(self, name, conditions, key):
return self.stub("UiAutomator").register_press_key_watcher(
name, conditions, key
)
def register_none_op_watcher(self, name, conditions):
return self.stub("UiAutomator").register_none_op_watcher(
name, conditions
)
def set_watcher_enabled(self, name, enable):
return self.stub("UiAutomator").set_watcher_enabled(name, enable)
def get_watcher_enabled(self, name):
return self.stub("UiAutomator").get_watcher_enabled(name)
def remove_watcher(self, name):
return self.stub("UiAutomator").remove_watcher(name)
def device_info(self):
return self.stub("UiAutomator").device_info()
def server_info(self):
return self.stub("Util").server_info()
def __call__(self, **kwargs):
return self.stub("UiAutomator")(**kwargs)
# OCR 功能扩展
def ocr(self, index=0, **kwargs):
if not isinstance(self._ocr, OcrEngine):
raise IllegalStateException("Ocr engine is not setted up")
if any(r not in ["text", "textContains", "textMatches"] \
for r in kwargs.keys()):
raise InvalidArgumentError("Only text* matches are supported")
if len(kwargs) != 1:
raise InvalidArgumentError("Only or at least one rule can be used")
image = self.screenshot(self._ocr_img_quality)
return OcrOperator(self,
elements=self._ocr.ocr(image),
index=index,
**kwargs
)
def setup_ocr_backend(self, service, *args, quality=75,
**kwargs):
self._ocr_img_quality = quality
self._ocr = OcrEngine(service, *args,
**kwargs)
# 日志打印
def set_debug_log_enabled(self, enable):
level = logging.DEBUG if enable else logging.WARN
logger.setLevel(level)
return enable
# 接口锁定
def _get_session_token(self):
return self.stub("Lock").get_session_token()
def _acquire_lock(self, leaseTime=60):
return self.stub("Lock").acquire_lock(leaseTime)
def _refresh_lock(self, leaseTime=60):
return self.stub("Lock").refresh_lock(leaseTime)
def _release_lock(self):
return self.stub("Lock").release_lock()
def __enter__(self):
self._acquire_lock(leaseTime=sys.maxsize)
return self
def __exit__(self, type, value, traceback):
self._release_lock()
if __name__ == "__main__":
import code
import readline
import rlcompleter
import argparse
parser = argparse.ArgumentParser()
crt = os.environ.get("CERTIFICATE", None)
port = int(os.environ.get("PORT", 65000))
parser.add_argument("-device", type=str, default="127.0.0.1",
help="service ip address")
parser.add_argument("-port", type=int, default=port,
help="service port")
parser.add_argument("-cert", type=str, default=crt,
help="ssl cert")
args = parser.parse_args()
readline.parse_and_bind("tab: complete")
d = Device(args.device, port=args.port,
certificate=args.cert)
code.interact(local=globals())
================================================
FILE: lamda/const.py
================================================
# Copyright 2022 rev1si0n (https://github.com/rev1si0n). All rights reserved.
#
# Distributed under MIT license.
# See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
# Android runtime permissions
PERMISSION_READ_SMS = "android.permission.READ_SMS"
PERMISSION_READ_CALENDAR = "android.permission.READ_CALENDAR"
PERMISSION_READ_CALL_LOG = "android.permission.READ_CALL_LOG"
PERMISSION_ACCESS_FINE_LOCATION = "android.permission.ACCESS_FINE_LOCATION"
PERMISSION_ANSWER_PHONE_CALLS = "android.permission.ANSWER_PHONE_CALLS"
PERMISSION_RECEIVE_WAP_PUSH = "android.permission.RECEIVE_WAP_PUSH"
PERMISSION_BODY_SENSORS = "android.permission.BODY_SENSORS"
PERMISSION_READ_PHONE_NUMBERS = "android.permission.READ_PHONE_NUMBERS"
PERMISSION_RECEIVE_MMS = "android.permission.RECEIVE_MMS"
PERMISSION_RECEIVE_SMS = "android.permission.RECEIVE_SMS"
PERMISSION_READ_EXTERNAL_STORAGE = "android.permission.READ_EXTERNAL_STORAGE"
PERMISSION_ACCESS_COARSE_LOCATION = "android.permission.ACCESS_COARSE_LOCATION"
PERMISSION_READ_PHONE_STATE = "android.permission.READ_PHONE_STATE"
PERMISSION_SEND_SMS = "android.permission.SEND_SMS"
PERMISSION_CALL_PHONE = "android.permission.CALL_PHONE"
PERMISSION_WRITE_CONTACTS = "android.permission.WRITE_CONTACTS"
PERMISSION_ACCEPT_HANDOVER = "android.permission.ACCEPT_HANDOVER"
PERMISSION_CAMERA = "android.permission.CAMERA"
PERMISSION_WRITE_CALENDAR = "android.permission.WRITE_CALENDAR"
PERMISSION_WRITE_CALL_LOG = "android.permission.WRITE_CALL_LOG"
PERMISSION_USE_SIP = "android.permission.USE_SIP"
PERMISSION_PROCESS_OUTGOING_CALLS = "android.permission.PROCESS_OUTGOING_CALLS"
PERMISSION_READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS"
PERMISSION_GET_ACCOUNTS = "android.permission.GET_ACCOUNTS"
PERMISSION_WRITE_EXTERNAL_STORAGE = "android.permission.WRITE_EXTERNAL_STORAGE"
PERMISSION_ACTIVITY_RECOGNITION = "android.permission.ACTIVITY_RECOGNITION"
PERMISSION_RECORD_AUDIO = "android.permission.RECORD_AUDIO"
PERMISSION_READ_CONTACTS = "android.permission.READ_CONTACTS"
PERMISSION_ACCESS_BACKGROUND_LOCATION = "android.permission.ACCESS_BACKGROUND_LOCATION"
PERMISSION_ACCESS_MEDIA_LOCATION = "android.permission.ACCESS_MEDIA_LOCATION"
# Android activity flags
FLAG_ACTIVITY_BROUGHT_TO_FRONT = 0x00400000
FLAG_ACTIVITY_CLEAR_TASK = 0x00008000
FLAG_ACTIVITY_CLEAR_TOP = 0x04000000
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS = 0x00800000
FLAG_ACTIVITY_FORWARD_RESULT = 0x02000000
FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY = 0x00100000
FLAG_ACTIVITY_LAUNCH_ADJACENT = 0x00001000
FLAG_ACTIVITY_MATCH_EXTERNAL = 0x00000800
FLAG_ACTIVITY_MULTIPLE_TASK = 0x08000000
FLAG_ACTIVITY_NEW_DOCUMENT = 0x00080000
FLAG_ACTIVITY_NEW_TASK = 0x10000000
FLAG_ACTIVITY_NO_ANIMATION = 0x00010000
FLAG_ACTIVITY_NO_HISTORY = 0x40000000
FLAG_ACTIVITY_NO_USER_ACTION = 0x00040000
FLAG_ACTIVITY_PREVIOUS_IS_TOP = 0x01000000
FLAG_ACTIVITY_REORDER_TO_FRONT = 0x00020000
FLAG_ACTIVITY_REQUIRE_DEFAULT = 0x00000200
FLAG_ACTIVITY_REQUIRE_NON_BROWSER = 0x00000400
FLAG_ACTIVITY_RESET_TASK_IF_NEEDED = 0x00200000
FLAG_ACTIVITY_RETAIN_IN_RECENTS = 0x00002000
FLAG_ACTIVITY_SINGLE_TOP = 0x20000000
FLAG_ACTIVITY_TASK_ON_HOME = 0x00004000
================================================
FILE: lamda/exceptions.py
================================================
# Copyright 2022 rev1si0n (https://github.com/rev1si0n). All rights reserved.
#
# Distributed under MIT license.
# See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
class CompatibilityException(Exception):
""" Exception """
class DeadSystemException(Exception):
""" Exception """
class DeviceUnavailable(Exception):
""" Exception """
class DuplicateEntryError(Exception):
""" Exception """
class IllegalArgumentException(Exception):
""" Exception """
class IllegalStateException(Exception):
""" Exception """
class InstallPackageFailed(Exception):
""" Exception """
class InternalRpcException(Exception):
""" Exception """
class InvalidAndroidPackage(Exception):
""" Exception """
class InvalidArgumentError(Exception):
""" Exception """
class InvalidOperationError(Exception):
""" Exception """
class InvalidRootCertificate(Exception):
""" Exception """
class MethodNotFoundException(Exception):
""" Exception """
class NameNotFoundException(Exception):
""" Exception """
class NotImplementedException(Exception):
""" Exception """
class NullPointerException(Exception):
""" Exception """
class SecurityException(Exception):
""" Exception """
class ServiceUnavailable(Exception):
""" Exception """
class StaleObjectException(Exception):
""" Exception """
class StartupActivityNotFound(Exception):
""" Exception """
class StorageOutOfMemory(Exception):
""" Exception """
class UiAutomatorException(Exception):
""" Exception """
class UiObjectNotFoundException(Exception):
""" Exception """
class UnHandledException(Exception):
""" Exception """
================================================
FILE: lamda/google/protobuf/any.proto
================================================
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
package google.protobuf;
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
option go_package = "google.golang.org/protobuf/types/known/anypb";
option java_package = "com.google.protobuf";
option java_outer_classname = "AnyProto";
option java_multiple_files = true;
option objc_class_prefix = "GPB";
// `Any` contains an arbitrary serialized protocol buffer message along with a
// URL that describes the type of the serialized message.
//
// Protobuf library provides support to pack/unpack Any values in the form
// of utility functions or additional generated methods of the Any type.
//
// Example 1: Pack and unpack a message in C++.
//
// Foo foo = ...;
// Any any;
// any.PackFrom(foo);
// ...
// if (any.UnpackTo(&foo)) {
// ...
// }
//
// Example 2: Pack and unpack a message in Java.
//
// Foo foo = ...;
// Any any = Any.pack(foo);
// ...
// if (any.is(Foo.class)) {
// foo = any.unpack(Foo.class);
// }
//
// Example 3: Pack and unpack a message in Python.
//
// foo = Foo(...)
// any = Any()
// any.Pack(foo)
// ...
// if any.Is(Foo.DESCRIPTOR):
// any.Unpack(foo)
// ...
//
// Example 4: Pack and unpack a message in Go
//
// foo := &pb.Foo{...}
// any, err := anypb.New(foo)
// if err != nil {
// ...
// }
// ...
// foo := &pb.Foo{}
// if err := any.UnmarshalTo(foo); err != nil {
// ...
// }
//
// The pack methods provided by protobuf library will by default use
// 'type.googleapis.com/full.type.name' as the type URL and the unpack
// methods only use the fully qualified type name after the last '/'
// in the type URL, for example "foo.bar.com/x/y.z" will yield type
// name "y.z".
//
//
// JSON
// ====
// The JSON representation of an `Any` value uses the regular
// representation of the deserialized, embedded message, with an
// additional field `@type` which contains the type URL. Example:
//
// package google.profile;
// message Person {
// string first_name = 1;
// string last_name = 2;
// }
//
// {
// "@type": "type.googleapis.com/google.profile.Person",
// "firstName": <string>,
// "lastName": <string>
// }
//
// If the embedded message type is well-known and has a custom JSON
// representation, that representation will be embedded adding a field
// `value` which holds the custom JSON in addition to the `@type`
// field. Example (for message [google.protobuf.Duration][]):
//
// {
// "@type": "type.googleapis.com/google.protobuf.Duration",
// "value": "1.212s"
// }
//
message Any {
// A URL/resource name that uniquely identifies the type of the serialized
// protocol buffer message. This string must contain at least
// one "/" character. The last segment
gitextract_h60805my/
├── .gitignore
├── CHANGELOG.txt
├── DISCLAIMER.TXT
├── LICENSE
├── README.md
├── SECURITY.md
├── all-llms.txt
├── examples/
│ ├── README.md
│ ├── activity_jump.py
│ └── search_in_taobao.py
├── extensions/
│ ├── README.md
│ ├── example_http_extension.py
│ ├── example_mcp_extension.py
│ ├── firerpa.py
│ ├── mcp_return_types.py
│ └── mcp_sms_reader.py
├── lamda/
│ ├── __init__.py
│ ├── client.py
│ ├── const.py
│ ├── exceptions.py
│ ├── google/
│ │ └── protobuf/
│ │ ├── any.proto
│ │ ├── api.proto
│ │ ├── compiler/
│ │ │ └── plugin.proto
│ │ ├── descriptor.proto
│ │ ├── duration.proto
│ │ ├── empty.proto
│ │ ├── field_mask.proto
│ │ ├── source_context.proto
│ │ ├── struct.proto
│ │ ├── timestamp.proto
│ │ ├── type.proto
│ │ └── wrappers.proto
│ ├── rpc/
│ │ ├── application.proto
│ │ ├── debug.proto
│ │ ├── file.proto
│ │ ├── policy.proto
│ │ ├── proxy.proto
│ │ ├── services.proto
│ │ ├── settings.proto
│ │ ├── shell.proto
│ │ ├── status.proto
│ │ ├── storage.proto
│ │ ├── types.proto
│ │ ├── uiautomator.proto
│ │ ├── util.proto
│ │ └── wifi.proto
│ └── types.py
├── properties.local.example
├── scripts/
│ ├── disable_flag_secure.yaml
│ └── disable_ssl_pinning_simple.yaml
├── setup.py
└── tools/
├── README.md
├── adb_pubkey.py
├── cert.py
├── debugimage.py
├── discover.py
├── firerpa.yml
├── frida_script_generate.py
├── fridarpc.py
├── globalmitm/
│ ├── DNS2SOCKS.c
│ ├── Dockerfile
│ └── entry
├── id_rsa
├── ida.py
├── magisk/
│ ├── META-INF/
│ │ └── com/
│ │ └── google/
│ │ └── android/
│ │ ├── update-binary
│ │ └── updater-script
│ ├── common/
│ │ ├── adb_keys
│ │ ├── properties.local
│ │ ├── server/
│ │ │ └── .keep
│ │ └── service.sh
│ ├── install.sh
│ ├── module.prop
│ └── uninstall.sh
├── objection-1.11.0-command-patch.diff
├── openvpn/
│ ├── Dockerfile
│ ├── config.ovpn
│ ├── entry
│ ├── ovpn-client-new
│ ├── ovpn-client-profile
│ ├── ovpn-client-renew
│ ├── ovpn-client-revoke
│ ├── ovpn-server-new
│ └── vars
├── paddle_ocr_http_api.py
├── requirements.txt
├── root.crt
├── root.key
├── rsync.sh
├── scp.sh
├── socks5/
│ ├── Dockerfile
│ └── entry
├── ssh.sh
├── startmitm.py
├── startmitm.spec
├── test-fridarpc.js
└── test.pem
SYMBOL INDEX (626 symbols across 13 files)
FILE: extensions/example_http_extension.py
class ExampleHttpExtension (line 21) | class ExampleHttpExtension(BaseHttpExtension):
method http_get (line 23) | def http_get(self, *args, **kwargs):
method http_post (line 26) | def http_post(self, *args, **kwargs):
method http_put (line 29) | def http_put(self, *args, **kwargs):
method http_delete (line 32) | def http_delete(self, *args, **kwargs):
method http_patch (line 35) | def http_patch(self, *args, **kwargs):
FILE: extensions/example_mcp_extension.py
class ExampleMcpExtension (line 12) | class ExampleMcpExtension(BaseMcpExtension):
method greeting (line 17) | def greeting(self, ctx, msg: Annotated[str, "Greeting message"],
method getprop (line 21) | def getprop(self, ctx, name: Annotated[str, "Android system property n...
method get_file (line 24) | def get_file(self, ctx, absolute_path: Annotated[str, "Absolute file p...
FILE: extensions/firerpa.py
class FireRpaMcpExtension (line 36) | class FireRpaMcpExtension(BaseMcpExtension):
method dump_window_hierarchy (line 42) | def dump_window_hierarchy(self, ctx, compressed: Annotated[bool, "Enab...
method click (line 46) | def click(self, ctx, pointX: Annotated[int, "X coordinate."], pointY: ...
method swipe (line 50) | def swipe(self, ctx, fromX: Annotated[int, "Swipe-from X coordinate."]...
method drag (line 54) | def drag(self, ctx, fromX: Annotated[int, "Drag-from X coordinate."], ...
method get_deviec_info (line 58) | def get_deviec_info(self, ctx):
method show_toast (line 62) | def show_toast(self, ctx, message: Annotated[str, "The toast message."]):
method execute_shell_script_foreground (line 66) | def execute_shell_script_foreground(self, ctx, scrip: Annotated[str, "...
method wake_up (line 70) | def wake_up(self, ctx):
method sleep (line 74) | def sleep(self, ctx):
method is_screen_on (line 78) | def is_screen_on(self, ctx):
method is_screen_locked (line 82) | def is_screen_locked(self, ctx):
method get_clipboard_text (line 86) | def get_clipboard_text(self, ctx):
method set_clipboard_text (line 90) | def set_clipboard_text(self, ctx, text: Annotated[str, "The text to se...
method press_key_code (line 94) | def press_key_code(self, ctx, key_code: Annotated[int, "The Android's ...
method get_last_toast (line 98) | def get_last_toast(self, ctx):
method getprop (line 102) | def getprop(self, ctx, name: Annotated[str, "Android system property n...
method click_by_text (line 105) | def click_by_text(self, ctx, text: Annotated[str, "The full text field...
method click_by_text_contains (line 109) | def click_by_text_contains(self, ctx, substring: Annotated[str, "The s...
method click_by_text_matches (line 113) | def click_by_text_matches(self, ctx, regex: Annotated[str, "The string...
method click_by_description (line 117) | def click_by_description(self, ctx, text: Annotated[str, "The full des...
method click_by_description_contains (line 121) | def click_by_description_contains(self, ctx, substring: Annotated[str,...
method click_by_description_matches (line 125) | def click_by_description_matches(self, ctx, regex: Annotated[str, "The...
method click_by_resource_id (line 129) | def click_by_resource_id(self, ctx, resource_id: Annotated[str, "Eleme...
method set_text_by_resource_id (line 133) | def set_text_by_resource_id(self, ctx, resource_id: Annotated[str, "In...
method set_text_by_class_name (line 137) | def set_text_by_class_name(self, ctx, class_name: Annotated[str, "Inpu...
method current_top_application_info (line 141) | def current_top_application_info(self, ctx):
method start_application_by_id (line 145) | def start_application_by_id(self, ctx, package_name: Annotated[str, "T...
method stop_application_by_id (line 149) | def stop_application_by_id(self, ctx, package_name: Annotated[str, "Th...
method is_application_installed (line 153) | def is_application_installed(self, ctx, package_name: Annotated[str, "...
method is_application_running_foreground (line 157) | def is_application_running_foreground(self, ctx, package_name: Annotat...
method list_application_permissions (line 161) | def list_application_permissions(self, ctx, package_name: Annotated[st...
method grant_application_permission (line 165) | def grant_application_permission(self, ctx, package_name: Annotated[st...
method revoke_application_permission (line 169) | def revoke_application_permission(self, ctx, package_name: Annotated[s...
method is_permission_granted (line 173) | def is_permission_granted(self, ctx, package_name: Annotated[str, "The...
method agent (line 177) | def agent(self, ctx):
method remove_attrs_and_empty (line 179) | def remove_attrs_and_empty(self, xml):
FILE: extensions/mcp_return_types.py
class BaseModel (line 20) | class BaseModel(Struct, omit_defaults=False):
method validate (line 21) | def validate(self):
class BaseStructuredModel (line 26) | class BaseStructuredModel(BaseModel):
class Result (line 30) | class Result(BaseModel):
class Annotations (line 34) | class Annotations(BaseModel):
class ResourceContents (line 39) | class ResourceContents(BaseModel):
class TextResourceContents (line 49) | class TextResourceContents(ResourceContents, kw_only=True):
class BlobResourceContents (line 55) | class BlobResourceContents(ResourceContents, kw_only=True):
class TextContent (line 61) | class TextContent(BaseModel, kw_only=True):
class ImageContent (line 70) | class ImageContent(BaseModel, kw_only=True):
class EmbeddedResource (line 81) | class EmbeddedResource(BaseModel, kw_only=True):
class PromptMessage (line 94) | class PromptMessage(BaseModel):
class GetPromptResult (line 101) | class GetPromptResult(Result, kw_only=True):
class EmptyResult (line 109) | class EmptyResult(Result):
class CallToolResult (line 113) | class CallToolResult(Result):
class ReadResourceResult (line 121) | class ReadResourceResult(Result):
FILE: extensions/mcp_sms_reader.py
class SmsMcpExtension (line 17) | class SmsMcpExtension(BaseMcpExtension):
method read_sms_database_by_sql (line 23) | def read_sms_database_by_sql(self, ctx, sql: Annotated[str, "A raw SQL...
FILE: lamda/client.py
function getXY (line 112) | def getXY(p):
function checkDupEntry (line 115) | def checkDupEntry(a, entries):
function checkArgumentTyp (line 119) | def checkArgumentTyp(a, types):
function touchSequenceSave (line 123) | def touchSequenceSave(s, fpath):
function touchSequenceLoad (line 126) | def touchSequenceLoad(s, fpath):
function touchSequenceIndexer (line 129) | def touchSequenceIndexer(s, index):
function touchSequenceIter (line 132) | def touchSequenceIter(s):
function touchSequenceAppendAction (line 135) | def touchSequenceAppendAction(s, **kwargs):
function touchSequenceAppendDown (line 139) | def touchSequenceAppendDown(s, **kwargs):
function touchSequenceAppendMove (line 142) | def touchSequenceAppendMove(s, **kwargs):
function touchSequenceAppendWait (line 145) | def touchSequenceAppendWait(s, **kwargs):
function touchSequenceAppendUp (line 148) | def touchSequenceAppendUp(s, **kwargs):
function touchActionRealAction (line 151) | def touchActionRealAction(a):
function touchActionType (line 154) | def touchActionType(a):
function touchMoveShiftX (line 157) | def touchMoveShiftX(a, offset):
function touchMoveShiftY (line 161) | def touchMoveShiftY(a, offset):
function touchWaitShift (line 165) | def touchWaitShift(w, offset):
function applicationInfoSet (line 169) | def applicationInfoSet(application, app):
function height (line 172) | def height(b):
function width (line 175) | def width(b):
function center (line 178) | def center(b):
function contain (line 183) | def contain(a, b):
function equal (line 189) | def equal(a, b):
function corner (line 197) | def corner(b, position):
function load_proto (line 290) | def load_proto(name):
function to_dict (line 295) | def to_dict(prot):
function Selector (line 301) | def Selector(**kwargs):
function child_sibling (line 308) | def child_sibling(s, name, **selector):
function child (line 315) | def child(s, **selector):
function sibling (line 319) | def sibling(s, **selector):
class CustomOcrBackend (line 328) | class CustomOcrBackend(object):
method __init__ (line 329) | def __init__(self, *args, **kwargs):
method ocr (line 331) | def ocr(self, image):
class BaseCryptor (line 335) | class BaseCryptor(object):
method encrypt (line 336) | def encrypt(self, data):
method decrypt (line 338) | def decrypt(self, data):
class BaseServiceStub (line 342) | class BaseServiceStub(object):
method __init__ (line 343) | def __init__(self, stub):
class FernetCryptor (line 347) | class FernetCryptor(BaseCryptor):
method __init__ (line 348) | def __init__(self, key=None):
method encrypt (line 351) | def encrypt(self, data):
method decrypt (line 353) | def decrypt(self, data):
method _get_key (line 355) | def _get_key(self, key):
class TouchBuilder (line 362) | class TouchBuilder(object):
method __init__ (line 363) | def __init__(self):
method down (line 365) | def down(self, x, y, pressure=50, track=0):
method move (line 369) | def move(self, x, y, pressure=50, track=0):
method up (line 373) | def up(self, track=0):
method wait (line 376) | def wait(self, mills):
method build (line 379) | def build(self):
class ClientLoggingInterceptor (line 385) | class ClientLoggingInterceptor(ClientInterceptor):
method truncate_string (line 386) | def truncate_string(self, s):
method intercept (line 388) | def intercept(self, function, request, details):
class ClientSessionMetadataInterceptor (line 401) | class ClientSessionMetadataInterceptor(ClientInterceptor):
method __init__ (line 402) | def __init__(self, session):
method intercept (line 405) | def intercept(self, function, request, details):
class GrpcRemoteExceptionInterceptor (line 416) | class GrpcRemoteExceptionInterceptor(ClientInterceptor):
method intercept (line 417) | def intercept(self, function, request, details):
method remote_exception (line 425) | def remote_exception(self, exception):
method raise_remote_exception (line 433) | def raise_remote_exception(self, res):
class ObjectUiAutomatorOpStub (line 440) | class ObjectUiAutomatorOpStub:
method __init__ (line 441) | def __init__(self, caller, selector):
method __str__ (line 449) | def __str__(self):
method child (line 454) | def child(self, **selector):
method sibling (line 461) | def sibling(self, **selector):
method take_screenshot (line 468) | def take_screenshot(self, quality=100):
method screenshot (line 476) | def screenshot(self, quality=100):
method get_text (line 478) | def get_text(self):
method clear_text_field (line 485) | def clear_text_field(self):
method set_text (line 492) | def set_text(self, text):
method click (line 500) | def click(self, corner=Corner.COR_CENTER):
method click_exists (line 508) | def click_exists(self, corner=Corner.COR_CENTER):
method long_click (line 516) | def long_click(self, corner=Corner.COR_CENTER):
method exists (line 524) | def exists(self):
method info (line 531) | def info(self):
method _new_object (line 537) | def _new_object(self, **kwargs):
method text (line 543) | def text(self, txt):
method resourceId (line 545) | def resourceId(self, name):
method description (line 547) | def description(self, desc):
method packageName (line 549) | def packageName(self, name):
method className (line 551) | def className(self, name):
method textContains (line 553) | def textContains(self, needle):
method descriptionContains (line 555) | def descriptionContains(self, needle):
method textStartsWith (line 557) | def textStartsWith(self, needle):
method descriptionStartsWith (line 559) | def descriptionStartsWith(self, needle):
method textMatches (line 561) | def textMatches(self, match):
method descriptionMatches (line 563) | def descriptionMatches(self, match):
method resourceIdMatches (line 565) | def resourceIdMatches(self, match):
method packageNameMatches (line 567) | def packageNameMatches(self, match):
method classNameMatches (line 569) | def classNameMatches(self, match):
method checkable (line 571) | def checkable(self, value):
method clickable (line 573) | def clickable(self, value):
method focusable (line 575) | def focusable(self, value):
method scrollable (line 577) | def scrollable(self, value):
method longClickable (line 579) | def longClickable(self, value):
method enabled (line 581) | def enabled(self, value):
method checked (line 583) | def checked(self, value):
method focused (line 585) | def focused(self, value):
method selected (line 587) | def selected(self, value):
method index (line 589) | def index(self, idx):
method instance (line 591) | def instance(self, idx):
method get (line 593) | def get(self, idx):
method __iter__ (line 598) | def __iter__(self):
method count (line 604) | def count(self):
method _set_target_Point (line 611) | def _set_target_Point(self, req, target):
method _set_target_Selector (line 613) | def _set_target_Selector(self, req, target):
method drag_to (line 615) | def drag_to(self, target, step=32):
method wait_for_exists (line 626) | def wait_for_exists(self, timeout):
method wait_until_gone (line 634) | def wait_until_gone(self, timeout):
method swipe (line 642) | def swipe(self, direction=Direction.DIR_UP, step=32):
method pinch_in (line 651) | def pinch_in(self, percent, step=16):
method pinch_out (line 659) | def pinch_out(self, percent, step=16):
method scroll_to (line 667) | def scroll_to(self, target, is_vertical=True):
method _fling_forward (line 677) | def _fling_forward(self, is_vertical=True):
method _fling_backward (line 682) | def _fling_backward(self, is_vertical=True):
method _fling_to_end (line 687) | def _fling_to_end(self, max_swipes, is_vertical=True):
method _fling_to_beginning (line 693) | def _fling_to_beginning(self, max_swipes, is_vertical=True):
method fling_from_top_to_bottom (line 699) | def fling_from_top_to_bottom(self):
method fling_from_bottom_to_top (line 704) | def fling_from_bottom_to_top(self):
method fling_from_left_to_right (line 709) | def fling_from_left_to_right(self):
method fling_from_right_to_left (line 714) | def fling_from_right_to_left(self):
method fling_from_top_to_bottom_to_end (line 719) | def fling_from_top_to_bottom_to_end(self, max_swipes):
method fling_from_bottom_to_top_to_end (line 724) | def fling_from_bottom_to_top_to_end(self, max_swipes):
method fling_from_left_to_right_to_end (line 729) | def fling_from_left_to_right_to_end(self, max_swipes):
method fling_from_right_to_left_to_end (line 734) | def fling_from_right_to_left_to_end(self, max_swipes):
method _scroll_forward (line 739) | def _scroll_forward(self, step, is_vertical=True):
method _scroll_backward (line 745) | def _scroll_backward(self, step, is_vertical=True):
method _scroll_to_end (line 751) | def _scroll_to_end(self, max_swipes, step, is_vertical=True):
method _scroll_to_beginning (line 758) | def _scroll_to_beginning(self, max_swipes, step, is_vertical=True):
method scroll_from_top_to_bottom (line 765) | def scroll_from_top_to_bottom(self, step):
method scroll_from_bottom_to_top (line 770) | def scroll_from_bottom_to_top(self, step):
method scroll_from_left_to_right (line 775) | def scroll_from_left_to_right(self, step):
method scroll_from_right_to_left (line 780) | def scroll_from_right_to_left(self, step):
method scroll_from_top_to_bottom_to_end (line 785) | def scroll_from_top_to_bottom_to_end(self, max_swipes, step):
method scroll_from_bottom_to_top_to_end (line 790) | def scroll_from_bottom_to_top_to_end(self, max_swipes, step):
method scroll_from_left_to_right_to_end (line 795) | def scroll_from_left_to_right_to_end(self, max_swipes, step):
method scroll_from_right_to_left_to_end (line 800) | def scroll_from_right_to_left_to_end(self, max_swipes, step):
class UiAutomatorStub (line 807) | class UiAutomatorStub(BaseServiceStub):
method __init__ (line 808) | def __init__(self, *args, **kwargs):
method device_info (line 811) | def device_info(self):
method set_watcher_loop_enabled (line 817) | def set_watcher_loop_enabled(self, enabled):
method get_watcher_loop_enabled (line 824) | def get_watcher_loop_enabled(self):
method get_watcher_triggered_count (line 830) | def get_watcher_triggered_count(self, name):
method reset_watcher_triggered_count (line 837) | def reset_watcher_triggered_count(self, name):
method get_applied_watchers (line 844) | def get_applied_watchers(self):
method remove_all_watchers (line 853) | def remove_all_watchers(self):
method register_click_target_selector_watcher (line 861) | def register_click_target_selector_watcher(self, name, conditions,
method register_press_key_watcher (line 872) | def register_press_key_watcher(self, name, conditions, key):
method register_none_op_watcher (line 882) | def register_none_op_watcher(self, name, conditions):
method _remove_watcher (line 891) | def _remove_watcher(self, name):
method set_watcher_enabled (line 893) | def set_watcher_enabled(self, name, enable):
method get_watcher_enabled (line 903) | def get_watcher_enabled(self, name):
method get_last_toast (line 908) | def get_last_toast(self):
method remove_watcher (line 914) | def remove_watcher(self, name):
method click (line 920) | def click(self, point):
method drag (line 927) | def drag(self, A, B, step=32):
method swipe (line 934) | def swipe(self, A, B, step=32):
method swipe_points (line 941) | def swipe_points(self, *points, step=32):
method open_notification (line 948) | def open_notification(self):
method open_quick_settings (line 954) | def open_quick_settings(self):
method wake_up (line 960) | def wake_up(self):
method sleep (line 966) | def sleep(self):
method is_screen_on (line 972) | def is_screen_on(self):
method is_screen_locked (line 978) | def is_screen_locked(self):
method set_clipboard (line 984) | def set_clipboard(self, text):
method get_clipboard (line 991) | def get_clipboard(self):
method _set_target_Area (line 997) | def _set_target_Area(self, req, area):
method _set_target_Bound (line 999) | def _set_target_Bound(self, req, bound):
method find_similar_image (line 1001) | def find_similar_image(self, data, threshold=0.0, distance=250,
method freeze_rotation (line 1020) | def freeze_rotation(self, freeze=True):
method set_orientation (line 1026) | def set_orientation(self, orien=Orientation.ORIEN_NATURE):
method press_key (line 1033) | def press_key(self, key):
method press_keycode (line 1040) | def press_keycode(self, code, meta=0):
method take_screenshot (line 1048) | def take_screenshot(self, quality, bound=None):
method screenshot (line 1056) | def screenshot(self, quality, bound=None):
method dump_window_hierarchy (line 1058) | def dump_window_hierarchy(self, compressed=False):
method wait_for_idle (line 1065) | def wait_for_idle(self, timeout):
method __call__ (line 1071) | def __call__(self, **kwargs):
class AppScriptRpcInterface (line 1075) | class AppScriptRpcInterface(object):
method __init__ (line 1076) | def __init__(self, stub, application,
method __str__ (line 1081) | def __str__(self):
method __call__ (line 1085) | def __call__(self, *args):
class ApplicationOpStub (line 1098) | class ApplicationOpStub:
method __init__ (line 1099) | def __init__(self, stub, applicationId, user=0):
method __str__ (line 1106) | def __str__(self):
method is_foreground (line 1110) | def is_foreground(self):
method permissions (line 1118) | def permissions(self):
method grant (line 1126) | def grant(self, permission, mode=GrantType.GRANT_ALLOW):
method revoke (line 1136) | def revoke(self, permission):
method query_launch_activity (line 1145) | def query_launch_activity(self):
method is_permission_granted (line 1153) | def is_permission_granted(self, permission):
method clear_cache (line 1162) | def clear_cache(self):
method reset (line 1170) | def reset(self):
method start (line 1178) | def start(self):
method stop (line 1186) | def stop(self):
method info (line 1194) | def info(self):
method uninstall (line 1202) | def uninstall(self):
method enable (line 1210) | def enable(self):
method disable (line 1218) | def disable(self):
method add_to_doze_mode_whitelist (line 1226) | def add_to_doze_mode_whitelist(self):
method remove_from_doze_mode_whitelist (line 1234) | def remove_from_doze_mode_whitelist(self):
method is_installed (line 1242) | def is_installed(self):
method attach_script (line 1250) | def attach_script(self, script, runtime=ScriptRuntime.RUNTIME_QJS,
method detach_script (line 1271) | def detach_script(self):
method is_attached_script (line 1280) | def is_attached_script(self):
method is_script_alive (line 1289) | def is_script_alive(self):
method __getattr__ (line 1298) | def __getattr__(self, name):
class ApplicationStub (line 1306) | class ApplicationStub(BaseServiceStub):
method current_application (line 1307) | def current_application(self):
method get_application_by_name (line 1315) | def get_application_by_name(self, name, user=0):
method enumerate_running_processes (line 1320) | def enumerate_running_processes(self):
method enumerate_all_pkg_names (line 1326) | def enumerate_all_pkg_names(self):
method get_last_activities (line 1332) | def get_last_activities(self, count=3):
method start_activity (line 1339) | def start_activity(self, **activity):
method install_local_file (line 1349) | def install_local_file(self, fpath, user=0):
method __call__ (line 1357) | def __call__(self, applicationId, user=0):
class StorageOpStub (line 1361) | class StorageOpStub:
method _decrypt (line 1363) | def _decrypt(self, data):
method _encrypt (line 1365) | def _encrypt(self, data):
method _unpack (line 1367) | def _unpack(self, value):
method _pack (line 1369) | def _pack(self, value):
method __init__ (line 1372) | def __init__(self, stub, name, cryptor=None):
method delete (line 1376) | def delete(self, key):
method exists (line 1384) | def exists(self, key):
method get (line 1392) | def get(self, key, default=None):
method set (line 1401) | def set(self, key, value):
method setex (line 1410) | def setex(self, key, value, ttl):
method setnx (line 1420) | def setnx(self, key, value):
method expire (line 1429) | def expire(self, key, ttl):
method ttl (line 1437) | def ttl(self, key):
class StorageStub (line 1447) | class StorageStub(BaseServiceStub):
method clear (line 1448) | def clear(self):
method use (line 1454) | def use(self, name, cryptor=BaseCryptor, **kwargs):
method remove (line 1459) | def remove(self, name):
class UtilStub (line 1468) | class UtilStub(BaseServiceStub):
method _get_file_content (line 1469) | def _get_file_content(self, certfile):
method is_ca_certificate_installed (line 1472) | def is_ca_certificate_installed(self, certfile):
method install_ca_certificate (line 1480) | def install_ca_certificate(self, certfile):
method uninstall_ca_certificate (line 1488) | def uninstall_ca_certificate(self, certfile):
method record_touch (line 1496) | def record_touch(self):
method perform_touch (line 1502) | def perform_touch(self, tas, wait=True):
method reboot (line 1510) | def reboot(self):
method shutdown (line 1516) | def shutdown(self):
method reload (line 1522) | def reload(self, clean=False):
method exit (line 1529) | def exit(self):
method beep (line 1535) | def beep(self):
method play_audio (line 1541) | def play_audio(self, file, type=AudioStreamType.AST_SYSTEM,
method show_toast (line 1553) | def show_toast(self, text, duration=ToastDuration.TD_SHORT):
method setprop (line 1560) | def setprop(self, name, value):
method getprop (line 1567) | def getprop(self, name):
method server_info (line 1574) | def server_info(self):
method hex_patch (line 1580) | def hex_patch(self, pattern, replacement, path,
class DebugStub (line 1595) | class DebugStub(BaseServiceStub):
method _read_pubkey (line 1596) | def _read_pubkey(self, pubkey):
method install_adb_pubkey (line 1599) | def install_adb_pubkey(self, pubkey):
method uninstall_adb_pubkey (line 1607) | def uninstall_adb_pubkey(self, pubkey):
method is_android_debug_bridge_running (line 1615) | def is_android_debug_bridge_running(self):
method start_android_debug_bridge (line 1621) | def start_android_debug_bridge(self):
method stop_android_debug_bridge (line 1627) | def stop_android_debug_bridge(self):
class SettingsStub (line 1635) | class SettingsStub(BaseServiceStub):
method _put (line 1636) | def _put(self, group, name, value):
method _get (line 1641) | def _get(self, group, name):
method get_system (line 1645) | def get_system(self, name):
method put_system (line 1650) | def put_system(self, name, value):
method get_global (line 1655) | def get_global(self, name):
method put_global (line 1660) | def put_global(self, name, value):
method get_secure (line 1665) | def get_secure(self, name):
method put_secure (line 1670) | def put_secure(self, name, value):
class ShellStub (line 1677) | class ShellStub(BaseServiceStub):
method execute_script (line 1678) | def execute_script(self, script, alias=None,
method execute_background_script (line 1687) | def execute_background_script(self, script, alias=None):
method is_background_script_finished (line 1694) | def is_background_script_finished(self, tid):
method kill_background_script (line 1701) | def kill_background_script(self, tid):
class StatusStub (line 1710) | class StatusStub(BaseServiceStub):
method get_boot_time (line 1711) | def get_boot_time(self):
method get_disk_usage (line 1717) | def get_disk_usage(self, mountpoint="/data"):
method get_battery_info (line 1724) | def get_battery_info(self):
method get_cpu_info (line 1730) | def get_cpu_info(self):
method get_overall_disk_io_info (line 1736) | def get_overall_disk_io_info(self):
method get_overall_net_io_info (line 1742) | def get_overall_net_io_info(self):
method get_userdata_disk_io_info (line 1748) | def get_userdata_disk_io_info(self):
method get_net_io_info (line 1754) | def get_net_io_info(self, interface):
method get_mem_info (line 1761) | def get_mem_info(self):
class ProxyStub (line 1769) | class ProxyStub(BaseServiceStub):
method is_openvpn_running (line 1770) | def is_openvpn_running(self):
method is_gproxy_running (line 1776) | def is_gproxy_running(self):
method start_openvpn (line 1782) | def start_openvpn(self, profile):
method start_gproxy (line 1789) | def start_gproxy(self, profile):
method stop_openvpn (line 1796) | def stop_openvpn(self):
method stop_gproxy (line 1802) | def stop_gproxy(self):
class SelinuxPolicyStub (line 1810) | class SelinuxPolicyStub(BaseServiceStub):
method allow (line 1811) | def allow(self, source, target, tclass, action):
method disallow (line 1819) | def disallow(self, source, target, tclass, action):
method get_enforce (line 1827) | def get_enforce(self):
method set_enforce (line 1833) | def set_enforce(self, enforced=True):
method enabled (line 1840) | def enabled(self):
method enforce (line 1846) | def enforce(self, name):
method permissive (line 1853) | def permissive(self, name):
method create_domain (line 1860) | def create_domain(self, name):
class FileStub (line 1869) | class FileStub(BaseServiceStub):
method _fd_stream_read (line 1870) | def _fd_stream_read(self, fd, chunksize):
method _fd_streaming_send (line 1873) | def _fd_streaming_send(self, fd, dest, chunksize):
method _fd_streaming_recv (line 1877) | def _fd_streaming_recv(self, fd, iterator):
method download_fd (line 1880) | def download_fd(self, fpath, fd):
method upload_fd (line 1889) | def upload_fd(self, fd, dest):
method download_file (line 1899) | def download_file(self, fpath, dest):
method upload_file (line 1905) | def upload_file(self, fpath, dest):
method delete_file (line 1911) | def delete_file(self, fpath):
method file_chmod (line 1918) | def file_chmod(self, fpath, mode=0o644):
method file_stat (line 1925) | def file_stat(self, fpath):
class LockStub (line 1934) | class LockStub(BaseServiceStub):
method acquire_lock (line 1935) | def acquire_lock(self, leaseTime=60):
method get_session_token (line 1942) | def get_session_token(self):
method refresh_lock (line 1948) | def refresh_lock(self, leaseTime=60):
method release_lock (line 1955) | def release_lock(self):
class WifiStub (line 1963) | class WifiStub(BaseServiceStub):
method status (line 1964) | def status(self):
method blacklist_add (line 1970) | def blacklist_add(self, bssid):
method blacklist_clear (line 1976) | def blacklist_clear(self):
method blacklist_get_all (line 1982) | def blacklist_get_all(self):
method scan (line 1988) | def scan(self):
method scan_results (line 1994) | def scan_results(self):
method get_mac_addr (line 2000) | def get_mac_addr(self):
method signal_poll (line 2006) | def signal_poll(self):
method list_networks (line 2012) | def list_networks(self):
method select_network (line 2018) | def select_network(self, networkId):
method enable_network (line 2020) | def enable_network(self, networkId):
method disable_network (line 2022) | def disable_network(self, networkId):
method add_network (line 2024) | def add_network(self):
method remove_network (line 2026) | def remove_network(self, networkId):
method set_network_config (line 2028) | def set_network_config(self, networkId, name, value):
method get_network_config (line 2030) | def get_network_config(self, networkId, name):
method disconnect (line 2032) | def disconnect(self):
method reconnect (line 2038) | def reconnect(self):
method set_config (line 2044) | def set_config(self, name, value):
method set_auto_connect (line 2046) | def set_auto_connect(self, auto=True):
method save_config (line 2048) | def save_config(self):
class OcrOperator (line 2052) | class OcrOperator(object):
method __init__ (line 2053) | def __init__(self, device, elements=None,
method text (line 2060) | def text(self, item):
method textMatches (line 2062) | def textMatches(self, item):
method textContains (line 2065) | def textContains(self, item):
method find_target_item (line 2067) | def find_target_item(self):
method find_item_or_throw (line 2072) | def find_item_or_throw(self):
method find_cb (line 2077) | def find_cb(self, func, ret, *args):
method find_or_throw_cb (line 2080) | def find_or_throw_cb(self, func, *args):
method throw (line 2083) | def throw(self, exception, *args):
method _screenshot (line 2085) | def _screenshot(self, item, quality):
method _click (line 2088) | def _click(self, item):
method __str__ (line 2091) | def __str__(self):
method exists (line 2094) | def exists(self):
method click (line 2099) | def click(self):
method click_exists (line 2104) | def click_exists(self):
method screenshot (line 2109) | def screenshot(self, quality=100):
method take_screenshot (line 2115) | def take_screenshot(self, quality=100):
method info (line 2120) | def info(self):
class OcrEngine (line 2128) | class OcrEngine(object):
method __init__ (line 2129) | def __init__(self, service, *args,
method init_paddleocr (line 2137) | def init_paddleocr(self, *args, **kwargs):
method init_easyocr (line 2141) | def init_easyocr(self, *args, **kwargs):
method init_custom (line 2145) | def init_custom(self, service, *args, **kwargs):
method ocr_custom (line 2148) | def ocr_custom(self, image):
method ocr_paddleocr (line 2151) | def ocr_paddleocr(self, image):
method ocr_easyocr (line 2157) | def ocr_easyocr(self, image):
method ocr (line 2160) | def ocr(self, screenshot):
method format (line 2165) | def format(self, box, text, confidence):
class Device (line 2176) | class Device(object):
method __init__ (line 2177) | def __init__(self, host, port=65000,
method frida (line 2223) | def frida(self):
method __str__ (line 2246) | def __str__(self):
method _parse_certdata (line 2249) | def _parse_certdata(self, data):
method _parse_cname (line 2255) | def _parse_cname(self, crt):
method _get_service_stub (line 2259) | def _get_service_stub(self, module):
method stub (line 2262) | def stub(self, module):
method download_fd (line 2270) | def download_fd(self, fpath, fd):
method upload_fd (line 2272) | def upload_fd(self, fd, dest):
method download_file (line 2274) | def download_file(self, fpath, dest):
method upload_file (line 2276) | def upload_file(self, fpath, dest):
method delete_file (line 2278) | def delete_file(self, fpath):
method file_chmod (line 2280) | def file_chmod(self, fpath, mode=0o644):
method file_stat (line 2282) | def file_stat(self, fpath):
method install_local_file (line 2285) | def install_local_file(self, rpath, user=0):
method current_application (line 2287) | def current_application(self):
method enumerate_all_pkg_names (line 2289) | def enumerate_all_pkg_names(self):
method enumerate_running_processes (line 2291) | def enumerate_running_processes(self):
method get_last_activities (line 2293) | def get_last_activities(self, count=3):
method start_activity (line 2295) | def start_activity(self, **activity):
method get_application_by_name (line 2297) | def get_application_by_name(self, name):
method application (line 2299) | def application(self, applicationId, user=0):
method record_touch (line 2302) | def record_touch(self):
method perform_touch (line 2304) | def perform_touch(self, sequence, wait=True):
method show_toast (line 2306) | def show_toast(self, text, duration=ToastDuration.TD_SHORT):
method is_ca_certificate_installed (line 2308) | def is_ca_certificate_installed(self, certdata):
method uninstall_ca_certificate (line 2310) | def uninstall_ca_certificate(self, certfile):
method install_ca_certificate (line 2312) | def install_ca_certificate(self, certfile):
method reboot (line 2314) | def reboot(self):
method shutdown (line 2316) | def shutdown(self):
method exit (line 2318) | def exit(self):
method reload (line 2320) | def reload(self, clean=False):
method beep (line 2322) | def beep(self):
method play_audio (line 2324) | def play_audio(self, file, type=AudioStreamType.AST_SYSTEM,
method setprop (line 2328) | def setprop(self, name, value):
method getprop (line 2330) | def getprop(self, name):
method hex_patch (line 2332) | def hex_patch(self, pattern, replacement, path,
method install_adb_pubkey (line 2338) | def install_adb_pubkey(self, pubkey):
method uninstall_adb_pubkey (line 2340) | def uninstall_adb_pubkey(self, pubkey):
method start_android_debug_bridge (line 2342) | def start_android_debug_bridge(self):
method is_android_debug_bridge_running (line 2344) | def is_android_debug_bridge_running(self):
method stop_android_debug_bridge (line 2346) | def stop_android_debug_bridge(self):
method is_openvpn_running (line 2349) | def is_openvpn_running(self):
method is_gproxy_running (line 2351) | def is_gproxy_running(self):
method start_openvpn (line 2353) | def start_openvpn(self, profile):
method start_gproxy (line 2355) | def start_gproxy(self, profile):
method stop_openvpn (line 2357) | def stop_openvpn(self):
method stop_gproxy (line 2359) | def stop_gproxy(self):
method execute_script (line 2362) | def execute_script(self, script, alias=None, timeout=60):
method execute_background_script (line 2365) | def execute_background_script(self, script, alias=None):
method is_background_script_finished (line 2367) | def is_background_script_finished(self, tid):
method kill_background_script (line 2369) | def kill_background_script(self, tid):
method click (line 2372) | def click(self, point):
method drag (line 2374) | def drag(self, A, B, step=32):
method swipe (line 2376) | def swipe(self, A, B, step=32):
method swipe_points (line 2378) | def swipe_points(self, *points, step=32):
method open_notification (line 2380) | def open_notification(self):
method open_quick_settings (line 2382) | def open_quick_settings(self):
method wake_up (line 2384) | def wake_up(self):
method sleep (line 2386) | def sleep(self):
method is_screen_on (line 2388) | def is_screen_on(self):
method is_screen_locked (line 2390) | def is_screen_locked(self):
method set_clipboard (line 2392) | def set_clipboard(self, text):
method get_clipboard (line 2394) | def get_clipboard(self):
method freeze_rotation (line 2396) | def freeze_rotation(self, freeze=True):
method set_orientation (line 2398) | def set_orientation(self, orien=Orientation.ORIEN_NATURE):
method press_key (line 2400) | def press_key(self, key):
method press_keycode (line 2402) | def press_keycode(self, code, meta=0):
method take_screenshot (line 2404) | def take_screenshot(self, quality=100, bound=None):
method screenshot (line 2406) | def screenshot(self, quality=100, bound=None):
method dump_window_hierarchy (line 2408) | def dump_window_hierarchy(self, compressed=False):
method wait_for_idle (line 2410) | def wait_for_idle(self, timeout):
method get_last_toast (line 2412) | def get_last_toast(self):
method find_similar_image (line 2414) | def find_similar_image(self, data, threshold=0.0, distance=250,
method remove_all_watchers (line 2421) | def remove_all_watchers(self):
method set_watcher_loop_enabled (line 2423) | def set_watcher_loop_enabled(self, enabled):
method get_watcher_loop_enabled (line 2425) | def get_watcher_loop_enabled(self):
method get_watcher_triggered_count (line 2427) | def get_watcher_triggered_count(self, name):
method reset_watcher_triggered_count (line 2429) | def reset_watcher_triggered_count(self, name):
method get_applied_watchers (line 2431) | def get_applied_watchers(self):
method register_click_target_selector_watcher (line 2433) | def register_click_target_selector_watcher(self, name, conditions,
method register_press_key_watcher (line 2438) | def register_press_key_watcher(self, name, conditions, key):
method register_none_op_watcher (line 2442) | def register_none_op_watcher(self, name, conditions):
method set_watcher_enabled (line 2446) | def set_watcher_enabled(self, name, enable):
method get_watcher_enabled (line 2448) | def get_watcher_enabled(self, name):
method remove_watcher (line 2450) | def remove_watcher(self, name):
method device_info (line 2452) | def device_info(self):
method server_info (line 2454) | def server_info(self):
method __call__ (line 2456) | def __call__(self, **kwargs):
method ocr (line 2459) | def ocr(self, index=0, **kwargs):
method setup_ocr_backend (line 2473) | def setup_ocr_backend(self, service, *args, quality=75,
method set_debug_log_enabled (line 2479) | def set_debug_log_enabled(self, enable):
method _get_session_token (line 2484) | def _get_session_token(self):
method _acquire_lock (line 2486) | def _acquire_lock(self, leaseTime=60):
method _refresh_lock (line 2488) | def _refresh_lock(self, leaseTime=60):
method _release_lock (line 2490) | def _release_lock(self):
method __enter__ (line 2492) | def __enter__(self):
method __exit__ (line 2495) | def __exit__(self, type, value, traceback):
FILE: lamda/exceptions.py
class CompatibilityException (line 5) | class CompatibilityException(Exception):
class DeadSystemException (line 7) | class DeadSystemException(Exception):
class DeviceUnavailable (line 9) | class DeviceUnavailable(Exception):
class DuplicateEntryError (line 11) | class DuplicateEntryError(Exception):
class IllegalArgumentException (line 13) | class IllegalArgumentException(Exception):
class IllegalStateException (line 15) | class IllegalStateException(Exception):
class InstallPackageFailed (line 17) | class InstallPackageFailed(Exception):
class InternalRpcException (line 19) | class InternalRpcException(Exception):
class InvalidAndroidPackage (line 21) | class InvalidAndroidPackage(Exception):
class InvalidArgumentError (line 23) | class InvalidArgumentError(Exception):
class InvalidOperationError (line 25) | class InvalidOperationError(Exception):
class InvalidRootCertificate (line 27) | class InvalidRootCertificate(Exception):
class MethodNotFoundException (line 29) | class MethodNotFoundException(Exception):
class NameNotFoundException (line 31) | class NameNotFoundException(Exception):
class NotImplementedException (line 33) | class NotImplementedException(Exception):
class NullPointerException (line 35) | class NullPointerException(Exception):
class SecurityException (line 37) | class SecurityException(Exception):
class ServiceUnavailable (line 39) | class ServiceUnavailable(Exception):
class StaleObjectException (line 41) | class StaleObjectException(Exception):
class StartupActivityNotFound (line 43) | class StartupActivityNotFound(Exception):
class StorageOutOfMemory (line 45) | class StorageOutOfMemory(Exception):
class UiAutomatorException (line 47) | class UiAutomatorException(Exception):
class UiObjectNotFoundException (line 49) | class UiObjectNotFoundException(Exception):
class UnHandledException (line 51) | class UnHandledException(Exception):
FILE: lamda/types.py
class AttributeDict (line 11) | class AttributeDict(dict):
method __getattr__ (line 12) | def __getattr__(self, attr):
method __setattr__ (line 14) | def __setattr__(self, attr, value):
method remove (line 16) | def remove(self, key):
class BytesIO (line 20) | class BytesIO(io.BytesIO):
method decode_from (line 22) | def decode_from(cls, data, encoding):
method encode (line 24) | def encode(self, encoding):
method decode (line 26) | def decode(self, encoding):
method save (line 28) | def save(self, fpath):
method load (line 32) | def load(cls, fpath):
FILE: tools/discover.py
function BcastCallMethod (line 19) | def BcastCallMethod(method):
FILE: tools/frida_script_generate.py
function embed_bridge (line 12) | def embed_bridge(source, lang):
FILE: tools/globalmitm/DNS2SOCKS.c
type SOCKET (line 33) | typedef int SOCKET;
type sockaddr_storage (line 89) | struct sockaddr_storage
type SEntry (line 94) | struct SEntry
type SEntry (line 104) | struct SEntry
type sockaddr_storage (line 107) | struct sockaddr_storage
type sockaddr_storage (line 108) | struct sockaddr_storage
function FreeSysError (line 153) | static void FreeSysError(char* szString)
function OutputToLog (line 158) | static void OutputToLog(unsigned int uOutputSettingBits, const char* szF...
function OpenLogFile (line 209) | static void OpenLogFile(const char* szFilePath, int bAppend)
function OpenConsole (line 236) | static void OpenConsole()
function OutputFatal (line 250) | static void OutputFatal(const char* szFormatString, ...)
function ThreadCreate (line 278) | int ThreadCreate(LPTHREAD_START_ROUTINE pThreadFunction, void* pParam)
function OutputToLog (line 304) | static void OutputToLog(unsigned int uOutputSettingBits, const char* szF...
function OpenLogFile (line 357) | static void OpenLogFile(const char* szFilePath, int bAppend)
function OpenConsole (line 376) | static void OpenConsole()
function ThreadCreate (line 391) | int ThreadCreate(void* (*pThreadFunction)(void*), void* pParam)
function GetAddrLen (line 409) | static unsigned int GetAddrLen(const struct sockaddr_storage* psAddr)
function SendAnswer (line 417) | static void SendAnswer(struct SEntry* psEntry)
function RemoveEntry (line 433) | static void RemoveEntry(struct SEntry* psEntry, SOCKET hSock, int bUseCr...
function InvalidDnsMsgErrorOutput (line 467) | static int InvalidDnsMsgErrorOutput(int32_t i32TimeOffset)
function IterateDnsMessage (line 479) | static int IterateDnsMessage(uint16_t* u16aMessage, int32_t i32TimeOffset)
function CalculateTimeToLive (line 572) | static int CalculateTimeToLive(struct SEntry* psEntry)
function ReceiveBytes (line 590) | static int ReceiveBytes(SOCKET hSock, unsigned int uAmount, uint16_t* u1...
function AddEcsOption (line 620) | static int AddEcsOption(uint16_t* u16aRequest, uint16_t* u16aDestBuf)
function HandleHttpProxy (line 716) | static int HandleHttpProxy(SOCKET hSock, char* caBuf)
type SEntry (line 793) | struct SEntry
type SEntry (line 793) | struct SEntry
type sockaddr (line 809) | struct sockaddr
type sockaddr_in6 (line 891) | struct sockaddr_in6
type sockaddr_in6 (line 893) | struct sockaddr_in6
type sockaddr_in6 (line 894) | struct sockaddr_in6
type sockaddr_in (line 899) | struct sockaddr_in
type sockaddr_in (line 900) | struct sockaddr_in
type sockaddr_in6 (line 905) | struct sockaddr_in6
type sockaddr_in6 (line 906) | struct sockaddr_in6
function HandleDnsRequest (line 1061) | static void HandleDnsRequest(uint16_t* u16aRequest, int iLen, void* pCli...
function OutputBindError (line 1161) | static void OutputBindError(SOCKET hSock, struct sockaddr_storage* psAdd...
type sockaddr_storage (line 1195) | struct sockaddr_storage
type sockaddr_storage (line 1198) | struct sockaddr_storage
type sockaddr (line 1207) | struct sockaddr
type sockaddr_storage (line 1207) | struct sockaddr_storage
type sockaddr_storage (line 1209) | struct sockaddr_storage
type sockaddr (line 1224) | struct sockaddr
function ParseEcs (line 1262) | static int ParseEcs(char* szIpAndAmountBits)
function ParseIpAndPort (line 1338) | static int ParseIpAndPort(int iFlag, const char* szParamName, const char...
function CreateHttpProxyConnectCommand (line 1423) | static int CreateHttpProxyConnectCommand()
function main (line 1460) | int main(int iArgCount, char** szaArgs)
function WinMain (line 1796) | int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR s...
FILE: tools/paddle_ocr_http_api.py
function ocr (line 14) | async def ocr(request: Request):
FILE: tools/startmitm.py
function cleanup (line 37) | def cleanup(*args, **kwargs):
function add_server (line 49) | def add_server(command, spec):
function add_upstream (line 54) | def add_upstream(args, ext):
function log (line 65) | def log(*args):
function die (line 69) | def die(*args):
function adb (line 74) | def adb(*args):
function adb_tcp (line 84) | def adb_tcp(action, aport, bport):
function reverse (line 90) | def reverse(aport, bport):
function forward (line 94) | def forward(aport, bport):
function get_default_interface_ip_imp (line 98) | def get_default_interface_ip_imp(target):
function get_default_interface_ip (line 104) | def get_default_interface_ip(target):
Condensed preview — 96 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (498K chars).
[
{
"path": ".gitignore",
"chars": 3071,
"preview": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packagi"
},
{
"path": "CHANGELOG.txt",
"chars": 14958,
"preview": "CHANGELOG\n\nVersion 9.20\n-----------\n\n* TOP Bridge now uniformly uses SAPI requests.\n* MQTT connection adds max_inflight_"
},
{
"path": "DISCLAIMER.TXT",
"chars": 5307,
"preview": "DISCLAIMER\r\n\r\nTo use this service, you (hereinafter referred to as \"the user\") must agree to all terms of this agreement"
},
{
"path": "LICENSE",
"chars": 1062,
"preview": "Copyright (c) 2021 - present, firerpa\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of t"
},
{
"path": "README.md",
"chars": 3286,
"preview": "# **FIRERPA Android** | AI-Powered Automation\n\n<img src=\"image/logo.svg\" alt=\"FIRERPA\" width=\"200\" align=\"right\" />\n\n<p>"
},
{
"path": "SECURITY.md",
"chars": 201,
"preview": "# Security Policy\n\n## Supported Versions\n\n| Version | Supported |\n| ------- | ------------------ |\n| 9.x | "
},
{
"path": "all-llms.txt",
"chars": 52562,
"preview": "FIRERPA (FIRERPAλ) - COMPLETE DOCUMENTATION\n\n========================================\nTABLE OF CONTENTS\n================"
},
{
"path": "examples/README.md",
"chars": 147,
"preview": "We’re not targeting any specific application; we’re just using it as a convenient example for the demo.\n\nAPI Document: h"
},
{
"path": "examples/activity_jump.py",
"chars": 685,
"preview": "# Copyright 2025 rev1si0n (lamda.devel@gmail.com). All rights reserved.\n#encoding=utf-8\nfrom lamda.client import *\nimpor"
},
{
"path": "examples/search_in_taobao.py",
"chars": 1091,
"preview": "# Copyright 2025 rev1si0n (lamda.devel@gmail.com). All rights reserved.\n#encoding=utf-8\nfrom lamda.client import *\nimpor"
},
{
"path": "extensions/README.md",
"chars": 42,
"preview": "API Document: https://device-farm.com/doc/"
},
{
"path": "extensions/example_http_extension.py",
"chars": 1947,
"preview": "# Copyright 2025 rev1si0n (lamda.devel@gmail.com). All rights reserved.\n#\n# Distributed under MIT license.\n# See file LI"
},
{
"path": "extensions/example_mcp_extension.py",
"chars": 1375,
"preview": "# Copyright 2025 rev1si0n (lamda.devel@gmail.com). All rights reserved.\n#\n# Distributed under MIT license.\n# See file LI"
},
{
"path": "extensions/firerpa.py",
"chars": 12739,
"preview": "# Copyright 2025 rev1si0n (lamda.devel@gmail.com). All rights reserved.\n#\n# Distributed under MIT license.\n# See file LI"
},
{
"path": "extensions/mcp_return_types.py",
"chars": 3698,
"preview": "#!/usr/bin/env python3.9\n# Copyright 2025 rev1si0n (lamda.devel@gmail.com). All rights reserved.\n#\n# Distributed under M"
},
{
"path": "extensions/mcp_sms_reader.py",
"chars": 1398,
"preview": "# Copyright 2025 rev1si0n (lamda.devel@gmail.com). All rights reserved.\n#\n# Distributed under MIT license.\n# See file LI"
},
{
"path": "lamda/__init__.py",
"chars": 205,
"preview": "# Copyright 2022 rev1si0n (lamda.devel@gmail.com). All rights reserved.\n#\n# Distributed under MIT license.\n# See file LI"
},
{
"path": "lamda/client.py",
"chars": 84225,
"preview": "# Copyright 2022 rev1si0n (https://github.com/rev1si0n). All rights reserved.\n#\n# Distributed under MIT license.\n# See f"
},
{
"path": "lamda/const.py",
"chars": 3167,
"preview": "# Copyright 2022 rev1si0n (https://github.com/rev1si0n). All rights reserved.\n#\n# Distributed under MIT license.\n# See f"
},
{
"path": "lamda/exceptions.py",
"chars": 1665,
"preview": "# Copyright 2022 rev1si0n (https://github.com/rev1si0n). All rights reserved.\n#\n# Distributed under MIT license.\n# See f"
},
{
"path": "lamda/google/protobuf/any.proto",
"chars": 5916,
"preview": "// Protocol Buffers - Google's data interchange format\n// Copyright 2008 Google Inc. All rights reserved.\n// https://de"
},
{
"path": "lamda/google/protobuf/api.proto",
"chars": 7734,
"preview": "// Protocol Buffers - Google's data interchange format\n// Copyright 2008 Google Inc. All rights reserved.\n// https://de"
},
{
"path": "lamda/google/protobuf/compiler/plugin.proto",
"chars": 8754,
"preview": "// Protocol Buffers - Google's data interchange format\n// Copyright 2008 Google Inc. All rights reserved.\n// https://de"
},
{
"path": "lamda/google/protobuf/descriptor.proto",
"chars": 37969,
"preview": "// Protocol Buffers - Google's data interchange format\n// Copyright 2008 Google Inc. All rights reserved.\n// https://de"
},
{
"path": "lamda/google/protobuf/duration.proto",
"chars": 4895,
"preview": "// Protocol Buffers - Google's data interchange format\n// Copyright 2008 Google Inc. All rights reserved.\n// https://de"
},
{
"path": "lamda/google/protobuf/empty.proto",
"chars": 2429,
"preview": "// Protocol Buffers - Google's data interchange format\n// Copyright 2008 Google Inc. All rights reserved.\n// https://de"
},
{
"path": "lamda/google/protobuf/field_mask.proto",
"chars": 8185,
"preview": "// Protocol Buffers - Google's data interchange format\n// Copyright 2008 Google Inc. All rights reserved.\n// https://de"
},
{
"path": "lamda/google/protobuf/source_context.proto",
"chars": 2341,
"preview": "// Protocol Buffers - Google's data interchange format\n// Copyright 2008 Google Inc. All rights reserved.\n// https://de"
},
{
"path": "lamda/google/protobuf/struct.proto",
"chars": 3778,
"preview": "// Protocol Buffers - Google's data interchange format\n// Copyright 2008 Google Inc. All rights reserved.\n// https://de"
},
{
"path": "lamda/google/protobuf/timestamp.proto",
"chars": 6459,
"preview": "// Protocol Buffers - Google's data interchange format\n// Copyright 2008 Google Inc. All rights reserved.\n// https://de"
},
{
"path": "lamda/google/protobuf/type.proto",
"chars": 6126,
"preview": "// Protocol Buffers - Google's data interchange format\n// Copyright 2008 Google Inc. All rights reserved.\n// https://de"
},
{
"path": "lamda/google/protobuf/wrappers.proto",
"chars": 4042,
"preview": "// Protocol Buffers - Google's data interchange format\n// Copyright 2008 Google Inc. All rights reserved.\n// https://de"
},
{
"path": "lamda/rpc/application.proto",
"chars": 3202,
"preview": "// Copyright 2022 rev1si0n (lamda.devel@gmail.com). All rights reserved.\n//\n// Distributed under MIT license.\n// See fil"
},
{
"path": "lamda/rpc/debug.proto",
"chars": 327,
"preview": "// Copyright 2022 rev1si0n (lamda.devel@gmail.com). All rights reserved.\n//\n// Distributed under MIT license.\n// See fil"
},
{
"path": "lamda/rpc/file.proto",
"chars": 710,
"preview": "// Copyright 2022 rev1si0n (lamda.devel@gmail.com). All rights reserved.\n//\n// Distributed under MIT license.\n// See fil"
},
{
"path": "lamda/rpc/policy.proto",
"chars": 391,
"preview": "// Copyright 2022 rev1si0n (lamda.devel@gmail.com). All rights reserved.\n//\n// Distributed under MIT license.\n// See fil"
},
{
"path": "lamda/rpc/proxy.proto",
"chars": 3341,
"preview": "// Copyright 2022 rev1si0n (lamda.devel@gmail.com). All rights reserved.\n//\n// Distributed under MIT license.\n// See fil"
},
{
"path": "lamda/rpc/services.proto",
"chars": 12251,
"preview": "// Copyright 2022 rev1si0n (lamda.devel@gmail.com). All rights reserved.\n//\n// Distributed under MIT license.\n// See fil"
},
{
"path": "lamda/rpc/settings.proto",
"chars": 478,
"preview": "// Copyright 2022 rev1si0n (lamda.devel@gmail.com). All rights reserved.\n//\n// Distributed under MIT license.\n// See fil"
},
{
"path": "lamda/rpc/shell.proto",
"chars": 609,
"preview": "// Copyright 2022 rev1si0n (lamda.devel@gmail.com). All rights reserved.\n//\n// Distributed under MIT license.\n// See fil"
},
{
"path": "lamda/rpc/status.proto",
"chars": 2046,
"preview": "// Copyright 2022 rev1si0n (lamda.devel@gmail.com). All rights reserved.\n//\n// Distributed under MIT license.\n// See fil"
},
{
"path": "lamda/rpc/storage.proto",
"chars": 393,
"preview": "// Copyright 2022 rev1si0n (lamda.devel@gmail.com). All rights reserved.\n//\n// Distributed under MIT license.\n// See fil"
},
{
"path": "lamda/rpc/types.proto",
"chars": 434,
"preview": "// Copyright 2022 rev1si0n (lamda.devel@gmail.com). All rights reserved.\n//\n// Distributed under MIT license.\n// See fil"
},
{
"path": "lamda/rpc/uiautomator.proto",
"chars": 19642,
"preview": "// Copyright 2022 rev1si0n (lamda.devel@gmail.com). All rights reserved.\n//\n// Distributed under MIT license.\n// See fil"
},
{
"path": "lamda/rpc/util.proto",
"chars": 2375,
"preview": "// Copyright 2022 rev1si0n (lamda.devel@gmail.com). All rights reserved.\n//\n// Distributed under MIT license.\n// See fil"
},
{
"path": "lamda/rpc/wifi.proto",
"chars": 1935,
"preview": "// Copyright 2022 rev1si0n (lamda.devel@gmail.com). All rights reserved.\n//\n// Distributed under MIT license.\n// See fil"
},
{
"path": "lamda/types.py",
"chars": 1007,
"preview": "# Copyright 2022 rev1si0n (lamda.devel@gmail.com). All rights reserved.\n#\n# Distributed under MIT license.\n# See file LI"
},
{
"path": "properties.local.example",
"chars": 11686,
"preview": "# properties.local configuration file\n#\n# DO NOT DIRECTLY COPY THIS FILE, PLEASE SELECT THE NECESSARY CONFIGURATION LINE"
},
{
"path": "scripts/disable_flag_secure.yaml",
"chars": 804,
"preview": "# EXAMPLE EXTENSION OF FIRERPA (https://github.com/firerpa/lamda)\n# This script is used to disable the android FLAG_SECU"
},
{
"path": "scripts/disable_ssl_pinning_simple.yaml",
"chars": 659,
"preview": "# EXAMPLE EXTENSION OF FIRERPA (https://github.com/firerpa/lamda)\n# This script is used to disable simple ssl pinning (c"
},
{
"path": "setup.py",
"chars": 1649,
"preview": "#!/usr/bin/python3\n# Copyright 2022 rev1si0n (lamda.devel@gmail.com). All rights reserved.\nimport setuptools\n\nexec(open("
},
{
"path": "tools/README.md",
"chars": 38,
"preview": "Document: https://device-farm.com/doc/"
},
{
"path": "tools/adb_pubkey.py",
"chars": 790,
"preview": "#!/usr/bin/env python3\n#encoding=utf-8\nimport os\nimport argparse\n\nfrom os.path import isfile\nfrom lamda.client import *\n"
},
{
"path": "tools/cert.py",
"chars": 3737,
"preview": "#!/usr/bin/env python3\nimport os\nimport sys\nimport random\nimport datetime\n\nfrom hashlib import sha256\nfrom cryptography."
},
{
"path": "tools/debugimage.py",
"chars": 1873,
"preview": "#!/usr/bin/env python3\nimport os\nimport argparse\n\nfrom PIL import Image, ImageDraw\nfrom lamda.client import *\n\ncert = os"
},
{
"path": "tools/discover.py",
"chars": 1590,
"preview": "#!/usr/bin/env python3\nimport os\nimport struct\nfrom socket import *\n\nfrom lamda import __version__\nfrom lamda.client imp"
},
{
"path": "tools/firerpa.yml",
"chars": 796,
"preview": "version: \"3\"\n# docker run -d --name firerpa --privileged -v ~/firerpa:/user -e LICENSE= -e DOMAIN=firerpa.local -e WEB_P"
},
{
"path": "tools/frida_script_generate.py",
"chars": 963,
"preview": "#!/usr/bin/env python3\n# Copyright 2025 rev1si0n (lamda.devel@gmail.com). All rights reserved.\n#\n# Distributed under MIT"
},
{
"path": "tools/fridarpc.py",
"chars": 1284,
"preview": "#!/usr/bin/env python3\nif __name__ == \"__main__\":\n import os\n import time\n import argparse\n from lamda.clien"
},
{
"path": "tools/globalmitm/DNS2SOCKS.c",
"chars": 57333,
"preview": "// \"stdafx.h\"\n#ifndef _STDAFX_H\n#define _STDAFX_H\n\n#ifdef _WIN32\n\n//Windows specific includes\n#define WINVER 0x501\n#defi"
},
{
"path": "tools/globalmitm/Dockerfile",
"chars": 833,
"preview": "FROM debian:bullseye-slim\n\nLABEL maintainer=\"rev1si0n <lamda.devel@gmail.com>\"\n\nENV VERSION=2.11.4\nENV PLAT=linux-386\n\nE"
},
{
"path": "tools/globalmitm/entry",
"chars": 1093,
"preview": "#!/bin/bash\nset -e\nexport GRPC_DNS_RESOLVER=native\nexport PROXYPORT=${PROXYPORT:-8118}\n\ndie () {\n echo $@; exit 1"
},
{
"path": "tools/id_rsa",
"chars": 1674,
"preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEA4QHmY32OT+F+maERMn1cvBRuIOIXH9yOALG+GMCngtjRJzSR\nn09dInmXE+PjiAqNRWvknVE"
},
{
"path": "tools/ida.py",
"chars": 2186,
"preview": "#!/usr/bin/env python3\n#encoding=utf-8\nimport os\nimport sys\nimport time\nimport argparse\nimport subprocess\n\nfrom shlex im"
},
{
"path": "tools/magisk/META-INF/com/google/android/update-binary",
"chars": 264,
"preview": "#!/sbin/sh\nsource /data/adb/magisk/util_functions.sh\n\nif [ ${MAGISK_VER_CODE} -lt 20400 ]; then\nabort \"Please install Ma"
},
{
"path": "tools/magisk/META-INF/com/google/android/updater-script",
"chars": 8,
"preview": "#MAGISK\n"
},
{
"path": "tools/magisk/common/adb_keys",
"chars": 0,
"preview": ""
},
{
"path": "tools/magisk/common/properties.local",
"chars": 17,
"preview": "brandname=FIRERPA"
},
{
"path": "tools/magisk/common/server/.keep",
"chars": 0,
"preview": ""
},
{
"path": "tools/magisk/common/service.sh",
"chars": 247,
"preview": "#!/system/bin/sh\nbase=${0%/*}\ncert=/data/usr/lamda.pem\nlaunch=\"sh ${base}/server/bin/launch.sh\"\nport=65000\n\nsleep 25\nexp"
},
{
"path": "tools/magisk/install.sh",
"chars": 1171,
"preview": "#!/system/bin/sh\nABI=$(getprop ro.product.cpu.abi)\nSERVER=$TMPDIR/lamda-server-$ABI.tar.gz\nBB=\"/data/adb/magisk/busybox\""
},
{
"path": "tools/magisk/module.prop",
"chars": 138,
"preview": "id=lamda\nname=LAMDA\nversion=VERSION\nversionCode=VERSIONCODE\nauthor=rev1si0n\ndescription=Android reverse engineering & au"
},
{
"path": "tools/magisk/uninstall.sh",
"chars": 97,
"preview": "#!/system/bin/sh\nMODPATH=${0%/*}\n\necho \"/data/usr will not be removed, please remove it manually\""
},
{
"path": "tools/objection-1.11.0-command-patch.diff",
"chars": 2221,
"preview": "diff --git a/console/cli.py b/console/cli.py\nindex 1fc22fb..80e52dc 100644\n--- a/console/cli.py\n+++ b/console/cli.py\n@@ "
},
{
"path": "tools/openvpn/Dockerfile",
"chars": 623,
"preview": "FROM alpine:3.15\n\nLABEL maintainer=\"rev1si0n <lamda.devel@gmail.com>\"\n\nENV VARS=/etc/openvpn/easy-rsa/vars\nENV OVPNCONFI"
},
{
"path": "tools/openvpn/config.ovpn",
"chars": 871,
"preview": "# openvpn Docker image config\n# https://github.com/rev1si0n/lamda/tree/master/tools/openvpn\n#\ntopology subnet\nserver 172"
},
{
"path": "tools/openvpn/entry",
"chars": 101,
"preview": "#!/bin/bash\nif [ -f \"${OVPNCONFIG}\" ] && [ -f \"${VARS}\" ]; then\nexec openvpn --config config.ovpn\nfi\n"
},
{
"path": "tools/openvpn/ovpn-client-new",
"chars": 220,
"preview": "#!/bin/bash\nTC_CLIENT=/etc/openvpn/tls-crypt-v2-client/$1\nTC_SERVER=/etc/openvpn/tls-crypt-v2-server.key\nopenvpn --genke"
},
{
"path": "tools/openvpn/ovpn-client-profile",
"chars": 3110,
"preview": "#!/bin/bash\nexport CLIENT=$2\n\nif [ -f \"easy-rsa/pki/issued/${CLIENT}.crt\" ]; then\n\nCRYPT_cfg_n=\"$(grep -Eo '^tls-(auth|c"
},
{
"path": "tools/openvpn/ovpn-client-renew",
"chars": 30,
"preview": "#!/bin/bash\neasyrsa renew \"$1\""
},
{
"path": "tools/openvpn/ovpn-client-revoke",
"chars": 47,
"preview": "#!/bin/bash\neasyrsa revoke \"$1\"\neasyrsa gen-crl"
},
{
"path": "tools/openvpn/ovpn-server-new",
"chars": 475,
"preview": "#!/bin/bash\nmkdir -p /etc/openvpn/ccd\nmkdir -p /etc/openvpn/easy-rsa\n\nif [ ! -f ${OVPNCONFIG} ]; then\ncp /root/config.ov"
},
{
"path": "tools/openvpn/vars",
"chars": 8492,
"preview": "# Easy-RSA 3 parameter settings\n\n# NOTE: If you installed Easy-RSA from your distro's package manager, don't edit\n# this"
},
{
"path": "tools/paddle_ocr_http_api.py",
"chars": 704,
"preview": "#!/usr/bin/env python3\n# THIS IS EXAMPLE HTTP OCR BACKEND,\n# DO NOT USE IN PRODUCTION\nimport uvicorn\nimport asyncio\n"
},
{
"path": "tools/requirements.txt",
"chars": 72,
"preview": "mitmproxy>=9.0.0,<=10.2.0\ndnspython\nhttpx[socks]<0.28.0\npackaging\nPillow"
},
{
"path": "tools/root.crt",
"chars": 956,
"preview": "-----BEGIN CERTIFICATE-----\nMIIClDCCAXwCAQAwDQYJKoZIhvcNAQELBQAwEDEOMAwGA1UECgwFTEFNREEwHhcN\nMjAwMTAxMDAwMDAxWhcNMjkxMjI"
},
{
"path": "tools/root.key",
"chars": 1704,
"preview": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC56nwWb6xmWqoJ\nOZrAkUaT86f/uOuIwZJ5ZkynrTn"
},
{
"path": "tools/rsync.sh",
"chars": 2318,
"preview": "#!/bin/bash\n[ $# -lt 2 ] && exit 1\nDEFAULT_ID_RSA=$(mktemp)\nPORT=${PORT:-65000}\ncase \"$1\" in\n *':'*)\n "
},
{
"path": "tools/scp.sh",
"chars": 2358,
"preview": "#!/bin/bash\n[ $# -lt 2 ] && exit 1\nDEFAULT_ID_RSA=$(mktemp)\nPORT=${PORT:-65000}\ncase \"$1\" in\n *':'*)\n "
},
{
"path": "tools/socks5/Dockerfile",
"chars": 654,
"preview": "FROM alpine:3.15\n# this will produce large image but IDC\nLABEL maintainer=\"rev1si0n <lamda.devel@gmail.com>\"\n\nENV SOURCE"
},
{
"path": "tools/socks5/entry",
"chars": 1067,
"preview": "#!/bin/bash\nLOGIN=${LOGIN:-lamda}\nPWD=${PASSWORD:-lamda}\nUDPRANGE=${UDPRANGE:-50000-55000}\nBIND=${BIND:-0.0.0.0}\nPORT=${"
},
{
"path": "tools/ssh.sh",
"chars": 2107,
"preview": "#!/bin/bash\nTARGET=${1:-localhost}\nPORT=${PORT:-65000}\nDEFAULT_ID_RSA=$(mktemp)\n\numask 077\nif [ ! -f \"${CERTIFICATE}\" ];"
},
{
"path": "tools/startmitm.py",
"chars": 5963,
"preview": "#!/usr/bin/env python3\n# Copyright 2025 rev1si0n (lamda.devel@gmail.com). All rights reserved.\n#\n# Distributed under MIT"
},
{
"path": "tools/startmitm.spec",
"chars": 815,
"preview": "# -*- mode: python ; coding: utf-8 -*-\nfrom PyInstaller.building.build_main import Analysis\nfrom PyInstaller.building.ap"
},
{
"path": "tools/test-fridarpc.js",
"chars": 1231,
"preview": "Java.perform(function() {\n var String = Java.use(\"java.lang.String\")\n rpc.exports = {\n getM"
},
{
"path": "tools/test.pem",
"chars": 3703,
"preview": "LAMDA SSL CERTIFICATE (CN=test,PASSWD=a1c0e3ea707a54de7a0f95)\n-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAA"
}
]
About this extraction
This page contains the full source code of the firerpa/lamda GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 96 files (460.3 KB), approximately 126.1k tokens, and a symbol index with 626 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.