跳转到帖子
View in the app

A better way to browse. Learn more.

网域社区-让世界触手可及

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.
欢迎来到网域社区,网域社区以延续互联网共享精神为荣!我们免费分享开心版(破解版)软件、php源码等;

推荐的帖子

发布于

一、项目概述

DroidPlugin是由360手机助手团队开发的Android插件化框架,于2015年首次发布,目前处于终止更新状态。核心目标是在免Root环境下实现APK文件的“免安装运行”,即无需经过系统安装流程即可直接加载并运行插件中的四大组件Activity、Service、BroadcastReceiver、ContentProvider

核心技术特性

免Root的应用层Hook:通过动态代理技术拦截系统关键API,无需依赖Root权限即可实现插件组件加载,突破系统对组件注册的硬性限制。

零安装与系统目录零占用:插件APK无需安装到系统应用目录/data/app,可直接从存储加载,支持“独立安装应用”与“纯插件”两种运行模式。

完全隔离机制:插件间、插件与宿主间实现代码与资源的沙箱化隔离,避免组件冲突与数据干扰,保障运行独立性。

二、技术原理分析

DroidPlugin的核心实现围绕“逆向绕过系统组件管理机制”展开,通过“Manifest欺骗→AMS拦截→资源路由→插件Service前台化”的完整调用链,实现插件组件的非法加载与运行。以下为关键技术链路的代码与原理解析:

完整调用链解析:Manifest欺骗→AMS拦截→资源路由→插件Service前台化下面把DroidPlugin实现「Manifest欺骗→AMS拦截→资源路由→插件Service前台化」的完整调用链、关键类和核心代码片段一次性梳理给你。所有行号以2016-12-20最后一次主干代码为准,类路径保持不变,可直接在仓库里grep验证。
1.Manifest欺骗(让未注册的组件合法通过AMS检查)核心目标:绕过Android系统对组件必须在Manifest中注册的静态检查,为插件组件提供“合法身份”。
1.1宿主占坑
实现文件:host/src/main/AndroidManifest.xml
关键代码

01

02

03

04

05

06

07

08

09

10

11

<activity android:name=".stub.StubActivityC0" android:exported="false"/>

 

<activity android:name=".stub.StubActivityC1" android:exported="false"/>

 

<!-- 共预注册C0-C99 100个StubActivity -->

 

<service android:name=".stub.StubServiceC0" android:exported="false"/>

 

<service android:name=".stub.StubServiceC1" android:exported="false"/>

 

<!--共预注册C0-C99 100个StubService-->



释义:宿主在Manifest中预先注册100个无实际业务逻辑的“占位组件”(StubActivity、StubService等),命名遵循“C0-C99”规则,满足系统对组件注册的静态校验要求,为后续插件组件替换提供“壳身份”。
1.2运行时偷梁换柱
Hook点:IActivityManager代理类HookedActivityManagerHandler
实现文件:com/morgoo/droidplugin/hook/handle/HookedActivityManagerHandler.java
关键代码

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

 

    if ("startActivity".equals(method.getName())) {

 

        Intent raw = (Intent) args[2];

 

        // 把插件真实Component替换为未占用的Stub组件

 

        ComponentName stub = StubSelector.selectStubActivity(raw);

 

        raw.setComponent(stub);            // 欺骗AMS,使其认为启动的是宿主注册的Stub组件

 

    }

 

    return method.invoke(mBase, args);

 

}



释义:通过动态代理拦截系统startActivity等方法,将插件的真实组件(如com.example.plugin.MainActivity)替换为宿主中未被占用的Stub组件(如StubActivityC0),使AMS(ActivityManagerService)误认为启动的是已注册的合法组件,绕过注册检查。
1.3AMS放行后换真身
Hook点ActivityThread.mH的LAUNCH_ACTIVITY事件
实现文件HookedActivityThreadHandler.java
关键代码

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

case LAUNCH_ACTIVITY:

 

    ActivityClientRecord r = (ActivityClientRecord) msg.obj;

 

    Intent intent = r.intent;

 

    // 从Intent中提取插件真实组件,替换Stub组件

 

    ComponentName plugin = IntentCompat.getSelector(intent);

 

    r.intent.setComponent(plugin);

 

    // 后续通过反射正常创建插件Activity实例

 

    break;



