安卓逆向-Xposed-Hook入门

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注入)
![[Pasted image 20240820135537.png]]

作用

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只在编译时有效,不会参与打包
![[Pasted image 20240820195223.png]]

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修改后的内容
![[Pasted image 20240821140432.png]]

修改返回值

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");
            }
        });
    }
}

![[Pasted image 20240821143133.png]]

而原本的返回值“这是一个普通方法”位于zxd030116标签中
![[Pasted image 20240821143337.png]]

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());
            }
        });

![[Pasted image 20240821153741.png]]

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 "";
            }
        });

可看到无替换函数的日志输出
![[Pasted image 20240821161925.png]]

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");
            }
                });


            }
        });
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值