DroidPlugin插件化开发

360手机助手使用的 DroidPlugin,它是360手机助手团队在Android系统上实现了一种插件机制。它可以在无需安装、修改的情况下运行APK文件,此机制对改进大型APP的架构,实现多团队协作开发具有一定的好处。

它是一种新的插件机制,一种免安装的运行机制

github地址: https://github.com/DroidPluginTeam/DroidPlugin

参考博客:http://blog.csdn.net/hejjunlin/article/details/52124397

    

下载地址:http://download.csdn.net/detail/u010864175/9806093

 

DroidPlugin的的基本原理:

  共享进程:为android提供一个进程运行多个apk的机制,通过API欺骗机制瞒过系统

  占坑:通过预先占坑的方式实现不用在manifest注册,通过一带多的方式实现服务管理

  Hook机制:动态代理实现函数hook,Binder代理绕过部分系统服务限制,IO重定向(先获取原始Object-->Read,然后动态代理Hook Object后-->Write回去,达到瞒天过海的目的)

 

public abstract class Hook {private boolean mEnable = false;//能否hookprotected Context mHostContext;//宿主context,外部传入protected BaseHookHandle mHookHandles;public void setEnable(boolean enable, boolean reInstallHook) {this.mEnable = enable;}public final void setEnable(boolean enable) {setEnable(enable, false);}public boolean isEnable() {return mEnable;}protected Hook(Context hostContext) {mHostContext = hostContext;mHookHandles = createHookHandle();}protected abstract BaseHookHandle createHookHandle();//用于子类创建Hook机制protected abstract void onInstall(ClassLoader classLoader) throws Throwable;//插件安装protected void onUnInstall(ClassLoader classLoader) throws Throwable {//插件卸载
}
}
public class HookedMethodHandler {//Hook方法private static final String TAG = HookedMethodHandler.class.getSimpleName();protected final Context mHostContext;/***  调用方法的时候会到AppOpsService进行判断uid(宿主apk)和插件的包名是否匹配,此处是不匹配的*  此时就可以经过转换欺骗系统让程序认为是宿主apk调过来的(这样的前提就需要宿主把所有的权限都申请了)*  因为系统只会去检测宿主apk* **/private Object mFakedResult = null;//用于欺骗系统private boolean mUseFakedResult = false;public HookedMethodHandler(Context hostContext) {this.mHostContext = hostContext;}public synchronized Object doHookInner(Object receiver, Method method, Object[] args) throws Throwable {long b = System.currentTimeMillis();try {mUseFakedResult = false;mFakedResult = null;boolean suc = beforeInvoke(receiver, method, args);Object invokeResult = null;if (!suc) {//false执行原始方法invokeResult = method.invoke(receiver, args);}afterInvoke(receiver, method, args, invokeResult);if (mUseFakedResult) {//true返回欺骗结果,false返回正常的调用方法return mFakedResult;} else {return invokeResult;}} finally {long time = System.currentTimeMillis() - b;if (time > 5) {Log.i(TAG, "doHookInner method(%s.%s) cost %s ms", method.getDeclaringClass().getName(), method.getName(), time);}}}public void setFakedResult(Object fakedResult) {this.mFakedResult = fakedResult;mUseFakedResult = true;}/*** 在某个方法被调用之前执行,如果返回true,则不执行原始的方法,否则执行原始方法*/protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {return false;}protected void afterInvoke(Object receiver, Method method, Object[] args, Object invokeResult) throws Throwable {}public boolean isFakedResult() {return mUseFakedResult;}public Object getFakedResult() {return mFakedResult;}
}

 

 

