Пример #1
0
        public void Enable(bool enable = true)
        {
            MemoryProtection oldProtect;
            long             patchSize   = Marshal.SizeOf(typeof(JMPCALL_REL));
            IntPtr           patchTarget = trampoline.Target;

            if (trampoline.PatchAbove)
            {
                patchTarget = patchTarget - Marshal.SizeOf(typeof(JMPCALL_REL));
                patchSize  += Marshal.SizeOf(typeof(JMP_REL_SHORT));
            }

            if (!VirtualProtect(patchTarget, (UIntPtr)patchSize, MemoryProtection.ExecuteReadWrite, out oldProtect))
            {
                throw new Exception("Failed to change memory protection");
            }

            if (enable)
            {
                JMPCALL_REL jmp = new JMPCALL_REL(0xE9);

                if (IntPtr.Size == 8)
                {
                    long rel = ((long)trampoline.Relay - ((long)patchTarget + Marshal.SizeOf(jmp)));
                    jmp.SetOperand((uint)rel);
                }
                else
                {
                    jmp.SetOperand((uint)((ulong)trampoline.Detour - ((ulong)patchTarget + (ulong)Marshal.SizeOf(jmp))));
                }

                Marshal.StructureToPtr(jmp, patchTarget, false);

                if (trampoline.PatchAbove)
                {
                    JMP_REL_SHORT shortJmp = new JMP_REL_SHORT(0xEB, (byte)(0 - (Marshal.SizeOf(typeof(JMP_REL_SHORT)) + Marshal.SizeOf(jmp))));
                    Marshal.StructureToPtr(shortJmp, trampoline.Target, false);
                }
            }
            else
            {
                if (trampoline.PatchAbove)
                {
                    Marshal.Copy(trampoline.Backup, 0, patchTarget, Marshal.SizeOf(typeof(JMPCALL_REL)) + Marshal.SizeOf(typeof(JMP_REL_SHORT)));
                }
                else
                {
                    Marshal.Copy(trampoline.Backup, 0, patchTarget, Marshal.SizeOf(typeof(JMPCALL_REL)));
                }
            }

            VirtualProtect(patchTarget, (UIntPtr)patchSize, oldProtect, out _);

            //TODO: flush instruction cache
            Enabled = true;
        }
