InjectFix学习笔记

1.InjectFix 工作流程

  • 打新包
    • 其他预处理
    • 预先配置好需要Patch的类跟函数,提交配置Configure代码
    • 打包机打包时,自动reload被Inject的dll,保证当前的dll没有被注入过
    • 调用InjectFix提供的注入函数,注入WrappersManagerImpl,ILFixInterfaceBridge,ILFixDynamicMethodWrapper,IDMAP-1跟插桩代码dll
    • 执行il2cpp处理
    • 执行其他预处理
  • Patch阶段
    • 修复代码
      • 修复函数:使用标签[IFix.Patch]
      • 新增类、属性、方法:使用标签[IFix.Interpret]
    • 调用InjectFix提供的打Patch函数,生成Patch文件
    • 生成Patch文件的AssetsBuddle包
    • 上传更新过后的AssetsBuddle包

ps:在patch过程中,如果没有重新打出新的包体,不要删除Patch标签

2.InjectFix 工作原理

2.1 Inject阶段

2.1.1 [IFix]跟[IFix.Filter]标签

在Inject阶段,预先对有可能需要修复的类跟函数提交配置类,调用Inject处理函数时,会对这些写上标签的函数注入插桩代码,例如有如下代码,其中Add跟Sub目前逻辑是错误的,是需要修复的函数:

namespace IFix.Test
{
    public class Calculator
    {
        public int Add(int a, int b)
        {
            return a * b;
        }
		
        public int Sub(int a, int b)
        {
            return a / b;
        }
		
        public int Mult(int a, int b)
        {
            return a * b;
        }
		
        public int Div(int a, int b)
        {
            return a / b;
        }
    }
}

打包时需要配置对应的配置类,具体配置Configure类如下,其中 [Configure] 标签是配置类的标签,用来配置注入阶段需要注入或者过滤的内容,配置类必须放在Editor目录下。

  • [IFix] 标签 :用来配置需要注入的类型
  • [IFix.Filter] 标签 :用来过滤不需要的字段
[Configure]
public class HelloworldCfg
{
    [IFix]
    static IEnumerable<Type> hotfix
    {
        get
        {
            return new List<Type>()
            {
                typeof(IFix.Test.Calculator),
            };
        }
    }

    [IFix.Filter]
    static bool Filter(System.Reflection.MethodInfo methodInfo)
    {
        return methodInfo.DeclaringType.FullName == "IFix.Test.Calculator" 
            && (methodInfo.Name == "Div" || methodInfo.Name == "Mult");
    }
}

使用ILSpy工具查看注入后dll,看到生成如下代码,其中AddSub方法增加了注入代码,MultDiv函数因为在配置类中被过滤了,没有注入代码。

public class Calculator
{
    public int Add(int a, int b)
    {
        if (IFix.WrappersManagerImpl.IsPatched(6))
        {
            return IFix.WrappersManagerImpl.GetPatch(6).__Gen_Wrap_1(this, a, b);
        }
        return a * b;
    }

    public int Sub(int a, int b)
    {
        if (IFix.WrappersManagerImpl.IsPatched(7))
        {
            return IFix.WrappersManagerImpl.GetPatch(7).__Gen_Wrap_1(this, a, b);
        }
        return a / b;
    }

    public int Mult(int a, int b)
    {
        return a * b;
    }

    public int Div(int a, int b)
    {
        return a / b;
    }
}

除了插桩代码,注入阶段还会注入几个相关的代码,其中IDMAP0是修复函数的索引ID,当索引值超过32760时,会生成下一个IDMAP1,依次类推。

public enum IDMAP0
{
    IFix-Test-Calculator-Add0 = 6,
    IFix-Test-Calculator-Sub0,
    Helloworld-test0 = 5,
    Helloworld-Start0 = 0
}

其中的GetPatch(6)中的参数6对应到注入代码中IDMAP0中IFix-Test-Calculator-Add0

注入WrappersManagerImpl类代码

public class WrappersManagerImpl : WrappersManager
{
    private VirtualMachine virtualMachine;

    public WrappersManagerImpl(VirtualMachine virtualMachine)
    {
        this.virtualMachine = virtualMachine;
    }

    public static ILFixDynamicMethodWrapper GetPatch(int id)
    {
        return ILFixDynamicMethodWrapper.wrapperArray[id];
    }

    public static bool IsPatched(int id)
    {
        return id < ILFixDynamicMethodWrapper.wrapperArray.Length && ILFixDynamicMethodWrapper.wrapperArray[id] != null;
    }

    public System.Delegate CreateDelegate(System.Type type, int id, object anon)
    {
        ILFixDynamicMethodWrapper iLFixDynamicMethodWrapper = new ILFixDynamicMethodWrapper(this.virtualMachine, id, anon);
        return Utils.TryAdapterToDelegate(iLFixDynamicMethodWrapper, type, "__Gen_Wrap_");
    }

    public object CreateWrapper(int id)
    {
        return new ILFixDynamicMethodWrapper(this.virtualMachine, id, null);
    }

    public object InitWrapperArray(int len)
    {
        ILFixDynamicMethodWrapper.wrapperArray = new ILFixDynamicMethodWrapper[len];
        return ILFixDynamicMethodWrapper.wrapperArray;
    }

    public AnonymousStorey CreateBridge(int fieldNum, int[] fieldTypes, int typeIndex, int[] vTable, int[] slots, VirtualMachine virtualMachine)
    {
        return new ILFixInterfaceBridge(fieldNum, fieldTypes, typeIndex, vTable, slots, virtualMachine);
    }
}

