Пример #1
0
        // returns true if new breakpoint was set
        public TraceReturn Log(ContextManager cm, Logger logFile, Process process, int threadId, Win32Imports.ContextX64 context, bool trace)
        {
            if (!trace)
            {
                // end trace
                return(new TraceReturn {
                    StepOver = false
                });
            }
            // http://stackoverflow.com/questions/14698350/x86-64-asm-maximum-bytes-for-an-instruction

            var breakAddress = context.Rip;

            var mem      = DebugProcessUtils.ReadBytes(process, breakAddress, AssemblyUtil.MaxInstructionBytes);
            var distance = (long)(breakAddress - cm.LastBreakAddress);

            var decodedInstruction = AssemblyUtil.Disassemble(process, breakAddress);
            var hex = DebugProcessUtils.BytesToHex(mem.Take(decodedInstruction.Length).ToArray());

            var moduleAddressTuple = _importResolver.LookupAddress(breakAddress);
            var module             = moduleAddressTuple.Item1;
            var relativeAddress    = breakAddress - moduleAddressTuple.Item2;

            var hitCounts =
                _oldState.ContainsKey(threadId) ?
                _oldState[threadId].HitCounts :
                new Dictionary <ulong, ulong>();

            var stackDepth = 0;

            if (_oldState.ContainsKey(threadId))
            {
                stackDepth = _oldState[threadId].StackDepth;
            }
            if (decodedInstruction.Mnemonic == ud_mnemonic_code.UD_Icall)
            {
                stackDepth++;
            }
            else if (decodedInstruction.Mnemonic == ud_mnemonic_code.UD_Iret)
            {
                stackDepth--;
            }

            if (breakAddress == _importResolver.ResolveRelativeAddress(Specifics.StartAddress))
            {
                // reset trace state when we hit first breakpoint
                // except for oldHitCounts, which will be added back at end of function
                _oldState.Remove(threadId);
            }
            else if (breakAddress != cm.LastBreakAddress)
            {
#if UseDebugger
                // we're not interested in logging threads where breakpoint was not found
                if (!_oldState.ContainsKey(threadId))
                {
                    logFile.WriteLine($"ignoring trace for thread {threadId}");
                    return(new TraceReturn {
                        Ignore = true,
                        StepOver = false
                    });
                }
#endif
            }

            // IMPORTANT: continues the trace
            var   retVal = false;
            ulong lastCallAddressInTraceModule =
                _oldState.ContainsKey(threadId) ?
                _oldState[threadId].LastCallAddressInTraceModule :
                0;
            // clean up breakpoints as soon as they are hit
            cm.DisableBreakPointOnHit(breakAddress);
#if UseDebugger
            if (decodedInstruction.Mnemonic == ud_mnemonic_code.UD_Icall && !module.Equals(Specifics.TraceModuleName))
            {
                // step over call
                var returnAddress = breakAddress + (ulong)decodedInstruction.Length;
                logFile.WriteLine($"installing return breakpoint at {returnAddress:X}");
                cm.EnableBreakPoint(returnAddress, new ContextManager.BreakPointInfo {
                    Description = "return breakpoint"
                });
                stackDepth--;     // since the RET will not be executed
                retVal = true;
            }
            else
            {
                if (module.Equals(Specifics.TraceModuleName))
                {
                    if (decodedInstruction.Mnemonic == ud_mnemonic_code.UD_Iret)
                    {
                    }
                    else if (decodedInstruction.Mnemonic == ud_mnemonic_code.UD_Ijmp)
                    {
                    }
                    else
                    {
                        var cAddress = breakAddress + (ulong)decodedInstruction.Length;
                        logFile.WriteLine($"setting continuation at 0x{cAddress:X} for {decodedInstruction.Mnemonic} of size {decodedInstruction.Length}");
                        cm.EnableBreakPoint(cAddress, new ContextManager.BreakPointInfo {
                            Description = $"for {decodedInstruction.Mnemonic} of size {(ulong) decodedInstruction.Length}"
                        });
                    }

                    /*foreach (var condJump in new string[] {
                     *  "JE", "JZ", "JNE", "JNZ", "JG", "JNLE", "JGE", "JNL", "JL", "JNGE",
                     *  "JLE", "JNG", "JA", "JNBE", "JAE", "JNB", "JB", "JNAE", "JBE", "JNA" }) {
                     *  if (decodedInstruction.Mnemonic.Equals(condJump)) {
                     *      var nextTarget = breakAddress + decodedInstruction.Size;
                     *      var jumpMatch = Regex.Match("0x([a-z0-9]+)", decodedInstruction.Operands);
                     *      if (jumpMatch.Success) {
                     *          var offset = ulong.Parse(jumpMatch.Groups[0].Value, System.Globalization.NumberStyles.AllowHexSpecifier);
                     *          var jumpTarget = (ulong) (new BigInteger(breakAddress) + (long) offset);
                     *          //logFile.WriteLine($"jump calc: offset: {(long) offset}, target: 0x{jumpTarget:X}");
                     *      }
                     *
                     *      //cm.continuationBreakAddress +=
                     *      break;
                     *  }
                     * }*/
                    if (decodedInstruction.Mnemonic == ud_mnemonic_code.UD_Icall)
                    {
                        // step over instruction
                        lastCallAddressInTraceModule  = breakAddress;
                        lastCallAddressInTraceModule += (ulong)decodedInstruction.Length;
                    }
                    logFile.WriteLine($"setting next trace for {threadId}");
                    ContextManager.setTrace((uint)threadId);
                }
                else
                {
                    if (lastCallAddressInTraceModule == 0)
                    {
                        logFile.WriteLine("In external module, but no call to get back to.");
                    }
                    else if (!cm.BreakPoints.ContainsKey(lastCallAddressInTraceModule))
                    {
                        logFile.WriteLine($"In external module, returning to {lastCallAddressInTraceModule:X}");
                        cm.EnableBreakPoint(lastCallAddressInTraceModule, new ContextManager.BreakPointInfo {
                            Description = $"return from external module: {module}+{relativeAddress}"
                        });
                    }
                }
            }
#else
            throw new NotImplementedException();

            /*
             * cm.continuationBreakAddress = breakAddress;
             * //cm.continuationBreakAddress += decodedInstruction.Size;
             * if (decodedInstruction.Mnemonic.Equals("RET")) {
             *  var stackPointer = context.Rsp;
             *  if (!decodedInstruction.Operands.Equals("")) {
             *      var offset = ulong.Parse(decodedInstruction.Operands);
             *      stackPointer += offset;
             *  }
             *  ulong returnAddress = BitConverter.ToUInt64(DebugProcessUtils.ReadBytes(process, stackPointer, 8), 0);
             *  cm.continuationBreakAddress = returnAddress;
             * }*/
#endif

            if (hitCounts.ContainsKey(breakAddress))
            {
                hitCounts[breakAddress]++;
            }
            else
            {
                hitCounts[breakAddress] = 1;
            }

            var registers = _oldState.ContainsKey(threadId) ?
                            AssemblyUtil.FormatContextDiff(context, _oldState[threadId].Context, _oldState[threadId].SdInstruction) :
                            AssemblyUtil.FormatContext(context);
            //logFile.WriteLine(registers);
            var previous  = "";
            var lineBreak = false;
            if (_oldState.ContainsKey(threadId))
            {
                previous = _oldState[threadId].Line;
                if (_oldState[threadId].Instruction.Mnemonic == ud_mnemonic_code.UD_Iret ||
                    _oldState[threadId].Instruction.Mnemonic == ud_mnemonic_code.UD_Icall)
                {
                    lineBreak = true;
                }
            }
            var    asm     = $"{ decodedInstruction.Mnemonic } { decodedInstruction.Operands}";
            double logdist =
                distance == 0 ?
                0.0 :
                distance < 0 ?
                -Math.Log(-distance, 2) :
                Math.Log(distance, 2);

            var pattern = "";
            pattern += Regex.Escape("[");
            pattern += "(?<reg1>[A-Z0-9]{3})";
            pattern += "((?<sign1>[" + Regex.Escape("+") + Regex.Escape("-") + "])(?<reg2>[A-Z0-9]{3})(" + Regex.Escape("*") + "(?<multiplier>[0-9]+))?)?";
            pattern += "((?<sign2>[" + Regex.Escape("+") + Regex.Escape("-") + "])0x(?<offset>[0-9a-f]+))?";
            pattern += Regex.Escape("]");
            var rex = new Regex(pattern);
            // TODO: rewrite offset code to use SharpDisasm structure instead of string parsing
            var        operands   = decodedInstruction.Operands.ToString();
            var        match      = rex.Matches(operands);
            BigInteger memAddress = 0;
            if (match.Count > 0)
            {
                var  reg1             = match[0].Groups[rex.GroupNumberFromName("reg1")].Value;
                var  reg2             = match[0].Groups[rex.GroupNumberFromName("reg2")].Value;
                long sign1            = match[0].Groups[rex.GroupNumberFromName("sign1")].Value.Equals("+") ? 1 : -1;
                long sign2            = match[0].Groups[rex.GroupNumberFromName("sign2")].Value.Equals("+") ? 1 : -1;
                var  multiplierString = match[0].Groups[rex.GroupNumberFromName("multiplier")].Value;
                var  multiplier       = multiplierString.Equals("") ? 1 : long.Parse(multiplierString);

                var   offsetHex = match[0].Groups[rex.GroupNumberFromName("offset")].Value;
                var   reg1Value = (ulong)_fieldMap[reg1].GetValue(context);
                ulong reg2Value = 0;
                if (!reg2.Equals(""))
                {
                    reg2Value = (ulong)_fieldMap[reg2].GetValue(context);
                }
                var offset = offsetHex.Equals("") ? 0 : long.Parse(offsetHex, System.Globalization.NumberStyles.AllowHexSpecifier);
                memAddress = new BigInteger(reg1Value) + sign1 * new BigInteger(reg2Value) * multiplier + sign2 * new BigInteger(offset);
            }
            else if (decodedInstruction.Mnemonic == ud_mnemonic_code.UD_Ipop || decodedInstruction.Mnemonic == ud_mnemonic_code.UD_Ipush)
            {
                memAddress = context.Rsp;
            }

            //var module = DebugProcessUtils.GetModuleByAddress(process, breakAddress);
            var oldLine          = $"d: {logdist,8:#.##}, thread: {threadId,6:D}, mem: {memAddress,16:X} ";
            var moduleAndAddress = $"{module}+0x{relativeAddress:X}:{breakAddress:X}";
            oldLine += $"instr: {hex,30},";
            var hits = hitCounts[breakAddress];
            oldLine += $" {moduleAndAddress,64}(h{hits,2})(s{stackDepth,2}): {asm,-40} ";
            logFile.WriteLine(previous + registers);
            if (lineBreak)
            {
                logFile.WriteLine("");
            }

            _oldState[threadId] = new State {
                Context     = context,
                Instruction = decodedInstruction,
                Line        = oldLine,
                HitCounts   = hitCounts,
                StackDepth  = stackDepth,
                LastCallAddressInTraceModule = lastCallAddressInTraceModule
            };

            return(new TraceReturn {
                StepOver = retVal
            });
        }
