Пример #1
0
        public ContextManager(String name, Logger loggerInstance, Specifics specificsInstance,
            Func<ContextManager, int, Win32Imports.ContextX64, bool, ContextTracer.TraceReturn> breakPointCallBack,
            ImportResolver importResolver) {

            LoggerInstance = loggerInstance;
            BreakPointCallBack = breakPointCallBack;
            _importResolver = importResolver;

            setDebugPrivilege();

            CurrentProcess = DebugProcessUtils.GetFirstProcessByName(name);

            foreach (ProcessThread thread in CurrentProcess.Threads) {
                loggerInstance.WriteLine($"main thread: {thread.Id}");
                break;
            }

            SpecificsInstance = specificsInstance;

            Console.WriteLine("debugger");

            Console.WriteLine("pid: " + CurrentProcess.Id);

#if RestartEachTime
            // Attach to the process we provided the thread as an argument
            if (!Win32Imports.DebugActiveProcess(CurrentProcess.Id)) {
                    throw new Win32Exception();
                }

                if (!Win32Imports.DebugSetProcessKillOnExit(false)) {
                    throw new Win32Exception();
                }

                ClearEvents();
            }
Пример #2
0
        public static List <ulong> LookForByteArray(Process process, ImportResolver ir, byte[] toLookFor)
        {
            var matches = new List <ulong>();

            foreach (ProcessModule mod in process.Modules)
            {
                if (mod.ModuleName.Equals(Specifics.TraceModuleName))
                {
                    byte[] mem;
                    try {
                        mem = DebugProcessUtils.ReadBytes(process, (ulong)mod.BaseAddress, mod.ModuleMemorySize);
                    } catch {
                        throw new Exception("could not read process main module memory");
                    }
                    for (var i = 0; i < mem.Length; i++)
                    {
                        var equal = true;
                        for (var j = 0; j < toLookFor.Length; j++)
                        {
                            if (mem[i + j] != toLookFor[j])
                            {
                                equal = false;
                                break;
                            }
                        }
                        if (equal)
                        {
                            matches.Add((ulong)mod.BaseAddress + (ulong)i);
                        }
                    }
                    break;
                }
            }
            return(matches);
        }
Пример #3
0
        private static int ScanModules(Process process, StreamWriter sw, out ProcessModule traceModule,
                                       out byte[] traceMemory)
        {
            var totalSize2 = 0;

            traceModule = null;
            traceMemory = new byte[] {};
            foreach (ProcessModule mod in process.Modules)
            {
                if (mod.ModuleName.Equals(Specifics.TraceModuleName))
                {
                    traceModule = mod;
                }
                var result = "success";
                try {
                    var mem = DebugProcessUtils.ReadBytes(process, (ulong)mod.BaseAddress, mod.ModuleMemorySize);
                    if (mod.ModuleName.Equals(Specifics.TraceModuleName))
                    {
                        Console.WriteLine($"mem: {mem.Length}");
                        traceMemory = mem;
                    }
                    totalSize2 += mod.ModuleMemorySize;
                } catch {
                    result = "failed";
                }
                sw.WriteLine($"module: {mod.ModuleName}, size: {mod.ModuleMemorySize} result: {result}");
            }
            sw.Close();
            return(totalSize2);
        }
Пример #4
0
        public static void Main()
        {
            var process = DebugProcessUtils.GetFirstProcessByName(Specifics.ProcessName);

            // ReSharper disable once UnusedVariable
            using (var logger = new Logger(Specifics.LogName, Specifics.LogNameLatest)) {
                ImportResolver ir = new ImportResolver(process);

                ir.DumpDebug();

                var matches = MemScan.LookForByteArray(process, ir, Specifics.StartAddressBytes);
                foreach (var match in matches)
                {
                    Console.WriteLine($"byte array match at: 0x{match:X}");
                }
                if (matches.Count != 1)
                {
                    return;
                }

                MemScan.MemModuleScan(process, ir);

                // TraceMain(process, ir, matches, logger);
            }
        }
