https://yuanfentiank789.github.io/2017/04/01/xposeddev/

原文章是从Xposed作者github上翻译过来的,但是其中一些内容在新版本的Android studio中不再适用,我将结合自己实践对原文进行补充。

Method hooking/replacing

Xposed真正强大的地方在于对Method调用的hook,如果通过反编译来验证逻辑猜想的话,只能多次的反编译代码,修改代码,重新签名打包来完成。而使用Xposed的hook功能,不用修改apk方法中的代码,通过再目标method前后插入要执行的代码即可,method是java中最小的执行单元。

创建project

一个xposed module本质上是一个普通的Android app,只是多了几个meta data和文件而已。因此,首先创建一个普通的Android project,sdk版本选择4.0.3(API 15)以上,因为module没有界面,因此不必要创建activity,这时,应该已经创建好了一个空的Android project。

但是不创建activity将无法安装到模拟器上,建议还是创建一个empty activity

接下来把上一步刚创建好的project改造成可以被Xposed加载的module,分为以下几个步骤:

添加Xposed Framework API到project

https://bintray.com/rovo89/de.robv.android.xposed/api下载相应的api,放到项目的libs文件夹下,并在app/build.gradle中引入。

image-20201202162922671

1
2
3
4
5
6
7
8
9
10
dependencies {
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'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

}

*将implementation fileTree(dir: “libs”, include: [“.jar”])这行注释掉,否则构建项目时会失败**

需要注意的是,这里是有provide而不是compile。如果使用compile,api库的classes将会被打包到apk中,会导致bug,特别是在Android 4.x设备上。使用provided只是让module可以通过编译,在apk中只有对api的引用,真正的实现在由运行设备的Xposed Framework提供。新版本的Android Studio中provide被compileOnly代替。

AndroidManifest.xml

Xposed Installer能列出系统已经安装的module,是因为它通过application中一个特殊的meta flag来识别。到AndroidManifest.xml => Application => Application Nodes (at the bottom) => Add => Meta Data.在Application节点底部添加几个meta data.

  1. name:xposedmodule,value:true;
  2. name:xposedminversion,value:上一节获得的API version;
  3. name:xposeddescription,value:module简单的描述
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
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.luckyfuture.firsthook">

<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="Hook AliCrackme的模块" />
<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>

Module实现

http://blog.llloverr.com/index.php/archives/21/中看雪ctf的一道题目为例

新建一个class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.luckyfuture.firsthook;

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

public class Start implements IXposedHookLoadPackage {
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
if (!lpparam.packageName.equals("com.ctf.test.ctf_100"))
return;
XposedBridge.log("Find target app:"+lpparam.packageName);
//确定要拦截的进程com.ctf.test.ctf_100
}
}

assets/xposed_init

最后要做的一件事是告诉XposedBridge那个class包含hook入口,通过一个叫xposed_init的文件。在asset目录下新建这个文件,该文件每行包含一个class的全限定名,在这里是:com.luckyfuture.firsthook.Start。

image-20201201171030739

编译运行

保存上述修改,运行这个application,因为是首次安装这个module,需要首先打开Xposed Installer激活。在Modules tab找到刚安装的module,激活,重启设备.通过logcat观察输出