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 ...
随机推荐
- WPF 之 调用线程必须为 STA,因为许多 UI 组件都需要
WPF中,代码中准备控制控件内容时,有时会报错:“ 调用线程必须为 STA,因为许多 UI 组件都需要 ”. 如在winform下面,使用多线程时,控件的值读取是可以的,但如果要更改,那么就必须进行一 ...
- [转]Ubuntu中apt与apt-get命令的区别
转载于https://www.sysgeek.cn/apt-vs-apt-get/ Ubuntu 16.04 发布时,一个引人注目的新特性便是 apt 命令的引入.其实早在 2014 年,apt 命令 ...
- JAVA 实现 简单的 HTTP服务器
关于JAVA的HTTP服务器的简单实现,还望大家给出宝贵建议,谢谢. 1.首先来说下HTTP所有的状态碼,相信对于网页开发的博友们,肯定不陌生. 状态码 状态码英文名称 中文描述 100 Contin ...
- SpringMVC的入门示例
1.配置流程说明 第一步:导入包 第二步:构建一个请求,编写请求页面 第三步:配置核心控制器 第四步:构建一个业务控制器 第五步:编写Spring配置文件 第六步:编写一个返回页面 2.配置流程--- ...
- mysql 的crud操作(增删改查)
1.mysql添加记录 --添加记录的语法(可添加单条记录或者多条记录),INTO是可以省略的,字段名也可以省略的,但是如果省略的话,后面对应的value的值就要全部填写 INSERT [INTO] ...
- Tomcat 下4个配置文件详解
Tomcat 的配置文件由4个 xml 文件构成,context.xml.web.xml.server.xml.tomcat-users.xml 这4个文件.每个文件都有自己的功能与配置方法,下列将逐 ...
- 金蝶K/3 报销相关SQL语句
金蝶K/3 报销相关SQL语句 use AIS20180607113701 select fopenid,* from dbo.t_XunTong_User where Fname ='' go us ...
- 【JavaScrpt】JS之数组去重
var arr = [] var peoples = [ {id:1,name:'wang'}, {id:2,name:'zhang'}, {id:2,name:'zhang'}, ] for(let ...
- 潭州课堂25班:Ph201805201 tornado 项目 第七课 界面美化和静态文件处理(课堂笔记)
tornado 相关说明 使用 Bootstrap 前端框架 可以在 bootCDN 这里找 jquery ,poppe.js 文件 美化项目 twittel 的开源项目, 在 static 目录下 ...
- 入门级----黑盒测试、白盒测试、手工测试、自动化测试、探索性测试、单元测试、性能测试、数据库性能、压力测试、安全性测试、SQL注入、缓冲区溢出、环境测试
黑盒测试 黑盒测试把产品软件当成是一个黑箱子,只有出口和入口,测试过程中只要知道往黑盒中输入什么东西,知道黑盒会出来什么结果就可以了,不需要了解黑箱子里面是如果做的. 即测试人员不用费神去理解软件里面 ...