Full Code of sensepost/objection for AI

master 3c3cde806f15 cached
316 files
696.5 KB
170.0k tokens
807 symbols
1 requests
Download .txt
Showing preview only (769K chars total). Download the full file or copy to clipboard to get everything.
Repository: sensepost/objection
Branch: master
Commit: 3c3cde806f15
Files: 316
Total size: 696.5 KB

Directory structure:
gitextract_j8lfqr1u/

├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   └── workflows/
│       ├── codeql-analysis.yml
│       └── pypi.yml
├── .gitignore
├── .python-version
├── .vscode/
│   └── settings.json
├── CONTRIBUTING.md
├── LICENSE
├── MANIFEST.in
├── Makefile
├── README.md
├── agent/
│   ├── .gitignore
│   ├── README.md
│   ├── package.json
│   ├── src/
│   │   ├── android/
│   │   │   ├── clipboard.ts
│   │   │   ├── filesystem.ts
│   │   │   ├── general.ts
│   │   │   ├── heap.ts
│   │   │   ├── hooking.ts
│   │   │   ├── intent.ts
│   │   │   ├── keystore.ts
│   │   │   ├── lib/
│   │   │   │   ├── intentUtils.ts
│   │   │   │   ├── interfaces.ts
│   │   │   │   ├── libjava.ts
│   │   │   │   └── types.ts
│   │   │   ├── monitor.ts
│   │   │   ├── pinning.ts
│   │   │   ├── proxy.ts
│   │   │   ├── root.ts
│   │   │   ├── shell.ts
│   │   │   └── userinterface.ts
│   │   ├── generic/
│   │   │   ├── custom.ts
│   │   │   ├── environment.ts
│   │   │   ├── http.ts
│   │   │   ├── memory.ts
│   │   │   └── ping.ts
│   │   ├── index.ts
│   │   ├── ios/
│   │   │   ├── binary.ts
│   │   │   ├── binarycookies.ts
│   │   │   ├── bundles.ts
│   │   │   ├── credentialstorage.ts
│   │   │   ├── crypto.ts
│   │   │   ├── filesystem.ts
│   │   │   ├── heap.ts
│   │   │   ├── hooking.ts
│   │   │   ├── jailbreak.ts
│   │   │   ├── keychain.ts
│   │   │   ├── lib/
│   │   │   │   ├── constants.ts
│   │   │   │   ├── helpers.ts
│   │   │   │   ├── interfaces.ts
│   │   │   │   ├── libobjc.ts
│   │   │   │   └── types.ts
│   │   │   ├── nsuserdefaults.ts
│   │   │   ├── pasteboard.ts
│   │   │   ├── pinning.ts
│   │   │   ├── plist.ts
│   │   │   └── userinterface.ts
│   │   ├── lib/
│   │   │   ├── color.ts
│   │   │   ├── constants.ts
│   │   │   ├── helpers.ts
│   │   │   ├── interfaces.ts
│   │   │   └── jobs.ts
│   │   └── rpc/
│   │       ├── android.ts
│   │       ├── environment.ts
│   │       ├── ios.ts
│   │       ├── jobs.ts
│   │       ├── memory.ts
│   │       └── other.ts
│   ├── tsconfig.json
│   └── tslint.json
├── objection/
│   ├── __init__.py
│   ├── api/
│   │   ├── __init__.py
│   │   ├── app.py
│   │   ├── rpc.py
│   │   └── script.py
│   ├── commands/
│   │   ├── __init__.py
│   │   ├── android/
│   │   │   ├── __init__.py
│   │   │   ├── clipboard.py
│   │   │   ├── command.py
│   │   │   ├── general.py
│   │   │   ├── generate.py
│   │   │   ├── heap.py
│   │   │   ├── hooking.py
│   │   │   ├── intents.py
│   │   │   ├── keystore.py
│   │   │   ├── monitor.py
│   │   │   ├── pinning.py
│   │   │   ├── proxy.py
│   │   │   └── root.py
│   │   ├── command_history.py
│   │   ├── custom.py
│   │   ├── device.py
│   │   ├── filemanager.py
│   │   ├── frida_commands.py
│   │   ├── http.py
│   │   ├── ios/
│   │   │   ├── __init__.py
│   │   │   ├── binary.py
│   │   │   ├── bundles.py
│   │   │   ├── cookies.py
│   │   │   ├── generate.py
│   │   │   ├── heap.py
│   │   │   ├── hooking.py
│   │   │   ├── jailbreak.py
│   │   │   ├── keychain.py
│   │   │   ├── monitor.py
│   │   │   ├── nsurlcredentialstorage.py
│   │   │   ├── nsuserdefaults.py
│   │   │   ├── pasteboard.py
│   │   │   ├── pinning.py
│   │   │   └── plist.py
│   │   ├── jobs.py
│   │   ├── memory.py
│   │   ├── mobile_packages.py
│   │   ├── plugin_manager.py
│   │   ├── sqlite.py
│   │   └── ui.py
│   ├── console/
│   │   ├── __init__.py
│   │   ├── cli.py
│   │   ├── commands.py
│   │   ├── completer.py
│   │   ├── helpfiles/
│   │   │   ├── !.txt
│   │   │   ├── android.clipboard.monitor.txt
│   │   │   ├── android.clipboard.txt
│   │   │   ├── android.heap.search.instances.txt
│   │   │   ├── android.hooking.list.activities.txt
│   │   │   ├── android.hooking.list.class_methods.txt
│   │   │   ├── android.hooking.list.classes.txt
│   │   │   ├── android.hooking.list.receivers.txt
│   │   │   ├── android.hooking.list.services.txt
│   │   │   ├── android.hooking.list.txt
│   │   │   ├── android.hooking.search.classes.txt
│   │   │   ├── android.hooking.search.methods.txt
│   │   │   ├── android.hooking.search.txt
│   │   │   ├── android.hooking.set.return_value.txt
│   │   │   ├── android.hooking.txt
│   │   │   ├── android.hooking.watch.class.txt
│   │   │   ├── android.hooking.watch.class_method.txt
│   │   │   ├── android.hooking.watch.txt
│   │   │   ├── android.intent.implicit_intents.txt
│   │   │   ├── android.intent.launch_activity.txt
│   │   │   ├── android.intent.launch_service.txt
│   │   │   ├── android.intent.txt
│   │   │   ├── android.keystore.clear.txt
│   │   │   ├── android.keystore.detail.txt
│   │   │   ├── android.keystore.list.txt
│   │   │   ├── android.keystore.txt
│   │   │   ├── android.keystore.watch.txt
│   │   │   ├── android.root.disable.txt
│   │   │   ├── android.root.simulate.txt
│   │   │   ├── android.shell_exec.txt
│   │   │   ├── android.sslpinning.disable.txt
│   │   │   ├── android.sslpinning.txt
│   │   │   ├── android.txt
│   │   │   ├── android.ui.FLAG_SECURE.txt
│   │   │   ├── android.ui.screenshot.txt
│   │   │   ├── cd.txt
│   │   │   ├── env.txt
│   │   │   ├── exit.txt
│   │   │   ├── file.download.txt
│   │   │   ├── file.txt
│   │   │   ├── file.upload.txt
│   │   │   ├── frida.txt
│   │   │   ├── import.txt
│   │   │   ├── ios.bundles.list_bundles.txt
│   │   │   ├── ios.bundles.list_frameworks.txt
│   │   │   ├── ios.bundles.txt
│   │   │   ├── ios.cookies.get.txt
│   │   │   ├── ios.cookies.txt
│   │   │   ├── ios.hooking.list.class_methods.txt
│   │   │   ├── ios.hooking.list.classes.txt
│   │   │   ├── ios.hooking.list.txt
│   │   │   ├── ios.hooking.search.classes.txt
│   │   │   ├── ios.hooking.search.methods.txt
│   │   │   ├── ios.hooking.search.txt
│   │   │   ├── ios.hooking.set.return_value.txt
│   │   │   ├── ios.hooking.set.txt
│   │   │   ├── ios.hooking.txt
│   │   │   ├── ios.hooking.watch.class.txt
│   │   │   ├── ios.hooking.watch.method.txt
│   │   │   ├── ios.hooking.watch.txt
│   │   │   ├── ios.jailbreak.disable.txt
│   │   │   ├── ios.jailbreak.simulate.txt
│   │   │   ├── ios.jailbreak.txt
│   │   │   ├── ios.keychain.add.txt
│   │   │   ├── ios.keychain.clear.txt
│   │   │   ├── ios.keychain.dump.txt
│   │   │   ├── ios.keychain.txt
│   │   │   ├── ios.monitor.crypto.txt
│   │   │   ├── ios.nsuserdefaults.get.txt
│   │   │   ├── ios.nsuserdefaults.txt
│   │   │   ├── ios.pasteboard.monitor.txt
│   │   │   ├── ios.pasteboard.txt
│   │   │   ├── ios.plist.cat.txt
│   │   │   ├── ios.plist.txt
│   │   │   ├── ios.sslpinning.disable.txt
│   │   │   ├── ios.sslpinning.txt
│   │   │   ├── ios.txt
│   │   │   ├── ios.ui.alert.txt
│   │   │   ├── ios.ui.dump.txt
│   │   │   ├── ios.ui.screenshot.txt
│   │   │   ├── ios.ui.touchid_bypass.txt
│   │   │   ├── ios.ui.txt
│   │   │   ├── jobs.kill.txt
│   │   │   ├── jobs.list.txt
│   │   │   ├── jobs.txt
│   │   │   ├── ls.txt
│   │   │   ├── memory.dump.all.txt
│   │   │   ├── memory.dump.from_base.txt
│   │   │   ├── memory.dump.txt
│   │   │   ├── memory.list.exports.txt
│   │   │   ├── memory.list.modules.txt
│   │   │   ├── memory.list.txt
│   │   │   ├── memory.search.txt
│   │   │   ├── memory.txt
│   │   │   ├── memory.write.txt
│   │   │   ├── plugin.load.txt
│   │   │   ├── plugin.txt
│   │   │   ├── pwd.print.txt
│   │   │   ├── pwd.txt
│   │   │   ├── reconnect.txt
│   │   │   ├── rm.txt
│   │   │   ├── sqlite.connect.txt
│   │   │   ├── sqlite.disconnect.txt
│   │   │   ├── sqlite.execute.query.txt
│   │   │   ├── sqlite.execute.schema.txt
│   │   │   ├── sqlite.execute.txt
│   │   │   ├── sqlite.status.txt
│   │   │   ├── sqlite.sync.txt
│   │   │   ├── sqlite.txt
│   │   │   ├── ui.alert.txt
│   │   │   └── ui.txt
│   │   └── repl.py
│   ├── state/
│   │   ├── __init__.py
│   │   ├── api.py
│   │   ├── app.py
│   │   ├── connection.py
│   │   ├── device.py
│   │   ├── filemanager.py
│   │   └── jobs.py
│   └── utils/
│       ├── __init__.py
│       ├── agent.py
│       ├── assets/
│       │   ├── javahookmanager.js
│       │   ├── network_security_config.xml
│       │   ├── objchookmanager.js
│       │   └── objection.jks
│       ├── helpers.py
│       ├── patchers/
│       │   ├── __init__.py
│       │   ├── android.py
│       │   ├── base.py
│       │   ├── github.py
│       │   └── ios.py
│       ├── plugin.py
│       └── update_checker.py
├── plugins/
│   ├── README.md
│   ├── api/
│   │   ├── __init__.py
│   │   └── index.js
│   ├── flex/
│   │   ├── README.md
│   │   ├── __init__.py
│   │   ├── index.js
│   │   ├── libFlex.h
│   │   └── libFlex.m
│   ├── mettle/
│   │   ├── README.md
│   │   ├── __init__.py
│   │   └── index.js
│   └── stetho/
│       ├── README.md
│       ├── __init__.py
│       └── index.js
├── pyproject.toml
└── tests/
    ├── __init__.py
    ├── commands/
    │   ├── __init__.py
    │   ├── android/
    │   │   ├── __init__.py
    │   │   ├── test_clipboard.py
    │   │   ├── test_command.py
    │   │   ├── test_heap.py
    │   │   ├── test_hooking.py
    │   │   ├── test_intents.py
    │   │   ├── test_keystore.py
    │   │   ├── test_pinning.py
    │   │   └── test_root.py
    │   ├── ios/
    │   │   ├── __init__.py
    │   │   ├── test_bundles.py
    │   │   ├── test_cookies.py
    │   │   ├── test_hooking.py
    │   │   ├── test_jailbreak.py
    │   │   ├── test_keychain.py
    │   │   ├── test_nsurlcredentialstorage.py
    │   │   ├── test_nsuserdefaults.py
    │   │   ├── test_pasteboard.py
    │   │   ├── test_pinning.py
    │   │   └── test_plist.py
    │   ├── test_command_history.py
    │   ├── test_device.py
    │   ├── test_filemanager.py
    │   ├── test_frida_commands.py
    │   ├── test_jobs.py
    │   ├── test_memory.py
    │   ├── test_mobile_packages.py
    │   ├── test_plugin_manager.py
    │   └── test_ui.py
    ├── console/
    │   ├── __init__.py
    │   ├── test_cli.py
    │   ├── test_completer.py
    │   └── test_repl.py
    ├── data/
    │   └── plugin/
    │       └── __init__.py
    ├── helpers.py
    ├── state/
    │   ├── __init__.py
    │   ├── test_app.py
    │   └── test_jobs.py
    └── utils/
        ├── __init__.py
        ├── patchers/
        │   ├── __init__.py
        │   ├── test_android.py
        │   ├── test_base.py
        │   ├── test_github.py
        │   └── test_ios.py
        └── test_helpers.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: "[bug] Bug Description Here"
labels: freshissue
assignees: ''

---

- Please, fill in all of the sections in this template.
- Please read each section carefully. Each has a description of what information will help.
- Windows support is limited. There is a good chance that a pull request would help!
- The more information you give, the better.
- Remove this section when you are done.

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior:
1. Run command '...'
2. Run command '...'

**Similar issues**
Please link the issues in this repository that is similar to yours.

For example: #358, #229 etc.

**Expected behavior**
A clear and concise description of what you expected to happen.

**Evidence / Logs / Screenshots**
Any output from objection, such as stack traces or errors that occurred. Be sure to run objection with the `--debug` flag so that errors from the agent are verbose enough to debug. For example:

```text
objection --debug explore
```

**Environment (please complete the following information):**
 - Device: [e.g. iPhone6]
 - OS: [e.g. iOS8.1]
 - Frida Version [e.g. 22]
- Objection Version [e.g. 1.6.2]

**Application**
If possible, please attach the target application where you can reproduce this bug to the issue.

**Additional context**
Add any other context about the problem here.


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''

---

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context**
Add any other context or screenshots about the feature request here.


================================================
FILE: .github/workflows/codeql-analysis.yml
================================================
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
name: "CodeQL"

on:
  push:
    branches: [master]
  pull_request:
    # The branches below must be a subset of the branches above
    branches: [master]
  schedule:
    - cron: '0 7 * * 6'

jobs:
  analyze:
    name: Analyze
    runs-on: ubuntu-latest

    strategy:
      fail-fast: false
      matrix:
        # Override automatic language detection by changing the below list
        # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python']
        language: ['python', 'javascript']
        # Learn more...
        # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection

    steps:
    - name: Checkout repository
      uses: actions/checkout@v4

    # Initializes the CodeQL tools for scanning.
    - name: Initialize CodeQL
      uses: github/codeql-action/init@v3
      with:
        languages: ${{ matrix.language }}
        # If you wish to specify custom queries, you can do so here or in a config file.
        # By default, queries listed here will override any specified in a config file. 
        # Prefix the list here with "+" to use these queries and those in the config file.
        # queries: ./path/to/local/query, your-org/your-repo/queries@main

    # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
    # If this step fails, then you should remove it and run the build manually (see below)
    - name: Autobuild
      uses: github/codeql-action/autobuild@v3

    # ℹ️ Command-line programs to run using the OS shell.
    # 📚 https://git.io/JvXDl

    # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
    #    and modify them (or add more) to build your code if your project
    #    uses a compiled language

    #- run: |
    #   make bootstrap
    #   make release

    - name: Perform CodeQL Analysis
      uses: github/codeql-action/analyze@v3


================================================
FILE: .github/workflows/pypi.yml
================================================
name: Release to PyPi

on:
  push:
    tags:
      - '*'

jobs:
  pypi:
    name: Publish to PyPI
    runs-on: ubuntu-latest
    environment:
      name: pypi
    permissions:
      id-token: write
      contents: read
    steps:
      - name: Checkout
        uses: actions/checkout@v5
      - name: Ensure node for agent
        uses: actions/setup-node@v4
        with:
          node-version: '24'
          cache: npm
          cache-dependency-path: agent/package-lock.json
      - name: Install uv
        uses: astral-sh/setup-uv@v6
      - name: Set tagged version
        run: |
          VERSION="$GITHUB_REF_NAME"
          uv version "$VERSION"
      - name: Build Agent
        run: cd agent && npm ci
      - name: Build
        run: uv build
      - name: Publish
        run: uv publish


================================================
FILE: .gitignore
================================================
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]

# C extensions
*.so

# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib64/
parts/
sdist/
*.egg-info/
.installed.cfg
*.egg

# 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
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover

# Translations
*.mo
*.pot

# Django stuff:
*.log

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# extra
settings.ini
python/
.idea/
.DS_Store

# Ignore the compiled agent.
objection/agent.js


================================================
FILE: .python-version
================================================
3.14


================================================
FILE: .vscode/settings.json
================================================
{
    "editor.tabSize": 2
}


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to Objection

First off, thanks for taking the time to contribute! 🎉💥

The following are some simple guidelines for contributing to the project. Before you get started though, it is highly recommended that you read the Wiki article entry available [here](https://github.com/sensepost/objection/wiki/Hacking) to get an idea of how the project is put structured and to learn about the various components.

Finally, when submitting your pull request, please try and be as descriptive as possible about what is changing/is fixed. Ideally, including tests greatly helps facilitate this process.

Thanks! 🤘

## Code Structure

Objection consists of two major parts. The Python command line environment and the TypeScript agent. Both of these parts live in this single, monorepo.

- The Python command line lives [here](https://github.com/sensepost/objection/tree/master/objection).
- The TypeScript agent lives [here](https://github.com/sensepost/objection/tree/master/agent).

## Environment Setup

Whether you want to contribute to the TypeScript agent or the Python command line, both components would require some setup.

### Python Command Line

Any Python 3 environment should do, but we recommend you use the latest version of Python. To satisfy all of the dependencies that you may need, install those defined in the [`requirements-dev.txt`](https://github.com/sensepost/objection/blob/master/requirements-dev.txt) file in the project's root. This would make all of the code dependencies available, as well as some useful debugging helpers.

### TypeScript Agent

The objection agent is written using TypeScript 3. It is recommended that you download [Visual Studio Code](https://code.visualstudio.com/) for agent development given the excellent TypeScript support that it has.

For more information on developing for the agent, please see the Wiki article [here](https://github.com/sensepost/objection/wiki/Agent-Development-Environment).


================================================
FILE: LICENSE
================================================
                    GNU GENERAL PUBLIC LICENSE
                       Version 3, 29 June 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The GNU General Public License is a free, copyleft license for
software and other kinds of works.

  The licenses for most software and other practical works are designed
to take away your freedom to share and change the works.  By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.  We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors.  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.

  To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights.  Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received.  You must make sure that they, too, receive
or can get the source code.  And you must show them these terms so they
know their rights.

  Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.

  For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software.  For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.

  Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so.  This is fundamentally incompatible with the aim of
protecting users' freedom to change the software.  The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable.  Therefore, we
have designed this version of the GPL to prohibit the practice for those
products.  If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.

  Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary.  To prevent this, the GPL assures that
patents cannot be used to render the program non-free.

  The precise terms and conditions for copying, distribution and
modification follow.

                       TERMS AND CONDITIONS

  0. Definitions.

  "This License" refers to version 3 of the GNU General Public License.

  "Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.

  "The Program" refers to any copyrightable work licensed under this
License.  Each licensee is addressed as "you".  "Licensees" and
"recipients" may be individuals or organizations.

  To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy.  The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.

  A "covered work" means either the unmodified Program or a work based
on the Program.

  To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy.  Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.

  To "convey" a work means any kind of propagation that enables other
parties to make or receive copies.  Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.

  An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License.  If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.

  1. Source Code.

  The "source code" for a work means the preferred form of the work
for making modifications to it.  "Object code" means any non-source
form of a work.

  A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.

  The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form.  A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.

  The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities.  However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work.  For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.

  The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.

  The Corresponding Source for a work in source code form is that
same work.

  2. Basic Permissions.

  All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met.  This License explicitly affirms your unlimited
permission to run the unmodified Program.  The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work.  This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.

  You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force.  You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright.  Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.

  Conveying under any other circumstances is permitted solely under
the conditions stated below.  Sublicensing is not allowed; section 10
makes it unnecessary.

  3. Protecting Users' Legal Rights From Anti-Circumvention Law.

  No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.

  When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.

  4. Conveying Verbatim Copies.

  You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.

  You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.

  5. Conveying Modified Source Versions.

  You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:

    a) The work must carry prominent notices stating that you modified
    it, and giving a relevant date.

    b) The work must carry prominent notices stating that it is
    released under this License and any conditions added under section
    7.  This requirement modifies the requirement in section 4 to
    "keep intact all notices".

    c) You must license the entire work, as a whole, under this
    License to anyone who comes into possession of a copy.  This
    License will therefore apply, along with any applicable section 7
    additional terms, to the whole of the work, and all its parts,
    regardless of how they are packaged.  This License gives no
    permission to license the work in any other way, but it does not
    invalidate such permission if you have separately received it.

    d) If the work has interactive user interfaces, each must display
    Appropriate Legal Notices; however, if the Program has interactive
    interfaces that do not display Appropriate Legal Notices, your
    work need not make them do so.

  A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit.  Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.

  6. Conveying Non-Source Forms.

  You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:

    a) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by the
    Corresponding Source fixed on a durable physical medium
    customarily used for software interchange.

    b) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by a
    written offer, valid for at least three years and valid for as
    long as you offer spare parts or customer support for that product
    model, to give anyone who possesses the object code either (1) a
    copy of the Corresponding Source for all the software in the
    product that is covered by this License, on a durable physical
    medium customarily used for software interchange, for a price no
    more than your reasonable cost of physically performing this
    conveying of source, or (2) access to copy the
    Corresponding Source from a network server at no charge.

    c) Convey individual copies of the object code with a copy of the
    written offer to provide the Corresponding Source.  This
    alternative is allowed only occasionally and noncommercially, and
    only if you received the object code with such an offer, in accord
    with subsection 6b.

    d) Convey the object code by offering access from a designated
    place (gratis or for a charge), and offer equivalent access to the
    Corresponding Source in the same way through the same place at no
    further charge.  You need not require recipients to copy the
    Corresponding Source along with the object code.  If the place to
    copy the object code is a network server, the Corresponding Source
    may be on a different server (operated by you or a third party)
    that supports equivalent copying facilities, provided you maintain
    clear directions next to the object code saying where to find the
    Corresponding Source.  Regardless of what server hosts the
    Corresponding Source, you remain obligated to ensure that it is
    available for as long as needed to satisfy these requirements.

    e) Convey the object code using peer-to-peer transmission, provided
    you inform other peers where the object code and Corresponding
    Source of the work are being offered to the general public at no
    charge under subsection 6d.

  A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.

  A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling.  In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage.  For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product.  A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.

  "Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source.  The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.

  If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information.  But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).

  The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed.  Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.

  Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.

  7. Additional Terms.

  "Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law.  If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.

  When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it.  (Additional permissions may be written to require their own
removal in certain cases when you modify the work.)  You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.

  Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:

    a) Disclaiming warranty or limiting liability differently from the
    terms of sections 15 and 16 of this License; or

    b) Requiring preservation of specified reasonable legal notices or
    author attributions in that material or in the Appropriate Legal
    Notices displayed by works containing it; or

    c) Prohibiting misrepresentation of the origin of that material, or
    requiring that modified versions of such material be marked in
    reasonable ways as different from the original version; or

    d) Limiting the use for publicity purposes of names of licensors or
    authors of the material; or

    e) Declining to grant rights under trademark law for use of some
    trade names, trademarks, or service marks; or

    f) Requiring indemnification of licensors and authors of that
    material by anyone who conveys the material (or modified versions of
    it) with contractual assumptions of liability to the recipient, for
    any liability that these contractual assumptions directly impose on
    those licensors and authors.

  All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10.  If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term.  If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.

  If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.

  Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.

  8. Termination.

  You may not propagate or modify a covered work except as expressly
provided under this License.  Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).

  However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.

  Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.

  Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License.  If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.

  9. Acceptance Not Required for Having Copies.

  You are not required to accept this License in order to receive or
run a copy of the Program.  Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance.  However,
nothing other than this License grants you permission to propagate or
modify any covered work.  These actions infringe copyright if you do
not accept this License.  Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.

  10. Automatic Licensing of Downstream Recipients.

  Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License.  You are not responsible
for enforcing compliance by third parties with this License.

  An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations.  If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.

  You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License.  For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.

  11. Patents.

  A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based.  The
work thus licensed is called the contributor's "contributor version".

  A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version.  For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.

  Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.

  In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement).  To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.

  If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients.  "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.

  If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.

  A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License.  You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.

  Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.

  12. No Surrender of Others' Freedom.

  If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all.  For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.

  13. Use with the GNU Affero General Public License.

  Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work.  The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.

  14. Revised Versions of this License.

  The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

  Each version is given a distinguishing version number.  If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation.  If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.

  If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.

  Later license versions may give you additional or different
permissions.  However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.

  15. Disclaimer of Warranty.

  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. Limitation of Liability.

  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.

  17. Interpretation of Sections 15 and 16.

  If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

Also add information on how to contact you by electronic and paper mail.

  If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:

    <program>  Copyright (C) <year>  <name of author>
    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".

  You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.

  The GNU General Public License does not permit incorporating your program
into proprietary programs.  If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library.  If this is what you want to do, use the GNU Lesser General
Public License instead of this License.  But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.


================================================
FILE: MANIFEST.in
================================================
include requirements.txt


================================================
FILE: Makefile
================================================
DIST_DIR := dist

default: clean frida-agent sdist

clean:
	$(RM) $(DIST_DIR)/*

frida-agent:
	cd agent && npm run build

sdist:
	uv build

testupload:
	uv publish --index testpypi

upload:
	uv publish


================================================
FILE: README.md
================================================
# 📱objection - Runtime Mobile Exploration

`objection` is a runtime mobile exploration toolkit, powered by [Frida](https://www.frida.re/), built to help you assess the security posture of your mobile applications, without needing a jailbreak.

[![Twitter](https://img.shields.io/badge/twitter-%40leonjza-blue.svg)](https://twitter.com/leonjza)
[![PyPi](https://badge.fury.io/py/objection.svg)](https://pypi.python.org/pypi/objection)
[![Black Hat Arsenal](https://raw.githubusercontent.com/toolswatch/badges/master/arsenal/europe/2017.svg?sanitize=true)](https://www.blackhat.com/eu-17/arsenal-overview.html)
[![Black Hat Arsenal](https://raw.githubusercontent.com/toolswatch/badges/master/arsenal/usa/2019.svg?sanitize=true)](https://www.blackhat.com/us-19/arsenal-overview.html)

<img align="right" src="./images/objection.png" height="220" alt="objection">

- Supports both iOS and Android.
- Inspect and interact with container file systems.
- Bypass SSL pinning.
- Dump keychains.
- Perform memory related tasks, such as dumping & patching.
- Explore and manipulate objects on the heap.
- And much, much [more](https://github.com/sensepost/objection/wiki/Features)...

Screenshots are available in the [wiki](https://github.com/sensepost/objection/wiki/Screenshots).

## installation

Installation is simply a matter of `pip3 install objection`. This will give you the `objection` command. You can update an existing `objection` installation with `pip3 install --upgrade objection`.

For more detailed update and installation instructions, please refer to the wiki page [here](https://github.com/sensepost/objection/wiki/Installation).

## license

`objection` is licensed under a [GNU General Public v3 License](https://www.gnu.org/licenses/gpl-3.0.en.html). Permissions beyond the scope of this license may be available at [http://sensepost.com/contact/](http://sensepost.com/contact/).


================================================
FILE: agent/.gitignore
================================================
node_modules/

================================================
FILE: agent/README.md
================================================
# objection agent

This directory contains the source code for the agent used within `objection`. These sources are compiled and shipped with a distribution of `objection`, and live in `/objection/agent.js` in its compiled state.

For more information, such as development environment setup instructions, please refer to the project wiki [here](https://github.com/sensepost/objection/wiki/Agent-Development-Environment).


================================================
FILE: agent/package.json
================================================
{
  "name": "objection",
  "version": "0.0.0",
  "description": "Runtime Mobile Exploration",
  "private": true,
  "type": "module",
  "main": "src/index.ts",
  "scripts": {
    "prepare": "npm run build",
    "build": "frida-compile src/index.ts -o ../objection/agent.js -T none",
    "watch": "frida-compile src/index.ts -o ../objection/agent.js -w",
    "lint": "tslint -c tslint.json 'src/**/*.ts'"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/sensepost/objection.git"
  },
  "keywords": [
    "frida",
    "runtime",
    "mobile",
    "security",
    "objection"
  ],
  "author": "Leon Jacobs",
  "license": "GPL-3.0",
  "bugs": {
    "url": "https://github.com/sensepost/objection/issues"
  },
  "homepage": "https://github.com/sensepost/objection#readme",
  "dependencies": {
    "frida-fs": "^7.0.0",
    "frida-java-bridge": "^7",
    "frida-objc-bridge": "^8",
    "frida-screenshot": "^6",
    "macho-ts": "^0.1.0"
  },
  "devDependencies": {
    "@types/frida-gum": "^19",
    "@types/node": "^24",
    "frida-compile": "^19",
    "tslint": "^6"
  }
}


================================================
FILE: agent/src/android/clipboard.ts
================================================
import { colors as c } from "../lib/color.js";
import {
  getApplicationContext,
  wrapJavaPerform, 
  Java
} from "./lib/libjava.js";
import { ClipboardManager } from "./lib/types.js";

export const monitor = (): Promise<void> => {
  // -- Sample Java
  //
  // ClipboardManager f = (ClipboardManager)getApplicationContext().getSystemService(CLIPBOARD_SERVICE);
  // ClipData.Item i = f.getPrimaryClip().getItemAt(0);
  // Log.e("t", "?:" + i.getText());

  send(`${c.yellowBright("Warning!")} This module is still broken. A pull request fixing it would be awesome!`);

  // https://developer.android.com/reference/android/content/Context.html#CLIPBOARD_SERVICE
  const CLIPBOARD_SERVICE: string = "clipboard";

  // a variable for clipboard text
  let data: string;

  return wrapJavaPerform(() => {

    const clipboardManager: ClipboardManager = Java.use("android.content.ClipboardManager");
    const context = getApplicationContext();
    const clipboardHandle = context.getApplicationContext().getSystemService(CLIPBOARD_SERVICE);
    const cp = Java.cast(clipboardHandle, clipboardManager);

    setInterval(() => {

      const primaryClip = cp.getPrimaryClip();

      // Check if there is at least some data
      if (primaryClip == null || primaryClip.getItemCount() <= 0) {
        return;
      }

      // If we have managed to get the primary clipboard and there are
      // items stored in it, process an update.
      const currentString = primaryClip.getItemAt(0).coerceToText(context).toString();

      // If the data is the same, just stop.
      if (data === currentString) {
        return;
      }

      // Update the data with the new string and report back.
      data = currentString;

      send(`${c.blackBright(`[pasteboard-monitor]`)} Data: ${c.greenBright(data.toString())}`);

    }, 1000 * 5);
  });
};


================================================
FILE: agent/src/android/filesystem.ts
================================================
import * as fs from "frida-fs";
import { Buffer } from "buffer";
import { hexStringToBytes } from "../lib/helpers.js";
import { IAndroidFilesystem } from "./lib/interfaces.js";
import {
  getApplicationContext,
  wrapJavaPerform, 
  Java
} from "./lib/libjava.js";
import {
  File,
  JavaClass
} from "./lib/types.js";

export const exists = (path: string): Promise<boolean> => {
  // -- Sample Java
  //
  // File path = new File(".");
  // Boolean e = path.exists();

  return wrapJavaPerform(() => {
    const file: File = Java.use("java.io.File");
    const currentFile: JavaClass = file.$new(path);

    return currentFile.exists();
  });
};

export const readable = (path: string): Promise<boolean> => {
  // -- Sample Java Code
  //
  // File d = new File(".");
  // d.canRead();

  return wrapJavaPerform(() => {
    const file: File = Java.use("java.io.File");
    const currentFile: JavaClass = file.$new(path);

    return currentFile.canRead();
  });
};

export const writable = (path: string): Promise<boolean> => {
  // -- Sample Java Code
  //
  // File d = new File(".");
  // d.canWrite();

  return wrapJavaPerform(() => {
    const file: File = Java.use("java.io.File");
    const currentFile: JavaClass = file.$new(path);

    return currentFile.canWrite();
  });
};

export const pathIsFile = (path: string): Promise<boolean> => {
  // -- Sample Java Code
  //
  // File d = new File(".");
  // d.isFile();

  return wrapJavaPerform(() => {
    const file: File = Java.use("java.io.File");
    const currentFile: JavaClass = file.$new(path);

    return currentFile.isFile();
  });
};

export const pwd = (): Promise<string> => {
  // -- Sample Java
  //
  // getApplicationContext().getFilesDir().getAbsolutePath()

  return wrapJavaPerform(() => {
    const context = getApplicationContext();
    return context.getFilesDir().getAbsolutePath().toString();
  });
};

// heavy lifting is done in frida-fs here.
export const readFile = (path: string): string | Buffer => {
  if (fs.statSync(path).size == 0)
    return Buffer.alloc(0);
  return fs.readFileSync(path);
};

// heavy lifting is done in frida-fs here.
export const writeFile = (path: string, data: string): void => {
  const writeStream: any = fs.createWriteStream(path);

  writeStream.on("error", (error: Error) => {
    throw error;
  });

  writeStream.write(hexStringToBytes(data));
  writeStream.end();
};

export const deleteFile = (path: string): Promise<boolean> => {
  // -- Sample Java Code
  //
  // File d = new File(".");
  // d.delete();

  return wrapJavaPerform(() => {
    const file: File = Java.use("java.io.File");
    const currentFile: JavaClass = file.$new(path);

    return currentFile.delete();
  });
};

export const ls = (p: string): Promise<IAndroidFilesystem> => {
  // -- Sample Java Code
  //
  // File d = new File(".");
  // File[] files = d.listFiles();
  // Log.e(getClass().getName(), "Files: " + files.length);
  // for (int i = 0; i < files.length; i++) {
  //     Log.e(getClass().getName(),
  //             files[i].getName() + ": " + files[i].canRead()
  //             + " " + files[i].lastModified()
  //             + " " + files[i].length()
  //     );
  // }

  return wrapJavaPerform(() => {
    const file: File = Java.use("java.io.File");
    const directory: JavaClass = file.$new(p);

    const response: IAndroidFilesystem = {
      files: {},
      path: p,
      readable: directory.canRead(),
      writable: directory.canWrite(),
    };

    if (!response.readable) { return response; }

    // get a listing of the files in the directory
    const files: any[] = directory.listFiles();

    for (const f of files) {
      response.files[f.getName()] = {
        attributes: {
          isDirectory: f.isDirectory(),
          isFile: f.isFile(),
          isHidden: f.isHidden(),
          lastModified: f.lastModified(),
          size: f.length(),
        },
        fileName: f.getName(),
        readable: f.canRead(),
        writable: f.canWrite(),
      };
    }

    return response;
  });
};


================================================
FILE: agent/src/android/general.ts
================================================
import {
  wrapJavaPerform,
  Java
} from "./lib/libjava.js";

export const deoptimize = (): Promise<void> => {
  return wrapJavaPerform(() => {
    Java.deoptimizeEverything();
  });
};


================================================
FILE: agent/src/android/heap.ts
================================================
import { colors as c } from "../lib/color.js";
import {
  IHeapClassDictionary,
  IHeapObject,
  IJavaField,
  IHeapNormalised
} from "./lib/interfaces.js";
import {
  wrapJavaPerform,
  Java
} from "./lib/libjava.js";
export let handles: IHeapClassDictionary = {};
import type { default as JavaTypes } from "frida-java-bridge";


const getInstance = (hashcode: number): JavaTypes.Wrapper | null => {
  const matches: IHeapObject[] = [];

  // Search for this handle, and push the results to matches
  Object.keys(handles).forEach((clazz) => {
    handles[clazz].filter((heapObject) => {
      if (heapObject.hashcode === hashcode) {
        matches.push(heapObject);
      }
    });
  });

  if (matches.length > 1) {
    c.log(`Found ${c.redBright(matches.length.toString())} handles, this is probably a bug, please report it!`);
  }

  if (matches.length > 0) {
    wrapJavaPerform(() => {
      c.log(`${c.blackBright(`Handle ` + hashcode + ` is to class `)}
        ${c.greenBright(matches[0].instance.$className)}`);
    });
    return matches[0].instance;
  }

  c.log(`${c.yellowBright(`Warning:`)} Could not find a known handle for ${hashcode}. ` +
    `Try searching class instances first.`);

  return null;
};

export const getInstances = (clazz: string): Promise<any[]> => {
  return wrapJavaPerform(() => {

    handles[clazz] = [];

    // tslint:disable:only-arrow-functions
    // tslint:disable:object-literal-shorthand
    // tslint:disable:no-empty
    Java.choose(clazz, {
      onComplete: function () {
        c.log(`Class instance enumeration complete for ${c.green(clazz)}`);
      },
      onMatch: function (instance) {
        handles[clazz].push({
          instance: instance,
          hashcode: instance.hashCode(),
        });
      },
    });
    // tslint:enable

    return handles[clazz].map((h): IHeapNormalised => {
      return {
        hashcode: h.hashcode,
        classname: clazz,
        tostring: h.instance.toString(),
      };
    });
  });
};

export const methods = (handle: number): Promise<string[]> => {
  return wrapJavaPerform(() => {
    const clazz = getInstance(handle);
    if (clazz == null) {
      return [];
    }

    return clazz.class.getDeclaredMethods().map((method: any) => {
      return method.toGenericString();
    });
  });
};

export const execute = (handle: number, method: string, returnString: boolean = false): Promise<string | null> => {
  return wrapJavaPerform(() => {
    const clazz = getInstance(handle);

    if (clazz == null) {
      return;
    }

    c.log(`${c.blackBright(`Executing method:`)} ${c.greenBright(`${method}()`)}`);
    const returnValue = clazz[method]();

    if (returnString && returnValue) {
      return returnValue.toString();
    }

    return returnValue;
  });
};

