├── .github └── workflows │ └── rust.yml ├── .gitignore ├── Cargo.toml ├── README.md ├── apk_analyzer ├── .gitignore ├── .idea │ ├── .gitignore │ ├── .name │ ├── codeStyles │ │ ├── Project.xml │ │ └── codeStyleConfig.xml │ ├── compiler.xml │ ├── jarRepositories.xml │ ├── kotlinc.xml │ ├── misc.xml │ ├── runConfigurations.xml │ └── vcs.xml ├── README.md ├── build.gradle.kts ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle.kts └── src │ ├── main │ └── kotlin │ │ └── com │ │ └── ammaraskar │ │ └── intent │ │ └── fuzz │ │ ├── ApkAnalyzer.kt │ │ ├── IntentExtraUsageTreeVisitor.kt │ │ ├── IntentReceiver.kt │ │ ├── IntentTemplate.kt │ │ └── Main.kt │ └── test │ └── kotlin │ └── com │ └── ammaraskar │ └── intent │ └── fuzz │ ├── ApkAnalyzerTest.kt │ └── IntentReceiverTest.kt ├── content_provider ├── .gitignore ├── .idea │ ├── .gitignore │ ├── .name │ ├── compiler.xml │ ├── gradle.xml │ ├── kotlinc.xml │ ├── misc.xml │ └── vcs.xml ├── README.md ├── app │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── org │ │ │ └── gts3 │ │ │ └── jnifuzz │ │ │ └── contentprovider │ │ │ ├── MainActivity.kt │ │ │ └── UriPermissionManager.kt │ │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── values-night │ │ └── themes.xml │ │ ├── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ ├── backup_rules.xml │ │ ├── data_extraction_rules.xml │ │ └── provider_paths.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle ├── eval ├── .gitignore ├── collect_edgecount_data.sh ├── compute_metrics.py ├── generate_configs.sh └── tmp.sh ├── intent_template.json ├── patches └── ActivityThread.patch ├── run.sh ├── run_multiple_apps.sh ├── rust-toolchain.toml ├── src ├── adb_device.rs ├── adb_executor.rs ├── intent_generator.rs ├── intent_input.rs ├── intent_mutator.rs ├── main.rs ├── socket_coverage_observer.rs └── util.rs └── test_coverage_agent ├── README.md └── test_server.py /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [ "master" ] 7 | pull_request: 8 | branches: [ "master" ] 9 | 10 | env: 11 | CARGO_TERM_COLOR: always 12 | 13 | jobs: 14 | build: 15 | 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - uses: actions/checkout@v3 20 | - name: Check style 21 | run: cargo fmt --check 22 | - name: Build 23 | run: cargo build --verbose 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb 15 | 16 | # Ammar's notes 17 | PERSONAL_NOTES.md 18 | 19 | # Byte-compiled / optimized / DLL files 20 | __pycache__/ 21 | *.py[cod] 22 | *$py.class 23 | 24 | # Crashes and corpus directory 25 | crashes/ 26 | corpus/ 27 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "intent_fuzzer_lib_afl" 3 | version = "0.1.0" 4 | edition = "2021" 5 | # libafl needs at least rust 1.64 for let-else statements 6 | rust-version = "1.64" 7 | 8 | [dependencies] 9 | libafl = "0.10.0" 10 | # Needed to implement a custom Input for libAFL 11 | serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } 12 | # Needed to parse intent_template.json file 13 | serde_json = { version = "1.0", default-features = false, features = ["alloc"] } 14 | # For parsing command line args 15 | clap = { version = "4.0", features = ["derive"] } 16 | # Include strum_macros 17 | strum = "0.24" 18 | strum_macros = "0.24" 19 | # Include fasthash 20 | fasthash = "0.4.0" 21 | # Include TempDir 22 | tempfile = "3.6.0" 23 | # Include futures 24 | futures = { version = "0.3", features = ["compat"] } 25 | # Include subprocess 26 | subprocess = { version = "0.2" } 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IntentFuzzer - libAFL version 2 | 3 | This is an automated greybox fuzzer for Intent receivers on Android. 4 | 5 | ## How To Use 6 | 7 | `cargo run -- --help` 8 | 9 | ## Architecture 10 | 11 | ``` 12 | Fuzzer Android Device/Emulator 13 | ┌───────────┐ ┌──────────────────┐ 14 | │ │ TCP Port │ App │ 15 | │ Collects │ over ADB │ ┌──────────────┐ │ 16 | │ coverage ◄├─────────────┼►┤Coverage Agent│ │ 17 | │ │ │ ├──────────────┤ │ 18 | │ │ │ │ │ │ 19 | │ │ │ │ │ │ 20 | │ │ │ │ │ │ 21 | │ │ │ │ │ │ 22 | │ │ │ └──────▲───────┘ │ 23 | │ │ │ │ │ 24 | │ Mutates │ │ │(Intents)│ 25 | │ intents │Sends Intents├────────┴─────────┤ 26 | └───────────┴────────────►│ Android Activity │ 27 | │ Manager │ 28 | └──────────────────┘ 29 | ``` 30 | 31 | ## Project Structure 32 | 33 | [AndroidCoverageAgent](https://github.com/sslab-gatech/AndroidCoverageAgent) is 34 | used to instrument apps on-device or on-emulator for coverage feedback. 35 | 36 | The `apk_analyzer` subfolder contains a Kotlin project that uses the 37 | [jadx](https://github.com/skylot/jadx) API to analyze an apk file and create 38 | an `intent_template.json` file from it. 39 | 40 | The root folder `.` contains the fuzzer written in Rust using 41 | [libafl](https://github.com/AFLplusplus/LibAFL) to implement the fuzzing loop 42 | and uses the generated `intent_template.json` and `adb` to communicate with the 43 | coverage agent in the Android environment. 44 | -------------------------------------------------------------------------------- /apk_analyzer/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | replay_pid* 25 | 26 | .gradle 27 | **/build/ 28 | !src/**/build/ 29 | 30 | # Ignore Gradle GUI config 31 | gradle-app.setting 32 | 33 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 34 | !gradle-wrapper.jar 35 | 36 | # Avoid ignore Gradle wrappper properties 37 | !gradle-wrapper.properties 38 | 39 | # Cache of project 40 | .gradletasknamecache 41 | 42 | # Eclipse Gradle plugin generated files 43 | # Eclipse Core 44 | .project 45 | # JDT-specific (Eclipse Java Development Tools) 46 | .classpath 47 | 48 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 49 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 50 | 51 | # User-specific stuff 52 | .idea/**/workspace.xml 53 | .idea/**/tasks.xml 54 | .idea/**/usage.statistics.xml 55 | .idea/**/dictionaries 56 | .idea/**/shelf 57 | 58 | # AWS User-specific 59 | .idea/**/aws.xml 60 | 61 | # Generated files 62 | .idea/**/contentModel.xml 63 | 64 | # Sensitive or high-churn files 65 | .idea/**/dataSources/ 66 | .idea/**/dataSources.ids 67 | .idea/**/dataSources.local.xml 68 | .idea/**/sqlDataSources.xml 69 | .idea/**/dynamic.xml 70 | .idea/**/uiDesigner.xml 71 | .idea/**/dbnavigator.xml 72 | 73 | # Gradle 74 | .idea/**/gradle.xml 75 | .idea/**/libraries 76 | 77 | # Gradle and Maven with auto-import 78 | # When using Gradle or Maven with auto-import, you should exclude module files, 79 | # since they will be recreated, and may cause churn. Uncomment if using 80 | # auto-import. 81 | # .idea/artifacts 82 | # .idea/compiler.xml 83 | # .idea/jarRepositories.xml 84 | # .idea/modules.xml 85 | # .idea/*.iml 86 | # .idea/modules 87 | # *.iml 88 | # *.ipr 89 | 90 | # CMake 91 | cmake-build-*/ 92 | 93 | # Mongo Explorer plugin 94 | .idea/**/mongoSettings.xml 95 | 96 | # File-based project format 97 | *.iws 98 | 99 | # IntelliJ 100 | out/ 101 | 102 | # mpeltonen/sbt-idea plugin 103 | .idea_modules/ 104 | 105 | # JIRA plugin 106 | atlassian-ide-plugin.xml 107 | 108 | # Cursive Clojure plugin 109 | .idea/replstate.xml 110 | 111 | # SonarLint plugin 112 | .idea/sonarlint/ 113 | 114 | # Crashlytics plugin (for Android Studio and IntelliJ) 115 | com_crashlytics_export_strings.xml 116 | crashlytics.properties 117 | crashlytics-build.properties 118 | fabric.properties 119 | 120 | # Editor-based Rest Client 121 | .idea/httpRequests 122 | 123 | # Android studio 3.1+ serialized cache file 124 | .idea/caches/build_file_checksums.ser 125 | 126 | # Android studio local properties 127 | local.properties 128 | -------------------------------------------------------------------------------- /apk_analyzer/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Datasource local storage ignored files 5 | /dataSources/ 6 | /dataSources.local.xml 7 | # Editor-based HTTP Client requests 8 | /httpRequests/ 9 | -------------------------------------------------------------------------------- /apk_analyzer/.idea/.name: -------------------------------------------------------------------------------- 1 | ApkAnalyzer -------------------------------------------------------------------------------- /apk_analyzer/.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 9 | 10 | -------------------------------------------------------------------------------- /apk_analyzer/.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /apk_analyzer/.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /apk_analyzer/.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | -------------------------------------------------------------------------------- /apk_analyzer/.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /apk_analyzer/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /apk_analyzer/.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /apk_analyzer/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /apk_analyzer/README.md: -------------------------------------------------------------------------------- 1 | # Apk Analyzer 2 | 3 | This is a Kotlin project that takes an Android `.apk` file, analyzes the 4 | `AndroidManifest.xml` and performs some basic static analysis to generate an 5 | `intent_template.json` file to be used with the IntentFuzzer. 6 | 7 | ## Usage 8 | 9 | ```bash 10 | gradle run --args="/path/to/application.apk /path/to/output/directory/" 11 | ``` 12 | -------------------------------------------------------------------------------- /apk_analyzer/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 2 | 3 | plugins { 4 | kotlin("jvm") version "1.7.10" 5 | kotlin("plugin.serialization") version "1.7.10" 6 | application 7 | } 8 | 9 | group = "com.ammaraskar" 10 | version = "1.0-SNAPSHOT" 11 | 12 | repositories { 13 | mavenCentral() 14 | google() 15 | } 16 | 17 | dependencies { 18 | implementation("io.github.skylot:jadx-core:1.4.7") 19 | implementation("io.github.skylot:jadx-dex-input:1.4.7") 20 | 21 | // Needed for jadx's logging library. 22 | implementation("org.slf4j:slf4j-simple:2.0.7") 23 | 24 | implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0") 25 | 26 | testImplementation(kotlin("test")) 27 | } 28 | 29 | tasks.test { 30 | useJUnit() 31 | } 32 | 33 | tasks.withType() { 34 | kotlinOptions.jvmTarget = "1.8" 35 | } 36 | 37 | application { 38 | mainClass.set("com.ammaraskar.intent.fuzz.MainKt") 39 | } -------------------------------------------------------------------------------- /apk_analyzer/gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official 2 | -------------------------------------------------------------------------------- /apk_analyzer/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sslab-gatech/MALintent/3bb0d77f66beb224ab86417bd1f5a1935451e44e/apk_analyzer/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /apk_analyzer/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /apk_analyzer/gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 84 | 85 | APP_NAME="Gradle" 86 | APP_BASE_NAME=${0##*/} 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | MAX_FD=$( ulimit -H -n ) || 147 | warn "Could not query maximum file descriptor limit" 148 | esac 149 | case $MAX_FD in #( 150 | '' | soft) :;; #( 151 | *) 152 | ulimit -n "$MAX_FD" || 153 | warn "Could not set maximum file descriptor limit to $MAX_FD" 154 | esac 155 | fi 156 | 157 | # Collect all arguments for the java command, stacking in reverse order: 158 | # * args from the command line 159 | # * the main class name 160 | # * -classpath 161 | # * -D...appname settings 162 | # * --module-path (only if needed) 163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 164 | 165 | # For Cygwin or MSYS, switch paths to Windows format before running java 166 | if "$cygwin" || "$msys" ; then 167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 169 | 170 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 171 | 172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 173 | for arg do 174 | if 175 | case $arg in #( 176 | -*) false ;; # don't mess with options #( 177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 178 | [ -e "$t" ] ;; #( 179 | *) false ;; 180 | esac 181 | then 182 | arg=$( cygpath --path --ignore --mixed "$arg" ) 183 | fi 184 | # Roll the args list around exactly as many times as the number of 185 | # args, so each arg winds up back in the position where it started, but 186 | # possibly modified. 187 | # 188 | # NB: a `for` loop captures its iteration list before it begins, so 189 | # changing the positional parameters here affects neither the number of 190 | # iterations, nor the values presented in `arg`. 191 | shift # remove old arg 192 | set -- "$@" "$arg" # push replacement arg 193 | done 194 | fi 195 | 196 | # Collect all arguments for the java command; 197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 198 | # shell script including quotes and variable substitutions, so put them in 199 | # double quotes to make sure that they get re-expanded; and 200 | # * put everything else in single quotes, so that it's not re-expanded. 201 | 202 | set -- \ 203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 204 | -classpath "$CLASSPATH" \ 205 | org.gradle.wrapper.GradleWrapperMain \ 206 | "$@" 207 | 208 | # Use "xargs" to parse quoted args. 209 | # 210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 211 | # 212 | # In Bash we could simply go: 213 | # 214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 215 | # set -- "${ARGS[@]}" "$@" 216 | # 217 | # but POSIX shell has neither arrays nor command substitution, so instead we 218 | # post-process each arg (as a line of input to sed) to backslash-escape any 219 | # character that might be a shell metacharacter, then use eval to reverse 220 | # that process (while maintaining the separation between arguments), and wrap 221 | # the whole thing up as a single "set" statement. 222 | # 223 | # This will of course break if any of these variables contains a newline or 224 | # an unmatched quote. 225 | # 226 | 227 | eval "set -- $( 228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 229 | xargs -n1 | 230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 231 | tr '\n' ' ' 232 | )" '"$@"' 233 | 234 | exec "$JAVACMD" "$@" 235 | -------------------------------------------------------------------------------- /apk_analyzer/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /apk_analyzer/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | 2 | rootProject.name = "ApkAnalyzer" 3 | 4 | -------------------------------------------------------------------------------- /apk_analyzer/src/main/kotlin/com/ammaraskar/intent/fuzz/ApkAnalyzer.kt: -------------------------------------------------------------------------------- 1 | package com.ammaraskar.intent.fuzz 2 | 3 | import jadx.api.JadxArgs 4 | import jadx.api.JadxDecompiler 5 | import jadx.core.dex.visitors.IDexTreeVisitor 6 | import jadx.core.dex.visitors.ReSugarCode 7 | import java.io.File 8 | 9 | class ApkAnalyzer(private val apkFile: File) { 10 | 11 | private val _intentTemplates: MutableList = mutableListOf() 12 | val intentTemplates: List 13 | get() = _intentTemplates 14 | 15 | init { 16 | val jadxArgs = JadxArgs() 17 | jadxArgs.setInputFile(apkFile) 18 | 19 | JadxDecompiler(jadxArgs).use { decompiler -> 20 | decompiler.load(); 21 | val intentExtraUsageVisitor = IntentExtraUsageTreeVisitor() 22 | addCustomPassAfter(decompiler.root.passes, intentExtraUsageVisitor) 23 | 24 | val manifestResource = 25 | decompiler.resources.stream().filter { resource -> resource.originalName.equals("AndroidManifest.xml") } 26 | .findFirst(); 27 | if (!manifestResource.isPresent) { 28 | throw IllegalArgumentException("APK does not contain AndroidManifest.xml") 29 | } 30 | 31 | val contents = manifestResource.get().loadContent(); 32 | val intentReceivers = 33 | parseIntentReceiversFromManifest(contents.text.codeStr, decompiler) 34 | 35 | // Invoke the tree visitor by decompiling all the classes in the apk. 36 | for (cls in decompiler.classes) { 37 | // Don't bother looking at built-in android, kotlin or java classes. 38 | if (cls.fullName.startsWith("androidx.") || cls.fullName.startsWith("kotlin.")) { 39 | continue; 40 | } 41 | cls.decompile() 42 | } 43 | 44 | for (intentReceiver in intentReceivers) { 45 | _intentTemplates.add(IntentTemplate( 46 | intentReceiver.receiverType, 47 | intentReceiver.componentName, 48 | intentReceiver.actions, 49 | intentReceiver.categories, 50 | // For now we assign all extras to every intent receiver. In the future maybe with some more 51 | // advanced static analysis we could figure out which extras correspond to which intent receiver. 52 | intentExtraUsageVisitor.extras, 53 | )) 54 | } 55 | } 56 | } 57 | 58 | } 59 | 60 | // From https://github.com/skylot/jadx/issues/1482 61 | private inline fun addCustomPassAfter(passes: MutableList, customPass: IDexTreeVisitor) { 62 | for ((i, pass) in passes.withIndex()) { 63 | if (pass is T) { 64 | passes.add(i + 1, customPass) 65 | break 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /apk_analyzer/src/main/kotlin/com/ammaraskar/intent/fuzz/IntentExtraUsageTreeVisitor.kt: -------------------------------------------------------------------------------- 1 | package com.ammaraskar.intent.fuzz 2 | 3 | import jadx.core.dex.instructions.BaseInvokeNode 4 | import jadx.core.dex.instructions.ConstStringNode 5 | import jadx.core.dex.instructions.args.InsnWrapArg 6 | import jadx.core.dex.nodes.MethodNode 7 | import jadx.core.dex.visitors.AbstractVisitor 8 | 9 | class IntentExtraUsageTreeVisitor : AbstractVisitor() { 10 | 11 | /** 12 | * Mapping of intent extra keys to their types. 13 | */ 14 | val extras: HashMap = hashMapOf() 15 | 16 | override fun visit(mth: MethodNode) { 17 | if (mth.isNoCode) { 18 | return; 19 | } 20 | 21 | for (basicBlock in mth.basicBlocks) { 22 | for (instr in basicBlock.instructions) { 23 | if (instr is BaseInvokeNode) { 24 | this.visitInvokeNode(instr) 25 | } 26 | } 27 | } 28 | } 29 | 30 | private val gettersToExtraTypes = hashMapOf( 31 | "getStringExtra" to "String", 32 | "getBooleanExtra" to "Boolean", 33 | "getByteExtra" to "Byte", 34 | "getCharExtra" to "Char", 35 | "getShortExtra" to "Short", 36 | "getIntExtra" to "Int", 37 | "getLongExtra" to "Long", 38 | "getFloatExtra" to "Float", 39 | "getDoubleExtra" to "Double", 40 | "getStringArrayExtra" to "StringArray", 41 | "getBooleanArrayExtra" to "BooleanArray", 42 | "getByteArrayExtra" to "ByteArray", 43 | "getCharArrayExtra" to "CharArray", 44 | "getShortArrayExtra" to "ShortArray", 45 | "getIntArrayExtra" to "IntArray", 46 | "getLongArrayExtra" to "LongArray", 47 | "getDoubleArrayExtra" to "DoubleArray", 48 | "getIntegerArrayListExtra" to "IntArrayList", 49 | "getStringArrayListExtra" to "StringArrayList", 50 | ) 51 | 52 | private fun visitInvokeNode(node: BaseInvokeNode) { 53 | if (node.callMth.declClass.fullName != "android.content.Intent") { 54 | return; 55 | } 56 | 57 | val extraType = gettersToExtraTypes[node.callMth.name] ?: return 58 | var key: String? = null; 59 | 60 | for (argument in node.arguments) { 61 | if (argument !is InsnWrapArg) { 62 | continue; 63 | } 64 | val wrappedInstruction = argument.wrapInsn 65 | if (wrappedInstruction !is ConstStringNode) { 66 | continue; 67 | } 68 | key = wrappedInstruction.string 69 | } 70 | 71 | if (key == null) { 72 | return 73 | } 74 | extras[key] = extraType 75 | } 76 | } -------------------------------------------------------------------------------- /apk_analyzer/src/main/kotlin/com/ammaraskar/intent/fuzz/IntentReceiver.kt: -------------------------------------------------------------------------------- 1 | package com.ammaraskar.intent.fuzz 2 | 3 | import jadx.api.JadxDecompiler 4 | import javax.xml.parsers.DocumentBuilderFactory 5 | 6 | class IntentReceiver( 7 | val receiverType: String, 8 | val componentName: String, 9 | val actions: Collection, 10 | val categories: Collection, 11 | val aliasTarget: String? 12 | ) { 13 | override fun equals(other: Any?): Boolean { 14 | if (other !is IntentReceiver) { 15 | return false 16 | } 17 | return receiverType == other.receiverType && 18 | componentName == other.componentName && 19 | actions == other.actions && 20 | categories == other.categories && 21 | aliasTarget == other.aliasTarget 22 | } 23 | } 24 | 25 | fun parseIntentReceiversFromManifest(manifestXML: String, decompiler: JadxDecompiler): List { 26 | val targets = mutableListOf() 27 | 28 | val documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder() 29 | val document = documentBuilder.parse(manifestXML.byteInputStream()) 30 | 31 | val manifestElement = document.getElementsByTagName("manifest") 32 | if (manifestElement.length != 1) { 33 | throw IllegalArgumentException("AndroidManifest contained ${manifestElement.length} manifest elements") 34 | } 35 | val packageName = manifestElement.item(0).attributes.getNamedItem("package")?.nodeValue 36 | ?: throw IllegalArgumentException(" element did not contain package") 37 | 38 | // Find all tags and iterate over them. 39 | val intentFilters = document.getElementsByTagName("intent-filter") 40 | for (i in 0 until intentFilters.length) { 41 | val node = intentFilters.item(i) 42 | 43 | val containingComponent = node.parentNode 44 | 45 | // Get the class name of the component containing this intent-filter. 46 | val intentClass = containingComponent.attributes.getNamedItem("android:name")?.nodeValue 47 | if (intentClass == null) { 48 | println("Skipping intent receiver because it doesn't have an android:name attribute") 49 | continue 50 | } 51 | 52 | // Check to see if the component is exported. That is, either the "android:exported" attribute is marked as 53 | // "true" or if it isn't present, it takes a default value of true when there is an 54 | val exportedAttribute = 55 | containingComponent.attributes.getNamedItem("android:exported")?.nodeValue 56 | ?: "true" 57 | val isExported = exportedAttribute == "true" 58 | 59 | // Only add to list of targets if it is exported. 60 | if (!isExported) { 61 | continue 62 | } 63 | 64 | // Check if this is an alias. 65 | val aliasTargetActivity = if (containingComponent.nodeName == "activity-alias") { 66 | containingComponent.attributes.getNamedItem("android:targetActivity")?.nodeValue 67 | ?: throw IllegalArgumentException("Manifest has an activity-alias without an android:targetActivity") 68 | } else { 69 | null 70 | } 71 | 72 | val actionNames = mutableListOf() 73 | val categoryNames = mutableListOf() 74 | // Gather all the action tags. 75 | for (j in 0 until node.childNodes.length) { 76 | val intentFilterChild = node.childNodes.item(j) 77 | 78 | // Check if this is a or an 79 | if (intentFilterChild.nodeName == "action") { 80 | actionNames.add(intentFilterChild.attributes.getNamedItem("android:name").nodeValue) 81 | } else if (intentFilterChild.nodeName == "category") { 82 | categoryNames.add(intentFilterChild.attributes.getNamedItem("android:name").nodeValue) 83 | } 84 | } 85 | 86 | val receiverType = when (containingComponent.nodeName) { 87 | "activity" -> "Activity" 88 | "activity-alias" -> "Activity" 89 | "service" -> continue // We do not support fuzzing services for now 90 | "receiver" -> "BroadcastReceiver" 91 | "provider" -> continue // We do not support content providers for now 92 | else -> throw IllegalArgumentException("Unknown component type: ${containingComponent.nodeName}") 93 | } 94 | 95 | // Create the intent receiver object. 96 | val intentReceiver = IntentReceiver( 97 | receiverType = receiverType, 98 | componentName = "$packageName/$intentClass", 99 | actions = actionNames, 100 | categories = categoryNames, 101 | aliasTarget = aliasTargetActivity 102 | ) 103 | 104 | // Only add alias if it has new actions or categories. 105 | if (aliasTargetActivity != null) { 106 | val existingTargets = targets.filter { 107 | it.componentName == "$packageName/$aliasTargetActivity" || it.aliasTarget == aliasTargetActivity 108 | } 109 | 110 | // We skip this alias if there already exists one that covers the same actions and categories. 111 | if (existingTargets.any { 112 | it.actions.containsAll(intentReceiver.actions) && it.categories.containsAll(intentReceiver.categories) 113 | }) { 114 | println("Skipping alias: ${intentReceiver.componentName} with target $aliasTargetActivity") 115 | continue 116 | } 117 | } 118 | 119 | // Skip duplicate intent receivers (i.e., all properties are the same). 120 | // Duplicates happen because an activity can declare multiple intent filters. 121 | // They may have different tags, which we do not parse (yet?). 122 | // Might be helpful to use them since they hint at the scheme and host of the URI. 123 | if (targets.contains(intentReceiver)) { 124 | println("Skipping duplicate intent receiver: ${intentReceiver.componentName}") 125 | continue 126 | } 127 | 128 | println("Adding intent receiver: ${intentReceiver.componentName}") 129 | targets.add(intentReceiver) 130 | } 131 | 132 | return targets 133 | } 134 | -------------------------------------------------------------------------------- /apk_analyzer/src/main/kotlin/com/ammaraskar/intent/fuzz/IntentTemplate.kt: -------------------------------------------------------------------------------- 1 | package com.ammaraskar.intent.fuzz 2 | 3 | import kotlinx.serialization.Serializable 4 | import kotlinx.serialization.encodeToString 5 | import kotlinx.serialization.json.Json 6 | import java.io.File 7 | 8 | @Serializable 9 | class IntentTemplate( 10 | val receiver_type: String, 11 | val component: String, 12 | val actions: Collection, 13 | val categories: Collection, 14 | val known_extras_keys: Map, 15 | ) { 16 | // Save this template to a json file in a given directory 17 | fun saveToFile(outputDir: File) { 18 | val jsonFormat = Json { prettyPrint = true } 19 | outputDir.mkdirs() 20 | File(outputDir, "${getActivityName()}.json").writeText(jsonFormat.encodeToString(this)) 21 | } 22 | 23 | fun getActivityName(): String { 24 | return component.substringAfterLast("/") 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /apk_analyzer/src/main/kotlin/com/ammaraskar/intent/fuzz/Main.kt: -------------------------------------------------------------------------------- 1 | package com.ammaraskar.intent.fuzz 2 | 3 | import kotlinx.serialization.encodeToString 4 | import kotlinx.serialization.json.Json 5 | import java.io.File 6 | 7 | fun main(args: Array) { 8 | val analyzer = ApkAnalyzer(File(args[0])) 9 | 10 | val jsonFormat = Json { prettyPrint = true } 11 | println(jsonFormat.encodeToString(analyzer.intentTemplates)) 12 | 13 | // Save all intent templates to a given output directory 14 | analyzer.intentTemplates.forEach { it.saveToFile(File(args[1])) } 15 | } -------------------------------------------------------------------------------- /apk_analyzer/src/test/kotlin/com/ammaraskar/intent/fuzz/ApkAnalyzerTest.kt: -------------------------------------------------------------------------------- 1 | package com.ammaraskar.intent.fuzz 2 | 3 | import org.junit.Ignore 4 | import org.junit.Test 5 | import java.io.File 6 | 7 | class ApkAnalyzerTest { 8 | @Ignore("Ignored until we actually commit actual testing apk files into the repo") 9 | @Test 10 | fun testLoadsApk() { 11 | ApkAnalyzer(File("../../VulnerableApp/app/build/outputs/apk/debug/app-debug.apk")) 12 | } 13 | } -------------------------------------------------------------------------------- /apk_analyzer/src/test/kotlin/com/ammaraskar/intent/fuzz/IntentReceiverTest.kt: -------------------------------------------------------------------------------- 1 | package com.ammaraskar.intent.fuzz 2 | 3 | import junit.framework.Assert.assertEquals 4 | import org.junit.Test 5 | 6 | internal class IntentReceiverTest { 7 | 8 | @Test 9 | fun parseIntentTargetsWorksOnARealManifest() { 10 | val testManifest = """ 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | """.trimIndent() 24 | 25 | val targets = parseIntentReceiversFromManifest(testManifest, decompiler) 26 | assertEquals(targets.size, 1) 27 | assertEquals(targets[0].componentName, "com.ammaraskar.vulnerableapp/com.ammaraskar.vulnerableapp.MainActivity") 28 | assertEquals(targets[0].categories, listOf("android.intent.category.LAUNCHER")) 29 | assertEquals(targets[0].actions, listOf("android.intent.action.MAIN")) 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /content_provider/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /content_provider/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /content_provider/.idea/.name: -------------------------------------------------------------------------------- 1 | Content Provider -------------------------------------------------------------------------------- /content_provider/.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /content_provider/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 18 | 19 | -------------------------------------------------------------------------------- /content_provider/.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /content_provider/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /content_provider/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /content_provider/README.md: -------------------------------------------------------------------------------- 1 | # Content Provider 2 | 3 | This content provider needs to be installed on the device to allow the fuzzer 4 | to create files in a content provider. This app doesn't have a UI and only 5 | needs to be installed; the fuzzer will handle the rest. 6 | 7 | ## Installation 8 | 9 | The following command will build and install the content provider: 10 | 11 | ```bash 12 | gradle installDebug 13 | ``` 14 | -------------------------------------------------------------------------------- /content_provider/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /content_provider/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'org.jetbrains.kotlin.android' 4 | } 5 | 6 | android { 7 | namespace 'org.gts3.jnifuzz.contentprovider' 8 | compileSdk 33 9 | 10 | defaultConfig { 11 | applicationId "org.gts3.jnifuzz.contentprovider" 12 | minSdk 24 13 | targetSdk 33 14 | versionCode 1 15 | versionName "1.0" 16 | 17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 18 | vectorDrawables { 19 | useSupportLibrary true 20 | } 21 | } 22 | 23 | buildTypes { 24 | release { 25 | minifyEnabled false 26 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 27 | } 28 | } 29 | compileOptions { 30 | sourceCompatibility JavaVersion.VERSION_1_8 31 | targetCompatibility JavaVersion.VERSION_1_8 32 | } 33 | kotlinOptions { 34 | jvmTarget = '1.8' 35 | } 36 | buildFeatures { 37 | compose true 38 | } 39 | composeOptions { 40 | kotlinCompilerExtensionVersion '1.4.6' 41 | } 42 | packagingOptions { 43 | resources { 44 | excludes += '/META-INF/{AL2.0,LGPL2.1}' 45 | } 46 | } 47 | } 48 | 49 | dependencies { 50 | 51 | implementation 'androidx.core:core-ktx:1.8.0' 52 | implementation 'androidx.appcompat:appcompat:1.6.1' 53 | implementation 'com.google.android.material:material:1.9.0' 54 | implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' 55 | implementation 'androidx.activity:activity-compose:1.5.1' 56 | implementation platform('androidx.compose:compose-bom:2022.10.00') 57 | implementation 'androidx.compose.ui:ui' 58 | implementation 'androidx.compose.ui:ui-graphics' 59 | implementation 'androidx.compose.ui:ui-tooling-preview' 60 | implementation 'androidx.compose.material3:material3' 61 | testImplementation 'junit:junit:4.13.2' 62 | androidTestImplementation 'androidx.test.ext:junit:1.1.5' 63 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' 64 | androidTestImplementation platform('androidx.compose:compose-bom:2022.10.00') 65 | androidTestImplementation 'androidx.compose.ui:ui-test-junit4' 66 | debugImplementation 'androidx.compose.ui:ui-tooling' 67 | debugImplementation 'androidx.compose.ui:ui-test-manifest' 68 | } 69 | -------------------------------------------------------------------------------- /content_provider/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /content_provider/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 17 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 34 | 37 | 38 | 39 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /content_provider/app/src/main/java/org/gts3/jnifuzz/contentprovider/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package org.gts3.jnifuzz.contentprovider 2 | 3 | import android.content.pm.PackageManager 4 | import android.os.Bundle 5 | import android.view.View 6 | import android.widget.Toast 7 | import androidx.appcompat.app.AppCompatActivity 8 | 9 | 10 | class MainActivity : AppCompatActivity() { 11 | override fun onCreate(savedInstanceState: Bundle?) { 12 | super.onCreate(savedInstanceState) 13 | setContentView(R.layout.activity_main) 14 | } 15 | 16 | fun grantUriPermissions(view: View?) { 17 | 18 | // Bail out if view is null 19 | if (view == null) { 20 | Toast.makeText(this, "Failed to grant permissions (view is null)", Toast.LENGTH_LONG).show() 21 | return 22 | } 23 | 24 | val context = view.getContext(); 25 | 26 | // Go through all packages 27 | val pm = getPackageManager(); 28 | val packages = pm.getInstalledPackages(PackageManager.GET_META_DATA); 29 | 30 | for (packageInfo in packages) { 31 | val packageName = packageInfo.packageName; 32 | 33 | // Skip packages that start with "com.android" or "android" 34 | if (packageName.startsWith("com.android") || packageName.startsWith("android")) { 35 | continue; 36 | } 37 | 38 | UriPermissionManager.grantUriPermissionsForPackage(context, packageName) 39 | } 40 | } 41 | } 42 | 43 | -------------------------------------------------------------------------------- /content_provider/app/src/main/java/org/gts3/jnifuzz/contentprovider/UriPermissionManager.kt: -------------------------------------------------------------------------------- 1 | package org.gts3.jnifuzz.contentprovider 2 | 3 | import android.content.BroadcastReceiver 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.content.Intent.EXTRA_PACKAGE_NAME 7 | import android.net.Uri 8 | import android.util.Log 9 | 10 | class UriPermissionManager : BroadcastReceiver() { 11 | private val suffixes = listOf( 12 | "aac", 13 | "apk", 14 | "gif", 15 | "html", 16 | "jpg", 17 | "midi", 18 | "mp3", 19 | "mp4", 20 | "ogg", 21 | "pdf", 22 | "png", 23 | "txt", 24 | "wav", 25 | "wma", 26 | "wmv", 27 | "xml" 28 | ); 29 | 30 | override fun onReceive(p0: Context?, p1: Intent?) { 31 | Log.i("UriPermissionManager", "Received intent") 32 | 33 | // Load the package name from the intent (string extra EXTRA_PACKAGE_NAME) 34 | val packageName = p1?.getStringExtra(EXTRA_PACKAGE_NAME) 35 | 36 | // Log if the context is null 37 | if (p0 == null) { 38 | Log.i("UriPermissionManager", "Context is null") 39 | return 40 | } 41 | 42 | if (packageName != null) { 43 | if (packageName.startsWith("com.android") || packageName.startsWith("android")) { 44 | Log.i("UriPermissionManager", "Package name starts with com.android or android") 45 | return 46 | } 47 | 48 | // Grant permissions to the package 49 | grantUriPermissionsForPackage(p0!!, packageName!!); 50 | } else { 51 | Log.i("UriPermissionManager", "No package specified, not granting permissions") 52 | return 53 | } 54 | } 55 | 56 | fun grantUriPermissionsForPackage(context: Context, packageName: String) { 57 | for (suffix in suffixes) { 58 | for (i in 0..10) { 59 | val uri = 60 | Uri.parse("content://" + context.packageName + ".provider/external_files/extra_input_" + i + "." + suffix); 61 | 62 | Log.i( 63 | "contentprovider", 64 | "Granting permission to " + packageName + " for " + uri.toString() 65 | ); 66 | 67 | context.grantUriPermission( 68 | packageName, 69 | uri, 70 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION 71 | ); 72 | } 73 | } 74 | } 75 | 76 | companion object { 77 | fun grantUriPermissionsForPackage(context: Context, packageName: String) { 78 | UriPermissionManager().grantUriPermissionsForPackage(context, packageName) 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /content_provider/app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | 31 | -------------------------------------------------------------------------------- /content_provider/app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /content_provider/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 |