陈建华的博客
专注web开发
Xposed中文学习内容
2017-02-09 18:01:25   阅读694次

原理

Xposed替换了/system/bin/app_process可执行文件,在启动Zygote时加载额外的jar文件(/data/data/de.robv.android.xposed.installer/bin/XposedBridge.jar),并执行一些初始化操作(执行XposedBridge的main方法)。然后我们就可以在这个Zygote上下文中进行某些hook操作。

安装XposedInstaller

  1. 下载XposedInstaller

  2. 安装。安装完会提示重启手机。如果是虚拟机要选择软重启,真实手机要选择硬重启,千万不要搞反。 注:手机启动会比较慢,但如果手机重启时卡在欢迎界面,可以通过连续按电源键来跳过Xposed加载。

开发Xposed项目

1. Xposed module基础

Xposed程序称为module,它是包含一些特殊元数据和文件的android项目。建议使用android SDK 4.0.3 (API 15)进行开发。 一个Xposed module项目结构如下:

26161501_VpYj.png

其中/assets/xposed_init文件指定了module的入口类,开发者要在这个类中实现需要的hook代码。这个类要实现特定的XposedBridge接口。 /assets/xposed_init内容:

com.example.xmodule.car.XModule

AndroidMannifest.xml内容:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.Xmodule" android:versionName="1.0" android:versionCode="1">
    <uses-sdk android:minSdkVersion="15"/>
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <meta-data
                android:name="xposedmodule"
                android:value="true"/>
        <meta-data
                android:name="xposeddescription"
                android:value="Xposed模块示例"/>
        <meta-data
                android:name="xposedminversion"
                android:value="54"/><!-- 对应的XposedBridge版本号 -->
    </application>
</manifest>

注意: Xposed module项目在开发时导入了XposedBridge jar文件,但编译时要去掉,否则因为在Zygote中已经导入该jar文件而导致类冲突。

2. 一个简单的示例

开发一个xposed module来修改手机imei,imsi。

  1. 新建一个普通的android项目,添加依赖XposedBridge.jar(源码下载),scope为Provided。

  2. 在AndroidManifest.xml中添加Xposed元数据

  3. 编写 /assets/xposed_init文件

  4. 编写hook代码

public class XModule implements IXposedHookLoadPackage {
    @Override
    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
        //只hook测试app
        if (lpparam.packageName.equals("com.example.test")) {
            XposedHelpers.findAndHookMethod(TelephonyManager.class, "getDeviceId", new XC_MethodReplacement() {
                @Override
                protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
                    return "this is imei";
                }
            });
            XposedHelpers.findAndHookMethod(TelephonyManager.class, "getSubscriberId", new XC_MethodReplacement() {
                @Override
                protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
                    return "this is imsi";
                }
            });
        }
    }
}

3. 运行module

module的安装与普通app一样,如果没有编写Activity,模块不会显示在launcher中。但可以在XposedInstaller应用中看到安装的module;另外module的启用与停止都需要在XposedInstaller中进行配置,配置完还需要重启才能生效。

4. 运行结果

test app的Activity代码

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    final TextView tv1 = (TextView) findViewById(R.id.tv1);
    final TextView tv2 = (TextView) findViewById(R.id.tv2);
    TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
    tv1.setText("imei: "+tm.getDeviceId());
    tv2.setText("imsi: "+tm.getSubscriberId());
}

运行结果如下:

26161625_APMW.png

参考链接


深入Module

模块入口

IXposedMod接口定义了Xposed模块。但用户不能直接实现这个接口(具体原因参见XposedBridge的main方法),只能实现作者定义好的四个接口,每个接口用于特定的用途:

  • public interface IXposedHookInitPackageResources extends IXposedMod 用于修改app的资源文件

  • public interface IXposedHookLoadPackage extends IXposedMod 用于hook应用的代码

  • public interface IXposedHookZygoteInit extends IXposedMod 在Zygote初始化时执行

  • public interface IXposedHookCmdInit extends IXposedMod 用于hook基于java的命令行工具(例如pm)。它需要app_process 55版本,还需要在XposedInstaller的data目录下创建一个特殊文件(conf/enable_for_tools)

