├── AUTHORS.txt
├── CHANGELOG.md
├── Introspy-Android Config.apk
├── Introspy-Android Config
├── .classpath
├── .project
├── .settings
│ └── org.eclipse.jdt.core.prefs
├── AndroidManifest.xml
├── proguard.cfg
├── project.properties
├── res
│ ├── drawable-hdpi
│ │ └── ic_launcher.png
│ ├── drawable-ldpi
│ │ └── ic_launcher.png
│ ├── drawable-mdpi
│ │ └── ic_launcher.png
│ ├── layout
│ │ ├── list_filter_types.xml
│ │ ├── list_item.xml
│ │ ├── listfragment.xml
│ │ └── main.xml
│ └── values
│ │ └── strings.xml
└── src
│ └── com
│ └── introspy
│ └── config
│ ├── AndroidListFragmentActivity.java
│ ├── ApplicationFilter.java
│ ├── AsyncOp.java
│ ├── HookTypes.java
│ ├── InjectConfig.java
│ └── UpdateAppList.java
├── Introspy-Android Core.apk
├── Introspy-Android Core
├── .classpath
├── .project
├── .settings
│ └── org.eclipse.jdt.core.prefs
├── AndroidManifest.xml
├── ic_launcher-web.png
├── libs
│ ├── android-support-v4.jar
│ └── substrate-api.jar
├── proguard-project.txt
├── project.properties
├── res
│ └── values
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
└── src
│ └── com
│ └── introspy
│ ├── core
│ ├── ApplicationConfig.java
│ ├── ApplicationState.java
│ ├── HookConfig.java
│ ├── IntroHook.java
│ ├── IntroStringHelper.java
│ ├── IntrospyAndroid.java
│ ├── LoadConfig.java
│ ├── LoggerWrap.java
│ ├── Main.java
│ └── package-info.java
│ ├── custom_hooks
│ ├── CustomHookList.java
│ └── HookExampleImpl.java
│ ├── hooks
│ ├── HookCryptoImpl.java
│ ├── HookFileSystemImpl.java
│ ├── HookGeneric.java
│ ├── HookHashImpl.java
│ ├── HookIPCImpl.java
│ ├── HookKeyImpl.java
│ ├── HookList.java
│ ├── HookSQLiteImpl.java
│ ├── HookSSLImpl.java
│ ├── HookSharedPrefImpl.java
│ ├── HookWebviewImpl.java
│ └── package-info.java
│ └── logging
│ ├── Logger.java
│ ├── LoggerConfig.java
│ ├── LoggerDB.java
│ ├── LoggerErrorHandler.java
│ ├── LoggerFile.java
│ ├── LoggerTraces.java
│ ├── SQLiteIntrospy.java
│ ├── SQLiteIntrospyHelper.java
│ ├── SQLiteIntrospyLog.java
│ └── package-info.java
├── LICENSE
├── README.md
├── TODO.txt
└── images
├── GUI.png
├── adding_hook_impl.PNG
├── adding_hooks.PNG
├── cydia_installed.png
├── example_logcat.png
├── html_report_android.PNG
├── impl_handle.PNG
└── new_object.PNG
/AUTHORS.txt:
--------------------------------------------------------------------------------
1 | Creators
2 | --------
3 | Marc Blanchou
4 |
5 |
6 | Maintainers
7 | -----------
8 | ...
9 |
10 |
11 | Contributors
12 | ------------
13 | ...
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iSECPartners/Introspy-Android/c611cf125f4ef93d5ddac4c714f6c08a9433b1ec/CHANGELOG.md
--------------------------------------------------------------------------------
/Introspy-Android Config.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iSECPartners/Introspy-Android/c611cf125f4ef93d5ddac4c714f6c08a9433b1ec/Introspy-Android Config.apk
--------------------------------------------------------------------------------
/Introspy-Android Config/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/Introspy-Android Config/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | Introspy-Android Config
4 |
5 |
6 |
7 |
8 |
9 | com.android.ide.eclipse.adt.ResourceManagerBuilder
10 |
11 |
12 |
13 |
14 | com.android.ide.eclipse.adt.PreCompilerBuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.jdt.core.javabuilder
20 |
21 |
22 |
23 |
24 | com.android.ide.eclipse.adt.ApkBuilder
25 |
26 |
27 |
28 |
29 |
30 | com.android.ide.eclipse.adt.AndroidNature
31 | org.eclipse.jdt.core.javanature
32 |
33 |
34 |
--------------------------------------------------------------------------------
/Introspy-Android Config/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
3 | org.eclipse.jdt.core.compiler.compliance=1.6
4 | org.eclipse.jdt.core.compiler.source=1.6
5 |
--------------------------------------------------------------------------------
/Introspy-Android Config/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
9 |
10 |
14 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Introspy-Android Config/proguard.cfg:
--------------------------------------------------------------------------------
1 | -optimizationpasses 5
2 | -dontusemixedcaseclassnames
3 | -dontskipnonpubliclibraryclasses
4 | -dontpreverify
5 | -verbose
6 | -optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
7 |
8 | -keep public class * extends android.app.Activity
9 | -keep public class * extends android.app.Application
10 | -keep public class * extends android.app.Service
11 | -keep public class * extends android.content.BroadcastReceiver
12 | -keep public class * extends android.content.ContentProvider
13 | -keep public class * extends android.app.backup.BackupAgentHelper
14 | -keep public class * extends android.preference.Preference
15 | -keep public class com.android.vending.licensing.ILicensingService
16 |
17 | -keepclasseswithmembernames class * {
18 | native ;
19 | }
20 |
21 | -keepclasseswithmembers class * {
22 | public (android.content.Context, android.util.AttributeSet);
23 | }
24 |
25 | -keepclasseswithmembers class * {
26 | public (android.content.Context, android.util.AttributeSet, int);
27 | }
28 |
29 | -keepclassmembers class * extends android.app.Activity {
30 | public void *(android.view.View);
31 | }
32 |
33 | -keepclassmembers enum * {
34 | public static **[] values();
35 | public static ** valueOf(java.lang.String);
36 | }
37 |
38 | -keep class * implements android.os.Parcelable {
39 | public static final android.os.Parcelable$Creator *;
40 | }
41 |
--------------------------------------------------------------------------------
/Introspy-Android Config/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system use,
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 |
10 | # Project target.
11 | target=android-19
12 |
--------------------------------------------------------------------------------
/Introspy-Android Config/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iSECPartners/Introspy-Android/c611cf125f4ef93d5ddac4c714f6c08a9433b1ec/Introspy-Android Config/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Introspy-Android Config/res/drawable-ldpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iSECPartners/Introspy-Android/c611cf125f4ef93d5ddac4c714f6c08a9433b1ec/Introspy-Android Config/res/drawable-ldpi/ic_launcher.png
--------------------------------------------------------------------------------
/Introspy-Android Config/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iSECPartners/Introspy-Android/c611cf125f4ef93d5ddac4c714f6c08a9433b1ec/Introspy-Android Config/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Introspy-Android Config/res/layout/list_filter_types.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Introspy-Android Config/res/layout/list_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Introspy-Android Config/res/layout/listfragment.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
16 |
17 |
22 |
23 |
--------------------------------------------------------------------------------
/Introspy-Android Config/res/layout/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
19 |
20 |
--------------------------------------------------------------------------------
/Introspy-Android Config/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Introspy Config
4 |
5 |
--------------------------------------------------------------------------------
/Introspy-Android Config/src/com/introspy/config/AndroidListFragmentActivity.java:
--------------------------------------------------------------------------------
1 | package com.introspy.config;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 |
6 | public class AndroidListFragmentActivity extends Activity {
7 | /** Called when the activity is first created. */
8 | @Override
9 | public void onCreate(Bundle savedInstanceState) {
10 | super.onCreate(savedInstanceState);
11 | setContentView(R.layout.main);
12 | }
13 | }
--------------------------------------------------------------------------------
/Introspy-Android Config/src/com/introspy/config/ApplicationFilter.java:
--------------------------------------------------------------------------------
1 | package com.introspy.config;
2 |
3 | import android.os.Bundle;
4 | import android.util.Log;
5 | import android.util.SparseBooleanArray;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.widget.ArrayAdapter;
10 | import android.widget.ListAdapter;
11 | import android.widget.ListView;
12 |
13 | public class ApplicationFilter extends UpdateAppList {
14 |
15 | private Boolean _alwaysOverwriteConfig = false;
16 |
17 | @Override
18 | public void onCreate(Bundle savedInstanceState) {
19 | super.onCreate(savedInstanceState);
20 |
21 | try {
22 | _context = this.getActivity().getApplicationContext();
23 |
24 | updateAppList();
25 |
26 | ListAdapter myListAdapter = new ArrayAdapter(
27 | getActivity(),
28 | R.layout.list_item,
29 | _completeAppList);
30 |
31 | setListAdapter(myListAdapter);
32 | } catch (Exception e) {
33 | Log.w("Introspy", "error: " + e);
34 | }
35 | }
36 |
37 | @Override
38 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
39 | Bundle savedInstanceState) {
40 | return inflater.inflate(R.layout.listfragment, container, false);
41 |
42 | }
43 |
44 | @Override
45 | public void onListItemClick(ListView l, View v, int position, long id) {
46 | try {
47 | _lastItemSelected = _appList.get(position);
48 | int count = getListView().getCount();
49 | SparseBooleanArray sparseBooleanArray = getListView().getCheckedItemPositions();
50 | _lastItemChecked = false;
51 | for (int i = 0; i < count; i++){
52 | if (sparseBooleanArray.get(i) && i == position) {
53 | _lastItemChecked = true;
54 | }
55 | }
56 | // commit the change to preferences
57 | _sp.edit().putBoolean(_lastItemSelected, _lastItemChecked).commit();
58 |
59 | } catch (Exception e) {
60 | Log.w("IntrospyConfig", "Error:onListItemClick:" + e +
61 | "\n SP: "+ _sp);
62 | }
63 | InjectConfig.getInstance().writeConfig(_lastItemChecked,
64 | _lastItemSelected, _context);
65 | InjectConfig.getInstance().commit();
66 | }
67 |
68 | @Override
69 | public void onStart() {
70 | super.onStart();
71 | try {
72 | _sp = _context.getSharedPreferences("ApplicationFilter", 0);
73 | Boolean setup = _sp.getBoolean("Enabled", false);
74 |
75 | if (setup == false) {
76 | _sp.edit().putBoolean("Enabled", true).commit();
77 | for(String item : _appList) {
78 | _sp.edit().putBoolean(item, false).commit();
79 | }
80 |
81 | final ListView list = getListView();
82 | for (int i = 0; i < getListAdapter().getCount(); i++) {
83 | list.setItemChecked(i, false);
84 | }
85 | }
86 | // create preferences with the appDir as key
87 | // TODO: uninstalled apps are never cleaned
88 | // it does not create a bug because installed apps
89 | // are compared against what is in the preferences
90 | // but it keeps an history of all apps ever installed
91 | // ..no time to change this behavior
92 | else {
93 | final ListView list = getListView();
94 | for (int i = 0; i < _appList.size(); i++) {
95 | String appDir = _appList.get(i);
96 | Boolean checked = _sp.getBoolean(appDir, false);
97 | list.setItemChecked(i, checked);
98 | // overwrite / remove the config file even if preferences
99 | // state otherwise because the tester may remove/add it
100 | if (_alwaysOverwriteConfig) {
101 | InjectConfig.getInstance().writeConfig(
102 | checked, appDir, _context);
103 | }
104 | }
105 | if (_alwaysOverwriteConfig)
106 | InjectConfig.getInstance().commit();
107 | }
108 | }
109 | catch (Exception e) {
110 | Log.w("IntrospyConfig", "Error: " + e);
111 | }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/Introspy-Android Config/src/com/introspy/config/AsyncOp.java:
--------------------------------------------------------------------------------
1 | package com.introspy.config;
2 |
3 | import android.content.Context;
4 | import android.os.AsyncTask;
5 |
6 | @SuppressWarnings("rawtypes")
7 | public class AsyncOp extends AsyncTask {
8 | public AsyncOp(Context _context) {
9 | // TODO Auto-generated constructor stub
10 | }
11 |
12 | // will be used to not freeze the GUI while executing commands
13 | @Override
14 | protected Object doInBackground(Object... params) {
15 | //if (!InjectConfig.getInstance().execute((String) params[0])) {
16 | //Toast.makeText(getActivity(), "This app. needs root!",
17 | // Toast.LENGTH_LONG).show();
18 | //}
19 | return null;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Introspy-Android Config/src/com/introspy/config/HookTypes.java:
--------------------------------------------------------------------------------
1 | package com.introspy.config;
2 |
3 | import android.content.SharedPreferences;
4 | import android.os.Bundle;
5 | import android.util.Log;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.widget.ArrayAdapter;
10 | import android.widget.ListAdapter;
11 | import android.widget.ListView;
12 |
13 | public class HookTypes extends UpdateAppList {
14 |
15 | private Boolean _alwaysOverwriteConfig = false;
16 |
17 | protected static String[] _hookTypes ={
18 | "GENERAL CRYPTO",
19 | "KEY",
20 | "HASH",
21 | "FS",
22 | "IPC",
23 | "PREF",
24 | "URI",
25 | "SSL",
26 | "WEBVIEW",
27 | "CUSTOM HOOKS",
28 | "SQLite (NO DB)",
29 | "_ STACK TRACES",
30 | "_ NO DB"
31 | };
32 |
33 | public static String[] getHookTypes() {
34 | return _hookTypes;
35 | }
36 |
37 | @Override
38 | public void onCreate(Bundle savedInstanceState) {
39 | super.onCreate(savedInstanceState);
40 |
41 | _context = this.getActivity().getApplicationContext();
42 |
43 | ListAdapter myListAdapter = new ArrayAdapter(
44 | getActivity(),
45 | R.layout.list_filter_types,
46 | _hookTypes);
47 |
48 | setListAdapter(myListAdapter);
49 | }
50 |
51 | @Override
52 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
53 | Bundle savedInstanceState) {
54 | return inflater.inflate(R.layout.listfragment, container, false);
55 | }
56 |
57 | @Override
58 | public void onListItemClick(ListView l, View v, int position, long id) {
59 |
60 | // save new config in prefs
61 | super.onListItemClick(l, v, position, id);
62 |
63 | // only update the list once (slow)
64 | // TODO: add asyncTask on this
65 | // and update list of apps regularly
66 | // (currently need to restart the app to refresh the list)
67 | if (_appList.isEmpty())
68 | updateAppList();
69 |
70 | // get list of supposedly monitored apps from prefs
71 | SharedPreferences _sp_appFilter =
72 | _context.getSharedPreferences("ApplicationFilter", 0);
73 |
74 | // update selected apps with the new config
75 | for (int i = 0; i < _appList.size(); i++) {
76 | String appDir = _appList.get(i);
77 | Boolean checked = _sp_appFilter.getBoolean(appDir, false);
78 | // overwrite / remove the config file even if preferences
79 | // state otherwise because the tester may remove/add it
80 | if (_alwaysOverwriteConfig)
81 | InjectConfig.getInstance().writeConfig(checked, appDir, _context);
82 | else if (checked) {
83 | InjectConfig.getInstance().enableApp(appDir, _context);
84 | }
85 | }
86 | InjectConfig.getInstance().commit();
87 |
88 | }
89 |
90 | @Override
91 | public void onStart() {
92 | super.onStart();
93 | try {
94 | _sp = _context.getSharedPreferences("HookTypes", 0);
95 | Boolean setup = _sp.getBoolean("Enabled", false);
96 |
97 | if (setup == false) {
98 | _sp.edit().putBoolean("Enabled", true).commit();
99 |
100 | final ListView list = getListView();
101 | for (int i = 0; i < _hookTypes.length; i++) {
102 |
103 | // disable the NO DB or STACK TRACES by default
104 | if (_hookTypes[i].contains("NO DB") ||
105 | _hookTypes[i].contains("STACK TRACES")) {
106 | _sp.edit().putBoolean(_hookTypes[i], false).commit();
107 | list.setItemChecked(i, false);
108 | }
109 | else {
110 | _sp.edit().putBoolean(_hookTypes[i], true).commit();
111 | list.setItemChecked(i, true);
112 | }
113 | }
114 | }
115 | else {
116 | final ListView list = getListView();
117 | for (int i = 0; i < _hookTypes.length; i++) {
118 | list.setItemChecked(i, _sp.getBoolean(_hookTypes[i], false));
119 | }
120 | }
121 | }
122 | catch (Exception e) {
123 | Log.w("IntrospyConfig", "Error: " + e + "\n "+e.fillInStackTrace());
124 | }
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/Introspy-Android Config/src/com/introspy/config/InjectConfig.java:
--------------------------------------------------------------------------------
1 | package com.introspy.config;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 | import android.util.Log;
6 |
7 | public class InjectConfig {
8 |
9 | private static String _TAG = "IntrospyGUI";
10 |
11 | private static InjectConfig _instance = null;
12 |
13 | protected InjectConfig() {
14 | }
15 | static InjectConfig getInstance() {
16 | if (_instance == null)
17 | _instance = new InjectConfig();
18 | return _instance;
19 | }
20 |
21 | public String getHookTypesFromPrefs(Context context) {
22 | String[] hookTypes = HookTypes.getHookTypes();
23 | SharedPreferences sp =
24 | context.getSharedPreferences("HookTypes", 0);
25 | String ret = "";
26 |
27 | for (int i = 0; i < hookTypes.length; i++) {
28 | if (sp.getBoolean(hookTypes[i], false)) {
29 | ret += hookTypes[i];
30 | ret += ",";
31 | }
32 | }
33 |
34 | ret = ret.substring(0, ret.length() - 1);
35 | // ret += "\n";
36 | return ret;
37 | }
38 |
39 | public void enableApp(String appDir, Context context) {
40 | String path = appDir + "/introspy.config";
41 | _command += "su -c echo '" + getHookTypesFromPrefs(context) + "' > " + path + " ; su -c chmod 664 " + path + " ; ";
42 | }
43 |
44 | public void disableApp(String appDir) {
45 | String path = appDir + "/introspy.config";
46 | _command += "su -c rm " + path + " ; ";
47 | }
48 |
49 | private String _command = "";
50 |
51 | public Boolean commit() {
52 | if (_command.isEmpty())
53 | return false;
54 | Log.i(_TAG, _command);
55 | try {
56 | Runtime.getRuntime().exec(_command);
57 | } catch (Exception e) {
58 | _command = "";
59 | Log.w(_TAG, "error: this app needs to be root.");
60 | return false;
61 | }
62 | _command = "";
63 | return true;
64 | }
65 |
66 | public void writeConfig(Boolean enable, String appDir, Context context) {
67 | if (enable)
68 | enableApp(appDir, context);
69 | else
70 | disableApp(appDir);
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/Introspy-Android Config/src/com/introspy/config/UpdateAppList.java:
--------------------------------------------------------------------------------
1 | package com.introspy.config;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | import android.app.ListFragment;
7 | import android.content.Context;
8 | import android.content.SharedPreferences;
9 | import android.util.Log;
10 | import android.util.SparseBooleanArray;
11 | import android.view.View;
12 | import android.widget.ListView;
13 |
14 | public class UpdateAppList extends ListFragment {
15 |
16 | protected SharedPreferences _sp;
17 | protected ArrayList _appList =
18 | new ArrayList();
19 | protected ArrayList _completeAppList =
20 | new ArrayList();
21 | protected Context _context;
22 |
23 | protected String _lastItemSelected = null;
24 | protected Boolean _lastItemChecked = null;
25 |
26 | @Override
27 | public void onListItemClick(ListView l, View v, int position, long id) {
28 | try {
29 | _lastItemSelected =
30 | getListView().getItemAtPosition(position).toString();
31 | // String prompt =
32 | // "clicked item: " + _lastItemSelected + "\n\n";
33 | // prompt += "selected items: \n";
34 |
35 | int count = getListView().getCount();
36 |
37 | SparseBooleanArray sparseBooleanArray = getListView().getCheckedItemPositions();
38 | _lastItemChecked = false;
39 | for (int i = 0; i < count; i++){
40 | if (sparseBooleanArray.get(i)) {
41 | // prompt += getListView().getItemAtPosition(i).toString() + "\n";
42 | if (i == position)
43 | _lastItemChecked = true;
44 | }
45 | }
46 |
47 | // commit the change to preferences
48 | _sp.edit().putBoolean(_lastItemSelected, _lastItemChecked).commit();
49 |
50 | } catch (Exception e) {
51 | Log.w("IntrospyConfig", "Error:onListItemClick:" + e +
52 | "\n SP: "+ _sp);
53 | }
54 | }
55 |
56 | protected void updateAppList() {
57 | android.content.pm.PackageManager pm = _context.getPackageManager();
58 | List list = pm.getInstalledPackages(0);
59 |
60 | for(android.content.pm.PackageInfo pi : list) {
61 | try{
62 | android.content.pm.ApplicationInfo ai = pm.getApplicationInfo(pi.packageName, 0);
63 | String currAppName = pm.getApplicationLabel(pi.applicationInfo).toString();
64 | if ((ai.flags & 129) == 0) {
65 | // one list for display and one list to keep track of app dirs
66 | _completeAppList.add("[" + currAppName + "]\n" +
67 | pi.applicationInfo.packageName);
68 | _appList.add(pi.applicationInfo.dataDir);
69 | }
70 |
71 | } catch (Exception e) {
72 | Log.w("IntrospyConfig", "Error: " + e);
73 | }
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/Introspy-Android Core.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iSECPartners/Introspy-Android/c611cf125f4ef93d5ddac4c714f6c08a9433b1ec/Introspy-Android Core.apk
--------------------------------------------------------------------------------
/Introspy-Android Core/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/Introspy-Android Core/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | Introspy-Android Core
4 |
5 |
6 |
7 |
8 |
9 | com.android.ide.eclipse.adt.ResourceManagerBuilder
10 |
11 |
12 |
13 |
14 | com.android.ide.eclipse.adt.PreCompilerBuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.jdt.core.javabuilder
20 |
21 |
22 |
23 |
24 | com.android.ide.eclipse.adt.ApkBuilder
25 |
26 |
27 |
28 |
29 |
30 | com.android.ide.eclipse.adt.AndroidNature
31 | org.eclipse.jdt.core.javanature
32 |
33 |
34 |
--------------------------------------------------------------------------------
/Introspy-Android Core/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
3 | org.eclipse.jdt.core.compiler.compliance=1.6
4 | org.eclipse.jdt.core.compiler.source=1.6
5 |
--------------------------------------------------------------------------------
/Introspy-Android Core/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
10 |
11 |
12 |
13 |
14 |
15 |
18 | ".classpath"
19 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Introspy-Android Core/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iSECPartners/Introspy-Android/c611cf125f4ef93d5ddac4c714f6c08a9433b1ec/Introspy-Android Core/ic_launcher-web.png
--------------------------------------------------------------------------------
/Introspy-Android Core/libs/android-support-v4.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iSECPartners/Introspy-Android/c611cf125f4ef93d5ddac4c714f6c08a9433b1ec/Introspy-Android Core/libs/android-support-v4.jar
--------------------------------------------------------------------------------
/Introspy-Android Core/libs/substrate-api.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iSECPartners/Introspy-Android/c611cf125f4ef93d5ddac4c714f6c08a9433b1ec/Introspy-Android Core/libs/substrate-api.jar
--------------------------------------------------------------------------------
/Introspy-Android Core/proguard-project.txt:
--------------------------------------------------------------------------------
1 | # To enable ProGuard in your project, edit project.properties
2 | # to define the proguard.config property as described in that file.
3 | #
4 | # Add project specific ProGuard rules here.
5 | # By default, the flags in this file are appended to flags specified
6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt
7 | # You can edit the include path and order by changing the ProGuard
8 | # include property in project.properties.
9 | #
10 | # For more details, see
11 | # http://developer.android.com/guide/developing/tools/proguard.html
12 |
13 | # Add any project specific keep options here:
14 |
15 | # If your project uses WebView with JS, uncomment the following
16 | # and specify the fully qualified class name to the JavaScript interface
17 | # class:
18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
19 | # public *;
20 | #}
21 |
--------------------------------------------------------------------------------
/Introspy-Android Core/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-19
15 |
--------------------------------------------------------------------------------
/Introspy-Android Core/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 16dp
5 | 16dp
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Introspy-Android Core/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Introspy Android Core
5 | Settings
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Introspy-Android Core/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
14 |
15 |
16 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Introspy-Android Core/src/com/introspy/core/ApplicationConfig.java:
--------------------------------------------------------------------------------
1 | package com.introspy.core;
2 |
3 | import android.content.Context;
4 |
5 | public class ApplicationConfig {
6 |
7 | private static String _packageName = null;
8 | private static String _dataDir = null;
9 | private static LoadConfig _loadConfig = null;
10 | private static Boolean _enabled = false;
11 | private static Context _context = null;
12 |
13 | public static boolean g_verbose_errors = false;
14 | public static boolean g_debug = false;
15 | public static boolean g_hook_em_all = true;
16 |
17 | // Private constructor prevents instantiation from other classes
18 | private ApplicationConfig() {}
19 |
20 | // get
21 | public static String getPackageName() {
22 | return _packageName;
23 | }
24 | public static String getDataDir() {
25 | return _dataDir;
26 | }
27 | public static LoadConfig getLoadConfig() {
28 | if (_loadConfig == null)
29 | _loadConfig = new LoadConfig();
30 | return _loadConfig;
31 | }
32 | public static Context getContext() {
33 | return _context;
34 | }
35 |
36 | // set
37 | public static void setPackageName(String packageName) {
38 | _packageName = packageName;
39 | }
40 | public static void setDataDir(String dataDir) {
41 | _dataDir = dataDir;
42 | }
43 | public static void setContext(Context context) {
44 | _context = context;
45 | }
46 |
47 | // ####
48 | public static void disable() {
49 | _enabled = false;
50 | }
51 | public static void enable() {
52 | _enabled = true;
53 | }
54 |
55 | public static boolean isEnabled() {
56 | return _enabled;
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/Introspy-Android Core/src/com/introspy/core/ApplicationState.java:
--------------------------------------------------------------------------------
1 | package com.introspy.core;
2 |
3 | import java.lang.reflect.Method;
4 |
5 | import com.introspy.logging.LoggerConfig;
6 | import com.saurik.substrate.MS;
7 |
8 | import android.content.Context;
9 | import android.content.pm.PackageInfo;
10 | import android.content.pm.PackageManager;
11 | import android.content.pm.PackageManager.NameNotFoundException;
12 | import android.util.Log;
13 |
14 | public class ApplicationState {
15 | // static private String _TAG = LoggerConfig.getTag();
16 | // static private String _TAG_LOG = LoggerConfig.getTagLog();
17 | static private String _TAG_ERROR = LoggerConfig.getTagError();
18 |
19 | @SuppressWarnings({ "unchecked", "rawtypes" })
20 | protected static void initApplicationState(Class> resources) {
21 |
22 | String methodName = "getPackageName";
23 | Class>[] params = new Class>[]{};
24 | Method pMethod = null;
25 | try {
26 | pMethod = resources.getMethod(methodName, params);
27 | } catch (Exception e) {
28 | Log.w(_TAG_ERROR, "Error - No such method: " + methodName);
29 | return;
30 | }
31 |
32 | final MS.MethodPointer old = new MS.MethodPointer();
33 | MS.hookMethod(resources, pMethod, new MS.MethodHook() {
34 | public Object invoked(Object resources,
35 | Object... args) throws Throwable {
36 |
37 | String packageName = (String)old.invoke(resources, args);
38 | if (!packageName.equals("android") &&
39 | ApplicationConfig.getPackageName() == null) {
40 |
41 | ApplicationConfig.setPackageName(packageName);
42 |
43 | try {
44 | Class> cls = Class.forName("android.app.ContextImpl");
45 | Class> noparams[] = {};
46 | Method _method =
47 | cls.getDeclaredMethod("getApplicationContext", noparams);
48 |
49 | Context context = (Context) _method.invoke(resources);
50 | ApplicationConfig.setContext(context);
51 |
52 | //PackageManager pm = context.getPackageManager();
53 | _method = cls.getDeclaredMethod("getPackageManager", noparams);
54 | PackageManager pm = (PackageManager) _method.invoke(resources);
55 |
56 |
57 | android.content.pm.ApplicationInfo ai =
58 | pm.getApplicationInfo(packageName, 0);
59 |
60 | if ((ai.flags & 0x81) == 0) {
61 | try {
62 | PackageInfo p = pm.getPackageInfo(packageName, 0);
63 | ApplicationConfig.setDataDir(p.applicationInfo.dataDir);
64 | ApplicationConfig.enable();
65 | } catch (NameNotFoundException e) {
66 | Log.w(_TAG_ERROR, "Error Package name not found ", e);
67 | }
68 | }
69 | }
70 | catch (Exception e) {
71 | Log.w(_TAG_ERROR, "Error when setting the " +
72 | "application state for ["+ packageName +"]: ", e);
73 | }
74 | }
75 | return packageName;
76 | }
77 | }, old);
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/Introspy-Android Core/src/com/introspy/core/HookConfig.java:
--------------------------------------------------------------------------------
1 | package com.introspy.core;
2 |
3 | public class HookConfig {
4 | private String _className;
5 | private String _methodName;
6 | private Class>[] _parameters;
7 | private boolean _active;
8 | private String _type;
9 | private IntroHook _IntroHook;
10 | private String _notes;
11 | private String _subType;
12 |
13 | // getters
14 | public String getClassName() { return _className; }
15 | public String getMethodName() { return _methodName; }
16 | public Class>[] getParameters() { return _parameters; }
17 | public boolean isActive() { return _active; }
18 | public IntroHook getFunc() { return _IntroHook; }
19 | public String getNotes() { return _notes; }
20 |
21 | public String getType() { return _type; }
22 | public String getSubType() { return _subType; }
23 |
24 | public String getCategory() {
25 | if (_subType.isEmpty())
26 | return _type;
27 | return _subType;
28 | }
29 |
30 | // constructor
31 | public HookConfig(boolean active,
32 | String type,
33 | String subType,
34 | String className,
35 | String methodName,
36 | IntroHook IntroHook,
37 | Class>[] parameters,
38 | String notes) {
39 |
40 | _IntroHook = IntroHook;
41 | _className = className;
42 | _active = active;
43 | _methodName = methodName;
44 | _parameters = parameters;
45 | _type = type;
46 | _subType = subType;
47 | _notes = notes;
48 | }
49 |
50 | // constructor for custom hooks
51 | public HookConfig(boolean active,
52 | String className,
53 | String methodName,
54 | Class>[] parameters,
55 | IntroHook IntroHook,
56 | String notes) {
57 |
58 | _IntroHook = IntroHook;
59 | _className = className;
60 | _active = active;
61 | _methodName = methodName;
62 | _parameters = parameters;
63 | _type = "CUSTOM HOOK";
64 | _subType = "CUSTOM HOOK";
65 | _notes = notes;
66 | }
67 |
68 | // constructor for custom hooks without notes
69 | public HookConfig(boolean active,
70 | String className,
71 | String methodName,
72 | Class>[] parameters,
73 | IntroHook IntroHook) {
74 |
75 | _IntroHook = IntroHook;
76 | _className = className;
77 | _active = active;
78 | _methodName = methodName;
79 | _parameters = parameters;
80 | _type = "CUSTOM HOOK";
81 | _subType = "CUSTOM HOOK";
82 | _notes = "";
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/Introspy-Android Core/src/com/introspy/core/IntroHook.java:
--------------------------------------------------------------------------------
1 | package com.introspy.core;
2 |
3 | import android.util.Log;
4 |
5 | import com.introspy.logging.LoggerConfig;
6 | import com.saurik.substrate.MS;
7 |
8 | @SuppressWarnings("rawtypes")
9 | public
10 | class IntroHook extends LoggerWrap {
11 | protected HookConfig _config = null;
12 |
13 | protected String _TAG = LoggerConfig.getTag();
14 | protected String _TAG_ERROR = LoggerConfig.getTagError();
15 |
16 | protected String _className, _methodName, _type;
17 | protected String _packageName, _dataDir;
18 | protected String _notes;
19 | protected Object _resources;
20 | protected Object _args;
21 |
22 | protected Class>[] _parameters;
23 | protected MS.MethodPointer _old = null;
24 |
25 | // Protected constructor prevents
26 | // instantiation from other classes
27 | protected IntroHook() {
28 |
29 | }
30 |
31 | public void init(HookConfig config, Object resources, MS.MethodPointer old, Object... args) {
32 | if (_config == null) {
33 | _config = config;
34 | _className = _config.getClassName();
35 | _methodName = _config.getMethodName();
36 | _parameters = _config.getParameters();
37 | _type = _config.getCategory();
38 | _old = old;
39 | _notes = _config.getNotes();
40 |
41 | _resources = resources;
42 |
43 | _packageName = ApplicationConfig.getPackageName();
44 | _dataDir = ApplicationConfig.getDataDir();
45 |
46 | _logInit(config);
47 | }
48 | }
49 |
50 | public void execute(Object... args) {
51 | // display info on the app related to the hook
52 | _logBasicInfo();
53 | if (!_config.getNotes().isEmpty())
54 | _logLine("Notes: " + _notes);
55 | _logLine("-> Resources type: " +
56 | (_resources != null ? _resources.getClass() : "None"));
57 |
58 | try {
59 | if (_config.getParameters() != null) {
60 | int argNb = 0;
61 | for (Class> elemParameter : _parameters) {
62 | _logLine("-> Argument " + (argNb + 1) +
63 | ", Data: " + args[argNb].toString() +
64 | " (Type: " + elemParameter.getName() + ")"
65 | );
66 | argNb++;
67 | }
68 | }
69 | }
70 | catch (Exception e) {
71 | Log.w(_TAG, "-> Error getting arguments");
72 | }
73 | _logFlush_I();
74 | }
75 |
76 | @SuppressWarnings("unchecked")
77 | protected Object _hookInvoke(Object... args) throws Throwable {
78 | return _old.invoke(_resources, args);
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/Introspy-Android Core/src/com/introspy/core/IntroStringHelper.java:
--------------------------------------------------------------------------------
1 | package com.introspy.core;
2 |
3 | import android.util.Base64;
4 |
5 | public class IntroStringHelper {
6 | protected String _byteArrayToHex(byte[] a) {
7 | StringBuilder sb = new StringBuilder();
8 | for(byte b: a)
9 | sb.append(String.format("%02x-", b&0xff));
10 | sb.deleteCharAt(sb.length()-1);
11 | return sb.toString();
12 | }
13 |
14 | protected String _byteArrayToReadableStr(byte[] a) {
15 | StringBuilder sb = new StringBuilder();
16 | for(byte b: a) {
17 | if (b >= 32 && b < 127)
18 | sb.append(String.format("%c", b));
19 | else
20 | sb.append('.');
21 | }
22 | //sb.deleteCharAt(sb.length()-1);
23 | return sb.toString();
24 | }
25 |
26 | protected String _byteArrayToB64(byte[] a) {
27 | return Base64.encodeToString(a, Base64.NO_WRAP);
28 | }
29 |
30 | protected Boolean _isItReadable(String input) {
31 | int readableChar = 0;
32 | for (int i = 0; i < input.length(); i++) {
33 | int c = input.charAt(i);
34 | // Check if a string only contains US-ASCII code point
35 | // if ((c >= 32 && c < 127) || c == 9 ||
36 | // c == 13 || c == 10 || c == 0) {
37 | if (c >= 32 && c < 127) {
38 | readableChar++;
39 | }
40 | }
41 |
42 | // can be considered readable if X% characters are ascii
43 | // (0 is considered a character here so that UTF16
44 | // can be considered readable too)
45 | return (readableChar > (input.length() * 0.75) ? true : false);
46 | }
47 |
48 | protected String _escapeXMLChars(String s) {
49 | return s.replaceAll("&", "&")
50 | .replaceAll("'", "'")
51 | .replaceAll("\"", """)
52 | .replaceAll("<", "<")
53 | .replaceAll(">", ">");
54 | }
55 |
56 | protected String _getReadableByteArr(byte[] input) {
57 | String out = new String(input);
58 | if (!_isItReadable(out))
59 | out = _byteArrayToB64(input);
60 | else
61 | out = _byteArrayToReadableStr(input);
62 | return out;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Introspy-Android Core/src/com/introspy/core/IntrospyAndroid.java:
--------------------------------------------------------------------------------
1 | package com.introspy.core;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 |
6 | public class IntrospyAndroid extends Application {
7 |
8 | private static Context context;
9 |
10 | public void onCreate(){
11 | super.onCreate();
12 | IntrospyAndroid.context = getApplicationContext();
13 | }
14 |
15 | public static Context getAppContext() {
16 | return IntrospyAndroid.context;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Introspy-Android Core/src/com/introspy/core/LoadConfig.java:
--------------------------------------------------------------------------------
1 | package com.introspy.core;
2 |
3 | import java.io.RandomAccessFile;
4 | import android.text.format.Time;
5 |
6 | public class LoadConfig {
7 | // set to true here makes it faster but won't
8 | // allows to update changes in the file a runtime
9 | private Boolean _onlyCheckOnce = false;
10 | // prevents the app for constantly opening the config file
11 | private Boolean _alreadyChecked = false;
12 | private Boolean _previousCheckValue = false;
13 |
14 | private String _configFileName = "introspy.config";
15 |
16 | private Time _lastCheck = new Time();
17 |
18 | private String _hookTypes = "";
19 |
20 | private static LoadConfig _instance = null;
21 |
22 | protected LoadConfig() {
23 | }
24 |
25 | static public LoadConfig getInstance() {
26 | if (_instance == null)
27 | _instance = new LoadConfig();
28 | return _instance;
29 | }
30 |
31 |
32 | public String getHookTypes() {
33 | return _hookTypes;
34 | }
35 |
36 | // no config file means the app won't be hooked
37 | public Boolean initConfig(String dataDir) {
38 | if (_alreadyChecked)
39 | return _previousCheckValue;
40 |
41 | Time now = new Time();
42 | now.setToNow();
43 |
44 | // check for modifications only every X
45 | if (_lastCheck.toMillis(true) + 3000 >= now.toMillis(true)) {
46 | return _previousCheckValue;
47 | }
48 |
49 | String path = dataDir + "/" + _configFileName;
50 | _hookTypes = readFirstLineOfFile(path);
51 | if (_onlyCheckOnce)
52 | _alreadyChecked = true;
53 | _lastCheck.setToNow();
54 |
55 | if (_hookTypes.isEmpty()) {
56 | return (_previousCheckValue = false);
57 | }
58 | return (_previousCheckValue = true);
59 | }
60 |
61 | private String readFirstLineOfFile(String fn) {
62 | String lineData = "";
63 | try{
64 | RandomAccessFile inFile = new RandomAccessFile(fn, "r");
65 | lineData = inFile.readLine();
66 | inFile.close();
67 | }
68 | // file not found
69 | catch(Exception e){
70 | // Log.i("IntrospyLog", "--> "+ e);
71 | // app won't be hooked
72 | }
73 | return lineData;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/Introspy-Android Core/src/com/introspy/core/LoggerWrap.java:
--------------------------------------------------------------------------------
1 | package com.introspy.core;
2 |
3 | import com.introspy.logging.Logger;
4 |
5 | public class LoggerWrap extends IntroStringHelper {
6 | private Logger _l = new Logger();
7 |
8 | protected void _logInit(HookConfig config) {
9 | _l.logInit(config);
10 | }
11 |
12 | protected void _logLine(String line) {
13 | _l.logLine(line);
14 | }
15 |
16 | protected void _logFlush_I(String notes) {
17 | _l.logFlush_I(notes);
18 | }
19 | protected void _logFlush_W(String notes) {
20 | _l.logFlush_W(notes);
21 | }
22 |
23 | protected void _logFlush_I() {
24 | _l.logFlush_I();
25 | }
26 |
27 | protected void _logFlush_W() {
28 | _l.logFlush_W();
29 | }
30 |
31 | protected void _logParameter(String name, String value) {
32 | _l.logParameter(name, value);
33 | }
34 |
35 | protected void _logParameter(String name, Object value) {
36 | _l.logParameter(name, "" + value);
37 | }
38 |
39 | protected void _logReturnValue(String name, String value) {
40 | _l.logReturnValue(name, value);
41 | }
42 |
43 | protected void _logReturnValue(String name, Object value) {
44 | _l.logReturnValue(name, "" + value);
45 | }
46 |
47 | protected void _logBasicInfo() {
48 | _l.logBasicInfo();
49 | }
50 |
51 | protected String _getFullTraces() {
52 | return _l.getFullTraces();
53 | }
54 |
55 | protected String _getLightTraces() {
56 | return _l.getLightTraces();
57 | }
58 |
59 | public void disableDBlogger() {
60 | _l.disableDBlogger();
61 | }
62 |
63 | public void enableDBlogger() {
64 | _l.enableDBlogger();
65 | }
66 |
67 | public void enableTraces() {
68 | _l.enableTraces();
69 | }
70 |
71 | public void disableTraces() {
72 | _l.disableTraces();
73 | }
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/Introspy-Android Core/src/com/introspy/core/Main.java:
--------------------------------------------------------------------------------
1 | package com.introspy.core;
2 |
3 | import java.lang.reflect.GenericDeclaration;
4 | import java.lang.reflect.Member;
5 | import android.util.Log;
6 |
7 | import com.introspy.hooks.HookList;
8 | import com.introspy.custom_hooks.CustomHookList;
9 | import com.introspy.logging.LoggerConfig;
10 | import com.saurik.substrate.*;
11 |
12 | public class Main {
13 | // static private String _TAG = LoggerConfig.getTag();
14 | static private String _TAG_ERROR = LoggerConfig.getTagError();
15 | static private String _TAG_LOG = LoggerConfig.getTagLog();
16 | static private boolean _debug = false;
17 |
18 | public static void initialize() {
19 | HookConfig[] _config = HookList.getHookList();
20 | HookConfig[] _custom_config = CustomHookList.getHookList();
21 |
22 | initializeConfig(_config);
23 | initializeConfig(_custom_config);
24 |
25 | MS.hookClassLoad("android.app.ContextImpl", new MS.ClassLoadHook() {
26 | public void classLoaded(Class> resources) {
27 | ApplicationState.initApplicationState(resources);
28 | }
29 | });
30 | }
31 |
32 | protected static void initializeConfig(HookConfig[] config) {
33 | for (final HookConfig elemConfig : config) {
34 | if (!elemConfig.isActive())
35 | continue;
36 |
37 | MS.hookClassLoad(elemConfig.getClassName(),
38 | new MS.ClassLoadHook() {
39 | public void classLoaded(Class> resources) {
40 | _hookMethod(resources, elemConfig);
41 | }
42 | });
43 | }
44 | }
45 |
46 | @SuppressWarnings({ "unchecked", "rawtypes" })
47 | protected static void _hookMethod(Class> resources, final HookConfig elemConfig) {
48 |
49 | GenericDeclaration pMethod = null;
50 | final String className = elemConfig.getClassName();
51 | final String methodName = elemConfig.getMethodName();
52 | final Class>[] parameters = elemConfig.getParameters();
53 |
54 | Log.i(_TAG_LOG, "### Hooking: " + className + "->" +
55 | methodName + "() with " +
56 | parameters.length + " args");
57 | try {
58 | // check if the method is a constructor
59 | if (className.substring(className.lastIndexOf('.') + 1).equals(methodName))
60 | pMethod = resources.getDeclaredConstructor(parameters);
61 | else
62 | pMethod = resources.getMethod(methodName, parameters);
63 | }
64 | catch (NoSuchMethodException e) {
65 | Log.w(_TAG_ERROR, "Error - No such method: " + methodName + " with " +
66 | parameters.length + " args");
67 | for (int j = 0; j < parameters.length; j++)
68 | Log.i(_TAG_ERROR, "Arg "+ (j+1) +" type: " + parameters[j]);
69 | return;
70 | }
71 |
72 | final MS.MethodPointer old = new MS.MethodPointer();
73 | MS.hookMethod_(resources, (Member) pMethod,
74 | new MS.MethodHook() {
75 | public Object invoked(final Object resources,
76 | final Object... args) throws Throwable {
77 | if (ApplicationConfig.isEnabled())
78 | _hookMethodImpl(old, resources, elemConfig, args);
79 | return old.invoke(resources, args);
80 | }
81 | }, old);
82 | }
83 |
84 | @SuppressWarnings("rawtypes")
85 | protected static void _hookMethodImpl(final MS.MethodPointer old,
86 | Object resources, final HookConfig elemConfig,
87 | Object... args) {
88 | String packageName = ApplicationConfig.getPackageName();
89 | String dataDir = ApplicationConfig.getDataDir();
90 | String type = elemConfig.getSubType();
91 |
92 | if (packageName != null && dataDir != null &&
93 | LoadConfig.getInstance().initConfig(dataDir) &&
94 | LoadConfig.getInstance().getHookTypes().contains(type)) {
95 | try {
96 | elemConfig.getFunc().init(elemConfig, resources, old, args);
97 |
98 | if (LoadConfig.getInstance().getHookTypes().contains("STACK TRACES"))
99 | elemConfig.getFunc().enableTraces();
100 | else
101 | elemConfig.getFunc().disableTraces();
102 |
103 | if (LoadConfig.getInstance().getHookTypes().contains("NO DB") ||
104 | LoadConfig.getInstance().getHookTypes().contains("SQLite"))
105 | elemConfig.getFunc().disableDBlogger();
106 | else
107 | elemConfig.getFunc().enableDBlogger();
108 |
109 | elemConfig.getFunc().init(elemConfig, resources, old, args);
110 | if (_debug)
111 | Log.i(_TAG_LOG, "=== Calling: " + elemConfig.getMethodName());
112 |
113 | elemConfig.getFunc().execute(args);
114 | } catch (Exception e) {
115 | Log.w(_TAG_ERROR, "-> Error in injected code: [" + e + "]" +
116 | "\nApp: " + ApplicationConfig.getPackageName() +
117 | ", method: " + elemConfig.getMethodName() +
118 | ", class: " + elemConfig.getClassName());
119 | // Log.w(_TAG_ERROR, LoggerErrorHandler._getStackTrace());
120 | }
121 | }
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/Introspy-Android Core/src/com/introspy/core/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Package containing the core functionality of introspy
3 | */
4 | /**
5 | * @author marc blanchou
6 | *
7 | */
8 | package com.introspy.core;
--------------------------------------------------------------------------------
/Introspy-Android Core/src/com/introspy/custom_hooks/CustomHookList.java:
--------------------------------------------------------------------------------
1 | package com.introspy.custom_hooks;
2 |
3 | import android.content.Intent;
4 |
5 | import com.introspy.core.HookConfig;
6 |
7 | public class CustomHookList {
8 |
9 | static public HookConfig[] getHookList() {
10 | return _hookList;
11 | }
12 |
13 | static private HookConfig[] _hookList = new HookConfig[] {
14 |
15 | new HookConfig(false, // set to true to enable the hook
16 | "android.content.ContextWrapper", "startService", new Class>[]{Intent.class},
17 | // class, method name, arguments
18 | new HookExampleImpl(),
19 | // instance of the implementation extending IntroHook (here in HookExampleInpl.java)
20 | "StartService with an intent as argument"),
21 | // notes (optional)
22 | };
23 | }
24 |
25 |
--------------------------------------------------------------------------------
/Introspy-Android Core/src/com/introspy/custom_hooks/HookExampleImpl.java:
--------------------------------------------------------------------------------
1 | package com.introspy.custom_hooks;
2 |
3 | import android.content.Intent;
4 |
5 | import com.introspy.core.IntroHook;
6 |
7 | public class HookExampleImpl extends IntroHook {
8 | @Override
9 | public void execute(Object... args) {
10 | _logBasicInfo();
11 | _logParameter("Intent details", (Intent)args[0]);
12 | _logFlush_I("Example of a CUSTOM HOOK. Notes: " + _notes);
13 | }
14 | }
15 |
16 |
--------------------------------------------------------------------------------
/Introspy-Android Core/src/com/introspy/hooks/HookCryptoImpl.java:
--------------------------------------------------------------------------------
1 | package com.introspy.hooks;
2 | import com.introspy.core.IntroHook;
3 |
4 | import java.security.Key;
5 | import java.util.Stack;
6 |
7 | import javax.crypto.Cipher;
8 |
9 | import android.util.Log;
10 |
11 | import com.introspy.core.ApplicationConfig;
12 |
13 | class Intro_CRYPTO extends IntroHook {
14 | protected static Cipher _lastCipher;
15 | protected static Integer _lastMode;
16 | }
17 |
18 | class Intro_CRYPTO_FINAL_UTIL extends Intro_CRYPTO {
19 | protected static Stack IVList = new Stack();
20 | protected String _out = "";
21 | protected boolean _warning = false;
22 |
23 | protected void _getIV(Cipher cipher) {
24 | if (cipher.getIV() != null) {
25 | String iv = _getReadableByteArr(cipher.getIV());
26 | _out += "; IV: " + iv;
27 | _logParameter("IV", iv);
28 |
29 | if (cipher.getIV()[0] == 0) {
30 | Log.w("Introspy", "!!! IV of 0");
31 | _warning = true;
32 | }
33 | else {
34 | // check if this IV has already been used
35 | if (IVList.contains(cipher.getIV())) {
36 | _out += " - !!! Static IV";
37 | _warning = true;
38 | }
39 | IVList.push(cipher.getIV());
40 | // keep a list of a max of 10 IVs
41 | if (IVList.size() > 10)
42 | IVList.pop();
43 | }
44 | }
45 | }
46 |
47 | protected void _getAlgo(Cipher cipher) {
48 | String algo = cipher.getAlgorithm();
49 | if (algo != null) {
50 | _out = "-> Algo: " + algo;
51 | _logParameter("Algo", algo);
52 | if (cipher.getAlgorithm().contains("ECB")) {
53 | _warning = true;
54 | _out += " - !!! ECB used. ECB mode is broken and should not be used.";
55 | }
56 | }
57 | }
58 | }
59 |
60 | class Intro_CRYPTO_FINAL extends Intro_CRYPTO_FINAL_UTIL {
61 |
62 | private void _getInput(byte[] data) {
63 | if (data != null && data.length != 0) { // when no args to doFinal (used only update())
64 | String i_sdata = null;
65 | i_sdata = new String(data);
66 | if (i_sdata != null && !i_sdata.isEmpty()) {
67 | if (_isItReadable(i_sdata)) {
68 | i_sdata = _byteArrayToReadableStr(data);
69 | _logParameter("input (Encrypt)", i_sdata);
70 | _logLine("-> ENCRYPT: [" + i_sdata + "]");
71 | }
72 | else {
73 | String sdata = _byteArrayToB64(data);
74 | _logLine("-> Input data is not in a readable format, " +
75 | "base64: ["+ sdata +"]");
76 | _logParameter("Output (converted to b64)", sdata);
77 | }
78 | }
79 | }
80 | }
81 |
82 | private void _getOutput(Object... args) {
83 | byte[] data = null;
84 | String o_sdata = null;
85 | // if (cipher == _lastCipher && _lastMode == Cipher.DECRYPT_MODE)
86 | try {
87 | data = (byte[]) _hookInvoke(args);
88 | }
89 | catch (Throwable e) {
90 | Log.i(_TAG_ERROR, "doFinal function failed: "+e);
91 | }
92 | if (data != null) {
93 | o_sdata = new String(data);
94 | if (_isItReadable(o_sdata)) {
95 | o_sdata = _byteArrayToReadableStr(data);
96 | _logParameter("Ouput (Decrypt)", o_sdata);
97 | _logLine("-> DECRYPT: [" + o_sdata + "]");
98 | }
99 | else {
100 | String sdata = _byteArrayToB64(data);
101 | _logLine("-> Output data is not in a readable format," +
102 | " base64: ["+ sdata +"]");
103 | _logReturnValue("Output (converted to b64)", sdata);
104 | }
105 | }
106 | // } else {
107 | // }
108 | }
109 |
110 | public void execute(Object... args) {
111 | if (_resources != null) {
112 | _warning = false;
113 | _out = "";
114 | Cipher cipher = (Cipher) _resources;
115 |
116 | _logBasicInfo();
117 |
118 | // input
119 | if (args.length != 0 && args[0] != null) {
120 | try {
121 | _getInput((byte[]) args[0]);
122 | }
123 | catch (Exception e) {
124 | Log.w(_TAG_ERROR, "Error in _getInput " +
125 | "(CRYPTO_IMPL->dofinal): " + e);
126 | }
127 | }
128 |
129 | //output
130 | try {
131 | _getOutput(args);
132 | }
133 | catch (Exception e) {
134 | Log.w(_TAG_ERROR, "Error in _getOutput " +
135 | "(CRYPTO_IMPL->dofinal): " + e);
136 | }
137 |
138 | // algo/IV
139 | try {
140 | _getAlgo(cipher);
141 | _getIV(cipher);
142 | }
143 | catch (Exception e) {
144 | Log.w(_TAG_ERROR, "Error in _getAlgo/_getCipher " +
145 | "(CRYPTO_IMPL->dofinal): " + e);
146 | }
147 |
148 | // dump some params
149 | if (cipher.getParameters() != null && ApplicationConfig.g_debug)
150 | _logLine("Parameters: " + cipher.getParameters());
151 |
152 | if (_warning)
153 | _logFlush_W(_out);
154 | else if (!_out.isEmpty())
155 | _logFlush_I(_out);
156 | }
157 | else {
158 | Log.w(_TAG_ERROR,
159 | "Error in Intro_CRYPTO: resource is null");
160 | }
161 | }
162 | }
163 |
164 | class Intro_CRYPTO_INIT extends Intro_CRYPTO {
165 | public void execute(Object... args) {
166 | // let's not care about init since we are hooking
167 | // the key class already
168 | // BUT it can be useful to get a state of the mode
169 | // if needed later
170 | if (_resources != null) {
171 | try {
172 | _lastCipher = (Cipher) _resources;
173 |
174 | // get the mode
175 | Integer mode = _lastMode = (Integer) args[0];
176 | String smode;
177 | switch (mode) {
178 | case Cipher.DECRYPT_MODE:
179 | smode = "DECRYPT_MODE";
180 | break;
181 | case Cipher.ENCRYPT_MODE:
182 | smode = "ENCRYPT_MODE";
183 | break;
184 | default:
185 | smode = "???";
186 | }
187 |
188 | _logBasicInfo();
189 |
190 | String out = "-> Mode: " + smode;
191 |
192 | // get the key
193 | Key key = (Key) args[1];
194 | String skey = "";
195 | if (key != null) {
196 | skey = _getReadableByteArr(key.getEncoded());
197 | out += ", Key format: " + key.getFormat() +
198 | ", Key: [" + skey + "]";
199 | }
200 | _logParameter("Mode", smode);
201 | _logParameter("Key", skey);
202 | _logParameter("Key Format", key.getFormat());
203 |
204 | _logFlush_I(out);
205 |
206 | } catch (Exception e) {
207 | Log.w(_TAG_ERROR, "Error in Intro_CRYPTO: " + e);
208 | }
209 | }
210 | }
211 | }
212 |
213 |
--------------------------------------------------------------------------------
/Introspy-Android Core/src/com/introspy/hooks/HookFileSystemImpl.java:
--------------------------------------------------------------------------------
1 | package com.introspy.hooks;
2 | import com.introspy.core.IntroHook;
3 |
4 | import java.io.File;
5 |
6 | import android.util.Log;
7 |
8 |
9 | class Intro_FILE_PARENT extends IntroHook {
10 | // enherited by the FS hook classes
11 | protected boolean is_SD_card(String path) {
12 | if (path != null &&
13 | (path.contains("sdcard") ||
14 | path.contains("/storage/"))) {
15 |
16 | // crashes with system app:
17 | // path.contains(Environment.getExternalStorageDirectory().toString()
18 |
19 | //super.execute(config, resources, old, args);
20 | return true;
21 | }
22 | return false;
23 | }
24 | }
25 |
26 | class Intro_FILE_CHECK_DIR extends Intro_FILE_PARENT {
27 | public void execute(Object... args) {
28 | // noisy so display data only when it reads the sdcard
29 | // arg0 is the path
30 | try {
31 | // String root = _dataDir + "/" + args[0];
32 | String path = "[" + args[0] + "]";
33 | _logParameter("Path", path);
34 | if (is_SD_card(path)) {
35 | _logBasicInfo();
36 | _logFlush_W("Read/write on sdcard: " + path);
37 | } else {
38 | // one liner on this to avoid too much noise
39 | _logFlush_I("### FS:"+ _packageName + ":" + path);
40 | }
41 |
42 | } catch (Exception e) {
43 | Log.w("IntrospyLog", "Exception in Intro_FILE_CHECK_DIR: " + e);
44 | Log.w("IntrospyLog", "-> App path: " + _dataDir +
45 | "\n" + e.fillInStackTrace());
46 | }
47 | }
48 | }
49 |
50 | class Intro_CHECK_FS_SET extends Intro_FILE_PARENT {
51 | public void execute(Object... args) {
52 | // noisy
53 | // arg0 is the path
54 | if ((Boolean)(args[0]) == true &&
55 | (Boolean)args[1] == false) {
56 | //super.execute(config, resources, old, args);
57 | File f = (File) _resources;
58 | _logBasicInfo();
59 | _logParameter("Mode", "WORLD read/write");
60 | _logParameter("Path", f.getAbsolutePath());
61 | _logFlush_W("Writing file with WORLD read/write mode: " +
62 | " in " + f.getAbsolutePath());
63 | }
64 | }
65 | }
66 |
67 | class Intro_FILE_CHECK_MODE extends Intro_FILE_PARENT {
68 | @SuppressWarnings("deprecation")
69 | public void execute(Object... args) {
70 | // arg0 is the path
71 | String path = ": [" + _dataDir + "/" + (String)args[0] + "]";
72 | if (is_SD_card(path)) {
73 | _logBasicInfo();
74 | _logParameter("Path", path);
75 | _logFlush_W("Read/write on sdcard: " + path);
76 | }
77 | else {
78 | // arg1 is the mode
79 | Integer mode = (Integer) args[1];
80 |
81 | String smode;
82 | switch (mode) {
83 | case android.content.Context.MODE_PRIVATE:
84 | smode = "Private";
85 | break;
86 | case android.content.Context.MODE_WORLD_READABLE:
87 | smode = "!!! World Readable !!!";
88 | break;
89 | case android.content.Context.MODE_WORLD_WRITEABLE:
90 | smode = "!!! World Writable !!!";
91 | break;
92 | default:
93 | smode = "???";
94 | }
95 | smode = "MODE: " + smode;
96 |
97 | if (mode == android.content.Context.MODE_WORLD_READABLE ||
98 | mode == android.content.Context.MODE_WORLD_WRITEABLE) {
99 | _logBasicInfo();
100 | _logParameter("Mode", smode);
101 | _logFlush_W("Writing file with dangerous mode: " +
102 | smode + " in " + path);
103 | }
104 | }
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/Introspy-Android Core/src/com/introspy/hooks/HookGeneric.java:
--------------------------------------------------------------------------------
1 | package com.introspy.hooks;
2 |
3 | import android.util.Log;
4 |
5 | import com.introspy.core.IntroHook;
6 |
7 | class Intro_DUMP extends IntroHook {
8 | protected void _WDump() {
9 | // dump in W logs
10 | Log.w(_TAG, "#### "+ _type +" ###, Package: " + _packageName +
11 | ", localdir: "+_dataDir);
12 | Log.w(_TAG, "Method: " + _config.getMethodName());
13 | Log.w(_TAG, "Notes: " + _config.getNotes());
14 | Log.w(_TAG, "Resources type: " +
15 | (_resources != null ? _resources.getClass() : "None (Static method?)"));
16 |
17 | }
18 |
19 | public void execute(Object... args) {
20 | super.execute(args);
21 | // dump a bit more info
22 | // _WDump();
23 | Log.i(_TAG, "------------------");
24 | }
25 | }
26 |
27 | class Intro_SHOULD_NOT_BE_USED extends Intro_DUMP {
28 | public void execute(Object... args) {
29 | _logBasicInfo();
30 | _logFlush_W("This method should not be used." +
31 | (!_notes.isEmpty() ? (" Note: " + _notes) : ""));
32 | }
33 | }
--------------------------------------------------------------------------------
/Introspy-Android Core/src/com/introspy/hooks/HookHashImpl.java:
--------------------------------------------------------------------------------
1 | package com.introspy.hooks;
2 | import com.introspy.core.IntroHook;
3 |
4 | import java.lang.reflect.Method;
5 | import java.security.MessageDigest;
6 |
7 | import android.util.Log;
8 |
9 |
10 | class Intro_HASH extends IntroHook {
11 | public void execute(Object... args) {
12 |
13 | StackTraceElement[] ste = Thread.currentThread().getStackTrace();
14 | // this is called within apps and is super noisy so not displaying it
15 | if (ste[7].toString().contains("com.crashlytics."))
16 | return;
17 | // the above code may only work on android 4.2.2
18 | // replace with the code below if so
19 | /* for (int i = 0; i < ste.length; i++)
20 | if (ste[i].toString().contains("com.crashlytics."))
21 | return; */
22 |
23 | if (args[0] != null) {
24 | _logBasicInfo();
25 | String input = _getReadableByteArr((byte[])args[0]);
26 |
27 | byte[] output = null;
28 | String s_output = "";
29 | try {
30 | // execution the method to calculate the digest
31 | // using reflection to call digest from the object's instance
32 | try {
33 | Class> cls = Class.forName("java.security.MessageDigest");
34 | Object obj =_resources;
35 | Class> noparams[] = {};
36 | Method xmethod = cls.getDeclaredMethod("digest", noparams);
37 | output = (byte[]) xmethod.invoke(obj);
38 | s_output = _getReadableByteArr(output);
39 | }
40 | catch (Exception e) {
41 | Log.w(_TAG_ERROR, "Error in Hash func: " + e);
42 | }
43 | }
44 | catch (Throwable e) {
45 | Log.w(_TAG_ERROR, "Error in Hash func: " + e);
46 | }
47 |
48 | // use reflection to call a method from this instance
49 | String algoName = null;
50 | try {
51 | Class> cls = Class.forName("java.security.MessageDigest");
52 | Object obj =_resources;
53 | Class> noparams[] = {};
54 | Method xmethod = cls.getDeclaredMethod("getAlgorithm", noparams);
55 | algoName = (String) xmethod.invoke(obj);
56 | }
57 | catch (Exception e) {
58 | algoName = "error: " + e;
59 | }
60 |
61 | _logLine("-> Hash of : [" + input + "] is: [" +
62 | s_output +"] , Algo: [" + algoName + "]");
63 |
64 | _logParameter("Input", input);
65 | _logParameter("Algo", algoName);
66 | _logReturnValue("Output", s_output);
67 |
68 | if (algoName.contains("MD5")) {
69 | _logFlush_W("MD5 used, this hashing algo " +
70 | "is broken and should not be used");
71 | }
72 | else
73 | _logFlush_I();
74 | }
75 | }
76 | }
77 |
78 | class Intro_GET_HASH extends Intro_CRYPTO {
79 | public void execute(Object... args) {
80 | try {
81 | byte[] data = (byte[]) _hookInvoke(args);
82 | MessageDigest dg = (MessageDigest) _resources;
83 | _logBasicInfo();
84 |
85 | String sdata = _getReadableByteArr(data);
86 |
87 | _logReturnValue("Data", sdata);
88 | _logParameter("Algo", dg);
89 |
90 | _logFlush_I("-> Algo: " + dg + ", Data: " + sdata);
91 | } catch (Throwable e) {
92 | Log.i(_TAG_ERROR, "Error in Fun_GET_HASH" + e);
93 | }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/Introspy-Android Core/src/com/introspy/hooks/HookIPCImpl.java:
--------------------------------------------------------------------------------
1 | package com.introspy.hooks;
2 | import com.introspy.core.IntroHook;
3 |
4 | import android.content.Intent;
5 | import android.content.IntentFilter;
6 | import android.os.Bundle;
7 |
8 | class FuncIPC extends IntroHook {
9 | protected String getExtras(Intent intent) {
10 | String out = "";
11 | try {
12 | Bundle bundle = intent.getExtras();
13 | if (bundle != null) {
14 | for (String key : bundle.keySet()) {
15 | Object value = bundle.get(key);
16 | out += String.format("--> [%s %s (%s)]\n", key,
17 | value.toString(), value.getClass().getName());
18 | }
19 | out = out.substring(0, out.length() - 1);
20 | }
21 | }
22 | catch (Exception e) {
23 | out = "Cannot get intent extra";
24 | }
25 | return out;
26 | }
27 | }
28 |
29 | class Intro_DUMP_INTENT extends FuncIPC {
30 | public void execute(Object... args) {
31 | _logBasicInfo();
32 |
33 | // arg0 is an Intent
34 | Intent intent = (Intent) args[0];
35 | String out = "-> " + intent;
36 | _logParameter("Intent", intent);
37 | String extra = getExtras(intent);
38 | if (!extra.isEmpty()) {
39 | _logParameter("Extra", extra);
40 | out += "\n-> Extra: \n" + extra + "";
41 | }
42 | _logFlush_I(out);
43 | }
44 | }
45 |
46 | // Hook:
47 | // Intent registerReceiver (BroadcastReceiver receiver, IntentFilter filter)
48 | // Intent registerReceiver (BroadcastReceiver receiver, IntentFilter filter,
49 | // String broadcastPermission, Handler scheduler)
50 | class Intro_IPC_RECEIVER extends FuncIPC {
51 | public void execute(Object... args) {
52 | _logBasicInfo();
53 | String out = "";
54 |
55 | // arg1 is an intent filter
56 | IntentFilter intentFilter = (IntentFilter) args[1];
57 | if (intentFilter != null) {
58 | out = "-> Intent Filter: \n";
59 | for (int i = 0; i < intentFilter.countActions(); i++)
60 | out += "--> [Action "+ i +":"+intentFilter.getAction(i)+"]\n";
61 | out = out.substring(0, out.length() - 1);
62 | _logParameter("Intent Filter", out);
63 | }
64 |
65 | // args[2] is the permissions
66 | if (args.length > 2 && args[2] != null) {
67 | out += ", permissions: " + args[2];
68 | _logParameter("Permissions", args[2]);
69 | }
70 | _logLine(out);
71 |
72 | if (args.length == 2 || (args.length > 2 && args[2] == null))
73 | _logFlush_I("-> No permissions explicitely defined for the Receiver");
74 | else
75 | _logFlush_I();
76 | }
77 | }
78 |
79 | class Intro_URI_REGISTER extends FuncIPC {
80 | public void execute(Object... args) {
81 | String uriPath = (String)args[1];
82 | _logParameter("URI Path", uriPath);
83 |
84 | String data = "URI:"+_config.getMethodName()+":"
85 | +_packageName+uriPath;
86 | _logBasicInfo();
87 | _logFlush_I(data);
88 | }
89 | }
90 |
91 | // IPCs disabled in the manifest can be enabled dynamically
92 | class Intro_IPC_MODIFIED extends FuncIPC {
93 | public void execute(Object... args) {
94 |
95 | // arg1: newState
96 | int newState = (Integer)args[1];
97 | if (newState ==
98 | android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
99 | _logBasicInfo();
100 | _logParameter("New State", "COMPONENT_ENABLED_STATE_ENABLED");
101 | _logFlush_W("-> !!! Component ["+ args[0] +
102 | "] is ENABLED dynamically");
103 | }
104 | }
105 | }
106 |
107 |
--------------------------------------------------------------------------------
/Introspy-Android Core/src/com/introspy/hooks/HookKeyImpl.java:
--------------------------------------------------------------------------------
1 | package com.introspy.hooks;
2 |
3 | class Intro_CRYPTO_KEY extends Intro_CRYPTO {
4 |
5 | }
6 |
7 | class Intro_GET_KEY extends Intro_CRYPTO_KEY {
8 | public void execute(Object... args) {
9 | byte[] key = (byte[]) args[0];
10 | if (key != null) {
11 | String skey = _getReadableByteArr(key);
12 | _logParameter("Key", skey);
13 | _logParameter("Algo", args[1]);
14 | _logBasicInfo();
15 | _logFlush_I("-> Key: ["+skey+"], algo: "+args[1]);
16 | }
17 | }
18 | }
19 |
20 | class Intro_CRYPTO_KEYSTORE_HOSTNAME extends Intro_CRYPTO_KEY {
21 | public void execute(Object... args) {
22 |
23 | _logBasicInfo();
24 | // arg2 is the passcode for this trustore
25 | if (args[2] != null) {
26 | String passcode =
27 | _getReadableByteArr((byte[]) args[2]);
28 | _logParameter("Passcode", args[2]);
29 | _logFlush_I("-> TrustStore passcode: " + passcode);
30 | }
31 | }
32 | }
33 |
34 | class Intro_CRYPTO_KEYSTORE extends Intro_CRYPTO_KEY {
35 | public void execute(Object... args) {
36 |
37 | _logBasicInfo();
38 | // arg1 is the passcode for the trustore
39 | if (args[1] != null) {
40 | String passcode =
41 | _getReadableByteArr((byte[]) args[1]);
42 | _logParameter("Passcode", args[1]);
43 | _logFlush_I("-> TrustStore passcode: " + passcode);
44 | }
45 | }
46 | }
47 |
48 | class Intro_CRYPTO_PBEKEY extends Intro_CRYPTO_KEY {
49 | public void execute(Object... args) {
50 | _logBasicInfo();
51 |
52 | String passcode = new String((char[])args[0]);
53 | String salt = null;
54 | int iterationCount = -1;
55 | if (args.length >= 2 && args[1] != null) {
56 | salt =
57 | _byteArrayToReadableStr((byte[])args[1]);
58 | iterationCount = (Integer)args[2];
59 | _logParameter("Passcode", passcode);
60 | _logParameter("Salt", salt);
61 | _logParameter("Iterations", iterationCount);
62 | // _logReturnValue("Key", _hookInvoke(args));
63 | _logFlush_I("-> Passcode: [" + passcode + "], Salt: [" + salt +
64 | "], iterations: " + iterationCount + "");
65 | }
66 | else {
67 | _logParameter("Passcode", passcode);
68 | _logFlush_I("Passcode: [" + passcode + "]");
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/Introspy-Android Core/src/com/introspy/hooks/HookList.java:
--------------------------------------------------------------------------------
1 | package com.introspy.hooks;
2 |
3 | import java.io.File;
4 | import java.net.InetAddress;
5 | import java.net.Socket;
6 | import java.net.URI;
7 | import java.nio.ByteBuffer;
8 | import java.security.AlgorithmParameters;
9 | import java.security.Key;
10 | import java.security.KeyStore;
11 | import java.security.SecureRandom;
12 | import java.security.spec.AlgorithmParameterSpec;
13 | import java.util.Set;
14 |
15 | import javax.net.ssl.HostnameVerifier;
16 | import javax.net.ssl.KeyManager;
17 | import javax.net.ssl.TrustManager;
18 |
19 | import org.apache.http.conn.scheme.HostNameResolver;
20 | import org.apache.http.conn.ssl.X509HostnameVerifier;
21 | import org.apache.http.params.HttpParams;
22 |
23 | import com.introspy.core.HookConfig;
24 |
25 | import android.content.BroadcastReceiver;
26 | import android.content.ComponentName;
27 | import android.content.ContentValues;
28 | import android.content.Intent;
29 | import android.content.IntentFilter;
30 | import android.net.Uri;
31 | import android.os.Bundle;
32 | import android.os.Handler;
33 |
34 | public class HookList {
35 |
36 | static public HookConfig[] getHookList() {
37 | return _hookList;
38 | }
39 |
40 | static private HookConfig[] _hookList = new HookConfig[] {
41 |
42 | /* ############################################
43 | * ############### Crypto Methods
44 | */
45 | new HookConfig(false, "CRYPTO", "GENERAL CRYPTO", "javax.crypto.Cipher", "getInstance",
46 | new Intro_CRYPTO(), new Class>[]{String.class}, ""),
47 |
48 | new HookConfig(false, "CRYPTO", "GENERAL CRYPTO", "javax.crypto.Cipher", "update",
49 | new Intro_CRYPTO(), new Class>[]{byte[].class},
50 | "Continues a multi-part transformation (encryption or decryption)"),
51 |
52 | new HookConfig(false, "CRYPTO", "GENERAL CRYPTO", "javax.crypto.Cipher", "update",
53 | new Intro_CRYPTO(), new Class>[]{byte[].class},
54 | "Continues a multi-part transformation (encryption or decryption)"),
55 |
56 | new HookConfig(false, "CRYPTO", "GENERAL CRYPTO", "java.util.Random", "Random",
57 | new Intro_SHOULD_NOT_BE_USED(), new Class>[]{}, "Weak RNG"),
58 |
59 | // doesn't need to hook that if you hook doFinal as the hook calls it already
60 | new HookConfig(false, "CRYPTO", "GENERAL CRYPTO", "javax.crypto.Cipher", "getIV",
61 | new Intro_CRYPTO(), new Class>[]{}, ""),
62 |
63 | // this gives the hash algo + hash
64 | // need to hook "update" to get what is hashed
65 | // we can also retrieve this info by hooking 'update'
66 | // so I'm disabling it
67 | new HookConfig(false, "CRYPTO", "HASH", "java.security.MessageDigest", "digest",
68 | new Intro_GET_HASH(), new Class>[]{}, ""),
69 |
70 | // ############ doFinal
71 | new HookConfig(true, "CRYPTO", "GENERAL CRYPTO", "javax.crypto.Cipher", "doFinal",
72 | new Intro_CRYPTO_FINAL(), new Class>[]{},
73 | "Finishes a multi-part transformation (encryption or decryption)"),
74 |
75 | // method doesn't seem to exist in Android < 4
76 | new HookConfig(false, "CRYPTO", "GENERAL CRYPTO", "javax.crypto.Cipher", "doFinal",
77 | new Intro_CRYPTO_FINAL(), new Class>[]{byte[].class, Integer.TYPE},
78 | "Finishes a multi-part transformation (encryption or decryption)"),
79 |
80 | new HookConfig(true, "CRYPTO", "GENERAL CRYPTO", "javax.crypto.Cipher", "doFinal",
81 | new Intro_CRYPTO_FINAL(), new Class>[]{byte[].class, Integer.TYPE, Integer.TYPE},
82 | "Finishes a multi-part transformation (encryption or decryption)"),
83 |
84 | new HookConfig(true, "CRYPTO", "GENERAL CRYPTO", "javax.crypto.Cipher", "doFinal",
85 | new Intro_CRYPTO_FINAL(), new Class>[]{byte[].class, Integer.TYPE, Integer.TYPE,
86 | byte[].class, Integer.TYPE},
87 | "Finishes a multi-part transformation (encryption or decryption)"),
88 |
89 | new HookConfig(true, "CRYPTO", "GENERAL CRYPTO", "javax.crypto.Cipher", "doFinal",
90 | new Intro_CRYPTO_FINAL(), new Class>[]{byte[].class, Integer.TYPE,
91 | Integer.TYPE, byte[].class},
92 | "Finishes a multi-part transformation (encryption or decryption)"),
93 |
94 | new HookConfig(true, "CRYPTO", "GENERAL CRYPTO", "javax.crypto.Cipher", "doFinal",
95 | new Intro_CRYPTO_FINAL(), new Class>[]{ByteBuffer.class, ByteBuffer.class},
96 | "Finishes a multi-part transformation (encryption or decryption)"),
97 |
98 | new HookConfig(true, "CRYPTO", "GENERAL CRYPTO", "javax.crypto.Cipher", "doFinal",
99 | new Intro_CRYPTO_FINAL(), new Class>[]{byte[].class},
100 | "Finishes a multi-part transformation (encryption or decryption)"),
101 |
102 | // ########### Key related functions
103 |
104 | new HookConfig(true, "CRYPTO", "KEY", "javax.crypto.spec.PBEKeySpec", "PBEKeySpec",
105 | new Intro_CRYPTO_PBEKEY(), new Class>[]{char[].class, byte[].class, Integer.TYPE, Integer.TYPE},
106 | "Create a key with: pwd, salt, iterations, keylength"),
107 |
108 | new HookConfig(true, "CRYPTO", "KEY", "javax.crypto.spec.PBEKeySpec", "PBEKeySpec",
109 | new Intro_CRYPTO_PBEKEY(), new Class>[]{char[].class, byte[].class, Integer.TYPE},
110 | "Create a key with: pwd, salt, iterations"),
111 |
112 | new HookConfig(true, "CRYPTO", "KEY", "javax.crypto.spec.PBEKeySpec", "PBEKeySpec",
113 | new Intro_CRYPTO_PBEKEY(), new Class>[]{char[].class},
114 | "Create a key with: pwd"),
115 |
116 | new HookConfig(true, "CRYPTO", "KEY", "javax.crypto.spec.SecretKeySpec", "SecretKeySpec",
117 | new Intro_GET_KEY(), new Class>[]{byte[].class, String.class}, ""),
118 |
119 | // keystore - get the cert and the .p12 passcode
120 | new HookConfig(true, "CRYPTO", "KEY", "org.apache.http.conn.ssl.SSLSocketFactory", "SSLSocketFactory",
121 | new Intro_CRYPTO_KEYSTORE_HOSTNAME(), new Class>[]{String.class, KeyStore.class, String.class, KeyStore.class,
122 | SecureRandom.class, HostNameResolver.class}, ""),
123 | new HookConfig(true, "CRYPTO", "KEY", "org.apache.http.conn.ssl.SSLSocketFactory", "SSLSocketFactory",
124 | new Intro_CRYPTO_KEYSTORE(), new Class>[]{KeyStore.class, String.class, KeyStore.class}, ""),
125 | new HookConfig(true, "CRYPTO", "KEY", "org.apache.http.conn.ssl.SSLSocketFactory", "SSLSocketFactory",
126 | new Intro_CRYPTO_KEYSTORE(), new Class>[]{KeyStore.class, String.class}, ""),
127 | //new HookConfig(true, "CRYPTO", "GENERAL CRYPTO", "org.apache.http.conn.ssl.SSLSocketFactory", "SSLSocketFactory",
128 | // new Intro_CRYPTO_KEYSTORE(), new Class>[]{KeyStore.class}, ""),
129 |
130 | // ############ digest (hash function)
131 |
132 | // may not need to call the digest methods when update is hooked (?)
133 | new HookConfig(false, "CRYPTO", "HASH", "java.security.MessageDigest", "digest",
134 | new Intro_HASH(), new Class>[]{byte[].class, Integer.TYPE, Integer.TYPE},
135 | "Performs the final update and then computes and returns the final hash value"),
136 | new HookConfig(false, "CRYPTO", "HASH", "java.security.MessageDigest", "digest",
137 | new Intro_HASH(), new Class>[]{byte[].class},
138 | "Performs the final update and then computes and returns the final hash value"),
139 | new HookConfig(true, "CRYPTO", "HASH", "java.security.MessageDigest", "update",
140 | new Intro_HASH(), new Class>[]{byte[].class},
141 | "Uses a one-way hash function to turn an arbitrary number of " +
142 | "bytes into a fixed-length byte sequence."),
143 | new HookConfig(true, "CRYPTO", "HASH", "java.security.MessageDigest", "update",
144 | new Intro_HASH(), new Class>[]{byte[].class, Integer.TYPE, Integer.TYPE},
145 | "Uses a one-way hash function to turn an arbitrary number of " +
146 | "bytes into a fixed-length byte sequence."),
147 | new HookConfig(true, "CRYPTO", "HASH", "java.security.MessageDigest", "update",
148 | new Intro_HASH(), new Class>[]{ByteBuffer.class},
149 | "Uses a one-way hash function to turn an arbitrary number of " +
150 | "bytes into a fixed-length byte sequence."),
151 |
152 | // ############ init
153 | new HookConfig(true, "CRYPTO", "GENERAL CRYPTO", "javax.crypto.Cipher", "init",
154 | new Intro_CRYPTO_INIT(), new Class>[]{Integer.TYPE, Key.class},
155 | "Initializes this cipher instance with the specified key"),
156 | new HookConfig(true, "CRYPTO", "GENERAL CRYPTO", "javax.crypto.Cipher", "init",
157 | new Intro_CRYPTO_INIT(), new Class>[]{Integer.TYPE, Key.class, SecureRandom.class},
158 | "Initializes this cipher instance with the specified key"),
159 | new HookConfig(true, "CRYPTO", "GENERAL CRYPTO", "javax.crypto.Cipher", "init",
160 | new Intro_CRYPTO_INIT(), new Class>[]{Integer.TYPE, Key.class, AlgorithmParameterSpec.class, SecureRandom.class},
161 | "Initializes this cipher instance with the specified key"),
162 | new HookConfig(true, "CRYPTO", "GENERAL CRYPTO", "javax.crypto.Cipher", "init",
163 | new Intro_CRYPTO_INIT(), new Class>[]{Integer.TYPE, Key.class, AlgorithmParameters.class, SecureRandom.class},
164 | "Initializes this cipher instance with the specified key"),
165 | new HookConfig(true, "CRYPTO", "GENERAL CRYPTO", "javax.crypto.Cipher", "init",
166 | new Intro_CRYPTO_INIT(), new Class>[]{Integer.TYPE, Key.class, AlgorithmParameters.class},
167 | "Initializes this cipher instance with the specified key"),
168 | new HookConfig(true, "CRYPTO", "GENERAL CRYPTO", "javax.crypto.Cipher", "init",
169 | new Intro_CRYPTO_INIT(), new Class>[]{Integer.TYPE, Key.class, AlgorithmParameterSpec.class},
170 | "Initializes this cipher instance with the specified key"),
171 |
172 | // TODO: 3 init calls missing (with certificates)
173 |
174 | /* ############################################
175 | * ############### File System Methods
176 | */
177 | new HookConfig(true, "STORAGE", "FS", "java.io.FileOutputStream", "FileOutputStream",
178 | new Intro_FILE_CHECK_DIR(), new Class>[]{File.class}, ""),
179 | // crashes
180 | new HookConfig(false, "STORAGE", "FS", "java.io.File", "File",
181 | new Intro_FILE_CHECK_DIR(), new Class>[]{String.class}, ""),
182 | // does not always work when other FS APIs are hooked (crashes)
183 | new HookConfig(false, "STORAGE", "FS", "java.io.File", "File",
184 | new Intro_FILE_CHECK_DIR(), new Class>[]{String.class, String.class}, ""),
185 | // URI
186 | new HookConfig(true, "STORAGE", "FS", "java.io.File", "File",
187 | new Intro_DUMP(), new Class>[]{URI.class}, ""),
188 |
189 | // bad: true, false: (boolean readable, boolean ownerOnly)
190 | new HookConfig(true, "STORAGE", "FS", "java.io.File", "setReadable",
191 | new Intro_CHECK_FS_SET(), new Class>[]{Boolean.TYPE, Boolean.TYPE},
192 | "Manipulates the read permissions for the abstract path designated by this file."),
193 | new HookConfig(true, "STORAGE", "FS", "java.io.File", "setWritable",
194 | new Intro_CHECK_FS_SET(), new Class>[]{Boolean.TYPE, Boolean.TYPE},
195 | "Manipulates the read permissions for the abstract path designated by this file."),
196 | new HookConfig(true, "STORAGE", "FS", "java.io.File", "setExecutable",
197 | new Intro_CHECK_FS_SET(), new Class>[]{Boolean.TYPE, Boolean.TYPE},
198 | "Manipulates the read permissions for the abstract path designated by this file."),
199 |
200 | // secu, world/readable
201 | new HookConfig(true, "STORAGE", "FS", "android.content.ContextWrapper", "openFileOutput",
202 | new Intro_FILE_CHECK_MODE(), new Class>[]{String.class, Integer.TYPE}, ""),
203 |
204 | /* ############################################
205 | * ############### IPC Methods
206 | */
207 |
208 | new HookConfig(true, "IPC", "IPC", "android.content.ContextWrapper", "startService",
209 | new Intro_DUMP_INTENT(), new Class>[]{Intent.class}, ""),
210 | new HookConfig(false, "IPC", "IPC", "android.content.ContextWrapper", "startActivities",
211 | new Intro_DUMP_INTENT(), new Class>[]{Intent[].class}, ""),
212 |
213 | // Android > 4.1
214 | new HookConfig(false, "IPC", "IPC", "android.content.ContextWrapper", "startActivity",
215 | new Intro_DUMP_INTENT(), new Class>[]{Intent.class, Bundle.class}, ""),
216 |
217 | new HookConfig(true, "IPC", "IPC", "android.content.ContextWrapper", "startActivity",
218 | new Intro_DUMP_INTENT(), new Class>[]{Intent.class, Bundle.class}, ""),
219 | new HookConfig(true, "IPC", "IPC", "android.content.ContextWrapper", "startActivity",
220 | new Intro_DUMP_INTENT(), new Class>[]{Intent.class}, ""),
221 |
222 | new HookConfig(true, "IPC", "IPC", "android.content.ContextWrapper", "startActivity",
223 | new Intro_DUMP_INTENT(), new Class>[]{Intent.class}, ""),
224 |
225 | new HookConfig(true, "IPC", "IPC", "android.content.ContextWrapper", "sendBroadcast",
226 | new Intro_DUMP_INTENT(), new Class>[]{Intent.class}, ""),
227 | new HookConfig(true, "IPC", "IPC", "android.content.ContextWrapper", "sendBroadcast",
228 | new Intro_DUMP(), new Class>[]{Intent.class, String.class}, ""),
229 |
230 | new HookConfig(true, "IPC", "IPC", "android.content.ContextWrapper", "registerReceiver",
231 | new Intro_IPC_RECEIVER(), new Class>[]{BroadcastReceiver.class, IntentFilter.class}, ""),
232 | new HookConfig(true, "IPC", "IPC", "android.content.ContextWrapper", "registerReceiver",
233 | new Intro_IPC_RECEIVER(), new Class>[]{BroadcastReceiver.class, IntentFilter.class,
234 | String.class, Handler.class}, ""),
235 | // TODO: more IPCs to hook here, sendBroadcast(s) for instance
236 |
237 |
238 | // useful to find dynamically enabled IPCs
239 | // crashes the zygote process on 4.2.2 (?)
240 | new HookConfig(false, "IPC", "IPC", "android.content.pm.PackageManager", "setComponentEnabledSetting",
241 | new Intro_IPC_MODIFIED(), new Class>[]{ComponentName.class, Integer.TYPE, Integer.TYPE}, ""),
242 |
243 | /* ############################################
244 | * ############### Shared Preference Methods
245 | */
246 | new HookConfig(true, "STORAGE", "PREF", "android.content.ContextWrapper", "getSharedPreferences",
247 | new Intro_CHECK_SHARED_PREF(), new Class>[]{String.class, Integer.TYPE},
248 | "Used to get shared preferences"),
249 |
250 | // Get shared preference methods
251 | new HookConfig(true, "STORAGE", "PREF", "android.app.SharedPreferencesImpl", "getString",
252 | new Intro_GET_SHARED_PREF(), new Class>[]{String.class, String.class},
253 | "Checks whether the preferences contains a preference"),
254 | new HookConfig(true, "STORAGE", "PREF", "android.app.SharedPreferencesImpl", "getStringSet",
255 | new Intro_GET_SHARED_PREF(), new Class>[]{String.class, Set.class},
256 | "Checks whether the preferences contains a preference"),
257 | new HookConfig(true, "STORAGE", "PREF", "android.app.SharedPreferencesImpl", "getAll",
258 | new Intro_GET_ALL_SHARED_PREF(), new Class>[]{},
259 | "Checks whether the preferences contains a preference"),
260 | new HookConfig(true, "STORAGE", "PREF", "android.app.SharedPreferencesImpl", "getBoolean",
261 | new Intro_GET_SHARED_PREF(), new Class>[]{String.class, Boolean.TYPE},
262 | "Checks whether the preferences contains a preference"),
263 | new HookConfig(true, "STORAGE", "PREF", "android.app.SharedPreferencesImpl", "getFloat",
264 | new Intro_GET_SHARED_PREF(), new Class>[]{String.class, Float.TYPE},
265 | "Checks whether the preferences contains a preference"),
266 | new HookConfig(true, "STORAGE", "PREF", "android.app.SharedPreferencesImpl", "getInt",
267 | new Intro_GET_SHARED_PREF(), new Class>[]{String.class, Integer.TYPE},
268 | "Checks whether the preferences contains a preference"),
269 | new HookConfig(true, "STORAGE", "PREF", "android.app.SharedPreferencesImpl", "getLong",
270 | new Intro_GET_SHARED_PREF(), new Class>[]{String.class, Long.TYPE},
271 | "Checks whether the preferences contains a preference"),
272 | new HookConfig(true, "STORAGE", "PREF", "android.app.SharedPreferencesImpl", "contains",
273 | new Intro_CONTAINS_SHARED_PREF(), new Class>[]{String.class},
274 | "Checks whether the preferences contains a preference"),
275 |
276 | new HookConfig(true, "STORAGE", "PREF", "android.content.SharedPreferences.Editor", "putString",
277 | new Intro_PUT_SHARED_PREF(), new Class>[]{String.class, String.class},
278 | "Set a String value in the preferences editor, to be written back once"),
279 | new HookConfig(true, "STORAGE", "PREF", "android.content.SharedPreferences.Editor", "putBoolean",
280 | new Intro_PUT_SHARED_PREF(), new Class>[]{String.class, Boolean.TYPE},
281 | "Set a bool value in the preferences editor, to be written back once"),
282 | new HookConfig(true, "STORAGE", "PREF", "android.content.SharedPreferences.Editor", "putInt",
283 | new Intro_PUT_SHARED_PREF(), new Class>[]{String.class, Integer.TYPE},
284 | "Set an Int value in the preferences editor, to be written back once"),
285 |
286 | new HookConfig(false, "STORAGE", "PREF", "android.content.SharedPreferences.Editor", "commit",
287 | new Intro_DUMP(), new Class>[]{},
288 | "Used to commit shared preferences"),
289 |
290 | /* ############################################
291 | * ############### URI Methods
292 | */
293 | new HookConfig(true, "IPC", "URI", "android.content.ContextWrapper", "grantUriPermission",
294 | new Intro_DUMP(), new Class>[]{String.class, Uri.class, Integer.TYPE},
295 | "Grant permission to access a specific Uri to another package"),
296 |
297 | // used to register URI for a content provider
298 | new HookConfig(false, "IPC", "URI", "android.content.UriMatcher", "addURI",
299 | new Intro_URI_REGISTER(), new Class>[]{String.class, String.class, Integer.TYPE},
300 | "Add a URI to match, and the code to return when this URI is matched."),
301 |
302 | /* ############################################
303 | * ############### SSL Methods
304 | */
305 | new HookConfig(true, "CRYPTO", "SSL", "org.apache.http.conn.ssl.SSLSocketFactory", "connectSocket",
306 | new Intro_DUMP(), new Class>[]{Socket.class, String.class, Integer.TYPE,
307 | InetAddress.class, Integer.TYPE, HttpParams.class},
308 | "Connects a socket to the given host (uses SSL)"),
309 |
310 | new HookConfig(true, "CRYPTO", "SSL", "org.apache.http.conn.ssl.SocketFactory", "connectSocket",
311 | new Intro_DUMP(), new Class>[]{Socket.class, String.class, Integer.TYPE,
312 | InetAddress.class, Integer.TYPE, HttpParams.class},
313 | "Connects a socket to the given host"),
314 |
315 | new HookConfig(true, "CRYPTO", "SSL", "org.apache.http.client.methods.HttpGet", "HttpGet",
316 | new Intro_CHECK_URI(), new Class>[]{String.class}, "HTTP GET"),
317 | //new HookConfig(false, "CRYPTO", "SSL", "org.apache.http.client.methods.HttpGet", "HttpGet",
318 | // new Intro_DUMP(), new Class>[]{URI.class}, "HTTP GET"),
319 | //new HookConfig(false, "CRYPTO", "SSL", "org.apache.http.client.methods.HttpPost", "HttpPost",
320 | // new Intro_DUMP(), new Class>[]{URI.class}, "HTTP POST"),
321 | new HookConfig(true, "CRYPTO", "SSL", "org.apache.http.client.methods.HttpPost", "HttpPost",
322 | new Intro_CHECK_URI(), new Class>[]{String.class}, "HTTP POST"),
323 |
324 | // Hooks methods used to do cert pinning or remove cert validation
325 | new HookConfig(true, "CRYPTO", "SSL", "javax.net.ssl.SSLContext", "init",
326 | new Intro_SSL_CHECK_TRUST_MANAGER(), new Class>[]{KeyManager[].class,
327 | TrustManager[].class, SecureRandom.class}, ""),
328 |
329 | new HookConfig(true, "CRYPTO", "SSL", "javax.net.ssl.HttpsURLConnection", "setSSLSocketFactory",
330 | new Intro_SSL_CHECK_TRUST_SOCKETFACTORY(), new Class>[]{javax.net.ssl.SSLSocketFactory.class}, ""),
331 |
332 | // these methods can be used to allow all hostnames to be used
333 | new HookConfig(true, "CRYPTO", "SSL", "org.apache.http.conn.ssl.SSLSocketFactory", "setHostnameVerifier",
334 | new Intro_CHECK_HOSTNAME_VERIFIER(), new Class>[]{X509HostnameVerifier.class}, ""),
335 |
336 | new HookConfig(true, "CRYPTO", "SSL", "javax.net.ssl.HttpsURLConnection", "setDefaultHostnameVerifier",
337 | new Intro_CHECK_HOSTNAME_VERIFIER(), new Class>[]{HostnameVerifier.class}, ""),
338 |
339 | /* ############################################
340 | * ############### WEBVIEW Methods
341 | */
342 | new HookConfig(true, "MISC", "WEBVIEW", "android.webkit.WebSettings", "setJavaScriptEnabled",
343 | new Intro_WEBVIEW_SET(), new Class>[]{Boolean.TYPE},
344 | "Tells the WebView to enable JavaScript execution"),
345 | // deprecated
346 | new HookConfig(false, "MISC", "WEBVIEW", "android.webkit.WebSettings", "setPluginState",
347 | new Intro_WEBVIEW_SET(), new Class>[]{Boolean.TYPE},
348 | "Tells the WebView to enable Plugin execution (deprecated in API 18)"),
349 | new HookConfig(true, "MISC", "WEBVIEW", "android.webkit.WebSettings", "setAllowFileAccess",
350 | new Intro_WEBVIEW_SET(), new Class>[]{Boolean.TYPE},
351 | "Tells the WebView to enable FileSystem access"),
352 |
353 | // can lead to RCE if Android <= JELLY_BEAN (v17?)
354 | new HookConfig(true, "MISC", "WEBVIEW", "android.webkit.WebView", "addJavascriptInterface",
355 | new Intro_WEBVIEW_JS_BRIDGE(), new Class>[]{Object.class, String.class},
356 | "Injects the supplied Java object into this WebView."),
357 | // This is a powerful feature, but also presents a security risk for applications
358 | // targeted to API level JELLY_BEAN or below, because JavaScript could use
359 | // reflection to access an injected object's public fields.
360 |
361 | /* ############################################
362 | * ############### SQLite Methods
363 | */
364 | // prone to SQLi
365 | new HookConfig(true, "STORAGE", "SQLite", "android.database.sqlite.SQLiteDatabase", "execSQL",
366 | new Intro_ExecSQL(), new Class>[]{String.class},
367 | "Execute a single SQL statement that is NOT a SELECT or any other SQL statement that returns data."),
368 | new HookConfig(true, "STORAGE", "SQLite", "android.database.sqlite.SQLiteDatabase", "execSQL",
369 | new Intro_ExecSQL(), new Class>[]{String.class, Object[].class},
370 | "Execute a single SQL statement that is NOT a SELECT or any other SQL statement that returns data."),
371 |
372 | new HookConfig(true, "STORAGE", "SQLite", "android.database.sqlite.SQLiteDatabase", "update",
373 | new Intro_SQLite_UPDATE(), new Class>[]{String.class, ContentValues.class, String.class, String[].class},
374 | "Convenience method for updating rows in the database."),
375 | new HookConfig(true, "STORAGE", "SQLite", "android.database.sqlite.SQLiteDatabase", "updateWithOnConflict",
376 | new Intro_SQLite_UPDATE(), new Class>[]{String.class, ContentValues.class, String.class, String[].class, Integer.TYPE},
377 | "Convenience method for updating rows in the database."),
378 |
379 | new HookConfig(true, "STORAGE", "SQLite", "android.database.sqlite.SQLiteDatabase", "insert",
380 | new Intro_SQLite_INSERT(), new Class>[]{String.class, String.class, ContentValues.class},
381 | "Convenience method for inserting a row into the database."),
382 | new HookConfig(true, "STORAGE", "SQLite", "android.database.sqlite.SQLiteDatabase", "insertOrThrow",
383 | new Intro_SQLite_INSERT(), new Class>[]{String.class, String.class, ContentValues.class},
384 | "Convenience method for inserting a row into the database."),
385 | new HookConfig(true, "STORAGE", "SQLite", "android.database.sqlite.SQLiteDatabase", "insertWithOnConflict",
386 | new Intro_SQLite_INSERT(), new Class>[]{String.class, String.class, ContentValues.class, Integer.TYPE},
387 | "Convenience method for inserting a row into the database."),
388 |
389 | new HookConfig(true, "STORAGE", "SQLite", "android.database.sqlite.SQLiteDatabase", "replace",
390 | new Intro_SQLite_INSERT(), new Class>[]{String.class, String.class, ContentValues.class},
391 | "Convenience method for inserting a row into the database."),
392 | new HookConfig(true, "STORAGE", "SQLite", "android.database.sqlite.SQLiteDatabase", "replace",
393 | new Intro_SQLite_INSERT(), new Class>[]{String.class, String.class, ContentValues.class},
394 | "Convenience method for inserting a row into the database."),
395 | };
396 | }
397 |
--------------------------------------------------------------------------------
/Introspy-Android Core/src/com/introspy/hooks/HookSQLiteImpl.java:
--------------------------------------------------------------------------------
1 | package com.introspy.hooks;
2 | import com.introspy.core.IntroHook;
3 |
4 | class Intro_ExecSQL extends IntroHook {
5 | public void execute(Object... args) {
6 | // SQLiteDatabase db = (SQLiteDatabase) _resources;
7 | _logBasicInfo();
8 | _logFlush_I("-> [" + args[0] + "]");
9 | }
10 | }
11 |
12 | class Intro_SQLite_UPDATE extends IntroHook {
13 | public void execute(Object... args) {
14 | // SQLiteDatabase db = (SQLiteDatabase) _resources;
15 | _logBasicInfo();
16 |
17 | _logParameter("Table", args[0]);
18 | _logParameter("Content Values", args[1]);
19 | _logParameter("Where", args[2]);
20 | _logParameter("Where Args", args[3]);
21 |
22 | _logFlush_I("-> " + "Table: " + args[0] + ", " +
23 | "ContentValues: " + args[1] + ", " +
24 | "Where: " + args[2] + ", " +
25 | "WhereArgs: " + args[3]);
26 | // TODO: dump 'where' args (array of strings for args[3])
27 | }
28 | }
29 |
30 | class Intro_SQLite_INSERT extends IntroHook {
31 | public void execute(Object... args) {
32 | // SQLiteDatabase db = (SQLiteDatabase) _resources;
33 | _logBasicInfo();
34 |
35 | _logParameter("Table", args[0]);
36 | _logParameter("Content Values", args[1]);
37 |
38 | _logFlush_I("-> " + "Table: " + args[0] + ", " +
39 | "ContentValues: " + args[2]);
40 | // TODO: dump this other array of strings
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Introspy-Android Core/src/com/introspy/hooks/HookSSLImpl.java:
--------------------------------------------------------------------------------
1 | package com.introspy.hooks;
2 | import com.introspy.core.IntroHook;
3 |
4 | import java.security.cert.X509Certificate;
5 |
6 | import javax.net.ssl.TrustManager;
7 | import javax.net.ssl.X509TrustManager;
8 |
9 | import org.apache.http.conn.ssl.SSLSocketFactory;
10 |
11 | // init(KeyManager[] km, TrustManager[] tm, SecureRandom sr) ->
12 | // init(null, trustManagers, null);
13 | class Intro_SSL_CHECK_TRUST_MANAGER extends IntroHook {
14 | public void execute(Object... args) {
15 |
16 | _logBasicInfo();
17 | TrustManager[] tm_arr = (TrustManager[]) args[1];
18 | // check the trust manager
19 | if (tm_arr != null && tm_arr[0] != null) {
20 | X509TrustManager tm = (X509TrustManager) tm_arr[0];
21 | X509Certificate[] chain = new X509Certificate[]{};
22 | boolean check = false;
23 | try {
24 | tm.checkClientTrusted(chain, "");
25 | tm.checkServerTrusted(chain, "");
26 | } catch (Exception e) { // should change to CertificateException
27 | // if it goes here with an invalid cert
28 | // the app may verify certs
29 | check = true;
30 | }
31 | if (!check)
32 | _logFlush_W("The app does not verify SSL certs");
33 | else
34 | _logFlush_I("Use of a custom Trust Manager, " +
35 | "the app may do cert. pinning (OR potentially validate any cert)");
36 | }
37 | }
38 | }
39 |
40 | //setSSLSocketFactory(SSLSocketFactory sf)
41 | class Intro_SSL_CHECK_TRUST_SOCKETFACTORY extends IntroHook {
42 | public void execute(Object... args) {
43 | // should only display data when there is a potential issue
44 |
45 | // check not implemented yet
46 | _logBasicInfo();
47 | _logFlush_W("Use of a custom SSLSocketFactory, " +
48 | "the app may do cert. pinning OR validate any cert");
49 | }
50 | }
51 |
52 | // setHostnameVerifier(X509HostnameVerifier hostnameVerifier)
53 | class Intro_CHECK_HOSTNAME_VERIFIER extends IntroHook {
54 | public void execute(Object... args) {
55 |
56 | // this only display data when there is a potential issue
57 | if ((org.apache.http.conn.ssl.X509HostnameVerifier)args[0] ==
58 | SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER) {
59 | _logBasicInfo();
60 | _logParameter("SSLSocketFactory", "ALLOW_ALL_HOSTNAME_VERIFIER");
61 | _logFlush_W("HostNameVerifier set to accept ANY hostname");
62 | }
63 | }
64 | }
65 |
66 | class Intro_CHECK_URI extends IntroHook {
67 | public void execute(Object... args) {
68 |
69 | // this is noisy so only display data when there is a potential issue
70 | // arg0 is a uri (string or uri (this may not actually work))
71 | String uri = (String) args[0];
72 | if (uri.contains("http:")) {
73 | _logBasicInfo();
74 | _logParameter("URI", uri);
75 | _logFlush_W("No SSL: ["+uri+"]");
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/Introspy-Android Core/src/com/introspy/hooks/HookSharedPrefImpl.java:
--------------------------------------------------------------------------------
1 | package com.introspy.hooks;
2 | import com.introspy.core.IntroHook;
3 |
4 | import java.util.Map;
5 |
6 | import android.content.SharedPreferences;
7 | import android.util.Log;
8 |
9 | class Intro_PREF_PARENT extends IntroHook {
10 | protected static boolean
11 | _allPrefsAlreadyRetrieved = false;
12 | }
13 |
14 | class Intro_CHECK_SHARED_PREF extends IntroHook {
15 | protected static boolean _onlyRetrievedPrefOnce = true;
16 | protected static boolean _prefRetrieved = true;
17 |
18 | @SuppressWarnings("deprecation")
19 | public void execute(Object... args) {
20 |
21 | // this is noisy so only display data when there is a potential issue
22 | if (!_onlyRetrievedPrefOnce ||
23 | (_onlyRetrievedPrefOnce && !_prefRetrieved)) {
24 | String prefName = (String) args[0];
25 | _logParameter("Preference Name", args[0]);
26 | _logLine("### PREF:"+ _packageName +
27 | ":getSharedPref:"+prefName);
28 | // display the pref retrieved
29 | try {
30 | SharedPreferences prefs = (SharedPreferences)
31 | _hookInvoke(args);
32 | if (prefs != null && prefs.getAll().size() > 0)
33 | _logFlush_I("-> " + prefs.getAll());
34 | } catch (Throwable e) {
35 | _logLine("-> not able to retrieve preferences");
36 | }
37 | _prefRetrieved = true;
38 | }
39 |
40 | // arg1 is the sharing modes
41 | Integer mode = (Integer) args[1];
42 | String smode = "";
43 |
44 | if (mode == android.content.Context.MODE_WORLD_READABLE)
45 | smode = "MODE_WORLD_READABLE";
46 | else if (mode == android.content.Context.MODE_WORLD_WRITEABLE)
47 | smode = "MODE_WORLD_WRITEABLE";
48 |
49 | if (!smode.isEmpty()) {
50 | _logParameter("Preference Name", args[0]);
51 | _logParameter("Mode: ", smode);
52 | _logFlush_W("Shared preference accessible to the WORLD. " +
53 | "(MODE: " + smode + ")");
54 | }
55 | }
56 | }
57 |
58 | class Intro_GET_SHARED_PREF extends IntroHook {
59 | public void execute(Object... args) {
60 | String prefName = (String) args[0]; // name of pref to retrieve
61 | if (prefName == null) {
62 | return;
63 | }
64 | // args[1] is the default value
65 |
66 | _logParameter("Preference Name", args[0]);
67 | String out = "### PREF:"+_packageName +
68 | ":getSharedPref:"+ _methodName +
69 | "; name: [" + args[0] + "]" +
70 | ", default: [" + args[1] + "]";
71 |
72 | Object o = null;
73 | try {
74 | o = _hookInvoke(args);
75 | } catch (Throwable e) {
76 | // this may throw if incorrect type specified in the code
77 | // Log.w("IntrospyLog", "error in Intro_GET_SHARED_PREF: "+e);
78 | }
79 |
80 | if (o != null) {
81 | out += "; retrieves: ["+o+"]";
82 | _logReturnValue("Value", o);
83 | _logFlush_I(out);
84 | }
85 | else {
86 | _logLine(out);
87 | _logFlush_I("-> Preference not found or incorrect type specified");
88 | }
89 | }
90 | }
91 |
92 | class Intro_PUT_SHARED_PREF extends IntroHook {
93 | public void execute(Object... args) {
94 |
95 | String prefName = (String) args[0]; // name of pref to retrieve
96 | _logParameter("Preference Name", args[0]);
97 | _logParameter("Value", args[1]);
98 | String out = "### PREF:"+_packageName + ":writeSharedPref:"
99 | +prefName+", value: "+args[1];
100 | _logFlush_I(out);
101 | }
102 | }
103 |
104 | class Intro_CONTAINS_SHARED_PREF extends IntroHook {
105 | public void execute(Object... args) {
106 | String out = "";
107 | String prefName = (String) args[0]; // name of pref to retrieve
108 |
109 | try {
110 | boolean o = (Boolean) _hookInvoke(args);
111 | _logParameter("Preference Name", args[0]);
112 | if (o == false) {
113 | out = "### PREF:"+_packageName+
114 | ":contains:"+ prefName;
115 | _logReturnValue("Value", o);
116 | _logLine(out);
117 | _logFlush_W("Preference not found (Hidden pref?)");
118 | }
119 | } catch (Throwable e) {
120 | Log.i("IntrospyLog", "error in Intro_CONTAINS_SHARED_PREF: "+e);
121 | }
122 | }
123 | }
124 |
125 | class Intro_GET_ALL_SHARED_PREF extends Intro_PREF_PARENT {
126 | public void execute(Object... args) {
127 | // display all the prefs retrieved
128 | // only do it once because it's too noisy
129 | if (_allPrefsAlreadyRetrieved)
130 | return;
131 | try {
132 | @SuppressWarnings("unchecked")
133 | Map keys = (Map) _hookInvoke(args);
134 | if ( keys != null && keys.size() > 0) {
135 | _logLine("### PREF:"+_packageName+":getAll:");
136 | for(Map.Entry entry : keys.entrySet()){
137 | _logLine("-> " + entry.getKey() + ": " +
138 | entry.getValue().toString());
139 | }
140 | }
141 | _logFlush_I();
142 | _allPrefsAlreadyRetrieved = true;
143 | } catch (Throwable e) {
144 | Log.i(_TAG_ERROR, "-> not able to retrieve all " +
145 | "preferences (get All in " +
146 | _packageName+"). Error: " + e);
147 | }
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/Introspy-Android Core/src/com/introspy/hooks/HookWebviewImpl.java:
--------------------------------------------------------------------------------
1 | package com.introspy.hooks;
2 | import com.introspy.core.IntroHook;
3 |
4 | //setPluginState(WebSettings.PluginState state)
5 | // with android.webkit.WebSettings.PluginState.ON
6 | // setJavaScriptEnabled(boolean flag) with true
7 | // setAllowFileAccess (boolean allow)
8 | class Intro_WEBVIEW_SET extends IntroHook {
9 | public void execute(Object... args) {
10 |
11 | // should only display data when first arg is set to true
12 |
13 | if ((Boolean)args[0] == true) {
14 | _logBasicInfo();
15 | _logFlush_W("-> !!! Set of a potentially dangerous " +
16 | "functionality to true for the webview using "+
17 | _config.getMethodName() + ", make sure this " +
18 | "functionality is necessary");
19 | }
20 | }
21 | }
22 |
23 | // addJavascriptInterface(Object object, String name)
24 | class Intro_WEBVIEW_JS_BRIDGE extends IntroHook {
25 | public void execute(Object... args) {
26 |
27 | // this only display data when there is a potential issue
28 | _logBasicInfo();
29 | _logFlush_W("-> !!! Javascript interface " +
30 | "added for the webview. Details: " +
31 | args[0] + ", name: " + args[1]);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Introspy-Android Core/src/com/introspy/hooks/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Package containing the hook implementations
3 | */
4 | /**
5 | * @author marc blanchou
6 | *
7 | */
8 | package com.introspy.hooks;
--------------------------------------------------------------------------------
/Introspy-Android Core/src/com/introspy/logging/Logger.java:
--------------------------------------------------------------------------------
1 | package com.introspy.logging;
2 |
3 | import com.introspy.core.*;
4 |
5 | import android.util.Log;
6 |
7 | // note: flush only when done with the logging
8 | // for a function as it dumps stack traces for the call
9 |
10 | public class Logger extends LoggerDB {
11 |
12 | private void clean() {
13 | _out = "";
14 | _pListArgsBody = "";
15 | _pListRetBody = "";
16 | }
17 |
18 | protected void _log(String data) {
19 | _out += data;
20 | }
21 |
22 | // ####### public
23 |
24 | public void logInit(HookConfig config) {
25 | _config = config;
26 | }
27 |
28 | public void logLine(String line) {
29 | _out += line + "\n";
30 | }
31 |
32 | public void logFlush_I(String notes) {
33 | _notes = notes;
34 | _out += notes;
35 | logFlush_I();
36 | }
37 | public void logFlush_W(String notes) {
38 | _notes = notes;
39 | _out += "-> !!! " + notes;
40 | logFlush_W();
41 | }
42 |
43 | // use a static ref to synchronize across threads
44 | public void logFlush_I() {
45 | _addTraces();
46 | synchronized (_TAG) {
47 | if (_enableDB)
48 | _logInDB("I");
49 | Log.i(_TAG, _out);
50 | }
51 | clean();
52 | }
53 |
54 | public void logFlush_W() {
55 | _addTraces();
56 | synchronized (_TAG) {
57 | if (_enableDB)
58 | _logInDB("W");
59 | Log.w(_TAG, _out);
60 | }
61 | clean();
62 | }
63 |
64 | public void logParameter(String name, String value) {
65 | if (_enableDB)
66 | _logDBParameter(name, value);
67 | }
68 |
69 | public void logParameter(String name, Object value) {
70 | if (_enableDB)
71 | _logDBParameter(name, "" + value);
72 | }
73 |
74 | public void logReturnValue(String name, String value) {
75 | if (_enableDB)
76 | _logDBReturnValue(name, value);
77 | }
78 |
79 | public void logReturnValue(String name, Object value) {
80 | if (_enableDB)
81 | _logDBReturnValue(name, "" + value);
82 | }
83 |
84 | public void logBasicInfo() {
85 | _log("### "+ _config.getCategory()+" ### " +
86 | ApplicationConfig.getPackageName() +
87 | " - " + _config.getClassName() + "->"
88 | + _config.getMethodName() + "()\n");
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/Introspy-Android Core/src/com/introspy/logging/LoggerConfig.java:
--------------------------------------------------------------------------------
1 | package com.introspy.logging;
2 |
3 | import com.introspy.core.HookConfig;
4 | import com.introspy.core.IntroStringHelper;
5 |
6 | public class LoggerConfig extends IntroStringHelper {
7 | protected LoggerConfig() {
8 | }
9 |
10 | protected HookConfig _config;
11 |
12 | public static String _TAG = "Introspy";
13 | public static String _TAG_ERROR = "IntrospyError";
14 | public static String _TAG_LOG = "IntrospyLog";
15 |
16 | public static String getTag() {
17 | return _TAG;
18 | }
19 |
20 | public static String getTagError() {
21 | return _TAG_ERROR;
22 | }
23 |
24 | public static String getTagLog() {
25 | return _TAG_LOG;
26 | }
27 |
28 | protected String _out = "";
29 | protected String _notes = "";
30 | protected String _traces = "";
31 |
32 | protected boolean _enableDB = true;
33 |
34 | // this can be enabled via the _config file
35 | protected boolean _stackTraces = false;
36 |
37 | // change this value to get full traces
38 | protected boolean _fullTraces = false;
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/Introspy-Android Core/src/com/introspy/logging/LoggerDB.java:
--------------------------------------------------------------------------------
1 | package com.introspy.logging;
2 |
3 | import android.util.Log;
4 |
5 | public class LoggerDB extends LoggerTraces {
6 | protected String _pListHeader =
7 | "" +
8 | "" +
10 | "" +
11 | "";
12 |
13 | protected String _pListArgsBody = "";
14 | /* "arguments" +
15 | "" +
16 | "pasteboardType" +
17 | "public.utf8-plain-text" +
18 | "";
19 | */
20 |
21 | // not necessary to include this
22 | protected String _pListRetBody = "";
23 | /* "returnValue" +
24 | "nil";
25 | */
26 |
27 | protected String _pListFooter =
28 | "";
29 |
30 | protected void _logDBParameter(String name, String value) {
31 | _pListArgsBody += "" + _escapeXMLChars(name) + "" +
32 | "" + _escapeXMLChars(value) + "";
33 | }
34 |
35 | protected void _logDBReturnValue(String name, String value) {
36 | // not using the name in the DB here yet
37 | _pListRetBody =
38 | "returnValue" +
39 | "" + _escapeXMLChars(value) + "";
40 | }
41 |
42 | protected String _logCreatePlistArgs() {
43 | return _pListHeader +
44 | "arguments" +
45 | "" +
46 | _pListArgsBody +
47 | "" +
48 | _pListRetBody +
49 | _pListFooter;
50 | }
51 |
52 | protected void _logInDB(String logType) {
53 | try {
54 | SQLiteIntrospy db = SQLiteIntrospy.getInstance();
55 | db.open();
56 | db.createRow(
57 | _config.getType(),
58 | _config.getSubType(),
59 | _config.getClassName(),
60 | _config.getMethodName(),
61 | _logCreatePlistArgs(),
62 | logType,
63 | // (logType.equalsIgnoreCase("W") ? _notes : ""),
64 | _escapeXMLChars(_notes),
65 | _escapeXMLChars(_traces));
66 | db.close();
67 | }
68 | catch (Exception e) {
69 | Log.w(_TAG_ERROR, "--> Error with DB: " + e);
70 | }
71 | }
72 |
73 | public void disableDBlogger() {
74 | _enableDB = false;
75 | }
76 |
77 | public void enableDBlogger() {
78 | _enableDB = true;
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/Introspy-Android Core/src/com/introspy/logging/LoggerErrorHandler.java:
--------------------------------------------------------------------------------
1 | package com.introspy.logging;
2 |
3 | public class LoggerErrorHandler {
4 | static public String _getStackTrace() {
5 | String out = "";
6 | StackTraceElement[] ste =
7 | Thread.currentThread().getStackTrace();
8 | for (int i = 0; i < ste.length; i++)
9 | out += ste[i].toString() + "\n";
10 | return out.substring(0, out.length() - 1);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Introspy-Android Core/src/com/introspy/logging/LoggerFile.java:
--------------------------------------------------------------------------------
1 | package com.introspy.logging;
2 |
3 | import java.io.BufferedWriter;
4 | import java.io.File;
5 | import java.io.FileWriter;
6 | import java.io.IOException;
7 |
8 | import com.introspy.core.ApplicationConfig;
9 |
10 | import android.util.Log;
11 |
12 | // This class is not used, a DB is used instead
13 |
14 | public class LoggerFile {
15 | protected void logToFile(String logEntry) {
16 | // junk code
17 | try {
18 | File logFile = new File(ApplicationConfig.getDataDir() + "/introspy.log"); // "sdcard/introspy.log"
19 |
20 | if (!logFile.exists())
21 | {
22 | try
23 | {
24 | logFile.createNewFile();
25 | }
26 | catch (IOException e)
27 | {
28 | Log.w("IntrospyLog", "Cannot create log file: " + e);
29 | }
30 | }
31 | try
32 | {
33 | //BufferedWriter for performance, true to set append to file flag
34 | BufferedWriter buf = new BufferedWriter(new FileWriter(logFile, true));
35 | buf.append(logEntry);
36 | buf.newLine();
37 | buf.close();
38 | }
39 | catch (IOException e)
40 | {
41 | Log.w("IntrospyLog", "Error with file logger: " + e);
42 | }
43 | } catch (Exception e) {
44 | Log.w("IntrospyLog", "Error with file logger: " + e);
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Introspy-Android Core/src/com/introspy/logging/LoggerTraces.java:
--------------------------------------------------------------------------------
1 | package com.introspy.logging;
2 |
3 | import android.util.Log;
4 |
5 | public class LoggerTraces extends LoggerConfig {
6 |
7 | public String getFullTraces() {
8 | StackTraceElement[] ste = Thread.currentThread().getStackTrace();
9 | if (ste == null)
10 | return "";
11 | StringBuilder stringBuilder = new StringBuilder();
12 | for (StackTraceElement element : ste)
13 | stringBuilder.append(element.toString()).append("\n");
14 | return stringBuilder.toString();
15 | }
16 |
17 | public String getLightTraces() {
18 | String out = "";
19 | try {
20 | StackTraceElement[] ste =
21 | Thread.currentThread().getStackTrace();
22 | if (ste == null)
23 | return "";
24 | out += "----------- " + ste[10] + "\n";
25 | out += "----------- " + ste[11] + "\n";
26 | out += "----------- " + ste[12] + "\n";
27 | }
28 | catch (Exception e) {
29 | out = "-> Cannot get Stack Traces";
30 | Log.w(_TAG_ERROR, out);
31 | }
32 | return out;
33 | }
34 |
35 | public void enableTraces() {
36 | _stackTraces = true;
37 | }
38 | public void disableTraces() {
39 | _stackTraces = false;
40 | }
41 |
42 | protected void _addTraces() {
43 | _traces = "";
44 | if (_stackTraces) {
45 | if (_fullTraces)
46 | _traces = getFullTraces();
47 | else
48 | _traces = getLightTraces();
49 |
50 | _out = _traces + _out;
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Introspy-Android Core/src/com/introspy/logging/SQLiteIntrospy.java:
--------------------------------------------------------------------------------
1 |
2 | package com.introspy.logging;
3 |
4 | import java.util.ArrayList;
5 | import java.util.List;
6 |
7 | import com.introspy.core.ApplicationConfig;
8 |
9 | import android.content.ContentValues;
10 | import android.content.Context;
11 | import android.database.Cursor;
12 | import android.database.SQLException;
13 | import android.database.sqlite.SQLiteDatabase;
14 |
15 | public class SQLiteIntrospy {
16 | private static SQLiteIntrospy _instance = null;
17 | // Database fields
18 | private SQLiteDatabase database;
19 | private SQLiteIntrospyHelper dbHelper;
20 | private String[] allColumns = {
21 | SQLiteIntrospyHelper.COLUMN_ID,
22 | SQLiteIntrospyHelper.COLUMN_TYPE,
23 | SQLiteIntrospyHelper.COLUMN_SUBTYPE,
24 | SQLiteIntrospyHelper.COLUMN_CLASS,
25 | SQLiteIntrospyHelper.COLUMN_METHOD,
26 | SQLiteIntrospyHelper.COLUMN_DETAILS,
27 | SQLiteIntrospyHelper.COLUMN_LOG_TYPE,
28 | SQLiteIntrospyHelper.COLUMN_NOTES,
29 | SQLiteIntrospyHelper.COLUMN_ST
30 | };
31 |
32 | public SQLiteIntrospy(Context context) {
33 | dbHelper = new SQLiteIntrospyHelper(context);
34 | }
35 |
36 | public void open() throws SQLException {
37 | database = dbHelper.getWritableDatabase();
38 | }
39 |
40 | public void close() {
41 | dbHelper.close();
42 | }
43 |
44 | public SQLiteIntrospyLog createRow(String type, String subType,
45 | String className, String methodName,
46 | String details, String logType) {
47 | return createRow(type, subType, className,
48 | methodName, details, logType, "", "");
49 | }
50 |
51 | public SQLiteIntrospyLog createRow(String type, String subType,
52 | String className, String methodName,
53 | String details, String logType, String notes) {
54 | return createRow(type, subType, className,
55 | methodName, details, logType, notes, "");
56 | }
57 |
58 | public SQLiteIntrospyLog createRow(String type, String subType,
59 | String className, String methodName, String details,
60 | String logType, String notes, String st) {
61 | ContentValues values = new ContentValues();
62 | values.put(SQLiteIntrospyHelper.COLUMN_TYPE, type);
63 | values.put(SQLiteIntrospyHelper.COLUMN_SUBTYPE, subType);
64 | values.put(SQLiteIntrospyHelper.COLUMN_CLASS, className);
65 | values.put(SQLiteIntrospyHelper.COLUMN_METHOD, methodName);
66 | values.put(SQLiteIntrospyHelper.COLUMN_DETAILS, details);
67 | values.put(SQLiteIntrospyHelper.COLUMN_LOG_TYPE, logType);
68 | values.put(SQLiteIntrospyHelper.COLUMN_NOTES, notes);
69 | values.put(SQLiteIntrospyHelper.COLUMN_ST, st);
70 |
71 | long insertId = database.insert(
72 | SQLiteIntrospyHelper.TABLE_TRACES,
73 | null, values);
74 |
75 | Cursor cursor = database.query(
76 | SQLiteIntrospyHelper.TABLE_TRACES,
77 | allColumns, SQLiteIntrospyHelper.COLUMN_ID +
78 | " = " + insertId, null,
79 | null, null, null);
80 |
81 | cursor.moveToFirst();
82 | SQLiteIntrospyLog
83 | newRow = cursorToRow(cursor);
84 | cursor.close();
85 | return newRow;
86 | }
87 |
88 | private SQLiteIntrospyLog cursorToRow(Cursor cursor) {
89 | SQLiteIntrospyLog Row =
90 | new SQLiteIntrospyLog();
91 | Row.setId(cursor.getLong(0));
92 | Row.setRow(cursor.getString(1));
93 | return Row;
94 | }
95 |
96 | public void deleteRow(SQLiteIntrospyLog Row) {
97 | long id = Row.getId();
98 | System.out.println("Row deleted with id: " + id);
99 | database.delete(SQLiteIntrospyHelper.TABLE_TRACES,
100 | SQLiteIntrospyHelper.COLUMN_ID
101 | + " = " + id, null);
102 | }
103 |
104 | public List getAllRows() {
105 | List Rows =
106 | new ArrayList();
107 |
108 | Cursor cursor = database.query(
109 | SQLiteIntrospyHelper.TABLE_TRACES,
110 | allColumns, null, null, null, null, null);
111 |
112 | cursor.moveToFirst();
113 | while (!cursor.isAfterLast()) {
114 | SQLiteIntrospyLog Row = cursorToRow(cursor);
115 | Rows.add(Row);
116 | cursor.moveToNext();
117 | }
118 | // make sure to close the cursor
119 | cursor.close();
120 | return Rows;
121 | }
122 |
123 | public static SQLiteIntrospy getInstance() {
124 | if (_instance == null) {
125 | _instance = new
126 | SQLiteIntrospy(ApplicationConfig.getContext());
127 | }
128 | return _instance;
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/Introspy-Android Core/src/com/introspy/logging/SQLiteIntrospyHelper.java:
--------------------------------------------------------------------------------
1 | package com.introspy.logging;
2 |
3 | import android.content.Context;
4 | import android.database.sqlite.SQLiteDatabase;
5 | import android.database.sqlite.SQLiteOpenHelper;
6 |
7 | public class SQLiteIntrospyHelper extends SQLiteOpenHelper {
8 |
9 | private static final String DATABASE_NAME = "introspy.db";
10 | // increment this number each time the DB structure changes
11 | private static final int DATABASE_VERSION = 8;
12 |
13 | public static final String TABLE_TRACES = "tracedCalls";
14 |
15 | public static final String COLUMN_ID = "_id";
16 | public static final String COLUMN_TYPE = "type";
17 | public static final String COLUMN_SUBTYPE = "subType";
18 | public static final String COLUMN_CLASS = "className";
19 | public static final String COLUMN_METHOD = "methodName";
20 | public static final String COLUMN_DETAILS =
21 | "argumentsAndReturnValueDict";
22 | public static final String COLUMN_LOG_TYPE = "logType";
23 | public static final String COLUMN_NOTES = "notes";
24 | public static final String COLUMN_ST = "callStack";
25 |
26 | private static final String DATABASE_CREATE =
27 | "create table "
28 | + TABLE_TRACES + "(" + COLUMN_ID
29 | + " integer primary key autoincrement, " +
30 | COLUMN_TYPE + " text not null, " +
31 | COLUMN_SUBTYPE + " text not null, " +
32 | COLUMN_CLASS + " text not null, " +
33 | COLUMN_METHOD + " text not null, " +
34 | COLUMN_DETAILS + " text not null, " +
35 | COLUMN_LOG_TYPE + " text not null, " +
36 | COLUMN_NOTES + " text not null, " +
37 | COLUMN_ST + " text not null " +
38 | ");";
39 |
40 | SQLiteIntrospyHelper(Context context) {
41 | super(context,
42 | DATABASE_NAME, null, DATABASE_VERSION);
43 | }
44 |
45 | @Override
46 | public void onCreate(SQLiteDatabase db) {
47 | db.execSQL(DATABASE_CREATE);
48 | }
49 |
50 | @Override
51 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
52 | db.execSQL("DROP TABLE IF EXISTS " + TABLE_TRACES);
53 | onCreate(db);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Introspy-Android Core/src/com/introspy/logging/SQLiteIntrospyLog.java:
--------------------------------------------------------------------------------
1 | package com.introspy.logging;
2 |
3 | public class SQLiteIntrospyLog {
4 | private long id;
5 | private String Row;
6 |
7 | public long getId() {
8 | return id;
9 | }
10 |
11 | public void setId(long id) {
12 | this.id = id;
13 | }
14 |
15 | public String getRow() {
16 | return Row;
17 | }
18 |
19 | public void setRow(String Row) {
20 | this.Row = Row;
21 | }
22 |
23 | @Override
24 | public String toString() {
25 | return Row;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Introspy-Android Core/src/com/introspy/logging/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Logger package
3 | */
4 | /**
5 | * @author marc blanchou
6 | *
7 | */
8 | package com.introspy.logging;
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Introspy-Android
2 | ========
3 |
4 | Blackbox tool to help understand what an Android application is doing at runtime
5 | and assist in the identification of potential security issues.
6 |
7 |
8 | Description
9 | -----------
10 |
11 | Introspy-Android comprises two separate components: a GUI interface to configure
12 | hooks, filters and options and a Cydia Substrate extension containing the core of
13 | the tool functionalities, including hooks and analysis of potential issues.
14 |
15 | Introspy-Android can be installed on a rooted device and dynamically
16 | configured to hook security-sensitive Android APIs at run-time. The tool records
17 | all the relevant API calls made by an application, including function calls, arguments
18 | and return values. It then perform tests for security issues in real time and persists
19 | the results in a database and in the Android logging system.
20 |
21 | The Introspy-Analyzer can then be used to analyse a database generated by the
22 | tracer, and generate HTML reports containing the list of logged function calls
23 | as well as a list of potential vulnerabilities affecting the application.
24 |
25 | See http://isecpartners.github.io/introspy-android/ for a quick introduction.
26 |
27 | Usage
28 | ---------------
29 |
30 |
31 | * Ensure that Cydia Substrate has been deployed on your test device. The installer requires a rooted device and can be found on the Google Play store at https://play.google.com/store/apps/details?id=com.saurik.substrate&hl=en
32 | * Download the pre-compiled APK available at https://github.com/iSECPartners/Introspy-Android
33 | * Install Introspy-Android Core.apk on a device where Cydia Substrate is installed with:
34 |
35 | adb install Introspy-Android Core.apk
36 |
37 | * Install Introspy-Android Config.apk:
38 |
39 | adb install Introspy-Android Config.apk
40 |
41 | The Instrospy-Android Config application displays apps the Core application will hook and the
42 | various filters and options applied to them. This application need root access (you can use supersu to give temporary root access to the application). The changes are dynamic and you do not need to restard the applications for them to be effective.
43 |
44 | * Once configured with the Config application, logs are dumped in the system logs and in a database in the directory of the application hooked (in databases/introspy.db)
45 | * To generate an html report using the generated database, you can use the Introspy-Analyzer (by A.Diquet and T.Daniels: https://github.com/iSECPartners/Introspy-Analyzer)
46 |
47 | It should be noted that the Core application can work on a device running Android 2.3 and above whereas the Config application can only run from Android 3.0 on due to the use of specific APIs. In order to test applications on older SDKs without the GUI by only using the Core application, you can simply create a file named "introspy.config" containing filters you want to hook in the directory of the applications you want to test. Example:
48 |
49 | adb shell su -c echo "GENERAL CRYPTO, KEY, HASH, FS, IPC, PREF, URI, WEBVIEW" > /data/data/com.YOUR_APP_NAME/introspy.config
50 |
51 | ### How to uninstall
52 |
53 | adb uninstall com.introspy.core
54 | adb uninstall com.introspy.config
55 |
56 | ### What if the extension crashes
57 | This tool has not been tested on all versions of Android. If the tool does not work on your version, please send us your error logs:
58 |
59 | adb logcat -s "InstrospyError"
60 |
61 | If due to the error the phone does not boot anymore, you can still connect to it via adb and simply remove the extension to fix it with:
62 |
63 | adb shell su -c rm /data/app/com.introspy.core*
64 |
65 | If you still have issues, it may be due to Cydia Substrate itself, which may not be compatible with your device? To uninstall it you can do the following (from Cydia Substrate's website): "By holding down the volume-up button on your device you can disable Substrate while it is attempting to load modifications (such as while it is turning on and starting); this will give you an opportunity to use Google Play to uninstall things that might be broken."
66 |
67 | Reporting
68 | -----------------
69 |
70 | #### Reporting
71 |
72 | * Relevant data including potential issues related to the APIs hooked is dumped in
73 | a database and in the system logs. You can do the following commands to display them:
74 |
75 | * Display the complete logs:
76 |
77 | adb logcat -s "Introspy"
78 |
79 | * Display potential issues:
80 |
81 | adb logcat -s "Introspy:W"
82 |
83 | * Use the Android version of the analyzer (TBD, it will be pushed to a different
84 | github repository soon) to generate an HTML formatted report.
85 |
86 | ### Display relevant call stacks
87 |
88 | Checking the "STACK TRACES" option within the Config tool will dump a relevant call
89 | stack (comprising of 3 calls) for the selected filters.
90 |
91 | ### What is being analysed/logged, exactly?
92 | ###### General Crypto:
93 | * Log encrypted/decrypted data before/after calls and the algo used
94 | (Note: "readable data is displayed if at least 75% of characters are readable, unreadable characters are stored as ".". If data is not readable, it is stored as base 64)
95 | * Spot static IVs and broken algorithms
96 | * Spot weak RNG
97 |
98 | ###### Hash:
99 | * Log data that is being hashed and the resulting hash
100 | * Display algo used and warns if weak (MD5)
101 |
102 | ###### Key:
103 | * Log any keys used to encrypt
104 | * Log PBKDF key creation (key, passcode, iterations)
105 | * Log passcode used with a keystore
106 |
107 | ###### FS:
108 | * Log only some file system accesses as they are very noisy
109 | * Spot read/write on SD card and the creation of file (or set property) as world readable/writable
110 |
111 | ###### IPC:
112 | * Log IPC creations with details
113 | * Log some Intent sent with details (and extra)
114 | * Programmatic permissions and creation
115 |
116 | ###### Pref:
117 | * Log read/write of preferences with its data, the type and the default value (value set if nothing is returned)
118 | * Dump all preferences when getAll is called, this is done only once to avoid noise
119 | * Log world read/writeable prefs
120 | * Warn for access to preferences that don't exist (could be a hidden preferences to enable logs for instance)
121 |
122 | ###### SSL:
123 | * Warn if SSL is used but any hostname is validated for a valid cert
124 | * Warn if the app validates any cert (self-signed etc.)
125 | * Log if cert pinning is potentially implemented
126 | * Log if SSL not used
127 |
128 | ###### Webview:
129 | * Log when JS, plugins or FS access are enabled for a webview
130 | * Warn/log when a JS interface is used (JS bridge)
131 |
132 | ###### SQLite:
133 | * Log data passed to execSQL, update*, insert*, replace
134 |
135 | Doing It Yourself
136 | -----------------
137 |
138 | ### Building From Source
139 |
140 | Most users should just download and install the pre-compiled packages.
141 | However, if you want to modify the tool's functionality you will have to
142 | clone the source repository and build the packages yourself.
143 |
144 | git clone https://github.com/iSECPartners/introspy-android.git
145 |
146 | Then you need to add the Cydia Substrate SDK to eclipse. See here for instructions on how to do so: http://www.cydiasubstrate.com/id/73e45fe5-4525-4de7-ac14-6016652cc1b8/.
147 |
148 | ### Adding hooks
149 | Adding hooks is simple and can be done within the com.introspy.custom_hooks module. See the pre-filled example in the code (CustomHookList.java and HookExampleImpl.java) and make sure to enable the "CUSTOM HOOKS" option in the Introspy Config application. See http://isecpartners.github.io/Introspy-Android/ for more instructions.
150 |
151 | Notes: Some methods simply cannot be hooked due to potential issues in Cydia Substrate and the hook may just crash the process. Also, make sure to not try hooking abstract methods as it just throws an exception that is never caught by Cydia Substrate (and will just crash the process). You need to hook their implementation, which is sometimes not documented but can be easily found in the Android code base (for example: android.content.Context is implemented in android.content.ContextImpl).
152 |
153 | License
154 | -------
155 |
156 | See ./LICENSE.
157 |
158 | Author
159 | -------
160 |
161 | Marc Blanchou
162 |
--------------------------------------------------------------------------------
/TODO.txt:
--------------------------------------------------------------------------------
1 | see TODOs within the code. Additionally:
2 |
3 | Crypto:
4 | Need to hook more crypto calls (not all cases of dofinal() are implemented)
5 |
6 | IPC:
7 | log setComponentEnabledSetting to spot dynamically enabled IPCs (seems to crash when hooking this method right now)
8 | add more details with inspection of specific data for extra of intents
9 |
10 | SSL:
11 | handle more cases of bad cert validations
12 | handle more checks on the use of non-SSL uri
13 |
14 | SQLite:
15 | hook more methods
--------------------------------------------------------------------------------
/images/GUI.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iSECPartners/Introspy-Android/c611cf125f4ef93d5ddac4c714f6c08a9433b1ec/images/GUI.png
--------------------------------------------------------------------------------
/images/adding_hook_impl.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iSECPartners/Introspy-Android/c611cf125f4ef93d5ddac4c714f6c08a9433b1ec/images/adding_hook_impl.PNG
--------------------------------------------------------------------------------
/images/adding_hooks.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iSECPartners/Introspy-Android/c611cf125f4ef93d5ddac4c714f6c08a9433b1ec/images/adding_hooks.PNG
--------------------------------------------------------------------------------
/images/cydia_installed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iSECPartners/Introspy-Android/c611cf125f4ef93d5ddac4c714f6c08a9433b1ec/images/cydia_installed.png
--------------------------------------------------------------------------------
/images/example_logcat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iSECPartners/Introspy-Android/c611cf125f4ef93d5ddac4c714f6c08a9433b1ec/images/example_logcat.png
--------------------------------------------------------------------------------
/images/html_report_android.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iSECPartners/Introspy-Android/c611cf125f4ef93d5ddac4c714f6c08a9433b1ec/images/html_report_android.PNG
--------------------------------------------------------------------------------
/images/impl_handle.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iSECPartners/Introspy-Android/c611cf125f4ef93d5ddac4c714f6c08a9433b1ec/images/impl_handle.PNG
--------------------------------------------------------------------------------
/images/new_object.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iSECPartners/Introspy-Android/c611cf125f4ef93d5ddac4c714f6c08a9433b1ec/images/new_object.PNG
--------------------------------------------------------------------------------