├── .github └── FUNDING.yml ├── .gitignore ├── INSTALLER.sh ├── LICENSE ├── install_termux.sh ├── readme.md └── src ├── flutterspy └── lib ├── asset_config_files ├── content_files ├── decompile ├── env_files ├── info_from_libapp ├── spy ├── utils └── visual_asset_files /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [anasfik] 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.apk -------------------------------------------------------------------------------- /INSTALLER.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # TODO: check for required deps. 4 | 5 | 6 | cp -r src .flutter-spy 7 | chmod +x ./.flutter-spy/flutterspy 8 | mv .flutter-spy ~/ 9 | 10 | new_path="export PATH=\$PATH:~/.flutter-spy" 11 | 12 | if grep -Fxq "$new_path" ~/.bashrc 13 | then 14 | echo "Path already exists in .bashrc" 15 | exit 1 16 | else 17 | 18 | onePresent=0 19 | 20 | if [ -f ~/.zshrc ]; then 21 | echo "$new_path" >> ~/.zshrc 22 | source ~/.zshrc 23 | echo "Path added to .zshrc" 24 | onePresent=1 25 | fi 26 | 27 | if [ -f ~/.bashrc ]; then 28 | echo "$new_path" >> ~/.bashrc 29 | source ~/.bashrc 30 | echo "Path added to .bashrc" 31 | onePresent=1 32 | fi 33 | 34 | if [ -f ~/.profile ]; then 35 | echo "$new_path" >> ~/.profile 36 | source ~/.profile 37 | echo "Path added to .profile" 38 | onePresent=1 39 | fi 40 | 41 | if [ $onePresent -eq 0 ]; then 42 | echo "No .bashrc, .zshrc or .profile found. Please add the following line to your shell config file:" 43 | echo "$new_path" 44 | exit 1 45 | fi 46 | fi 47 | 48 | echo "Flutter Spy installed successfully!" 49 | echo "open a new terminal and run 'flutterspy' to get started." 50 | 51 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Anas FIKHI 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /install_termux.sh: -------------------------------------------------------------------------------- 1 | if ! command -v termux-setup-storage; then 2 | echo "This script can be executed only on Termux" 3 | exit 1 4 | fi 5 | 6 | termux-setup-storage 7 | 8 | cd $HOME 9 | pkg update 10 | pkg upgrade -y 11 | pkg i -y ncurses-utils 12 | 13 | green="$(tput setaf 2)" 14 | nocolor="$(tput sgr0)" 15 | red="$(tput setaf 1)" 16 | blue="$(tput setaf 32)" 17 | yellow="$(tput setaf 3)" 18 | note="$(tput setaf 6)" 19 | 20 | echo "${green}━━━ Basic Requirements Setup ━━━${nocolor}" 21 | 22 | pkg i -y python git cmake rust golang clang make wget ndk-sysroot zlib libxml2 libxslt pkg-config python-cryptography libjpeg-turbo which 23 | LDFLAGS="-L${PREFIX}/lib/" CFLAGS="-I${PREFIX}/include/" pip install --upgrade wheel pillow 24 | pip install cython setuptools 25 | CFLAGS="-Wno-error=incompatible-function-pointer-types -O0" pip install lxml 26 | 27 | echo "${green}━━━ Starting JADX installation ━━━${nocolor}" 28 | if [ -d "$PREFIX/lib/jadx" ]; then 29 | echo "${red}Seems like JADX is already installed, skipping...${nocolor}" 30 | else 31 | cd $HOME 32 | wget https://github.com/skylot/jadx/releases/download/v1.4.7/jadx-1.4.7.zip 33 | mkdir -p $PREFIX/lib/jadx 34 | unzip jadx-1.4.7.zip -d $PREFIX/lib/jadx 35 | fi 36 | echo "${yellow}ANDROID SDK TOOLS Successfully Installed!${nocolor}" 37 | 38 | echo "${green}━━━ Starting SDK Tools installation ━━━${nocolor}" 39 | if [ -d "android-sdk" ]; then 40 | echo "${red}Seems like sdk tools already installed, skipping...${nocolor}" 41 | elif [ -d "androidide-tools" ]; then 42 | rm -rf androidide-tools 43 | git clone https://github.com/AndroidIDEOfficial/androidide-tools 44 | cd androidide-tools/scripts 45 | ./idesetup -c 46 | else 47 | git clone https://github.com/AndroidIDEOfficial/androidide-tools 48 | cd androidide-tools/scripts 49 | ./idesetup -c 50 | fi 51 | 52 | echo "${yellow}ANDROID SDK TOOLS Successfully Installed!${nocolor}" 53 | 54 | cd $HOME 55 | echo 56 | echo "${green}━━━ Setting up apktool ━━━${nocolor}" 57 | if [ -f "$PREFIX/bin/apktool.jar" ]; then 58 | echo "${blue}apktool is already installed${nocolor}" 59 | else 60 | sh -c 'wget https://bitbucket.org/iBotPeaches/apktool/downloads/apktool_2.9.1.jar -O $PREFIX/bin/apktool.jar' 61 | 62 | chmod +r $PREFIX/bin/apktool.jar 63 | 64 | sh -c 'wget https://raw.githubusercontent.com/iBotPeaches/Apktool/master/scripts/linux/apktool -O $PREFIX/bin/apktool' && chmod +x $PREFIX/bin/apktool || exit 2 65 | fi 66 | echo "${yellow}apktool Successfully Installed!${nocolor}" 67 | 68 | echo 69 | echo "${green}━━━ Starting flutter-spy installation ━━━${nocolor}" 70 | cd $HOME 71 | if [ -d "flutter-spy" ]; then 72 | rm -rf flutter-spy 73 | git clone https://github.com/anasfik/flutter-spy 74 | cd flutter-spy 75 | chmod +x INSTALLER.sh 76 | ./INSTALLER.sh || exit 2 77 | else 78 | git clone https://github.com/anasfik/flutter-spy 79 | cd flutter-spy 80 | chmod +x INSTALLER.sh 81 | ./INSTALLER.sh || exit 2 82 | fi 83 | 84 | echo "${yellow}Made with ⌨ by Abhi${nocolor}" 85 | 86 | if [ -f "~/.bashrc" ]; then 87 | cat <<- EOL >> ~/.bashrc 88 | export ANDROID_HOME=$HOME/android-sdk 89 | export PATH=$PATH:$HOME/android-sdk/cmdline-tools/latest/bin 90 | export PATH=$PATH:$HOME/android-sdk/platform-tools 91 | export PATH=$PATH:$HOME/android-sdk/build-tools/34.0.4 92 | export PATH=$PATH:$HOME/android-sdk/ndk/$ndk_version 93 | export PATH=$PATH:$PREFIX/lib/jadx/bin 94 | export ANDROID_NDK_ROOT=$HOME/android-sdk/ndk/$ndk_version 95 | EOL 96 | elif [ -f "~/.zshrc" ]; then 97 | cat <<- EOL >> ~/.zshrc 98 | export ANDROID_HOME=$HOME/android-sdk 99 | export PATH=$PATH:$HOME/android-sdk/cmdline-tools/latest/bin 100 | export PATH=$PATH:$HOME/android-sdk/platform-tools 101 | export PATH=$PATH:$HOME/android-sdk/build-tools/34.0.4 102 | export PATH=$PATH:$HOME/android-sdk/ndk/$ndk_version 103 | export PATH=$PATH:$PREFIX/lib/jadx/bin 104 | export ANDROID_NDK_ROOT=$HOME/android-sdk/ndk/$ndk_version 105 | EOL 106 | else 107 | cat <<- EOL >> $PREFIX/etc/bash.bashrc 108 | export ANDROID_HOME=$HOME/android-sdk 109 | export PATH=$PATH:$HOME/android-sdk/cmdline-tools/latest/bin 110 | export PATH=$PATH:$HOME/android-sdk/platform-tools 111 | export PATH=$PATH:$HOME/android-sdk/build-tools/34.0.4 112 | export PATH=$PATH:$HOME/android-sdk/ndk/$ndk_version 113 | export PATH=$PATH:$PREFIX/lib/jadx/bin 114 | export ANDROID_NDK_ROOT=$HOME/android-sdk/ndk/$ndk_version 115 | EOL 116 | fi 117 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Flutter Spy 2 | 3 | ```bash 4 | ________ __ __ _____ 5 | / ____/ __ __/ /_/ /____ _____ / ___/____ __ __ 6 | / /_ / / / / / __/ __/ _ \/ ___/ \__ \/ __ \/ / / / 7 | / __/ / / /_/ / /_/ /_/ __/ / ___/ / /_/ / /_/ / 8 | /_/ /_/\__,_/\__/\__/\___/_/ /____/ .___/\__, / 9 | /_/ /____/ 10 | 11 | Usage: flutter-spy 12 | 13 | Arguments: 14 | file The APK file to spy on 15 | ``` 16 | ## Demo 17 |