Пример #5
0
 public void Int3Check() {
     var addresses = new ulong[] { 0x7FF9C61653E0 };
     foreach (var address in addresses) {
         var b = DebugProcessUtils.ReadByte(CurrentProcess, address);
         var overwritten = false;
         if (b == AssemblyUtil.Int3) {
             DebugProcessUtils.WriteByte(CurrentProcess, address, AssemblyUtil.Nop);
             overwritten = true;
         }
         LoggerInstance.WriteLine($"checking int3 at: 0x{address:X}, overwritten: {overwritten}");
     }
 }
Пример #6
0
        public void InstallBreakPoint(ulong startAddress) {
            //const byte INT3Trap = 0xCC;
            /*if (CheckBreakPointActive()) {
                throw new Exception("only one breakpoint at a time supported");
            }*/

            LoggerInstance.WriteLine($"installing breakpoint at {startAddress:X}");

            if (BreakPoints[startAddress].OriginalCode == null) {
                byte[] mem = DebugProcessUtils.ReadBytes(CurrentProcess, startAddress, AssemblyUtil.InfiniteLoop.Length);
                BreakPoints[startAddress].OriginalCode = mem;
            }
            DebugProcessUtils.WriteBytes(CurrentProcess, startAddress, AssemblyUtil.InfiniteLoop);

            BreakPoints[startAddress].IsSoftware = true;
            BreakPoints[startAddress].IsActive = true;
        }
Пример #7
0
        private static int ScanFunctions(Process process, ImportResolver ir, StreamWriter sw)
        {
            var totalSize = 0;

            foreach (var mi in ir.ModuleFunctions)
            {
                var result = "success";
                try {
                    DebugProcessUtils.ReadBytes(process, mi.Address, (int)mi.Size);
                    totalSize += (int)mi.Size;
                }
                catch {
                    result = "failed";
                }
                sw.WriteLine($"function: {mi.Module.ModuleName}.{mi.FunctionName}, size: {mi.Size} result: {result}");
            }
            return(totalSize);
        }
Пример #8
0
        public static void TraceMain(Process process, ImportResolver ir, List <ulong> matches, Logger logger)
        {
            MemScan.MemMain(process, ir, true);

            var patchSite = matches.First();

            var asmSizes = File.ReadAllBytes(Specifics.ReadAsmSizesDumpFileName);
            var branches = File.ReadAllBytes(Specifics.ReadAsmBranchDumpFileName);

            logger.WriteLine($"patch site: {patchSite:X}");
            EmulatedMemTracer.TraceState traceState = null;
            SimpleMemTracer.TraceIt(process, patchSite, logger, false,
                                    (x, y, z) => { traceState = EmulatedMemTracer.InstallTracer(x, y, z, ir, asmSizes, branches); });
            if (traceState != null)
            {
                // TODO: fix race
                var threadId = BitConverter.ToUInt32(DebugProcessUtils.ReadBytes(process, traceState.TraceLogAddress, 4), 0);
                Console.WriteLine($"thread id: {threadId:X}");
            }
        }
Пример #9
0
 private void UninstallBreakPoint(ulong address) {
     if (BreakPoints[address].IsActive) {
         if (BreakPoints[address].IsSoftware) {
             byte[] mem = DebugProcessUtils.ReadBytes(CurrentProcess, address, AssemblyUtil.InfiniteLoop.Length);
             if (mem[0] == AssemblyUtil.InfiniteLoop[0] && mem[1] == AssemblyUtil.InfiniteLoop[1]) {
                 LoggerInstance.WriteLine($"{nameof(UninstallBreakPoint)}: restoring inf-jmp with original code at 0x{address:X}");
                 DebugProcessUtils.WriteBytes(CurrentProcess, address, BreakPoints[address].OriginalCode);
             }
             else if (mem[0] != BreakPoints[address].OriginalCode[0] && mem[1] != BreakPoints[address].OriginalCode[1]) {
                 LoggerInstance.WriteLine($"{nameof(UninstallBreakPoint)}: code changed (self-modifying?) at 0x{address:X}");
             }
             else {
                 LoggerInstance.WriteLine($"{nameof(UninstallBreakPoint)}: code was intact at 0x{address:X}");
             }
         } else if (BreakPoints[address].IsHardware && BreakPoints[address].HardwareBreakPointHandle != 0) {
             RemoveHardwareBreakpoint(BreakPoints[address].HardwareBreakPointHandle);
             /*foreach (ProcessThread thread in CurrentProcess.Threads) {
                 setHardwareBreakPoint((uint)thread.Id, address, true, true);
             }*/
         }
     }
     BreakPoints[address].IsActive = false;
 }
