直接放代码

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. 【原创】大数据基础之Zookeeper(3)选举算法

    提到zookeeper选举算法,就不得不提Paxos算法,因为zookeeper选举算法是Paxos算法的一个变种: Paxos要解决的问题是:在一个分布式网络环境中有众多的参与者,但是每个参与者都不 ...

  2. 【原创】大叔问题定位分享(11)Spark中对大表子查询加limit为什么会报Broadcast超时错误

    当两个表需要join时,如果一个是大表,一个是小表,正常的map-reduce流程需要shuffle,这会导致大表数据在节点间网络传输,常见的优化方式是将小表读到内存中并广播到大表处理,避免shuff ...

  3. centos6.5环境下的web项目mysql编码方式导致的中文乱码问题

    最近在centos6.5下部署web项目时网页出现中文乱码的问题,在排除掉php之后,把问题锁定在mysql的编码方式上. 解决方法如下: 首先进入mysql命令行,输入命令:SHOW VARIABL ...

  4. 【转载】Linux启动初始化配置文件浅析(解决source /etc/profile重启后就失效?)

    1)/etc/profile   登录时,会执行. 全局(公有)配置,不管是哪个用户,登录时都会读取该文件. (2)/ect/bashrc   Ubuntu没有此文件,与之对应的是/ect/bash. ...

  5. 网络流24题——圆桌问题 luogu 3254

    题目传送门:这里 这是网络流24题里最简单的一道,我们从这里开始 虽然是网络流24题之一,但可以不用网络流... 本题采用贪心即可 有一个很显然的思想:在分配每一组时,我们都应当优先分配给当前可容纳人 ...

  6. Emacs Org-mode 2 文档结构

    2.1 章节 org-mode用* 标识章节,一个* 代表一级标题,两个* 代表两级标题,以此类推.最多6颗星,也就是最多6级. 书写格式如下: * 标题一 ** 标题二 注意, * 后有空格.不同的 ...

  7. 使用Python下载文件

    python -c "with open('/tmp/file.sh', 'wb') as f: import urllib2; f.write(urllib2.urlopen('http: ...

  8. win10安装Oracle11g-出现INS-13001环境不满足最低要求问题

    今天安装Oracle11g,出现INS-13001环境不满足最低要求问题: 解决方法 在安装时点击setup.exe之后,出现了:[INS-13001]环境不满足最低要求 这时,打开你的解压后的dat ...

  9. webpack报错运行时没有定义

    一.问题描述 ReferenceError: regeneratorRuntime is not defined 二.问题分析 缺少regenerator的运行时库,具体原理,可查看babel文章. ...

  10. python与RabbitMQ

    RabbitMQ 前言 什么是MQ? MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法.应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用 ...