xposed是什么?

一个很牛逼的框架,可以在不修改APK的情况下影响程序的运行,比如:

  • 直接把APP的界面改成自己想要的样
  • 去掉界面里不喜欢的东西,
  • 自动抢红包
  • 消息防撤回
  • 步数修改等等

Xposed的工作原理

在开始修改之前,你应该大致了解Xposed如何工作(如果你觉得太无聊,你可以跳过这一部分)。方法如下:

有一个叫做“Zygote”的过程。从它的名字(中文含义——受精卵)这是Android运行时的核心。每个应用程序都作为它的副本(“fork”)启动。/init.rc手机启动时,脚本会启动此过程。进程开始完成/system/bin/app_process,加载所需的类并调用初始化方法。

这就是Xposed发挥作用的地方。安装框架时,会将扩展的app_process可执行文件复制到/system/bin。这个扩展的app_process就会把XposedBridge.jar加载到运行时环境,这样我们就可以在虚拟机启动之前,甚至是在Zygote的main方法被执行之前做一些爱做的事(捂脸,其实就是加载插件)。此时我们的插件被执行,就是Zygote进程的一部分,所以可以直接获取到应用的上下文Context,然后做很多超出想象的事情——对于任何一个app ,我们都可以hook或者替换掉其中的类或方法或对象。再加上已经root。我们可以为所欲为了。

jar位于,/data/data/de.robv.android.xposed.installer/bin/XposedBridge.jar其源代码可在此处找到。查看类XposedBridge,您可以看到该main方法。这就是我上面写的内容,这个过程在一开始就被调用了。在那里进行了一些初始化,并且还加载了模块(稍后我将回到模块加载)。

方法挂钩/更换

真正创造Xposed力量的是“钩”方法调用的可能性。通过反编译APK进行修改时,可以直接在任意位置插入/更改命令。但是,您需要在之后重新编译/签署APK,并且您只能分发整个包。使用可以放置Xposed的钩子,你不能修改方法内的代码(不可能清楚地定义你想在哪个地方做什么样的改变)。相反,您可以在方法之前和之后注入自己的代码,这是Java中可以清楚解决的最小单元。

XposedBridge有一个私有的本机方法hookMethodNative。此方法也在扩展中实现app_process。它会将方法类型更改为“native”,并将方法实现链接到其自己的本机通用方法。这意味着每次调用hooked方法时,都会调用泛型方法,而不会让调用者知道它。在此方法中,handleHookedMethod调用XposedBridge中的方法,将参数传递给方法调用,this引用等。然后,此方法负责调用已为此方法调用注册的回调。这些可以更改调用的参数,更改实例/静态变量,调用其他方法,对结果执行某些操作...或者跳过任何内容。它非常灵活。

安装:

安装XposedInstalle(Xposed安装程序)

XposedInstalle.apk下载链接(安卓5.0以下不用这个,自己找去吧):
下载地址,论坛下面有下载地址

开发模块

一、创建Xposed模块

首先需要知道,Xposed模块是以APK的格式提供的,本身也是需要安装到手机上的,也像普通应用一样可以启动,只是因为APK中包含了一些声明,被Xposed框架检测到了,所以同时也可以以Xposed模块的方式来进行hook操作。那么这些声明是什么呢?
在AndroidManifest.xml中添加下面的声明,
meta-data中的内容分别用于声明是否为插件,插件的描述和兼容的最低Xposed版本。加入3个meta-data就好了。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.sxkj_mg.xposeddome">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <meta-data
            android:name="xposedmodule"
            android:value="true"/>
        <meta-data
            android:name="xposeddescription"
            android:value="我就是个简单的Xposed Demo"/>
        <meta-data
            android:name="xposedminversion"
            android:value="83"/>
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

二、添加依赖

apply plugin: 'com.android.application'

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "com.example.sxkj_mg.xposeddome"
        minSdkVersion 23
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    
//    主要是下面这两行,再下面的是自动生成的
    compileOnly 'de.robv.android.xposed:api:82'
    //如果需要引入文档,方便查看的话
    compileOnly 'de.robv.android.xposed:api:82:sources'
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}

三、创建xposed_init

xposed Android13 安装 xposed如何安装_sed

xposed_init创建位置

在图片位置创建xposed_init,写入代码为:

# 我的是这个,你们根据自己的路径改,最后一个是文件名,一会创建这个文件
com.example.sxkj_mg.xposeddome.HookModule

修改MainActivity.java

xposed Android13 安装 xposed如何安装_xposed Android13 安装_02

修改id

package com.example.sxkj_mg.xposeddome;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    private TextView tv;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv=findViewById(R.id.tv);
        tv.setText("我是渣渣辉");
    }
}

编写HookModule

package com.example.sxkj_mg.xposeddome;

import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

import java.lang.reflect.Field;

import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
public class HookModule implements IXposedHookLoadPackage {
    public static final String TAG = "MyHook";
    @Override
    public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
        Log.d(TAG, "重启手机后,我执行了,说明这个 Xposed 模块生效了");
        if (loadPackageParam.packageName.equals("com.example.sxkj_mg.xposeddome")){
            Log.d(TAG, "进入到这个app了");
            XposedHelpers.findAndHookMethod("com.example.sxkj_mg.xposeddome.MainActivity",loadPackageParam.classLoader,
                    "onCreate", Bundle.class,new XC_MethodHook(){

                        @Override
                        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                             Log.d(TAG, "这里是hook方法之前");
                            super.beforeHookedMethod(param);

                        }
                        @Override
                        protected void afterHookedMethod(MethodHookParam param) throws Throwable{
                            Log.d(TAG, "这个是hook方法之后");
                            Class c= loadPackageParam.classLoader.loadClass("com.example.sxkj_mg.xposeddome.MainActivity");
                            Field field = c.getDeclaredField("tv");
                            field.setAccessible(true);
                            Log.d(TAG, "这次到这里了2");
                            TextView tv = (TextView) field.get(param.thisObject);
                            tv.setText("王境泽");
                        }
                    });
        }
    }
}

