懒人福利|一秒创建Xposed模版

*本文原创作者:Tasfa,本文属FreeBuf原创奖励计划,未经许可禁止转载

FreeBuf:http://www.freebuf.com/sectool/167274.html

懒惰是科技的第一生产力。

0×00 背景

由于Android逆向每次想要使用Xposed进行Hook时,总是需要重复性地操作一遍Android Studio新建项目的流程.ps:当然可以只用一个项目,强迫症需要分开 :)

由于Xposed实现的方式,每次修改hook代码后,需要重启机器,这也是白白浪费了很多时间。

基于以上两点,参考现有的方案,实现了一个Module,只需在AS中new一下即可解决问题。

0×01 创建XposedModule

1.效果:

2222.png

2.代码结构:

目录树.png

3.代码解析:

template.xml:

<?xml version="1.0"?>
<template
    name="XposedModuleFreeRestart"
    description="Creates a new Xposed Module without restart"
    format="3"
    minApi="15"
    minBuildApi="15"
    revision="4">

    <category value="Other"/>

    <!-- parameter 主要是:效果图中,需要输入的几个设置栏 -->
    <parameter
        name="Xposed Mod class"
        constraints="nonempty|unique|class"
        default="XposedMod"
        help="Class that contains Xposed code"
        id="xposedModClass"
        type="string"/>

    <parameter
        name="Xposed Description"
        constraints="nonempty"
        help="Description of Xposed Module"
        id="xposedDescription"
        type="string"/>

    <parameter
        name="Package name"
        constraints="package"
        default="com.xxx.xxxx.xposed"
        id="packageName"
        type="string"/>

    <parameter
        name="Hooked Package name"
        constraints="nonempty"
        default="com.xxxx.xxx.xposed"
        id="hookPackageName"
        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

感谢参考文章的作者以及同事。

*本文原创作者:Tasfa,本文属FreeBuf原创奖励计划,未经许可禁止转载

发表评论

电子邮件地址不会被公开。

You must enable javascript to see captcha here!