diff --git a/README.md b/README.md index 739600d..4355c4c 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ To get started: -l ./android/android-proxy-override.js \ -l ./android/android-system-certificate-injection.js \ -l ./android/android-certificate-unpinning.js \ - -l ./android/android-fallback-script.js \ + -l ./android/android-certificate-unpinning-fallback.js \ -f $PACKAGE_ID ``` 7. Explore, examine & modify all the traffic you're interested in! If you have any problems, please [open an issue](https://github.com/httptoolkit/frida-android-unpinning/issues/new) and help make these scripts even better. diff --git a/android/android-fallback-script.js b/android/android-certificate-unpinning-fallback.js similarity index 90% rename from android/android-fallback-script.js rename to android/android-certificate-unpinning-fallback.js index b36b511..d808f3b 100644 --- a/android/android-fallback-script.js +++ b/android/android-certificate-unpinning-fallback.js @@ -1,22 +1,24 @@ -/** - * Once we've set up the configuration and certificate, and then disabled all the - * pinning techniques we're aware of, we add one last touch: a fallback hook, - * designed to spot and handle unknown unknowns. +/************************************************************************************************** * - * This can also be useful for heavily obfuscated apps, where 3rd party libraries - * are obfuscated sufficiently that our hooks no longer recognize the methods we - * care about. + * Once we've set up the configuration and certificate, and then disabled all the pinning + * techniques we're aware of, we add one last touch: a fallback hook, designed to spot and handle + * unknown unknowns. * - * To handle this, we watch for methods that throw known built-in TLS errors - * (these are _very_ widely used, and always recognizable as they're defined natively), - * and then subsequently patch them for all future calls. Whenever a method throws - * this, we attempt to recognize it from signatures alone, and automatically hook it. + * This can also be useful for heavily obfuscated apps, where 3rd party libraries are obfuscated + * sufficiently that our hooks no longer recognize the methods we care about. * - * These are very much a fallback! They might not work! They almost certainly won't - * work on the first request, so applications will see at least one failure. - * Even when they fail though, they will at least log the method that's failing, so - * this works well as a starting point for manual reverse engineering. - */ + * To handle this, we watch for methods that throw known built-in TLS errors (these are *very* + * widely used, and always recognizable as they're defined natively), and then subsequently patch + * them for all future calls. Whenever a method throws this, we attempt to recognize it from + * signatures alone, and automatically hook it. + * + * These are very much a fallback! They might not work! They almost certainly won't work on the + * first request, so applications will see at least one failure. Even when they fail though, they + * will at least log the method that's failing, so this works well as a starting point for manual + * reverse engineering. If this does fail and cause problems, you may want to skip this script + * and use only the known-good patches provided elsewhere. + * + *************************************************************************************************/ // Capture the full fields or methods from a Frida class reference via JVM reflection: const getFields = (cls) => getFridaValues(cls, cls.class.getDeclaredFields()); diff --git a/android/android-certificate-unpinning.js b/android/android-certificate-unpinning.js index 5d4121c..ad54882 100644 --- a/android/android-certificate-unpinning.js +++ b/android/android-certificate-unpinning.js @@ -1,3 +1,24 @@ +/************************************************************************************************** + * + * This script defines a large set of targeted certificate unpinning hooks: matching specific + * methods in certain classes, and transforming their behaviour to ensure that restrictions to + * TLS trust are disabled. + * + * This does not disable TLS protections completely - each hook is designed to disable only + * *additional* restrictions, and to explicitly trust the certificate provided as CERT_PEM in the + * config.js configuration file, preserving normal TLS protections wherever possible, even while + * allowing for controlled MitM of local traffic. + * + * The file consists of a few general-purpose methods, then a data structure declaratively + * defining the classes & methods to match, and how to transform them, and then logic at the end + * which uses this data structure, applying the transformation for each found match to the + * target process. + * + * For more details on what was matched, and log output when each hooked method is actually used, + * enable DEBUG_MODE in config.js, and watch the Frida output after running this script. + * + *************************************************************************************************/ + function buildX509CertificateFromBytes(certBytes) { const ByteArrayInputStream = Java.use('java.io.ByteArrayInputStream'); const CertFactory = Java.use('java.security.cert.CertificateFactory'); diff --git a/android/android-proxy-override.js b/android/android-proxy-override.js index 4e5e9ca..b268c71 100644 --- a/android/android-proxy-override.js +++ b/android/android-proxy-override.js @@ -1,4 +1,5 @@ -/** +/************************************************************************************************** + * * The first step in intercepting HTTP & HTTPS traffic is to set the default proxy settings, * telling the app that all requests should be sent via our HTTP proxy. * @@ -11,7 +12,8 @@ * with a framework that does not do this by default (Flutter is notably in this category). * To handle those less tidy cases, we manually capture traffic to recognized target ports * in the native connect() hook script. - */ + * + *************************************************************************************************/ Java.perform(() => { // Set default JVM system properties for the proxy address. Notably these are used diff --git a/android/android-system-certificate-injection.js b/android/android-system-certificate-injection.js index c86801f..4b9897c 100644 --- a/android/android-system-certificate-injection.js +++ b/android/android-system-certificate-injection.js @@ -1,4 +1,5 @@ -/** +/************************************************************************************************** + * * Once we have captured traffic (once it's being sent to our proxy port) the next step is * to ensure any clients using TLS (HTTPS) trust our CA certificate, to allow us to intercept * encrypted connections successfully. @@ -11,7 +12,8 @@ * This does not handle all standalone certificate pinning techniques - where the application * actively rejects certificates that are trusted by default on the system. That's dealt with * in the separate certificate unpinning script. - */ + * + *************************************************************************************************/ Java.perform(() => { // First, we build a JVM representation of our certificate: diff --git a/config.js b/config.js index 71831dd..a91c09b 100644 --- a/config.js +++ b/config.js @@ -1,3 +1,11 @@ +/****************************************************************************** + * + * This file defines various config parameters, used later within the other + * scripts. In all cases, you'll want to set CERT_PEM and likely + * PROXY_HOST and PROXY_PORT. + * + *****************************************************************************/ + // Local testing certificate for now const CERT_PEM = `-----BEGIN CERTIFICATE----- MIIDTzCCAjegAwIBAgIRClDpdJeylUmDvNyq5qq+plcwDQYJKoZIhvcNAQELBQAw