Xposed搭建开发

篇幅有限

完整内容及源码关注公众号:ReverseCode,发送

pixel刷安卓7

proxychains wget https://dl.google.com/dl/android/aosp/sailfish-n2g47o-factory-f2bc8024.zip

proxychains wget https://forum.xda-developers.com/attachments/xposedinstaller_3-1-5-apk.4393082/

proxychains wget https://supersuroot.org/downloads/supersu-pro.apk

proxychains wget https://dl.twrp.me/sailfish/twrp-3.3.0-0-sailfish.img

1
2
3
4
5
6
cd sailfish-opm4.171019.021.p1 && adb reboot bootloader &&  ./flash-all.sh
adb push SR5-SuperSU-v2.82-SR5-20171001224502.zip /data/local/tmp
adb reboot bootloader && fastboot boot twrp-3.3.0-0-sailfish.img 刷入twrp后安装supersu
adb install XposedInstaller_3.1.5.apk 常见模块https://github.com/WrBug/GravityBox.git
adb push timeadjust.sh /data/local/tmp && sh timeadjust.sh 时间修改正确
adb install com.ttxapps.wifiadb_2.1.3-810031745_minAPI15.apk

pixel刷安卓10

1
2
3
4
5
6
7
8
9
10
11
12
13
14
adb reboot bootloader  安卓10,marlin-qp1a.191005.007.a3解压缩拿到boot.img
flash-all.sh
adb install magisk-25.2.apk 开发者模式开启后安装magisk
adb push boot.img /sdcard/Download 在magisk安装选择一个修补文件,生成同目录magisk_patched-25200_5K3cq.img
adb pull /storage/emulated/0/Download/magisk_patched-25200_5K3cq.img
adb reboot bootloader
fastboot flash boot magisk_patched-25200_5K3cq.img
settings put global captive_portal_http_url https://www.google.cn/generate_204 去除wifi上的×
settings put global captive_portal_https_url https://www.google.cn/generate_204
settings put global ntp_server 1.hk.pool.ntp.org 修改时区
adb push Shamiko-v0.5.2-120-release.zip /sdcard/Download
adb push LSPosed-v1.8.4-6609-zygisk-release.zip /sdcard/Download
adb install com.ttxapps.wifiadb_2.1.3-810031745_minAPI15.apk
adb push MagiskHidePropsConf-v5.3.4.zip /sdcard/Download 通过magisk安装后adb shell-props-413yy,getprop ro.debuggable 即可查看1,开启全局可调式

如何卸载Magisk:
方法一:在Magisk首页点击“卸载Magisk”并按提示操作;
方法二:下载官方Magisk.apk,复制并改名为uninstall.zip,再使用Recovery刷入;
从Magisk v22.0开始,Magisk和Magisk Manager就合并为一个文件了,将文件名为Magisk.apk时它是Magisk应用程序,用于管理Magisk模块及功能;当文件名为Magisk.zip时它是刷入Magisk的卡刷包;当文件名为uninstall.zip时它是用于卸载Magisk的卡刷包。
如果安装Magisk导致无法正常开机,可在Recovery中刷入uninstall.zip即可卸载Magisk。
magisk在v24之后取消了magisk hide和在线仓库,可以通过Zygisk来实现root隐藏,刷入Shamiko模块,关闭遵守排除列表。

为什么要用Shamiko做root隐藏,而不是使用zygisk magisk自带的遵守排除列表?
不适用Shamiko也是可以的,zygisk magisk自带排除列表功能,但是这种情况下排除列表中的应用是无法使用magisk和xposed模块的,如果我想对某个排除列表中的应用使用虚拟框架和模块,就需要使用到Shamiko模块

zygisk是什么?和riru有什么关系
在Zygisk出现之前,Xposed是通过riru实现的,riru注入zygote以允许模块在应用程序中运行。在magisk v24+中,推出zygisk,可以理解成Zygisk = Zygote + Magisk,和riru的功能类似。使用了Zygisk后,就不需要安装riru模块了,同时依赖于riru的xposed框架也无法使用,但是可以使用支持zygisk的LSPosed。目前riru已经停止更新。

