AES
This commit is contained in:
@@ -46,6 +46,7 @@ public class APISecurity {
|
||||
* @param str
|
||||
*/
|
||||
public static native String sign(String str);
|
||||
public static native String getRelayPackName();
|
||||
public static native void verifyApp(Application applicationByReflect);
|
||||
public static native boolean init(Context context);
|
||||
|
||||
@@ -151,54 +152,14 @@ public class APISecurity {
|
||||
} else {
|
||||
//第四中方法 本包
|
||||
path = context.getPackageResourcePath();
|
||||
//第五种方法
|
||||
// path = context.getPackageCodePath();
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动构建 Context
|
||||
*/
|
||||
@SuppressLint({"DiscouragedPrivateApi", "PrivateApi"})
|
||||
public static Context createContext() throws ClassNotFoundException,
|
||||
NoSuchMethodException,
|
||||
InvocationTargetException,
|
||||
IllegalAccessException,
|
||||
NoSuchFieldException,
|
||||
NullPointerException {
|
||||
|
||||
// 反射获取 ActivityThread 的 currentActivityThread 获取 mainThread
|
||||
Class activityThreadClass = Class.forName("android.app.ActivityThread");
|
||||
Method currentActivityThreadMethod =
|
||||
activityThreadClass.getDeclaredMethod("currentActivityThread");
|
||||
currentActivityThreadMethod.setAccessible(true);
|
||||
Object mainThreadObj = currentActivityThreadMethod.invoke(null);
|
||||
|
||||
// 反射获取 mainThread 实例中的 mBoundApplication 字段
|
||||
Field mBoundApplicationField = activityThreadClass.getDeclaredField("mBoundApplication");
|
||||
mBoundApplicationField.setAccessible(true);
|
||||
Object mBoundApplicationObj = mBoundApplicationField.get(mainThreadObj);
|
||||
|
||||
// 获取 mBoundApplication 的 packageInfo 变量
|
||||
if (mBoundApplicationObj == null)
|
||||
throw new NullPointerException("mBoundApplicationObj 反射值空");
|
||||
Class mBoundApplicationClass = mBoundApplicationObj.getClass();
|
||||
Field infoField = mBoundApplicationClass.getDeclaredField("info");
|
||||
infoField.setAccessible(true);
|
||||
Object packageInfoObj = infoField.get(mBoundApplicationObj);
|
||||
|
||||
// 反射调用 ContextImpl.createAppContext(ActivityThread mainThread, LoadedApk packageInfo)
|
||||
if (mainThreadObj == null) throw new NullPointerException("mainThreadObj 反射值空");
|
||||
if (packageInfoObj == null) throw new NullPointerException("packageInfoObj 反射值空");
|
||||
Method createAppContextMethod = Class.forName("android.app.ContextImpl").getDeclaredMethod(
|
||||
"createAppContext",
|
||||
mainThreadObj.getClass(),
|
||||
packageInfoObj.getClass());
|
||||
createAppContextMethod.setAccessible(true);
|
||||
return (Context) createAppContextMethod.invoke(null, mainThreadObj, packageInfoObj);
|
||||
|
||||
}
|
||||
|
||||
//需要读取应用列表权限
|
||||
//**安装列表**需要读取应用列表权限
|
||||
public static List<String> getAppList(Context context) {
|
||||
List<String> list = new ArrayList<>();
|
||||
PackageManager pm = context.getPackageManager();
|
||||
@@ -261,7 +222,7 @@ public class APISecurity {
|
||||
list.add(line.split(":")[1]);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e("runCommand", "e=" + e);
|
||||
Log.e("runCommand", "e=" + e.getMessage());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
@@ -323,29 +284,5 @@ public class APISecurity {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***
|
||||
* 防代理
|
||||
*/
|
||||
private boolean isProxy(Context context) {
|
||||
String proxyAddress = "";
|
||||
int proxyPort = 0;
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
proxyAddress = System.getProperty("http.proxyHost");
|
||||
String proxyPortString = System.getProperty("http.proxyPort");
|
||||
proxyPort = Integer.parseInt((proxyPortString != null ? proxyPortString : "-1"));
|
||||
} else {
|
||||
proxyAddress = android.net.Proxy.getHost(context);
|
||||
proxyPort = android.net.Proxy.getPort(context);
|
||||
}
|
||||
if (!TextUtils.isEmpty(proxyAddress) && proxyPort != -1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// 忽视代理
|
||||
// OkHttpClient okHttpClient = new OkHttpClient.Builder()
|
||||
// .proxy(Proxy.NO_PROXY)
|
||||
// .build();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package cn.android.security;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
@@ -14,12 +15,15 @@ import androidx.annotation.StringDef;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.math.BigInteger;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
@@ -27,6 +31,8 @@ import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 签名验证
|
||||
@@ -37,12 +43,6 @@ import java.util.Arrays;
|
||||
|
||||
public class AppSigning {
|
||||
|
||||
static String TAG = "AppSigning";
|
||||
|
||||
public final static String MD5 = "MD5";
|
||||
public final static String SHA1 = "SHA1";
|
||||
public final static String SHA256 = "SHA256";
|
||||
|
||||
/**
|
||||
* 注解限定String类型为指定
|
||||
*/
|
||||
@@ -50,6 +50,99 @@ public class AppSigning {
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface SigniType {
|
||||
}
|
||||
public final static String MD5 = "MD5";
|
||||
public final static String SHA1 = "SHA1";
|
||||
public final static String SHA256 = "SHA256";
|
||||
|
||||
/**
|
||||
* 提早检测签名
|
||||
*/
|
||||
public static void earlyCheckSign() {
|
||||
// 手动构造 context
|
||||
try {
|
||||
Context context = createContext();
|
||||
//用新 context 校验签名的过程(正常的检测一样)
|
||||
String sing = APISecurity.getInstalledAPKSignature(context, context.getPackageName());
|
||||
Log.e("mhyLog手动构造", sing);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动构建 Context
|
||||
*/
|
||||
@SuppressLint({"DiscouragedPrivateApi", "PrivateApi"})
|
||||
public static Context createContext() throws ClassNotFoundException,
|
||||
NoSuchMethodException,
|
||||
InvocationTargetException,
|
||||
IllegalAccessException,
|
||||
NoSuchFieldException,
|
||||
NullPointerException {
|
||||
|
||||
// 反射获取 ActivityThread 的 currentActivityThread 获取 mainThread
|
||||
Class activityThreadClass = Class.forName("android.app.ActivityThread");
|
||||
Method currentActivityThreadMethod =
|
||||
activityThreadClass.getDeclaredMethod("currentActivityThread");
|
||||
currentActivityThreadMethod.setAccessible(true);
|
||||
Object mainThreadObj = currentActivityThreadMethod.invoke(null);
|
||||
|
||||
// 反射获取 mainThread 实例中的 mBoundApplication 字段
|
||||
Field mBoundApplicationField = activityThreadClass.getDeclaredField("mBoundApplication");
|
||||
mBoundApplicationField.setAccessible(true);
|
||||
Object mBoundApplicationObj = mBoundApplicationField.get(mainThreadObj);
|
||||
|
||||
// 获取 mBoundApplication 的 packageInfo 变量
|
||||
if (mBoundApplicationObj == null)
|
||||
throw new NullPointerException("mBoundApplicationObj 反射值空");
|
||||
Class mBoundApplicationClass = mBoundApplicationObj.getClass();
|
||||
Field infoField = mBoundApplicationClass.getDeclaredField("info");
|
||||
infoField.setAccessible(true);
|
||||
Object packageInfoObj = infoField.get(mBoundApplicationObj);
|
||||
|
||||
// 反射调用 ContextImpl.createAppContext(ActivityThread mainThread, LoadedApk packageInfo)
|
||||
if (mainThreadObj == null) throw new NullPointerException("mainThreadObj 反射值空");
|
||||
if (packageInfoObj == null) throw new NullPointerException("packageInfoObj 反射值空");
|
||||
Method createAppContextMethod = Class.forName("android.app.ContextImpl").getDeclaredMethod(
|
||||
"createAppContext",
|
||||
mainThreadObj.getClass(),
|
||||
packageInfoObj.getClass());
|
||||
createAppContextMethod.setAccessible(true);
|
||||
return (Context) createAppContextMethod.invoke(null, mainThreadObj, packageInfoObj);
|
||||
|
||||
}
|
||||
/**
|
||||
* 校验 application
|
||||
*/
|
||||
public static boolean checkApplication(){
|
||||
//在这里使用反射 获取比较靠谱 如果 被替换换 就查出来了
|
||||
Application nowApplication = getApplicationByReflect();
|
||||
APISecurity.verifyApp(nowApplication);
|
||||
String trueApplicationName = "cn.android.sample.MyApplication";//getSimpleName()自己的Application类名 防止替换
|
||||
String nowApplicationName = nowApplication.getClass().getName();
|
||||
Log.e("mhyLogAppName", "反射获取:"+nowApplicationName);
|
||||
return trueApplicationName.equals(nowApplicationName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过反射获取 当前application
|
||||
*/
|
||||
@SuppressLint("PrivateApi")
|
||||
public static Application getApplicationByReflect() {
|
||||
try {
|
||||
Class<?> activityThread = Class.forName("android.app.ActivityThread");
|
||||
Object thread = activityThread.getMethod("currentActivityThread").invoke(null);
|
||||
Object app = activityThread.getMethod("getApplication").invoke(thread);
|
||||
if (app == null) {
|
||||
throw new NullPointerException("you should init first");
|
||||
}
|
||||
return (Application) app;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
throw new NullPointerException("you should init first");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取相应的类型的签名信息(把签名的byte[]信息转换成16进制)
|
||||
@@ -158,7 +251,7 @@ public class AppSigning {
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测 PackageManager 代理
|
||||
* 检测 PackageManager 被代理
|
||||
*/
|
||||
@SuppressLint("PrivateApi")
|
||||
public static boolean checkPMProxy(Context context) {
|
||||
@@ -172,11 +265,12 @@ public class AppSigning {
|
||||
Object mPM = mPMField.get(packageManager);
|
||||
// 取得类名
|
||||
nowPMName = mPM.getClass().getName();
|
||||
Log.e("mhyLog_PM代理",truePMName+"=="+nowPMName);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
// 类名改变说明被代理了
|
||||
return truePMName.equals(nowPMName);
|
||||
return !truePMName.equals(nowPMName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -222,14 +316,14 @@ public class AppSigning {
|
||||
Field infoField = mSigningDetails.getClass().getDeclaredField("signatures");
|
||||
infoField.setAccessible(true);
|
||||
Signature[] info = (Signature[]) infoField.get(mSigningDetails);
|
||||
return AppSigning.getSignatureString(info,AppSigning.SHA1);
|
||||
return getSignatureString(info,AppSigning.SHA1);
|
||||
}else {
|
||||
Method pkgParser_collectCertificatesMtd = pkgParserCls.getDeclaredMethod("collectCertificates", pkgParserPkg.getClass(), Integer.TYPE);
|
||||
pkgParser_collectCertificatesMtd.invoke(pkgParser, pkgParserPkg, PackageManager.GET_SIGNATURES);
|
||||
|
||||
Field packageInfoFld = pkgParserPkg.getClass().getDeclaredField("mSignatures");
|
||||
Signature[] info = (Signature[]) packageInfoFld.get(pkgParserPkg);
|
||||
return AppSigning.getSignatureString(info,AppSigning.SHA1);
|
||||
return getSignatureString(info,AppSigning.SHA1);
|
||||
}
|
||||
|
||||
|
||||
@@ -243,7 +337,7 @@ public class AppSigning {
|
||||
// 应用程序信息包, 这个公开的, 不过有些函数, 变量没公开
|
||||
Field packageInfoFld = pkgParserPkg.getClass().getDeclaredField("mSignatures");
|
||||
Signature[] info = (Signature[]) packageInfoFld.get(pkgParserPkg);
|
||||
return AppSigning.getSignatureString(info,AppSigning.SHA1);
|
||||
return getSignatureString(info,AppSigning.SHA1);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e("getAPKSignatures", e.getMessage());
|
||||
@@ -304,11 +398,72 @@ public class AppSigning {
|
||||
Signature[] info = (Signature[]) packageInfoFld.get(pkgParserPkg);
|
||||
// MediaApplication.logD(DownloadApk.class, "size:"+info.length);
|
||||
// MediaApplication.logD(DownloadApk.class, info[0].toCharsString());
|
||||
return AppSigning.getSignatureString(info,AppSigning.SHA1);
|
||||
return getSignatureString(info,AppSigning.SHA1);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过判断云端MD5判断apk是否被改过
|
||||
*/
|
||||
public static String apkMD5(Context context){
|
||||
return getFileMD5(new File(context.getPackageCodePath()),16);
|
||||
// context.getPackageResourcePath();
|
||||
}
|
||||
/**
|
||||
* 获取单个文件的MD5值
|
||||
* @param file 文件
|
||||
* @param radix 位 16 32 64
|
||||
*/
|
||||
|
||||
public static String getFileMD5(File file,int radix) {
|
||||
if (!file.isFile()) {
|
||||
return null;
|
||||
}
|
||||
MessageDigest digest = null;
|
||||
FileInputStream in = null;
|
||||
byte buffer[] = new byte[1024];
|
||||
int len;
|
||||
try {
|
||||
digest = MessageDigest.getInstance(MD5);
|
||||
in = new FileInputStream(file);
|
||||
while ((len = in.read(buffer, 0, 1024)) != -1) {
|
||||
digest.update(buffer, 0, len);
|
||||
}
|
||||
in.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
BigInteger bigInt = new BigInteger(1, digest.digest());
|
||||
return bigInt.toString(radix);
|
||||
}
|
||||
/**
|
||||
* 获取文件夹中文件的MD5值
|
||||
* @param file
|
||||
* @param listChild ;true递归子目录中的文件
|
||||
*/
|
||||
public static Map<String, String> getDirMD5(File file, boolean listChild) {
|
||||
if (!file.isDirectory()) {
|
||||
return null;
|
||||
}
|
||||
Map<String, String> map = new HashMap<>();
|
||||
String md5;
|
||||
File files[] = file.listFiles();
|
||||
for (int i = 0; i < files.length; i++) {
|
||||
File f = files[i];
|
||||
if (f.isDirectory() && listChild) {
|
||||
map.putAll(getDirMD5(f, listChild));
|
||||
} else {
|
||||
md5 = getFileMD5(f,16);
|
||||
if (md5 != null) {
|
||||
map.put(f.getPath(), md5);
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,14 +34,20 @@ public class InitProvider extends ContentProvider {
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
Context application = getContext().getApplicationContext();
|
||||
if (application == null) {
|
||||
application = getApplicationByReflect();
|
||||
if (application == null) {//这时候context有可能没准备好 可以通过反射获取
|
||||
application = AppSigning.getApplicationByReflect();
|
||||
}
|
||||
Log.e("mhyLog", "initContentProvider:");
|
||||
// chekSignature(application);
|
||||
//checkApplication();
|
||||
chekSignature(application);
|
||||
|
||||
Log.e("mhyLog检测Provider_APP", String.valueOf(AppSigning.checkApplication()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查 PM是否被代理! 被代理我就恢复
|
||||
*/
|
||||
void chekSignature(Context application){
|
||||
//防hook 并检测
|
||||
if (AppSigning.checkPMProxy(application)) {
|
||||
@@ -51,38 +57,6 @@ public class InitProvider extends ContentProvider {
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* 校验 application
|
||||
*/
|
||||
|
||||
private boolean checkApplication(){
|
||||
//在这里使用反射 获取比较靠谱 如果 被替换换 就查出来了
|
||||
Application nowApplication = getApplicationByReflect();
|
||||
APISecurity.verifyApp(nowApplication);
|
||||
String trueApplicationName = "cn.android.sample.MyApplication";//getSimpleName()自己的Application类名 防止替换
|
||||
String nowApplicationName = nowApplication.getClass().getName();
|
||||
Log.e("mhyLogAppName", "反射获取:"+nowApplicationName);
|
||||
return trueApplicationName.equals(nowApplicationName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过反射获取 当前application
|
||||
*/
|
||||
@SuppressLint("PrivateApi")
|
||||
public Application getApplicationByReflect() {
|
||||
try {
|
||||
Class<?> activityThread = Class.forName("android.app.ActivityThread");
|
||||
Object thread = activityThread.getMethod("currentActivityThread").invoke(null);
|
||||
Object app = activityThread.getMethod("getApplication").invoke(thread);
|
||||
if (app == null) {
|
||||
throw new NullPointerException("you should init first");
|
||||
}
|
||||
return (Application) app;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
throw new NullPointerException("you should init first");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
|
||||
@@ -15,9 +15,14 @@ package cn.android.security;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
|
||||
|
||||
/**
|
||||
@@ -25,7 +30,28 @@ import java.net.URL;
|
||||
* Date 2021/3/14 23:43
|
||||
*/
|
||||
public class NetProxy {
|
||||
public static boolean isProxy(Context ctx) {
|
||||
|
||||
// 忽视代理
|
||||
// OkHttpClient okHttpClient = new OkHttpClient.Builder()
|
||||
// .proxy(Proxy.NO_PROXY)
|
||||
// .build();
|
||||
private void noProxy(String urlStr){
|
||||
// try {
|
||||
// URL url =new URL(urlStr);
|
||||
// HttpURLConnection urlConnection=(HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
|
||||
// OkHttpClient client =new OkhttpClient().newBuilder().proxy(Proxy.NO_PROXY).build;
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
}
|
||||
|
||||
public static boolean isHookNet(Context context){
|
||||
return isProxy(context)||isVpnUsed();
|
||||
}
|
||||
/**
|
||||
* 网络是否代理
|
||||
*/
|
||||
private static boolean isProxy(Context ctx) {
|
||||
final boolean IS_ICS_OR_LATER = Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH;
|
||||
String proxyAddress;
|
||||
int proxyPort;
|
||||
@@ -39,13 +65,29 @@ public class NetProxy {
|
||||
}
|
||||
return (!TextUtils.isEmpty(proxyAddress))&&(proxyPort!=-1);
|
||||
}
|
||||
private void noProxy(String urlStr){
|
||||
// try {
|
||||
// URL url =new URL(urlStr);
|
||||
// HttpURLConnection urlConnection=(HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
|
||||
// OkHttpClient client =new OkhttpClient().newBuilder().proxy(Proxy.NO_PROXY).build;
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
|
||||
/**
|
||||
* 是否正在使用VPN
|
||||
*/
|
||||
private static boolean isVpnUsed() {
|
||||
try {
|
||||
Enumeration<?> niList = NetworkInterface.getNetworkInterfaces();
|
||||
if(niList != null) {
|
||||
for (Object obj : Collections.list(niList)) {
|
||||
NetworkInterface intf=(NetworkInterface)obj;
|
||||
if(!intf.isUp() || intf.getInterfaceAddresses().size() == 0) {
|
||||
continue;
|
||||
}
|
||||
Log.d("-----", "isVpnUsed() NetworkInterface Name: " + intf.getName());
|
||||
if ("tun0".equals(intf.getName()) || "ppp0".equals(intf.getName())){
|
||||
return true; // The VPN is up
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user