abstract class BinderHook extends Hook implements InvocationHandler {private Object mOldObj;public BinderHook(Context hostContext) {super(hostContext);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {if (!isEnable()) {//如果不能Hook,执行原方法return method.invoke(mOldObj, args);}HookedMethodHandler hookedMethodHandler = mHookHandles.getHookedMethodHandler(method);if (hookedMethodHandler != null) {return hookedMethodHandler.doHookInner(mOldObj, method, args);} else {return method.invoke(mOldObj, args);}} catch (InvocationTargetException e) {Throwable cause = e.getTargetException();if (cause != null && MyProxy.isMethodDeclaredThrowable(method, cause)) {throw cause;} else if (cause != null) {RuntimeException runtimeException = !TextUtils.isEmpty(cause.getMessage()) ? new RuntimeException(cause.getMessage()) : new RuntimeException();runtimeException.initCause(cause);throw runtimeException;} else {RuntimeException runtimeException = !TextUtils.isEmpty(e.getMessage()) ? new RuntimeException(e.getMessage()) : new RuntimeException();runtimeException.initCause(e);throw runtimeException;}} catch (IllegalArgumentException e) {try {StringBuilder sb = new StringBuilder();sb.append(" DROIDPLUGIN{");if (method != null) {sb.append("method[").append(method.toString()).append("]");} else {sb.append("method[").append("NULL").append("]");}if (args != null) {sb.append("args[").append(Arrays.toString(args)).append("]");} else {sb.append("args[").append("NULL").append("]");}sb.append("}");String message = e.getMessage() + sb.toString();throw new IllegalArgumentException(message, e);} catch (Throwable e1) {throw e;}} catch (Throwable e) {if (MyProxy.isMethodDeclaredThrowable(method, e)) {throw e;} else {RuntimeException runtimeException = !TextUtils.isEmpty(e.getMessage()) ? new RuntimeException(e.getMessage()) : new RuntimeException();runtimeException.initCause(e);throw runtimeException;}}}abstract Object getOldObj() throws Exception;void setOldObj(Object mOldObj) {this.mOldObj = mOldObj;}public abstract String getServiceName();//具体Hook哪一个service/*** 先调用ServiceManagerCacheBinderHook的onInstall()方法更新一下service cache* 然后生成一个新的代理对象放到mProxiedObjCache里。这样下次不管是从cache里取,还是直接通过binder调用,就都会返回我们的代理对象。* **/@Overrideprotected void onInstall(ClassLoader classLoader) throws Throwable {new ServiceManagerCacheBinderHook(mHostContext, getServiceName()).onInstall(classLoader);mOldObj = getOldObj();Class<?> clazz = mOldObj.getClass();//得到classList<Class<?>> interfaces = Utils.getAllInterfaces(clazz);Class[] ifs = interfaces != null && interfaces.size() > 0 ? interfaces.toArray(new Class[interfaces.size()]) : new Class[0];//用原始对象的classloader传入动态代理,得到代理对象Object proxiedObj = MyProxy.newProxyInstance(clazz.getClassLoader(), ifs, this);MyServiceManager.addProxiedObj(getServiceName(), proxiedObj);}
}

结论就是读取插件apk,和宿主的uid对比,然后进行包替换,在利用binder代理Hook,启动插件,这概括很是大概,不过涉及太复杂

 

然后是使用了,结束和使用都很多资料,很详细,不过自己研究了一翻记录下心得,也能加深理解和印象

 

 

public class MainActivity extends AppCompatActivity {private String filepath = null, packageName = "cn.liuzhen.plugin";private TextView tv_val;private Context context;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);context = MainActivity.this;tv_val = (TextView)findViewById(R.id.tv_val);filepath = Environment.getExternalStorageDirectory().getAbsolutePath().concat("/test.apk");}public void click(View view) {if (filepath == null){Toast.makeText(context,"filepath is null",Toast.LENGTH_SHORT).show();return;}String result = null;int code = -1;try {switch (view.getId()) {case R.id.btn_install:code = PluginManager.getInstance().installPackage(filepath, PackageManagerCompat.INSTALL_REPLACE_EXISTING);result = "install";switch (code) {case PluginManager.INSTALL_FAILED_NO_REQUESTEDPERMISSION:result = "安装失败,文件请求的权限太多";break;case PackageManagerCompat.INSTALL_FAILED_NOT_SUPPORT_ABI:result = "宿主不支持插件的abi环境,可能宿主运行时为64位,但插件只支持32位";break;case PackageManagerCompat.INSTALL_SUCCEEDED:result = "安装完成";break;}break;case R.id.btn_del:PluginManager.getInstance().deletePackage(packageName, 0);result = "del";break;case R.id.btn_open:PackageManager pm = getPackageManager();Intent intent = pm.getLaunchIntentForPackage("cn.liuzhen.plugin");if (intent == null){result = "intent is null";}elsestartActivity(intent);break;}} catch (RemoteException e) {result = "安装失败 "+e.getMessage();}tv_val.setText(result);}}

运行程序成功,然后把运行的apk复制一份,我上面的名称是写死的,test.apk,然后放在根目录,点击安装,显示成功后在点击打开,就能见到跳转到插件界面了,插件化通了

接下来就是看自己怎么设计和开发了,什么东西也不能随便使用,得好好考虑,个人觉得插件化不宜大范围使用,适合小菜单的集成,毕竟都是反射的,而且还得考虑好安全问题

 

转载于:https://www.cnblogs.com/LiuZhen/p/6269340.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/429705.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

io wait linux,另辟蹊径-诊断工具之 IO wait

导读最近在做日志的实时同步&#xff0c;上线之前是做过单份线上日志压力测试的&#xff0c;消息队列和客户端、本机都没问题&#xff0c;但是没想到上了第二份日志之后&#xff0c;问题来了&#xff1a;1、问题&#xff1a;集群中的某台机器 top 看到负载巨高&#xff0c;集群…

Oracle第二天

Oracle第二天 整体安排&#xff08;3天&#xff09; 第一天&#xff1a;Oracle的安装配置&#xff08;服务端和客户端&#xff09;&#xff0c;SQL增强&#xff08;单表查询&#xff09;。 第二天&#xff1a;SQL增强&#xff08;多表查询、子查询、伪列-分页&#xff09;&…

linux搭建虚拟化平台报告,部署KVM虚拟化平台------搭建(示例代码)

一 、部署KVM虚拟化平台hyper-v是windows中的虚拟化1、KVM模块直接整合在Linux内核中&#xff0c;kvm是内核模块&#xff0c;虚拟机与kvm模块之间为管理工具2、KVM组成1.KVM Driver---虚拟机创建---虚拟机内存分配---虚拟CPU寄存器读写---虚拟CPU运行2.QEMU (经过简化与修改)--…

linux 选择文件指定的行数据类型,06练习题

1.如何删除一个非空子目录/tmp&#xff1f; BA. del /tmp/*B. rm -rf /tmpC. rm -Ra /tmp/*D. rm -rf /tmp/*2.存放linux默认系统日志文件是 BA./var/log/dmesg #系统启动时日志B./var/log/messages #系统日志C./var/log/secure #登录相关 安全3.用命令ls -l显示出来文件txt的描…

C#多线程编程

C#多线程编程 一、使用线程的理由 1、可以使用线程将代码同其他代码隔离&#xff0c;提高应用程序的可靠性。 2、可以使用线程来简化编码。 3、可以使用线程来实现并发执行。 二、基本知识 1、进程与线程&#xff1a;进程作为操作系统执行程序的基本单位&#xff0c;拥有应用程…

linux apache24 使用,科学网—linux_centos第24_2次课Apache的安装 - 郭会强的博文

第24次课Apache的安装下载apache的网址[rootghq~]# cd /usr/local/src[rootghqsrc]# lsmysql-5.1.40-linux-i686-icc-glibc23.tar.gz从网上下载&#xff0c;使用命令wget[rootghq src]# wgethttp://apache.fayea.com//httpd/httpd-2.2.31.tar.bz2--2015-08-2007:24:43-- http:/…

《DSP using MATLAB 》示例Example6.3

代码&#xff1a; C0 0; B1 [2 4; 3 1]; A1 [1 1 0.9; 1 0.4 -0.4]; B2 [0.5 0.7; 1.5 2.5; 0.8 1]; A2 [1 -1 0.8; 1 0.5 0.5; 1 0 -0.5]; [b1, a1] par2dir(C0, B1, A1)[b2, a2] par2dir(C0, B2, A2)b conv(b1, b2) % Overall direct form numerator a conv(a1…

简单说明c语言程序步骤,C语言的入门简介和三个简单的C语言程序详细说明

学习一种编程语言&#xff0c;最重要的是建立一个练习环境&#xff0c;边学边练才能学好。Keil软件是目前最流行开发80C51系列单片机的软件&#xff0c;Keil提供了包括C编译器、宏汇编、连接器、库管理和一个功能强大的仿真调试器等在内的完整开发方案&#xff0c;通过一个集成…

java入门,学习笔记

编译 通过javac编译java程序&#xff0c;会编译出一个后缀为class的文件&#xff0c;我们再通过java虚拟机&#xff08;jvm&#xff09;执行编译后的java程序。 在java中始终有一个main函数&#xff0c;它作为程序的入口&#xff0c;程序从这个入口开始执行一直到结束。 public…

C语言 第五章 选择结构 答案,c语言第五章 选择结构程序设计(习题册答案).doc

第五章 选择结构程序设计基础练习(A)一、填空题1、关系表达式的运算结果是 逻辑 值。C语言没有逻辑型数据&#xff0c;以 1 代表“真”&#xff0c;以 0代表“假”。2、逻辑运算符&#xff01;是 单目 运算符&#xff0c;其结合性是由 右结合性 。3、C语言提供的三种逻辑运算符…

linux命令之ifconfig详细解释

依赖于ifconfig命令中使用一些选项属性&#xff0c;ifconfig工具不仅可以被用来简单地获取网络接口配置信息&#xff0c;还可以修改这些配置。 1&#xff0e;命令格式&#xff1a; ifconfig [网络设备] [参数] 2&#xff0e;命令功能&#xff1a; ifconfig 命令用来查看和配置网…

c语言 如何连接两个程序,C语言连接两个链表程序

创建两个链表&#xff0c;并这两链表连接起来成为一个链表的示例程序&#xff0c;将以下代码保存到一个源文件中&#xff1a;combine_linked_list.c&#xff0c; 如下所示 –#include #include struct node { int data; struct node *next; }; struct node *even NULL; struct…

微信小程序UI组件、开发框架、实用库...

UI组件 weui-wxss ★852 - 同微信原生视觉体验一致的基础样式库Wa-UI ★122 - 针对微信小程序整合的一套UI库wx-charts ★105 - 微信小程序图表工具wemark ★85 - 微信小程序Markdown渲染库WeZRender ★36 - 微信小程序Canvas增强组件wetoast ★21 - 仿照微信小程序提供的showT…

c语言编程一个 图书管理,我也要用c语言编程一个图书管理系统,

#includeusing namespace std;const int N 25;//定义20本图书struct Book {char name[25];char author[15];char price[7];char publisher[20];char isbn[20];};int ii 19;Book book[N] { { "红与黑","司汤达","26.00","长江文艺出版社&…

十二个球称三次C语言编程,十二个球,有一个不知轻重,现有一个天平,称三次,找出此球!...

平均分成A、B、C三组,每组4个&#xff1b;第一秤&#xff1a;A、B两组先分别放天平左右&#xff1a;情况一&#xff1a;平衡.则问题出在C组,A、B组共8个为标准球. 第二秤用3个标准球和C组的3个球对比, 如果第二秤平衡,剩下的一个就是问题球,第三秤用标准球和问题球对比,得出问题…

loadrunner controller:实时查看VUser的运行情况

1) 如下图&#xff0c;在Run标签页&#xff0c;点击“Vusers...”打开Vuser窗口&#xff1b; 2) 如下图选中一个Vuser点击按钮可以打开Run-Time Viewer窗口&#xff0c;RTV窗口页面显示的就是该Vuser当前的操作&#xff1b; 3) 如下图所示&#xff0c;…

