Files
API-Security/apisecurity/src/main/java/cn/android/security/APISecurity.java
mahongyin 5728c6befc hook getPackageArchiveInfo无效
hook 参数区分 64 & ((Integer) objArr[1]).intValue()
2021-03-04 18:08:45 +08:00

352 lines
14 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package cn.android.security;
import android.annotation.SuppressLint;
import android.app.Application;
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.InvocationTargetException;
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.Iterator;
import java.util.List;
public class APISecurity {
static {
System.loadLibrary("apisecurity-lib");
}
/**
* 通过次Native方法调用Java的方法
*
* @param str
*/
public static native String sign(String str);
public static native void verifyApp(Application applicationByReflect);
public static native boolean init(Context context);
/**
* 被Native调用的Java方法
*/
public void javaMethod(String msg) {
Log.e("mhyLog错误", msg);
// System.exit(1);
}
public static void verify(Context context) {
// Log.e("mhyLog", "hash:" + AppSigning.getSignatureHash(context));
// Log.e("mhyLog", "sha1:" + getSignSha1(context));
//runCommand();
Log.e("mhyLog包文件", "签名:"+getApkSignatures(context, context.getPackageName()));
//Log.e("mhyLog已安装", "签名:"+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 从源安装文件获取签名信息
* 有bug
*/
public static String getApkSignatures(Context context, String packname) {
String path = getApkPath(context, packname);
File apkFile = new File(path);
if (apkFile.exists()) {
Log.e("mhyLog包安装路径", apkFile.getAbsolutePath());
PackageManager pm = context.getPackageManager();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
//TODO 这里获取的signingInfo 为空 猜想是flag不对 但看源码好像 目前只能使【GET_SIGNATURES 对应signatures】
PackageInfo packageInfo = pm.getPackageArchiveInfo(path, PackageManager.GET_SIGNING_CERTIFICATES);
if (packageInfo != null&&packageInfo.signingInfo!=null) {
Signature[] signatures = packageInfo.signingInfo.getApkContentsSigners();
return AppSigning.getSignatureString(signatures, AppSigning.SHA1);
}else {
return AppSigning.getAPKSignatures(path);
}
//如果获取失败就用下面方法喽
}else {
PackageInfo packageInfo = pm.getPackageArchiveInfo(path, PackageManager.GET_SIGNATURES);
if (packageInfo != null) {
Signature[] signatures = packageInfo.signatures;
return AppSigning.getSignatureString(signatures, AppSigning.SHA1);
}else {
return AppSigning.showUninstallAPKSignatures(path);
}
}
}
return "";
}
/**
* //获取此包安装路径
*/
public static String getApkPath(Context context, String packname) {
String path = "";
if (!TextUtils.isEmpty(packname)) {
try {
//第一种方法
ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(packname, 0);
path = applicationInfo.sourceDir;
//第二种方法
// Log.e("mhyLog其他已知包名apk的安装路径", applicationInfo.sourceDir + "&---&" + applicationInfo.publicSourceDir);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
try {
ApplicationInfo applicationInfo = context.getPackageManager().getPackageInfo(packname, PackageManager.GET_META_DATA).applicationInfo;
path = applicationInfo.publicSourceDir;//sourceDir; // 获取当前apk包的绝对路径
} catch (PackageManager.NameNotFoundException exception) {
exception.printStackTrace();
//第三中方法 本包
path = context.getApplicationInfo().sourceDir;
}
// Log.e("mhyLOg在apk中获取自身安装路径", path);
}
} else {
//第四中方法 本包
path = context.getPackageResourcePath();
}
return path;
}
/**
* 手动构建 Context
*/
@SuppressLint({"DiscouragedPrivateApi", "PrivateApi"})
public static Context createContext() throws ClassNotFoundException,
NoSuchMethodException,
InvocationTargetException,
IllegalAccessException,
NoSuchFieldException,
NullPointerException {
// 反射获取 ActivityThread 的 currentActivityThread 获取 mainThread
Class activityThreadClass = Class.forName("android.app.ActivityThread");
Method currentActivityThreadMethod =
activityThreadClass.getDeclaredMethod("currentActivityThread");
currentActivityThreadMethod.setAccessible(true);
Object mainThreadObj = currentActivityThreadMethod.invoke(null);
// 反射获取 mainThread 实例中的 mBoundApplication 字段
Field mBoundApplicationField = activityThreadClass.getDeclaredField("mBoundApplication");
mBoundApplicationField.setAccessible(true);
Object mBoundApplicationObj = mBoundApplicationField.get(mainThreadObj);
// 获取 mBoundApplication 的 packageInfo 变量
if (mBoundApplicationObj == null)
throw new NullPointerException("mBoundApplicationObj 反射值空");
Class mBoundApplicationClass = mBoundApplicationObj.getClass();
Field infoField = mBoundApplicationClass.getDeclaredField("info");
infoField.setAccessible(true);
Object packageInfoObj = infoField.get(mBoundApplicationObj);
// 反射调用 ContextImpl.createAppContext(ActivityThread mainThread, LoadedApk packageInfo)
if (mainThreadObj == null) throw new NullPointerException("mainThreadObj 反射值空");
if (packageInfoObj == null) throw new NullPointerException("packageInfoObj 反射值空");
Method createAppContextMethod = Class.forName("android.app.ContextImpl").getDeclaredMethod(
"createAppContext",
mainThreadObj.getClass(),
packageInfoObj.getClass());
createAppContextMethod.setAccessible(true);
return (Context) createAppContextMethod.invoke(null, mainThreadObj, packageInfoObj);
}
//需要读取应用列表权限
public static List<String> getAppList(Context context) {
List<String> list = new ArrayList<>();
PackageManager pm = context.getPackageManager();
List<PackageInfo> packages = pm.getInstalledPackages(0);
// pm.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES);// GET_UNINSTALLED_PACKAGES代表已删除但还有安装目录的
for (PackageInfo packageInfo : packages) {
// 判断系统/非系统应用
if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {// 非系统应用
Log.e("mhyLog", "packageInfo=" + packageInfo.packageName);
list.add(packageInfo.packageName);
} else {
// 系统应用
}
}
return list;
}
/**
* 通过已安装app 获取当前app签名
*/
private static String getSignSha1(Context context) {
List<PackageInfo> apps;
PackageManager pm = context.getPackageManager();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
apps = pm.getInstalledPackages(PackageManager.GET_SIGNING_CERTIFICATES);
} else {
apps = pm.getInstalledPackages(PackageManager.GET_SIGNATURES);
}
for (PackageInfo packageinfo : apps) {
String packageName = packageinfo.packageName;
if (packageName.equals(context.getPackageName())) {
Log.e("mhyLog", "packageInfo=" + packageinfo.packageName);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
if (packageinfo.signingInfo != null) {
return AppSigning.getSignatureString(packageinfo.signingInfo.getApkContentsSigners(), AppSigning.SHA1);
}
} else {
if (packageinfo.signatures != null) {
return AppSigning.getSignatureString(packageinfo.signatures, AppSigning.SHA1);
}
}
}
}
return "";
}
/**
* 通过指令获取已安装的包
*/
private static ArrayList<String> runCommand() {
ArrayList<String> list = new ArrayList<>();
list.clear();
try {
Process process = Runtime.getRuntime().exec("pm list package -3");
BufferedReader bis = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = null;
while ((line = bis.readLine()) != null) {
Log.e("runCommand", "line=" + line);
list.add(line.split(":")[1]);
}
} catch (IOException 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 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();
}