/// <summary> /// Injects a CALL instruction into the given <see cref="ILRef"/>. If <paramref name="insert"/> is false, it will replace the target instruction. /// If <paramref name="insert"/> is true, then it will insert the new instruction and push forward the instructions at /// and after the given <see cref="ILRef"/> /// </summary> public ILRef InjectHook(ILRef target, MethodBase hook, bool insert = true) { if (target == null) { throw new ArgumentNullException("target"); } if (hook == null) { throw new ArgumentNullException("hook"); } if (!target.Valid) { throw new InvalidOperationException("Cannot use an invalid ILRef!"); } int targetIndex = target.Index; CodeInstruction targetInsn = GetInsn(target); if (insert) { ShiftInsns(target, 1); } Insns[targetIndex] = new CodeInstruction(hook.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, hook); Insns[targetIndex].labels.AddRange(targetInsn.labels); targetInsn.labels.Clear(); UpdateMetadata(); return(insert ? GetRefByIndex(targetIndex) : target); }
/// <summary> /// Injects an instruction into the given <see cref="ILRef"/>. If <paramref name="insert"/> is false, it will replace the target instruction. /// If <paramref name="insert"/> is true, then it will insert the new instruction and push forward the instructions at /// and after the given <see cref="ILRef"/> /// </summary> public ILRef InjectInsn(ILRef target, CodeInstruction insn, bool insert = true) { if (target == null) { throw new ArgumentNullException("target"); } if (insn == null) { throw new ArgumentNullException("insn"); } if (!target.Valid) { throw new InvalidOperationException("Cannot use an invalid ILRef!"); } int targetIndex = target.Index; CodeInstruction targetInsn = GetInsn(target); insn.labels.AddRange(targetInsn.labels); targetInsn.labels.Clear(); if (insert) { ShiftInsns(target, 1); } Insns[targetIndex] = new CodeInstruction(insn); UpdateMetadata(); return(insert ? GetRefByIndex(targetIndex) : target); }
/// <summary> /// Injects an elseif structure onto the end of a specified if block specified by the <see cref="ILRef"/> that is targeting a conditional branch instruction. /// The returned <see cref="ILRef"/>s will point to NOPs you can use and/or replace. /// </summary> public void InjectElseIf(ILRef ifBranch, OpCode conditionCode, out ILRef conditionBlock, out ILRef codeBlock) { if (ifBranch == null) { throw new ArgumentNullException("ifBranch"); } if (!ifBranch.Valid) { throw new InvalidOperationException("Cannot use an invalid ILRef!"); } CodeInstruction brCode = GetInsn(ifBranch); if (brCode.opcode.FlowControl != FlowControl.Cond_Branch) { throw new InvalidOperationException("ifBranch must point to a conditional branch instruction!"); } Label exitLabel = (Label)brCode.operand; Label conditionLabel = ILGen.DefineLabel(); brCode.operand = conditionLabel; ILRef exitRef = FindRefByLabel(exitLabel); InjectInsn(exitRef, new CodeInstruction(OpCodes.Br, exitLabel), true); CodeInstruction conditionInsn = new CodeInstruction(conditionCode, exitLabel); conditionBlock = InjectInsn(exitRef, new CodeInstruction(OpCodes.Nop), true); GetInsn(conditionBlock).labels.Add(conditionLabel); InjectInsn(exitRef, conditionInsn, true); codeBlock = InjectInsn(exitRef, new CodeInstruction(OpCodes.Nop), true); UpdateMetadata(); }
/// <summary> /// Dumps the instructions of this <see cref="CILProcessor"/> as a string, /// optionally with a given starting <see cref="ILRef"/> and count of instructions to print. /// lines are seperated by the \n character. /// </summary> public string DumpInstructions(ILRef start, int count = int.MaxValue) { if (!start.Valid) { throw new InvalidOperationException("Cannot use an invalid ILRef!"); } return(DumpInstructions(start?.Index ?? 0, count)); }
/// <summary> /// Finds the last instruction that has the given opcode. /// </summary> public ILRef FindLastRefByOpCode(OpCode code, ILRef startRef) { if (code == null) { throw new ArgumentNullException("code"); } int index = Insns.FindLastIndex(startRef.Index, x => x?.opcode == code); return(index >= 0 ? GetRefByIndex(index) : null); }
/// <summary> /// Finds the last instruction that matches the given instruction's opcode and operand. /// Ignores the instruction's labels and exception blocks. /// </summary> public ILRef FindLastRefByInsn(CodeInstruction insn, ILRef startRef) { if (insn == null) { throw new ArgumentNullException("insn"); } bool hasToString = insn.operand != null && !(insn.operand is Label) && insn.operand.GetType().GetMethods().Any(m => m.Name == "ToString" && m.GetParameters().Length == 0 && m.DeclaringType != typeof(object)); int index = Insns.FindLastIndex(startRef.Index, x => x?.opcode == insn.opcode && (insn.operand == null || (hasToString ? x?.operand?.ToString() == insn.operand?.ToString() : x?.operand is Label xLab && insn.operand is Label insnLab ? xLab == insnLab : x?.operand == insn.operand))); return(index >= 0 ? GetRefByIndex(index) : null); }
/// <summary> /// Finds the last instruction block that matches the given instructions' opcodes and operands. /// Ignores the instruction's labels and exception blocks. /// The <see cref="ILRef"/> points to the first instruction in the block. /// </summary> public ILRef FindLastRefByInsns(CodeInstruction[] searchInsns, ILRef startRef) { if (searchInsns == null) { throw new ArgumentNullException("searchInsns"); } EqualityComparison <CodeInstruction>[] comparers = searchInsns.Select(x => x == null ? (a, b) => true : x.operand == null ? (a, b) => a?.opcode == b?.opcode : !(x.operand is Label) && x.operand.GetType().GetMethods().Any(m => m.Name == "ToString" && m.GetParameters().Length == 0 && m.DeclaringType != typeof(object)) ? (a, b) => a?.opcode == b?.opcode && a?.operand?.ToString() == b?.operand?.ToString() : (EqualityComparison <CodeInstruction>)((a, b) => a?.opcode == b?.opcode && (a?.operand is Label aLab && b?.operand is Label bLab ? aLab == bLab : a?.operand == b?.operand))).ToArray(); int index = Insns.LastIndexOfSublist(searchInsns.ToList(), startRef.Index, comparers); return(index >= 0 ? GetRefByIndex(index) : null); }
/// <summary> /// Marks a label at the target instruction, for use in branch instructions. /// </summary> public Label MarkLabel(ILRef target) { Label l = ILGen.DefineLabel(); CodeInstruction insn = GetInsn(target); if (insn.labels == null) { insn.labels = new List <Label>(); } insn.labels.Add(l); return(l); }
private void ShiftInsns(ILRef target, int shift) { if (shift == 0) { return; } int targetIndex = target.Index; if (shift > 0) { Insns.InsertRange(targetIndex, new CodeInstruction[shift]); UpdateMetadata(); foreach (KeyValuePair <int, ILRef> r in ILRefs.Where(x => x.Key >= targetIndex).OrderByDescending(x => x.Key).ToList()) { r.Value.Shift(shift); ILRefs[r.Key + shift] = r.Value; ILRefs.Remove(r.Key); } } else if (target.Index + shift >= 0) { Label[] labels = Insns.GetRange(targetIndex + shift, -shift).SelectMany(x => x.labels).ToArray(); Insns.RemoveRange(targetIndex + shift, -shift); Insns.TrimExcess(); CodeInstruction firstInsn = Insns[targetIndex + shift]; foreach (Label label in labels) { firstInsn.labels.Add(label); } UpdateMetadata(); foreach (KeyValuePair <int, ILRef> r in ILRefs.Where(x => x.Key >= targetIndex + shift).OrderBy(x => x.Key).ToList()) { if (r.Key >= targetIndex) { r.Value.Shift(shift); ILRefs[r.Key + shift] = r.Value; ILRefs.Remove(r.Key); } else { r.Value.Invalidate(); } } } else { throw new InvalidOperationException("Cannot shift past the bottom of the insn list!"); } }
/// <summary> /// Injects a set of instructions into the block started by the given <see cref="ILRef"/>. If <paramref name="insert"/> is false, it will replace the target instructions. /// If <paramref name="insert"/> is true, then it will insert the new instructions and push forward the instructions at /// and after the given <see cref="ILRef"/> /// </summary> public ILRef InjectInsns(ILRef target, CodeInstruction[] newInsns, bool insert = true) { if (target == null) { throw new ArgumentNullException("target"); } if (newInsns == null) { throw new ArgumentNullException("newInsns"); } if (newInsns.Length < 1) { throw new ArgumentOutOfRangeException("newInsns"); } if (!target.Valid) { throw new InvalidOperationException("Cannot use an invalid ILRef!"); } int targetIndex = target.Index; CodeInstruction targetInsn = GetInsn(target); if (newInsns[0] != null) { newInsns[0].labels.AddRange(targetInsn.labels); targetInsn.labels.Clear(); } if (insert) { ShiftInsns(target, newInsns.Length); } if (Insns.Capacity < targetIndex + newInsns.Length) { Insns.Capacity = Math.Max(targetIndex + newInsns.Length, (int)Math.Pow(2, Math.Floor(Math.Log(Insns.Capacity, 2) + 1))); } for (int i = targetIndex; i < targetIndex + newInsns.Length; i++) { if (Insns[i] != null) { newInsns[i - targetIndex].labels.AddRange(Insns[i].labels); } Insns[i] = newInsns[i - targetIndex]; } UpdateMetadata(); return(insert ? GetRefByIndex(targetIndex) : target); }
/// <summary> /// Injects a LDFLD instruction into the given <see cref="ILRef"/>. Will fail to load a non-static field from a static method. /// If <paramref name="insert"/> is false, it will replace the target instruction. /// If <paramref name="insert"/> is true, then it will insert the new instruction and push forward the instructions at /// and after the given <see cref="ILRef"/> /// </summary> public ILRef InjectLoadField(ILRef target, FieldInfo field, bool insert = true) { if (target == null) { throw new ArgumentNullException("target"); } if (field == null) { throw new ArgumentNullException("hook"); } if (!target.Valid) { throw new InvalidOperationException("Cannot use an invalid ILRef!"); } int targetIndex = target.Index; CodeInstruction targetInsn = GetInsn(target); if (field.IsStatic) { if (insert) { ShiftInsns(target, 1); } Insns[targetIndex] = new CodeInstruction(OpCodes.Ldsfld, field); } else { if (insert) { ShiftInsns(target, 2); } else { ShiftInsns(target, 1); } Insns[targetIndex] = new CodeInstruction(OpCodes.Ldarg_0); Insns[targetIndex + 1] = new CodeInstruction(OpCodes.Ldfld, field); } Insns[targetIndex].labels.AddRange(targetInsn.labels); targetInsn.labels.Clear(); UpdateMetadata(); return(insert ? GetRefByIndex(targetIndex) : target); }
/// <summary> /// Simplifies needlessly complicated conditional branch structures. /// </summary> public ILRef SimplifyConditionalBranch(ILRef ifBranch) { if (ifBranch == null) { throw new ArgumentNullException("ifBranch"); } if (!ifBranch.Valid) { throw new InvalidOperationException("Cannot use an invalid ILRef!"); } CodeInstruction conditionalBranch = GetInsn(ifBranch); CodeInstruction nonconditionalBranch = GetInsn(ifBranch.GetRefByOffset(1)); if (conditionalBranch.opcode.FlowControl == FlowControl.Cond_Branch && nonconditionalBranch.opcode.FlowControl == FlowControl.Branch && GetInsn(ifBranch.GetRefByOffset(2)).labels.Contains((Label)conditionalBranch.operand) && conditionalBranch.opcode != GetReverseConditionalCode(conditionalBranch.opcode)) { conditionalBranch.opcode = GetReverseConditionalCode(conditionalBranch.opcode); conditionalBranch.operand = nonconditionalBranch.operand; RemoveInsn(ifBranch.GetRefByOffset(1)); } UpdateMetadata(); return(ifBranch); }
/// <summary> /// Removes the set of instructions in the block started by the given <see cref="ILRef"/>s. /// </summary> public void RemoveInsns(ILRef start, ILRef end) { ShiftInsns(end, end.Index - start.Index + 1); }
/// <summary> /// Removes the set of instructions in the block started by the given <see cref="ILRef"/>. /// Accepts values of <paramref name="count"/> higher than the number of instructions. /// </summary> public void RemoveInsns(ILRef target, int count) { ShiftInsns(target.GetRefByOffset(count), -count); }
/// <summary> /// Removes the instruction at the given <see cref="ILRef"/>. /// </summary> public void RemoveInsn(ILRef target) { RemoveInsns(target, 1); }
/// <summary> /// Returns an <see cref="ILRef"/> for the instruction with its index offset from the given <see cref="ILRef"/> /// </summary> public ILRef GetRefByOffset(ILRef current, int offset) { return(GetRefByIndex(current.Index + offset)); }
/// <summary> /// Gets the <see cref="CodeInstruction"/> referenced by the given <see cref="ILRef"/> /// </summary> public CodeInstruction GetInsn(ILRef target) { return(Insns[target.Index]); }