export const fields = (handle: number): Promise<IJavaField[]> => {
  return wrapJavaPerform(() => {
    const clazz = getInstance(handle);

    if (clazz == null) {
      return;
    }

    return clazz.class.getDeclaredFields().map((field: any): IJavaField => {
      const fieldName: string = field.getName();
      const fieldInstance: JavaTypes.Wrapper = clazz.class.getDeclaredField(fieldName);
      fieldInstance.setAccessible(true);

      let fieldValue = fieldInstance.get(clazz);

      // Cast a string if possible
      if (fieldValue) {
        fieldValue = fieldValue.toString();
      }

      return {
        name: fieldName,
        value: fieldValue,
      };
    });
  });
};

export const evaluate = (handle: number, js: string): Promise<void> => {
  return wrapJavaPerform(() => {
    const clazz = getInstance(handle);

    if (clazz == null) {
      return;
    }

    // tslint:disable-next-line:no-eval
    eval(js);
  });
};


================================================
FILE: agent/src/android/hooking.ts
================================================
import { colors as c } from "../lib/color.js";
import * as jobs from "../lib/jobs.js";
import { ICurrentActivityFragment } from "./lib/interfaces.js";
import {
  getApplicationContext,
  R,
  wrapJavaPerform,
  Java
} from "./lib/libjava.js";
import {
  Activity,
  ActivityClientRecord,
  ActivityThread,
  ArrayMap,
  JavaClass,
  PackageManager,
  Throwable,
  JavaMethodsOverloadsResult,
} from "./lib/types.js";
import type { default as JavaTypes } from "frida-java-bridge";

enum PatternType {
  Regex = 'regex',
  Klass = 'klass',
}

const splitClassMethod = (fqClazz: string): string[] => {
  // split a fully qualified class name, assuming the last period denotes the method
  const methodSeperatorIndex: number = fqClazz.lastIndexOf(".");

  const clazz: string = fqClazz.substring(0, methodSeperatorIndex);
  const method: string = fqClazz.substring(methodSeperatorIndex + 1); // Increment by 1 to exclude the leading period

  return [clazz, method];
};

export const getClasses = (): Promise<string[]> => {
  return wrapJavaPerform(() => {
    return Java.enumerateLoadedClassesSync();
  });
};

export const getClassLoaders = (): Promise<string[]> => {
  return wrapJavaPerform(() => {
    const loaders: string[] = [];
    Java.enumerateClassLoaders({
      onMatch: function (l) {
        if (l == null) {
          return;
        }
        loaders.push(l.toString());
      },
      onComplete: function () { }
    });

    return loaders;
  });
};

const getPatternType = (pattern: string): PatternType => {
  if (pattern.indexOf('!') !== -1) {
    return PatternType.Regex;
  }

  return PatternType.Klass;
};

export const lazyWatchForPattern = (query: string, watch: boolean, dargs: boolean, dret: boolean, dbt: boolean): void => {
  // TODO: Use param to control interval
  let found = false;
  const job: jobs.Job = new jobs.Job(jobs.identifier(),`notify-class for: ${query}`);

  // This method loops over all enumerate matches and then calls watch
  // with the arguments specified in the parent function
  const watchMatches = (matches: JavaTypes.EnumerateMethodsMatchGroup[]) => {
    matches.forEach(match => {
      match.classes.forEach(_class => {
        _class.methods.forEach(_method => {
          watchMethod(_class.name + "." + _method, job, dargs, dbt, dret);
        })
      })
    })
  }

  // Check if the pattern is found before starting an interval
  javaEnumerate(query).then(matches => {
    if (matches.length > 0) {
      found = true;
      send(`${c.green(query)} is already loaded / available`);
      if (watch) {
        watchMatches(matches);
        jobs.add(job);
      }
    }
  });

  if (found) return;

  send(`Watching for ${c.green(query)} ${c.blackBright(`(not starting a job)`)}`);

  // TODO: The javaEnumerate promise makes this racy. Figure it out one day.
  const interval = setInterval(() => {
    javaEnumerate(query).then(matches => {
      // Only notify if we haven't before
      if (!found && matches.length > 0) {
        send(`${c.green(query)} is now available`);
        found = true;
        if (watch) {
          watchMatches(matches);
          jobs.add(job);
        }
      }

      if (found) clearInterval(interval);
    });
  }, 1000 * 5);
};

export const javaEnumerate = (query: string): Promise<JavaTypes.EnumerateMethodsMatchGroup[]> => {
  // If the query is just a classname, strongarm it into a pattern.
  if (getPatternType(query) === PatternType.Klass) {
    query = `*${query}*!*`;
  }

  return wrapJavaPerform(() => {
    return Java.enumerateMethods(query);
  });
};

export const getClassMethods = (className: string): Promise<string[]> => {
  return wrapJavaPerform(() => {

    const clazz: JavaClass = Java.use(className);
    return clazz.class.getDeclaredMethods().map((method) => {
      return method.toGenericString();
    });
  });
};

// This function takes in a method such as package.class.perform()
// and extracts only the method name, ie "perform"
const genericMethodNameToMethodOnly = (fullMethodName: string): string => {
  // Reduces [package, class, perform()] to "perform()"
  const method = fullMethodName.split('.').filter((part: string) => part.includes('('))[0];
  // Now extract everything before the first '('
  return method.substring(0, method.indexOf('('));
};

// This method assumes that it's being called from inside wrapJavaPerform
// TODO: Not in use yet, but this is a proposal to replace Java.use() to
//  support multiple classloaders transparently.
export const getClassHandle = (className: string): JavaClass | null => {
  let clazz: JavaClass = null;
  const loaders = Java.enumerateClassLoadersSync();
  let found = false;

  // Try to get a handle using each of the class loaders
  for (let i = 0; i < loaders.length; i++) {
    const loader = loaders[i];
    const factory = Java.ClassFactory.get(loader);
    try {
      clazz = factory.use(className);
      found = true;
      break;
    } catch { }
  }
  if (found) {
    return clazz;
  } else {
    return null;
  }
};

// This method assumes that it's being called from inside wrapJavaPerform
// It behaves the same as the above, except only uses the specified class
// loader
export const getClassHandleWithLoaderClassName = (className: string, loaderClassName: any): JavaClass | null => {
  let clazz: JavaClass = null;
  const loaders = Java.enumerateClassLoadersSync()
    .filter(loader => loaderClassName === loader.$className);

  if (loaders.length == 0) return null;

  let found = false;
  // Try to get a handle using each of the class loaders
  // This is still required because some loaders may have the
  // same name, so distinguishing between them using this is
  // incorrect. I'm sure there is a way of finding the correct
  // one efficiently.
  for (let i = 0; i < loaders.length; i++) {
    const loader = loaders[i];
    const factory = Java.ClassFactory.get(loader);
    try {
      clazz = factory.use(className);
      found = true;
      break;
    } catch { }
  }

  if (found) return clazz;

  return null;
};

export const getClassMethodsOverloads = (className: string,
  methodsAllowList: string[] = [], loader?: string): Promise<JavaMethodsOverloadsResult> => {

  return wrapJavaPerform(() => {
    const result: JavaMethodsOverloadsResult = {};
    const clazz = loader !== null ? getClassHandleWithLoaderClassName(className, loader) : Java.use(className);

    if (clazz === null) {
      throw new Error("Could not find class!");
    }

    // TODO(cduplooy): The below line can fail with Error: java.lang.NoClassDefFoundError: Failed resolution of: Landroidx/datastore/core/DataStore;
    // This seems to involve custom class loaders...
    const methods = clazz.class.getDeclaredMethods()
      .map(method => genericMethodNameToMethodOnly(method.toGenericString()));
    methods.forEach(methodName => {
      if (methodsAllowList.length === 0 || (methodsAllowList.length > 0 && methodsAllowList.includes(methodName))) {
        const overloads = clazz[methodName].overloads;
        result[methodName] = {
          'argTypes': overloads.map(overload => overload.argumentTypes),
          'returnType': overloads.map(overload => overload.returnType),
          'methodName': overloads.map(overload => overload.methodName),
          'handle': overloads.map(overload => overload.handle),
          'holder': overloads.map(overload => overload.holder),
          'type': overloads.map(overload => overload.type),
        };
      }
    });

    // Finally append the constructor details
    if (clazz.class.getConstructors().length > 0) {
      if (methodsAllowList.length === 0 || (methodsAllowList.length > 0 && methodsAllowList.includes("$init"))) {
        const overloads = clazz['$init'].overloads;
        result['$init'] = {
          'argTypes': overloads.map(overload => overload.argumentTypes),
          'returnType': overloads.map(overload => overload.returnType),
          'methodName': overloads.map(overload => overload.methodName),
          'handle': overloads.map(overload => overload.handle),
          'holder': overloads.map(overload => overload.holder),
          'type': overloads.map(overload => overload.type),
        };
      }
    }

    return result;
  });
};

export const watch = (pattern: string, dargs: boolean, dbt: boolean, dret: boolean): Promise<void> => {

  // The general idea here is that we enumerate the total functions (based on the pattern type)
  // and via watchClass (which calls wathMethod) apply hooks.
  const patternType = getPatternType(pattern);

  if (patternType === PatternType.Klass) {

    // start a new job container
    const job: jobs.Job = new jobs.Job(jobs.identifier(),`watch-class for: ${pattern}`);

    const w = watchClass(pattern, job, dargs, dbt, dret);
    jobs.add(job);

    return w;
  }

  // assume we have PatternType.Regex
  const job: jobs.Job = new jobs.Job(jobs.identifier(),`watch-pattern for: ${pattern}`);
  jobs.add(job);

  return new Promise((resolve, reject) => {
    javaEnumerate(pattern).then((matches: JavaTypes.EnumerateMethodsMatchGroup[]) => {
      matches.forEach((match: JavaTypes.EnumerateMethodsMatchGroup) => {
        match.classes.forEach((klass: JavaTypes.EnumerateMethodsMatchClass) => {
          klass.methods.forEach(method => {
            // Only watch matched methods
            watchMethod(`${klass.name}.${method}`, job, dargs, dbt, dret);
          });
        });
      });
      resolve();
    }).catch((error) => {
      reject(error);
    });
  });
};

const watchClass = (clazz: string, job: jobs.Job, dargs: boolean = false, dbt: boolean = false, dret: boolean = false): Promise<void> => {
  return wrapJavaPerform(() => {
    const clazzInstance: JavaClass = Java.use(clazz);

    clazzInstance.class.getDeclaredMethods().map((method) => {
      // perform a cleanup of the method. An example after toGenericString() would be:
      // public void android.widget.ScrollView.draw(android.graphics.Canvas) throws Exception
      // public final rx.c.b<java.lang.Throwable> com.apple.android.music.icloud.a.a(rx.c.b<java.lang.Throwable>)
      let m: string = method.toGenericString();

      // Remove generics from the method
      while (m.includes("<")) { m = m.replace(/<.*?>/g, ""); }

      // remove any "Throws" the method may have
      if (m.indexOf(" throws ") !== -1) { m = m.substring(0, m.indexOf(" throws ")); }

      // remove scope and return type declarations (aka: first two words)
      // remove the class name
      // remove the signature and return
      m = m.slice(m.lastIndexOf(" "));
      m = m.replace(` ${clazz}.`, "");

      return m.split("(")[0];

    }).filter((value, index, self) => {

      return self.indexOf(value) === index;
    }).forEach((method) => {

      // get the argument types for this overload
      // send(`Watching ${c.green(clazz)}.${c.greenBright(method)}()`);
      const fqClazz = `${clazz}.${method}`;
      watchMethod(fqClazz, job, dargs, dbt, dret);
    });
  });
};

const watchMethod = (
  fqClazz: string, job: jobs.Job, dargs: boolean, dbt: boolean, dret: boolean,
): Promise<void> => {
  const [clazz, method] = splitClassMethod(fqClazz);
  // send(`Attempting to watch class ${c.green(clazz)} and method ${c.green(method)}.`);

  return wrapJavaPerform(() => {
    const throwable: Throwable = Java.use("java.lang.Throwable");
    const targetClass: JavaClass = Java.use(clazz);

    // Ensure that the method exists on the class
    if (targetClass[method] === undefined) {
      send(`${c.red("Error:")} Unable to find method ${c.redBright(method)} in class ${c.green(clazz)}`);
      return;
    }

    targetClass[method].overloads.forEach((m: any) => {
      // get the argument types for this overload
      const calleeArgTypes: string[] = m.argumentTypes.map((arg) => arg.className);

      send(`Watching ${c.green(clazz)}.${c.greenBright(method)}(${c.red(calleeArgTypes.join(", "))})`);
      // replace the implementation of this method
      // tslint:disable-next-line:only-arrow-functions
      m.implementation = function () {
        send(
          c.blackBright(`[${job.identifier}] `) +
          `Called ${c.green(clazz)}.${c.greenBright(m.methodName)}(${c.red(calleeArgTypes.join(", "))})`,
        );

        // dump a backtrace
        if (dbt) {
          send(
            c.blackBright(`[${job.identifier}] `) + "Backtrace:\n\t" +
            throwable.$new().getStackTrace().map((traceElement) => traceElement.toString() + "\n\t").join(""),
          );
        }

        // dump arguments
        if (dargs && calleeArgTypes.length > 0) {
          const argValues: string[] = [];
          for (const h of arguments) {
            argValues.push((h || "(none)").toString());
          }

          send(
            c.blackBright(`[${job.identifier}] `) +
            `Arguments ${c.green(clazz)}.${c.greenBright(m.methodName)}(${c.red(argValues.join(", "))})`,
          );
        }

        // actually run the intended method
        const retVal: any = m.apply(this, arguments);

        // dump the return value
        if (dret) {
          const retValStr: string = (retVal || "(none)").toString();
          send(c.blackBright(`[${job.identifier}] `) + `Return Value: ${c.red(retValStr)}`);
        }

        // also return the captured return value
        return retVal;
      };

      // Push the implementation so that it can be nulled later
      job.addImplementation(m);

    });
  });
};

export const getCurrentActivity = (): Promise<ICurrentActivityFragment> => {
  return wrapJavaPerform(() => {
    const activityThread: ActivityThread = Java.use("android.app.ActivityThread");
    const activity: Activity = Java.use("android.app.Activity");
    const activityClientRecord: ActivityClientRecord = Java.use("android.app.ActivityThread$ActivityClientRecord");

    const currentActivityThread = activityThread.currentActivityThread();
    const activityRecords = currentActivityThread.mActivities.value.values().toArray();
    let currentActivity;

    for (const i of activityRecords) {
      const activityRecord = Java.cast(i, activityClientRecord);
      if (!activityRecord.paused.value) {
        currentActivity = Java.cast(Java.cast(activityRecord, activityClientRecord).activity.value, activity);
        break;
      }
    }

    if (currentActivity) {
      // Discover an active fragment
      const fm = currentActivity.getFragmentManager();
      const fragment = fm.findFragmentById(R("content_frame", "id"));

      return {
        activity: currentActivity.$className,
        fragment: fragment.$className,
      };
    }

    return {
      activity: null,
      fragment: null,
    };
  });
};

export const getActivities = (): Promise<string[]> => {
  return wrapJavaPerform(() => {

    const packageManager: PackageManager = Java.use("android.content.pm.PackageManager");
    const GET_ACTIVITIES = packageManager.GET_ACTIVITIES.value;
    const context = getApplicationContext();

    return Array.prototype.concat(context.getPackageManager()
      .getPackageInfo(context.getPackageName(), GET_ACTIVITIES).activities.value.map((activityInfo) => {
        return activityInfo.name.value;
      }),
    );
  });
};

export const getServices = (): Promise<string[]> => {
  return wrapJavaPerform(() => {
    const activityThread: ActivityThread = Java.use("android.app.ActivityThread");
    const arrayMap: ArrayMap = Java.use("android.util.ArrayMap");
    const packageManager: PackageManager = Java.use("android.content.pm.PackageManager");

    const GET_SERVICES = packageManager.GET_SERVICES.value;

    const currentApplication = activityThread.currentApplication();
    // not using the helper as we need other variables too
    const context = currentApplication.getApplicationContext();

    var services: string[] = [];

    currentApplication.mLoadedApk.value.mServices.value.values().toArray().map((potentialServices) => {
      Java.cast(potentialServices, arrayMap).keySet().toArray().map((service) => {
        services.push(service.$className);
      });
    });

    services = services.concat(context.getPackageManager()
      .getPackageInfo(context.getPackageName(), GET_SERVICES).services.value.map((activityInfo) => {
        return activityInfo.name.value;
      }),
    );

    return services;
  });
};

export const getBroadcastReceivers = (): Promise<string[]> => {
  return wrapJavaPerform(() => {
    const activityThread: ActivityThread = Java.use("android.app.ActivityThread");
    const arrayMap: ArrayMap = Java.use("android.util.ArrayMap");
    const packageManager: PackageManager = Java.use("android.content.pm.PackageManager");

    const GET_RECEIVERS = packageManager.GET_RECEIVERS.value;

    const currentApplication = activityThread.currentApplication();
    // not using the helper as we need other variables too
    const context = currentApplication.getApplicationContext();
    const receiversFromContext = context.getPackageManager().getPackageInfo(
      context.getPackageName(),
      GET_RECEIVERS
    ).receivers.value

    var receivers: string[] = [];

    currentApplication.mLoadedApk.value.mReceivers.value.values().toArray().map((potentialReceivers) => {
      Java.cast(potentialReceivers, arrayMap).keySet().toArray().map((receiver) => {
        receivers.push(receiver.$className);
      });
    });

    if (receiversFromContext != null)
      receivers = receivers.concat(receiversFromContext.map((activityInfo) => {
        return activityInfo.name.value;
      }));

    return receivers;
  });
};

export const setReturnValue = (fqClazz: string, filterOverload: string | null, newRet: boolean): Promise<void> => {
  const [clazz, method] = splitClassMethod(fqClazz);
  send(`Attempting to modify return value for class ${c.green(clazz)} and method ${c.green(method)}.`);

  if (filterOverload != null) {
    send(c.blackBright(`Will filter for method overload with arguments:`) +
      ` ${c.green(filterOverload)}`);
  }

  return wrapJavaPerform(() => {
    const job: jobs.Job = new jobs.Job(jobs.identifier(), `set-return for: ${fqClazz}`);

    const targetClazz: JavaClass = Java.use(clazz);

    targetClazz[method].overloads.forEach((m: any) => {
      // get the argument types for this method
      const calleeArgTypes: string[] = m.argumentTypes.map((arg) => arg.className);

      // check if we need to filter on a specific overload
      if (filterOverload != null && calleeArgTypes.join(",") !== filterOverload) {
        return;
      }

      send(`Hooking ${c.green(clazz)}.${c.greenBright(method)}(${c.red(calleeArgTypes.join(", "))})`);

      // tslint:disable-next-line:only-arrow-functions
      m.implementation = function () {
        let retVal = m.apply(this, arguments);

        // Override retval if needed
        if (retVal !== newRet) {
          send(
            c.blackBright(`[${job.identifier}] `) + `Return value was not ${c.red(newRet.toString())}, ` +
            `setting to ${c.green(newRet.toString())}.`,
          );
          // update the return value
          retVal = newRet;
        }
        return retVal;
      };

      // record override
      job.addImplementation(m);
      
    });

    jobs.add(job);
  });
};


================================================
FILE: agent/src/android/intent.ts
================================================
import { colors as c } from "../lib/color.js";
import {
  getApplicationContext,
  wrapJavaPerform,
  Java
} from "./lib/libjava.js";
import { Intent, FridaOverload } from "./lib/types.js";
import { analyseIntent } from "./lib/intentUtils.js";
import * as jobs from "../lib/jobs.js";
import type { default as JavaTypes } from "frida-java-bridge";

// https://developer.android.com/reference/android/content/Intent.html#FLAG_ACTIVITY_NEW_TASK
const FLAG_ACTIVITY_NEW_TASK = 0x10000000;

// starts an Android activity
// This method does not yet allow for 'extra' data to be send along
// with the intent.
export const startActivity = (activityClass: string): Promise<void> => {
  // -- Sample Java
  //
  // Intent intent = new Intent(this, DisplayMessageActivity.class);
  // intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  //
  // startActivity(intent);
  return wrapJavaPerform(() => {
    const context = getApplicationContext();

    // Setup a new Intent
    const androidIntent: Intent = Java.use("android.content.Intent");

    // Get the Activity class's .class
    const newActivity: JavaTypes.Wrapper = Java.use(activityClass).class;
    send(`Starting activity ${c.green(activityClass)}...`);

    // Init and launch the intent
    const newIntent: Intent = androidIntent.$new(context, newActivity);
    newIntent.setFlags(FLAG_ACTIVITY_NEW_TASK);

    context.startActivity(newIntent);
    send(c.blackBright(`Activity successfully asked to start.`));
  });
};

// starts an Android service
export const startService = (serviceClass: string): Promise<void> => {
  // -- Sample Java
  //
  // Intent intent = new Intent(this, Service.class);
  // intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  //
  // startService(intent);
  return wrapJavaPerform(() => {
    const context = getApplicationContext();

    // Setup a new Intent
    const androidIntent: Intent = Java.use("android.content.Intent");

    // Get the Activity class's .class
    const newService: string = Java.use(serviceClass).$className;
    send(`Starting service ${c.green(serviceClass)}...`);

    // Init and launch the intent
    const newIntent: Intent = androidIntent.$new(context, newService);
    newIntent.setFlags(FLAG_ACTIVITY_NEW_TASK);

    context.startService(newIntent);
    send(c.blackBright(`Service successfully asked to start.`));
  });
};

// Analyzes and Detects Android Implicit Intents
// https://developer.android.com/guide/components/intents-filters#Types
export const analyzeImplicits = (backtrace = false): Promise<void> => {

  const job = new jobs.Job(jobs.identifier(),`implicit-intent-analyser`)
  jobs.add(job)

  return wrapJavaPerform(() => {
    const classesToHook = [
      { className: "android.app.Activity", methodName: "startActivityForResult" },
      { className: "android.app.Activity", methodName: "onActivityResult" },
      { className: "androidx.activity.ComponentActivity", methodName: "onActivityResult" },
      { className: "android.content.Context", methodName: "startActivity" },
      { className: "android.content.BroadcastReceiver", methodName: "onReceive" }
      // Add other classes and methods as needed
    ];

    classesToHook.forEach(hook => {
      try {
        const clazz = Java.use(hook.className);
        const method = clazz[hook.methodName];
        method.overloads.forEach((overload: FridaOverload) => {
          overload.implementation = function (...args: any[]): any {
            args.forEach(arg => {
              if (arg && arg.$className === "android.content.Intent") {
                analyseIntent(`${hook.className}::${hook.methodName}`, arg, backtrace = backtrace);
              }
            });
            return overload.apply(this, args);
          };
          job.addImplementation(overload);
        });
      } catch (e) {
        send(`[-] Error hooking ${c.redBright(`${hook.className}.${hook.methodName}: ${e}`)}`);
      }
    });
  });
};

================================================
FILE: agent/src/android/keystore.ts
================================================
import { colors as c } from "../lib/color.js";
import {
  IKeyStoreDetail,
  IKeyStoreEntry
} from "./lib/interfaces.js";
import { 
  wrapJavaPerform, 
  Java 
} from "./lib/libjava.js";
import {
  KeyFactory,
  KeyInfo,
  KeyStore,
  SecretKeyFactory
} from "./lib/types.js";
import * as jobs from "../lib/jobs.js";

// Dump entries in the Android Keystore, together with a flag
// indicating if its a key or a certificate.
//
// Ref: https://developer.android.com/reference/java/security/KeyStore.html
export const list = (): Promise<IKeyStoreEntry[]> => {
  // - Sample Java
  //
  // KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
  // ks.load(null);
  // Enumeration<String> aliases = ks.aliases();
  //
  // while(aliases.hasMoreElements()) {
  //     Log.e("E", "Aliases = " + aliases.nextElement());
  // }
  return wrapJavaPerform(() => {
    const keyStore: KeyStore = Java.use("java.security.KeyStore");
    const entries: IKeyStoreEntry[] = [];

    // Prepare the AndroidKeyStore keystore provider and load it.
    // Maybe at a later stage we should support adding other stores
    // like from file or JKS.
    const ks: KeyStore = keyStore.getInstance("AndroidKeyStore");
    ks.load(null, null);

    // Get the aliases and loop through them. The aliases() method
    // return an Enumeration<String> type.
    const aliases = ks.aliases();
    while (aliases.hasMoreElements()) {
      const alias = aliases.nextElement();

      entries.push({
        alias: alias.toString(),
        is_certificate: ks.isCertificateEntry(alias),
        is_key: ks.isKeyEntry(alias),
      });
    }

    return entries;
  });
};

// Dump detailed information about keystore entries per alias.
//
// Refs:
//  https://labs.f-secure.com/blog/how-secure-is-your-android-keystore-authentication
//  https://github.com/FSecureLABS/android-keystore-audit
export const detail = (): Promise<IKeyStoreDetail[]> => {

  // helper function to extract  keystore alias information
  const keystore_info = (alias): IKeyStoreDetail => {
    const r: IKeyStoreDetail = {};

    wrapJavaPerform(() => {
      // java class handles
      const keyStore: KeyStore = Java.use('java.security.KeyStore');
      const keyFactory: KeyFactory = Java.use('java.security.KeyFactory');
      const keyInfo: KeyInfo = Java.use('android.security.keystore.KeyInfo');
      const keySecretKeyFactory: SecretKeyFactory = Java.use('javax.crypto.SecretKeyFactory');

      // load the keystore entry
      const keyStoreObj = keyStore.getInstance('AndroidKeyStore');
      keyStoreObj.load(null);
      const key = keyStoreObj.getKey(alias, null);
      if (key == null) return null;

      let keySpec = null;
      try {
        keySpec = keyFactory.getInstance(key.getAlgorithm(), 'AndroidKeyStore')
          .getKeySpec(key, keyInfo.class);
      } catch (err) {
        keySpec = keySecretKeyFactory.getInstance(key.getAlgorithm(), 'AndroidKeyStore')
          .getKeySpec(key, keyInfo.class);
      }

      // set result fields
      r.keyAlgorithm = key.getAlgorithm();
      r.keySize = keyInfo['getKeySize'].call(keySpec);
      r.blockModes = keyInfo['getBlockModes'].call(keySpec);
      r.digests = keyInfo['getDigests'].call(keySpec);
      r.encryptionPaddings = keyInfo['getEncryptionPaddings'].call(keySpec);
      r.keyValidityForConsumptionEnd = keyInfo['getKeyValidityForConsumptionEnd'].call(keySpec);
      r.keyValidityForOriginationEnd = keyInfo['getKeyValidityForOriginationEnd'].call(keySpec);
      r.keyValidityStart = keyInfo['getKeyValidityStart'].call(keySpec);
      r.keystoreAlias = keyInfo['getKeystoreAlias'].call(keySpec);
      r.origin = keyInfo['getOrigin'].call(keySpec);
      r.purposes = keyInfo['getPurposes'].call(keySpec);
      r.signaturePaddings = keyInfo['getSignaturePaddings'].call(keySpec);
      r.userAuthenticationValidityDurationSeconds = keyInfo['getUserAuthenticationValidityDurationSeconds'].call(keySpec);
      r.isInsideSecureHardware = keyInfo['isInsideSecureHardware'].call(keySpec);
      r.isInvalidatedByBiometricEnrollment = keyInfo['isInvalidatedByBiometricEnrollment'].call(keySpec);
      r.isUserAuthenticationRequired = keyInfo['isUserAuthenticationRequired'].call(keySpec);
      r.isUserAuthenticationRequirementEnforcedBySecureHardware = keyInfo['isUserAuthenticationRequirementEnforcedBySecureHardware'].call(keySpec);
      r.isUserAuthenticationValidWhileOnBody = keyInfo['isUserAuthenticationValidWhileOnBody'].call(keySpec);

      // "crashy" calls that's ok if they fail
      try {
        r.isTrustedUserPresenceRequired = keyInfo['isTrustedUserPresenceRequired'].call(keySpec);
      } catch (err) { }
      try {
        r.isUserConfirmationRequired = keyInfo['isUserConfirmationRequired'].call(keySpec);
      } catch (err) { }

      // translate some values to string representation if they are not empty
      if (r.keyValidityForConsumptionEnd != null)
        r.keyValidityForConsumptionEnd = r.keyValidityForConsumptionEnd.toString();
      if (r.keyValidityForOriginationEnd != null)
        r.keyValidityForOriginationEnd = r.keyValidityForOriginationEnd.toString();
      if (r.keyValidityStart != null)
        r.keyValidityStart = r.keyValidityStart.toString();
    });

    return r;
  };

  return wrapJavaPerform((): IKeyStoreDetail[] => {
    const keyStore: KeyStore = Java.use("java.security.KeyStore");
    const ks: KeyStore = keyStore.getInstance("AndroidKeyStore");
    ks.load(null, null);

    const aliases = ks.aliases();
    const info: IKeyStoreDetail[] = [];

    while (aliases.hasMoreElements()) {
      var a = aliases.nextElement();
      info.push(keystore_info(a.toString()));
    }

    return info;
  });
};

// Delete all entries in the Android Keystore
//
// Ref: https://developer.android.com/reference/java/security/KeyStore.html#deleteEntry(java.lang.String)
export const clear = () => {
  // - Sample Java
  //
  // KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
  // ks.load(null);
  // Enumeration<String> aliases = ks.aliases();
  //
  // while(aliases.hasMoreElements()) {
  //     ks.deleteEntry(aliases.nextElement());
  // }
  return wrapJavaPerform(() => {
    const keyStore: KeyStore = Java.use("java.security.KeyStore");

    // Prepare the AndroidKeyStore keystore provider and load it.
    // Maybe at a later stage we should support adding other stores
    // like from file or JKS.
    const ks: KeyStore = keyStore.getInstance("AndroidKeyStore");
    ks.load(null, null);

    // Get the aliases and loop through them. The aliases() method
    // return an Enumeration<String> type.
    const aliases = ks.aliases();
    while (aliases.hasMoreElements()) {
      ks.deleteEntry(aliases.nextElement());
    }

    send(c.blackBright(`Keystore entries cleared`));
  });
};

// keystore watch methods

// Watch for KeyStore.load();
// TODO: Store the keystores themselves maybe?
const keystoreLoad = (ident: number): Promise<any> => {
  return wrapJavaPerform(() => {
    const ks: KeyStore = Java.use("java.security.KeyStore");
    const ksLoad = ks.load.overload("java.io.InputStream", "[C");
    send(c.blackBright(`[${ident}] Watching Keystore.load("java.io.InputStream", "[C")`));

    ksLoad.implementation = function (stream, password) {
      send(c.blackBright(`[${ident}] `) +
        `Keystore.load(${c.greenBright(stream)}, ${c.redBright(password || `null`)}) ` +
        `called, loading a ${c.cyanBright(this.getType())} keystore.`);
      return this.load(stream, password);
    };

    return ksLoad
  });
};

// Watch for Keystore.getKey().
// TODO: Extract more information, like the key itself maybe?
const keystoreGetKey = (ident: number): Promise<any> => {
  return wrapJavaPerform(() => {
    const ks: KeyStore = Java.use("java.security.KeyStore");
    const ksGetKey = ks.getKey.overload("java.lang.String", "[C");
    send(c.blackBright(`[${ident}] Watching Keystore.getKey("java.lang.String", "[C")`));

    ksGetKey.implementation = function (alias, password) {
      const key = this.getKey(alias, password);
      send(c.blackBright(`[${ident}] `) +
        `Keystore.getKey(${c.greenBright(alias)}, ${c.redBright(password || `null`)}) ` +
        `called, returning a ${c.greenBright(key.$className)} instance.`);
      return key;
    };

    return ksGetKey;
  });
};

// Android KeyStore watcher.
// Many, many more methods can be added here..
export const watchKeystore = async (): Promise<void> =>  {
  const job: jobs.Job = new jobs.Job(jobs.identifier(), "android-keystore-watch");

  job.addImplementation(await keystoreLoad(job.identifier));
  job.addImplementation(await keystoreGetKey(job.identifier));
  
  jobs.add(job);
};


================================================
FILE: agent/src/android/lib/intentUtils.ts
================================================
import { Java } from "./libjava.js";
import { colors as c } from "../../lib/color.js";

export const analyseIntent = (methodName: string, intent: any, backtrace: boolean = false): void => {
  try {
    send(`\nAnalyzing Intent from: ${c.green(`${methodName}`)}`);

    // Get Component
    const component = intent.getComponent();
    if (component) {
      send(`[-] ${c.green('Intent Type: Explicit Intent')}`);
    } else {
      send(`[+] ${c.redBright('Intent Type: Implicit Intent Detected!')}`);
      if (backtrace) {
        send(
          Java.use('android.util.Log')
            .getStackTraceString(Java.use('java.lang.Exception').$new())
        )
      }

      // Log intent details
      send(`[+] Action: ${`${c.green(`${intent.getAction()}`)}` || `${c.redBright(`[None]`)}`}`);
      send(`[+] Data URI: ${`${c.green(`${intent.getDataString()}`)}` || `${c.redBright(`[None]`)}`}`);
      send(`[+] Type: ${`${c.green(`${intent.getType()}`)}` || `${c.redBright(`[None]`)}`}`);
      send(`[+] Flags: ${c.green(`0x${intent.getFlags().toString(16)}`)}`);

      // Categories
      const categories = intent.getCategories();
      if (categories) {
        send("\n[+] Categories:");
        const iterator = categories.iterator();
        while (iterator.hasNext()) {
          send(`[+] Category: ${c.green(`${iterator.next()}`)} `);
        }
      } else {
        send(`[-] Category: ${`${c.redBright(`[None]`)}`}`);
      }

      // Extras
      const extras = intent.getExtras();
      if (extras) {
        send(`[+] Extras: ${c.green(`${extras}`)}`);
      } else {
        send(`[-] Extras: ${`${c.redBright(`[None]`)}`}`);
      }

      // Resolving implicit intents
      const activityContext = Java.use("android.app.ActivityThread").currentApplication().getApplicationContext();
      if (activityContext) {
        const packageManager = activityContext.getPackageManager();
        const resolveInfoList = packageManager.queryIntentActivities(intent, Java.use("android.content.pm.PackageManager").MATCH_ALL.value);

        send("[+] Responding apps:");
        for (let i = 0; i < resolveInfoList.size(); i++) {
          const resolveInfo = resolveInfoList.get(i);
          send(`[*] Resolve Info List at position ${i}: ${c.green(`${resolveInfo.toString()}`)}`);
        }
      } else {
        send("[-] No activity context available");
      }

    }
  } catch (e) {
    send(`[!] Error analyzing intent: ${e}`);
  }
};


================================================
FILE: agent/src/android/lib/interfaces.ts
================================================
import type { default as JavaTypes } from "frida-java-bridge";

export interface IAndroidFilesystem {
  files: any;
  path: string;
  readable: boolean;
  writable: boolean;
}

export interface IExecutedCommand {
  command: string;
  stdOut: string;
  stdErr: string;
}

export interface IKeyStoreEntry {
  alias: string;
  is_certificate: boolean;
  is_key: boolean;
}

export interface ICurrentActivityFragment {
  activivity: string | null;
  fragment: string | null;
}

export interface IHeapClassDictionary {
  [index: string]: IHeapObject[];
}

export interface IHeapObject {
  hashcode: number;
  instance: JavaTypes.Wrapper;
}

export interface IHeapNormalised {
  hashcode: number;
  classname: string;
  tostring: string;
}

export interface IJavaField {
  name: string;
  value: string;
}

export interface IKeyStoreDetail {
  keyAlgorithm?: string;
  keySize?: string;
  blockModes?: string;
  digests?: string;
  encryptionPaddings?: string;
  keyValidityForConsumptionEnd?: string;
  keyValidityForOriginationEnd?: string;
  keyValidityStart?: string;
  keystoreAlias?: string;
  origin?: string;
  purposes?: string;
  signaturePaddings?: string;
  userAuthenticationValidityDurationSeconds?: string;
  isInsideSecureHardware?: string;
  isInvalidatedByBiometricEnrollment?: string;
  isUserAuthenticationRequired?: string;
  isUserAuthenticationRequirementEnforcedBySecureHardware?: string;
  isUserAuthenticationValidWhileOnBody?: string;
  // "crashy" fields
  isTrustedUserPresenceRequired?: string;
  isUserConfirmationRequired?: string;
}

================================================
FILE: agent/src/android/lib/libjava.ts
================================================
import Java_bridge from "frida-java-bridge";
import { colors as c } from "../../lib/color.js";

let Java: typeof Java_bridge;
// Compatibility with frida < 17
if (globalThis.Java) {
  send(c.blackBright("Pre-v17 version of Frida detected. Attempting to use old bridge interface."))
  Java = globalThis.Java   
} else {
  Java = Java_bridge
}

export { Java }

// all Java calls need to be wrapped in a Java.perform().
// this helper just wraps that into a Promise that the
// rpc export will sniff and resolve before returning
// the result when its ready.
export const wrapJavaPerform = (fn: any): Promise<any> => {
  return new Promise((resolve, reject) => {
    Java.perform(() => {
      try {
        resolve(fn());
      } catch (e) {
        reject(e);
      }
    });
  });
};

export const getApplicationContext = (): any => {
  const ActivityThread = Java.use("android.app.ActivityThread");
  const currentApplication = ActivityThread.currentApplication();

  return currentApplication.getApplicationContext();
};

// A helper method to access the R class for the app.
// Typical usage within an app would be something like:
//  R.id.content_frame.
//
// Using this method, the above example would be:
//  R("content_frame", "id")
export const R = (name: string, type: string): any => {
  const context = getApplicationContext();
  // https://github.com/bitpay/android-sdk/issues/14#issue-202495610
  return context.getResources().getIdentifier(name, type, context.getPackageName());
};


================================================
FILE: agent/src/android/lib/types.ts
================================================
export type JavaClass = any;

export type JavaMethodsOverloadsResult = any;

export type ClipboardManager = JavaClass | any;
export type File = JavaClass | any;
export type Throwable = JavaClass | any;
export type PackageManager = JavaClass | any;
export type ArrayMap = JavaClass | any;
export type ActivityThread = JavaClass | any;
export type Intent = JavaClass | any;
export type KeyStore = JavaClass | any;
export type KeyFactory = JavaClass | any;
export type KeyInfo = JavaClass | any;
export type SecretKeyFactory = JavaClass | any;
export type X509TrustManager = JavaClass | any;
export type SSLContext = JavaClass | any;
export type CertificatePinner = JavaClass | any;
export type PinningTrustManager = JavaClass | any;
export type SSLCertificateChecker = JavaClass | any;
export type TrustManagerImpl = JavaClass | any;
export type ArrayList = JavaClass | any;
export type JavaString = JavaClass | any;
export type Runtime = JavaClass | any;
export type IOException = JavaClass | any;
export type InputStreamReader = JavaClass | any;
export type BufferedReader = JavaClass | any;
export type StringBuilder = JavaClass | any;
export type Activity = JavaClass | any;
export type ActivityClientRecord = JavaClass | any;
export type Bitmap = JavaClass | any;
export type ByteArrayOutputStream = JavaClass | any;
export type CompressFormat = JavaClass | any;
export type FridaOverload = {
  implementation: (...args: any[]) => any;
  apply: (thisArg: any, args: any[]) => any;
};

