ContentProvider Hook

5 ContentProvider Hook

ContentProvider的Hook和 service类似,都是通过代理分发技术来实现,必须要实现增删改查四个方法。

还有一点不同的是,可以监听数据库的变化。同样的,也有注册,换马甲以及脱马甲过程。

5.1 ContentProvider注册

同样的, provider个数和service一样的少,因为使用没有Activity那么频繁。

<provider
    android:name=".stub.ContentProviderStub$StubP00"
    android:authorities="com.morgoo.droidplugin_stub_P00"
       android:exported="false"
       android:label="@string/stub_name_povider" />

这些provider都是ContentProviderStub的内部类,并且都继承于ContentProviderStub, ContentProviderStub继承于

AbstractContentProviderStub类, AbstractContentProviderStub类继承于ContentProvider,并且实现了query/insert/delete/update等方法。

5.2 替换

在AMS章节论述过, 调用AMS的getContentProvider方法其实是调用getContentProvider的beforeInvoke方法。

IactivityManagerHookHandle的内部类getContentProvider的beforeInvoke方法逻辑如下,

1,获取真实的Provider,

ProviderInfo info = mHostContext.getPackageManager().resolveContentProvider(name, 0);
mTargetProvider = PluginManager.getInstance().resolveContentProvider(name, 0);

2,获取一个宿主中已注册的Provider,

mStubProvider = PluginManager.getInstance().selectStubProviderInfo(name);

3,替换,

args[index] = mStubProvider.authority;

并且在afterInvoke方法中,直接Hook 了ContentProvider,

IContentProviderHook invocationHandler = new IContentProviderHook(mHostContext, provider, mStubProvider, mTargetProvider, localProvider);
invocationHandler.setEnable(true);
Class<?> clazz = provider.getClass();
List<Class<?>> interfaces = Utils.getAllInterfaces(clazz);
Class[] ifs = interfaces != null && interfaces.size() > 0 ? interfaces.toArray(new Class[interfaces.size()]) : new Class[0];
Object proxyprovider = MyProxy.newProxyInstance(clazz.getClassLoader(), ifs, invocationHandler);
FieldUtils.writeField(invokeResult, "provider", proxyprovider);

IcontentProviderHook相关的结构图如下,


IcontentProviderInvokeHandle的init方法如下,

sHookedMethodHandlers.put("query", new query(mHostContext));
sHookedMethodHandlers.put("getType", new getType(mHostContext));
sHookedMethodHandlers.put("insert", new insert(mHostContext));
sHookedMethodHandlers.put("bulkInsert", new bulkInsert(mHostContext));
sHookedMethodHandlers.put("delete", new delete(mHostContext));
sHookedMethodHandlers.put("update", new update(mHostContext));
sHookedMethodHandlers.put("openFile", new openFile(mHostContext));
sHookedMethodHandlers.put("openAssetFile", new openAssetFile(mHostContext));
sHookedMethodHandlers.put("applyBatch", new applyBatch(mHostContext));
sHookedMethodHandlers.put("call", new call(mHostContext));
•••

除了Hook 增删改查之外,还Hook 了一些其他的方法。

这些类都继承于MyHandler方法, MyHandler的beforeInvoke方法如下,

final int index = indexFirstUri(args);
if (index >= 0) {
     Uri uri = (Uri) args[index];
     String authority = uri.getAuthority();
     if (!TextUtils.equals(authority, mStubProvider.authority)) {
         Uri.Builder b = new Builder();
         b.scheme(uri.getScheme());
         b.authority(mStubProvider.authority);
         b.path(uri.getPath());
         b.query(uri.getQuery());
         b.appendQueryParameter(Env.EXTRA_TARGET_AUTHORITY, authority);
         b.fragment(uri.getFragment());
         args[index] = b.build();
       }
  }

把Uri的authority替换成stub provider的authority, 这样增删改查等一系列方法都是访问的是stub provider指向的路径了。

真实的authority放在EXTRA_TARGET_AUTHORITY变量中。

5.3 脱马甲过程

AbstractContentProviderStub的query/insert/delete/update等方法都会脱掉马甲, query方法如下,

String targetAuthority = uri.getQueryParameter(Env.EXTRA_TARGET_AUTHORITY);
if (!TextUtils.isEmpty(targetAuthority) && !TextUtils.equals(targetAuthority, uri.getAuthority())) {
     ContentProviderClient client = getContentProviderClient(targetAuthority);
     try {
         return client.query(buildNewUri(uri, targetAuthority), projection, selection, selectionArgs, sortOrder);
     } catch (RemoteException e) {
         handleExpcetion(e);
     }
}

首先获取真实的Authority,然后根据该值获取ContentProviderClient对象,最后调用对应的query方法。

5.4通知

ContentProvider相对于其他三大组件来说,还有个不同的地方。ContentProvider数据库的变化可以通知注册了

该数据的组件。通过contentService Hook来实现该目的。IcontentServiceBinderHook和其他服务的AP实现的

过程完全一样。IContentServiceBinderHook的结构图如下,


Hook 类

IContentServiceBinderHook

Hook 代理类

IContentServiceHandle

 

Hook 方法实现类

registerContentObserver

notifyChange

 

IContentServiceHandle的内部类IContentServiceHookedMethodHandler的beforeInvoke方法如下,

protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
   if (args != null) {
        final int index = 1;
        if (args.length > index && args[index] instanceof Uri) {
            Uri uri = (Uri) args[index];
            String authority = uri.getAuthority();
            ProviderInfo provider = PluginManager.getInstance().resolveContentProvider(authority, 0);
             if (provider != null) {
                 ProviderInfo info = PluginManager.getInstance().selectStubProviderInfo(authority);
                 Uri.Builder newUri = new Uri.Builder();
                 newUri.scheme("content");
                 newUri.authority(uri.getAuthority());
                 newUri.path(uri.getPath());
                 newUri.query(uri.getQuery());
                        newUri.appendQueryParameter(Env.EXTRA_TARGET_AUTHORITY,authority);
                 args[index] = newUri.build();
//                return true;
              } else {
                 Log.w(TAG, "getContentProvider,fake fail 2=%s", authority);
              }
          }
       }
     return super.beforeInvoke(receiver, method, args);
}

就是将插件的Uri替换成宿主的Uri,这样就可以完成注册和通知了,因为系统只认宿主ContentProvider的相关信息。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值