background

A lot of times systems hide things for security reasons, and sometimes we have to use those hidden things. Even more, we want to inject some of our own code into the system to make the program more flexible. There is just such a special callback pattern that the Hook pattern can achieve this vision.

Hook dynamically injected code

Hook mechanism is a kind of callback mechanism, ordinary callback is static, we must write callback interface in advance; The Hook mechanism in Java uses reflection to make code change at run time by substituting pointcuts (usually a member variable). This sounds abstract, but let’s take a quick look at the code.

  1. It should be a member variable, and its method should be called in the method we need to inject, or its value should be used;
  2. Create subclasses of objects that inherit from Hook points and modify their corresponding methods as required;
  3. Use reflection to replace objects created by ourselves with objects in the object instance.
public class Hero { private Weapon weaponMain; public Hero(Weapon weaponMain) { this.weaponMain = weaponMain; } public void attack(){ weaponMain.attack(); } } public class Weapon { int damage = 10; Public void attack(){system.out.println (string.format (" damage % D ",damage)); } } public class Game{ public static void main(String[] args){ Hero hero = new Hero(new Weapon()); hero.attack(); }} // For the above program, the game hid Weapon damage for us, but now we want to know the Weapon damage value every time we attack. // Let's see how to implement this using the Hook mechanism. // First we look and see that the entry point is weaponMain and we will attack it. // To create a Weapon replica, we need our own human Weapon hook. Weapon hook everything looks like Weapon, but we have a back door so we can monitor WeaponHook. public class WeaponHook extends Weapon{ private OnUseWeaponAttackListener onUseWeaponAttackListener; @Override public void attack(){ super.attack(); if (onUseWeaponAttackListener ! = null){ onUseWeaponAttackListener.onUseWeaponAttack(damage); } } public void setOnUseWeaponAttackListener(OnUseWeaponAttackListener onUseWeaponAttackListener) { this.onUseWeaponAttackListener = onUseWeaponAttackListener; } / / this is we the back door of the public static interface OnUseWeaponAttackListener {int onUseWeaponAttack (int damage). Public class Game{public static void main(String[] args){Hero Hero = new Hero(new Weapon()); try { Field weapon = ReflectUtils.getVariable(hero.getClass(), "weaponMain"); weapon.setAccessible(true); Weapon weaponHook = new WeaponHook(); (WeaponHook WeaponHook). SetOnUseWeaponAttackListener (damage - > {/ / through the back door, System.out.println("damage = "+ damage); return damage; }); weapon.set(hero, weaponHook); // Tou Tian hero. Attack (); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); }Hero hero = new Hero(new Weapon()); hero.attack(); Damage = 10 // We got the Weapon damage valueCopy the code

conclusion

Because the content is not much, I will not review the previous summary, we will look at a kind of idea to prevent Hook invasion. We add a checking mechanism to the Hero class.

public class Hero { private Weapon weaponMain; private final int weaponMainId; public Hero(Weapon weaponMain) { this.weaponMain = weaponMain; weaponMainId = this.weaponMain.hashCode(); // Record the Id of the original Weapon object. HashCode is unique for each object. } public void attack() { if (this.weaponMain.hashCode() ! = weaponMainId) {throw new IllegalAccessError(String. Format (" warning! Being invaded! %d", this.weaponmain.hashcode ())); } weaponMain.attack(); }}Copy the code

Now run the program again with the following output:

Java. Lang. IllegalAccessError: warning! Being invaded! Intruder id :1288141870Copy the code