App.hook(context);//hook签名验证

//在这里 重置PackageManager 只要在验证前重置即可
 // AppSigning.resetPackageManager(getBaseContext());再hook之后 验证签名之前重置即可 获取真实签名

 APISecurity.init(this)//验证三步走
 1.验证签名是否符合自己预设
 2.验证包名
 3验证apk源文件签名信息
This commit is contained in:
mahongyin
2021-03-02 16:36:38 +08:00
parent 93670d6af7
commit a8c51116b8
14 changed files with 687 additions and 237 deletions

View File

@@ -55,3 +55,11 @@ Android API Security(.so)安卓APP/API安全加密so库防二次打包
String val = "POST https://www.xxx.com/login?id=1&pwd=xxx......"; String val = "POST https://www.xxx.com/login?id=1&pwd=xxx......";
String sign = MGAPISecurity.sign(aptStr) String sign = MGAPISecurity.sign(aptStr)
``` ```
App.hook(context);//hook签名验证
//在这里 重置PackageManager 只要在验证前重置即可
// AppSigning.resetPackageManager(getBaseContext());再hook之后 验证签名之前重置即可 获取真实签名
APISecurity.init(this)//验证三步走
1.验证签名是否符合自己预设
2.验证包名
3验证apk源文件签名信息

View File

@@ -8,7 +8,7 @@ android {
targetSdkVersion ANDROID_BUILD_TARGET_SDK_VERSION as int targetSdkVersion ANDROID_BUILD_TARGET_SDK_VERSION as int
versionCode 1 versionCode 1
versionName "1.0" versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
} }
@@ -21,5 +21,5 @@ android {
} }
dependencies { dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'androidx.annotation:annotation:1.0.0'
} }

View File

