├── .gitignore ├── BadIntentAndroid ├── app │ ├── build.gradle │ ├── libs │ │ └── nanohttpd-webserver-2.3.2-SNAPSHOT-jar-with-dependencies.jar │ ├── proguard-rules.pro │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── assets │ │ └── xposed_init │ │ ├── java │ │ └── de │ │ │ └── mat3 │ │ │ └── badintent │ │ │ ├── app │ │ │ ├── AppAnalyzer.java │ │ │ ├── BadIntentConstants.java │ │ │ └── BadIntentPreferencesActivity.java │ │ │ ├── hooking │ │ │ ├── BaseHook.java │ │ │ └── proxy │ │ │ │ ├── AppInformation.java │ │ │ │ ├── ParcelAgent.java │ │ │ │ ├── ParcelContainer.java │ │ │ │ ├── ParcelIdGenerator.java │ │ │ │ ├── RestAPI.java │ │ │ │ ├── dao │ │ │ │ ├── NotSerializable.java │ │ │ │ ├── ParcelOperation.java │ │ │ │ ├── ParcelOperationDAO.java │ │ │ │ ├── ParcelPoolElementDAO.java │ │ │ │ └── SerializationUtils.java │ │ │ │ └── hooks │ │ │ │ ├── ConnectionUtils.java │ │ │ │ ├── LogHooks.java │ │ │ │ ├── ParcelBaseWriteHook.java │ │ │ │ ├── ParcelProxyHooks.java │ │ │ │ └── TransactionHooks.java │ │ │ └── utils │ │ │ ├── HookingManager.java │ │ │ ├── LoggingInstrumentation.java │ │ │ └── LoggingMetaData.java │ │ └── res │ │ ├── drawable │ │ ├── android_icon.png │ │ ├── ic_info_black_24dp.xml │ │ ├── ic_notifications_black_24dp.xml │ │ └── ic_sync_black_24dp.xml │ │ ├── values-w820dp │ │ └── dimens.xml │ │ ├── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ └── bad_intent_preferences.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle ├── BadIntentBurp ├── pom.xml └── src │ └── main │ └── java │ ├── burp │ ├── ActiveAppsTab.java │ └── BurpExtender.java │ ├── controller │ └── BadIntentMessageEditorTab.java │ ├── dao │ ├── AppInfo.java │ ├── ParcelOperationDAO.java │ └── ParcelPoolElementDAO.java │ ├── listener │ └── ColoringListener.java │ ├── ui │ ├── ActiveAppView.java │ ├── ParcelByteArrDetails.java │ ├── ParcelPanel.java │ └── ParcelTab.java │ └── util │ └── HttpHelper.java ├── LICENSE ├── README.md └── doc └── img ├── access_control_repeat.png ├── access_control_result.png ├── aidl_keystore_generate_key.png ├── aidl_keystore_result.png ├── aidl_keystore_sequencer.png ├── aidl_keystore_sequencer_results.png ├── aidl_keystore_stub.png ├── aidl_keystore_transaction_codes.png ├── backend_attacks_backend.png ├── backend_attacks_intruder.png ├── backend_attacks_intruder_config.png ├── burp_interfaces.png ├── configuration.png ├── insecure_logging.png ├── intent_sniffing_broadcast.png ├── keylogger.png ├── main.png ├── mobile_xss_intercept.png ├── mobile_xss_modify.png ├── mobile_xss_result.png ├── overview.png └── pasteboard.png /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/java,maven,android,intellij,androidstudio 3 | 4 | ### Android ### 5 | # Built application files 6 | *.apk 7 | *.ap_ 8 | 9 | # Files for the ART/Dalvik VM 10 | *.dex 11 | 12 | # Java class files 13 | *.class 14 | 15 | # Generated files 16 | bin/ 17 | gen/ 18 | out/ 19 | 20 | # Gradle files 21 | .gradle/ 22 | build/ 23 | 24 | # Local configuration file (sdk path, etc) 25 | local.properties 26 | 27 | # Proguard folder generated by Eclipse 28 | proguard/ 29 | 30 | # Log Files 31 | *.log 32 | 33 | # Android Studio Navigation editor temp files 34 | .navigation/ 35 | 36 | # Android Studio captures folder 37 | captures/ 38 | 39 | # Intellij 40 | *.iml 41 | .idea/workspace.xml 42 | .idea/tasks.xml 43 | .idea/gradle.xml 44 | .idea/dictionaries 45 | .idea/libraries 46 | 47 | # Keystore files 48 | *.jks 49 | 50 | # External native build folder generated in Android Studio 2.2 and later 51 | .externalNativeBuild 52 | 53 | # Google Services (e.g. APIs or Firebase) 54 | google-services.json 55 | 56 | # Freeline 57 | freeline.py 58 | freeline/ 59 | freeline_project_description.json 60 | 61 | ### Android Patch ### 62 | gen-external-apklibs 63 | 64 | ### AndroidStudio ### 65 | # Covers files to be ignored for android development using Android Studio. 66 | 67 | # Built application files 68 | 69 | # Files for the ART/Dalvik VM 70 | 71 | # Java class files 72 | 73 | # Generated files 74 | 75 | # Gradle files 76 | .gradle 77 | 78 | # Signing files 79 | .signing/ 80 | 81 | # Local configuration file (sdk path, etc) 82 | 83 | # Proguard folder generated by Eclipse 84 | 85 | # Log Files 86 | 87 | # Android Studio 88 | /*/build/ 89 | /*/local.properties 90 | /*/out 91 | /*/*/build 92 | /*/*/production 93 | *.ipr 94 | *~ 95 | *.swp 96 | 97 | # Android Patch 98 | 99 | # External native build folder generated in Android Studio 2.2 and later 100 | 101 | # NDK 102 | obj/ 103 | 104 | # IntelliJ IDEA 105 | *.iws 106 | /out/ 107 | 108 | # User-specific configurations 109 | .idea/libraries/ 110 | .idea/.name 111 | .idea/compiler.xml 112 | .idea/copyright/profiles_settings.xml 113 | .idea/encodings.xml 114 | .idea/misc.xml 115 | .idea/modules.xml 116 | .idea/scopes/scope_settings.xml 117 | .idea/vcs.xml 118 | .idea/jsLibraryMappings.xml 119 | .idea/datasources.xml 120 | .idea/dataSources.ids 121 | .idea/sqlDataSources.xml 122 | .idea/dynamic.xml 123 | .idea/uiDesigner.xml 124 | 125 | # Keystore files 126 | 127 | # OS-specific files 128 | .DS_Store 129 | .DS_Store? 130 | ._* 131 | .Spotlight-V100 132 | .Trashes 133 | ehthumbs.db 134 | Thumbs.db 135 | 136 | # Legacy Eclipse project files 137 | .classpath 138 | .project 139 | 140 | # Mobile Tools for Java (J2ME) 141 | .mtj.tmp/ 142 | 143 | # Package Files # 144 | #*.jar 145 | *gradle-wrapper.jar 146 | *.war 147 | *.ear 148 | 149 | # virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml) 150 | hs_err_pid* 151 | 152 | ## Plugin-specific files: 153 | 154 | # mpeltonen/sbt-idea plugin 155 | .idea_modules/ 156 | 157 | # JIRA plugin 158 | atlassian-ide-plugin.xml 159 | 160 | # Mongo Explorer plugin 161 | .idea/mongoSettings.xml 162 | 163 | # Crashlytics plugin (for Android Studio and IntelliJ) 164 | com_crashlytics_export_strings.xml 165 | crashlytics.properties 166 | crashlytics-build.properties 167 | fabric.properties 168 | 169 | ### AndroidStudio Patch ### 170 | # Google Services plugin 171 | 172 | !/gradle/wrapper/gradle-wrapper.jar 173 | 174 | ### Intellij ### 175 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 176 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 177 | 178 | # User-specific stuff: 179 | .idea/**/workspace.xml 180 | .idea/**/tasks.xml 181 | 182 | # Sensitive or high-churn files: 183 | .idea/**/dataSources/ 184 | .idea/**/dataSources.ids 185 | .idea/**/dataSources.xml 186 | .idea/**/dataSources.local.xml 187 | .idea/**/sqlDataSources.xml 188 | .idea/**/dynamic.xml 189 | .idea/**/uiDesigner.xml 190 | 191 | # Gradle: 192 | .idea/**/gradle.xml 193 | .idea/**/libraries 194 | 195 | # CMake 196 | cmake-build-debug/ 197 | 198 | # Mongo Explorer plugin: 199 | .idea/**/mongoSettings.xml 200 | 201 | ## File-based project format: 202 | 203 | ## Plugin-specific files: 204 | 205 | # IntelliJ 206 | .idea 207 | 208 | # mpeltonen/sbt-idea plugin 209 | 210 | # JIRA plugin 211 | 212 | # Cursive Clojure plugin 213 | .idea/replstate.xml 214 | 215 | # Crashlytics plugin (for Android Studio and IntelliJ) 216 | 217 | ### Intellij Patch ### 218 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 219 | 220 | # *.iml 221 | # modules.xml 222 | # .idea/misc.xml 223 | # *.ipr 224 | 225 | # Sonarlint plugin 226 | .idea/sonarlint 227 | 228 | ### Java ### 229 | # Compiled class file 230 | 231 | # Log file 232 | 233 | # BlueJ files 234 | *.ctxt 235 | 236 | # Mobile Tools for Java (J2ME) 237 | 238 | # Package Files # 239 | *.zip 240 | *.tar.gz 241 | *.rar 242 | 243 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 244 | 245 | ### Maven ### 246 | target/ 247 | pom.xml.tag 248 | pom.xml.releaseBackup 249 | pom.xml.versionsBackup 250 | pom.xml.next 251 | release.properties 252 | dependency-reduced-pom.xml 253 | buildNumber.properties 254 | .mvn/timing.properties 255 | 256 | # Avoid ignoring Maven wrapper jar file (.jar files are usually ignored) 257 | !/.mvn/wrapper/maven-wrapper.jar 258 | 259 | # End of https://www.gitignore.io/api/java,maven,android,intellij,androidstudio 260 | -------------------------------------------------------------------------------- /BadIntentAndroid/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.3" 6 | defaultConfig { 7 | applicationId "de.mat3.badintent" 8 | minSdkVersion 15 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "0.9.9.9" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | vectorDrawables.useSupportLibrary = true 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | } 22 | 23 | dependencies { 24 | provided 'de.robv.android.xposed:api:82:sources' 25 | compile fileTree(dir: 'libs', include: ['*.jar']) 26 | compile group: 'com.google.code.gson', name: 'gson', version: '2.8.0' 27 | compile 'com.android.support:appcompat-v7:25.1.0' 28 | compile 'com.google.guava:guava:22.0-android' 29 | compile 'com.android.support:support-v4:25.3.1' 30 | compile 'com.android.support:support-vector-drawable:25.3.1' 31 | compile 'com.android.support.constraint:constraint-layout:+' 32 | compile 'com.android.support:design:25.3.1' 33 | testCompile 'junit:junit:4.12' 34 | provided 'de.robv.android.xposed:api:82' 35 | } 36 | -------------------------------------------------------------------------------- /BadIntentAndroid/app/libs/nanohttpd-webserver-2.3.2-SNAPSHOT-jar-with-dependencies.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1ultimat3/BadIntent/fbd34c61ad980e2d475fb95d2bfac9759cd81941/BadIntentAndroid/app/libs/nanohttpd-webserver-2.3.2-SNAPSHOT-jar-with-dependencies.jar -------------------------------------------------------------------------------- /BadIntentAndroid/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in C:\Users\mkhalil\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /BadIntentAndroid/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 13 | 14 | 15 | 18 | 21 | 24 | 25 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /BadIntentAndroid/app/src/main/assets/xposed_init: -------------------------------------------------------------------------------- 1 | de.mat3.badintent.app.AppAnalyzer -------------------------------------------------------------------------------- /BadIntentAndroid/app/src/main/java/de/mat3/badintent/app/AppAnalyzer.java: -------------------------------------------------------------------------------- 1 | package de.mat3.badintent.app; 2 | 3 | 4 | import android.os.StrictMode; 5 | import android.util.Log; 6 | 7 | import java.io.File; 8 | import java.io.IOException; 9 | import java.net.ServerSocket; 10 | 11 | import de.mat3.badintent.hooking.BaseHook; 12 | import de.mat3.badintent.hooking.proxy.AppInformation; 13 | import de.mat3.badintent.hooking.proxy.hooks.LogHooks; 14 | import de.mat3.badintent.hooking.proxy.hooks.ParcelProxyHooks; 15 | import de.mat3.badintent.hooking.proxy.hooks.TransactionHooks; 16 | import de.mat3.badintent.utils.HookingManager; 17 | import de.robv.android.xposed.IXposedHookLoadPackage; 18 | import de.robv.android.xposed.XSharedPreferences; 19 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 20 | 21 | 22 | public class AppAnalyzer implements IXposedHookLoadPackage { 23 | 24 | public static final String PREFNAME = "de.mat3.badintent_preferences"; 25 | public static final String PREFERENCE_PATH = "/data/data/de.mat3.badintent/shared_prefs/" + PREFNAME + ".xml"; 26 | public static XSharedPreferences sPrefs; 27 | protected static final String TAG = "BadIntentAnalyzer"; 28 | 29 | @Override 30 | public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable { 31 | sPrefs = new XSharedPreferences(new File(PREFERENCE_PATH)); 32 | sPrefs.reload(); 33 | 34 | String target = lpparam.packageName; 35 | 36 | //bypass various apps depending on preferences 37 | String[] bypass = getBypassList(); 38 | String packageNameFilter = sPrefs.getString("package_filter", ""); 39 | for (String bypassElement : bypass) { 40 | /* check if package filter does not equal this package 41 | NOTE: this is a special condition in order to overwrite the hook_system_switch; 42 | However, the packageNameFilter has to exactly match exactly the target package. 43 | */ 44 | if (target.matches(bypassElement) && !target.equals(packageNameFilter)){ 45 | return; 46 | } 47 | } 48 | 49 | //disable strict mode in order to prevent unclosed connections file usage 50 | StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().build()); 51 | 52 | HookingManager hookingManager = new HookingManager(lpparam, target); 53 | 54 | int port = getRandomPort(); 55 | 56 | //updating current app's meta data 57 | AppInformation.Instance.packageName = lpparam.packageName; 58 | AppInformation.Instance.port = port; 59 | Log.d(TAG, "Hooking package:" + target + " port: " + port); 60 | if (hookingManager.continueHooking()) { 61 | BaseHook base = hookingManager.getBaseHook(); 62 | ParcelProxyHooks parcelProxyHooks = new ParcelProxyHooks(base, port); 63 | parcelProxyHooks.hookParcel(); 64 | TransactionHooks transactionHooks = new TransactionHooks(base, sPrefs, port); 65 | transactionHooks.hookBinder(); 66 | LogHooks logHooks = new LogHooks(base, sPrefs, port); 67 | logHooks.hookLogs(); 68 | } 69 | } 70 | 71 | /** 72 | * Bypass various typical system apps 73 | * Note: this is only a best effort strategy; 74 | * in order to get complete list of system apps use 75 | * "pm list packages -s" 76 | * 77 | * @return list of package regex which should be bypassed 78 | */ 79 | protected String[] getBypassList() { 80 | boolean hookSystem = sPrefs.getBoolean(BadIntentConstants.HOOK_SYSTEM_SWITCH, false); 81 | if (hookSystem) { 82 | //only bypass android core 83 | return new String[]{ 84 | "android" 85 | }; 86 | } else { 87 | return new String[]{ 88 | "android", 89 | "com.android.*", 90 | "com.google.*", 91 | "de.mat3.badintent", 92 | }; 93 | } 94 | } 95 | 96 | protected static int getRandomPort() throws IOException { 97 | ServerSocket s = new ServerSocket(0); 98 | int port = s.getLocalPort(); 99 | s.close(); 100 | return port; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /BadIntentAndroid/app/src/main/java/de/mat3/badintent/app/BadIntentConstants.java: -------------------------------------------------------------------------------- 1 | package de.mat3.badintent.app; 2 | 3 | 4 | /** 5 | * Preferences constants 6 | */ 7 | public class BadIntentConstants { 8 | public static final String TARGET_IP = "target_ip"; 9 | public static final String FILTER_INTERFACE_TOKEN = "interface_filter"; 10 | public static final String FILTER_PACKAGE_NAME = "package_filter"; 11 | public static final String CAPTURE_LOG = "capture_log"; 12 | public static final String HOOK_SYSTEM_SWITCH = "hook_system_switch"; 13 | public static final String USE_SYSTEM_PROXY = "use_system_proxy"; 14 | public static final String PROXY_HOST = "proxy_host"; 15 | public static final String PROXY_PORT = "proxy_port"; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /BadIntentAndroid/app/src/main/java/de/mat3/badintent/app/BadIntentPreferencesActivity.java: -------------------------------------------------------------------------------- 1 | package de.mat3.badintent.app; 2 | 3 | import android.content.SharedPreferences; 4 | import android.net.wifi.WifiManager; 5 | import android.os.Bundle; 6 | import android.preference.PreferenceActivity; 7 | import android.text.format.Formatter; 8 | 9 | 10 | public class BadIntentPreferencesActivity extends PreferenceActivity { 11 | 12 | @Override 13 | protected void onCreate(Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | 16 | //try to set this WiFi IP (in case there is no [valid] value set yet) 17 | WifiManager wm = (WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE); 18 | String wifiIP = Formatter.formatIpAddress(wm.getConnectionInfo().getIpAddress()); 19 | SharedPreferences sPrefs = getSharedPreferences(AppAnalyzer.PREFNAME, MODE_WORLD_READABLE); 20 | String target_ip = sPrefs.getString(BadIntentConstants.TARGET_IP, " "); 21 | if (target_ip.equals(" ") || target_ip.equals("0.0.0.0")) { 22 | sPrefs.edit() 23 | .putString(BadIntentConstants.TARGET_IP, wifiIP) 24 | .apply(); 25 | } 26 | addPreferencesFromResource(R.xml.bad_intent_preferences); 27 | 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /BadIntentAndroid/app/src/main/java/de/mat3/badintent/hooking/BaseHook.java: -------------------------------------------------------------------------------- 1 | package de.mat3.badintent.hooking; 2 | 3 | import java.lang.reflect.Method; 4 | 5 | import de.mat3.badintent.utils.LoggingInstrumentation; 6 | import de.mat3.badintent.utils.LoggingMetaData; 7 | import de.robv.android.xposed.XC_MethodHook; 8 | import de.robv.android.xposed.XposedBridge; 9 | import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam; 10 | 11 | /** 12 | * BaseHook has the objective to support the Xposed module developer to writing hooks more efficient. 13 | */ 14 | public class BaseHook { 15 | 16 | 17 | protected String targetPackage; 18 | protected LoadPackageParam lpparam; 19 | protected ClassLoader cloader; 20 | 21 | public BaseHook(LoadPackageParam lpparam, String targetPackage) { 22 | this.lpparam = lpparam; 23 | this.targetPackage = targetPackage; 24 | cloader = lpparam.classLoader; 25 | } 26 | 27 | public BaseHook(BaseHook h) { 28 | this.lpparam = h.lpparam; 29 | this.targetPackage = h.targetPackage; 30 | cloader = lpparam.classLoader; 31 | } 32 | 33 | 34 | public void logAll(String classname){ 35 | logAllConstructors(classname); 36 | logAllMethods(classname); 37 | } 38 | 39 | /** 40 | * This methods logs all methods and results in a best effort approach. 41 | * @param classname 42 | * 43 | */ 44 | public void logAllMethods(final String classname) { 45 | try { 46 | Class aClass = cloader.loadClass(classname); 47 | for (Method method : aClass.getDeclaredMethods()) { 48 | logMethod(classname, method.getName()); 49 | } 50 | } 51 | catch (RuntimeException e) { /** best effort */ } 52 | catch (Exception e) { /** best effort */ } 53 | } 54 | 55 | public void logMethod(String classname, String methodname) { 56 | try { 57 | Class aClass = cloader.loadClass(classname); 58 | final LoggingMetaData metaData = new LoggingMetaData(classname); 59 | 60 | XposedBridge.hookAllMethods(aClass, methodname, new XC_MethodHook() { 61 | @Override 62 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable { 63 | metaData.beforeHookedMethod(param); 64 | LoggingInstrumentation.printParameters(metaData); 65 | super.beforeHookedMethod(param); 66 | } 67 | 68 | @Override 69 | protected void afterHookedMethod(MethodHookParam param) throws Throwable { 70 | metaData.afterHookedMethod(param); 71 | LoggingInstrumentation.printResult(metaData); 72 | super.afterHookedMethod(param); 73 | 74 | } 75 | }); 76 | 77 | } 78 | catch (RuntimeException e) { /** best effort */ } 79 | catch (Exception e) { /** best effort */ } 80 | } 81 | 82 | 83 | /** 84 | * This methods logs all methods and results in a best effort approach. 85 | * @param classname 86 | * 87 | */ 88 | public void logAllConstructors(final String classname) { 89 | try { 90 | Class aClass = cloader.loadClass(classname); 91 | final LoggingMetaData metaData = new LoggingMetaData(classname); 92 | 93 | XposedBridge.hookAllConstructors(aClass, new XC_MethodHook() { 94 | @Override 95 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable { 96 | metaData.beforeHookedMethod(param); 97 | LoggingInstrumentation.printParameters(metaData); 98 | super.beforeHookedMethod(param); 99 | } 100 | 101 | @Override 102 | protected void afterHookedMethod(MethodHookParam param) throws Throwable { 103 | metaData.afterHookedMethod(param); 104 | LoggingInstrumentation.printResult(metaData); 105 | super.afterHookedMethod(param); 106 | 107 | } 108 | }); 109 | } 110 | catch (RuntimeException e) { /** best effort */ } 111 | catch (Exception e) { /** best effort */ } 112 | } 113 | 114 | public void overwriteResults(String classname, String method, final Object result){ 115 | try { 116 | Class aClass = cloader.loadClass(classname); 117 | XposedBridge.hookAllMethods(aClass, method, new XC_MethodHook() { 118 | @Override 119 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable { 120 | param.setResult(result); 121 | } 122 | }); 123 | 124 | } catch (ClassNotFoundException e) { 125 | e.printStackTrace(); 126 | } 127 | } 128 | 129 | 130 | } 131 | -------------------------------------------------------------------------------- /BadIntentAndroid/app/src/main/java/de/mat3/badintent/hooking/proxy/AppInformation.java: -------------------------------------------------------------------------------- 1 | package de.mat3.badintent.hooking.proxy; 2 | 3 | import android.os.Parcel; 4 | import android.util.Log; 5 | 6 | import com.google.common.collect.Sets; 7 | 8 | import java.util.Set; 9 | 10 | import de.mat3.badintent.app.BadIntentConstants; 11 | import de.mat3.badintent.hooking.proxy.dao.ParcelOperation; 12 | import de.robv.android.xposed.XSharedPreferences; 13 | 14 | /** 15 | * AppInformation contains data about the currently hooked App 16 | */ 17 | public enum AppInformation { 18 | 19 | Instance; 20 | 21 | public String packageName; 22 | public int port; 23 | public XSharedPreferences sPrefs; 24 | public Set usedInterfaceTokens = Sets.newConcurrentHashSet(); 25 | public Set interceptedInterfaceTokens = Sets.newConcurrentHashSet(); 26 | 27 | private static final String TAG = "BadIntentAppInfo"; 28 | 29 | /** 30 | * Determine if the parcel has to be intercepted with respect to: 31 | * 1. interface token regex 32 | * 2. current package regex 33 | * @param data 34 | * @return true iff should be intercepted 35 | */ 36 | public boolean intercept(Parcel data) { 37 | String filterInterfaceToken = sPrefs.getString(BadIntentConstants.FILTER_INTERFACE_TOKEN, ""); 38 | String filterPackageName = sPrefs.getString( 39 | BadIntentConstants.FILTER_PACKAGE_NAME, ""); 40 | //check if should be intercepted 41 | if (packageName.matches(filterPackageName)) { 42 | ParcelContainer container = ParcelAgent.Instance.getOrCreateParcelContainer(data); 43 | 44 | if (container.operationList.size() == 0) { 45 | Log.w(TAG, "Container without operations identified..."); 46 | return false; 47 | } else { 48 | ParcelOperation op = container.operationList.get(0); 49 | if (op.operationType.equals(ParcelOperation.ParcelType.INTERFACE_TOKEN)) { 50 | //add for statistic purposes 51 | String interfaceTokenValue = op.operationValue.toString(); 52 | usedInterfaceTokens.add(interfaceTokenValue); 53 | 54 | //check if filter matches 55 | boolean matches = interfaceTokenValue.matches(filterInterfaceToken); 56 | if (matches) { 57 | interceptedInterfaceTokens.add(interfaceTokenValue); 58 | } 59 | return matches; 60 | } else { 61 | //Log.w(TAG, "no interface token found, no intercept!"); 62 | return false; 63 | } 64 | } 65 | } else { 66 | return false; 67 | } 68 | 69 | } 70 | 71 | /** 72 | * Determine if the package has to be intercepted 73 | * @return true iff should be intercepted 74 | */ 75 | public boolean intercept() { 76 | String filterPackageName = sPrefs.getString( 77 | BadIntentConstants.FILTER_PACKAGE_NAME, ""); 78 | //check if should be intercepted 79 | return packageName.matches(filterPackageName); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /BadIntentAndroid/app/src/main/java/de/mat3/badintent/hooking/proxy/ParcelAgent.java: -------------------------------------------------------------------------------- 1 | package de.mat3.badintent.hooking.proxy; 2 | 3 | 4 | import android.os.IBinder; 5 | import android.os.Parcel; 6 | import android.os.RemoteException; 7 | import android.util.Log; 8 | 9 | import java.io.FileDescriptor; 10 | import java.lang.reflect.InvocationTargetException; 11 | import java.lang.reflect.Method; 12 | import java.util.Arrays; 13 | import java.util.Map; 14 | import java.util.NoSuchElementException; 15 | import java.util.concurrent.ConcurrentHashMap; 16 | 17 | import de.mat3.badintent.hooking.proxy.dao.ParcelOperation; 18 | 19 | /** 20 | * This singleton is responsible to create a bridge between Android Parcels/Transactions and the app-specific RestAPI. 21 | * Note: Parcel hooks are aware if operations are created by ParcelAgent or another class. 22 | */ 23 | public enum ParcelAgent { 24 | 25 | //Singleton 26 | Instance; 27 | 28 | //storage of parcel operations and meta data 29 | protected Map parcelPool = new ConcurrentHashMap<>(); 30 | //strong binders are stored here 31 | protected Map strongBinderPool = new ConcurrentHashMap<>(); 32 | 33 | private static final String TAG = "BadIntentAgent"; 34 | 35 | 36 | protected ParcelContainer getOrCreateParcelContainer(Parcel parcel){ 37 | if (!parcelPool.containsKey(parcel)) { 38 | ParcelContainer container = new ParcelContainer(parcel); 39 | parcelPool.put(parcel, container); 40 | } 41 | return parcelPool.get(parcel); 42 | } 43 | 44 | public void interruptString(Parcel parcel, String arg) { 45 | ParcelContainer container = getOrCreateParcelContainer(parcel); 46 | waitForPreconditions(container); 47 | container.addString(arg); 48 | } 49 | 50 | public void interruptInteger(Parcel parcel, Integer arg) { 51 | ParcelContainer container = getOrCreateParcelContainer(parcel); 52 | waitForPreconditions(container); 53 | container.addInt(arg); 54 | } 55 | 56 | public void interruptLong(Parcel parcel, Long arg) { 57 | ParcelContainer container = getOrCreateParcelContainer(parcel); 58 | waitForPreconditions(container); 59 | container.addLong(arg); 60 | } 61 | 62 | public void interruptFloat(Parcel parcel, Float arg) { 63 | ParcelContainer container = getOrCreateParcelContainer(parcel); 64 | waitForPreconditions(container); 65 | container.addFloat(arg); 66 | } 67 | 68 | public void interruptDouble(Parcel parcel, Double arg) { 69 | ParcelContainer container = getOrCreateParcelContainer(parcel); 70 | waitForPreconditions(container); 71 | container.addDouble(arg); 72 | } 73 | 74 | public void interruptByteArray(Parcel parcel, byte[] arg, int offset, int len) { 75 | ParcelContainer container = getOrCreateParcelContainer(parcel); 76 | waitForPreconditions(container); 77 | container.addByteArray(arg, offset, len); 78 | } 79 | 80 | public void interruptBlob(Parcel parcel, byte[] arg, int offset, int len) { 81 | ParcelContainer container = getOrCreateParcelContainer(parcel); 82 | waitForPreconditions(container); 83 | container.addBlob(arg, offset, len); 84 | } 85 | 86 | public void interruptFileDescriptor(Parcel parcel, FileDescriptor arg) { 87 | ParcelContainer container = getOrCreateParcelContainer(parcel); 88 | waitForPreconditions(container); 89 | container.addFileDescriptor(arg); 90 | } 91 | 92 | public void interruptStrongBinder(Parcel parcel, IBinder arg) { 93 | ParcelContainer container = getOrCreateParcelContainer(parcel); 94 | waitForPreconditions(container); 95 | if (arg != null) { 96 | strongBinderPool.put(arg.toString(), arg); 97 | } 98 | container.addStrongBinder(arg); 99 | } 100 | 101 | public void interruptInterfaceToken(Parcel parcel, String arg) { 102 | ParcelContainer container = getOrCreateParcelContainer(parcel); 103 | waitForPreconditions(container); 104 | container.addInterfaceToken(arg); 105 | } 106 | 107 | public void interruptDataPosition(Parcel parcel, int arg) { 108 | ParcelContainer container = getOrCreateParcelContainer(parcel); 109 | waitForPreconditions(container); 110 | container.addDataPosition(arg); 111 | } 112 | 113 | public void interruptDataCapacity(Parcel parcel, int arg) { 114 | ParcelContainer container = getOrCreateParcelContainer(parcel); 115 | waitForPreconditions(container); 116 | container.addDataCapacity(arg); 117 | } 118 | 119 | public void interruptDataSize(Parcel parcel, int arg) { 120 | ParcelContainer container = getOrCreateParcelContainer(parcel); 121 | waitForPreconditions(container); 122 | container.addDataSize(arg); 123 | } 124 | 125 | public void interruptPushAllowFds(Parcel parcel, boolean arg) { 126 | ParcelContainer container = getOrCreateParcelContainer(parcel); 127 | waitForPreconditions(container); 128 | container.addPushAllowFds(arg); 129 | } 130 | 131 | public void interruptAppendFrom(Parcel parcel, Parcel arg, int arg1, int arg2) { 132 | ParcelContainer container = getOrCreateParcelContainer(parcel); 133 | waitForPreconditions(container); 134 | container.addAppendFrom(arg, arg1, arg2); 135 | } 136 | 137 | private void waitForPreconditions(ParcelContainer container) { 138 | waitForState(container, ParcelContainer.State.NEW, ParcelContainer.State.DONE); 139 | } 140 | 141 | public void writeInterfaceToken(Parcel parcel, String arg) { 142 | parcel.writeInterfaceToken(arg); 143 | } 144 | 145 | public void writeStrongBinder(Parcel parcel, IBinder arg) { 146 | parcel.writeStrongBinder(arg); 147 | } 148 | 149 | public void writeByteArray(Parcel parcel, byte[] arg, int offset, int len) { 150 | parcel.writeByteArray(arg, offset, len); 151 | } 152 | 153 | public void writeBlob(Parcel parcel, byte[] arg, int offset, int len) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { 154 | Method writeBlob = Parcel.class.getDeclaredMethod("writeBlob", new Class[] { byte[].class, int.class, int.class}); 155 | writeBlob.invoke(parcel, new Object[]{arg, offset, len}); 156 | } 157 | 158 | public void writeString(Parcel parcel, String arg) { 159 | parcel.writeString(arg); 160 | } 161 | 162 | public void writeInt(Parcel parcel, Integer arg) { 163 | parcel.writeInt(arg); 164 | } 165 | 166 | public void writeLong(Parcel parcel, Long arg) { 167 | parcel.writeLong(arg); 168 | } 169 | 170 | public void writeFloat(Parcel parcel, Float arg) { 171 | parcel.writeFloat(arg); 172 | } 173 | 174 | public void writeDouble(Parcel parcel, Double arg) { 175 | parcel.writeDouble(arg); 176 | } 177 | 178 | public void writeFileDescriptor(Parcel parcel, FileDescriptor arg) { 179 | parcel.writeFileDescriptor(arg); 180 | } 181 | 182 | public Map getParcelPool() { 183 | return parcelPool; 184 | } 185 | 186 | 187 | public void writeDataPosition(Parcel parcel, int arg) { 188 | parcel.setDataPosition(arg); 189 | } 190 | 191 | public void writeDataCapacity(Parcel parcel, int arg) { 192 | parcel.setDataCapacity(arg); 193 | } 194 | 195 | public void writeDataSize(Parcel parcel, int arg) { 196 | parcel.setDataSize(arg); 197 | } 198 | 199 | public void writePushAllowFds(Parcel parcel, boolean arg) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { 200 | Method writeBlob = Parcel.class.getDeclaredMethod("pushAllowFds", new Class[] { boolean.class}); 201 | writeBlob.invoke(parcel, new Object[]{arg}); 202 | } 203 | 204 | public void writeAppendFrom(Parcel parcel, Parcel arg, int arg1, int arg2) { 205 | parcel.appendFrom(arg, arg1, arg2); 206 | } 207 | 208 | /** 209 | * Looks up a parcel by the parcelId which is currently the hashcode of the parcel object. 210 | * @param parcelId 211 | * @return ParcelContainer form the parcel pool 212 | * @throws NoSuchElementException 213 | */ 214 | public ParcelContainer getParcelContainerById(int parcelId) throws NoSuchElementException { 215 | for (Map.Entry poolEntry : parcelPool.entrySet()){ 216 | if (poolEntry.getValue().getParcelID() == parcelId) 217 | return poolEntry.getValue(); 218 | } 219 | throw new NoSuchElementException(); 220 | } 221 | 222 | public ParcelContainer getParcelContainer(Parcel data) { 223 | return parcelPool.get(data); 224 | } 225 | 226 | public boolean isInterruptRequest(int parcelId) { 227 | for (Map.Entry poolEntry : parcelPool.entrySet()){ 228 | if (poolEntry.getValue().getParcelID() == parcelId) 229 | return poolEntry.getValue().getState() == ParcelContainer.State.SENT; 230 | } 231 | return false; 232 | } 233 | 234 | /** 235 | * Write operations from container to native binder 236 | * @param container 237 | */ 238 | public void materialize(ParcelContainer container, Parcel parcel) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { 239 | for (ParcelOperation operation : container.operationList){ 240 | Object value = operation.operationValue; 241 | //Log.d(TAG, "materializing: " + operation.operationType); 242 | //Log.d(TAG, " value: " + value); 243 | switch (operation.operationType){ 244 | case BLOB: 245 | ParcelOperation.ArrayValue val = (ParcelOperation.ArrayValue) value; 246 | writeBlob(parcel, val.array, val.offset, val.len); 247 | break; 248 | case BYTE_ARRAY: 249 | val = (ParcelOperation.ArrayValue) value; 250 | writeByteArray(parcel, val.array, val.offset, val.len); 251 | break; 252 | case DOUBLE: 253 | writeDouble(parcel, (Double) value); 254 | break; 255 | case FILE_DESCRIPTOR: 256 | writeFileDescriptor(parcel, (FileDescriptor) value); 257 | break; 258 | case FLOAT: 259 | writeFloat(parcel, (Float) value); 260 | break; 261 | case INTEGER: 262 | writeInt(parcel, (Integer) value); 263 | break; 264 | case INTERFACE_TOKEN: 265 | writeInterfaceToken(parcel, (String) value); 266 | break; 267 | case LONG: 268 | writeLong(parcel, (Long) value); 269 | break; 270 | case STRING: 271 | writeString(parcel, (String) value); 272 | break; 273 | case STRONG_BINDER: 274 | IBinder binder = null; 275 | if (value != null) { 276 | binder = strongBinderPool.get(value); 277 | } 278 | writeStrongBinder(parcel, binder); 279 | break; 280 | case APPEND_FROM: 281 | ParcelOperation.AppendFromValue appendFromValue = (ParcelOperation.AppendFromValue) value; 282 | Parcel otherParcel = getParcelContainerById(appendFromValue.parcelId).parcel; 283 | writeAppendFrom(parcel, otherParcel, appendFromValue.offset, appendFromValue.length); 284 | break; 285 | case DATA_CAPACITY: 286 | writeDataCapacity(parcel, (int) value); 287 | break; 288 | case DATA_POSITION: 289 | writeDataPosition(parcel, (int) value); 290 | break; 291 | case DATA_SIZE: 292 | writeDataSize(parcel, (int) value); 293 | break; 294 | case PUSH_ALLOW_FDS: 295 | writePushAllowFds(parcel, (boolean) value); 296 | break; 297 | default: 298 | Log.w(TAG, "Missing Implementation for Operation: " + operation.operationType); 299 | } 300 | } 301 | } 302 | 303 | /** 304 | * This method waits until the container, which corresponds to the passed parcel 305 | * moves its state to any of the expected ones. 306 | * @param container 307 | * @param states one of the expected states has to be met 308 | */ 309 | public void waitForState(ParcelContainer container, ParcelContainer.State... states) { 310 | 311 | try { 312 | while (true) { 313 | if (states.length == 1) { 314 | //Log.d(TAG, data.hashCode() + " current " + getParcelContainerById(data.hashCode()).getState().toString() + " waiting for: " + states[0].toString()); 315 | 316 | } else { 317 | // Log.d(TAG, data.hashCode() + " current " + getParcelContainerById(data.hashCode()).getState().toString() + " waiting for any:" + Arrays.asList(states).toString()); 318 | } 319 | for (ParcelContainer.State state : states) { 320 | if (container.getState().equals(state)) { 321 | return; 322 | } 323 | } 324 | Thread.sleep(100); 325 | } 326 | } catch (InterruptedException e) { 327 | Log.w(TAG, "interrupted waiting for state(s)"); 328 | } 329 | } 330 | 331 | 332 | public void addBinder(IBinder binderProxy, ParcelContainer container) { 333 | try { 334 | container.binderProxyMap.put(binderProxy.getInterfaceDescriptor(), binderProxy); 335 | } catch (RemoteException e) { 336 | Log.e(TAG, "could not retrieve interface descriptor!"); 337 | } 338 | } 339 | 340 | public Parcel createNewParcelBaseOnContainer(ParcelContainer data) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { 341 | Parcel newParcel = Parcel.obtain(); 342 | materialize(data, newParcel); 343 | return newParcel; 344 | } 345 | 346 | public Parcel obtainParcel() { 347 | return Parcel.obtain(); 348 | } 349 | 350 | public Parcel copyParcel(Parcel reply) { 351 | Parcel copy = Parcel.obtain(); 352 | copy.appendFrom(reply, 0, reply.dataSize()); 353 | return copy; 354 | } 355 | 356 | public boolean transact(IBinder binderProxy, int code, Parcel send, Parcel receive, int flags) throws RemoteException { 357 | return binderProxy.transact(code, send, receive, flags); 358 | } 359 | 360 | 361 | public void updateState(ParcelContainer container, ParcelContainer.State state) { 362 | container.setState(state); 363 | } 364 | } 365 | -------------------------------------------------------------------------------- /BadIntentAndroid/app/src/main/java/de/mat3/badintent/hooking/proxy/ParcelContainer.java: -------------------------------------------------------------------------------- 1 | package de.mat3.badintent.hooking.proxy; 2 | 3 | import android.os.IBinder; 4 | import android.os.Parcel; 5 | 6 | import java.io.FileDescriptor; 7 | import java.util.HashMap; 8 | import java.util.LinkedList; 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | import de.mat3.badintent.hooking.proxy.dao.NotSerializable; 13 | import de.mat3.badintent.hooking.proxy.dao.ParcelOperation; 14 | 15 | /** 16 | * This class is supposed to save serialized Parcel data 17 | * 18 | * The lifecycle for the container is as follows 19 | * (however, please note that parcels are reused and can therefore be reset to NEW/DONE): 20 | * 1. In case the transaction is intercepted: 21 | * NEW,DONE --> SENT --> RECEIVED --> REPLY --> DONE 22 | * 2. Transaction is not intercepted 23 | * NEW,DONE --> BYPASS --> BYPASS --> DONE 24 | * 3. In a error situation 25 | * * --> ERROR --> ERROR --> DONE 26 | */ 27 | public class ParcelContainer { 28 | 29 | private static final String TAG = "BadIntentPContainer"; 30 | 31 | public enum State { NEW, SENT, RECEIVED, REPLY, ERROR, DONE, BYPASS}; 32 | 33 | protected List operationList = new LinkedList<>(); 34 | protected Map binderProxyMap = new HashMap<>(); 35 | 36 | @NotSerializable protected Parcel parcel; 37 | private int parcelID; 38 | private State state; 39 | private Parcel reply; 40 | 41 | public ParcelContainer(Parcel parcel) { 42 | this.parcel = parcel; 43 | this.parcelID = ParcelIdGenerator.getNextParcelId(); 44 | state = State.NEW; 45 | } 46 | 47 | public void addString(String arg) { 48 | ParcelOperation operation = new ParcelOperation(ParcelOperation.ParcelType.STRING, arg); 49 | operationList.add(operation); 50 | } 51 | 52 | public void addInt(Integer arg) { 53 | ParcelOperation operation = new ParcelOperation(ParcelOperation.ParcelType.INTEGER, arg); 54 | operationList.add(operation); 55 | } 56 | 57 | public void addLong(Long arg) { 58 | ParcelOperation operation = new ParcelOperation(ParcelOperation.ParcelType.LONG, arg); 59 | operationList.add(operation); 60 | } 61 | 62 | public void addFloat(Float arg) { 63 | ParcelOperation operation = new ParcelOperation(ParcelOperation.ParcelType.FLOAT, arg); 64 | operationList.add(operation); 65 | } 66 | 67 | public void addDouble(Double arg) { 68 | ParcelOperation operation = new ParcelOperation(ParcelOperation.ParcelType.DOUBLE, arg); 69 | operationList.add(operation); 70 | } 71 | 72 | public void addByteArray(byte[] arg, int offset, int len) { 73 | ParcelOperation.ArrayValue arrayValue = new ParcelOperation.ArrayValue(arg, offset, len); 74 | ParcelOperation operation = new ParcelOperation(ParcelOperation.ParcelType.BYTE_ARRAY, arrayValue); 75 | operationList.add(operation); 76 | } 77 | 78 | public void addBlob(byte[] arg, int offset, int len) { 79 | ParcelOperation.ArrayValue arrayValue = new ParcelOperation.ArrayValue(arg, offset, len); 80 | ParcelOperation operation = new ParcelOperation(ParcelOperation.ParcelType.BLOB, arrayValue); 81 | operationList.add(operation); 82 | } 83 | 84 | public void addFileDescriptor(FileDescriptor arg) { 85 | ParcelOperation operation = new ParcelOperation(ParcelOperation.ParcelType.FILE_DESCRIPTOR, arg); 86 | operationList.add(operation); 87 | } 88 | 89 | public void addStrongBinder(IBinder arg) { 90 | String binderArg = null; 91 | if (arg != null){ 92 | binderArg = arg.toString(); 93 | } 94 | ParcelOperation operation = new ParcelOperation(ParcelOperation.ParcelType.STRONG_BINDER, binderArg); 95 | operationList.add(operation); 96 | } 97 | 98 | public void addInterfaceToken(String arg) { 99 | ParcelOperation operation = new ParcelOperation(ParcelOperation.ParcelType.INTERFACE_TOKEN, arg); 100 | operationList.add(operation); 101 | } 102 | 103 | public void addDataPosition(int arg) { 104 | ParcelOperation operation = new ParcelOperation(ParcelOperation.ParcelType.DATA_POSITION, arg); 105 | operationList.add(operation); 106 | } 107 | 108 | public void addDataCapacity(int arg) { 109 | ParcelOperation operation = new ParcelOperation(ParcelOperation.ParcelType.DATA_CAPACITY, arg); 110 | operationList.add(operation); 111 | } 112 | 113 | public void addDataSize(int arg) { 114 | ParcelOperation operation = new ParcelOperation(ParcelOperation.ParcelType.DATA_SIZE, arg); 115 | operationList.add(operation); 116 | } 117 | 118 | public void addPushAllowFds(boolean arg) { 119 | ParcelOperation operation = new ParcelOperation(ParcelOperation.ParcelType.PUSH_ALLOW_FDS, arg); 120 | operationList.add(operation); 121 | } 122 | 123 | public void addAppendFrom(Parcel arg, int arg1, int arg2) { 124 | ParcelContainer otherParcel = ParcelAgent.Instance.getOrCreateParcelContainer(arg); 125 | ParcelOperation.AppendFromValue appendFromValue = new ParcelOperation.AppendFromValue(otherParcel.getParcelID(), arg1, arg2); 126 | ParcelOperation operation = new ParcelOperation(ParcelOperation.ParcelType.APPEND_FROM, appendFromValue); 127 | operationList.add(operation); 128 | } 129 | 130 | public void setReply(Parcel reply) { 131 | this.reply = reply; 132 | } 133 | 134 | public Parcel getReply() { 135 | return reply; 136 | } 137 | 138 | public int getParcelID() { 139 | return parcelID; 140 | } 141 | 142 | public State getState() { 143 | return state; 144 | } 145 | 146 | public void setState(State state) { 147 | // Log.d(TAG, "ParcelId: " + parcelID + " setting state: " + state.toString()); 148 | this.state = state; 149 | } 150 | 151 | public Parcel getParcel() { 152 | return parcel; 153 | } 154 | 155 | public List getOperationList() { 156 | return operationList; 157 | } 158 | 159 | @Override 160 | public String toString() { 161 | StringBuffer buf = new StringBuffer("ParcelContainer state: " + state.toString() + " #Parcels" + operationList.size()); 162 | return buf.toString(); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /BadIntentAndroid/app/src/main/java/de/mat3/badintent/hooking/proxy/ParcelIdGenerator.java: -------------------------------------------------------------------------------- 1 | package de.mat3.badintent.hooking.proxy; 2 | 3 | 4 | 5 | public class ParcelIdGenerator { 6 | 7 | private static int nextParcelId = 0; 8 | 9 | public synchronized static int getNextParcelId(){ 10 | return nextParcelId++; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /BadIntentAndroid/app/src/main/java/de/mat3/badintent/hooking/proxy/RestAPI.java: -------------------------------------------------------------------------------- 1 | package de.mat3.badintent.hooking.proxy; 2 | 3 | import android.os.IBinder; 4 | import android.os.Parcel; 5 | import android.support.annotation.NonNull; 6 | import android.util.Log; 7 | 8 | import com.google.common.io.BaseEncoding; 9 | import com.google.gson.Gson; 10 | import com.google.gson.internal.LinkedTreeMap; 11 | 12 | import org.nanohttpd.protocols.http.IHTTPSession; 13 | import org.nanohttpd.protocols.http.NanoHTTPD; 14 | import org.nanohttpd.protocols.http.response.Response; 15 | 16 | import java.util.HashMap; 17 | import java.util.Iterator; 18 | import java.util.LinkedList; 19 | import java.util.List; 20 | import java.util.Map; 21 | import java.util.NoSuchElementException; 22 | 23 | import de.mat3.badintent.hooking.proxy.dao.ParcelOperation; 24 | import de.mat3.badintent.hooking.proxy.dao.ParcelOperationDAO; 25 | import de.mat3.badintent.hooking.proxy.dao.ParcelPoolElementDAO; 26 | import de.mat3.badintent.hooking.proxy.dao.SerializationUtils; 27 | 28 | import static org.nanohttpd.protocols.http.response.Response.newFixedLengthResponse; 29 | 30 | /** 31 | * REST API in order to interact with the Parcel: 32 | * GET /parcel/list in order to get all ids in the parcel pool 33 | * GET /parcel/ retrieves the parcel data 34 | * POST /parcel/ updates and writes parcel data to Binder 35 | * PUT /parcel/ creates and writes parcel data to Binder 36 | * GET /info received information about the app (currently only package name) 37 | * PUT /log log information target (no action) 38 | */ 39 | public class RestAPI extends NanoHTTPD { 40 | 41 | public static final String TAG = "BadIntentRestAPI"; 42 | 43 | public RestAPI(String hostname, int port) { 44 | super(hostname, port); 45 | Log.i(TAG, "Starting internal Webserver at " + hostname + ":" + port); 46 | } 47 | 48 | @Override 49 | public Response serve(IHTTPSession session) { 50 | String path = session.getUri(); 51 | 52 | if (path.startsWith("/parcel/list")) { 53 | Log.d(TAG, "Serving HTTP Request /parcel/list"); 54 | try { 55 | List parcelIds = new LinkedList<>(); 56 | Map parcelPool = ParcelAgent.Instance.getParcelPool(); 57 | Log.d(TAG, "ParcelPool contains #elements: " + parcelPool.size()); 58 | for (ParcelContainer container : parcelPool.values()) { 59 | parcelIds.add(container.getParcelID()); 60 | } 61 | Log.d(TAG, "ParcelPool IDs: " + parcelIds.toString()); 62 | Response response = newFixedLengthResponse(SerializationUtils.gson.toJson(parcelIds)); 63 | return finalize(response); 64 | } catch (Exception e) { 65 | Response response = newFixedLengthResponse("ERROR\n" + e.getMessage()); 66 | return finalize(response); 67 | } 68 | 69 | } else if (path.startsWith("/parcel/")){ 70 | //Log.d(TAG, "Serving HTTP Request /parcel/"); 71 | //a parcelID is expected /parcel/ 72 | int parcelId = Integer.parseInt(path.split("/")[2]); 73 | //Log.d(TAG, "Receiving request regarding " + parcelId); 74 | switch (session.getMethod()){ 75 | case GET: 76 | ParcelContainer container = ParcelAgent.Instance.getParcelContainerById(parcelId); 77 | //Log.d(TAG, "#Operations: " + container.operationList.size()); 78 | ParcelPoolElementDAO parcelPoolElementDAO = convertContainerToDAO(container, 0, 0); 79 | String jsonResponse = SerializationUtils.gson.toJson(parcelPoolElementDAO); 80 | //Log.d(TAG, "writing out container via HTTP"); 81 | return finalize(newFixedLengthResponse(jsonResponse)); 82 | case POST: 83 | //update and materialize parcel 84 | try { 85 | Map files = new HashMap(); 86 | session.parseBody(files); 87 | String postBody = files.get("postData"); 88 | //Log.d(TAG, "parsing JSON in POST body:" + postBody); 89 | ParcelPoolElementDAO receivedPoolElementDAO = SerializationUtils.gson.fromJson(postBody, ParcelPoolElementDAO.class); 90 | if (ParcelAgent.Instance.isInterruptRequest(receivedPoolElementDAO.parcelID)) { 91 | //Log.d(TAG, "INTERRUPT identified for: " + receivedPoolElementDAO.parcelID); 92 | //Log.d(TAG, "Retrieving ParcelContainer for: " + receivedPoolElementDAO.parcelID); 93 | ParcelContainer localContainer = ParcelAgent.Instance.getParcelContainerById(receivedPoolElementDAO.parcelID); 94 | Iterator localOperations = localContainer.operationList.iterator(); 95 | Iterator receivedOperations = receivedPoolElementDAO.operations.iterator(); 96 | //Log.d(TAG, "Taking over changes for " + receivedPoolElementDAO.parcelID); 97 | //Take over changes from DAO to container 98 | while (localOperations.hasNext() && receivedOperations.hasNext()) { 99 | ParcelOperation localOperation = localOperations.next(); 100 | ParcelOperationDAO receivedOperation = receivedOperations.next(); 101 | switch (receivedOperation.parcelType) { 102 | case STRONG_BINDER: 103 | localOperation.operationValue = receivedOperation.value; 104 | break; 105 | case BLOB: 106 | LinkedTreeMap receivedStructure = (LinkedTreeMap) receivedOperation.value; 107 | localOperation.operationValue = ParcelOperation.ArrayValue.parseArrayValue(receivedStructure); 108 | break; 109 | case BYTE_ARRAY: 110 | receivedStructure = (LinkedTreeMap) receivedOperation.value; 111 | localOperation.operationValue = ParcelOperation.ArrayValue.parseArrayValue(receivedStructure); 112 | break; 113 | case DOUBLE: 114 | localOperation.operationValue = Double.parseDouble((String) receivedOperation.value); 115 | break; 116 | case FLOAT: 117 | localOperation.operationValue = Float.parseFloat((String) receivedOperation.value); 118 | break; 119 | case INTEGER: 120 | localOperation.operationValue = Integer.parseInt((String) receivedOperation.value); 121 | break; 122 | case LONG: 123 | localOperation.operationValue = Long.parseLong((String) receivedOperation.value); 124 | break; 125 | case DATA_SIZE: 126 | localOperation.operationValue = Integer.parseInt((String) receivedOperation.value); 127 | break; 128 | case DATA_CAPACITY: 129 | localOperation.operationValue = Integer.parseInt((String) receivedOperation.value); 130 | break; 131 | case DATA_POSITION: 132 | localOperation.operationValue = Integer.parseInt((String) receivedOperation.value); 133 | break; 134 | case PUSH_ALLOW_FDS: 135 | localOperation.operationValue = Boolean.parseBoolean((String) receivedOperation.value); 136 | break; 137 | case INTERFACE_TOKEN: 138 | localOperation.operationValue = receivedOperation.value; 139 | break; 140 | case FILE_DESCRIPTOR: 141 | localOperation.operationValue = Integer.parseInt((String) receivedOperation.value); 142 | break; 143 | case APPEND_FROM: 144 | receivedStructure = (LinkedTreeMap) receivedOperation.value; 145 | localOperation.operationValue = ParcelOperation.AppendFromValue.parseAppendFromValue(receivedStructure); 146 | break; 147 | case STRING: 148 | localOperation.operationValue = receivedOperation.value; 149 | break; 150 | } 151 | } 152 | localContainer.setState(ParcelContainer.State.RECEIVED); 153 | 154 | ParcelAgent.Instance.waitForState(localContainer, ParcelContainer.State.REPLY, ParcelContainer.State.ERROR); 155 | 156 | Response response = createHttpResponseFromReply(localContainer.getReply()); 157 | 158 | //Need to reset state 159 | cleanContainer(localContainer); 160 | 161 | return finalize(response); 162 | } else { 163 | Log.d(TAG, "REPEAT identified for: " + receivedPoolElementDAO.parcelID); 164 | int code = receivedPoolElementDAO.transactionCode; 165 | int flags = receivedPoolElementDAO.transactionFlags; 166 | Parcel send = ParcelAgent.Instance.obtainParcel(); 167 | Parcel receive = ParcelAgent.Instance.obtainParcel(); 168 | ParcelContainer newContainer = ParcelAgent.Instance.getOrCreateParcelContainer(send); 169 | 170 | //taking over data 171 | container = ParcelAgent.Instance.getParcelContainerById(parcelId); 172 | newContainer.binderProxyMap.putAll(container.binderProxyMap); 173 | 174 | for (ParcelOperationDAO operation : receivedPoolElementDAO.operations) { 175 | //Log.d(TAG, "data: " + operation.parcelType + " " + operation.value); 176 | switch (operation.parcelType) { 177 | case STRONG_BINDER: 178 | IBinder binder = newContainer.binderProxyMap.get(operation.value); 179 | newContainer.addStrongBinder(binder); 180 | break; 181 | case BLOB: 182 | LinkedTreeMap receivedStructure = (LinkedTreeMap) operation.value; 183 | ParcelOperation.ArrayValue operationValue = ParcelOperation.ArrayValue.parseArrayValue(receivedStructure); 184 | newContainer.addBlob(operationValue.array, operationValue.offset, operationValue.len); 185 | break; 186 | case BYTE_ARRAY: 187 | receivedStructure = (LinkedTreeMap) operation.value; 188 | operationValue = ParcelOperation.ArrayValue.parseArrayValue(receivedStructure); 189 | newContainer.addByteArray(operationValue.array, operationValue.offset, operationValue.len); 190 | break; 191 | case DOUBLE: 192 | newContainer.addDouble(Double.parseDouble((String) operation.value)); 193 | break; 194 | case FLOAT: 195 | newContainer.addFloat(Float.parseFloat((String) operation.value)); 196 | break; 197 | case INTEGER: 198 | newContainer.addInt(Integer.parseInt((String) operation.value)); 199 | break; 200 | case LONG: 201 | newContainer.addLong(Long.parseLong((String) operation.value)); 202 | break; 203 | case DATA_SIZE: 204 | newContainer.addDataSize(Integer.parseInt((String) operation.value)); 205 | break; 206 | case DATA_CAPACITY: 207 | newContainer.addDataCapacity(Integer.parseInt((String) operation.value)); 208 | break; 209 | case DATA_POSITION: 210 | newContainer.addDataPosition(Integer.parseInt((String) operation.value)); 211 | break; 212 | case PUSH_ALLOW_FDS: 213 | newContainer.addPushAllowFds(Boolean.parseBoolean((String) operation.value)); 214 | break; 215 | case INTERFACE_TOKEN: 216 | newContainer.addInterfaceToken((String) operation.value); 217 | break; 218 | case FILE_DESCRIPTOR: 219 | newContainer.addInt(Integer.parseInt((String) operation.value)); 220 | Log.w(TAG, "(new) FileDescriptors are supported in repeat mode. However, value is written out as an integer"); 221 | break; 222 | case APPEND_FROM: 223 | receivedStructure = (LinkedTreeMap) operation.value; 224 | ParcelOperation.AppendFromValue operationValueAppendFrom = ParcelOperation.AppendFromValue.parseAppendFromValue(receivedStructure); 225 | Log.w(TAG, "AppendFrom not fully supported in repeat mode."); 226 | newContainer.addAppendFrom( 227 | ParcelAgent.Instance.getParcelPool().get(operationValueAppendFrom.parcelId).parcel, 228 | operationValueAppendFrom.offset, 229 | operationValueAppendFrom.length 230 | ); 231 | break; 232 | case STRING: 233 | newContainer.addString((String) operation.value); 234 | break; 235 | default: 236 | Log.w(TAG, "value not added to container: " + operation.value); 237 | } 238 | } 239 | ParcelAgent.Instance.materialize(newContainer, send); 240 | 241 | try { 242 | String interfaceToken = (String) newContainer.operationList.get(0).operationValue; 243 | IBinder binderProxy = newContainer.binderProxyMap.get(interfaceToken); 244 | boolean result = ParcelAgent.Instance.transact(binderProxy, code, send, receive, flags); 245 | Response response = createHttpResponseFromReply(receive); 246 | return response; 247 | } catch (Exception e) { 248 | //show detailed error 249 | return finalize(newFixedLengthResponse(e.getMessage())); 250 | } finally { 251 | //finally set back state to DONE and clear operations 252 | cleanContainer(container); 253 | } 254 | } 255 | 256 | } catch (Exception e) { 257 | Log.e(TAG, e.getMessage()); 258 | try { 259 | ParcelAgent.Instance.getParcelContainerById(parcelId).setState(ParcelContainer.State.ERROR); 260 | } catch (NoSuchElementException _) { 261 | Log.e(TAG, "Cannot find parcel " + parcelId + " - no state update to ERROR"); 262 | } 263 | } 264 | 265 | break; 266 | default: 267 | break; //return default error 268 | } 269 | } else if (path.startsWith("/info")){ 270 | Map data = new HashMap<>(); 271 | data.put("packageName", AppInformation.Instance.packageName); 272 | data.put("restPort", Integer.toString(AppInformation.Instance.port)); 273 | data.put("interfaceTokens", AppInformation.Instance.usedInterfaceTokens); 274 | data.put("interceptedInterfaceTokens", AppInformation.Instance.interceptedInterfaceTokens); 275 | String metadata = SerializationUtils.createGson().toJson(data); 276 | return finalize(newFixedLengthResponse(metadata)); 277 | } else if (path.startsWith("/log")) { 278 | Map data = new HashMap<>(); 279 | data.put("status", "OK"); 280 | String metadata = SerializationUtils.createGson().toJson(data); 281 | return finalize(newFixedLengthResponse(metadata)); 282 | } 283 | 284 | Log.e(TAG, "Serving HTTP Request: no action found!"); 285 | return finalize(newFixedLengthResponse("ERROR")); 286 | 287 | } 288 | 289 | private Response finalize(Response response) { 290 | response.closeConnection(true); 291 | return response; 292 | } 293 | 294 | @NonNull 295 | protected Response createHttpResponseFromReply(Parcel reply) { 296 | Gson gson = SerializationUtils.createGson(); 297 | Map data = createResponseMap(reply); 298 | return finalize(newFixedLengthResponse(gson.toJson(data))); 299 | } 300 | 301 | @NonNull 302 | private Map createResponseMap(Parcel reply) { 303 | Map data = new HashMap<>(); 304 | data.put("message", "reply"); 305 | if (reply != null) { 306 | try { 307 | byte[] marshalled = reply.marshall(); 308 | data.put("data", marshalled); 309 | if (marshalled != null) { 310 | String base64 = BaseEncoding.base64().encode(marshalled).replace("/", ""); 311 | data.put("data_base64", base64); 312 | } 313 | } catch (RuntimeException e) { 314 | data.put("info", "activeObject"); 315 | } 316 | } 317 | return data; 318 | } 319 | 320 | protected void cleanContainer(ParcelContainer localContainer) { 321 | localContainer.setState(ParcelContainer.State.DONE); 322 | } 323 | 324 | @NonNull 325 | public static ParcelPoolElementDAO convertContainerToDAO(ParcelContainer container, int code, int flags) { 326 | ParcelPoolElementDAO parcelPoolElementDAO = new ParcelPoolElementDAO(); 327 | parcelPoolElementDAO.parcelID = container.getParcelID(); 328 | parcelPoolElementDAO.state = container.getState(); 329 | parcelPoolElementDAO.transactionCode = code; 330 | parcelPoolElementDAO.transactionFlags = flags; 331 | 332 | for (ParcelOperation operation : container.operationList){ 333 | //Log.d(TAG, " " + operation.operationType + " : " + operation.operationValue); 334 | ParcelOperationDAO parcelOperationDAO = new ParcelOperationDAO(); 335 | parcelOperationDAO.parcelType = operation.operationType; 336 | if (operation.operationType.equals(ParcelOperation.ParcelType.BYTE_ARRAY) 337 | || operation.operationType.equals(ParcelOperation.ParcelType.BLOB) 338 | || operation.operationType.equals(ParcelOperation.ParcelType.APPEND_FROM)) { 339 | parcelOperationDAO.value = operation.operationValue; 340 | } else { 341 | if (operation.operationValue != null) { 342 | parcelOperationDAO.value = operation.operationValue.toString(); 343 | } 344 | } 345 | 346 | parcelPoolElementDAO.operations.add(parcelOperationDAO); 347 | } 348 | return parcelPoolElementDAO; 349 | } 350 | 351 | } 352 | 353 | -------------------------------------------------------------------------------- /BadIntentAndroid/app/src/main/java/de/mat3/badintent/hooking/proxy/dao/NotSerializable.java: -------------------------------------------------------------------------------- 1 | package de.mat3.badintent.hooking.proxy.dao; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target({ElementType.FIELD}) 10 | public @interface NotSerializable { 11 | } 12 | -------------------------------------------------------------------------------- /BadIntentAndroid/app/src/main/java/de/mat3/badintent/hooking/proxy/dao/ParcelOperation.java: -------------------------------------------------------------------------------- 1 | package de.mat3.badintent.hooking.proxy.dao; 2 | 3 | import android.support.annotation.NonNull; 4 | 5 | import com.google.gson.internal.LinkedTreeMap; 6 | 7 | import java.util.ArrayList; 8 | 9 | /** 10 | * A parcel operation consists of a type and value. 11 | */ 12 | public class ParcelOperation { 13 | 14 | 15 | public enum ParcelType { 16 | STRING, 17 | LONG, 18 | FLOAT, 19 | DOUBLE, 20 | BYTE_ARRAY, 21 | BLOB, 22 | FILE_DESCRIPTOR, 23 | STRONG_BINDER, 24 | INTEGER, 25 | 26 | DATA_POSITION, 27 | DATA_CAPACITY, 28 | DATA_SIZE, 29 | 30 | PUSH_ALLOW_FDS, 31 | 32 | APPEND_FROM, 33 | INTERFACE_TOKEN 34 | 35 | } 36 | 37 | public static class ArrayValue { 38 | public byte[] array; 39 | public int offset; 40 | public int len; 41 | 42 | public ArrayValue(byte[] array, int offset, int len) { 43 | this.len = len; 44 | this.array = array; 45 | this.offset = offset; 46 | } 47 | 48 | public ArrayValue(ArrayList array, int offset, int len) { 49 | this.len = len; 50 | if (array != null) { 51 | byte[] result = new byte[array.size()]; 52 | for (int i = 0; i < array.size(); i++) { 53 | result[i] = (byte) ((Double) array.get(i)).intValue(); 54 | } 55 | this.array = result; 56 | } 57 | this.offset = offset; 58 | } 59 | 60 | @NonNull 61 | public static ArrayValue parseArrayValue(LinkedTreeMap receivedStructure) { 62 | return new ArrayValue( 63 | (ArrayList) receivedStructure.get("array"), 64 | ((Double) receivedStructure.get("len")).intValue(), 65 | ((Double) receivedStructure.get("offset")).intValue() 66 | ); 67 | } 68 | } 69 | 70 | public static class AppendFromValue { 71 | public int parcelId; 72 | public int offset; 73 | public int length; 74 | 75 | public AppendFromValue(int parcelId, int offset, int length) { 76 | this.parcelId = parcelId; 77 | this.offset = offset; 78 | this.length = length; 79 | } 80 | 81 | @NonNull 82 | public static AppendFromValue parseAppendFromValue(LinkedTreeMap receivedStructure) { 83 | return new AppendFromValue( 84 | ((Double) receivedStructure.get("parcelId")).intValue(), 85 | ((Double) receivedStructure.get("offset")).intValue(), 86 | ((Double) receivedStructure.get("length")).intValue() 87 | ); 88 | } 89 | } 90 | 91 | public ParcelType operationType; 92 | public Object operationValue; 93 | 94 | public ParcelOperation(ParcelType operationType, Object operationValue) { 95 | this.operationType = operationType; 96 | this.operationValue = operationValue; 97 | } 98 | 99 | @Override 100 | public String toString() { 101 | return "Operation: " + operationType.name() + " Value: " + operationValue; 102 | } 103 | 104 | 105 | } 106 | -------------------------------------------------------------------------------- /BadIntentAndroid/app/src/main/java/de/mat3/badintent/hooking/proxy/dao/ParcelOperationDAO.java: -------------------------------------------------------------------------------- 1 | package de.mat3.badintent.hooking.proxy.dao; 2 | 3 | 4 | public class ParcelOperationDAO { 5 | public ParcelOperation.ParcelType parcelType; 6 | public Object value; 7 | } 8 | -------------------------------------------------------------------------------- /BadIntentAndroid/app/src/main/java/de/mat3/badintent/hooking/proxy/dao/ParcelPoolElementDAO.java: -------------------------------------------------------------------------------- 1 | package de.mat3.badintent.hooking.proxy.dao; 2 | 3 | 4 | import java.util.LinkedList; 5 | import java.util.List; 6 | 7 | import de.mat3.badintent.hooking.proxy.ParcelContainer; 8 | 9 | public class ParcelPoolElementDAO { 10 | public int parcelID; 11 | public List operations = new LinkedList<>(); 12 | public ParcelContainer.State state; 13 | public int transactionCode; 14 | public int transactionFlags; 15 | } 16 | -------------------------------------------------------------------------------- /BadIntentAndroid/app/src/main/java/de/mat3/badintent/hooking/proxy/dao/SerializationUtils.java: -------------------------------------------------------------------------------- 1 | package de.mat3.badintent.hooking.proxy.dao; 2 | 3 | 4 | import android.os.Parcel; 5 | import android.support.annotation.NonNull; 6 | 7 | import com.google.gson.ExclusionStrategy; 8 | import com.google.gson.FieldAttributes; 9 | import com.google.gson.Gson; 10 | import com.google.gson.GsonBuilder; 11 | 12 | import de.mat3.badintent.hooking.proxy.ParcelAgent; 13 | import de.mat3.badintent.hooking.proxy.ParcelContainer; 14 | import de.mat3.badintent.hooking.proxy.RestAPI; 15 | 16 | public class SerializationUtils { 17 | 18 | public final static Gson gson = createGson(); 19 | 20 | public static String getJson(ParcelContainer container, int code, int flags) { 21 | ParcelPoolElementDAO parcelPoolElementDAO = RestAPI.convertContainerToDAO(container, code, flags); 22 | return gson.toJson(parcelPoolElementDAO); 23 | } 24 | 25 | @NonNull 26 | public static Gson createGson() { 27 | return new GsonBuilder().setExclusionStrategies(new ExclusionStrategy() { 28 | @Override 29 | public boolean shouldSkipField(FieldAttributes f) { 30 | return f.getAnnotation(NotSerializable.class) != null; 31 | } 32 | 33 | @Override 34 | public boolean shouldSkipClass(Class clazz) { 35 | return false; 36 | } 37 | }).create(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /BadIntentAndroid/app/src/main/java/de/mat3/badintent/hooking/proxy/hooks/ConnectionUtils.java: -------------------------------------------------------------------------------- 1 | package de.mat3.badintent.hooking.proxy.hooks; 2 | 3 | 4 | import android.support.annotation.NonNull; 5 | import android.util.Log; 6 | 7 | import java.io.BufferedReader; 8 | import java.io.IOException; 9 | import java.io.InputStreamReader; 10 | import java.net.HttpURLConnection; 11 | import java.net.InetSocketAddress; 12 | import java.net.MalformedURLException; 13 | import java.net.Proxy; 14 | import java.net.URL; 15 | 16 | import de.mat3.badintent.app.BadIntentConstants; 17 | import de.robv.android.xposed.XSharedPreferences; 18 | 19 | public class ConnectionUtils { 20 | 21 | /** 22 | * Opens proxyfied connection based on BadIntent preferences. 23 | * @param url 24 | * @param sPrefs 25 | * @return opened HttpURLConnection 26 | * @throws IOException 27 | */ 28 | public static HttpURLConnection getBadIntentHttpURLConnection(URL url, XSharedPreferences sPrefs) throws IOException { 29 | final HttpURLConnection conn; 30 | if (sPrefs.getBoolean(BadIntentConstants.USE_SYSTEM_PROXY, true)){ 31 | conn = (HttpURLConnection) url.openConnection(); 32 | } else { 33 | String host = sPrefs.getString(BadIntentConstants.PROXY_HOST, "localhost"); 34 | int port = Integer.parseInt(sPrefs.getString(BadIntentConstants.PROXY_PORT, "8080")); 35 | Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(host, port)); 36 | conn = (HttpURLConnection) url.openConnection(proxy); 37 | } 38 | return conn; 39 | } 40 | 41 | /** 42 | * Get REST URL based on the BadIntent preferences. 43 | * @param resource needs to start with / 44 | * @param sPrefs 45 | * @param port 46 | * @return 47 | * @throws MalformedURLException 48 | */ 49 | public static URL getBadIntentURL(String resource, XSharedPreferences sPrefs, int port) throws MalformedURLException { 50 | String ip = sPrefs.getString(BadIntentConstants.TARGET_IP, "localhost"); 51 | return new URL("http://" + ip + ":" + port + resource); 52 | } 53 | 54 | public static Thread readResponseAndCloseConnection(final HttpURLConnection conn) { 55 | return new Thread(new Runnable() { 56 | @Override 57 | public void run() { 58 | BufferedReader reader = null; 59 | try { 60 | reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); 61 | StringBuffer buffer = new StringBuffer(); 62 | String line = ""; 63 | while ((line = reader.readLine()) != null) { 64 | buffer.append(line); 65 | } 66 | } catch (IOException e) { 67 | Log.e(TransactionHooks.TAG, "could not receive HTTP data!"); 68 | Log.e(TransactionHooks.TAG, " " + e.getMessage()); 69 | } finally { 70 | try { 71 | if (reader != null) { 72 | reader.close(); 73 | } 74 | } catch (IOException e) { 75 | //best effort 76 | } 77 | } 78 | } 79 | }); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /BadIntentAndroid/app/src/main/java/de/mat3/badintent/hooking/proxy/hooks/LogHooks.java: -------------------------------------------------------------------------------- 1 | package de.mat3.badintent.hooking.proxy.hooks; 2 | 3 | import android.util.Log; 4 | 5 | import java.io.DataOutputStream; 6 | import java.net.HttpURLConnection; 7 | import java.net.URL; 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | import de.mat3.badintent.app.BadIntentConstants; 12 | import de.mat3.badintent.hooking.BaseHook; 13 | import de.mat3.badintent.hooking.proxy.AppInformation; 14 | import de.mat3.badintent.hooking.proxy.dao.SerializationUtils; 15 | import de.robv.android.xposed.XC_MethodHook; 16 | import de.robv.android.xposed.XSharedPreferences; 17 | import de.robv.android.xposed.XposedBridge; 18 | 19 | 20 | 21 | /** 22 | * This hook sends all log data of this app via Burp to the local log REST resource. 23 | */ 24 | public class LogHooks extends BaseHook { 25 | 26 | protected XSharedPreferences prefs; 27 | protected int port; 28 | 29 | public LogHooks(BaseHook h, XSharedPreferences sPrefs, int port) { 30 | super(h); 31 | prefs = sPrefs; 32 | this.port = port; 33 | 34 | } 35 | 36 | private class XposedAndroidLogMethodHook extends XC_MethodHook { 37 | protected String logType; 38 | 39 | public XposedAndroidLogMethodHook(String logType) { 40 | this.logType = logType; 41 | } 42 | 43 | @Override 44 | protected void afterHookedMethod(MethodHookParam param) throws Throwable { 45 | try { 46 | prefs.reload(); 47 | if (prefs.getBoolean(BadIntentConstants.CAPTURE_LOG, false) && AppInformation.Instance.intercept()) { 48 | String tag = (String) param.args[0]; 49 | String log = (String) param.args[1]; 50 | URL url = ConnectionUtils.getBadIntentURL("/log", prefs, port); 51 | HttpURLConnection conn = ConnectionUtils.getBadIntentHttpURLConnection(url, prefs); 52 | 53 | conn.setRequestMethod("POST"); 54 | conn.setRequestProperty("__BadIntent__", "Log"); 55 | conn.setRequestProperty("__BadIntent__.package", lpparam.packageName); 56 | conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); 57 | conn.setRequestProperty("Accept", "application/json"); 58 | conn.setDoOutput(true); 59 | conn.connect(); 60 | 61 | DataOutputStream writer = new DataOutputStream(conn.getOutputStream()); 62 | 63 | Map data = new HashMap<>(); 64 | data.put("tag", tag); 65 | data.put("log", log); 66 | data.put("logType", logType); 67 | writer.writeBytes(SerializationUtils.createGson().toJson(data)); 68 | writer.flush(); 69 | writer.close(); 70 | 71 | ConnectionUtils.readResponseAndCloseConnection(conn).start(); 72 | } 73 | } catch (Exception e) {/* best effort */} 74 | } 75 | } 76 | 77 | public void hookLogs(){ 78 | XposedBridge.hookAllMethods(Log.class, "d", new XposedAndroidLogMethodHook("debug")); 79 | XposedBridge.hookAllMethods(Log.class, "v", new XposedAndroidLogMethodHook("verbose")); 80 | XposedBridge.hookAllMethods(Log.class, "i", new XposedAndroidLogMethodHook("info")); 81 | XposedBridge.hookAllMethods(Log.class, "w", new XposedAndroidLogMethodHook("warning")); 82 | XposedBridge.hookAllMethods(Log.class, "e", new XposedAndroidLogMethodHook("exception")); 83 | XposedBridge.hookAllMethods(Log.class, "wtf", new XposedAndroidLogMethodHook("wtf")); 84 | } 85 | 86 | 87 | } 88 | -------------------------------------------------------------------------------- /BadIntentAndroid/app/src/main/java/de/mat3/badintent/hooking/proxy/hooks/ParcelBaseWriteHook.java: -------------------------------------------------------------------------------- 1 | package de.mat3.badintent.hooking.proxy.hooks; 2 | 3 | 4 | import android.os.Parcel; 5 | 6 | import de.mat3.badintent.hooking.proxy.ParcelAgent; 7 | import de.robv.android.xposed.XC_MethodHook; 8 | 9 | /** 10 | * Base XC_MethodHook for a type-specific Parcel writing-transaction. 11 | */ 12 | public abstract class ParcelBaseWriteHook extends XC_MethodHook { 13 | 14 | @Override 15 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable { 16 | if (!isParcelAgentCaller()) { 17 | interruptWrite((Parcel) param.thisObject, param.args); 18 | } 19 | } 20 | 21 | /** 22 | * This method is needed to handle the type-dependent interrupt. 23 | * 24 | * @param parcel 25 | * @param args 26 | */ 27 | protected abstract void interruptWrite(Parcel parcel, Object args[]); 28 | 29 | /** 30 | * This method checks if the ParcelAgent is part of the current stack trace 31 | * 32 | * @return true iff ParcelAgent is in stack trace 33 | */ 34 | private boolean isParcelAgentCaller() { 35 | for (StackTraceElement stackTraceElement : Thread.currentThread().getStackTrace()) { 36 | if (stackTraceElement.getClassName().equals(ParcelAgent.class.getName())) { 37 | return true; 38 | } 39 | } 40 | return false; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /BadIntentAndroid/app/src/main/java/de/mat3/badintent/hooking/proxy/hooks/ParcelProxyHooks.java: -------------------------------------------------------------------------------- 1 | package de.mat3.badintent.hooking.proxy.hooks; 2 | 3 | import android.os.IBinder; 4 | import android.os.Parcel; 5 | import android.util.Log; 6 | 7 | import java.io.FileDescriptor; 8 | import java.io.IOException; 9 | 10 | import de.mat3.badintent.hooking.BaseHook; 11 | import de.mat3.badintent.hooking.proxy.ParcelAgent; 12 | import de.mat3.badintent.hooking.proxy.RestAPI; 13 | import de.robv.android.xposed.XposedHelpers; 14 | 15 | 16 | /** 17 | * Interrupts and stores (native invoking) parcel operations in a custom container. 18 | */ 19 | public class ParcelProxyHooks extends BaseHook { 20 | 21 | protected RestAPI rest; 22 | protected int port; 23 | 24 | protected static final String TAG = "BadIntentParcelProxy"; 25 | 26 | public ParcelProxyHooks(BaseHook h, int port) throws IOException { 27 | super(h); 28 | this.port = port; 29 | startBackgroundThreads(); 30 | } 31 | 32 | protected void startBackgroundThreads() throws IOException { 33 | Log.i(TAG, "starting internal WebServer on port: " + port); 34 | rest = new RestAPI("0.0.0.0", port); 35 | rest.start(); 36 | } 37 | 38 | public void hookParcel() { 39 | try { 40 | XposedHelpers.findAndHookMethod("android.os.Parcel", cloader, "writeString", String.class, new ParcelBaseWriteHook() { 41 | @Override 42 | protected void interruptWrite(Parcel parcel, Object[] args) { 43 | ParcelAgent.Instance.interruptString(parcel, (String) args[0]); 44 | } 45 | }); 46 | XposedHelpers.findAndHookMethod("android.os.Parcel", cloader, "writeInt", int.class, new ParcelBaseWriteHook() { 47 | @Override 48 | protected void interruptWrite(Parcel parcel, Object[] args) { 49 | ParcelAgent.Instance.interruptInteger(parcel, (Integer) args[0]); 50 | } 51 | }); 52 | XposedHelpers.findAndHookMethod("android.os.Parcel", cloader, "writeLong", long.class, new ParcelBaseWriteHook() { 53 | @Override 54 | protected void interruptWrite(Parcel parcel, Object[] args) { 55 | ParcelAgent.Instance.interruptLong(parcel, (Long) args[0]); 56 | } 57 | }); 58 | XposedHelpers.findAndHookMethod("android.os.Parcel", cloader, "writeFloat", float.class, new ParcelBaseWriteHook() { 59 | @Override 60 | protected void interruptWrite(Parcel parcel, Object[] args) { 61 | ParcelAgent.Instance.interruptFloat(parcel, (Float) args[0]); 62 | } 63 | }); 64 | XposedHelpers.findAndHookMethod("android.os.Parcel", cloader, "writeDouble", double.class, new ParcelBaseWriteHook() { 65 | @Override 66 | protected void interruptWrite(Parcel parcel, Object[] args) { 67 | ParcelAgent.Instance.interruptDouble(parcel, (Double) args[0]); 68 | } 69 | }); 70 | XposedHelpers.findAndHookMethod("android.os.Parcel", cloader, "writeByteArray", byte[].class, int.class, int.class, new ParcelBaseWriteHook() { 71 | @Override 72 | protected void interruptWrite(Parcel parcel, Object[] args) { 73 | ParcelAgent.Instance.interruptByteArray(parcel, (byte[]) args[0], (int) args[1], (int) args[2]); 74 | } 75 | }); 76 | XposedHelpers.findAndHookMethod("android.os.Parcel", cloader, "writeFileDescriptor", FileDescriptor.class, new ParcelBaseWriteHook() { 77 | @Override 78 | protected void interruptWrite(Parcel parcel, Object[] args) { 79 | ParcelAgent.Instance.interruptFileDescriptor(parcel, (FileDescriptor) args[0]); 80 | } 81 | }); 82 | XposedHelpers.findAndHookMethod("android.os.Parcel", cloader, "writeStrongBinder", IBinder.class, new ParcelBaseWriteHook() { 83 | @Override 84 | protected void interruptWrite(Parcel parcel, Object[] args) { 85 | ParcelAgent.Instance.interruptStrongBinder(parcel, (IBinder) args[0]); 86 | } 87 | }); 88 | XposedHelpers.findAndHookMethod("android.os.Parcel", cloader, "writeInterfaceToken", String.class, new ParcelBaseWriteHook() { 89 | @Override 90 | protected void interruptWrite(Parcel parcel, Object[] args) { 91 | ParcelAgent.Instance.interruptInterfaceToken(parcel, (String) args[0]); 92 | } 93 | }); 94 | XposedHelpers.findAndHookMethod("android.os.Parcel", cloader, "setDataPosition", int.class, new ParcelBaseWriteHook() { 95 | @Override 96 | protected void interruptWrite(Parcel parcel, Object[] args) { 97 | ParcelAgent.Instance.interruptDataPosition(parcel, (int) args[0]); 98 | } 99 | }); 100 | try { 101 | XposedHelpers.findAndHookMethod("android.os.Parcel", cloader, "writeBlob", byte[].class, int.class, int.class, new ParcelBaseWriteHook() { 102 | @Override 103 | protected void interruptWrite(Parcel parcel, Object[] args) { 104 | ParcelAgent.Instance.interruptBlob(parcel, (byte[]) args[0], (int) args[1], (int) args[2]); 105 | } 106 | }); 107 | } catch (NoSuchMethodError e) { /* method does not exist in Android 4.4 */ 108 | Log.e(TAG, e.getLocalizedMessage()); 109 | } 110 | 111 | XposedHelpers.findAndHookMethod("android.os.Parcel", cloader, "setDataCapacity", int.class, new ParcelBaseWriteHook() { 112 | @Override 113 | protected void interruptWrite(Parcel parcel, Object[] args) { 114 | ParcelAgent.Instance.interruptDataCapacity(parcel, (int) args[0]); 115 | } 116 | }); 117 | 118 | XposedHelpers.findAndHookMethod("android.os.Parcel", cloader, "setDataSize", int.class, new ParcelBaseWriteHook() { 119 | @Override 120 | protected void interruptWrite(Parcel parcel, Object[] args) { 121 | ParcelAgent.Instance.interruptDataSize(parcel, (int) args[0]); 122 | } 123 | }); 124 | 125 | XposedHelpers.findAndHookMethod("android.os.Parcel", cloader, "pushAllowFds", boolean.class, new ParcelBaseWriteHook() { 126 | @Override 127 | protected void interruptWrite(Parcel parcel, Object[] args) { 128 | try { 129 | ParcelAgent.Instance.interruptPushAllowFds(parcel, (boolean) args[0]); 130 | } catch (Exception e) { 131 | Log.e(TAG, e.getMessage()); 132 | } 133 | } 134 | }); 135 | 136 | XposedHelpers.findAndHookMethod("android.os.Parcel", cloader, "appendFrom", Parcel.class, int.class, int.class, new ParcelBaseWriteHook() { 137 | @Override 138 | protected void interruptWrite(Parcel parcel, Object[] args) { 139 | ParcelAgent.Instance.interruptAppendFrom(parcel, (Parcel) args[0], (int) args[1], (int) args[2]); 140 | } 141 | }); 142 | 143 | try { 144 | XposedHelpers.findAndHookMethod("android.os.Parcel", cloader, "writeRawFileDescriptor", FileDescriptor.class, new ParcelBaseWriteHook() { 145 | @Override 146 | protected void interruptWrite(Parcel parcel, Object[] args) { 147 | ParcelAgent.Instance.interruptFileDescriptor(parcel, (FileDescriptor) args[0]); 148 | } 149 | }); 150 | } catch (NoSuchMethodError e) { /* obsolete method writeRawFileDescriptor */ 151 | Log.e(TAG, e.getLocalizedMessage()); 152 | } 153 | } catch (RuntimeException e) { 154 | Log.e(TAG, e.getLocalizedMessage()); 155 | } catch (Exception e) { 156 | Log.e(TAG, e.getLocalizedMessage()); 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /BadIntentAndroid/app/src/main/java/de/mat3/badintent/hooking/proxy/hooks/TransactionHooks.java: -------------------------------------------------------------------------------- 1 | package de.mat3.badintent.hooking.proxy.hooks; 2 | 3 | import android.os.IBinder; 4 | import android.os.Parcel; 5 | import android.os.StrictMode; 6 | import android.util.Log; 7 | 8 | import java.io.DataOutputStream; 9 | import java.io.IOException; 10 | import java.net.HttpURLConnection; 11 | import java.net.URL; 12 | import java.util.Arrays; 13 | import java.util.Map; 14 | 15 | import de.mat3.badintent.hooking.BaseHook; 16 | import de.mat3.badintent.hooking.proxy.AppInformation; 17 | import de.mat3.badintent.hooking.proxy.ParcelAgent; 18 | import de.mat3.badintent.hooking.proxy.ParcelContainer; 19 | import de.mat3.badintent.hooking.proxy.dao.SerializationUtils; 20 | import de.robv.android.xposed.XC_MethodHook; 21 | import de.robv.android.xposed.XSharedPreferences; 22 | import de.robv.android.xposed.XposedHelpers; 23 | 24 | 25 | 26 | /** 27 | * Various Binder related hooks are set in this class, which are required to redirect Parcels before the actual transaction occurs. 28 | */ 29 | public class TransactionHooks extends BaseHook { 30 | 31 | protected XSharedPreferences sPrefs; 32 | protected int port; 33 | 34 | protected static final String TAG = "BadIntentTransactions"; 35 | 36 | /** 37 | * @param h 38 | * @param sharedPreferences which is used to determine the proxy settings (BadIntent HTTP-Proxy) 39 | * @param port on which the current app's BadIntent RestAPI is listening 40 | */ 41 | public TransactionHooks(BaseHook h, XSharedPreferences sharedPreferences, int port) { 42 | super(h); 43 | sPrefs = sharedPreferences; 44 | AppInformation.Instance.sPrefs = sPrefs; 45 | this.port = port; 46 | } 47 | 48 | /** 49 | * After completion of the Parcel creation, the relevant Binder transactions are changed in order to 50 | * use the separate user-modified Parcel. 51 | * 52 | * This BinderProxy hook is implemented as a co-routine to @RestAPI. 53 | * 54 | * BinderProxy Hook RestAPI 55 | * | | State Parcel Container = New 56 | * |---beforeTransact----------->| HTTP State Parcel Container = Sent 57 | * | | (Possible HTTP changes) 58 | * |<--updates Parcel Container--| State Parcel Container = Received 59 | * | | 60 | * | execTransaction | 61 | * |---afterTransact------------>| State Parcel Container = Reply 62 | * |<----------------------------| Writes HTTP Response [possibly marshalled reply] 63 | * | | Setting DONE, clearing Container 64 | */ 65 | public void hookBinder() { 66 | try { 67 | Log.d(TAG, "hooking android.os.BinderProxy"); 68 | XposedHelpers.findAndHookMethod("android.os.BinderProxy", cloader, "transact", int.class, Parcel.class, Parcel.class, int.class, new XC_MethodHook() { 69 | 70 | //main identification of states and actions 71 | ThreadLocal originalParcel = new ThreadLocal(); 72 | 73 | @Override 74 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable { 75 | //wait here for my Parcel to be received by REST API 76 | int code = (int) param.args[0]; 77 | Parcel data = (Parcel) param.args[1]; 78 | Parcel reply = (Parcel) param.args[2]; 79 | int flags = (int) param.args[3]; 80 | 81 | //set parcel in thread local storage 82 | originalParcel.set(data); 83 | 84 | //Log.d(TAG, "+++++++++++++++++++++\nCaller: " + param.thisObject.toString()); 85 | // Log.d(TAG, "PackageName: " + lpparam.packageName + " ")); 86 | ParcelContainer container = ParcelAgent.Instance.getParcelContainer(data); 87 | 88 | ParcelAgent.Instance.waitForState(container, ParcelContainer.State.NEW, ParcelContainer.State.DONE); 89 | 90 | ParcelAgent.Instance.addBinder((IBinder) param.thisObject, container); 91 | 92 | //checking if parcel is defined to be intercepted 93 | if (!AppInformation.Instance.intercept(data)) { 94 | ParcelAgent.Instance.updateState(container, ParcelContainer.State.BYPASS); 95 | return; 96 | } else { 97 | //send parcel via HTTP 98 | ParcelAgent.Instance.updateState(container, ParcelContainer.State.SENT); 99 | Thread waitingThread; 100 | try { 101 | waitingThread = sendParcel(container, code, flags); 102 | waitingThread.start(); 103 | 104 | //wait until the parcel values are received via HTTP 105 | ParcelAgent.Instance.waitForState(container, ParcelContainer.State.RECEIVED, ParcelContainer.State.ERROR); 106 | if(container.getState() == ParcelContainer.State.ERROR) { 107 | Log.e(TAG, "setting back state to ERROR and omitting further intercept process"); 108 | return; 109 | } 110 | Parcel newData = ParcelAgent.Instance.createNewParcelBaseOnContainer(container); 111 | //Log.d(TAG, "adding replacer for parcelID: " + container.getParcelID()); 112 | param.args[1] = newData; 113 | //materialize original parcel, because the parcel can be used after the transaction 114 | ParcelAgent.Instance.materialize(container, data); 115 | } catch (IOException e) { 116 | //catch exceptions during sendParcel; set state to ERROR -> write directly to binder 117 | Log.e(TAG, e.getLocalizedMessage()); 118 | ParcelAgent.Instance.updateState(container, ParcelContainer.State.ERROR); 119 | } 120 | } 121 | } 122 | 123 | @Override 124 | protected void afterHookedMethod(MethodHookParam param) throws Throwable { 125 | Parcel data = (Parcel) param.args[1]; 126 | Parcel reply = (Parcel) param.args[2]; 127 | 128 | ParcelContainer originalContainer = ParcelAgent.Instance.getParcelContainer(originalParcel.get()); 129 | ParcelContainer.State directState = originalContainer.getState(); 130 | 131 | if (directState == ParcelContainer.State.BYPASS || directState == ParcelContainer.State.ERROR) { 132 | //Do nothing 133 | } else { 134 | //assume intercepted workflow 135 | originalContainer.setReply(reply); 136 | originalContainer.setState(ParcelContainer.State.REPLY); 137 | ParcelAgent.Instance.waitForState(originalContainer, ParcelContainer.State.DONE, ParcelContainer.State.ERROR); 138 | data.recycle(); 139 | } 140 | 141 | //finish and clear transaction 142 | originalContainer.setState(ParcelContainer.State.DONE); 143 | // Log.d(TAG, "clearing Operations " + originalContainer.getParcelID()); 144 | originalContainer.getOperationList().clear(); 145 | // Log.d(TAG, "-----------------------------------"); 146 | } 147 | }); 148 | } catch (RuntimeException e) { 149 | Log.e(TAG, e.getLocalizedMessage()); 150 | } catch (Exception e) { 151 | Log.e(TAG, e.getLocalizedMessage()); 152 | } 153 | 154 | } 155 | 156 | protected Thread sendParcel(ParcelContainer container, int code, int flags) throws IOException { 157 | //Prevent from casting exceptions with respect to "Network Access on Main Thread" 158 | StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().permitAll().build()); 159 | String resource = "/parcel/" + container.getParcelID(); 160 | 161 | URL url = ConnectionUtils.getBadIntentURL(resource, sPrefs, port); 162 | String jsonData = SerializationUtils.getJson(container, code, flags); 163 | 164 | final HttpURLConnection conn = ConnectionUtils.getBadIntentHttpURLConnection(url, sPrefs); 165 | 166 | conn.setRequestMethod("POST"); 167 | conn.setRequestProperty("__BadIntent__", "Parcel"); 168 | conn.setRequestProperty("__BadIntent__.package", lpparam.packageName); 169 | conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); 170 | conn.setRequestProperty("Accept", "application/json"); 171 | conn.setDoOutput(true); 172 | conn.connect(); 173 | 174 | DataOutputStream writer = new DataOutputStream(conn.getOutputStream()); 175 | 176 | writer.writeBytes(jsonData); 177 | writer.flush(); 178 | writer.close(); 179 | 180 | //create new thread, which reads HTTP result 181 | return ConnectionUtils.readResponseAndCloseConnection(conn); 182 | 183 | 184 | } 185 | 186 | } 187 | -------------------------------------------------------------------------------- /BadIntentAndroid/app/src/main/java/de/mat3/badintent/utils/HookingManager.java: -------------------------------------------------------------------------------- 1 | package de.mat3.badintent.utils; 2 | 3 | import de.mat3.badintent.hooking.BaseHook; 4 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 5 | 6 | 7 | 8 | public class HookingManager { 9 | 10 | protected XC_LoadPackage.LoadPackageParam lpparam; 11 | protected String target; 12 | protected BaseHook hooks; 13 | 14 | public HookingManager(XC_LoadPackage.LoadPackageParam lpparam, String target) { 15 | this.lpparam = lpparam; 16 | this.target = target; 17 | hooks = new BaseHook(lpparam, target); 18 | } 19 | 20 | 21 | public boolean continueHooking(){ 22 | return continueHooking(this.lpparam, this.target); 23 | } 24 | 25 | /** 26 | * Determines if the target package is currently being loaded. 27 | * @param lpparam 28 | * @param targetPackage 29 | * @return true iff target package is currently loaded. 30 | */ 31 | public static boolean continueHooking(XC_LoadPackage.LoadPackageParam lpparam, String targetPackage) { 32 | return lpparam.packageName.equals(targetPackage); 33 | } 34 | 35 | public BaseHook getBaseHook() { 36 | return hooks; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /BadIntentAndroid/app/src/main/java/de/mat3/badintent/utils/LoggingInstrumentation.java: -------------------------------------------------------------------------------- 1 | package de.mat3.badintent.utils; 2 | 3 | import android.util.Base64; 4 | import android.util.Log; 5 | 6 | import java.io.File; 7 | import java.lang.reflect.Member; 8 | import java.security.interfaces.RSAPrivateKey; 9 | 10 | import javax.crypto.spec.IvParameterSpec; 11 | import javax.crypto.spec.PBEKeySpec; 12 | import javax.crypto.spec.SecretKeySpec; 13 | 14 | /** 15 | * Pretty printing and logging instrumentation 16 | */ 17 | 18 | public class LoggingInstrumentation { 19 | 20 | public static String TAG; 21 | 22 | public static String toString(Object object) { 23 | try { 24 | if (object == null) { 25 | return "null"; 26 | } else if (object.getClass().isArray()) { 27 | if (object.getClass().getComponentType().isPrimitive()) { 28 | if (object instanceof byte[]) { 29 | return "ByteArray (Base64): " + Base64.encodeToString((byte[]) object, Base64.DEFAULT); 30 | } else { 31 | return "PrimitiveArray: " + object.toString(); 32 | } 33 | } else { 34 | Object[] array = (Object[]) object; 35 | StringBuilder arrStr = new StringBuilder("Array:"); 36 | for(Object item : array) { 37 | arrStr.append(" (" + LoggingInstrumentation.toString(item) + ") "); 38 | } 39 | return "Array: " + arrStr.toString(); 40 | } 41 | } else if (object instanceof File) { 42 | return "File: " + ((File) object).getAbsolutePath(); 43 | } else if (object instanceof PBEKeySpec) { 44 | PBEKeySpec spec = (PBEKeySpec) object; 45 | return "PBEKeySpec: Password:" + new String(spec.getPassword()) + " Iterations:" + spec.getIterationCount() + " Salt(Base64):" + Base64.encodeToString(spec.getSalt(), Base64.DEFAULT); 46 | } else if (object instanceof RSAPrivateKey) { 47 | RSAPrivateKey priv = (RSAPrivateKey) object; 48 | return "RSAPrivateKey: " + priv.getAlgorithm() + " Encoded(Base64):" + Base64.encodeToString(priv.getEncoded(), Base64.DEFAULT); 49 | } else if (object instanceof SecretKeySpec) { 50 | SecretKeySpec key = (SecretKeySpec) object; 51 | return "SecretKeySpec (" + key.getAlgorithm() + "): Encoded(Base64): " + Base64.encodeToString(key.getEncoded(), Base64.DEFAULT); 52 | } else if (object instanceof IvParameterSpec) { 53 | IvParameterSpec iv = (IvParameterSpec) object; 54 | return "IvParameterSpec: Encoded(Base64): " + Base64.encodeToString(iv.getIV(), Base64.DEFAULT); 55 | } 56 | else { 57 | return object.toString(); 58 | } 59 | } catch (RuntimeException e) { 60 | /**best effort */ 61 | return "Could not create string representation of " + object.getClass(); 62 | } catch (Exception e) { 63 | /**best effort */ 64 | return "Could not create string representation of " + object.getClass(); 65 | } 66 | } 67 | 68 | /** 69 | * Pretty print and log all method parameters 70 | * @param metaData 71 | */ 72 | public static void printParameters(LoggingMetaData metaData) { 73 | Member method = metaData.param.method; 74 | if (metaData.param.args == null) { 75 | Log.i(TAG, metaData.methodHookId + " Call " + method.getName() + " (" + metaData.classname + ") without parameters"); 76 | } else if (metaData.param.args.length > 0) { 77 | StringBuilder s = new StringBuilder(metaData.methodHookId + " Call " + method.getName() + " (" + metaData.classname + ") with parameters " + metaData.param.args.length + ":\n"); 78 | for (Object param : metaData.param.args) { 79 | s.append(toString(param) + " | "); 80 | } 81 | Log.i(TAG, s.toString()); 82 | } else { 83 | Log.i(TAG, metaData.methodHookId + " Call " + method.getName() + " (" + metaData.classname + ")"); 84 | } 85 | } 86 | 87 | /** 88 | * Pretty print and log result 89 | * @param metaData 90 | */ 91 | public static void printResult(LoggingMetaData metaData) { 92 | Member method = metaData.param.method; 93 | 94 | String s = toString(metaData.param.getResult()); 95 | if (!s.equals("null")) { 96 | Log.i(TAG, metaData.methodHookId + " Result " + method.getName() + " (" + metaData.classname + "):\n" + s); 97 | } 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /BadIntentAndroid/app/src/main/java/de/mat3/badintent/utils/LoggingMetaData.java: -------------------------------------------------------------------------------- 1 | package de.mat3.badintent.utils; 2 | 3 | import de.robv.android.xposed.XC_MethodHook; 4 | 5 | 6 | public class LoggingMetaData { 7 | 8 | public String classname; 9 | public String methodHookId = "NONE"; 10 | public boolean finished = false; 11 | public XC_MethodHook.MethodHookParam param; 12 | 13 | protected static Long counter = new Long(1); 14 | 15 | public LoggingMetaData(String classname) { 16 | this.classname = classname; 17 | } 18 | 19 | public void beforeHookedMethod(XC_MethodHook.MethodHookParam param) { 20 | methodHookId = Long.toString(counter++) + ":" + param.hashCode(); 21 | this.param = param; 22 | } 23 | 24 | public void afterHookedMethod(XC_MethodHook.MethodHookParam param) throws Throwable { 25 | finished = true; 26 | this.param = param; 27 | } 28 | 29 | 30 | } 31 | -------------------------------------------------------------------------------- /BadIntentAndroid/app/src/main/res/drawable/android_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1ultimat3/BadIntent/fbd34c61ad980e2d475fb95d2bfac9759cd81941/BadIntentAndroid/app/src/main/res/drawable/android_icon.png -------------------------------------------------------------------------------- /BadIntentAndroid/app/src/main/res/drawable/ic_info_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /BadIntentAndroid/app/src/main/res/drawable/ic_notifications_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /BadIntentAndroid/app/src/main/res/drawable/ic_sync_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /BadIntentAndroid/app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /BadIntentAndroid/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | #EFEFEF 7 | 8 | -------------------------------------------------------------------------------- /BadIntentAndroid/app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 16dp 6 | 7 | -------------------------------------------------------------------------------- /BadIntentAndroid/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | BadIntent 3 | Settings 4 | BadIntent Preferences 5 | 6 | -------------------------------------------------------------------------------- /BadIntentAndroid/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 15 | 16 |