================================================
FILE: agent/src/android/monitor.ts
================================================
import { wrapJavaPerform } from "./lib/libjava.js";

export namespace monitor {
  export const stringCanary = (can: string): Promise<void> => {
    return wrapJavaPerform(() => {

    });
  };
}


================================================
FILE: agent/src/android/pinning.ts
================================================
import { colors as c } from "../lib/color.js";
import { qsend } from "../lib/helpers.js";
import * as jobs from "../lib/jobs.js";
import {
  wrapJavaPerform,
  Java
} from "./lib/libjava.js";
import {
  ArrayList,
  CertificatePinner,
  PinningTrustManager,
  SSLCertificateChecker,
  SSLContext,
  TrustManagerImpl,
  X509TrustManager,
} from "./lib/types.js";


// a simple flag to control if we should be quiet or not
let quiet: boolean = false;

const sslContextEmptyTrustManager = (ident: number): Promise<any> => {
  // -- Sample Java
  //
  // "Generic" TrustManager Example
  //
  // TrustManager[] trustAllCerts = new TrustManager[] {
  //     new X509TrustManager() {
  //         public java.security.cert.X509Certificate[] getAcceptedIssuers() {
  //             return null;
  //         }
  //         public void checkClientTrusted(X509Certificate[] certs, String authType) {  }
  //         public void checkServerTrusted(X509Certificate[] certs, String authType) {  }
  //     }
  // };
  // SSLContext sslcontect = SSLContext.getInstance("TLS");
  // sslcontect.init(null, trustAllCerts, null);
  return wrapJavaPerform(() => {
    const x509TrustManager: X509TrustManager = Java.use("javax.net.ssl.X509TrustManager");
    const sSLContext: SSLContext = Java.use("javax.net.ssl.SSLContext");

    // Some 'anti-frida' detections will scan /proc/<pid>/maps.
    // Rename the tempFileNaming prefix as this could end up in maps.
    // https://github.com/frida/frida-java-bridge/blob/8b3790f7489ff5be7b19ddaccf5149d4e7738460/lib/class-factory.js#L94
    if (Java.classFactory.tempFileNaming.prefix == 'frida') {
      Java.classFactory.tempFileNaming.prefix = 'onetwothree';
    }

    // Implement a new TrustManager
    // ref: https://gist.github.com/oleavr/3ca67a173ff7d207c6b8c3b0ca65a9d8
    const TrustManager: X509TrustManager = Java.registerClass({
      implements: [x509TrustManager],
      methods: {
        // tslint:disable-next-line:no-empty
        checkClientTrusted(chain, authType) { },
        // tslint:disable-next-line:no-empty
        checkServerTrusted(chain, authType) { },
        getAcceptedIssuers() {
          return [];
        },
      },
      name: "com.sensepost.test.TrustManager",
    });

    // Prepare the TrustManagers array to pass to SSLContext.init()
    const TrustManagers: X509TrustManager[] = [TrustManager.$new()];
    send(c.blackBright("Custom TrustManager ready, overriding SSLContext.init()"));

    // Get a handle on the init() on the SSLContext class
    const SSLContextInit = sSLContext.init.overload(
      "[Ljavax.net.ssl.KeyManager;", "[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom");

    // Override the init method, specifying our new TrustManager
    SSLContextInit.implementation = function (keyManager, trustManager, secureRandom) {
      qsend(quiet,
        c.blackBright(`[${ident}] `) + `Called ` +
        c.green(`SSLContext.init()`) +
        `, overriding TrustManager with empty one.`,
      );

      SSLContextInit.call(this, keyManager, TrustManagers, secureRandom);
    };

    return SSLContextInit;
  });
};

const okHttp3CertificatePinnerCheck = (ident: number): Promise<any | undefined> => {
  // -- Sample Java
  //
  // Example used to test this bypass.
  //
  // String hostname = "swapi.co";
  // CertificatePinner certificatePinner = new CertificatePinner.Builder()
  //         .add(hostname, "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
  //         .build();
  // OkHttpClient client = new OkHttpClient.Builder()
  //         .certificatePinner(certificatePinner)
  //         .build();
  // Request request = new Request.Builder()
  //         .url("https://swapi.co/api/people/1")
  //         .build();
  // Response response = client.newCall(request).execute();
  return wrapJavaPerform(() => {
    try {
      const certificatePinner: CertificatePinner = Java.use("okhttp3.CertificatePinner");
      send(c.blackBright(`Found okhttp3.CertificatePinner, overriding CertificatePinner.check()`));

      if(!certificatePinner.check) {
        return null;
      }

      const CertificatePinnerCheck = certificatePinner.check.overload("java.lang.String", "java.util.List");

      // tslint:disable-next-line:only-arrow-functions
      CertificatePinnerCheck.implementation = function () {
        qsend(quiet,
          c.blackBright(`[${ident}] `) + `Called ` +
          c.green(`OkHTTP 3.x CertificatePinner.check()`) +
          `, not throwing an exception.`,
        );
      };

      return CertificatePinnerCheck;

    } catch (err) {
      if ((err as Error).message.indexOf("java.lang.ClassNotFoundException") !== 0) {
        throw err;
      }
      return null;
    }
  });
};

const okHttp3CertificatePinnerCheckOkHttp = (ident: number): Promise<any | undefined> => {
  // -- Sample Java
  //
  // Example used to test this bypass.
  //
  // String hostname = "swapi.co";
  // CertificatePinner certificatePinner = new CertificatePinner.Builder()
  //         .add(hostname, "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
  //         .build();
  // OkHttpClient client = new OkHttpClient.Builder()
  //         .certificatePinner(certificatePinner)
  //         .build();
  // Request request = new Request.Builder()
  //         .url("https://swapi.co/api/people/1")
  //         .build();
  // Response response = client.newCall(request).execute();
  return wrapJavaPerform(() => {
    try {
      const certificatePinner: CertificatePinner = Java.use("okhttp3.CertificatePinner");

      if(!certificatePinner.check$okhttp) {
        return null;
      }
      
      send(c.blackBright(`Found okhttp3.CertificatePinner, overriding CertificatePinner.check$okhttp()`));

      const CertificatePinnerCheckOkHttp = certificatePinner.check$okhttp.overload("java.lang.String", "u15");

      // tslint:disable-next-line:only-arrow-functions
      CertificatePinnerCheckOkHttp.implementation = function () {
        qsend(quiet,
          c.blackBright(`[${ident}] `) + `Called check$okhttp ` +
          c.green(`OkHTTP 3.x CertificatePinner.check$okhttp()`) +
          `, not throwing an exception.`,
        );
      };

      return CertificatePinnerCheckOkHttp;

    } catch (err) {
      if ((err as Error).message.indexOf("java.lang.ClassNotFoundException") !== 0) {
        throw err;
      }
      return null;
    }
  });
};

const appceleratorTitaniumPinningTrustManager = (ident: number): Promise<any | undefined> => {
  return wrapJavaPerform(() => {
    try {
      const pinningTrustManager: PinningTrustManager = Java.use("appcelerator.https.PinningTrustManager");
      const PinningTrustManagerCheckServerTrusted = pinningTrustManager.checkServerTrusted;

      if(!PinningTrustManagerCheckServerTrusted) {
        return null;
      }

      send(
        c.blackBright(`Found appcelerator.https.PinningTrustManager, ` +
          `overriding PinningTrustManager.checkServerTrusted()`),
      );


      // tslint:disable-next-line:only-arrow-functions
      PinningTrustManagerCheckServerTrusted.implementation = function () {
        qsend(quiet,
          c.blackBright(`[${ident}] `) + `Called ` +
          c.green(`PinningTrustManager.checkServerTrusted()`) +
          `, not throwing an exception.`,
        );
      };

      return PinningTrustManagerCheckServerTrusted;

    } catch (err) {
      if ((err as Error).message.indexOf("java.lang.ClassNotFoundException") !== 0) {
        throw err;
      }
      return null;
    }
  });
};

// Android 7+ TrustManagerImpl.verifyChain()
// The work in the following NCC blog post was a great help for this hook!
// hattip @AdriVillaB :)
// https://www.nccgroup.trust/uk/about-us/newsroom-and-events/
//  blogs/2017/november/bypassing-androids-network-security-configuration/
//
// More information: https://sensepost.com/blog/2018/tip-toeing-past-android-7s-network-security-configuration/
const trustManagerImplVerifyChainCheck = (ident: number): Promise<any> => {
  return wrapJavaPerform(() => {
    try {
      const trustManagerImpl: TrustManagerImpl = Java.use("com.android.org.conscrypt.TrustManagerImpl");

      // https://github.com/google/conscrypt/blob/c88f9f55a523f128f0e4dace76a34724bfa1e88c/
      //  platform/src/main/java/org/conscrypt/TrustManagerImpl.java#L650
      const TrustManagerImplverifyChain = trustManagerImpl.verifyChain;

      if((!TrustManagerImplverifyChain)) {
        return null;
      }

       send(
        c.blackBright(`Found com.android.org.conscrypt.TrustManagerImpl, ` +
          `overriding TrustManagerImpl.verifyChain()`),
      );


      // tslint:disable-next-line:only-arrow-functions
      TrustManagerImplverifyChain.implementation = function (untrustedChain, trustAnchorChain,
        host, clientAuth, ocspData, tlsSctData) {
        qsend(quiet,
          c.blackBright(`[${ident}] `) + `Called (Android 7+) ` +
          c.green(`TrustManagerImpl.verifyChain()`) + `, not throwing an exception.`,
        );

        // Skip all the logic and just return the chain again :P
        return untrustedChain;
      };

      return TrustManagerImplverifyChain;

    } catch (err) {
      if ((err as Error).message.indexOf("java.lang.ClassNotFoundException") !== 0) {
        throw err;
      }
      return null;
    }
  });
};

// Android 7+ TrustManagerImpl.checkTrustedRecursive()
// The work in the following method is based on:
// https://techblog.mediaservice.net/2018/11/universal-android-ssl-pinning-bypass-2/
const trustManagerImplCheckTrustedRecursiveCheck = (ident: number): Promise<any> => {
  return wrapJavaPerform(() => {
    try {
      const arrayList: ArrayList = Java.use("java.util.ArrayList");
      const trustManagerImpl: TrustManagerImpl = Java.use("com.android.org.conscrypt.TrustManagerImpl");
      
      if(!trustManagerImpl.checkTrustedRecursive) {
        return null;
      }

      // https://android.googlesource.com/platform/external/conscrypt/+/1186465/src/
      //  platform/java/org/conscrypt/TrustManagerImpl.java#391
      const TrustManagerImplcheckTrustedRecursive = trustManagerImpl.checkTrustedRecursive;
      send(
        c.blackBright(`Found com.android.org.conscrypt.TrustManagerImpl, ` +
          `overriding TrustManagerImpl.checkTrustedRecursive()`),
      );

      // tslint:disable-next-line:only-arrow-functions
      TrustManagerImplcheckTrustedRecursive.implementation = function (certs, host, clientAuth, untrustedChain,
        trustAnchorChain, used) {
        qsend(quiet,
          c.blackBright(`[${ident}] `) + `Called (Android 7+) ` +
          c.green(`TrustManagerImpl.checkTrustedRecursive()`) + `, not throwing an exception.`,
        );

        // Return an empty list
        return arrayList.$new();
      };

      return TrustManagerImplcheckTrustedRecursive;

    } catch (err) {
      if ((err as Error).message.indexOf("java.lang.ClassNotFoundException") !== 0) {
        throw err;
      }
      return null;
    }
  });
};

const phoneGapSSLCertificateChecker = (ident: number): Promise<any> => {
  return wrapJavaPerform(() => {
    try {
      const sslCertificateChecker: SSLCertificateChecker = Java.use("nl.xservices.plugins.SSLCertificateChecker");

      if(!sslCertificateChecker.execute) {
        return null;
      }
      
      send(
        c.blackBright(`Found nl.xservices.plugins.SSLCertificateChecker, ` +
          `overriding SSLCertificateChecker.execute()`),
      );

      const SSLCertificateCheckerExecute = sslCertificateChecker.execute.overload("java.lang.String", 
        "org.json.JSONArray", "org.apache.cordova.CallbackContext");

      SSLCertificateCheckerExecute.implementation = function (str, jsonArray, callBackContext) {
        qsend(quiet,
          c.blackBright(`[${ident}] `) + `Called ` +
          c.green(`SSLCertificateChecker.execute()`) +
          `, not throwing an exception.`,
        );
        callBackContext.success("CONNECTION_SECURE");
        return true;
      };

      return SSLCertificateCheckerExecute;

    } catch (err) {
      if ((err as Error).message.indexOf("java.lang.ClassNotFoundException") !== 0) {
        throw err;
      }
      return null;
    }
  });
};

// the main exported function to run all of the pinning bypass methods known
export const disable = async (q: boolean): Promise<void> => {
  if (q) {
    send(c.yellow(`Quiet mode enabled. Not reporting invocations.`));
    quiet = true;
  }

  const job: jobs.Job = new jobs.Job(jobs.identifier(), "android-sslpinning-disable");
  
  job.addImplementation(await sslContextEmptyTrustManager(job.identifier));
  // Exceptions can cause undefined values if classes are not found. Thus addImplementation only adds if function was hooked
  job.addImplementation(await okHttp3CertificatePinnerCheck(job.identifier));
  job.addImplementation(await okHttp3CertificatePinnerCheckOkHttp(job.identifier));
  job.addImplementation(await appceleratorTitaniumPinningTrustManager(job.identifier));
  job.addImplementation(await trustManagerImplVerifyChainCheck(job.identifier));
  job.addImplementation(await trustManagerImplCheckTrustedRecursiveCheck(job.identifier));
  job.addImplementation(await phoneGapSSLCertificateChecker(job.identifier));

  jobs.add(job);
};


================================================
FILE: agent/src/android/proxy.ts
================================================
import { 
  wrapJavaPerform,
  Java
} from "./lib/libjava.js";
import { colors as c } from "../lib/color.js";

export const set = (host: string, port: string): Promise<void> => {
  return wrapJavaPerform(() => {
    var proxyHost = host;
    var proxyPort = port;

    var System = Java.use("java.lang.System");

    if (System != undefined) {
      send(c.green(`Setting properties for a proxy`));
      System.setProperty("http.proxyHost", proxyHost);
      System.setProperty("http.proxyPort", proxyPort);

      System.setProperty("https.proxyHost", proxyHost);
      System.setProperty("https.proxyPort", proxyPort);

      send(`${c.green(`Proxy configured to ` + proxyHost + ` ` + proxyPort)}`);
    }
  });
};


================================================
FILE: agent/src/android/root.ts
================================================
import { colors as c } from "../lib/color.js";
import * as jobs from "../lib/jobs.js";
import {
  wrapJavaPerform,
  Java
} from "./lib/libjava.js";
import {
  File,
  IOException,
  JavaString,
  Runtime
} from "./lib/types.js";

const commonPaths = [
  "/data/local/bin/su",
  "/data/local/su",
  "/data/local/xbin/su",
  "/dev/com.koushikdutta.superuser.daemon/",
  "/sbin/su",
  "/system/app/Superuser.apk",
  "/system/bin/failsafe/su",
  "/system/bin/su",
  "/system/etc/init.d/99SuperSUDaemon",
  "/system/sd/xbin/su",
  "/system/xbin/busybox",
  "/system/xbin/daemonsu",
  "/system/xbin/su",
];

const testKeysCheck = (success: boolean, ident: number): any => {
  return wrapJavaPerform(() => {
    const JavaString: JavaString = Java.use("java.lang.String");

    JavaString.contains.implementation = function (name) {
      if (name !== "test-keys") {
        return this.contains.call(this, name);
      }

      if (success) {
        send(c.blackBright(`[${ident}] `) + `Marking "test-keys" check as ` + c.green(`successful`) + `.`);
        return true;
      }

      send(c.blackBright(`[${ident}] `) + `Marking "test-keys" check as ` + c.green(`failed`) + `.`);
      return false;
    };

    return JavaString.contains;
  });
};

const execSuCheck = (success: boolean, ident: number): any => {
  return wrapJavaPerform(() => {
    const JavaRuntime: Runtime = Java.use("java.lang.Runtime");
    const iOException: IOException = Java.use("java.io.IOException");
    const JavaRuntime_exec = JavaRuntime.exec.overload("java.lang.String");

    JavaRuntime_exec.implementation = function (command: string) {
      if (command.endsWith("su")) {
        if (success) {
          send(c.blackBright(`[${ident}] `) + `Check for 'su' using command exec detected, allowing.`);
          return this.apply(this, arguments);
        }

        send(c.blackBright(`[${ident}] `) + `Check for 'su' using command exec detected, throwing IOException.`);
        throw iOException.$new("objection anti-root");
      }

      // call the original method
      return this.exec.overload("java.lang.String").call(this, command);
    };

    return JavaRuntime_exec;
  });
};

const fileExistsCheck = (success: boolean, ident: number): any => {
  return wrapJavaPerform(() => {
    const JavaFile: File = Java.use("java.io.File");
    JavaFile.exists.implementation = function () {
      const filename = this.getAbsolutePath();
      if (commonPaths.indexOf(filename) >= 0) {
        if (success) {
          send(
            c.blackBright(`[${ident}] `) +
            `File existence check for ${filename} detected, marking as ${c.green("true")}.`,
          );
          return true;
        }

        send(
          c.blackBright(`[${ident}] `) +
          `File existence check for ${filename} detected, marking as ${c.green("false")}.`,
        );
        return false;
      }

      // call the original method
      return this.exists.call(this);
    };

    return JavaFile.exists;
  });
};

// RootBeer: https://github.com/scottyab/rootbeer

const rootBeerIsRooted = (success: boolean, ident: number): any => {
  return wrapJavaPerform(() => {
    const RootBeer = Java.use("com.scottyab.rootbeer.RootBeer");
    const RootBeer_isRooted = RootBeer.isRooted.overload();

    RootBeer_isRooted.implementation = function () {
      if (success) {
        send(
          c.blackBright(`[${ident}] `) +
          `RootBeer->isRooted() check detected, marking as ${c.green("true")}.`,
        );
        return true;
      }

      send(
        c.blackBright(`[${ident}] `) +
        `RootBeer->isRooted() check detected, marking as ${c.green("false")}.`,
      );
      return false;
    };

    return RootBeer_isRooted;
  });
};

const rootBeerCheckForBinary = (success: boolean, ident: number): any => {
  return wrapJavaPerform(() => {
    const RootBeer = Java.use("com.scottyab.rootbeer.RootBeer");
    RootBeer.checkForBinary.overload('java.lang.String').implementation = function () {
      if (success) {
        send(
          c.blackBright(`[${ident}] `) +
          `RootBeer->checkForBinary() check detected, marking as ${c.green("true")}.`,
        );
        return true;
      }

      send(
        c.blackBright(`[${ident}] `) +
        `RootBeer->checkForBinary() check detected, marking as ${c.green("false")}.`,
      );
      return false;
    };

    return RootBeer.checkForBinary;
  });
};

const rootBeerCheckForDangerousProps = (success: boolean, ident: number): any => {
  return wrapJavaPerform(() => {
    const RootBeer = Java.use("com.scottyab.rootbeer.RootBeer");
    RootBeer.checkForDangerousProps.overload().implementation = function () {
      if (success) {
        send(
          c.blackBright(`[${ident}] `) +
          `RootBeer->checkForDangerousProps() check detected, marking as ${c.green("true")}.`,
        );
        return true;
      }

      send(
        c.blackBright(`[${ident}] `) +
        `RootBeer->checkForDangerousProps() check detected, marking as ${c.green("false")}.`,
      );
      return false;
    };

    return RootBeer.checkForDangerousProps;
  });
};

const rootBeerDetectRootCloakingApps = (success: boolean, ident: number): any => {
  return wrapJavaPerform(() => {
    const RootBeer = Java.use("com.scottyab.rootbeer.RootBeer");
    const RootBeer_detectRootCloakingApps = RootBeer.detectRootCloakingApps.overload();

    RootBeer_detectRootCloakingApps.implementation = function () {
      if (success) {
        send(
          c.blackBright(`[${ident}] `) +
          `RootBeer->detectRootCloakingApps() check detected, marking as ${c.green("true")}.`,
        );
        return true;
      }

      send(
        c.blackBright(`[${ident}] `) +
        `RootBeer->detectRootCloakingApps() check detected, marking as ${c.green("false")}.`,
      );
      return false;
    };

    return RootBeer_detectRootCloakingApps;
  });
};

const rootBeerCheckSuExists = (success: boolean, ident: number): any => {
  return wrapJavaPerform(() => {
    const RootBeer = Java.use("com.scottyab.rootbeer.RootBeer");
    RootBeer.checkSuExists.overload().implementation = function () {
      if (success) {
        send(
          c.blackBright(`[${ident}] `) +
          `RootBeer->checkSuExists() check detected, marking as ${c.green("true")}.`,
        );
        return true;
      }

      send(
        c.blackBright(`[${ident}] `) +
        `RootBeer->checkSuExists() check detected, marking as ${c.green("false")}.`,
      );
      return false;
    };

    return RootBeer.checkSuExists;
  });
};

const rootBeerDetectTestKeys = (success: boolean, ident: number): any => {
  return wrapJavaPerform(() => {
    const RootBeer = Java.use("com.scottyab.rootbeer.RootBeer");
    RootBeer.detectTestKeys.overload().implementation = function () {
      if (success) {
        send(
          c.blackBright(`[${ident}] `) +
          `RootBeer->detectTestKeys() check detected, marking as ${c.green("true")}.`,
        );
        return true;
      }

      send(
        c.blackBright(`[${ident}] `) +
        `RootBeer->detectTestKeys() check detected, marking as ${c.green("false")}.`,
      );
      return false;
    };

    return RootBeer.detectTestKeys;
  });
};

const rootBeerCheckSeLinux = (success: boolean, ident: number): any => {
  return wrapJavaPerform(() => {
    try {
      const Util = Java.use("com.scottyab.rootbeer.util");
      Util.isSelinuxFlagInEnabled.overload().implementation = function () {
        if (success) {
          send(
            c.blackBright(`[${ident}]`) +
            `Rootbeer.util->isSelinuxFlagInEnabled() check detected, marking as ${c.green("true")}`,
          );
          return true;
        }
  
        send(
          c.blackBright(`[${ident}] `) +
          `Rootbeer.util->isSelinuxFlagInEnabled() check detected, marking as ${c.green("false")}`,
        );
        return false;
      };
  
      return Util.isSelinuxFlagInEnabled;
    } catch (err) {
      if ((err as Error).message.indexOf("java.lang.ClassNotFoundException") === 0) {
        return null;
      };
      throw err;
    }
  });
};

const rootBeerNative = (success: boolean, ident: number): any => {
  return wrapJavaPerform(() => {
    const RootBeerNative = Java.use("com.scottyab.rootbeer.RootBeerNative");
    const RootBeerNative_checkForRoot = RootBeerNative.checkForRoot.overload('[Ljava.lang.Object;');
    RootBeerNative_checkForRoot.implementation = function () {
      if (success) {
        send(
          c.blackBright(`[${ident}] `) +
          `RootBeerNative->checkForRoot() check detected, marking as ${c.green("1")}.`,
        );
        return 1;
      }

      send(
        c.blackBright(`[${ident}] `) +
        `RootBeerNative->checkForRoot() check detected, marking as ${c.green("0")}.`,
      );
      return 0;
    };

    return RootBeerNative_checkForRoot;
  });
};

// ref: https://www.ayrx.me/gantix-jailmonkey-root-detection-bypass/
const jailMonkeyBypass = (success: boolean, ident: number): Promise<any> => {
  return wrapJavaPerform(() => {
    try {
      const JavaJailMonkeyModule = Java.use("com.gantix.JailMonkey.JailMonkeyModule");
      const JavaHashMap = Java.use("java.util.HashMap");
      const JavaBoolean = Java.use("java.lang.Boolean")
      const JavaFalseObject = JavaBoolean.FALSE.value;
      const JavaTrueObject = JavaBoolean.TRUE.value;

      JavaJailMonkeyModule.getConstants.implementation = function () {
        if (success) {
          send(
            c.blackBright(`[${ident}] `) +
            `RootBeer->checkForDangerousProps() check detected, marking as ${c.green("true")} for all keys.`,
          );
          const hm = JavaHashMap.$new();
          hm.put("isJailBroken", JavaTrueObject);
          hm.put("hookDetected", JavaTrueObject);
          hm.put("canMockLocation", JavaTrueObject);
          hm.put("isOnExternalStorage", JavaTrueObject);
          hm.put("AdbEnabled", JavaTrueObject);
          
          return hm;
        }
        send(
          c.blackBright(`[${ident}] `) +
          `JailMonkeyModule.getConstants() called, returning ${c.green("false")} for all keys.`
        );

        const hm = JavaHashMap.$new();
        hm.put("isJailBroken", JavaFalseObject);
        hm.put("hookDetected", JavaFalseObject);
        hm.put("canMockLocation", JavaFalseObject);
        hm.put("isOnExternalStorage", JavaFalseObject);
        hm.put("AdbEnabled", JavaFalseObject);

        return hm;
      };

      return JavaJailMonkeyModule.getConstants;
    } catch (err) {
      if ((err as Error).message.indexOf("java.lang.ClassNotFoundException") === 0) {
        return null;
      };
      throw err;
    }
  });
};

export const disable = async (): Promise<void> => {
  const job: jobs.Job = new jobs.Job(jobs.identifier(), 'root-detection-disable');

  job.addImplementation(await testKeysCheck(false, job.identifier));
  job.addImplementation(await execSuCheck(false, job.identifier));
  job.addImplementation(await fileExistsCheck(false, job.identifier));
  job.addImplementation(await jailMonkeyBypass(false, job.identifier));
  // RootBeer functions
  job.addImplementation(await rootBeerIsRooted(false, job.identifier));
  job.addImplementation(await rootBeerCheckForBinary(false, job.identifier));
  job.addImplementation(await rootBeerCheckForDangerousProps(false, job.identifier));
  job.addImplementation(await rootBeerDetectRootCloakingApps(false, job.identifier));
  job.addImplementation(await rootBeerCheckSuExists(false, job.identifier));
  job.addImplementation(await rootBeerDetectTestKeys(false, job.identifier));
  job.addImplementation(await rootBeerNative(false, job.identifier));
  job.addImplementation(await rootBeerCheckSeLinux(false, job.identifier));

  jobs.add(job);
};

export const enable = async (): Promise<void> => {
  const job: jobs.Job = new jobs.Job(jobs.identifier(), "root-detection-enable");

  job.addImplementation(await testKeysCheck(true, job.identifier));
  job.addImplementation(await execSuCheck(true, job.identifier));
  job.addImplementation(await fileExistsCheck(true, job.identifier));
  job.addImplementation(await jailMonkeyBypass(true, job.identifier));

  // RootBeer functions
  job.addImplementation(await rootBeerIsRooted(true, job.identifier));
  job.addImplementation(await rootBeerCheckForBinary(true, job.identifier));
  job.addImplementation(await rootBeerCheckForDangerousProps(true, job.identifier));
  job.addImplementation(await rootBeerDetectRootCloakingApps(true, job.identifier));
  job.addImplementation(await rootBeerCheckSuExists(true, job.identifier));
  job.addImplementation(await rootBeerDetectTestKeys(true, job.identifier));
  job.addImplementation(await rootBeerNative(true, job.identifier));
  job.addImplementation(await rootBeerCheckSeLinux(false, job.identifier));

  jobs.add(job);
};


================================================
FILE: agent/src/android/shell.ts
================================================
import { IExecutedCommand } from "./lib/interfaces.js";
import {
  wrapJavaPerform,
  Java
} from "./lib/libjava.js";
import {
  BufferedReader,
  InputStreamReader,
  Runtime,
  StringBuilder
} from "./lib/types.js";


// Executes shell commands on an Android device using Runtime.getRuntime().exec()
export const execute = (cmd: string): Promise<IExecutedCommand> => {
  // -- Sample Java
  //
  // Process command = Runtime.getRuntime().exec("ls -l /");
  // InputStreamReader isr = new InputStreamReader(command.getInputStream());
  // BufferedReader br = new BufferedReader(isr);
  //
  // StringBuilder sb = new StringBuilder();
  // String line = "";
  //
  // while ((line = br.readLine()) != null) {
  //     sb.append(line + "\n");
  // }
  //
  // String output = sb.toString();
  return wrapJavaPerform(() => {

    const runtime: Runtime = Java.use("java.lang.Runtime");
    const inputStreamReader: InputStreamReader = Java.use("java.io.InputStreamReader");
    const bufferedReader: BufferedReader = Java.use("java.io.BufferedReader");
    const stringBuilder: StringBuilder = Java.use("java.lang.StringBuilder");

    // Run the command
    const command = runtime.getRuntime().exec(cmd);

    // Read 'stderr'
    const stdErrInputStreamReader: InputStreamReader = inputStreamReader.$new(command.getErrorStream());
    let bufferedReaderInstance: BufferedReader = bufferedReader.$new(stdErrInputStreamReader);

    const stdErrStringBuilder: StringBuilder = stringBuilder.$new();
    let lineBuffer: string;

    // tslint:disable-next-line:no-conditional-assignment
    while ((lineBuffer = bufferedReaderInstance.readLine()) != null) {
      stdErrStringBuilder.append(lineBuffer + "\n");
    }

    // Read 'stdout'
    const stdOutInputStreamReader: InputStreamReader = inputStreamReader.$new(command.getInputStream());
    bufferedReaderInstance = bufferedReader.$new(stdOutInputStreamReader);

    const stdOutStringBuilder = stringBuilder.$new();
    lineBuffer = "";

    // tslint:disable-next-line:no-conditional-assignment
    while ((lineBuffer = bufferedReaderInstance.readLine()) != null) {
      stdOutStringBuilder.append(lineBuffer + "\n");
    }

    return {
      command: cmd,
      stdErr: stdErrStringBuilder.toString(),
      stdOut: stdOutStringBuilder.toString(),
    };
  });
};


================================================
FILE: agent/src/android/userinterface.ts
================================================
import { colors as c } from "../lib/color.js";
import {
  wrapJavaPerform,
  Java
} from "./lib/libjava.js";
import {
  Activity,
  ActivityClientRecord,
  ActivityThread,
  Bitmap,
  ByteArrayOutputStream,
  CompressFormat,
} from "./lib/types.js";


// https://developer.android.com/reference/android/view/WindowManager.LayoutParams.html#FLAG_SECURE
const FLAG_SECURE = 0x00002000;

export const screenshot = (): Promise<any> => {
  return wrapJavaPerform(() => {
    // Take a screenshot by making use of a View's drawing cache:
    //  ref: https://developer.android.com/reference/android/view/View.html#getDrawingCache(boolean)
    const activityThread: ActivityThread = Java.use("android.app.ActivityThread");
    const activity: Activity = Java.use("android.app.Activity");
    const activityClientRecord: ActivityClientRecord = Java.use("android.app.ActivityThread$ActivityClientRecord");
    const bitmap: Bitmap = Java.use("android.graphics.Bitmap");
    const byteArrayOutputStream: ByteArrayOutputStream = Java.use("java.io.ByteArrayOutputStream");
    const compressFormat: CompressFormat = Java.use("android.graphics.Bitmap$CompressFormat");

    let bytes;

    const currentActivityThread = activityThread.currentActivityThread();
    const activityRecords = currentActivityThread.mActivities.value.values().toArray();
    let currentActivity;

    for (const i of activityRecords) {
      const activityRecord = Java.cast(i, activityClientRecord);

      if (!activityRecord.paused.value) {
        currentActivity = Java.cast(Java.cast(activityRecord, activityClientRecord).activity.value, activity);
        break;
      }
    }

    if (currentActivity) {
      const view = currentActivity.getWindow().getDecorView().getRootView();
      view.setDrawingCacheEnabled(true);
      const bitmapInstance = bitmap.createBitmap(view.getDrawingCache());
      view.setDrawingCacheEnabled(false);

      const outputStream: ByteArrayOutputStream = byteArrayOutputStream.$new();
      bitmapInstance.compress(compressFormat.PNG.value, 100, outputStream);
      bytes = outputStream.buf.value;
    }

    return bytes;
  });
};

export const setFlagSecure = (v: boolean): Promise<void> => {
  return wrapJavaPerform(() => {
    const activityThread: ActivityThread = Java.use("android.app.ActivityThread");
    const activity: Activity = Java.use("android.app.Activity");
    const activityClientRecord: ActivityClientRecord = Java.use("android.app.ActivityThread$ActivityClientRecord");

    const currentActivityThread = activityThread.currentActivityThread();
    const activityRecords = currentActivityThread.mActivities.value.values().toArray();
    let currentActivity;

    for (const i of activityRecords) {
      const activityRecord = Java.cast(i, activityClientRecord);
      if (!activityRecord.paused.value) {
        currentActivity = Java.cast(Java.cast(activityRecord, activityClientRecord).activity.value, activity);
        break;
      }
    }

    if (currentActivity) {
      // Somehow the next line prevents Frida from throwing an abort error
      currentActivity.getWindow();
      // Set flag and trigger update (Throws abort without first calling getWindow())
      Java.scheduleOnMainThread(() => {
        currentActivity.getWindow().setFlags(v ? FLAG_SECURE : 0, FLAG_SECURE);
        send(`FLAG_SECURE set to ${c.green(v.toString())}`);
      });
    }
  });
};


================================================
FILE: agent/src/generic/custom.ts
================================================
export const evaluate = (js: string): void => {
  // tslint:disable-next-line:no-eval
  eval(js);
};


================================================
FILE: agent/src/generic/environment.ts
================================================
import { ObjC } from "../ios/lib/libobjc.js";
import {
  getApplicationContext,
  wrapJavaPerform,
  Java
} from "../android/lib/libjava.js";
import {
  NSSearchPaths,
  NSUserDomainMask
} from "../ios/lib/constants.js";
import {
  getNSFileManager,
  getNSMainBundle
} from "../ios/lib/helpers.js";
import { NSBundle } from "../ios/lib/types.js";
import { DeviceType } from "../lib/constants.js";
import {
  IAndroidPackage,
  IFridaInfo,
  IIosBundlePaths,
  IIosPackage
} from "../lib/interfaces.js";


// small helper function to lookup ios bundle paths
const getPathForNSLocation = (NSPath: NSSearchPaths): string => {
  const p = getNSFileManager().URLsForDirectory_inDomains_(NSPath, NSUserDomainMask).lastObject();

  if (p) {
    return p.path().toString();
  }

  return "";
};

export const runtime = (): string => {
  if (ObjC.available) { return DeviceType.IOS; }
  if (Java.available) { return DeviceType.ANDROID; }

  return DeviceType.UNKNOWN;
};

export const frida = (): IFridaInfo => {
  return {
    arch: Process.arch,
    debugger: Process.isDebuggerAttached(),
    heap: Frida.heapSize,
    platform: Process.platform,
    runtime: Script.runtime,
    version: Frida.version,
  };
};

export const iosPackage = (): IIosPackage => {
  // -- Sample Objective-C
  //
  // NSFileManager *fm = [NSFileManager defaultManager];
  // NSString *pictures = [[fm URLsForDirectory:NSPicturesDirectory inDomains:NSUserDomainMask] lastObject].path;
  // NSBundle *bundle = [NSBundle mainBundle];
  // NSString *bundlePath = [bundle bundlePath];
  // NSString *receipt = [bundle appStoreReceiptURL].path;
  // NSString *resourcePath = [bundle resourcePath];

  const { UIDevice } = ObjC.classes;
  const mb: NSBundle = getNSMainBundle();

  return {
    applicationName: mb.objectForInfoDictionaryKey_("CFBundleIdentifier").toString(),
    deviceName: UIDevice.currentDevice().name().toString(),
    identifierForVendor: UIDevice.currentDevice().identifierForVendor().toString(),
    model: UIDevice.currentDevice().model().toString(),
    systemName: UIDevice.currentDevice().systemName().toString(),
    systemVersion: UIDevice.currentDevice().systemVersion().toString(),
  };
};

export const iosPaths = (): IIosBundlePaths => {
  const mb: NSBundle = getNSMainBundle();

  return {
    BundlePath: mb.bundlePath().toString(),
    CachesDirectory: getPathForNSLocation(NSSearchPaths.NSCachesDirectory),
    DocumentDirectory: getPathForNSLocation(NSSearchPaths.NSDocumentDirectory),
    LibraryDirectory: getPathForNSLocation(NSSearchPaths.NSLibraryDirectory),
  };
};

export const androidPackage = (): Promise<IAndroidPackage> => {
  return wrapJavaPerform(() => {

    // https://developer.android.com/reference/android/os/Build.html
    const Build: any = Java.use("android.os.Build");

    return {
      application_name: getApplicationContext().getPackageName(),
      board: Build.BOARD.value.toString(),
      brand: Build.BRAND.value.toString(),
      device: Build.DEVICE.value.toString(),
      host: Build.HOST.value.toString(),
      id: Build.ID.value.toString(),
      model: Build.MODEL.value.toString(),
      product: Build.PRODUCT.value.toString(),
      user: Build.USER.value.toString(),
      version: Java.androidVersion,
    };
  });
};

export const androidPaths = (): Promise<any> => {
  // -- Sample Java
  //
  // getApplicationContext().getFilesDir().getAbsolutePath()
  return wrapJavaPerform(() => {
    const context = getApplicationContext();

    return {
      cacheDirectory: context.getCacheDir().getAbsolutePath().toString(),
      codeCacheDirectory: "getCodeCacheDir" in context ? context.getCodeCacheDir()
        .getAbsolutePath().toString() : "n/a",
      externalCacheDirectory: context.getExternalCacheDir().getAbsolutePath().toString(),
      filesDirectory: context.getFilesDir().getAbsolutePath().toString(),
      obbDir: context.getObbDir().getAbsolutePath().toString(),
      packageCodePath: context.getPackageCodePath().toString(),
    };
  });
};


================================================
FILE: agent/src/generic/http.ts
================================================
import * as fs from "frida-fs";
import * as httpLib from "http";
import * as url from "url";
import { colors as c } from "../lib/color.js";

let httpServer: httpLib.Server;
let listenPort: number;
let servePath: string;

const log = (m: string): void => {
  c.log(`[http server] ${m}`);
};

