├── .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 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/BadIntentAndroid/app/src/main/res/xml/bad_intent_preferences.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
12 |
19 |
24 |
29 |
30 |
31 |
38 |
39 |
40 |
41 |
45 |
51 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/BadIntentAndroid/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.2.3'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
21 | task clean(type: Delete) {
22 | delete rootProject.buildDir
23 | }
24 |
--------------------------------------------------------------------------------
/BadIntentAndroid/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/BadIntentAndroid/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed May 03 08:56:29 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
7 |
--------------------------------------------------------------------------------
/BadIntentAndroid/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/BadIntentAndroid/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/BadIntentAndroid/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/BadIntentBurp/pom.xml:
--------------------------------------------------------------------------------
1 |
2 | 4.0.0
3 | de.mat3.badintent
4 | badintent-burp
5 | 0.9.9.9
6 | badintent
7 | BadIntent Burp Plugin
8 |
9 |
10 | UTF-8
11 | UTF-8
12 |
13 |
14 |
15 |
16 |
17 | maven-compiler-plugin
18 | 2.5.1
19 |
20 | 1.8
21 | 1.8
22 |
23 |
24 |
25 | maven-assembly-plugin
26 |
27 |
28 | package
29 |
30 | single
31 |
32 |
33 |
34 |
35 |
36 | jar-with-dependencies
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | net.portswigger.burp.extender
45 | burp-extender-api
46 | 1.7.22
47 |
48 |
49 | com.google.code.gson
50 | gson
51 | 2.8.1
52 |
53 |
54 | com.google.guava
55 | guava
56 | 22.0-android
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/BadIntentBurp/src/main/java/burp/ActiveAppsTab.java:
--------------------------------------------------------------------------------
1 | package burp;
2 |
3 |
4 | import com.google.gson.Gson;
5 | import com.google.gson.GsonBuilder;
6 | import dao.AppInfo;
7 | import ui.ActiveAppView;
8 |
9 | import java.awt.*;
10 | import java.io.IOException;
11 | import java.io.InputStream;
12 | import java.io.InputStreamReader;
13 | import java.net.HttpURLConnection;
14 | import java.net.InetAddress;
15 | import java.net.MalformedURLException;
16 | import java.net.URL;
17 | import java.util.*;
18 | import java.util.List;
19 | import java.util.concurrent.ConcurrentLinkedQueue;
20 |
21 | public class ActiveAppsTab implements ITab {
22 |
23 | /** identified bad intent ports are collected here */
24 | public final static Queue openTasks = new ConcurrentLinkedQueue<>();
25 |
26 | protected Collection activeApps = Collections.synchronizedCollection(new LinkedList());
27 | protected Thread activityCheckerThread;
28 | protected ActiveAppView activeAppView;
29 |
30 | public ActiveAppsTab(){
31 | activeAppView = new ActiveAppView(activeApps);
32 | activityCheckerThread = new Thread(new Runnable() {
33 |
34 | Gson gson = new GsonBuilder().create();
35 |
36 | @Override
37 | public void run() {
38 | System.out.println("starting app info checker thread");
39 | while (true) {
40 | //process open tasks first
41 | for (int i = 0; i < openTasks.size(); i++) {
42 | processOpenTask();
43 | }
44 | //process existing apps (check if still active)
45 | List appInfoRemovalList = new LinkedList<>();
46 | for(AppInfo info : activeApps) {
47 | try {
48 | getAppInfo(info.endpoint);
49 | } catch(Exception e) {
50 | System.out.println("appinfo not present / removing " + info.endpoint);
51 | appInfoRemovalList.add(info);
52 | }
53 | }
54 | //remove app infos now
55 | for(AppInfo remove : appInfoRemovalList){
56 | activeApps.remove(remove);
57 | }
58 | if(openTasks.isEmpty()) {
59 | try {
60 | Thread.sleep(1000);
61 | } catch (InterruptedException e) {
62 | /* best effort */
63 | }
64 | }
65 | activeAppView.rebuild();
66 | }
67 |
68 | }
69 |
70 | private void processOpenTask() {
71 | try {
72 | String target = openTasks.remove();
73 | if(!activeApp(target)) {
74 | AppInfo appInfo = getAppInfo(target);
75 | activeApps.add(appInfo);
76 | } else {
77 | //update existing app value
78 | AppInfo appInfo = getAppInfo(target);
79 | AppInfo appInfoOld = getActiveApp(target);
80 | activeApps.remove(appInfoOld);
81 | activeApps.add(appInfo);
82 | }
83 |
84 | } catch (MalformedURLException e) {
85 | System.out.println(e.getLocalizedMessage());
86 | } catch (IOException e) {
87 | System.out.println(e.getLocalizedMessage());
88 | }
89 | }
90 |
91 | private AppInfo getAppInfo(String target) throws IOException {
92 | HttpURLConnection conn = (HttpURLConnection) new URL(target).openConnection();
93 | InputStream response = conn.getInputStream();
94 | AppInfo appInfo = gson.fromJson(new InputStreamReader(response), AppInfo.class);
95 | appInfo.endpoint = target;
96 | return appInfo;
97 | }
98 |
99 | private boolean activeApp(String target) {
100 | if (target == null) return false;
101 | for (AppInfo appInfo : activeApps) {
102 | if(target.equals(appInfo.endpoint)) {
103 | return true;
104 | }
105 | }
106 | return false;
107 | }
108 |
109 | private AppInfo getActiveApp(String target) {
110 | if (target == null) return null;
111 | for (AppInfo appInfo : activeApps) {
112 | if(target.equals(appInfo.endpoint)) {
113 | return appInfo;
114 | }
115 | }
116 | return null;
117 | }
118 | });
119 | activityCheckerThread.start();
120 | }
121 |
122 | @Override
123 | public String getTabCaption() {
124 | return "ActiveApps";
125 | }
126 |
127 | @Override
128 | public Component getUiComponent() {
129 | return activeAppView;
130 | }
131 |
132 | public static void addOpenTask(InetAddress clientIpAddress, int port) {
133 | String target = "http://" + clientIpAddress.getHostAddress() + ":" + port + "/info";
134 | openTasks.add(target);
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/BadIntentBurp/src/main/java/burp/BurpExtender.java:
--------------------------------------------------------------------------------
1 | package burp;
2 |
3 | import controller.BadIntentMessageEditorTab;
4 | import listener.ColoringListener;
5 |
6 |
7 | public class BurpExtender implements IBurpExtender, IMessageEditorTabFactory {
8 |
9 | private IBurpExtenderCallbacks callbacks;
10 | private BadIntentMessageEditorTab badIntentMessageEditorTab;
11 |
12 | @Override
13 | public void registerExtenderCallbacks(final IBurpExtenderCallbacks callbacks) {
14 | this.callbacks = callbacks;
15 | this.callbacks.setExtensionName("BadIntent");
16 |
17 | this.callbacks.registerMessageEditorTabFactory(this);
18 | this.callbacks.registerProxyListener(new ColoringListener(callbacks));
19 | this.callbacks.addSuiteTab(new ActiveAppsTab());
20 |
21 | }
22 |
23 | @Override
24 | public IMessageEditorTab createNewInstance(final IMessageEditorController controller, final boolean editable) {
25 | this.badIntentMessageEditorTab = new BadIntentMessageEditorTab(this.callbacks, editable);
26 | return badIntentMessageEditorTab;
27 |
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/BadIntentBurp/src/main/java/controller/BadIntentMessageEditorTab.java:
--------------------------------------------------------------------------------
1 | package controller;
2 |
3 | import burp.IBurpExtenderCallbacks;
4 | import burp.IExtensionHelpers;
5 | import burp.IMessageEditorTab;
6 | import burp.IRequestInfo;
7 | import com.google.common.primitives.Bytes;
8 | import com.google.gson.GsonBuilder;
9 | import dao.ParcelOperationDAO;
10 | import dao.ParcelPoolElementDAO;
11 | import ui.ParcelPanel;
12 | import ui.ParcelTab;
13 | import util.HttpHelper;
14 |
15 | import java.awt.*;
16 | import java.io.ByteArrayInputStream;
17 | import java.io.InputStreamReader;
18 | import java.util.Arrays;
19 | import java.util.List;
20 |
21 |
22 | /**
23 | * This tab is used as a controller for the ParcelEditor view;
24 | * there it is possible to edit the received actions and values.
25 | */
26 | public class BadIntentMessageEditorTab implements IMessageEditorTab {
27 |
28 | private IBurpExtenderCallbacks callbacks;
29 | private ParcelPanel overview;
30 | private ParcelTab tab;
31 | private boolean editable;
32 |
33 | private IRequestInfo requestInfo;
34 | private byte[] content;
35 | private ParcelPoolElementDAO parcelPoolElementDao;
36 |
37 |
38 | public BadIntentMessageEditorTab(final IBurpExtenderCallbacks callbacks, final boolean editable) {
39 | this.callbacks = callbacks;
40 | this.overview = new ParcelPanel();
41 | this.tab = new ParcelTab(overview);
42 | this.editable = editable;
43 | }
44 |
45 | @Override
46 | public String getTabCaption() {
47 | return "ParcelEditor";
48 | }
49 |
50 | @Override
51 | public Component getUiComponent() {
52 | return tab;
53 | }
54 |
55 | @Override
56 | public boolean isEnabled(final byte[] content, final boolean isRequest) {
57 | IExtensionHelpers helpers = callbacks.getHelpers();
58 | return HttpHelper.isBadIntentParcelRequest(helpers.analyzeRequest(content), isRequest, helpers);
59 | }
60 |
61 | @Override
62 | public void setMessage(final byte[] content, final boolean isRequest) {
63 | this.content = content;
64 | if (content == null) return;
65 | requestInfo = callbacks.getHelpers().analyzeRequest(content);
66 | int bodyStart = requestInfo.getBodyOffset();
67 | ByteArrayInputStream byteInputStream = new ByteArrayInputStream(content, bodyStart, content.length);
68 | InputStreamReader reader = new InputStreamReader(byteInputStream);
69 | parcelPoolElementDao = new GsonBuilder().create().fromJson(reader, ParcelPoolElementDAO.class);
70 | overview.setParcelOperations(parcelPoolElementDao.operations);
71 | overview.rebuild();
72 | }
73 |
74 | @Override
75 | public byte[] getMessage() {
76 | List parcelOperationDaos = overview.retrievedParcelOperations();
77 | if (isModified()) {
78 | parcelPoolElementDao.operations = parcelOperationDaos;
79 | String responseBody = new GsonBuilder().create().toJson(parcelPoolElementDao);
80 | int bodyStart = requestInfo.getBodyOffset();
81 | byte[] header = Arrays.copyOfRange(content, 0, bodyStart);
82 | return Bytes.concat(header, responseBody.getBytes());
83 | } else {
84 | return content;
85 | }
86 | }
87 |
88 | @Override
89 | public boolean isModified() {
90 | return overview.isModified();
91 | }
92 |
93 | @Override
94 | public byte[] getSelectedData() {
95 | return tab.getSelectedData();
96 | }
97 |
98 | }
99 |
--------------------------------------------------------------------------------
/BadIntentBurp/src/main/java/dao/AppInfo.java:
--------------------------------------------------------------------------------
1 | package dao;
2 |
3 |
4 | import java.util.List;
5 |
6 | public class AppInfo {
7 | public String packageName;
8 | public String restPort;
9 | public String endpoint;
10 | public List interfaceTokens;
11 | }
12 |
--------------------------------------------------------------------------------
/BadIntentBurp/src/main/java/dao/ParcelOperationDAO.java:
--------------------------------------------------------------------------------
1 | package dao;
2 |
3 | public class ParcelOperationDAO {
4 |
5 | public String parcelType;
6 | public Object value;
7 |
8 | public ParcelOperationDAO(String parcelOperation, Object parcelValue) {
9 | this.parcelType = parcelOperation;
10 | this.value = parcelValue;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/BadIntentBurp/src/main/java/dao/ParcelPoolElementDAO.java:
--------------------------------------------------------------------------------
1 | package dao;
2 |
3 | import java.util.LinkedList;
4 | import java.util.List;
5 |
6 |
7 | public class ParcelPoolElementDAO {
8 | public int parcelID;
9 | public List operations = new LinkedList<>();
10 | public String state;
11 | public int transactionCode;
12 | public int transactionFlags;
13 | }
14 |
--------------------------------------------------------------------------------
/BadIntentBurp/src/main/java/listener/ColoringListener.java:
--------------------------------------------------------------------------------
1 | package listener;
2 |
3 | import burp.*;
4 | import util.HttpHelper;
5 |
6 |
7 | public class ColoringListener implements IProxyListener {
8 |
9 | protected IBurpExtenderCallbacks callbacks;
10 |
11 | public ColoringListener(IBurpExtenderCallbacks callbacks) {
12 | this.callbacks = callbacks;
13 | }
14 |
15 | @Override
16 | public void processProxyMessage(boolean isRequest, IInterceptedProxyMessage iInterceptedProxyMessage) {
17 | IHttpRequestResponse messageInfo = iInterceptedProxyMessage.getMessageInfo();
18 | IExtensionHelpers helpers = callbacks.getHelpers();
19 | IRequestInfo iRequestInfo = helpers.analyzeRequest(messageInfo.getRequest());
20 | if (HttpHelper.isBadIntentParcelRequest(iRequestInfo, isRequest, helpers)) {
21 | updateColor(messageInfo, iRequestInfo, "green");
22 | ActiveAppsTab.addOpenTask(iInterceptedProxyMessage.getClientIpAddress(),
23 | iInterceptedProxyMessage.getMessageInfo().getHttpService().getPort());
24 | } else if (HttpHelper.isBadIntentLogRequest(iRequestInfo, isRequest, helpers)) {
25 | updateColor(messageInfo, iRequestInfo, "gray");
26 | }
27 | }
28 |
29 | public void updateColor(IHttpRequestResponse messageInfo, IRequestInfo iRequestInfo, String color) {
30 | messageInfo.setHighlight(color);
31 | messageInfo.setComment(HttpHelper.getPackageName(iRequestInfo));
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/BadIntentBurp/src/main/java/ui/ActiveAppView.java:
--------------------------------------------------------------------------------
1 | package ui;
2 |
3 | import dao.AppInfo;
4 |
5 | import javax.swing.*;
6 | import javax.swing.table.DefaultTableModel;
7 | import javax.swing.table.TableColumnModel;
8 | import java.awt.*;
9 | import java.util.Collection;
10 |
11 |
12 | public class ActiveAppView extends JPanel {
13 |
14 | protected Collection activeApps;
15 |
16 | protected JTable apps;
17 | protected JScrollPane scrollPane;
18 | protected JPanel panel = new JPanel();
19 |
20 | public ActiveAppView(Collection activeApps) {
21 | this.activeApps = activeApps;
22 |
23 | setLayout(new BorderLayout());
24 | apps = new JTable();
25 | apps.setModel(new DefaultTableModel(0, 3));
26 | TableColumnModel columnModel = apps.getTableHeader().getColumnModel();
27 | columnModel.getColumn(0).setHeaderValue("Package");
28 | columnModel.getColumn(1).setHeaderValue("Interface");
29 | columnModel.getColumn(2).setHeaderValue("Port");
30 |
31 | panel.setLayout(new BorderLayout());
32 | panel.add(apps.getTableHeader(), BorderLayout.PAGE_START);
33 | panel.add(apps, BorderLayout.CENTER);
34 | panel.add(apps);
35 |
36 | scrollPane = new JScrollPane(panel);
37 | scrollPane.setVerticalScrollBar(scrollPane.createVerticalScrollBar());
38 | scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
39 | scrollPane.setViewportView(panel);
40 |
41 | add(scrollPane);
42 |
43 | }
44 |
45 | public void rebuild() {
46 | DefaultTableModel model = (DefaultTableModel) apps.getModel();
47 | int rowCount = model.getRowCount();
48 | for (int i = 0; i < rowCount; i++) {
49 | model.removeRow(0);
50 | }
51 | for (AppInfo appInfo : activeApps){
52 | for (String interfaceToken : appInfo.interfaceTokens){
53 | model.addRow(new Object[] {appInfo.packageName, interfaceToken, appInfo.restPort});
54 | }
55 | }
56 | revalidate();
57 | repaint();
58 | }
59 |
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/BadIntentBurp/src/main/java/ui/ParcelByteArrDetails.java:
--------------------------------------------------------------------------------
1 | package ui;
2 |
3 |
4 | import com.google.gson.internal.LinkedTreeMap;
5 |
6 | import javax.swing.*;
7 | import javax.xml.bind.DatatypeConverter;
8 | import java.awt.*;
9 | import java.awt.event.ActionEvent;
10 | import java.awt.event.ActionListener;
11 | import java.util.ArrayList;
12 |
13 | /**
14 | * represents details view with respect to byte array parcel values
15 | */
16 | public class ParcelByteArrDetails extends JPanel {
17 |
18 | public JScrollPane scrollable;
19 | public JTextArea byteArray = new JTextArea();
20 | public JTextField offset = new JTextField();
21 | public JLabel offsetLabel = new JLabel("offset");
22 | public JTextField length = new JTextField();
23 | public JLabel lengthLabel = new JLabel("length");
24 | public JButton updateButton = new JButton("Update");
25 | public JPanel inner;
26 |
27 | public ParcelByteArrDetails(LinkedTreeMap map) {
28 | setLayout(new GridBagLayout());
29 |
30 | setOffset(((Double) map.get("offset")).intValue());
31 | setLength(((Double) map.get("len")).intValue());
32 | setByteArray((ArrayList) map.get("array"));
33 | scrollable = new JScrollPane(byteArray);
34 | scrollable.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
35 | scrollable.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
36 |
37 | byteArray.setLineWrap(true);
38 | byteArray.setWrapStyleWord(true);
39 |
40 | inner = new JPanel();
41 | inner.setLayout(new BorderLayout());
42 | inner.add(scrollable);
43 |
44 | updateButton.addActionListener(new ActionListener() {
45 | @Override
46 | public void actionPerformed(ActionEvent e) {
47 | System.out.println(getByteArrayList().toString());
48 | map.put("offset", getOffset());
49 | map.put("len", getLength());
50 | map.put("array", getByteArrayList());
51 | }
52 | });
53 |
54 | //building GUI
55 | GridBagConstraints c = new GridBagConstraints();
56 | c.gridx = 0;
57 | c.gridy = 0;
58 | add(offsetLabel, c);
59 | c.gridx = 1;
60 | add(offset, c);
61 | c.gridx = 3;
62 | add(lengthLabel);
63 | c.gridx = 4;
64 | add(length, c);
65 | c.gridx = 6;
66 | c.anchor = GridBagConstraints.LINE_END;
67 | add(updateButton, c);
68 | c.anchor = GridBagConstraints.LINE_START;
69 | c.gridy = 1;
70 | c.gridx = 0;
71 | c.weightx = 1;
72 | c.weighty = 1;
73 | c.gridwidth = 7;
74 | c.gridheight = 3;
75 | c.fill = GridBagConstraints.BOTH;
76 | add(inner, c);
77 | }
78 |
79 | public void setOffset(int offset) {
80 | this.offset.setText(Integer.toString(offset));
81 | }
82 |
83 | public double getOffset(){
84 | return Double.parseDouble(this.offset.getText());
85 | }
86 |
87 | public void setLength(int length) {
88 | this.length.setText(Integer.toString(length));
89 | }
90 |
91 | public double getLength(){
92 | return Double.parseDouble(this.length.getText());
93 | }
94 |
95 | public void setByteArray(ArrayList byteArray) {
96 | StringBuilder builder = new StringBuilder();
97 | if (byteArray != null) {
98 | for (Object element : byteArray){
99 | builder.append(String.format("%02X ", ((Double) element).byteValue()));
100 | builder.append(" ");
101 | }
102 | }
103 | this.byteArray.setText(builder.toString());
104 | }
105 |
106 | public ArrayList getByteArrayList(){
107 | String hexStr = this.byteArray.getText().replace(" ", "");
108 | byte[] bytes = DatatypeConverter.parseHexBinary(hexStr);
109 | ArrayList doubleArrayList = new ArrayList();
110 | for (byte byteVal : bytes){
111 | doubleArrayList.add((double) byteVal);
112 | }
113 | return doubleArrayList;
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/BadIntentBurp/src/main/java/ui/ParcelPanel.java:
--------------------------------------------------------------------------------
1 | package ui;
2 |
3 | import com.google.gson.internal.LinkedTreeMap;
4 | import dao.ParcelOperationDAO;
5 |
6 | import javax.swing.*;
7 | import javax.swing.event.ListSelectionEvent;
8 | import javax.swing.event.ListSelectionListener;
9 | import javax.swing.table.TableModel;
10 | import java.awt.*;
11 | import java.util.*;
12 | import java.util.List;
13 |
14 |
15 | public class ParcelPanel extends JScrollPane {
16 |
17 | public JPanel panel = new JPanel();
18 | protected JPanel details;
19 |
20 | protected List parcelOperations = new LinkedList<>();
21 | protected boolean hasBeenEdited = false;
22 | private JTable table;
23 |
24 | public ParcelPanel() {
25 | panel.setLayout(new BorderLayout());
26 | setVerticalScrollBar(createVerticalScrollBar());
27 | setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
28 | add(panel);
29 | setViewportView(panel);
30 | }
31 |
32 | public void setParcelOperations(List parcelOperations) {
33 | this.parcelOperations = parcelOperations;
34 | }
35 |
36 | public void setDetails(JPanel details) {
37 | this.details = details;
38 | }
39 |
40 | public void rebuild() {
41 | panel.removeAll();
42 | if (details != null) {
43 | details.removeAll(); //clear view
44 | }
45 | Object[] columnNames = {"Operation", "Value"};
46 | Object[][] data = new Object[parcelOperations.size()][];
47 | for (int i = 0; i < parcelOperations.size(); i++){
48 | ParcelOperationDAO parcelOperationDAO = parcelOperations.get(i);
49 | data[i] = new Object[]{parcelOperationDAO.parcelType.toString(), parcelOperationDAO.value};
50 | }
51 | table = new JTable(data, columnNames);
52 | table.getSelectionModel().addListSelectionListener(new ListSelectionListener(){
53 | public void valueChanged(ListSelectionEvent event) {
54 | //make sure that JTable index matches parcelOperations index
55 | int selectedRow = table.getSelectedRow();
56 | String operation = (String) table.getValueAt(selectedRow, 0);
57 | Object value = parcelOperations.get(selectedRow).value;
58 | details.removeAll(); //clear view first
59 | if (operation.equals("BYTE_ARRAY") || operation.equals("BLOB")){
60 | details.add(new ParcelByteArrDetails((LinkedTreeMap) value));
61 | } else {
62 | details.removeAll();
63 | }
64 | details.revalidate();
65 | details.repaint();
66 | }
67 | });
68 | panel.add(table.getTableHeader(), BorderLayout.PAGE_START);
69 | panel.add(table, BorderLayout.CENTER);
70 | panel.add(table);
71 | table.getColumnModel().getColumn(0).setWidth(5);
72 | table.getColumnModel().getColumn(1).setWidth(95);
73 | revalidate();
74 | repaint();
75 | }
76 |
77 | public List retrievedParcelOperations(){
78 | hasBeenEdited = false;
79 | List ops = new LinkedList<>();
80 | TableModel model = table.getModel();
81 | if (model.getRowCount() != parcelOperations.size()) {
82 | hasBeenEdited = true;
83 | }
84 | for (int i = 0; i < model.getRowCount(); i++){
85 | String parcelOperation = (String) model.getValueAt(i, 0);
86 | Object parcelValue = model.getValueAt(i, 1);
87 | String originalOperation = parcelOperations.get(i).parcelType;
88 | Object originalValue = parcelOperations.get(i).value;
89 | if (parcelValue == null && originalValue == null) {
90 | ops.add(parcelOperations.get(i));
91 | } else if (parcelValue == null || originalValue == null) {
92 | hasBeenEdited = true;
93 | ops.add(new ParcelOperationDAO(parcelOperation, parcelValue));
94 | } else if (!originalOperation.equals(parcelOperation) || !originalValue.equals(parcelValue)) {
95 | hasBeenEdited = true;
96 | ops.add(new ParcelOperationDAO(parcelOperation, parcelValue));
97 | } else {
98 | ops.add(parcelOperations.get(i));
99 | }
100 | }
101 | return ops;
102 | }
103 |
104 |
105 | public boolean isModified() {
106 | return hasBeenEdited;
107 | }
108 |
109 | public byte[] getSelectedData() {
110 | int row = table.getSelectedRow();
111 | int column = table.getSelectedColumn();
112 | return table.getValueAt(row, column).toString().getBytes();
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/BadIntentBurp/src/main/java/ui/ParcelTab.java:
--------------------------------------------------------------------------------
1 | package ui;
2 |
3 | import javax.swing.*;
4 | import java.awt.*;
5 |
6 |
7 | public class ParcelTab extends JPanel {
8 |
9 | protected JSplitPane splitPane;
10 | //carries table (op : value)
11 | protected ParcelPanel overview;
12 | //carries details of various entries (e.g. byte array)
13 | protected JPanel details = new JPanel();
14 |
15 | public ParcelTab(ParcelPanel overview) {
16 | this.overview = overview;
17 | overview.setDetails(details);
18 | setLayout(new BorderLayout());
19 | details.setLayout(new BorderLayout());
20 | splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, overview, details);
21 | splitPane.setOneTouchExpandable(true);
22 | splitPane.setDividerLocation(250);
23 | add(splitPane);
24 | }
25 |
26 | public byte[] getSelectedData() {
27 | return overview.getSelectedData();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/BadIntentBurp/src/main/java/util/HttpHelper.java:
--------------------------------------------------------------------------------
1 | package util;
2 |
3 |
4 | import burp.IExtensionHelpers;
5 | import burp.IRequestInfo;
6 |
7 | import java.util.List;
8 |
9 | public class HttpHelper {
10 |
11 | /**
12 | * BadIntent is recognized if the HTTP header __BadIntent__: Parcel is set.
13 | * @param iRequestInfo
14 | * @param isRequest
15 | * @param helpers
16 | * @return true iff is BadIntent request
17 | */
18 | public static boolean isBadIntentParcelRequest(IRequestInfo iRequestInfo, boolean isRequest, IExtensionHelpers helpers) {
19 | List headers = iRequestInfo.getHeaders();
20 | return isRequest && headers.contains("__BadIntent__: Parcel");
21 | }
22 |
23 | /**
24 | * BadIntent is recognized if the HTTP header __BadIntent__: Log is set.
25 | * @param iRequestInfo
26 | * @param isRequest
27 | * @param helpers
28 | * @return true iff is BadIntent log request
29 | */
30 | public static boolean isBadIntentLogRequest(IRequestInfo iRequestInfo, boolean isRequest, IExtensionHelpers helpers) {
31 | List headers = iRequestInfo.getHeaders();
32 | return isRequest && headers.contains("__BadIntent__: Log");
33 | }
34 |
35 | /**
36 | * Returns package name which is contained in HTTP header __BadIntent__.package: .
37 | * @param iRequestInfo
38 | * @return package name
39 | */
40 | public static String getPackageName(IRequestInfo iRequestInfo) {
41 | for (String header: iRequestInfo.getHeaders()){
42 | if (header.startsWith("__BadIntent__.package:")){
43 | return header.split(":")[1].trim();
44 | }
45 | } return "UNKNOWN";
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2017, Mateusz Khalil
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | * Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | * Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # BadIntent
2 |
3 | ## Introduction
4 | BadIntent is the missing link between the Burp Suite and the core Android's IPC/Messaging-system. BadIntent consists of two parts, an Xposed-based module running on Android and a Burp-plugin. Based on this interplay, it is possible to use the Burp's common workflow and all involved tools and extensions, since the intercept and repeater functionality is provided. BadIntent hooks deeply into the Android system, performs various method redirections in Parcels and adds additional services to provide the described features. Most notably, BadIntent works system-wide (**experimental**) and is not restricted to individual user apps.
5 |
6 |
7 | BadIntent can used to perform various pentesting activities such as the following [examples](https://github.com/mateuszk87/BadIntent/wiki/Showcases):
8 | * identifying [insecure logging](https://github.com/mateuszk87/BadIntent/wiki/Showcases#insecure-logging), [access control issues](https://github.com/mateuszk87/BadIntent/wiki/Showcases#access-control-issues), [pasteboard vulnerabilities](https://github.com/mateuszk87/BadIntent/wiki/Showcases#pasteboard-vulnerability),
9 | * conduct and configure [intent sniffing](https://github.com/mateuszk87/BadIntent/wiki/Showcases#intent-sniffing), [brute force attacks](https://github.com/mateuszk87/BadIntent/wiki/Showcases#brute-force),
10 | * [AIDL testing](https://github.com/mateuszk87/BadIntent/wiki/Showcases#aidl-testing), [GCM attacks](https://github.com/mateuszk87/BadIntent/wiki/Showcases#cloud-messaging), and searching for [WebView vulnerabilities](https://github.com/mateuszk87/BadIntent/wiki/Showcases#mobile-xss-web-view)
11 | * and finally how BadIntent can be (mis-)used as a [keylogger](https://github.com/mateuszk87/BadIntent/wiki/Showcases#keylogger)
12 |
13 |
14 |
15 |
16 |
17 | ## Installation
18 | The most handy approach is to install BadIntent Android from the Xposed Module Repository and BadIntent Burp from the Burp’s BApp Store. Both are made available/submitted before the Arsenal presentation of BadIntent in Black Hat Las Vegas 2017.
19 | * [Xposed Repo Module](http://repo.xposed.info/module/de.mat3.badintent)
20 | * [BApp Store Ref](https://portswigger.net/bappstore/bapps/)
21 |
22 | ## Environment
23 | BadIntent has been tested on Genymotion with Xposed v87 on Android Marshmallow (6.0) and Burp Suite 1.7.23 (Free and Pro).
24 |
25 | There are known limitations in hooking all system apps and all interfaces. During the boot proccess the Android system will remain in a boot loop and you will not be able to uninstall BadIntent from your Android device. Therefore, it is strongly recommended to use the mentioned setup in case all system apps are hooked.
26 |
27 | ## Configuration & Usage
28 | Please refer to the [wiki](https://github.com/mateuszk87/BadIntent/wiki) for more details.
29 |
30 | ## License
31 | BadIntent is released under a 3-clause BSD License. See LICENSE for full details.
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/doc/img/access_control_repeat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/1ultimat3/BadIntent/fbd34c61ad980e2d475fb95d2bfac9759cd81941/doc/img/access_control_repeat.png
--------------------------------------------------------------------------------
/doc/img/access_control_result.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/1ultimat3/BadIntent/fbd34c61ad980e2d475fb95d2bfac9759cd81941/doc/img/access_control_result.png
--------------------------------------------------------------------------------
/doc/img/aidl_keystore_generate_key.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/1ultimat3/BadIntent/fbd34c61ad980e2d475fb95d2bfac9759cd81941/doc/img/aidl_keystore_generate_key.png
--------------------------------------------------------------------------------
/doc/img/aidl_keystore_result.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/1ultimat3/BadIntent/fbd34c61ad980e2d475fb95d2bfac9759cd81941/doc/img/aidl_keystore_result.png
--------------------------------------------------------------------------------
/doc/img/aidl_keystore_sequencer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/1ultimat3/BadIntent/fbd34c61ad980e2d475fb95d2bfac9759cd81941/doc/img/aidl_keystore_sequencer.png
--------------------------------------------------------------------------------
/doc/img/aidl_keystore_sequencer_results.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/1ultimat3/BadIntent/fbd34c61ad980e2d475fb95d2bfac9759cd81941/doc/img/aidl_keystore_sequencer_results.png
--------------------------------------------------------------------------------
/doc/img/aidl_keystore_stub.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/1ultimat3/BadIntent/fbd34c61ad980e2d475fb95d2bfac9759cd81941/doc/img/aidl_keystore_stub.png
--------------------------------------------------------------------------------
/doc/img/aidl_keystore_transaction_codes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/1ultimat3/BadIntent/fbd34c61ad980e2d475fb95d2bfac9759cd81941/doc/img/aidl_keystore_transaction_codes.png
--------------------------------------------------------------------------------
/doc/img/backend_attacks_backend.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/1ultimat3/BadIntent/fbd34c61ad980e2d475fb95d2bfac9759cd81941/doc/img/backend_attacks_backend.png
--------------------------------------------------------------------------------
/doc/img/backend_attacks_intruder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/1ultimat3/BadIntent/fbd34c61ad980e2d475fb95d2bfac9759cd81941/doc/img/backend_attacks_intruder.png
--------------------------------------------------------------------------------
/doc/img/backend_attacks_intruder_config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/1ultimat3/BadIntent/fbd34c61ad980e2d475fb95d2bfac9759cd81941/doc/img/backend_attacks_intruder_config.png
--------------------------------------------------------------------------------
/doc/img/burp_interfaces.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/1ultimat3/BadIntent/fbd34c61ad980e2d475fb95d2bfac9759cd81941/doc/img/burp_interfaces.png
--------------------------------------------------------------------------------
/doc/img/configuration.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/1ultimat3/BadIntent/fbd34c61ad980e2d475fb95d2bfac9759cd81941/doc/img/configuration.png
--------------------------------------------------------------------------------
/doc/img/insecure_logging.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/1ultimat3/BadIntent/fbd34c61ad980e2d475fb95d2bfac9759cd81941/doc/img/insecure_logging.png
--------------------------------------------------------------------------------
/doc/img/intent_sniffing_broadcast.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/1ultimat3/BadIntent/fbd34c61ad980e2d475fb95d2bfac9759cd81941/doc/img/intent_sniffing_broadcast.png
--------------------------------------------------------------------------------
/doc/img/keylogger.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/1ultimat3/BadIntent/fbd34c61ad980e2d475fb95d2bfac9759cd81941/doc/img/keylogger.png
--------------------------------------------------------------------------------
/doc/img/main.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/1ultimat3/BadIntent/fbd34c61ad980e2d475fb95d2bfac9759cd81941/doc/img/main.png
--------------------------------------------------------------------------------
/doc/img/mobile_xss_intercept.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/1ultimat3/BadIntent/fbd34c61ad980e2d475fb95d2bfac9759cd81941/doc/img/mobile_xss_intercept.png
--------------------------------------------------------------------------------
/doc/img/mobile_xss_modify.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/1ultimat3/BadIntent/fbd34c61ad980e2d475fb95d2bfac9759cd81941/doc/img/mobile_xss_modify.png
--------------------------------------------------------------------------------
/doc/img/mobile_xss_result.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/1ultimat3/BadIntent/fbd34c61ad980e2d475fb95d2bfac9759cd81941/doc/img/mobile_xss_result.png
--------------------------------------------------------------------------------
/doc/img/overview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/1ultimat3/BadIntent/fbd34c61ad980e2d475fb95d2bfac9759cd81941/doc/img/overview.png
--------------------------------------------------------------------------------
/doc/img/pasteboard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/1ultimat3/BadIntent/fbd34c61ad980e2d475fb95d2bfac9759cd81941/doc/img/pasteboard.png
--------------------------------------------------------------------------------