Java 反射之Hook 实例

2022-04-22  本文已影响0人  行走中的3卡

Hook 机制主要是通过反射机制,在运行时改变某个对象的属性或者行为(很抽象!!!),
所以必须以实例作为学习的切入点。
或者可以参考:其实用高大上的Hook技术动态注入代码很简单,一看就会! - JavaShuo

这里仅做实例记录

1. 普通场景

直接上代码, 很容易看懂
(1) Weapon 类

package com.example.javalearnproject.reflectbasictest.hook;

public class Weapon {
    protected int damage = 10;

    public void attack(){
        System.out.println(String.format("对目标形成 %d 点伤害",damage));
    }
}

(2) Hero 类

package com.example.javalearnproject.reflectbasictest.hook;

public class Hero {
    private Weapon weaponMain;

    public Hero(Weapon weaponMain) {
        this.weaponMain = weaponMain;
    }

    public void attack(){
        weaponMain.attack();
    }
}

(3) Game 类

package com.example.javalearnproject.reflectbasictest.hook;

public class Game {
    public static void main(String[] args){
        Hero hero = new Hero(new Weapon());
        hero.attack();
    }
}

输出:

对目标形成 10 点伤害

2. Hook 场景(改变属性值)

(1) Hero 保存不变
(2) WeaponHook 继承Weapon, 并且在attack方法中设置回调

package com.example.javalearnproject.reflectbasictest.hook.dohook;

//对于上面这段程序,游戏对咱们隐藏了Weapon的伤害值,但如今咱们想要在每次攻击的时候知道这个伤害值是多少。
//下面看看使用Hook机制如何来实现。

import com.example.javalearnproject.reflectbasictest.hook.Weapon;

//首先咱们经过观察,发现切入点就是weaponMain,咱们要对它下手。
//建立一个Weapon的复制品WeaponHook,咱们须要用本身的人WeaponHook打入内部。
//WeaponHook一切看起来都和Weapon那么类似,可是咱们给它留了一个后门,使得咱们能够进行监控。
public class WeaponHook extends Weapon {
    private OnUseWeaponAttackListener onUseWeaponAttackListener;

    @Override
    public void attack(){
        super.attack();
        System.out.println("WeaponHook has called Weapon.attach");
        if (onUseWeaponAttackListener != null){
            onUseWeaponAttackListener.onUseWeaponAttack(damage);
        }
    }

    public void setOnUseWeaponAttackListener(OnUseWeaponAttackListener onUseWeaponAttackListener) {
        this.onUseWeaponAttackListener = onUseWeaponAttackListener;
    }

    //这就是咱们的后门
    public static interface OnUseWeaponAttackListener {
        int onUseWeaponAttack(int damage);
    }
}

(3) GameHook 反射注入 WeaponHook对象

package com.example.javalearnproject.reflectbasictest.hook.dohook;

import com.example.javalearnproject.reflectbasictest.hook.Hero;
import com.example.javalearnproject.reflectbasictest.hook.Weapon;

import java.lang.reflect.Field;

public class GameHook {
    public static void main(String[] args){
        Hero hero = new Hero(new Weapon());
        try {
            //Field weapon = ReflectUtils.getVariable(hero.getClass(), "weaponMain");
            Class<?> heroClass = hero.getClass();
            //Field weapon = heroClass.getField("weaponMain");// 不包含私有属性
            Field weapon = heroClass.getDeclaredField("weaponMain");// 包含私有属性
            weapon.setAccessible(true);
            Weapon weaponHook = new WeaponHook();
            ((WeaponHook) weaponHook).setOnUseWeaponAttackListener(damage -> {
                //经过后门进行操做,这其实就是咱们注入的代码
                System.out.println("damage = " + damage);
                return damage;
            });
            weapon.set(hero, weaponHook); //偷天换日  >>> 关键操作,替换掉原来的成员变量  (属性.set(对象,新的成员变量的对象))
            hero.attack(); //最终调用的是 weaponHook
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

此时输出:

对目标形成 10 点伤害
WeaponHook has called Weapon.attach
damage = 10

3. 防止Hook入侵

(1) HeroPro 增加hashcode判断

package com.example.javalearnproject.reflectbasictest.hook.hookprotect;

import com.example.javalearnproject.reflectbasictest.hook.Weapon;

public class HeroPro {
    private Weapon weaponMain;
    private final int weaponMainId;

    public HeroPro(Weapon weaponMain) {
        this.weaponMain = weaponMain;
        weaponMainId = this.weaponMain.hashCode();//记录原始Weapon对象的Id,hashCode对于每一个对象而言都是惟一的。
    }

    public void attack() {
        if (this.weaponMain.hashCode() != weaponMainId) { //关键位置检查是否遭到替换
            throw new IllegalAccessError(String.format("警告!遭到入侵!入侵者身份:%d", this.weaponMain.hashCode()));
        }
        weaponMain.attack();
    }
}

(2) GamePro 确认是否还可以hook 入侵

package com.example.javalearnproject.reflectbasictest.hook.hookprotect;

import com.example.javalearnproject.reflectbasictest.hook.Weapon;
import com.example.javalearnproject.reflectbasictest.hook.dohook.WeaponHook;

import java.lang.reflect.Field;

public class GamePro {
    public static void main(String[] args){
        HeroPro hero = new HeroPro(new Weapon());
        try {
            //Field weapon = ReflectUtils.getVariable(hero.getClass(), "weaponMain");
            Class<?> heroClass = hero.getClass();
            //Field weapon = heroClass.getField("weaponMain");// 不包含私有属性
            Field weapon = heroClass.getDeclaredField("weaponMain");// 包含私有属性
            weapon.setAccessible(true);
            Weapon weaponHook = new WeaponHook();
            ((WeaponHook) weaponHook).setOnUseWeaponAttackListener(damage -> {
                //经过后门进行操做,这其实就是咱们注入的代码
                System.out.println("damage = " + damage);
                return damage;
            });
            weapon.set(hero, weaponHook); //偷天换日  >>> 关键操作,替换掉原来的成员变量  (属性.set(对象,新的成员变量的对象))
            hero.attack(); //最终调用的是 weaponHook
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

(3) WeaponHook保持不变

输出(Exception):

Exception in thread "main" java.lang.IllegalAccessError: 警告!遭到入侵!入侵者身份:1060830840
    at com.example.javalearnproject.reflectbasictest.hook.hookprotect.HeroPro.attack(HeroPro.java:16)
    at com.example.javalearnproject.reflectbasictest.hook.hookprotect.GamePro.main(GamePro.java:24)

-- End --

上一篇 下一篇

猜你喜欢

热点阅读