diff --git a/tools/AVEVA.Historian.ReverseEngineering/Program.cs b/tools/AVEVA.Historian.ReverseEngineering/Program.cs index 2b87fc1..f93f3b1 100644 --- a/tools/AVEVA.Historian.ReverseEngineering/Program.cs +++ b/tools/AVEVA.Historian.ReverseEngineering/Program.cs @@ -1401,7 +1401,7 @@ static int InstrumentGrpcNonStream(string[] args) continue; } - // Input byte[] params are "System.Byte[]"; out/ref byte[] are "System.Byte[]&". + // ENTRY: log non-byref byte[] inputs ("System.Byte[]"). foreach (dnlib.DotNet.Parameter bufParam in method.Parameters .Where(p => !p.IsHiddenThisParameter && p.Type.FullName == "System.Byte[]") .ToArray()) @@ -1425,10 +1425,49 @@ static int InstrumentGrpcNonStream(string[] args) Type = type.Name.String, Method = method.Name.String, Phase = phase, - Param = bufParam.Name, + Direction = "in", Token = "0x" + method.MDToken.Raw.ToString("X8"), }); } + + // EXIT: log out/ref byte[] responses ("System.Byte[]&") before each ret. ldarg loads the + // managed pointer; ldind.ref dereferences it to the byte[]. (RPC wrappers set the out + // param right before a single ret, so branch-to-ret skew is not a concern here.) + dnlib.DotNet.Parameter[] outParams = method.Parameters + .Where(p => !p.IsHiddenThisParameter && p.Type.FullName == "System.Byte[]&") + .ToArray(); + if (outParams.Length > 0) + { + Instruction[] rets = method.Body.Instructions.Where(i => i.OpCode == OpCodes.Ret).ToArray(); + foreach (Instruction ret in rets) + { + var exit = new List(); + foreach (dnlib.DotNet.Parameter op in outParams) + { + exit.Add(Instruction.Create(OpCodes.Ldstr, $"{type.Name}.{method.Name}.{op.Name}.out")); + exit.Add(Instruction.Create(OpCodes.Ldarg, op)); + exit.Add(Instruction.Create(OpCodes.Ldind_Ref)); + exit.Add(Instruction.Create(OpCodes.Call, logByteArray)); + } + int retIndex = method.Body.Instructions.IndexOf(ret); + foreach (Instruction instruction in ((IEnumerable)exit).Reverse()) + { + method.Body.Instructions.Insert(retIndex, instruction); + } + } + method.Body.MaxStack = (ushort)Math.Max((int)method.Body.MaxStack, 8); + foreach (dnlib.DotNet.Parameter op in outParams) + { + instrumented.Add(new + { + Type = type.Name.String, + Method = method.Name.String, + Phase = $"{type.Name}.{method.Name}.{op.Name}.out", + Direction = "out", + Token = "0x" + method.MDToken.Raw.ToString("X8"), + }); + } + } } }