App.hook(context);//hook签名验证
//在这里 重置PackageManager 只要在验证前重置即可 // AppSigning.resetPackageManager(getBaseContext());再hook之后 验证签名之前重置即可 获取真实签名 APISecurity.init(this)//验证三步走 1.验证签名是否符合自己预设 2.验证包名 3验证apk源文件签名信息
This commit is contained in:
@@ -55,3 +55,11 @@ Android API Security(.so),安卓APP/API安全加密so库,防二次打包,
|
||||
String val = "POST https://www.xxx.com/login?id=1&pwd=xxx......";
|
||||
String sign = MGAPISecurity.sign(aptStr)
|
||||
```
|
||||
App.hook(context);//hook签名验证
|
||||
//在这里 重置PackageManager 只要在验证前重置即可
|
||||
// AppSigning.resetPackageManager(getBaseContext());再hook之后 验证签名之前重置即可 获取真实签名
|
||||
|
||||
APISecurity.init(this)//验证三步走
|
||||
1.验证签名是否符合自己预设
|
||||
2.验证包名
|
||||
3验证apk源文件签名信息
|
||||
@@ -8,7 +8,7 @@ android {
|
||||
targetSdkVersion ANDROID_BUILD_TARGET_SDK_VERSION as int
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
|
||||
}
|
||||
|
||||
@@ -21,5 +21,5 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation 'androidx.annotation:annotation:1.0.0'
|
||||
}
|
||||
@@ -4,14 +4,30 @@ import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.Signature;
|
||||
import android.os.Build;
|
||||
import android.os.Debug;
|
||||
import android.text.TextUtils;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -31,97 +47,108 @@ public class APISecurity {
|
||||
|
||||
/**
|
||||
* 被Native调用的Java方法
|
||||
*
|
||||
* @param msg
|
||||
*/
|
||||
public void javaMethod(String msg) {
|
||||
Log.e("错误代码",msg);
|
||||
Log.e("错误代码", msg);
|
||||
// System.exit(1);
|
||||
}
|
||||
|
||||
private void verify(Context context){
|
||||
// String ppp = runCommand().get(0);
|
||||
|
||||
Log.e("包路径文件签名", getApkSignatures(context,"com.tencent.mm"));
|
||||
|
||||
Log.e("已安装APP签名", AppSigning.getSingInfo(context, "com.tencent.mm", AppSigning.SHA1));
|
||||
public static void verify(Context context) {
|
||||
Log.e("mhyLog", "hash"+AppSigning.getSignatureHash(context));
|
||||
runCommand();
|
||||
Log.e("包路径文件签名", getApkSignatures(context, context.getPackageName()));
|
||||
Log.e("已安装APP签名", getInstalledAPKSignature(context, context.getPackageName()));
|
||||
//通过获取其他应用的签名 如果一样那么被hook了
|
||||
}
|
||||
|
||||
//从安装文件获取签名
|
||||
/** 防破签名 1
|
||||
* 获取已安装的app签名
|
||||
* */
|
||||
public static String getInstalledAPKSignature(Context context, String packageName) {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
|
||||
PackageInfo appInfo = pm.getPackageInfo(packageName.trim(), PackageManager.GET_SIGNING_CERTIFICATES);
|
||||
if (appInfo == null || appInfo.signingInfo == null)
|
||||
return "";
|
||||
return AppSigning.getSignatureString(appInfo.signingInfo.getApkContentsSigners(), AppSigning.SHA1);
|
||||
} else {
|
||||
PackageInfo appInfo = pm.getPackageInfo(packageName.trim(), PackageManager.GET_SIGNATURES);
|
||||
if (appInfo == null || appInfo.signatures == null)
|
||||
return "";
|
||||
return AppSigning.getSignatureString(appInfo.signatures, AppSigning.SHA1);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/** 防破签名 2
|
||||
* C调用Java 从源安装文件获取签名信息
|
||||
* */
|
||||
public static String getApkSignatures(Context context, String packname) {
|
||||
String sign = "";
|
||||
String path = null;
|
||||
try {
|
||||
path = context.getPackageManager().getApplicationInfo(packname, 0).sourceDir;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
// PackageInfo packageInfo = context.getPackageManager().getPackageInfo(packname, PackageManager.GET_META_DATA);
|
||||
// ApplicationInfo applicationInfo = packageInfo.applicationInfo;
|
||||
// String path =applicationInfo.publicSourceDir;//sourceDir; // 获取当前apk包的绝对路径
|
||||
File apkFile=new File(path);
|
||||
// try {//获取此包安装路径
|
||||
// //第一种方法
|
||||
// ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(packname, 0);
|
||||
// path = applicationInfo.sourceDir;
|
||||
// //第二种方法
|
||||
//// ApplicationInfo applicationInfo = context.getPackageManager().getPackageInfo(packname, PackageManager.GET_META_DATA).applicationInfo;
|
||||
//// path = applicationInfo.publicSourceDir;//sourceDir; // 获取当前apk包的绝对路径
|
||||
// Log.e("其他已知包名apk的安装路径", applicationInfo.sourceDir+ "&---&" + applicationInfo.publicSourceDir);
|
||||
// } catch (PackageManager.NameNotFoundException e) {
|
||||
// e.printStackTrace();
|
||||
// //第三中方法
|
||||
// path=context.getPackageResourcePath();
|
||||
// Log.e("在apk中获取自身安装路径", path);
|
||||
// }
|
||||
//第三中方法
|
||||
path=context.getPackageResourcePath();
|
||||
// Log.e("在apk中获取自身安装路径", path);
|
||||
File apkFile = new File(path);
|
||||
if (apkFile != null && apkFile.exists()) {
|
||||
Log.e("pppp",apkFile.getAbsolutePath());
|
||||
Log.e("包安装路径", apkFile.getAbsolutePath());
|
||||
PackageManager pm = context.getPackageManager();
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
|
||||
PackageInfo pkgInfo = pm.getPackageArchiveInfo(apkFile.getAbsolutePath(), PackageManager.GET_SIGNING_CERTIFICATES);
|
||||
if (pkgInfo != null && pkgInfo.signingInfo != null && pkgInfo.signingInfo.getApkContentsSigners().length > 0) {
|
||||
// sign = pkgInfo.signingInfo.getApkContentsSigners()[0].toCharsString();
|
||||
sign = AppSigning.getSignatureString(pkgInfo.signingInfo.getApkContentsSigners()[0],AppSigning.SHA1);
|
||||
sign = AppSigning.getSignatureString(pkgInfo.signingInfo.getApkContentsSigners(), AppSigning.SHA1);
|
||||
}
|
||||
} else {
|
||||
PackageInfo pkgInfo = pm.getPackageArchiveInfo(apkFile.getAbsolutePath(), PackageManager.GET_SIGNATURES);
|
||||
if (pkgInfo != null && pkgInfo.signatures != null && pkgInfo.signatures.length > 0) {
|
||||
// sign = pkgInfo.signatures[0].toCharsString();
|
||||
sign = AppSigning.getSignatureString(pkgInfo.signatures[0],AppSigning.SHA1);
|
||||
sign = AppSigning.getSignatureString(pkgInfo.signatures, AppSigning.SHA1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return sign;
|
||||
}
|
||||
|
||||
//获取已安装的app签名
|
||||
private static String getInstalledAPKSignature(Context context, String packageName) {
|
||||
|
||||
|
||||
//需要读取应用列表权限
|
||||
public void getAppList(Context context) {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
// String packageName="com.android.calendar";
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
|
||||
PackageInfo appInfo = pm.getPackageInfo(packageName.trim(), PackageManager.GET_SIGNING_CERTIFICATES);
|
||||
if (appInfo == null || appInfo.signingInfo == null)
|
||||
return "";
|
||||
// return appInfo.signingInfo.getApkContentsSigners()[0].toCharsString();
|
||||
return AppSigning.getSignatureString(appInfo.signingInfo.getApkContentsSigners()[0],AppSigning.SHA1);
|
||||
} else {
|
||||
PackageInfo appInfo = pm.getPackageInfo(packageName.trim(), PackageManager.GET_SIGNATURES);
|
||||
if (appInfo == null || appInfo.signatures == null)
|
||||
return "";
|
||||
// return appInfo.signatures[0].toCharsString();
|
||||
return AppSigning.getSignatureString(appInfo.signatures[0],AppSigning.SHA1);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
//需要读取应用列表权限
|
||||
private void getAppList(Context context) {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
// Return a List of all packages that are installed on the device.
|
||||
List<PackageInfo> packages = pm.getInstalledPackages(0);
|
||||
for (PackageInfo packageInfo : packages) {
|
||||
// 判断系统/非系统应用
|
||||
if ((
|
||||
packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) // 非系统应用
|
||||
{
|
||||
if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {// 非系统应用
|
||||
System.out.println("MainActivity.getAppList, packageInfo=" + packageInfo.packageName);
|
||||
} else {
|
||||
// 系统应用
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static ArrayList<String> list = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 通过指令获取已安装的包
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private static ArrayList<String> runCommand() {
|
||||
list.clear();
|
||||
try {
|
||||
@@ -133,25 +160,91 @@ public class APISecurity {
|
||||
list.add(line.split(":")[1]);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.out.println("runCommand,e=" + e);
|
||||
Log.e("runCommand", "e=" + e);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测动态调试
|
||||
* 检测动态调试检查应用是否处于调试状态
|
||||
* 这个也是借助系统的一个api来进行判断isDebuggerConnected()
|
||||
* jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700,当连接成功之后,这个方法就会返回true
|
||||
*/
|
||||
public void detectedDynamicDebug(){
|
||||
if (!BuildConfig.DEBUG){
|
||||
if (Debug.isDebuggerConnected()){
|
||||
public static void detectedDynamicDebug() {
|
||||
if (!BuildConfig.DEBUG) {
|
||||
if (Debug.isDebuggerConnected()) {
|
||||
//进程自杀
|
||||
int myPid = android.os.Process.myPid();
|
||||
android.os.Process.killProcess(myPid);
|
||||
|
||||
//异常退出虚拟机
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查应用是否属于debug模式
|
||||
* 直接调用Android中的flag属性:ApplicationInfo.FLAG_DEBUGGABLE;
|
||||
* 判断是否属于debug模式:防调试
|
||||
*/
|
||||
public void checkDebug(Context context) {
|
||||
int i = context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE;
|
||||
if (0 != (context.getApplicationInfo().flags &= ApplicationInfo.FLAG_DEBUGGABLE)) {
|
||||
/**
|
||||
*
|
||||
* 验证是否可以调试
|
||||
* i != 0 已经打开可调式
|
||||
*/
|
||||
Log.e("debug", "被调试");
|
||||
|
||||
}
|
||||
boolean debuggerConnected = Debug.isDebuggerConnected();
|
||||
Log.e("debug", "是否连接调试 : " + debuggerConnected);
|
||||
/**
|
||||
*
|
||||
* 获取TracerPid来判断
|
||||
*获取获取TracerPid来判断(TracerPid正常情况是0,如果被调试这个是不为0的)
|
||||
*/
|
||||
int pid = android.os.Process.myPid();
|
||||
String info = null;
|
||||
File file = new File("/proc/" + pid + "/status");
|
||||
try {
|
||||
FileInputStream fileInputStream = new FileInputStream(file);
|
||||
InputStreamReader reader = new InputStreamReader(fileInputStream);
|
||||
BufferedReader bufferedReader = new BufferedReader(reader);
|
||||
while ((info = bufferedReader.readLine()) != null) {
|
||||
Log.e("debug", "proecc info : " + info);
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***
|
||||
* 防代理
|
||||
*/
|
||||
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,24 +1,34 @@
|
||||
package cn.android.security;
|
||||
|
||||
/**
|
||||
* @author mahongyin
|
||||
* @Project Android-API-Security-master
|
||||
* @Package cn.android.sample
|
||||
* @data 2020-04-11 22:20
|
||||
* @CopyRight mhy.work@qq.com
|
||||
* @description:
|
||||
*/
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.Signature;
|
||||
import android.os.Build;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.StringDef;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
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.Method;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 签名验证
|
||||
*
|
||||
* @author lWX537240
|
||||
* @date 2019/6/13
|
||||
@@ -33,67 +43,21 @@ public class AppSigning {
|
||||
public final static String SHA256 = "SHA256";
|
||||
|
||||
/**
|
||||
* 返回一个签名的对应类型的字符串
|
||||
*
|
||||
* @param context
|
||||
* @param packageName
|
||||
* @param type
|
||||
* @return
|
||||
* 注解限定String类型为指定
|
||||
*/
|
||||
public static String getSingInfo(Context context, String packageName, String type) {
|
||||
String tmp = "error!";
|
||||
try {
|
||||
Signature[] signs = getSignatures(context, packageName);
|
||||
// Log.e(TAG, "signs = " + Arrays.asList(signs));
|
||||
Signature sig = signs[0];
|
||||
|
||||
if (MD5.equals(type)) {
|
||||
tmp = getSignatureString(sig, MD5);
|
||||
} else if (SHA1.equals(type)) {
|
||||
tmp = getSignatureString(sig, SHA1);
|
||||
} else if (SHA256.equals(type)) {
|
||||
tmp = getSignatureString(sig, SHA256);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return tmp;
|
||||
@StringDef({MD5, SHA1, SHA256})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface SigniType {
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回对应包的签名信息
|
||||
*
|
||||
* @param context
|
||||
* @param packageName
|
||||
* @return
|
||||
* 获取相应的类型的签名信息(把签名的byte[]信息转换成16进制)
|
||||
*/
|
||||
public static Signature[] getSignatures(Context context, String packageName){
|
||||
try {
|
||||
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
|
||||
PackageInfo appInfo = context.getPackageManager().getPackageInfo(packageName.trim(), PackageManager.GET_SIGNING_CERTIFICATES);
|
||||
|
||||
return appInfo.signingInfo.getApkContentsSigners();
|
||||
} else {
|
||||
PackageInfo appInfo = context.getPackageManager().getPackageInfo(packageName.trim(), PackageManager.GET_SIGNATURES);
|
||||
|
||||
return appInfo.signatures;
|
||||
public static String getSignatureString(Signature[] sigs, @SigniType String type) {
|
||||
for (Signature sig : sigs) {
|
||||
Log.e("mhyLog","getSignatureString:"+sig.toCharsString());
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取相应的类型的字符串(把签名的byte[]信息转换成16进制)
|
||||
*
|
||||
* @param sig
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
public static String getSignatureString(Signature sig, String type) {
|
||||
byte[] hexBytes = sig.toByteArray();
|
||||
byte[] hexBytes = sigs[0].toByteArray();
|
||||
String fingerprint = "error!";
|
||||
try {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
@@ -118,7 +82,219 @@ public class AppSigning {
|
||||
}
|
||||
|
||||
|
||||
public static int getSignatureHash(Context context) {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
PackageInfo pi;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int flags = PackageManager.GET_SIGNATURES;
|
||||
try {
|
||||
pi = pm.getPackageInfo(context.getPackageName(), flags);
|
||||
Signature[] signatures = pi.signatures;
|
||||
for (Signature signature : signatures) {
|
||||
sb.append(signature.toCharsString());
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return sb.toString().hashCode();
|
||||
}
|
||||
|
||||
//这个是获取SHA1的方法 上面那个方法是获取签名的hash值 这个和cmd里面获取的是一样的
|
||||
public static String getCertificateSHA1Fingerprint(Context context) {
|
||||
//获取包管理器
|
||||
PackageManager pm = context.getPackageManager();
|
||||
//获取当前要获取SHA1值的包名,也可以用其他的包名,但需要注意,
|
||||
//在用其他包名的前提是,此方法传递的参数Context应该是对应包的上下文。
|
||||
String packageName = context.getPackageName();
|
||||
//返回包括在包中的签名信息
|
||||
int flags = PackageManager.GET_SIGNATURES;
|
||||
PackageInfo packageInfo = null;
|
||||
try {
|
||||
//获得包的所有内容信息类
|
||||
packageInfo = pm.getPackageInfo(packageName, flags);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
//签名信息
|
||||
Signature[] signatures = packageInfo.signatures;
|
||||
byte[] cert = signatures[0].toByteArray();
|
||||
//将签名转换为字节数组流
|
||||
InputStream input = new ByteArrayInputStream(cert);
|
||||
//证书工厂类,这个类实现了出厂合格证算法的功能
|
||||
CertificateFactory cf = null;
|
||||
try {
|
||||
cf = CertificateFactory.getInstance("X509");
|
||||
} catch (CertificateException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
//X509证书,X.509是一种非常通用的证书格式
|
||||
X509Certificate c = null;
|
||||
try {
|
||||
c = (X509Certificate) cf.generateCertificate(input);
|
||||
} catch (CertificateException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
String hexString = null;
|
||||
try {
|
||||
//加密算法的类,这里的参数可以使MD4,MD5等加密算法
|
||||
MessageDigest md = MessageDigest.getInstance("SHA1");
|
||||
//获得公钥
|
||||
byte[] publicKey = md.digest(c.getEncoded());
|
||||
//字节到十六进制的格式转换
|
||||
hexString = byte2HexFormatted(publicKey);
|
||||
} catch (NoSuchAlgorithmException e1) {
|
||||
e1.printStackTrace();
|
||||
} catch (CertificateEncodingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return hexString;
|
||||
}
|
||||
|
||||
//这里是将获取到得编码进行16进制转换
|
||||
private static String byte2HexFormatted(byte[] arr) {
|
||||
StringBuilder str = new StringBuilder(arr.length * 2);
|
||||
for (int i = 0; i < arr.length; i++) {
|
||||
String h = Integer.toHexString(arr[i]);
|
||||
int l = h.length();
|
||||
if (l == 1)
|
||||
h = "0" + h;
|
||||
if (l > 2)
|
||||
h = h.substring(l - 2, l);
|
||||
str.append(h.toUpperCase());
|
||||
if (i < (arr.length - 1))
|
||||
str.append(':');
|
||||
}
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
/** 防破签名 3 使原生检测签名不被hook 助力防破签名1而存在
|
||||
* 通过重置PackageManager防止getPackageInfo方法被代理设置
|
||||
* 亲测MT管理器(当前2.9.1)的一键去签名校验(包括加强版)无效!
|
||||
* 当然如果别人反编译把代码删除的话那就没办法了
|
||||
*/
|
||||
public static void resetPackageManager(Context baseContext) {
|
||||
try {
|
||||
//重置全局sPackageManager对象
|
||||
reset1:
|
||||
{
|
||||
Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
|
||||
Field sPackageManagerField = activityThreadClass.getDeclaredField("sPackageManager");
|
||||
sPackageManagerField.setAccessible(true);
|
||||
sPackageManagerField.set(activityThreadClass, null);
|
||||
//因为上面已经把sPackageManager变量设置为null了,调用这个方法重新赋值
|
||||
Method getPackageManagerMethod = activityThreadClass.getDeclaredMethod("getPackageManager");
|
||||
getPackageManagerMethod.setAccessible(true);
|
||||
getPackageManagerMethod.invoke(activityThreadClass);
|
||||
}
|
||||
//重置当前上下文mPackageManager对象
|
||||
reset2:
|
||||
{
|
||||
Class<?> baseContextClass = baseContext.getClass();
|
||||
Field mPackageManagerField = baseContextClass.getDeclaredField("mPackageManager");
|
||||
mPackageManagerField.setAccessible(true);
|
||||
mPackageManagerField.set(baseContext, null);
|
||||
//重新设置为已经重置好的sPackageManager
|
||||
Method getPackageManagerMethod = baseContextClass.getDeclaredMethod("getPackageManager");
|
||||
getPackageManagerMethod.setAccessible(true);
|
||||
getPackageManagerMethod.invoke(baseContext);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 检测 PackageManager 代理
|
||||
*/
|
||||
@SuppressLint("PrivateApi")
|
||||
private boolean checkPMProxy(Context context){
|
||||
String truePMName = "android.content.pm.IPackageManager$Stub$Proxy";
|
||||
String nowPMName = "";
|
||||
try {
|
||||
// 被代理的对象是 PackageManager.mPM
|
||||
PackageManager packageManager = context.getPackageManager();
|
||||
Field mPMField = packageManager.getClass().getDeclaredField("mPM");
|
||||
mPMField.setAccessible(true);
|
||||
Object mPM = mPMField.get(packageManager);
|
||||
// 取得类名
|
||||
nowPMName = mPM.getClass().getName();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
// 类名改变说明被代理了
|
||||
return truePMName.equals(nowPMName);
|
||||
}
|
||||
|
||||
/** 防破签名 2
|
||||
* 安装路径获取签名
|
||||
*/
|
||||
public static String getAPKSignatures(String apkPath) {
|
||||
String PATH_PackageParser = "android.content.pm.PackageParser";
|
||||
try {
|
||||
// apk包的文件路径
|
||||
// 这是一个Package 解释器, 是隐藏的
|
||||
// 构造函数的参数只有一个, apk文件的路径
|
||||
// PackageParser packageParser = new PackageParser(apkPath);
|
||||
Class pkgParserCls = Class.forName(PATH_PackageParser);
|
||||
Class[] typeArgs = new Class[1];
|
||||
typeArgs[0] = String.class;
|
||||
// 这个是与显示有关的, 里面涉及到一些像素显示等等
|
||||
// 本来是用来根据屏幕大小解析对应资源,但是签名校验与屏幕大小无关,
|
||||
// 所以setDefault来使用默认的情况
|
||||
DisplayMetrics metrics = new DisplayMetrics();
|
||||
metrics.setToDefaults();
|
||||
Constructor pkgParserCt = null;
|
||||
Object pkgParser = null;
|
||||
if (Build.VERSION.SDK_INT > 20) {
|
||||
pkgParserCt = pkgParserCls.getConstructor();
|
||||
pkgParser = pkgParserCt.newInstance();
|
||||
Method pkgParser_parsePackageMtd = pkgParserCls.getDeclaredMethod("parsePackage", File.class, int.class);
|
||||
Object pkgParserPkg = pkgParser_parsePackageMtd.invoke(pkgParser, new File(apkPath), PackageManager.GET_SIGNATURES);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 28) {
|
||||
Method pkgParser_collectCertificatesMtd = pkgParserCls.getDeclaredMethod("collectCertificates", pkgParserPkg.getClass(), Boolean.TYPE);
|
||||
pkgParser_collectCertificatesMtd.invoke(pkgParser, pkgParserPkg, Build.VERSION.SDK_INT > 28);
|
||||
|
||||
// Method pkgParser_collectCertificatesMtd = pkgParserCls.getDeclaredMethod("collectCertificates", pkgParserPkg.getClass(), Boolean.TYPE);
|
||||
// pkgParser_collectCertificatesMtd.invoke(pkgParser, pkgParserPkg, false);
|
||||
|
||||
Field mSigningDetailsField = pkgParserPkg.getClass().getDeclaredField("mSigningDetails"); // SigningDetails
|
||||
mSigningDetailsField.setAccessible(true);
|
||||
|
||||
Object mSigningDetails = mSigningDetailsField.get(pkgParserPkg);
|
||||
Field infoField = mSigningDetails.getClass().getDeclaredField("signatures");
|
||||
infoField.setAccessible(true);
|
||||
Signature[] info = (Signature[]) infoField.get(mSigningDetails);
|
||||
return info[0].toCharsString();
|
||||
|
||||
} 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 info[0].toCharsString();
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
pkgParserCt = pkgParserCls.getConstructor(typeArgs);
|
||||
pkgParser = pkgParserCt.newInstance(apkPath);
|
||||
Method pkgParser_parsePackageMtd = pkgParserCls.getDeclaredMethod("parsePackage", File.class, String.class, DisplayMetrics.class, Integer.TYPE);
|
||||
Object pkgParserPkg = pkgParser_parsePackageMtd.invoke(pkgParser, new File(apkPath), apkPath, metrics, PackageManager.GET_SIGNATURES);
|
||||
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 info[0].toCharsString();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e("getAPKSignatures", e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ android {
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
// ndk{
|
||||
// //生成so库名称 也是System.load
|
||||
// moduleName "apisecurity-lib"
|
||||
@@ -19,8 +19,10 @@ android {
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
cppFlags ""
|
||||
// abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a', 'arm64-v8a'
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
@@ -30,15 +32,18 @@ android {
|
||||
storeFile file("test.keystore")
|
||||
storePassword '123456'
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
debuggable false//xml android:debuggable="false"
|
||||
minifyEnabled false
|
||||
signingConfig signingConfigs.release
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
debug{
|
||||
debuggable true
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
}
|
||||
@@ -54,10 +59,7 @@ android {
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
implementation 'com.android.support:appcompat-v7:28.0.0'
|
||||
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
|
||||
testImplementation 'junit:junit:4.12'
|
||||
androidTestImplementation 'com.android.support.test:runner:1.0.2'
|
||||
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
|
||||
implementation 'androidx.appcompat:appcompat:1.0.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
implementation project(':apisecurity')
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme"
|
||||
|
||||
tools:ignore="GoogleAppIndexingWarning">
|
||||
<activity android:name=".MainActivity">
|
||||
<intent-filter>
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#include <jni.h>
|
||||
#include <string>
|
||||
#include <android/log.h>
|
||||
#include <string.h>
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
|
||||
//log定义
|
||||
#define LOG "APISECURITY" // 这个是自定义的LOG的TAG
|
||||
@@ -11,32 +14,32 @@
|
||||
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,LOG,__VA_ARGS__)
|
||||
|
||||
//此处改为你的APP签名
|
||||
//#define SHA1 "a8e3d91a4f77dd7ccb8d43ee5046a4b6833f4785"//区分小写
|
||||
#define SHA1 "04c1411b0662acd9e4aa300559677e5f106a5255"
|
||||
//#define SHA1 "a8e3d91a4f77dd7ccb8d43ee5046a4b6833f4785"//真实test.keystore
|
||||
#define SHA1 "04c1411b0662acd9e4aa300559677e5f106a5255"//区分da小写
|
||||
#define ALGORITHM_SHA1 "SHA1"
|
||||
#define ALGORITHM_MD5 "MD5"
|
||||
|
||||
//此处改为你的APP包名
|
||||
#define APP_PKG "cn.android.sample"
|
||||
//此处填写API盐值
|
||||
#define API_SECRET "ABC1234567"//设置api 密钥 返回md5
|
||||
#define API_SECRET "ABC1234567"//设置api 密钥 MD5加盐
|
||||
|
||||
|
||||
static bool isInit = false;
|
||||
static char *secret;
|
||||
|
||||
|
||||
//void printByte(JNIEnv *env, jbyteArray jbytes) {
|
||||
// //转换成char
|
||||
// jsize array_size = env->GetArrayLength(jbytes);
|
||||
// jbyte *sha1 = env->GetByteArrayElements(jbytes, NULL);
|
||||
//
|
||||
// char *hexA = new char[array_size * 2 + 1]();
|
||||
// for (int i = 0; i < array_size; ++i) {
|
||||
// sprintf(hexA + 2 * i, "%02x", (u_char) sha1[i]);
|
||||
// }
|
||||
// LOGD("printByte:%s", hexA);
|
||||
//}
|
||||
void printByte(JNIEnv *env, jbyteArray jbytes) {
|
||||
//转换成char
|
||||
jsize array_size = env->GetArrayLength(jbytes);
|
||||
jbyte *sha1 = env->GetByteArrayElements(jbytes, nullptr);
|
||||
|
||||
char *hexA = new char[array_size * 2 + 1]();
|
||||
for (int i = 0; i < array_size; ++i) {
|
||||
sprintf(hexA + 2 * i, "%02x", (u_char) sha1[i]);
|
||||
}
|
||||
LOGD("printByte:%s", hexA);
|
||||
}
|
||||
|
||||
char *digest(JNIEnv *env, const char *algorithm, jbyteArray cert_byte) {
|
||||
jclass message_digest_class = env->FindClass("java/security/MessageDigest");
|
||||
@@ -51,7 +54,7 @@ char *digest(JNIEnv *env, const char *algorithm, jbyteArray cert_byte) {
|
||||
|
||||
//转换成char
|
||||
jsize array_size = env->GetArrayLength(sha1_byte);
|
||||
jbyte *sha1 = env->GetByteArrayElements(sha1_byte, NULL);
|
||||
jbyte *sha1 = env->GetByteArrayElements(sha1_byte, nullptr);
|
||||
char *hex = new char[array_size * 2 + 1]();
|
||||
for (int i = 0; i < array_size; ++i) {
|
||||
sprintf(hex + 2 * i, "%02x", (unsigned char) sha1[i]);
|
||||
@@ -81,37 +84,71 @@ jstring getPackageName(JNIEnv *env, jclass context_class, jobject context_object
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取PackageInfo对象
|
||||
* 获取PackageInfo对象 PackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNING_CERTIFICATES)
|
||||
*/
|
||||
jobject getPackageInfo(JNIEnv *env, jobject package_manager, jstring package_name) {
|
||||
jclass pack_manager_class = env->GetObjectClass(package_manager);
|
||||
jmethodID methodId = env->GetMethodID(pack_manager_class, "getPackageInfo",
|
||||
"(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
|
||||
env->DeleteLocalRef(pack_manager_class);
|
||||
jobject package_info = env->CallObjectMethod(package_manager, methodId, package_name, 0x40);//安卓10 0x80
|
||||
|
||||
jobject package_info = env->CallObjectMethod(package_manager, methodId, package_name,
|
||||
0x40);//安卓10 0x80
|
||||
return package_info;
|
||||
}
|
||||
//目录获取PackageInfo
|
||||
jobject getPackageArchiveInfo(JNIEnv *env, jobject package_manager, jstring absolutePath) {
|
||||
|
||||
/**
|
||||
* PackageInfo pkgInfo = pm.getPackageArchiveInfo(apkFile.getAbsolutePath(), PackageManager.GET_SIGNATURES);
|
||||
* */
|
||||
jobject getPackageInfoArchive(JNIEnv *env, jobject package_manager, jstring apkPath) {
|
||||
jclass pack_manager_class = env->GetObjectClass(package_manager);
|
||||
jmethodID methodId = env->GetMethodID(pack_manager_class, "getPackageArchiveInfo",
|
||||
"(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
|
||||
env->DeleteLocalRef(pack_manager_class);
|
||||
jobject package_info = env->CallObjectMethod(package_manager, methodId, absolutePath, 0x80);
|
||||
jobject package_info = env->CallObjectMethod(package_manager, methodId, apkPath, 0x40);
|
||||
return package_info;
|
||||
}
|
||||
//安装路径
|
||||
jstring getAbsolutePath(JNIEnv *env, jobject package_info) {//packageInfo
|
||||
jclass package_info_class = env->GetObjectClass(package_info);//applicationInfo获取类对象 以获取方法
|
||||
//pack_manager_class.getApplicationInfo()
|
||||
jfieldID field = env->GetFieldID(package_info_class,"applicationInfo","Landroid/content/pm/ApplicationInfo;");//获取此类中的sourceDir成员id
|
||||
jobject apppack_info_object=(jstring)env ->GetObjectField(package_info,field);//applicationInfo
|
||||
|
||||
/**安装目录获取ApplicationInfo
|
||||
* ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(packname, 0);
|
||||
* path = applicationInfo.sourceDir;
|
||||
* */
|
||||
jobject getApplicationInfo(JNIEnv *env, jobject package_manager, jstring package_name) {
|
||||
jclass pack_manager_class = env->GetObjectClass(package_manager);
|
||||
jmethodID methodId = env->GetMethodID(pack_manager_class, "getApplicationInfo",
|
||||
"(Ljava/lang/String;I)Landroid/content/pm/ApplicationInfo;");
|
||||
env->DeleteLocalRef(pack_manager_class);
|
||||
jobject applicationInfo = env->CallObjectMethod(package_manager, methodId, package_name, 0x00);
|
||||
return applicationInfo;
|
||||
}
|
||||
|
||||
jstring getApkPath(JNIEnv *env, jobject applicationInfo_object) {
|
||||
jclass applicationInfo_class = env->GetObjectClass(applicationInfo_object);//ApplicationInfo
|
||||
jfieldID sourceDir = env->GetFieldID(applicationInfo_class, "sourceDir",
|
||||
"Ljava/lang/String;");//获取此类中的sourceDir成员id P->publicSourceDir
|
||||
env->DeleteLocalRef(applicationInfo_class);
|
||||
jstring apkPath = (jstring) env->GetObjectField(applicationInfo_object, sourceDir);
|
||||
return apkPath;
|
||||
}
|
||||
//context.getPackageResourcePath()
|
||||
jstring getApkResPath(JNIEnv *env, jclass context_class, jobject context_object) {
|
||||
jmethodID methodId = env->GetMethodID(context_class, "getPackageResourcePath", "()Ljava/lang/String;");
|
||||
jstring apkPath = (jstring) env->CallObjectMethod(context_object, methodId);
|
||||
return apkPath;
|
||||
}
|
||||
//弃用 安装路径 package_info= pm.getPackageInfo(packname, 0x80).applicationInfo
|
||||
jstring getAbsolutePath(JNIEnv *env, jobject package_info) {
|
||||
jclass package_info_class = env->GetObjectClass(package_info);//packageInfo
|
||||
jfieldID field = env->GetFieldID(package_info_class, "applicationInfo",
|
||||
"Landroid/content/pm/ApplicationInfo;");//applicationInfo获取类对象 以获取方法
|
||||
env->DeleteLocalRef(package_info_class);
|
||||
jobject apppack_info_object = (jstring) env->GetObjectField(package_info,field);//applicationInfo
|
||||
jclass apppack_info_class = env->GetObjectClass(apppack_info_object);//ApplicationInfo
|
||||
jfieldID appfield = env->GetFieldID(apppack_info_class,"sourceDir","Ljava/lang/String;");//获取此类中的sourceDir成员id P->publicSourceDir
|
||||
// env->DeleteLocalRef(pack_manager_class);
|
||||
jstring absoluteapp=(jstring)env ->GetObjectField(apppack_info_object,appfield);
|
||||
if (absoluteapp == NULL)
|
||||
jfieldID appfield = env->GetFieldID(apppack_info_class, "sourceDir",
|
||||
"Ljava/lang/String;");//获取此类中的sourceDir成员id P->publicSourceDir
|
||||
env->DeleteLocalRef(apppack_info_class);
|
||||
jstring absoluteapp = (jstring) env->GetObjectField(apppack_info_object, appfield);
|
||||
if (absoluteapp == nullptr)
|
||||
return env->NewStringUTF("");
|
||||
return absoluteapp;
|
||||
}
|
||||
@@ -125,8 +162,8 @@ jobject getSignature(JNIEnv *env, jobject package_info) {
|
||||
"[Landroid/content/pm/Signature;");
|
||||
env->DeleteLocalRef(package_info_class);
|
||||
jobjectArray signature_object_array = (jobjectArray) env->GetObjectField(package_info, fieldId);
|
||||
if (signature_object_array == NULL)
|
||||
return NULL;
|
||||
if (signature_object_array == nullptr)
|
||||
return nullptr;
|
||||
return env->GetObjectArrayElement(signature_object_array, 0);
|
||||
}
|
||||
|
||||
@@ -156,7 +193,55 @@ jbyteArray getSHA1(JNIEnv *env, jobject signature_object) {
|
||||
return cert_byte;
|
||||
}
|
||||
|
||||
/**
|
||||
* apk安装路径 获取签名信息 getApkPathSignatures(Context)
|
||||
*/
|
||||
jboolean getApkPathSignatures(JNIEnv *env, jobject context_object) {
|
||||
//上下文对象
|
||||
jclass context_class = env->GetObjectClass(context_object);
|
||||
|
||||
//反射获取PackageManager
|
||||
jobject package_manager = getPackageManager(env, context_object, context_class);
|
||||
if (package_manager == nullptr)
|
||||
return JNI_FALSE;
|
||||
//反射获取包名
|
||||
jstring package_name = getPackageName(env, context_class, context_object);
|
||||
if (package_name == nullptr)
|
||||
return JNI_FALSE;
|
||||
//插空 替换下面 214-218
|
||||
jstring apkPath = getApkResPath(env, context_class, context_object);
|
||||
env->DeleteLocalRef(context_class);//等用完再释放啦
|
||||
// //获取applictionInfo
|
||||
// jobject applicationInfo = getApplicationInfo(env, package_manager, package_name);
|
||||
// if (applicationInfo == nullptr)
|
||||
// return JNI_FALSE;
|
||||
// jstring apkPath = getApkPath(env, applicationInfo);
|
||||
if (apkPath == nullptr)
|
||||
return JNI_FALSE;
|
||||
//获取PackageInfo对象
|
||||
jobject package_info = getPackageInfoArchive(env, package_manager, apkPath);
|
||||
if (package_info == nullptr)
|
||||
return JNI_FALSE;
|
||||
env->DeleteLocalRef(package_manager);
|
||||
//获取签名信息
|
||||
jobject signature_object = getSignature(env, package_info);
|
||||
if (signature_object == nullptr)
|
||||
return JNI_FALSE;
|
||||
env->DeleteLocalRef(package_info);
|
||||
jbyteArray cert_byte = getSHA1(env, signature_object);
|
||||
|
||||
char *hex_sha = digest(env, ALGORITHM_SHA1, cert_byte);
|
||||
|
||||
if (strcmp(hex_sha, SHA1) != 0) {//签名不同
|
||||
LOGE("非法调用4,SHA1: %s", hex_sha);
|
||||
return JNI_FALSE;
|
||||
}
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用
|
||||
*/
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_cn_android_security_APISecurity_init(
|
||||
JNIEnv *env,
|
||||
@@ -168,24 +253,24 @@ Java_cn_android_security_APISecurity_init(
|
||||
|
||||
//反射获取PackageManager
|
||||
jobject package_manager = getPackageManager(env, context_object, context_class);
|
||||
if (package_manager == NULL)
|
||||
if (package_manager == nullptr)
|
||||
return JNI_FALSE;
|
||||
|
||||
//反射获取包名
|
||||
jstring package_name = getPackageName(env, context_class, context_object);
|
||||
if (package_name == NULL)
|
||||
if (package_name == nullptr)
|
||||
return JNI_FALSE;
|
||||
env->DeleteLocalRef(context_class);
|
||||
|
||||
//获取PackageInfo对象
|
||||
jobject package_info = getPackageInfo(env, package_manager, package_name);
|
||||
if (package_info == NULL)
|
||||
if (package_info == nullptr)
|
||||
return JNI_FALSE;
|
||||
env->DeleteLocalRef(package_manager);
|
||||
|
||||
//获取签名信息
|
||||
jobject signature_object = getSignature(env, package_info);
|
||||
if (signature_object == NULL)
|
||||
if (signature_object == nullptr)
|
||||
return JNI_FALSE;
|
||||
env->DeleteLocalRef(package_info);
|
||||
jbyteArray cert_byte = getSHA1(env, signature_object);
|
||||
@@ -193,50 +278,54 @@ Java_cn_android_security_APISecurity_init(
|
||||
char *hex_sha = digest(env, ALGORITHM_SHA1, cert_byte);
|
||||
|
||||
if (strcmp(hex_sha, SHA1) != 0) {//签名不对
|
||||
LOGE("非法调用1,SHA1: %s:%s", hex_sha, SHA1);
|
||||
LOGE("非法调用1,SHA1: %s", hex_sha);
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
const char *pkgName = env->GetStringUTFChars(package_name, NULL);
|
||||
|
||||
//包名验证
|
||||
const char *pkgName = env->GetStringUTFChars(package_name, nullptr);
|
||||
if (strcmp(pkgName, APP_PKG) == 0) {
|
||||
secret = API_SECRET;//包名匹配 拿取api
|
||||
} else {
|
||||
LOGE("非法调用2,Package: %s:%s", pkgName, APP_PKG);
|
||||
LOGE("非法调用2,Package: %s", pkgName);
|
||||
return JNI_FALSE;
|
||||
}
|
||||
// 接着调用Java方法验证 安装目录文件签名
|
||||
jclass cls_util = env->FindClass(
|
||||
"cn/android/security/APISecurity");//注意,这里的使用的斜杠而不是点
|
||||
if (cls_util == NULL) {
|
||||
/*********接着调用Java方法验证 安装目录apk文件de签名**/
|
||||
// jclass cls_util = env->FindClass(
|
||||
// "cn/android/security/APISecurity");
|
||||
// //注意,这里的使用的斜杠而不是点
|
||||
// if (cls_util == nullptr) {
|
||||
// return JNI_FALSE;
|
||||
// }
|
||||
// jobject j_obj = env->AllocObject(cls_util);
|
||||
// //**这里是关键**类,方法,(参数类型)返回类型
|
||||
// jmethodID mtd_static_method = env->GetStaticMethodID(cls_util,
|
||||
// "getApkSignatures",
|
||||
// "(Landroid/content/Context;Ljava/lang/String;)Ljava/lang/String;");
|
||||
// if (mtd_static_method == nullptr) {
|
||||
// return JNI_FALSE;
|
||||
// }
|
||||
// //调用Java方法
|
||||
// jstring sigin = static_cast<jstring>(env->CallStaticObjectMethod(cls_util, mtd_static_method,
|
||||
// context_object, package_name));
|
||||
// const char *ss = env->GetStringUTFChars(sigin, nullptr);
|
||||
// //删除引用
|
||||
// env->DeleteLocalRef(cls_util);
|
||||
// env->DeleteLocalRef(j_obj);
|
||||
////调用Java方法结束
|
||||
// if (strcmp(ss, SHA1) != 0) {
|
||||
// LOGE("非法调用3,SHA1: %s", ss);
|
||||
// return JNI_FALSE;
|
||||
// }
|
||||
/*******************调用Java方法结束***/
|
||||
//加强验证
|
||||
if (!getApkPathSignatures(env, context_object)) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
jobject j_obj = env->AllocObject(cls_util);//**这里是关键**类,方法,(参数类型)返回类型
|
||||
jmethodID mtd_static_method = env->GetStaticMethodID(cls_util,
|
||||
"getApkSignatures",
|
||||
"(Landroid/content/Context;Ljava/lang/String;)Ljava/lang/String;");
|
||||
if (mtd_static_method == NULL) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
//调用Java方法
|
||||
jstring sigin= static_cast<jstring>(env->CallStaticObjectMethod(cls_util, mtd_static_method,
|
||||
context_object, package_name));
|
||||
const char *ss=env->GetStringUTFChars(sigin,NULL);
|
||||
//删除引用
|
||||
env->DeleteLocalRef(cls_util);
|
||||
env->DeleteLocalRef(j_obj);
|
||||
//调用Java方法结束
|
||||
if (strcmp(ss, SHA1) != 0) {
|
||||
LOGE("非法调用3,SHA1: %s:%s", ss, SHA1);
|
||||
return JNI_FALSE;
|
||||
}
|
||||
//
|
||||
isInit = true;
|
||||
LOGI("初始化成功!");
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
|
||||
extern "C" JNIEXPORT jstring JNICALL
|
||||
Java_cn_android_security_APISecurity_sign(
|
||||
JNIEnv *env,
|
||||
@@ -246,34 +335,37 @@ Java_cn_android_security_APISecurity_sign(
|
||||
if (!isInit) {
|
||||
LOGE("请先初始化!");
|
||||
|
||||
jclass cls_util = env->FindClass(
|
||||
"cn/android/security/APISecurity");//注意,这里的使用的斜杠而不是点
|
||||
if (cls_util == NULL) {
|
||||
return env->NewStringUTF("");
|
||||
}
|
||||
jobject j_obj = env->AllocObject(cls_util);//**这里是关键**
|
||||
jmethodID mtd_static_method = env->GetMethodID(cls_util,
|
||||
"javaMethod",
|
||||
"(Ljava/lang/String;)V");
|
||||
if (mtd_static_method == NULL) {
|
||||
return env->NewStringUTF("");
|
||||
}
|
||||
jclass cls_util = env->FindClass(
|
||||
"cn/android/security/APISecurity");//注意,这里的使用的斜杠而不是点
|
||||
if (cls_util == nullptr) {
|
||||
return env->NewStringUTF("");
|
||||
}
|
||||
//调用Java 方法
|
||||
jobject j_obj = env->AllocObject(cls_util);
|
||||
//**这里是关键**GetMethodID是普通方法 GetStaticMethodID静态方法
|
||||
jmethodID mtd_static_method = env->GetMethodID(cls_util,
|
||||
"javaMethod",
|
||||
"(Ljava/lang/String;)V");
|
||||
if (mtd_static_method == nullptr) {
|
||||
return env->NewStringUTF("");
|
||||
}
|
||||
|
||||
jstring data = env->NewStringUTF("noinit");
|
||||
jstring data = env->NewStringUTF("验证失败");
|
||||
|
||||
if (data == NULL) {}//调用Java方法
|
||||
env->CallVoidMethod(/*context_object*/j_obj, mtd_static_method, data);
|
||||
if (data != nullptr) {//nullptr
|
||||
//调用Java方法
|
||||
env->CallVoidMethod(j_obj, mtd_static_method, data);
|
||||
|
||||
//删除引用
|
||||
env->DeleteLocalRef(cls_util);
|
||||
env->DeleteLocalRef(j_obj);
|
||||
env->DeleteLocalRef(data);
|
||||
|
||||
}
|
||||
return env->NewStringUTF("");
|
||||
}
|
||||
//已经通过初始化
|
||||
const char *sx;
|
||||
sx = env->GetStringUTFChars(str, NULL);
|
||||
sx = env->GetStringUTFChars(str, nullptr);
|
||||
//通过传来计算
|
||||
char *full = new char[strlen(sx) + strlen(secret) + 1]();
|
||||
strcat(full, sx);
|
||||
@@ -293,22 +385,80 @@ Java_cn_android_security_APISecurity_sign(
|
||||
//JNIEXPORT jboolean JNICALL
|
||||
//Java_cn_android_security_APISecurity_check(JNIEnv *env, jclass clazz, jstring str) {
|
||||
// const char *sx;
|
||||
// sx = env->GetStringUTFChars(str, NULL);
|
||||
// sx = env->GetStringUTFChars(str, nullptr);
|
||||
// char name[512];
|
||||
// strcpy(name, sx);
|
||||
//
|
||||
// return 0 == strcmp(SHA1, "md5");
|
||||
//}
|
||||
|
||||
//APISecurity.adbshell("pm list package -3",getFilesDir().getPath() + File.separator + "files"+File.separator + "adbshell.txt");
|
||||
//extern "C"
|
||||
//JNIEXPORT jstring JNICALL
|
||||
//Java_cn_android_security_APISecurity_adbshell(JNIEnv *env, jclass clazz, jstring str,jstring path) {
|
||||
// // int ret= system("pm list package -3");//获取安装应用
|
||||
//
|
||||
// char* str2=(char *)env->GetStringUTFChars(str, NULL);
|
||||
// char* path2=(char *)env->GetStringUTFChars(path, NULL);
|
||||
// strcat(str2," > ");
|
||||
// strcat(str2,path2);
|
||||
//extern "C" JNIEXPORT jstring JNICALL
|
||||
//Java_cn_android_security_APISecurity_adbshell(JNIEnv *env, jclass clazz, jstring str,
|
||||
// jstring path) {
|
||||
// int ret = system("pm list package -3");//获取安装应用
|
||||
// char *str2 = (char *) env->GetStringUTFChars(str, nullptr);
|
||||
// char *path2 = (char *) env->GetStringUTFChars(path, nullptr);
|
||||
// strcat(str2, " > ");
|
||||
// strcat(str2, path2);
|
||||
// system(str2);
|
||||
// return env->NewStringUTF(str2);
|
||||
//}
|
||||
|
||||
/*1.调试端口检测
|
||||
读取/proc/net/tcp,查找IDA远程调试所用的23946端口,若发现说明进程正在被IDA调试。*/
|
||||
void CheckPort23946ByTcp() {
|
||||
FILE *pfile = nullptr;
|
||||
char buf[0x1000] = {0};
|
||||
// 执行命令
|
||||
char *strCatTcp = "cat /proc/net/tcp |grep :5D8A";//5D8A转化成十进制就是23946
|
||||
//char* strNetstat="netstat |grep :23946";
|
||||
pfile = popen(strCatTcp, "r");
|
||||
if (nullptr == pfile) {
|
||||
LOGD("CheckPort23946ByTcp popen打开命令失败!\n");
|
||||
return;
|
||||
}
|
||||
// 获取结果
|
||||
while (fgets(buf, sizeof(buf), pfile)) {
|
||||
// 执行到这里,判定为调试状态
|
||||
LOGD("执行cat /proc/net/tcp |grep :5D8A的结果:\n");
|
||||
LOGD("%s", buf);
|
||||
}//while
|
||||
pclose(pfile);
|
||||
}
|
||||
|
||||
|
||||
/*5.APK线程检测
|
||||
正常apk进程一般会有十几个线程在运行(比如会有jdwp线程),
|
||||
自己写可执行文件加载so一般只有一个线程,
|
||||
可以根据这个差异来进行调试环境检测*/
|
||||
|
||||
void CheckTaskCount() {
|
||||
char buf[0x100] = {0};
|
||||
char *str = "/proc/%d/task";
|
||||
snprintf(buf, sizeof(buf), str, getpid());
|
||||
// 打开目录:
|
||||
DIR *pdir = opendir(buf);
|
||||
if (!pdir) {
|
||||
perror("CheckTaskCount open() fail.\n");
|
||||
return;
|
||||
}
|
||||
// 查看目录下文件个数:
|
||||
struct dirent *pde = nullptr;
|
||||
int Count = 0;
|
||||
while ((pde = readdir(pdir))) {
|
||||
// 字符过滤
|
||||
if ((pde->d_name[0] <= '9') && (pde->d_name[0] >= '0')) {
|
||||
++Count;
|
||||
LOGD("%d 线程名称:%s\n", Count, pde->d_name);
|
||||
}
|
||||
}
|
||||
LOGD("线程个数为:%d", Count);
|
||||
if (1 >= Count) {
|
||||
// 此处判定为调试状态.
|
||||
LOGD("调试状态!\n");
|
||||
}
|
||||
int i = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
|
||||
import cn.android.security.AppSigning;
|
||||
//继承原application
|
||||
|
||||
public class HookApplication extends Application implements InvocationHandler {
|
||||
@@ -35,12 +37,15 @@ public class HookApplication extends Application implements InvocationHandler {
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
app=this;
|
||||
app = this;
|
||||
//在签名校验被hook 之后重置PackageManager
|
||||
/*在这里 重置PackageManager 只要在验证前重置即可*/
|
||||
AppSigning.resetPackageManager(getBaseContext());
|
||||
}
|
||||
|
||||
public static Context getContext() {
|
||||
if (app==null){
|
||||
app=new HookApplication();
|
||||
if (app == null) {
|
||||
app = new HookApplication();
|
||||
app.onCreate();
|
||||
}
|
||||
return app;
|
||||
@@ -49,16 +54,22 @@ public class HookApplication extends Application implements InvocationHandler {
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(Context context) {
|
||||
//在这里hook 签名校验被
|
||||
hook(context);
|
||||
super.attachBaseContext(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* 全局hook 签名校验
|
||||
*
|
||||
* @param context
|
||||
*/
|
||||
private void hook(Context context) {
|
||||
try {
|
||||
DataInputStream dataInputStream = new DataInputStream(new ByteArrayInputStream(Base64.decode("AQAAAjAwggIsMIIBlaADAgECAgMY2gowDQYJKoZIhvcNAQEFBQAwWzELMAkGA1UEBhMCQ04xCzAJ\nBgNVBAgTAmhlMQwwCgYDVQQHEwNzanoxDDAKBgNVBAoTA2VkdTEPMA0GA1UECxMGc2Nob29sMRIw\nEAYDVQQDEwltYWhvbmd5aW4wHhcNMTgxMDExMDcwMjE1WhcNNDMxMDA1MDcwMjE1WjBbMQswCQYD\nVQQGEwJDTjELMAkGA1UECBMCaGUxDDAKBgNVBAcTA3NqejEMMAoGA1UEChMDZWR1MQ8wDQYDVQQL\nEwZzY2hvb2wxEjAQBgNVBAMTCW1haG9uZ3lpbjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA\nr0aFNvrxBnBEEbAANDcsrmBlcQBGJKsvT5onXngek2ZbkWZx8/1o8nbgCBSjAZvnXEYYjjkC5k+A\nIne1PJUF5bPKTjIQepNmtK+KVHsAJLjn6rG4fQ3oaeu0vvNBehuzt54bACbzkXZj9nV5rs8OllD9\nRronLsOb3DVJ95DyLIMCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCpF6kB++zR0FW4eZaJCEAnQNP0\nGtwAnrXEpvP7ePcakk/JT/e56uTS/OAbpmM/tWETvPtx9hOB4RoPwRl3Q0G1ieCMeVyIABmGAeOk\ntARqtiExfHvorrmk4mxVIiPTwUJSWzAKuhLV93pMxTFZSZK0iTJFVVM/l8Wh3CTdFtpW+w==\n", 0)));
|
||||
String relay="308203273082020fa003020102020477d6d1f6300d06092a864886f70d01010b05003044310c300a06035504061303303231310e300c060355040813056368696e613111300f060355040713087368616e676861693111300f0603550403130877757a6f6e67626f301e170d3139303330353032343132345a170d3434303232373032343132345a3044310c300a06035504061303303231310e300c060355040813056368696e613111300f060355040713087368616e676861693111300f0603550403130877757a6f6e67626f30820122300d06092a864886f70d01010105000382010f003082010a0282010100a3ac52268a32e8420a20a727c184c133d513998a207e198f5a535d628a436ba5e095e7ba3f92535234a83fb6272e70ed6113d8f6facc3dee2cfc076a3bd93dad3520fd5d9d9ae4c48afe56e7b421f5de2adfbc23e450f7a5f71e0afdec047b1ce8d7be62ef754a9d43bf36d9b9e0728fc268cb845b464cce1370573dfafd6c40b2efb98ba1f20c5a63c417264b69d86adb839241dc37d1a7113295a9c51623e51e9408f9623ed49a63a3ba6269172872088213332f38370af530d5be56e54115b0884ace6813911bfc6873bea28207741f4b2471b797bab156e4c6ead91659076553cee1db82c0cebdd17b64802a20c7ee6a3414f959133e6c435efe9241ab7d0203010001a321301f301d0603551d0e041604143806aea351c74f2a8b83fa26c0a9e3d3820b6699300d06092a864886f70d01010b050003820101006ebcc664b996f15c1e03d041eebbdf74a0976d117d68f34d21ef67855b614f5a2bfede66c9d4ea78fe3b50e3673890dfa2eb9eaf4321b30eb76be6f5944004b6501b2629ae4f2c6750f784ea2f9be6c26318258f98772fd3ff0c6ea817fb76d9ae02daa1fa1b91653d531db345f52aa4e7b21e8f92387a2d15d1afd5556213b0c32aadd529bac330516536948bcf85398fb86a65dbae95ef0e5582a87e26b1dbcceeaf77e6e93c63042acdf49c74927561df508020547426ad37776e360feb219523ef4e2a6f5f41a43cd0c0514c53f8644c71014080cfbe036f120a6daad6e12d6b1a07939ca840af2b3373388c0ed6b18594dd838122174304d5eb720f1cef";
|
||||
String singnStr = "AQAAAjAwggIsMIIBlaADAgECAgMY2gowDQYJKoZIhvcNAQEFBQAwWzELMAkGA1UEBhMCQ04xCzAJ\nBgNVBAgTAmhlMQwwCgYDVQQHEwNzanoxDDAKBgNVBAoTA2VkdTEPMA0GA1UECxMGc2Nob29sMRIw\nEAYDVQQDEwltYWhvbmd5aW4wHhcNMTgxMDExMDcwMjE1WhcNNDMxMDA1MDcwMjE1WjBbMQswCQYD\nVQQGEwJDTjELMAkGA1UECBMCaGUxDDAKBgNVBAcTA3NqejEMMAoGA1UEChMDZWR1MQ8wDQYDVQQL\nEwZzY2hvb2wxEjAQBgNVBAMTCW1haG9uZ3lpbjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA\nr0aFNvrxBnBEEbAANDcsrmBlcQBGJKsvT5onXngek2ZbkWZx8/1o8nbgCBSjAZvnXEYYjjkC5k+A\nIne1PJUF5bPKTjIQepNmtK+KVHsAJLjn6rG4fQ3oaeu0vvNBehuzt54bACbzkXZj9nV5rs8OllD9\nRronLsOb3DVJ95DyLIMCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCpF6kB++zR0FW4eZaJCEAnQNP0\nGtwAnrXEpvP7ePcakk/JT/e56uTS/OAbpmM/tWETvPtx9hOB4RoPwRl3Q0G1ieCMeVyIABmGAeOk\ntARqtiExfHvorrmk4mxVIiPTwUJSWzAKuhLV93pMxTFZSZK0iTJFVVM/l8Wh3CTdFtpW+w==\n";
|
||||
DataInputStream dataInputStream = new DataInputStream(
|
||||
new ByteArrayInputStream(Base64.decode(singnStr, 0)));
|
||||
byte[][] bArr = new byte[(dataInputStream.read() & 255)][];
|
||||
for (int i = 0; i < bArr.length; i++) {
|
||||
bArr[i] = new byte[dataInputStream.readInt()];
|
||||
@@ -89,7 +100,7 @@ public class HookApplication extends Application implements InvocationHandler {
|
||||
|
||||
@Override
|
||||
public Object invoke(Object obj, Method method, Object[] objArr) throws Throwable {
|
||||
if ("getPackageInfo".equals(method.getName())) {
|
||||
if ("getPackageInfo".equals(method.getName())) {//方法名对上
|
||||
String str = (String) objArr[0];
|
||||
if ((((Integer) objArr[1]).intValue() & 64) != 0 && this.appPkgName.equals(str)) {
|
||||
PackageInfo packageInfo = (PackageInfo) method.invoke(this.base, objArr);
|
||||
|
||||
@@ -2,7 +2,7 @@ package cn.android.sample;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
@@ -23,8 +23,10 @@ public class MainActivity extends AppCompatActivity {
|
||||
|
||||
if(APISecurity.init(this)){
|
||||
tv.setText("初始化ok");
|
||||
}else {
|
||||
tv.setText("初始化fail");
|
||||
}
|
||||
|
||||
APISecurity.verify(this);
|
||||
findViewById(R.id.btnTest).setOnClickListener(new View.OnClickListener() {
|
||||
@SuppressLint("SetTextI18n")
|
||||
@Override
|
||||
@@ -39,4 +41,9 @@ public class MainActivity extends AppCompatActivity {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
APISecurity.detectedDynamicDebug();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="cn.wzbos.android.sample.MainActivity">
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sample_text"
|
||||
@@ -26,4 +26,4 @@
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/sample_text" />
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -7,7 +7,7 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.6.2'
|
||||
classpath 'com.android.tools.build:gradle:3.6.3'
|
||||
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
|
||||
@@ -15,3 +15,5 @@ android.useDeprecatedNdk=true
|
||||
ANDROID_BUILD_MIN_SDK_VERSION=16
|
||||
ANDROID_BUILD_TARGET_SDK_VERSION=28
|
||||
ANDROID_BUILD_SDK_VERSION=28
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
|
||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,4 +1,4 @@
|
||||
#Tue Mar 05 11:25:17 CST 2019
|
||||
#Sat Aug 01 16:28:14 CST 2020
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
BIN
防止被一键去除签名校验.zip
Normal file
BIN
防止被一键去除签名校验.zip
Normal file
Binary file not shown.
Reference in New Issue
Block a user