From e4cda0b64fd78d0d2bcef96769abcb195321a46d Mon Sep 17 00:00:00 2001 From: hyb1996 <946994919@qq.com> Date: Fri, 23 Feb 2018 11:21:18 +0800 Subject: [PATCH] feat: verify dex files --- app/build.gradle | 4 +- .../scriptdroid/ui/log/LogActivity.java | 1 + .../scriptdroid/ui/main/MainActivity.java | 7 +- .../ui/main/drawer/DrawerFragment.java | 11 +- app/src/main/res/values/strings.xml | 1 + .../com/stardust/util/DeveloperUtils.java | 120 ++++++++++++++++-- .../com/stardust/util/UnderuseExecutors.java | 5 +- 7 files changed, 123 insertions(+), 26 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 20d49f0b..49179cc2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,8 +8,8 @@ android { applicationId "com.stardust.scriptdroid" minSdkVersion 17 targetSdkVersion 23 - versionCode 251 - versionName "3.1.0 Alpha" + versionCode 252 + versionName "3.1.0 Alpha2" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" multiDexEnabled true ndk { diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/log/LogActivity.java b/app/src/main/java/com/stardust/scriptdroid/ui/log/LogActivity.java index 105f9773..5fec53ae 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/log/LogActivity.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/log/LogActivity.java @@ -31,6 +31,7 @@ public class LogActivity extends BaseActivity { setToolbarAsBack(getString(R.string.text_log)); mStardustConsole = (StardustConsole) AutoJs.getInstance().getGlobalConsole(); mConsoleView.setConsole(mStardustConsole); + mConsoleView.findViewById(R.id.input_container).setVisibility(View.GONE); } @Click(R.id.fab) diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/main/MainActivity.java b/app/src/main/java/com/stardust/scriptdroid/ui/main/MainActivity.java index 71c55869..4611ac81 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/main/MainActivity.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/main/MainActivity.java @@ -3,10 +3,8 @@ package com.stardust.scriptdroid.ui.main; import android.Manifest; import android.content.Intent; import android.content.pm.PackageManager; -import android.os.Build; import android.os.Bundle; import android.support.annotation.NonNull; -import android.support.annotation.RequiresApi; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.TabLayout; import android.support.v4.app.ActivityCompat; @@ -41,7 +39,6 @@ import com.stardust.scriptdroid.ui.main.sample.SampleListFragment_; import com.stardust.scriptdroid.ui.main.scripts.MyScriptListFragment_; import com.stardust.scriptdroid.ui.main.task.TaskManagerFragment_; import com.stardust.theme.ThemeColorManager; -import com.stardust.theme.app.ThemeColorMaterialDialog; import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder; import com.stardust.util.DeveloperUtils; import com.stardust.scriptdroid.tool.AccessibilityServiceTool; @@ -253,8 +250,8 @@ public class MainActivity extends BaseActivity implements OnActivityResultDelega @Override protected void onStart() { super.onStart(); - if (!BuildConfig.DEBUG && !DeveloperUtils.checkSignature(this)) { - finish(); + if (!BuildConfig.DEBUG) { + DeveloperUtils.verifyApk(this, R.string.dex_crcs); } if (!DeveloperUtils.isActivityRegistered(this, ADActivity.class) || !DeveloperUtils.isServiceRegistered(this, DownloadService.class)) { diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/main/drawer/DrawerFragment.java b/app/src/main/java/com/stardust/scriptdroid/ui/main/drawer/DrawerFragment.java index b3049a35..4a86dab3 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/main/drawer/DrawerFragment.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/main/drawer/DrawerFragment.java @@ -9,6 +9,7 @@ import android.support.annotation.Nullable; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.text.TextUtils; +import android.util.Log; import android.view.View; import android.widget.TextView; import android.widget.Toast; @@ -310,6 +311,8 @@ public class DrawerFragment extends android.support.v4.app.Fragment { } private void setUpUserInfo(@Nullable User user) { + if (mUserName == null || mAvatar == null) + return; if (user == null) { mUserName.setText(R.string.not_login); mAvatar.setIcon(R.drawable.profile_avatar_placeholder); @@ -318,11 +321,11 @@ public class DrawerFragment extends android.support.v4.app.Fragment { mAvatar.setUser(user); } setCoverImage(user); - - } private void setCoverImage(User user) { + if (mDefaultCover == null || mShadow == null || mHeaderView == null) + return; if (user == null || TextUtils.isEmpty(user.getCoverUrl()) || user.getCoverUrl().equals("/assets/images/cover-default.png")) { mDefaultCover.setVisibility(View.VISIBLE); mShadow.setVisibility(View.GONE); @@ -336,7 +339,9 @@ public class DrawerFragment extends android.support.v4.app.Fragment { .into(new SimpleTarget() { @Override public void onResourceReady(Drawable resource, Transition transition) { - mHeaderView.setBackground(resource); + if (mHeaderView != null) { + mHeaderView.setBackground(resource); + } } }); } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 922c8c8b..a15c0769 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -342,4 +342,5 @@ 获取权限失败 :-( 重新授予 软件需要显示启动页广告来维持服务器和软件的开发和维护,因此需要\"读取手机状态\"的权限。\n如果您不喜欢广告,可以在设置中设定广告每天只显示一次。 + 2399694286\n1797533891\n0 diff --git a/common/src/main/java/com/stardust/util/DeveloperUtils.java b/common/src/main/java/com/stardust/util/DeveloperUtils.java index db2453b3..1f162f5f 100644 --- a/common/src/main/java/com/stardust/util/DeveloperUtils.java +++ b/common/src/main/java/com/stardust/util/DeveloperUtils.java @@ -4,18 +4,25 @@ import android.annotation.SuppressLint; import android.app.Activity; import android.app.Service; import android.content.Context; -import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.Signature; import android.support.annotation.Nullable; import android.util.Base64; +import android.util.Log; +import com.stardust.R; + +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.lang.reflect.Method; import java.security.MessageDigest; -import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; /** * Created by Stardust on 2017/4/5. @@ -24,13 +31,10 @@ import java.util.List; public class DeveloperUtils { private static final String PACKAGE_NAME = "com.stardust.scriptdroid"; - private static final String SIGNATURE = "nPNPcy4Lk/eP6fLvZitP0VPbHdFCbKua77m59vis5fA=\n"; - - public static void ensureRunningPackageNotSelf(@Nullable String runningPackage) { - if (PACKAGE_NAME.equals(runningPackage)) { - throw new SecurityException(); - } - } + private static final String SIGNATURE = "nPNPcy4Lk/eP6fLvZitP0VPbHdFCbKua77m59vis5fA="; + private static final String LOG_TAG = "DeveloperUtils"; + private static final ExecutorService sExecutor = UnderuseExecutors.getExecutor(); + private static final String SALT = "let\nlife\nbe\nbeautiful\nlike\nsummer\nflowers\nand\ndeath\nlike\nautumn\nleaves\n."; public static boolean isSelfPackage(@Nullable String runningPackage) { return PACKAGE_NAME.equals(runningPackage); @@ -40,13 +44,15 @@ public class DeveloperUtils { public static String getSignatureSHA(Context context, String packageName) { try { @SuppressLint("PackageManagerGetSignatures") - PackageInfo packageInfo = context.getPackageManager().getPackageInfo(packageName, PackageManager.GET_SIGNATURES); + PackageInfo packageInfo = getPackageInfo(context, packageName, PackageManager.GET_SIGNATURES); + if (packageInfo == null) + return null; Signature[] signatures = packageInfo.signatures; StringBuilder builder = new StringBuilder(); for (Signature signature : signatures) { MessageDigest md = MessageDigest.getInstance("SHA-256"); md.update(signature.toByteArray()); - final String sha = Base64.encodeToString(md.digest(), Base64.DEFAULT); + final String sha = Base64.encodeToString(md.digest(), Base64.NO_WRAP); builder.append(sha); } return builder.toString(); @@ -56,16 +62,30 @@ public class DeveloperUtils { return null; } + private static PackageInfo getPackageInfo(Context context, String packageName, int flags) { + try { + return context.getPackageManager().getPackageInfo(packageName, flags); + } catch (PackageManager.NameNotFoundException e) { + return null; + } + } + /** * 此方法仅防止那些不会改源码直接用apk编辑器修改应用内字符串(QQ群号)等的恶意用户行为。 * 为了开源社区的发展,请善用源码:-) */ public static boolean checkSignature(Context context) { - return SIGNATURE.equals(getSignatureSHA(context, context.getPackageName())); + return checkSignature(context, context.getPackageName()); } public static boolean checkSignature(Context context, String packageName) { - return SIGNATURE.equals(getSignatureSHA(context, packageName)); + String sha = getSignatureSHA(context, packageName); + if (sha == null) + return false; + if (sha.endsWith("\n")) { + sha = sha.substring(0, sha.length() - 1); + } + return SIGNATURE.equals(sha); } @@ -109,4 +129,76 @@ public class DeveloperUtils { return false; } } + + public static boolean checkDexFile(Context context, long[] crc) { + String apkPath = context.getPackageCodePath(); + try { + ZipFile zipFile = new ZipFile(apkPath); + for (int i = 0; i < crc.length; i++) { + String dexFile; + if (i == 0) { + dexFile = "classes.dex"; + } else { + dexFile = "classes" + (i + 1) + ".dex"; + } + ZipEntry dexEntry = zipFile.getEntry(dexFile); + long dexEntryCrc = dexEntry.getCrc(); + Log.d(LOG_TAG, String.valueOf(dexEntryCrc)); + if (dexEntryCrc != crc[i]) { + return false; + } + } + return true; + } catch (IOException e) { + e.printStackTrace(); + return false; + } + } + + public static void verifyApk(Activity activity, final int crcRes) { + final WeakReference activityWeakReference = new WeakReference<>(activity); + sExecutor.execute(new Runnable() { + @Override + public void run() { + Activity a = activityWeakReference.get(); + if (a == null) + return; + if (!checkSignature(a)) { + a.finish(); + return; + } + long[] crc = readCrc(a.getString(crcRes)); + if (!checkDexFile(a, crc)) { + a.finish(); + } + + } + }); + } + + private static long[] readCrc(String crcStr) { + String[] crcStrings = crcStr.split("\n"); + StringBuilder iHash = new StringBuilder(); + long[] crc = new long[crcStrings.length - 1]; + for (int i = 0; i < crc.length; i++) { + crc[i] = Long.parseLong(crcStrings[i]); + iHash.append(iHash(crcStrings[i])); + } + if (!crcStrings[crcStrings.length - 1].equals(iHash.toString())) { + return new long[crc.length]; + } + return crc; + } + + private static String iHash(String data) { + try { + MessageDigest md5 = MessageDigest.getInstance("MD5"); + for (int i = 0; i < 8; i++) { + data = Base64.encodeToString(md5.digest((data + SALT).getBytes()), Base64.NO_WRAP); + } + return data; + } catch (Exception e) { + return null; + } + } } diff --git a/common/src/main/java/com/stardust/util/UnderuseExecutors.java b/common/src/main/java/com/stardust/util/UnderuseExecutors.java index 76154ec6..ddb287c3 100644 --- a/common/src/main/java/com/stardust/util/UnderuseExecutors.java +++ b/common/src/main/java/com/stardust/util/UnderuseExecutors.java @@ -1,6 +1,7 @@ package com.stardust.util; import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** @@ -9,13 +10,13 @@ import java.util.concurrent.Executors; public class UnderuseExecutors { - private static Executor mExecutor = Executors.newSingleThreadExecutor(); + private static ExecutorService mExecutor = Executors.newSingleThreadExecutor(); public static void execute(Runnable runnable) { mExecutor.execute(runnable); } - public static Executor getExecutor() { + public static ExecutorService getExecutor() { return mExecutor; } }