This commit is contained in:
mahongyin
2021-03-24 18:04:50 +08:00
parent 28c433af4a
commit ba6eb721e1
26 changed files with 2401 additions and 158 deletions

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -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;
}
}