Presentation is loading. Please wait.

Presentation is loading. Please wait.

©wequick GalenLin All rights reserved

Similar presentations


Presentation on theme: "©wequick GalenLin All rights reserved"— Presentation transcript:

1 ©wequick GalenLin All rights reserved
FBI WARNING 本文件仅作为学习交流之用 请勿用以商业用途 ©wequick GalenLin All rights reserved

2 Small:插件化轻巧之道 林光亮

3 首页 0x00 诞生 0x01 0x02 0x03 TODO

4 0x00 诞生 诞生-头疼 模块1 模块2 模块3 模块4 模块5 模块6

5 两篇文章 诞生 分析支付宝客户端插件机制 @唐巧-猿题库 手机淘宝客户端架构探索实践 @于佳-阿里 模块1 模块2 模块3 模块4 模块5
模块6 分析支付宝客户端插件机制 @唐巧-猿题库 手机淘宝客户端架构探索实践 @于佳-阿里

6 基础架构 诞生 + Bundle Launcher 分析支付宝客户端插件机制 @唐巧-猿题库 手机淘宝客户端架构探索实践 @于佳-阿里
iOS HTML Android Bundle Launcher

7 开源探索 诞生 + Bundle Launcher Dynamic-Load-APK @任玉刚-百度 Direct-Load-APK
Android Bundle Launcher + iOS HTML Dynamic-Load-APK @任玉刚-百度 Direct-Load-APK @罗迪-高中生

8 DLA架构 诞生 Host Dynamic-Load-APK @任玉刚-百度 Direct-Load-APK @罗迪-高中生 模块1 模块2
Android 模块1 模块2 模块3 Dynamic-Load-APK @任玉刚-百度 Host Direct-Load-APK @罗迪-高中生

9 诞生 不支持公共库 模块1 公共 模块 Host 模块3

10 诞生 公共并宿主 模块1 公共 模块 公共 模块 Host 模块3

11 DLA改造 诞生 Host 使用public.xml 锁定公共资源ID 打通宿主与插件的资源与代码共享 模块1 公共 模块 DLA 改进
模块3

12 重新探索 诞生 Gradle 1.3+ Unsupported Host 使用public.xml 锁定公共资源ID
模块1 模块3 公共 模块 DLA 改进 Host 使用public.xml 锁定公共资源ID 打通宿主与插件的资源与代码共享 Gradle 1.3+ Unsupported Android-Plugin-Framework @Limpoxe ACDD @Bunny Blue

13 aapt改造 诞生 aapt Android-Plugin-Framework @Limpoxe ACDD @Bunny Blue 模块1
0x7A 模块2 0x7B 模块3 0x7C Android-Plugin-Framework @Limpoxe aapt ACDD @Bunny Blue

14 诞生 Small诞生 模块1 0x7A 模块2 0x7B 模块3 0x7C aapt

15 A A’ B A C B 轻盈产出 @Compile-time 轻度Hook @Run-time

