Xposed学习--工作原理

Xposed工作原理

zygote进程

Android运行的心脏。每一个应用都是这个进程的fork,都从这个进程的一个fork开始。

Android系统启动时,zygote进程被/init.rc脚本启动。当/system/bin/app_process加载完成时,zygote启动完成。/system/bin/app_process负责加载必要的类和调用初始化方法。

Xposed

安装xposed后,会复制一份扩展后的app_process到/system/bin,这个app_process会添加一个额外的jar到classpath并且在特定的地方调用方法。

举个例子,当虚拟机创建后,在zygote的main函数执行之前,或者main函数中,xposed时zygote的一部分并且可以控制上下文。

jar位于/data/data/de.robv.android.xposed.installer/bin/XposedBridge.jar

使用Xposed可以hook方法,在方法前后注入代码。

XposedBridge还有native方法hookMethodNative,会将某个方法改成native并且把方法link到自己的native方法。

Xposed入门示例

  1. 新建Android Studio工程,选择空白activity

  2. 下载xposedAPI,放在工程的libs目录下

  3. 改动build.gradle(:app),如果有implementation fileTree()就换成compileOnly,如果没有就添加一行compileOnly

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    apply plugin: 'com.android.application'

    android {
    compileSdkVersion 30
    buildToolsVersion "30.0.2"

    defaultConfig {
    applicationId "com.example.redclocknew"
    minSdkVersion 20
    targetSdkVersion 30
    versionCode 1
    versionName "1.0"

    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
    release {
    minifyEnabled false
    proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
    }
    }

    dependencies {
    // implementation fileTree(dir: "libs", include: ["*.jar"])
    compileOnly fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.2.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

    }
  4. 编辑AndroidManifest.xml,添加一系列meta-data,比如我的是这样

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.redclocknew">

    <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="Easy example which makes the status bar clock red and adds a smiley" />
    <meta-data
    android:name="xposedminversion"
    android:value="53" />
    </application>

    </manifest>
  5. 创建类,添加代码,示例如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    package com.example.redclocknew;

    import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
    import android.graphics.Color;
    import android.widget.TextView;
    import de.robv.android.xposed.IXposedHookLoadPackage;
    import de.robv.android.xposed.XC_MethodHook;
    import de.robv.android.xposed.XposedBridge;
    import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;

    public class RedClock implements IXposedHookLoadPackage {
    public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable {
    if (!lpparam.packageName.equals("com.android.systemui"))
    return;
    // XposedBridge.log("Loaded app: " + lpparam.packageName);
    findAndHookMethod("com.android.systemui.statusbar.policy.Clock", lpparam.classLoader, "updateClock", new XC_MethodHook() {
    @Override
    protected void afterHookedMethod(MethodHookParam param) throws Throwable {

    TextView tv = (TextView) param.thisObject;
    String text = tv.getText().toString();
    tv.setText(text + "🍖");
    tv.setTextColor(Color.RED);
    if(text!=null&&text.length()>0){
    char c = text.charAt(text.length()-1);
    if((c&1)!=0){
    tv.setText(text+"🍭");
    tv.setTextColor(Color.GREEN);
    }
    }
    }
    });
    }
    }
  6. 添加assets/xposed_init文本文件,在文件中添加你的完整类名,比如我的是

    1
    com.example.redclocknew.RedClock
  7. 工具栏build->build apk(s)

  8. 找到对应的apk,使用

    1
    adb install 文件名

    进行安装

  9. 重启手机,愉快体验

自定义Xposed模块

根据Xposed实现原理,大概的替换思路有了:找到要hook的方法,然后利用xposedhook该方法并且实施自己要做的动作。

找源码有两种方式,反编译和阅读源码(如果有)

相关资料

  1. apktool

  2. jd-gui or luyten

  3. dex2java

  4. 下载android源码 or here

  5. 在线查看android源码

  6. 获取系统中指定App安装包的方法

    1. 获取栈顶activity(当前界面所属activity)

      1
      2
      3
      adb shell dumpsys window | findstr mCurrentFocus
      # or
      adb shell dumpsys activity | findstr mFucusedActivity
    2. 获取app文件安装路径

      1
      adb shell pm path com.test
      • /system/app rom附带软件
      • /system/priv-app 手机厂商定制软件
      • /data/app 用户安装软件
    3. 使用pull命令获取应用

      1
      adb pull path_to_apk path_to_store

      使用push命令可以存储应用

      1
      adb push path_to_file path_on_phone

找到可以修改的点和相关函数,注意xposed只能hook函数。

使用下面的代码确定相关app是否存在

1
2
3
4
5
6
public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable {
if (!lpparam.packageName.equals("com.android.systemui"))
return;

XposedBridge.log("we are in SystemUI!");
}

使用findAndHook方法hook方法→_→

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package de.robv.android.xposed.mods.tutorial;

import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;

public class Tutorial implements IXposedHookLoadPackage {
public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {
if (!lpparam.packageName.equals("com.android.systemui"))
return;

findAndHookMethod("com.android.systemui.statusbar.policy.Clock", lpparam.classLoader, "updateClock", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
// this will be called before the clock was updated by the original method
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
// this will be called after the clock was updated by the original method
}
});
}
}

参考资料

[1] Xposed turtorial

[2] Android系统启动流程之Zygote启动

[3] adb获取手机中已安装app的按转包