Xposed免重启调试工具类
直接放代码
package com.xirtam.hello; import android.app.Application;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build; import java.io.File;
import java.io.FileNotFoundException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List; import dalvik.system.PathClassLoader;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage; /**
* @author DX
* 这种方案建议只在开发调试的时候使用,因为这将损耗一些性能(需要额外加载apk文件),调试没问题后,直接修改xposed_init文件为正确的类即可
* 可以实现免重启,由于存在缓存,需要杀死宿主程序以后才能生效
* 这种免重启的方式针对某些特殊情况的hook无效
* 例如我们需要implements IXposedHookZygoteInit,并将自己的一个服务注册为系统服务,这种就必须重启了
* Created by DX on 2017/10/4.
*/ public class HookLoader implements IXposedHookLoadPackage {
//按照实际使用情况修改下面几项的值
/**
* 当前Xposed模块的包名,方便寻找apk文件 TODO 需要配置
*/
private final String modulePackage = "com.xirtam.hello";
/**
* 宿主程序的包名(允许多个),过滤无意义的包名,防止无意义的apk文件加载
*/
private static List<String> hostAppPackages = new ArrayList<>(); static {
// TODO: Add the package name of application your want to hook!
hostAppPackages.add("com.xirtam.hello");
} /**
* 实际hook逻辑处理类 TODO 需要配置
*/
private final String handleHookClass = TestHook2.class.getName();
/**
* 实际hook逻辑处理类的入口方法
*/
private final String handleHookMethod = "handleLoadPackage"; @Override
public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
if (hostAppPackages.contains(loadPackageParam.packageName)) {
//将loadPackageParam的classloader替换为宿主程序Application的classloader,解决宿主程序存在多个.dex文件时,有时候ClassNotFound的问题
XposedHelpers.findAndHookMethod(Application.class, "attach", Context.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
Context context = (Context) param.args[0];
loadPackageParam.classLoader = context.getClassLoader();
invokeHandleHookMethod(context, modulePackage, handleHookClass, handleHookMethod, loadPackageParam);
}
});
}
} /**
* 安装app以后,系统会在/data/app/下备份了一份.apk文件,通过动态加载这个apk文件,调用相应的方法
* 这样就可以实现,只需要第一次重启,以后修改hook代码就不用重启了
*
* @param context context参数
* @param modulePackageName 当前模块的packageName
* @param handleHookClass 指定由哪一个类处理相关的hook逻辑
* @param loadPackageParam 传入XC_LoadPackage.LoadPackageParam参数
* @throws Throwable 抛出各种异常,包括具体hook逻辑的异常,寻找apk文件异常,反射加载Class异常等
*/
private void invokeHandleHookMethod(Context context, String modulePackageName, String handleHookClass, String handleHookMethod, XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
// File apkFile = findApkFileBySDK(modulePackageName);//会受其它Xposed模块hook 当前宿主程序的SDK_INT的影响
// File apkFile = findApkFile(modulePackageName);
//原来的两种方式不是很好,改用这种新的方式
File apkFile = findApkFile(context, modulePackageName);
if (apkFile == null) {
throw new RuntimeException("寻找模块apk失败");
}
//加载指定的hook逻辑处理类,并调用它的handleHook方法
PathClassLoader pathClassLoader = new PathClassLoader(apkFile.getAbsolutePath(), ClassLoader.getSystemClassLoader());
Class<?> cls = Class.forName(handleHookClass, true, pathClassLoader);
Object instance = cls.newInstance();
Method method = cls.getDeclaredMethod(handleHookMethod, XC_LoadPackage.LoadPackageParam.class);
method.invoke(instance, loadPackageParam);
} /**
* 根据包名构建目标Context,并调用getPackageCodePath()来定位apk
*
* @param context context参数
* @param modulePackageName 当前模块包名
* @return return apk file
*/
private File findApkFile(Context context, String modulePackageName) {
if (context == null) {
return null;
}
try {
Context moudleContext = context.createPackageContext(modulePackageName, Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
String apkPath = moudleContext.getPackageCodePath();
return new File(apkPath);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return null;
} /**
* 寻找这个Android设备上的当前apk文件,不受其它Xposed模块hook SDK_INT的影响
*
* @param modulePackageName 当前模块包名
* @return File 返回apk文件
* @throws FileNotFoundException 在/data/app/下的未找到本模块apk文件,请检查本模块包名配置是否正确.
* 具体检查build.gradle中的applicationId和AndroidManifest.xml中的package
*/
@Deprecated
private File findApkFile(String modulePackageName) throws FileNotFoundException {
File apkFile = null;
try {
apkFile = findApkFileAfterSDK21(modulePackageName);
} catch (Exception e) {
try {
apkFile = findApkFileBeforeSDK21(modulePackageName);
} catch (Exception e2) {
//忽略这个异常
}
}
if (apkFile == null) {
throw new FileNotFoundException("没在/data/app/下找到文件对应的apk文件");
}
return apkFile;
} /**
* 根据当前的SDK_INT寻找这个Android设备上的当前apk文件
*
* @param modulePackageName 当前模块包名
* @return File 返回apk文件
* @throws FileNotFoundException 在/data/app/下的未找到本模块apk文件,请检查本模块包名配置是否正确.
* 具体检查build.gradle中的applicationId和AndroidManifest.xml中的package
*/
@Deprecated
private File findApkFileBySDK(String modulePackageName) throws FileNotFoundException {
File apkFile;
//当前Xposed模块hook了Build.VERSION.SDK_INT不用担心,因为这是发生在hook之前,不会有影响
//但是其它的Xposed模块hook了当前宿主的这个值以后,就会有影响了,所以这里没有使用这个方法
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
apkFile = findApkFileAfterSDK21(modulePackageName);
} else {
apkFile = findApkFileBeforeSDK21(modulePackageName);
}
return apkFile;
} /**
* 寻找apk文件(api_21之后)
* 在Android sdk21以及之后,apk文件的路径发生了变化
*
* @param packageName 当前模块包名
* @return File 返回apk文件
* @throws FileNotFoundException apk文件未找到
*/
@Deprecated
private File findApkFileAfterSDK21(String packageName) throws FileNotFoundException {
File apkFile;
File path = new File(String.format("/data/app/%s-%s", packageName, "1"));
if (!path.exists()) {
path = new File(String.format("/data/app/%s-%s", packageName, "2"));
}
if (!path.exists() || !path.isDirectory()) {
throw new FileNotFoundException(String.format("没找到目录/data/app/%s-%s", packageName, "1/2"));
}
apkFile = new File(path, "base.apk");
if (!apkFile.exists() || apkFile.isDirectory()) {
throw new FileNotFoundException(String.format("没找到文件/data/app/%s-%s/base.apk", packageName, "1/2"));
}
return apkFile;
} /**
* 寻找apk文件(api_21之前)
*
* @param packageName 当前模块包名
* @return File 返回apk文件
* @throws FileNotFoundException apk文件未找到
*/
@Deprecated
private File findApkFileBeforeSDK21(String packageName) throws FileNotFoundException {
File apkFile = new File(String.format("/data/app/%s-%s.apk", packageName, "1"));
if (!apkFile.exists()) {
apkFile = new File(String.format("/data/app/%s-%s.apk", packageName, "2"));
}
if (!apkFile.exists() || apkFile.isDirectory()) {
throw new FileNotFoundException(String.format("没找到文件/data/app/%s-%s.apk", packageName, "1/2"));
}
return apkFile;
}
}
配置一下3处TODO标签的位置,然后把xposed_init文件中的配置改成这个类即可,并不适用于所有情况的免重启,注释写的很明确了,感谢原作者,本文代码有少量修改。
Xposed免重启调试工具类的更多相关文章
- tomcat免重启随意更改java代码 提高开发效率
转载:http://developer.51cto.com/art/201012/241243.htm 做为了一个java开发人员,总是为因为要增加一个类,或是增加删除一个方法,甚至修改一个小处代码而 ...
- Eclipse的Tomcat热部署,免重启的方法
背景与目标: 最好使用MyEclipse部署Web应用,在开发调试时,非常方式.资源文件修改可以自动的同步.修改Java文件,除非改变类的结构定义,也可以实现热部署的效果. 后来使用Eclipse J ...
- php简单实用的调试工具类
<?php /* * 调试类 */ class Common_Debug { //打开错误报告 public static function showError($debug = true) { ...
- Node.js中的express框架,修改内容后自动更新(免重启),express热更新
个人网站 https://iiter.cn 程序员导航站 开业啦,欢迎各位观众姥爷赏脸参观,如有意见或建议希望能够不吝赐教! 以前node中的express框架,每次修改代码之后,都需要重新npm s ...
- HDFS免重启挂载新磁盘
背景 在生产环境中,集群节点磁盘大小不同,其使用率也会不同,HDFS虽有均衡策略,但也会有数据不平衡的情况,有些节点磁盘就会被打满,然后这个节点就不健康了(Unhealthy Nodes),Yarn的 ...
- Xposed模块开发基本方法记录
由于某些课程实验的要求,需要通过xposed框架对某应用进行hook操作,笔者选用了开源且免费的xposed框架进行实现.虽然网上存在一些利用xposed实现特定功能的文章资源,但大多均将xposed ...
- Xposed的新打开方式--Xpatch工作流程分析
1. Xpatch概述 Xpatch是一款利用重打包的方式,使得被处理的Apk启动时自动加载Xposed模块,来实现应用内Hook的工具. 项目地址:https://github.com/WindyS ...
- 使用更清晰DebugLog开发和调试工具
在开发和应用的开发和调试过程中难免会发现故障的过程中.我相信很多做iOS开发程序员Xcode的debug调试功能大加关注. 但在这样做Android开发过程中,却不那么方便,虽然IDE也提供了debu ...
- Spring Developer Tools 源码分析:二、类路径监控
在 Spring Developer Tools 源码分析一中介绍了 devtools 提供的文件监控实现,在第二部分中,我们将会使用第一部分提供的目录监控功能,实现对开发环境中 classpath ...
随机推荐
- 008_tcp探测
一. 线上出现了监控指标抖动,netscaler心跳监测的问题,如下图: 二.工具如下: 原理参考:https://www.dearcodes.com/index.php/archives/17/ N ...
- 集腋成裘-09-ECharts -HelloECharts-02
上一篇我们讲了可视化数据分析的重要性以及ECharts的一些的特性,这一篇我们继续学习一下ECharts的简单Demo 一:5 分钟上手 ECharts <!DOCTYPE html> & ...
- Javascript我学之五对象的创建与使用
本文是金旭亮老师网易云课堂的课程笔记,记录下来,以供备忘. 对象的创建 JavaScript对象有两种类型 1).Native:在ECMAScript标准中定义和描述,包括JavaScript内置 ...
- Linux查看当前目录下所有子目录是否包含某个文件
在Linux下,当需要找某个文件但又不知道这个文件在哪个具体的目录下,这时可以使用全目录查找 使用find命令: find . -type f -name "job_21_output*&q ...
- WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!问题
➜ web_develop git:(master) ✗ ssh root@172.16.146.143@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ...
- react-native项目中集成react-native-camera插件
1. 安装 yarn add react-native-camera 2. 手动关联 (1)在AndroidManifest.xml中添加权限配置 <uses-permission androi ...
- IdentityServer4 密码模式认证
授权服务器设置 添加用户 添加测试用户,也可以从数据库查 public static List<TestUser> GetTestUser() { return new List< ...
- xlrd模块;xlwt模块使用,smtp发送邮件
先安装 pip3 install xlwt pip3 install xlrd import xlwt, xlrd from xlrd.book import Book from xlrd.sheet ...
- POJ 3280 Cheapest Palindrome (区间DP) 经典
<题目链接> 题目大意: 一个由小写字母组成的字符串,给出字符的种类,以及字符串的长度,再给出添加每个字符和删除每个字符的代价,问你要使这个字符串变成回文串的最小代价. 解题分析: 一道区 ...
- HttpConnection
1.HttpConnection 用于接受和发送网络数据 网络操作必须新开个子线程执行,否则会出现 ANR(Application Not Response) 应用无响应异常 Get: /** * 通 ...