类加载器

01105336_P3Et.png

从上图可以发现Xposed Module的类加载器与要运行Apk的类加载器都继承自PathClassLoader,但它们之间相互独立,每个ClassLoader只用于加载某一特定的apk(dex),所以在Xposed module中如果要加载apk中的类需要用原应用的ApkClassLoader进行加载。

Hook过程

  1. Find: 通过特定的类加载器加载要hook的类,通过反射找到被hook的成员。

  2. Hook: 被hook成员调用前后执行特定的回调方法。

Find

工具类XposedHelpers提供了一些工具方法来简化find过程;XposedBridge的hook*方法用于处理hook并执行回调。

XposedHelpers静态方法-
findClass使用classLoader加载class
findField*通过反射查找类的数据成员并设置可访问性(setAccessible(true))
findMethod*通过反射查找类的成员函数并设置可访问性
findConstructor*通过反射查找类的构造函数并设置可访问性
setStatic*通过反射设置类静态变量的值
set*通过反射设置对象数据成员的值
findAndHook*查找并hook

Hook

XC_MethodHook中定义了回调方法: beforeHookedMethod(MethodHookParam param):被hook方法调用前执行,调用param.setResult可以跳过被hook的方法。 afterHookedMethod(MethodHookParam param) : 被hook方法调用后执行,调用param.setResult更改被hook方法的执行结果。

XC_MethodReplacement继承自XC_MethodHook,通过在beforeHookedMethod中调用param.setResult实现了方法的替换。

@Override
protected final void beforeHookedMethod(MethodHookParam param) throws Throwable {
    try {
        Object result = replaceHookedMethod(param);
        param.setResult(result);
    } catch (Throwable t) {
        param.setThrowable(t);
    }
}

实例

目标

笔者在网上找到了一个有广告的小游戏。我们的目标是通过xposed将广告去掉,测试的应用包可以在test.zip下载

08143020_29lo.png

step1

首先要找到目标方法,通过逆向找到主要的广告类com.umonistudio.utils.Ads.AdsAdmob,它有两个方法来显示广告public void showBannerAd(final boolean paramBoolean) 和 public void showFullScreenAd(final Boolean paramBoolean),其主要代码如下:

08143124_J5lU.png

因为方法完成了广告的请求与显示,所以只要hook这两个方法就可以了(其中传入PluginWrapper.runOnMainThread方法的Runnable对象就是用于加载并显示广告的线程类)。

step2

接下来就是hook了,代码挺简单,只要让原来的方法什么都不干就行。

public class XModule implements IXposedHookLoadPackage {
    @Override
    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
        if (lpparam.packageName.equals("com.umonistudio.tile")) {
            Class adClz = lpparam.classLoader.loadClass("com.umonistudio.utils.Ads.AdsAdmob");//需要使用ApkClassLoader加载apk内的类
            XposedBridge.log("已加载广告类" + adClz.getSimpleName());
            XposedHelpers.findAndHookMethod(adClz, "showBannerAd", boolean.class, new XC_MethodReplacement() {
                @Override
                protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
                    XposedBridge.log("Banner广告已屏蔽");
                    return null;
                }
            });
            XposedHelpers.findAndHookMethod(adClz, "showFullScreenAd", Boolean.class, new XC_MethodReplacement() {
                @Override
                protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
                    XposedBridge.log("全屏广告已屏蔽");
                    return null;
                }
            });
        }
    }
}

效果很不错,可以在xposedInstaller的日志中看到XposedBridge.log()输出的日志:

08143405_yqjO.png

附录

  1. Xposed Helpers 

  2. Xposed Replacing resources






-----------------------------------------------------
转载请注明来源此处
原地址:#

-----网友评论----
暂无评论
-----发表评论----
微网聚博客乐园 ©2014 blog.mn886.net 鲁ICP备14012923号   网站导航