const dirListingHTML = (pwd: string, path: string): string => {
  let h = `
    <html>
      <body>
        <h2 style="margin: 0;">Index Of ${path}</h2>
        {file_listing}
      </body>
    </html>
    `;

  h = h.replace(`{file_listing}`, () => {
    return fs.list(pwd + decodeURIComponent(path)).map((f) => {
      if (f.name === '.') return;

      // Add a slash at the end if it is a directory.
      var fname = f.name + (f.type == 4 ? '/' : '');
      
      if (path !== '/') {
        return `<a href="${path + fname}">${fname}</a>`;
      } else if (fname !== '../') {
        return `<a href="${fname}">${fname}</a>`;
      }
    }).join("<br>");
  });

  return h;
};

export const start = (pwd: string, port: number = 9000): void => {
  log(c.redBright(`httpServer module not currently available.`))

  if (httpServer) {
    log(c.redBright(`Server appears to already be running`));
    return;
  }

  // if (!pwd.endsWith("/")) {
  //   pwd = pwd + "/";
  // }
  // log(`${c.blackBright(`Starting HTTP server in: ${pwd}`)}`);
  // servePath = pwd;

  // httpServer = httpLib.createServer((req, res) => {
    // if (req.method && req.url) {
    //   log(`${c.greenBright(req.method)} ${req.url}`);
    // } else {
    //   log(`${c.redBright('Missing URL or request method.')}`);
    //   return;
    // }

    // try {    
    //   const parsedUrl = url.parse(req.url);  
    //   const fileLocation = pwd + decodeURIComponent(parsedUrl.path);
      
    //   if (fs.statSync(fileLocation).isDirectory()) {
    //     res.end(dirListingHTML(pwd, parsedUrl.path));
    //     return;
    //   }

    //   res.setHeader("Content-type", "application/octet-stream");

    //   // Check that we are not reading an empty file
    //   if (fs.statSync(fileLocation).size !== 0) {
    //     const file = fs.readFileSync(fileLocation);
    //     res.write(file, 'utf-8')
    //   }
    //   res.end();
        
    // } catch (error) {
    //   if (error instanceof Error && error.message == "No such file or directory") {
    //     res.statusCode = 404;
    //     res.end("File not found")
    //   } else {
    //     if (error instanceof Error) {
    //       log(c.redBright(`${error.stack}`));
    //     } else {
    //       log(c.redBright(`${error}`));
    //     }
       
    //     res.statusCode = 500;
    //     res.end("Internal Server Error")
    //   }
    // }
  // });

  // httpServer.listen(port);
  // listenPort = port;
};

export const stop = (): void => {
  if (!httpServer) {
    log(c.yellowBright(`Server does not appear to be running.`));
    return;
  }

  log(c.blackBright(`Waiting for client connections to close then stopping...`));
  httpServer.close()
    .once("close", () => {
      log(c.blackBright(`Server closed.`));
      httpServer = undefined;
    });
};

export const status = (): void => {
  if (httpServer && httpServer.listening) {
    log(`Server is running on port ` +
      `${c.greenBright(listenPort.toString())} serving ${c.greenBright(servePath)}`);
    return;
  }

  log(c.yellowBright(`Server does not appear to be running.`));
};


================================================
FILE: agent/src/generic/memory.ts
================================================
import { colors } from "../lib/color.js"

export const listModules = (): Module[] => {
  return Process.enumerateModules();
};

export const listExports = (name: string): ModuleExportDetails[] => {
  const mod: Module[] = Process.enumerateModules().filter((m) => m.name === name);
  if (mod.length <= 0) {
    return [];
  }
  return mod[0].enumerateExports();
};

export const listRanges = (protection: string = "rw-"): RangeDetails[] => {
  return Process.enumerateRanges(protection);
};

export const dump = (address: string, size: number): ArrayBuffer => {
  // Originally part of Frida <=11 but got removed in 12.
  // https://github.com/frida/frida-python/commit/72899a4315998289fb171149d62477ba7d1fcb91
  const data = new NativePointer(address).readByteArray(size);
  if (data) {
    return data;
  }
  else {
    return new ArrayBuffer(0);
  }
};

export const search = (pattern: string, onlyOffsets: boolean = false): string[] => {
  const addresses = listRanges("rw-")
    .map((range) => {
      return Memory.scanSync(range.base, range.size, pattern)
        .map((match) => {
          if (!onlyOffsets) {
            colors.log(hexdump(match.address, {
              ansi: true,
              header: false,
              length: 48,
            }));
          }
          return match.address.toString();
        });
    }).filter((m) => m.length !== 0);

  if (addresses.length <= 0) {
    return [];
  }

  return addresses.reduce((a, b) => a.concat(b));
};

export const replace = (pattern: string, replace: number[]): string[] => {  
  return search(pattern, true).map((match) => {
    write(match, replace);
    return match;
  })
};

export const write = (address: string, value: number[]): void => {
  new NativePointer(address).writeByteArray(value);
};


================================================
FILE: agent/src/generic/ping.ts
================================================
export const ping = (): boolean => true;


================================================
FILE: agent/src/index.ts
================================================
import { ping } from "./generic/ping.js";
import { android } from "./rpc/android.js";
import { env } from "./rpc/environment.js";
import { ios } from "./rpc/ios.js";
import { jobs } from "./rpc/jobs.js";
import { memory } from "./rpc/memory.js";
import { other } from "./rpc/other.js";

rpc.exports = {
  ...android,
  ...ios,
  ...env,
  ...jobs,
  ...memory,
  ...other,
  ping: (): boolean => ping(),
};


================================================
FILE: agent/src/ios/binary.ts
================================================
import macho from "macho-ts";

import * as iosfilesystem from "./filesystem.js";
import { IBinaryModuleDictionary } from "./lib/interfaces.js";


const isEncrypted = (cmds: any[]): boolean => {
  for (const cmd of cmds) {
    // https://opensource.apple.com/source/cctools/cctools-921/include/mach-o/loader.h.auto.html
    // struct encryption_info_command {
    //    [ ... ]
    //   uint32_t	cryptid;	/* which enryption system, 0 means not-encrypted yet */
    // };
    if (cmd.type === "encryption_info" || cmd.type === "encryption_info_64") {
      if (cmd.id !== 0) {
        return true;
      }
    }
  }
  return false;
};

export const info = (): IBinaryModuleDictionary => {
  const modules = Process.enumerateModules();
  const parsedModules: IBinaryModuleDictionary = {};

  modules.forEach((a) => {
    if (!a.path.includes(".app")) {
      return;
    }

    const imports: Set<string> = new Set(a.enumerateImports().map((i) => i.name));
    const fb = iosfilesystem.readFile(a.path);
    if (typeof(fb) == 'string') {
      return;
    }

    try {
      const exe = macho.parse(fb);

      parsedModules[a.name] = {
        arc: imports.has("objc_release"),
        canary: imports.has("__stack_chk_fail"),
        encrypted: isEncrypted(exe.cmds),
        pie: exe.flags.pie ? true : false,
        rootSafe: exe.flags.root_safe ? true : false,
        stackExec: exe.flags.allow_stack_execution ? true : false,
        type: exe.filetype,
      };

    } catch (e) {
      // ignore any errors. especially ones where
      // the target path is not a mach-o
    }
  });

  return parsedModules;
};


================================================
FILE: agent/src/ios/binarycookies.ts
================================================
import { ObjC } from "../ios/lib/libobjc.js";
import { IIosCookie } from "./lib/interfaces.js";
import {
  NSArray,
  NSData,
  NSHTTPCookieStorage
} from "./lib/types.js";


export const get = (): IIosCookie[] => {

  // -- Sample Objective-C
  //
  // NSHTTPCookieStorage *cs = [NSHTTPCookieStorage sharedHTTPCookieStorage];
  // NSArray *cookies = [cs cookies];
  const cookies: IIosCookie[] = [];

  const HTTPCookieStorage = ObjC.classes.NSHTTPCookieStorage;
  const cookieStore: NSHTTPCookieStorage = HTTPCookieStorage.sharedHTTPCookieStorage();
  const cookieJar: NSArray = cookieStore.cookies();

  if (cookieJar.count() <= 0) {
    return cookies;
  }

  for (let i = 0; i < cookieJar.count(); i++) {

    // get the actual cookie from the jar
    const cookie: NSData = cookieJar.objectAtIndex_(i);

    // <NSHTTPCookie version:0 name:"__cfduid" value:"d2546c60b09a710a151d974e662f40c081498064665"
    // expiresDate:2018-06-21 17:04:25 +0000 created:2017-06-21 17:04:26 +0000 sessionOnly:FALSE
    // domain:".swapi.co" partition:"none" path:"/" isSecure:FALSE>
    const cookieData: IIosCookie = {
      domain: cookie.domain().toString(),
      expiresDate: cookie.expiresDate() ? cookie.expiresDate().toString() : "null",
      isHTTPOnly: cookie.isHTTPOnly().toString(),
      isSecure: cookie.isSecure().toString(),
      name: cookie.name().toString(),
      path: cookie.path().toString(),
      value: cookie.value().toString(),
      version: cookie.version().toString(),
    };

    cookies.push(cookieData);
  }

  return cookies;
};


================================================
FILE: agent/src/ios/bundles.ts
================================================
import { ObjC } from "../ios/lib/libobjc.js";
import { BundleType } from "./lib/constants.js";
import { IFramework } from "./lib/interfaces.js";
import {
  NSArray,
  NSBundle,
  NSDictionary
} from "./lib/types.js";


// https://developer.apple.com/documentation/foundation/nsbundle/1408056-allframeworks?language=objc
// https://developer.apple.com/documentation/foundation/nsbundle/1413705-allbundles?language=objc
export const getBundles = (type: BundleType): IFramework[] => {

  // -- Sample ObjC
  //
  // for (id ob in [NSBundle allBundles]) {
  //   NSDictionary *i = [ob infoDictionary];
  //   NSString *p = [ob bundlePath];
  //   NSLog(@"%@:%@ @ %@", [i objectForKey:@"CFBundleIdentifier"],
  //         [i objectForKey:@"CFBundleShortVersionString"], p);
  // }

  // Figure out which bundle type to enumerate
  let frameworks: NSArray;
  if (type === BundleType.NSBundleFramework) {
    frameworks = ObjC.classes.NSBundle.allFrameworks();
  } else if (type === BundleType.NSBundleAllBundles) {
    frameworks = ObjC.classes.NSBundle.allBundles();
  }

  const appBundles: IFramework[] = [];
  const frameworksLength: number = frameworks.count().valueOf();

  for (let i = 0; i !== frameworksLength; i++) {

    // get information about the bundle itself
    const bundle: NSBundle = frameworks.objectAtIndex_(i);
    const bundleInfo: NSDictionary = bundle.infoDictionary();

    // get values for the keys we are interested in
    const bundlePath: string = bundle.bundlePath();
    const CFBundleIdentifier: string = bundleInfo.objectForKey_("CFBundleIdentifier");
    const CFBundleShortVersionString: string = bundleInfo.objectForKey_("CFBundleShortVersionString");
    const CFBundleExecutable: string = bundleInfo.objectForKey_("CFBundleExecutable");

    appBundles.push({
      bundle: CFBundleIdentifier ? CFBundleIdentifier.toString() : null,
      executable: CFBundleExecutable ? CFBundleExecutable.toString() : null,
      path: bundlePath.toString(),
      version: CFBundleShortVersionString ? CFBundleShortVersionString.toString() : null,
    });
  }

  return appBundles;
};


================================================
FILE: agent/src/ios/credentialstorage.ts
================================================
import { ObjC } from "../ios/lib/libobjc.js";
import { ICredential } from "./lib/interfaces.js";
import {
  NSArray,
  NSData,
  NSURLCredentialStorage
} from "./lib/types.js";


export const dump = (): ICredential[] => {

  // -- Sample ObjC to create and dump a credential
  // NSURLProtectionSpace *ps = [[NSURLProtectionSpace alloc]
  //  initWithHost:@"foo.com" port:80 protocol:@"https" realm:NULL
  //  authenticationMethod:NSURLAuthenticationMethodHTTPBasic];
  // NSURLCredential *creds = [[NSURLCredential alloc]
  //  initWithUser:@"user" password:@"password" persistence:NSURLCredentialPersistencePermanent];
  // NSURLCredentialStorage *cs = [NSURLCredentialStorage sharedCredentialStorage];

  // [cs setCredential:creds forProtectionSpace:ps];

  // NSDictionary *allcreds = [cs allCredentials];
  // NSLog(@"%@", allcreds);

  const credentialStorage: NSURLCredentialStorage = ObjC.classes.NSURLCredentialStorage;
  const data: ICredential[] = [];
  const credentialsDict: NSArray = credentialStorage.sharedCredentialStorage().allCredentials();

  if (credentialsDict.count() <= 0) {
    return data;
  }

  const protectionSpaceEnumerator = credentialsDict.keyEnumerator();
  let urlProtectionSpace;

  // tslint:disable-next-line:no-conditional-assignment
  while ((urlProtectionSpace = protectionSpaceEnumerator.nextObject()) !== null) {

    const userNameEnumerator = credentialsDict.objectForKey_(urlProtectionSpace).keyEnumerator();
    let userName;

    // tslint:disable-next-line:no-conditional-assignment
    while ((userName = userNameEnumerator.nextObject()) !== null) {

      const creds: NSData = credentialsDict.objectForKey_(urlProtectionSpace).objectForKey_(userName);

      // Add the creds for this protection space.
      const credentialData: ICredential = {
        authMethod: urlProtectionSpace.authenticationMethod().toString(),
        host: urlProtectionSpace.host().toString(),
        password: creds.password().toString(),
        port: urlProtectionSpace.port(),
        protocol: urlProtectionSpace.protocol().toString(),
        user: creds.user().toString(),
      };

      data.push(credentialData);
    }
  }

  return data;
};


================================================
FILE: agent/src/ios/crypto.ts
================================================
import { colors as c } from "../lib/color.js";
import { fsend } from "../lib/helpers.js";
import * as jobs from "../lib/jobs.js";
import {
  arrayBufferToHex,
  hexToString
} from "./lib/helpers.js";

type CCAlgorithm = {
  [key: number]: { name: string; blocksize: number };
};

type AlgorithmType = {
  [key: number]: string;
};

// Encryption algorithms implemented by this module.
const CCAlgorithm: CCAlgorithm = {
  0: { name: "kCCAlgorithmAES128", blocksize: 16 },
  1: { name: "kCCAlgorithmDES", blocksize: 8 },
  2: { name: "kCCAlgorithm3DES", blocksize: 8 },
  3: { name: "kCCAlgorithmCAST", blocksize: 8 },
  4: { name: "kCCAlgorithmRC4", blocksize: 8 },
  5: { name: "kCCAlgorithmRC2", blocksize: 8 }
};

// Encryption algorithms implemented by this module.
const CCOperation: AlgorithmType = {
  0: "kCCEncrypt",
  1: "kCCDecrypt"
};

// Options flags, passed to CCCryptorCreate().
const CCOption: AlgorithmType = {
  1: "kCCOptionPKCS7Padding",
  2: "kCCOptionECBMode"
};

// alg for pbkdf. Right now only pbkdf2 is supported by CommonCrypto
const CCPBKDFAlgorithm: AlgorithmType = {
  2: "kCCPBKDF2"
};

// alg for prt for pbkdf
const CCPseudoRandomAlgorithm: AlgorithmType = {
  1: "kCCPRFHmacAlgSHA1",
  2: "kCCPRFHmacAlgSHA224",
  3: "kCCPRFHmacAlgSHA256",
  4: "kCCPRFHmacAlgSHA384",
  5: "kCCPRFHmacAlgSHA512"
};


// ident for crypto hooks job
let cryptoidentifier: number = 0;

// operation being performed 0=encrypt 1=decrypt
let op = 0;

// needed to keep track of CCAlgorithm so we can know
// blocksize from CCCryptorCreate to CCCryptorUpdate
let alg = 0;

// keep track of all the output bytes.
// this is necessary because CCCryptorUpdate needs to be
// append the final block from CCCryptorFinal
let dataOutBytes: string = "";

// Compatibility with frida < 16.7
if (!Module.getGlobalExportByName) {
  Module.getGlobalExportByName = function(name) {
    return Module['getExportByName'](null, name);
  }
}

const secrandomcopybytes = (ident: number): InvocationListener => {
  const hook = "SecRandomCopyBytes";
  return Interceptor.attach(
    Module.getGlobalExportByName(hook), {
    onEnter(args) {

      this.secrandomcopybytes = {};

      this.secrandomcopybytes.rnd = args[0].toInt32();
      this.secrandomcopybytes.count = args[1].toInt32();
      this.bytes = args[2];
    },
    onLeave(retval) {
      this.secrandomcopybytes.bytes = arrayBufferToHex(this.bytes.readByteArray(this.secrandomcopybytes.count));

      fsend(ident, hook, this.secrandomcopybytes);
    }
  });
};

const cckeyderivationpbkdf = (ident: number): InvocationListener => {
  const hook = "CCKeyDerivationPBKDF";
  return Interceptor.attach(
    Module.getGlobalExportByName(hook), {
    onEnter(args) {

      this.cckeyderivationpbkdf = {};

      // args[0]  "kCCPBKDF2" is the only alg supported by CommonCrypto
      this.cckeyderivationpbkdf.algorithm = CCPBKDFAlgorithm[args[0].toInt32()];

      // args[1]  The text password used as input to the derivation
      //          function. The actual octets present in this string
      //          will be used with no additional processing.  It's
      //          extremely important that the same encoding and
      //          normalization be used each time this routine is
      //          called if the same key is  expected to be derived.
      // args[2]  The length of the text password in bytes.
      const passwordPtr = args[1];
      const passwordLen = args[2].toInt32();
      const passwordBytes = arrayBufferToHex(passwordPtr.readByteArray(passwordLen));
      try {
        this.cckeyderivationpbkdf.password = hexToString(passwordBytes);
      } catch {
        this.cckeyderivationpbkdf.password = passwordBytes;
      }

      // args[3]  The salt byte values used as input to the derivation function.
      // args[4]  The length of the salt in bytes.
      const saltPtr = args[3];
      const saltLen = args[4].toInt32();
      this.cckeyderivationpbkdf.saltBytes = arrayBufferToHex(saltPtr.readByteArray(saltLen));

      // args[5]  The Pseudo Random Algorithm to use for the derivation iterations.
      this.cckeyderivationpbkdf.prf = CCPseudoRandomAlgorithm[args[5].toInt32()];

      // args[6]  The number of rounds of the Pseudo Random Algorithm to use.
      this.cckeyderivationpbkdf.rounds = args[6].toInt32();

      // args[7]  The resulting derived key produced by the function.
      //          The space for this must be provided by the caller.
      this.derivedKeyPtr = args[7];

      // args[8]  The expected length of the derived key in bytes.
      this.derivedKeyLen = args[8].toInt32();
    },
    onLeave(retval) {
      this.cckeyderivationpbkdf.derivedKey = arrayBufferToHex(this.derivedKeyPtr.readByteArray(this.derivedKeyLen));

      fsend(ident, hook, this.cckeyderivationpbkdf);
    }
  });
};

const cccrypt = (ident: number): InvocationListener => {
  const hook = "CCCrypt";
  return Interceptor.attach(
    Module.getGlobalExportByName(hook), {
    onEnter(args) {

      this.cccrpyt = {};

      // args[0]  Defines the basic operation: kCCEncrypt or kCCDecrypt.
      this.op = args[0].toInt32();
      this.cccrpyt.op = CCOperation[this.op];

      // args[1]  Defines the encryption algorithm.
      this.alg = args[1].toInt32();
      this.cccrpyt.alg = CCAlgorithm[alg].name;

      // args[2]  A word of flags defining options. See discussion for the CCOptions type.
      this.cccrpyt.options = CCOption[args[2].toInt32()];

      // args[3]  Raw key material, length keyLength bytes.
      // args[4]  Length of key material. Must be appropriate
      // 				  for the select algorithm. Some algorithms may
      //  				provide for varying key lengths.
      const key = args[3];
      this.cccrpyt.keyLength = args[4].toInt32();
      this.cccrpyt.key = arrayBufferToHex(key.readByteArray(this.cccrpyt.keyLength));

      // args[5]  Initialization vector, optional. Used for
      // 				  Cipher Block Chaining (CBC) mode. If present,
      // 				  must be the same length as the selected
      // 				  algorithm's block size. If CBC mode is
      // 				  selected (by the absence of any mode bits in
      // 				  the options	flags) and no IV is present, a
      // 				  NULL (all zeroes) IV will be used. This is
      // 				  ignored if ECB mode is used or if a stream
      // 		  		cipher algorithm is selected.
      const iv = args[5];
      this.cccrpyt.iv = arrayBufferToHex(iv.readByteArray(CCAlgorithm[alg].blocksize));

      // args[6]  Data to encrypt or decrypt, length dataInLength bytes.
      // args[7]  Length of data to encrypt or decrypt.
      const dataInPtr = args[6];
      const dataInLength = args[7].toInt32();
      const dataInHex = arrayBufferToHex(dataInPtr.readByteArray(dataInLength));
      this.cccrpyt.dataIn = this.op ? dataInHex : hexToString(dataInHex);

      // args[8]  Result is written here. Allocated by caller.
      //          Encryption and decryption can be performed
      //          "in-place", with the same buffer used for
      //          input and output.
      this.dataOut = args[8];

      // args[9]  The size of the dataOut buffer in bytes.
      this.dataOutAvailable = args[9].toInt32();

      // args[10] On successful return, the number of bytes written
      //          to dataOut. If kCCBufferTooSmall is returned as
      //          a result of insufficient buffer space being
      //          provided, the required buffer space is returned
      //          here.
      this.dataOutMoved = args[10];
    },
    onLeave(retval) {
      const dataOutHex = arrayBufferToHex(this.dataOut.readByteArray(this.dataOutAvailable));
      this.cccrpyt.dataOut = this.op ? hexToString(dataOutHex) : dataOutHex;

      fsend(ident, hook, this.cccrpyt);
    }
  });
};

const cccryptorcreate = (ident: number): InvocationListener => {
  const hook = "CCCryptorCreate";
  return Interceptor.attach(
    Module.getGlobalExportByName(hook), {
    onEnter(args) {

      this.cccryptorcreate = {};

      // args[0]  Defines the basic operation: kCCEncrypt or kCCDecrypt.
      op = args[0].toInt32();
      this.cccryptorcreate.op = CCOperation[op];

      // args[1]  Defines the encryption algorithm.
      alg = args[1].toInt32();
      this.cccryptorcreate.alg = CCAlgorithm[alg].name;

      // args[2]  A word of flags defining options. See discussion for the CCOptions type.
      const option = args[2].toInt32();
      this.cccryptorcreate.options = CCOption[option];

      // args[3]  Raw key material, length keyLength bytes.
      // args[4]  Length of key material. Must be appropriate
      // 				  for the select algorithm. Some algorithms may
      //  				provide for varying key lengths.
      const keyPtr = args[3];
      this.cccryptorcreate.keyLength = args[4].toInt32();
      this.cccryptorcreate.key = arrayBufferToHex(keyPtr.readByteArray(this.cccryptorcreate.keyLength));

      // args[5]  Initialization vector, optional. Used for
      // 				  Cipher Block Chaining (CBC) mode. If present,
      // 				  must be the same length as the selected
      // 				  algorithm's block size. If CBC mode is
      // 				  selected (by the absence of any mode bits in
      // 				  the options	flags) and no IV is present, a
      // 				  NULL (all zeroes) IV will be used. This is
      // 				  ignored if ECB mode is used or if a stream
      // 		  		cipher algorithm is selected.
      const ivPtr = args[5];
      this.cccryptorcreate.iv = arrayBufferToHex(ivPtr.readByteArray(CCAlgorithm[alg].blocksize));
    },
    onLeave(retval) {
      fsend(ident, hook, this.cccryptorcreate);
    }
  });
};

const cccryptorupdate = (ident: number): InvocationListener => {
  const hook = "CCCryptorUpdate";
  return Interceptor.attach(
    Module.getGlobalExportByName(hook), {
    onEnter(args) {
      this.cccryptorupdate = {};

      // reset for the next operation.
      dataOutBytes = "";

      // args[1]  Data to process, length dataInLength bytes.
      const dataInPtr = args[1];

      // args[2]  Length of data to process.
      this.dataInLength = args[2].toInt32();
      // args[3]  Result is written here. Allocated by caller.
      // 	  		  Encryption and decryption can be performed
      // 				  "in-place", with the same buffer used for
      // 				  input and output.
      this.dataOutPtr = args[3];

      // args[4]  The size of the dataOut buffer in bytes.
      this.dataOutAvailable = args[4].toInt32();

      const dataIn = arrayBufferToHex(dataInPtr.readByteArray(this.dataInLength));
      this.cccryptorupdate.dataIn = op ? dataIn : hexToString(dataIn);
    },
    onLeave(retval) {
      const blocksize = CCAlgorithm[alg].blocksize;
      // if the message is longer than 1 block then we need to
      // remember everything before the final block
      if (this.dataInLength > blocksize) {
        // TODO: There is sometimes padding added to the end of this message
        // someone please fix this in a pull request. it is super hacky.
        dataOutBytes = arrayBufferToHex(this.dataOutPtr.readByteArray(this.dataOutAvailable)).split("000000")[0];
        this.cccryptorupdate.dataOut = dataOutBytes;
      }

      fsend(ident, hook, this.cccryptorupdate);
    }
  });
};

const cccryptorfinal = (ident: number): InvocationListener => {
  const hook = "CCCryptorFinal";
  return Interceptor.attach(
    Module.getGlobalExportByName(hook), {
    onEnter(args) {

      this.cccryptorfinal = {};

      // args[1]  Result is written here. Allocated by caller.
      // 	  		  Encryption and decryption can be performed
      // 				  "in-place", with the same buffer used for
      // 				  input and output.
      this.dataOutPtr = args[1];

      // args[2]  The size of the dataOut buffer in bytes.
      this.dataOutAvailable = args[2].toInt32();
    },
    onLeave(retval) {
      // var dataOutHex = arrayBufferToHex(this.dataOutPtr.readByteArray(this.dataOutAvailable))
      // this.cccryptorfinal.dataOut = op ? hexToString(dataOutHex) : dataOutHex

      // append the final block the any previous blocks that might exist
      dataOutBytes += arrayBufferToHex(this.dataOutPtr.readByteArray(this.dataOutAvailable));
      this.cccryptorfinal.dataOut = this.op ? hexToString(dataOutBytes) : dataOutBytes;

      // this.cccryptorfinal.dataOut = dataOutBytes

      fsend(ident, hook, this.cccryptorfinal);
    }
  });
};

export const monitor = (): void => {
  // if we already have a job registered then return
  if (jobs.hasIdent(cryptoidentifier)) {
    send(`${c.greenBright("Job already registered")}: ${c.blueBright(cryptoidentifier.toString())}`);
    return;
  }

  const job: jobs.Job = new jobs.Job(jobs.identifier(), "ios-crypto-monitor");

  cryptoidentifier = job.identifier;
  
  job.addInvocation(secrandomcopybytes(job.identifier));
  job.addInvocation(cckeyderivationpbkdf(job.identifier));
  job.addInvocation(cccrypt(job.identifier));
  job.addInvocation(cccryptorcreate(job.identifier));
  job.addInvocation(cccryptorupdate(job.identifier));
  job.addInvocation(cccryptorfinal(job.identifier));

  jobs.add(job);
};


================================================
FILE: agent/src/ios/filesystem.ts
================================================
import { ObjC } from "../ios/lib/libobjc.js";
import * as fs from "frida-fs";
import { Buffer } from "buffer";
import { hexStringToBytes } from "../lib/helpers.js";
import { getNSFileManager } from "./lib/helpers.js";
import {
  IIosFilePath,
  IIosFileSystem
} from "./lib/interfaces.js";
import {
  NSDictionary,
  NSFileManager,
  NSString as NSStringType
} from "./lib/types.js";


// a resolved nsfilemanager instance
let fileManager: NSFileManager;

const getFileManager = (): NSFileManager => {
  if (fileManager === undefined) {
    fileManager = getNSFileManager();
    return fileManager;
  }

  return fileManager;
};

export const exists = (path: string): boolean => {
  // -- Sample Objective-C
  //
  // NSFileManager *fm = [NSFileManager defaultManager];
  // if ([fm fileExistsAtPath:@"/"]) {
  //     NSLog(@"Yep!");
  // }

  const fm: NSFileManager = getFileManager();
  const p: NSStringType = ObjC.classes.NSString.stringWithString_(path);

  return fm.fileExistsAtPath_(p);
};

export const readable = (path: string): boolean => {
  // -- Sample Objective-C
  //
  // NSFileManager *fm = [NSFileManager defaultManager];
  // NSLog(@"%d / readable?", [fm isReadableFileAtPath:@"/"]);

  const fm: NSFileManager = getFileManager();
  const p: NSStringType = ObjC.classes.NSString.stringWithString_(path);

  return fm.isReadableFileAtPath_(p);
};

export const writable = (path: string): boolean => {
  // -- Sample Objective-C
  //
  // NSFileManager *fm = [NSFileManager defaultManager];
  // NSLog(@"%d / readable?", [fm isReadableFileAtPath:@"/"]);

  const fm: NSFileManager = getFileManager();
  const p: NSStringType = ObjC.classes.NSString.stringWithString_(path);

  return fm.isWritableFileAtPath_(p);
};

export const pathIsFile = (path: string): boolean => {
  const fm: NSFileManager = getFileManager();

  const isDir: NativePointer = Memory.alloc(Process.pointerSize);
  fm.fileExistsAtPath_isDirectory_(path, isDir);

  // deref the isDir pointer to get the bool
  // *isDir === 1 means the path is a directory
  return isDir.readInt() === 0;
};

// returns a 'pwd' that assumes the current bundle's path
// is the directory we are interested in. the handling of
// pwd is actually handled in the python world and this
// method is only really called as a starting point.
export const pwd = (): string => {
  // -- Sample Objective-C
  //
  // NSURL *bundleURL = [[NSBundle mainBundle] bundleURL];

  const NSBundle = ObjC.classes.NSBundle;
  return NSBundle.mainBundle().bundlePath().toString();
};

// heavy lifting is done in frida-fs here.
export const readFile = (path: string): string | Buffer => {
  if (fs.statSync(path).size == 0)
    return Buffer.alloc(0);
  return fs.readFileSync(path);
};

// heavy lifting is done in frida-fs here.
export const writeFile = (path: string, data: string): void => {
  const writeStream: any = fs.createWriteStream(path);

  writeStream.on("error", (error: Error) => {
    throw error;
  });

  writeStream.write(hexStringToBytes(data));
  writeStream.end();
};

export const deleteFile = (path: string): boolean => {
  const fm: NSFileManager = getFileManager();

  const err: NativePointer = Memory.alloc(Process.pointerSize);
  fm.removeItemAtPath_error_(path, err);

  // deref the isDir pointer to get the bool
  // *isDir === 1 means the path is a directory
  return err.readInt() === 0;
};

export const ls = (path: string): IIosFileSystem => {
  // -- Sample Objective-C
  //
  // NSFileManager *fm = [NSFileManager defaultManager];
  // NSString *bundleURL = [[NSBundle mainBundle] bundlePath];
  // NSArray *contents = [fm contentsOfDirectoryAtPath:bundleURL error:nil];

  // for (id item in contents) {
  //     NSString *p = [[NSString alloc] initWithFormat:@"%@/%@",bundleURL, item];
  //     NSDictionary *attribs = [fm attributesOfItemAtPath:p error:nil];
  //     NSLog(@"%@ - %@", p, attribs);
  // }

  const fm: NSFileManager = getFileManager();
  const p: NSStringType = ObjC.classes.NSString.stringWithString_(path);

  const response: IIosFileSystem = {
    files: {},
    path: `${path}`,
    readable: fm.isReadableFileAtPath_(p),
    writable: fm.isWritableFileAtPath_(p),
  };

  // not being able to read the path should leave us bailing early
  if (!response.readable) { return response; }

  const pathContents: NSDictionary = fm.contentsOfDirectoryAtPath_error_(path, NULL);
  const fileCount: number = pathContents.count();

  // loop-de-loop files
  for (let i = 0; i < fileCount; i++) {

    // pick a file off contents
    const file: string = pathContents.objectAtIndex_(i);

    const pathFileData: IIosFilePath = {
      attributes: {},
      fileName: file.toString(),
      readable: undefined,
      writable: undefined,
    };

    // generate a full path to the file
    const filePath: string = [path, "/", file].join("");
    const currentFilePath: NSStringType = ObjC.classes.NSString.stringWithString_(filePath);

    // check read / write
    pathFileData.readable = fm.isReadableFileAtPath_(currentFilePath);
    pathFileData.writable = fm.isWritableFileAtPath_(currentFilePath);

    // get attributes
    const attributes = fm.attributesOfItemAtPath_error_(currentFilePath, NULL);

    // if we were able to get attributes for the item,
    // append them to those for this file. (example is listing
    // files in / have some that cant have attributes read for :|)
    if (attributes) {

      // loop the attributes and set them in the file_data
      // dictionary
      const enumerator = attributes.keyEnumerator();
      let key;
      // tslint:disable-next-line:no-conditional-assignment
      while ((key = enumerator.nextObject()) !== null) {

        // get attribute data
        const value: any = attributes.objectForKey_(key);
        // add it to the attributes for this item
        pathFileData.attributes[key] = value.toString();
      }
    }

    // finally, add the file to the final response
    response.files[file] = pathFileData;
  }

  return response;
};


================================================
FILE: agent/src/ios/heap.ts
================================================
import { ObjC } from "../ios/lib/libobjc.js";
import type { default as ObjCTypes } from "frida-objc-bridge";
import { colors as c } from "../lib/color.js";
import { bytesToUTF8 } from "./lib/helpers.js";
import { IHeapObject } from "./lib/interfaces.js";


const enumerateInstances = (clazz: string): ObjCTypes.Object[] => {
  if (!ObjC.classes.hasOwnProperty(clazz)) {
    c.log(`Unknown Objective-C class: ${c.redBright(clazz)}`);
    return [];
  }

  const specifier: ObjCTypes.DetailedChooseSpecifier = {
    class: ObjC.classes[clazz],
    subclasses: true,  // don't skip subclasses
  };

  return ObjC.chooseSync(specifier);
};

export const getInstances = (clazz: string): IHeapObject[] => {
  c.log(`${c.blackBright(`Enumerating live instances of`)} ${c.greenBright(clazz)}...`);
  
  const instances: IHeapObject[] = [];

  enumerateInstances(clazz).map((instance) => {
    try {
      instances.push({
        className: instance.$className,
        handle: instance.handle.toString(),
        ivars: instance.$ivars,
        kind: instance.$kind,
        methods: instance.$ownMethods,
        superClass: instance.$superClass.$className,
      });
    } catch (err) {
      c.log(`Warning: ${c.yellowBright((err as Error).message)}`);
    }
  });

  return instances;
};

const resolvePointer = (pointer: string): ObjCTypes.Object => {
  const o = new ObjC.Object(new NativePointer(pointer));
  c.log(`${c.blackBright(`Pointer ` + pointer + ` is to class `)}${c.greenBright(o.$className)}`);

  return o;
};

export const getIvars = (pointer: string, toUTF8: boolean): [string, any[string]] => {
  const { $className, $ivars } = resolvePointer(pointer);

  // if we need to get utf8 representations, start a new object with
  // which cloned properties will have utf8 values. we _could_ have
  // just gone and replaces values in $ivars, but there are some
  // access errors for that.
  if (toUTF8) {
    const $clonedIvars: {[name: string]: any} = {};
    c.log(c.blackBright(`Converting ivar values to UTF8 strings...`));
    for (const k in $ivars) {
      if ($ivars.hasOwnProperty(k)) {
        const v = $ivars[k];
        $clonedIvars[k] = bytesToUTF8(v);
      }
    }

    return [$className, $clonedIvars];
  }

  return [$className, $ivars];
};

export const getMethods = (pointer: string): [string, any[string]] => {
  const { $className, $ownMethods } = resolvePointer(pointer);
  return [$className, $ownMethods];
};

export const callInstanceMethod = (pointer: string, method: string, returnString: boolean): void => {
  const i = resolvePointer(pointer);
  c.log(`${c.blackBright(`Executing:`)} ${c.greenBright(`[${i.$className} ${method}]`)}`);

  const result = i[method]();

  if (returnString) {
    return result.toString();
  }
  return i[method]();
};

export const evaluate = (pointer: string, js: string): void => {
  const ptr = resolvePointer(pointer);
  // tslint:disable-next-line:no-eval
  eval(js);
};


================================================
FILE: agent/src/ios/hooking.ts
================================================
import { ObjC } from "../ios/lib/libobjc.js";
import { colors as c } from "../lib/color.js";
import * as jobs from "../lib/jobs.js";


export const getClasses = () => {
  return ObjC.classes;
};

export const getClassMethods = (className: string, includeParents: boolean): string[] => {
  if (ObjC.classes[className] === undefined) {
    return [];
  }

  // Show all methods of the class
  if (includeParents) {
    return ObjC.classes[className].$methods;
  }

  return ObjC.classes[className].$ownMethods;
};

const objcEnumerate = (pattern: string): ApiResolverMatch[] => {
  return new ApiResolver('objc').enumerateMatches(pattern);
};

export const search = (patternOrClass: string): ApiResolverMatch[] => {

  // if we didnt get a pattern, make one assuming its meant to be a class
  if (!patternOrClass.includes('[')) patternOrClass = `*[*${patternOrClass}* *]`;

  return objcEnumerate(patternOrClass);
};

export const watch = (patternOrClass: string, dargs: boolean = false, dbt: boolean = false,
  dret: boolean = false, watchParents: boolean = false): void => {

  // Add the job
  // We init a new job here as the child watch* calls will be grouped in a single job.
  // mostly commandline fluff
  const job: jobs.Job = new jobs.Job(jobs.identifier(), `ios-watch for: ${patternOrClass}`);
  jobs.add(job);

  const isPattern = patternOrClass.includes('[');

  // if we have a patterm we'll loop the methods, hook and push a listener to the job
  if (isPattern === true) {
    const matches = objcEnumerate(patternOrClass);
    matches.forEach((match: ApiResolverMatch) => {
      watchMethod(match.name, job, dargs, dbt, dret);
    });

    return;
  }

  watchClass(patternOrClass, job, dargs, dbt, dret, watchParents);
};

const watchClass = (clazz: string, job: jobs.Job, dargs: boolean = false, dbt: boolean = false,
  dret: boolean = false, parents: boolean = false): void => {

  const target = ObjC.classes[clazz];

  if (!target) {
    send(`${c.red(`Error!`)} Unable to find class ${c.redBright(clazz)}!`);
    return;
  }

  // with parents as true, include methods from a parent class,
  // otherwise simply hook the target class' own  methods
  (parents ? target.$methods : target.$ownMethods).forEach((method) => {
    // filter and make sure we have a type and name. Looks like some methods can
    // have '' as name... am expecting something like "- isJailBroken"
    const fullMethodName = `${method[0]}[${clazz} ${method.substring(2)}]`;
    watchMethod(fullMethodName, job, dargs, dbt, dret);
  });

};

