# EdXposed例子

## 背景

"纸上得来终觉浅，绝知此事要躬行"，尤其看代码的时候经常觉得都懂了，自己写得时候全忘了。所以最好的记忆还是自己写个例子。所以学习EdXposed的时候写了个例子把整个流程走一遍。

## 依赖

需要完成安装Magisk和EdXposed

## 例子说明

EdXposed完全Follow Xposed的API，所以整个开发流程完全一致。 从本质上来讲，EdXposed 模块也是一个 Android 程序。但与普通程序不同的是，想要让写出的Android程序成为一个Xposed 模块，要额外多完成以下四个硬性任务：

1. 让手机上的xposed框架知道我们安装的这个程序是个xposed模块。
2. 模块里要包含有xposed的API的jar包，以实现下一步的hook操作。
3. 这个模块里面要有对目标程序进行hook操作的方法。
4. 要让手机上的xposed框架知道，我们编写的xposed模块中，哪一个方法是实现hook操作的。

对应上面的四个步骤我们需要做的修改有：

1. AndroidManifest.xml
2. XposedBridgeApi-xx.jar 与 build.gradle
3. 实现hook操作的具体代码
4. xposed\_Init

### 目标APP

首先我们实现一个目标App， App的功能很简单，只有一个button，点击button返回一个字符串

```java
package com.wq.xposedtargetapp;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import java.util.concurrent.ExecutionException;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button button = (Button) findViewById(R.id.button);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(MainActivity.this, getIpAddr(), Toast.LENGTH_SHORT).show();
            }
        });
    }

    public String getIpAddr() {
        return "192.168.180.123";
    }
}
```

![目标APP Hook前](https://1819138102-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MM7mD6Z7_ShtqqizKx0%2Fsync%2Fe4555bda5f7edabab7f5c8c070259633418199d3.png?generation=1608902803312436\&alt=media)

### Hook App

另外实现一个Hook App用于替换点击按钮后的返回值，我们按照上面说的四步一步一步修改：

#### AndroidManifest.xml

```markup
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.wq.xpasedmodule">

    <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">
        <!--表示当前app是一个xposedmodule-->
        <meta-data
            android:name="xposedmodule"
            android:value="true" />
        <!--当前module的描述-->
        <meta-data
            android:name="xposeddescription"
            android:value="edxposed test" />
        <!--edxposed 最低版本-->
        <meta-data
            android:name="xposedminversion"
            android:value="53" />
        <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>
```

当修改完`AndroidManifest.xml`后, EdXposedManager就会显示当前模块

![EdXposed Manager](https://1819138102-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MM7mD6Z7_ShtqqizKx0%2Fsync%2F8971032d801ee9a9819a1b8789835325eb3bc31b.png?generation=1608902801563452\&alt=media)

#### Xposed API build.gradle修改

[Xposed API](https://api.xposed.info/reference/packages.html)说明

```java
dependencies {
    //指定Xposed API
    compileOnly 'de.robv.android.xposed:api:82'
    compileOnly 'de.robv.android.xposed:api:82:sources'

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

}
```

#### 实现Hook具体实现代码

```java
package com.wq.xpasedmodule;

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 EdXposedHookTest implements IXposedHookLoadPackage {
    @Override
    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
        // 指定Hook目标app
        if (loadPackageParam.packageName.equals("com.wq.xposedtargetapp")) {
            // 指定Hook的目标类
            Class clazz = loadPackageParam.classLoader.loadClass("com.wq.xposedtargetapp.MainActivity");
            // 查找Hook的方法
            XposedHelpers.findAndHookMethod(clazz, "getIpAddr", new XC_MethodHook() {
                // 具体执行逻辑
                @Override
                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                    super.beforeHookedMethod(param);
                }

                @Override
                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    // 替换getIpAddr方法的返回值
                    param.setResult("111.111.111.111");
                }
            });
        }
    }
}
```

#### xposed\_init指定Hook模块

```
com.wq.xpasedmodule.EdXposedHookTest
```

## Hook 结果

![目标APP Hook后](https://1819138102-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MM7mD6Z7_ShtqqizKx0%2Fsync%2F7263862722749544d95746a2b56f90137335d3b7.png?generation=1608902802238262\&alt=media)

**上面的例子可以在**[**Github**](https://github.com/sphantix/EdXposed-Test)**上找到**