Пример #10
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
            });
        }
Пример #11
0
        public static void InstallTracer(Process process, ulong patchSite, Logger logger)
        {
            var processHandle = Win32Imports.OpenProcess(Win32Imports.ProcessAccessFlags.All, false, process.Id);

            var retVal = new TraceState();

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

            const int patchSiteSize = 24;
            const int patchAreaSize = 100;
            const int codeSize      = 1024 + patchAreaSize * 100;
            var       originalCode  = DebugProcessUtils.ReadBytes(process, patchSite, patchAreaSize);

            // minus patchSiteSize because we need to restore patchSiteSize bytes
            var ci = new diStorm.CodeInfo(0, originalCode.Take(patchAreaSize - patchSiteSize).ToArray(), diStorm.DecodeType.Decode64Bits, 0);
            var dr = new diStorm.DecodedResult(patchAreaSize);

            diStorm.diStorm3.Decode(ci, dr);
            var decodedInstructions = dr.Instructions;

            var bogusAddress = Win32Imports.VirtualAllocEx(
                processHandle,
                new IntPtr(0),
                new IntPtr(patchSiteSize * 2),
                Win32Imports.AllocationType.Commit | Win32Imports.AllocationType.Reserve,
                Win32Imports.MemoryProtection.ReadWrite);

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

            retVal.CodeAddress = (ulong)codeAddress;

            if ((ulong)codeAddress == 0)
            {
                logger.WriteLine("could not allocate memory");
                return;
            }

            var patchSiteCode = new byte[patchSiteSize];
            var codeSiteCode  = new byte[codeSize];
            var patchPtr      = 0;
            var ptr           = 0;

            //patchSiteCode[patchPtr++] = 0xCC;
            // push rax, see RESTORE_RAX
            patchSiteCode[patchPtr++] = 0x50;
            // lea rax, [rip-8]
            patchSiteCode[patchPtr++] = 0x48;
            patchSiteCode[patchPtr++] = 0x8D;
            patchSiteCode[patchPtr++] = 0x05;
            patchSiteCode[patchPtr++] = 0xF8;
            patchSiteCode[patchPtr++] = 0xFF;
            patchSiteCode[patchPtr++] = 0xFF;
            patchSiteCode[patchPtr++] = 0xFF;
            // push rax (rip)
            patchSiteCode[patchPtr++] = 0x50;
            // mov rax, constant
            patchSiteCode[patchPtr++] = 0x48;
            patchSiteCode[patchPtr++] = 0xB8;
            foreach (var b in BitConverter.GetBytes((ulong)codeAddress))
            {
                patchSiteCode[patchPtr++] = b;
            }
            // push rax
            patchSiteCode[patchPtr++] = 0x50;
            // ret
            patchSiteCode[patchPtr++] = 0xC3;
            patchPtr += 3;

            if (patchPtr != patchSiteSize)
            {
                logger.WriteLine($"made a mistake: {patchPtr} != {patchSiteSize}");
                return;
            }

            // push rbp
            codeSiteCode[ptr++] = 0x55;
            // mov rbp, rsp
            codeSiteCode[ptr++] = 0x48;
            codeSiteCode[ptr++] = 0x89;
            codeSiteCode[ptr++] = 0xE5;
            // push flags
            codeSiteCode[ptr++] = 0x9C;
            // push rbx
            codeSiteCode[ptr++] = 0x53;
            // push rcx
            codeSiteCode[ptr++] = 0x51;
            // push rdx
            codeSiteCode[ptr++] = 0x52;

            // reserve space for last Rip address
            // mov rax, constant
            codeSiteCode[ptr++] = 0x48;
            codeSiteCode[ptr++] = 0xB8;
            var smcLastRip = ptr;

            ptr += 8;

            // lea rax, [rip-7-8]
            codeSiteCode[ptr++] = 0x48;
            codeSiteCode[ptr++] = 0x8D;
            codeSiteCode[ptr++] = 0x05;
            codeSiteCode[ptr++] = 0xF1;
            codeSiteCode[ptr++] = 0xFF;
            codeSiteCode[ptr++] = 0xFF;
            codeSiteCode[ptr++] = 0xFF;

            // mov rcx, [rax]
            codeSiteCode[ptr++] = 0x48;
            codeSiteCode[ptr++] = 0x8B;
            codeSiteCode[ptr++] = 0x08;

            // inc rcx
            codeSiteCode[ptr++] = 0x48;
            codeSiteCode[ptr++] = 0xFF;
            codeSiteCode[ptr++] = 0xC1;

            // xchg [rax], rcx
            codeSiteCode[ptr++] = 0x48;
            codeSiteCode[ptr++] = 0x87;
            codeSiteCode[ptr++] = 0x08;

            // find return address
            // mov rbx, [rbp+8]
            codeSiteCode[ptr++] = 0x48;
            codeSiteCode[ptr++] = 0x8B;
            codeSiteCode[ptr++] = 0x5D;
            codeSiteCode[ptr++] = 0x08;

            // call pastEndCode
            codeSiteCode[ptr++] = 0xE8;
            var callPastEndCodeImmediate = ptr;

            ptr += 4;
            var callPastEndCodeBaseOffset = ptr;

            // END FUNCTION

            // put new patch in place
            // mov rax, constant
            codeSiteCode[ptr++] = 0x48;
            codeSiteCode[ptr++] = 0xB8;
            foreach (var b in patchSiteCode.Skip(0).Take(8))
            {
                codeSiteCode[ptr++] = b;
            }

            // restore patch site 1
            // mov [rbx], rax
            codeSiteCode[ptr++] = 0x48;
            codeSiteCode[ptr++] = 0x89;
            codeSiteCode[ptr++] = 0x03;

            // mov rax, constant
            codeSiteCode[ptr++] = 0x48;
            codeSiteCode[ptr++] = 0xB8;
            foreach (var b in patchSiteCode.Skip(8).Take(8))
            {
                codeSiteCode[ptr++] = b;
            }

            // restore patch site 2
            // mov [rbx+8], rax
            codeSiteCode[ptr++] = 0x48;
            codeSiteCode[ptr++] = 0x89;
            codeSiteCode[ptr++] = 0x43;
            codeSiteCode[ptr++] = 0x08;

            // mov rax, constant
            codeSiteCode[ptr++] = 0x48;
            codeSiteCode[ptr++] = 0xB8;
            foreach (var b in patchSiteCode.Skip(16).Take(8))
            {
                codeSiteCode[ptr++] = b;
            }

            // restore patch site 3
            // mov [rbx+16], rax
            codeSiteCode[ptr++] = 0x48;
            codeSiteCode[ptr++] = 0x89;
            codeSiteCode[ptr++] = 0x43;
            codeSiteCode[ptr++] = 0x10;

            // end put new patch in place

            // mov rax,[rbp+16]
            codeSiteCode[ptr++] = 0x48;
            codeSiteCode[ptr++] = 0x8B;
            codeSiteCode[ptr++] = 0x45;
            codeSiteCode[ptr++] = 0x10;

            // pop rdx
            codeSiteCode[ptr++] = 0x5A;
            // pop rcx
            codeSiteCode[ptr++] = 0x59;
            // pop rbx
            codeSiteCode[ptr++] = 0x5B;
            // pop flags
            codeSiteCode[ptr++] = 0x9D;
            // pop rbp
            codeSiteCode[ptr++] = 0x5D;
            // ret 8
            codeSiteCode[ptr++] = 0xC2;
            codeSiteCode[ptr++] = 0x08;
            codeSiteCode[ptr++] = 0x00;

            codeSiteCode[callPastEndCodeImmediate] = (byte)(ptr - callPastEndCodeBaseOffset);

            // imul rcx,rcx,constant
            codeSiteCode[ptr++] = 0x48;
            codeSiteCode[ptr++] = 0x6B;
            codeSiteCode[ptr++] = 0xC9;
            // = size of restore function
            var multiplyImmediate = ptr;

            codeSiteCode[ptr++] = 0x00;

            // lea rcx, rcx+eax+constant
            codeSiteCode[ptr++] = 0x48;
            codeSiteCode[ptr++] = 0x8D;
            codeSiteCode[ptr++] = 0x4C;
            codeSiteCode[ptr++] = 0x08;
            var leaRcxConstant = ptr;

            codeSiteCode[ptr++] = 0x00;

            // jmp rcx
            codeSiteCode[ptr++] = 0xFF;
            codeSiteCode[ptr++] = 0xE1;

            codeSiteCode[leaRcxConstant] = (byte)(ptr - smcLastRip);

            // restore function
            uint offset     = 0;
            var  lastReturn = 0;

            foreach (var instr in decodedInstructions.Select((value, index) => new { Value = value, Index = index }))
            {
                var startOfRestore = ptr;

                Console.WriteLine($"instruction {instr.Value.Mnemonic}, size: {instr.Value.Size}");
                // reserve space for call site restore 1
                // mov rax, constant
                codeSiteCode[ptr++] = 0x48;
                codeSiteCode[ptr++] = 0xB8;
                var originalOffset = 0;
                var patchOffset    = 0;
                foreach (var b in originalCode.Skip((int)offset).Take(8))
                {
                    if (originalOffset < instr.Value.Size)
                    {
                        codeSiteCode[ptr++] = b;
                    }
                    else
                    {
                        codeSiteCode[ptr++] = patchSiteCode[patchOffset++];
                    }
                    originalOffset++;
                }

                // restore call site 1
                // mov [rbx], rax
                codeSiteCode[ptr++] = 0x48;
                codeSiteCode[ptr++] = 0x89;
                codeSiteCode[ptr++] = 0x03;

                // reserve space for call site restore 2
                // mov rax, constant
                codeSiteCode[ptr++] = 0x48;
                codeSiteCode[ptr++] = 0xB8;
                foreach (var b in originalCode.Skip((int)offset + 8).Take(8))
                {
                    if (originalOffset < instr.Value.Size)
                    {
                        codeSiteCode[ptr++] = b;
                    }
                    else
                    {
                        codeSiteCode[ptr++] = patchSiteCode[patchOffset++];
                    }
                    originalOffset++;
                }

                // restore call site 2
                // mov [rbx+8], rax
                codeSiteCode[ptr++] = 0x48;
                codeSiteCode[ptr++] = 0x89;
                codeSiteCode[ptr++] = 0x43;
                codeSiteCode[ptr++] = 0x08;

                // reserve space for call site restore 3
                // mov rax, constant
                codeSiteCode[ptr++] = 0x48;
                codeSiteCode[ptr++] = 0xB8;
                foreach (var b in originalCode.Skip((int)offset + 16).Take(8))
                {
                    if (originalOffset < instr.Value.Size)
                    {
                        codeSiteCode[ptr++] = b;
                    }
                    else
                    {
                        codeSiteCode[ptr++] = patchSiteCode[patchOffset++];
                    }
                    originalOffset++;
                }

                // restore call site 3
                // mov [rbx+16], rax
                codeSiteCode[ptr++] = 0x48;
                codeSiteCode[ptr++] = 0x89;
                codeSiteCode[ptr++] = 0x43;
                codeSiteCode[ptr++] = 0x10;

                // prepare rbx to put new patch in place
                // add rbx,instrSize
                codeSiteCode[ptr++] = 0x48;
                codeSiteCode[ptr++] = 0x83;
                codeSiteCode[ptr++] = 0xC3;
                codeSiteCode[ptr++] = (byte)(instr.Value.Size);

                // return
                lastReturn          = ptr;
                codeSiteCode[ptr++] = 0xC3;
                var endOfRestore = ptr;

                Console.WriteLine($"mul: {endOfRestore - startOfRestore}");
                codeSiteCode[multiplyImmediate] = (byte)(endOfRestore - startOfRestore);

                offset += instr.Value.Size;
            }

            ptr = lastReturn;

            // we set rbx to bogus so that patch is not written for last instruction in set
            // mov rbx, constant
            codeSiteCode[ptr++] = 0x48;
            codeSiteCode[ptr++] = 0xBB;
            foreach (var b in BitConverter.GetBytes((ulong)bogusAddress))
            {
                codeSiteCode[ptr++] = b;
            }

            // return
            codeSiteCode[ptr] = 0xC3;

            DebugProcessUtils.WriteBytes(process, (ulong)codeAddress, codeSiteCode);
            DebugProcessUtils.WriteBytes(process, patchSite, patchSiteCode);
        }
