> ## Content Index
> Fetch the complete content index at: https://iiong.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Android Xposed 模块入门
- URL: https://iiong.com/getting-started-with-android-xposed-modules/
- Published: 2024-07-02T15:29:41.000Z
- Updated: 2026-06-22T15:24:38.000Z
- Description: 老早以前就想搞 Xposed 开发了，借此机会就入门了，不过写此教程是记录我学习下来的笔记，记录软件逆向开发过程。
- Author: 淮城一只猫
- Tags: 编程技术

### 前言

老早以前就想搞 `Xposed` 开发了，一直没有机会学习用上，再加上 `iPhone` 是主要设备，这几年一直没有关注安卓社区，等手中有个安卓备用机后发现现在玩机社区不如过去了，生态越来越封闭，不过这不影响我继续学习 `Xposed` 模块开发，目前主流 `Xposed` 框架环境是 [LSPosed Framework](https://github.com/LSPosed/LSPosed?ref=iiong.com) ，支持 `A8.1 ~ A14` ，不过开发者好像放弃维护了，不知道后续怎么样就不清楚了。`Xposed` 很强大，强大到在安卓几乎什么都能干，系统美化、软件破解、去广告等诸多操作，这些操作都不会影响原来应用下进行运行，所以说以前玩机最大的快乐来自 `Xposed`。

我想写 `Xposed` 模块缘由是女朋友一直用醒图，网上对最新醒图破解资源很少，再加上老早以前就想学逆向开发，借此机会就入门了，不过写此教程是记录我学习下来的笔记，不记录醒图逆向开发过程。

### 准备

想要开发 `Xposed` 模块需要做到：

- 一台安装好 `Xposed` 框架环境的手机（`EdXposed`, `LSPosed` 等）
- 装有 `AS` 编辑器（`Android Studio`）且有 `JDK` 环境的电脑
- 查看 `apk` 的反编译工具，例如：[jd-gui](https://github.com/java-decompiler/jd-gui?ref=iiong.com), [JADX](https://github.com/skylot/jadx?ref=iiong.com) 等诸多
- 查看 `apk` 的布局应用：`MT 管理器`, `开发助手专业版`
- 熟悉的且掌握 [Java 反射](https://iiong.com/java-reflection-learning-notes/)

本篇不研究怎么破解醒图会员过程，记录在我手机下拉全局搜索，在输入框在联网的情况下会有热点广告词轮播，我的目的将把这个热点删除且还原原来的搜索占位词：

![90521719911648_.pic](https://cdn.iiong.com/2024/07/90521719911648_.pic.jpg)

### 创建项目

打开 `AS` 编辑器，选择没有 `No Activity` , 语言我选择 `kotlin` , `SDK` 选择 `API 26` 。

创建好项目后引入 `Xposed` 库，需在在 `Gradle Script` 下的 `settings.gradle.kts` 添加：

```kotlin
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        maven { url=uri("https://api.xposed.info") }
    }
}

```

之后在 `app` 目录下的 `build.gradle.kts` 添加 `Xposed` 声明：

```kotlin
dependencies {
    implementation(libs.androidx.core.ktx)
    implementation(libs.androidx.appcompat)
    implementation(libs.material)
    testImplementation(libs.junit)
    androidTestImplementation(libs.androidx.junit)
    androidTestImplementation(libs.androidx.espresso.core)
    compileOnly(libs.api)
}

```

如果编译的文件很大的话可以删除里面的主题文件即可：

- `dependencies`保留 只保留：`compileOnly(libs.api)`
- 移除 `src/res/values/themes.xml` 里面的主题，注意还有个夜间模式，在 `values-night` 文件夹下
- 移除 `AndroidManifest.xml` 文件里 `<application ... />` 中的 `android:theme="xxx"` 的一行

之后还需要在 `src/res/values/` 文件夹新建 `arrays.xml` 文件，来声明 `LSPosed` 作用域：

```xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="xposedscope" >
        <!-- 这里填写模块的作用域应用的包名，可以填多个。 -->
        <item>com.xt.app1</item>
        <item>com.xt.app2</item>
    </string-array>
</resources>

```

然后在编辑器运行按钮编辑一下启动配置，勾选 `Always install with package manager` 并且将 `Launch Options` 改成 `Nothing` 即可。

### 声明模块

创建好项目后需要声明它是一个 `Xposed` 模块，好让框架发现它，这样才能激活模块 到 `AndroidManifest.xml` 文件里声明如下：

```xml
<application ... > 
        <!-- 是否为Xposed模块 -->
        <meta-data
            android:name="xposedmodule"
            android:value="true"/>
        <!-- 模块的简介（在框架中显示） -->
        <meta-data
            android:name="xposeddescription"
            android:value="我是Xposed模块简介" />
        <!-- 模块最低支持的Api版本 一般填54即可 -->
        <meta-data 
            android:name="xposedminversion"     
            android:value="54"/>
        <!-- 模块作用域 -->
        <meta-data
            android:name="xposedscope"
            android:resource="@array/xposedscope"/>
</appication>

```

然后在 `src/main` 目录下创建一个文件夹名叫 `assets`，并且创建一个文件叫 `xposed_init`，内容写模块的入口类名，例如像我的：

```kotlin
com.iiong.xtcracked.MainHook

```

### 编写模块

现在在搜索要去广告，得先知道软件包名：

![Screenshot_2024-07-02-18-38-53-94_fc704e6b13c4fb26bf5e411f75da84f2](https://cdn.iiong.com/2024/07/Screenshot_2024-07-02-18-38-53-94_fc704e6b13c4fb26bf5e411f75da84f2.jpg)

根据 `IXposedHookLoadPackage` 接口操作如下：

```kotlin
package com.iiong.xtcracked

import de.robv.android.xposed.IXposedHookLoadPackage
import de.robv.android.xposed.XposedBridge
import de.robv.android.xposed.callbacks.XC_LoadPackage

class MainHook : IXposedHookLoadPackage {
    override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
        // ColorOS 全局搜索
        if (lpparam.packageName == "com.heytap.quicksearchbox") {
            XposedBridge.log("找到应用：" + lpparam.packageName)
            // hook it
            qsbHook(lpparam)
        }
    }

    private fun qsbHook(lpparam: XC_LoadPackage.LoadPackageParam) {
      // 具体业务
    }
}

```

然后在作用域声明下包名，打包后调试在手机上可以激活了。

### 反编译

如果想写具体的业务需要对 `apk` 进行反编译去读代码，找到关键点去修改即可。将搜索从手机导出 `apk` 后在电脑端使用 `JADX` 打开 `apk` 反编译。

每次打开搜索的时候，发现它的默认占位词是：`搜索本机和全网内容` ：

![Screenshot_2024-07-02-18-46-43-38_d97e70df473fd2e37898d35a3e82c2a2](https://cdn.iiong.com/2024/07/Screenshot_2024-07-02-18-46-43-38_d97e70df473fd2e37898d35a3e82c2a2.jpg)

所以利用这个作为突破口，在 `JADA` 搜索这个关键词后发现确实有眉目：

![iShot_2024-07-02_18.58.19](https://cdn.iiong.com/2024/07/iShot_2024-07-02_18.58.19.png)

看了一下其中的 `dark_word` 是用于桌面组件的，寻思我也用不到桌面小组件就不管它了，就从第三个 `dock_dark_word_default_all` 进入：

![iShot_2024-07-02_19.12.38](https://cdn.iiong.com/2024/07/iShot_2024-07-02_19.12.38.png)

入眼就是所谓的判断了，所以切入点直接走这里，直接将 `if` 判断 给 `true` 就不会出现热点轮播：

```kotlin
XposedHelpers.findAndHookMethod(
    "com.heytap.quicksearchbox.common.helper.EnterSourceManager",
    lpparam.classLoader,
    "c",
    object : XC_MethodHook() {
        override fun beforeHookedMethod(param: MethodHookParam) {}

        override fun afterHookedMethod(param: MethodHookParam) {
        XposedBridge.log("TestHookLog 1b：$param")
        param.result = true
    }
})

```

然后再打包更新确实有效果了，后续又找到一个入口方向，大概是这样的：

![未命名](https://cdn.iiong.com/2024/07/%E6%9C%AA%E5%91%BD%E5%90%8D.png)

直觉给我就是给占位框输入广告词，就用日志输出：

```kotlin
val darkWordSetClass = lpparam.classLoader.loadClass("com.heytap.quicksearchbox.core.db.entity.DarkWordSet")
XposedHelpers.findAndHookMethod(
    "com.heytap.quicksearchbox.ui.widget.searchbar.BaseSearchBar",
    lpparam.classLoader,
    "F0",
    darkWordSetClass, String::class.java, String::class.java, Boolean::class.java, Boolean::class.java, String::class.java, Boolean::class.java, object : XC_MethodHook() {
    override fun beforeHookedMethod(param: MethodHookParam) {
        super.beforeHookedMethod(param)
        XposedBridge.log("TestHookLog 1b for args1：${param.args[0]}")
        XposedBridge.log("TestHookLog 1b for args2：${param.args[1]}")
        XposedBridge.log("TestHookLog 1b for args3：${param.args[2]}")
        XposedBridge.log("TestHookLog 1b for args4：${param.args[3]}")
        XposedBridge.log("TestHookLog 1b for args5：${param.args[4]}")
        XposedBridge.log("TestHookLog 1b for args6：${param.args[5]}")
        XposedBridge.log("TestHookLog 1b for args7：${param.args[6]}")
    }

    override fun afterHookedMethod(param: MethodHookParam) {
        super.afterHookedMethod(param)
        // XposedBridge.log("TestHookLog 1a for：${param.result}")
    }
})

```

从上面的 `args1` 和 `args2` 打印知道传入广告占位的广告词，下面就简单的：

```kotlin
val darkWordSetClass = lpparam.classLoader.loadClass("com.heytap.quicksearchbox.core.db.entity.DarkWordSet")
XposedHelpers.findAndHookMethod(
    "com.heytap.quicksearchbox.ui.widget.searchbar.BaseSearchBar",
    lpparam.classLoader,
    "F0",
    darkWordSetClass, String::class.java, String::class.java, Boolean::class.java, Boolean::class.java, String::class.java, Boolean::class.java, object : XC_MethodHook() {
    override fun beforeHookedMethod(param: MethodHookParam) {
        super.beforeHookedMethod(param)
        param.args[0] = null
        param.args[1] = "请输入本机内容和全网内容"
    }

    override fun afterHookedMethod(param: MethodHookParam) {
        super.afterHookedMethod(param)
        // XposedBridge.log("TestHookLog 1a for：${param.result}")
    }
})

```

打包后确实生效了。

### Hook 函数的复杂参数

例如会遇到要 `hook` 的函数：

```java
public static boolean fun1(String[][] strAry, Map mp1, Map<String,String> mp2, Map<Integer, String> mp3, ArrayList<String> al1, ArrayList<Integer> al2, ArgClass ac)

```

可以利用 `getDeclaredMethods` 方法打印出这个函数的参数列表输出如下：

```java
([[Ljava/lang/String;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;Ljava/util/ArrayList;Ljava/util/ArrayList;Laqcxbom/xposedhooktarget/ArgClass;)

```

梳理下如下：

```java
String[][] ==> [[Ljava/lang/String;
Map 数组不论何种形式 ==> Ljava/util/Map;
ArrayList 无论何种形式 ==> Ljava/util/ArrayList;
ArgClass 自定义类给个全路径的事==> Laqcxbom/xposedhooktarget/ArgClass;

```

在 `Xposed` 项目下可以尝试这么写：

```kotlin
package com.iiong.xtcracked

import android.util.Log
import de.robv.android.xposed.IXposedHookLoadPackage
import de.robv.android.xposed.XC_MethodHook
import de.robv.android.xposed.XposedHelpers
import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam

class XposedMain : IXposedHookLoadPackage {
    var TAG: String = "AqCxBoM"
    private val mStrPackageName = "aqcxbom.xposedhooktarget" //HOOK APP目标的包名
    private val mStrClassPath = "aqcxbom.xposedhooktarget.MyClass" //HOOK 目标类全路径
    private val mStrMethodName = "fun1" //HOOK 目标函数名
    private fun LOGI(ct: String) {
        Log.d(TAG, ct)
    }
    
    override fun handleLoadPackage(loadPackageParam: LoadPackageParam) {
        //判断包名是否一致
        if (loadPackageParam.packageName == mStrPackageName) {
            LOGI("found target: " + loadPackageParam.packageName)
            val ArgClass = XposedHelpers.findClass(
                "aqcxbom.xposedhooktarget.ArgClass",
                loadPackageParam.classLoader
            )
            val ArrayList =
                XposedHelpers.findClass("java.util.ArrayList", loadPackageParam.classLoader)
            val Map = XposedHelpers.findClass("java.util.Map", loadPackageParam.classLoader)
            //包名一致时查找是否有匹配参数的类及函数
            XposedHelpers.findAndHookMethod(mStrClassPath,  //类路径
                loadPackageParam.classLoader,  //ClassLoader
                mStrMethodName,  //目标函数名
                "[[Ljava.lang.String;",  //参数1
                Map,  //参数2
                Map,  //参数3
                Map,  //参数4
                ArrayList,  //参数5
                ArrayList,  //参数6
                ArgClass,  //参数7
                object : XC_MethodHook() {
                    override fun beforeHookedMethod(param: MethodHookParam) {
                        super.beforeHookedMethod(param) //这个函数会在被hook的函数执行前执行
                        LOGI("beforeHook")
                    }
                    
                    override fun afterHookedMethod(param: MethodHookParam) {
                        super.afterHookedMethod(param) //这个函数会在被hook的函数执行后执行
                        LOGI("afterHooke param: ")
                    }
                })
        }
    }
}

```

### 去广告

现在安卓广告大多数都是国内几大广告商：腾讯、穿山甲、快手等几家，不过后面我懒得自己去定位查找，直接百度搜索发现相关的资料，不过大多数都是 `Smail` 语法。

比如想去掉穿山甲广告可以这么做：

```kotlin
XposedHelpers.findAndHookMethod("com.bytedance.pangle.Zeus", lpparam.classLoader, "hasInit", object : XC_MethodHook() {
    override fun beforeHookedMethod(param: MethodHookParam) {
        super.beforeHookedMethod(param)
    }

    override fun afterHookedMethod(param: MethodHookParam) {
        super.afterHookedMethod(param)
        param.result = false
    }
})

```

根据上面的定位可以知道 `com.bytedance.pangle.Zeus.hasInit` 是用于 `SDK` 是否初始化成功，如果成功后则加载广告业务，这里直接返回 `false` 来达到去广告效果，所以其他厂商广告大同小异，故不再详写了。

### 总结

在后面几天又摸索了几个逆向虽然还是容易绕弯子，不过现在有了入手点和思路无非就是多花点时间，到后续熟悉了话很快找到其中的关键点。