Skip to content

Commit c1e76e2

Browse files
committed
增加插件校验步骤,签名验证失败或者插件与宿主证书不同时不予安装,
1 parent 6e85763 commit c1e76e2

File tree

7 files changed

+304
-34
lines changed

7 files changed

+304
-34
lines changed

PluginCore/src/com/plugin/core/PluginIntentResolver.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,12 @@ public class PluginIntentResolver {
3939

4040
/* package */static void hackReceiverForClassLoader(Object msgObj) {
4141
Intent intent = (Intent) RefInvoker.getFieldObject(msgObj, "android.app.ActivityThread$ReceiverData", "intent");
42+
LogUtil.d("receiver", intent.toUri(0));
4243
if (intent.getComponent().getClassName().equals(PluginStubReceiver.class.getName())) {
4344
Intent realIntent = (Intent) (intent.getParcelableExtra(RECEIVER_ID_IN_PLUGIN));
44-
LogUtil.d("receiver", realIntent.toUri(0));
45+
if (realIntent == null) {
46+
return;
47+
}
4548
intent.putExtras(realIntent.getExtras());
4649
String realClassName = PluginLoader.isMatchPlugin(realIntent);
4750
// PluginReceiverClassLoader检测到这个特殊标记后会进行替换
@@ -97,6 +100,7 @@ public static Intent resolveNotificationIntent(Intent intent) {
97100
return intent;
98101
}
99102

103+
@SuppressWarnings("ResourceType")
100104
public static PendingIntent resolvePendingIntent(PendingIntent origin) {
101105
if (origin != null) {
102106
Intent originIntent = (Intent)RefInvoker.invokeMethod(origin,

PluginCore/src/com/plugin/core/PluginLoader.java

Lines changed: 61 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,13 @@
1313
import android.content.Context;
1414
import android.content.ContextWrapper;
1515
import android.content.Intent;
16+
import android.content.pm.PackageInfo;
17+
import android.content.pm.PackageManager;
18+
import android.content.pm.Signature;
1619
import android.content.res.Resources;
1720
import android.os.Handler;
1821
import android.text.TextUtils;
22+
import android.util.Log;
1923

2024
import com.plugin.content.PluginDescriptor;
2125
import com.plugin.content.PluginIntentFilter;
@@ -26,11 +30,14 @@
2630
import com.plugin.util.LogUtil;
2731
import com.plugin.util.ManifestParser;
2832
import com.plugin.util.FileUtil;
33+
import com.plugin.util.PackageVerifyer;
2934
import com.plugin.util.RefInvoker;
3035

3136
import dalvik.system.DexClassLoader;
3237

3338
public class PluginLoader {
39+
40+
private static final boolean needVefifyCert = true;
3441
private static Application sApplication;
3542
private static Object activityThread;
3643
private static PluginManager pluginManager;
@@ -141,48 +148,76 @@ public static boolean isInstalled(String pluginId, String pluginVersion) {
141148
* @return
142149
*/
143150
public static synchronized boolean installPlugin(String srcPluginFile) {
144-
LogUtil.d("Install plugin ", srcPluginFile);
151+
LogUtil.d("开始安装插件", srcPluginFile);
152+
153+
//第0步,先将apk复制到宿主程序私有目录,防止在安装过程中文件被篡改
154+
//TODO XXX
155+
156+
// 第1步,验证插件APK签名,如果被篡改过,将获取不到证书
157+
//sApplication.getPackageManager().getPackageArchiveInfo(srcPluginFile, PackageManager.GET_SIGNATURES);
158+
Signature[] pluginSignatures = PackageVerifyer.collectCertificates(srcPluginFile, false);
159+
if (pluginSignatures == null) {
160+
LogUtil.e("插件签名验证失败", srcPluginFile);
161+
return false;
162+
} else if (needVefifyCert) {
163+
//可选步骤,验证插件APK证书是否和宿主程序证书相同。
164+
//证书中存放的是公钥和算法信息,而公钥和私钥是1对1的
165+
//公钥相同意味着是同一个作者发布的程序
166+
Signature[] mainSignatures = null;
167+
try {
168+
PackageInfo pkgInfo = sApplication.getPackageManager().getPackageInfo(sApplication.getPackageName(), PackageManager.GET_SIGNATURES);
169+
mainSignatures = pkgInfo.signatures;
170+
} catch (PackageManager.NameNotFoundException e) {
171+
e.printStackTrace();
172+
}
173+
if (!PackageVerifyer.isSignaturesSame(mainSignatures, pluginSignatures)) {
174+
LogUtil.d("插件证书和宿主证书不一致", srcPluginFile);
175+
return false;
176+
}
177+
}
145178

146-
boolean isInstallSuccess = false;
147-
// 第一步,读取插件描述文件
179+
// 第2步,解析Manifest,获得插件详情
148180
PluginDescriptor pluginDescriptor = ManifestParser.parseManifest(srcPluginFile);
149181
if (pluginDescriptor == null || TextUtils.isEmpty(pluginDescriptor.getPackageName())) {
150-
return isInstallSuccess;
182+
LogUtil.d("解析插件Manifest文件失败", srcPluginFile);
183+
return false;
151184
}
152185

153-
// 第二步,检查插件是否已经存在,若存在删除旧的
186+
// 第2步,检查插件是否已经存在,若存在删除旧的
154187
PluginDescriptor oldPluginDescriptor = getPluginDescriptorByPluginId(pluginDescriptor.getPackageName());
155188
if (oldPluginDescriptor != null) {
156189
remove(pluginDescriptor.getPackageName());
157190
}
158191

159-
// 第三步骤,复制插件到插件目录
160-
if (pluginDescriptor != null) {
161-
162-
String destPluginFile = genInstallPath(pluginDescriptor.getPackageName(), pluginDescriptor.getVersion());
163-
boolean isCopySuccess = FileUtil.copyFile(srcPluginFile, destPluginFile);
164-
if (isCopySuccess) {
165-
166-
//第四步,复制插件so到插件so目录, 在构造插件Dexclassloader的时候,会使用这个so目录作为参数
167-
File tempDir = new File(new File(destPluginFile).getParentFile(), "temp");
168-
Set<String> soList = FileUtil.unZipSo(srcPluginFile, tempDir);
169-
if (soList != null) {
170-
for (String soName : soList) {
171-
FileUtil.copySo(tempDir, soName, new File(destPluginFile).getParent() + File.separator + "lib");
172-
}
192+
// 第4步骤,复制插件到插件目录
193+
String destPluginFile = genInstallPath(pluginDescriptor.getPackageName(), pluginDescriptor.getVersion());
194+
boolean isCopySuccess = FileUtil.copyFile(srcPluginFile, destPluginFile);
195+
if (isCopySuccess) {
196+
197+
//第5步,复制插件so到插件so目录, 在构造插件Dexclassloader的时候,会使用这个so目录作为参数
198+
File tempDir = new File(new File(destPluginFile).getParentFile(), "temp");
199+
Set<String> soList = FileUtil.unZipSo(srcPluginFile, tempDir);
200+
if (soList != null) {
201+
for (String soName : soList) {
202+
FileUtil.copySo(tempDir, soName, new File(destPluginFile).getParent() + File.separator + "lib");
173203
}
204+
}
174205

175-
// 第五步 添加到已安装插件列表
176-
pluginDescriptor.setInstalledPath(destPluginFile);
177-
isInstallSuccess = pluginManager.addOrReplace(pluginDescriptor);
206+
// 第6步 添加到已安装插件列表
207+
pluginDescriptor.setInstalledPath(destPluginFile);
208+
boolean isInstallSuccess = pluginManager.addOrReplace(pluginDescriptor);
178209

179-
if (isInstallSuccess) {
180-
changeListener.onPluginInstalled(pluginDescriptor.getPackageName(), pluginDescriptor.getVersion());
181-
}
210+
if (isInstallSuccess) {
211+
changeListener.onPluginInstalled(pluginDescriptor.getPackageName(), pluginDescriptor.getVersion());
212+
LogUtil.d("安装插件成功", srcPluginFile);
213+
return true;
182214
}
215+
} else {
216+
LogUtil.d("复制插件到安装目录失败", srcPluginFile);
183217
}
184218

185-
return isInstallSuccess;
219+
LogUtil.d("安装插件失败", srcPluginFile);
220+
return false;
186221
}
187222

188223
/**

PluginCore/src/com/plugin/util/FileUtil.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ public static Set<String> unZipSo(String apkFile, File tempDir) {
123123
tempDir.mkdirs();
124124
}
125125

126-
LogUtil.d("开始解压到", tempDir.getAbsolutePath());
126+
LogUtil.d("开始so文件", tempDir.getAbsolutePath());
127127

128128
ZipFile zfile = null;
129129
boolean isSuccess = false;
@@ -156,7 +156,7 @@ public static Set<String> unZipSo(String apkFile, File tempDir) {
156156
}
157157

158158
File targetFile = new File(tempDir, relativePath);
159-
LogUtil.d("正在解压文件", targetFile.getAbsolutePath());
159+
LogUtil.d("正在解压so文件", targetFile.getAbsolutePath());
160160
if (!targetFile.getParentFile().exists()) {
161161
targetFile.getParentFile().mkdirs();
162162
}
@@ -205,7 +205,7 @@ public static Set<String> unZipSo(String apkFile, File tempDir) {
205205
}
206206
}
207207

208-
LogUtil.d("解压结束", isSuccess);
208+
LogUtil.d("解压so文件结束", isSuccess);
209209
return result;
210210
}
211211

PluginCore/src/com/plugin/util/LogUtil.java

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,22 @@
55
public class LogUtil {
66

77
private static final boolean isDebug = true;
8-
8+
9+
private static final int stackLevel = 4;
10+
11+
public static void v(Object... msg) {
12+
printLog(Log.VERBOSE, msg);
13+
}
14+
915
public static void d(Object... msg) {
16+
printLog(Log.DEBUG, msg);
17+
}
18+
19+
public static void e(Object... msg) {
20+
printLog(Log.ERROR, msg);
21+
}
22+
23+
private static void printLog(int level, Object... msg) {
1024
if (isDebug) {
1125
StringBuilder str = new StringBuilder();
1226

@@ -24,8 +38,8 @@ public static void d(Object... msg) {
2438
StackTraceElement[] sts = Thread.currentThread().getStackTrace();
2539
StackTraceElement st = null;
2640
String tag = null;
27-
if (sts != null && sts.length > 3) {
28-
st = sts[3];
41+
if (sts != null && sts.length > stackLevel) {
42+
st = sts[stackLevel];
2943
if (st != null) {
3044
String fileName = st.getFileName();
3145
tag = (fileName == null) ? "Unkown" : fileName.replace(".java", "");
@@ -37,7 +51,13 @@ public static void d(Object... msg) {
3751
tag = (tag==null)?"Plugin":("Plugin_" + tag);
3852
// use logcat log
3953
while (str.length() > 0) {
40-
Log.v(tag, str.substring(0, Math.min(2000, str.length())).toString());
54+
if (level == Log.DEBUG) {
55+
Log.d(tag, str.substring(0, Math.min(2000, str.length())).toString());
56+
} else if (level == Log.ERROR) {
57+
Log.e(tag, str.substring(0, Math.min(2000, str.length())).toString());
58+
} else {
59+
Log.v(tag, str.substring(0, Math.min(2000, str.length())).toString());
60+
}
4161
str.delete(0, 2000);
4262
}
4363
} catch (Exception exception) {

0 commit comments

Comments
 (0)