xposed

xposed api

image-20210423225219896

demo

过滤子进程:loadPackageParam.processName,可以通过hook参数、调用栈、返回值 ,打印和修改,Xposed的开发,本质上就是Java的开发。

  1. 创建安卓项目xposed1

  2. build.gradle引入xposed api依赖

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
apply plugin: 'com.android.application'

android {
compileSdkVersion 30
buildToolsVersion "30.0.2"
defaultConfig {
applicationId "com.roysue.xposed1"
minSdkVersion 16
targetSdkVersion 30
versionCode 1
versionName "1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
}

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'
implementation 'androidx.constraintlayout:constraintlayout:2.0.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

}
  1. AndroidManifest.xml添加xposed项目基本配置
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
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.roysue.xposed1">

<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例程" />
<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>
  1. activity_main.xml添加按钮组件
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
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
tools:layout_editor_absoluteX="158dp"
tools:layout_editor_absoluteY="238dp" />

</androidx.constraintlayout.widget.ConstraintLayout>
  1. MainActivity调用Button组件,实现被hook方法toastMessage
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class MainActivity extends AppCompatActivity {

private Button button;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {

public void onClick(View v) {
Toast.makeText(MainActivity.this, toastMessage("我未被劫持"), Toast.LENGTH_SHORT).show();
}

});

}
public String toastMessage(String message) {
return message;
}

}
  1. 继承IXposedHookLoadPackage实现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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class HookTest implements IXposedHookLoadPackage {
// 堆栈打印
public void PrintStack(){

XposedBridge.log("Dump Stack: "+ "---------------start----------------");
Throwable ex = new Throwable();
StackTraceElement[] stackElements = ex.getStackTrace();
if (stackElements != null) {
for (int i = 0; i < stackElements.length; i++) {

XposedBridge.log("Dump Stack"+i+": "+ stackElements[i].getClassName()
+"----"+stackElements[i].getFileName()
+"----" + stackElements[i].getLineNumber()
+"----" +stackElements[i].getMethodName());
}
}
XposedBridge.log("Dump Stack: "+ "---------------over----------------");
RuntimeException e = new RuntimeException("<Start dump Stack !>");
e.fillInStackTrace();
Log.i("<Dump Stack>:", "++++++++++++", e);

}

public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
//XposedBridge.log(loadPackageParam.processName);
if (loadPackageParam.packageName.equals("com.roysue.xposed1")) {
XposedBridge.log(" has Hooked!");
XposedBridge.log("inner"+loadPackageParam.processName);
Class clazz = loadPackageParam.classLoader.loadClass("com.roysue.xposed1.MainActivity");
XposedHelpers.findAndHookMethod(clazz, "toastMessage", String.class,new XC_MethodHook() {
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
// 原始参数
String oldText = (String) param.args[0];
Log.d("din not hijacked=>", oldText);
//param.args[0] = "test";
// 修改新参数
param.args[0] = "你已被劫持";
PrintStack();
//super.beforeHookedMethod(param);
//XposedBridge.log(" has Hooked!");
}
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
Log.d("getResult is => ",(String) param.getResult());
param.setResult("你已被劫持2");
}
});
}
}
}
  1. xposed_init配置hook方法
    com.roysue.xposed1.HookTest

编译启动该app,注入Xposed

image-20210423225205264

点击按钮查看log及调用栈

image-20210423233543529

1
2
3
objection -g com.roysue.xposed1 explore
android hooking search classes HookTest
android hooking list class_methods com.roysue.xposed1.HookTest 找不到该类,原因是实现了接口IXposedHookLoadPackage,HookTest不在接口包中,需要在xposed_init里指定路径

hook

Hook HookTest