Пример #2
0
        public Trampoline(IntPtr target, IntPtr detour, MemorySlot memorySlot)
        {
            Target          = target;
            Detour          = detour;
            Tramp           = memorySlot.Address;
            this.memorySlot = memorySlot;

            uint   oldPos   = 0;
            uint   newPos   = 0;
            IntPtr jmpDest  = IntPtr.Zero;
            IntPtr instBuf  = Marshal.AllocHGlobal(16);
            bool   finished = false;
            bool   is64     = IntPtr.Size == 8;
            object jmp;
            object call;
            object jcc;

            if (is64)
            {
                call = new CALL_ABS(IntPtr.Zero);
                jmp  = new JMP_ABS(IntPtr.Zero);
                jcc  = new JCC_ABS(IntPtr.Zero);
            }
            else
            {
                call = new JMPCALL_REL(0xE8);
                jmp  = new JMPCALL_REL(0xE9);
                jcc  = new JCC_REL(0);
            }

            do
            {
                object srcObj = null;
                uint   copySize;
                IntPtr copySrc;
                IntPtr oldInst = (IntPtr)((long)Target + oldPos);
                IntPtr newInst = (IntPtr)((long)Tramp + newPos);

                Disassembler disasm = new Disassembler(oldInst, 16, is64 ? ArchitectureMode.x86_64 : ArchitectureMode.x86_32, 0, true);
                disasm.Disassemble();

                Instruction inst = disasm.NextInstruction();

                if (inst.Error)
                {
                    throw new Exception(inst.ErrorMessage);
                }

                copySrc  = oldInst;
                copySize = (uint)inst.Length;

                if (oldPos >= Marshal.SizeOf(typeof(JMPCALL_REL)))
                {
                    if (is64)
                    {
                        ((AddressSupport)jmp).SetAddress(oldInst);
                    }
                    else
                    {
                        ((OperandSupport)jmp).SetOperand((uint)((uint)oldInst - ((uint)newInst + Marshal.SizeOf(jmp))));
                    }

                    srcObj   = jmp;
                    copySize = (uint)Marshal.SizeOf(jmp);
                    finished = true;
                }
                else if (is64 && inst.HasModRM && ((inst.ModRM & 0xC7) == 0x5))
                {
                    byte modRMReg = (byte)((inst.ModRM & 0x3f) >> 3);
                    copySrc = instBuf;
                    CopyMemory(instBuf, oldInst, (int)copySize);

                    //TODO: figure out how to calculate imm length with udisasm engine
                    uint   immLen     = 0;
                    IntPtr relAddr    = (IntPtr)((long)instBuf + inst.Length - immLen - 4);
                    uint   updatedRel = (uint)((ulong)oldInst + (ulong)inst.Length + inst.Operands[0].LvalUDWord - ((ulong)newInst + (ulong)inst.Length));
                    Marshal.WriteInt32(relAddr, (int)updatedRel);

                    // Complete function if JMP (FF /4)
                    if (inst.Bytes[0] == 0xFF & modRMReg == 4)
                    {
                        finished = true;
                    }
                }
                else if (inst.Mnemonic == ud_mnemonic_code.UD_Icall)
                {
                    IntPtr dest = (IntPtr)((long)oldInst + inst.Length + inst.Operands[0].LvalSDWord);

                    if (is64)
                    {
                        ((AddressSupport)call).SetAddress(dest);
                    }
                    else
                    {
                        ((OperandSupport)call).SetOperand((uint)dest - (uint)(newInst + Marshal.SizeOf(call)));
                    }

                    srcObj   = call;
                    copySize = (uint)Marshal.SizeOf(call);
                }
                else if ((inst.Bytes[0] & 0xFD) == 0xE9)
                {
                    IntPtr dest = oldInst + inst.Length;

                    if ((inst.Bytes[0] == 0xE9))   //short JMP
                    {
                        dest = IntPtr.Add(dest, inst.Operands[0].LvalByte);
                    }
                    else
                    {
                        dest = IntPtr.Add(dest, inst.Operands[0].LvalSDWord);
                    }

                    if ((long)Target <= (long)dest && (long)dest < ((long)Target + Marshal.SizeOf(typeof(JMPCALL_REL))))
                    {
                        if ((long)jmpDest < (long)dest)
                        {
                            jmpDest = dest;
                        }
                    }
                    else
                    {
                        if (is64)
                        {
                            ((AddressSupport)jmp).SetAddress(dest);
                        }
                        else
                        {
                            ((OperandSupport)jmp).SetOperand((uint)((uint)dest - ((uint)newInst + Marshal.SizeOf(jmp))));
                        }

                        srcObj   = jmp;
                        copySize = (uint)Marshal.SizeOf(jmp);
                        finished = ((long)oldInst >= (long)jmpDest);
                    }
                }
                else if (inst.Length >= 2 &&
                         ((inst.Bytes[0] & 0xF0) == 0x70) ||
                         ((inst.Bytes[0] & 0xFC) == 0xE0) ||
                         (inst.Bytes[0] == 0xF0 && (inst.Bytes[1] & 0xF0) == 0x80))
                {
                    //Jcc Handler

                    IntPtr dest = oldInst + inst.Length;

                    if ((inst.Bytes[0] & 0xF0) == 0x70 ||
                        (inst.Bytes[0] & 0xFC) == 0xE0)
                    {
                        dest = IntPtr.Add(dest, inst.Operands[0].LvalByte);
                    }
                    else
                    {
                        dest = IntPtr.Add(dest, (int)inst.Operands[0].LvalUDWord);
                    }

                    if ((long)Target <= (long)dest && (long)dest < ((long)Target + Marshal.SizeOf(typeof(JMPCALL_REL))))
                    {
                        if ((long)jmpDest < (long)dest)
                        {
                            jmpDest = dest;
                        }
                    }
                    else if ((inst.Bytes[0] & 0xFC) == 0xE0)
                    {
                        throw new Exception("LOOPx/Jxx to the outside not supported");
                    }
                    else
                    {
                        byte cond = (byte)((inst.Bytes[0] != 0x0F ? inst.Bytes[0] : inst.Bytes[1]) & 0xF0);

                        if (is64)
                        {
                            ((OpcodeSupport)jcc).SetOpcode((byte)(0x71 ^ cond));
                            ((AddressSupport)jcc).SetAddress(dest);
                        }
                        else
                        {
                            ((OpcodeSupport)jcc).SetOpcode((byte)(0x80 ^ cond));
                            ((OperandSupport)jcc).SetOperand((uint)((uint)dest - ((uint)newInst + Marshal.SizeOf(jcc))));
                        }

                        srcObj   = jcc;
                        copySize = (uint)Marshal.SizeOf(jcc);
                    }
                }
                else if (inst.Mnemonic == ud_mnemonic_code.UD_Iret || inst.Mnemonic == ud_mnemonic_code.UD_Iretf)
                {
                    finished = ((long)oldInst >= (long)jmpDest);
                }

                if ((long)oldInst < (long)jmpDest && copySize != inst.Length)
                {
                    throw new Exception("Can't alter intruction length in a branch");
                }

                if ((newPos + copySize) > TrampolineMaxSize)
                {
                    throw new Exception("Trampoline function too big");
                }

                if (InstructionBoundaries > OldInstructionBoundaries.Length)
                {
                    throw new Exception("Trampoline has too many instructions");
                }

                OldInstructionBoundaries[InstructionBoundaries] = (byte)oldPos;
                NewInstructionBoundaries[InstructionBoundaries] = (byte)newPos;
                InstructionBoundaries++;

                if (srcObj != null)
                {
                    IntPtr data = Marshal.AllocHGlobal((int)copySize);
                    Marshal.StructureToPtr(srcObj, data, false);
                    CopyMemory((IntPtr)((long)Tramp + newPos), data, (int)copySize);
                    Marshal.FreeHGlobal(data);
                }
                else
                {
                    CopyMemory((IntPtr)((long)Tramp + newPos), copySrc, (int)copySize);
                }

                newPos += copySize;
                oldPos += (uint)inst.Length;
            } while (!finished);


            if (oldPos < Marshal.SizeOf(typeof(JMPCALL_REL))

                && IsCodePadding((IntPtr)((long)Target + oldPos), (int)(Marshal.SizeOf(typeof(JMPCALL_REL)) - oldPos)))
            {
                if (oldPos < Marshal.SizeOf(typeof(JMP_REL_SHORT)) &&
                    IsCodePadding((IntPtr)((long)Target + oldPos), (int)(Marshal.SizeOf(typeof(JMP_REL_SHORT)) - oldPos)))
                {
                    throw new Exception("Not enough space for patch");
                }

                if (!IsExecutableAddress((IntPtr)((long)Target - Marshal.SizeOf(typeof(JMPCALL_REL)))))
                {
                    throw new Exception("Not enought space for patch");
                }

                if (!IsCodePadding((IntPtr)((long)Target - Marshal.SizeOf(typeof(JMPCALL_REL))), Marshal.SizeOf(typeof(JMPCALL_REL))))
                {
                    throw new Exception("Not enought space for patch");
                }

                PatchAbove = true;
            }

            if (is64)
            {
                ((AddressSupport)jmp).SetAddress(Detour);
                Relay = (IntPtr)((long)Tramp + newPos);

                IntPtr data = Marshal.AllocHGlobal((int)Marshal.SizeOf(jmp));
                Marshal.StructureToPtr(jmp, data, false);
                CopyMemory(Relay, data, Marshal.SizeOf(jmp));
                Marshal.FreeHGlobal(data);
            }

            BackupPrologue();
        }