6、XPOSED二、叉叉助手框架--用XPOSED实现

2014/04/08 android 共 6165 字,约 18 分钟

继《xposed框架初探》之后,编写一个小小的demo应用,刚好之前分析叉叉的游戏辅助框架(参考《叉叉助手逆向分析续集–模拟实现游戏插件框架–再扩展到脱壳机》,我们是用了libsubstrate的hook框架来完成的),这次就用XPOSED实现一下。

相关参考:

官方教程《Making the project an Xposed module

Android Hook框架Xposed原理与源代码分析

看雪论坛《XPOSED的小笔记》,文章介绍的很详细很清晰。

Xpoded模块开发教程

准备工作:

上一篇中我们用的是“华为专用Xposed框架”,也并不是很完美,于是还是从官网上下载稳定版本的,这个要视不同手机而定。

http://repo.xposed.info/module/de.robv.android.xposed.installer,点下面的“Show older versions”逐个版本安装试用,每个版本都要激活一次并重启手机,还是挺麻烦的,好在试到2.5.1版本的时候成功了!这个版本确实比“华为专用Xposed框架”要稳定完美许多,果断换用2.5.1官方版本。

实现思路:

实现接口IXposedHookLoadPackage的接口函数handleLoadPackage,判断参数包名是否是目标APK,若是则调用findAndHookMethod对目标启动类的onCreate函数进行HOOK,在afterHookedMethod函数中动态加载插件,将参数中有关activity的实例传给插件。

一个 XposedModule 本质上是设定了部分特殊元数据标志位的普通应用程序,需要在 AndroidManifest.xml 文件中添加如下设置:

AndroidManifest.xml => Application => Application Nodes (at the bottom) => Add => Meta Data

添加节点:name = xposedmodule,value = true。name = xposedminiversion, value = API level。

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme"
        android:name=".MainApplication">

        <!-- Xposed -->
        <meta-data
            android:name="xposedmodule"
            android:value="true" />
        <meta-data
            android:name="xposedminversion"
            android:value="42+" />
        <meta-data
            android:name="xposeddescription"
            android:value="GameAssistant" />

        <activity android:name="com.netease.ga.view.MainActivity"
            android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".view.MyGamesActivity" android:theme="@android:style/Theme.NoTitleBar.Fullscreen"/>
        <activity android:name=".view.PluginsActivity" />
        <activity android:name=".view.MoreActivity" />
        <receiver android:name=".receiver.MyReceiver" />
    </application>

添加标记的作用估计是让XPOSED安装器识别为插件。

添加库文件:XposedBridgeApi.jar

需要注意的是这个jar包不能放到工程的libs目录,否则运行会出现异常。最好是放到工程主目录下,然后在eclipse里右键选择该jar-Build Path => Add to Build Path。

注意这个jar包版本选择,经测试XposedBridgeApi-54.jar在2.5.1版本的xposedinstaller下是兼容的,但是最新版的靠谱助手(2.5.1143)上默认集成安装了2.6.1版本的xposedinstaller,测试发现XposedBridgeApi-54.jar并不是很稳定,后来改用XposedBridgeApi-42.jar是可以的。