objection无法找到,通过frida进行hook拿到HookTest,frida -UF -l hookXposed.js

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
function hook() {
Java.perform(function () {
console.log("start")
Java.enumerateClassLoaders({
onMatch: function (loader) {
try {
if(loader.findClass("com.roysue.xposed1.HookTest")){
console.log("Successfully found loader")
console.log(loader);
Java.classFactory.loader = loader ;
}
}
catch(error){
console.log("find error:" + error)
}
},
onComplete: function () {
console.log("end1")
}
})
})
Java.use("com.roysue.xposed1.HookTest").PrintStack.implementation = function (param){
console.log("entering PrintStack");
return true;
}
console.log("end2")
}
function main(){
hook()
}
setImmediate(main)

image-20210501135526330

Hook XposedBridge

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
function hook() {
Java.perform(function () {
console.log("start")
Java.enumerateClassLoaders({
onMatch: function (loader) {
try {
// if (loader.findClass("com.roysue.xposed1.HookTest")) {
if(loader.findClass("de.robv.android.xposed.XposedBridge")){
console.log("Successfully found loader")
console.log(loader);
Java.classFactory.loader = loader;
}
}
catch (error) {
console.log("find error:" + error)
}
},
onComplete: function () {
console.log("end1")
}
})

Java.use("de.robv.android.xposed.XposedBridge").log.overload('java.lang.String').implementation = function (str) {
console.log("entering XposedBridge.log",str.toString());
return true;
}
console.log("end2")
})
}

image-20210501140005696

Hook All Methods

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
function uniqBy(array, key) {
var seen = {};
return array.filter(function (item) {
var k = key(item);
return seen.hasOwnProperty(k) ? false : (seen[k] = true);
});
}

// trace a specific Java Method
function traceMethod(targetClassMethod) {
var delim = targetClassMethod.lastIndexOf(".");
if (delim === -1) return;

var targetClass = targetClassMethod.slice(0, delim)
var targetMethod = targetClassMethod.slice(delim + 1, targetClassMethod.length)

var hook = Java.use(targetClass);
var overloadCount = hook[targetMethod].overloads.length;

console.log("Tracing " + targetClassMethod + " [" + overloadCount + " overload(s)]");


/*
// hook all class_method
for (var i = 0; i < overloadCount; i++) {

hook[targetMethod].overloads[i].implementation = function () {
console.warn("\n*** entered " + targetClassMethod);

// print backtrace
// Java.perform(function() {
// var bt = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new());
// console.log("\nBacktrace:\n" + bt);
// });

// print args
if (arguments.length) console.log();
for (var j = 0; j < arguments.length; j++) {
console.log("arg[" + j + "]: " + arguments[j]);

}

// print retval
var retval = this[targetMethod].apply(this, arguments); // rare crash (Frida bug?)
console.log("\nretval: " + retval);
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
console.warn("\n*** exiting " + targetClassMethod);
return retval;
}
}
*/

}