此时运行一下,就装到手机上了。(前提USB连接手机)
手机打开,是不是还是渣渣辉,然后XposedInstalle启用模块后,手机重启,就显示王境泽了。

API介绍

  • IXposedHookLoadPackage接口:App被加载的时候调用,用于App应用的Hook
    回调方法是:handleLoadPackage(final XC_LoadPackage.LoadPackageParam lpparam)
  • XC_LoadPackage.LoadPackageParam:包含与正在加载的应用程序的有关信息。
  • XposedHelpers.findAndHookMethod(要Hook的类,classLoader,方法名,参数,回调对象)
  • Hook一个方法的时候使用,回调对象XC_MethodHook()需重写两个方法
  • beforeHookedMethod(MethodHookParam param):方法调用前执行
  • afterHookedMethod(MethodHookParam param) 方法调用后执行
  • 注:可以调用param.setResult()设置方法的返回值!
  • MethodHookParam:包含与调用方法有关的信息
    比较关注的是这个thisObject,代表调用该方法的对象实例,如果是静态方法
    的话,返回一个Null,比如这里调用onCreate()方法的是MainActivity,获得
    的自然是MainActivity实例。

接着是获取成员变量,分为私有与非私有变量,非私有直接调用下述方法
即可获得class

Class c = lpparam.classLoader.loadClass("com.coderpig.cpwechatxposed.MainActivity");
Field field = c.getField("tv");

如果是私有,则需要先设置访问权限(setAccessible)

Class c = lpparam.classLoader.loadClass("com.coderpig.cpwechatxposed.MainActivity");
Field field = c.getDeclaredField("tv");
field.setAccessible(true);

接着调用获得该对象

TextView tv = (TextView) field.get(param.thisObject);
tv.setText("贪玩难约");

代码外内容

  • IXposedHookZygoteInit:在Zygote启动时调用,用于系统服务的Hook
    回调方法initZygote()
  • IXposedHookInitPackageResources:在资源布局初始化时会回被执行(inflate方法)
    回调方法:handleInitPackageResources(XC_InitPackageResources.InitPackageResourcesParam resparam)
  • InitPackageResourcesParam包含两个参数,包名和XResource(资源相关)
    有了这个XResource对象,就可以拿到布局资源树了,通过重写hookLayout方法,
  • LayoutInflatedParam,里面这个view就是布局资源树了,你可以拿到遍历,拿到某个特定控件,然后做一些骚操作。
  • XposeHelpers提供了一些辅助方法
  • callMethod(Object obj,String methodName, Object… args):在APP中调用特定方法; 参数依次是:调用方法的所在类,调用方法名,方法参数
  • findClass(String className,ClassLoader classLoader):获取class类实例
    参数依次是类名,类加载器
  • findMethodExact:通过反射查找类的成员方法(可setAccessible(true)设置非私有)
  • findConstructorExact:通过反射查找构造函数(同样可设置可访问下性)
  • findAndHookXXX:查找并Hook
  • setXxx:通过反射设置对象数据成员的值
  • setStaticXxx:通过反射设置静态变量的值
  • XposedBridge.log(“日志内容”):输入日志和写入到/data/xposed/debug.log
    Xposed Installer日志那里可以看到!

内部类:通过$符号链接内部类

只能Hook方法与构造方法,不能Hook接口和抽象方法

通过这些api是不是大概清楚Xposed能干啥了,除了Xposed,还有其他的框架可以干这些事比如下面:

Magisk框架,Xposed框架比较,VirtualXposed框架

框架

Magisk

Xposed

Vxp

平台

android5.0~8.1

android4.4以下,android5.0 ~ 8.1,但是8.0~8.1稳定版还没出来,出来的beta版本

android5.0~9.0

模拟器,真机支持情况

都支持

都支持

不支持 x86,也就是不支持模拟器

更新情况

更新快一直在更新

已经停更了

框架一直在更新

稳定性

模块少,但是可以装systemless版Xposed模块来弥补

最稳定、模块最多

不会变砖、模块少

激活模块

必须重启激活模块

必须重启激活模块

支持免重启手机激活模块

root

内置ROOT

需要root

免root

hook

支持

支持

暂不支持资源以及系统api的HOOK

使用必须将需要 hook 的 APP 和模块 APP 安装到VirtualXposed

github地址

Magisk

Xposed

VirtualXposed

原理

对系统侵入较少,仅修改boot.img,同时能够对系统隐藏自身存在,支持OTA升级,可以实现Multirom多系统等功能

Xposed框架的原理是通过替换/system/bin/app_process程序控制zygote进程,使得app_process在启动过程中会加载XposedBridge.jar这个jar包,从而完成对Zygote进程及其创建的Dalvik或者art虚拟机的劫持

它去启动别的App,在启动过程中通过 epic Hook本进程,从而控制被启动的App