释义:在AMS放行组件启动请求后,通过Hook ActivityThread的消息处理机制,将Intent中的Stub组件换回插件真实组件,确保最终启动的是插件中的实际业务组件,完成“欺骗-还原”闭环。
2.AMS拦截(所有Binder入口都被动态代理替换)核心目标:拦截系统服务(AMS、PMS)的Binder调用,伪造插件“已安装”的假象,实现组件生命周期管理。 2.1Hook 时机
实现文件PluginHelper.java
关键代码

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

[url=home.php?mod=space&uid=1892347]@Override[/url]

 

protected void attachBaseContext(Context base) {

 

    PluginHelper.getInstance().applicationAttachBaseContext(base);

 

    // 在宿主初始化时Hook系统服务

 

    AMSHook.hookActivityManager(context);   // Hook AMS

 

    PMSHook.hookPackageManager(context);   // Hook PMS

 

    super.attachBaseContext(base);

 

}



释义:在宿主 Application 初始化阶段,通过反射 Hook AMS(ActivityManagerService)和 PMS(PackageManagerService)的 Binder 接口,替换为框架自定义的代理对象,实现对系统服务调用的全程拦截。
2.2IActivityManager 代理链
代理链路:AMSHook→HookedActivityManagerHandler
拦截方法:startActivity、startService、stopService、bindService、unbindService、getContentProvider、registerReceiver 等。
关键逻辑:将插件的组件调用请求(如startService(pluginServiceIntent))替换为宿主 Stub 组件的调用请求,使系统始终认为操作的是宿主内部组件,规避跨应用组件调用限制。2.3IPackageManager 代理链
代理链路:PMSHook →HookedPackageManagerHandler
拦截方法:getPackageInfo ()、queryIntentActivities ()、getApplicationInfo () 等。
关键逻辑:当系统查询插件包信息时,返回伪造的 “已安装” 数据(如插件包名、版本、权限等),使系统误认为插件已通过正常流程安装,从而允许其组件被调用。3. 资源路由(插件独立 Resources,与宿主隔离)核心目标:实现插件与宿主的资源完全隔离,避免资源ID冲突(如R.drawable.icon 重名导致的显示异常)。 3.1创建插件LoadedApk
实现文件PluginProcessManager.java
关键代码

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

LoadedApk pluginApk = Reflector.with(host).field("mPackages")

 

                 .call("get", pluginPkgName).get();

 

// 为插件创建独立的AssetManager

 

AssetManager am = AssetManager.class.newInstance();

 

Reflector.with(am).method("addAssetPath", String.class)

 

         .call(pluginApk.getResDir());  // 加载插件APK的资源路径

 

// 基于独立AssetManager创建插件Resources

 

Resources pluginRes = new Resources(am,

 

        host.getResources().getDisplayMetrics(),

 

        host.getResources().getConfiguration());


释义:通过反射为每个插件创建独立的AssetManager和Resources实例,仅加载插件APK中的资源文件(res、assets目录),使插件资源与宿主资源完全隔离,避免因资源ID重复导致的冲突。
3.2Context 注入
实现类:PluginContext(继承 ContextWrapper)
关键重写方法

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

@Override

 

public Resources getResources() {

 

    return mPluginResources;  // 返回插件独立Resources

 

}

 

@Override

 

public AssetManager getAssets() {

 

    return mPluginResources.getAssets();  // 返回插件独立AssetManager

 

}

 

@Override

 

public String getPackageName() {

 

    return mPluginPkgName;  // 返回插件包名,而非宿主包名

 

}


释义:通过自定义PluginContext覆盖资源获取方法,确保插件代码中调用getResources()、getAssets()等接口时,访问的是插件自身的资源,而非宿主资源,实现资源路由的彻底隔离。
3.3Activity 启动时挂接
实现文件HookedInstrumentation.java
关键代码

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

@Override

 

public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException {

 

    Activity pluginActivity = (Activity) clazz.newInstance();

 

    // 创建插件专属Context并注入Activity

 

    Context pluginCtx = new PluginContext(realIntent, pluginRes, pluginPkgName);

 

    Reflector.with(pluginActivity).field("mBase").set(pluginCtx);  // 替换Activity的mBase为插件Context

 

    return pluginActivity;

 

}