其中使用到的iLFixDynamicMethodWrapper的代码如下,其中 __Gen_Wrap_X 函数会根据注入时函数的参数个数,类型来生成,相同的参数只生成一个对应的 __Gen_Wrap_X 方法,例子中的Add,Sub方法共用了 __Gen_Wrap_1 方法

public class ILFixDynamicMethodWrapper
{
    private VirtualMachine virtualMachine;

    private int methodId;

    private object anonObj;

    public static ILFixDynamicMethodWrapper[] wrapperArray;

    public ILFixDynamicMethodWrapper(VirtualMachine virtualMachine, int methodId, object anonObj)
    {
        this.virtualMachine = virtualMachine;
        this.methodId = methodId;
        this.anonObj = anonObj;
    }

    public void __Gen_Wrap_0(string P0)
    {
        Call call = Call.Begin();
        if (this.anonObj != null)
        {
            call.PushObject(this.anonObj);
        }
        call.PushObject(P0);
        this.virtualMachine.Execute(this.methodId, ref call, (this.anonObj != null) ? 2 : 1, 0);
    }

    public int __Gen_Wrap_1(object P0, int P1, int P2)
    {
        Call call = Call.Begin();
        if (this.anonObj != null)
        {
            call.PushObject(this.anonObj);
        }
        call.PushObject(P0);
        call.PushInt32(P1);
        call.PushInt32(P2);
        this.virtualMachine.Execute(this.methodId, ref call, (this.anonObj != null) ? 4 : 3, 0);
        return call.GetInt32(0);
    }

    public int __Gen_Wrap_2(int P0)
    {
        Call call = Call.Begin();
        if (this.anonObj != null)
        {
            call.PushObject(this.anonObj);
        }
        call.PushInt32(P0);
        this.virtualMachine.Execute(this.methodId, ref call, (this.anonObj != null) ? 2 : 1, 0);
        return call.GetInt32(0);
    }

    public void __Gen_Wrap_3(object P0)
    {
        Call call = Call.Begin();
        if (this.anonObj != null)
        {
            call.PushObject(this.anonObj);
        }
        call.PushObject(P0);
        this.virtualMachine.Execute(this.methodId, ref call, (this.anonObj != null) ? 2 : 1, 0);
    }

    static ILFixDynamicMethodWrapper()
    {
        ILFixDynamicMethodWrapper.wrapperArray = new ILFixDynamicMethodWrapper[0];
    }
}

如果Patch阶段,有对Add方法进行修复,生成Patch,则执行逻辑会通过IFix.WrappersManagerImpl.GetPatch(6).__Gen_Wrap_1(this, a, b)调用ILFixDynamicMethodWrapper 中的__Gen_Wrap_1函数,最终调用virtualMachine的函数,通过IFix内部实现的虚拟机,解析执行Patch中的IL指令,执行修复后的代码逻辑,最后然后通过 call.GetInt32(0) 获取计算返回的结果,将正确的值返回。

2.1.2 [IFix.CustomBridge] :interface和delegate桥接

在注入阶段使用,用来把一个在虚拟机上的类适配到原生interface或者把一个虚拟机的函数适配到原生delegate。
- 修复代码赋值一个闭包到一个delegate变量;
- 修复代码的Unity协程用了yield return;
- 新增一个函数,赋值到一个delegate变量;
- 新增一个类,赋值到一个原生interface变量;
- 新增函数,用了yield return;

例如,原生类代码如下:

public interface ISubSystem
{
    bool running { get; }
    void Print();
}

public class Test 
{
    public delegate int MyDelegate(int a, int b);
}

配置类代码如下:

[IFix.CustomBridge]
public static class AdditionalBridge
{
    static List<Type> bridge = new List<Type>()
    {
        typeof(ISubSystem),
        typeof(IEnumerator),
        typeof(Test.MyDelegate)
    };
}

新增函数(或者修复代码[IFix.Patch]的Unity协程),用到了 yield return

[IFix.Interpret]
public IEnumerator TestInterface()
{
    yield return new WaitForSeconds(1);
    UnityEngine.Debug.Log("wait one second");
}

新增函数(或者修复代码[IFix.Patch]),赋值到一个delegate变量

public class Test 
{
    public delegate int MyDelegate(int a, int b);
    
    [IFix.Interpret]
    public MyDelegate TestDelegate()
    {
        return (a,b) => a + b;
    }
}

新增一个类,该类实现了一个接口

[IFix.Interpret]
public class SubSystem : ISubSystem
{
    public bool running { get { return true; } }
    public void Print()
    {
        UnityEngine.Debug.Log("SubSystem1.Print");
    }
}

注入dll后,使用ILSpy能看到dll中有注入的类ILFixInterfaceBridge

2.2 Patch阶段

Patch阶段针对修复内容使用对应的Tag

  • 修复方法 :[IFix.Patch]
  • 新增方法、类型 :[IFix.Interpret]

2.2.1 修复方法

需要修复的方法,再Patch阶段加上[Patch]标签,在执行生成Patch时,被修复的方法就会被写到生成的Patch里,例如,将Add方法从原先的 a * b 改成正确的逻辑 a + b

[Patch]
public int Add(int a, int b)
{
    return a + b;
}

不支持修复泛型函数,不支持修复构造函数

2.2.2 新增函数、类型

  • 新增一个函数
[IFix.Interpret]
public bool Greater(int a,int b)
{
    return a > b;
}
  • 新增一个类
[IFix.Interpret]
public class NewClass
{
    ...
}
  • 新增一个属性
private string name;//这个name字段是原生的

public string Name
{
    [IFix.Interpret]
    set
    {
    	name = value;    
    }
    [IFix.Interpret]
    get
    {
        return name;
    }
}

不支持继承原生类,泛型类,不支持在原生类中新增字段