16 APK结构 B C 轻/轻盈产出 APK A A’ B AM.xml classes.dex resources.arsc res/* A
轻度Hook @Run-time A A’ B AM.xml 轻盈产出 @Compile-time A C B classes.dex APK resources.arsc res/*

17 APK拆解 轻/轻盈产出 AM$1 dex$1 arsc$1 res$1 AM.xml classes.dex resources.arsc
文本 文件(*.jar) 二进制 文件(*.xml/png) 拆分粒度/方案 AM$1 dex$1 arsc$1 res$1 AM.xml classes.dex resources.arsc res/* AM$2 dex$2 arsc$2 res$2

18 Arsc结构 轻/轻盈产出 resources.arsc 0000000: 0000010: 0000020: 0000030:
: : : : : : 00000a0: 00000b0: 00000c0: 00000d0: 00000e0: 00000f0: : : : : 0200 0c c00 8c 2c f 6d69 706d d d f69 635f 6c61 756e e70 6e 6573 2f6d d61 702d d76 342f f70 6c e2e 706e 6700 0c0c 4c e69 6e 0650 6c e b 7f e e b00 2e ....H , %... H...W...""res/mi pmap-hdpi-v4/ic_ launcher.png. r es/mipmap-hdpi-v 4/ic_plugin.png. ..LearningArsc.. .Plugin ....n.e.t...w.e. q.u.i.c.k...a.r. s.c resources.arsc

19 Arsc区域高亮 轻/轻盈产出/arsc格式 0000000: 0000010: 0000020: 0000030: 0000040:
: : : : : 00000a0: 00000b0: 00000c0: 00000d0: 00000e0: 00000f0: : : : : 0200 0c c00 8c 2c f 6d69 706d d d f69 635f 6c61 756e e70 6e 6573 2f6d d61 702d d76 342f f70 6c e2e 706e 6700 0c0c 4c e69 6e 0650 6c e b 7f e e b00 2e ....H , %... H...W...""res/mi pmap-hdpi-v4/ic_ launcher.png. r es/mipmap-hdpi-v 4/ic_plugin.png. ..LearningArsc.. .Plugin ....n.e.t...w.e. q.u.i.c.k...a.r. s.c

20 Arsc区段 轻/轻盈产出/arsc格式 0000000: 0000010: 0000020: 0000030: 0000040:
: : : : : 00000a0: 00000b0: 00000c0: 00000d0: 00000e0: 00000f0: : : : : 0200 0c c00 8c 2c f 6d69 706d d d f69 635f 6c61 756e e70 6e 6573 2f6d d61 702d d76 342f f70 6c e2e 706e 6700 0c0c 4c e69 6e 0650 6c e b 7f e e b00 2e ....H , %... H...W...""res/mi pmap-hdpi-v4/ic_ launcher.png. r es/mipmap-hdpi-v 4/ic_plugin.png. ..LearningArsc.. .Plugin ....n.e.t...w.e. q.u.i.c.k...a.r. s.c hex(LE) 小端代码 struct 数据结构 0200 ResTable_header 0100 ResStringPool_header 0002 ResTable_package 0202 ResTable_typeSpec 0102 ResTable_type

21 Arsc读取 轻/轻盈产出/arsc格式 1c00 8c00 0000 0400 0000 0000 0000 0001 0000
f 6d69 706d d d f69 635f 6c61 756e e70 6e 6573 2f6d d61 702d d76 342f f70 6c e2e 706e 6700 0c0c 4c e69 6e 0650 6c e00 0200 0c 0100 hex(LE) 小端代码 struct 数据结构 0200 ResTable_header 0100 ResStringPool_header 0002 ResTable_package 0202 ResTable_typeSpec 0102 ResTable_type                2001 b 7f e e b00 2e 0002

22 Arsc/ID解析 轻/轻盈产出/arsc格式 资源ID 并非实际存在 hex(LE) 小端代码 struct 数据结构 0200
ResTable_header 0100 ResStringPool_header 0002 ResTable_package 0202 ResTable_typeSpec 0102 ResTable_type table: { package: { id: 0x7F, name: "net.wequick.arsc" }, strings: [ "res/mipmap-hdpi-v4/ic_launcher.png", "res/mipmap-hdpi-v4/ic_plugin.png", "LearningArsc", "Plugin" ], typeStrings: [ "attr", "mipmap", "string", "style" ], keyStrings: [ "ic_launcher", "ic_plugin", "app_name", "s_plugin", "AppTheme", "PluginTheme" typeSpecs: [ { types: [] }, { types: [ ] }, { types: [ ] }, { types: [ ] } ] } 7F 资源ID PP 包ID TT 类型ID NNNN 项目ID 01 02 03 04 0x 01 0000 0001 02 资源ID 并非实际存在 03 04

23 分离方案1 轻/轻盈产出/arsc分离 想象中最简单的分离方式 存在问题:必须补齐资源(输出变大)、只能分离一个插件 host (0x7f)
table: { package: { id: 0x7F, name: "net.wequick.arsc" }, strings: [ "res/mipmap-hdpi-v4/ic_launcher.png", "res/mipmap-hdpi-v4/ic_plugin.png", "LearningArsc", "Plugin" ], typeStrings: [ "attr", "mipmap", "string", "style" ], keyStrings: [ "ic_launcher", "ic_plugin", "app_name", "s_plugin", "AppTheme", "PluginTheme" typeSpecs: [ { types: [] }, { types: [ ] }, { types: [ ] }, { types: [ ] } ] } 01 02 03 04 0001 host (0x7f) |-- mipmap (02) | |-- ic_launcher (0000) | `-- values |-- strings.xml (03) | |-- app_name (0000) `-- syles.xml (04) |-- AppTheme (0000) plugin (0x7f) |-- mipmap (02) | |-- | `-- values |-- strings.xml (03) `-- syles.xml (04) |-- `-- ic_plugin (0001) `-- s_plugin (0001) `-- PluginTheme (0001) padding_mipmap_0000 资源ID PP 包ID TT 类型ID NNNN 项目ID 02 7F 0x 0000 padding_string_0000 padding_style_0000 存在问题:必须补齐资源(输出变大)、只能分离一个插件

24 分离方案2 轻/轻盈产出/arsc分离 实践中最极致的分离方式 host (0x7f) |-- mipmap (02)
| |-- ic_launcher (0000) | `-- values |-- strings.xml (03) | |-- app_name (0000) `-- syles.xml (04) |-- AppTheme (0000) plugin ( |-- mipmap (02) | | | `-- ic_plugin ( `-- values |-- strings.xml (03) | `-- s_plugin ( `-- syles.xml (04) | `-- PluginTheme ( 0x7e ) 0000 0000 0000

25 融合 轻/轻盈产出 plugin ( |-- mipmap (02) | | | `-- ic_plugin ( `-- values
| | | `-- ic_plugin ( `-- values |-- strings.xml (03) | `-- s_plugin ( `-- syles.xml (04) | `-- PluginTheme ( ) 0000 0x7e HOST 0x7e 0x7d 7c

26 轻度Hook 我重写一个「对象」的「方法」 轻/轻度Hook 我是一个Hook 让她忘了从前 0x7e HOST 方法A Hook A
方法B 我是一个Hook 我重写一个「对象」的「方法」 让她忘了从前 HOST 0x7e 0x7d 7c 一个对象

27 Hook条件 我重写一个「对象」的「方法」 轻/轻度Hook 我是一个Hook 让她忘了从前 但是首先 我要找到她 她「静态」的坐着
方法A Hook A 方法B 我是一个Hook 我重写一个「对象」的「方法」 让她忘了从前 但是首先 我要找到她 在我的「进程」里 她「静态」的坐着 她的方法向我「开放」 一个对象

28 Hook任务 轻/轻度Hook 但是首先 我要找到她 在 我的「进程」 里 她「静态」的坐着 她的方法向我「开放」
一个对象 方法A Hook A 方法B 但是首先 我要找到她 她「静态」的坐着 她的方法向我「开放」 com.user.galen 我的「进程」 启动 插件Activity

29 Activity启动过程 轻/轻度Hook Not Found 系统进程 我的「进程」 我的「进程」 system.process
Intent解析 任务栈调度 Activity栈调度 我的「进程」 com.user.galen 启动 插件Activity 我的「进程」 com.user.galen 实际启动 插件Activity Not Found

30 伪装宿主 轻/轻度Hook Not Found 系统进程 我的「进程」 我的「进程」 system.process
Intent解析 任务栈调度 Activity栈调度 我的「进程」 我的「进程」 com.user.galen 实际启动 插件Activity com.user.galen 启动 插件Activity Hook 伪装宿主 Not Found

31 还原插件 轻/轻度Hook 系统进程 我的「进程」 我的「进程」 system.process com.user.galen
Intent解析 任务栈调度 Activity栈调度 我的「进程」 我的「进程」 com.user.galen com.user.galen 启动 插件Activity Hook 伪装宿主 Hook 还原插件 实际启动 插件Activity

32 还原解析 轻/轻度Hook 我的「进程」 系统进程 com.user.galen system.process Intent解析 任务栈调度
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { // 创建Activity if (r.activityInfo.targetActivity != null) { component = new ComponentName(r.activityInfo.packageName, r.activityInfo.targetActivity); } java.lang.ClassLoader cl = r.packageInfo.getClassLoader(); activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); // 绑定Context Context appContext = createBaseContextForActivity(r, activity); activity.attach(appContext, this, getInstrumentation(), ...); // 设置主题 int theme = r.activityInfo.getThemeResource(); if (theme != 0) { activity.setTheme(theme); // 触发onCreate mInstrumentation.callActivityOnCreate(activity, r.state); 我的「进程」 com.user.galen 系统进程 system.process Intent解析 任务栈调度 Activity栈调度 启动 插件Activity Hook 伪装宿主 还原插件 Small Droid Plugin 实际启动 插件Activity Android-Plugin- Framework ACDD Dynamic-Load-APK Direct-Load-APK

33 Android-Plugin-Framework
轻/轻度Hook 方案对比 插件方案 代表框架 包数 类加载器 个数 资源管理器 Context 完全隔离 Droid Plugin 1/插件 Dynamic-Load-APK 1 1/插件Activity Direct-Load-APK 宿主插件 两两融合 Android-Plugin-Framework 除主题外 完全融合 ACDD Small

34 IDE友好 @Debug 模块变身 @Release

35 IDE友好 巧/IDE友好 支持创建插件模块 支持编译插件模块 支持插件模块间依赖 支持联合调试 模块变身 @Release IDE友好
@Debug 支持创建插件模块 支持编译插件模块 支持插件模块间依赖 支持联合调试

36 IDE多模块 巧/IDE友好 支持创建插件模块 支持编译插件模块 支持插件模块间依赖 支持联合调试 app.* web.* app
lib.* lib.*

37 模块依赖 巧/IDE友好 app.* web.* app lib.* lib.* vendor lib.* vendor vendor

38 模块变身 巧/模块变身 模块是开发态,插件是目标态。 模块 开发态 目标态 转换难点 app.* Application模块
可以依赖其他模块、可以独立运行 带代码、资源的插件 AAR(代码/资源)剥离 lib.* Library模块 可以被app.*依赖 资源ID锁定 [other].* 可以独立运行 仅含assets的插件

39 AAR分离1 巧/模块变身 模块是开发态,插件是目标态。 模块 开发态 目标态 转换难点 app.* Application模块
dependencies { provided fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:23.2.1' compile 'com.android.support:design:23.2.1' } 巧/模块变身 AAR分离1 模块是开发态,插件是目标态。 模块 开发态 目标态 转换难点 app.* Application模块 可以依赖其他模块、可以独立运行 带代码、资源的插件 AAR(代码/资源)剥离 lib.* Library模块 可以被app.*依赖 资源ID锁定 [other].* 可以独立运行 仅含assets的插件 AAR

40 AAR分离2 巧/模块变身 classes.jar *.class classes.dex classes.dex res/* res/*
dependencies { provided fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:23.2.1' compile 'com.android.support:design:23.2.1' } app.main `-- build/intermediates/exploded-aar `-- com.android.support/appcompat-v7/23.2.1 dex classes.jar *.class classes.dex classes.dex aapt Small res/* res/* arsc res/* arsc res/*

41 总结 巧/模块变身 总结 classes.jar res/* arsc classes.dex *.class 开发时聚合 编译时分离
AAR dependencies { provided fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:23.2.1' compile 'com.android.support:design:23.2.1' } app.main `-- build/intermediates/exploded-aar `-- com.android.support/appcompat-v7/23.2.1 classes.jar res/* aapt arsc Small dex classes.dex *.class IDE多模块 公共库依赖 开发时聚合 PP分段 AAR分离 编译时分离 并入宿主 轻度Hook 运行时融合

42 TODO 总结 TODO 开发时聚合 编译时分离 运行时融合 按需加载 iOS插件化App Store布局 IDE多模块 公共库依赖
插件间依赖关系 iOS插件化App Store布局 xib级别的warm swap IDE多模块 公共库依赖 开发时聚合 PP分段 AAR分离 编译时分离 并入宿主 轻度Hook 运行时融合

43 GalenLin


Download ppt "©wequick GalenLin All rights reserved"

Similar presentations


Ads by Google