释义:在插件Activity实例化时,将其内部的Context(mBase)替换为插件专属的PluginContext,确保Activity生命周期中所有资源访问均指向插件自身,彻底切断与宿主资源的关联。
4. 插件Service前台化(防止被系统回收)核心目标:通过复用宿主前台服务身份,提升插件Service的进程优先级,避免因系统内存不足被杀死。 4.1仍然用StubService 占位
实现文件:host/src/main/AndroidManifest.xml
关键代码

1

2

3

4

5

<service android:name=".stub.StubServiceC0"

 

         android:foregroundServiceType="media|location"

 

         android:exported="false"/>


  • 释义:宿主预注册的StubService在Manifest中声明为前台服务(foregroundServiceType),使其具备系统赋予的高优先级,为插件Service复用前台身份提供基础。

4.2StubService.onStartCommand () 里二次转发
实现文件StubService.java
关键代码

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

public int onStartCommand(Intent raw, int flags, int startId) {

 

    // 从Intent中提取插件Service的真实请求

 

    Intent pluginIntent = raw.getParcelableExtra(EXTRA_TARGET_INTENT);

 

    // 获取插件Service实例

 

    Service plugin = PluginServiceManager.getService(pluginIntent);

 

    if (plugin != null) {

 

        // 复用StubService的前台身份,提升插件Service优先级

 

        startForeground(notificationId, notification);

 

        // 转发启动命令给插件Service

 

        return plugin.onStartCommand(pluginIntent, flags, startId);

 

    }

 

    return super.onStartCommand(raw, flags, startId);

 

}


释义:StubService作为“壳服务”接收系统启动命令后,提取插件Service的真实请求,复用自身前台服务身份(调用startForeground     ()),并将命令转发给插件Service,使插件Service间接获得前台优先级,降低被系统回收的概率。
4.3前台通知持久化
实现逻辑:由宿主统一管理前台通知的创建与更新,即使插件进程处于空转状态(无活跃操作),系统仍会识别为前台服务,维持进程优先级≥FOREGROUND,保障插件核心功能的持续运行。三、局限性分析
DroidPlugin的设计缺陷与技术限制使其在逆向场景中面临多重障碍,主要包括:
1. 通知限制
技术根源:PluginContext的资源隔离机制导致插件无法直接访问系统通知服务的自定义资源接口。
具体表现:无法发送带自定义RemoteLayout或通过R.drawable.XXX指定图标的Notification,插件通知图标会被强制转为Bitmap,可能导致显示异常。
逆向影响:常规调试无法直接跟踪插件通知逻辑,需通过Hook NotificationManagerService拦截通知发送过程,增加调试复杂度。2. IntentFilter限制
技术根源:插件组件未在系统中真正注册,其声明的IntentFilter无法被系统索引。
具体表现:插件的Service、Activity等组件无法通过隐式Intent被外部应用调用,仅支持显式Intent启动。
逆向影响:动态调试时无法通过系统Intent分发机制唤醒插件组件,需静态解析插件Manifest提取组件信息,手动构造显式Intent触发逻辑。3. Native 支持不足
技术根源:Hook机制仅作用于Java层,未对Native层(.so文件)函数进行拦截与适配。
具体表现:含Native代码的插件(如游戏、加密加固应用)可能无法运行,存在进程崩溃或功能失效风险。
逆向影响:Java层调试工具(如AndroidStudioDebugger)对Native逻辑失效,需结合IDAPro 等工具单独分析.so 文件,补充逆向链路。4. 静态广播处理缺陷
技术根源:插件静态广播被转为动态广播处理,依赖插件进程存活状态。
具体表现:若插件进程未启动或意外终止,静态广播事件无法触发,导致依赖广播的逻辑(如开机启动、网络变化监听)失效。
逆向影响:调试静态广播需维持插件进程存活,需通过宿主主动启动插件 Service,增加调试环境稳定性维护成本。

参与讨论

你可以现在发布并稍后注册. 如果你有帐户,现在就登录发布帖子.

游客
回帖…

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.