├── .eslintrc.js ├── .github ├── FUNDING.yml └── workflows │ ├── deploy.yml │ └── pags.yml ├── .gitignore ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── agent ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── src │ ├── index.ts │ └── log.ts └── tsconfig.json ├── backend ├── .gitignore ├── __init__.py ├── android │ └── get-frida.py ├── core.py ├── driver.py ├── fruit │ └── __init__.py ├── pause.py ├── rpc.py ├── syslog.py ├── test │ ├── __init__.py │ └── test_device.py └── venv.py ├── i10n └── bundle.l10n.zh-cn.json ├── icon.png ├── package-lock.json ├── package.json ├── package.nls.json ├── package.nls.zh-cn.json ├── resources ├── dark │ ├── add.svg │ ├── apps.svg │ ├── attach.svg │ ├── del.svg │ ├── error.svg │ ├── local.svg │ ├── refresh.svg │ ├── remote.svg │ ├── statusRun.svg │ ├── statusStop.svg │ └── usb.svg ├── debugger.preload.js ├── doc │ ├── demo.gif │ ├── list.png │ ├── syslog.gif │ └── typing.gif ├── icon.svg ├── light │ ├── add.svg │ ├── apps.svg │ ├── attach.svg │ ├── del.svg │ ├── error.svg │ ├── local.svg │ ├── refresh.svg │ ├── remote.svg │ ├── statusRun.svg │ ├── statusStop.svg │ └── usb.svg ├── run.svg ├── templates │ ├── launch.json │ └── tasks.json └── terminal.png ├── src ├── commands │ ├── android.ts │ ├── boilerplate.ts │ ├── clipboard.ts │ ├── dump.ts │ ├── objection.ts │ ├── print.ts │ ├── repl.ts │ ├── ssh.ts │ ├── syslog.ts │ └── typing.ts ├── driver │ ├── adb.ts │ ├── backend.ts │ ├── frida.ts │ └── remote.ts ├── extension.ts ├── logger.ts ├── providers │ └── devices.ts ├── shebang.ts ├── term.ts ├── types.ts └── utils.ts ├── tsconfig.json ├── tslint.json └── types.d.ts /.eslintrc.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/naming-convention */ 2 | /* 3 | 👋 Hi! This file was autogenerated by tslint-to-eslint-config. 4 | https://github.com/typescript-eslint/tslint-to-eslint-config 5 | 6 | It represents the closest reasonable ESLint configuration to this 7 | project's original TSLint configuration. 8 | 9 | We recommend eventually switching this configuration to extend from 10 | the recommended rulesets in typescript-eslint. 11 | https://github.com/typescript-eslint/tslint-to-eslint-config/blob/master/docs/FAQs.md 12 | 13 | Happy linting! 💖 14 | */ 15 | module.exports = { 16 | "env": { 17 | "es6": true, 18 | "node": true 19 | }, 20 | "parser": "@typescript-eslint/parser", 21 | "parserOptions": { 22 | "sourceType": "module" 23 | }, 24 | "plugins": [ 25 | "@typescript-eslint" 26 | ], 27 | "root": true, 28 | "rules": { 29 | "@typescript-eslint/member-delimiter-style": [ 30 | "warn", 31 | { 32 | "multiline": { 33 | "delimiter": "semi", 34 | "requireLast": true 35 | }, 36 | "singleline": { 37 | "delimiter": "semi", 38 | "requireLast": false 39 | } 40 | } 41 | ], 42 | "@typescript-eslint/naming-convention": ["warn", { 43 | "selector": "enumMember", 44 | "format": ["PascalCase"] 45 | }], 46 | "@typescript-eslint/no-unused-expressions": "warn", 47 | "@typescript-eslint/semi": [ 48 | "warn", 49 | "always" 50 | ], 51 | "curly": "warn", 52 | "eqeqeq": [ 53 | "warn", 54 | "always" 55 | ], 56 | "no-redeclare": "warn", 57 | "no-throw-literal": "warn", 58 | "no-unused-expressions": "off", 59 | "semi": "off" 60 | } 61 | }; 62 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: ChiChou 2 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | on: 3 | push: 4 | tags: 5 | - v* 6 | 7 | jobs: 8 | build: 9 | strategy: 10 | matrix: 11 | os: [ubuntu-latest] 12 | runs-on: ${{ matrix.os }} 13 | permissions: 14 | contents: write 15 | pull-requests: write 16 | repository-projects: write 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v2 20 | - name: Install Node.js 21 | uses: actions/setup-node@v1 22 | with: 23 | node-version: 23.x 24 | - run: npm install 25 | - run: npm install -g vsce 26 | - name: Build Agent 27 | run: | 28 | cd agent 29 | npm install 30 | npm run build 31 | - name: Publish 32 | run: npm run deploy 33 | env: 34 | VSCE_PAT: ${{ secrets.VSCE_PAT }} 35 | - name: Create Release 36 | id: create_release 37 | uses: actions/create-release@v1 38 | env: 39 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 40 | with: 41 | tag_name: ${{ github.ref }} 42 | release_name: Release ${{ github.ref }} 43 | body: '' 44 | draft: false 45 | prerelease: false 46 | -------------------------------------------------------------------------------- /.github/workflows/pags.yml: -------------------------------------------------------------------------------- 1 | name: Pages 2 | 3 | on: 4 | push: 5 | branches: ["pages"] 6 | 7 | # Allows you to run this workflow manually from the Actions tab 8 | workflow_dispatch: 9 | 10 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 11 | permissions: 12 | contents: read 13 | pages: write 14 | id-token: write 15 | 16 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 17 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 18 | concurrency: 19 | group: "pages" 20 | cancel-in-progress: false 21 | 22 | jobs: 23 | build: 24 | runs-on: ubuntu-latest 25 | steps: 26 | - name: Checkout 27 | uses: actions/checkout@v4 28 | with: 29 | ref: 'pages' 30 | - name: Detect package manager 31 | id: detect-package-manager 32 | run: | 33 | if [ -f "${{ github.workspace }}/yarn.lock" ]; then 34 | echo "manager=yarn" >> $GITHUB_OUTPUT 35 | echo "command=install" >> $GITHUB_OUTPUT 36 | echo "runner=yarn" >> $GITHUB_OUTPUT 37 | exit 0 38 | elif [ -f "${{ github.workspace }}/package.json" ]; then 39 | echo "manager=npm" >> $GITHUB_OUTPUT 40 | echo "command=ci" >> $GITHUB_OUTPUT 41 | echo "runner=npx --no-install" >> $GITHUB_OUTPUT 42 | exit 0 43 | else 44 | echo "Unable to determine package manager" 45 | exit 1 46 | fi 47 | - name: Setup Node 48 | uses: actions/setup-node@v4 49 | with: 50 | node-version: "20" 51 | cache: ${{ steps.detect-package-manager.outputs.manager }} 52 | - name: Setup Pages 53 | uses: actions/configure-pages@v5 54 | with: 55 | # Automatically inject basePath in your Next.js configuration file and disable 56 | # server side image optimization (https://nextjs.org/docs/api-reference/next/image#unoptimized). 57 | # 58 | # You may remove this line if you want to manage the configuration yourself. 59 | static_site_generator: next 60 | - name: Restore cache 61 | uses: actions/cache@v4 62 | with: 63 | path: | 64 | .next/cache 65 | # Generate a new cache whenever packages or source files change. 66 | key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }} 67 | # If source files changed but packages didn't, rebuild from a prior cache. 68 | restore-keys: | 69 | ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}- 70 | - name: Install dependencies 71 | run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }} 72 | - name: Build with Next.js 73 | run: ${{ steps.detect-package-manager.outputs.runner }} next build 74 | - name: Upload artifact 75 | uses: actions/upload-pages-artifact@v3 76 | with: 77 | path: ./out 78 | 79 | # Deployment job 80 | deploy: 81 | environment: 82 | name: github-pages 83 | url: ${{ steps.deployment.outputs.page_url }} 84 | runs-on: ubuntu-latest 85 | needs: build 86 | steps: 87 | - name: Deploy to GitHub Pages 88 | id: deployment 89 | uses: actions/deploy-pages@v4 90 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modules 3 | .vscode-test/ 4 | *.vsix 5 | 6 | .DS_Store 7 | .ccls-cache/ -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "ms-vscode.vscode-typescript-tslint-plugin" 6 | ] 7 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Run Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "runtimeExecutable": "${execPath}", 13 | "args": [ 14 | "--extensionDevelopmentPath=${workspaceFolder}" 15 | ], 16 | "outFiles": [ 17 | "${workspaceFolder}/out/**/*.js" 18 | ], 19 | "preLaunchTask": "${defaultBuildTask}" 20 | }, 21 | { 22 | "name": "Extension Tests", 23 | "type": "extensionHost", 24 | "request": "launch", 25 | "runtimeExecutable": "${execPath}", 26 | "args": [ 27 | "--extensionDevelopmentPath=${workspaceFolder}", 28 | "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" 29 | ], 30 | "outFiles": [ 31 | "${workspaceFolder}/out/test/**/*.js" 32 | ], 33 | "preLaunchTask": "${defaultBuildTask}" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": true 5 | }, 6 | "search.exclude": { 7 | "out": true // set this to false to include "out" folder in search results 8 | }, 9 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 10 | "typescript.tsc.autoDetect": "off" 11 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "watch", 8 | "type": "npm", 9 | "script": "watch", 10 | "problemMatcher": "$tsc-watch", 11 | "isBackground": true, 12 | "presentation": { 13 | "reveal": "never" 14 | } 15 | }, 16 | { 17 | "label": "agent", 18 | "type": "npm", 19 | "script": "build", 20 | "presentation": { 21 | "reveal": "silent", 22 | "revealProblems": "onProblem", 23 | "close": true 24 | }, 25 | "options": { 26 | "cwd": "agent" 27 | } 28 | }, 29 | { 30 | "label": "beforeRun", 31 | "dependsOn": [ 32 | "agent", 33 | "watch" 34 | ], 35 | "group": { 36 | "kind": "build", 37 | "isDefault": true 38 | } 39 | } 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | out/test/** 4 | src/** 5 | .gitignore 6 | vsc-extension-quickstart.md 7 | **/tsconfig.json 8 | **/tslint.json 9 | **/*.map 10 | **/*.ts 11 | resources/doc/** 12 | agent/node_modules 13 | **/*.pyc -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ### v0.9.0 - 10 Sep, 2024 4 | 5 | * Support frida-tools installed from virtualenv, pipx, etc. 6 | 7 | ### v0.8.3 - 9 Sep, 2024 8 | 9 | * **Remove** lldb shortcut because it's impossible to adapt to different jailbreaks 10 | 11 | ### v0.7.3 - 25 Jun, 2023 12 | 13 | * Bring back `bagbak` 14 | 15 | ### v0.7.0 - 14 Jun, 2023 16 | 17 | * Use `fruity-frida` to support iOS debugging and SSH 18 | * Temporary disable bagbak for compatibility issue 19 | 20 | ### v0.4.11 - 07 Apr, 2023 21 | 22 | * Remove Fouldecrypt 23 | 24 | ### v0.4.6 - 13 Aug, 2022 25 | 26 | * add idevicesyslog 27 | 28 | ### v0.4.2 - 07 Aug, 2022 29 | 30 | * Bugfix: Debug config only works for local device 31 | * Bugfix: Debug config does not handle attaching to app properly 32 | * Bugfix: Context menu for USB devices is missing 33 | 34 | ### v0.4.0 - 07 Aug, 2022 35 | 36 | * Supports frida js debug! 37 | 38 | ### v0.3.19 - 06 Aug, 2022 39 | 40 | * Supports connecting / disconnecting remote devices 41 | 42 | ### v0.3.17 - 05 Aug, 2022 43 | 44 | * Bring back `execute` command 45 | 46 | ### v0.3.16 - 29 Mar, 2022 47 | 48 | * Ask to install frida-tools when not found 49 | * Fix issue #26 when multiple python interpreters are present 50 | 51 | ### v0.3.14 - 25 Jan, 2022 52 | 53 | * Fix `iproxy` command 54 | 55 | ### v0.3.12 - 27 Nov, 2021 56 | 57 | * Replace FlexDecrypt with Fouldecrypt 58 | * Bugfix: Shutdown iproxy when ref count is 0 59 | * Bugfix: Decryptor failes when App path has spaces 60 | 61 | ### v0.0.8 - 8 Aug, 2020 62 | 63 | * Bugfix: Attaching objection to a running application 64 | * Do not close terminal immediatly when exit code is not 0 65 | * More error logs 66 | 67 | ### v0.0.6 - 4 Aug, 2020 68 | 69 | Bugfix: `iproxy` fallback 70 | New: show ipa in folder 71 | 72 | ### v0.0.4 - 3 Aug, 2020 73 | 74 | * Add experimental lldb shortcut 75 | 76 | ### v0.0.3 - 2 Aug, 2020 77 | 78 | Added 79 | 80 | * Shell for both iOS (requires libimobiledevice) and Androd (requires adb) 81 | * Install public SSH key to iOS (like `ssh-copy-id`, but also supports Windows) 82 | * FlexDecrypt support. Requires `flexdecrypt` and `zip` on iOS, `ssh` and `iproxy` on desktop 83 | * FlexDecrypt installer 84 | 85 | Deprecation 86 | 87 | * [bagbak](https://github.com/ChiChou/bagbak) has been removed, in favor of [FlexDecrypt](https://github.com/JohnCoates/flexdecrypt) 88 | 89 | ### v0.0.2 - 22 Jul, 2020 90 | 91 | * support logcat for Android 92 | * pull `frida-gum.d.ts` from github 93 | 94 | ### v0.0.1 - 19 Jul, 2020 95 | 96 | * first release 97 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2022 Zhi and others 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vscode-frida 2 | 3 | **Unofficial** frida workbench for VSCode [![](https://img.shields.io/visual-studio-marketplace/v/CodeColorist.vscode-frida?color=%230af&label=install&logo=visual-studio-code&logoColor=%230ac&style=plastic)](https://marketplace.visualstudio.com/items?itemName=CodeColorist.vscode-frida) 4 | 5 | ## Prerequisites 6 | 7 | * libimobiledevices (for `inetcat` command) 8 | * Python >= 3.7 9 | * frida-tools python package 10 | * iTunes on Windows 11 | 12 | If you are on Windows, you need to keep iTunes open in order to interact with iOS devices via USB. 13 | 14 | ### Install frida-tools 15 | 16 | Because of [PEP0668](https://peps.python.org/pep-0668/), you might encounter error when `pip3 install frida-tools` globally. 17 | 18 | The recommended way is to open a folder (workspace) in VSCode, then create and activate a virtual environment using python extension. In this case, the extension will pick your current active python venv to load frida commands. 19 | 20 | Or you can use package manager like [pipx](https://github.com/pypa/pipx) or [UV](https://docs.astral.sh/uv/guides/tools/) to install it to $PATH, meanwhile keeping it isolated. 21 | 22 | ## Features 23 | 24 | ![demo](resources/doc/demo.gif) 25 | 26 | ### Target Selector 27 | 28 | User friendly UI 29 | 30 | ### Debug Log 31 | 32 | Now supports both iOS syslog and Android logcat! 33 | 34 | ![Debug Log](resources/doc/syslog.gif) 35 | 36 | ### Download and Apply frida-gum Typing Info 37 | 38 | ![Typing](resources/doc/typing.gif) 39 | 40 | ### Objection 41 | 42 | * [Objection](https://github.com/sensepost/objection) Runtime Mobile Exploration 43 | 44 | ### Javascript REPL shortcut 45 | 46 | Open and activate an REPL at the bottom. Use the "frida" button at the top of any active `js` / `typescript` document, it will send the code to the active REPL. 47 | 48 | ### Shell 49 | 50 | For Android devices, **Open Shell** is simply a wrapper for `adb shell`. 51 | 52 | For iOS it gives a SSH shell. It might ask for credentials depending on your setup. 53 | 54 | ## Todo 55 | 56 | * More Android features 57 | * More mobile security terminal tools intergration 58 | 59 | ## [CHANGELOG](CHANGELOG.md) 60 | 61 | ## Contributors 62 | 63 | ![](https://contrib.rocks/image?repo=chichou/vscode-frida) 64 | -------------------------------------------------------------------------------- /agent/.gitignore: -------------------------------------------------------------------------------- 1 | /_agent.js 2 | /node_modules 3 | -------------------------------------------------------------------------------- /agent/README.md: -------------------------------------------------------------------------------- 1 | ### How to compile & load 2 | 3 | ```sh 4 | $ git clone git://github.com/oleavr/frida-agent-example.git 5 | $ cd frida-agent-example/ 6 | $ npm install 7 | $ frida -U -f com.example.android --no-pause -l _agent.js 8 | ``` 9 | 10 | ### Development workflow 11 | 12 | To continuously recompile on change, keep this running in a terminal: 13 | 14 | ```sh 15 | $ npm run watch 16 | ``` 17 | 18 | And use an editor like Visual Studio Code for code completion and instant 19 | type-checking feedback. 20 | -------------------------------------------------------------------------------- /agent/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vscode-frida-agent", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "vscode-frida-agent", 9 | "version": "1.0.0", 10 | "devDependencies": { 11 | "@types/frida-gum": "^18.1.0", 12 | "@types/node": "^18.6.4", 13 | "frida-compile": "^15.1.0" 14 | } 15 | }, 16 | "node_modules/@frida/assert": { 17 | "version": "3.0.2", 18 | "resolved": "https://registry.npmjs.org/@frida/assert/-/assert-3.0.2.tgz", 19 | "integrity": "sha512-JXJq5SbXGrM5EkjrZKfRmB29zOoEOix02NC6A5TSJ+C1GE/X051EinJJsuOO2pEOx7KZwpvAHvS0WXW0+levKg==", 20 | "dev": true 21 | }, 22 | "node_modules/@frida/base64-js": { 23 | "version": "2.0.3", 24 | "resolved": "https://registry.npmjs.org/@frida/base64-js/-/base64-js-2.0.3.tgz", 25 | "integrity": "sha512-2w0F+1TynOTCZ/v7du9LdHPWwq0lJhazjo2fF9upMyQmA1zHetT14fLuQ1v/6T0qPgyeEGkiSrybstU8EsgeUA==", 26 | "dev": true, 27 | "funding": [ 28 | { 29 | "type": "github", 30 | "url": "https://github.com/sponsors/feross" 31 | }, 32 | { 33 | "type": "patreon", 34 | "url": "https://www.patreon.com/feross" 35 | }, 36 | { 37 | "type": "consulting", 38 | "url": "https://feross.org/support" 39 | } 40 | ] 41 | }, 42 | "node_modules/@frida/buffer": { 43 | "version": "7.0.4", 44 | "resolved": "https://registry.npmjs.org/@frida/buffer/-/buffer-7.0.4.tgz", 45 | "integrity": "sha512-RxQ1lZRRiCJj7nhcCiD8xeJx0NsLpGGnjqsmTg7jShGmbnVFMN5W7+J+3gqdPSQhc/IxNBIWc6zRXVp4+qnYHg==", 46 | "dev": true, 47 | "funding": [ 48 | { 49 | "type": "github", 50 | "url": "https://github.com/sponsors/feross" 51 | }, 52 | { 53 | "type": "patreon", 54 | "url": "https://www.patreon.com/feross" 55 | }, 56 | { 57 | "type": "consulting", 58 | "url": "https://feross.org/support" 59 | } 60 | ], 61 | "dependencies": { 62 | "base64-js": "^1.5.1", 63 | "ieee754": "^1.2.1" 64 | } 65 | }, 66 | "node_modules/@frida/crosspath": { 67 | "version": "3.0.0", 68 | "resolved": "https://registry.npmjs.org/@frida/crosspath/-/crosspath-3.0.0.tgz", 69 | "integrity": "sha512-bNdO1spIPD2P40XtK89N49oZpJhstdlnkJZcD4yJ17jrdkm9Ctu0sd9MIEX6Z8Tm8ydhVJBAOMEKl9/R27onAQ==", 70 | "dev": true, 71 | "dependencies": { 72 | "@types/node": "^17.0.36" 73 | }, 74 | "engines": { 75 | "node": ">=14.9.0" 76 | } 77 | }, 78 | "node_modules/@frida/crosspath/node_modules/@types/node": { 79 | "version": "17.0.45", 80 | "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", 81 | "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==", 82 | "dev": true 83 | }, 84 | "node_modules/@frida/diagnostics_channel": { 85 | "version": "1.0.0", 86 | "resolved": "https://registry.npmjs.org/@frida/diagnostics_channel/-/diagnostics_channel-1.0.0.tgz", 87 | "integrity": "sha512-mYX1jp/5Bpk24tHArJNx65iCk7qSuV8YJkdU0gFNVtJUXxfV8BG5WuPa4mL+ynxsbWWpsg/cwKZbLAepYKTdQQ==", 88 | "dev": true 89 | }, 90 | "node_modules/@frida/events": { 91 | "version": "4.0.4", 92 | "resolved": "https://registry.npmjs.org/@frida/events/-/events-4.0.4.tgz", 93 | "integrity": "sha512-qJVQ6VWHf9sjUKuiJzoCAC00frbpcwxeYfvQ+PP9LU/d70j+QvjWgYe98Qa3ekLaBU6r/AvWm8ThKCDUCLWrQQ==", 94 | "dev": true, 95 | "engines": { 96 | "node": ">=0.8.x" 97 | } 98 | }, 99 | "node_modules/@frida/http": { 100 | "version": "4.0.2", 101 | "resolved": "https://registry.npmjs.org/@frida/http/-/http-4.0.2.tgz", 102 | "integrity": "sha512-cvkc7ex7GmVXVOWqtjXKBWUUbYEBgpNRKZbEEoMeI8KiIs8zejKwg+N7rx7296Ao+EP3+xcUr4wBVr3xLaUVfQ==", 103 | "dev": true, 104 | "dependencies": { 105 | "http-parser-js": "^0.5.3" 106 | } 107 | }, 108 | "node_modules/@frida/http-parser-js": { 109 | "version": "1.0.0", 110 | "resolved": "https://registry.npmjs.org/@frida/http-parser-js/-/http-parser-js-1.0.0.tgz", 111 | "integrity": "sha512-2nMrNXt/OeTlWbqnE8AH4Sfz4I2+BGoN206dzKEyC/g2svtn83Xu+zuv/V3TkwrA27s26Mcy84ZwsXeNlqNxUQ==", 112 | "dev": true 113 | }, 114 | "node_modules/@frida/https": { 115 | "version": "1.0.0", 116 | "resolved": "https://registry.npmjs.org/@frida/https/-/https-1.0.0.tgz", 117 | "integrity": "sha512-OiqQ6qsALcWOktRLq07oJ0i6sH8eX6MXb/MdZS1qVKDRf6wchH4Pjn6fiLB+pt/OlYbggk+DOfpHwSdjTwuHMQ==", 118 | "dev": true 119 | }, 120 | "node_modules/@frida/ieee754": { 121 | "version": "2.0.2", 122 | "resolved": "https://registry.npmjs.org/@frida/ieee754/-/ieee754-2.0.2.tgz", 123 | "integrity": "sha512-wlcUebnne4ENN7GDr5pTH598ZDLMVOsh0FjenxeVOe6u7ewZkz9gGRnLnZKJAm9kl5G6XhdxhI0cSXVQK/rQUw==", 124 | "dev": true, 125 | "funding": [ 126 | { 127 | "type": "github", 128 | "url": "https://github.com/sponsors/feross" 129 | }, 130 | { 131 | "type": "patreon", 132 | "url": "https://www.patreon.com/feross" 133 | }, 134 | { 135 | "type": "consulting", 136 | "url": "https://feross.org/support" 137 | } 138 | ] 139 | }, 140 | "node_modules/@frida/net": { 141 | "version": "4.0.1", 142 | "resolved": "https://registry.npmjs.org/@frida/net/-/net-4.0.1.tgz", 143 | "integrity": "sha512-lmzg7LMjjWluu2RqE4PSe5QpgYEbCWjL08twsOLcYnhMz95jWmxY35A0PBySrEw1ulaflsMvXwsONnhl5bk6AQ==", 144 | "dev": true 145 | }, 146 | "node_modules/@frida/os": { 147 | "version": "1.0.2", 148 | "resolved": "https://registry.npmjs.org/@frida/os/-/os-1.0.2.tgz", 149 | "integrity": "sha512-3ISAiGNiyIya3QN2EHBCz1wqP0enTdSxP99wUeroeh8+AQRmgoOr/5TRnrVry8pe378anay3fmV/tdUMMSkehQ==", 150 | "dev": true 151 | }, 152 | "node_modules/@frida/path": { 153 | "version": "2.0.3", 154 | "resolved": "https://registry.npmjs.org/@frida/path/-/path-2.0.3.tgz", 155 | "integrity": "sha512-2RQy36QatoC846fzBhBhV8sXMsSOBGoYvwTHeaE1zUdz7F4RNScP4QEekTTooBYWYX/XjiF36KQpYAzc9OYFtg==", 156 | "dev": true 157 | }, 158 | "node_modules/@frida/process": { 159 | "version": "1.2.1", 160 | "resolved": "https://registry.npmjs.org/@frida/process/-/process-1.2.1.tgz", 161 | "integrity": "sha512-nvCu22DstFW2ttGFtOKekHM7vnjbZm+XgtvavOt427GNT6uV7k0JYK9tnMbcLMRWv57DG6udAmuJlWs8Paq1ag==", 162 | "dev": true 163 | }, 164 | "node_modules/@frida/punycode": { 165 | "version": "3.0.0", 166 | "resolved": "https://registry.npmjs.org/@frida/punycode/-/punycode-3.0.0.tgz", 167 | "integrity": "sha512-XVSDY2KamDs1D5/fTVgHcOSNxdU4kTboxzqJMBbTjcQC7XScIT9c0EfbwKCq7Kci6gWQdsHSCr7lU+9Oc4KAdg==", 168 | "dev": true 169 | }, 170 | "node_modules/@frida/querystring": { 171 | "version": "1.0.0", 172 | "resolved": "https://registry.npmjs.org/@frida/querystring/-/querystring-1.0.0.tgz", 173 | "integrity": "sha512-15m1fOZPmoO/vWlgPJrG/J9/BJDz6a2/JpVGpS8ynNzo+fBhTznaStX5nHxUs24mVTqh/OqLo0EiYJM5WWHXxg==", 174 | "dev": true 175 | }, 176 | "node_modules/@frida/readable-stream": { 177 | "version": "4.1.3", 178 | "resolved": "https://registry.npmjs.org/@frida/readable-stream/-/readable-stream-4.1.3.tgz", 179 | "integrity": "sha512-ntGUFmi+CryRGRJIK13a/VST2Ad19uivbln8Xd92vKPAARq+6vMIASDyZIqyl5BLRccfiyCHdYgrgQ6RI5rUig==", 180 | "dev": true 181 | }, 182 | "node_modules/@frida/reserved-words": { 183 | "version": "1.0.0", 184 | "resolved": "https://registry.npmjs.org/@frida/reserved-words/-/reserved-words-1.0.0.tgz", 185 | "integrity": "sha512-2yG/XxJlsGlk/mm6eZTb4OAaQEhkTI2qaFfZFtAsrA/XuCpuMWkS4y/guyBlsRu4hAuhK2HPmNM8+OLLK1zM9Q==", 186 | "dev": true 187 | }, 188 | "node_modules/@frida/stream": { 189 | "version": "1.0.2", 190 | "resolved": "https://registry.npmjs.org/@frida/stream/-/stream-1.0.2.tgz", 191 | "integrity": "sha512-4OuaC1ztmEKgTq3WeBhsy8Oq+AwW9n9cYnvLklcC9jwD93AEwgbWpecLlxJCVuALvTMdhKPg0nQVfyGYP/i9Bw==", 192 | "dev": true, 193 | "dependencies": { 194 | "@frida/readable-stream": "^4.1.3" 195 | } 196 | }, 197 | "node_modules/@frida/string_decoder": { 198 | "version": "2.0.0", 199 | "resolved": "https://registry.npmjs.org/@frida/string_decoder/-/string_decoder-2.0.0.tgz", 200 | "integrity": "sha512-in371tYZMHQiW9HF5MS3JDw6Ao6tyBoq34UWy2rzOswYyMG1rpizh85ofi/yVkxDiaqybEZefxzkVittpPGT6g==", 201 | "dev": true 202 | }, 203 | "node_modules/@frida/timers": { 204 | "version": "3.0.0", 205 | "resolved": "https://registry.npmjs.org/@frida/timers/-/timers-3.0.0.tgz", 206 | "integrity": "sha512-3b+0igv10aT8TMxefrTAd06rActqbxJLY2Xkkq9vYcPBffB/yHszl0NYIp/5ko8WC3ecDYPU6bQiY6fjs72zTA==", 207 | "dev": true 208 | }, 209 | "node_modules/@frida/tty": { 210 | "version": "1.0.0", 211 | "resolved": "https://registry.npmjs.org/@frida/tty/-/tty-1.0.0.tgz", 212 | "integrity": "sha512-p/kjLnKYxEAB1MdYP8+5rKv9CsHzyA+0jg9BcGETzjQVKHHcroHDULRxDYUh+DC7qs6cpX8QdDQh9E+a6ydgsQ==", 213 | "dev": true 214 | }, 215 | "node_modules/@frida/url": { 216 | "version": "1.0.2", 217 | "resolved": "https://registry.npmjs.org/@frida/url/-/url-1.0.2.tgz", 218 | "integrity": "sha512-ZKunbKJHMr8w2Eb/5K1avy0MzK1B998S17wYXNv3RmzBGxMm8S5T0F3qEpRxkU7/72P8m4izyQU87fWl+FjQsQ==", 219 | "dev": true, 220 | "dependencies": { 221 | "@frida/punycode": "^3.0.0", 222 | "@frida/querystring": "^1.0.0" 223 | } 224 | }, 225 | "node_modules/@frida/util": { 226 | "version": "1.0.3", 227 | "resolved": "https://registry.npmjs.org/@frida/util/-/util-1.0.3.tgz", 228 | "integrity": "sha512-htcG3uDiRXv89ERVNNYhfase39kJ2X75ZARfrYcYEtJLFEsSk0nemM1YnEIR4CjrHvdvkWHrwgKkS+acOyoNEg==", 229 | "dev": true 230 | }, 231 | "node_modules/@frida/vm": { 232 | "version": "2.0.0", 233 | "resolved": "https://registry.npmjs.org/@frida/vm/-/vm-2.0.0.tgz", 234 | "integrity": "sha512-7fsjLWscZT5odNIBtg6qbLNI+vAk1xmii6H5W2kaYkMYt0vRohQEcDSUWacA+eaWlu5SvMjZI82Yibj/3G9pJw==", 235 | "dev": true 236 | }, 237 | "node_modules/@jridgewell/gen-mapping": { 238 | "version": "0.3.2", 239 | "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", 240 | "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", 241 | "dev": true, 242 | "dependencies": { 243 | "@jridgewell/set-array": "^1.0.1", 244 | "@jridgewell/sourcemap-codec": "^1.4.10", 245 | "@jridgewell/trace-mapping": "^0.3.9" 246 | }, 247 | "engines": { 248 | "node": ">=6.0.0" 249 | } 250 | }, 251 | "node_modules/@jridgewell/resolve-uri": { 252 | "version": "3.1.0", 253 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", 254 | "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", 255 | "dev": true, 256 | "engines": { 257 | "node": ">=6.0.0" 258 | } 259 | }, 260 | "node_modules/@jridgewell/set-array": { 261 | "version": "1.1.2", 262 | "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", 263 | "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", 264 | "dev": true, 265 | "engines": { 266 | "node": ">=6.0.0" 267 | } 268 | }, 269 | "node_modules/@jridgewell/source-map": { 270 | "version": "0.3.2", 271 | "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", 272 | "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", 273 | "dev": true, 274 | "dependencies": { 275 | "@jridgewell/gen-mapping": "^0.3.0", 276 | "@jridgewell/trace-mapping": "^0.3.9" 277 | } 278 | }, 279 | "node_modules/@jridgewell/sourcemap-codec": { 280 | "version": "1.4.14", 281 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", 282 | "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", 283 | "dev": true 284 | }, 285 | "node_modules/@jridgewell/trace-mapping": { 286 | "version": "0.3.17", 287 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", 288 | "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", 289 | "dev": true, 290 | "dependencies": { 291 | "@jridgewell/resolve-uri": "3.1.0", 292 | "@jridgewell/sourcemap-codec": "1.4.14" 293 | } 294 | }, 295 | "node_modules/@types/frida-gum": { 296 | "version": "18.3.1", 297 | "resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-18.3.1.tgz", 298 | "integrity": "sha512-8lD4UQZa7V9+eQ9wfBAf+iLk/bq+u998podeXYTeWQu03sacSF70XL+WcYmhvAkNMF3OS9nkb4VtfRd8Ly7a3A==", 299 | "dev": true 300 | }, 301 | "node_modules/@types/node": { 302 | "version": "18.15.9", 303 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.9.tgz", 304 | "integrity": "sha512-dUxhiNzBLr6IqlZXz6e/rN2YQXlFgOei/Dxy+e3cyXTJ4txSUbGT2/fmnD6zd/75jDMeW5bDee+YXxlFKHoV0A==", 305 | "dev": true 306 | }, 307 | "node_modules/acorn": { 308 | "version": "8.8.2", 309 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", 310 | "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", 311 | "dev": true, 312 | "bin": { 313 | "acorn": "bin/acorn" 314 | }, 315 | "engines": { 316 | "node": ">=0.4.0" 317 | } 318 | }, 319 | "node_modules/base64-js": { 320 | "version": "1.5.1", 321 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 322 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 323 | "dev": true, 324 | "funding": [ 325 | { 326 | "type": "github", 327 | "url": "https://github.com/sponsors/feross" 328 | }, 329 | { 330 | "type": "patreon", 331 | "url": "https://www.patreon.com/feross" 332 | }, 333 | { 334 | "type": "consulting", 335 | "url": "https://feross.org/support" 336 | } 337 | ] 338 | }, 339 | "node_modules/buffer-from": { 340 | "version": "1.1.2", 341 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", 342 | "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", 343 | "dev": true 344 | }, 345 | "node_modules/commander": { 346 | "version": "9.5.0", 347 | "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", 348 | "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", 349 | "dev": true, 350 | "engines": { 351 | "node": "^12.20.0 || >=14" 352 | } 353 | }, 354 | "node_modules/frida-compile": { 355 | "version": "15.1.1", 356 | "resolved": "https://registry.npmjs.org/frida-compile/-/frida-compile-15.1.1.tgz", 357 | "integrity": "sha512-cSmFwgazxIWi5lyPRntqZI+VUhfh6EwxTPDIA1PQOUoupv5B0MMri8g7wZ8/FpFxcGUsy6YtoQ9FEh22oq6pBQ==", 358 | "dev": true, 359 | "dependencies": { 360 | "@frida/assert": "^3.0.1", 361 | "@frida/base64-js": "^2.0.3", 362 | "@frida/buffer": "^7.0.4", 363 | "@frida/crosspath": "^3.0.0", 364 | "@frida/diagnostics_channel": "^1.0.0", 365 | "@frida/events": "^4.0.4", 366 | "@frida/http": "^4.0.1", 367 | "@frida/http-parser-js": "^1.0.0", 368 | "@frida/https": "^1.0.0", 369 | "@frida/ieee754": "^2.0.2", 370 | "@frida/net": "^4.0.0", 371 | "@frida/os": "^1.0.0", 372 | "@frida/path": "^2.0.2", 373 | "@frida/process": "^1.2.0", 374 | "@frida/punycode": "^3.0.0", 375 | "@frida/querystring": "^1.0.0", 376 | "@frida/readable-stream": "^4.1.3", 377 | "@frida/reserved-words": "^1.0.0", 378 | "@frida/stream": "^1.0.1", 379 | "@frida/string_decoder": "^2.0.0", 380 | "@frida/timers": "^3.0.0", 381 | "@frida/tty": "^1.0.0", 382 | "@frida/url": "^1.0.1", 383 | "@frida/util": "^1.0.3", 384 | "@frida/vm": "^2.0.0", 385 | "commander": "^9.3.0", 386 | "frida-fs": "^5.2.0", 387 | "terser": "^5.14.1", 388 | "typed-emitter": "^2.1.0" 389 | }, 390 | "bin": { 391 | "frida-compile": "dist/cli.js" 392 | } 393 | }, 394 | "node_modules/frida-fs": { 395 | "version": "5.2.2", 396 | "resolved": "https://registry.npmjs.org/frida-fs/-/frida-fs-5.2.2.tgz", 397 | "integrity": "sha512-AMOuNtYC76z/b13PnRMyWC+JPHr134SU+erBsZLyCAFaRGOkaDVRQLT2vfXcXqJTLdoIdiUeIUREF78Js18NLA==", 398 | "dev": true 399 | }, 400 | "node_modules/http-parser-js": { 401 | "version": "0.5.8", 402 | "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", 403 | "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", 404 | "dev": true 405 | }, 406 | "node_modules/ieee754": { 407 | "version": "1.2.1", 408 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 409 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", 410 | "dev": true, 411 | "funding": [ 412 | { 413 | "type": "github", 414 | "url": "https://github.com/sponsors/feross" 415 | }, 416 | { 417 | "type": "patreon", 418 | "url": "https://www.patreon.com/feross" 419 | }, 420 | { 421 | "type": "consulting", 422 | "url": "https://feross.org/support" 423 | } 424 | ] 425 | }, 426 | "node_modules/source-map": { 427 | "version": "0.6.1", 428 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 429 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 430 | "dev": true, 431 | "engines": { 432 | "node": ">=0.10.0" 433 | } 434 | }, 435 | "node_modules/source-map-support": { 436 | "version": "0.5.21", 437 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", 438 | "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", 439 | "dev": true, 440 | "dependencies": { 441 | "buffer-from": "^1.0.0", 442 | "source-map": "^0.6.0" 443 | } 444 | }, 445 | "node_modules/terser": { 446 | "version": "5.16.8", 447 | "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.8.tgz", 448 | "integrity": "sha512-QI5g1E/ef7d+PsDifb+a6nnVgC4F22Bg6T0xrBrz6iloVB4PUkkunp6V8nzoOOZJIzjWVdAGqCdlKlhLq/TbIA==", 449 | "dev": true, 450 | "dependencies": { 451 | "@jridgewell/source-map": "^0.3.2", 452 | "acorn": "^8.5.0", 453 | "commander": "^2.20.0", 454 | "source-map-support": "~0.5.20" 455 | }, 456 | "bin": { 457 | "terser": "bin/terser" 458 | }, 459 | "engines": { 460 | "node": ">=10" 461 | } 462 | }, 463 | "node_modules/terser/node_modules/commander": { 464 | "version": "2.20.3", 465 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 466 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", 467 | "dev": true 468 | }, 469 | "node_modules/typed-emitter": { 470 | "version": "2.1.0", 471 | "resolved": "https://registry.npmjs.org/typed-emitter/-/typed-emitter-2.1.0.tgz", 472 | "integrity": "sha512-g/KzbYKbH5C2vPkaXGu8DJlHrGKHLsM25Zg9WuC9pMGfuvT+X25tZQWo5fK1BjBm8+UrVE9LDCvaY0CQk+fXDA==", 473 | "dev": true, 474 | "optionalDependencies": { 475 | "rxjs": "*" 476 | } 477 | } 478 | }, 479 | "dependencies": { 480 | "@frida/assert": { 481 | "version": "3.0.2", 482 | "resolved": "https://registry.npmjs.org/@frida/assert/-/assert-3.0.2.tgz", 483 | "integrity": "sha512-JXJq5SbXGrM5EkjrZKfRmB29zOoEOix02NC6A5TSJ+C1GE/X051EinJJsuOO2pEOx7KZwpvAHvS0WXW0+levKg==", 484 | "dev": true 485 | }, 486 | "@frida/base64-js": { 487 | "version": "2.0.3", 488 | "resolved": "https://registry.npmjs.org/@frida/base64-js/-/base64-js-2.0.3.tgz", 489 | "integrity": "sha512-2w0F+1TynOTCZ/v7du9LdHPWwq0lJhazjo2fF9upMyQmA1zHetT14fLuQ1v/6T0qPgyeEGkiSrybstU8EsgeUA==", 490 | "dev": true 491 | }, 492 | "@frida/buffer": { 493 | "version": "7.0.4", 494 | "resolved": "https://registry.npmjs.org/@frida/buffer/-/buffer-7.0.4.tgz", 495 | "integrity": "sha512-RxQ1lZRRiCJj7nhcCiD8xeJx0NsLpGGnjqsmTg7jShGmbnVFMN5W7+J+3gqdPSQhc/IxNBIWc6zRXVp4+qnYHg==", 496 | "dev": true, 497 | "requires": { 498 | "base64-js": "^1.5.1", 499 | "ieee754": "^1.2.1" 500 | } 501 | }, 502 | "@frida/crosspath": { 503 | "version": "3.0.0", 504 | "resolved": "https://registry.npmjs.org/@frida/crosspath/-/crosspath-3.0.0.tgz", 505 | "integrity": "sha512-bNdO1spIPD2P40XtK89N49oZpJhstdlnkJZcD4yJ17jrdkm9Ctu0sd9MIEX6Z8Tm8ydhVJBAOMEKl9/R27onAQ==", 506 | "dev": true, 507 | "requires": { 508 | "@types/node": "^17.0.36" 509 | }, 510 | "dependencies": { 511 | "@types/node": { 512 | "version": "17.0.45", 513 | "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", 514 | "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==", 515 | "dev": true 516 | } 517 | } 518 | }, 519 | "@frida/diagnostics_channel": { 520 | "version": "1.0.0", 521 | "resolved": "https://registry.npmjs.org/@frida/diagnostics_channel/-/diagnostics_channel-1.0.0.tgz", 522 | "integrity": "sha512-mYX1jp/5Bpk24tHArJNx65iCk7qSuV8YJkdU0gFNVtJUXxfV8BG5WuPa4mL+ynxsbWWpsg/cwKZbLAepYKTdQQ==", 523 | "dev": true 524 | }, 525 | "@frida/events": { 526 | "version": "4.0.4", 527 | "resolved": "https://registry.npmjs.org/@frida/events/-/events-4.0.4.tgz", 528 | "integrity": "sha512-qJVQ6VWHf9sjUKuiJzoCAC00frbpcwxeYfvQ+PP9LU/d70j+QvjWgYe98Qa3ekLaBU6r/AvWm8ThKCDUCLWrQQ==", 529 | "dev": true 530 | }, 531 | "@frida/http": { 532 | "version": "4.0.2", 533 | "resolved": "https://registry.npmjs.org/@frida/http/-/http-4.0.2.tgz", 534 | "integrity": "sha512-cvkc7ex7GmVXVOWqtjXKBWUUbYEBgpNRKZbEEoMeI8KiIs8zejKwg+N7rx7296Ao+EP3+xcUr4wBVr3xLaUVfQ==", 535 | "dev": true, 536 | "requires": { 537 | "http-parser-js": "^0.5.3" 538 | } 539 | }, 540 | "@frida/http-parser-js": { 541 | "version": "1.0.0", 542 | "resolved": "https://registry.npmjs.org/@frida/http-parser-js/-/http-parser-js-1.0.0.tgz", 543 | "integrity": "sha512-2nMrNXt/OeTlWbqnE8AH4Sfz4I2+BGoN206dzKEyC/g2svtn83Xu+zuv/V3TkwrA27s26Mcy84ZwsXeNlqNxUQ==", 544 | "dev": true 545 | }, 546 | "@frida/https": { 547 | "version": "1.0.0", 548 | "resolved": "https://registry.npmjs.org/@frida/https/-/https-1.0.0.tgz", 549 | "integrity": "sha512-OiqQ6qsALcWOktRLq07oJ0i6sH8eX6MXb/MdZS1qVKDRf6wchH4Pjn6fiLB+pt/OlYbggk+DOfpHwSdjTwuHMQ==", 550 | "dev": true 551 | }, 552 | "@frida/ieee754": { 553 | "version": "2.0.2", 554 | "resolved": "https://registry.npmjs.org/@frida/ieee754/-/ieee754-2.0.2.tgz", 555 | "integrity": "sha512-wlcUebnne4ENN7GDr5pTH598ZDLMVOsh0FjenxeVOe6u7ewZkz9gGRnLnZKJAm9kl5G6XhdxhI0cSXVQK/rQUw==", 556 | "dev": true 557 | }, 558 | "@frida/net": { 559 | "version": "4.0.1", 560 | "resolved": "https://registry.npmjs.org/@frida/net/-/net-4.0.1.tgz", 561 | "integrity": "sha512-lmzg7LMjjWluu2RqE4PSe5QpgYEbCWjL08twsOLcYnhMz95jWmxY35A0PBySrEw1ulaflsMvXwsONnhl5bk6AQ==", 562 | "dev": true 563 | }, 564 | "@frida/os": { 565 | "version": "1.0.2", 566 | "resolved": "https://registry.npmjs.org/@frida/os/-/os-1.0.2.tgz", 567 | "integrity": "sha512-3ISAiGNiyIya3QN2EHBCz1wqP0enTdSxP99wUeroeh8+AQRmgoOr/5TRnrVry8pe378anay3fmV/tdUMMSkehQ==", 568 | "dev": true 569 | }, 570 | "@frida/path": { 571 | "version": "2.0.3", 572 | "resolved": "https://registry.npmjs.org/@frida/path/-/path-2.0.3.tgz", 573 | "integrity": "sha512-2RQy36QatoC846fzBhBhV8sXMsSOBGoYvwTHeaE1zUdz7F4RNScP4QEekTTooBYWYX/XjiF36KQpYAzc9OYFtg==", 574 | "dev": true 575 | }, 576 | "@frida/process": { 577 | "version": "1.2.1", 578 | "resolved": "https://registry.npmjs.org/@frida/process/-/process-1.2.1.tgz", 579 | "integrity": "sha512-nvCu22DstFW2ttGFtOKekHM7vnjbZm+XgtvavOt427GNT6uV7k0JYK9tnMbcLMRWv57DG6udAmuJlWs8Paq1ag==", 580 | "dev": true 581 | }, 582 | "@frida/punycode": { 583 | "version": "3.0.0", 584 | "resolved": "https://registry.npmjs.org/@frida/punycode/-/punycode-3.0.0.tgz", 585 | "integrity": "sha512-XVSDY2KamDs1D5/fTVgHcOSNxdU4kTboxzqJMBbTjcQC7XScIT9c0EfbwKCq7Kci6gWQdsHSCr7lU+9Oc4KAdg==", 586 | "dev": true 587 | }, 588 | "@frida/querystring": { 589 | "version": "1.0.0", 590 | "resolved": "https://registry.npmjs.org/@frida/querystring/-/querystring-1.0.0.tgz", 591 | "integrity": "sha512-15m1fOZPmoO/vWlgPJrG/J9/BJDz6a2/JpVGpS8ynNzo+fBhTznaStX5nHxUs24mVTqh/OqLo0EiYJM5WWHXxg==", 592 | "dev": true 593 | }, 594 | "@frida/readable-stream": { 595 | "version": "4.1.3", 596 | "resolved": "https://registry.npmjs.org/@frida/readable-stream/-/readable-stream-4.1.3.tgz", 597 | "integrity": "sha512-ntGUFmi+CryRGRJIK13a/VST2Ad19uivbln8Xd92vKPAARq+6vMIASDyZIqyl5BLRccfiyCHdYgrgQ6RI5rUig==", 598 | "dev": true 599 | }, 600 | "@frida/reserved-words": { 601 | "version": "1.0.0", 602 | "resolved": "https://registry.npmjs.org/@frida/reserved-words/-/reserved-words-1.0.0.tgz", 603 | "integrity": "sha512-2yG/XxJlsGlk/mm6eZTb4OAaQEhkTI2qaFfZFtAsrA/XuCpuMWkS4y/guyBlsRu4hAuhK2HPmNM8+OLLK1zM9Q==", 604 | "dev": true 605 | }, 606 | "@frida/stream": { 607 | "version": "1.0.2", 608 | "resolved": "https://registry.npmjs.org/@frida/stream/-/stream-1.0.2.tgz", 609 | "integrity": "sha512-4OuaC1ztmEKgTq3WeBhsy8Oq+AwW9n9cYnvLklcC9jwD93AEwgbWpecLlxJCVuALvTMdhKPg0nQVfyGYP/i9Bw==", 610 | "dev": true, 611 | "requires": { 612 | "@frida/readable-stream": "^4.1.3" 613 | } 614 | }, 615 | "@frida/string_decoder": { 616 | "version": "2.0.0", 617 | "resolved": "https://registry.npmjs.org/@frida/string_decoder/-/string_decoder-2.0.0.tgz", 618 | "integrity": "sha512-in371tYZMHQiW9HF5MS3JDw6Ao6tyBoq34UWy2rzOswYyMG1rpizh85ofi/yVkxDiaqybEZefxzkVittpPGT6g==", 619 | "dev": true 620 | }, 621 | "@frida/timers": { 622 | "version": "3.0.0", 623 | "resolved": "https://registry.npmjs.org/@frida/timers/-/timers-3.0.0.tgz", 624 | "integrity": "sha512-3b+0igv10aT8TMxefrTAd06rActqbxJLY2Xkkq9vYcPBffB/yHszl0NYIp/5ko8WC3ecDYPU6bQiY6fjs72zTA==", 625 | "dev": true 626 | }, 627 | "@frida/tty": { 628 | "version": "1.0.0", 629 | "resolved": "https://registry.npmjs.org/@frida/tty/-/tty-1.0.0.tgz", 630 | "integrity": "sha512-p/kjLnKYxEAB1MdYP8+5rKv9CsHzyA+0jg9BcGETzjQVKHHcroHDULRxDYUh+DC7qs6cpX8QdDQh9E+a6ydgsQ==", 631 | "dev": true 632 | }, 633 | "@frida/url": { 634 | "version": "1.0.2", 635 | "resolved": "https://registry.npmjs.org/@frida/url/-/url-1.0.2.tgz", 636 | "integrity": "sha512-ZKunbKJHMr8w2Eb/5K1avy0MzK1B998S17wYXNv3RmzBGxMm8S5T0F3qEpRxkU7/72P8m4izyQU87fWl+FjQsQ==", 637 | "dev": true, 638 | "requires": { 639 | "@frida/punycode": "^3.0.0", 640 | "@frida/querystring": "^1.0.0" 641 | } 642 | }, 643 | "@frida/util": { 644 | "version": "1.0.3", 645 | "resolved": "https://registry.npmjs.org/@frida/util/-/util-1.0.3.tgz", 646 | "integrity": "sha512-htcG3uDiRXv89ERVNNYhfase39kJ2X75ZARfrYcYEtJLFEsSk0nemM1YnEIR4CjrHvdvkWHrwgKkS+acOyoNEg==", 647 | "dev": true 648 | }, 649 | "@frida/vm": { 650 | "version": "2.0.0", 651 | "resolved": "https://registry.npmjs.org/@frida/vm/-/vm-2.0.0.tgz", 652 | "integrity": "sha512-7fsjLWscZT5odNIBtg6qbLNI+vAk1xmii6H5W2kaYkMYt0vRohQEcDSUWacA+eaWlu5SvMjZI82Yibj/3G9pJw==", 653 | "dev": true 654 | }, 655 | "@jridgewell/gen-mapping": { 656 | "version": "0.3.2", 657 | "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", 658 | "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", 659 | "dev": true, 660 | "requires": { 661 | "@jridgewell/set-array": "^1.0.1", 662 | "@jridgewell/sourcemap-codec": "^1.4.10", 663 | "@jridgewell/trace-mapping": "^0.3.9" 664 | } 665 | }, 666 | "@jridgewell/resolve-uri": { 667 | "version": "3.1.0", 668 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", 669 | "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", 670 | "dev": true 671 | }, 672 | "@jridgewell/set-array": { 673 | "version": "1.1.2", 674 | "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", 675 | "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", 676 | "dev": true 677 | }, 678 | "@jridgewell/source-map": { 679 | "version": "0.3.2", 680 | "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", 681 | "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", 682 | "dev": true, 683 | "requires": { 684 | "@jridgewell/gen-mapping": "^0.3.0", 685 | "@jridgewell/trace-mapping": "^0.3.9" 686 | } 687 | }, 688 | "@jridgewell/sourcemap-codec": { 689 | "version": "1.4.14", 690 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", 691 | "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", 692 | "dev": true 693 | }, 694 | "@jridgewell/trace-mapping": { 695 | "version": "0.3.17", 696 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", 697 | "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", 698 | "dev": true, 699 | "requires": { 700 | "@jridgewell/resolve-uri": "3.1.0", 701 | "@jridgewell/sourcemap-codec": "1.4.14" 702 | } 703 | }, 704 | "@types/frida-gum": { 705 | "version": "18.3.1", 706 | "resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-18.3.1.tgz", 707 | "integrity": "sha512-8lD4UQZa7V9+eQ9wfBAf+iLk/bq+u998podeXYTeWQu03sacSF70XL+WcYmhvAkNMF3OS9nkb4VtfRd8Ly7a3A==", 708 | "dev": true 709 | }, 710 | "@types/node": { 711 | "version": "18.15.9", 712 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.9.tgz", 713 | "integrity": "sha512-dUxhiNzBLr6IqlZXz6e/rN2YQXlFgOei/Dxy+e3cyXTJ4txSUbGT2/fmnD6zd/75jDMeW5bDee+YXxlFKHoV0A==", 714 | "dev": true 715 | }, 716 | "acorn": { 717 | "version": "8.8.2", 718 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", 719 | "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", 720 | "dev": true 721 | }, 722 | "base64-js": { 723 | "version": "1.5.1", 724 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 725 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 726 | "dev": true 727 | }, 728 | "buffer-from": { 729 | "version": "1.1.2", 730 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", 731 | "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", 732 | "dev": true 733 | }, 734 | "commander": { 735 | "version": "9.5.0", 736 | "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", 737 | "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", 738 | "dev": true 739 | }, 740 | "frida-compile": { 741 | "version": "15.1.1", 742 | "resolved": "https://registry.npmjs.org/frida-compile/-/frida-compile-15.1.1.tgz", 743 | "integrity": "sha512-cSmFwgazxIWi5lyPRntqZI+VUhfh6EwxTPDIA1PQOUoupv5B0MMri8g7wZ8/FpFxcGUsy6YtoQ9FEh22oq6pBQ==", 744 | "dev": true, 745 | "requires": { 746 | "@frida/assert": "^3.0.1", 747 | "@frida/base64-js": "^2.0.3", 748 | "@frida/buffer": "^7.0.4", 749 | "@frida/crosspath": "^3.0.0", 750 | "@frida/diagnostics_channel": "^1.0.0", 751 | "@frida/events": "^4.0.4", 752 | "@frida/http": "^4.0.1", 753 | "@frida/http-parser-js": "^1.0.0", 754 | "@frida/https": "^1.0.0", 755 | "@frida/ieee754": "^2.0.2", 756 | "@frida/net": "^4.0.0", 757 | "@frida/os": "^1.0.0", 758 | "@frida/path": "^2.0.2", 759 | "@frida/process": "^1.2.0", 760 | "@frida/punycode": "^3.0.0", 761 | "@frida/querystring": "^1.0.0", 762 | "@frida/readable-stream": "^4.1.3", 763 | "@frida/reserved-words": "^1.0.0", 764 | "@frida/stream": "^1.0.1", 765 | "@frida/string_decoder": "^2.0.0", 766 | "@frida/timers": "^3.0.0", 767 | "@frida/tty": "^1.0.0", 768 | "@frida/url": "^1.0.1", 769 | "@frida/util": "^1.0.3", 770 | "@frida/vm": "^2.0.0", 771 | "commander": "^9.3.0", 772 | "frida-fs": "^5.2.0", 773 | "terser": "^5.14.1", 774 | "typed-emitter": "^2.1.0" 775 | } 776 | }, 777 | "frida-fs": { 778 | "version": "5.2.2", 779 | "resolved": "https://registry.npmjs.org/frida-fs/-/frida-fs-5.2.2.tgz", 780 | "integrity": "sha512-AMOuNtYC76z/b13PnRMyWC+JPHr134SU+erBsZLyCAFaRGOkaDVRQLT2vfXcXqJTLdoIdiUeIUREF78Js18NLA==", 781 | "dev": true 782 | }, 783 | "http-parser-js": { 784 | "version": "0.5.8", 785 | "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", 786 | "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", 787 | "dev": true 788 | }, 789 | "ieee754": { 790 | "version": "1.2.1", 791 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 792 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", 793 | "dev": true 794 | }, 795 | "source-map": { 796 | "version": "0.6.1", 797 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 798 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 799 | "dev": true 800 | }, 801 | "source-map-support": { 802 | "version": "0.5.21", 803 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", 804 | "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", 805 | "dev": true, 806 | "requires": { 807 | "buffer-from": "^1.0.0", 808 | "source-map": "^0.6.0" 809 | } 810 | }, 811 | "terser": { 812 | "version": "5.16.8", 813 | "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.8.tgz", 814 | "integrity": "sha512-QI5g1E/ef7d+PsDifb+a6nnVgC4F22Bg6T0xrBrz6iloVB4PUkkunp6V8nzoOOZJIzjWVdAGqCdlKlhLq/TbIA==", 815 | "dev": true, 816 | "requires": { 817 | "@jridgewell/source-map": "^0.3.2", 818 | "acorn": "^8.5.0", 819 | "commander": "^2.20.0", 820 | "source-map-support": "~0.5.20" 821 | }, 822 | "dependencies": { 823 | "commander": { 824 | "version": "2.20.3", 825 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 826 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", 827 | "dev": true 828 | } 829 | } 830 | }, 831 | "typed-emitter": { 832 | "version": "2.1.0", 833 | "resolved": "https://registry.npmjs.org/typed-emitter/-/typed-emitter-2.1.0.tgz", 834 | "integrity": "sha512-g/KzbYKbH5C2vPkaXGu8DJlHrGKHLsM25Zg9WuC9pMGfuvT+X25tZQWo5fK1BjBm8+UrVE9LDCvaY0CQk+fXDA==", 835 | "dev": true, 836 | "requires": { 837 | "rxjs": "*" 838 | } 839 | } 840 | } 841 | } 842 | -------------------------------------------------------------------------------- /agent/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vscode-frida-agent", 3 | "version": "1.0.0", 4 | "description": "", 5 | "private": true, 6 | "main": "src/index.ts", 7 | "scripts": { 8 | "prepare": "npm run build", 9 | "build": "frida-compile src/index.ts -o _agent.js", 10 | "watch": "npm run build -- -w" 11 | }, 12 | "devDependencies": { 13 | "@types/frida-gum": "^18.1.0", 14 | "@types/node": "^18.6.4", 15 | "frida-compile": "^15.1.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /agent/src/index.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable curly */ 2 | /* eslint-disable @typescript-eslint/naming-convention */ 3 | 4 | import { start, stop } from './log.js'; 5 | 6 | enum Runtime { 7 | Java = 'Java', 8 | ObjectiveC = 'ObjectiveC', 9 | Generic = 'Generic', 10 | }; 11 | 12 | rpc.exports = { 13 | start, 14 | stop, 15 | 16 | runtime() { 17 | if (Java.available) return Runtime.Java; 18 | if (ObjC.available) return Runtime.ObjectiveC; 19 | return Runtime.Generic; 20 | }, 21 | 22 | ping: () => Process.id, 23 | 24 | classes: async () => [] as string[], 25 | protocols: async () => [] as string[], 26 | 27 | methodsOf: async (_name: string) => [] as string[], 28 | ownMethodsOf: async (_name: string) => [] as string[], 29 | superClass: async (_name: string) => { throw new Error('Not implemented'); }, 30 | 31 | modules: () => Process.enumerateModules(), 32 | exports: (name: string) => Process.findModuleByName(name)?.enumerateExports(), 33 | imports: (name: string) => Process.findModuleByName(name)?.enumerateImports(), 34 | symbols: (name: string) => Process.findModuleByName(name)?.enumerateSymbols(), 35 | }; 36 | 37 | if (Java.available) { 38 | function perform(fn: () => T): Promise { 39 | return new Promise((resolve) => { 40 | Java.perform(() => { 41 | resolve(fn()); 42 | }); 43 | }); 44 | }; 45 | 46 | rpc.exports.classes = async () => perform(() => Java.enumerateLoadedClassesSync()); 47 | rpc.exports.ownMethodsOf = 48 | rpc.exports.methodsOf = 49 | async (name: string) => perform(() => Java.use(name).class.getMethods()); 50 | rpc.exports.fieldsOf = async (name: string) => perform(() => Java.use(name).class.getDeclaredFields()); 51 | rpc.exports.superClass = async (name: string) => perform(() => Java.use(name).class.getSuperclass()?.getName()); 52 | 53 | } else if (ObjC.available) { 54 | rpc.exports.classes = async () => Object.keys(ObjC.classes); 55 | rpc.exports.protocols = async () => Object.keys(ObjC.protocols); 56 | 57 | function getClass(name: string) { 58 | if (ObjC.classes.hasOwnProperty(name)) return ObjC.classes[name]; 59 | throw new Error(`Class ${name} not found`); 60 | } 61 | 62 | rpc.exports.ownMethodsOf = async (name: string) => getClass(name).$ownMethods; 63 | rpc.exports.methodsOf = async (name: string) => getClass(name).$methods; 64 | rpc.exports.fieldsOf = async (name: string) => getClass(name).$ivars; 65 | rpc.exports.superClass = async (name: string) => getClass(name).$superClass?.name; 66 | } 67 | -------------------------------------------------------------------------------- /agent/src/log.ts: -------------------------------------------------------------------------------- 1 | // sys/fcntl.h 2 | const F_SETFL = 4; 3 | const O_NONBLOCK = 0x0004; 4 | 5 | const stderr = 2; 6 | const SIZEOF_INT = 4; // for mac & iOS 7 | 8 | const subject = 'syslog'; 9 | const fildes: NativePointer = Memory.alloc(SIZEOF_INT * 2); 10 | 11 | let stream: UnixInputStream; 12 | 13 | export function start() { 14 | stop(); 15 | 16 | const resolve = (name: string): NativePointer => { 17 | const p = Module.findExportByName(null, name); 18 | if (!p) { 19 | throw new Error(`Platform not supported. Is your target on ${name}?`); 20 | } 21 | return p; 22 | } 23 | 24 | const pipe = new NativeFunction(resolve('pipe'), 'int', ['pointer']); 25 | const dup2 = new NativeFunction(resolve('dup2'), 'int', ['int', 'int']); 26 | const close = new NativeFunction(resolve('close'), 'int', ['int']); 27 | const fcntl = new NativeFunction(resolve('fcntl'), 'int', ['int', 'int', 'int']); 28 | 29 | pipe(fildes); 30 | 31 | const input = fildes.readInt(); 32 | const output = fildes.add(SIZEOF_INT).readInt(); 33 | 34 | dup2(output, stderr); 35 | close(output); 36 | fcntl(input, F_SETFL, O_NONBLOCK); 37 | 38 | stream = new UnixInputStream(input); 39 | 40 | function read() { 41 | stream.read(4096).then((buf) => { 42 | if (buf.byteLength) { 43 | send({ subject }, buf); 44 | } 45 | 46 | setImmediate(read); 47 | }); 48 | } 49 | 50 | setImmediate(read); 51 | } 52 | 53 | export function stop() { 54 | if (stream) { 55 | stream.close(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /agent/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "experimentalDecorators": true, 4 | "target": "es2020", 5 | "lib": ["es2020"], 6 | "allowJs": true, 7 | "noEmit": true, 8 | "strict": true, 9 | "esModuleInterop": true, 10 | }, 11 | "exclude": ["node_modules", "dist", "_agent.js"] 12 | } -------------------------------------------------------------------------------- /backend/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /backend/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChiChou/vscode-frida/8f69a68e1c1e0955ef8be77a1246ea6ee9dc686f/backend/__init__.py -------------------------------------------------------------------------------- /backend/android/get-frida.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import logging 4 | import urllib.request 5 | import json 6 | import lzma 7 | import shutil 8 | from pathlib import Path 9 | 10 | MAPPING = { 11 | 'x86': 'x86', 12 | 'x86_64': 'x86_64', 13 | 'arm64-v8a': 'arm64', 14 | 'armeabi-v7a': 'arm', 15 | } 16 | 17 | RELEASE_URL = 'https://api.github.com/repos/frida/frida/releases/latest' 18 | 19 | 20 | def download(abi: str, path: Path): 21 | try: 22 | arch = MAPPING[abi] 23 | except KeyError: 24 | raise RuntimeError('Unknown ABI: %s' % abi) 25 | 26 | suffix = '-android-%s.xz' % arch 27 | with urllib.request.urlopen(RELEASE_URL) as response: 28 | info = json.loads(response.read()) 29 | 30 | for asset in info['assets']: 31 | name = asset['name'] 32 | logging.debug('asset: %s', name) 33 | if name.startswith('frida-server') and name.endswith(suffix): 34 | url = asset['browser_download_url'] 35 | break 36 | else: 37 | raise RuntimeError('Unable to find frida-server for %s' % arch) 38 | 39 | logging.debug('downloading %s to %s', url, path) 40 | with urllib.request.urlopen(url) as response: 41 | with lzma.LZMAFile(response) as archive: 42 | with path.open('wb') as fp: 43 | shutil.copyfileobj(archive, fp) 44 | 45 | 46 | if __name__ == "__main__": 47 | import sys 48 | 49 | if len(sys.argv) != 3: 50 | print('usage: %s ' % sys.argv[0]) 51 | sys.exit(1) 52 | 53 | abi = sys.argv[1] 54 | path = sys.argv[2] 55 | 56 | download(abi, Path(path)) 57 | -------------------------------------------------------------------------------- /backend/core.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | import time 3 | import tempfile 4 | import base64 5 | 6 | try: 7 | import frida 8 | except ImportError: 9 | import sys 10 | sys.stderr.write( 11 | 'Unable to import frida. Please ensure you have installed frida-tools via pip\n') 12 | sys.exit(-1) 13 | 14 | 15 | def devices() -> list: 16 | props = ['id', 'name', 'type'] 17 | 18 | def wrap(dev: frida.core.Device): 19 | obj = {prop: getattr(dev, prop) for prop in props} 20 | try: 21 | obj['os'] = dev.query_system_parameters()['os']['id'] 22 | except (frida.ServerNotRunningError, KeyError, frida.TransportError): 23 | obj['os'] = 'unknown' 24 | return obj 25 | 26 | # workaround 27 | try: 28 | frida.get_usb_device(1) 29 | except: 30 | pass 31 | 32 | return [wrap(dev) for dev in frida.enumerate_devices()] 33 | 34 | 35 | def get_device(device_id: str) -> frida.core.Device: 36 | if device_id == 'usb': 37 | return frida.get_usb_device(1) 38 | elif device_id == 'local': 39 | return frida.get_local_device() 40 | else: 41 | return frida.get_device(device_id, timeout=1) 42 | 43 | 44 | def info_wrap(props, fmt): 45 | def wrap(target): 46 | obj = {prop: getattr(target, prop) for prop in props} 47 | 48 | icons = target.parameters.get('icons', []) 49 | try: 50 | icon = next(icon for icon in icons if icon.get('format') == 'png') 51 | data = icon['image'] 52 | obj['icon'] = 'data:image/png;base64,' + \ 53 | base64.b64encode(data).decode('ascii') 54 | except StopIteration: 55 | pass 56 | 57 | return obj 58 | 59 | return wrap 60 | 61 | 62 | def apps(device: frida.core.Device) -> list: 63 | props = ['identifier', 'name', 'pid'] 64 | 65 | def fmt(app): 66 | return '%s-%s' % (device.id, app.pid or app.identifier) 67 | wrap = info_wrap(props, fmt) 68 | try: 69 | apps = device.enumerate_applications(scope='full') 70 | except frida.TransportError: 71 | apps = device.enumerate_applications() 72 | return [wrap(app) for app in apps] 73 | 74 | 75 | def ps(device: frida.core.Device) -> list: 76 | props = ['name', 'pid'] 77 | 78 | def fmt(p): 79 | return '%s-%s' % (device.id, p.name or p.pid) 80 | wrap = info_wrap(props, fmt) 81 | 82 | try: 83 | ps = device.enumerate_processes(scope='full') 84 | except frida.TransportError: 85 | ps = device.enumerate_processes() 86 | return [wrap(p) for p in ps] 87 | 88 | 89 | def device_info(device: frida.core.Device) -> dict: 90 | return device.query_system_parameters() 91 | 92 | 93 | def find_app(device: frida.core.Device, bundle: str): 94 | try: 95 | app = next(app for app in device.enumerate_applications() 96 | if app.identifier == bundle) 97 | except StopIteration: 98 | raise ValueError('app "%s" not found' % bundle) 99 | 100 | return app 101 | 102 | 103 | def spawn_or_attach(device: frida.core.Device, bundle: str) -> frida.core.Session: 104 | app = find_app(device, bundle) 105 | 106 | if app.pid > 0: 107 | frontmost = device.get_frontmost_application() 108 | if frontmost and frontmost.identifier == bundle: 109 | return device.attach(app.pid) 110 | 111 | device.kill(app.pid) 112 | 113 | pid = device.spawn(bundle) 114 | session = device.attach(pid) 115 | device.resume(pid) 116 | return session 117 | 118 | 119 | def read_agent(): 120 | agent_path = Path(__file__).parent.parent / 'agent' / '_agent.js' 121 | with agent_path.open('r', encoding='utf8', newline='\n') as fp: 122 | return fp.read() 123 | -------------------------------------------------------------------------------- /backend/driver.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import os 5 | from pathlib import Path 6 | 7 | sys.path.insert(0, str(Path(__file__).parent.parent)) 8 | 9 | 10 | def main(args): 11 | from backend import core, rpc, syslog 12 | 13 | if args.remote: 14 | import frida 15 | remote_list = args.remote.split(',') 16 | mgr = frida.get_device_manager() 17 | for host in remote_list: 18 | mgr.add_remote_device(host) 19 | 20 | if args.action == 'devices': 21 | return core.devices() 22 | 23 | if not args.device: 24 | raise RuntimeError('NOTREACHED') 25 | 26 | device = core.get_device(args.device) 27 | if args.action == 'ps': 28 | return core.ps(device) 29 | 30 | if args.action == 'apps': 31 | return core.apps(device) 32 | 33 | if args.action == 'info': 34 | return core.device_info(device) 35 | 36 | if args.action == 'location': 37 | for app in device.enumerate_applications(identifiers=[args.bundle], scope='metadata'): 38 | return app.parameters.get('path') 39 | raise RuntimeError('app with bundle %s does not exist' % args.bundle) 40 | 41 | if args.action == 'syslog2': 42 | return syslog.start(device, pid=args.pid, bundle=args.app) 43 | 44 | target = args.pid or args.name 45 | agent = rpc.ProcessAgent(device, target) if target else \ 46 | rpc.AppAgent(device, args.app) 47 | agent.load() 48 | 49 | if args.action == 'rpc': 50 | return agent.invoke(args.method, *args.args) 51 | 52 | if args.action == 'syslog': 53 | syslog.pipe(agent) 54 | return 55 | 56 | 57 | if __name__ == '__main__': 58 | import argparse 59 | 60 | requires_device = argparse.ArgumentParser(add_help=False) 61 | requires_device.add_argument('device') 62 | 63 | requires_path = argparse.ArgumentParser(add_help=False) 64 | requires_path.add_argument('path') 65 | 66 | requires_app = argparse.ArgumentParser(add_help=False) 67 | requires_app.add_argument('--device', required=True) 68 | group = requires_app.add_mutually_exclusive_group() 69 | group.add_argument('--app') 70 | group.add_argument('--pid', type=int) 71 | group.add_argument('--name') 72 | group.required = True 73 | 74 | parser = argparse.ArgumentParser(description='frida driver') 75 | parser.add_argument('--remote', type=str) 76 | subparsers = parser.add_subparsers(dest='action', required=True) 77 | subparsers.add_parser('devices') 78 | subparsers.add_parser('apps', parents=[requires_device]) 79 | subparsers.add_parser('ps', parents=[requires_device]) 80 | subparsers.add_parser('info', parents=[requires_device]) 81 | subparsers.add_parser('type', parents=[requires_device]) 82 | subparsers.add_parser('ssh-copy-id', parents=[requires_device]) 83 | subparsers.add_parser('sign-debugserver', parents=[requires_device]) 84 | location_parser = subparsers.add_parser( 85 | 'location', parents=[requires_device]) 86 | location_parser.add_argument('bundle') 87 | 88 | rpc_parser = subparsers.add_parser('rpc', parents=[requires_app]) 89 | rpc_parser.add_argument('method') 90 | rpc_parser.add_argument('args', metavar='N', nargs='*', default=[]) 91 | 92 | subparsers.add_parser('syslog', parents=[requires_app]) 93 | subparsers.add_parser('syslog2', parents=[requires_app]) 94 | 95 | args = parser.parse_args() 96 | 97 | if 'DEBUG' in os.environ or args.action == 'syslog2': 98 | result = main(args) 99 | else: 100 | try: 101 | result = main(args) 102 | except Exception as e: 103 | sys.stderr.write('%s\n' % e) 104 | sys.exit(-1) 105 | 106 | import json 107 | if args.action not in ['syslog', 'download', 'upload']: 108 | print(json.dumps(result)) 109 | -------------------------------------------------------------------------------- /backend/fruit/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChiChou/vscode-frida/8f69a68e1c1e0955ef8be77a1246ea6ee9dc686f/backend/fruit/__init__.py -------------------------------------------------------------------------------- /backend/pause.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import sys 3 | 4 | 5 | if len(sys.argv) > 1: 6 | try: 7 | subprocess.check_call(sys.argv[1:]) 8 | except (subprocess.SubprocessError, FileNotFoundError) as e: 9 | sys.stdout.write(repr(e) + '\n') 10 | finally: 11 | input('Press Enter to continue...') 12 | -------------------------------------------------------------------------------- /backend/rpc.py: -------------------------------------------------------------------------------- 1 | import frida 2 | import sys 3 | import backend.core 4 | from typing import List, Union 5 | 6 | 7 | class BaseAgent(object): 8 | def __init__(self, device: frida.core.Device): 9 | self.device = device 10 | self.session = None # type: frida.core.Session 11 | self.script = None # type: frida.core.Script 12 | 13 | def invoke(self, method: str, *args: List[str]): 14 | if not self.session: 15 | raise RuntimeError('invalid state') 16 | 17 | if not self.script: 18 | self.load() 19 | 20 | invocation = getattr(self.script.exports, method) 21 | return invocation(*args) 22 | 23 | def load(self): 24 | if not self.session: 25 | raise RuntimeError('invalid state') 26 | 27 | session = self.session 28 | 29 | def on_detach(reason): 30 | if reason == 'application-requested': 31 | sys.exit(0) 32 | sys.stderr.write('[FATAL Error] target disconnected\n') 33 | sys.exit(-1) 34 | session.on('detached', on_detach) 35 | 36 | source = backend.core.read_agent() 37 | script = session.create_script(source) 38 | script.load() 39 | self.script = script 40 | 41 | def unload(self): 42 | if self.session: 43 | self.session.detach() 44 | 45 | 46 | class AppAgent(BaseAgent): 47 | def __init__(self, device: frida.core.Device, bundle: str): 48 | super().__init__(device) 49 | self.session = backend.core.spawn_or_attach(self.device, bundle) 50 | 51 | 52 | class ProcessAgent(BaseAgent): 53 | def __init__(self, device, target: Union[int, str]): 54 | super().__init__(device) 55 | self.session = self.device.attach(target) 56 | -------------------------------------------------------------------------------- /backend/syslog.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from typing import Callable 4 | from enum import Enum 5 | 6 | import frida 7 | import sys 8 | 9 | from backend.rpc import BaseAgent 10 | 11 | 12 | def pipe(agent: BaseAgent): 13 | agent.invoke('start') 14 | 15 | def on_message(message, data): 16 | sys.stdout.buffer.write(data) 17 | sys.stdout.flush() 18 | 19 | agent.script.on('message', on_message) 20 | 21 | try: 22 | sys.stdin.read() 23 | except KeyboardInterrupt: 24 | pass 25 | finally: 26 | agent.invoke('stop') 27 | agent.unload() 28 | 29 | 30 | class SyslogService(object): 31 | def __init__(self, device: frida.core.Device): 32 | self.dev = device 33 | self.pipe = None 34 | 35 | def __enter__(self): 36 | self.pipe = self.dev.open_channel( 37 | 'lockdown:com.apple.syslog_relay') 38 | return self 39 | 40 | def __exit__(self, exc_type, exc_value, traceback): 41 | self.pipe.close() 42 | 43 | def close(self): 44 | self.pipe.close() 45 | 46 | 47 | CHUNK_SIZE = 4096 48 | TIME_FORMAT = '%H:%M:%S' 49 | SYSLOG_LINE_SPLITTER = b'\n\x00' 50 | 51 | 52 | class Level(Enum): 53 | Unknown = 0 54 | Notice = 1 55 | Warning = 2 56 | Error = 3 57 | 58 | 59 | def parse_level(level_text: str) -> Level: 60 | if level_text == b'': 61 | return Level.Notice 62 | 63 | if level_text == b'': 64 | return Level.Warning 65 | 66 | if level_text == b'': 67 | return Level.Error 68 | 69 | return Level.Unknown 70 | 71 | 72 | def parse_line(line: bytes): 73 | pid_start = line.find(b'[') 74 | pid_end = line.find(b']', pid_start + 1) 75 | 76 | assert pid_start > -1 and pid_end > -1 77 | pid = int(line[pid_start + 1:pid_end], 10) 78 | 79 | colon_index = line.find(b':', pid_end + 1) 80 | level_text = line[pid_end + 2:colon_index] 81 | 82 | name_start = line.rfind(b' ', 0, pid_start) + 1 83 | name_and_module = line[name_start:pid_start] 84 | 85 | device_name_end = line.rfind(b' ', 0, name_start) 86 | device_name_start = line.rfind(b' ', 0, device_name_end) 87 | ts = line[0:device_name_start] 88 | 89 | if b'(' in name_and_module: 90 | name_end = name_and_module.find(b'(') 91 | name = name_and_module[0:name_end] 92 | module_end = name_and_module.find(b')') 93 | module = name_and_module[name_end + 1:module_end] 94 | else: 95 | name, module = name_and_module, None 96 | 97 | body = line[colon_index + 2:] 98 | return ts, pid, level_text, name, module, body 99 | 100 | 101 | class bcolors: 102 | HEADER = b'\033[95m' 103 | OKBLUE = b'\033[94m' 104 | OKCYAN = b'\033[96m' 105 | OKGREEN = b'\033[92m' 106 | WARNING = b'\033[93m' 107 | FAIL = b'\033[91m' 108 | ENDC = b'\033[0m' 109 | BOLD = b'\033[1m' 110 | UNDERLINE = b'\033[4m' 111 | 112 | 113 | def color(level: Level): 114 | mapping = { 115 | # Level.Unknown: b'', 116 | Level.Notice: bcolors.OKGREEN, 117 | Level.Warning: bcolors.WARNING, 118 | Level.Error: bcolors.FAIL 119 | } 120 | 121 | return mapping.get(level, b'') 122 | 123 | 124 | def get_proc_name(device: frida.core.Device, bundle: str): 125 | try: 126 | info = next(app for app in device.enumerate_applications( 127 | scope='metadata') if app.identifier == bundle) 128 | except StopIteration: 129 | raise RuntimeError('app with bundle %s does not exist' % bundle) 130 | 131 | if info.pid == 0: 132 | device.resume(device.spawn(bundle)) 133 | 134 | return info.parameters.get('path') 135 | 136 | 137 | def stream(device: frida.core.Device, filter_cb: Callable[[int, str], bool]): 138 | with SyslogService(device) as channel: 139 | buf = b'' 140 | while True: 141 | chunk: bytes = channel.pipe.read_all(CHUNK_SIZE) 142 | buf += chunk 143 | 144 | # SYSLOG_LINE_SPLITTER is used to split each syslog line 145 | if SYSLOG_LINE_SPLITTER not in buf: 146 | continue 147 | 148 | lines = buf.split(SYSLOG_LINE_SPLITTER) 149 | 150 | # handle partial last lines 151 | if not buf.endswith(SYSLOG_LINE_SPLITTER): 152 | buf = lines[-1] 153 | lines = lines[:-1] 154 | 155 | for line in lines: 156 | if len(line) == 0: 157 | continue 158 | 159 | ts, pid, level_text, name, module, body = parse_line(line) 160 | if not filter_cb(pid, name): 161 | continue 162 | 163 | # timestamp 164 | yield ts 165 | yield b' ' 166 | 167 | # process 168 | yield bcolors.OKCYAN 169 | yield name 170 | yield bcolors.ENDC 171 | 172 | # module 173 | yield bcolors.OKBLUE 174 | if module: 175 | yield b'(' 176 | yield module 177 | yield b')' 178 | 179 | yield b'[%d]' % pid 180 | yield bcolors.ENDC 181 | yield b' ' 182 | 183 | # level 184 | level = parse_level(level_text) 185 | yield color(level) 186 | yield level_text 187 | yield bcolors.ENDC 188 | yield b': ' 189 | 190 | # body 191 | yield body 192 | yield b'\n' 193 | 194 | 195 | def start(device: frida.core.Device, pid: int = None, bundle: str = None): 196 | if type(pid) is int and pid > 0: 197 | def filter_cb(target_pid, target_name): 198 | return pid == target_pid 199 | 200 | elif type(bundle) is str: 201 | name = get_proc_name(device, bundle).encode('utf-8') 202 | 203 | def filter_cb(target_pid, target_name): 204 | return name == target_name 205 | 206 | else: 207 | raise ValueError( 208 | f'invalid parameter combination: pid={pid} bundle={bundle}') 209 | 210 | try: 211 | for buf in stream(device, filter_cb): 212 | sys.stdout.buffer.write(buf) 213 | if buf == b'\n': 214 | sys.stdout.flush() 215 | 216 | except KeyboardInterrupt: 217 | pass 218 | -------------------------------------------------------------------------------- /backend/test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChiChou/vscode-frida/8f69a68e1c1e0955ef8be77a1246ea6ee9dc686f/backend/test/__init__.py -------------------------------------------------------------------------------- /backend/test/test_device.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import frida 3 | 4 | from backend import core 5 | 6 | 7 | class TestDeviceOperations(unittest.TestCase): 8 | def setUp(self): 9 | self.local = frida.get_local_device() 10 | self.usb = frida.get_usb_device() 11 | 12 | def test_info(self): 13 | usb = self.usb 14 | local = self.local 15 | 16 | self.assertIsInstance(core.devices(), list) 17 | self.assertIsInstance(core.ps(local), list) 18 | self.assertIsInstance(core.apps(usb), list) 19 | 20 | def test_attach(self): 21 | usb = self.usb 22 | local = self.local 23 | 24 | # make safari a background app 25 | usb.resume(usb.spawn('com.apple.mobilesafari')) 26 | usb.resume(usb.spawn('com.apple.Preferences')) 27 | 28 | with self.assertRaises(RuntimeError): 29 | core.spawn_or_attach(usb, 'com.apple.mobilesafari') 30 | session = core.spawn_or_attach(usb, 'com.apple.Preferences') 31 | self.assertIsInstance(session, frida.core.Session) 32 | 33 | usb.kill('Settings') 34 | usb.kill('Safari') 35 | 36 | session.detach() 37 | 38 | 39 | if __name__ == '__main__': 40 | unittest.main() 41 | -------------------------------------------------------------------------------- /backend/venv.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | print('true' if sys.prefix == sys.base_prefix else 'false') 4 | -------------------------------------------------------------------------------- /i10n/bundle.l10n.zh-cn.json: -------------------------------------------------------------------------------- 1 | { 2 | "This command is only expected to be used in the context menu": "此命令仅在上下文菜单中使用", 3 | "This command only supports iOS or Android": "此命令仅支持 iOS 或 Android", 4 | "Select destination folder": "选择目标文件夹", 5 | "frida-server not found on device, downloading...": "设备上未找到 frida-server,正在下载...", 6 | "Downloading frida-server": "正在下载 frida-server", 7 | "Done": "完成", 8 | "Open": "打开", 9 | "Dismiss": "关闭", 10 | "frida-server deployed to {0} successfully": "frida-server 已成功部署到 {0}", 11 | "sanity check: frida-server version on device {0}": "完整性检查:设备上的 frida-server 版本 {0}", 12 | "failed to dump application:\n{0}": "应用导出失败:\n{0}", 13 | "Successfully pulled package {0}": "成功导出软件包 {0}", 14 | "Failed to get package path: {0}": "获取软件包路径失败:{0}", 15 | "Unsupported device type": "不支持的设备类型", 16 | "Unsupported item type": "不支持的项目类型", 17 | "Target is not running": "目标未运行", 18 | "App \"{0}\" must be running before attaching to it": "必须先运行应用 \"{0}\" 才能附加到它", 19 | "Host or IP of the remote device": "远程设备的主机名或IP地址", 20 | "Frida - {0}": "Frida - {0}", 21 | "Install typescript typings": "安装 TypeScript 类型提示", 22 | "@types/frida-gum has been successfully installed": "@types/frida-gum 已成功安装", 23 | "Downloading typing info for frida-gum": "正在下载 frida-gum 的类型提示", 24 | "Failed to download typing info for frida-gum: {0}": "下载 frida-gum 的类型提示失败:{0}", 25 | "The command requires a workspace or an active document": "此命令需要工作区或活动文档", 26 | "This document is not Javascript or TypeScript": "此文档不是 JavaScript 或 TypeScript", 27 | "The current document is unsaved": "当前文档未保存", 28 | "This command is not applicable to the selected item": "此命令不适用于所选项目", 29 | "This command is not applicable to the local device": "此命令不适用于本地设备", 30 | "Warning: failed to launch App {0}\n{1}": "警告:启动应用 {0} 失败\n{1}", 31 | "You must select a folder to create the project": "您必须选择一个文件夹来创建项目", 32 | "This command only works in a workspace. Please open a workspace first": "此命令仅在工作区中有效。请先打开一个工作区", 33 | "{0} already exists. Do you want to overwrite it?": "{0} 已存在。您要覆盖它吗?", 34 | "Yes": "是", 35 | "No": "否", 36 | "Debug Command": "调试命令", 37 | "Debug configuration created. You can now start debugging by pressing F5": "调试配置已创建。您现在可以按 F5 开始调试", 38 | "inetcat not found": "未找到 inetcat", 39 | "inetcat exited with code {0}": "inetcat 以代码 {0} 退出", 40 | "Invalid protocol": "无效的协议", 41 | "No valid SSH port found": "未找到有效的 SSH 端口", 42 | "SSH: {0}": "安全外壳协议:{0}", 43 | "This feature is not enabled on Windows due to lack of inetcat": "由于缺少 inetcat,此功能在 Windows 上未启用", 44 | "inetcat command not present in $PATH": "inetcat 命令不在 $PATH 中", 45 | "OS type {0} is not supported": "操作系统类型 {0} 不受支持", 46 | "Could not find command {0} in $PATH, have you installed it or activated the correct venv?": "在 $PATH 中找不到命令 {0},您是否安装了它或激活了正确的虚拟环境?", 47 | "Frida Extension": "Frida 扩展", 48 | "Python extension not activated": "Python 扩展未激活", 49 | "Python extension not found": "未找到 Python 扩展", 50 | "Could not find command adb in $PATH": "在 $PATH 中找不到命令 adb", 51 | "Create Here": "在此处创建", 52 | "Create Project": "创建项目", 53 | "Close Console": "关闭控制台", 54 | "Ignore": "忽略", 55 | "Console {0} is already open, do you want to close it?": "控制台 {0} 已经打开,您要关闭它吗?", 56 | "Invalid target {0}": "无效的目标 {0}", 57 | "Output: {0} ({1})": "输出:{0} ({1})", 58 | "{0} ({1}) is not running": "{0} ({1}) 未运行", 59 | "Not Running": "未运行" 60 | } -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChiChou/vscode-frida/8f69a68e1c1e0955ef8be77a1246ea6ee9dc686f/icon.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vscode-frida", 3 | "icon": "icon.png", 4 | "publisher": "codecolorist", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/ChiChou/vscode-frida" 8 | }, 9 | "displayName": "frida Workbench", 10 | "description": "%extension.description%", 11 | "version": "0.10.3", 12 | "engines": { 13 | "vscode": "^1.94.0" 14 | }, 15 | "categories": [ 16 | "Other" 17 | ], 18 | "activationEvents": [ 19 | "onLanguage:javascript" 20 | ], 21 | "main": "./out/extension.js", 22 | "l10n": "./l10n", 23 | "contributes": { 24 | "configuration": { 25 | "title": "Frida", 26 | "properties": { 27 | "frida.decryptOutput": { 28 | "type": [ 29 | "string", 30 | "null" 31 | ], 32 | "default": null, 33 | "description": "%frida.decryptOutput%" 34 | }, 35 | "frida.androidServerPath": { 36 | "type": [ 37 | "string", 38 | "null" 39 | ], 40 | "default": "/data/local/tmp/frida-server", 41 | "description": "%frida.androidServerPath%" 42 | } 43 | } 44 | }, 45 | "commands": [ 46 | { 47 | "command": "frida.ps.refresh", 48 | "title": "%frida.ps.refresh%", 49 | "icon": { 50 | "light": "resources/light/refresh.svg", 51 | "dark": "resources/dark/refresh.svg" 52 | }, 53 | "category": "Frida" 54 | }, 55 | { 56 | "command": "frida.apps.refresh", 57 | "title": "%frida.apps.refresh%", 58 | "icon": { 59 | "light": "resources/light/refresh.svg", 60 | "dark": "resources/dark/refresh.svg" 61 | }, 62 | "category": "Frida" 63 | }, 64 | { 65 | "command": "frida.remote.add", 66 | "title": "%frida.remote.add%", 67 | "icon": { 68 | "light": "resources/light/add.svg", 69 | "dark": "resources/dark/add.svg" 70 | }, 71 | "category": "Frida" 72 | }, 73 | { 74 | "command": "frida.remote.remove", 75 | "title": "%frida.remote.remove%", 76 | "icon": { 77 | "light": "resources/light/del.svg", 78 | "dark": "resources/dark/del.svg" 79 | }, 80 | "category": "Frida" 81 | }, 82 | { 83 | "command": "frida.attach", 84 | "title": "%frida.attach%", 85 | "icon": { 86 | "light": "resources/light/attach.svg", 87 | "dark": "resources/dark/attach.svg" 88 | }, 89 | "category": "Frida" 90 | }, 91 | { 92 | "command": "frida.device.copy", 93 | "title": "%frida.device.copy%" 94 | }, 95 | { 96 | "command": "frida.bundle.copy", 97 | "title": "%frida.bundle.copy%" 98 | }, 99 | { 100 | "command": "frida.name.copy", 101 | "title": "%frida.name.copy%" 102 | }, 103 | { 104 | "command": "frida.pid.copy", 105 | "title": "%frida.pid.copy%" 106 | }, 107 | { 108 | "command": "frida.spawn", 109 | "title": "%frida.spawn%", 110 | "icon": { 111 | "light": "resources/light/statusRun.svg", 112 | "dark": "resources/dark/statusRun.svg" 113 | }, 114 | "category": "Frida" 115 | }, 116 | { 117 | "command": "frida.spawn.suspended", 118 | "title": "%frida.spawn.suspended%", 119 | "category": "Frida" 120 | }, 121 | { 122 | "command": "frida.kill", 123 | "title": "%frida.kill%", 124 | "category": "Frida" 125 | }, 126 | { 127 | "command": "frida.debug.setup", 128 | "title": "%frida.debug.setup%", 129 | "category": "Frida" 130 | }, 131 | { 132 | "command": "frida.syslog", 133 | "title": "%frida.syslog%", 134 | "category": "Frida" 135 | }, 136 | { 137 | "command": "frida.syslog.vacuum", 138 | "title": "%frida.syslog.vacuum%", 139 | "category": "Frida" 140 | }, 141 | { 142 | "command": "frida.print.modules", 143 | "title": "%frida.print.modules%", 144 | "category": "Frida" 145 | }, 146 | { 147 | "command": "frida.print.classes", 148 | "title": "%frida.print.classes%", 149 | "category": "Frida" 150 | }, 151 | { 152 | "command": "frida.external.objection", 153 | "title": "%frida.external.objection%", 154 | "category": "Frida" 155 | }, 156 | { 157 | "command": "frida.external.apk", 158 | "title": "%frida.external.apk%", 159 | "category": "Frida" 160 | }, 161 | { 162 | "command": "frida.external.dump", 163 | "title": "%frida.external.dump%", 164 | "category": "Frida" 165 | }, 166 | { 167 | "command": "frida.device.androidserver", 168 | "title": "%frida.device.androidserver%", 169 | "category": "Android" 170 | }, 171 | { 172 | "command": "frida.external.shell", 173 | "title": "%frida.external.shell%", 174 | "category": "SSH over USB" 175 | }, 176 | { 177 | "command": "frida.boilerplate.agent", 178 | "title": "%frida.boilerplate.agent%", 179 | "category": "Frida Boilerplate" 180 | }, 181 | { 182 | "command": "frida.boilerplate.module", 183 | "title": "%frida.boilerplate.module%", 184 | "category": "Frida Boilerplate" 185 | }, 186 | { 187 | "command": "frida.typing.init", 188 | "title": "%frida.typing.init%", 189 | "category": "Frida" 190 | } 191 | ], 192 | "viewsContainers": { 193 | "activitybar": [ 194 | { 195 | "id": "frida-sidebar", 196 | "title": "%panel.title%", 197 | "icon": "resources/icon.svg" 198 | } 199 | ] 200 | }, 201 | "views": { 202 | "frida-sidebar": [ 203 | { 204 | "id": "fridaApps", 205 | "name": "%frida.apps%" 206 | }, 207 | { 208 | "id": "fridaPs", 209 | "name": "%frida.ps%" 210 | } 211 | ] 212 | }, 213 | "menus": { 214 | "commandPalette": [ 215 | { 216 | "command": "frida.boilerplate.agent" 217 | }, 218 | { 219 | "command": "frida.boilerplate.module" 220 | }, 221 | { 222 | "command": "frida.ps.refresh" 223 | }, 224 | { 225 | "command": "frida.apps.refresh" 226 | }, 227 | { 228 | "command": "frida.remote.add" 229 | }, 230 | { 231 | "command": "frida.remote.remove" 232 | }, 233 | { 234 | "command": "frida.typing.init" 235 | }, 236 | { 237 | "command": "frida.external.objection", 238 | "when": "false" 239 | }, 240 | { 241 | "command": "frida.external.dump", 242 | "when": "false" 243 | }, 244 | { 245 | "command": "frida.external.apk", 246 | "when": "false" 247 | }, 248 | { 249 | "command": "frida.device.androidserver", 250 | "when": "false" 251 | }, 252 | { 253 | "command": "frida.external.shell", 254 | "when": "false" 255 | }, 256 | { 257 | "command": "frida.syslog", 258 | "when": "false" 259 | }, 260 | { 261 | "command": "frida.print.modules", 262 | "when": "false" 263 | }, 264 | { 265 | "command": "frida.print.classes", 266 | "when": "false" 267 | }, 268 | { 269 | "command": "frida.attach", 270 | "when": "false" 271 | }, 272 | { 273 | "command": "frida.kill", 274 | "when": "false" 275 | }, 276 | { 277 | "command": "frida.spawn", 278 | "when": "false" 279 | }, 280 | { 281 | "command": "frida.debug.setup", 282 | "when": "false" 283 | }, 284 | { 285 | "command": "frida.spawn.suspended", 286 | "when": "false" 287 | }, 288 | { 289 | "command": "frida.bundle.copy", 290 | "when": "false" 291 | }, 292 | { 293 | "command": "frida.name.copy", 294 | "when": "false" 295 | }, 296 | { 297 | "command": "frida.device.copy", 298 | "when": "false" 299 | } 300 | ], 301 | "view/title": [ 302 | { 303 | "command": "frida.remote.add", 304 | "when": "view == fridaApps", 305 | "group": "navigation@1" 306 | }, 307 | { 308 | "command": "frida.apps.refresh", 309 | "when": "view == fridaApps", 310 | "group": "navigation@2" 311 | }, 312 | { 313 | "command": "frida.remote.add", 314 | "when": "view == fridaPs", 315 | "group": "navigation@1" 316 | }, 317 | { 318 | "command": "frida.ps.refresh", 319 | "when": "view == fridaPs", 320 | "group": "navigation@2" 321 | } 322 | ], 323 | "view/item/context": [ 324 | { 325 | "command": "frida.remote.remove", 326 | "when": "view == fridaApps && viewItem =~ /^device\\|socket@/", 327 | "group": "inline" 328 | }, 329 | { 330 | "command": "frida.remote.remove", 331 | "when": "view == fridaPs && viewItem =~ /^device\\|socket@/", 332 | "group": "inline" 333 | }, 334 | { 335 | "command": "frida.attach", 336 | "when": "view == fridaApps && viewItem == 'app|running'", 337 | "group": "inline" 338 | }, 339 | { 340 | "command": "frida.attach", 341 | "when": "view == fridaPs && viewItem =~ /^process\\|/", 342 | "group": "inline" 343 | }, 344 | { 345 | "command": "frida.spawn", 346 | "when": "view == fridaApps && viewItem =~ /^app\\|/", 347 | "group": "inline" 348 | }, 349 | { 350 | "command": "frida.debug.setup", 351 | "when": "view == fridaApps && viewItem =~ /^app\\|/", 352 | "group": "1_frida_info@1" 353 | }, 354 | { 355 | "command": "frida.debug.setup", 356 | "when": "view == fridaPs && viewItem =~ /^process\\|/", 357 | "group": "1_frida_info@1" 358 | }, 359 | { 360 | "command": "frida.bundle.copy", 361 | "when": "view == fridaApps && viewItem =~ /^app\\|/", 362 | "group": "1_frida_info@1" 363 | }, 364 | { 365 | "command": "frida.name.copy", 366 | "when": "view == fridaPs && viewItem =~ /^process\\|/", 367 | "group": "1_frida_info@1" 368 | }, 369 | { 370 | "command": "frida.pid.copy", 371 | "when": "view == fridaPs && viewItem =~ /^process\\|/", 372 | "group": "1_frida_info@1" 373 | }, 374 | { 375 | "command": "frida.kill", 376 | "when": "viewItem =~ /\\|running$/", 377 | "group": "2_frida_process@2" 378 | }, 379 | { 380 | "command": "frida.spawn.suspended", 381 | "when": "view == fridaApps && viewItem =~ /^app\\|/", 382 | "group": "2_frida_process@1" 383 | }, 384 | { 385 | "command": "frida.syslog", 386 | "when": "view == fridaApps && viewItem =~ /^app\\|/", 387 | "group": "3_frida_logger@1" 388 | }, 389 | { 390 | "command": "frida.syslog", 391 | "when": "view == fridaPs && viewItem =~ /^process\\|/", 392 | "group": "3_frida_logger@1" 393 | }, 394 | { 395 | "command": "frida.print.modules", 396 | "when": "view == fridaApps && viewItem =~ /^app\\|/", 397 | "group": "3_frida_logger@2" 398 | }, 399 | { 400 | "command": "frida.print.modules", 401 | "when": "view == fridaPs && viewItem =~ /^process\\|/", 402 | "group": "3_frida_logger@2" 403 | }, 404 | { 405 | "command": "frida.print.classes", 406 | "when": "view == fridaApps && viewItem =~ /^app\\|/", 407 | "group": "3_frida_logger@3" 408 | }, 409 | { 410 | "command": "frida.print.classes", 411 | "when": "view == fridaPs && viewItem =~ /^process\\|/", 412 | "group": "3_frida_logger@3" 413 | }, 414 | { 415 | "command": "frida.external.objection", 416 | "when": "view == fridaApps && viewItem =~ /^app\\|/", 417 | "group": "4_frida_external@2" 418 | }, 419 | { 420 | "command": "frida.external.dump", 421 | "when": "view == fridaApps && viewItem =~ /^app\\|/ && viewItem =~ /\\|ios$/", 422 | "group": "4_frida_external@3" 423 | }, 424 | { 425 | "command": "frida.external.apk", 426 | "when": "view == fridaApps && viewItem =~ /^app\\|/ && viewItem =~ /\\|android$/", 427 | "group": "4_frida_external@3" 428 | }, 429 | { 430 | "command": "frida.external.shell", 431 | "when": "viewItem =~ /^device\\|\\w+\\|(android|ios)$/ || viewItem =~ /^device\\|\\local/", 432 | "group": "1_frida_dev_external@1" 433 | }, 434 | { 435 | "command": "frida.device.copy", 436 | "when": "viewItem =~ /^device\\|/", 437 | "group": "1_frida_dev_setup@1" 438 | }, 439 | { 440 | "command": "frida.device.androidserver", 441 | "when": "viewItem =~ /^device\\|\\w+\\|android$/", 442 | "group": "2_frida_dev_setup@2" 443 | } 444 | ] 445 | } 446 | }, 447 | "scripts": { 448 | "vscode:prepublish": "npm run compile", 449 | "compile": "tsc -p ./", 450 | "watch": "tsc -watch -p ./", 451 | "pretest": "npm run compile", 452 | "deploy": "vsce publish" 453 | }, 454 | "devDependencies": { 455 | "@types/node": "^22.8.1", 456 | "@types/vscode": "^1.94.0", 457 | "@types/which": "^3.0.4", 458 | "@typescript-eslint/eslint-plugin": "^8.11.0", 459 | "@typescript-eslint/parser": "^8.11.0", 460 | "eslint": "^9.13.0", 461 | "typescript": "^5.6.3" 462 | }, 463 | "dependencies": { 464 | "which": "^5.0.0" 465 | }, 466 | "extensionDependencies": [ 467 | "ms-python.python" 468 | ] 469 | } 470 | -------------------------------------------------------------------------------- /package.nls.json: -------------------------------------------------------------------------------- 1 | { 2 | "panel.title": "Frida Utilities", 3 | "extension.description": "Unofficial frida workbench for VSCode", 4 | "frida.decryptOutput": "Preferred directory to save ipa or apk", 5 | "frida.androidServerPath": "Path to frida-server on Android", 6 | "frida.ps.refresh": "Reload process list", 7 | "frida.apps.refresh": "Reload application list", 8 | "frida.remote.add": "Connect Remote Device...", 9 | "frida.remote.remove": "Disconnect Remote Device...", 10 | "frida.attach": "Attach to", 11 | "frida.device.copy": "Copy Device ID", 12 | "frida.bundle.copy": "Copy Bundle ID", 13 | "frida.name.copy": "Copy Process Name", 14 | "frida.pid.copy": "Copy Process ID", 15 | "frida.spawn": "Spawn", 16 | "frida.spawn.suspended": "Spawn Suspended", 17 | "frida.kill": "End Process", 18 | "frida.debug.setup": "Create Debugger Configuration", 19 | "frida.syslog": "View Debug Logs", 20 | "frida.syslog.vacuum": "Close All Console", 21 | "frida.print.modules": "Export All Modules To Editor", 22 | "frida.print.classes": "Export All Classes To Editor", 23 | "frida.external.objection": "Objection", 24 | "frida.external.apk": "Pull APK from Device", 25 | "frida.external.dump": "Dump App Package", 26 | "frida.device.androidserver": "Start frida-server", 27 | "frida.external.shell": "Open Shell", 28 | "frida.boilerplate.agent": "Create Frida Agent", 29 | "frida.boilerplate.module": "Create Frida CModule", 30 | "frida.typing.init": "Get type hint for frida", 31 | "frida.apps": "Application List", 32 | "frida.ps": "Process List" 33 | } -------------------------------------------------------------------------------- /package.nls.zh-cn.json: -------------------------------------------------------------------------------- 1 | { 2 | "panel.title": "Frida 集成面板", 3 | "extension.description": "将 Frida 集成到 VSCode 的非官方插件", 4 | "frida.decryptOutput": "保存 ipa 或 apk 的首选目录", 5 | "frida.androidServerPath": "Android 上 frida-server 的路径", 6 | "frida.ps.refresh": "刷新进程列表", 7 | "frida.apps.refresh": "刷新应用列表", 8 | "frida.remote.add": "连接远程设备...", 9 | "frida.remote.remove": "断开远程设备...", 10 | "frida.attach": "附加到", 11 | "frida.device.copy": "复制设备 ID", 12 | "frida.bundle.copy": "复制包 ID", 13 | "frida.name.copy": "复制进程名称", 14 | "frida.pid.copy": "复制进程 ID", 15 | "frida.spawn": "启动", 16 | "frida.spawn.suspended": "启动并暂停", 17 | "frida.kill": "结束进程", 18 | "frida.debug.setup": "创建调试配置", 19 | "frida.syslog": "查看调试日志", 20 | "frida.syslog.vacuum": "关闭所有控制台", 21 | "frida.print.modules": "导出所有模块到编辑器", 22 | "frida.print.classes": "导出所有类到编辑器", 23 | "frida.external.objection": "Objection", 24 | "frida.external.apk": "从设备提取 APK", 25 | "frida.external.dump": "导出应用包", 26 | "frida.device.androidserver": "启动 frida-server", 27 | "frida.external.shell": "打开终端", 28 | "frida.boilerplate.agent": "创建 Frida Agent", 29 | "frida.boilerplate.module": "创建 Frida CModule", 30 | "frida.typing.init": "获取 frida 类型提示", 31 | "frida.apps": "应用列表", 32 | "frida.ps": "进程列表" 33 | } -------------------------------------------------------------------------------- /resources/dark/add.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /resources/dark/apps.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/attach.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/del.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/error.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/dark/local.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/refresh.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/dark/remote.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/statusRun.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/dark/statusStop.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/dark/usb.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/debugger.preload.js: -------------------------------------------------------------------------------- 1 | global.process = { pid: Process.id, version: Frida.version, arch: Process.arch }; -------------------------------------------------------------------------------- /resources/doc/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChiChou/vscode-frida/8f69a68e1c1e0955ef8be77a1246ea6ee9dc686f/resources/doc/demo.gif -------------------------------------------------------------------------------- /resources/doc/list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChiChou/vscode-frida/8f69a68e1c1e0955ef8be77a1246ea6ee9dc686f/resources/doc/list.png -------------------------------------------------------------------------------- /resources/doc/syslog.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChiChou/vscode-frida/8f69a68e1c1e0955ef8be77a1246ea6ee9dc686f/resources/doc/syslog.gif -------------------------------------------------------------------------------- /resources/doc/typing.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChiChou/vscode-frida/8f69a68e1c1e0955ef8be77a1246ea6ee9dc686f/resources/doc/typing.gif -------------------------------------------------------------------------------- /resources/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Slice 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /resources/light/add.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /resources/light/apps.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/attach.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/del.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/error.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/light/local.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/refresh.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/light/remote.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/statusRun.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/light/statusStop.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/light/usb.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/run.svg: -------------------------------------------------------------------------------- 1 | run2 -------------------------------------------------------------------------------- /resources/templates/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "preLaunchTask": "frida", 6 | "name": "Attach", 7 | "port": 9229, 8 | "request": "attach", 9 | "skipFiles": [ 10 | "/**" 11 | ], 12 | "type": "node" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /resources/templates/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "isBackground": true, 6 | "label": "frida", 7 | "command": "frida", 8 | "args": [ 9 | "explorer.exe", 10 | "-l", 11 | "pid.js", 12 | "--debug", 13 | "--runtime=v8" 14 | ], 15 | "type": "shell", 16 | "problemMatcher": [ 17 | { 18 | "pattern": [ 19 | { 20 | "regexp": ".", 21 | "file": 1, 22 | "location": 2, 23 | "message": 3 24 | } 25 | ], 26 | "background": { 27 | "activeOnStart": true, 28 | "beginsPattern": "Chrome Inspector server listening on port", 29 | "endsPattern": "\\d+" 30 | } 31 | } 32 | ] 33 | } 34 | ] 35 | } -------------------------------------------------------------------------------- /resources/terminal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChiChou/vscode-frida/8f69a68e1c1e0955ef8be77a1246ea6ee9dc686f/resources/terminal.png -------------------------------------------------------------------------------- /src/commands/android.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/naming-convention */ 2 | import * as vscode from 'vscode'; 3 | import { join } from 'path'; 4 | import { tmpdir } from 'os'; 5 | 6 | import ADB from '../driver/adb'; 7 | import { DeviceItem, TargetItem } from '../providers/devices'; 8 | import { logger } from '../logger'; 9 | import { interpreter, sleep } from '../utils'; 10 | import { run } from '../term'; 11 | 12 | function getServerPath() { 13 | return vscode.workspace.getConfiguration('frida') 14 | .get('androidServerPath', '/data/local/tmp/frida-server'); 15 | } 16 | 17 | export async function startServer(target: TargetItem) { 18 | if (!(target instanceof DeviceItem)) { 19 | vscode.window.showErrorMessage(vscode.l10n.t('This command is only expected to be used in the context menu')); 20 | return; 21 | } 22 | 23 | const server = getServerPath(); 24 | const adb = new ADB(target.data.id); 25 | const installed = await adb.shell(server, '--version').then((ver: string) => { 26 | logger.appendLine(vscode.l10n.t('sanity check: frida-server version on device {0}', ver)); 27 | return true; 28 | }).catch(() => { 29 | logger.appendLine(vscode.l10n.t('frida-server not found on device, downloading...')); 30 | return false; 31 | }); 32 | 33 | if (!installed) { 34 | const abi = (await adb.shell('getprop', 'ro.product.cpu.abi')).trimEnd(); 35 | const py = join(__dirname, '..', '..', 'backend', 'android', 'get-frida.py'); 36 | const tmp = join(tmpdir(), `frida-server-${abi}`); 37 | 38 | const shellPath = await interpreter(); 39 | await vscode.window.withProgress({ 40 | location: vscode.ProgressLocation.Notification, 41 | title: vscode.l10n.t('Downloading frida-server'), 42 | cancellable: false 43 | }, async (progress) => { 44 | await run({ 45 | name: `Download frida-server`, 46 | shellPath, 47 | shellArgs: [py, abi, tmp] 48 | }); 49 | progress.report({ message: vscode.l10n.t('Done') }); 50 | }); 51 | 52 | const uri = vscode.Uri.file(tmp); 53 | await adb.push(uri, server); 54 | 55 | vscode.window.showInformationMessage(vscode.l10n.t('frida-server deployed to {0} successfully', server)); 56 | await adb.shell('chmod', '755', server); 57 | } 58 | 59 | const term = adb.interactive(); 60 | await sleep(1000); 61 | term.sendText('su', true); 62 | await sleep(500); 63 | term.sendText(server, true); 64 | } 65 | -------------------------------------------------------------------------------- /src/commands/boilerplate.ts: -------------------------------------------------------------------------------- 1 | import { createReadStream, createWriteStream, promises as fsp } from 'fs'; 2 | import { join as joinPath } from 'path'; 3 | import * as vscode from 'vscode'; 4 | 5 | import { AppItem, ProcessItem } from '../providers/devices'; 6 | import { run } from '../term'; 7 | import { cmd, expandDevParam, interpreter, resource } from '../utils'; 8 | 9 | 10 | async function create(template: string) { 11 | let dest: vscode.Uri; 12 | 13 | const { workspaceFolders } = vscode.workspace; 14 | { 15 | if (workspaceFolders?.length) { dest = workspaceFolders[0].uri; } 16 | 17 | const fileUri = await vscode.window.showOpenDialog({ 18 | canSelectFolders: true, 19 | canSelectMany: false, 20 | openLabel: vscode.l10n.t('Create Here') 21 | }); 22 | 23 | if (fileUri?.length) { 24 | dest = fileUri[0]; 25 | } else { 26 | vscode.window.showInformationMessage( 27 | vscode.l10n.t('You must select a folder to create the project')); 28 | return; 29 | } 30 | } 31 | 32 | const args = ['-m', 'frida_tools.creator', '-t', template]; 33 | const shellPath = await interpreter(); 34 | await run({ 35 | cwd: dest, 36 | name: vscode.l10n.t('Create Project'), 37 | shellPath, 38 | shellArgs: args 39 | }); 40 | 41 | if (!(workspaceFolders?.length)) { 42 | vscode.commands.executeCommand('vscode.openFolder', dest); 43 | } 44 | 45 | await run({ 46 | cwd: dest.path, 47 | name: `npm install`, 48 | shellPath: cmd('npm'), 49 | shellArgs: ['install'] 50 | }); 51 | } 52 | 53 | export function agent() { 54 | return create('agent'); 55 | } 56 | 57 | export function module() { 58 | return create('module'); 59 | } 60 | 61 | function* tokenize(cmd: string) { 62 | let inQuote = false; 63 | let buf = []; 64 | for (const c of cmd) { 65 | if (c === '\"') { 66 | inQuote = !inQuote; 67 | } else if (c === ' ' && !inQuote) { 68 | yield buf.join(''); 69 | buf.length = 0; 70 | } else { 71 | buf.push(c); 72 | } 73 | } 74 | 75 | yield buf.join(''); 76 | } 77 | 78 | export async function debug(node?: AppItem | ProcessItem) { 79 | if (!node) { return; } 80 | 81 | const { activeTextEditor, showInformationMessage, showInputBox } = vscode.window; 82 | const { workspaceFolders } = vscode.workspace; 83 | 84 | if (!workspaceFolders?.length) { 85 | showInformationMessage( 86 | vscode.l10n.t('This command only works in a workspace. Please open a workspace first')); 87 | return; 88 | } 89 | 90 | const FILE_LAUNCH = 'launch.json'; 91 | const FILE_TASKS = 'tasks.json'; 92 | 93 | const dest = workspaceFolders[0].uri; 94 | 95 | const folder = joinPath(dest.fsPath, '.vscode'); 96 | const launchJSON = joinPath(folder, FILE_LAUNCH); 97 | const tasksJSON = joinPath(folder, FILE_TASKS); 98 | 99 | const isDir = (path: string) => 100 | fsp.stat(path).then(stat => stat.isDirectory()).catch(() => false); 101 | 102 | const isFile = (path: string) => 103 | fsp.stat(path).then(stat => stat.isFile()).catch(() => false); 104 | 105 | if (await isDir(folder)) { 106 | // check if launch.json exists 107 | for (const file of [launchJSON, tasksJSON]) { 108 | if (await isFile(file)) { 109 | const msg = vscode.l10n.t('{0} already exists. Do you want to overwrite it?', file); 110 | const y = vscode.l10n.t('Yes'); 111 | const n = vscode.l10n.t('No'); 112 | const answer = await showInformationMessage(msg, y, n); 113 | if (answer === n) { return; } 114 | if (answer === y) { break; } // only have to answer yes once 115 | } 116 | } 117 | } else { 118 | await fsp.mkdir(folder); 119 | } 120 | 121 | const quote = (s: string) => s.includes(' ') ? `"${s}"` : s; 122 | 123 | let cmd: string[] = []; 124 | // create debug command line 125 | cmd.push(...expandDevParam(node)); 126 | 127 | if (node instanceof AppItem) { 128 | if (node.data.pid) { 129 | cmd.push(quote(node.data.name)); // attach to app 130 | } else { 131 | cmd.push('-f', quote(node.data.identifier)); // spawn 132 | } 133 | } else if (node instanceof ProcessItem) { 134 | cmd.push(node.data.pid.toString()); // attach to pid 135 | } 136 | 137 | // attach current document to the target 138 | if (activeTextEditor && !activeTextEditor.document.isDirty) { 139 | cmd.push('-l', quote(activeTextEditor.document.uri.fsPath)); 140 | } 141 | 142 | // enable v8 debug 143 | cmd.push('--runtime=v8', '--debug'); 144 | 145 | const placeHolder = cmd.join(' '); 146 | const userInput = await showInputBox({ 147 | placeHolder, 148 | prompt: vscode.l10n.t('Debug Command'), 149 | value: placeHolder, 150 | }); 151 | 152 | // user cancelled 153 | if (!userInput) { return; } 154 | 155 | // copy launch.json to workspace 156 | await new Promise((resolve, reject) => { 157 | createReadStream(resource('templates', FILE_LAUNCH).fsPath) 158 | .pipe(createWriteStream(launchJSON)) 159 | .on('finish', () => resolve(true)) 160 | .on('error', (err) => reject(err)); 161 | }); 162 | 163 | const tasksTemplatePath = resource('templates', FILE_TASKS).fsPath; 164 | const content = JSON.parse(await fsp.readFile(tasksTemplatePath, 'utf8')); 165 | // replace the command line in tasks.json 166 | content.tasks[0].args = [...tokenize(userInput)]; 167 | await fsp.writeFile(tasksJSON, JSON.stringify(content, null, 2)); 168 | 169 | showInformationMessage( 170 | vscode.l10n.t('Debug configuration created. You can now start debugging by pressing F5') 171 | ); 172 | } 173 | -------------------------------------------------------------------------------- /src/commands/clipboard.ts: -------------------------------------------------------------------------------- 1 | import { env, l10n } from 'vscode'; 2 | import { TargetItem, AppItem, ProcessItem, DeviceItem } from "../providers/devices"; 3 | 4 | export function copy(item: TargetItem) { 5 | const getText = (item: TargetItem) => { 6 | if (item instanceof AppItem) { 7 | return item.data.identifier; 8 | } else if (item instanceof ProcessItem) { 9 | return item.data.name; 10 | } else if (item instanceof DeviceItem) { 11 | return item.data.id; 12 | } 13 | 14 | throw new Error(l10n.t('Unsupported item type')); 15 | } 16 | 17 | env.clipboard.writeText(getText(item)); 18 | } 19 | 20 | export function copyPid(item: AppItem | ProcessItem) { 21 | if (item instanceof AppItem || item instanceof ProcessItem) { 22 | env.clipboard.writeText(item.data.pid.toString()); 23 | return; 24 | } 25 | 26 | throw new Error(l10n.t('Unsupported item type')); 27 | } -------------------------------------------------------------------------------- /src/commands/dump.ts: -------------------------------------------------------------------------------- 1 | import { homedir } from 'os'; 2 | import * as vscode from 'vscode'; 3 | 4 | import ADB from '../driver/adb'; 5 | import { AppItem, TargetItem } from "../providers/devices"; 6 | import { DeviceType } from '../types'; 7 | import { cmd } from '../utils'; 8 | 9 | export default async function dump(target: TargetItem) { 10 | if (!(target instanceof AppItem)) { 11 | vscode.window.showErrorMessage(vscode.l10n.t('This command is only expected to be used in the context menu')); 12 | return; 13 | } 14 | 15 | if (target.device.os !== 'ios' && target.device.os !== 'android') { 16 | vscode.window.showErrorMessage(vscode.l10n.t('This command only supports iOS or Android')); 17 | return; 18 | } 19 | 20 | const preferred = vscode.workspace.getConfiguration('frida').get('decryptOutput', homedir()); 21 | const defaultUri = vscode.Uri.file(preferred); 22 | const destinations = await vscode.window.showOpenDialog({ 23 | defaultUri, 24 | canSelectFiles: false, 25 | canSelectFolders: true, 26 | canSelectMany: false, 27 | openLabel: 'Select', 28 | title: vscode.l10n.t('Select destination folder') 29 | }); 30 | 31 | if (!destinations?.length) { return; } 32 | 33 | const destURI = destinations[0]; 34 | const output = destURI.fsPath; 35 | 36 | // save preferred path 37 | vscode.workspace.getConfiguration('frida').update('decryptOutput', output, true); 38 | 39 | let artifact: vscode.Uri; 40 | 41 | try { 42 | if (target.device.os === 'ios') { 43 | artifact = vscode.Uri.joinPath(destURI, `${target.data.identifier}.ipa`); 44 | await bagbak(target, artifact); 45 | } else if (target.device.os === 'android') { 46 | artifact = vscode.Uri.joinPath(destURI, `${target.data.identifier}.apk`); 47 | await pull(target, artifact); 48 | } else { 49 | vscode.window.showErrorMessage(vscode.l10n.t('This command only supports iOS or Android')); 50 | return; 51 | } 52 | } catch (e) { 53 | vscode.window.showInformationMessage( 54 | vscode.l10n.t('failed to dump application:\n{0}', (e as Error).message)); 55 | return; 56 | } 57 | 58 | const actionOpen = vscode.l10n.t('Open'); 59 | const option = await vscode.window.showInformationMessage( 60 | vscode.l10n.t('Successfully pulled package {0}', target.data.identifier), 61 | actionOpen, 62 | vscode.l10n.t('Dismiss')); 63 | 64 | if (option === actionOpen) { 65 | vscode.commands.executeCommand('revealFileInOS', artifact); 66 | } 67 | } 68 | 69 | async function pull(target: AppItem, output: vscode.Uri) { 70 | const adb = new ADB(target.device.id); 71 | const path = await adb.shell('pm', 'path', target.data.identifier); 72 | 73 | if (path.startsWith('package:')) { 74 | await adb.pull(path.substring(8).trimEnd(), output); 75 | } else { 76 | vscode.window.showErrorMessage(vscode.l10n.t('Failed to get package path: {0}', path)); 77 | } 78 | } 79 | 80 | async function bagbak(target: AppItem, output: vscode.Uri) { 81 | const shellArgs: string[] = []; 82 | switch (target.device.type) { 83 | case DeviceType.Remote: 84 | shellArgs.push.apply(shellArgs, ['-H', target.device.id]); 85 | break; 86 | case DeviceType.USB: 87 | if (target.device.id !== 'usb') { shellArgs.push.apply(shellArgs, ['-D', target.device.id]); } 88 | break; 89 | default: 90 | vscode.window.showErrorMessage(vscode.l10n.t('Unsupported device type')); 91 | return; 92 | } 93 | 94 | shellArgs.push.apply(shellArgs, [target.data.identifier, '-o', output.fsPath]); 95 | 96 | const term = vscode.window.createTerminal({ 97 | name: 'bagbak', 98 | shellPath: cmd('bagbak'), 99 | shellArgs, 100 | }); 101 | 102 | return new Promise((resolve, reject) => { 103 | vscode.window.onDidCloseTerminal((e) => { 104 | if (e === term) { 105 | if (term.exitStatus?.code === 0) { 106 | resolve(); 107 | } else { 108 | reject(new Error(`bagbak exited with code ${term.exitStatus?.code}`)); 109 | } 110 | } 111 | }); 112 | 113 | term.show(); 114 | }); 115 | 116 | } 117 | -------------------------------------------------------------------------------- /src/commands/objection.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { l10n } from 'vscode'; 3 | 4 | import { launch } from '../driver/frida'; 5 | import { AppItem, ProcessItem, TargetItem } from "../providers/devices"; 6 | import { run } from '../term'; 7 | import { DeviceType } from '../types'; 8 | import { interpreter } from '../utils'; 9 | 10 | export async function explore(target: TargetItem) : Promise { 11 | if (!target) { 12 | vscode.window.showErrorMessage(l10n.t('This command is only expected to be used in the context menu')); 13 | return; 14 | } 15 | 16 | if (!(target instanceof AppItem || target instanceof ProcessItem)) { 17 | vscode.window.showErrorMessage(l10n.t('This command is not applicable to the selected item')); 18 | return; 19 | } 20 | 21 | const name = `Objection - ${target.label}`; 22 | 23 | let device: string[]; 24 | switch (target.device.type) { 25 | case DeviceType.TCP: 26 | case DeviceType.Remote: 27 | // todo: support remote connection 28 | device = ['-N', '-h', target.device.id]; 29 | break; 30 | case DeviceType.Local: 31 | device = []; 32 | vscode.window.showErrorMessage(l10n.t('This command is not applicable to the local device')); 33 | return; 34 | case DeviceType.USB: 35 | default: 36 | device = []; 37 | } 38 | 39 | let { pid } = target.data; 40 | let gadget = pid.toString(); 41 | if (target instanceof AppItem && !pid) { 42 | try { 43 | gadget = (await launch(target.device.id, target.data.identifier)).toString(); 44 | } catch (e) { 45 | vscode.window.showWarningMessage( 46 | l10n.t('Warning: failed to launch App {0}\n{1}', target.data.identifier, `${e}`)); 47 | gadget = target.data.name; 48 | } 49 | } 50 | 51 | const shellArgs = ['-m', 'objection.console.cli', '-g', gadget, ...device, 'explore']; 52 | const shellPath = await interpreter('objection'); 53 | run({ 54 | name, 55 | shellArgs, 56 | shellPath, 57 | }); 58 | } -------------------------------------------------------------------------------- /src/commands/print.ts: -------------------------------------------------------------------------------- 1 | import { Uri, window } from "vscode"; 2 | import { rpc } from "../driver/backend"; 3 | import { TargetItem } from "../providers/devices"; 4 | 5 | interface Module { 6 | name: string; 7 | base: string; 8 | size: number; 9 | path: string; 10 | }; 11 | 12 | type ClassesResult = string[]; 13 | type ModulesResult = Module[]; 14 | 15 | function create(name: string, content: string) { 16 | window.showTextDocument(Uri.parse(`untitled:${encodeURIComponent(name)}`)).then((editor) => { 17 | editor.edit((editBuilder) => { 18 | editBuilder.insert(editor.selection.active, content); 19 | }); 20 | }); 21 | } 22 | 23 | export function classes(target: TargetItem) { 24 | rpc(target, 'classes') 25 | .then((result: ClassesResult) => { 26 | const text = result.join('\n'); 27 | create(`classes - ${target.label}.txt`, text); 28 | }) 29 | .catch(err => window.showErrorMessage(err.message)); 30 | } 31 | 32 | export function modules(target: TargetItem) { 33 | rpc(target, 'modules') 34 | .then((modules: ModulesResult) => { 35 | const text = modules.map(m => `${m.base} ${m.name} ${m.size} ${m.path}`).join('\n'); 36 | create(`modules - ${target.label}.txt`, text); 37 | }) 38 | .catch(err => window.showErrorMessage(err.message)); 39 | } 40 | -------------------------------------------------------------------------------- /src/commands/repl.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as vscode from 'vscode'; 3 | 4 | import { terminate } from '../driver/frida'; 5 | import { all, connect, disconnect } from '../driver/remote'; 6 | import { AppItem, DeviceItem, ProcessItem, TargetItem } from '../providers/devices'; 7 | import { DeviceType } from '../types'; 8 | import { expandDevParam, interpreter, refresh } from '../utils'; 9 | 10 | const terminals = new Set(); 11 | 12 | async function repl(args: string[], id: string) { 13 | const name = vscode.l10n.t('Frida - {0}', id); 14 | const shellPath = await interpreter(); 15 | const py = path.join(__dirname, '..', '..', 'backend', 'pause.py'); 16 | const shellArgs = [py, shellPath, '-m', 'frida_tools.repl', ...args]; 17 | const term = vscode.window.createTerminal({ 18 | name, 19 | shellPath, 20 | shellArgs, 21 | hideFromUser: true 22 | }); 23 | term.show(); 24 | terminals.add(term); 25 | } 26 | 27 | vscode.window.onDidCloseTerminal(t => terminals.delete(t)); 28 | 29 | export function spawn(node?: AppItem) { 30 | if (!node) { return; } 31 | 32 | repl(['-f', node.data.identifier, ...expandDevParam(node)], node.data.name); 33 | refresh(); 34 | } 35 | 36 | export function spawnSuspended(node?: AppItem) { 37 | if (!node) { return; } 38 | 39 | repl(['-f', node.data.identifier, ...expandDevParam(node), '--pause'], node.data.name); 40 | refresh(); 41 | } 42 | 43 | export function kill(node?: TargetItem) { 44 | if (!node) { return; } 45 | 46 | if ((node instanceof AppItem && node.data.pid) || node instanceof ProcessItem) { 47 | terminate(node.device.id, node.data.pid.toString()); 48 | refresh(); 49 | } else { 50 | vscode.window.showWarningMessage(vscode.l10n.t('Target is not running')); 51 | } 52 | } 53 | 54 | export function attach(node?: TargetItem) { 55 | if (!node) { return; } 56 | 57 | if (node instanceof AppItem || node instanceof ProcessItem) { 58 | if (!node.data.pid) { 59 | vscode.window.showErrorMessage( 60 | vscode.l10n.t('App "{0}" must be running before attaching to it', node.data.name)); 61 | } 62 | 63 | repl([node.data.pid.toString(), ...expandDevParam(node)], node.data.pid.toString()); 64 | } 65 | } 66 | 67 | export async function addRemote() { 68 | const host = await vscode.window.showInputBox({ 69 | placeHolder: '192.168.1.2:27042', 70 | prompt: vscode.l10n.t('Host or IP of the remote device'), 71 | value: '' 72 | }); 73 | 74 | if (typeof host !== 'string' || host.length === 0) { 75 | return; 76 | } 77 | 78 | connect(host); 79 | refresh(); 80 | } 81 | 82 | export async function delRemote(node?: TargetItem) { 83 | if (!node) { 84 | const selected = await vscode.window.showQuickPick(all()); 85 | if (typeof selected === 'string') { 86 | disconnect(selected); 87 | } 88 | } else if (node instanceof DeviceItem && node.data.type === DeviceType.Remote) { 89 | disconnect(node.data.id.substring('socket@'.length)); 90 | } 91 | refresh(); 92 | } 93 | -------------------------------------------------------------------------------- /src/commands/ssh.ts: -------------------------------------------------------------------------------- 1 | import { spawn } from 'child_process'; 2 | import { commands, window, l10n } from 'vscode'; 3 | 4 | import { os } from '../driver/backend'; 5 | import { DeviceItem, TargetItem } from '../providers/devices'; 6 | import { run } from '../term'; 7 | import { DeviceType } from '../types'; 8 | import { executable } from '../utils'; 9 | 10 | 11 | class PortNotFoundError extends Error { } 12 | class ToolNotFoundError extends Error { } 13 | class InvalidProtocolError extends Error { } 14 | 15 | const nc = executable('inetcat'); 16 | 17 | async function findSSHPort(device: DeviceItem) { 18 | function validate(port: number, ...args: string[]): Promise { 19 | const magic = Buffer.from('SSH-2.0-'); 20 | return new Promise((resolve, reject) => { 21 | const inetcat = spawn(nc, [port.toString(), ...args]); 22 | inetcat 23 | .on('error', (err) => { 24 | if ((err as NodeJS.ErrnoException).code === 'ENOENT') { 25 | reject(new ToolNotFoundError(l10n.t('inetcat not found'))); 26 | } else { 27 | reject(err); 28 | } 29 | }) 30 | .on('close', (code) => { 31 | if (code !== 0) { 32 | reject(new InvalidProtocolError(l10n.t('inetcat exited with code {0}', `${code}`))); 33 | } 34 | }); 35 | 36 | inetcat.stdout.once('data', (data) => { 37 | if (data.slice(0, magic.length).equals(magic)) { 38 | resolve(true); 39 | } else { 40 | reject(new InvalidProtocolError(l10n.t('Invalid protocol'))); 41 | } 42 | inetcat.stdin.end(); 43 | }); 44 | }); 45 | } 46 | 47 | const candidates = [22, 44]; 48 | const args = ['-u', device.data.id]; 49 | if (device.data.type === DeviceType.Remote) { 50 | args.push('-n'); 51 | } 52 | 53 | for (const port of candidates) { 54 | try { 55 | if (await validate(port, ...args)) { 56 | return port; 57 | } 58 | } catch (err) { 59 | if (err instanceof ToolNotFoundError) { throw err; } 60 | console.error((err as Error).message); 61 | } 62 | } 63 | 64 | throw new PortNotFoundError(l10n.t('No valid SSH port found')); 65 | } 66 | 67 | export async function shell(node: TargetItem) { 68 | if (!(node instanceof DeviceItem)) { 69 | window.showErrorMessage( 70 | l10n.t('This command is only expected to be used in the context menu')); 71 | return; 72 | } 73 | 74 | if (node.data.id === 'local') { 75 | // execute command to open a new terminal 76 | commands.executeCommand('workbench.action.terminal.new'); 77 | return; 78 | } 79 | 80 | const system = await os(node.data.id); 81 | const name = l10n.t('SSH: {0}', node.data.name); 82 | let shellPath, shellArgs; 83 | 84 | if (system === 'ios') { 85 | if (process.platform === 'win32') { 86 | window.showErrorMessage( 87 | l10n.t('This feature is not enabled on Windows due to lack of inetcat')); 88 | return 89 | } 90 | 91 | try { 92 | const port = await findSSHPort(node); 93 | shellPath = executable('ssh'); 94 | shellArgs = [ 95 | '-o', 'StrictHostKeyChecking=no', 96 | '-o', 'UserKnownHostsFile=/dev/null', 97 | '-o', `ProxyCommand='${nc}' 22`, 98 | `mobile@localhost`, // todo: customize username 99 | '-p', port.toString() 100 | ]; 101 | } catch (err) { 102 | if (err instanceof ToolNotFoundError) { 103 | window.showErrorMessage(l10n.t('inetcat command not present in $PATH')); 104 | return; 105 | } else if (err instanceof PortNotFoundError) { 106 | window.showErrorMessage(l10n.t('No valid SSH port found for device {0}', node.data.name)); 107 | return; 108 | } 109 | } 110 | } else if (system === 'android') { 111 | // todo: use adb.ts 112 | shellPath = executable('adb'); 113 | shellArgs = ['-s', node.data.id, 'shell']; 114 | } else { 115 | window.showErrorMessage(l10n.t("OS type {0} is not supported", system)); 116 | return; 117 | } 118 | 119 | return run({ 120 | name, 121 | shellArgs, 122 | shellPath, 123 | hideFromUser: true, 124 | }); 125 | } -------------------------------------------------------------------------------- /src/commands/syslog.ts: -------------------------------------------------------------------------------- 1 | import * as cp from 'child_process'; 2 | import { l10n, OutputChannel, window } from 'vscode'; 3 | 4 | import { driverScript, lockdownSyslog, os } from '../driver/backend'; 5 | import { AppItem, ProcessItem, TargetItem } from "../providers/devices"; 6 | import { DeviceType } from '../types'; 7 | import { interpreter, refresh } from '../utils'; 8 | 9 | const active: { [key: string]: OutputChannel } = {}; 10 | 11 | export function vacuum() { 12 | for (const channel of Object.values(active)) { 13 | channel.dispose(); 14 | } 15 | } 16 | 17 | export async function show(node: TargetItem) { 18 | function cmdChannel(name: string, bin: string, args: string[]) { 19 | if (name in active) { 20 | return active[name]; 21 | } 22 | 23 | const channel = window.createOutputChannel(name); 24 | const child = cp.spawn(bin, args); 25 | const write = (data: Buffer) => channel.append(data.toString()); 26 | child.stdout.on('data', write); 27 | child.stderr.on('data', write); 28 | 29 | const actionClose = l10n.t('Close Console'); 30 | const actionIgnore = l10n.t('Dismiss'); 31 | const question = l10n.t('Console {0} is already open, do you want to close it?', name); 32 | 33 | child.on('close', () => 34 | window.showWarningMessage( 35 | question, actionClose, actionIgnore).then(option => { 36 | if (option === actionClose) { 37 | channel.dispose(); 38 | } 39 | refresh(); 40 | })); 41 | return channel; 42 | } 43 | 44 | let bundleOrPid: string[]; 45 | if (node instanceof AppItem) { 46 | bundleOrPid = ['--app', node.data.identifier]; 47 | } else if (node instanceof ProcessItem) { 48 | bundleOrPid = ['--pid', node.data.pid.toString()]; 49 | } else { 50 | window.showErrorMessage(l10n.t('Invalid target {0}', `${node.label}`)); 51 | return; 52 | } 53 | 54 | const type = await os(node.device.id); 55 | if (type === 'ios' && node.device.type === DeviceType.USB) { 56 | lockdownSyslog(node.device.id, bundleOrPid); 57 | } else if (type === 'linux' || type === 'macos') { 58 | const args = [driverScript(), 'syslog', '--device', node.device.id, ...bundleOrPid]; 59 | const python3 = await interpreter(); 60 | const title = l10n.t('Output: {0} ({1})', node.data.name, node.device.name); 61 | cmdChannel(title, python3, args).show(); 62 | } else if (type === 'android') { 63 | if (node.data.pid > 0) { 64 | const args = ['-s', node.device.id, 'logcat', `--pid=${node.data.pid}`]; 65 | const title = l10n.t('Output: {0} ({1})', node.data.name, node.device.name); 66 | cmdChannel(title, 'adb', args).show(); 67 | } else { 68 | // todo: 69 | // adb shell monkey -p BUNDLE 1 70 | // adb shell pidof BUNDLE 71 | window.showErrorMessage(l10n.t('{0} ({1}) is not running', node.data.name, node.device.name)); 72 | } 73 | } else { 74 | window.showErrorMessage(l10n.t('Unsupported device type {0}', node.device.type)); 75 | } 76 | } -------------------------------------------------------------------------------- /src/commands/typing.ts: -------------------------------------------------------------------------------- 1 | import { createWriteStream, promises as fsp } from 'fs'; 2 | import { get as httpGet } from 'https'; 3 | import { join } from 'path'; 4 | import { Position, ProgressLocation, window, workspace, l10n } from 'vscode'; 5 | import { cmd } from '../utils'; 6 | 7 | function npmInstall() { 8 | const name = l10n.t('Install typescript typings'); 9 | const shellPath = cmd('npm'); 10 | const shellArgs = ['install', '@types/frida-gum', '--save']; 11 | const term = window.createTerminal({ 12 | name, 13 | shellPath, 14 | shellArgs, 15 | }); 16 | window.onDidCloseTerminal(t => { 17 | if (t === term && t.exitStatus?.code === 0) { 18 | window.showInformationMessage( 19 | l10n.t('@types/frida-gum has been successfully installed')); 20 | } 21 | }) 22 | term.show(); 23 | } 24 | 25 | async function downloadTypeHint(cwd: string) { 26 | const URL = 'https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/master/types/frida-gum/index.d.ts'; 27 | const NAME = 'frida-gum.d.ts'; 28 | 29 | // will override existing one 30 | const dest = join(cwd, NAME); 31 | const stream = createWriteStream(dest); 32 | 33 | await window.withProgress({ 34 | location: ProgressLocation.Notification, 35 | title: l10n.t("Downloading typing info for frida-gum"), 36 | cancellable: false, 37 | }, (progress, token) => { 38 | return new Promise(resolve => { 39 | const req = httpGet(URL, resp => { 40 | const rawLen = resp.headers['content-length']; 41 | const len = rawLen ? parseInt(rawLen, 10) : NaN; 42 | resp.pipe(stream); 43 | 44 | if (rawLen) { 45 | resp.on('data', chunk => 46 | progress.report({ 47 | increment: chunk.length / len * 100 48 | }) 49 | ); 50 | } 51 | }); 52 | 53 | req 54 | .on('finish', async () => { 55 | resolve(NAME); 56 | }) 57 | .on('error', (err) => { 58 | stream.close(); 59 | window.showErrorMessage( 60 | l10n.t('Failed to download typing info for frida-gum: {0}', err.message)); 61 | fsp.unlink(dest); 62 | }); 63 | }); 64 | }); 65 | 66 | return dest; 67 | } 68 | 69 | 70 | export async function init() { 71 | if (workspace.workspaceFolders?.length) { 72 | npmInstall(); 73 | return; 74 | } 75 | 76 | const editor = window.activeTextEditor; 77 | if (!editor) { 78 | window.showErrorMessage(l10n.t('The command requires a workspace or an active document')); 79 | return; 80 | } 81 | 82 | const doc = editor.document; 83 | if (!['javascript', 'typescript'].includes(doc.languageId)) { 84 | window.showErrorMessage(l10n.t('This document is not Javascript or TypeScript')); 85 | return; 86 | } 87 | 88 | const fileUri = doc.uri; 89 | if (!fileUri) { 90 | window.showErrorMessage(l10n.t('The current document is unsaved')); 91 | return; 92 | } 93 | 94 | const folder = workspace.getWorkspaceFolder(fileUri); 95 | const cwd = folder ? folder.uri.fsPath : join(fileUri.fsPath, '..'); 96 | const name = await downloadTypeHint(cwd); 97 | await editor.edit(e => { 98 | e.insert(new Position(0, 0), `/// \n`); 99 | }); 100 | editor.document.save(); 101 | } 102 | -------------------------------------------------------------------------------- /src/driver/adb.ts: -------------------------------------------------------------------------------- 1 | import * as cp from 'child_process'; 2 | import * as vscode from 'vscode'; 3 | import { run } from '../term'; 4 | import { executable } from "../utils"; 5 | 6 | export default class ADB { 7 | path: string; 8 | 9 | constructor(private device: string) { 10 | this.path = executable('adb'); 11 | 12 | if (!this.path) { 13 | const msg = vscode.l10n.t('Could not find command adb in $PATH'); 14 | vscode.window.showErrorMessage(msg); 15 | throw new Error(msg); 16 | } 17 | } 18 | 19 | async cmd(action: string, ...args: string[]) { 20 | const shellPath = this.path; 21 | const shellArgs = ['-s', this.device, action, ...args]; 22 | return run({ 23 | shellPath, 24 | shellArgs 25 | }); 26 | } 27 | 28 | async push(local: vscode.Uri, remote: string) { 29 | return this.cmd('push', local.fsPath, remote); 30 | } 31 | 32 | async pull(remote: string, local: vscode.Uri) { 33 | return this.cmd('pull', remote, local.fsPath); 34 | } 35 | 36 | interactive(...cmd: string[]) { 37 | const name = 'adb'; 38 | const shellPath = this.path; 39 | const shellArgs = ['-s', this.device, 'shell', ...cmd]; 40 | const term = vscode.window.createTerminal({ 41 | name, 42 | shellPath, 43 | shellArgs 44 | }); 45 | term.show(); 46 | return term; 47 | } 48 | 49 | async shell(...cmd: string[]) { 50 | const shellPath = this.path; 51 | const shellArgs = ['-s', this.device, 'shell', ...cmd]; 52 | 53 | return new Promise((resolve, reject) => { 54 | cp.execFile(shellPath, shellArgs, (err, stdout, stderr) => { 55 | if (err) { 56 | reject(err); 57 | } else { 58 | resolve(stdout); 59 | } 60 | }); 61 | }); 62 | } 63 | } -------------------------------------------------------------------------------- /src/driver/backend.ts: -------------------------------------------------------------------------------- 1 | import { l10n } from 'vscode'; 2 | import { join } from 'path'; 3 | import { execFile } from 'child_process'; 4 | 5 | import { asParam } from './remote'; 6 | import { logger } from '../logger'; 7 | import { run } from '../term'; 8 | import { interpreter } from '../utils'; 9 | import { App, Device, Process } from '../types'; 10 | import { AppItem, ProcessItem, TargetItem } from '../providers/devices'; 11 | 12 | const base = join(__dirname, '..', '..', 'backend'); 13 | const py = join(base, 'driver.py'); 14 | 15 | export const driverScript = () => py; 16 | 17 | export function exec(...args: string[]): Promise { 18 | const remoteDevices = asParam(); 19 | 20 | return interpreter().then(path => new Promise((resolve, reject) => { 21 | execFile(path, [py, ...remoteDevices, ...args], { maxBuffer: 1024 * 1024 * 20 }, (err, stdout, stderr) => { 22 | if (err) { 23 | logger.appendLine(`Error: Failed to execute driver, arguments: ${args.join(' ')}`); 24 | logger.appendLine(stderr); 25 | logger.appendLine(`Exited with ${err.code}`); 26 | reject(new Error(stderr)); 27 | } else { 28 | resolve(JSON.parse(stdout)); 29 | } 30 | }); 31 | })); 32 | } 33 | 34 | export function devices() { 35 | return exec('devices') as Promise; 36 | } 37 | 38 | export function apps(id: string) { 39 | return exec('apps', id) as Promise; 40 | } 41 | 42 | export function ps(id: string) { 43 | return exec('ps', id) as Promise; 44 | } 45 | 46 | export async function os(id: string) { 47 | interface Result { 48 | os: { 49 | version: string; 50 | id: 'ios' | 'macos' | 'windows' | 'linux' | 'android'; 51 | name: string; 52 | }; 53 | }; 54 | 55 | const result = await exec('info', id) as Result; 56 | return result.os.id; 57 | } 58 | 59 | export function location(id: string, bundle: string) { 60 | return exec('location', id, bundle); 61 | } 62 | 63 | export function rpc(target: TargetItem, method: string, ...args: string[]) { 64 | let bundleOrPid: string[]; 65 | if (target instanceof AppItem) { 66 | bundleOrPid = ['--app', target.data.identifier]; 67 | } else if (target instanceof ProcessItem) { 68 | bundleOrPid = ['--pid', target.data.pid.toString()]; 69 | } else { 70 | throw new Error(l10n.t('Invalid target {0}', target)); 71 | } 72 | 73 | return exec('rpc', '--device', target.device.id, ...bundleOrPid, method, ...args); 74 | } 75 | 76 | export async function lockdownSyslog(id: string, bundleOrPid: string[]) { 77 | const shellPath = await interpreter(); 78 | return run({ 79 | name: `Syslog: ${bundleOrPid}`, 80 | shellPath, 81 | shellArgs: [py, 'syslog2', '--device', id, ...bundleOrPid] 82 | }); 83 | } 84 | -------------------------------------------------------------------------------- /src/driver/frida.ts: -------------------------------------------------------------------------------- 1 | import { l10n } from 'vscode'; 2 | import { execFile } from 'child_process'; 3 | import { interpreter } from '../utils'; 4 | 5 | 6 | function deviceParam(device: string) { 7 | const prefix = 'socket@'; 8 | return device.startsWith(prefix) ? 9 | ['-H', device.substring(prefix.length)] : 10 | ['--device', device]; 11 | } 12 | 13 | export async function launch(device: string, bundle: string): Promise { 14 | const params = ['-f', bundle, ...deviceParam(device), bundle, '-q', '-e', 'Process.id']; 15 | const py3 = await interpreter(); 16 | const args = ['-m', 'frida_tools.repl', ...params]; 17 | 18 | return new Promise((resolve, reject) => { 19 | execFile(py3, args, {}, (err, stdout) => { 20 | if (err) { 21 | reject(err); 22 | } else { 23 | const lines = stdout.split('\n'); 24 | if (lines.length <= 2) { 25 | reject(new Error(l10n.t('Unknown output: {0}', stdout))); 26 | } 27 | resolve(parseInt(lines[1], 10)); 28 | } 29 | }); 30 | }); 31 | } 32 | 33 | export async function terminate(device: string, target: string) { 34 | const py3 = await interpreter('frida-kill'); 35 | const args = ['-m', 'frida_tools.kill', ...deviceParam(device), target]; 36 | return new Promise((resolve, reject) => { 37 | execFile(py3, args, {}, (err, stdout) => { 38 | if (err) { 39 | reject(err); 40 | } else { 41 | resolve(stdout); 42 | } 43 | }); 44 | }); 45 | } 46 | 47 | -------------------------------------------------------------------------------- /src/driver/remote.ts: -------------------------------------------------------------------------------- 1 | import { workspace } from "vscode"; 2 | 3 | const remoteHosts = new Set(); 4 | function loadRemoteHosts() { 5 | const hosts = workspace.getConfiguration('frida').get>('remoteHosts'); 6 | if (Array.isArray(hosts)) { 7 | hosts.forEach(host => { 8 | if (typeof host === 'string') { 9 | remoteHosts.add(host); 10 | } 11 | }); 12 | } 13 | } 14 | 15 | loadRemoteHosts(); 16 | 17 | function saveRemoteHosts() { 18 | workspace.getConfiguration('frida').update('remoteHosts', Array.from(remoteHosts), true); 19 | } 20 | 21 | export function connect(remote: string) { 22 | remoteHosts.add(remote); 23 | saveRemoteHosts(); 24 | } 25 | 26 | export function disconnect(remote: string) { 27 | remoteHosts.delete(remote); 28 | saveRemoteHosts(); 29 | } 30 | 31 | export function all() { 32 | return Array.from(remoteHosts); 33 | } 34 | 35 | export function asParam() { 36 | return remoteHosts.size > 0 ? ['--remote', Array.from(remoteHosts).join(',')] : [] 37 | } -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | // The module 'vscode' contains the VS Code extensibility API 2 | // Import the module and reference it with the alias vscode in your code below 3 | import * as vscode from 'vscode'; 4 | 5 | import { DevicesProvider } from './providers/devices'; 6 | import { ProviderType } from './types'; 7 | 8 | import * as android from './commands/android'; 9 | import * as boilerplate from './commands/boilerplate'; 10 | import * as clipboard from './commands/clipboard'; 11 | import dump from './commands/dump'; 12 | import * as objection from './commands/objection'; 13 | import * as repl from './commands/repl'; 14 | import * as ssh from './commands/ssh'; 15 | import * as syslog from './commands/syslog'; 16 | import * as typing from './commands/typing'; 17 | import * as print from './commands/print'; 18 | 19 | export function activate(context: vscode.ExtensionContext) { 20 | const register = (cmd: string, cb: (...args: any[]) => any) => vscode.commands.registerCommand(cmd, cb); 21 | const push = (item: vscode.Disposable) => context.subscriptions.push(item); 22 | 23 | const appsProvider = new DevicesProvider(ProviderType.Apps); 24 | vscode.window.registerTreeDataProvider('fridaApps', appsProvider); 25 | 26 | push(register('frida.apps.refresh', () => appsProvider.refresh())); 27 | 28 | const psProvider = new DevicesProvider(ProviderType.Processes); 29 | vscode.window.registerTreeDataProvider('fridaPs', psProvider); 30 | 31 | push(register('frida.ps.refresh', () => psProvider.refresh())); 32 | push(register('frida.spawn', repl.spawn)); 33 | push(register('frida.spawn.suspended', repl.spawnSuspended)); 34 | push(register('frida.attach', repl.attach)); 35 | push(register('frida.kill', repl.kill)); 36 | push(register('frida.remote.add', repl.addRemote)); 37 | push(register('frida.remote.remove', repl.delRemote)); 38 | 39 | push(register('frida.syslog', syslog.show)); 40 | push(register('frida.syslog.vacuum', syslog.vacuum)); 41 | 42 | push(register('frida.bundle.copy', clipboard.copy)); 43 | push(register('frida.name.copy', clipboard.copy)); 44 | push(register('frida.pid.copy', clipboard.copyPid)); 45 | push(register('frida.device.copy', clipboard.copy)); 46 | 47 | push(register('frida.device.androidserver', android.startServer)); 48 | push(register('frida.external.objection', objection.explore)); 49 | push(register('frida.external.dump', dump)); 50 | push(register('frida.external.apk', dump)); 51 | 52 | push(register('frida.external.shell', ssh.shell)); 53 | 54 | push(register('frida.boilerplate.agent', boilerplate.agent)); 55 | push(register('frida.boilerplate.module', boilerplate.module)); 56 | push(register('frida.debug.setup', boilerplate.debug)); 57 | 58 | push(register('frida.typing.init', typing.init)); 59 | 60 | push(register('frida.print.classes', print.classes)); 61 | push(register('frida.print.modules', print.modules)); 62 | } 63 | 64 | // this method is called when your extension is deactivated 65 | export function deactivate() { 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/logger.ts: -------------------------------------------------------------------------------- 1 | import { window, l10n } from 'vscode'; 2 | 3 | export const logger = window.createOutputChannel(l10n.t('Frida Extension')); 4 | -------------------------------------------------------------------------------- /src/providers/devices.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | import * as ipc from '../driver/backend'; 4 | 5 | import { resource } from '../utils'; 6 | import { ProviderType, App, Process, Device, DeviceType } from '../types'; 7 | 8 | export class DevicesProvider implements vscode.TreeDataProvider { 9 | 10 | private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter(); 11 | readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; 12 | 13 | constructor( 14 | public readonly type: ProviderType 15 | ) { 16 | 17 | } 18 | 19 | refresh(): void { 20 | this._onDidChangeTreeData.fire(undefined); 21 | } 22 | 23 | getTreeItem(element: TargetItem): vscode.TreeItem { 24 | return element; 25 | } 26 | 27 | getChildren(element?: TargetItem): Thenable { 28 | if (element) { 29 | return element.children(); 30 | } else { 31 | return ipc.devices().then(devices => devices.map(device => new DeviceItem(device, this.type))); 32 | } 33 | } 34 | } 35 | 36 | export abstract class TargetItem extends vscode.TreeItem { 37 | abstract children(): Thenable; 38 | abstract tooltip?: string; 39 | abstract description?: string; 40 | abstract iconPath?: string | vscode.IconPath | undefined; 41 | abstract contextValue?: string | undefined; 42 | } 43 | 44 | export class DeviceItem extends TargetItem { 45 | constructor( 46 | public readonly data: Device, 47 | public readonly type: ProviderType, 48 | ) { 49 | super(data.name, vscode.TreeItemCollapsibleState.Collapsed); 50 | } 51 | 52 | get tooltip(): string { 53 | return `${this.data.id} (${this.data.type})`; 54 | } 55 | 56 | get description(): string { 57 | return this.data.id; 58 | } 59 | 60 | get iconPath() { 61 | return { 62 | light: resource('light', `${this.data.type}.svg`), 63 | dark: resource('dark', `${this.data.type}.svg`), 64 | }; 65 | } 66 | 67 | children(): Thenable { 68 | const device = this.data; 69 | if (this.type === ProviderType.Apps) { 70 | return ipc.apps(device.id) 71 | .then(apps => apps.map(app => new AppItem(app, device))) 72 | .catch(e => [new NotFound(e, device)]); 73 | } else if (this.type === ProviderType.Processes) { 74 | return ipc.ps(device.id) 75 | .then(ps => ps.map(p => new ProcessItem(p, device))) 76 | .catch(e => [new NotFound(e, device)]); 77 | } 78 | return Promise.resolve([]); 79 | } 80 | 81 | get contextValue() { 82 | const { type, os } = this.data; 83 | return `device|${type}|${os}`; 84 | } 85 | 86 | } 87 | 88 | enum AppState { 89 | Running = 'running', 90 | Dead = 'dead', 91 | }; 92 | 93 | export class NotFound extends TargetItem { 94 | constructor( 95 | public readonly reason: Error, 96 | public readonly device: Device, 97 | ) { 98 | super(reason.message, vscode.TreeItemCollapsibleState.None); 99 | } 100 | 101 | children(): Thenable { 102 | return Promise.resolve([]); 103 | } 104 | 105 | get tooltip() { return this.reason.message; } 106 | 107 | iconPath = { 108 | dark: resource('dark', 'error.svg'), 109 | light: resource('light', 'error.svg') 110 | }; 111 | 112 | contextValue = 'empty'; 113 | 114 | description = ''; 115 | } 116 | 117 | export class AppItem extends TargetItem { 118 | constructor( 119 | public readonly data: App, 120 | public readonly device: Device, 121 | ) { 122 | super(data.name, vscode.TreeItemCollapsibleState.None); 123 | } 124 | 125 | get tooltip(): string { 126 | return `${this.label} (${this.data.pid || vscode.l10n.t('Not Running')})`; 127 | } 128 | 129 | get description(): string { 130 | return this.data.identifier; 131 | } 132 | 133 | children(): Thenable { 134 | return Promise.resolve([]); 135 | } 136 | 137 | get iconPath() { 138 | return this.data.icon ? vscode.Uri.parse(this.data.icon) : resource('terminal.png'); 139 | } 140 | 141 | get contextValue() { 142 | return `app|${this.data.pid ? AppState.Running : AppState.Dead}|${this.device.os}`; 143 | } 144 | } 145 | 146 | export class ProcessItem extends TargetItem { 147 | constructor( 148 | public readonly data: Process, 149 | public readonly device: Device, 150 | ) { 151 | super(data.name, vscode.TreeItemCollapsibleState.None); 152 | } 153 | 154 | get tooltip(): string { 155 | return `${this.label} (${this.data.pid})`; 156 | } 157 | 158 | get description(): string { 159 | return this.data.pid.toString(); 160 | } 161 | 162 | children(): Thenable { 163 | return Promise.resolve([]); 164 | } 165 | 166 | get iconPath() { 167 | return this.data.icon ? vscode.Uri.parse(this.data.icon) : resource('terminal.png'); 168 | } 169 | 170 | get contextValue() { 171 | return `process|${this.data.pid ? AppState.Running : AppState.Dead}|${this.device.os}`; 172 | } 173 | } -------------------------------------------------------------------------------- /src/shebang.ts: -------------------------------------------------------------------------------- 1 | import { promises as fs } from 'node:fs'; 2 | 3 | const SHEBANG_UNIX = '#!'; 4 | const BUF_SHEBANG_UNIX = Buffer.from(SHEBANG_UNIX); 5 | const PE_MAGIC = Buffer.from('MZ'); 6 | 7 | // const ZIP_SIGNATURE = Buffer.from([0x50, 0x4B, 0x05, 0x06]); 8 | 9 | // typedef struct { 10 | // DWORD sig; 11 | // DWORD unused_disk_nos; 12 | // DWORD unused_numrecs; 13 | // DWORD cdsize; 14 | // DWORD cdoffset; 15 | // } ENDCDR; 16 | 17 | // https://github.com/pypa/distlib/blob/32301789a7815de0e74f57fe013ae52af717a3da/PC/launcher.c#L177 18 | // 19 | // the implementation is so cursed that we might as well simply search for #! 20 | 21 | function parseWindowsLauncher(buffer: Buffer) { 22 | let subarray = buffer; 23 | 24 | while (true) { 25 | let p = subarray.lastIndexOf(BUF_SHEBANG_UNIX); 26 | if (p === -1) { throw new Error('Invalid shebang'); } 27 | 28 | const end = subarray.indexOf('\n', p); 29 | if (end === -1) { throw new Error('Invalid shebang'); } 30 | 31 | const line = subarray.subarray(p, end).toString().trim(); 32 | if (line.endsWith('.exe')) { 33 | return interpreter(line); 34 | } 35 | 36 | subarray = subarray.subarray(end + 1); 37 | } 38 | } 39 | 40 | function interpreter(line: string) { 41 | if (!line.startsWith(SHEBANG_UNIX)) { throw new Error('Invalid shebang'); } 42 | return line.substring(SHEBANG_UNIX.length); 43 | } 44 | 45 | export default async function shebang(path: string) { 46 | const buffer = await fs.readFile(path); 47 | const magic = buffer.subarray(0, SHEBANG_UNIX.length); 48 | 49 | if (magic.compare(BUF_SHEBANG_UNIX) === 0) { 50 | const lineEnd = buffer.indexOf('\n'); 51 | return interpreter(buffer.subarray(0, lineEnd).toString()); 52 | } else if (magic.compare(PE_MAGIC) === 0) { 53 | return parseWindowsLauncher(buffer); 54 | } 55 | 56 | throw new Error('Invalid file format'); 57 | } 58 | 59 | -------------------------------------------------------------------------------- /src/term.ts: -------------------------------------------------------------------------------- 1 | import { TerminalOptions, window } from "vscode"; 2 | 3 | export class CommandNotFoundError extends Error { 4 | constructor(cmd: string) { 5 | super(`Command not found: ${cmd}`); 6 | } 7 | } 8 | 9 | export function run(opt: TerminalOptions) { 10 | const ext: TerminalOptions = { hideFromUser: true }; 11 | return new Promise((resolve, reject) => { 12 | const term = window.createTerminal(Object.assign(opt, ext)); 13 | 14 | const disposable = window.onDidCloseTerminal(terminal => { 15 | if (terminal !== term) { return; } 16 | 17 | if (term.exitStatus?.code === undefined && opt.shellPath) { 18 | reject(new CommandNotFoundError(opt.shellPath)); 19 | } else if (term.exitStatus?.code !== 0) { 20 | reject(new Error(`Terminal exited with status ${term.exitStatus?.code}`)); 21 | } else { 22 | resolve(); 23 | } 24 | disposable.dispose(); 25 | }); 26 | term.show(); 27 | }); 28 | } -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | 2 | export enum ProviderType { 3 | Apps = 'apps', 4 | Processes = 'ps', 5 | } 6 | 7 | export enum DeviceType { 8 | Local = 'local', 9 | Remote = 'remote', 10 | USB = 'usb', 11 | TCP = 'tcp', // legacy 12 | } 13 | 14 | export class Device { 15 | id: string = ''; 16 | name: string = ''; 17 | os: 'ios' | 'macos' | 'windows' | 'linux' | 'android' | 'n/a' = 'n/a'; 18 | type: DeviceType = DeviceType.Local; 19 | icon: string = ''; 20 | } 21 | 22 | export class App { 23 | identifier: string = ''; 24 | name: string = ''; 25 | pid: number = 0; 26 | icon: string = ''; 27 | } 28 | 29 | export class Process { 30 | name: string = ''; 31 | pid: number = 0; 32 | icon: string = ''; 33 | } -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import which from 'which'; 2 | import * as vscode from 'vscode'; 3 | import * as cp from 'child_process'; 4 | import { join } from 'path'; 5 | import { platform } from 'os'; 6 | import { DeviceType } from './types'; 7 | import { AppItem, ProcessItem } from './providers/devices'; 8 | 9 | import shebang from './shebang'; 10 | 11 | export function resource(...paths: string[]): vscode.Uri { 12 | const file = join(__dirname, '..', 'resources', ...paths); 13 | return vscode.Uri.file(file); 14 | } 15 | 16 | export function sleep(ms: number) { 17 | return new Promise(resolve => setTimeout(resolve, ms)); 18 | } 19 | 20 | export function refresh() { 21 | vscode.commands.executeCommand('frida.ps.refresh'); 22 | vscode.commands.executeCommand('frida.apps.refresh'); 23 | } 24 | 25 | export function cmd(name: string) { 26 | return name + (platform() === 'win32' ? '.cmd' : ''); 27 | } 28 | 29 | export function executable(name: string) { 30 | return name + (platform() === 'win32' ? '.exe' : ''); 31 | } 32 | 33 | interface PythonExtensionApi { 34 | settings: { 35 | getExecutionDetails(): { execCommand: string[] }; 36 | get onDidChangeExecutionDetails(): vscode.Event; 37 | }; 38 | } 39 | 40 | async function virtualenv(): Promise { 41 | const pyext = vscode.extensions.getExtension('ms-python.python'); 42 | if (!pyext) { 43 | throw new Error(vscode.l10n.t('Python extension not found')); 44 | } 45 | 46 | const api = pyext.exports as PythonExtensionApi; 47 | const { execCommand } = api.settings.getExecutionDetails(); 48 | if (!execCommand) { 49 | throw new Error(vscode.l10n.t('Python extension not activated')); 50 | } 51 | 52 | api.settings.onDidChangeExecutionDetails(() => { 53 | cache.clear(); 54 | }); 55 | 56 | const cmd = [...execCommand, '-c', 'import frida_tools;import sys;print(sys.executable)']; 57 | 58 | return new Promise((resolve, reject) => { 59 | cp.execFile(cmd[0], cmd.slice(1), {}, (err, stdout) => { 60 | if (err) { 61 | reject(err); 62 | } else { 63 | resolve(stdout.trim()); 64 | } 65 | }); 66 | }); 67 | } 68 | 69 | const cache = new Map(); 70 | 71 | export async function interpreter(cli = 'frida'): Promise { 72 | if (cache.has(cli)) { return Promise.resolve(cache.get(cli) as string); } 73 | 74 | try { 75 | return await virtualenv(); 76 | } catch (_) { 77 | // fallback to global package 78 | } 79 | 80 | const where = await which(cli, { all: false, nothrow: true }); 81 | if (!where) { 82 | const msg = vscode.l10n.t('Could not find command {0} in $PATH, have you installed it or activated the correct venv?', cli); 83 | throw new Error(msg); 84 | } 85 | 86 | const path = await shebang(where); 87 | cache.set(cli, path); 88 | return path; 89 | } 90 | 91 | export function expandDevParam(node: AppItem | ProcessItem) { 92 | switch (node.device.type) { 93 | case DeviceType.Local: 94 | return []; 95 | case DeviceType.Remote: 96 | return ['-H', node.device.id.substring('socket@'.length)]; 97 | case DeviceType.USB: 98 | // return ['-U']; 99 | default: 100 | return ['--device', node.device.id]; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "esModuleInterop": true, 4 | "experimentalDecorators": true, 5 | "module": "commonjs", 6 | "target": "es6", 7 | "outDir": "out", 8 | "lib": [ 9 | "es6" 10 | ], 11 | "sourceMap": true, 12 | "rootDir": "src", 13 | "strict": true /* enable all strict type-checking options */ 14 | /* Additional Checks */ 15 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 16 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 17 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 18 | }, 19 | "exclude": [ 20 | "node_modules", 21 | ".vscode-test", 22 | "server", 23 | "agent" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-string-throw": true, 4 | "no-unused-expression": true, 5 | "no-duplicate-variable": true, 6 | "curly": true, 7 | "class-name": true, 8 | "semicolon": [ 9 | true, 10 | "always" 11 | ], 12 | "triple-equals": true 13 | }, 14 | "defaultSeverity": "warning" 15 | } 16 | -------------------------------------------------------------------------------- /types.d.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChiChou/vscode-frida/8f69a68e1c1e0955ef8be77a1246ea6ee9dc686f/types.d.ts --------------------------------------------------------------------------------