解决鼠标滚动的时候多次执行函数

有这种场景&#xff0c;鼠标滚动到页面的底部的时候&#xff0c;显示剩余的数据&#xff1b; 这种情况有时候会出现的错误是鼠标滚动的时候会多次触发&#xff0c;同样的多次执行ajax&#xff0c;调取数据&#xff1b; 解决的方法是:设置一个全局变量flag&#xff0c;当鼠标滚动…

上海工程技术大学c语言,上海工程技术大学 C语言实习报告.doc

实 习 报 告《高级语言程序设计》2012&#xff5e;2013学年第 一 学期学院(部) 管理学院指导教师 李旭芳班级代号 0315121姓名/学号 张何兵/031512130同组人 无实验一一&#xff1a;实验项目名称&#xff1a;在函数中进行10个学生成绩从高到低排名 sort(int a[10])二&#xff1…

【二色汉诺塔 】

/* 二色汉诺塔 */#include <stdio.h>void hanoi(int disks, char sources, char temp, char target) {if(disks 1){printf("move disk from %c to %c \n", sources, target);printf("move disk from %c to %c \n", sources, target);}else{hanoi(d…

c语言程序定义不知数量的一维数组,c语言程序设计10-第6章 利用数组处理批量数据 6.1 怎样定义和引用一维数组.ppt...

c语言程序设计10-第6章 利用数组处理批量数据 6.1 怎样定义和引用一维数组* 临沂大学汽车学院&#xff1a;韩晓翠 第6章 利用数组处理批量数据 6.1 怎样定义和引用一维数组 授课要点 数组的概念 一维数组的定义和引用 一维数组的初始化和赋值 数组元素的遍历 问题&#xff1a;从…