Next, make the XposedBridge API known to the project. You can download**XposedBridgeApi-.jar** **from the first post of** [**this XDA thread**](http://forum.xda-developers.com/xposed/xposed-api-changelog-t2714067)**. Copy it into a subfolder called** **lib****. Then right-click on it and select Build Path => Add to Build Path. The** **** **from the file name is the one you insert as****xposedminversion** **in the manifest.**

Make sure that the API classes are not included (but only referenced) in your compiled APK, otherwise you will get an IllegalAccessError. Files in thelibs (with “s”) folder are automatically included by Eclipse, so don’t put the API file there.

这个设置对应到androidstudio的操作步骤:

F4打开工程结构,选择Modules-当前项目-Dependencies-+-选择“Jar or directiories”,

img

选择jar包后,在“Scope”栏选择“Provided”,不要选择“Complie”。

img

然后声明一个类XposedXXHook实现接口IXposedHookLoadPackage

在 assets 目录下新建一个 xposed_init 文件,这个文件声明了需要加载到 XposedInstaller 的入口类:com.netease.ga.XposedXXHook

img

由于改动不大,因此直接在原来的GA工程(基于libsubstrate的叉叉游戏辅助框架)上添加。

完善XposedXXHook的代码:

package com.netease.ga;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;

import java.lang.reflect.Method;

import dalvik.system.DexClassLoader;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.callbacks.XC_LoadPackage;

import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
import static de.robv.android.xposed.XposedHelpers.findClass;

/**
 * Created by sing on 14-9-17.
 * desc:
 */
public class XposedXXHook implements IXposedHookLoadPackage {
    private static final String TAG = "XposedXXHook";
    private static final String TARGET_PACKAGE = "com.example.helloapplication";
    private static final String TARGET_CLASS = "com.example.helloapplication.MainActivity";
    private static final String TARGET_FUNCTION = "onCreate";
    //private SharedPreferences sp;

    /**
     *
     * @param param
     * @throws Throwable
     */
    @Override
    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam param) throws Throwable {
        String packageName = param.packageName;
        Log.d(TAG, "handleLoadPackage: " + packageName);
        if (packageName.equals(TARGET_PACKAGE) == false) {
            return;
        }

        Log.d(TAG, "handleLoadPackage: star hook");
        XC_MethodHook.Unhook unhook = findAndHookMethod(TARGET_CLASS, param.classLoader, TARGET_FUNCTION, Bundle.class, new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                Log.d(TAG, "[handleLoadPackage]beforeHookedMethod");
            }

            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                Log.d(TAG, "[handleLoadPackage]afterHookedMethod: " + param.thisObject.toString());
                String plugApkPath = "/data/data/com.netease.ga/app_plugin/lianmengplug.apk";
                String plugSoPath = "/data/data/com.netease.ga/app_plugin/libxxlianmeng_mm.so";
                String dexOutputDir = "/data/data/" + TARGET_PACKAGE + "/cache";
                ClassLoader localClassLoader = ClassLoader.getSystemClassLoader();
                DexClassLoader localDexClassLoader = new DexClassLoader(plugApkPath, dexOutputDir, null, localClassLoader);
                java.lang.Class<?> plugClass = localDexClassLoader.loadClass("com.xxAssistant.UI.UniversalUI");
                Method mInit = plugClass.getDeclaredMethod("init", Activity.class, String.class);
                mInit.invoke(null, param.thisObject, plugSoPath);
            }
        });

        if (unhook!=null) {
            Log.d(TAG, "handleLoadPackage: hook ok");
        }else{
            Log.d(TAG, "handleLoadPackage: hook failed");
        }

    }
}

注意这段代码只HOOK包名为com.example.helloapplication的应用程序,如果要HOOK其他应用程序则把包名过滤的判断去掉,并动态解析应用程序的主启动类即可。

编译安装好后在xposed安装器的模块中激活GA并重启手机。

img

启动手机后不用运行xposed安装器和GA,直接运行helloapplication程序:

img

可以看出插件已经被我们成功加载了。

常见错误:

09-17 07:58:42.049: I/Xposed(2447): java.lang.NoSuchMethodError: com.example.helloapplication.MainActivity#onCreate()#exact

错误为未找到对应的函数,在hook的时候需要指定函数的参数形式。

findAndHookMethod(TARGET_CLASS, param.classLoader, TARGET_FUNCTION, new XC_MethodHook(){...});

因为onCreate是有参数的,所以正确的调用方式是:

findAndHookMethod(TARGET_CLASS, param.classLoader, TARGET_FUNCTION, Bundle.class, new XC_MethodHook(){...});

xposed与libsubstrate的对比:

测试都需要经过反复的重启,此次xposed的测试demo也是如是,即便写的只是java层代码,也需要每次改动都要重启手机。而之前用libsubstrate实现的底层hook,在每次改动JNI代码后也是需要重启手机的。

代码量方面还是xposed框架下少很多,而且都是java层代码,写起来速度也快不容易出错,测试的时间自然也少很多。

文档信息

Search

    Table of Contents