feat: verify dex files
This commit is contained in:
@@ -8,8 +8,8 @@ android {
|
|||||||
applicationId "com.stardust.scriptdroid"
|
applicationId "com.stardust.scriptdroid"
|
||||||
minSdkVersion 17
|
minSdkVersion 17
|
||||||
targetSdkVersion 23
|
targetSdkVersion 23
|
||||||
versionCode 251
|
versionCode 252
|
||||||
versionName "3.1.0 Alpha"
|
versionName "3.1.0 Alpha2"
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
multiDexEnabled true
|
multiDexEnabled true
|
||||||
ndk {
|
ndk {
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ public class LogActivity extends BaseActivity {
|
|||||||
setToolbarAsBack(getString(R.string.text_log));
|
setToolbarAsBack(getString(R.string.text_log));
|
||||||
mStardustConsole = (StardustConsole) AutoJs.getInstance().getGlobalConsole();
|
mStardustConsole = (StardustConsole) AutoJs.getInstance().getGlobalConsole();
|
||||||
mConsoleView.setConsole(mStardustConsole);
|
mConsoleView.setConsole(mStardustConsole);
|
||||||
|
mConsoleView.findViewById(R.id.input_container).setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Click(R.id.fab)
|
@Click(R.id.fab)
|
||||||
|
|||||||
@@ -3,10 +3,8 @@ package com.stardust.scriptdroid.ui.main;
|
|||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.RequiresApi;
|
|
||||||
import android.support.design.widget.FloatingActionButton;
|
import android.support.design.widget.FloatingActionButton;
|
||||||
import android.support.design.widget.TabLayout;
|
import android.support.design.widget.TabLayout;
|
||||||
import android.support.v4.app.ActivityCompat;
|
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.scripts.MyScriptListFragment_;
|
||||||
import com.stardust.scriptdroid.ui.main.task.TaskManagerFragment_;
|
import com.stardust.scriptdroid.ui.main.task.TaskManagerFragment_;
|
||||||
import com.stardust.theme.ThemeColorManager;
|
import com.stardust.theme.ThemeColorManager;
|
||||||
import com.stardust.theme.app.ThemeColorMaterialDialog;
|
|
||||||
import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder;
|
import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder;
|
||||||
import com.stardust.util.DeveloperUtils;
|
import com.stardust.util.DeveloperUtils;
|
||||||
import com.stardust.scriptdroid.tool.AccessibilityServiceTool;
|
import com.stardust.scriptdroid.tool.AccessibilityServiceTool;
|
||||||
@@ -253,8 +250,8 @@ public class MainActivity extends BaseActivity implements OnActivityResultDelega
|
|||||||
@Override
|
@Override
|
||||||
protected void onStart() {
|
protected void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
if (!BuildConfig.DEBUG && !DeveloperUtils.checkSignature(this)) {
|
if (!BuildConfig.DEBUG) {
|
||||||
finish();
|
DeveloperUtils.verifyApk(this, R.string.dex_crcs);
|
||||||
}
|
}
|
||||||
if (!DeveloperUtils.isActivityRegistered(this, ADActivity.class) ||
|
if (!DeveloperUtils.isActivityRegistered(this, ADActivity.class) ||
|
||||||
!DeveloperUtils.isServiceRegistered(this, DownloadService.class)) {
|
!DeveloperUtils.isServiceRegistered(this, DownloadService.class)) {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import android.support.annotation.Nullable;
|
|||||||
import android.support.v7.widget.LinearLayoutManager;
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
@@ -310,6 +311,8 @@ public class DrawerFragment extends android.support.v4.app.Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setUpUserInfo(@Nullable User user) {
|
private void setUpUserInfo(@Nullable User user) {
|
||||||
|
if (mUserName == null || mAvatar == null)
|
||||||
|
return;
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
mUserName.setText(R.string.not_login);
|
mUserName.setText(R.string.not_login);
|
||||||
mAvatar.setIcon(R.drawable.profile_avatar_placeholder);
|
mAvatar.setIcon(R.drawable.profile_avatar_placeholder);
|
||||||
@@ -318,11 +321,11 @@ public class DrawerFragment extends android.support.v4.app.Fragment {
|
|||||||
mAvatar.setUser(user);
|
mAvatar.setUser(user);
|
||||||
}
|
}
|
||||||
setCoverImage(user);
|
setCoverImage(user);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setCoverImage(User 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")) {
|
if (user == null || TextUtils.isEmpty(user.getCoverUrl()) || user.getCoverUrl().equals("/assets/images/cover-default.png")) {
|
||||||
mDefaultCover.setVisibility(View.VISIBLE);
|
mDefaultCover.setVisibility(View.VISIBLE);
|
||||||
mShadow.setVisibility(View.GONE);
|
mShadow.setVisibility(View.GONE);
|
||||||
@@ -336,7 +339,9 @@ public class DrawerFragment extends android.support.v4.app.Fragment {
|
|||||||
.into(new SimpleTarget<Drawable>() {
|
.into(new SimpleTarget<Drawable>() {
|
||||||
@Override
|
@Override
|
||||||
public void onResourceReady(Drawable resource, Transition<? super Drawable> transition) {
|
public void onResourceReady(Drawable resource, Transition<? super Drawable> transition) {
|
||||||
mHeaderView.setBackground(resource);
|
if (mHeaderView != null) {
|
||||||
|
mHeaderView.setBackground(resource);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -342,4 +342,5 @@
|
|||||||
<string name="text_no_phone_state_permission">获取权限失败 :-(</string>
|
<string name="text_no_phone_state_permission">获取权限失败 :-(</string>
|
||||||
<string name="text_request_permission">重新授予</string>
|
<string name="text_request_permission">重新授予</string>
|
||||||
<string name="info_no_phone_state_permission">软件需要显示启动页广告来维持服务器和软件的开发和维护,因此需要\"读取手机状态\"的权限。\n如果您不喜欢广告,可以在设置中设定广告每天只显示一次。</string>
|
<string name="info_no_phone_state_permission">软件需要显示启动页广告来维持服务器和软件的开发和维护,因此需要\"读取手机状态\"的权限。\n如果您不喜欢广告,可以在设置中设定广告每天只显示一次。</string>
|
||||||
|
<string name="dex_crcs">2399694286\n1797533891\n0</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -4,18 +4,25 @@ import android.annotation.SuppressLint;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.pm.ActivityInfo;
|
import android.content.pm.ActivityInfo;
|
||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.pm.ResolveInfo;
|
|
||||||
import android.content.pm.ServiceInfo;
|
import android.content.pm.ServiceInfo;
|
||||||
import android.content.pm.Signature;
|
import android.content.pm.Signature;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.util.Base64;
|
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.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.
|
* Created by Stardust on 2017/4/5.
|
||||||
@@ -24,13 +31,10 @@ import java.util.List;
|
|||||||
public class DeveloperUtils {
|
public class DeveloperUtils {
|
||||||
|
|
||||||
private static final String PACKAGE_NAME = "com.stardust.scriptdroid";
|
private static final String PACKAGE_NAME = "com.stardust.scriptdroid";
|
||||||
private static final String SIGNATURE = "nPNPcy4Lk/eP6fLvZitP0VPbHdFCbKua77m59vis5fA=\n";
|
private static final String SIGNATURE = "nPNPcy4Lk/eP6fLvZitP0VPbHdFCbKua77m59vis5fA=";
|
||||||
|
private static final String LOG_TAG = "DeveloperUtils";
|
||||||
public static void ensureRunningPackageNotSelf(@Nullable String runningPackage) {
|
private static final ExecutorService sExecutor = UnderuseExecutors.getExecutor();
|
||||||
if (PACKAGE_NAME.equals(runningPackage)) {
|
private static final String SALT = "let\nlife\nbe\nbeautiful\nlike\nsummer\nflowers\nand\ndeath\nlike\nautumn\nleaves\n.";
|
||||||
throw new SecurityException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isSelfPackage(@Nullable String runningPackage) {
|
public static boolean isSelfPackage(@Nullable String runningPackage) {
|
||||||
return PACKAGE_NAME.equals(runningPackage);
|
return PACKAGE_NAME.equals(runningPackage);
|
||||||
@@ -40,13 +44,15 @@ public class DeveloperUtils {
|
|||||||
public static String getSignatureSHA(Context context, String packageName) {
|
public static String getSignatureSHA(Context context, String packageName) {
|
||||||
try {
|
try {
|
||||||
@SuppressLint("PackageManagerGetSignatures")
|
@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;
|
Signature[] signatures = packageInfo.signatures;
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
for (Signature signature : signatures) {
|
for (Signature signature : signatures) {
|
||||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||||
md.update(signature.toByteArray());
|
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);
|
builder.append(sha);
|
||||||
}
|
}
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
@@ -56,16 +62,30 @@ public class DeveloperUtils {
|
|||||||
return null;
|
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群号)等的恶意用户行为。
|
* 此方法仅防止那些不会改源码直接用apk编辑器修改应用内字符串(QQ群号)等的恶意用户行为。
|
||||||
* 为了开源社区的发展,请善用源码:-)
|
* 为了开源社区的发展,请善用源码:-)
|
||||||
*/
|
*/
|
||||||
public static boolean checkSignature(Context context) {
|
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) {
|
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;
|
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<Activity> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.stardust.util;
|
package com.stardust.util;
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -9,13 +10,13 @@ import java.util.concurrent.Executors;
|
|||||||
|
|
||||||
public class UnderuseExecutors {
|
public class UnderuseExecutors {
|
||||||
|
|
||||||
private static Executor mExecutor = Executors.newSingleThreadExecutor();
|
private static ExecutorService mExecutor = Executors.newSingleThreadExecutor();
|
||||||
|
|
||||||
public static void execute(Runnable runnable) {
|
public static void execute(Runnable runnable) {
|
||||||
mExecutor.execute(runnable);
|
mExecutor.execute(runnable);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Executor getExecutor() {
|
public static ExecutorService getExecutor() {
|
||||||
return mExecutor;
|
return mExecutor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user