const watchMethod = (selector: string, job: jobs.Job, dargs: boolean, dbt: boolean,
  dret: boolean): void => {

  const resolver = new ApiResolver("objc");
  let matchedMethod: ApiResolverMatch = {
    address: NULL,
    name: '',
  };

  // handle the resolvers error it may throw if the selector format is off.
  try {
    // select the first match
    const resolved = resolver.enumerateMatches(selector);
    if (resolved.length <= 0) {
      send(`${c.red(`Error:`)} No matches for selector ${c.redBright(`${selector}`)}. ` +
        `Double check the name, or try "ios hooking list class_methods" first.`);
      return;
    }

    // not sure if this will ever be the case... but lets log it
    // anyways
    if (resolved.length > 1) {
      send(`${c.yellow(`Warning:`)} More than one result for selector ${c.redBright(`${selector}`)}!`);
    }

    matchedMethod = resolved[0];
  } catch (error) {
    send(
      `${c.red(`Error:`)} Unable to find address for selector ${c.redBright(`${selector}`)}! ` +
      `The error was:\n` + c.red((error as Error).message),
    );
    return;
  }

  // Attach to the discovered match
  // TODO: loop correctly when globbing
  send(`Found selector at ${c.green(matchedMethod.address.toString())} as ${c.green(matchedMethod.name)}`);

  const watchInvocation: InvocationListener = Interceptor.attach(matchedMethod.address, {
    // tslint:disable-next-line:object-literal-shorthand
    onEnter: function (args) {
      // how many arguments do we have in this selector?
      const argumentCount: number = (selector.match(/:/g) || []).length;
      const receiver = new ObjC.Object(args[0]);
      send(
        c.blackBright(`[${job.identifier}] `) +
        `Called: ${c.green(`${selector}`)} ${c.blue(`${argumentCount}`)} arguments` +
        `(Kind: ${c.cyan(receiver.$kind)}) (Super: ${c.cyan(receiver.$superClass.$className)})`,
      );

      // if we should include a backtrace to here, do that.
      if (dbt) {
        send(
          c.blackBright(`[${job.identifier}] `) +
          `${c.green(`${selector}`)} Backtrace:\n\t` +
          Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join("\n\t"),
        );
      }

      if (dargs && argumentCount > 0) {
        const methodSplit = ObjC.selectorAsString(args[1]).split(":").filter((val) => val);
        const r = methodSplit.map((argName, position) => {
          // As this is an ObjectiveC method, the arguments are as follows:
          // 0. 'self'
          // 1. The selector (object.name:)
          // 2. The first arg
          //
          // For this reason do we shift it by 2 positions to get an 'instance' for
          // the argument value.
          const t = new ObjC.Object(args[position + 2]);
          return `${argName}: ${c.greenBright(`${t}`)}`;
        });

        send(c.blackBright(`[${job.identifier}] `) +
          `Argument dump: [${c.green(receiver.$className)} ${r.join(" ")}]`);
      }
    },
    onLeave: (retval) => {
      // do nothing if we are not expected to dump return values
      if (!dret) { return; }
      send(c.blackBright(`[${job.identifier}] `) + `Return Value: ${c.red(retval.toString())}`);
    },
  });
  
  job.addInvocation(watchInvocation);
};

export const setMethodReturn = (selector: string, returnValue: boolean): void => {
  const TRUE = new NativePointer(0x1);
  const FALSE = new NativePointer(0x0);

  const resolver = new ApiResolver("objc");
  let matchedMethod: ApiResolverMatch = {
    address: NULL,
    name: '',
  };

  // handle the resolvers error it may throw if the selector format
  // is off.
  try {
    // select the first match
    matchedMethod = resolver.enumerateMatches(selector)[0];
  } catch (error) {
    send(
      `${c.red(`Error!`)} Unable to find address for selector ${c.redBright(`${selector}`)}! ` +
      `The error was:\n` + c.red((error as Error).message),
    );
    return;
  }

  // no match? then just leave.
  if (!matchedMethod.address) {
    send(`${c.red(`Error!`)} Unable to find address for selector ${c.redBright(`${selector}`)}!`);
    return;
  }

  // Start a new Job
  const job: jobs.Job = new jobs.Job(jobs.identifier(), `set-method-return for: ${selector}`);

  // Attach to the discovered match
  // TODO: loop correctly when globbing
  send(`Found selector at ${c.green(matchedMethod.address.toString())} as ${c.green(matchedMethod.name)}`);
  const watchInvocation: InvocationListener = Interceptor.attach(matchedMethod.address, {
    onLeave: (retval) => {

      switch (returnValue) {
        case true:
          if (retval.equals(TRUE)) {
            return;
          }
          send(
            c.blackBright(`[${job.identifier}] `) +
            `${c.green(selector)} ` +
            `Return value was: ${c.red(retval.toString())}, overriding to ${c.green(TRUE.toString())}`,
          );
          retval.replace(TRUE);
          break;

        case false:
          if (retval.equals(FALSE)) {
            return;
          }
          send(
            c.blackBright(`[${job.identifier}] `) +
            `${c.green(selector)} ` +
            `Return value was: ${c.red(retval.toString())}, overriding to ${c.green(FALSE.toString())}`,
          );
          retval.replace(FALSE);
          break;
      }
    },
  });

  // register the job
  job.addInvocation(watchInvocation);
  jobs.add(job);
};


================================================
FILE: agent/src/ios/jailbreak.ts
================================================
import { ObjC } from "../ios/lib/libobjc.js";
import { colors as c } from "../lib/color.js";
import * as jobs from "../lib/jobs.js";

// Attempts to disable Jailbreak detection.
// This seems like an odd thing to do on a device that is probably not
// jailbroken. However, in the case of a device losing a jailbreak due to
// an OS upgrade, some filesystem artifacts may still exist, causing some
// of the typical checks to incorrectly detect the jailbreak status!

// Hook NSFileManager and fopen calls and check if it is to a common path.
// Hook canOpenURL for Cydia deep link.

const jailbreakPaths = [
  "/Applications/Cydia.app",
  "/Applications/FakeCarrier.app",
  "/Applications/Icy.app",
  "/Applications/IntelliScreen.app",
  "/Applications/MxTube.app",
  "/Applications/RockApp.app",
  "/Applications/SBSetttings.app",
  "/Applications/WinterBoard.app",
  "/Applications/blackra1n.app",
  "/Library/MobileSubstrate/DynamicLibraries/LiveClock.plist",
  "/Library/MobileSubstrate/DynamicLibraries/Veency.plist",
  "/Library/MobileSubstrate/MobileSubstrate.dylib",
  "/System/Library/LaunchDaemons/com.ikey.bbot.plist",
  "/System/Library/LaunchDaemons/com.saurik.Cy@dia.Startup.plist",
  "/System/Library/LaunchDaemons/com.saurik.Cydia.Startup.plist",
  "/bin/bash",
  "/bin/sh",
  "/etc/apt",
  "/etc/ssh/sshd_config",
  "/private/var/stash",
  "/private/var/tmp/cydia.log",
  "/private/var/lib/apt",
  "/usr/bin/cycript",
  "/usr/bin/ssh",
  "/usr/bin/sshd",
  "/usr/libexec/sftp-server",
  "/usr/libexec/sftp-server",
  "/usr/libexec/ssh-keysign",
  "/usr/sbin/sshd",
  "/var/cache/apt",
  "/var/lib/cydia",
  "/var/log/syslog",
  "/var/tmp/cydia.log",
];


// toggles replies to fileExistsAtPath: for the paths in jailbreakPaths
const fileExistsAtPath = (success: boolean, ident: number): InvocationListener => {

  return Interceptor.attach(
    ObjC.classes.NSFileManager["- fileExistsAtPath:"].implementation, {
    onEnter(args) {

      // Use a marker to check onExit if we need to manipulate
      // the response.
      this.is_common_path = false;

      // Extract the path
      this.path = new ObjC.Object(args[2]).toString();

      // check if the looked up path is in the list of common_paths
      if (jailbreakPaths.indexOf(this.path) >= 0) {

        // Mark this path as one that should have its response
        // modified if needed.
        this.is_common_path = true;
      }
    },
    onLeave(retval) {

      // stop if we dont care about the path
      if (!this.is_common_path) {
        return;
      }

      // depending on the desired state, we flip retval
      switch (success) {
        case (true):
          // ignore successful lookups
          if (!retval.isNull()) {
            return;
          }
          send(
            c.blackBright(`[${ident}] `) + `fileExistsAtPath: check for ` +
            c.green(this.path) + ` failed with: ` +
            c.red(retval.toString()) + `, marking it as successful.`,
          );

          retval.replace(new NativePointer(0x01));
          break;

        case (false):
          // ignore failed lookups
          if (retval.isNull()) {
            return;
          }
          send(
            c.blackBright(`[${ident}] `) + `fileExistsAtPath: check for ` +
            c.green(this.path) + ` was successful with: ` +
            c.red(retval.toString()) + `, marking it as failed.`,
          );

          retval.replace(new NativePointer(0x00));
          break;
      }
    },
  },
  );
};


// toggles replies to fopen: for the paths in jailbreakPaths
const fopen = (success: boolean, ident: number): InvocationListener | null => {

  // Compatibility with frida < 16.7
  if (!Module.findGlobalExportByName) {
    Module.findGlobalExportByName = function(name) {
      return Module['findExportByName'](null, name);
    }
  }

  const fopen_addr = Module.findGlobalExportByName("fopen");
  if (!fopen_addr) {
    send(c.red(`fopen function not found!`));
    return null; 
  }

  return Interceptor.attach(fopen_addr, {
    onEnter(args) {

      this.is_common_path = false;

      // Extract the path
      this.path = args[0].readCString();

      // check if the looked up path is in the list of common_paths
      if (jailbreakPaths.indexOf(this.path) >= 0) {

        // Mark this path as one that should have its response
        // modified if needed.
        this.is_common_path = true;
      }
    },
    onLeave(retval) {

      // stop if we dont care about the path
      if (!this.is_common_path) {
        return;
      }

      // depending on the desired state, we flip retval
      switch (success) {
        case (true):
          // ignore successful lookups
          if (!retval.isNull()) {
            return;
          }
          send(
            c.blackBright(`[${ident}] `) + `fopen: check for ` +
            c.green(this.path) + ` failed with: ` +
            c.red(retval.toString()) + `, marking it as successful.`,
          );

          retval.replace(new NativePointer(0x01));
          break;

        case (false):
          // ignore failed lookups
          if (retval.isNull()) {
            return;
          }
          send(
            c.blackBright(`[${ident}] `) + `fopen: check for ` +
            c.green(this.path) + ` was successful with: ` +
            c.red(retval.toString()) + `, marking it as failed.`,
          );

          retval.replace(new NativePointer(0x00));
          break;
      }
    },
  },
  );
};

// toggles replies to canOpenURL for Cydia
const canOpenURL = (success: boolean, ident: number): InvocationListener => {

  return Interceptor.attach(
    ObjC.classes.UIApplication["- canOpenURL:"].implementation, {
    onEnter(args) {

      this.is_flagged = false;

      // Extract the path
      this.path = new ObjC.Object(args[2]).toString();

      if (this.path.startsWith('cydia') || this.path.startsWith('Cydia')) {
        this.is_flagged = true;
      }
    },
    onLeave(retval) {

      if (!this.is_flagged) {
        return;
      }

      // depending on the desired state, we flip retval
      switch (success) {
        case (true):
          // ignore successful lookups
          if (!retval.isNull()) {
            return;
          }
          send(
            c.blackBright(`[${ident}] `) + `canOpenURL: check for ` +
            c.green(this.path) + ` failed with: ` +
            c.red(retval.toString()) + `, marking it as successful.`,
          );

          retval.replace(new NativePointer(0x01));
          break;

        case (false):
          // ignore failed
          if (retval.isNull()) {
            return;
          }
          send(
            c.blackBright(`[${ident}] `) + `canOpenURL: check for ` +
            c.green(this.path) + ` was successful with: ` +
            c.red(retval.toString()) + `, marking it as failed.`,
          );

          retval.replace(new NativePointer(0x00));
          break;
      }
    },
  },
  );
};


const libSystemBFork = (success: boolean, ident: number): InvocationListener | null => {
  // Hook fork() in libSystem.B.dylib and return 0
  // TODO: Hook vfork
  const libSystemBdylib = Process.findModuleByName("libSystem.B.dylib");

  if (!libSystemBdylib) return null;
  const libSystemBdylibFork = libSystemBdylib.findExportByName("fork");
  if (!libSystemBdylibFork) return null;

  return Interceptor.attach(libSystemBdylibFork, {
    onLeave(retval) {

      switch (success) {
        case (true):
          // already successful forks are ok
          if (!retval.isNull()) {
            return;
          }
          send(
            c.blackBright(`[${ident}] `) + `Call to ` +
            c.green(`libSystem.B.dylib::fork()`) + ` failed with ` +
            c.red(retval.toString()) + ` marking it as successful.`,
          );

          retval.replace(new NativePointer(0x1));
          break;

        case (false):
          // already failed forks are ok
          if (retval.isNull()) {
            return;
          }
          send(
            c.blackBright(`[${ident}] `) + `Call to ` +
            c.green(`libSystem.B.dylib::fork()`) + ` was successful with ` +
            c.red(retval.toString()) + ` marking it as failed.`,
          );

          retval.replace(new NativePointer(0x0));
          break;
      }
    },
  });
};

// ref: https://www.ayrx.me/gantix-jailmonkey-root-detection-bypass/
const jailMonkeyBypass = (success: boolean, ident: number): InvocationListener | null => {
  const JailMonkeyClass = ObjC.classes.JailMonkey;
  if (JailMonkeyClass === undefined) return null;

  return Interceptor.attach(JailMonkeyClass["- isJailBroken"].implementation, {
    onLeave(retval) {
      send(
        c.blackBright(`[${ident}] `) + `JailMonkey.isJailBroken called, returning false.`
      );
      retval.replace(new NativePointer(0x00));
    }
  });
};

export const disable = (): void => {
  const job: jobs.Job = new jobs.Job(jobs.identifier(), "ios-jailbreak-disable");

  job.addInvocation(fileExistsAtPath(false, job.identifier));
  job.addInvocation(libSystemBFork(false, job.identifier));
  job.addInvocation(fopen(false, job.identifier));
  job.addInvocation(canOpenURL(false, job.identifier));
  job.addInvocation(jailMonkeyBypass(false, job.identifier));

  jobs.add(job);
};

export const enable = (): void => {
  const job: jobs.Job = new jobs.Job(jobs.identifier(), "ios-jailbreak-enable");

  job.addInvocation(fileExistsAtPath(true, job.identifier));
  job.addInvocation(libSystemBFork(true, job.identifier));
  job.addInvocation(fopen(true, job.identifier));
  job.addInvocation(canOpenURL(true, job.identifier));
  job.addInvocation(jailMonkeyBypass(true, job.identifier));

  jobs.add(job);
};


================================================
FILE: agent/src/ios/keychain.ts
================================================
// dumps all of the keychain items available to the current
// application.
import { colors as c } from "../lib/color.js";
import { reverseEnumLookup } from "../lib/helpers.js";
import {
  kSec,
  NSUTF8StringEncoding
} from "./lib/constants.js";
import {
  bytesToHexString,
  bytesToUTF8,
  smartDataToString
} from "./lib/helpers.js";
import {
  IKeychainData,
  IKeychainItem
} from "./lib/interfaces.js";
import { 
  libObjc, 
  ObjC 
} from "./lib/libobjc.js";
import {
  NSDictionary,
  NSMutableDictionary as NSMutableDictionaryType,
  NSString as NSStringType,
} from "./lib/types.js";

// keychain item times to query for
const itemClasses = [
  kSec.kSecClassKey,
  kSec.kSecClassIdentity,
  kSec.kSecClassCertificate,
  kSec.kSecClassGenericPassword,
  kSec.kSecClassInternetPassword,
];


// The parent method that enumerates the iOS keychain
const enumerateKeychain = (): IKeychainData[] => {
  // -- Sample Objective-C
  //
  // NSMutableDictionary *query = [[NSMutableDictionary alloc] init];
  // [query setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnAttributes];
  // [query setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];
  // [query setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnRef];
  // [query setObject:(__bridge id)kSecMatchLimitAll forKey:(__bridge id)kSecMatchLimit];
  // NSArray *itemClasses = [NSArray arrayWithObjects:
  //                         (__bridge id)kSecClassKey,
  //                         (__bridge id)kSecClassIdentity,
  //                         (__bridge id)kSecClassCertificate,
  //                         (__bridge id)kSecClassGenericPassword,
  //                         (__bridge id)kSecClassInternetPassword,
  //                         nil];
  // for (id itemClass in itemClasses) {
  //     NSLog(@"Querying: %@", itemClass);
  //     [query setObject:itemClass forKey:(__bridge id)kSecClass];
  //     CFTypeRef result = NULL;
  //     OSStatus findStatus = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
  //     if(findStatus != errSecSuccess) {
  //         NSLog(@"Failed to query keychain for types %@", itemClass);
  //         continue;
  //     }
  //     // loopy-loop the results
  //     for (NSDictionary *entry in (__bridge NSDictionary *)result) {
  //         NSString *stringRes = [[NSString alloc] initWithData:[entry objectForKey:@"v_Data"]
  //                                                     encoding:NSUTF8StringEncoding];
  //         NSLog(@"%@", stringRes);
  //     }
  //     if (result != NULL) {
  //         CFRelease(result);
  //     }
  // }

  // http://nshipster.com/bool/
  const kCFBooleanTrue = ObjC.classes.__NSCFBoolean.numberWithBool_(true);

  // the base query dictionary to use for the keychain lookups
  const searchDictionary: NSMutableDictionaryType = ObjC.classes.NSMutableDictionary.alloc().init();
  searchDictionary.setObject_forKey_(kCFBooleanTrue, kSec.kSecReturnAttributes);
  searchDictionary.setObject_forKey_(kCFBooleanTrue, kSec.kSecReturnData);
  searchDictionary.setObject_forKey_(kCFBooleanTrue, kSec.kSecReturnRef);
  searchDictionary.setObject_forKey_(kSec.kSecMatchLimitAll, kSec.kSecMatchLimit);
  searchDictionary.setObject_forKey_(kSec.kSecAttrSynchronizableAny, kSec.kSecAttrSynchronizable);

  // loop each of the keychain class types and extract data
  const itemClassResults: IKeychainData[][] = itemClasses.map((clazz) => {
    const data: IKeychainData[] = [];  // start empty.
    searchDictionary.setObject_forKey_(clazz, kSec.kSecClass);

    // prepare a pointer for the results and call SecItemCopyMatching to get them
    const resultsPointer: NativePointer = Memory.alloc(Process.pointerSize);
    const copyResult: NativePointer = libObjc.SecItemCopyMatching(searchDictionary, resultsPointer);

    // without results (aka non-zero OSStatus) we just move along.
    if (!copyResult.isNull()) { return data; }

    // read the resultant dict of the lookup from memory
    const searchResults: NSDictionary = new ObjC.Object(resultsPointer.readPointer());

    // if the results in the dict is empty (which is not something I expect),
    // fail fast too.
    if (searchResults.length <= 0) { return data; }

    // read each key chain entry for the current item_class and populate
    // the item_class items we will return
    for (let i: number = 0; i < searchResults.count(); i++) {
      data.push({
        clazz,
        data: searchResults.objectAtIndex_(i),
      });
    }

    return data;
  });
  
  const keyChainData: IKeychainData[] = [];
  return keyChainData.concat(...itemClassResults).filter((n) => n !== undefined);
};

// print raw entries using some Frida magic
// to do the toString() repr...
export const listRaw = (): void => {
  enumerateKeychain().forEach((e) => {
    c.log(e.data);
  });
};

// dump the contents of the iOS keychain, returning the
// results as an array representation.
export const list = (smartDecode: boolean = false): IKeychainItem[] => {
  return enumerateKeychain().map((entry) => {
    const { data, clazz } = entry;
    return {
      access_control: (data.containsKey_(kSec.kSecAttrAccessControl)) ? decodeAcl(data) : "",
      accessible_attribute: reverseEnumLookup(kSec,
        bytesToUTF8(data.objectForKey_(kSec.kSecAttrAccessible))),
      account: bytesToUTF8(data.objectForKey_(kSec.kSecAttrAccount)),
      alias: bytesToUTF8(data.objectForKey_(kSec.kSecAttrAlias)),
      comment: bytesToUTF8(data.objectForKey_(kSec.kSecAttrComment)),
      create_date: bytesToUTF8(data.objectForKey_(kSec.kSecAttrCreationDate)),
      creator: bytesToUTF8(data.objectForKey_(kSec.kSecAttrCreator)),
      custom_icon: bytesToUTF8(data.objectForKey_(kSec.kSecAttrHasCustomIcon)),
      data: (clazz !== "keys") ?
        (smartDecode) ?
          smartDataToString(data.objectForKey_(kSec.kSecValueData)) :
          bytesToUTF8(data.objectForKey_(kSec.kSecValueData)) :
        "(Key data not displayed)",
      dataHex: bytesToHexString(data.objectForKey_(kSec.kSecValueData)),
      description: bytesToUTF8(data.objectForKey_(kSec.kSecAttrDescription)),
      entitlement_group: bytesToUTF8(data.objectForKey_(kSec.kSecAttrAccessGroup)),
      generic: bytesToUTF8(data.objectForKey_(kSec.kSecAttrGeneric)),
      invisible: bytesToUTF8(data.objectForKey_(kSec.kSecAttrIsInvisible)),
      item_class: reverseEnumLookup(kSec, clazz),
      label: bytesToUTF8(data.objectForKey_(kSec.kSecAttrLabel)),
      modification_date: bytesToUTF8(data.objectForKey_(kSec.kSecAttrModificationDate)),
      negative: bytesToUTF8(data.objectForKey_(kSec.kSecAttrIsNegative)),
      protected: bytesToUTF8(data.objectForKey_(kSec.kSecProtectedDataItemAttr)),
      script_code: bytesToUTF8(data.objectForKey_(kSec.kSecAttrScriptCode)),
      service: bytesToUTF8(data.objectForKey_(kSec.kSecAttrService)),
      type: bytesToUTF8(data.objectForKey_(kSec.kSecAttrType)),
    };
  });
};

