public void ClearEvents() { // const uint DBG_EXCEPTION_NOT_HANDLED = 0x80010001; // const uint EXCEPTION_SINGLE_STEP = 0x80000004; const int dbgContinue = 0x00010002; // Seems to work better than DBG_EXCEPTION_NOT_HANDLED while (true) { Win32Imports.DebugEvent evt; if (!Win32Imports.WaitForDebugEvent(out evt, 0)) { break; } CurrentInfo.EventCount++; Win32Imports.ContinueDebugEvent(evt.dwProcessId, evt.dwThreadId, dbgContinue);//DBG_EXCEPTION_NOT_HANDLED); } }
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; }
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(); } } } }