/// <summary> /// This method starts from an API call instruction and goes backward to locate the instruction that /// pushes the "this" object into stack. We insert out instrumentation patch at that point so that /// the interception method can access the "this" object. After the interception method ends, we /// push the "this" object to stack again so that the original API call can continue. /// </summary> /// <param name="callInstruction">Instruction that calls the API.</param> /// <returns>The instruction that pushes the "this" object into stack.</returns> private Instruction LocateLoadThisInstruction(Instruction callInstruction) { Tuple <int, int> poppedPushed = MSILHelper.GetStackTransition(callInstruction); int toPop = poppedPushed.Item1; Instruction current = callInstruction.Previous; while (toPop > 1) { poppedPushed = MSILHelper.GetStackTransition(current); toPop += poppedPushed.Item1; toPop -= poppedPushed.Item2; current = current.Previous; } return(current); }
private bool InstrumentMethodCallInstruction(MethodDefinition method, Instruction instruction) { var methodName = $"{method.DeclaringType.FullName}::{method.Name}"; var processor = method.Body.GetILProcessor(); var calleeRef = (MethodReference)instruction.Operand; var calleeName = $"{calleeRef.DeclaringType.FullName}::{calleeRef.Name}"; if (Constants.MethodPrefixBlackList.Any(x => calleeName.StartsWith(x))) { return(false); } if (calleeRef.DeclaringType.IsValueType) { return(false); // method in struct } var patchTarget = instruction; var isValueType = false; if (patchTarget.Previous != null && patchTarget.Previous.OpCode == OpCodes.Constrained) { patchTarget = patchTarget.Previous; if (patchTarget.Operand is GenericInstanceType) { isValueType |= ((GenericInstanceType)patchTarget.Operand).IsValueType; } else if (patchTarget.Operand is TypeReference) { var typeRef = (TypeReference)patchTarget.Operand; isValueType |= typeRef.IsValueType; } } if (isValueType) { return(false); // method in struct } var signature = calleeRef.GetResolvedMethodSignature(); var isNewObj = instruction.OpCode == OpCodes.Newobj; Instruction patchStart = null; VariableDefinition instanceVarDef = null; if (!isNewObj && calleeRef.HasThis) { instanceVarDef = new VariableDefinition(method.Module.TypeSystem.Object); method.Body.Variables.Add(instanceVarDef); var loadThisInstruction = MSILHelper.LocateLoadThisInstruction(processor, instruction); if (loadThisInstruction == null) { return(false); } var patch1 = new List <Instruction>() { processor.Create(OpCodes.Dup), processor.Create(OpCodes.Stloc, instanceVarDef), }; processor.InsertAfter(loadThisInstruction, patch1); patchStart = patch1[0]; } VariableDefinition contextVarDef = null; var afterMethodCallbackWillBeCalled = calleeRef.Name.Equals("Dispose"); if (afterMethodCallbackWillBeCalled) { contextVarDef = new VariableDefinition(method.Module.TypeSystem.Object); method.Body.Variables.Add(contextVarDef); } var patch = this.GetPatchForBeforeMethodCall( processor, instanceVarDef, methodName, calleeRef, instruction.Offset, isValueType, contextVarDef); if (patchStart == null) { patchStart = patch[0]; } processor.InsertBefore(patchTarget, patch); method.UpdateInstructionReferences(patchTarget, patchStart, true); /* * if (contextVarDef != null) * { * patch = this.GetPatchForAfterMethodCall(processor, contextVarDef); * processor.InsertAfter(instruction, patch); * } */ return(true); }
public static Instruction LocateLoadThisInstruction(ILProcessor ilProcessor, Instruction callInstruction) { Dictionary <Instruction, int> stackSize = new Dictionary <Instruction, int>(); Tuple <int, int> poppedPushed = MSILHelper.GetStackTransition(callInstruction); int toPop = poppedPushed.Item1; stackSize[callInstruction] = toPop; if (callInstruction.Previous == null) { ilProcessor.InsertBefore(callInstruction, ilProcessor.Create(OpCodes.Nop)); } Instruction current = callInstruction.Previous; // fail if there current is a branch instruction if (current.OpCode.FlowControl != FlowControl.Next) { return(null); } while (toPop > 1) { if (IsStackEmptyingInstruction(current)) { Instruction branchSrc; Instruction branchDst; GetNextBranchDestinationInstruction(current, callInstruction, out branchSrc, out branchDst); if (branchDst != null) { current = branchSrc; toPop = stackSize[branchDst]; } } poppedPushed = MSILHelper.GetStackTransition(current); toPop += poppedPushed.Item1; toPop -= poppedPushed.Item2; stackSize[current] = toPop; if (current.Previous == null) { ilProcessor.InsertBefore(current, ilProcessor.Create(OpCodes.Nop)); } current = current.Previous; // fail if there current is a branch instruction if (current.OpCode.FlowControl != FlowControl.Next) { return(null); } } if (current.OpCode == OpCodes.Constrained) { current = current.Previous; // fail if there current is a branch instruction if (current.OpCode.FlowControl != FlowControl.Next) { return(null); } } return(current); }