Пример #2
0
        public static MaybeJumpSimple IsBranch(Instruction instruction)
        {
            var retVal       = new MaybeJumpSimple();
            var asm          = instruction.ToString();
            var mnemonic     = udis86.ud_lookup_mnemonic(instruction.Mnemonic);
            var asmOperands  = Regex.Replace(asm, "^.*" + mnemonic + " ", "");
            var freeRegister = FindFreeRegister(asm);

            retVal.RipEquals = freeRegister;

            string ripRegister      = freeRegister.ToString().ToLower();
            string targetRegister64 = "rax";
            string targetRegister   = "rax";

            if (asmOperands.Contains("far word"))
            {
                asmOperands    = asmOperands.Replace("far word", "word");
                targetRegister = AssemblyUtil.Get16BitRegisterFrom64BitRegister(targetRegister64.ToUpper()).ToLower();
            }
            else if (asmOperands.Contains("far dword"))
            {
                asmOperands    = asmOperands.Replace("far dword", "dword");
                targetRegister = AssemblyUtil.Get32BitRegisterFrom64BitRegister(targetRegister64.ToUpper()).ToLower();
            }
            else if (asmOperands.Contains("far qword"))
            {
                asmOperands = asmOperands.Replace("far qword", "qword");
            }
            else if (instruction.Operands.Length > 0)
            {
                // maybe invalid instruction on 64-bit, so won't be called, but wth
                var opr = instruction.Operands[0];

                switch (instruction.Operands[0].Size)
                {
                case 8:
                    targetRegister = AssemblyUtil.Get8BitLowerRegisterFrom64BitRegister(targetRegister64.ToUpper()).ToLower();
                    break;

                case 16:
                    targetRegister = AssemblyUtil.Get16BitRegisterFrom64BitRegister(targetRegister64.ToUpper()).ToLower();
                    break;

                case 32:
                    targetRegister = AssemblyUtil.Get32BitRegisterFrom64BitRegister(targetRegister64.ToUpper()).ToLower();
                    break;

                case 64:
                    break;
                }

                if (opr.Type == ud_type.UD_OP_JIMM)
                {
                    switch (opr.Size)
                    {
                    case 8:
                        asmOperands = ((sbyte)(instruction.PC + (ulong)opr.LvalSByte)).ToString();
                        break;

                    case 16:
                        asmOperands = ((int)(instruction.PC + (ulong)opr.LvalSWord)).ToString();
                        break;

                    case 32:
                        asmOperands = ((long)(instruction.PC + (ulong)opr.LvalSDWord)).ToString();
                        break;

                    default:
                        throw new Exception("invalid relative offset size.");
                    }
                }
            }

            asmOperands = asmOperands.Replace("rip", ripRegister);

            Func <bool>   getRelative          = () => instruction.Operands.Length > 0 && instruction.Operands[0].Type == ud_type.UD_OP_JIMM;
            Func <string> getTargetInstruction = () => getRelative() ? "add" : "mov";

            retVal.AddBranchOfSameType       = (label) => $"{mnemonic} {label}";
            retVal.AddInstrToGetBranchTarget = () => new[] {
                $";; {asm}",
                $"{getTargetInstruction()} {targetRegister}, {asmOperands}"
            };

            switch (instruction.Mnemonic)
            {
            // UNCONDITIONAL BRANCHES

            case ud_mnemonic_code.UD_Ijmp:
                retVal.Present             = true;
                retVal.Branch              = BranchInstruction.Jmp;
                retVal.AddBranchOfSameType = (label) => $"jmp {label}";
                break;

            case ud_mnemonic_code.UD_Icall:
                retVal.Present                   = true;
                retVal.Branch                    = BranchInstruction.Call;
                retVal.AddBranchOfSameType       = (label) => $"jmp {label}";
                retVal.AddInstrToGetBranchTarget = () => new[] {
                    $";; {asm}",
                    $"{getTargetInstruction()} {targetRegister}, {asmOperands}",
                    $"push {targetRegister64}"
                };
                break;

            case ud_mnemonic_code.UD_Iret:
                retVal.Present                   = true;
                retVal.Branch                    = BranchInstruction.Ret;
                retVal.AddBranchOfSameType       = (label) => $"jmp {label}";
                retVal.AddInstrToGetBranchTarget = () => new[] {
                    $";; {asm}",
                    $"pop {targetRegister64}"
                };
                break;

            // CONDITIONAL BRANCHES

            // loop
            case ud_mnemonic_code.UD_Iloop:
                retVal.Present = true;
                retVal.Branch  = BranchInstruction.Loop;
                break;

            // loop if equal
            case ud_mnemonic_code.UD_Iloope:
                retVal.Present = true;
                retVal.Branch  = BranchInstruction.Loope;
                break;

            // loop if not equal
            case ud_mnemonic_code.UD_Iloopne:
                retVal.Present = true;
                retVal.Branch  = BranchInstruction.Loopne;
                break;

            // jump if above
            case ud_mnemonic_code.UD_Ija:
                retVal.Present = true;
                retVal.Branch  = BranchInstruction.Ja;
                break;

            // jump if above or equal
            case ud_mnemonic_code.UD_Ijae:
                retVal.Present = true;
                retVal.Branch  = BranchInstruction.Jae;
                break;

            // jump if below
            case ud_mnemonic_code.UD_Ijb:
                retVal.Present = true;
                retVal.Branch  = BranchInstruction.Jb;
                break;

            // jump if below or equal
            case ud_mnemonic_code.UD_Ijbe:
                retVal.Present = true;
                retVal.Branch  = BranchInstruction.Jbe;
                break;

            // jump if cx register is 0
            case ud_mnemonic_code.UD_Ijcxz:
                retVal.Present = true;
                retVal.Branch  = BranchInstruction.Jcxz;
                break;

            // jump if ecx register is 0
            case ud_mnemonic_code.UD_Ijecxz:
                retVal.Present = true;
                retVal.Branch  = BranchInstruction.Jecxz;
                break;

            // jump if greater
            case ud_mnemonic_code.UD_Ijg:
                retVal.Present = true;
                retVal.Branch  = BranchInstruction.Jg;
                break;

            // jump if greater or equal
            case ud_mnemonic_code.UD_Ijge:
                retVal.Present = true;
                retVal.Branch  = BranchInstruction.Jge;
                break;

            // jump if less than
            case ud_mnemonic_code.UD_Ijl:
                retVal.Present = true;
                retVal.Branch  = BranchInstruction.Jl;
                break;

            // jump if less than or equal
            case ud_mnemonic_code.UD_Ijle:
                retVal.Present = true;
                retVal.Branch  = BranchInstruction.Jle;
                break;

            // jump if not overflow
            case ud_mnemonic_code.UD_Ijno:
                retVal.Present = true;
                retVal.Branch  = BranchInstruction.Jno;
                break;

            // jump if not parity
            case ud_mnemonic_code.UD_Ijnp:
                retVal.Present = true;
                retVal.Branch  = BranchInstruction.Jnp;
                break;

            // jump if not sign
            case ud_mnemonic_code.UD_Ijns:
                retVal.Present = true;
                retVal.Branch  = BranchInstruction.Jns;
                break;

            // jump if not zero
            case ud_mnemonic_code.UD_Ijnz:
                retVal.Present = true;
                retVal.Branch  = BranchInstruction.Jnz;
                break;

            // jump if overflow
            case ud_mnemonic_code.UD_Ijo:
                retVal.Present = true;
                retVal.Branch  = BranchInstruction.Jno;
                break;

            // jump if parity
            case ud_mnemonic_code.UD_Ijp:
                retVal.Present = true;
                retVal.Branch  = BranchInstruction.Jp;
                break;

            // jump if rcx zero
            case ud_mnemonic_code.UD_Ijrcxz:
                retVal.Present = true;
                retVal.Branch  = BranchInstruction.Jrcxz;
                break;

            // jump if signed
            case ud_mnemonic_code.UD_Ijs:
                retVal.Present = true;
                retVal.Branch  = BranchInstruction.Js;
                break;

            // jump if zero
            case ud_mnemonic_code.UD_Ijz:
                retVal.Present = true;
                retVal.Branch  = BranchInstruction.Jz;
                break;
            }
            return(retVal);
        }
