package com.android.reverse.apimonitor; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.Set; import android.content.ContentProviderOperation; import android.content.ContentValues; import android.database.ContentObserver; import android.net.Uri; import android.text.TextUtils; import com.android.reverse.hook.HookParam; import com.android.reverse.util.Logger; import com.android.reverse.util.RefInvoke; public class ContentResolverHook extends ApiMonitorHook { private static final String[] privacyUris = { "content://com.android.contacts", "content://sms", "content://mms-sms", "content://contacts/", "content://call_log", "content://browser/bookmarks" }; private boolean isSensitiveUri(Uri uri) { String url = uri.toString().toLowerCase(); Logger.log_behavior(url); for (int i = 0; i < privacyUris.length; i++) { if (url.startsWith(privacyUris[i])) { return true; } } return false; } private String concatenateStringArray(String[] array, String splitstr) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < array.length; i++) { if (i == array.length - 1) sb.append(array[i]); else sb.append(array[i] + splitstr); } return sb.toString(); } private String concatenateQuery(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { StringBuilder sb = new StringBuilder("select "); if (projection == null) { sb.append("* "); } else { sb.append(concatenateStringArray(projection, ",")); } sb.append(" from [" + uri.toString() + "] "); if (!TextUtils.isEmpty(selection)) { sb.append(" where "); if (selectionArgs == null) { sb.append(selection); } else { String selectstr = selection; for (int i = 0; i < selectionArgs.length; i++) { selectstr = selectstr.replaceFirst("?", selectionArgs[i]); } sb.append(selectstr); } } if (!TextUtils.isEmpty(sortOrder)) sb.append(" order by " + sortOrder); return sb.toString(); } private String concatenateInsert(Uri uri, ContentValues cv) { StringBuilder sb = new StringBuilder(); sb.append(" insert into "); sb.append("[" + uri.toString() + "]"); sb.append(" ( "); String[] keysArray = new String[cv.size()]; keysArray = this.getContentValuesKeySet(cv).toArray(keysArray); sb.append(concatenateStringArray(keysArray, ",")); sb.append(" ) "); sb.append(" values ("); for (int i = 0; i < keysArray.length; i++) { if (i == keysArray.length - 1) sb.append(" " + cv.get(keysArray[i])); else sb.append(" " + cv.get(keysArray[i]) + ","); } sb.append(" )"); return sb.toString(); } private String concatenateDelete(Uri uri, String selection, String[] selectionArgs) { StringBuilder sb = new StringBuilder(); sb.append(" delete from "); sb.append("[" + uri.toString() + "]"); if (!TextUtils.isEmpty(selection)) { sb.append(" where "); if (selectionArgs == null) sb.append(selection); else { String selectstr = selection; for (int i = 0; i < selectionArgs.length; i++) { selectstr = selectstr.replaceFirst("?", selectionArgs[i]); } sb.append(selectstr); } } return sb.toString(); } private String concatenateUpdate(Uri uri, ContentValues cv, String selection, String[] selectionArgs) { StringBuilder sb = new StringBuilder(); sb.append(" update "); sb.append("[" + uri.toString() + "]"); sb.append(" set "); String[] keysArray = (String[]) this.getContentValuesKeySet(cv).toArray(); for (int i = 0; i < keysArray.length; i++) { if (i == keysArray.length - 1) sb.append(" " + keysArray[i] + "=" + cv.get(keysArray[i])); else sb.append(" " + keysArray[i] + "=" + cv.get(keysArray[i]) + ", "); } if (!TextUtils.isEmpty(selection)) { sb.append(" where "); if (selectionArgs == null) sb.append(selection); else { String selectstr = selection; for (int i = 0; i < selectionArgs.length; i++) { selectstr = selectstr.replaceFirst("?", selectionArgs[i]); } sb.append(selectstr); } } return sb.toString(); } @Override public void startHook() { Method querymethod = RefInvoke.findMethodExact("android.content.ContentResolver", ClassLoader.getSystemClassLoader(), "query", Uri.class, String[].class, String.class, String[].class, String.class); hookhelper.hookMethod(querymethod, new AbstractBahaviorHookCallBack() { @Override public void descParam(HookParam param) { // TODO Auto-generated method stub Uri uri = (Uri) param.args[0]; if (isSensitiveUri(uri)) { Logger.log_behavior("Read ContentProvider -> Uri = " + uri.toString()); String queryStr = concatenateQuery(uri, (String[]) param.args[1], (String) param.args[2], (String[]) param.args[3], (String) param.args[4]); Logger.log_behavior("Query SQL = " + queryStr); } } }); Method registerContentObservermethod = RefInvoke.findMethodExact("android.content.ContentResolver", ClassLoader.getSystemClassLoader(), "registerContentObserver", Uri.class, boolean.class, ContentObserver.class, int.class); hookhelper.hookMethod(registerContentObservermethod, new AbstractBahaviorHookCallBack() { @Override public void descParam(HookParam param) { // TODO Auto-generated method stub Uri uri = (Uri) param.args[0]; if (isSensitiveUri(uri)) { Logger.log_behavior("Register ContentProvider Change -> Uri = " + uri.toString()); Logger.log_behavior("ContentObserver ClassName =" + param.args[1].getClass().toString()); } } }); Method insertmethod = RefInvoke.findMethodExact("android.content.ContentResolver", ClassLoader.getSystemClassLoader(), "insert", Uri.class, ContentValues.class); hookhelper.hookMethod(insertmethod, new AbstractBahaviorHookCallBack() { @Override public void descParam(HookParam param) { // TODO Auto-generated method stub Uri uri = (Uri) param.args[0]; if (isSensitiveUri(uri)) { Logger.log_behavior("Insert ContentProvider -> Uri = " + uri.toString()); ContentValues cv = (ContentValues) param.args[1]; String insertStr = concatenateInsert(uri, cv); Logger.log_behavior("Insert SQL = " + insertStr); } } }); Method bulkInsertmethod = RefInvoke.findMethodExact("android.content.ContentResolver", ClassLoader.getSystemClassLoader(), "bulkInsert", Uri.class, ContentValues[].class); hookhelper.hookMethod(bulkInsertmethod, new AbstractBahaviorHookCallBack() { @Override public void descParam(HookParam param) { // TODO Auto-generated method stub Uri uri = (Uri) param.args[0]; if (isSensitiveUri(uri)) { ContentValues[] cv = (ContentValues[]) param.args[1]; Logger.log_behavior("Bulk Insert ContentProvider -> Uri = " + uri.toString()); String insertStr = null; for (int i = 0; i < cv.length; i++) { insertStr = concatenateInsert(uri, cv[i]); Logger.log_behavior("Insert " + i + " SQL = " + insertStr); } } } }); Method deletemethod = RefInvoke.findMethodExact("android.content.ContentResolver", ClassLoader.getSystemClassLoader(), "delete", Uri.class, String.class, String[].class); hookhelper.hookMethod(deletemethod, new AbstractBahaviorHookCallBack() { @Override public void descParam(HookParam param) { // TODO Auto-generated method stub Uri uri = (Uri) param.args[0]; if (isSensitiveUri(uri)) { Logger.log_behavior("Delete ContentProvider -> uri= " + uri.toString()); String deleteStr = concatenateDelete(uri, (String) param.args[1], (String[]) param.args[2]); Logger.log_behavior("Delete SQL = " + deleteStr); } } }); Method updatemethod = RefInvoke.findMethodExact("android.content.ContentResolver", ClassLoader.getSystemClassLoader(), "update", Uri.class, ContentValues.class, String.class, String[].class); hookhelper.hookMethod(updatemethod, new AbstractBahaviorHookCallBack() { @Override public void descParam(HookParam param) { // TODO Auto-generated method stub Uri uri = (Uri) param.args[0]; if (isSensitiveUri(uri)) { Logger.log_behavior("Update ContentProvider -> uri=" + uri.toString()); String updateStr = concatenateUpdate(uri, (ContentValues) param.args[1], (String) param.args[2], (String[]) param.args[3]); Logger.log_behavior("Update SQL = " + updateStr); } } }); Method applyBatchMethod = RefInvoke.findMethodExact("android.content.ContentResolver", ClassLoader.getSystemClassLoader(), "applyBatch", String.class, ArrayList.class); hookhelper.hookMethod(applyBatchMethod, new AbstractBahaviorHookCallBack() { @Override public void descParam(HookParam param) { // TODO Auto-generated method stub ArrayList<ContentProviderOperation> opts = (ArrayList<ContentProviderOperation>) param.args[1]; for(int i=0; i< opts.size(); i++){ Logger.log_behavior("Batch SQL = " + descContentProviderOperation(opts.get(i))); } } }); } private Set<String> getContentValuesKeySet(ContentValues cv){ HashMap<String,Object> mValue = (HashMap<String,Object>) RefInvoke.getFieldOjbect("android.content.ContentValues", cv, "mValues"); return mValue.keySet(); } private final static int TYPE_INSERT = 1; private final static int TYPE_UPDATE = 2; private final static int TYPE_DELETE = 3; private String descContentProviderOperation(ContentProviderOperation opt) { String sqlstr = null; int mType = RefInvoke.getFieldInt("android.content.ContentProviderOperation", opt, "mType"); switch (mType) { case TYPE_INSERT: { Uri uri = (Uri) RefInvoke.getFieldOjbect("android.content.ContentProviderOperation", opt, "mUri"); ContentValues cv = (ContentValues) RefInvoke.getFieldOjbect("android.content.ContentProviderOperation", opt, "mValues"); sqlstr = concatenateInsert(uri, cv); break; } case TYPE_UPDATE: { Uri uri = (Uri) RefInvoke.getFieldOjbect("android.content.ContentProviderOperation", opt, "mUri"); ContentValues cv = (ContentValues) RefInvoke.getFieldOjbect("android.content.ContentProviderOperation", opt, "mValues"); String selection = (String) RefInvoke.getFieldOjbect("android.content.ContentProviderOperation", opt, "mSelection"); String[] selectionArgs = (String[]) RefInvoke.getFieldOjbect("android.content.ContentProviderOperation", opt, "mSelectionArgs"); sqlstr = concatenateUpdate(uri, cv, selection, selectionArgs); break; } case TYPE_DELETE: Uri uri = (Uri) RefInvoke.getFieldOjbect("android.content.ContentProviderOperation", opt, "mUri"); String selection = (String) RefInvoke.getFieldOjbect("android.content.ContentProviderOperation", opt, "mSelection"); String[] selectionArgs = (String[]) RefInvoke.getFieldOjbect("android.content.ContentProviderOperation", opt, "mSelectionArgs"); sqlstr = concatenateDelete(uri, selection, selectionArgs); break; } return sqlstr; } }