懒惰是科技的第一生产力。
0×00 背景由于Android逆向每次想要使用Xposed进行Hook时,总是需要重复性地操作一遍Android Studio新建项目的流程.ps:当然可以只用一个项目,强迫症需要分开 :)
由于Xposed实现的方式,每次修改hook代码后,需要重启机器,这也是白白浪费了很多时间。
基于以上两点,参考现有的方案,实现了一个Module,只需在AS中new一下即可解决问题。
0×01 创建XposedModule 1.效果:2.代码结构:
3.代码解析:
template.xml:
<?xml version="1.0"?> <template description="Creates a new Xposed Module without restart" format="3" minApi="15" minBuildApi="15" revision="4"> <category value="Other"/> <!-- parameter 主要是:效果图中,需要输入的几个设置栏 --> <parameter constraints="nonempty|unique|class" default="XposedMod" help="Class that contains Xposed code" type="string"/> <parameter constraints="nonempty" help="Description of Xposed Module" type="string"/> <parameter constraints="package" default="com.xxx.xxxx.xposed" type="string"/> <parameter constraints="nonempty" default="com.xxxx.xxx.xposed" type="string"/> <!-- Module图标 --> <thumbs> <thumb>template_xposed_module.png</thumb> </thumbs> <!-- 全局变量 --> <globals file="globals.xml.ftl"/> <!-- 需要执行的操作 关键点--> <execute file="recipe.xml.ftl"/> </template>recipe.xml.ftl
<?xml version="1.0"?> <recipe> <!-- 创建文件 --> <mkdir at="${escapeXmlAttribute(manifestOut)}/assets/"/> <!-- 移动合并文件 --> <merge from="AndroidManifest.xml.ftl" to="${escapeXmlAttribute(manifestOut)}/AndroidManifest.xml"/> <merge from="build.gradle.ftl" to="${escapeXmlAttribute(projectOut)}/build.gradle"/> <merge from="res/values/strings.xml.ftl" to="${escapeXmlAttribute(resOut)}/values/strings.xml"/> <!-- 重命名文件 --> <instantiate from="assets/xposed_init.ftl" to="${escapeXmlAttribute(manifestOut)}/assets/xposed_init"/> <instantiate from="src/app_package/XposedMod.java.ftl" to="${escapeXmlAttribute(srcOut)}/${xposedModClass}.java"/> <!-- 打开文件 --> <open file="${escapeXmlAttribute(srcOut)}/${xposedModClass}.java"/> </recipe>xposed_init.ftl 存储的是 Hook类的入口地址
strings.xml.ftl 存储的是 Xpodse模块的描述
XposedMod.java.ftl 创建后的模版代码,可以根据自己的需求,修改模版里面的代码
AndroidManifest.xml.ftl 主要是Xposed的meta字段
build.gradle.ftl 为空
4.bug修复由于Xposed会预先加载好jar包,因此,build.gradle中的implements需要修改为provided,才不会出现错误。
具体修改build.gradle.ftl,添加下面依赖:
dependencies { provided 'de.robv.android.xposed:api:82' } 0×02 加入免重启功能原理分析:原理这里不作多描述,实际上就是通过替换Xposed插件生成的APK,然后通过动态加载的方式来调用,以实现免重启的功能。具体可阅读参考文章。
改进:对上面的AS模版进行改造,以实现免重启。
package ${packageName}; import android.app.Application; import android.content.Context; import android.content.pm.PackageManager; 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; public class HookLoader implements IXposedHookLoadPackage { /** * 当前Xposed模块的包名,方便寻找apk文件 */ private final String modulePackage = "${packageName}"; /** * 宿主程序的包名(允许多个),过滤无意义的包名,防止无意义的apk文件加载 */ private static List<String> hostAppPackages = new ArrayList<>(); /** * 实际hook逻辑处理类 */ private final String handleHookClass = ${xposedModClass}.class.getName(); /** * 实际hook逻辑处理类的入口方法 */ private final String handleHookMethod = "handleLoadPackage"; static { // TODO: Add the package name of application your want to hook! hostAppPackages.add("${hookPackageName}"); } @Override public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable { if (hostAppPackages.contains(loadPackageParam.packageName)) { 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, handleHookClass, handleHookMethod, loadPackageParam); } }); } } /** * 安装app以后,通过动态加载这个apk文件,调用相应的方法 * 从而实现免重启 * @param context context参数 * @param handleHookClass 指定由哪一个类处理相关的hook逻辑 * @param loadPackageParam 传入XC_LoadPackage.LoadPackageParam参数 * @throws Throwable 抛出各种异常,包括具体hook逻辑的异常,寻找apk文件异常,反射加载Class异常等 */ private void invokeHandleHookMethod(Context context, String handleHookClass, String handleHookMethod, XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable { String apkPath = context.getPackageManager().getApplicationInfo(this.modulePackage,PackageManager.GET_META_DATA).sourceDir; PathClassLoader pathClassLoader = new PathClassLoader(apkPath, 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); } }文章作者是通过区分不同的sdk以实现找到apk的findapk的方法,实际上这里有很简便的方法:通过系统API便可找到对应的apk路径:
一行代码即可搞定
String apkPath = context.getPackageManager().getApplicationInfo(this.modulePackage,PackageManager.GET_META_DATA).sourceDir; 0×03 产出XpdModule 需重启
XpdFreeRebootModule 免重启
参考文章&致谢Xposed模块开发,免重启改进方案
Xposed Module Template for Android Studio
下一篇: 华为云安全攻防团队招聘渗透测试工程师