Пример #12
0
        public static void DoIt()
        {
            var name    = Specifics.ProcessName;
            var address = Specifics.StartAddress;
            var process = DebugProcessUtils.GetFirstProcessByName(name);

            using (Form form = new Form()) {
                form.Text = "Inspector";
                form.Size = new Size(600, 1080);

                var table         = new DataGridView();
                var bindingSource = new BindingSource();
                table.DataSource = bindingSource;

                var infoTable  = new DataGridView();
                var infoSource = new BindingSource();
                infoTable.DataSource = infoSource;

                var formClosed      = false;
                var cleanupFinished = false;

                var splitter = new Splitter();

                infoTable.Dock = DockStyle.Left;
                splitter.Dock  = DockStyle.Left;
                table.Dock     = DockStyle.Fill;
                form.Controls.AddRange(new Control[] { table, splitter, infoTable });

                Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) {
                    e.Cancel = true;
                    //form.Close();
                };

                var progress = new Progress <List <ContextManager.ThreadData> >(
                    (contexts) => {
                    AddContexts(bindingSource, contexts);
                }
                    );

                IProgress <ContextManager.Info> infoProgress = new Progress <ContextManager.Info>(
                    (info) => {
                    UpdateInfo(infoSource, info);
                }
                    );

                var specifics = new Specifics();

                Task.Factory.StartNew(
                    () => {
                    var logName  = Specifics.LogName;
                    var logName2 = Specifics.LogNameLatest;
                    using (var logFile = new Logger(logName, logName2)) {
                        ContextManager cg = null;
                        logFile.WriteLine("");
                        logFile.WriteLine("STARTING INSPECTOR");
                        try {
                            var importResolver = new ImportResolver(process);
                            importResolver.DumpDebug();
                            var logContext = new ContextTracer(importResolver);
                            var logFile2   = logFile;
                            cg             = new ContextManager(name, logFile, specifics, (cm, threadId, context, trace) => {
                                Debug.Assert(logFile2 != null, "logFile2 != null");
                                return(logContext.Log(cm, logFile2, process, threadId, context, trace));
                            }, importResolver);

                            //if (File.Exists(Specifics.appStateFileName)) {
                            //SaveAndRestore.Restore(Specifics.appStateFileName, cg);
                            //} else {
                            cg.EnableBreakPoint(importResolver.ResolveRelativeAddress(address), new ContextManager.BreakPointInfo {
                                Description = "starting breakpoint"
                            });
                            //}
                            cg.AntiAntiDebug();

                            /*try {
                             *  cg.InstallBreakPoint(address);
                             * } catch (InvalidOperationException e) {
                             *  Console.WriteLine($"Failed to install break points: {e.ToString()}");
                             * }*/

                            while (!formClosed)
                            {
                                logFile.WriteLine("main debugger loop");
                                cg.CurrentProcess = DebugProcessUtils.GetFirstProcessByName(cg.CurrentProcess.ProcessName);
                                cg.TestBreak();
                                Update(cg, progress);
                                infoProgress.Report(cg.CurrentInfo);
                                SaveAndRestore.Save(Specifics.AppStateFileName, cg);
                                Task.Delay(Specifics.MainLoopDelay).Wait();
                            }
                        }
                        catch (Exception e) {
                            Console.WriteLine($"Exception: {e.Message}");
                            Console.WriteLine(e.StackTrace);
                            logFile.WriteLine($"Exception: {e.Message}");
                            logFile.WriteLine(e.StackTrace);
                        }
                        // cleanup
                        Console.WriteLine("cleaning up");
                        SaveAndRestore.Save(Specifics.AppStateFileName, cg);
                        cg?.Stop();
                        cleanupFinished = true;
                    }
                },
                    TaskCreationOptions.LongRunning
                    );

                /*Task.Factory.StartNew(
                 *  () => {
                 *      while (true) {
                 *          cg.ResumeEvents();
                 *      }
                 *  },
                 *  TaskCreationOptions.LongRunning
                 * );*/

                form.FormClosing += (sender, e) => {
                    Console.WriteLine("form closing");
                    formClosed = true;
                    while (!cleanupFinished)
                    {
                        Console.WriteLine("waiting for cleanup");
                        Task.Delay(1000).Wait();
                    }
                };

                form.ShowDialog();
            }
        }
