Example #1
0
        internal MethodRedirection(MethodBase original, MethodBase replacement, bool start)
        {
            Original    = original;
            Replacement = replacement;

            // Note: I'm making local copies of the following fields to avoid accessing fields multiple times.
            RuntimeMethodHandle originalHandle    = original.GetRuntimeMethodHandle();
            RuntimeMethodHandle replacementHandle = replacement.GetRuntimeMethodHandle();

            const string JIT_ERROR = "The specified method hasn't been jitted yet, and thus cannot be used in a redirection.";

            // Fetch their respective start
            IntPtr originalStart    = originalHandle.GetMethodStart();
            IntPtr replacementStart = replacementHandle.GetMethodStart();

            // Edge case: calling this on the same method
            if (originalStart == replacementStart)
            {
                throw new InvalidOperationException("Cannot redirect a method to itself.");
            }

            // Edge case: methods are too close to one another
            int difference = (int)Math.Abs(originalStart.ToInt64() - replacementStart.ToInt64());
            int sizeOfPtr  = IntPtr.Size;

            if (difference <= Helpers.PatchSize)
            {
                throw new InvalidOperationException("Unable to redirect methods whose bodies are too close to one another.");
            }

            // Make sure they're jitted
            if (!originalStart.HasBeenCompiled())
            {
                if (!Helpers.TryPrepareMethod(original, originalHandle))
                {
                    throw new ArgumentException(JIT_ERROR, nameof(original));
                }

                originalStart = originalHandle.GetMethodStart();
            }

            if (!replacementStart.HasBeenCompiled())
            {
                if (!Helpers.TryPrepareMethod(replacement, replacementHandle))
                {
                    throw new ArgumentException(JIT_ERROR, nameof(replacement));
                }

                replacementStart = replacementHandle.GetMethodStart();
            }

            // Copy local value to field
            originalMethodStart = originalStart;

            // In some cases, the memory might need to be readable / writable:
            // Make the memory region rw right away just in case.
            Helpers.AllowRW(originalStart);

            // Save bytes to change to redirect method
            byte[] replBytes = replacementBytes = Helpers.GetJmpBytes(replacementStart);
            byte[] origBytes = originalBytes = new byte[replBytes.Length];

            Marshal.Copy(originalStart, origBytes, 0, origBytes.Length);

            if (start)
            {
                CopyToStart(replBytes, originalStart);
                isRedirecting = true;
            }

            // Save methods in static array to make sure they're not garbage collected
            PersistingMethods.Add(original);
            PersistingMethods.Add(replacement);
        }