@@ -4,14 +4,30 @@ import android.content.Context;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.os.Build; import android.os.Build;
import android.os.Debug; import android.os.Debug;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log; import android.util.Log;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader; 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.ArrayList;
import java.util.List; import java.util.List;
@@ -31,97 +47,108 @@ public class APISecurity {
/** /**
* 被Native调用的Java方法 * 被Native调用的Java方法
*
* @param msg
*/ */
public void javaMethod(String msg) { public void javaMethod(String msg) {
Log.e("错误代码",msg); Log.e("错误代码", msg);
// System.exit(1); // System.exit(1);
} }
private void verify(Context context){ public static void verify(Context context) {
// String ppp = runCommand().get(0); Log.e("mhyLog", "hash"+AppSigning.getSignatureHash(context));
runCommand();
Log.e("包路径文件签名", getApkSignatures(context,"com.tencent.mm")); Log.e("包路径文件签名", getApkSignatures(context, context.getPackageName()));
Log.e("已安装APP签名", getInstalledAPKSignature(context, context.getPackageName()));
Log.e("已安装APP签名", AppSigning.getSingInfo(context, "com.tencent.mm", AppSigning.SHA1));
//通过获取其他应用的签名 如果一样那么被hook了 //通过获取其他应用的签名 如果一样那么被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) { public static String getApkSignatures(Context context, String packname) {
String sign = ""; String sign = "";
String path = null; String path = null;
try { // try {//获取此包安装路径
path = context.getPackageManager().getApplicationInfo(packname, 0).sourceDir; // //第一种方法
} catch (PackageManager.NameNotFoundException e) { // ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(packname, 0);
e.printStackTrace(); // path = applicationInfo.sourceDir;
} // //第二种方法
// PackageInfo packageInfo = context.getPackageManager().getPackageInfo(packname, PackageManager.GET_META_DATA); //// ApplicationInfo applicationInfo = context.getPackageManager().getPackageInfo(packname, PackageManager.GET_META_DATA).applicationInfo;
// ApplicationInfo applicationInfo = packageInfo.applicationInfo; //// path = applicationInfo.publicSourceDir;//sourceDir; // 获取当前apk包的绝对路径
// String path =applicationInfo.publicSourceDir;//sourceDir; // 获取当前apk包的绝对路径 // Log.e("其他已知包名apk的安装路径", applicationInfo.sourceDir+ "&---&" + applicationInfo.publicSourceDir);
File apkFile=new File(path); // } 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()) { if (apkFile != null && apkFile.exists()) {
Log.e("pppp",apkFile.getAbsolutePath()); Log.e("包安装路径", apkFile.getAbsolutePath());
PackageManager pm = context.getPackageManager(); PackageManager pm = context.getPackageManager();
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
PackageInfo pkgInfo = pm.getPackageArchiveInfo(apkFile.getAbsolutePath(), PackageManager.GET_SIGNING_CERTIFICATES); PackageInfo pkgInfo = pm.getPackageArchiveInfo(apkFile.getAbsolutePath(), PackageManager.GET_SIGNING_CERTIFICATES);
if (pkgInfo != null && pkgInfo.signingInfo != null && pkgInfo.signingInfo.getApkContentsSigners().length > 0) { if (pkgInfo != null && pkgInfo.signingInfo != null && pkgInfo.signingInfo.getApkContentsSigners().length > 0) {
// sign = pkgInfo.signingInfo.getApkContentsSigners()[0].toCharsString(); sign = AppSigning.getSignatureString(pkgInfo.signingInfo.getApkContentsSigners(), AppSigning.SHA1);
sign = AppSigning.getSignatureString(pkgInfo.signingInfo.getApkContentsSigners()[0],AppSigning.SHA1);
} }
} else { } else {
PackageInfo pkgInfo = pm.getPackageArchiveInfo(apkFile.getAbsolutePath(), PackageManager.GET_SIGNATURES); PackageInfo pkgInfo = pm.getPackageArchiveInfo(apkFile.getAbsolutePath(), PackageManager.GET_SIGNATURES);
if (pkgInfo != null && pkgInfo.signatures != null && pkgInfo.signatures.length > 0) { if (pkgInfo != null && pkgInfo.signatures != null && pkgInfo.signatures.length > 0) {
// sign = pkgInfo.signatures[0].toCharsString(); sign = AppSigning.getSignatureString(pkgInfo.signatures, AppSigning.SHA1);
sign = AppSigning.getSignatureString(pkgInfo.signatures[0],AppSigning.SHA1);
} }
} }
} }
return sign; return sign;
} }
//获取已安装的app签名
private static String getInstalledAPKSignature(Context context, String packageName) {
//需要读取应用列表权限
public void getAppList(Context context) {
PackageManager pm = context.getPackageManager(); 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); List<PackageInfo> packages = pm.getInstalledPackages(0);
for (PackageInfo packageInfo : packages) { for (PackageInfo packageInfo : packages) {
// 判断系统/非系统应用 // 判断系统/非系统应用
if (( if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {// 非系统应用
packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) // 非系统应用
{
System.out.println("MainActivity.getAppList, packageInfo=" + packageInfo.packageName); System.out.println("MainActivity.getAppList, packageInfo=" + packageInfo.packageName);
} else { } else {
// 系统应用 // 系统应用
} }
} }
} }
static ArrayList<String> list = new ArrayList<>(); static ArrayList<String> list = new ArrayList<>();
/**
* 通过指令获取已安装的包
*
* @return
*/
private static ArrayList<String> runCommand() { private static ArrayList<String> runCommand() {
list.clear(); list.clear();
try { try {
@@ -133,25 +160,91 @@ public class APISecurity {
list.add(line.split(":")[1]); list.add(line.split(":")[1]);
} }
} catch (IOException e) { } catch (IOException e) {
System.out.println("runCommand,e=" + e); Log.e("runCommand", "e=" + e);
} }
return list; return list;
} }
/** /**
* 检测动态调试 * 检测动态调试检查应用是否处于调试状态
* 这个也是借助系统的一个api来进行判断isDebuggerConnected()
* jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700当连接成功之后这个方法就会返回true
*/ */
public void detectedDynamicDebug(){ public static void detectedDynamicDebug() {
if (!BuildConfig.DEBUG){ if (!BuildConfig.DEBUG) {
if (Debug.isDebuggerConnected()){ if (Debug.isDebuggerConnected()) {
//进程自杀 //进程自杀
int myPid = android.os.Process.myPid(); int myPid = android.os.Process.myPid();
android.os.Process.killProcess(myPid); android.os.Process.killProcess(myPid);
//异常退出虚拟机 //异常退出虚拟机
System.exit(1); 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();
} }

View File

@@ -1,24 +1,34 @@
package cn.android.security; package cn.android.security;
/** import android.annotation.SuppressLint;
* @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.content.Context; import android.content.Context;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.Signature; import android.content.pm.Signature;
import android.os.Build; 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.MessageDigest;
import java.security.NoSuchAlgorithmException; 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 * @author lWX537240
* @date 2019/6/13 * @date 2019/6/13
@@ -33,67 +43,21 @@ public class AppSigning {
public final static String SHA256 = "SHA256"; public final static String SHA256 = "SHA256";
/** /**
* 返回一个签名的对应类型的字符串 * 注解限定String类型为指定
*
* @param context
* @param packageName
* @param type
* @return
*/ */
public static String getSingInfo(Context context, String packageName, String type) { @StringDef({MD5, SHA1, SHA256})
String tmp = "error!"; @Retention(RetentionPolicy.SOURCE)
try { @interface SigniType {
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;
} }
/** /**
* 返回对应包的签名信息 * 获取相应的类型的签名信息把签名的byte[]信息转换成16进制
*
* @param context
* @param packageName
* @return
*/ */
public static Signature[] getSignatures(Context context, String packageName){ public static String getSignatureString(Signature[] sigs, @SigniType String type) {
try { for (Signature sig : sigs) {
Log.e("mhyLog","getSignatureString:"+sig.toCharsString());
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;
} }
} catch (PackageManager.NameNotFoundException e) { byte[] hexBytes = sigs[0].toByteArray();
e.printStackTrace();
}
return null;
}
/**
* 获取相应的类型的字符串把签名的byte[]信息转换成16进制
*
* @param sig
* @param type
* @return
*/
public static String getSignatureString(Signature sig, String type) {
byte[] hexBytes = sig.toByteArray();
String fingerprint = "error!"; String fingerprint = "error!";
try { try {
StringBuffer buffer = new StringBuffer(); 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;
}
} }

View File

@@ -10,7 +10,7 @@ android {
versionCode 1 versionCode 1
versionName "1.0" versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
// ndk{ // ndk{
// //生成so库名称 也是System.load // //生成so库名称 也是System.load
// moduleName "apisecurity-lib" // moduleName "apisecurity-lib"
@@ -19,8 +19,10 @@ android {
externalNativeBuild { externalNativeBuild {
cmake { cmake {
cppFlags "" cppFlags ""
// abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a', 'arm64-v8a'
} }
} }
} }
signingConfigs { signingConfigs {
@@ -30,15 +32,18 @@ android {
storeFile file("test.keystore") storeFile file("test.keystore")
storePassword '123456' storePassword '123456'
} }
} }
buildTypes { buildTypes {
release { release {
debuggable false//xml android:debuggable="false"
minifyEnabled false minifyEnabled false
signingConfig signingConfigs.release signingConfig signingConfigs.release
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
} }
debug{ debug{
debuggable true
signingConfig signingConfigs.release signingConfig signingConfigs.release
} }
} }
@@ -54,10 +59,7 @@ android {
dependencies { dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs') implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'androidx.appcompat:appcompat:1.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3' implementation 'androidx.constraintlayout:constraintlayout: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 project(':apisecurity') implementation project(':apisecurity')
} }

View File

@@ -11,6 +11,7 @@
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning"> tools:ignore="GoogleAppIndexingWarning">
<activity android:name=".MainActivity"> <activity android:name=".MainActivity">
<intent-filter> <intent-filter>

View File

@@ -1,6 +1,9 @@
#include <jni.h> #include <jni.h>
#include <string> #include <string>
#include <android/log.h> #include <android/log.h>
#include <string.h>
#include <dirent.h>
#include <unistd.h>
//log定义 //log定义
#define LOG "APISECURITY" // 这个是自定义的LOG的TAG #define LOG "APISECURITY" // 这个是自定义的LOG的TAG
@@ -11,32 +14,32 @@
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,LOG,__VA_ARGS__) #define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,LOG,__VA_ARGS__)
//此处改为你的APP签名 //此处改为你的APP签名
//#define SHA1 "a8e3d91a4f77dd7ccb8d43ee5046a4b6833f4785"//区分小写 //#define SHA1 "a8e3d91a4f77dd7ccb8d43ee5046a4b6833f4785"//真实test.keystore
#define SHA1 "04c1411b0662acd9e4aa300559677e5f106a5255" #define SHA1 "04c1411b0662acd9e4aa300559677e5f106a5255"//区分da小写
#define ALGORITHM_SHA1 "SHA1" #define ALGORITHM_SHA1 "SHA1"
#define ALGORITHM_MD5 "MD5" #define ALGORITHM_MD5 "MD5"
//此处改为你的APP包名 //此处改为你的APP包名
#define APP_PKG "cn.android.sample" #define APP_PKG "cn.android.sample"
//此处填写API盐值 //此处填写API盐值
#define API_SECRET "ABC1234567"//设置api 密钥 返回md5 #define API_SECRET "ABC1234567"//设置api 密钥 MD5加盐
static bool isInit = false; static bool isInit = false;
static char *secret; static char *secret;
//void printByte(JNIEnv *env, jbyteArray jbytes) { void printByte(JNIEnv *env, jbyteArray jbytes) {
// //转换成char //转换成char
// jsize array_size = env->GetArrayLength(jbytes); jsize array_size = env->GetArrayLength(jbytes);
// jbyte *sha1 = env->GetByteArrayElements(jbytes, NULL); jbyte *sha1 = env->GetByteArrayElements(jbytes, nullptr);
//
// char *hexA = new char[array_size * 2 + 1](); char *hexA = new char[array_size * 2 + 1]();
// for (int i = 0; i < array_size; ++i) { for (int i = 0; i < array_size; ++i) {
// sprintf(hexA + 2 * i, "%02x", (u_char) sha1[i]); sprintf(hexA + 2 * i, "%02x", (u_char) sha1[i]);
// } }
// LOGD("printByte:%s", hexA); LOGD("printByte:%s", hexA);
//} }
char *digest(JNIEnv *env, const char *algorithm, jbyteArray cert_byte) { char *digest(JNIEnv *env, const char *algorithm, jbyteArray cert_byte) {
jclass message_digest_class = env->FindClass("java/security/MessageDigest"); jclass message_digest_class = env->FindClass("java/security/MessageDigest");
@@ -51,7 +54,7 @@ char *digest(JNIEnv *env, const char *algorithm, jbyteArray cert_byte) {
//转换成char //转换成char
jsize array_size = env->GetArrayLength(sha1_byte); 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](); char *hex = new char[array_size * 2 + 1]();
for (int i = 0; i < array_size; ++i) { for (int i = 0; i < array_size; ++i) {
sprintf(hex + 2 * i, "%02x", (unsigned char) sha1[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) { jobject getPackageInfo(JNIEnv *env, jobject package_manager, jstring package_name) {
jclass pack_manager_class = env->GetObjectClass(package_manager); jclass pack_manager_class = env->GetObjectClass(package_manager);
jmethodID methodId = env->GetMethodID(pack_manager_class, "getPackageInfo", jmethodID methodId = env->GetMethodID(pack_manager_class, "getPackageInfo",
"(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;"); "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
env->DeleteLocalRef(pack_manager_class); 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; 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); jclass pack_manager_class = env->GetObjectClass(package_manager);
jmethodID methodId = env->GetMethodID(pack_manager_class, "getPackageArchiveInfo", jmethodID methodId = env->GetMethodID(pack_manager_class, "getPackageArchiveInfo",
"(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;"); "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
env->DeleteLocalRef(pack_manager_class); 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; 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 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 jfieldID appfield = env->GetFieldID(apppack_info_class, "sourceDir",
// env->DeleteLocalRef(pack_manager_class); "Ljava/lang/String;");//获取此类中的sourceDir成员id P->publicSourceDir
jstring absoluteapp=(jstring)env ->GetObjectField(apppack_info_object,appfield); env->DeleteLocalRef(apppack_info_class);
if (absoluteapp == NULL) jstring absoluteapp = (jstring) env->GetObjectField(apppack_info_object, appfield);
if (absoluteapp == nullptr)
return env->NewStringUTF(""); return env->NewStringUTF("");
return absoluteapp; return absoluteapp;
} }
@@ -125,8 +162,8 @@ jobject getSignature(JNIEnv *env, jobject package_info) {
"[Landroid/content/pm/Signature;"); "[Landroid/content/pm/Signature;");
env->DeleteLocalRef(package_info_class); env->DeleteLocalRef(package_info_class);
jobjectArray signature_object_array = (jobjectArray) env->GetObjectField(package_info, fieldId); jobjectArray signature_object_array = (jobjectArray) env->GetObjectField(package_info, fieldId);
if (signature_object_array == NULL) if (signature_object_array == nullptr)
return NULL; return nullptr;
return env->GetObjectArrayElement(signature_object_array, 0); return env->GetObjectArrayElement(signature_object_array, 0);
} }
@@ -156,7 +193,55 @@ jbyteArray getSHA1(JNIEnv *env, jobject signature_object) {
return cert_byte; 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("非法调用4SHA1: %s", hex_sha);
return JNI_FALSE;
}
return JNI_TRUE;
}
/**
* 调用
*/
extern "C" JNIEXPORT jboolean JNICALL extern "C" JNIEXPORT jboolean JNICALL
Java_cn_android_security_APISecurity_init( Java_cn_android_security_APISecurity_init(
JNIEnv *env, JNIEnv *env,
@@ -168,24 +253,24 @@ Java_cn_android_security_APISecurity_init(
//反射获取PackageManager //反射获取PackageManager
jobject package_manager = getPackageManager(env, context_object, context_class); jobject package_manager = getPackageManager(env, context_object, context_class);
if (package_manager == NULL) if (package_manager == nullptr)
return JNI_FALSE; return JNI_FALSE;
//反射获取包名 //反射获取包名
jstring package_name = getPackageName(env, context_class, context_object); jstring package_name = getPackageName(env, context_class, context_object);
if (package_name == NULL) if (package_name == nullptr)
return JNI_FALSE; return JNI_FALSE;
env->DeleteLocalRef(context_class); env->DeleteLocalRef(context_class);
//获取PackageInfo对象 //获取PackageInfo对象
jobject package_info = getPackageInfo(env, package_manager, package_name); jobject package_info = getPackageInfo(env, package_manager, package_name);
if (package_info == NULL) if (package_info == nullptr)
return JNI_FALSE; return JNI_FALSE;
env->DeleteLocalRef(package_manager); env->DeleteLocalRef(package_manager);
//获取签名信息 //获取签名信息
jobject signature_object = getSignature(env, package_info); jobject signature_object = getSignature(env, package_info);
if (signature_object == NULL) if (signature_object == nullptr)
return JNI_FALSE; return JNI_FALSE;
env->DeleteLocalRef(package_info); env->DeleteLocalRef(package_info);
jbyteArray cert_byte = getSHA1(env, signature_object); 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); char *hex_sha = digest(env, ALGORITHM_SHA1, cert_byte);
if (strcmp(hex_sha, SHA1) != 0) {//签名不对 if (strcmp(hex_sha, SHA1) != 0) {//签名不对
LOGE("非法调用1SHA1: %s:%s", hex_sha, SHA1); LOGE("非法调用1SHA1: %s", hex_sha);
return JNI_FALSE; 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) { if (strcmp(pkgName, APP_PKG) == 0) {
secret = API_SECRET;//包名匹配 拿取api secret = API_SECRET;//包名匹配 拿取api
} else { } else {
LOGE("非法调用2Package: %s:%s", pkgName, APP_PKG); LOGE("非法调用2Package: %s", pkgName);
return JNI_FALSE; return JNI_FALSE;
} }
// 接着调用Java方法验证 安装目录文件签名 /*********接着调用Java方法验证 安装目录apk文件de签名**/
jclass cls_util = env->FindClass( // jclass cls_util = env->FindClass(
"cn/android/security/APISecurity");//注意,这里的使用的斜杠而不是点 // "cn/android/security/APISecurity");
if (cls_util == NULL) { // //注意,这里的使用的斜杠而不是点
// 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("非法调用3SHA1: %s", ss);
// return JNI_FALSE;
// }
/*******************调用Java方法结束***/
//加强验证
if (!getApkPathSignatures(env, context_object)) {
return JNI_FALSE; 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("非法调用3SHA1: %s:%s", ss, SHA1);
return JNI_FALSE;
}
//
isInit = true; isInit = true;
LOGI("初始化成功!"); LOGI("初始化成功!");
return JNI_TRUE; return JNI_TRUE;
} }
extern "C" JNIEXPORT jstring JNICALL extern "C" JNIEXPORT jstring JNICALL
Java_cn_android_security_APISecurity_sign( Java_cn_android_security_APISecurity_sign(
JNIEnv *env, JNIEnv *env,
@@ -246,34 +335,37 @@ Java_cn_android_security_APISecurity_sign(
if (!isInit) { if (!isInit) {
LOGE("请先初始化!"); LOGE("请先初始化!");
jclass cls_util = env->FindClass( jclass cls_util = env->FindClass(
"cn/android/security/APISecurity");//注意,这里的使用的斜杠而不是点 "cn/android/security/APISecurity");//注意,这里的使用的斜杠而不是点
if (cls_util == NULL) { if (cls_util == nullptr) {
return env->NewStringUTF(""); return env->NewStringUTF("");
} }
jobject j_obj = env->AllocObject(cls_util);//**这里是关键** //调用Java 方法
jmethodID mtd_static_method = env->GetMethodID(cls_util, jobject j_obj = env->AllocObject(cls_util);
"javaMethod", //**这里是关键**GetMethodID是普通方法 GetStaticMethodID静态方法
"(Ljava/lang/String;)V"); jmethodID mtd_static_method = env->GetMethodID(cls_util,
if (mtd_static_method == NULL) { "javaMethod",
return env->NewStringUTF(""); "(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方法 if (data != nullptr) {//nullptr
env->CallVoidMethod(/*context_object*/j_obj, mtd_static_method, data); //调用Java方法
env->CallVoidMethod(j_obj, mtd_static_method, data);
//删除引用 //删除引用
env->DeleteLocalRef(cls_util); env->DeleteLocalRef(cls_util);
env->DeleteLocalRef(j_obj); env->DeleteLocalRef(j_obj);
env->DeleteLocalRef(data); env->DeleteLocalRef(data);
}
return env->NewStringUTF(""); return env->NewStringUTF("");
} }
//已经通过初始化 //已经通过初始化
const char *sx; const char *sx;
sx = env->GetStringUTFChars(str, NULL); sx = env->GetStringUTFChars(str, nullptr);
//通过传来计算 //通过传来计算
char *full = new char[strlen(sx) + strlen(secret) + 1](); char *full = new char[strlen(sx) + strlen(secret) + 1]();
strcat(full, sx); strcat(full, sx);
@@ -293,22 +385,80 @@ Java_cn_android_security_APISecurity_sign(
//JNIEXPORT jboolean JNICALL //JNIEXPORT jboolean JNICALL
//Java_cn_android_security_APISecurity_check(JNIEnv *env, jclass clazz, jstring str) { //Java_cn_android_security_APISecurity_check(JNIEnv *env, jclass clazz, jstring str) {
// const char *sx; // const char *sx;
// sx = env->GetStringUTFChars(str, NULL); // sx = env->GetStringUTFChars(str, nullptr);
// char name[512]; // char name[512];
// strcpy(name, sx); // strcpy(name, sx);
// //
// return 0 == strcmp(SHA1, "md5"); // return 0 == strcmp(SHA1, "md5");
//} //}
//APISecurity.adbshell("pm list package -3",getFilesDir().getPath() + File.separator + "files"+File.separator + "adbshell.txt"); //APISecurity.adbshell("pm list package -3",getFilesDir().getPath() + File.separator + "files"+File.separator + "adbshell.txt");
//extern "C" //extern "C" JNIEXPORT jstring JNICALL
//JNIEXPORT jstring JNICALL //Java_cn_android_security_APISecurity_adbshell(JNIEnv *env, jclass clazz, jstring str,
//Java_cn_android_security_APISecurity_adbshell(JNIEnv *env, jclass clazz, jstring str,jstring path) { // jstring path) {
// // int ret= system("pm list package -3");//获取安装应用 // int ret = system("pm list package -3");//获取安装应用
// // char *str2 = (char *) env->GetStringUTFChars(str, nullptr);
// char* str2=(char *)env->GetStringUTFChars(str, NULL); // char *path2 = (char *) env->GetStringUTFChars(path, nullptr);
// char* path2=(char *)env->GetStringUTFChars(path, NULL); // strcat(str2, " > ");
// strcat(str2," > "); // strcat(str2, path2);
// strcat(str2,path2);
// system(str2); // system(str2);
// return env->NewStringUTF(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;
}

View File

@@ -23,6 +23,8 @@ import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Proxy; import java.lang.reflect.Proxy;
import cn.android.security.AppSigning;
//继承原application //继承原application
public class HookApplication extends Application implements InvocationHandler { public class HookApplication extends Application implements InvocationHandler {
@@ -35,12 +37,15 @@ public class HookApplication extends Application implements InvocationHandler {
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
app=this; app = this;
//在签名校验被hook 之后重置PackageManager
/*在这里 重置PackageManager 只要在验证前重置即可*/
AppSigning.resetPackageManager(getBaseContext());
} }
public static Context getContext() { public static Context getContext() {
if (app==null){ if (app == null) {
app=new HookApplication(); app = new HookApplication();
app.onCreate(); app.onCreate();
} }
return app; return app;
@@ -49,16 +54,22 @@ public class HookApplication extends Application implements InvocationHandler {
@Override @Override
protected void attachBaseContext(Context context) { protected void attachBaseContext(Context context) {
//在这里hook 签名校验被
hook(context); hook(context);
super.attachBaseContext(context); super.attachBaseContext(context);
} }
/** /**
* 全局hook 签名校验 * 全局hook 签名校验
*
* @param context * @param context
*/ */
private void hook(Context context) { private void hook(Context context) {
try { 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)][]; byte[][] bArr = new byte[(dataInputStream.read() & 255)][];
for (int i = 0; i < bArr.length; i++) { for (int i = 0; i < bArr.length; i++) {
bArr[i] = new byte[dataInputStream.readInt()]; bArr[i] = new byte[dataInputStream.readInt()];
@@ -89,7 +100,7 @@ public class HookApplication extends Application implements InvocationHandler {
@Override @Override
public Object invoke(Object obj, Method method, Object[] objArr) throws Throwable { 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]; String str = (String) objArr[0];
if ((((Integer) objArr[1]).intValue() & 64) != 0 && this.appPkgName.equals(str)) { if ((((Integer) objArr[1]).intValue() & 64) != 0 && this.appPkgName.equals(str)) {
PackageInfo packageInfo = (PackageInfo) method.invoke(this.base, objArr); PackageInfo packageInfo = (PackageInfo) method.invoke(this.base, objArr);

View File

@@ -2,7 +2,7 @@ package cn.android.sample;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.os.Bundle; import android.os.Bundle;
import android.support.v7.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import android.view.View; import android.view.View;
import android.widget.TextView; import android.widget.TextView;
@@ -23,8 +23,10 @@ public class MainActivity extends AppCompatActivity {
if(APISecurity.init(this)){ if(APISecurity.init(this)){
tv.setText("初始化ok"); tv.setText("初始化ok");
}else {
tv.setText("初始化fail");
} }
APISecurity.verify(this);
findViewById(R.id.btnTest).setOnClickListener(new View.OnClickListener() { findViewById(R.id.btnTest).setOnClickListener(new View.OnClickListener() {
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
@Override @Override
@@ -39,4 +41,9 @@ public class MainActivity extends AppCompatActivity {
} }
@Override
protected void onResume() {
super.onResume();
APISecurity.detectedDynamicDebug();
}
} }

View File

@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?> <?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:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context="cn.wzbos.android.sample.MainActivity"> >
<TextView <TextView
android:id="@+id/sample_text" android:id="@+id/sample_text"
@@ -26,4 +26,4 @@
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/sample_text" /> app:layout_constraintTop_toBottomOf="@+id/sample_text" />
</android.support.constraint.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -7,7 +7,7 @@ buildscript {
jcenter() jcenter()
} }
dependencies { 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 // NOTE: Do not place your application dependencies here; they belong

View File

@@ -15,3 +15,5 @@ android.useDeprecatedNdk=true
ANDROID_BUILD_MIN_SDK_VERSION=16 ANDROID_BUILD_MIN_SDK_VERSION=16
ANDROID_BUILD_TARGET_SDK_VERSION=28 ANDROID_BUILD_TARGET_SDK_VERSION=28
ANDROID_BUILD_SDK_VERSION=28 ANDROID_BUILD_SDK_VERSION=28
android.useAndroidX=true
android.enableJetifier=true

View File

@@ -1,4 +1,4 @@
#Tue Mar 05 11:25:17 CST 2019 #Sat Aug 01 16:28:14 CST 2020
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

Binary file not shown.