Пример #13
0
        public static Instruction ReadOneAndDisassemble(Process process, ulong address)
        {
            var mem = DebugProcessUtils.ReadBytes(process, address, MaxInstructionBytes);

            return(Disassemble(mem));
        }
Пример #14
0
        public static Instruction[] DisassembleMany(Process process, ulong address, ulong targetOffset)
        {
            var mem = DebugProcessUtils.ReadBytes(process, address, (int)(targetOffset - address));

            return(DisassembleMany(mem, targetOffset));
        }
Пример #15
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);
        }
Пример #16
0
        // see also StackWalk64, but I'm not sure I can use that, because I don't have a function table
        public static void LogStackTrace(ImportResolver ir, Logger log, Process process, ulong stackPointer)
        {
            int size    = 4096;
            var mem     = DebugProcessUtils.ReadBytes(process, stackPointer, 4096);
            var ptrSize = Marshal.SizeOf(typeof(IntPtr));

            for (var offset = 0; offset + ptrSize < size; offset += 1)
            {
                ulong ptr = (ulong)BitConverter.ToInt64(mem, offset);
                if (ptr == 0)
                {
                    continue;
                }
                Tuple <string, ulong> ret = null;
                try {
                    ret = ir.LookupAddress(ptr);
                }
                catch (Exception)
                {
                    // ignored
                }
                string module   = "lookup-failed";
                ulong  relative = 0;
                if (ret != null)
                {
                    module = ret.Item1;
                    var functionAddress = ret.Item2;
                    relative = ptr - functionAddress;
                }

                byte[] ptrMem = null;
                try {
                    ptrMem = DebugProcessUtils.ReadBytes(process, ptr, ptrSize);
                }
                catch (Exception)
                {
                    // ignored
                }
                ulong data = 0;
                if (ptrMem != null)
                {
                    data = (ulong)BitConverter.ToInt64(ptrMem, 0);
                }
                for (ulong potentialCallOffset = 0; potentialCallOffset <= 6; potentialCallOffset++)
                {
                    try {
                        var callLocation = ptr - potentialCallOffset;
                        var instr        = Disassemble(process, callLocation);
                        var asm          = FormatInstruction(instr);
                        if (instr.Mnemonic == ud_mnemonic_code.UD_Icall || potentialCallOffset == 0)
                        {
                            log.WriteLine($"stack call {offset}-{potentialCallOffset}: {module}+0x{relative:X} 0x{ptr:X}: asm 0x{data:X} {asm}");
                        }
                    } catch (Exception) {
                        if (potentialCallOffset == 0)
                        {
                            log.WriteLine($"stack trace {offset}-{potentialCallOffset}: {module}+0x{relative:X} 0x{ptr:X}: asm 0x{data:X} exception");
                        }
                    }
                }
            }
        }
Пример #17
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;
        }