Пример #3
0
        private static void DumpAssembly(byte[] traceMemory)
        {
            BigInteger oldProgress = 0;
            var        subRange    = new byte[AssemblyUtil.MaxInstructionBytes];

            using (StreamWriter sw2 = new StreamWriter(Specifics.AsmDumpFileName)) {
                using (FileStream asmSizesFs = new FileStream(Specifics.WriteAsmSizesDumpFileName, FileMode.Create)) {
                    using (BinaryWriter asmSizesBw = new BinaryWriter(asmSizesFs)) {
                        StreamWriter asmBranchesNasm = null;

                        for (ulong i = 0; i < (ulong)traceMemory.Length; i++)
                        {
                            ulong instructionsPerFile = 50000;
                            if (i % instructionsPerFile == 0)
                            {
                                asmBranchesNasm?.Close();
                                asmBranchesNasm = new StreamWriter(Specifics.WriteAsmBranchNasmDumpFileName + $"{(i / instructionsPerFile):D8}.asm");
                                asmBranchesNasm.Write(@"BITS 64
; %idefine rip rel $+(.next-.prev)
");
                            }
                            if (asmBranchesNasm == null)
                            {
                                throw new Exception("shouldn't happen");
                            }

                            Array.Copy(traceMemory, (int)i, subRange, 0,
                                       (int)Math.Min(AssemblyUtil.MaxInstructionBytes, (ulong)traceMemory.Length - i));
                            var progress = new BigInteger(i) * 100 / traceMemory.Length;
                            //Console.WriteLine($"progress: {progress}");

                            if (progress != oldProgress)
                            {
                                Console.WriteLine($"decoding asm, progress: {progress}");
                            }
                            oldProgress = progress;

                            byte instructionLength = 0;

                            Instruction instruction = null;
                            try {
                                instruction       = AssemblyUtil.Disassemble(subRange);
                                instructionLength = (byte)instruction.Length;
                            } catch (IndexOutOfRangeException) {
                                // ignore
                                sw2.WriteLine("SharpDisasm: failed with IndexOutOfRangeException");
                            } catch (DecodeException) {
                                // ignore
                                sw2.WriteLine("SharpDisasm: failed with DecodeException");
                            }

                            string asm;
                            bool   toStringWorked = false;
                            try {
                                asm            = instruction?.ToString();
                                toStringWorked = true;
                            } catch (IndexOutOfRangeException) {
                                asm = "IndexOutOfRangeException";
                            }

                            asmBranchesNasm.WriteLine($"instr{i}:");
                            asmBranchesNasm.WriteLine(".prev:");
                            if (toStringWorked &&
                                instruction != null &&
                                AssemblyUtil.IsNasmValid(instruction, asm))
                            {
                                var maybeJump = BranchRewriteSimple.IsBranch(instruction);

                                sw2.WriteLine($"{i:X}: {asm}");

                                if (maybeJump.Present)
                                {
                                    var flags = (byte)(maybeJump.RipEquals |
                                                       BranchRewriteSimple.RegisterMaskAndFlags.IsBranch);
                                    asmBranchesNasm.WriteLine($"db 0x{flags:X2}");
                                    asmBranchesNasm.WriteLine($"{maybeJump.AddBranchOfSameType(".setrip")}");
                                    asmBranchesNasm.WriteLine("clc"); // clear carry flag if we didn't jump == modify rip/rax
                                    asmBranchesNasm.WriteLine("jmp .next");
                                    asmBranchesNasm.WriteLine(".setrip:");
                                    foreach (var s in maybeJump.AddInstrToGetBranchTarget())
                                    {
                                        asmBranchesNasm.WriteLine(s);
                                    }
                                    asmBranchesNasm.WriteLine("stc"); // set carry flag if we didn't jump == modify rip/rax
                                }
                                else
                                {
                                    if (asm.Contains("rip"))
                                    {
                                        var    freeRegister = BranchRewriteSimple.FindFreeRegister(asm);
                                        string ripRegister  = freeRegister.ToString().ToLower();
                                        asm = asm.Replace("rip", ripRegister);
                                        // flags
                                        asmBranchesNasm.WriteLine($"db 0x{(byte) freeRegister:X2}");
                                        asmBranchesNasm.WriteLine(asm);
                                    }
                                    else
                                    {
                                        // flags
                                        asmBranchesNasm.WriteLine("db 0x0");
                                        asmBranchesNasm.WriteLine("db " + AssemblyUtil.BytesToHexZeroX(instruction.Bytes, ","));
                                    }
                                }
                            }
                            else
                            {
                                asmBranchesNasm.WriteLine("; failed to decode");
                                // flags
                                asmBranchesNasm.WriteLine($"db 0x{(byte) BranchRewriteSimple.RegisterMaskAndFlags.NotDecoded:X2}");
                                asmBranchesNasm.WriteLine("db " + AssemblyUtil.BytesToHexZeroX(subRange, ","));
                            }
                            asmBranchesNasm.WriteLine(".next:");
                            asmBranchesNasm.WriteLine($"%if {AssemblyUtil.MaxBranchBytes}-(.next-.prev) < 0");
                            asmBranchesNasm.WriteLine("%error Not enough space allocated");
                            asmBranchesNasm.WriteLine("%endif");
                            asmBranchesNasm.WriteLine($"times {AssemblyUtil.MaxBranchBytes}-(.next-.prev) nop");
                            asmBranchesNasm.WriteLine("");

                            asmSizesBw.Write(instructionLength);
                        }
                        asmBranchesNasm?.Close();
                    }
                }
            }
        }
Пример #4
0
        public static TraceState InstallTracer(Process process, ulong patchSite, Logger logger, byte[] asmSizes, ImportResolver ir)
        {
            var processHandle = Win32Imports.OpenProcess(Win32Imports.ProcessAccessFlags.All, false, process.Id);

            var traceState = new TraceState();

            if ((ulong)processHandle == 0)
            {
                logger.WriteLine("could not open process");
                return(null);
            }

            const int patchSiteSize = 24;
            const int codeSize      = 1024;
            const int traceLogSize  = 1024;

            var originalCode = DebugProcessUtils.ReadBytes(process, (ulong)process.MainModule.BaseAddress, process.MainModule.ModuleMemorySize);

            var originalCodeAddress = Win32Imports.VirtualAllocEx(
                processHandle,
                new IntPtr(0),
                new IntPtr(originalCode.Length),
                Win32Imports.AllocationType.Commit | Win32Imports.AllocationType.Reserve,
                Win32Imports.MemoryProtection.ReadWrite);

            if ((ulong)originalCodeAddress == 0)
            {
                logger.WriteLine($"could not allocate memory for {nameof(originalCodeAddress)}");
                return(null);
            }

            var traceLogAddress = Win32Imports.VirtualAllocEx(
                processHandle,
                new IntPtr(0),
                new IntPtr(traceLogSize),
                Win32Imports.AllocationType.Commit | Win32Imports.AllocationType.Reserve,
                Win32Imports.MemoryProtection.ReadWrite);

            if ((ulong)traceLogAddress == 0)
            {
                logger.WriteLine($"could not allocate memory for {nameof(traceLogAddress)}");
                return(null);
            }

            var injectedCodeAddress = Win32Imports.VirtualAllocEx(
                processHandle,
                new IntPtr(0),
                new IntPtr(codeSize),
                Win32Imports.AllocationType.Commit | Win32Imports.AllocationType.Reserve,
                Win32Imports.MemoryProtection.ExecuteReadWrite);

            if ((ulong)injectedCodeAddress == 0)
            {
                logger.WriteLine($"could not allocate memory for {nameof(injectedCodeAddress)}");
                return(null);
            }

            var asmSizesAddress = Win32Imports.VirtualAllocEx(
                processHandle,
                new IntPtr(0),
                new IntPtr(asmSizes.Length),
                Win32Imports.AllocationType.Commit | Win32Imports.AllocationType.Reserve,
                Win32Imports.MemoryProtection.ReadWrite);

            if ((ulong)asmSizesAddress == 0)
            {
                logger.WriteLine($"could not allocate memory for {nameof(asmSizesAddress)}");
                return(null);
            }

            traceState.CodeAddress     = (ulong)injectedCodeAddress;
            traceState.TraceLogAddress = (ulong)traceLogAddress;

            int addrSize = Marshal.SizeOf(typeof(IntPtr));

            var c1 = Assembler.CreateContext <Action>();

            // save rax
            c1.Push(c1.Rax);

            // push return address
            c1.Lea(c1.Rax, Memory.QWord(CodeContext.Rip, -addrSize));
            c1.Push(c1.Rax);

            // push "call" address
            c1.Mov(c1.Rax, (ulong)injectedCodeAddress);
            c1.Push(c1.Rax);

            // "call" codeAddress
            c1.Ret();

            c1.Nop();
            c1.Nop();
            c1.Nop();

            var patchSiteCodeJit = AssemblyUtil.GetAsmJitBytes(c1);

            Debug.Assert(patchSiteCodeJit.Length == patchSiteSize, "patch site size incorrect");

            var c = Assembler.CreateContext <Action>();

            c.Push(c.Rbp);
            c.Mov(c.Rbp, c.Rsp);
            c.Pushf();
            c.Push(c.Rbx);
            c.Push(c.Rcx);
            c.Push(c.Rdx);

            // log thread id
            var getThreadContext = ir.LookupFunction("KERNEL32.DLL:GetCurrentThreadId");

            //c.Call(getThreadContext);
            c.Lea(c.Rax, Memory.QWord(CodeContext.Rip, 13)); // skips next instructions
            c.Push(c.Rax);
            c.Mov(c.Rax, getThreadContext);
            c.Push(c.Rax);
            c.Ret();
            c.Mov(c.Rbx, (ulong)traceLogAddress);
            c.Mov(Memory.Word(c.Rbx), c.Eax);

            // reserve space for instruction counter, the 01-08 is just so AsmJit doesn't shorten the instruction
            // we overwrite the value to 0 later on
            c.Mov(c.Rax, (ulong)0x0102030405060708);
            var smcLastRipJit = AssemblyUtil.GetAsmJitBytes(c).Length - addrSize;

            c.Lea(c1.Rax, Memory.QWord(CodeContext.Rip, -7 - addrSize));
            c.Mov(c.Rcx, Memory.QWord(c.Rax));
            c.Inc(c.Rcx);
            c.Xchg(Memory.QWord(c.Rax), c.Rcx);

            // find return address and store size of instruction at return address in rcx
            c.Mov(c.Rdx, Memory.QWord(c.Rbp, addrSize));
            c.Mov(c.Rbx, c.Rdx);
            c.Mov(c.Rax, (ulong)process.MainModule.BaseAddress);
            c.Sub(c.Rdx, c.Rax);
            c.Mov(c.Rax, (ulong)asmSizesAddress);
            c.Add(c.Rdx, c.Rax);
            c.Movzx(c.Rcx, Memory.Byte(c.Rdx));

            // RESTORE ORIGINAL CODE

            // get address of original code in Rdx
            c.Mov(c.Rdx, c.Rbx);
            c.Mov(c.Rax, (ulong)process.MainModule.BaseAddress);
            c.Sub(c.Rdx, c.Rax);
            // TODO: compare Rdx with process.MainModule.ModuleMemorySize - patchSiteSize and abort if greater
            c.Mov(c.Rax, (ulong)originalCodeAddress);
            c.Add(c.Rdx, c.Rax);

            // restore call site 1
            c.Mov(c.Rax, Memory.QWord(c.Rdx));
            c.Mov(Memory.QWord(c.Rbx), c.Rax);

            // restore call site 2
            c.Mov(c.Rax, Memory.QWord(c.Rdx, addrSize));
            c.Mov(Memory.QWord(c.Rbx, addrSize), c.Rax);

            // restore call site 3
            c.Mov(c.Rax, Memory.QWord(c.Rdx, addrSize * 2));
            c.Mov(Memory.QWord(c.Rbx, addrSize * 2), c.Rax);

            // CLEAN UP AND RETURN

            // put new patch in place

            // add instruction size to return address, so we write patch at next instruction
            c.Add(c.Rbx, c.Rcx);

            // restore patch site 1
            c.Mov(c.Rax, BitConverter.ToUInt64(patchSiteCodeJit, 0));
            c.Mov(Memory.QWord(c.Rbx), c.Rax);

            // restore patch site 2
            c.Mov(c.Rax, BitConverter.ToUInt64(patchSiteCodeJit, addrSize));
            c.Mov(Memory.QWord(c.Rbx, addrSize), c.Rax);

            // restore patch site 3
            c.Mov(c.Rax, BitConverter.ToUInt64(patchSiteCodeJit, addrSize * 2));
            c.Mov(Memory.QWord(c.Rbx, addrSize * 2), c.Rax);

            // end put new patch in place

            // restore rax from call site
            c.Mov(c.Rax, Memory.QWord(c.Rbp, 2 * addrSize));

            c.Pop(c.Rdx);
            c.Pop(c.Rcx);
            c.Pop(c.Rbx);
            c.Popf();
            c.Pop(c.Rbp);
            c.Ret((ulong)8);

            // overwrite some pieces of the code with values computed later on
            var codeSiteCodeJit = AssemblyUtil.GetAsmJitBytes(c);

            for (var j = 0; j < 8; j++)
            {
                codeSiteCodeJit[smcLastRipJit + j] = 0;
            }

            var i           = 0;
            var nextOffset1 = 0;

            using (var sw = new StreamWriter(Specifics.PatchAsmDumpFileName)) {
                foreach (var s in codeSiteCodeJit.Select((b1) => $"b: 0x{b1:X2}"))
                {
                    string asm1S;
                    try {
                        var asm1 = AssemblyUtil.Disassemble(codeSiteCodeJit.Skip(i).Take(AssemblyUtil.MaxInstructionBytes).ToArray());
                        asm1S = i == nextOffset1?asm1.ToString() : "";

                        if (i == nextOffset1)
                        {
                            nextOffset1 += asm1.Length;
                        }
                    }
                    catch (DecodeException) {
                        asm1S = "failed";
                    }

                    sw.WriteLine($"{s}: ASM: {asm1S}");

                    i++;
                }
            }

            if (codeSiteCodeJit.Length > codeSize)
            {
                throw new Exception("did not reserve enough memory for code");
            }

            // write process memory
            DebugProcessUtils.WriteBytes(process, (ulong)originalCodeAddress, originalCode);
            DebugProcessUtils.WriteBytes(process, (ulong)asmSizesAddress, asmSizes);
            DebugProcessUtils.WriteBytes(process, (ulong)injectedCodeAddress, codeSiteCodeJit);
            DebugProcessUtils.WriteBytes(process, patchSite, patchSiteCodeJit);

            return(traceState);
        }
Пример #5
0
        public bool ResumeBreak(bool trace = true) {
            //Console.WriteLine("ResumeBreak");
            //for (var i = 0; i < 10000; i++) {
            var done = false;
            var goNextBreakPoint = false;
            var continueCode = Win32Imports.DbgContinue;
            while (!done)
            {
                Win32Imports.DebugEvent evt;
                if (!Win32Imports.WaitForDebugEvent(out evt, 0)) {
                //if (!handleDebugEvent(out evt, out continueCode)) {
                        //Console.WriteLine("WaitForDebugEvent failed");
                        //throw new Win32Exception();
                        done = true;
                }
                else {
                    CurrentInfo.EventCount++;

                    // Multiple if's for easier debugging at this moment
                    switch (evt.dwDebugEventCode) {
                        case Win32Imports.DebugEventType.LoadDllDebugEvent:
                            //Console.WriteLine($"resumed load dll event: {evt.dwThreadId}");
                            break;
                        case Win32Imports.DebugEventType.UnloadDllDebugEvent:
                            //Console.WriteLine($"resumed unload dll event: {evt.dwThreadId}");
                            break;
                        case Win32Imports.DebugEventType.ExceptionDebugEvent:
                            var exceptionAddress = (ulong) evt.Exception.ExceptionRecord.ExceptionAddress.ToInt64(); 

                            //Console.WriteLine($"first addr: {breakAddress:X} vs {address:X}");
                            var context = new Win32Imports.ContextX64();
                            var breakAddress = getRip((uint) evt.dwThreadId, ref context, GetRipAction.ActionGetContext);
                            var code = evt.Exception.ExceptionRecord.ExceptionCode;
                            //Console.WriteLine($"code: {code}, events: {eventCount}, thread: {evt.dwThreadId}, addr: {breakAddress2:X} vs {address:X}");

                            if (BreakPoints.ContainsKey(breakAddress) && BreakPoints[breakAddress].IsActive) {
                                LoggerInstance.WriteLine($"match at {breakAddress:X}, trace: {trace}");

                                setTrace((uint)evt.dwThreadId);
                                UninstallBreakPoint(breakAddress);
                                _lastBreakAddress = breakAddress;

                                CurrentInfo.LastContext = context;
                                CurrentInfo.LastContextReady = true;
                                // trace
                                //setTrace((uint) evt.dwThreadId, false);
                                if (BreakPoints[breakAddress].Description.Equals(CloseHandleDescription)) {
                                    LoggerInstance.WriteLine("CloseHandle hit");
                                    LoggerInstance.WriteLine("Registers:" + AssemblyUtil.FormatContext(context));
                                } else {
                                }
                                done = BreakPointCallBack(this, evt.dwThreadId, context, trace).StepOver;
                                goNextBreakPoint = done;
                                //} else if (!CheckBreakPointActive()) {
                            }
                            else {
                                // if we have seen it before
                                var shouldTrace = !BreakPoints.ContainsKey(breakAddress) ||
                                    !(BreakPoints.ContainsKey(breakAddress) && !BreakPoints[breakAddress].ShouldEnable) ||
                                    !(BreakPoints.ContainsKey(breakAddress) && !BreakPoints[breakAddress].IsActive);
                                // no breakpoint active, so we are tracing
                                if (trace && shouldTrace) {
                                    LoggerInstance.WriteLine($"tracing thread {evt.dwThreadId} at 0x{breakAddress:X}");
                                    var ret = BreakPointCallBack(this, evt.dwThreadId, context, true);
                                    done = ret.StepOver;
                                    goNextBreakPoint = done;
                                    if (ret.Ignore) {
                                        LoggerInstance.WriteLine("continuing with exception handlers");
                                        continueCode = Win32Imports.DbgExceptionNotHandled;
                                    }
                                }
                                else {
                                    LoggerInstance.WriteLine("continuing with exception handlers");
                                    continueCode = Win32Imports.DbgExceptionNotHandled;
                                }
                            }

                            Instruction instr = null;
                            string asm = "N/A";
                            string asm2 = "N/A";
                            try {
                                instr = AssemblyUtil.Disassemble(CurrentProcess, exceptionAddress);
                                asm = AssemblyUtil.FormatInstruction(AssemblyUtil.Disassemble(CurrentProcess, breakAddress));
                                asm2 = AssemblyUtil.FormatInstruction(AssemblyUtil.Disassemble(CurrentProcess, exceptionAddress));
                            }
                            catch (Exception)
                            {
                                // ignored
                            }

                            string msg;

                            switch (code) {
                                case Win32Imports.ExceptionCodeStatus.ExceptionSingleStep:
                                    asm = AssemblyUtil.FormatInstruction(AssemblyUtil.Disassemble(CurrentProcess, breakAddress));
                                    LoggerInstance.WriteLine($"single step at {breakAddress:X}, evtCount: {CurrentInfo.EventCount}, asm: {asm}");
                                    continueCode = HasBreakPoint(breakAddress) ? Win32Imports.DbgContinue : Win32Imports.DbgExceptionNotHandled;
                                    break;
                                case Win32Imports.ExceptionCodeStatus.ExceptionBreakpoint:        
                                    asm = AssemblyUtil.FormatInstruction(instr);
                                    //if (instr.Mnemonic.Equals("INT") && instr.Operands.Equals("3")) {
                                    if (instr != null && instr.Mnemonic == ud_mnemonic_code.UD_Iint3) {
                                        LoggerInstance.WriteLine($"int3 breakpoint at: {exceptionAddress:X}, evtCount: {CurrentInfo.EventCount}, asm: {asm}");
                                        LoggerInstance.WriteLine("overwriting with NOP");
                                        DebugProcessUtils.WriteByte(CurrentProcess, exceptionAddress, AssemblyUtil.Nop);
                                        continueCode = Win32Imports.DbgContinue;
                                    } else {
                                        msg = $"breakpoint, chance: { evt.Exception.dwFirstChance}";
                                        msg += $", at: 0x{breakAddress:X}, exc at: 0x{exceptionAddress:X}, asm: {asm}, exc asm: {asm2}";
                                        LoggerInstance.WriteLine(msg);
                                    }
                                    break;
                                case Win32Imports.ExceptionCodeStatus.ExceptionInvalidHandle:
                                    msg = $"invalid handle, chance: { evt.Exception.dwFirstChance}";
                                    msg += $", at: 0x{breakAddress:X}, exc at: 0x{exceptionAddress:X}, asm: {asm}, exc asm: {asm2}";
                                    LoggerInstance.WriteLine(msg);
                                    LoggerInstance.WriteLine(msg);
                                    AssemblyUtil.LogStackTrace(_importResolver, LoggerInstance, CurrentProcess, context.Rsp);
                                    continueCode = Win32Imports.DbgExceptionNotHandled;
                                    break;
                                case Win32Imports.ExceptionCodeStatus.ExceptionInvalidOperation:
                                    msg = $"invalid operation, chance: { evt.Exception.dwFirstChance}";
                                    msg += $", at: 0x{breakAddress:X}, exc at: 0x{exceptionAddress:X}, asm: {asm}, exc asm: {asm2}";
                                    // anti-anti-debug measure
                                    if (instr != null && instr.Mnemonic == ud_mnemonic_code.UD_Iud2) {
                                        LoggerInstance.WriteLine("overwriting UD2 with NOP");
                                        DebugProcessUtils.WriteBytes(CurrentProcess, exceptionAddress, new[] { AssemblyUtil.Nop, AssemblyUtil.Nop });
                                    }
                                    // anti-anti-debug measure
                                    var instr2 = AssemblyUtil.Disassemble(CurrentProcess, exceptionAddress - 1);
                                    if (instr2.Mnemonic == ud_mnemonic_code.UD_Iint && instr2.Operands[0].Value == 0x2D) {
                                        ulong rip = context.Rip - 1;
                                        setRip((uint) evt.dwThreadId, false, rip);
                                        LoggerInstance.WriteLine("INT2D encountered, subtracting 1 from Rip");
                                        continueCode = Win32Imports.DbgContinue;
                                    } else {
                                        continueCode = Win32Imports.DbgExceptionNotHandled;
                                    }
                                    LoggerInstance.WriteLine(msg);                                    
                                    break;
                                case Win32Imports.ExceptionCodeStatus.ExceptionAccessViolation:
                                    msg = $"access violation: {code:X}, chance: { evt.Exception.dwFirstChance}";
                                    msg += $", at: 0x{breakAddress:X}, exc at: 0x{exceptionAddress:X}, asm: {asm}, exc asm: {asm2}";
                                    LoggerInstance.WriteLine(msg);
                                    break;
                                case 0:
                                    LoggerInstance.WriteLine($"event 0 at: {breakAddress:X}");
                                    break;
                                default:
                                    msg = $"unknown code: {code:X}, chance: { evt.Exception.dwFirstChance}";
                                    msg += $", at: 0x{breakAddress:X}, exc at: 0x{exceptionAddress:X}, asm: {asm}, exc asm: {asm2}";
                                    LoggerInstance.WriteLine(msg);
                                    break;
                            }
                            break;
                        case Win32Imports.DebugEventType.CreateProcessDebugEvent:
                            //Console.WriteLine($"resumed create process event for thread {evt.dwThreadId}");
                            break;
                        case Win32Imports.DebugEventType.CreateThreadDebugEvent:
                            //Console.WriteLine($"resumed create thread event for thread {evt.dwThreadId}");
                            break;
                        case Win32Imports.DebugEventType.ExitThreadDebugEvent:
                            //Console.WriteLine($"resumed exit thread event for thread {evt.dwThreadId}");
                            break;
                        case Win32Imports.DebugEventType.ExitProcessDebugEvent:
                            Console.WriteLine($"resumed exit process event for thread {evt.dwThreadId}");
                            break;
                        default:
                            Console.WriteLine($"resumed debug event for thread: {evt.dwThreadId} {evt.dwDebugEventCode}");
                            break;
                    }

                    
                    //LoggerInstance.WriteLine($"debug event of type {evt.dwDebugEventCode}");

                    if (!Win32Imports.ContinueDebugEvent(evt.dwProcessId, evt.dwThreadId, continueCode)) {
                        throw new Win32Exception();
                    }
                    //ContinueDebugEvent(evt.dwProcessId, evt.dwThreadId, DBG_EXCEPTION_NOT_HANDLED);
                    //setBreakPoint((uint) evt.dwThreadId, address, false);
                }
            }

            //Console.WriteLine("End ResumeBreak");
            //return events;
            return goNextBreakPoint;
        }
Пример #6
0
        public static void TraceIt(Process process, ulong patchSite, Logger logger, bool debug, Action <Process, ulong, Logger> installTracer)
        {
            if (debug)
            {
                if (!Win32Imports.DebugActiveProcess(process.Id))
                {
                    throw new Win32Exception();
                }

                if (!Win32Imports.DebugSetProcessKillOnExit(false))
                {
                    throw new Win32Exception();
                }
            }
            foreach (ProcessThread thread in process.Threads)
            {
                var threadId = thread.Id;
                var context  = new Win32Imports.ContextX64();
                ContextManager.getRip((uint)threadId, ref context, ContextManager.GetRipAction.ActionSuspend);
            }

            Console.WriteLine("installing tracer");
            installTracer(process, patchSite, logger);

            var mainThread = 0;

            foreach (ProcessThread thread in process.Threads)
            {
                var threadId     = thread.Id;
                var context      = new Win32Imports.ContextX64();
                var breakAddress = ContextManager.getRip((uint)threadId, ref context, ContextManager.GetRipAction.ActionGetContext);
                var diff         = new BigInteger(breakAddress) - new BigInteger(patchSite);
                diff = diff < 0 ? -diff : diff;
                if (diff < 1000)
                {
                    mainThread = threadId;
                    logger.WriteLine($"thread {threadId} setting Rip to patch site: {patchSite:X}");
                    ContextManager.setRip((uint)threadId, false, patchSite);
                    if (debug)
                    {
                        ContextManager.setTrace((uint)threadId, false);
                    }
                }
                ContextManager.getRip((uint)threadId, ref context, ContextManager.GetRipAction.ActionResume);
            }

            /*if (!Win32Imports.DebugBreakProcess(process.Id)) {
             *  throw new Win32Exception();
             * }*/

            if (debug)
            {
                Dictionary <int, OldState> oldThreadState = new Dictionary <int, OldState>();
                try {
                    while (true)
                    {
                        Win32Imports.DebugEvent evt;
                        if (Win32Imports.WaitForDebugEvent(out evt, -1))
                        {
                            Console.WriteLine($"debug event {evt.dwDebugEventCode}");
                            var continueCode = Win32Imports.DbgContinue;
                            if (evt.dwDebugEventCode == Win32Imports.DebugEventType.ExceptionDebugEvent && evt.dwThreadId == mainThread)
                            {
                                var exceptionAddress = (ulong)evt.Exception.ExceptionRecord.ExceptionAddress.ToInt64();
                                var context          = new Win32Imports.ContextX64();
                                var breakAddress     = ContextManager.getRip((uint)evt.dwThreadId, ref context, ContextManager.GetRipAction.ActionGetContext);
                                var code             = evt.Exception.ExceptionRecord.ExceptionCode;
                                Console.WriteLine($"thread {evt.dwThreadId} break at 0x{exceptionAddress:X} code {code}, 0x{breakAddress:X}");
                                ContextManager.setTrace((uint)evt.dwThreadId);

                                var instr      = AssemblyUtil.Disassemble(process, exceptionAddress);
                                var asm        = AssemblyUtil.FormatInstruction(instr);
                                var strContext = oldThreadState.ContainsKey(evt.dwThreadId) ?
                                                 AssemblyUtil.FormatContextDiff(context, oldThreadState[evt.dwThreadId].Context, oldThreadState[evt.dwThreadId].Instruction) :
                                                 AssemblyUtil.FormatContext(context);
                                logger.WriteLine($"thread {evt.dwThreadId} break at 0x{exceptionAddress:X}, 0x{breakAddress:X} code {code}: {asm}, regs-1: {strContext}");

                                oldThreadState[evt.dwThreadId] = new OldState {
                                    Context     = context,
                                    Instruction = instr
                                };

                                if (code == Win32Imports.ExceptionCodeStatus.ExceptionAccessViolation)
                                {
                                    continueCode = Win32Imports.DbgExceptionNotHandled;
                                }
                            }

                            if (!Win32Imports.ContinueDebugEvent(evt.dwProcessId, evt.dwThreadId, continueCode))
                            {
                                throw new Win32Exception();
                            }
                        }
                    }
                }
                finally {
                    if (!Win32Imports.DebugActiveProcessStop(process.Id))
                    {
                        throw new Win32Exception();
                    }
                }
            }
        }