好奇一些常见的java层签名校验实现思路,于是研究了一下SRpatch这一优秀的工具,学到了不少知识
<!--more-->
SRpatch入口
通过观察Manifest.xml,观察到app入口被改为了com.srp.patch.Init类,发现其被混淆(事实上,SRpatch的核心代码均使用Milk混淆,以增加分析难度)
于是进行了初步的反混淆,得到了大致执行流程:
public class AppInfo {
// 配置JSON文件中的键名常量
public static final String KEY_PACKAGE_NAME = "packageName";
public static final String KEY_SIGNATURE = "signature";
public static final String KEY_SIGNATURE_STRENGTH = "signatureStrength";
public static final String KEY_APK_SIZE = "apkSize";
public static final String KEY_ORIGINAL_APPLICATION_NAME = "originalApplicationName";
public static final String KEY_PMS_PROXY_METHOD = "pmsProxyMethod";
public static final String KEY_PATH_REDIRECTION_ENABLED = "pathRedirectionEnabled";
// 目标应用的包名(要被伪装的应用)
public static String packageName;
// 伪造的签名数据(Base64编码的签名)
public static String signature;
// 签名绕过强度等级(0-4,数值越大绕过能力越强)
public static int signatureStrength;
// 原始APK文件大小,用于大小校验绕过
public static long apkSize;
// 原始应用的Application类名,用于应用名伪装
public static String originalApplicationName;
// PMS代理方法类型("IBINDER_PROXY" 或 "CREATOR_REPLACE")
public static String pmsProxyMethod;
// 是否启用APK路径重定向
public static boolean pathRedirectionEnabled;
}
AppInfo类中保存从SRPatch_config.json中解析到的原app信息及配置
//自定义application实现优先加载
public class Init extends Application {
/**
* 异常处理工具类 - 提供Throwable.addSuppressed的兼容性支持
* 用于在try-with-resources中正确处理异常链
*/
public static final class ExceptionCompat {
/**
* 安全地添加被抑制的异常
* 在低版本Android上捕获可能的异常
*/
public static void addSuppressedSafely(Throwable mainThrowable, Throwable suppressedThrowable) {
try {
mainThrowable.addSuppressed(suppressedThrowable);
} catch (Exception ignored) {
// 在低版本Android上可能不支持addSuppressed方法
// 忽略异常,不影响主要功能
}
}
}
// 配置文件名称
private static final String CONFIG_FILE_NAME = "SRPatch_config.json";
// Native库名称
private static final String NATIVE_LIB_NAME = "libSRPatch.so";
// 日志标签
private static final String LOG_TAG = "SRPatch.Init";
// 原始Instrumentation对象引用
private Instrumentation originalInstrumentation;
// 原始Application对象引用
private static Application originalApplication;
/**
* 应用初始化入口 - 在应用创建时最早被调用
* 执行所有必要的Hook和伪装操作
*/
@Override
protected void attachBaseContext(Context context) {
super.attachBaseContext(context);
try {
Log.i(LOG_TAG, "开始初始化签名绕过工具...");
// 步骤1: 解析配置文件
parseAndStoreConfig(context);
// 步骤2: 加载Native库
loadNativeLibrary(context);
// 步骤3: 如果需要路径重定向或高强度签名绕过,确保基础APK存在
if (AppInfo.pathRedirectionEnabled || AppInfo.signatureStrength >= 2) {
ensureBaseApkExists(context);
}
// 步骤4: 执行应用名称伪装
ApplicationNameSpoofing.spoofApplicationName(context);
// 步骤5: 初始化Patch模块
Patch.init(context);
// 步骤6: 伪装当前类名
disguiseClassName(this.getClass(), AppInfo.originalApplicationName);
// 步骤7: 创建原始Application实例
createOriginalApplication(context);
Log.i(LOG_TAG, "签名绕过工具初始化完成");
} catch (Exception e) {
Log.e(LOG_TAG, "初始化过程中发生异常", e);
// 注意:这里捕获异常但不抛出,确保应用即使Hook失败也能正常启动
}
}
/**
* 创建原始Application实例
* 通过反射创建并替换原始Application,保持应用正常功能
*/
private void createOriginalApplication(Context context) throws Exception {
if (AppInfo.originalApplicationName == null) {
Log.w(LOG_TAG, "未配置原始应用名,跳过创建原始Application");
return;
}
// 获取当前ActivityThread实例
ActivityThread activityThread = ActivityThread.currentActivityThread();
// 获取并修改Instrumentation字段
Field instrumentationField = ActivityThread.class.getDeclaredField("mInstrumentation");
instrumentationField.setAccessible(true);
this.originalInstrumentation = (Instrumentation) instrumentationField.get(activityThread);
// 创建原始Application实例
Class<?> originalAppClass = context.getClassLoader().loadClass(AppInfo.originalApplicationName);
Init.originalApplication = Instrumentation.newApplication(originalAppClass, context);
// 替换ActivityThread中的初始Application
Field initialAppField = ActivityThread.class.getDeclaredField("mInitialApplication");
initialAppField.setAccessible(true);
activityThread.mInitialApplication = Init.originalApplication;
// 修改应用名称信息
modifyApplicationName(context);
Log.i(LOG_TAG, "原始Application实例创建完成: " + AppInfo.originalApplicationName);
}
/**
* 伪装类名 - 通过Unsafe直接修改Class对象中的名称字段
* 这是一种深度Hook技术,可以改变类的身份标识
*/
private static void disguiseClassName(Class<?> targetClass, String newClassName) throws Exception {
if (newClassName == null) {
return;
}
// 获取Unsafe实例
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) unsafeField.get(null);
// 修改类的name字段
Field nameField = Class.class.getDeclaredField("name");
unsafe.putObject(targetClass, unsafe.objectFieldOffset(nameField), newClassName);
// 清空类加载器引用,增加伪装效果
Field classLoaderField = Class.class.getDeclaredField("classLoader");
unsafe.putObject(targetClass, unsafe.objectFieldOffset(classLoaderField), null);
Log.i(LOG_TAG, "类名伪装完成: " + targetClass + " -> " + newClassName);
}
/**
* 确保基础APK文件存在
* 如果不存在则从assets中复制,用于路径重定向
*/
private void ensureBaseApkExists(Context context) {
File patchDirectory = new File(context.getFilesDir().getParentFile(), "srpatch");
// 创建目录(如果不存在)
if (!patchDirectory.exists()) {
boolean dirsCreated = patchDirectory.mkdirs();
Log.i(LOG_TAG, "创建patch目录: " + dirsCreated + ", 路径: " + patchDirectory.getAbsolutePath());
}
File baseApkFile = new File(patchDirectory, "base.apk");
if (!baseApkFile.exists()) {
Log.i(LOG_TAG, "base.apk不存在,从assets复制...");
copyBaseApkFromAssets(context, baseApkFile);
} else {
Log.i(LOG_TAG, "base.apk已存在: " + baseApkFile.getAbsolutePath());
}
}
/**
* 从assets复制基础APK文件
*/
private void copyBaseApkFromAssets(Context context, File targetFile) {
try (InputStream inputStream = context.getAssets().open("base.apk");
FileOutputStream outputStream = new FileOutputStream(targetFile)) {
byte[] buffer = new byte[8192]; // 8KB缓冲区
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
Log.i(LOG_TAG, "成功复制base.apk到: " + targetFile.getAbsolutePath());
// 设置文件权限 (0x100 = 256, 对应rw-r--r--)
Os.chmod(targetFile.getAbsolutePath(), 0x100);
} catch (Exception e) {
Log.e(LOG_TAG, "复制base.apk失败", e);
}
}
/**
* 查找已加载的APK信息对象
* 通过多种方式尝试获取LoadedApk实例
*/
private Object findLoadedApk(Context context) throws Exception {
Object loadedApk = null;
// 方式1: 尝试从Context获取mPackageInfo字段
try {
loadedApk = getFieldValue(context, "mPackageInfo");
if (loadedApk != null) {
return loadedApk;
}
} catch (NoSuchFieldException e) {
// 字段不存在,继续尝试其他方式
}
// 方式2: 尝试从Context获取mLoadedApk字段
try {
loadedApk = getFieldValue(context, "mLoadedApk");
if (loadedApk != null) {
return loadedApk;
}
} catch (NoSuchFieldException e) {
// 字段不存在,继续尝试其他方式
}
// 方式3: 从ActivityThread的包管理器中查找
Object activityThread = ActivityThread.currentActivityThread();
Map<String, WeakReference<?>> packagesMap = (Map<String, WeakReference<?>>)
getFieldValue(activityThread, ActivityThread.class, "mPackages");
if (packagesMap != null) {
WeakReference<?> weakRef = packagesMap.get("com.example.app");
if (weakRef != null) {
loadedApk = weakRef.get();
}
}
return loadedApk;
}
/**
* 通用的字段获取方法(带Class参数)
*/
private static Object getFieldValue(Object target, Class<?> targetClass, String fieldName) throws Exception {
Field field = targetClass.getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(target);
}
/**
* 通用的字段获取方法
*/
private static Object getFieldValue(Object target, String fieldName) throws Exception {
return getFieldValue(target, target.getClass(), fieldName);
}
/**
* 加载Native库
* 从assets中提取并加载native so库
*/
private void loadNativeLibrary(Context context) {
if (context == null) {
Log.e(LOG_TAG, "Context为null,无法加载Native库");
return;
}
try {
File filesDir = context.getFilesDir();
if (filesDir == null) {
Log.e(LOG_TAG, "filesDir为null,无法加载Native库");
return;
}
File soFile = new File(filesDir, NATIVE_LIB_NAME);
// 如果so文件不存在或为空,从assets中提取
if (!soFile.exists() || soFile.length() == 0) {
extractSoFromAssets(context, soFile);
}
// 加载Native库
System.load(soFile.getAbsolutePath());
Log.i(LOG_TAG, "Native库加载成功: " + soFile.getAbsolutePath());
} catch (Throwable t) {
Log.e(LOG_TAG, "加载Native库失败", t);
}
}
/**
* 从assets中提取so文件
*/
private void extractSoFromAssets(Context context, File targetSoFile) {
String assetPath = "patch/lib/arm64-v8a/" + NATIVE_LIB_NAME;
try (InputStream inputStream = context.getAssets().open(assetPath);
FileOutputStream outputStream = new FileOutputStream(targetSoFile)) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
outputStream.flush();
// 设置文件权限为755 (rwxr-xr-x)
try {
Os.chmod(targetSoFile.getAbsolutePath(), 493); // 493 = 0755 in octal
} catch (Throwable unused) {
// 权限设置失败不影响主要功能
}
Log.i(LOG_TAG, "成功提取so文件到: " + targetSoFile.getAbsolutePath());
} catch (Exception e) {
Log.e(LOG_TAG, "提取so文件失败,asset: " + assetPath, e);
}
}
/**
* 修改应用名称信息
* 在多个位置更新应用名称,确保伪装效果
*/
private void modifyApplicationName(Context context) throws Exception {
if (context == null) {
return;
}
// 修改当前Context中的应用信息
ApplicationInfo currentAppInfo = context.getApplicationInfo();
currentAppInfo.name = AppInfo.originalApplicationName;
// 修改LoadedApk中的应用信息
Object loadedApk = findLoadedApk(context);
if (loadedApk != null) {
ApplicationInfo loadedApkAppInfo = (ApplicationInfo) getFieldValue(loadedApk, "mApplicationInfo");
loadedApkAppInfo.className = AppInfo.originalApplicationName;
loadedApkAppInfo.name = AppInfo.originalApplicationName;
}
}
/**
* 应用创建完成回调
* 替换应用实例并调用原始Application的onCreate方法
*/
@Override
public void onCreate() {
super.onCreate();
if (Init.originalApplication != null && this.originalInstrumentation != null) {
try {
replaceApplicationInstance();
this.originalInstrumentation.callApplicationOnCreate(Init.originalApplication);
Log.i(LOG_TAG, "原始Application onCreate调用完成");
} catch (Exception e) {
throw new RuntimeException("替换应用实例失败", e);
}
}
}
/**
* 解析并存储配置信息
* 从assets中的JSON配置文件读取各种Hook参数
*/
private void parseAndStoreConfig(Context context) throws Exception {
StringBuilder configContent = new StringBuilder();
try (InputStream inputStream = context.getAssets().open("patch/" + CONFIG_FILE_NAME);
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
configContent.append(line);
}
}
JSONObject configJson = new JSONObject(configContent.toString());
// 解析各项配置参数
AppInfo.packageName = configJson.getString("packageName");
AppInfo.signature = configJson.getString("signature");
AppInfo.apkSize = configJson.getLong("apkSize");
AppInfo.signatureStrength = configJson.getInt("signatureStrength");
AppInfo.originalApplicationName = configJson.optString("originalApplicationName", null);
AppInfo.pmsProxyMethod = configJson.getString("pmsProxyMethod");
AppInfo.pathRedirectionEnabled = configJson.getBoolean("pathRedirectionEnabled");
Log.i(LOG_TAG, "配置解析完成,目标包名: " + AppInfo.packageName);
}
/**
* 替换应用实例
* 在ActivityThread中完全替换当前应用实例为原始应用
*/
private void replaceApplicationInstance() throws Exception {
// 获取当前ActivityThread实例
ActivityThread activityThread = ActivityThread.currentActivityThread();
// 替换mAllApplications列表中的应用实例
Field allApplicationsField = ActivityThread.class.getDeclaredField("mAllApplications");
allApplicationsField.setAccessible(true);
List<Application> allApplications = activityThread.mAllApplications;
allApplications.remove(this);
allApplications.add(Init.originalApplication);
// 修改LoadedApk中的mApplication字段
Object loadedApk = findLoadedApk(this);
if (loadedApk != null) {
setFieldValue(loadedApk, "mApplication", Init.originalApplication);
// 修改ApplicationInfo中的类名和名称
ApplicationInfo appInfo = (ApplicationInfo) getFieldValue(loadedApk, "mApplicationInfo");
if (appInfo != null) {
appInfo.className = AppInfo.originalApplicationName;
appInfo.name = AppInfo.originalApplicationName;
}
}
Log.i(LOG_TAG, "应用实例替换完成");
}
/**
* 通用的字段设置方法(带Class参数)
*/
private static void setFieldValue(Object target, Class<?> targetClass, String fieldName, Object value) throws Exception {
Field field = targetClass.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(target, value);
}
/**
* 通用的字段设置方法
*/
private static void setFieldValue(Object target, String fieldName, Object value) throws Exception {
setFieldValue(target, target.getClass(), fieldName, value);
}
}
/**
* 应用名称伪装工具类
* 通过修改ApplicationInfo中的名称和类名字段,伪装应用身份
*/
public class ApplicationNameSpoofing {
// 目标应用的包名常量
private static final String TARGET_PACKAGE_NAME = "com.example.app";
// 获取应用信息的标志位(0x80 = 128,通常用于获取元数据)
private static final int APPLICATION_INFO_FLAGS = 0x80;
/**
* 执行应用名称伪装
* 修改目标应用的ApplicationInfo中的名称和类名字段
*
* @Param context Android上下文对象,用于获取PackageManager
* @throws PackageManager.NameNotFoundException 如果目标包名不存在
*/
public static void spoofApplicationName(Context context) throws PackageManager.NameNotFoundException {
// 获取目标包名的ApplicationInfo对象
// 使用GET_META_DATA标志获取包含元数据的应用信息
ApplicationInfo targetAppInfo = context.getPackageManager()
.getApplicationInfo(TARGET_PACKAGE_NAME, APPLICATION_INFO_FLAGS);
// 修改应用显示名称
targetAppInfo.name = AppInfo.originalApplicationName;
// 修改应用类名
targetAppInfo.className = AppInfo.originalApplicationName;
}
}
我们发现入口的主要功能是解析配置、Patch初始化和伪装Application
Patch类分析
public class Patch {
// 日志标签
private static final String LOG_TAG = "SRPatch";
// PMS代理方法常量
private static final String PMS_PROXY_METHOD_IBINDER = "IBINDER_PROXY";
// 伪造APK文件路径
public static String FakeApkPath = null;
// 原始Parcelable.Creator引用
public static Parcelable.Creator ORIGINAL_APP_INFO_CREATOR = null;
public static Parcelable.Creator ORIGINAL_CREATOR = null;
// 配置参数
public static long sApkSize;
public static Signature sFakeSignature;
public static boolean sPathRedirectionEnabled;
public static String sPmsProxyMethod;
public static int sSignatureStrength;
public static String sSourceDir;
public static String sTargetPackageName;
/**
* 初始化Patch模块(已弃用,使用init方法替代)
* 保持向后兼容性
*
* @param context Android上下文对象
*/
@Deprecated
public static void HookInit(Context context) {
init(context);
}
/**
* 应用Native层绕过方法
* 根据签名绕过强度等级选择不同的Native绕过策略
*
* @param fakeApkPath 伪造APK文件路径
*/
private static void applyNativeBypass(String fakeApkPath) {
int signatureStrength = sSignatureStrength;
// 根据签名绕过强度等级选择不同的Native绕过方法
switch (signatureStrength) {
case 2:
case 3:
// 标准签名绕过 + 大小绕过
SigBypass.SigBypass(sSourceDir, fakeApkPath);
SigBypass.SizeBypass(sApkSize);
break;
case 4:
//SVC Hook实现绕过
SigBypass.SvcHookSigna(sSourceDir, fakeApkPath, AppInfo.apkSize);
break;
default:
// 默认情况:使用标准签名绕过
SigBypass.SigBypass(sSourceDir, fakeApkPath);
SigBypass.SizeBypass(sApkSize);
break;
}
Log.i(LOG_TAG, "Native绕过方法应用完成,强度等级: " + signatureStrength);
}
/**
* 应用路径重定向
* 修改APK路径指向伪造的APK文件
*
* @param context Android上下文对象
* @param fakeApkPath 伪造APK文件路径
*/
private static void applyPathRedirection(Context context, String fakeApkPath) {
if (sPathRedirectionEnabled) {
try {
ApkPathHook.ApkPathSpoofing(context, fakeApkPath);
Log.i(LOG_TAG, "APK路径重定向完成: " + fakeApkPath);
} catch (Throwable throwable) {
Log.e(LOG_TAG, "APK路径重定向失败", throwable);
}
}
}
/**
* 初始化Patch模块
* 主要的初始化方法,执行所有必要的Hook和伪装操作
*
* @param context Android上下文对象
*/
public static void init(Context context) {
Log.i(LOG_TAG, "开始初始化Patch模块...");
// 初始化配置数据
initPatchData(context);
// 如果签名绕过强度大于0,执行java层Hook
if (sSignatureStrength > 0) {
try {
startjavaSignatureReplace();
} catch (Throwable throwable) {
Log.e(LOG_TAG, "java层签名替换失败", throwable);
}
// 构建伪造APK路径并应用各种绕过技术
String fakeApkPath = PathUtils.buildFakeApkPath(context);
FakeApkPath = fakeApkPath;
applyPathRedirection(context, fakeApkPath);
applyNativeBypass(fakeApkPath);
}
Log.i(LOG_TAG, "Patch模块初始化完成");
}
/**
* 初始化Patch配置数据
* 从AppInfo中读取配置参数
*
* @param context Android上下文对象
*/
private static void initPatchData(Context context) {
sTargetPackageName = AppInfo.packageName;
sFakeSignature = new Signature(AppInfo.signature);
sSourceDir = context.getApplicationInfo().sourceDir;
sPmsProxyMethod = AppInfo.pmsProxyMethod;
sSignatureStrength = AppInfo.signatureStrength;
sApkSize = AppInfo.apkSize;
sPathRedirectionEnabled = AppInfo.pathRedirectionEnabled;
Log.i(LOG_TAG, "Patch数据初始化完成 - 目标包名: " + sTargetPackageName);
}
/**
* 启动java层签名替换
* 根据配置选择Binder代理或PMS代理方式
*
* @throws Throwable 如果Hook过程中发生异常
*/
private static void startjavaSignatureReplace() throws Throwable {
Log.i(LOG_TAG, "开始java层签名替换,代理方法: " + sPmsProxyMethod);
// 根据配置选择不同的Hook实现方式
if (PMS_PROXY_METHOD_IBINDER.equals(sPmsProxyMethod)) {
// 使用Binder代理方式进行Hook
FakeIBinder.installBinderHook();
} else {
// 使用PMS代理方式进行Hook
PMSAgent.replaceParcelableCreators();
}
Log.i(LOG_TAG, "java层签名替换完成");
}
}
在Patch类中,SRpatch主要进行了io重定向,代理Binder或代理PMS,分别研究这两部分
java层IO重定向
/**
* 路径工具类
* 用于构建和管理伪造的APK文件路径,支持路径重定向功能
* 这是签名绕过工具中路径欺骗功能的核心组件
*/
public class PathUtils {
// 伪造APK存储目录名称
private static final String FAKE_APK_DIRECTORY = "srpatch";
// 伪造APK文件名称
private static final String FAKE_APK_FILENAME = "base.apk";
/**
* 构建伪造APK文件路径
* 在应用的私有数据目录下创建特定的目录结构来存储伪造的APK文件
*
* @param context Android上下文对象,用于获取应用文件目录
* @Return 伪造APK文件的绝对路径
*
* 路径结构示例:
* /data/data/[package_name]/srpatch/base.apk
*/
public static String buildFakeApkPath(Context context) {
// 获取应用的文件目录(/data/data/[package_name]/files)
File filesDir = context.getFilesDir();
// 获取文件目录的父目录(/data/data/[package_name])
File dataDir = filesDir.getParentFile();
// 在数据目录下创建伪造APK存储目录
File fakeApkDirectory = new File(dataDir, FAKE_APK_DIRECTORY);
// 创建伪造APK文件对象
File fakeApkFile = new File(fakeApkDirectory, FAKE_APK_FILENAME);
// 返回伪造APK文件的绝对路径
return fakeApkFile.getAbsolutePath();
}
/**
* 获取伪造APK存储目录路径
*
* @param context Android上下文对象
* @return 伪造APK存储目录的绝对路径
*/
public static String getFakeApkDirectoryPath(Context context) {
File filesDir = context.getFilesDir();
File dataDir = filesDir.getParentFile();
File fakeApkDirectory = new File(dataDir, FAKE_APK_DIRECTORY);
return fakeApkDirectory.getAbsolutePath();
}
/**
* 检查伪造APK文件是否存在
*
* @param context Android上下文对象
* @return 如果伪造APK文件存在返回true,否则返回false
*/
public static boolean isFakeApkExists(Context context) {
String fakeApkPath = buildFakeApkPath(context);
File fakeApkFile = new File(fakeApkPath);
return fakeApkFile.exists() && fakeApkFile.length() > 0;
}
/**
* 获取伪造APK文件对象
*
* @param context Android上下文对象
* @return 伪造APK文件对象
*/
public static File getFakeApkFile(Context context) {
String fakeApkPath = buildFakeApkPath(context);
return new File(fakeApkPath);
}
/**
* 清理伪造APK文件
* 用于在卸载或重置时清理相关文件
*
* @param context Android上下文对象
* @return 如果成功删除返回true,否则返回false
*/
public static boolean cleanupFakeApk(Context context) {
try {
File fakeApkFile = getFakeApkFile(context);
File fakeApkDirectory = fakeApkFile.getParentFile();
boolean fileDeleted = fakeApkFile.delete();
boolean dirDeleted = fakeApkDirectory.delete();
return fileDeleted || dirDeleted;
} catch (Exception e) {
return false;
}
}
}
PathUtils类主要用于构造重定向目标app及其所在文件夹
/**
* APK路径Hook工具类
* 通过修改ApplicationInfo和Context内部字段,将APK路径指向伪造的文件
* 这是路径重定向功能的核心实现,用于绕过基于APK路径的校验
*/
public class ApkPathHook {
// 目标应用包名(会在修补时写入)
private static final String TARGET_PACKAGE_NAME = "com.example.app";
// 获取应用信息的标志位(0x80 = GET_META_DATA,用于获取包含元数据的应用信息)
private static final int APPLICATION_INFO_FLAGS = 0x80;
/**
* 执行APK路径欺骗
* 修改多个位置的APK路径指向,确保路径重定向完全生效
*
* @param context Android上下文对象
* @param fakeApkPath 伪造的APK文件路径
* @throws PackageManager.NameNotFoundException 如果目标包名不存在
*/
public static void spoofApkPath(Context context, String fakeApkPath)
throws PackageManager.NameNotFoundException {
// 步骤1: 修改当前应用的应用信息中的路径
modifyCurrentApplicationInfo(context, fakeApkPath);
// 步骤2: 修改目标包名的应用信息中的路径
modifyTargetApplicationInfo(context, fakeApkPath);
// 步骤3: 修改Context内部字段中的路径
modifyContextInternalFields(context, fakeApkPath);
// 步骤4: 强制刷新资源管理器
refreshResources(context);
}
/**
* 修改当前应用的应用信息路径
*/
private static void modifyCurrentApplicationInfo(Context context, String fakeApkPath) {
ApplicationInfo currentAppInfo = context.getApplicationInfo();
currentAppInfo.sourceDir = fakeApkPath;
currentAppInfo.publicSourceDir = fakeApkPath;
}
/**
* 修改目标包名的应用信息路径
*/
private static void modifyTargetApplicationInfo(Context context, String fakeApkPath)
throws PackageManager.NameNotFoundException {
ApplicationInfo targetAppInfo = context.getPackageManager()
.getApplicationInfo(TARGET_PACKAGE_NAME, APPLICATION_INFO_FLAGS);
targetAppInfo.sourceDir = fakeApkPath;
targetAppInfo.publicSourceDir = fakeApkPath;
}
/**
* 修改Context内部字段中的路径
* 通过反射修改ContextImpl中的mPackageInfo字段相关路径
*/
private static void modifyContextInternalFields(Context context, String fakeApkPath) {
Context baseContext = getBaseContext(context);
if (baseContext == null) {
return;
}
try {
// 获取mPackageInfo字段
Field packageInfoField = baseContext.getClass().getDeclaredField("mPackageInfo");
packageInfoField.setAccessible(true);
Object packageInfoObject = packageInfoField.get(baseContext);
if (packageInfoObject != null) {
// 修改mAppDir字段(APK目录)
modifyPackageInfoField(packageInfoObject, "mAppDir", fakeApkPath);
// 尝试修改mResDir字段(资源目录)
modifyPackageInfoField(packageInfoObject, "mResDir", fakeApkPath);
}
} catch (Throwable throwable) {
// 记录异常但不中断执行
throwable.printStackTrace();
}
}
/**
* 获取最底层的Base Context
* 遍历ContextWrapper链直到找到非包装的Context
*/
private static Context getBaseContext(Context context) {
Context currentContext = context;
while (currentContext instanceof ContextWrapper) {
Context baseContext = ((ContextWrapper) currentContext).getBaseContext();
if (baseContext == null) {
break;
}
currentContext = baseContext;
}
return currentContext;
}
/**
* 修改PackageInfo对象中的字段
*/
private static void modifyPackageInfoField(Object packageInfoObject, String fieldName, String newValue) {
try {
Field field = packageInfoObject.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(packageInfoObject, newValue);
} catch (NoSuchFieldException ignored) {
// 字段不存在,忽略
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
/**
* 强制刷新资源管理器
* 通过访问Resources触发重新加载,确保路径修改生效
*/
private static void refreshResources(Context context) {
try {
// 访问Resources会触发重新加载,应用路径修改
context.getResources();
} catch (Throwable ignored) {
// 忽略资源加载异常
}
}
/**
* 重载方法:使用指定的包名进行路径欺骗
*
* @param context Android上下文
* @param fakeApkPath 伪造的APK文件路径
* @param targetPackageName 目标包名
* @throws PackageManager.NameNotFoundException 如果目标包名不存在
*/
public static void spoofApkPath(Context context, String fakeApkPath, String targetPackageName)
throws PackageManager.NameNotFoundException {
modifyCurrentApplicationInfo(context, fakeApkPath);
ApplicationInfo targetAppInfo = context.getPackageManager()
.getApplicationInfo(targetPackageName, APPLICATION_INFO_FLAGS);
targetAppInfo.sourceDir = fakeApkPath;
targetAppInfo.publicSourceDir = fakeApkPath;
modifyContextInternalFields(context, fakeApkPath);
refreshResources(context);
}
/**
* 检查APK路径欺骗是否生效
*
* @param context Android上下文
* @return 如果当前应用路径已被修改返回true,否则返回false
*/
public static boolean isApkPathSpoofed(Context context) {
ApplicationInfo appInfo = context.getApplicationInfo();
return appInfo.sourceDir != null &&
!appInfo.sourceDir.equals(context.getPackageCodePath());
}
}
代理IBinder
/**
* Binder代理Hook工具类
* 通过动态代理Hook PackageManager服务的Binder通信,拦截并修改包信息查询结果
* 这是签名绕过工具中最高级的Hook技术之一
*/
public class FakeIBinder {
private static final String LOG_TAG = "FakeIBinder";
/**
* Binder调用拦截处理器
* 负责拦截PackageManager服务的transact调用并修改返回数据
*/
private static class BinderInvocationHandler implements InvocationHandler {
private final IBinder originalBinder;
private final Set<Integer> packageInfoTransactionCodes;
private final Set<Integer> applicationInfoTransactionCodes;
public BinderInvocationHandler(IBinder originalBinder, Set<Integer> packageInfoTransactions,
Set<Integer> applicationInfoTransactions) {
this.originalBinder = originalBinder;
this.packageInfoTransactionCodes = packageInfoTransactions;
this.applicationInfoTransactionCodes = applicationInfoTransactions;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return handleBinderTransaction(originalBinder, packageInfoTransactionCodes,
applicationInfoTransactionCodes, proxy, method, args);
}
}
/**
* Binder死亡监听器
* 当原始Binder死亡时重新安装Hook
*/
private static class BinderDeathRecipient implements IBinder.DeathRecipient {
@Override
public void binderDied() {
try {
reinstallBinderHook();
} catch (RemoteException | ClassNotFoundException | IllegalAccessException |
NoSuchFieldException e) {
Log.e(LOG_TAG, "Binder死亡后重新安装Hook失败", e);
throw new RuntimeException("重新安装Binder Hook失败", e);
}
}
}
/**
* 安装Binder代理Hook
* 替换系统的PackageManager服务Binder代理,拦截所有包信息查询请求
*/
public static void installBinderHook() throws IllegalAccessException, ClassNotFoundException,
RemoteException, NoSuchFieldException {
Log.i(LOG_TAG, "开始安装Binder代理Hook...");
// 获取当前ActivityThread实例
ActivityThread activityThread = ActivityThread.currentActivityThread();
// 获取sPackageManager字段
Field packageManagerField = ActivityThread.class.getDeclaredField("sPackageManager");
packageManagerField.setAccessible(true);
Object packageManagerInstance = packageManagerField.get(null);
if (packageManagerInstance == null) {
Log.w(LOG_TAG, "sPackageManager为null,无法安装Hook");
return;
}
// 查找IBinder类型的字段
Field binderField = findBinderField(packageManagerInstance);
if (binderField == null) {
Log.e(LOG_TAG, "未找到IBinder类型字段");
return;
}
binderField.setAccessible(true);
IBinder originalBinder = (IBinder) binderField.get(packageManagerInstance);
if (originalBinder == null) {
Log.w(LOG_TAG, "原始Binder为null,无法安装Hook");
return;
}
// 收集需要拦截的Transaction Code
Set<Integer> packageInfoTransactions = new HashSet<>();
Set<Integer> applicationInfoTransactions = new HashSet<>();
collectTransactionCodes(packageInfoTransactions, applicationInfoTransactions);
// 创建动态代理
ClassLoader classLoader = originalBinder.getClass().getClassLoader();
BinderInvocationHandler handler = new BinderInvocationHandler(
originalBinder, packageInfoTransactions, applicationInfoTransactions);
IBinder proxyBinder = (IBinder) Proxy.newProxyInstance(
classLoader, new Class[]{IBinder.class}, handler);
// 替换原始Binder
binderField.set(packageManagerInstance, proxyBinder);
// 清理缓存
CacheHandling.clearCaches();
// 注册Binder死亡监听
originalBinder.linkToDeath(new BinderDeathRecipient(), 0);
Log.i(LOG_TAG, "Binder代理Hook安装完成");
}
/**
* 查找IBinder类型的字段
*/
private static Field findBinderField(Object targetInstance) {
Field[] fields = targetInstance.getClass().getDeclaredFields();
for (Field field : fields) {
Class<?> fieldType = field.getType();
if (IBinder.class.isAssignableFrom(fieldType)) {
return field;
}
}
return null;
}
/**
* 收集需要拦截的Transaction Code
* 通过反射获取IPackageManager中定义的所有TRANSACTION常量
*/
private static void collectTransactionCodes(Set<Integer> packageInfoTransactions,
Set<Integer> applicationInfoTransactions)
throws ClassNotFoundException {
Class<?> stubClass = Class.forName("android.content.pm.IPackageManager$Stub");
Field[] fields = stubClass.getDeclaredFields();
for (Field field : fields) {
if (Modifier.isStatic(field.getModifiers()) && field.getType() == int.class) {
field.setAccessible(true);
String fieldName = field.getName();
try {
int transactionCode = field.getInt(null);
if (fieldName.startsWith("TRANSACTION_getPackageInfo")) {
packageInfoTransactions.add(transactionCode);
} else if (fieldName.startsWith("TRANSACTION_getApplicationInfo")) {
applicationInfoTransactions.add(transactionCode);
}
} catch (IllegalAccessException ignored) {
// 忽略无法访问的字段
}
}
}
Log.i(LOG_TAG, "收集到PackageInfo Transactions: " + packageInfoTransactions.size());
Log.i(LOG_TAG, "收集到ApplicationInfo Transactions: " + applicationInfoTransactions.size());
}
/**
* 处理Binder事务拦截
* 核心拦截逻辑,修改PackageManager服务返回的数据
*/
private static Object handleBinderTransaction(IBinder originalBinder,
Set<Integer> packageInfoTransactions,
Set<Integer> applicationInfoTransactions,
Object proxy, Method method, Object[] args)
throws Throwable {
// 只拦截transact方法
if (!"transact".equals(method.getName())) {
return method.invoke(originalBinder, args);
}
// 检查参数有效性
if (args == null || args.length < 4) {
return method.invoke(originalBinder, args);
}
Integer transactionCode = (Integer) args[0];
Parcel replyParcel = (Parcel) args[2];
// 调用原始方法
Object result = method.invoke(originalBinder, args);
// 根据Transaction Code处理返回数据
if (packageInfoTransactions.contains(transactionCode)) {
patchPackageInfoReply(replyParcel);
} else if (applicationInfoTransactions.contains(transactionCode)) {
patchApplicationInfoReply(replyParcel);
}
return result;
}
/**
* 重新安装Binder Hook
* 当原始Binder死亡时调用
*/
private static void reinstallBinderHook() throws RemoteException, NoSuchFieldException,
ClassNotFoundException, IllegalAccessException {
Log.i(LOG_TAG, "检测到Binder死亡,重新安装Hook...");
installBinderHook();
}
/**
* 修补PackageInfo返回数据
* 修改签名信息和应用名称
*/
private static void patchSignatureInfo(PackageInfo packageInfo) {
if (packageInfo == null || packageInfo.applicationInfo == null) {
return;
}
// 修补应用信息
if (AppInfo.originalApplicationName != null &&
!AppInfo.originalApplicationName.isEmpty()) {
packageInfo.applicationInfo.name = AppInfo.originalApplicationName;
packageInfo.applicationInfo.className = AppInfo.originalApplicationName;
}
// 修补路径重定向
if (Patch.sPathRedirectionEnabled) {
packageInfo.applicationInfo.sourceDir = Patch.FakeApkPath;
packageInfo.applicationInfo.publicSourceDir = Patch.FakeApkPath;
}
// 修补签名信息
Signature fakeSignature = Patch.sFakeSignature;
if (fakeSignature != null) {
packageInfo.signatures = new Signature[]{fakeSignature};
// Android P (API 28) 及以上版本需要额外处理signingInfo
if (Build.VERSION.SDK_INT >= 28) {
patchSigningInfoForAndroidP(packageInfo, fakeSignature);
}
}
}
/**
* 为Android P及以上版本修补签名信息
*/
@SuppressLint("PrivateApi")
private static void patchSigningInfoForAndroidP(PackageInfo packageInfo, Signature fakeSignature) {
try {
if (packageInfo.signingInfo == null) {
// 创建新的SigningInfo
createNewSigningInfo(packageInfo, fakeSignature);
} else {
// 修改现有的SigningInfo
modifyExistingSigningInfo(packageInfo, fakeSignature);
}
} catch (Throwable t) {
Log.w(LOG_TAG, "修改SigningInfo失败", t);
}
}
/**
* 创建新的SigningInfo对象
*/
private static void createNewSigningInfo(PackageInfo packageInfo, Signature fakeSignature)
throws Exception {
Class<?> signingDetailsClass = Class.forName("android.content.pm.SigningDetails");
Object signingDetails;
try {
signingDetails = signingDetailsClass.getConstructor(Signature[].class, int.class, Signature[].class)
.newInstance(new Signature[]{fakeSignature}, 2, null);
} catch (NoSuchMethodException e) {
signingDetails = signingDetailsClass.getConstructor(Signature[].class, int.class)
.newInstance(new Signature[]{fakeSignature}, 2);
}
SigningInfo newSigningInfo = (SigningInfo) SigningInfo.class.getConstructor(signingDetailsClass)
.newInstance(signingDetails);
Field signingInfoField = PackageInfo.class.getDeclaredField("signingInfo");
signingInfoField.setAccessible(true);
packageInfo.signingInfo = newSigningInfo;
}
/**
* 修改现有的SigningInfo对象
*/
private static void modifyExistingSigningInfo(PackageInfo packageInfo, Signature fakeSignature) {
try {
// 修改APK内容签名
Signature[] apkSigners = packageInfo.signingInfo.getApkContentsSigners();
if (apkSigners != null && apkSigners.length > 0) {
apkSigners[0] = fakeSignature;
}
// 修改签名证书历史
Signature[] signingHistory = packageInfo.signingInfo.getSigningCertificateHistory();
if (signingHistory != null && signingHistory.length > 0) {
signingHistory[0] = fakeSignature;
}
// 通过反射修改内部签名细节
modifySigningDetails(packageInfo, fakeSignature);
} catch (Throwable t) {
Log.w(LOG_TAG, "修改现有SigningInfo失败", t);
}
}
/**
* 通过反射修改SigningDetails内部字段
*/
private static void modifySigningDetails(PackageInfo packageInfo, Signature fakeSignature) {
try {
Field signingDetailsField = packageInfo.signingInfo.getClass().getDeclaredField("mSigningDetails");
signingDetailsField.setAccessible(true);
Object signingDetails = signingDetailsField.get(packageInfo.signingInfo);
if (signingDetails != null) {
Field signaturesField = signingDetails.getClass().getDeclaredField("mSignatures");
signaturesField.setAccessible(true);
signaturesField.set(signingDetails, new Signature[]{fakeSignature});
}
} catch (Throwable t) {
Log.w(LOG_TAG, "修改SigningDetails失败", t);
}
}
/**
* 修补ApplicationInfo返回数据
*/
private static void patchApplicationInfoReply(Parcel replyParcel) {
if (replyParcel == null) {
return;
}
int originalPosition = replyParcel.dataPosition();
replyParcel.setDataPosition(0);
try {
// 检查Parcel中是否有数据
if (replyParcel.readInt() != 0) {
return;
}
ApplicationInfo applicationInfo = readApplicationInfoFromParcel(replyParcel);
// 检查是否需要修补
if (shouldPatchApplicationInfo(applicationInfo)) {
patchApplicationInfo(applicationInfo);
rewriteApplicationInfoToParcel(replyParcel, applicationInfo);
}
} catch (Throwable t) {
Log.w(LOG_TAG, "修补ApplicationInfo返回数据失败", t);
} finally {
replyParcel.setDataPosition(originalPosition);
}
}
/**
* 从Parcel中读取ApplicationInfo
*/
private static ApplicationInfo readApplicationInfoFromParcel(Parcel parcel) {
try {
if (Build.VERSION.SDK_INT >= 26) {
try {
return parcel.readTypedObject(ApplicationInfo.CREATOR);
} catch (Throwable t) {
return (ApplicationInfo) parcel.readParcelable(ApplicationInfo.class.getClassLoader());
}
} else {
return (ApplicationInfo) parcel.readParcelable(ApplicationInfo.class.getClassLoader());
}
} catch (Throwable t) {
return null;
}
}
/**
* 检查是否需要修补ApplicationInfo
*/
private static boolean shouldPatchApplicationInfo(ApplicationInfo appInfo) {
return appInfo != null &&
appInfo.packageName != null &&
Patch.sTargetPackageName != null &&
Patch.sTargetPackageName.equals(appInfo.packageName);
}
/**
* 修补ApplicationInfo对象
*/
private static void patchApplicationInfo(ApplicationInfo appInfo) {
// 修补应用名
if (AppInfo.originalApplicationName != null &&
!AppInfo.originalApplicationName.isEmpty()) {
appInfo.name = AppInfo.originalApplicationName;
appInfo.className = AppInfo.originalApplicationName;
}
// 修补路径
if (Patch.sPathRedirectionEnabled) {
appInfo.sourceDir = Patch.FakeApkPath;
appInfo.publicSourceDir = Patch.FakeApkPath;
}
}
/**
* 重写ApplicationInfo到Parcel
*/
private static void rewriteApplicationInfoToParcel(Parcel parcel, ApplicationInfo appInfo) {
parcel.setDataPosition(0);
parcel.setDataSize(0);
parcel.writeInt(0); // 表示有数据
parcel.writeInt(1); // 标志位
appInfo.writeToParcel(parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
/**
* 修补PackageInfo返回数据
*/
private static void patchPackageInfoReply(Parcel replyParcel) {
if (replyParcel == null) {
return;
}
int originalPosition = replyParcel.dataPosition();
replyParcel.setDataPosition(0);
try {
// 检查Parcel中是否有数据
if (replyParcel.readInt() != 0) {
return;
}
PackageInfo packageInfo = readPackageInfoFromParcel(replyParcel);
// 检查是否需要修补
if (shouldPatchPackageInfo(packageInfo)) {
patchSignatureInfo(packageInfo);
rewritePackageInfoToParcel(replyParcel, packageInfo);
}
} catch (Throwable t) {
Log.w(LOG_TAG, "修补PackageInfo返回数据失败", t);
} finally {
replyParcel.setDataPosition(originalPosition);
}
}
/**
* 从Parcel中读取PackageInfo
*/
private static PackageInfo readPackageInfoFromParcel(Parcel parcel) {
try {
if (Build.VERSION.SDK_INT >= 26) {
try {
return parcel.readTypedObject(PackageInfo.CREATOR);
} catch (Throwable t) {
return (PackageInfo) parcel.readParcelable(PackageInfo.class.getClassLoader());
}
} else {
return (PackageInfo) parcel.readParcelable(PackageInfo.class.getClassLoader());
}
} catch (Throwable t) {
return null;
}
}
/**
* 检查是否需要修补PackageInfo
*/
private static boolean shouldPatchPackageInfo(PackageInfo packageInfo) {
return packageInfo != null &&
packageInfo.packageName != null &&
Patch.sTargetPackageName != null &&
Patch.sTargetPackageName.equals(packageInfo.packageName);
}
/**
* 重写PackageInfo到Parcel
*/
private static void rewritePackageInfoToParcel(Parcel parcel, PackageInfo packageInfo) {
parcel.setDataPosition(0);
parcel.setDataSize(0);
parcel.writeInt(0); // 表示有数据
parcel.writeInt(1); // 标志位
packageInfo.writeToParcel(parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
}
代理PMS
/**
* PackageManager服务代理工具类
* 通过替换Parcelable.Creator来修改PackageManager返回的包信息和应用信息
* 这是签名绕过工具中另一种重要的Hook技术
*/
public class PMSAgent {
private static final String LOG_TAG = "PMSAgent";
/**
* Parcelable.Creator代理基类
* 提供通用的Creator代理功能,子类专注于特定类型的修改逻辑
*/
private static abstract class BaseCreatorProxy<T> implements Parcelable.Creator<T> {
/**
* 从Parcel创建对象的具体实现,由子类提供
*/
protected abstract T doCreateFromParcel(Parcel source);
/**
* 创建对象数组的具体实现,由子类提供
*/
protected abstract T[] doNewArray(int size);
@Override
public T createFromParcel(Parcel source) {
return doCreateFromParcel(source);
}
@Override
public T[] newArray(int size) {
return doNewArray(size);
}
/**
* 通用的对象修改方法 - 修改应用信息
*/
protected void modifyApplicationInfo(ApplicationInfo appInfo) {
if (appInfo == null) return;
// 修改应用名称
if (shouldModifyAppName(appInfo)) {
appInfo.name = AppInfo.originalApplicationName;
appInfo.className = AppInfo.originalApplicationName;
}
// 修改应用路径
if (Patch.sPathRedirectionEnabled) {
appInfo.sourceDir = Patch.FakeApkPath;
appInfo.publicSourceDir = Patch.FakeApkPath;
}
}
/**
* 检查是否需要修改应用名称
*/
private boolean shouldModifyAppName(ApplicationInfo appInfo) {
return appInfo.packageName != null &&
Patch.sTargetPackageName != null &&
Patch.sTargetPackageName.equals(appInfo.packageName) &&
AppInfo.originalApplicationName != null &&
!AppInfo.originalApplicationName.isEmpty();
}
/**
* 通用的对象修改方法 - 修改包信息
*/
protected void modifyPackageInfo(PackageInfo packageInfo) {
if (packageInfo == null) return;
// 修改应用信息
modifyApplicationInfo(packageInfo.applicationInfo);
// 修改签名信息
if (shouldModifySignature(packageInfo)) {
modifySignatureInfo(packageInfo);
}
}
/**
* 检查是否需要修改签名
*/
private boolean shouldModifySignature(PackageInfo packageInfo) {
return packageInfo.packageName != null &&
Patch.sTargetPackageName != null &&
Patch.sTargetPackageName.equals(packageInfo.packageName) &&
packageInfo.signatures != null &&
packageInfo.signatures.length > 0;
}
/**
* 修改签名信息
*/
private void modifySignatureInfo(PackageInfo packageInfo) {
// 修改基础签名
packageInfo.signatures[0] = Patch.sFakeSignature;
// Android P及以上版本需要额外处理signingInfo
if (Build.VERSION.SDK_INT >= 28 && packageInfo.signingInfo != null) {
modifySigningInfo(packageInfo);
}
}
/**
* 修改Android P+的签名信息
*/
private void modifySigningInfo(PackageInfo packageInfo) {
try {
Signature[] apkSigners = packageInfo.signingInfo.getApkContentsSigners();
if (apkSigners != null && apkSigners.length > 0) {
apkSigners[0] = Patch.sFakeSignature;
}
} catch (Exception e) {
Log.w(LOG_TAG, "修改SigningInfo失败", e);
}
}
}
/**
* ApplicationInfo Creator代理(Android P以下版本)
* 用于替换ApplicationInfo.CREATOR,修改返回的应用信息
*/
private static class ApplicationInfoCreatorProxyLegacy extends BaseCreatorProxy<ApplicationInfo> {
@Override
protected ApplicationInfo doCreateFromParcel(Parcel source) {
// 使用原始Creator创建对象
ApplicationInfo appInfo = (ApplicationInfo) Patch.ORIGINAL_APP_INFO_CREATOR.createFromParcel(source);
// 修改应用信息
modifyApplicationInfo(appInfo);
return appInfo;
}
@Override
protected ApplicationInfo[] doNewArray(int size) {
return (ApplicationInfo[]) Patch.ORIGINAL_APP_INFO_CREATOR.newArray(size);
}
@Override
public boolean equals(Object obj) {
return this == obj;
}
@Override
public int hashCode() {
return System.identityHashCode(this);
}
}
/**
* ApplicationInfo Creator代理(Android P及以上版本)
* 支持ClassLoaderCreator接口
*/
private static class ApplicationInfoCreatorProxyP extends BaseCreatorProxy<ApplicationInfo>
implements Parcelable.ClassLoaderCreator<ApplicationInfo> {
@Override
protected ApplicationInfo doCreateFromParcel(Parcel source) {
// 使用原始Creator创建对象
ApplicationInfo appInfo = (ApplicationInfo) Patch.ORIGINAL_APP_INFO_CREATOR.createFromParcel(source);
// 修改应用信息
modifyApplicationInfo(appInfo);
return appInfo;
}
@Override
protected ApplicationInfo[] doNewArray(int size) {
return (ApplicationInfo[]) Patch.ORIGINAL_APP_INFO_CREATOR.newArray(size);
}
@Override
public ApplicationInfo createFromParcel(Parcel source, ClassLoader loader) {
return doCreateFromParcel(source);
}
}
/**
* PackageInfo Creator代理(Android P以下版本)
* 用于替换PackageInfo.CREATOR,修改返回的包信息
*/
private static class PackageInfoCreatorProxyLegacy extends BaseCreatorProxy<PackageInfo> {
@Override
protected PackageInfo doCreateFromParcel(Parcel source) {
// 使用原始Creator创建对象
PackageInfo packageInfo = (PackageInfo) Patch.ORIGINAL_CREATOR.createFromParcel(source);
// 修改包信息
modifyPackageInfo(packageInfo);
return packageInfo;
}
@Override
protected PackageInfo[] doNewArray(int size) {
return (PackageInfo[]) Patch.ORIGINAL_CREATOR.newArray(size);
}
@Override
public boolean equals(Object obj) {
return this == obj;
}
@Override
public int hashCode() {
return System.identityHashCode(this);
}
}
/**
* PackageInfo Creator代理(Android P及以上版本)
* 支持ClassLoaderCreator接口
*/
private static class PackageInfoCreatorProxyP extends BaseCreatorProxy<PackageInfo>
implements Parcelable.ClassLoaderCreator<PackageInfo> {
@Override
protected PackageInfo doCreateFromParcel(Parcel source) {
// 使用原始Creator创建对象
PackageInfo packageInfo = (PackageInfo) Patch.ORIGINAL_CREATOR.createFromParcel(source);
// 修改包信息
modifyPackageInfo(packageInfo);
return packageInfo;
}
@Override
protected PackageInfo[] doNewArray(int size) {
return (PackageInfo[]) Patch.ORIGINAL_CREATOR.newArray(size);
}
@Override
public PackageInfo createFromParcel(Parcel source, ClassLoader loader) {
return doCreateFromParcel(source);
}
}
/**
* 获取Unsafe实例
* 用于直接操作内存,修改final字段
*/
private static Unsafe getUnsafeInstance() throws Exception {
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
return (Unsafe) unsafeField.get(null);
}
/**
* 替换Parcelable.Creator
* 核心方法:通过反射和Unsafe替换PackageInfo和ApplicationInfo的CREATOR字段
*/
public static void replaceParcelableCreators() throws Exception {
Log.i(LOG_TAG, "开始替换Parcelable.Creator...");
// 替换PackageInfo.CREATOR
replacePackageInfoCreator();
// 替换ApplicationInfo.CREATOR
replaceApplicationInfoCreator();
// 清理缓存
CacheHandling.clearCaches();
Log.i(LOG_TAG, "Parcelable.Creator替换完成");
}
/**
* 替换PackageInfo.CREATOR
*/
private static void replacePackageInfoCreator() throws Exception {
Field creatorField = PackageInfo.class.getDeclaredField("CREATOR");
creatorField.setAccessible(true);
// 保存原始Creator
Patch.ORIGINAL_CREATOR = (Parcelable.Creator) creatorField.get(null);
// 根据Android版本选择代理实现
BaseCreatorProxy<PackageInfo> packageInfoProxy = createPackageInfoProxy();
// 伪装代理类名
disguiseProxyClassName(packageInfoProxy, "android.content.pm.PackageInfo$1");
// 替换CREATOR字段
replaceFinalField(creatorField, packageInfoProxy);
}
/**
* 替换ApplicationInfo.CREATOR
*/
private static void replaceApplicationInfoCreator() throws Exception {
Field creatorField = ApplicationInfo.class.getDeclaredField("CREATOR");
creatorField.setAccessible(true);
// 保存原始Creator
Patch.ORIGINAL_APP_INFO_CREATOR = (Parcelable.Creator) creatorField.get(null);
// 根据Android版本选择代理实现
BaseCreatorProxy<ApplicationInfo> appInfoProxy = createApplicationInfoProxy();
// 伪装代理类名
disguiseProxyClassName(appInfoProxy, "android.content.pm.ApplicationInfo$1");
// 替换CREATOR字段
replaceFinalField(creatorField, appInfoProxy);
}
/**
* 创建PackageInfo代理实例
*/
private static BaseCreatorProxy<PackageInfo> createPackageInfoProxy() {
if (Build.VERSION.SDK_INT >= 28) {
return new PackageInfoCreatorProxyP();
} else {
return new PackageInfoCreatorProxyLegacy();
}
}
/**
* 创建ApplicationInfo代理实例
*/
private static BaseCreatorProxy<ApplicationInfo> createApplicationInfoProxy() {
if (Build.VERSION.SDK_INT >= 28) {
return new ApplicationInfoCreatorProxyP();
} else {
return new ApplicationInfoCreatorProxyLegacy();
}
}
/**
* 伪装代理类名
* 使用Unsafe直接修改Class对象的name字段,增加隐蔽性
*/
private static void disguiseProxyClassName(Object proxy, String targetClassName) throws Exception {
Unsafe unsafe = getUnsafeInstance();
Field nameField = Class.class.getDeclaredField("name");
long nameFieldOffset = unsafe.objectFieldOffset(nameField);
unsafe.putObject(proxy.getClass(), nameFieldOffset, targetClassName);
}
/**
* 替换final字段的值
* 通过反射修改字段访问标志,然后设置新值
*/
private static void replaceFinalField(Field field, Object newValue) throws Exception {
// 获取原始访问标志
Field modifiersField = Field.class.getDeclaredField("accessFlags");
modifiersField.setAccessible(true);
int originalModifiers = field.getModifiers();
try {
// 移除final标志
modifiersField.setInt(field, originalModifiers & ~Modifier.FINAL);
// 设置新值
field.set(null, newValue);
} finally {
// 恢复final标志
modifiersField.setInt(field, originalModifiers | Modifier.FINAL);
}
}
}
清理缓存
/**
* 缓存清理工具类
* 清理PackageManager和Parcel中的各种缓存,确保Hook修改能够立即生效
* 在修改系统状态后调用,避免缓存导致修改不生效
*/
public class CacheHandling {
/**
* 清理所有相关缓存
* 包括PackageManager的包信息缓存和Parcel的Creator缓存
* 在修改PackageInfo或ApplicationInfo后必须调用此方法
*/
public static void clearCaches() {
clearPackageInfoCache();
clearParcelCreatorCache();
clearParcelPairedCreatorCache();
}
/**
* 清理PackageManager的包信息缓存
* 清除系统对包信息的缓存,确保下次查询时重新加载修改后的信息
*/
private static void clearPackageInfoCache() {
try {
// 获取PackageManager的sPackageInfoCache字段
Field cacheField = findFieldRecursively(PackageManager.class, "sPackageInfoCache");
Object cacheInstance = cacheField.get(null);
if (cacheInstance != null) {
// 调用缓存对象的clear方法清空缓存
cacheInstance.getClass().getMethod("clear").invoke(cacheInstance);
}
} catch (Throwable ignored) {
// 忽略清理缓存时的异常,不影响主要功能
}
}
/**
* 清理Parcel的Creator缓存
* 清除Parcel对Parcelable.Creator的缓存,确保使用我们替换的Creator
*/
private static void clearParcelCreatorCache() {
try {
// 获取Parcel的mCreators字段
Field creatorsField = findFieldRecursively(Parcel.class, "mCreators");
Map<?, ?> creatorsMap = (Map<?, ?>) creatorsField.get(null);
if (creatorsMap != null) {
creatorsMap.clear();
}
} catch (Throwable ignored) {
// 忽略清理缓存时的异常
}
}
/**
* 清理Parcel的配对Creator缓存
* 清除Parcel的sPairedCreators缓存(某些Android版本特有)
*/
private static void clearParcelPairedCreatorCache() {
try {
// 获取Parcel的sPairedCreators字段
Field pairedCreatorsField = findFieldRecursively(Parcel.class, "sPairedCreators");
Map<?, ?> pairedCreatorsMap = (Map<?, ?>) pairedCreatorsField.get(null);
if (pairedCreatorsMap != null) {
pairedCreatorsMap.clear();
}
} catch (Throwable ignored) {
// 忽略清理缓存时的异常
}
}
/**
* 递归查找字段
* 在当前类及其父类中查找指定字段
*
* @param targetClass 要查找的起始类
* @param fieldName 要查找的字段名
* @return 找到的Field对象
* @throws NoSuchFieldException 如果字段不存在
*/
private static Field findFieldRecursively(Class<?> targetClass, String fieldName)
throws NoSuchFieldException {
Class<?> currentClass = targetClass;
// 在当前类及其父类中递归查找字段
while (currentClass != null && !Object.class.equals(currentClass)) {
try {
Field field = currentClass.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException e) {
// 在当前类中未找到,继续在父类中查找
currentClass = currentClass.getSuperclass();
}
}
throw new NoSuchFieldException("字段未找到: " + fieldName);
}
}