xposed是一款可以在不修改APK的情况下景程序运行的框架,基于它可以制作出许多功能强大的模块,且在功能不,冲突的情况下同时运作。在这个框架下,我们可以编写并
加载自己编写的插件APP,实现对目标apk的注入拦载等。
假如签名校验无法绕过,则可利用xposed或frida去hook
原理
用自己实现的app_process普换掉了系统原本提供的app_process,加载一个额外的jar包,入口从原来的:com.android.internal.0sygoteInit.main(O)被替换成了:
de.robvandroid.xposed.XposedBridge.main(),
创建的Zygote进程就变成Hook的zygote进程了,从而完成对zygote进程及其创建的Dalvik/ART虚拟机的劫持(zytoge注入)
作用
1.修改app布局
2.劫持数据,修改参数值、返回值、主动调用等。例:微信防撤回、步数修改、一键新机
环境配置
1.Android Studio创建新项目
2.将下载的sposedBridgeApi.jar包拖进libs文件夹
3.右键jar包,选择add as library
4.修改xml文件配置
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.XposedTest"
tools:targetApi="31" >
<!-- 是否是xposed模块,xposed根据这个判断是否是模块 -->
<meta-data
android:name="xposedmoudle"
android:value="true" />
<!-- 模块描述显示在xposed模块的第二行 -->
<meta-data
android:name="xposeddescription"
android:value="这是一个xposed模块" />
<meta-data
android:name="xposedminversion"
android:value="89" />
</application>
</manifest>
5.修改build.gradle(位于app目录下),将此处修改为compileOnly,默认的为implementation
使用implementation依赖的库将会参与编译和打包
compileOnly只在编译时有效,不会参与打包
6.新建Folder–Assets Folder,创建xposed_init,声明入口类
com.example.xposedtest.Hook
7.新建Hook类,实现IXposedHookLoadPackage接口,在handleLoadPackage中编写逻辑,继承了此接口才有hook能力
package com.example.xposedtest;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
public class Hook implements IXposedHookLoadPackage{
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
}
}
编写
新建Hook类,实现IXposdHookLoadPackage接口,然后在handleLoadPackage函数内编写Hook逻辑
继承了IXposdHookLoadPackage接口便有了Hook能力
修改方法参数
例如使用jadx反编译出如下代码
package com.zj.wuaipojie;
import android.util.Log;
import androidx.recyclerview.widget.ItemTouchHelper;
import java.util.ArrayList;
import java.util.HashMap;
import kotlin.Metadata;
/* compiled from: Demo.kt */
@Metadata(d1 = {"\u00002\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\u000e\n\u0002\b\u0002\n\u0002\u0010\b\n\u0002\b\u0006\n\u0002\u0010\u0002\n\u0000\n\u0002\u0018\u0002\n\u0002\b\u0003\n\u0002\u0018\u0002\n\u0002\b\b\u0018\u0000 \u001a2\u00020\u0001:\u0003\u0019\u001a\u001bB\u0007\b\u0016¢\u0006\u0002\u0010\u0002B\u000f\b\u0002\u0012\u0006\u0010\u0003\u001a\u00020\u0004¢\u0006\u0002\u0010\u0005J\u001a\u0010\r\u001a\u00020\u000e2\n\u0010\u000f\u001a\u00060\u0010R\u00020\u00002\u0006\u0010\u0003\u001a\u00020\u0004J\u000e\u0010\u0011\u001a\u00020\u00042\u0006\u0010\u0003\u001a\u00020\u0004J$\u0010\u0012\u001a\u00020\u000e2\u0006\u0010\u0003\u001a\u00020\u00042\u0012\u0010\u0013\u001a\u000e\u0012\u0004\u0012\u00020\u0001\u0012\u0004\u0012\u00020\u00010\u0014H\u0002J\u0010\u0010\u0015\u001a\u00020\u000e2\u0006\u0010\u0003\u001a\u00020\u0004H\u0002J\b\u0010\u0016\u001a\u00020\u000eH\u0002J\u0010\u0010\u0017\u001a\u00020\u000e2\u0006\u0010\u0003\u001a\u00020\u0004H\u0002J\u0006\u0010\u0018\u001a\u00020\u000eR\u000e\u0010\u0006\u001a\u00020\u0007X\u0082D¢\u0006\u0002\n\u0000R\u001a\u0010\b\u001a\u00020\u0007X\u0086\u000e¢\u0006\u000e\n\u0000\u001a\u0004\b\t\u0010\n\"\u0004\b\u000b\u0010\f¨\u0006\u001c"}, d2 = {"Lcom/zj/wuaipojie/Demo;", "", "()V", "str", "", "(Ljava/lang/String;)V", "privateInt", "", "publicInt", "getPublicInt", "()I", "setPublicInt", "(I)V", "Inner", "", "animal", "Lcom/zj/wuaipojie/Demo$Animal;", "a", "complexParameterFunc", "map", "Ljava/util/HashMap;", "privateFunc", "repleaceFunc", "staticPrivateFunc", "test", "Animal", "Companion", "InnerClass", "app_release"}, k = 1, mv = {1, 7, 1}, xi = 48)
/* loaded from: classes.dex */
public final class Demo {
private static final String Tag = "zj2595";
private static final int staticInt = 100;
private final int privateInt;
private int publicInt;
private Demo(String str) {
this.privateInt = 300;
this.publicInt = ItemTouchHelper.Callback.DEFAULT_DRAG_ANIMATION_DURATION;
Log.d(Tag, "这是有参构造函数 || " + str);
}
public final int getPublicInt() {
return this.publicInt;
}
public final void setPublicInt(int i) {
this.publicInt = i;
}
public Demo() {
this(Tag);
Log.d(Tag, "这是无参构造函数");
}
public final String a(String str) {
return "这是一个" + str + "方法";
}
public final void test() {
Log.d(Tag, a("普通"));
Log.d(Tag, "staticInt = 100");
Log.d(Tag, "publicInt = " + this.publicInt);
Log.d(Tag, "privateInt = " + this.privateInt);
Log.d(Tag, "privateInt = " + this.privateInt);
privateFunc("wuaipojie");
staticPrivateFunc("wuaipojie");
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put("key", "value");
new ArrayList().add("listValue");
complexParameterFunc("wuaipojie", hashMap);
repleaceFunc();
Inner(new Animal(this) { // from class: com.zj.wuaipojie.Demo$test$1
/* JADX INFO: Access modifiers changed from: package-private */
{
super();
}
@Override // com.zj.wuaipojie.Demo.Animal
public void eatFunc(String str6) {
Log.d("zj2595", "this is eatFunc(String value) log || " + str6);
Log.d("zj2595", "AnimalInt = " + getAnimalInt());
}
}, "wuaipojie");
new InnerClass().innerFunc("wuaipojie");
}
private final void privateFunc(String str) {
Log.d(Tag, "这是私有变量方法 || " + str);
}
private final void staticPrivateFunc(String str) {
Log.d(Tag, "这是静态私有变量方法 || " + str);
}
private final void complexParameterFunc(String str, HashMap<Object, Object> map) {
Log.d(Tag, "这是复杂参数方法 || " + str);
}
private final void repleaceFunc() {
Log.d(Tag, "这是替换函数");
}
public final void Inner(Animal animal, String str) {
Log.d(Tag, "这是自定义参数 ||" + str);
animal.eatFunc("wuaipojie");
}
/* compiled from: Demo.kt */
@Metadata(d1 = {"\u0000 \n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\b\n\u0002\b\u0006\n\u0002\u0010\u0002\n\u0000\n\u0002\u0010\u000e\n\u0000\b\u0080\u0004\u0018\u00002\u00020\u0001B\u0005¢\u0006\u0002\u0010\u0002J\u000e\u0010\n\u001a\u00020\u000b2\u0006\u0010\f\u001a\u00020\rR\u000e\u0010\u0003\u001a\u00020\u0004X\u0082D¢\u0006\u0002\n\u0000R\u001a\u0010\u0005\u001a\u00020\u0004X\u0086\u000e¢\u0006\u000e\n\u0000\u001a\u0004\b\u0006\u0010\u0007\"\u0004\b\b\u0010\t¨\u0006\u000e"}, d2 = {"Lcom/zj/wuaipojie/Demo$InnerClass;", "", "(Lcom/zj/wuaipojie/Demo;)V", "innerPrivateInt", "", "innerPublicInt", "getInnerPublicInt", "()I", "setInnerPublicInt", "(I)V", "innerFunc", "", "str", "", "app_release"}, k = 1, mv = {1, 7, 1}, xi = 48)
/* loaded from: classes.dex */
public final class InnerClass {
private int innerPublicInt = 1000;
private final int innerPrivateInt = 2000;
public InnerClass() {
Log.d(Demo.Tag, "这是内部类构造函数");
}
public final int getInnerPublicInt() {
return this.innerPublicInt;
}
public final void setInnerPublicInt(int i) {
this.innerPublicInt = i;
}
public final void innerFunc(String str) {
Log.d(Demo.Tag, "这是内部类方法 || " + str);
Log.d(Demo.Tag, "内部类变量 = " + this.innerPublicInt);
Log.d(Demo.Tag, "内部类私有变量 = " + this.innerPrivateInt);
}
}
/* compiled from: Demo.kt */
@Metadata(d1 = {"\u0000 \n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\b\n\u0002\b\u0005\n\u0002\u0010\u0002\n\u0000\n\u0002\u0010\u000e\n\u0000\b¦\u0004\u0018\u00002\u00020\u0001B\u0005¢\u0006\u0002\u0010\u0002J\u0010\u0010\t\u001a\u00020\n2\u0006\u0010\u000b\u001a\u00020\fH&R\u001a\u0010\u0003\u001a\u00020\u0004X\u0086\u000e¢\u0006\u000e\n\u0000\u001a\u0004\b\u0005\u0010\u0006\"\u0004\b\u0007\u0010\b¨\u0006\r"}, d2 = {"Lcom/zj/wuaipojie/Demo$Animal;", "", "(Lcom/zj/wuaipojie/Demo;)V", "AnimalInt", "", "getAnimalInt", "()I", "setAnimalInt", "(I)V", "eatFunc", "", "str", "", "app_release"}, k = 1, mv = {1, 7, 1}, xi = 48)
/* loaded from: classes.dex */
public abstract class Animal {
private int AnimalInt = 400;
public abstract void eatFunc(String str);
public Animal() {
}
public final int getAnimalInt() {
return this.AnimalInt;
}
public final void setAnimalInt(int i) {
this.AnimalInt = i;
}
}
}
我们想要hook该类中的a方法的第一个参数,并修改其参数值:
public class Hook implements IXposedHookLoadPackage{
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
if(!loadPackageParam.packageName.equals("com.zj.wuaipojie")){
//过滤出是com.zj.wuaipojie包的内容
return;
}
XposedHelpers.findAndHookMethod("com.zj.wuaipojie.Demo", loadPackageParam.classLoader, "a", String.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
XposedBridge.log(param.args[0].toString()); //xp自带api
Log.d("zxd030116",param.args[0].toString()); //安卓自带log 日志标签:参数
String a = "pt";
param.args[0] = a;
Log.d("zxd030116",param.args[0].toString());
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
}
});
}
}
logcat中显示结果,日志中显示了两次,第一个为代码中自定义内容,第二个为hook修改后的内容
修改返回值
public class Hook implements IXposedHookLoadPackage{
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
if(!loadPackageParam.packageName.equals("com.zj.wuaipojie")){
return;
}
XposedHelpers.findAndHookMethod("com.zj.wuaipojie.Demo", loadPackageParam.classLoader, "a", String.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
// super.beforeHookedMethod(param);
// XposedBridge.log(param.args[0].toString()); //xp自带api
// Log.d("zxd030116",param.args[0].toString()); //安卓自带log 日志标签:参数
// String a = "pt";
// param.args[0] = a;
// Log.d("zxd030116",param.args[0].toString());
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Log.d("zxd030116",param.getResult().toString());
param.setResult("123456");
}
});
}
}
而原本的返回值“这是一个普通方法”位于zxd030116标签中
Hook复杂&自定义参数
如上述其中一个方法
private final void complexParameterFunc(String str, HashMap<Object, Object> map) {
Log.d(Tag, "这是复杂参数方法 || " + str);
}
编写如下hook代码,使用了XposedBridge中的hookAllMethods方法避免了使用findAndHookMethod时需要输入一些复杂的或自定义的参数
Class a = loadPackageParam.classLoader.loadClass("com.zj.wuaipojie.Demo");
XposedBridge.hookAllMethods(a, "complexParameterFunc", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Log.d("zxd030116",param.args[0].toString());
}
});
Hook替换函数
private final void repleaceFunc() {
Log.d(Tag, "这是替换函数");
}
hook函数使其置空,猜测可用于一些障碍方法的置空
Class a = loadPackageParam.classLoader.loadClass("com.zj.wuaipojie.Demo");
XposedBridge.hookAllMethods(a,"replaceFunc", new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam methodHookParam) throws Throwable {
return "";
}
});
可看到无替换函数的日志输出
Hook加固通杀(绝大部分免杀壳)
加固后的app做hook需要
通过Application的attach方法获取classLoader传入,在进行Hook逻辑编写
XposedHelpers.findAndHookMethod(Application.class, "attach", Context.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Context context = (Context) param.args[0];
ClassLoader classLoader = context.getClassLoader();
XposedHelpers.findAndHookMethod("com.zj.wuaipojie.Demo", classLoader, "a", String.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
XposedBridge.log(param.args[0].toString()); //xp自带api
Log.d("zxd030116", param.args[0].toString()); //安卓自带log 日志标签:参数
String a = "pt";
param.args[0] = a;
Log.d("zxd030116", param.args[0].toString());
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Log.d("zxd030116",param.getResult().toString());
param.setResult("123456");
}
});
}
});