直接放代码

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免重启调试工具类的更多相关文章

  1. tomcat免重启随意更改java代码 提高开发效率

    转载:http://developer.51cto.com/art/201012/241243.htm 做为了一个java开发人员,总是为因为要增加一个类,或是增加删除一个方法,甚至修改一个小处代码而 ...

  2. Eclipse的Tomcat热部署,免重启的方法

    背景与目标: 最好使用MyEclipse部署Web应用,在开发调试时,非常方式.资源文件修改可以自动的同步.修改Java文件,除非改变类的结构定义,也可以实现热部署的效果. 后来使用Eclipse J ...

  3. php简单实用的调试工具类

    <?php /* * 调试类 */ class Common_Debug { //打开错误报告 public static function showError($debug = true) { ...

  4. Node.js中的express框架,修改内容后自动更新(免重启),express热更新

    个人网站 https://iiter.cn 程序员导航站 开业啦,欢迎各位观众姥爷赏脸参观,如有意见或建议希望能够不吝赐教! 以前node中的express框架,每次修改代码之后,都需要重新npm s ...

  5. HDFS免重启挂载新磁盘

    背景 在生产环境中,集群节点磁盘大小不同,其使用率也会不同,HDFS虽有均衡策略,但也会有数据不平衡的情况,有些节点磁盘就会被打满,然后这个节点就不健康了(Unhealthy Nodes),Yarn的 ...

  6. Xposed模块开发基本方法记录

    由于某些课程实验的要求,需要通过xposed框架对某应用进行hook操作,笔者选用了开源且免费的xposed框架进行实现.虽然网上存在一些利用xposed实现特定功能的文章资源,但大多均将xposed ...

  7. Xposed的新打开方式--Xpatch工作流程分析

    1. Xpatch概述 Xpatch是一款利用重打包的方式,使得被处理的Apk启动时自动加载Xposed模块,来实现应用内Hook的工具. 项目地址:https://github.com/WindyS ...

  8. 使用更清晰DebugLog开发和调试工具

    在开发和应用的开发和调试过程中难免会发现故障的过程中.我相信很多做iOS开发程序员Xcode的debug调试功能大加关注. 但在这样做Android开发过程中,却不那么方便,虽然IDE也提供了debu ...

  9. Spring Developer Tools 源码分析:二、类路径监控

    在 Spring Developer Tools 源码分析一中介绍了 devtools 提供的文件监控实现,在第二部分中,我们将会使用第一部分提供的目录监控功能,实现对开发环境中 classpath ...

随机推荐

  1. 【原创】算法基础之Anaconda(1)简介、安装、使用

    Anaconda 2 官方:https://www.anaconda.com/ 一 简介 The Most Popular Python Data Science Platform Anaconda® ...

  2. docker简单介绍---网络端口管理

    一.查看docker支持的网络类型 docker network ls bridge:容器使用虚拟交换机的进行通信 host:使用宿主机的网络 none:只给容器分配一个lo的网卡,无法和外界进行通信 ...

  3. Java面试题复习笔记(前端)

    1.Html,CSS,Jsp在网页开发中的定位? Html——定义网页结构(超文本标记语言) CSS——层叠样式表,用来美化界面 Jsp——主要用来验证表单,做动态交互(Ajax) 2.介绍Ajax? ...

  4. <?xml version="1.0" encoding="UTF-8"?> 的作用

    version="1.0" 声明用的xml版本是1.0 encoding="UTF-8" 声明用xml传输数据的时候的字符编码,假如文档里面有中文,编码方式不是 ...

  5. Java桥接模式

    定义:将抽象部分与它的具体实现部分分离,使他们都可以独立的变化 通过组合的方式建立两个类之间联系,而不是继承 类型:结构型 适用场景: 抽象和具体实现之间增加更多的灵活性 一个类存在两个(或多个)独立 ...

  6. P1525 关押罪犯 并查集

    题目描述 SS城现有两座监狱,一共关押着NN名罪犯,编号分别为1-N1−N.他们之间的关系自然也极不和谐.很多罪犯之间甚至积怨已久,如果客观条件具备则随时可能爆发冲突.我们用“怨气值”(一个正整数值) ...

  7. PyCharm 怎么查看 Python 的变量类型和变量内容

    一.在程序的某一行添加断点 二.选择 debug 程序

  8. springmvc映射html文件以及解决乱码问题

    <servlet-mapping> <servlet-name>jsp</servlet-name> <url-pattern>*.html</u ...

  9. Django & JavaScript 用Ajax实现JSON数据的请求和响应

    [描述] 1.Server端定义了两个字段:Article.title 和 Article.content 2.客户端用JavaScript Ajax异步加载请求服务器的JSON数据 效果是点击按钮从 ...

  10. Python中何时使用断言

    这个问题是如何在一些场景下使用断言表达式,通常会有人误用它,所以我决定写一篇文章来说明何时使用断言,什么时候不用. 为那些还不清楚它的人,Python的assert是用来检查一个条件,如果它为真,就不 ...