18 | 19 |

20 | 21 | ## Overview 22 | 23 | Flutter Spy is a Bash-based command-line tool designed to provide insightful code analysis and data extraction capabilities from built Flutter apps with reverse engineering. It empowers developers, bug hunters, and security enthusiasts to explore and uncover valuable information within Flutter app binaries. 24 | 25 | Flutter Spy supports currently gathering info from Android apps only (APK files), thus it is universal for almost all other platforms since this is what Flutter is used for. 26 | 27 | ## Features 28 | 29 | - [x] Data Extraction: Extracts essential information such as API endpoints, URLs, emails, used packages, phone numbers, secret/public keys, environment variables, and config files. 30 | - [x] Extract emails. 31 | - [x] Extract URLs/URIs. 32 | - [x] Extract potential phone numbers. 33 | - [x] Extract potential API endpoints/routes. 34 | - [x] Extract used Flutter packages and their links at [pub.dev](https://pub.dev/). 35 | - [x] Extract SQL and other database queries... 36 | - [x] Extract `.env` (Environment Variables) files. 37 | - [x] Extract Potential JSON/YAML config files. 38 | - [x] Extract content-specific files. (md, html, css, js...) 39 | - [x] Extract Potential localization files. 40 | - [x] Extract all other Flutter assets (PNG, GIF, SVG, TTF, PSD, WEBP...). 41 | - [ ] Extract used Flutter Engine information as the used Flutter engine version, Dart language version, version hashes... 42 | - [x] Extract the original project file structure. 43 | 44 | - [x] Exportable Reports: Output a detailed report folder with all results. 45 | 46 | - [x] Easy to use: Flutter Spy is designed to be easy to use and requires no prior knowledge of Flutter or Dart, you can use it on any built Flutter app. 47 | 48 | ## Prerequisites 49 | To run Flutter Spy without issues, make sure these utilities are installed globally in your machine. 50 | 51 | - [x] [Jadx CLI](https://github.com/skylot/jadx): Used to decompile the APK file, Flutter Spy will look to get its full path with `which jadx` command. (if you are on Debian-based distros such as Ubuntu/Kali Linux, you can install it via `sudo apt install jadx`) 52 | 53 | **NOTE:** For Termux Installation You don't need to do the installation manually, the installation script will take care of it, also if you've jadx previously installed it'll skip jadx installation. 54 | 55 | ## Installation 56 | 57 | - **Linux:** 58 | Note: Before installing Flutter Spy, please know that Flutter Spy is under active development, there are a lot of [features](#features) that are not implemented or incomplete. 59 | 60 | Run this command in your terminal to install Flutter Spy: 61 | 62 | ```bash 63 | git clone https://github.com/anasfik/flutter-spy.git # Clone the repo 64 | cd flutter-spy # Change the directory to the repo 65 | chmod +x INSTALLER.sh # Make the installer executable 66 | ./INSTALLER.sh # Run the installer 67 | ``` 68 | 69 | - **Android(Termux):** 70 | ```shell 71 | pkg install wget && wget https://raw.githubusercontent.com/anasfik/flutter-spy/main/install_termux.sh && chmod +x install_termux.sh && ./install_termux.sh 72 | ``` 73 | 74 | This will install Flutter Spy in your home directory, at `~/.flutter-spy/`, and add the export path command to the correct profile file (`.bashrc` for Bash, `.zshrc` for ZSH, and `.profile` for Fish). 75 | 76 | Open a new terminal window and run `flutterspy` to make sure that the installation was successful. 77 | 78 | ## Usage 79 | 80 | ```bash 81 | flutter-spy /path/to/apk/file.apk 82 | ``` 83 | 84 | And that's it, the script will do the rest for showing you all findings by exporting a report folder containing all findings is named based on the exact date of export: 85 | 86 | ```bash 87 | report-$(date +%Y-%m-%d-%H-%M-%S) 88 | 89 | # Example: report-2023-12-18-18-17-43 90 | ``` 91 | it will contain everything. 92 | 93 | ## Want to help this? 94 | as a Developer, and, because I don't know everything, you can help this project by informing me about possible findings in a FLutter app other than existing ones, and this is by: 95 | 96 | - if you're a Flutter developer, try this tool on your build app, if you notice that important information like API keys, phrases, assets, and native libraries... are not extracted, then please open an issue with details, it is not necessary to include your app, just expected findings. 97 | - if you're a Bug hunter or Pen tester, and you know any information or vulnerabilities that are previously reported by you or the community, just open an issue and mention it, and I will handle the rest. 98 | - Try the tool on any found Flutter-built apps, then mention any issues or unexpected behaviors you face if any. 99 | 100 | ## License 101 | 102 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. 103 | -------------------------------------------------------------------------------- /src/flutterspy: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 1.0.0 4 | 5 | echo -e "\e[33mFlutter-spy is under active development, please check https://github.com/anasfik/flutter-spy/ for updates.\e[0m" 6 | echo "" 7 | 8 | providedFileName=$(pwd)/$1 9 | 10 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 11 | 12 | source "$DIR/lib/utils" 13 | source "$DIR/lib/spy" 14 | source "$DIR/lib/decompile" 15 | source "$DIR/lib/info_from_libapp" 16 | source "$DIR/lib/env_files" 17 | source "$DIR/lib/asset_config_files" 18 | source "$DIR/lib/content_files" 19 | source "$DIR/lib/visual_asset_files" 20 | 21 | if noArgumentsProvided $@; then 22 | showRootCommandContent 23 | exit 0 24 | else 25 | echo "File name provided: $providedFileName" 26 | fi 27 | 28 | echo $providedFileName 29 | if doesFileExist $providedFileName; then 30 | echo "" 31 | echo "File $providedFileName exists." 32 | else 33 | echo "File $providedFileName does not exist." 34 | exit 1 35 | fi 36 | 37 | 38 | if jadxExist $providedFileName; then 39 | echo "" 40 | echo "Jadx is installed." 41 | else 42 | echo "Jadx is not found, Please install it." 43 | exit 1 44 | fi 45 | 46 | 47 | 48 | if isProvidedFileApk $providedFileName; then 49 | echo "" 50 | echo "File $providedFileName is an apk file." 51 | else 52 | echo "File $providedFileName is not an apk file." 53 | exit 1 54 | fi 55 | 56 | startSpy $providedFileName -------------------------------------------------------------------------------- /src/lib/asset_config_files: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | spyOnAssetConfigFiles() { 4 | decompiledFolderBaseName=$(basename $1) 5 | reportOutputDir=$2 6 | assetFilePaths=$(find "$reportOutputDir/$decompiledFolderBaseName-decompiled" -name "*.json*" -o -name "*.yaml*" -o -name "*.yml*" -o -name "*.xml*" -o -name "*.properties*" -o -name "*.ini*" | grep -Ea "assets" | sort -u) 7 | 8 | 9 | if [ -n "$assetFilePaths" ]; then 10 | echo "" 11 | 12 | mkdir -p $reportOutputDir/config_files 13 | 14 | echo "" 15 | for assetFilePath in $assetFilePaths ; do 16 | onlyFileName=$(basename $assetFilePath) 17 | 18 | echo "Found $onlyFileName file." 19 | 20 | 21 | targetPath=$reportOutputDir/config_files/$onlyFileName 22 | # echo "Copying $assetFilePath to $targetPath" 23 | cp $assetFilePath $targetPath 24 | done 25 | else 26 | echo "No .json, .yaml, .yml, .xml, .properties or .ini files found." 27 | 28 | fi 29 | 30 | 31 | } -------------------------------------------------------------------------------- /src/lib/content_files: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | spyOnContentFiles() { 4 | decompiledFolderBaseName=$(basename $1) 5 | reportOutputDir=$2 6 | contentFilePaths=$(find "$reportOutputDir/$decompiledFolderBaseName-decompiled" -name "*.md*" -o -name "*.txt*" -o -name "*.html*" -o -name "*.htm*" -o -name "*.css*" -o -name "*.js" | grep -Ea "assets" | sort -u) 7 | 8 | if [ -n "$contentFilePaths" ]; then 9 | echo "" 10 | 11 | mkdir -p $reportOutputDir/content_files 12 | 13 | echo "" 14 | for contentFilePath in $contentFilePaths ; do 15 | onlyFileName=$(basename $contentFilePath) 16 | 17 | echo "Found $onlyFileName file." 18 | 19 | targetPath=$reportOutputDir/content_files/file_$onlyFileName 20 | #echo "Copying $contentFilePath to $targetPath" 21 | cp $contentFilePath $targetPath 22 | 23 | done 24 | else 25 | echo "No .md, .txt, .html, .htm, .css or .js files found." 26 | fi 27 | 28 | 29 | } -------------------------------------------------------------------------------- /src/lib/decompile: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | decompileApp() { 4 | local file=$1 5 | local reportOutputDir=$2 6 | 7 | echo "Decompiling $file..." 8 | 9 | jadxPath=$(which jadx) 10 | 11 | echo "jadx path: $jadxPath" 12 | 13 | $jadxPath -d "$file-decompiled" $file 14 | 15 | if [ $? -eq 0 ]; then 16 | mv "$file-decompiled" $reportOutputDir 17 | 18 | echo "Decompiled $file successfully." 19 | else 20 | echo "Failed to decompile $file." 21 | exit 1 22 | fi 23 | } 24 | -------------------------------------------------------------------------------- /src/lib/env_files: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | spyOnEnvFiles() { 4 | decompiledFolderBaseName=$(basename $1) 5 | reportOutputDir=$2 6 | envFilePaths=$(find "$reportOutputDir/$decompiledFolderBaseName-decompiled" -name "*.env*") 7 | 8 | if [ -n "$envFilePaths" ]; then 9 | echo "" 10 | 11 | mkdir -p $reportOutputDir/env_files 12 | 13 | echo "" 14 | for envFilePath in $envFilePaths ; do 15 | onlyFileName=$(basename $envFilePath) 16 | 17 | echo "Found $onlyFileName file." 18 | 19 | 20 | targetPath=$reportOutputDir/env_files/file_$onlyFileName 21 | #echo "Copying $envFilePath to $targetPath" 22 | cp $envFilePath $targetPath 23 | 24 | done 25 | else 26 | echo "No .env files found." 27 | fi 28 | 29 | 30 | } -------------------------------------------------------------------------------- /src/lib/info_from_libapp: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | extractEmailsFromLibAppSoFile() { 4 | soFiles="$1/libapp" 5 | 6 | emails=$(grep -Eioa "[-0-9a-zA-Z.+_]+@[-0-9a-zA-Z.+_]+\.[a-zA-Z]{2,4}" -rh $soFiles | grep -Eiah "^[^_]" | sort -u) 7 | 8 | if [ -z "$emails" ] 9 | then 10 | echo "No emails found." 11 | return 12 | fi 13 | 14 | echo "" 15 | echo "emails:" 16 | echo $emails 17 | echo "" 18 | 19 | echo "$emails" > $reportOutputDir/emails.txt 20 | } 21 | 22 | extractUrlsFromLibAppSoFile() { 23 | soFiles="$1/libapp" 24 | 25 | uriFormattedCommand=$(grep -Eioa "\b\w+:\/\/[-A-Za-z0-9+&@#\/%?=~_|!:,.;]*[-A-Za-z0-9+&@#\/%=~_|]+" -rh $soFiles | sort -u) 26 | 27 | # echo $("$uriFormattedCommand") 28 | 29 | httpAndHttpsUrls=$(echo "$uriFormattedCommand" | grep -Eia '^(http|https)' | sort -u) 30 | echo "HTTP and HTTPs Urls:" 31 | 32 | if [ -z "$httpAndHttpsUrls" ] 33 | then 34 | echo "No HTTP or HTTPS urls found." 35 | return 36 | fi 37 | 38 | for url in $httpAndHttpsUrls ; do 39 | echo $url 40 | done 41 | echo "" 42 | 43 | ftpUrls=$(echo "$uriFormattedCommand" | grep -Eia '^(ftp)' | sort -u) 44 | echo "FTP Urls:" 45 | 46 | if [ -z "$ftpUrls" ] 47 | then 48 | echo "No FTP urls found." 49 | return 50 | fi 51 | 52 | for url in $ftpUrls ; do 53 | echo $url 54 | done 55 | echo "" 56 | 57 | wsAndWssUrls=$(echo "$uriFormattedCommand" | grep -Eia '^(ws|wss)' | sort -u) 58 | echo "WS and WSS Urls:" 59 | 60 | if [ -z "$wsAndWssUrls" ] 61 | then 62 | echo "No WS or WSS urls found." 63 | return 64 | fi 65 | 66 | for url in $wsAndWssUrls ; do 67 | echo $url 68 | done 69 | echo "" 70 | 71 | fileprotocolUrls=$(echo "$uriFormattedCommand" | grep -Eia '^(file)' | sort -u) 72 | echo "File protocol paths" 73 | if [ -z "$fileprotocolUrls" ] 74 | then 75 | echo "No file protocol paths found." 76 | return 77 | fi 78 | 79 | 80 | for url in $fileprotocolUrls ; do 81 | echo $url 82 | done 83 | echo "" 84 | 85 | contentprotocolUrls=$(echo "$uriFormattedCommand" | grep -Eia '^(content)' | sort -u) 86 | echo "Content protocol paths" 87 | if [ -z "$contentprotocolUrls" ] 88 | then 89 | echo "No content protocol paths found." 90 | return 91 | fi 92 | 93 | for url in $contentprotocolUrls ; do 94 | echo $url 95 | done 96 | echo "" 97 | 98 | 99 | # other urls, exluding http, https, ftp, ws, wss 100 | otherUrls=$(echo "$uriFormattedCommand" | grep -Eia -v '^(http|https|ftp|ws|wss|file|content)' | sort -u) 101 | echo "Other URIs:" 102 | if [ -z "$otherUrls" ] 103 | then 104 | echo "No other URIs found." 105 | return 106 | fi 107 | for url in $otherUrls ; do 108 | echo $url 109 | done 110 | echo "" 111 | 112 | echo "$uriFormattedCommand" > $reportOutputDir/urls.txt 113 | } 114 | 115 | extractPhoneNumbersFromLibAppSoFile() { 116 | soFiles="$1/libapp" 117 | 118 | phoneNumbers=$(grep -iaoP "\b(?:\+?[0-9()-.\s]*){7,}\b" -rh $soFiles | awk 'length($0) > 9' | sort -u) 119 | 120 | all=$(echo "$phoneNumbers") 121 | 122 | echo "" 123 | echo "Potential Phone numbers:" 124 | 125 | if [ -z "$all" ] 126 | then 127 | echo "No phone numbers found." 128 | return 129 | fi 130 | 131 | 132 | for phoneNumber in $all ; do 133 | echo $phoneNumber 134 | done 135 | echo "" 136 | 137 | echo "$phoneNumbers" > $reportOutputDir/phone_numbers.txt 138 | } 139 | 140 | extractApiEndpointsFromLibAppSoFile() { 141 | soFiles="$1/libapp" 142 | 143 | apiEndpoints=$(grep -Eioa "\/[a-zA-Z0-9_-]+\/?[a-zA-Z0-9_-]*" -rh $soFiles | awk 'length($0) > 3' | grep -vi ".dart" | sort -u) 144 | 145 | all=$(echo "$apiEndpoints") 146 | 147 | echo "" 148 | echo "Potential API endpoints:" 149 | 150 | if [ -z "$all" ] 151 | then 152 | echo "No API endpoints found." 153 | return 154 | fi 155 | 156 | for apiEndpoint in $all ; do 157 | echo $apiEndpoint 158 | done 159 | echo "" 160 | 161 | echo "$apiEndpoints" > $reportOutputDir/api_endpoints.txt 162 | } 163 | 164 | extractPackageNamesFromLibAppSoFile() { 165 | soFiles="$1/libapp" 166 | 167 | pathFromAppFlutterPackageName=$(grep -Eoa 'package:.*main.dart' -rh $soFiles | sort -u) 168 | 169 | appFlutterPackageName=$(echo "$pathFromAppFlutterPackageName" | awk -F'[:/]' '{print $2}' | sort -u) 170 | 171 | 172 | packageNames=$(grep -Eoa 'package:.*dart' -rh $soFiles | awk 'length($0) > 3' | grep -oP ':\K[^:/]+/' | grep -va "$appFlutterPackageName" | awk -F '/' '{print $1}'| sort -u) 173 | 174 | all=$(echo "$packageNames") 175 | 176 | echo "" 177 | echo "Potential FLutter packages:" 178 | 179 | if [ -z "$all" ] 180 | then 181 | echo "No Flutter packages found." 182 | return 183 | fi 184 | 185 | 186 | for packageName in $all ; do 187 | echo "https://pub.dev/packages/$packageName" 188 | done 189 | echo "" 190 | 191 | echo "$packageNames" > $reportOutputDir/packages.txt 192 | 193 | ###################################################### 194 | 195 | pathsOfFlutterAppPackageName=$(grep -Eoa "package:$appFlutterPackageName.*dart" -rh $soFiles | sort -u) 196 | 197 | echo "" 198 | 199 | echo "Paths of $appFlutterPackageName: (The app flutter package name)" 200 | for path in $pathsOfFlutterAppPackageName ; do 201 | echo $path 202 | done 203 | 204 | echo "" 205 | echo "$pathsOfFlutterAppPackageName" > $reportOutputDir/paths_of_flutter_app_package_name.txt 206 | } 207 | 208 | 209 | spyOnSqlCommandsFromLibAppSoFile() { 210 | soFiles="$1/libapp" 211 | 212 | sqlCommands=$(grep -Eoa "SELECT|INSERT|UPDATE|DELETE|CREATE|DROP|ALTER|TRUNCATE|REPLACE|GRANT|REVOKE|LOCK|UNLOCK|RENAME|COMMENT|CALL|START|COMMIT|ROLLBACK|SAVEPOINT|SET|SHOW|DESCRIBE|EXPLAIN|HELP|USE|ANALYZE|ATTACH|BEGIN|DETACH|END|EXPLAIN|PRAGMA|VACUUM" -rh $soFiles | sort -u) 213 | 214 | 215 | echo "" 216 | 217 | echo "SQL Commands:" 218 | 219 | if [ -z "$sqlCommands" ] 220 | then 221 | echo "No SQL commands found." 222 | return 223 | fi 224 | 225 | 226 | for sqlCommand in $sqlCommands ; do 227 | echo $sqlCommand 228 | done 229 | 230 | echo "" 231 | echo "$sqlCommands" > $reportOutputDir/sql_commands.txt 232 | } 233 | 234 | spyOnSqliteDatabasesFromLibAppSoFile() { 235 | soFiles="$1/libapp" 236 | 237 | sqliteDatabases=$(grep -Eioa "\b[a-zA-Z0-9_-]+\.db\b" -rh $soFiles | sort -u) 238 | 239 | 240 | if [ -z "$sqliteDatabases" ] 241 | then 242 | echo "No SQL databases found." 243 | return 244 | fi 245 | echo "" 246 | 247 | echo "SQLite Databases:" 248 | for sqliteDatabase in $sqliteDatabases ; do 249 | echo $sqliteDatabase 250 | done 251 | 252 | echo "" 253 | echo "$sqliteDatabases" > $reportOutputDir/sqlite_databases.txt 254 | } 255 | 256 | extractInfoFromLibAppSoFile() { 257 | 258 | libappSoFile=$1 259 | reportOutputDir=$2 260 | 261 | 262 | mkdir -p $reportOutputDir/libapp 263 | pwd 264 | echo "Copying $libappSoFile to $reportOutputDir/libapp/libapp.so" 265 | 266 | cp $libappSoFile "$reportOutputDir/libapp/libapp.so" 267 | echo "libapp.so copied to $reportOutputDir/libapp/libapp.so" 268 | 269 | 270 | extractEmailsFromLibAppSoFile $reportOutputDir 271 | extractUrlsFromLibAppSoFile $reportOutputDir 272 | extractPhoneNumbersFromLibAppSoFile $reportOutputDir 273 | extractApiEndpointsFromLibAppSoFile $reportOutputDir 274 | extractPackageNamesFromLibAppSoFile $reportOutputDir 275 | spyOnSqlCommandsFromLibAppSoFile $reportOutputDir 276 | spyOnSqliteDatabasesFromLibAppSoFile $reportOutputDir 277 | } 278 | 279 | 280 | -------------------------------------------------------------------------------- /src/lib/spy: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | spyOnLibAppSoFile() { 4 | reportOutputDir=$2 5 | onlyApkFileName=$(basename $1) 6 | 7 | fromLibAppSoFile=$(find $2/$onlyApkFileName-decompiled -name "libapp.so") 8 | 9 | if [ $? -eq 0 ]; then 10 | echo "" 11 | echo "Found libapp.so files:" 12 | for libAppSoFile in $fromLibAppSoFile ; do 13 | echo $libAppSoFile 14 | done 15 | 16 | 17 | firstLibAppSoFile=$(echo $fromLibAppSoFile | cut -d ' ' -f 1) 18 | echo "" 19 | echo "Flutter Spy will use $firstLibAppSoFile to extract information." 20 | echo "" 21 | extractInfoFromLibAppSoFile $firstLibAppSoFile $reportOutputDir 22 | 23 | else 24 | echo "Failed to find libapp.so file, please make sure that the provided apk file is a flutter app." 25 | exit 1 26 | fi 27 | 28 | } 29 | 30 | 31 | 32 | startSpy() { 33 | 34 | reportOutputDir="report-$(date +%Y-%m-%d-%H-%M-%S)" 35 | mkdir -p $reportOutputDir 36 | 37 | decompileApp $1 $reportOutputDir 38 | spyOnLibAppSoFile $1 $reportOutputDir 39 | spyOnEnvFiles $1 $reportOutputDir 40 | spyOnAssetConfigFiles $1 $reportOutputDir 41 | spyOnContentFiles $1 $reportOutputDir 42 | spyOnVisualAssetFiles $1 $reportOutputDir 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/lib/utils: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | doesFileExist() { 4 | local file=$1 5 | 6 | if [ -e $file ]; then 7 | true 8 | else 9 | false 10 | fi 11 | } 12 | 13 | 14 | noArgumentsProvided() { 15 | if [ $# -eq 0 ]; then 16 | true 17 | else 18 | false 19 | fi 20 | } 21 | 22 | 23 | jadxExist() { 24 | local file=$1 25 | 26 | which jadx 27 | 28 | echo $? 29 | 30 | if [ $? -eq 0 ]; then 31 | true 32 | else 33 | false 34 | fi 35 | } 36 | 37 | 38 | isProvidedFileApk() { 39 | local file=$1 40 | local apkFileRegex=".*\.apk" 41 | 42 | if [[ $file =~ $apkFileRegex ]]; then 43 | true 44 | else 45 | false 46 | fi 47 | } 48 | 49 | 50 | showRootCommandContent() { 51 | echo " 52 | ________ __ __ _____ 53 | / ____/ __ __/ /_/ /____ _____ / ___/____ __ __ 54 | / /_ / / / / / __/ __/ _ \/ ___/ \__ \/ __ \/ / / / 55 | / __/ / / /_/ / /_/ /_/ __/ / ___/ / /_/ / /_/ / 56 | /_/ /_/\__,_/\__/\__/\___/_/ /____/ .___/\__, / 57 | /_/ /____/ 58 | " 59 | echo "Usage: flutterspy " 60 | echo "" 61 | echo "Arguments:" 62 | echo " file The apk file to spy on" 63 | } -------------------------------------------------------------------------------- /src/lib/visual_asset_files: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | spyOnVisualAssetFiles() { 4 | decompiledFolderBaseName=$(basename $1) 5 | reportOutputDir=$2 6 | # include video files, ttf, psd, music 7 | visualviusalAssetFilePaths=$(find "$reportOutputDir/$decompiledFolderBaseName-decompiled" -name "*.png*" -o -name "*.jpg*" -o -name "*.jpeg*" -o -name "*.gif*" -o -name "*.svg*" -o -name "*.webp*" -o -name "*.bmp*" -o -name "*.ico*" -o -name "*.tif*" -o -name "*.tiff*" -o -name "*.psd*" -o -name "*.ttf*" -o -name "*.mp3*" -o -name "*.mp4*" -o -name "*.avi*" -o -name "*.mov*" -o -name "*.flv*" -o -name "*.wav*" -o -name "*.aac*" -o -name "*.ogg*" -o -name "*.flac*" -o -name "*.wma*" -o -name "*.psd*" -o -name "*.ai*" -o -name "*.eps*" -o -name "*.indd*" -o -name "*.raw*" -o -name "*.svg*" -o -name "*.pdf*" -o -name "*.sketch*" -o -name "*.zip*" -o -name "*.rar*" -o -name "*.7z*" -o -name "*.tar*" -o -name "*.gz*" -o -name "*.bz2*" -o -name "*.dmg*" -o -name "*.iso*" -o -name "*.apk*" -o -name "*.exe*" -o -name "*.jar*" -o -name "*.war*" -o -name "*.ear*" -o -name "*.deb*" -o -name "*.rpm*" -o -name "*.msi*" -o -name "*.cab*" -o -name "*.tar.gz*" -o -name "*.tar.bz2*" -o -name "*.tar.xz*" -o -name "*.tar.Z*" -o -name "*.tar.lz*" -o -name "*.tar.lzma*" -o -name "*.tar.lzo*" -o -name "*.tar.xz*" -o -name "*.tar.Z*" -o -name "*.tar.lz*" -o -name "*.tar.lzma*" -o -name "*.tar.lzo*" -o -name "*.tar.zst*" | grep -Ea "assets" | sort -u) 8 | 9 | 10 | 11 | 12 | if [ -n "$visualviusalAssetFilePaths" ]; then 13 | echo "" 14 | 15 | mkdir -p $reportOutputDir/visual_assets_files 16 | 17 | echo "" 18 | for viusalAssetFilePath in $visualviusalAssetFilePaths ; do 19 | onlyFileName=$(basename $viusalAssetFilePath) 20 | 21 | echo "Found $onlyFileName file." 22 | 23 | 24 | targetPath=$reportOutputDir/visual_assets_files/$onlyFileName 25 | # echo "Copying $viusalAssetFilePath to $targetPath" 26 | cp $viusalAssetFilePath $targetPath 27 | done 28 | else 29 | echo "No other visual/audio files found." 30 | 31 | fi 32 | 33 | 34 | 35 | } --------------------------------------------------------------------------------