├── 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 --------------------------------------------------------------------------------