// clean out the keychain
export const empty = (): void => {
  const searchDictionary: NSMutableDictionaryType = ObjC.classes.NSMutableDictionary.alloc().init();
  searchDictionary.setObject_forKey_(kSec.kSecAttrSynchronizableAny, kSec.kSecAttrSynchronizable);
  itemClasses.forEach((clazz) => {

    // set the class-type we are querying for now & delete
    searchDictionary.setObject_forKey_(clazz, kSec.kSecClass);
    libObjc.SecItemDelete(searchDictionary);
  });
};

  // remove matching itemms from the keychain
  export const remove = (account: string, service: string): void => {
    const searchDictionary: NSMutableDictionaryType = ObjC.classes.NSMutableDictionary.alloc().init();
    searchDictionary.setObject_forKey_(kSec.kSecAttrSynchronizableAny, kSec.kSecAttrSynchronizable);
    itemClasses.forEach((clazz) => {
      // set the class-type we are querying for now & delete
      searchDictionary.setObject_forKey_(clazz, kSec.kSecClass);
      searchDictionary.setObject_forKey_(account, kSec.kSecAttrAccount);
      searchDictionary.setObject_forKey_(service, kSec.kSecAttrService);
      libObjc.SecItemDelete(searchDictionary);
    });
  };

  // update matching item from the keychain
  export const update = (account: string, service: string, newData: string): void => {
    
    const searchDictionary: NSMutableDictionaryType = ObjC.classes.NSMutableDictionary.alloc().init();    
    searchDictionary.setObject_forKey_(kSec.kSecAttrSynchronizableAny, kSec.kSecAttrSynchronizable);
    
    // set the class-type we are querying for now & update
    searchDictionary.setObject_forKey_(kSec.kSecClassGenericPassword, kSec.kSecCla
Download .txt
gitextract_j8lfqr1u/

├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   └── workflows/
│       ├── codeql-analysis.yml
│       └── pypi.yml
├── .gitignore
├── .python-version
├── .vscode/
│   └── settings.json
├── CONTRIBUTING.md
├── LICENSE
├── MANIFEST.in
├── Makefile
├── README.md
├── agent/
│   ├── .gitignore
│   ├── README.md
│   ├── package.json
│   ├── src/
│   │   ├── android/
│   │   │   ├── clipboard.ts
│   │   │   ├── filesystem.ts
│   │   │   ├── general.ts
│   │   │   ├── heap.ts
│   │   │   ├── hooking.ts
│   │   │   ├── intent.ts
│   │   │   ├── keystore.ts
│   │   │   ├── lib/
│   │   │   │   ├── intentUtils.ts
│   │   │   │   ├── interfaces.ts
│   │   │   │   ├── libjava.ts
│   │   │   │   └── types.ts
│   │   │   ├── monitor.ts
│   │   │   ├── pinning.ts
│   │   │   ├── proxy.ts
│   │   │   ├── root.ts
│   │   │   ├── shell.ts
│   │   │   └── userinterface.ts
│   │   ├── generic/
│   │   │   ├── custom.ts
│   │   │   ├── environment.ts
│   │   │   ├── http.ts
│   │   │   ├── memory.ts
│   │   │   └── ping.ts
│   │   ├── index.ts
│   │   ├── ios/
│   │   │   ├── binary.ts
│   │   │   ├── binarycookies.ts
│   │   │   ├── bundles.ts
│   │   │   ├── credentialstorage.ts
│   │   │   ├── crypto.ts
│   │   │   ├── filesystem.ts
│   │   │   ├── heap.ts
│   │   │   ├── hooking.ts
│   │   │   ├── jailbreak.ts
│   │   │   ├── keychain.ts
│   │   │   ├── lib/
│   │   │   │   ├── constants.ts
│   │   │   │   ├── helpers.ts
│   │   │   │   ├── interfaces.ts
│   │   │   │   ├── libobjc.ts
│   │   │   │   └── types.ts
│   │   │   ├── nsuserdefaults.ts
│   │   │   ├── pasteboard.ts
│   │   │   ├── pinning.ts
│   │   │   ├── plist.ts
│   │   │   └── userinterface.ts
│   │   ├── lib/
│   │   │   ├── color.ts
│   │   │   ├── constants.ts
│   │   │   ├── helpers.ts
│   │   │   ├── interfaces.ts
│   │   │   └── jobs.ts
│   │   └── rpc/
│   │       ├── android.ts
│   │       ├── environment.ts
│   │       ├── ios.ts
│   │       ├── jobs.ts
│   │       ├── memory.ts
│   │       └── other.ts
│   ├── tsconfig.json
│   └── tslint.json
├── objection/
│   ├── __init__.py
│   ├── api/
│   │   ├── __init__.py
│   │   ├── app.py
│   │   ├── rpc.py
│   │   └── script.py
│   ├── commands/
│   │   ├── __init__.py
│   │   ├── android/
│   │   │   ├── __init__.py
│   │   │   ├── clipboard.py
│   │   │   ├── command.py
│   │   │   ├── general.py
│   │   │   ├── generate.py
│   │   │   ├── heap.py
│   │   │   ├── hooking.py
│   │   │   ├── intents.py
│   │   │   ├── keystore.py
│   │   │   ├── monitor.py
│   │   │   ├── pinning.py
│   │   │   ├── proxy.py
│   │   │   └── root.py
│   │   ├── command_history.py
│   │   ├── custom.py
│   │   ├── device.py
│   │   ├── filemanager.py
│   │   ├── frida_commands.py
│   │   ├── http.py
│   │   ├── ios/
│   │   │   ├── __init__.py
│   │   │   ├── binary.py
│   │   │   ├── bundles.py
│   │   │   ├── cookies.py
│   │   │   ├── generate.py
│   │   │   ├── heap.py
│   │   │   ├── hooking.py
│   │   │   ├── jailbreak.py
│   │   │   ├── keychain.py
│   │   │   ├── monitor.py
│   │   │   ├── nsurlcredentialstorage.py
│   │   │   ├── nsuserdefaults.py
│   │   │   ├── pasteboard.py
│   │   │   ├── pinning.py
│   │   │   └── plist.py
│   │   ├── jobs.py
│   │   ├── memory.py
│   │   ├── mobile_packages.py
│   │   ├── plugin_manager.py
│   │   ├── sqlite.py
│   │   └── ui.py
│   ├── console/
│   │   ├── __init__.py
│   │   ├── cli.py
│   │   ├── commands.py
│   │   ├── completer.py
│   │   ├── helpfiles/
│   │   │   ├── !.txt
│   │   │   ├── android.clipboard.monitor.txt
│   │   │   ├── android.clipboard.txt
│   │   │   ├── android.heap.search.instances.txt
│   │   │   ├── android.hooking.list.activities.txt
│   │   │   ├── android.hooking.list.class_methods.txt
│   │   │   ├── android.hooking.list.classes.txt
│   │   │   ├── android.hooking.list.receivers.txt
│   │   │   ├── android.hooking.list.services.txt
│   │   │   ├── android.hooking.list.txt
│   │   │   ├── android.hooking.search.classes.txt
│   │   │   ├── android.hooking.search.methods.txt
│   │   │   ├── android.hooking.search.txt
│   │   │   ├── android.hooking.set.return_value.txt
│   │   │   ├── android.hooking.txt
│   │   │   ├── android.hooking.watch.class.txt
│   │   │   ├── android.hooking.watch.class_method.txt
│   │   │   ├── android.hooking.watch.txt
│   │   │   ├── android.intent.implicit_intents.txt
│   │   │   ├── android.intent.launch_activity.txt
│   │   │   ├── android.intent.launch_service.txt
│   │   │   ├── android.intent.txt
│   │   │   ├── android.keystore.clear.txt
│   │   │   ├── android.keystore.detail.txt
│   │   │   ├── android.keystore.list.txt
│   │   │   ├── android.keystore.txt
│   │   │   ├── android.keystore.watch.txt
│   │   │   ├── android.root.disable.txt
│   │   │   ├── android.root.simulate.txt
│   │   │   ├── android.shell_exec.txt
│   │   │   ├── android.sslpinning.disable.txt
│   │   │   ├── android.sslpinning.txt
│   │   │   ├── android.txt
│   │   │   ├── android.ui.FLAG_SECURE.txt
│   │   │   ├── android.ui.screenshot.txt
│   │   │   ├── cd.txt
│   │   │   ├── env.txt
│   │   │   ├── exit.txt
│   │   │   ├── file.download.txt
│   │   │   ├── file.txt
│   │   │   ├── file.upload.txt
│   │   │   ├── frida.txt
│   │   │   ├── import.txt
│   │   │   ├── ios.bundles.list_bundles.txt
│   │   │   ├── ios.bundles.list_frameworks.txt
│   │   │   ├── ios.bundles.txt
│   │   │   ├── ios.cookies.get.txt
│   │   │   ├── ios.cookies.txt
│   │   │   ├── ios.hooking.list.class_methods.txt
│   │   │   ├── ios.hooking.list.classes.txt
│   │   │   ├── ios.hooking.list.txt
│   │   │   ├── ios.hooking.search.classes.txt
│   │   │   ├── ios.hooking.search.methods.txt
│   │   │   ├── ios.hooking.search.txt
│   │   │   ├── ios.hooking.set.return_value.txt
│   │   │   ├── ios.hooking.set.txt
│   │   │   ├── ios.hooking.txt
│   │   │   ├── ios.hooking.watch.class.txt
│   │   │   ├── ios.hooking.watch.method.txt
│   │   │   ├── ios.hooking.watch.txt
│   │   │   ├── ios.jailbreak.disable.txt
│   │   │   ├── ios.jailbreak.simulate.txt
│   │   │   ├── ios.jailbreak.txt
│   │   │   ├── ios.keychain.add.txt
│   │   │   ├── ios.keychain.clear.txt
│   │   │   ├── ios.keychain.dump.txt
│   │   │   ├── ios.keychain.txt
│   │   │   ├── ios.monitor.crypto.txt
│   │   │   ├── ios.nsuserdefaults.get.txt
│   │   │   ├── ios.nsuserdefaults.txt
│   │   │   ├── ios.pasteboard.monitor.txt
│   │   │   ├── ios.pasteboard.txt
│   │   │   ├── ios.plist.cat.txt
│   │   │   ├── ios.plist.txt
│   │   │   ├── ios.sslpinning.disable.txt
│   │   │   ├── ios.sslpinning.txt
│   │   │   ├── ios.txt
│   │   │   ├── ios.ui.alert.txt
│   │   │   ├── ios.ui.dump.txt
│   │   │   ├── ios.ui.screenshot.txt
│   │   │   ├── ios.ui.touchid_bypass.txt
│   │   │   ├── ios.ui.txt
│   │   │   ├── jobs.kill.txt
│   │   │   ├── jobs.list.txt
│   │   │   ├── jobs.txt
│   │   │   ├── ls.txt
│   │   │   ├── memory.dump.all.txt
│   │   │   ├── memory.dump.from_base.txt
│   │   │   ├── memory.dump.txt
│   │   │   ├── memory.list.exports.txt
│   │   │   ├── memory.list.modules.txt
│   │   │   ├── memory.list.txt
│   │   │   ├── memory.search.txt
│   │   │   ├── memory.txt
│   │   │   ├── memory.write.txt
│   │   │   ├── plugin.load.txt
│   │   │   ├── plugin.txt
│   │   │   ├── pwd.print.txt
│   │   │   ├── pwd.txt
│   │   │   ├── reconnect.txt
│   │   │   ├── rm.txt
│   │   │   ├── sqlite.connect.txt
│   │   │   ├── sqlite.disconnect.txt
│   │   │   ├── sqlite.execute.query.txt
│   │   │   ├── sqlite.execute.schema.txt
│   │   │   ├── sqlite.execute.txt
│   │   │   ├── sqlite.status.txt
│   │   │   ├── sqlite.sync.txt
│   │   │   ├── sqlite.txt
│   │   │   ├── ui.alert.txt
│   │   │   └── ui.txt
│   │   └── repl.py
│   ├── state/
│   │   ├── __init__.py
│   │   ├── api.py
│   │   ├── app.py
│   │   ├── connection.py
│   │   ├── device.py
│   │   ├── filemanager.py
│   │   └── jobs.py
│   └── utils/
│       ├── __init__.py
│       ├── agent.py
│       ├── assets/
│       │   ├── javahookmanager.js
│       │   ├── network_security_config.xml
│       │   ├── objchookmanager.js
│       │   └── objection.jks
│       ├── helpers.py
│       ├── patchers/
│       │   ├── __init__.py
│       │   ├── android.py
│       │   ├── base.py
│       │   ├── github.py
│       │   └── ios.py
│       ├── plugin.py
│       └── update_checker.py
├── plugins/
│   ├── README.md
│   ├── api/
│   │   ├── __init__.py
│   │   └── index.js
│   ├── flex/
│   │   ├── README.md
│   │   ├── __init__.py
│   │   ├── index.js
│   │   ├── libFlex.h
│   │   └── libFlex.m
│   ├── mettle/
│   │   ├── README.md
│   │   ├── __init__.py
│   │   └── index.js
│   └── stetho/
│       ├── README.md
│       ├── __init__.py
│       └── index.js
├── pyproject.toml
└── tests/
    ├── __init__.py
    ├── commands/
    │   ├── __init__.py
    │   ├── android/
    │   │   ├── __init__.py
    │   │   ├── test_clipboard.py
    │   │   ├── test_command.py
    │   │   ├── test_heap.py
    │   │   ├── test_hooking.py
    │   │   ├── test_intents.py
    │   │   ├── test_keystore.py
    │   │   ├── test_pinning.py
    │   │   └── test_root.py
    │   ├── ios/
    │   │   ├── __init__.py
    │   │   ├── test_bundles.py
    │   │   ├── test_cookies.py
    │   │   ├── test_hooking.py
    │   │   ├── test_jailbreak.py
    │   │   ├── test_keychain.py
    │   │   ├── test_nsurlcredentialstorage.py
    │   │   ├── test_nsuserdefaults.py
    │   │   ├── test_pasteboard.py
    │   │   ├── test_pinning.py
    │   │   └── test_plist.py
    │   ├── test_command_history.py
    │   ├── test_device.py
    │   ├── test_filemanager.py
    │   ├── test_frida_commands.py
    │   ├── test_jobs.py
    │   ├── test_memory.py
    │   ├── test_mobile_packages.py
    │   ├── test_plugin_manager.py
    │   └── test_ui.py
    ├── console/
    │   ├── __init__.py
    │   ├── test_cli.py
    │   ├── test_completer.py
    │   └── test_repl.py
    ├── data/
    │   └── plugin/
    │       └── __init__.py
    ├── helpers.py
    ├── state/
    │   ├── __init__.py
    │   ├── test_app.py
    │   └── test_jobs.py
    └── utils/
        ├── __init__.py
        ├── patchers/
        │   ├── __init__.py
        │   ├── test_android.py
        │   ├── test_base.py
        │   ├── test_github.py
        │   └── test_ios.py
        └── test_helpers.py
Download .txt
SYMBOL INDEX (807 symbols across 122 files)

FILE: agent/src/android/hooking.ts
  type PatternType (line 22) | enum PatternType {

FILE: agent/src/android/intent.ts
  constant FLAG_ACTIVITY_NEW_TASK (line 13) | const FLAG_ACTIVITY_NEW_TASK = 0x10000000;

FILE: agent/src/android/lib/interfaces.ts
  type IAndroidFilesystem (line 3) | interface IAndroidFilesystem {
  type IExecutedCommand (line 10) | interface IExecutedCommand {
  type IKeyStoreEntry (line 16) | interface IKeyStoreEntry {
  type ICurrentActivityFragment (line 22) | interface ICurrentActivityFragment {
  type IHeapClassDictionary (line 27) | interface IHeapClassDictionary {
  type IHeapObject (line 31) | interface IHeapObject {
  type IHeapNormalised (line 36) | interface IHeapNormalised {
  type IJavaField (line 42) | interface IJavaField {
  type IKeyStoreDetail (line 47) | interface IKeyStoreDetail {

FILE: agent/src/android/lib/types.ts
  type JavaClass (line 1) | type JavaClass = any;
  type JavaMethodsOverloadsResult (line 3) | type JavaMethodsOverloadsResult = any;
  type ClipboardManager (line 5) | type ClipboardManager = JavaClass | any;
  type File (line 6) | type File = JavaClass | any;
  type Throwable (line 7) | type Throwable = JavaClass | any;
  type PackageManager (line 8) | type PackageManager = JavaClass | any;
  type ArrayMap (line 9) | type ArrayMap = JavaClass | any;
  type ActivityThread (line 10) | type ActivityThread = JavaClass | any;
  type Intent (line 11) | type Intent = JavaClass | any;
  type KeyStore (line 12) | type KeyStore = JavaClass | any;
  type KeyFactory (line 13) | type KeyFactory = JavaClass | any;
  type KeyInfo (line 14) | type KeyInfo = JavaClass | any;
  type SecretKeyFactory (line 15) | type SecretKeyFactory = JavaClass | any;
  type X509TrustManager (line 16) | type X509TrustManager = JavaClass | any;
  type SSLContext (line 17) | type SSLContext = JavaClass | any;
  type CertificatePinner (line 18) | type CertificatePinner = JavaClass | any;
  type PinningTrustManager (line 19) | type PinningTrustManager = JavaClass | any;
  type SSLCertificateChecker (line 20) | type SSLCertificateChecker = JavaClass | any;
  type TrustManagerImpl (line 21) | type TrustManagerImpl = JavaClass | any;
  type ArrayList (line 22) | type ArrayList = JavaClass | any;
  type JavaString (line 23) | type JavaString = JavaClass | any;
  type Runtime (line 24) | type Runtime = JavaClass | any;
  type IOException (line 25) | type IOException = JavaClass | any;
  type InputStreamReader (line 26) | type InputStreamReader = JavaClass | any;
  type BufferedReader (line 27) | type BufferedReader = JavaClass | any;
  type StringBuilder (line 28) | type StringBuilder = JavaClass | any;
  type Activity (line 29) | type Activity = JavaClass | any;
  type ActivityClientRecord (line 30) | type ActivityClientRecord = JavaClass | any;
  type Bitmap (line 31) | type Bitmap = JavaClass | any;
  type ByteArrayOutputStream (line 32) | type ByteArrayOutputStream = JavaClass | any;
  type CompressFormat (line 33) | type CompressFormat = JavaClass | any;
  type FridaOverload (line 34) | type FridaOverload = {

FILE: agent/src/android/pinning.ts
  method checkClientTrusted (line 55) | checkClientTrusted(chain, authType) { }
  method checkServerTrusted (line 57) | checkServerTrusted(chain, authType) { }
  method getAcceptedIssuers (line 58) | getAcceptedIssuers() {

FILE: agent/src/android/userinterface.ts
  constant FLAG_SECURE (line 17) | const FLAG_SECURE = 0x00002000;

FILE: agent/src/ios/crypto.ts
  type CCAlgorithm (line 9) | type CCAlgorithm = {
  type AlgorithmType (line 13) | type AlgorithmType = {
  method onEnter (line 80) | onEnter(args) {
  method onLeave (line 88) | onLeave(retval) {
  method onEnter (line 100) | onEnter(args) {
  method onLeave (line 142) | onLeave(retval) {
  method onEnter (line 154) | onEnter(args) {
  method onLeave (line 212) | onLeave(retval) {
  method onEnter (line 225) | onEnter(args) {
  method onLeave (line 261) | onLeave(retval) {
  method onEnter (line 271) | onEnter(args) {
  method onLeave (line 294) | onLeave(retval) {
  method onEnter (line 314) | onEnter(args) {
  method onLeave (line 327) | onLeave(retval) {

FILE: agent/src/ios/jailbreak.ts
  method onEnter (line 56) | onEnter(args) {
  method onLeave (line 73) | onLeave(retval) {
  method onEnter (line 133) | onEnter(args) {
  method onLeave (line 148) | onLeave(retval) {
  method onEnter (line 195) | onEnter(args) {
  method onLeave (line 206) | onLeave(retval) {
  method onLeave (line 258) | onLeave(retval) {
  method onLeave (line 299) | onLeave(retval) {

FILE: agent/src/ios/lib/constants.ts
  type kSec (line 3) | enum kSec {
  type NSSearchPaths (line 80) | enum NSSearchPaths {
  type BundleType (line 100) | enum BundleType {

FILE: agent/src/ios/lib/interfaces.ts
  type IKeychainData (line 3) | interface IKeychainData {
  type IKeychainItem (line 8) | interface IKeychainItem {
  type IIosFileSystem (line 33) | interface IIosFileSystem {
  type IIosFilePath (line 40) | interface IIosFilePath {
  type IIosCookie (line 47) | interface IIosCookie {
  type ICredential (line 58) | interface ICredential {
  type IFramework (line 67) | interface IFramework {
  type IHeapObject (line 74) | interface IHeapObject {
  type IBinaryModuleDictionary (line 83) | interface IBinaryModuleDictionary {
  type IBinaryInfo (line 87) | interface IBinaryInfo {

FILE: agent/src/ios/lib/types.ts
  type NSDictionary (line 3) | type NSDictionary = ObjCTypes.Object | any;
  type NSMutableDictionary (line 4) | type NSMutableDictionary = ObjCTypes.Object | any;
  type NSString (line 5) | type NSString = ObjCTypes.Object | any;
  type NSFileManager (line 6) | type NSFileManager = ObjCTypes.Object | any;
  type NSBundle (line 7) | type NSBundle = ObjCTypes.Object | any;
  type NSUserDefaults (line 8) | type NSUserDefaults = ObjCTypes.Object | any;
  type NSHTTPCookieStorage (line 9) | type NSHTTPCookieStorage = ObjCTypes.Object | any;
  type NSURLCredentialStorage (line 10) | type NSURLCredentialStorage = ObjCTypes.Object | any;
  type NSArray (line 11) | type NSArray = ObjCTypes.Object | any;
  type NSData (line 12) | type NSData = ObjCTypes.Object | any;
  type CFDictionaryRef (line 14) | type CFDictionaryRef = any;
  type CFTypeRef (line 15) | type CFTypeRef = any;

FILE: agent/src/ios/pinning.ts
  method onEnter (line 64) | onEnter(args) {
  method onEnter (line 93) | onEnter(args) {
  method onEnter (line 117) | onEnter(args) {
  method onEnter (line 147) | onEnter(args) {
  method onEnter (line 198) | onEnter(args) {
  method onLeave (line 267) | onLeave(retval) {
  method onLeave (line 299) | onLeave(retval) {

FILE: agent/src/ios/userinterface.ts
  method onEnter (line 84) | onEnter(args) {
  method onEnter (line 138) | onEnter(args) {

FILE: agent/src/lib/constants.ts
  type DeviceType (line 1) | enum DeviceType {

FILE: agent/src/lib/helpers.ts
  function reverseEnumLookup (line 6) | function reverseEnumLookup<T>(enumType: T, value: string): string {
  function printArgs (line 51) | function printArgs(args: {[key: string]:object}): string {

FILE: agent/src/lib/interfaces.ts
  type IFridaInfo (line 1) | interface IFridaInfo {
  type IIosPackage (line 10) | interface IIosPackage {
  type IAndroidPackage (line 19) | interface IAndroidPackage {
  type IIosBundlePaths (line 32) | interface IIosBundlePaths {

FILE: agent/src/lib/jobs.ts
  class Job (line 3) | class Job {
    method constructor (line 10) | constructor(identifier: number, type: string) {
    method addInvocation (line 15) | addInvocation(invocation: any): void {
    method addImplementation (line 25) | addImplementation(implementation: any): void {
    method addReplacement (line 36) | addReplacement(replacement: any): void {
    method killAll (line 41) | killAll(): void {

FILE: objection/__init__.py
  function _load_version (line 8) | def _load_version() -> str:

FILE: objection/api/app.py
  function create_app (line 7) | def create_app() -> Flask:

FILE: objection/api/rpc.py
  function invoke (line 10) | def invoke(method):

FILE: objection/api/script.py
  function runonce (line 9) | def runonce():

FILE: objection/commands/android/clipboard.py
  function monitor (line 4) | def monitor(args: list = None) -> None:

FILE: objection/commands/android/command.py
  function execute (line 6) | def execute(args: list) -> None:

FILE: objection/commands/android/general.py
  function deoptimise (line 4) | def deoptimise(args: list) -> None:

FILE: objection/commands/android/generate.py
  function clazz (line 8) | def clazz(args: list) -> None:
  function simple (line 26) | def simple(args: list) -> None:

FILE: objection/commands/android/heap.py
  function _should_ignore_methods_with_arguments (line 12) | def _should_ignore_methods_with_arguments(args) -> bool:
  function _should_return_as_string (line 23) | def _should_return_as_string(args) -> bool:
  function instances (line 34) | def instances(args: list) -> None:
  function methods (line 63) | def methods(args: list) -> None:
  function execute (line 92) | def execute(args: list) -> None:
  function fields (line 119) | def fields(args: list) -> None:
  function evaluate (line 143) | def evaluate(args: list) -> None:

FILE: objection/commands/android/hooking.py
  function _is_pattern_or_constant (line 10) | def _is_pattern_or_constant(s: str) -> bool:
  function _string_is_true (line 32) | def _string_is_true(s: str) -> bool:
  function _should_dump_backtrace (line 43) | def _should_dump_backtrace(args: list = None) -> bool:
  function _should_watch (line 54) | def _should_watch(args: list = None) -> bool:
  function _should_dump_args (line 65) | def _should_dump_args(args: list = None) -> bool:
  function _should_dump_return_value (line 76) | def _should_dump_return_value(args: list = None) -> bool:
  function _should_dump_json (line 87) | def _should_dump_json(args: list) -> bool:
  function _should_be_quiet (line 98) | def _should_be_quiet(args: list) -> bool:
  function _should_print_only_classes (line 109) | def _should_print_only_classes(args: list = None) -> bool:
  function _get_flag_value (line 120) | def _get_flag_value(flag: str, args: list) -> Optional[str]:
  function show_android_classes (line 143) | def show_android_classes(args: list = None) -> None:
  function show_android_class_loaders (line 162) | def show_android_class_loaders(args: list = None) -> None:
  function show_android_class_methods (line 179) | def show_android_class_methods(args: list = None) -> None:
  function notify (line 203) | def notify(args: list = None) -> None:
  function watch (line 231) | def watch(args: list = None) -> None:
  function search (line 261) | def search(args: list = None) -> None:
  function show_registered_broadcast_receivers (line 329) | def show_registered_broadcast_receivers(args: list = None) -> None:
  function show_registered_services (line 345) | def show_registered_services(args: list = None) -> None:
  function show_registered_activities (line 361) | def show_registered_activities(args: list = None) -> None:
  function get_current_activity (line 377) | def get_current_activity(args: list = None) -> None:
  function set_method_return_value (line 391) | def set_method_return_value(args: list = None) -> None:

FILE: objection/commands/android/intents.py
  function _should_dump_backtrace (line 6) | def _should_dump_backtrace(args: list = None) -> bool:
  function analyze_implicit_intents (line 16) | def analyze_implicit_intents(args: list) -> None:
  function launch_activity (line 30) | def launch_activity(args: list) -> None:
  function launch_service (line 48) | def launch_service(args: list) -> None:

FILE: objection/commands/android/keystore.py
  function _should_output_json (line 9) | def _should_output_json(args: list) -> bool:
  function entries (line 21) | def entries(args: list = None) -> None:
  function detail (line 36) | def detail(args: list = None) -> None:
  function clear (line 72) | def clear(args: list = None) -> None:
  function watch (line 87) | def watch(args: list = None) -> None:

FILE: objection/commands/android/monitor.py
  function string_canary (line 6) | def string_canary(args: list) -> None:

FILE: objection/commands/android/pinning.py
  function _should_be_quiet (line 4) | def _should_be_quiet(args: list) -> bool:
  function android_disable (line 16) | def android_disable(args: list = None) -> None:

FILE: objection/commands/android/proxy.py
  function android_proxy_set (line 6) | def android_proxy_set(args: list = None) -> None:

FILE: objection/commands/android/root.py
  function disable (line 4) | def disable(args: list = None) -> None:
  function simulate (line 16) | def simulate(args: list = None) -> None:

FILE: objection/commands/command_history.py
  function history (line 8) | def history(args: list) -> None:
  function save (line 22) | def save(args: list) -> None:
  function clear (line 43) | def clear(args: list) -> None:

FILE: objection/commands/custom.py
  function evaluate (line 12) | def evaluate(args: list) -> None:

FILE: objection/commands/device.py
  function get_environment (line 8) | def get_environment(args: list = None) -> None:
  function _get_ios_environment (line 26) | def _get_ios_environment() -> None:
  function _get_android_environment (line 43) | def _get_android_environment() -> None:

FILE: objection/commands/filemanager.py
  function _should_download_folder (line 19) | def _should_download_folder(args: list) -> bool:
  function cd (line 30) | def cd(args: list) -> None:
  function path_exists (line 140) | def path_exists(path: str) -> bool:
  function _path_exists_ios (line 155) | def _path_exists_ios(path: str) -> bool:
  function _path_exists_android (line 167) | def _path_exists_android(path: str) -> bool:
  function pwd (line 179) | def pwd(args: list = None) -> str:
  function pwd_print (line 202) | def pwd_print(args: list = None) -> None:
  function _pwd_ios (line 213) | def _pwd_ios() -> str:
  function _pwd_android (line 230) | def _pwd_android() -> str:
  function ls (line 247) | def ls(args: list) -> None:
  function _ls_ios (line 272) | def _ls_ios(path: str) -> None:
  function _ls_android (line 346) | def _ls_android(path: str) -> None:
  function download (line 397) | def download(args: list) -> None:
  function _download_ios (line 428) | def _download_ios(path: str, destination: str, should_download_folder: b...
  function _download_android (line 492) | def _download_android(path: str, destination: str, should_download_folde...
  function upload (line 555) | def upload(args: list) -> None:
  function _upload_ios (line 581) | def _upload_ios(path: str, destination: str) -> None:
  function _upload_android (line 616) | def _upload_android(path: str, destination: str) -> None:
  function rm (line 651) | def rm(args: list) -> None:
  function _rm_android (line 679) | def _rm_android(t: str) -> None:
  function _rm_ios (line 701) | def _rm_ios(t: str) -> None:
  function cat (line 723) | def cat(args: list):
  function _get_short_ios_listing (line 759) | def _get_short_ios_listing() -> list:
  function _get_short_android_listing (line 804) | def _get_short_android_listing() -> list:
  function list_folders_in_current_fm_directory (line 842) | def list_folders_in_current_fm_directory() -> dict:
  function list_files_in_current_fm_directory (line 876) | def list_files_in_current_fm_directory() -> dict:
  function list_content_in_current_fm_directory (line 910) | def list_content_in_current_fm_directory() -> dict:

FILE: objection/commands/frida_commands.py
  function _should_disable_exception_handler (line 10) | def _should_disable_exception_handler(args: list = None) -> bool:
  function frida_environment (line 22) | def frida_environment(args: list = None) -> None:
  function ping (line 42) | def ping(args: list = None) -> None:
  function load_background (line 57) | def load_background(args: list = None) -> None:

FILE: objection/commands/http.py
  function start (line 7) | def start(args: list) -> None:
  function stop (line 26) | def stop(args: list) -> None:
  function status (line 38) | def status(args: list) -> None:

FILE: objection/commands/ios/binary.py
  function info (line 7) | def info(args: list) -> None:

FILE: objection/commands/ios/bundles.py
  function _should_include_apple_bundles (line 8) | def _should_include_apple_bundles(args: list) -> bool:
  function _should_print_full_path (line 19) | def _should_print_full_path(args: list) -> bool:
  function _is_apple_bundle (line 30) | def _is_apple_bundle(bundle: str) -> bool:
  function show_frameworks (line 50) | def show_frameworks(args: list = None) -> None:
  function show_bundles (line 79) | def show_bundles(args: list = None) -> None:

FILE: objection/commands/ios/cookies.py
  function _should_dump_json (line 9) | def _should_dump_json(args: list) -> bool:
  function get (line 20) | def get(args: list) -> None:

FILE: objection/commands/ios/generate.py
  function clazz (line 8) | def clazz(args: list) -> None:
  function simple (line 26) | def simple(args: list) -> None:

FILE: objection/commands/ios/heap.py
  function _should_ignore_methods_with_arguments (line 12) | def _should_ignore_methods_with_arguments(args) -> bool:
  function _should_print_as_utf8 (line 23) | def _should_print_as_utf8(args) -> bool:
  function _should_return_as_string (line 34) | def _should_return_as_string(args) -> bool:
  function _should_interpret_inline_js (line 45) | def _should_interpret_inline_js(args) -> bool:
  function instances (line 56) | def instances(args: list) -> None:
  function ivars (line 97) | def ivars(args: list) -> None:
  function methods (line 121) | def methods(args: list) -> None:
  function execute (line 152) | def execute(args: list) -> None:
  function evaluate (line 178) | def evaluate(args: list) -> None:

FILE: objection/commands/ios/hooking.py
  function _should_ignore_native_classes (line 52) | def _should_ignore_native_classes(args: list) -> bool:
  function _should_include_parent_methods (line 67) | def _should_include_parent_methods(args: list) -> bool:
  function _class_is_prefixed_with_native (line 82) | def _class_is_prefixed_with_native(class_name: str) -> bool:
  function _string_is_true (line 99) | def _string_is_true(s: str) -> bool:
  function _should_dump_backtrace (line 110) | def _should_dump_backtrace(args: list) -> bool:
  function _should_dump_args (line 121) | def _should_dump_args(args: list) -> bool:
  function _should_dump_return_value (line 132) | def _should_dump_return_value(args: list) -> bool:
  function _should_print_only_classes (line 143) | def _should_print_only_classes(args: list) -> bool:
  function _should_dump_json (line 154) | def _should_dump_json(args: list) -> bool:
  function _should_be_quiet (line 165) | def _should_be_quiet(args: list) -> bool:
  function _get_flag_value (line 176) | def _get_flag_value(flag: str, args: list) -> Optional[str]:
  function show_ios_classes (line 199) | def show_ios_classes(args: list = None) -> None:
  function show_ios_class_methods (line 224) | def show_ios_class_methods(args: list) -> None:
  function set_method_return_value (line 253) | def set_method_return_value(args: list) -> None:
  function watch (line 274) | def watch(args: list) -> None:
  function search (line 296) | def search(args: list) -> None:

FILE: objection/commands/ios/jailbreak.py
  function disable (line 4) | def disable(args: list = None) -> None:
  function simulate (line 16) | def simulate(args: list = None) -> None:

FILE: objection/commands/ios/keychain.py
  function _should_output_json (line 9) | def _should_output_json(args: list) -> bool:
  function _should_do_smart_decode (line 21) | def _should_do_smart_decode(args: list) -> bool:
  function _data_flag_has_identifier (line 33) | def _data_flag_has_identifier(args: list) -> bool:
  function _get_flag_value (line 48) | def _get_flag_value(args: list, flag: str) -> str:
  function dump (line 60) | def dump(args: list = None) -> None:
  function dump_raw (line 107) | def dump_raw(args: list = None) -> None:
  function clear (line 122) | def clear(args: list = None) -> None:
  function remove (line 141) | def remove(args: list) -> None:
  function update (line 165) | def update(args: list) -> None:
  function add (line 191) | def add(args: list) -> None:

FILE: objection/commands/ios/monitor.py
  function crypto_enable (line 4) | def crypto_enable(args: list = None) -> None:

FILE: objection/commands/ios/nsurlcredentialstorage.py
  function dump (line 7) | def dump(args: list = None) -> None:

FILE: objection/commands/ios/nsuserdefaults.py
  function get (line 6) | def get(args: list = None) -> None:

FILE: objection/commands/ios/pasteboard.py
  function monitor (line 4) | def monitor(args: list = None) -> None:

FILE: objection/commands/ios/pinning.py
  function _should_be_quiet (line 4) | def _should_be_quiet(args: list) -> bool:
  function ios_disable (line 16) | def ios_disable(args: list = None) -> None:

FILE: objection/commands/ios/plist.py
  function cat (line 10) | def cat(args: list = None) -> None:

FILE: objection/commands/jobs.py
  function show (line 8) | def show(args: list = None) -> None:
  function kill (line 38) | def kill(args: list) -> None:
  function list_current_jobs (line 55) | def list_current_jobs() -> dict:
  function sync_job_manager (line 70) | def sync_job_manager() -> dict[int, Job]:

FILE: objection/commands/memory.py
  function _is_string_input (line 15) | def _is_string_input(args: list) -> bool:
  function _should_only_dump_offsets (line 27) | def _should_only_dump_offsets(args: list) -> bool:
  function _is_string_pattern (line 39) | def _is_string_pattern(args: list) -> bool:
  function _is_string_replace (line 51) | def _is_string_replace(args: list) -> bool:
  function _should_output_json (line 63) | def _should_output_json(args: list) -> bool:
  function _get_chunks (line 74) | def _get_chunks(addr: int, size: int, block_size: int = BLOCK_SIZE) -> L...
  function dump_all (line 107) | def dump_all(args: list) -> None:
  function dump_from_base (line 164) | def dump_from_base(args: list) -> None:
  function list_modules (line 205) | def list_modules(args: list = None) -> None:
  function list_exports (line 245) | def list_exports(args: list) -> None:
  function find_pattern (line 290) | def find_pattern(args: list) -> None:
  function replace_pattern (line 323) | def replace_pattern(args: list) -> None:
  function write (line 362) | def write(args: list) -> None:

FILE: objection/commands/mobile_packages.py
  function patch_ios_ipa (line 13) | def patch_ios_ipa(source: str, codesign_signature: str, provision_file: ...
  function patch_android_apk (line 99) | def patch_android_apk(source: str, architecture: str, pause: bool, skip_...
  function sign_android_apk (line 239) | def sign_android_apk(source: str, skip_cleanup: bool = True) -> None:

FILE: objection/commands/plugin_manager.py
  function load_plugin (line 11) | def load_plugin(args: list = None) -> None:

FILE: objection/commands/sqlite.py
  function modify_config (line 12) | def modify_config(rc):
  function cleanup (line 32) | def cleanup(p) -> None:
  function _should_sync_once_done (line 43) | def _should_sync_once_done(args: list) -> bool:
  function connect (line 54) | def connect(args: list) -> None:

FILE: objection/commands/ui.py
  function alert (line 7) | def alert(args: list = None) -> None:
  function _alert_ios (line 28) | def _alert_ios(message: str):
  function ios_screenshot (line 40) | def ios_screenshot(args: list = None) -> None:
  function dump_ios_ui (line 66) | def dump_ios_ui(args: list = None) -> None:
  function bypass_touchid (line 80) | def bypass_touchid(args: list = None) -> None:
  function android_screenshot (line 93) | def android_screenshot(args: list = None) -> None:
  function android_flag_secure (line 120) | def android_flag_secure(args: list = None) -> None:

FILE: objection/console/cli.py
  function get_agent (line 19) | def get_agent() -> Agent:
  function cli (line 62) | def cli(network: bool, host: str, port: int, api_host: str, api_port: int,
  function api (line 105) | def api():
  function start (line 129) | def start(plugin_folder: str, quiet: bool, startup_command: str, file_co...
  function explore (line 205) | def explore(plugin_folder: str, quiet: bool, startup_command: str, file_...
  function run (line 224) | def run(hook_debug: bool, command: tuple) -> None:
  function version (line 247) | def version() -> None:
  function patchipa (line 276) | def patchipa(source: str, gadget_version: str, codesign_signature: str, ...
  function patchapk (line 322) | def patchapk(source: str, architecture: str, gadget_version: str, pause:...
  function signapk (line 351) | def signapk(sources, skip_cleanup: bool) -> None:

FILE: objection/console/completer.py
  class CommandCompleter (line 10) | class CommandCompleter(Completer):
    method __init__ (line 15) | def __init__(self) -> None:
    method find_completions (line 19) | def find_completions(self, document: Document) -> dict:
    method get_completions (line 99) | def get_completions(self, document: Document, complete_event: Complete...

FILE: objection/console/repl.py
  class Repl (line 23) | class Repl(object):
    method __init__ (line 28) | def __init__(self) -> None:
    method get_prompt_session (line 35) | def get_prompt_session(self) -> PromptSession:
    method get_prompt_style (line 52) | def get_prompt_style() -> Style:
    method get_prompt_message (line 78) | def get_prompt_message() -> list:
    method run_command (line 101) | def run_command(self, document: str) -> None:
    method _find_command_exec_method (line 174) | def _find_command_exec_method(self, tokens: list) -> tuple:
    method _find_command_help (line 229) | def _find_command_help(self, tokens: list) -> str:
    method handle_reconnect (line 283) | def handle_reconnect(document: str) -> bool:
    method run (line 345) | def run(self, quiet: bool) -> None:

FILE: objection/state/api.py
  class ApiState (line 4) | class ApiState(object):
    method __init__ (line 7) | def __init__(self):
    method append_api_blueprint (line 11) | def append_api_blueprint(self, blueprint):
    method start (line 25) | def start(self, host: str, port: int, debug: bool = False):

FILE: objection/state/app.py
  class AppState (line 1) | class AppState(object):
    method __init__ (line 4) | def __init__(self):
    method add_command_to_history (line 11) | def add_command_to_history(self, command: str) -> None:
    method clear_command_history (line 22) | def clear_command_history(self) -> None:
    method should_debug_hooks (line 32) | def should_debug_hooks(self) -> bool:
    method should_debug (line 41) | def should_debug(self) -> bool:

FILE: objection/state/connection.py
  class StateConnection (line 1) | class StateConnection(object):
    method __init__ (line 4) | def __init__(self) -> None:
    method use_usb (line 26) | def use_usb(self) -> None:
    method use_network (line 36) | def use_network(self) -> None:
    method get_comms_type (line 46) | def get_comms_type(self) -> int:
    method get_api (line 53) | def get_api(self):
    method set_agent (line 65) | def set_agent(self, agent):
    method get_agent (line 75) | def get_agent(self):
    method __repr__ (line 82) | def __repr__(self) -> str:

FILE: objection/state/device.py
  class Device (line 1) | class Device(object):
  class Android (line 6) | class Android(Device):
  class Ios (line 13) | class Ios(Device):
  class DeviceState (line 20) | class DeviceState(object):
    method set_version (line 26) | def set_version(self, v: str):
    method set_platform (line 36) | def set_platform(self, t: Device):
    method __repr__ (line 46) | def __repr__(self) -> str:

FILE: objection/state/filemanager.py
  class FileManagerState (line 1) | class FileManagerState(object):
    method __init__ (line 4) | def __init__(self) -> None:

FILE: objection/state/jobs.py
  class Job (line 10) | class Job(object):
    method __init__ (line 13) | def __init__(self, name, job_type, handle, uuid: int = None) -> None:
    method end (line 31) | def end(self):
  class JobManagerState (line 49) | class JobManagerState(object):
    method __init__ (line 52) | def __init__(self) -> None:
    method add_job (line 63) | def add_job(self, new_job: Job) -> None:
    method remove_job (line 75) | def remove_job(self, job_uuid: int):
    method cleanup (line 89) | def cleanup(self) -> None:

FILE: objection/utils/__init__.py
  class MakeFileHandler (line 10) | class MakeFileHandler(logging.FileHandler):
    method __init__ (line 15) | def __init__(self, filename: str, mode: str = 'a', encoding: str = Non...
  function new_secho (line 30) | def new_secho(text: str, **kwargs) -> None:

FILE: objection/utils/agent.py
  class AgentConfig (line 20) | class AgentConfig(object):
  class OutputHandlers (line 35) | class OutputHandlers(object):
    method device_output (line 38) | def device_output(self):
    method device_lost (line 41) | def device_lost(self):
    method session_on_detached (line 45) | def session_on_detached(message: str, crash):
    method script_on_message (line 79) | def script_on_message(message: dict, data):
  class Agent (line 112) | class Agent(object):
    method __init__ (line 127) | def __init__(self, config: AgentConfig):
    method _get_agent_source (line 143) | def _get_agent_source(self) -> str:
    method set_device (line 155) | def set_device(self):
    method set_target_pid (line 190) | def set_target_pid(self):
    method attach (line 276) | def attach(self):
    method attach_script (line 304) | def attach_script(self, job_name, source):
    method update_device_state (line 321) | def update_device_state(self):
    method resume (line 340) | def resume(self):
    method exports (line 356) | def exports(self):
    method run (line 368) | def run(self):
    method teardown (line 387) | def teardown(self):

FILE: objection/utils/assets/javahookmanager.js
  class JavaHookManager (line 10) | class JavaHookManager {
    method constructor (line 14) | constructor(clazzName, verbose = false) {
    method printVerbose (line 25) | printVerbose(message) {
    method print (line 30) | print(message) {
    method populateAvailableMethods (line 36) | populateAvailableMethods(clazz) {
    method validMethod (line 62) | validMethod(method) {
    method isHookingMethod (line 69) | isHookingMethod(method) {
    method hook (line 80) | hook(m, f = null) {
    method unhook (line 111) | unhook(method) {

FILE: objection/utils/assets/objchookmanager.js
  class ObjCHookManager (line 10) | class ObjCHookManager {
    method constructor (line 14) | constructor(clazzName, verbose = false) {
    method printVerbose (line 25) | printVerbose(message) {
    method print (line 30) | print(message) {
    method populateAvailableMethods (line 34) | populateAvailableMethods(clazz) {
    method validMethod (line 40) | validMethod(method) {
    method isHookingMethod (line 47) | isHookingMethod(method) {
    method hook (line 58) | hook(m, enter = null, leave = null) {
    method unhook (line 86) | unhook(method) {

FILE: objection/utils/helpers.py
  function debug_print (line 10) | def debug_print(message: str) -> None:
  function pretty_concat (line 23) | def pretty_concat(data: str, at_most: int = 75, left: bool = False) -> str:
  function sizeof_fmt (line 47) | def sizeof_fmt(num: float, suffix: str = 'B') -> str:
  function get_tokens (line 59) | def get_tokens(text: str) -> list:
  function clean_argument_flags (line 83) | def clean_argument_flags(args: list) -> list:
  function to_snake_case (line 97) | def to_snake_case(w: str) -> str:
  function print_frida_connection_help (line 109) | def print_frida_connection_help() -> None:
  function warn_about_older_operating_systems (line 129) | def warn_about_older_operating_systems() -> None:

FILE: objection/utils/patchers/android.py
  class AndroidGadget (line 19) | class AndroidGadget(BasePlatformGadget):
    method __init__ (line 36) | def __init__(self, github: Github) -> None:
    method set_architecture (line 56) | def set_architecture(self, architecture: str):
    method get_architecture (line 72) | def get_architecture(self) -> str:
    method get_frida_library_path (line 81) | def get_frida_library_path(self, packed: bool = False) -> str:
    method gadget_exists (line 95) | def gadget_exists(self) -> bool:
    method download (line 107) | def download(self):
    method _get_download_url (line 131) | def _get_download_url(self) -> str:
    method unpack (line 155) | def unpack(self):
    method cleanup (line 170) | def cleanup(self):
  class AndroidPatcher (line 182) | class AndroidPatcher(BasePlatformPatcher):
    method __init__ (line 203) | def __init__(self, skip_cleanup: bool = False, skip_resources: bool = ...
    method is_apktool_ready (line 220) | def is_apktool_ready(self) -> bool:
    method set_apk_source (line 270) | def set_apk_source(self, source: str):
    method _get_android_manifest (line 285) | def _get_android_manifest(self) -> ElementTree:
    method _get_appt_output (line 305) | def _get_appt_output(self):
    method _get_launchable_activity (line 328) | def _get_launchable_activity(self) -> str:
    method get_patched_apk_path (line 382) | def get_patched_apk_path(self) -> str:
    method get_temp_working_directory (line 391) | def get_temp_working_directory(self) -> str:
    method unpack_apk (line 400) | def unpack_apk(self, fix_concurrency_to = None):
    method inject_internet_permission (line 429) | def inject_internet_permission(self):
    method extract_native_libs_patch (line 463) | def extract_native_libs_patch(self):
    method flip_debug_flag_to_true (line 496) | def flip_debug_flag_to_true(self):
    method add_network_security_config (line 531) | def add_network_security_config(self):
    method _determine_smali_path_for_class (line 586) | def _determine_smali_path_for_class(self, target_class) -> str:
    method _determine_end_of_smali_method_from_line (line 631) | def _determine_end_of_smali_method_from_line(smali: list, start: int) ...
    method _determine_first_inject_point_of_smali_method_from_line (line 666) | def _determine_first_inject_point_of_smali_method_from_line(smali: lis...
    method _patch_smali_with_load_library (line 704) | def _patch_smali_with_load_library(self, smali_lines: list, inject_mar...
    method _revalue_locals_count (line 760) | def _revalue_locals_count(self, patched_smali: list, inject_marker: int):
    method inject_load_library (line 816) | def inject_load_library(self, target_class: str = None):
    method add_gadget_to_apk (line 867) | def add_gadget_to_apk(self, architecture: str, gadget_source: str, gad...
    method build_new_apk (line 892) | def build_new_apk(self, use_aapt2: bool = False, fix_concurrency_to = ...
    method zipalign_apk (line 918) | def zipalign_apk(self):
    method sign_apk (line 942) | def sign_apk(self):
    method __del__ (line 974) | def __del__(self):

FILE: objection/utils/patchers/base.py
  function list2posix_cmdline (line 16) | def list2posix_cmdline(seq):
  class BasePlatformGadget (line 28) | class BasePlatformGadget(object):
    method __init__ (line 31) | def __init__(self, github: Github) -> None:
    method get_local_version (line 41) | def get_local_version(gadget_type: str) -> str:
    method set_local_version (line 68) | def set_local_version(self, gadget_type: str, version: str):
  class BasePlatformPatcher (line 104) | class BasePlatformPatcher(object):
    method __init__ (line 110) | def __init__(self):
    method _check_commands (line 121) | def _check_commands(self) -> bool:
    method are_requirements_met (line 143) | def are_requirements_met(self):

FILE: objection/utils/patchers/github.py
  class Github (line 4) | class Github(object):
    method __init__ (line 13) | def __init__(self, gadget_version: str = None):
    method _call (line 23) | def _call(self, endpoint: str) -> dict:
    method get_latest_version (line 44) | def get_latest_version(self) -> str:
    method get_assets (line 56) | def get_assets(self) -> dict:

FILE: objection/utils/patchers/ios.py
  class IosGadget (line 17) | class IosGadget(BasePlatformGadget):
    method __init__ (line 24) | def __init__(self, github: Github) -> None:
    method get_gadget_path (line 38) | def get_gadget_path(self) -> str:
    method gadget_exists (line 48) | def gadget_exists(self):
    method download (line 57) | def download(self):
    method _get_download_url (line 83) | def _get_download_url(self) -> str:
    method unpack (line 103) | def unpack(self):
    method cleanup (line 124) | def cleanup(self):
  class IosPatcher (line 136) | class IosPatcher(BasePlatformPatcher):
    method __init__ (line 167) | def __init__(self, skip_cleanup: bool = False):
    method set_provsioning_profile (line 192) | def set_provsioning_profile(self, provision_file: str = None, bundle_i...
    method extract_ipa (line 269) | def extract_ipa(self, unzip_unicode, ipa_source: str) -> None:
    method set_application_binary (line 304) | def set_application_binary(self, binary: str = None) -> None:
    method patch_and_codesign_binary (line 330) | def patch_and_codesign_binary(self, frida_gadget: str, codesign_signat...
    method archive_and_codesign (line 395) | def archive_and_codesign(self, original_name: str, codesign_signature:...
    method get_patched_ipa_path (line 441) | def get_patched_ipa_path(self) -> str:
    method _set_bundle_id_from_profile (line 450) | def _set_bundle_id_from_profile(self):
    method _cleanup_extracted_data (line 493) | def _cleanup_extracted_data(self) -> None:
    method __del__ (line 504) | def __del__(self):

FILE: objection/utils/plugin.py
  class Plugin (line 9) | class Plugin(ABC):
    method __init__ (line 12) | def __init__(self, plugin_file: str, namespace: str, implementation: d...
    method _prepare_source (line 41) | def _prepare_source(self):
    method inject (line 77) | def inject(self) -> None:
    method _append_to_api (line 101) | def _append_to_api(self):

FILE: objection/utils/update_checker.py
  function cached_version_data (line 22) | def cached_version_data() -> version_data:
  function update_version_cache (line 41) | def update_version_cache(version: str) -> None:
  function notify_newer_version (line 56) | def notify_newer_version() -> None:
  function check_version (line 75) | def check_version() -> None:

FILE: plugins/api/__init__.py
  class ApiLoader (line 6) | class ApiLoader(Plugin):
    method __init__ (line 23) | def __init__(self, ns):
    method http_api (line 36) | def http_api(self) -> Blueprint:

FILE: plugins/flex/__init__.py
  class FlexLoader (line 10) | class FlexLoader(Plugin):
    method __init__ (line 13) | def __init__(self, ns):
    method load_flex (line 36) | def load_flex(self, args: list):
    method _upload_flex (line 56) | def _upload_flex(self, location: str) -> bool:

FILE: plugins/mettle/__init__.py
  class MettleLoader (line 10) | class MettleLoader(Plugin):
    method __init__ (line 13) | def __init__(self, ns):
    method load_mettle (line 40) | def load_mettle(self, args: list):
    method _upload_mettle (line 61) | def _upload_mettle(self, location: str) -> bool:
    method connect_mettle (line 78) | def connect_mettle(self, args: list):

FILE: plugins/stetho/__init__.py
  class StethoLoader (line 10) | class StethoLoader(Plugin):
    method __init__ (line 13) | def __init__(self, ns):
    method load_stetho (line 36) | def load_stetho(self, args: list):
    method _upload_stetho (line 55) | def _upload_stetho(self, location: str) -> bool:

FILE: tests/commands/android/test_clipboard.py
  class TestClipboard (line 7) | class TestClipboard(unittest.TestCase):
    method test_monitor (line 9) | def test_monitor(self, mock_api):

FILE: tests/commands/android/test_command.py
  class TestCommand (line 8) | class TestCommand(unittest.TestCase):
    method test_execute_prints_output (line 10) | def test_execute_prints_output(self, mock_api):

FILE: tests/commands/android/test_heap.py
  class TestHeap (line 8) | class TestHeap(unittest.TestCase):
    method test_print_live_instances_validates_command (line 10) | def test_print_live_instances_validates_command(self, mock_api):
    method test_print_live_instances_validates_command (line 18) | def test_print_live_instances_validates_command(self, mock_api):

FILE: tests/commands/android/test_hooking.py
  class TestHooking (line 11) | class TestHooking(unittest.TestCase):
    method test_checks_if_string_value_is_python_boolean_true (line 12) | def test_checks_if_string_value_is_python_boolean_true(self):
    method test_checks_if_string_value_is_python_boolean_false (line 17) | def test_checks_if_string_value_is_python_boolean_false(self):
    method test_argument_includes_backtrace_flag (line 22) | def test_argument_includes_backtrace_flag(self):
    method test_argument_dump_args_returns_true (line 30) | def test_argument_dump_args_returns_true(self):
    method test_argument_dump_args_returns_false (line 38) | def test_argument_dump_args_returns_false(self):
    method test_argument_dump_return_returns_true (line 45) | def test_argument_dump_return_returns_true(self):
    method test_argument_dump_return_returns_false (line 53) | def test_argument_dump_return_returns_false(self):
    method test_show_android_classes (line 61) | def test_show_android_classes(self, mock_api):
    method test_show_android_class_methods_validates_arguments (line 80) | def test_show_android_class_methods_validates_arguments(self):
    method test_show_android_class_methods (line 87) | def test_show_android_class_methods(self, mock_api):
    method test_show_registered_broadcast_receivers_handles_empty_data (line 106) | def test_show_registered_broadcast_receivers_handles_empty_data(self, ...
    method test_show_registered_broadcast_receivers (line 115) | def test_show_registered_broadcast_receivers(self, mock_api):
    method test_show_registered_services_handles_empty_data (line 133) | def test_show_registered_services_handles_empty_data(self, mock_api):
    method test_show_services (line 142) | def test_show_services(self, mock_api):
    method test_show_registered_activities_handles_empty_data (line 160) | def test_show_registered_activities_handles_empty_data(self, mock_api):
    method test_show_registered_activities (line 169) | def test_show_registered_activities(self, mock_api):
    method test_set_method_return_value_validates_arguments (line 186) | def test_set_method_return_value_validates_arguments(self):
    method test_set_method_return_value (line 195) | def test_set_method_return_value(self, mock_api):
    method test_get_current_activity_and_fragment (line 201) | def test_get_current_activity_and_fragment(self, mock_api):

FILE: tests/commands/android/test_intents.py
  class TestIntents (line 8) | class TestIntents(unittest.TestCase):
    method test_launch_activity_validates_arguments (line 9) | def test_launch_activity_validates_arguments(self):
    method test_launch_activity (line 16) | def test_launch_activity(self, mock_api):
    method test_launch_service_validates_arguments (line 21) | def test_launch_service_validates_arguments(self):
    method test_launch_service (line 28) | def test_launch_service(self, mock_api):
    method test_analyze_implicit_intents (line 34) | def test_analyze_implicit_intents(self, mock_api):

FILE: tests/commands/android/test_keystore.py
  class TestKeystore (line 8) | class TestKeystore(unittest.TestCase):
    method test_entries_handles_empty_data (line 10) | def test_entries_handles_empty_data(self, mock_api):
    method test_entries_handles (line 23) | def test_entries_handles(self, mock_api):
    method test_clear (line 42) | def test_clear(self, mock_confirm, mock_api):

FILE: tests/commands/android/test_pinning.py
  class TestPinning (line 7) | class TestPinning(unittest.TestCase):
    method test_pinning_disable (line 9) | def test_pinning_disable(self, mock_api):

FILE: tests/commands/android/test_root.py
  class TestRoot (line 7) | class TestRoot(unittest.TestCase):
    method test_disable (line 9) | def test_disable(self, mock_api):
    method test_simulate (line 15) | def test_simulate(self, mock_api):

FILE: tests/commands/ios/test_bundles.py
  class TestBundles (line 9) | class TestBundles(unittest.TestCase):
    method setUp (line 10) | def setUp(self) -> None:
    method test_should_include_apple_bundles_helper_is_true (line 38) | def test_should_include_apple_bundles_helper_is_true(self):
    method test_should_include_apple_bundles_helper_is_false (line 42) | def test_should_include_apple_bundles_helper_is_false(self):
    method test_should_print_full_path_helper_is_true (line 46) | def test_should_print_full_path_helper_is_true(self):
    method test_should_print_full_path_helper_is_false (line 50) | def test_should_print_full_path_helper_is_false(self):
    method test_is_apple_bunlde_returns_false_on_none (line 54) | def test_is_apple_bunlde_returns_false_on_none(self):
    method test_is_apple_bunlde_returns_true_for_apple_bundle (line 57) | def test_is_apple_bunlde_returns_true_for_apple_bundle(self):
    method test_is_apple_bunlde_returns_false_for_string_not_starting_with_com_apple (line 60) | def test_is_apple_bunlde_returns_false_for_string_not_starting_with_co...
    method test_is_apple_bunlde_returns_false_for_non_apple_bundle (line 63) | def test_is_apple_bunlde_returns_false_for_non_apple_bundle(self):
    method test_show_frameworks_prints_without_apple_bundles (line 67) | def test_show_frameworks_prints_without_apple_bundles(self, mock_api):
    method test_show_frameworks_prints_with_apple_bundles (line 82) | def test_show_frameworks_prints_with_apple_bundles(self, mock_api):
    method test_show_frameworks_prints_with_apple_bundles_and_full_paths (line 99) | def test_show_frameworks_prints_with_apple_bundles_and_full_paths(self...
    method test_show_bundles_prints_bundles (line 116) | def test_show_bundles_prints_bundles(self, mock_api):
    method test_show_bundles_prints_bundles (line 133) | def test_show_bundles_prints_bundles(self, mock_api):

FILE: tests/commands/ios/test_cookies.py
  class TestCookies (line 8) | class TestCookies(unittest.TestCase):
    method test_get_handles_empty_data (line 10) | def test_get_handles_empty_data(self, mock_api):
    method test_get (line 19) | def test_get(self, mock_api):

FILE: tests/commands/ios/test_hooking.py
  class TestHooking (line 9) | class TestHooking(unittest.TestCase):
    method test_should_ignore_native_classes_returns_true (line 10) | def test_should_ignore_native_classes_returns_true(self):
    method test_should_ignore_native_classes_returns_false (line 18) | def test_should_ignore_native_classes_returns_false(self):
    method test_should_include_parents_includes_returns_true (line 25) | def test_should_include_parents_includes_returns_true(self):
    method test_should_include_parents_includes_returns_false (line 33) | def test_should_include_parents_includes_returns_false(self):
    method test_class_is_prefixed_with_native_returns_true (line 40) | def test_class_is_prefixed_with_native_returns_true(self):
    method test_class_is_prefixed_with_native_returns_false (line 45) | def test_class_is_prefixed_with_native_returns_false(self):
    method test_string_is_true_returns_true (line 50) | def test_string_is_true_returns_true(self):
    method test_string_is_true_returns_false (line 55) | def test_string_is_true_returns_false(self):
    method test_show_ios_class_methods_validates_arguments (line 60) | def test_show_ios_class_methods_validates_arguments(self):
    method test_show_ios_class_methods (line 67) | def test_show_ios_class_methods(self, mock_api):
    method test_set_method_return_value_validates_arguments (line 81) | def test_set_method_return_value_validates_arguments(self):
    method test_set_method_return_value (line 89) | def test_set_method_return_value(self, mock_api):

FILE: tests/commands/ios/test_jailbreak.py
  class TestJailbreak (line 7) | class TestJailbreak(unittest.TestCase):
    method test_disable (line 9) | def test_disable(self, mock_api):
    method test_simulate (line 15) | def test_simulate(self, mock_api):

FILE: tests/commands/ios/test_keychain.py
  class TestKeychain (line 9) | class TestKeychain(unittest.TestCase):
    method test_should_output_json_in_arguments_returns_true (line 10) | def test_should_output_json_in_arguments_returns_true(self):
    method test_should_output_json_in_arguments_returns_false (line 18) | def test_should_output_json_in_arguments_returns_false(self):
    method test_dump_validates_arguments_if_json_output_is_wanted (line 25) | def test_dump_validates_arguments_if_json_output_is_wanted(self):
    method test_data_flag_check_ignored_without_data_flag (line 31) | def test_data_flag_check_ignored_without_data_flag(self):
    method test_data_flag_is_checked_when_flag_is_specified (line 36) | def test_data_flag_is_checked_when_flag_is_specified(self):
    method test_data_flag_is_checked_when_only_data_flag_is_specified_without_key (line 41) | def test_data_flag_is_checked_when_only_data_flag_is_specified_without...
    method test_should_do_smart_decode_returns_true (line 46) | def test_should_do_smart_decode_returns_true(self):
    method test_should_do_smart_decode_returns_false (line 51) | def test_should_do_smart_decode_returns_false(self):
    method test_get_flag_value_gets_value_of_flag (line 56) | def test_get_flag_value_gets_value_of_flag(self):
    method test_dump_to_screen_handles_empty_data (line 62) | def test_dump_to_screen_handles_empty_data(self, mock_api):
    method test_dump_to_screen (line 77) | def test_dump_to_screen(self, mock_api):
    method test_dump_raw (line 97) | def test_dump_raw(self, mock_api):
    method test_dump_to_json (line 107) | def test_dump_to_json(self, mock_open, mock_api):
    method test_clear (line 131) | def test_clear(self, mock_confirm, mock_api):
    method test_adds_item_validates_data_key_to_need_identifier (line 140) | def test_adds_item_validates_data_key_to_need_identifier(self):
    method test_adds_item_successfully (line 147) | def test_adds_item_successfully(self, mock_api):
    method test_adds_item_with_failure (line 160) | def test_adds_item_with_failure(self, mock_api):

FILE: tests/commands/ios/test_nsurlcredentialstorage.py
  class TestNsusercredentialstorage (line 8) | class TestNsusercredentialstorage(unittest.TestCase):
    method test_dump (line 10) | def test_dump(self, mock_api):

FILE: tests/commands/ios/test_nsuserdefaults.py
  class TestNsuserdefaults (line 8) | class TestNsuserdefaults(unittest.TestCase):
    method test_get (line 10) | def test_get(self, mock_api):

FILE: tests/commands/ios/test_pasteboard.py
  class TestPasteboard (line 7) | class TestPasteboard(unittest.TestCase):
    method test_monitor (line 9) | def test_monitor(self, mock_api):

FILE: tests/commands/ios/test_pinning.py
  class TestPinning (line 7) | class TestPinning(unittest.TestCase):
    method test_disable (line 9) | def test_disable(self, mock_api):
    method test_should_be_quiet_returns_true (line 14) | def test_should_be_quiet_returns_true(self):
    method test_should_be_quiet_returns_false (line 18) | def test_should_be_quiet_returns_false(self):

FILE: tests/commands/ios/test_plist.py
  class TestPlist (line 9) | class TestPlist(unittest.TestCase):
    method test_cat_validates_arguments (line 10) | def test_cat_validates_arguments(self):
    method test_cat_with_full_path (line 17) | def test_cat_with_full_path(self, mock_api):
    method test_cat_with_relative (line 27) | def test_cat_with_relative(self, mock_file_manager, mock_api):

FILE: tests/commands/test_command_history.py
  class TestCommandHistory (line 9) | class TestCommandHistory(unittest.TestCase):
    method setUp (line 10) | def setUp(self):
    method tearDown (line 13) | def tearDown(self):
    method test_prints_command_history (line 16) | def test_prints_command_history(self):
    method test_save_validates_arguments (line 27) | def test_save_validates_arguments(self):
    method test_save_saves_to_file (line 34) | def test_save_saves_to_file(self, mock_open):
    method test_clear_clears_command_history (line 41) | def test_clear_clears_command_history(self):

FILE: tests/commands/test_device.py
  class TestDevice (line 9) | class TestDevice(unittest.TestCase):
    method test_gets_environment_and_calls_ios_platform_specific_method (line 13) | def test_gets_environment_and_calls_ios_platform_specific_method(self,...
    method test_gets_environment_and_calls_android_platform_specific_method (line 22) | def test_gets_environment_and_calls_android_platform_specific_method(s...
    method test_prints_ios_environment_via_platform_helpers (line 31) | def test_prints_ios_environment_via_platform_helpers(self, mock_api):
    method test_prints_android_environment_via_platform_helpers (line 47) | def test_prints_android_environment_via_platform_helpers(self, mock_api):

FILE: tests/commands/test_filemanager.py
  class TestFileManager (line 11) | class TestFileManager(unittest.TestCase):
    method tearDown (line 12) | def tearDown(self):
    method test_cd_argument_validation (line 15) | def test_cd_argument_validation(self):
    method test_cd_to_dot_directory_does_nothing (line 21) | def test_cd_to_dot_directory_does_nothing(self):
    method test_cd_to_double_dot_moves_up_one_directory (line 28) | def test_cd_to_double_dot_moves_up_one_directory(self):
    method test_cd_to_double_dot_moves_stays_in_current_directory_if_already_root (line 37) | def test_cd_to_double_dot_moves_stays_in_current_directory_if_already_...
    method test_cd_to_absoluate_ios_path (line 47) | def test_cd_to_absoluate_ios_path(self, mock_path_exists_ios):
    method test_cd_to_absoluate_android_path (line 60) | def test_cd_to_absoluate_android_path(self, mock_path_exists_android):
    method test_cd_to_absoluate_ios_path_that_does_not_exist (line 73) | def test_cd_to_absoluate_ios_path_that_does_not_exist(self, mock_path_...
    method test_cd_to_relative_path_ios (line 86) | def test_cd_to_relative_path_ios(self, mock_path_exists_ios):
    method test_cd_to_relative_path_android (line 99) | def test_cd_to_relative_path_android(self, mock_path_exists_android):
    method test_cd_to_relative_path_ios_that_does_not_exist (line 112) | def test_cd_to_relative_path_ios_that_does_not_exist(self, mock_path_e...
    method test_ios_path_exists_helper (line 125) | def test_ios_path_exists_helper(self, mock_api):
    method test_rm_dispatcher_validates_arguments (line 130) | def test_rm_dispatcher_validates_arguments(self):
    method test_rm_dispatcher_confirms_before_delete (line 140) | def test_rm_dispatcher_confirms_before_delete(self, mock_android_rm, m...
    method test_rm_dispatcher_calls_android_rm_helper (line 155) | def test_rm_dispatcher_calls_android_rm_helper(self, mock_android_rm, ...
    method test_rm_android_helper_file_exists (line 166) | def test_rm_android_helper_file_exists(self, mock_exists, mock_api):
    method test_android_path_exists_helper (line 178) | def test_android_path_exists_helper(self, mock_api):
    method test_returns_current_directory_via_helper_when_already_set (line 183) | def test_returns_current_directory_via_helper_when_already_set(self):
    method test_returns_current_directory_via_helper_for_ios (line 189) | def test_returns_current_directory_via_helper_for_ios(self, mock_pwd_i...
    method test_returns_current_directory_via_helper_for_android (line 197) | def test_returns_current_directory_via_helper_for_android(self, mock_p...
    method test_prints_the_current_working_directory (line 204) | def test_prints_the_current_working_directory(self):
    method test_get_ios_pwd_via_helper (line 213) | def test_get_ios_pwd_via_helper(self, mock_api):
    method test_get_android_pwd_via_helper (line 220) | def test_get_android_pwd_via_helper(self, mock_api):
    method test_ls_gets_pwd_from_helper_with_no_argument (line 228) | def test_ls_gets_pwd_from_helper_with_no_argument(self, _, mock_pwd):
    method test_ls_calls_ios_helper_method (line 236) | def test_ls_calls_ios_helper_method(self, mock_ls_ios):
    method test_ls_calls_android_helper_method (line 244) | def test_ls_calls_android_helper_method(self, mock_ls_android):
    method test_lists_readable_ios_directory_using_helper_method (line 252) | def test_lists_readable_ios_directory_using_helper_method(self, mock_a...
    method test_lists_readable_ios_directory_using_helper_method_no_attributes (line 290) | def test_lists_readable_ios_directory_using_helper_method_no_attribute...
    method test_lists_unreadable_ios_directory_using_helper_method (line 318) | def test_lists_unreadable_ios_directory_using_helper_method(self, mock...
    method test_lists_readable_android_directory_using_helper_method (line 332) | def test_lists_readable_android_directory_using_helper_method(self, mo...
    method test_lists_unreadable_android_directory_using_helper_method (line 366) | def test_lists_unreadable_android_directory_using_helper_method(self, ...
    method test_download_platform_proxy_validates_arguments (line 379) | def test_download_platform_proxy_validates_arguments(self):
    method test_download_platform_proxy_calls_ios_method (line 386) | def test_download_platform_proxy_calls_ios_method(self, mock_download_...
    method test_download_platform_proxy_calls_android_method (line 394) | def test_download_platform_proxy_calls_android_method(self, mock_downl...
    method test_downloads_file_with_ios_helper (line 403) | def test_downloads_file_with_ios_helper(self, mock_open, mock_api):
    method test_downloads_file_but_fails_on_unreadable_with_ios_helper (line 423) | def test_downloads_file_but_fails_on_unreadable_with_ios_helper(self, ...
    method test_downloads_file_but_fails_on_file_type_with_ios_helper (line 432) | def test_downloads_file_but_fails_on_file_type_with_ios_helper(self, m...
    method test_downloads_file_with_android_helper (line 443) | def test_downloads_file_with_android_helper(self, mock_open, mock_api):
    method test_downloads_file_but_fails_on_unreadable_with_android_helper (line 464) | def test_downloads_file_but_fails_on_unreadable_with_android_helper(se...
    method test_downloads_file_but_fails_on_file_type_with_android_helper (line 477) | def test_downloads_file_but_fails_on_file_type_with_android_helper(sel...
    method test_file_upload_method_proxy_validates_arguments (line 489) | def test_file_upload_method_proxy_validates_arguments(self):
    method test_file_upload_method_proxy_calls_ios_helper_method (line 496) | def test_file_upload_method_proxy_calls_ios_helper_method(self, mock_u...
    method test_file_upload_method_proxy_calls_android_helper_method (line 504) | def test_file_upload_method_proxy_calls_android_helper_method(self, mo...

FILE: tests/commands/test_frida_commands.py
  class TestFridaCommands (line 8) | class TestFridaCommands(unittest.TestCase):
    method test_detects_no_exception_handler_argument (line 9) | def test_detects_no_exception_handler_argument(self):
    method test_gets_frida_environment (line 18) | def test_gets_frida_environment(self, mock_api):

FILE: tests/commands/test_jobs.py
  class MockJob (line 9) | class MockJob:
    method __init__ (line 14) | def __init__(self):
    method end (line 20) | def end(self):
  class TestJobs (line 24) | class TestJobs(unittest.TestCase):
    method setUp (line 25) | def setUp(self):
    method tearDown (line 28) | def tearDown(self):
    method test_displays_empty_jobs_message (line 32) | def test_displays_empty_jobs_message(self, mock_api):
    method test_displays_list_of_jobs (line 44) | def test_displays_list_of_jobs(self, mock_api):
    method test_kill_validates_arguments (line 58) | def test_kill_validates_arguments(self):
    method test_cant_find_job_by_uuid (line 65) | def test_cant_find_job_by_uuid(self, mock_api):
    method test_kills_job_by_uuid (line 71) | def test_kills_job_by_uuid(self, mock_api):

FILE: tests/commands/test_memory.py
  class MockRange (line 9) | class MockRange:
    method __init__ (line 14) | def __init__(self):
  class TestMemory (line 19) | class TestMemory(unittest.TestCase):
    method test_parses_is_string_input_flag_from_arguments (line 20) | def test_parses_is_string_input_flag_from_arguments(self):
    method test_dump_all_validates_arguments (line 28) | def test_dump_all_validates_arguments(self):
    method test_dump_all (line 36) | def test_dump_all(self, mock_open, mock_api):
    method test_dump_from_base_validates_arguments (line 52) | def test_dump_from_base_validates_arguments(self):
    method test_dump_from_base (line 61) | def test_dump_from_base(self, mock_open, mock_api):
    method test_list_modules_without_errors_without_json_flag (line 75) | def test_list_modules_without_errors_without_json_flag(self, mock_api):
    method test_list_modules_without_errors_with_json_flag (line 96) | def test_list_modules_without_errors_with_json_flag(self, mock_open, m...
    method test_dump_exports_validates_arguments_without_json_flag (line 114) | def test_dump_exports_validates_arguments_without_json_flag(self):
    method test_dump_exports_validates_arguments_with_json_flag (line 124) | def test_dump_exports_validates_arguments_with_json_flag(self):
    method test_dump_exports_without_error (line 131) | def test_dump_exports_without_error(self, mock_api):
    method test_dump_exports_without_error_as_json (line 151) | def test_dump_exports_without_error_as_json(self, mock_open, mock_api):
    method test_find_pattern_validates_arguments (line 168) | def test_find_pattern_validates_arguments(self):
    method test_find_pattern_without_string_argument (line 175) | def test_find_pattern_without_string_argument(self, mock_api):
    method test_find_pattern_with_string_argument (line 188) | def test_find_pattern_with_string_argument(self, mock_api):
    method test_find_pattern_without_string_argument_with_offets_only (line 201) | def test_find_pattern_without_string_argument_with_offets_only(self, m...
    method test_replace_pattern_validates_arguments (line 215) | def test_replace_pattern_validates_arguments(self):
    method test_replace_pattern_without_string_argument (line 222) | def test_replace_pattern_without_string_argument(self, mock_api):
    method test_replace_pattern_with_string_argument (line 236) | def test_replace_pattern_with_string_argument(self, mock_api):
    method test_replace_pattern_without_string_argument_with_offets_only (line 250) | def test_replace_pattern_without_string_argument_with_offets_only(self...

FILE: tests/commands/test_mobile_packages.py
  class TestMobilePackages (line 8) | class TestMobilePackages(unittest.TestCase):
    method test_patching_ios_ipa (line 14) | def test_patching_ios_ipa(self, mock_os, mock_shutil, mock_iospatcher,...
    method test_patching_android_apk (line 43) | def test_patching_android_apk(self, mock_input, mock_delegator, mock_o...

FILE: tests/commands/test_plugin_manager.py
  class TestPluginManager (line 9) | class TestPluginManager(unittest.TestCase):
    method setUp (line 10) | def setUp(self):
    method test_load_plugin_validates_arguments (line 13) | def test_load_plugin_validates_arguments(self):
    method test_load_plugin_validates_plugin_init_exists (line 21) | def test_load_plugin_validates_plugin_init_exists(self, mock_exists):
    method test_load_plugin_loads_plugin (line 29) | def test_load_plugin_loads_plugin(self, mock_state_connection):

FILE: tests/commands/test_ui.py
  class TestUI (line 10) | class TestUI(unittest.TestCase):
    method tearDown (line 11) | def tearDown(self):
    method test_alert_helper_method_proxy_calls_ios (line 15) | def test_alert_helper_method_proxy_calls_ios(self, mock_alert_ios):
    method test_alert_helper_method_proxy_calls_ios_custom_message (line 23) | def test_alert_helper_method_proxy_calls_ios_custom_message(self, mock...
    method test_alert_ios_helper_method (line 31) | def test_alert_ios_helper_method(self, mock_api):
    method test_ios_screenshot_validates_arguments (line 36) | def test_ios_screenshot_validates_arguments(self):
    method test_ios_screenshot (line 44) | def test_ios_screenshot(self, mock_open, mock_api):
    method test_dump_ios_ui (line 54) | def test_dump_ios_ui(self, mock_api):
    method test_bypass_touchid (line 63) | def test_bypass_touchid(self, mock_api):
    method test_android_screenshot_validates_arguments (line 68) | def test_android_screenshot_validates_arguments(self):
    method test_android_screenshot (line 76) | def test_android_screenshot(self, mock_open, mock_api):
    method test_android_flag_secure_validates_argument_as_boolean_string (line 85) | def test_android_flag_secure_validates_argument_as_boolean_string(self):
    method test_android_flag_secure_validates_argument_is_present (line 91) | def test_android_flag_secure_validates_argument_is_present(self):
    method test_android_flag_secure (line 98) | def test_android_flag_secure(self, mock_api):

FILE: tests/console/test_cli.py
  class TestsCommandLineInteractions (line 10) | class TestsCommandLineInteractions(unittest.TestCase):
    method test_version (line 11) | def test_version(self):
    method test_patchapk_runs_with_minimal_cli_arguments (line 20) | def test_patchapk_runs_with_minimal_cli_arguments(self, _):
    method test_patchapk_runs_with_all_cli_arguments (line 28) | def test_patchapk_runs_with_all_cli_arguments(self, _):
    method test_patchapk_fails_and_wants_source (line 43) | def test_patchapk_fails_and_wants_source(self):
    method test_patchipa_runs_with_source_and_codesignature (line 51) | def test_patchipa_runs_with_source_and_codesignature(self, _):
    method test_patchipa_runs_with_all_cli_arguments (line 59) | def test_patchipa_runs_with_all_cli_arguments(self, _):
    method test_patchipa_fails_and_wants_source (line 72) | def test_patchipa_fails_and_wants_source(self):
    method test_patchipa_fails_and_wants_codesign_signature (line 79) | def test_patchipa_fails_and_wants_codesign_signature(self):

FILE: tests/console/test_completer.py
  class TestConsoleCommandCompletion (line 8) | class TestConsoleCommandCompletion(unittest.TestCase):
    method setUp (line 9) | def setUp(self):
    method test_can_find_command_completion (line 12) | def test_can_find_command_completion(self):
    method test_will_have_empty_dict_for_invalid_command (line 20) | def test_will_have_empty_dict_for_invalid_command(self):

FILE: tests/console/test_repl.py
  class TestRepl (line 9) | class TestRepl(unittest.TestCase):
    method setUp (line 10) | def setUp(self):
    method test_does_nothing_when_empty_command_is_passed (line 13) | def test_does_nothing_when_empty_command_is_passed(self):
    method test_does_nothing_when_only_spaces_as_command_is_passed (line 17) | def test_does_nothing_when_only_spaces_as_command_is_passed(self):
    method test_runs_os_command_when_prefixed_with_excalmation_mark (line 24) | def test_runs_os_command_when_prefixed_with_excalmation_mark(self, pat...
    method test_finds_help_when_prefixed_with_help_command (line 36) | def test_finds_help_when_prefixed_with_help_command(self):
    method test_fails_to_find_help_for_invalid_command (line 46) | def test_fails_to_find_help_for_invalid_command(self):
    method test_fails_when_invalid_command_is_passed (line 55) | def test_fails_when_invalid_command_is_passed(self):
    method test_is_able_to_find_an_executable_method_to_run_with_tokens_passed (line 62) | def test_is_able_to_find_an_executable_method_to_run_with_tokens_passe...
    method test_will_fail_to_find_exec_method_with_invalid_tokens (line 68) | def test_will_fail_to_find_exec_method_with_invalid_tokens(self):
    method test_is_able_to_locate_nested_helpfile_contents (line 74) | def test_is_able_to_locate_nested_helpfile_contents(self):
    method test_runs_commands_and_catches_exceptions (line 95) | def test_runs_commands_and_catches_exceptions(self, prompt, run_command):

FILE: tests/data/plugin/__init__.py
  class VersionInfo (line 16) | class VersionInfo(Plugin):
    method __init__ (line 19) | def __init__(self, ns):
    method version (line 43) | def version(self, args: list):

FILE: tests/helpers.py
  function capture (line 8) | def capture(command, *args, **kwargs):

FILE: tests/state/test_app.py
  class TestApp (line 6) | class TestApp(unittest.TestCase):
    method tearDown (line 7) | def tearDown(self):
    method test_app_should_not_debug_hooks_by_default (line 11) | def test_app_should_not_debug_hooks_by_default(self):
    method test_app_should_debug_hooks_if_true (line 14) | def test_app_should_debug_hooks_if_true(self):
    method test_adds_command_to_history (line 19) | def test_adds_command_to_history(self):
    method test_clears_command_history (line 25) | def test_clears_command_history(self):

FILE: tests/state/test_jobs.py
  class TestJobManager (line 6) | class TestJobManager(unittest.TestCase):
    method tearDown (line 7) | def tearDown(self):
    method test_job_manager_starts_with_empty_jobs (line 10) | def test_job_manager_starts_with_empty_jobs(self):
    method test_adds_jobs (line 13) | def test_adds_jobs(self):
    method test_removes_jobs (line 18) | def test_removes_jobs(self):

FILE: tests/utils/patchers/test_android.py
  class TestAndroidGadget (line 8) | class TestAndroidGadget(unittest.TestCase):
    method setUp (line 11) | def setUp(self, github, mock_os):
    method test_sets_architecture (line 35) | def test_sets_architecture(self):
    method test_raises_exception_with_invalid_architecture (line 39) | def test_raises_exception_with_invalid_architecture(self):
    method test_sets_architecture_and_returns_context (line 43) | def test_sets_architecture_and_returns_context(self):
    method test_gets_architecture_when_set (line 47) | def test_gets_architecture_when_set(self):
    method test_gets_frida_library_path (line 53) | def test_gets_frida_library_path(self):
    method test_fails_to_get_frida_library_path_without_architecture (line 59) | def test_fails_to_get_frida_library_path_without_architecture(self):
    method test_checks_if_gadget_exists_if_it_really_exists (line 64) | def test_checks_if_gadget_exists_if_it_really_exists(self, mock_os):
    method test_checks_if_gadget_exists_if_it_really_does_not_exist (line 73) | def test_checks_if_gadget_exists_if_it_really_does_not_exist(self, moc...
    method test_check_if_gadget_exists_fails_without_architecture (line 81) | def test_check_if_gadget_exists_fails_without_architecture(self):
    method test_can_find_download_url_for_gadget (line 85) | def test_can_find_download_url_for_gadget(self):
    method test_throws_exception_when_download_url_could_not_be_determined (line 98) | def test_throws_exception_when_download_url_could_not_be_determined(se...
  class TestAndroidPatcher (line 110) | class TestAndroidPatcher(unittest.TestCase):
    method test_inits_patcher (line 114) | def test_inits_patcher(self, tempfile):
    method test_set_android_apk_source (line 130) | def test_set_android_apk_source(self, _, mock_os):

FILE: tests/utils/patchers/test_base.py
  class TestBasePlatformGadget (line 8) | class TestBasePlatformGadget(unittest.TestCase):
    method setUp (line 10) | def setUp(self, mock_github):
    method test_sets_version_to_zero_if_no_local_record_is_found (line 14) | def test_sets_version_to_zero_if_no_local_record_is_found(self, mock_os):
  class TestBasePlatformPatcher (line 21) | class TestBasePlatformPatcher(unittest.TestCase):
    method setUp (line 22) | def setUp(self):
    method test_inits_base_patcher (line 26) | def test_inits_base_patcher(self):
    method test_are_requirements_met_returns_true_if_met (line 33) | def test_are_requirements_met_returns_true_if_met(self):
    method test_are_requirements_met_returns_false_if_not_met (line 39) | def test_are_requirements_met_returns_false_if_not_met(self):
    method test_check_commands_finds_commands_and_sets_location (line 46) | def test_check_commands_finds_commands_and_sets_location(self, mock_sh...
    method test_check_commands_fails_to_find_command_and_displays_error (line 63) | def test_check_commands_fails_to_find_command_and_displays_error(self,...

FILE: tests/utils/patchers/test_github.py
  class TestGithub (line 7) | class TestGithub(unittest.TestCase):
    method setUp (line 8) | def setUp(self):
    method test_makes_call_and_stores_result_in_cache (line 32) | def test_makes_call_and_stores_result_in_cache(self, mock_requests):
    method test_makes_call_and_stores_result_in_cache_and_fetches_next_from_cache (line 45) | def test_makes_call_and_stores_result_in_cache_and_fetches_next_from_c...
    method test_makes_call_and_gets_latest_version (line 67) | def test_makes_call_and_gets_latest_version(self, mock_requests):
    method test_makes_call_and_fails_to_get_assets (line 79) | def test_makes_call_and_fails_to_get_assets(self, mock_requests):
    method test_makes_call_and_gets_assets (line 90) | def test_makes_call_and_gets_assets(self, mock_requests):

FILE: tests/utils/patchers/test_ios.py
  class TestIosGadget (line 7) | class TestIosGadget(unittest.TestCase):
    method setUp (line 10) | def setUp(self, mock_github, mock_os):
    method test_gets_gadget_path (line 34) | def test_gets_gadget_path(self):
    method test_checks_if_gadget_exists (line 42) | def test_checks_if_gadget_exists(self, mock_os):
    method test_can_find_asset_download_url (line 49) | def test_can_find_asset_download_url(self):
  class TestIosPatcher (line 61) | class TestIosPatcher(unittest.TestCase):
    method test_sets_provisioning_profile (line 65) | def test_sets_provisioning_profile(self):

FILE: tests/utils/test_helpers.py
  class TestHelpers (line 13) | class TestHelpers(unittest.TestCase):
    method test_pretty_concat_with_less_than_seventy_five_chars (line 14) | def test_pretty_concat_with_less_than_seventy_five_chars(self):
    method test_pretty_concat_with_more_than_max_chars (line 19) | def test_pretty_concat_with_more_than_max_chars(self):
    method test_pretty_concat_with_more_than_max_chars_to_the_left (line 24) | def test_pretty_concat_with_more_than_max_chars_to_the_left(self):
    method test_sizeof_formats_values (line 29) | def test_sizeof_formats_values(self):
    method test_gets_tokens_without_quotes (line 34) | def test_gets_tokens_without_quotes(self):
    method test_gets_tokens_with_quotes (line 39) | def test_gets_tokens_with_quotes(self):
    method test_gets_tokens_and_handles_missing_quotes (line 44) | def test_gets_tokens_and_handles_missing_quotes(self):
    method test_cleans_argument_lists_with_flags (line 49) | def test_cleans_argument_lists_with_flags(self):
    method test_prints_frida_connection_help (line 53) | def test_prints_frida_connection_help(self):
Condensed preview — 316 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (760K chars).
[
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 1450,
    "preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: \"[bug] Bug Description Here\"\nlabels: freshissue\nas"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 595,
    "preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your fea"
  },
  {
    "path": ".github/workflows/codeql-analysis.yml",
    "chars": 2203,
    "preview": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# Y"
  },
  {
    "path": ".github/workflows/pypi.yml",
    "chars": 804,
    "preview": "name: Release to PyPi\n\non:\n  push:\n    tags:\n      - '*'\n\njobs:\n  pypi:\n    name: Publish to PyPI\n    runs-on: ubuntu-la"
  },
  {
    "path": ".gitignore",
    "chars": 788,
    "preview": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\n"
  },
  {
    "path": ".python-version",
    "chars": 5,
    "preview": "3.14\n"
  },
  {
    "path": ".vscode/settings.json",
    "chars": 28,
    "preview": "{\n    \"editor.tabSize\": 2\n}\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 1957,
    "preview": "# Contributing to Objection\n\nFirst off, thanks for taking the time to contribute! 🎉💥\n\nThe following are some simple guid"
  },
  {
    "path": "LICENSE",
    "chars": 35147,
    "preview": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free "
  },
  {
    "path": "MANIFEST.in",
    "chars": 25,
    "preview": "include requirements.txt\n"
  },
  {
    "path": "Makefile",
    "chars": 202,
    "preview": "DIST_DIR := dist\n\ndefault: clean frida-agent sdist\n\nclean:\n\t$(RM) $(DIST_DIR)/*\n\nfrida-agent:\n\tcd agent && npm run build"
  },
  {
    "path": "README.md",
    "chars": 1894,
    "preview": "# 📱objection - Runtime Mobile Exploration\n\n`objection` is a runtime mobile exploration toolkit, powered by [Frida](https"
  },
  {
    "path": "agent/.gitignore",
    "chars": 13,
    "preview": "node_modules/"
  },
  {
    "path": "agent/README.md",
    "chars": 421,
    "preview": "# objection agent\n\nThis directory contains the source code for the agent used within `objection`. These sources are comp"
  },
  {
    "path": "agent/package.json",
    "chars": 1096,
    "preview": "{\n  \"name\": \"objection\",\n  \"version\": \"0.0.0\",\n  \"description\": \"Runtime Mobile Exploration\",\n  \"private\": true,\n  \"type"
  },
  {
    "path": "agent/src/android/clipboard.ts",
    "chars": 1840,
    "preview": "import { colors as c } from \"../lib/color.js\";\nimport {\n  getApplicationContext,\n  wrapJavaPerform, \n  Java\n} from \"./li"
  },
  {
    "path": "agent/src/android/filesystem.ts",
    "chars": 4044,
    "preview": "import * as fs from \"frida-fs\";\nimport { Buffer } from \"buffer\";\nimport { hexStringToBytes } from \"../lib/helpers.js\";\ni"
  },
  {
    "path": "agent/src/android/general.ts",
    "chars": 187,
    "preview": "import {\n  wrapJavaPerform,\n  Java\n} from \"./lib/libjava.js\";\n\nexport const deoptimize = (): Promise<void> => {\n  return"
  },
  {
    "path": "agent/src/android/heap.ts",
    "chars": 3735,
    "preview": "import { colors as c } from \"../lib/color.js\";\nimport {\n  IHeapClassDictionary,\n  IHeapObject,\n  IJavaField,\n  IHeapNorm"
  },
  {
    "path": "agent/src/android/hooking.ts",
    "chars": 19251,
    "preview": "import { colors as c } from \"../lib/color.js\";\nimport * as jobs from \"../lib/jobs.js\";\nimport { ICurrentActivityFragment"
  },
  {
    "path": "agent/src/android/intent.ts",
    "chars": 3928,
    "preview": "import { colors as c } from \"../lib/color.js\";\nimport {\n  getApplicationContext,\n  wrapJavaPerform,\n  Java\n} from \"./lib"
  },
  {
    "path": "agent/src/android/keystore.ts",
    "chars": 8699,
    "preview": "import { colors as c } from \"../lib/color.js\";\nimport {\n  IKeyStoreDetail,\n  IKeyStoreEntry\n} from \"./lib/interfaces.js\""
  },
  {
    "path": "agent/src/android/lib/intentUtils.ts",
    "chars": 2460,
    "preview": "import { Java } from \"./libjava.js\";\nimport { colors as c } from \"../../lib/color.js\";\n\nexport const analyseIntent = (me"
  },
  {
    "path": "agent/src/android/lib/interfaces.ts",
    "chars": 1559,
    "preview": "import type { default as JavaTypes } from \"frida-java-bridge\";\n\nexport interface IAndroidFilesystem {\n  files: any;\n  pa"
  },
  {
    "path": "agent/src/android/lib/libjava.ts",
    "chars": 1497,
    "preview": "import Java_bridge from \"frida-java-bridge\";\nimport { colors as c } from \"../../lib/color.js\";\n\nlet Java: typeof Java_br"
  },
  {
    "path": "agent/src/android/lib/types.ts",
    "chars": 1486,
    "preview": "export type JavaClass = any;\n\nexport type JavaMethodsOverloadsResult = any;\n\nexport type ClipboardManager = JavaClass | "
  },
  {
    "path": "agent/src/android/monitor.ts",
    "chars": 195,
    "preview": "import { wrapJavaPerform } from \"./lib/libjava.js\";\n\nexport namespace monitor {\n  export const stringCanary = (can: stri"
  },
  {
    "path": "agent/src/android/pinning.ts",
    "chars": 13273,
    "preview": "import { colors as c } from \"../lib/color.js\";\nimport { qsend } from \"../lib/helpers.js\";\nimport * as jobs from \"../lib/"
  },
  {
    "path": "agent/src/android/proxy.ts",
    "chars": 718,
    "preview": "import { \n  wrapJavaPerform,\n  Java\n} from \"./lib/libjava.js\";\nimport { colors as c } from \"../lib/color.js\";\n\nexport co"
  },
  {
    "path": "agent/src/android/root.ts",
    "chars": 12876,
    "preview": "import { colors as c } from \"../lib/color.js\";\nimport * as jobs from \"../lib/jobs.js\";\nimport {\n  wrapJavaPerform,\n  Jav"
  },
  {
    "path": "agent/src/android/shell.ts",
    "chars": 2323,
    "preview": "import { IExecutedCommand } from \"./lib/interfaces.js\";\nimport {\n  wrapJavaPerform,\n  Java\n} from \"./lib/libjava.js\";\nim"
  },
  {
    "path": "agent/src/android/userinterface.ts",
    "chars": 3404,
    "preview": "import { colors as c } from \"../lib/color.js\";\nimport {\n  wrapJavaPerform,\n  Java\n} from \"./lib/libjava.js\";\nimport {\n  "
  },
  {
    "path": "agent/src/generic/custom.ts",
    "chars": 101,
    "preview": "export const evaluate = (js: string): void => {\n  // tslint:disable-next-line:no-eval\n  eval(js);\n};\n"
  },
  {
    "path": "agent/src/generic/environment.ts",
    "chars": 4014,
    "preview": "import { ObjC } from \"../ios/lib/libobjc.js\";\nimport {\n  getApplicationContext,\n  wrapJavaPerform,\n  Java\n} from \"../and"
  },
  {
    "path": "agent/src/generic/http.ts",
    "chars": 3396,
    "preview": "import * as fs from \"frida-fs\";\nimport * as httpLib from \"http\";\nimport * as url from \"url\";\nimport { colors as c } from"
  },
  {
    "path": "agent/src/generic/memory.ts",
    "chars": 1777,
    "preview": "import { colors } from \"../lib/color.js\"\n\nexport const listModules = (): Module[] => {\n  return Process.enumerateModules"
  },
  {
    "path": "agent/src/generic/ping.ts",
    "chars": 41,
    "preview": "export const ping = (): boolean => true;\n"
  },
  {
    "path": "agent/src/index.ts",
    "chars": 407,
    "preview": "import { ping } from \"./generic/ping.js\";\nimport { android } from \"./rpc/android.js\";\nimport { env } from \"./rpc/environ"
  },
  {
    "path": "agent/src/ios/binary.ts",
    "chars": 1618,
    "preview": "import macho from \"macho-ts\";\n\nimport * as iosfilesystem from \"./filesystem.js\";\nimport { IBinaryModuleDictionary } from"
  },
  {
    "path": "agent/src/ios/binarycookies.ts",
    "chars": 1557,
    "preview": "import { ObjC } from \"../ios/lib/libobjc.js\";\nimport { IIosCookie } from \"./lib/interfaces.js\";\nimport {\n  NSArray,\n  NS"
  },
  {
    "path": "agent/src/ios/bundles.ts",
    "chars": 2107,
    "preview": "import { ObjC } from \"../ios/lib/libobjc.js\";\nimport { BundleType } from \"./lib/constants.js\";\nimport { IFramework } fro"
  },
  {
    "path": "agent/src/ios/credentialstorage.ts",
    "chars": 2185,
    "preview": "import { ObjC } from \"../ios/lib/libobjc.js\";\nimport { ICredential } from \"./lib/interfaces.js\";\nimport {\n  NSArray,\n  N"
  },
  {
    "path": "agent/src/ios/crypto.ts",
    "chars": 13131,
    "preview": "import { colors as c } from \"../lib/color.js\";\nimport { fsend } from \"../lib/helpers.js\";\nimport * as jobs from \"../lib/"
  },
  {
    "path": "agent/src/ios/filesystem.ts",
    "chars": 6016,
    "preview": "import { ObjC } from \"../ios/lib/libobjc.js\";\nimport * as fs from \"frida-fs\";\nimport { Buffer } from \"buffer\";\nimport { "
  },
  {
    "path": "agent/src/ios/heap.ts",
    "chars": 2948,
    "preview": "import { ObjC } from \"../ios/lib/libobjc.js\";\nimport type { default as ObjCTypes } from \"frida-objc-bridge\";\nimport { co"
  },
  {
    "path": "agent/src/ios/hooking.ts",
    "chars": 7930,
    "preview": "import { ObjC } from \"../ios/lib/libobjc.js\";\nimport { colors as c } from \"../lib/color.js\";\nimport * as jobs from \"../l"
  },
  {
    "path": "agent/src/ios/jailbreak.ts",
    "chars": 9727,
    "preview": "import { ObjC } from \"../ios/lib/libobjc.js\";\nimport { colors as c } from \"../lib/color.js\";\nimport * as jobs from \"../l"
  },
  {
    "path": "agent/src/ios/keychain.ts",
    "chars": 12323,
    "preview": "// dumps all of the keychain items available to the current\n// application.\nimport { colors as c } from \"../lib/color.js"
  },
  {
    "path": "agent/src/ios/lib/constants.ts",
    "chars": 4131,
    "preview": "// constants used for Security Attributes etc.\n// NSLog(@\"kSecAttrService: %@\", kSecAttrService);\nexport enum kSec {\n\n  "
  },
  {
    "path": "agent/src/ios/lib/helpers.ts",
    "chars": 4591,
    "preview": "import { ObjC } from \"./libobjc.js\";\nimport type { default as ObjCTypes } from \"frida-objc-bridge\";\nimport { NSUTF8Strin"
  },
  {
    "path": "agent/src/ios/lib/interfaces.ts",
    "chars": 1751,
    "preview": "import { NSDictionary } from \"./types.js\";\n\nexport interface IKeychainData {\n  clazz: string;\n  data: NSDictionary;\n}\n\ne"
  },
  {
    "path": "agent/src/ios/lib/libobjc.ts",
    "chars": 3419,
    "preview": "import ObjC_bridge from \"frida-objc-bridge\";\n\nlet ObjC: typeof ObjC_bridge;\n// Compatibility with frida < 17\nif (globalT"
  },
  {
    "path": "agent/src/ios/lib/types.ts",
    "chars": 647,
    "preview": "import type { default as ObjCTypes } from \"frida-objc-bridge\";\n\nexport type NSDictionary = ObjCTypes.Object | any;\nexpor"
  },
  {
    "path": "agent/src/ios/nsuserdefaults.ts",
    "chars": 477,
    "preview": "import { ObjC } from \"../ios/lib/libobjc.js\";\nimport {\n  NSDictionary,\n  NSUserDefaults\n} from \"./lib/types.js\";\n\n\nexpor"
  },
  {
    "path": "agent/src/ios/pasteboard.ts",
    "chars": 875,
    "preview": "import { ObjC } from \"../ios/lib/libobjc.js\";\nimport { colors as c } from \"../lib/color.js\";\n\n\nexport const monitor = ()"
  },
  {
    "path": "agent/src/ios/pinning.ts",
    "chars": 19814,
    "preview": "import { colors as c } from \"../lib/color.js\";\nimport { qsend } from \"../lib/helpers.js\";\nimport * as jobs from \"../lib/"
  },
  {
    "path": "agent/src/ios/plist.ts",
    "chars": 498,
    "preview": "import { ObjC } from \"../ios/lib/libobjc.js\";\nimport { NSMutableDictionary } from \"./lib/types.js\";\n\n\nexport const read "
  },
  {
    "path": "agent/src/ios/userinterface.ts",
    "chars": 7247,
    "preview": "// tslint:disable-next-line:no-var-requires\nimport { ObjC } from \"../ios/lib/libobjc.js\";\nimport type { default as ObjCT"
  },
  {
    "path": "agent/src/lib/color.ts",
    "chars": 1707,
    "preview": "export namespace colors {\n\n  const base: string = `\\x1B[%dm`;\n  const reset: string = `\\x1b[39m`;\n\n  // return an ansifi"
  },
  {
    "path": "agent/src/lib/constants.ts",
    "chars": 88,
    "preview": "export enum DeviceType {\n  IOS = \"ios\",\n  ANDROID = \"android\",\n  UNKNOWN = \"unknown\",\n}\n"
  },
  {
    "path": "agent/src/lib/helpers.ts",
    "chars": 1632,
    "preview": "import util from \"util\";\nimport { colors as c } from \"./color.js\";\n\n// sure, TS does not support this, but meh.\n// https"
  },
  {
    "path": "agent/src/lib/interfaces.ts",
    "chars": 685,
    "preview": "export interface IFridaInfo {\n  arch: string;\n  debugger: boolean;\n  heap: number;\n  platform: string;\n  runtime: string"
  },
  {
    "path": "agent/src/lib/jobs.ts",
    "chars": 3665,
    "preview": "import { colors as c } from \"./color.js\";\n\nexport class Job {\n  identifier: number;\n  private invocations?: InvocationLi"
  },
  {
    "path": "agent/src/rpc/android.ts",
    "chars": 5223,
    "preview": "import type { default as JavaTypes } from \"frida-java-bridge\";\nimport * as clipboard from \"../android/clipboard.js\";\nimp"
  },
  {
    "path": "agent/src/rpc/environment.ts",
    "chars": 372,
    "preview": "import * as environment from \"../generic/environment.js\";\n\nexport const env = {\n  // environment\n  envAndroid: () => env"
  },
  {
    "path": "agent/src/rpc/ios.ts",
    "chars": 4828,
    "preview": "import * as binary from \"../ios/binary.js\";\nimport * as binarycookies from \"../ios/binarycookies.js\";\nimport * as bundle"
  },
  {
    "path": "agent/src/rpc/jobs.ts",
    "chars": 145,
    "preview": "import * as j from \"../lib/jobs.js\";\n\nexport const jobs = {\n  // jobs\n  jobsGet: () => j.all(),\n  jobsKill: (ident: numb"
  },
  {
    "path": "agent/src/rpc/memory.ts",
    "chars": 649,
    "preview": "import * as m from \"../generic/memory.js\";\n\nexport const memory = {\n\n  memoryDump: (address: string, size: number) => m."
  },
  {
    "path": "agent/src/rpc/other.ts",
    "chars": 357,
    "preview": "import * as custom from \"../generic/custom.js\";\nimport * as http from \"../generic/http.js\";\n\nexport const other = {\n  ev"
  },
  {
    "path": "agent/tsconfig.json",
    "chars": 250,
    "preview": "{\n  \"compilerOptions\": {\n    \"target\": \"es2020\",\n    \"lib\": [\"es2020\"],\n    \"allowJs\": true,\n    \"noEmit\": true,\n    \"st"
  },
  {
    "path": "agent/tslint.json",
    "chars": 216,
    "preview": "{\n    \"defaultSeverity\": \"warning\",\n    \"extends\": [\n        \"tslint:recommended\"\n    ],\n    \"jsRules\": {},\n    \"rules\":"
  },
  {
    "path": "objection/__init__.py",
    "chars": 1462,
    "preview": "import sys\nfrom importlib import metadata\nfrom pathlib import Path\n\nimport tomllib\n\n\ndef _load_version() -> str:\n    \"\"\""
  },
  {
    "path": "objection/api/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "objection/api/app.py",
    "chars": 302,
    "preview": "from flask import Flask\n\nfrom . import rpc\nfrom . import script\n\n\ndef create_app() -> Flask:\n    \"\"\"\n        Creates a n"
  },
  {
    "path": "objection/api/rpc.py",
    "chars": 1622,
    "preview": "from flask import Blueprint, jsonify, request, abort\n\nfrom objection.state.connection import state_connection\nfrom ..uti"
  },
  {
    "path": "objection/api/script.py",
    "chars": 957,
    "preview": "from flask import Blueprint, jsonify, request, abort\n\nfrom objection.state.connection import state_connection\n\nbp = Blue"
  },
  {
    "path": "objection/commands/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "objection/commands/android/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "objection/commands/android/clipboard.py",
    "chars": 340,
    "preview": "from objection.state.connection import state_connection\n\n\ndef monitor(args: list = None) -> None:\n    \"\"\"\n        Starts"
  },
  {
    "path": "objection/commands/android/command.py",
    "chars": 636,
    "preview": "import click\n\nfrom objection.state.connection import state_connection\n\n\ndef execute(args: list) -> None:\n    \"\"\"\n       "
  },
  {
    "path": "objection/commands/android/general.py",
    "chars": 420,
    "preview": "from objection.state.connection import state_connection\n\n\ndef deoptimise(args: list) -> None:\n    \"\"\"\n        Forces the"
  },
  {
    "path": "objection/commands/android/generate.py",
    "chars": 1430,
    "preview": "import os\n\nimport click\n\nfrom objection.state.connection import state_connection\n\n\ndef clazz(args: list) -> None:\n    \"\""
  },
  {
    "path": "objection/commands/android/heap.py",
    "chars": 4286,
    "preview": "import pprint\n\nimport click\nfrom prompt_toolkit import prompt\nfrom prompt_toolkit.lexers import PygmentsLexer\nfrom pygme"
  },
  {
    "path": "objection/commands/android/hooking.py",
    "chars": 11004,
    "preview": "import json\nfrom typing import Optional\n\nimport click\n\nfrom objection.state.connection import state_connection\nfrom obje"
  },
  {
    "path": "objection/commands/android/intents.py",
    "chars": 1630,
    "preview": "import click\n\nfrom objection.state.connection import state_connection\nfrom objection.utils.helpers import clean_argument"
  },
  {
    "path": "objection/commands/android/keystore.py",
    "chars": 2237,
    "preview": "import json\n\nimport click\nfrom tabulate import tabulate\n\nfrom objection.state.connection import state_connection\n\n\ndef _"
  },
  {
    "path": "objection/commands/android/monitor.py",
    "chars": 504,
    "preview": "import click\n\nfrom objection.state.connection import state_connection\n\n\ndef string_canary(args: list) -> None:\n    \"\"\"\n "
  },
  {
    "path": "objection/commands/android/pinning.py",
    "chars": 597,
    "preview": "from objection.state.connection import state_connection\n\n\ndef _should_be_quiet(args: list) -> bool:\n    \"\"\"\n        Chec"
  },
  {
    "path": "objection/commands/android/proxy.py",
    "chars": 435,
    "preview": "import click\n\nfrom objection.state.connection import state_connection\n\n\ndef android_proxy_set(args: list = None) -> None"
  },
  {
    "path": "objection/commands/android/root.py",
    "chars": 495,
    "preview": "from objection.state.connection import state_connection\n\n\ndef disable(args: list = None) -> None:\n    \"\"\"\n        Perfor"
  },
  {
    "path": "objection/commands/command_history.py",
    "chars": 1167,
    "preview": "import os\n\nimport click\n\nfrom ..state.app import app_state\n\n\ndef history(args: list) -> None:\n    \"\"\"\n        Lists the "
  },
  {
    "path": "objection/commands/custom.py",
    "chars": 1466,
    "preview": "import os\n\nimport click\nimport frida\nfrom prompt_toolkit import prompt\nfrom prompt_toolkit.lexers import PygmentsLexer\nf"
  },
  {
    "path": "objection/commands/device.py",
    "chars": 1289,
    "preview": "import click\nfrom tabulate import tabulate\n\nfrom ..state.connection import state_connection\nfrom ..state.device import d"
  },
  {
    "path": "objection/commands/filemanager.py",
    "chars": 26519,
    "preview": "import os\nimport tempfile\nimport time\n\nimport click\nfrom tabulate import tabulate\n\nfrom ..state.connection import state_"
  },
  {
    "path": "objection/commands/frida_commands.py",
    "chars": 2091,
    "preview": "import os\n\nimport click\nfrom tabulate import tabulate\n\nfrom objection.state.connection import state_connection\nfrom ..ut"
  },
  {
    "path": "objection/commands/http.py",
    "chars": 878,
    "preview": "import click\n\nfrom ..commands.filemanager import pwd\nfrom ..state.connection import state_connection\n\n\ndef start(args: l"
  },
  {
    "path": "objection/commands/ios/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "objection/commands/ios/binary.py",
    "chars": 782,
    "preview": "import click\nfrom tabulate import tabulate\n\nfrom objection.state.connection import state_connection\n\n\ndef info(args: lis"
  },
  {
    "path": "objection/commands/ios/bundles.py",
    "chars": 2668,
    "preview": "import click\nfrom tabulate import tabulate\n\nfrom objection.state.connection import state_connection\nfrom objection.utils"
  },
  {
    "path": "objection/commands/ios/cookies.py",
    "chars": 1117,
    "preview": "import json\n\nimport click\nfrom tabulate import tabulate\n\nfrom objection.state.connection import state_connection\n\n\ndef _"
  },
  {
    "path": "objection/commands/ios/generate.py",
    "chars": 1382,
    "preview": "import os\n\nimport click\n\nfrom objection.state.connection import state_connection\n\n\ndef clazz(args: list) -> None:\n    \"\""
  },
  {
    "path": "objection/commands/ios/heap.py",
    "chars": 5600,
    "preview": "import pprint\n\nimport click\nfrom prompt_toolkit import prompt\nfrom prompt_toolkit.lexers import PygmentsLexer\nfrom pygme"
  },
  {
    "path": "objection/commands/ios/hooking.py",
    "chars": 7547,
    "preview": "import json\nfrom typing import Optional\n\nimport click\n\nfrom objection.state.connection import state_connection\nfrom obje"
  },
  {
    "path": "objection/commands/ios/jailbreak.py",
    "chars": 493,
    "preview": "from objection.state.connection import state_connection\n\n\ndef disable(args: list = None) -> None:\n    \"\"\"\n        Attemp"
  },
  {
    "path": "objection/commands/ios/keychain.py",
    "chars": 6162,
    "preview": "import json\n\nimport click\nfrom tabulate import tabulate\n\nfrom objection.state.connection import state_connection\n\n\ndef _"
  },
  {
    "path": "objection/commands/ios/monitor.py",
    "chars": 283,
    "preview": "from objection.state.connection import state_connection\n\n\ndef crypto_enable(args: list = None) -> None:\n    \"\"\"\n        "
  },
  {
    "path": "objection/commands/ios/nsurlcredentialstorage.py",
    "chars": 734,
    "preview": "import click\nfrom tabulate import tabulate\n\nfrom objection.state.connection import state_connection\n\n\ndef dump(args: lis"
  },
  {
    "path": "objection/commands/ios/nsuserdefaults.py",
    "chars": 375,
    "preview": "import click\n\nfrom objection.state.connection import state_connection\n\n\ndef get(args: list = None) -> None:\n    \"\"\"\n    "
  },
  {
    "path": "objection/commands/ios/pasteboard.py",
    "chars": 334,
    "preview": "from objection.state.connection import state_connection\n\n\ndef monitor(args: list = None) -> None:\n    \"\"\"\n        Starts"
  },
  {
    "path": "objection/commands/ios/pinning.py",
    "chars": 585,
    "preview": "from objection.state.connection import state_connection\n\n\ndef _should_be_quiet(args: list) -> bool:\n    \"\"\"\n        Chec"
  },
  {
    "path": "objection/commands/ios/plist.py",
    "chars": 746,
    "preview": "import os\n\nimport click\n\nfrom objection.commands import filemanager\nfrom objection.state.connection import state_connect"
  },
  {
    "path": "objection/commands/jobs.py",
    "chars": 1946,
    "preview": "import click\nfrom tabulate import tabulate\n\nfrom objection.state.connection import state_connection\nfrom ..state.jobs im"
  },
  {
    "path": "objection/commands/memory.py",
    "chars": 11315,
    "preview": "import json\nimport os\nfrom typing import List\n\nimport click\nfrom tabulate import tabulate\n\nfrom objection.state.connecti"
  },
  {
    "path": "objection/commands/mobile_packages.py",
    "chars": 10691,
    "preview": "import os\nimport shutil\n\nimport click\nimport delegator\nfrom packaging.version import Version\n\nfrom ..utils.patchers.andr"
  },
  {
    "path": "objection/commands/plugin_manager.py",
    "chars": 1732,
    "preview": "import importlib.util\nimport os\nimport traceback\nimport uuid\n\nimport click\n\nfrom ..utils.plugin import Plugin as PluginT"
  },
  {
    "path": "objection/commands/sqlite.py",
    "chars": 4072,
    "preview": "import binascii\nimport os\nimport tempfile\n\nimport click\nimport litecli\nfrom litecli.main import LiteCli\n\nfrom ..commands"
  },
  {
    "path": "objection/commands/ui.py",
    "chars": 3066,
    "preview": "import click\n\nfrom objection.state.connection import state_connection\nfrom ..state.device import device_state, Ios, Andr"
  },
  {
    "path": "objection/console/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "objection/console/cli.py",
    "chars": 16359,
    "preview": "import threading\nimport time\nfrom pathlib import Path\n\nimport click\nfrom frida import ServerNotRunningError\n\nfrom object"
  },
  {
    "path": "objection/console/commands.py",
    "chars": 29959,
    "preview": "from objection.commands import http\nfrom ..commands import command_history\nfrom ..commands import custom\nfrom ..commands"
  },
  {
    "path": "objection/console/completer.py",
    "chars": 5122,
    "preview": "import collections\n\nfrom prompt_toolkit.completion import Completer, Completion, CompleteEvent\nfrom prompt_toolkit.docum"
  },
  {
    "path": "objection/console/helpfiles/!.txt",
    "chars": 312,
    "preview": "Command: !\n\nUsage: ! <shell command>\n\nExecutes operating system commands using pythons Subprocess module.\nCommands that "
  },
  {
    "path": "objection/console/helpfiles/android.clipboard.monitor.txt",
    "chars": 288,
    "preview": "Command: android clipboard monitor\n\nUsage: android clipboard monitor\n\nGets a handle on the Android clipboard service and"
  },
  {
    "path": "objection/console/helpfiles/android.clipboard.txt",
    "chars": 57,
    "preview": "Contains subcommands to work with the Android Clipboard.\n"
  },
  {
    "path": "objection/console/helpfiles/android.heap.search.instances.txt",
    "chars": 510,
    "preview": "Command: android heap search instances\n\nUsage: android heap search instances <class>\n\nSearch for and print live instance"
  },
  {
    "path": "objection/console/helpfiles/android.hooking.list.activities.txt",
    "chars": 327,
    "preview": "Command: android hooking list activities\n\nUsage: android hooking list activities\n\nList all the Activities that have been"
  },
  {
    "path": "objection/console/helpfiles/android.hooking.list.class_methods.txt",
    "chars": 371,
    "preview": "Command: android hooking list class_methods\n\nUsage: android hooking list class_methods <class_name>\n\nLists the methods d"
  },
  {
    "path": "objection/console/helpfiles/android.hooking.list.classes.txt",
    "chars": 237,
    "preview": "Command: android hooking list classes\n\nUsage: android hooking list classes\n\nList the classes *currently loaded*. As the "
  },
  {
    "path": "objection/console/helpfiles/android.hooking.list.receivers.txt",
    "chars": 250,
    "preview": "Command: android hooking list receivers\n\nUsage: android hooking list receivers\n\nList all the Broadcast Receivers that ha"
  },
  {
    "path": "objection/console/helpfiles/android.hooking.list.services.txt",
    "chars": 236,
    "preview": "Command: android hooking list services\n\nUsage: android hooking list services\n\nList all the Services that have been regis"
  },
  {
    "path": "objection/console/helpfiles/android.hooking.list.txt",
    "chars": 69,
    "preview": "Contains subcommands to list various bits of Java class information.\n"
  },
  {
    "path": "objection/console/helpfiles/android.hooking.search.classes.txt",
    "chars": 293,
    "preview": "Command: android hooking search classes\n\nUsage: android hooking search classes <search string>\n\nSearch for classes in th"
  },
  {
    "path": "objection/console/helpfiles/android.hooking.search.methods.txt",
    "chars": 503,
    "preview": "Command: android hooking search methods\n\nUsage: android hooking search methods <search string> (optional: package-filter"
  },
  {
    "path": "objection/console/helpfiles/android.hooking.search.txt",
    "chars": 69,
    "preview": "Contains subcommands helpful when searching for classes and methods.\n"
  },
  {
    "path": "objection/console/helpfiles/android.hooking.set.return_value.txt",
    "chars": 975,
    "preview": "Command: android hooking set return_value\n\nUsage: android hooking set return_value \"<fully qualified class>\" \"<overload "
  },
  {
    "path": "objection/console/helpfiles/android.hooking.txt",
    "chars": 51,
    "preview": "Contains subcommands to hook Android Java methods.\n"
  },
  {
    "path": "objection/console/helpfiles/android.hooking.watch.class.txt",
    "chars": 202,
    "preview": "Command: android hooking watch class\n\nUsage: android hooking watch class <class>\nHooks a specified class' methods and re"
  },
  {
    "path": "objection/console/helpfiles/android.hooking.watch.class_method.txt",
    "chars": 1087,
    "preview": "Command: android hooking watch class_method\n\nUsage: android hooking watch class_method <fully qualified class method> <o"
  },
  {
    "path": "objection/console/helpfiles/android.hooking.watch.txt",
    "chars": 84,
    "preview": "Contains subcommands to watch for various bits of information on class invocations.\n"
  },
  {
    "path": "objection/console/helpfiles/android.intent.implicit_intents.txt",
    "chars": 240,
    "preview": "Command: android intent implicit_intents\n\nUsage: android intent implicit_intents\n\nStarts a hook to analyze implicit inte"
  },
  {
    "path": "objection/console/helpfiles/android.intent.launch_activity.txt",
    "chars": 522,
    "preview": "Command: android intent launch_activity\n\nUsage: android intent launch_activity <activity class>\n\nLaunches an activity cl"
  },
  {
    "path": "objection/console/helpfiles/android.intent.launch_service.txt",
    "chars": 470,
    "preview": "Command: android intent launch_service\n\nUsage: android intent launch_service <service class>\n\nLaunches an exported servi"
  },
  {
    "path": "objection/console/helpfiles/android.intent.txt",
    "chars": 51,
    "preview": "Contains subcommands to work with Android Intents.\n"
  },
  {
    "path": "objection/console/helpfiles/android.keystore.clear.txt",
    "chars": 281,
    "preview": "Command: android keystore clear\n\nUsage: android keystore clear\n\nClears all aliases in the current applications 'AndroidK"
  },
  {
    "path": "objection/console/helpfiles/android.keystore.detail.txt",
    "chars": 251,
    "preview": "Command: android keystore detail\n\nUsage: android keystore detail\n\nLists detailed 'AndroidKeyStore' items for the current"
  },
  {
    "path": "objection/console/helpfiles/android.keystore.list.txt",
    "chars": 322,
    "preview": "Command: android keystore list\n\nUsage: android keystore list\n\nLists aliases in the current applications 'AndroidKeyStore"
  },
  {
    "path": "objection/console/helpfiles/android.keystore.txt",
    "chars": 52,
    "preview": "Contains subcommands to work with Android KeyStore.\n"
  },
  {
    "path": "objection/console/helpfiles/android.keystore.watch.txt",
    "chars": 413,
    "preview": "Command: android keystore watch\n\nUsage: android keystore watch\n\nWatches usage of the Android Keystore. Two KeyStore meth"
  },
  {
    "path": "objection/console/helpfiles/android.root.disable.txt",
    "chars": 771,
    "preview": "Command: android root disable\n\nUsage: android root disable\n\nAttempts to disable root detection on Android devices. This "
  },
  {
    "path": "objection/console/helpfiles/android.root.simulate.txt",
    "chars": 257,
    "preview": "Command: android root simulate\n\nUsage: android root simulate\n\nAttempts to simulate a rooted Android environment. This is"
  },
  {
    "path": "objection/console/helpfiles/android.shell_exec.txt",
    "chars": 342,
    "preview": "Command: android shell_exec\n\nUsage: android shell_exec <command to run>\n\nExecute a shell command on an android device. T"
  },
  {
    "path": "objection/console/helpfiles/android.sslpinning.disable.txt",
    "chars": 1117,
    "preview": "Command: android sslpinning disable\n\nUsage: android sslpinning disable\n\nAttempts to disable SSL Pinning on Android devic"
  },
  {
    "path": "objection/console/helpfiles/android.sslpinning.txt",
    "chars": 69,
    "preview": "Contains subcommands to work with Android SSL pinning related calls.\n"
  },
  {
    "path": "objection/console/helpfiles/android.txt",
    "chars": 150,
    "preview": "Contains subcommands to work with Android specific features. These include\nshell commands, bypassing SSL pinning and sim"
  },
  {
    "path": "objection/console/helpfiles/android.ui.FLAG_SECURE.txt",
    "chars": 496,
    "preview": "Command: android ui FLAG_SECURE\n\nUsage: android ui FLAG_SECURE <true/false>\n\nControl the value of FLAG_SECURE for the cu"
  },
  {
    "path": "objection/console/helpfiles/android.ui.screenshot.txt",
    "chars": 358,
    "preview": "Command: android ui screenshot\n\nUsage: android ui screenshot <local png destination>\n\nScreenshots the current foreground"
  },
  {
    "path": "objection/console/helpfiles/cd.txt",
    "chars": 508,
    "preview": "Usage: cd <directory on remote device>\n\nChanges the current working directory on the device.\nMany commands within object"
  },
  {
    "path": "objection/console/helpfiles/env.txt",
    "chars": 324,
    "preview": "Command: env\n\nUsage: env\n\nDisplay directory information for the current application environment.\n\nOn iOS devices, this i"
  },
  {
    "path": "objection/console/helpfiles/exit.txt",
    "chars": 59,
    "preview": "Performs cleanups operations and quits the objection REPL.\n"
  },
  {
    "path": "objection/console/helpfiles/file.download.txt",
    "chars": 411,
    "preview": "Command: file download\n\nUsage: file download <remote location> (optional: <local destination>)\n\nDownload a file from a l"
  },
  {
    "path": "objection/console/helpfiles/file.txt",
    "chars": 65,
    "preview": "Contains subcommands to work with files on the remote filesystem\n"
  },
  {
    "path": "objection/console/helpfiles/file.upload.txt",
    "chars": 574,
    "preview": "Command: file upload\n\nUsage: file upload <local source file> (optional: <remote destination>)\n\nUpload a file from the lo"
  },
  {
    "path": "objection/console/helpfiles/frida.txt",
    "chars": 166,
    "preview": "Command: frida\n\nUsage: frida\n\nDisplays information about Frida. This includes the version of the Frida gadget,\nprocess a"
  },
  {
    "path": "objection/console/helpfiles/import.txt",
    "chars": 880,
    "preview": "Command: import\n\nUsage: import <path to local fridascript> (optional: <job name>) (optional: --no-exception-handler)\n\nIm"
  },
  {
    "path": "objection/console/helpfiles/ios.bundles.list_bundles.txt",
    "chars": 520,
    "preview": "Command: ios bundles list_bundles\n\nUsage: ios bundles list_bundles (optional: --full-path)\n\nReturns all the application'"
  },
  {
    "path": "objection/console/helpfiles/ios.bundles.list_frameworks.txt",
    "chars": 1023,
    "preview": "Command: ios bundles list_frameworks\n\nUsage: ios bundles list_frameworks (optional: --include-apple-frameworks) (optiona"
  },
  {
    "path": "objection/console/helpfiles/ios.bundles.txt",
    "chars": 47,
    "preview": "Contains subcommands to work with iOS bundles.\n"
  },
  {
    "path": "objection/console/helpfiles/ios.cookies.get.txt",
    "chars": 370,
    "preview": "Command: ios cookies get\n\nUsage: ios cookies get\n\nQueries iOS's NSHTTPCookieStorage class, extracting cookie values out "
  },
  {
    "path": "objection/console/helpfiles/ios.cookies.txt",
    "chars": 54,
    "preview": "Contains subcommands to work with iOS shared cookies.\n"
  },
  {
    "path": "objection/console/helpfiles/ios.hooking.list.class_methods.txt",
    "chars": 390,
    "preview": "Command: ios hooking list class_methods\n\nUsage: ios hooking list class_methods <class_name> (--include-parents)\n\nLists t"
  },
  {
    "path": "objection/console/helpfiles/ios.hooking.list.classes.txt",
    "chars": 313,
    "preview": "Command: ios hooking list classes\n\nUsage: ios hooking list classes (optional: --ignore-native)\n\nLists all of the classes"
  },
  {
    "path": "objection/console/helpfiles/ios.hooking.list.txt",
    "chars": 106,
    "preview": "Contains subcommands to list various bits of information, such as \nObjective-C classes and class methods.\n"
  },
  {
    "path": "objection/console/helpfiles/ios.hooking.search.classes.txt",
    "chars": 284,
    "preview": "Command: ios hooking search classes\n\nUsage: ios hooking search classes <search string>\n\nSearch for classes in the curren"
  },
  {
    "path": "objection/console/helpfiles/ios.hooking.search.methods.txt",
    "chars": 296,
    "preview": "Command: ios hooking search methods\n\nUsage: ios hooking search methods <search string>\n\nSearch for methods in classes in"
  },
  {
    "path": "objection/console/helpfiles/ios.hooking.search.txt",
    "chars": 69,
    "preview": "Contains subcommands helpful when searching for classes and methods.\n"
  },
  {
    "path": "objection/console/helpfiles/ios.hooking.set.return_value.txt",
    "chars": 492,
    "preview": "Command: ios hooking set return_value\n\nUsage: ios hooking set return_value \"<full class & selector>\" <true/false>\n\nHooks"
  },
  {
    "path": "objection/console/helpfiles/ios.hooking.set.txt",
    "chars": 50,
    "preview": "Sets various bits of hooking related information.\n"
  },
  {
    "path": "objection/console/helpfiles/ios.hooking.txt",
    "chars": 205,
    "preview": "Contains subcommands helpful when developing custom hooks. This includes discovery\nof Objective-C classes and methods in"
  },
  {
    "path": "objection/console/helpfiles/ios.hooking.watch.class.txt",
    "chars": 486,
    "preview": "Command: ios hooking watch class\n\nUsage: ios hooking watch <class_name> (--include-parents)\n\nHooks into all of the metho"
  },
  {
    "path": "objection/console/helpfiles/ios.hooking.watch.method.txt",
    "chars": 750,
    "preview": "Command: ios hooking watch method\n\nUsage: ios hooking method \"<full class & selector>\" (optional: --dump-backtrace)\n    "
  },
  {
    "path": "objection/console/helpfiles/ios.hooking.watch.txt",
    "chars": 77,
    "preview": "Contains subcommands to watch for method invocations on Objective-C classes.\n"
  },
  {
    "path": "objection/console/helpfiles/ios.jailbreak.disable.txt",
    "chars": 411,
    "preview": "Command: ios jailbreak disable\n\nUsage: ios jailbreak disable\n\nAttempts to disable Jailbreak detection on iOS devices. Th"
  },
  {
    "path": "objection/console/helpfiles/ios.jailbreak.simulate.txt",
    "chars": 331,
    "preview": "Command: ios jailbreak simulate\n\nUsage: ios jailbreak simulate\n\nAttempts to simulate a Jailbroken iOS environment. This "
  },
  {
    "path": "objection/console/helpfiles/ios.jailbreak.txt",
    "chars": 124,
    "preview": "Contains subcommands to work with iOS Jailbreak detection, such as disabling\nit, or simulating that a device is Jailbrok"
  },
  {
    "path": "objection/console/helpfiles/ios.keychain.add.txt",
    "chars": 395,
    "preview": "Command: ios keychain add\n\nUsage: ios keychain add --account <account> --service <service> --data <entry data>\n\nAdds a n"
  },
  {
    "path": "objection/console/helpfiles/ios.keychain.clear.txt",
    "chars": 476,
    "preview": "Command: ios keychain clear\n\nUsage: ios keychain clear\n\nClears all the keychain items for the current application. This "
  },
  {
    "path": "objection/console/helpfiles/ios.keychain.dump.txt",
    "chars": 1036,
    "preview": "Command: ios keychain dump\n\nUsage: ios keychain dump (optional: --json <filename>) (optional: --smart)\n\nExtracts the key"
  },
  {
    "path": "objection/console/helpfiles/ios.keychain.txt",
    "chars": 52,
    "preview": "Contains subcommands to work with the iOS keychain.\n"
  },
  {
    "path": "objection/console/helpfiles/ios.monitor.crypto.txt",
    "chars": 383,
    "preview": "Command: ios monitor crypto monitor\n\nUsage: ios monitor crypto monitor\n\nHooks CommonCrypto to output information about c"
  },
  {
    "path": "objection/console/helpfiles/ios.nsuserdefaults.get.txt",
    "chars": 238,
    "preview": "Command: ios nsuserdefaults get\n\nUsage: ios nsuserdefaults get\n\nQueries the applications NSUserDefaults class for all of"
  },
  {
    "path": "objection/console/helpfiles/ios.nsuserdefaults.txt",
    "chars": 64,
    "preview": "Contains subcommands to work with the iOS NSUserDefaults class.\n"
  },
  {
    "path": "objection/console/helpfiles/ios.pasteboard.monitor.txt",
    "chars": 289,
    "preview": "Command: ios pasteboard monitor\n\nUsage: ios pasteboard monitor\n\nHooks into the iOS UIPasteboard class and polls the gene"
  },
  {
    "path": "objection/console/helpfiles/ios.pasteboard.txt",
    "chars": 54,
    "preview": "Contains subcommands to work with the iOS pasteboard.\n"
  },
  {
    "path": "objection/console/helpfiles/ios.plist.cat.txt",
    "chars": 290,
    "preview": "Command: ios plist cat\n\nUsage: ios plist cat <remote plist filename>\n\nParses and echoes a plist file on the remote iOS d"
  },
  {
    "path": "objection/console/helpfiles/ios.plist.txt",
    "chars": 53,
    "preview": "Contains subcommands to work with iOS Plist entries.\n"
  },
  {
    "path": "objection/console/helpfiles/ios.sslpinning.disable.txt",
    "chars": 787,
    "preview": "Command: ios sslpinning disable\n\nUsage: ios sslpinning disable\n\nAttempts to disable SSL Pinning on iOS devices. This is "
  },
  {
    "path": "objection/console/helpfiles/ios.sslpinning.txt",
    "chars": 65,
    "preview": "Contains subcommands to work with iOS SSL pinning related calls.\n"
  },
  {
    "path": "objection/console/helpfiles/ios.txt",
    "chars": 148,
    "preview": "Contains subcommands to work with iOS specific features. These include features\nsuch as keychain dumping, reading plists"
  },
  {
    "path": "objection/console/helpfiles/ios.ui.alert.txt",
    "chars": 222,
    "preview": "Command: ios ui alert\n\nUsage: ios ui alert (optional: \"<alert message>\")\n\nDisplays an alert popup on an iOS device. A me"
  },
  {
    "path": "objection/console/helpfiles/ios.ui.dump.txt",
    "chars": 189,
    "preview": "Command: ios ui dump\n\nUsage: ios ui dump\n\nDumps the current, serialized user interface. This is useful to see which valu"
  }
]

// ... and 116 more files (download for full content)

About this extraction

This page contains the full source code of the sensepost/objection GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 316 files (696.5 KB), approximately 170.0k tokens, and a symbol index with 807 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.

Copied to clipboard!