function traceClass(targetClass) {
//Java.use是新建一个对象哈,大家还记得么?
var hook = Java.use(targetClass);
//利用反射的方式,拿到当前类的所有方法
var methods = hook.class.getDeclaredMethods();
// var methods = hook.class.getMethods();
//建完对象之后记得将对象释放掉哈
hook.$dispose;
//将方法名保存到数组中
var parsedMethods = [];
methods.forEach(function (method) {
parsedMethods.push(method.toString().replace(targetClass + ".", "TOKEN").match(/\sTOKEN(.*)\(/)[1]);
});
//去掉一些重复的值
var targets = uniqBy(parsedMethods, JSON.stringify);
//对数组中所有的方法进行hook,traceMethod也就是第一小节的内容
targets.forEach(function (targetMethod) {
traceMethod(targetClass + "." + targetMethod);
});
}

traceClass(“de.robv.android.xposed.XposedBridge”);

image-20210501141314991

1
2
if (loader.findClass("com.roysue.xposed1.HookTest$1")) {   
traceClass("com.roysue.xposed1.HookTest$1");

image-20210501141552600

说明com.roysue.xposed1.HookTest$1就是XposedHelpers.findAndHookMethod(clazz, "toastMessage", String.class,new XC_MethodHook() {中的XC_MethodHook内部类

hook afterHookedMethod

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
function hook() {
Java.perform(function () {
console.log("start")
Java.enumerateClassLoaders({
onMatch: function (loader) {
try {
// if (loader.findClass("com.roysue.xposed1.HookTest")) {
if(loader.findClass("com.roysue.xposed1.HookTest$1")){
console.log("Successfully found loader")
console.log(loader);
Java.classFactory.loader = loader;
}
}
catch (error) {
console.log("find error:" + error)
}
},
onComplete: function () {
console.log("end1")
}
})

Java.use("com.roysue.xposed1.HookTest$1").afterHookedMethod.implementation = function (param) {
console.log("entering afterHookedMethod param is ",param);
return this.afterHookedMethod(param);
}
console.log("end2")
})
}

image-20210501142210586

通过traceClass("de.robv.android.xposed.XC_MethodHook$MethodHookParam")拿到所有的类方法,在classloader中而不是在app中

image-20210501142632392

1
2
3
4
5
6
Java.use("de.robv.android.xposed.XC_MethodHook$MethodHookParam").setResult.implementation = function (param) {
console.log("entering XC_MethodHook$MethodHookParam setResult param is ",param);
// 打印调用栈
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
return this.setResult(param);
}

image-20210501143142237

XC_MethodHook是抽象类不能直接hook,需要hook实现。

traceClass(“com.roysue.xposed1.HookTest$1”)

image-20210501143735138

GravityBox

git clone https://github.com/GravityBox/GravityBox.git

修改app/build.gradle

1
2
3
4
5
6
7
8
9
compileSdkVersion 23
targetSdkVersion 23
debug { 避免打包需要秘钥
// versionNameSuffix "-Dev"
// if (signingConfigs.releaseConfig != null) {
// signingConfig signingConfigs.releaseConfig
// }
}
compile 'com.android.support:appcompat-v7:26.0.2' 指定版本

build.gradle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
buildscript {
repositories {
jcenter()
google() 添加google镜像源
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
}
}

allprojects {
repositories {
jcenter()
google()
maven { url 'https://jitpack.io' }
}
}

源码分析

src/assets/xposed_init 文件中提供了入口类com.wrbug.gravitybox.nougat.GravityBox

1
public class GravityBox implements IXposedHookZygoteInit, IXposedHookInitPackageResources, IXposedHookLoadPackage

入口类中实现了三个接口IXposedHookInitPackageResourcesIXposedHookLoadPackageIXposedHookZygoteInit

IXposedHookZygoteInit 所有的进程

Hook the initialization of ** (es), from which **all the apps are forked.

Implement this interface in your module’s main class in order to be notified when Android is starting up. In IXposedHookZygoteInit, you can modify objects and place hooks that should be applied for every app. Only the Android framework/system classes are available at that point in time. Use null as class loader for XposedHelpers.findAndHookMethod(String, ClassLoader, String, Object...) and its variants.

If you want to hook one/multiple specific apps, use IXposedHookLoadPackage instead.

说明initZygote只有在系统启动的时候执行一遍,只有系统框架库可以使用。

XposedBridge.log(“GB:Hardware: “ + Build.HARDWARE); 代码中调用了Build.HARDWARE

1
2
3
frida-ps -U|grep gravity
objection -g com.ceco.nougat.gravitybox explore -P ~/.objection/plugins
plugin wallbreaker classdump --fullname android.os.Build

**IXposedHookInitPackageResources ** 所有的资源

Get notified when the resources for an app are initialized. In handleInitPackageResources(XC_InitPackageResources.InitPackageResourcesParam), resource replacements can be created.

This interface should be implemented by the module’s main class. Xposed will take care of registering it as a callback automatically.

说明handleInitPackageResources实现的回调在创建之后就会得到通知

**IXposedHookLoadPackage ** 所有的包

Get notified when an app (“Android package”) is loaded. This is especially useful to hook some app-specific methods.

This interface should be implemented by the module’s main class. Xposed will take care of registering it as a callback automatically.

xposed会将handleLoadPackage注册成为回调,app在加载时都会经过该回调,主要用来实现具体的hook逻辑。

在handleLoadPackage中调用了ModStatusbarColor.init

1
2
3
if (lpparam.packageName.equals(ModStatusbarColor.PACKAGE_NAME)) {
ModStatusbarColor.init(prefs, lpparam.classLoader);
}
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
public static final String PACKAGE_NAME = "com.android.systemui"; 
private static final String CLASS_PHONE_STATUSBAR = "com.android.systemui.statusbar.phone.PhoneStatusBar";
public static void init(final XSharedPreferences prefs, final ClassLoader classLoader) {
try {
// findClass底层通过反射获取CLASS_PHONE_STATUSBAR类
final Class<?> phoneStatusbarClass = XposedHelpers.findClass(CLASS_PHONE_STATUSBAR, classLoader);
final Class<?> statusbarIconViewClass = XposedHelpers.findClass(CLASS_STATUSBAR_ICON_VIEW, classLoader);
final Class<?> sbTransitionsClass = XposedHelpers.findClass(CLASS_SB_TRANSITIONS, classLoader);

XposedHelpers.findAndHookMethod(phoneStatusbarClass,
// hook相同的类时XCallback.PRIORITY_LOWEST最低,优先级最高
// https://api.xposed.info/reference/de/robv/android/xposed/XC_MethodHook.html
"makeStatusBarView", new XC_MethodHook(XCallback.PRIORITY_LOWEST) {
@Override
protected void afterHookedMethod(final MethodHookParam param) throws Throwable {
mPhoneStatusBar = param.thisObject;
// getObjectField获取对象的属性值
// https://api.xposed.info/reference/de/robv/android/xposed/XposedHelpers.html
Context context = (Context) XposedHelpers.getObjectField(param.thisObject, "mContext");

if (SysUiManagers.IconManager != null) {
SysUiManagers.IconManager.registerListener(mIconManagerListener);
}

Intent i = new Intent(ACTION_PHONE_STATUSBAR_VIEW_MADE);
context.sendBroadcast(i);
}
});
} catch (Throwable t) {
GravityBox.log(TAG, t);
}
}

由于com.android.systemui和当前hook的包在不同进程中

1
2
3
objection -g com.android.systemui explore
android hooking search classes com.android.systemui ~/.objection/plugins
plugin wallbreaker classdump --fullname com.android.systemui.statusbar.phone.PhoneStatusBar

静态域

https://api.xposed.info/reference/de/robv/android/xposed/XposedHelpers.html

1
2
3
4
5
BRIGHTNESS_ON = XposedHelpers.getStaticIntField(powerManagerClass, "BRIGHTNESS_ON");
plugin wallbreaker classdump --fullname android.os.PowerManager 查看静态域
(int[])XposedHelpers.getStaticObjectField(classAudioService, "MAX_STREAM_VOLUME");
XposedHelpers.getStaticLongField(param.thisObject.getClass(),"SWIPE_TIMEOUT_MS")
(boolean) XposedHelpers.getStaticBooleanField(mDisplayPowerController.getClass(), "MTK_ULTRA_DIMMING_SUPPORT");

动态域

https://api.xposed.info/reference/de/robv/android/xposed/XposedHelpers.html

1
2
3
4
5
XposedHelpers.getObjectField(param.thisObject, "mNotification") == null
objection -g com.ceco.nougat.gravitybox explore -P ~/.objection/plugins
android heap search instances com.android.systemui.statusbar.phone.PhoneStatusBar 内存搜索PhoneStatusBar类实例
plugin wallbreaker objectsearch com.android.systemui.statusbar.phone.PhoneStatusBar 内存搜索对象
plugin wallbreaker objectiondump --fullname 0x100e6e 查看类内容中的动态域mNotification

主动调用

https://api.xposed.info/reference/de/robv/android/xposed/XposedHelpers.html

1
2
3
4
5
6
(Float) XposedHelpers.callMethod(param.thisObject, "getNonBatteryClockAlphaFor", (Integer) param.args[0]);
XposedHelpers.callStaticMethod(mClsPhoneFactory, "getPhone", mSimSlot);
plugin wallbreaker objectsearch com.android.systemui.statusbar.phone.PhoneStatusBarTransitions
plugin wallbreaker objectdump --fullname 0x10141e 查找getNonBatteryClockAlphaFor方法
int phoneId = XposedHelpers.getIntField(param.thisObject, "mPhoneId");
Context context = (Context) XposedHelpers.getObjectField(param.thisObject, "mContext");

构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
XposedHelpers.findAndHookConstructor("android.media.AudioManager", classLoader, Context.class,
new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
Object objService = XposedHelpers.callMethod(param.thisObject, "getService");
Context mApplicationContext = (Context) XposedHelpers.getObjectField(param.thisObject,
"mApplicationContext");
if (objService != null && mApplicationContext != null) {
XposedHelpers.callMethod(param.thisObject, "disableSafeMediaVolume");
}
}
});
android hooking search classes android.media.AudioManger
plugin wallbreaker objectsearch android.media.AudioManager
plugin wallbreaker objectdump --fullname 0x186e 查看getService和disableSafeMediaVolume和mApplicationContext

Not In Frida

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
setAdditionalInstanceField(param.thisObject, "mVolumeUpLongPress", mVolumeUpLongPress);  给对象加静态域
Object ls = XposedHelpers.getSurroundingThis(mLight); 内部类对象返回给外部类
Class[] params = method.getParameterTypes(); 获取参数列表
UserHandle uh = (UserHandle) uhConst.newInstance(-2); 创建新实例对象
Constructor<?> uhConst = XposedHelpers.findConstructorExact(UserHandle.class, int.class); 查找一个构造函数让其可用
XposedBridge.hookAllConstructors(XposedHelpers.findClass( hook所有构造函数
CLASS_TRUST_MANAGER_SERVICE, classLoader), new XC_MethodHook() {
@Override
protected void afterHookedMethod(final MethodHookParam param) throws Throwable {
mTrustManager = param.thisObject;
Context context = (Context) XposedHelpers.getObjectField(param.thisObject, "mContext");
mWifiManager = new WifiManagerWrapper(context, null);
mConnectivityManager = (ConnectivityManager) context.getSystemService(
Context.CONNECTIVITY_SERVICE);

IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(WifiPriorityActivity.ACTION_WIFI_TRUSTED_CHANGED);
intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
context.registerReceiver(mBroadcastReceiver, intentFilter);

if (DEBUG) log("Trust manager constructed");
}
});
XposedBridge.hookMethod(mtdHandlePlay, new XC_MethodHook() { hook所有方法并创建回调
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
qhPrefs.reload();
QuietHours qh = new QuietHours(qhPrefs);
if (qh.isSystemSoundMuted(QuietHours.SystemSound.RINGER)) {
param.setResult(null);
}
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
if (!mRingerConfig.enabled) return;

mRingtone = (Ringtone) XposedHelpers.getObjectField(mAsyncRinger, "mRingtone");
if (mRingtone == null) {
if (DEBUG) log("handlePlay called but ringtone is null");
return;
}

setVolume(mRingerConfig.minVolume);
mIncrementAmount = (1f - mRingerConfig.minVolume) / (float) mRingerConfig.rampUpDuration;
mCurrentIncrementVolume = mRingerConfig.minVolume;
mHandler = (Handler) XposedHelpers.getObjectField(mAsyncRinger, "mHandler");
mHandler.postDelayed(mRunnable, 1000);
if (DEBUG) log("Starting increasing ring");
}
});
  • set(get)AdditionalInstanceField
  • getMD5Sum
  • getMethodDepth
  • getParameterTypes
  • getSurroundingThis
  • hookMethod
  • 系统级别的,过滤所有的进程
  • 只要Xposed生效了,可以把Xposed理解为系统框架,作为系统的本身来考虑没有关系。

Not In Xposed

  • Java.choose
  • rpc
  • 热重载/加载
  • 单进程级别的,只能在hook的进程内生效

hook

本项目中 xposed 系统级别的,过滤所有的进程,入口类中实现了三个接口,hook了所有的资源,进程,包。只会hook进程中的包等于ModStatusbarColor.PACKAGE_NAME即com.android.systemui时,才启动hook。所有逻辑不在GravityBox中,也不在xposed.jar中,而是在com.android.systemui

if (lpparam.packageName.equals(ModStatusbarColor.PACKAGE_NAME)) {
    ModStatusbarColor.init(prefs, lpparam.classLoader);
}

frida-ps -U |grep com.android.systemui

在app逆向中遇到interface搜索其实现还是一顿乱搜,可以使用反射getInterfaces得到实现的接口数组,然后打印出来即可

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
function hook() {
Java.perform(function () {
console.log("start")
Java.enumerateClassLoaders({
onMatch: function (loader) {
try {
if (loader.findClass("com.ceco.nougat.gravitybox.ModStatusbarColor$1")) {
// if(loader.findClass("de.robv.android.xposed.XC_MethodHook")){
// if(loader.findClass("com.roysue.xposed1.HookTest")){
// if(loader.findClass("de.robv.android.xposed.XposedBridge")){
//if(loader.findClass("com.android.internal.statusbar.StatusBarIcon")){

console.log("Successfully found loader")
console.log(loader);
Java.classFactory.loader = loader;
}
}
catch (error) {
console.log("find error:" + error)
}
},
onComplete: function () {
console.log("end1")
}
})
// Java.use("de.robv.android.xposed.XposedBridge").log.overload('java.lang.String').implementation = function (str) {
// console.log("entering Xposedbridge.log ",str.toString())
// return true
// }
//traceClass("com.ceco.nougat.gravitybox.ModStatusbarColor")
// Java.use("com.roysue.xposed1.HookTest$1").afterHookedMethod.implementation = function (param){
// console.log("entering afterHookedMethod param is => ",param);
// return this.afterHookedMethod(param);
// }
// traceClass("de.robv.android.xposed.XC_MethodHook")
// Java.use("de.robv.android.xposed.XC_MethodHook$MethodHookParam").setResult.implementation = function(str){
// console.log("entersing de.robv.android.xposed.XC_MethodHook$MethodHookParam setResult => ",str)
// console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
// return this.setResult(str);
// }

Java.enumerateLoadedClasses({
onMatch: function (className) {
if (className.toString().indexOf("gravitybox") > 0 &&
className.toString().indexOf("$") > 0
) {
console.log("found => ", className)
// var interFaces = Java.use(className).class.getInterfaces();
// if(interFaces.length>0){
// console.log("interface is => ");
// for(var i in interFaces){
// console.log("\t",interFaces[i].toString())
// }
// }
if (Java.use(className).class.getSuperclass()) {
var superClass = Java.use(className).class.getSuperclass().getName();
// console.log("superClass is => ",superClass);
if (superClass.indexOf("XC_MethodHook") > 0) {
console.log("found class is => ", className.toString())
traceClass(className);
}

}

}
}, onComplete: function () {
console.log("search completed!")

}
})

console.log("end2")
})
}
文章作者: J
文章链接: http://onejane.github.io/2021/04/23/Xposed搭建开发/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 万物皆可逆向
支付宝打赏
微信打赏