diff --git a/GleeBug/Debugger.Loop.cpp b/GleeBug/Debugger.Loop.cpp index 0a3ad19..f558016 100644 --- a/GleeBug/Debugger.Loop.cpp +++ b/GleeBug/Debugger.Loop.cpp @@ -1,5 +1,6 @@ #include "Debugger.h" #include "Debugger.Thread.Registers.h" +#include "Emulation.h" namespace GleeBug { @@ -24,12 +25,37 @@ namespace GleeBug return; } + bool allowEmulation = true; + bool inEmulation = false; + while (!mBreakDebugger) { //wait for a debug event mIsRunning = true; - if (!MyWaitForDebugEvent(&mDebugEvent, INFINITE)) - break; + + // We go over all active processes and see if any emulator has an active event + // if thats the case process the emulated event first. + if (inEmulation) + { + inEmulation = false; + + for (auto&& process : mProcesses) + { + if (process.second->emulator.WaitForEvent(mDebugEvent)) + { + inEmulation = true; + break; + } + } + } + + // Emulated events have priority over normal debug events. + if (!inEmulation) + { + if (!MyWaitForDebugEvent(&mDebugEvent, INFINITE)) + break; + } + mIsRunning = false; //set default continue status @@ -117,8 +143,18 @@ namespace GleeBug } //continue the debug event - if (!ContinueDebugEvent(mDebugEvent.dwProcessId, mDebugEvent.dwThreadId, mContinueStatus)) - break; + + if (allowEmulation && mThread != nullptr && mThread->isSingleStepping) + { + auto& emulator = mProcess->emulator; + inEmulation = emulator.Emulate(mThread); + } + + if (inEmulation == false) + { + if (!ContinueDebugEvent(mDebugEvent.dwProcessId, mDebugEvent.dwThreadId, mContinueStatus)) + break; + } if (mDetach || mDetachAndBreak) { @@ -133,4 +169,4 @@ namespace GleeBug mProcess = nullptr; mIsDebugging = false; } -}; \ No newline at end of file +}; diff --git a/GleeBug/Debugger.Process.Breakpoint.cpp b/GleeBug/Debugger.Process.Breakpoint.cpp index 3551dc6..8c48b84 100644 --- a/GleeBug/Debugger.Process.Breakpoint.cpp +++ b/GleeBug/Debugger.Process.Breakpoint.cpp @@ -225,7 +225,6 @@ namespace GleeBug bool Process::SetNewPageProtection(ptr page, MemoryBreakpointData & data, MemoryType type) { - DPRINTF(); //TODO: handle PAGE_NOACCESS and such correctly (since it cannot be combined with PAGE_GUARD) auto found = memoryBreakpointPages.find(page); @@ -268,7 +267,6 @@ namespace GleeBug bool Process::SetMemoryBreakpoint(ptr address, ptr size, MemoryType type, bool singleshoot) { - DPRINTF(); dprintf("SetMemoryBreakpoint(%p, %p, %d, %d)\n", address, size, type, singleshoot); //TODO: error reporting @@ -421,4 +419,4 @@ namespace GleeBug return false; } } -}; \ No newline at end of file +}; diff --git a/GleeBug/Debugger.Process.cpp b/GleeBug/Debugger.Process.cpp index 743b129..5e61f8d 100644 --- a/GleeBug/Debugger.Process.cpp +++ b/GleeBug/Debugger.Process.cpp @@ -15,7 +15,8 @@ namespace GleeBug createProcessInfo(createProcessInfo), thread(nullptr), systemBreakpoint(false), - permanentDep(false) + permanentDep(false), + emulator(this) { for (int i = 0; i < HWBP_COUNT; i++) hardwareBreakpoints[i].internal.hardware.enabled = false; @@ -58,4 +59,4 @@ namespace GleeBug } thread->StepInto(cbStep); } -}; \ No newline at end of file +}; diff --git a/GleeBug/Debugger.Process.h b/GleeBug/Debugger.Process.h index 48d38b0..86b8860 100644 --- a/GleeBug/Debugger.Process.h +++ b/GleeBug/Debugger.Process.h @@ -6,6 +6,7 @@ #include "Debugger.Dll.h" #include "Debugger.Breakpoint.h" #include "Static.Pattern.h" +#include "Emulation.h" namespace GleeBug { @@ -31,6 +32,7 @@ namespace GleeBug BreakpointInfo hardwareBreakpoints[4]; MemoryBreakpointSet memoryBreakpointRanges; MemoryBreakpointMap memoryBreakpointPages; + X86Emulator emulator; /** \brief Constructor. @@ -399,4 +401,4 @@ namespace GleeBug }; }; -#endif //DEBUGGER_PROCESS_H \ No newline at end of file +#endif //DEBUGGER_PROCESS_H diff --git a/GleeBug/Debugger.Thread.cpp b/GleeBug/Debugger.Thread.cpp index 072aefa..45c5541 100644 --- a/GleeBug/Debugger.Thread.cpp +++ b/GleeBug/Debugger.Thread.cpp @@ -52,4 +52,4 @@ namespace GleeBug { return ResumeThread(hThread) != -1; } -}; \ No newline at end of file +}; diff --git a/GleeBug/Emulation.Helpers.h b/GleeBug/Emulation.Helpers.h new file mode 100644 index 0000000..edda6db --- /dev/null +++ b/GleeBug/Emulation.Helpers.h @@ -0,0 +1,183 @@ +#ifndef EMULATIONHELPERS_H +#define EMULATIONHELPERS_H + +#include + +namespace GleeBug +{ + +template +inline T getRegisterValue(const ZydisRegister reg, Registers& regs) +{ + switch (reg) + { + // 64 Bit. + case ZYDIS_REGISTER_RAX: + return (T)regs.Rax.Get(); + case ZYDIS_REGISTER_RBX: + return (T)regs.Rbx.Get(); + case ZYDIS_REGISTER_RCX: + return (T)regs.Rcx.Get(); + case ZYDIS_REGISTER_RDX: + return (T)regs.Rdx.Get(); + case ZYDIS_REGISTER_RBP: + return (T)regs.Rbp.Get(); + case ZYDIS_REGISTER_RSP: + return (T)regs.Rsp.Get(); + case ZYDIS_REGISTER_RSI: + return (T)regs.Rsi.Get(); + case ZYDIS_REGISTER_RDI: + return (T)regs.Rdi.Get(); + case ZYDIS_REGISTER_R8: + return (T)regs.R8.Get(); + case ZYDIS_REGISTER_R9: + return (T)regs.R9.Get(); + case ZYDIS_REGISTER_R10: + return (T)regs.R10.Get(); + case ZYDIS_REGISTER_R11: + return (T)regs.R11.Get(); + case ZYDIS_REGISTER_R12: + return (T)regs.R12.Get(); + case ZYDIS_REGISTER_R13: + return (T)regs.R13.Get(); + case ZYDIS_REGISTER_R14: + return (T)regs.R14.Get(); + // 32 bit + case ZYDIS_REGISTER_EAX: + return (T)regs.Eax.Get(); + case ZYDIS_REGISTER_EBX: + return (T)regs.Ebx.Get(); + case ZYDIS_REGISTER_ECX: + return (T)regs.Ecx.Get(); + case ZYDIS_REGISTER_EDX: + return (T)regs.Edx.Get(); + case ZYDIS_REGISTER_EBP: + return (T)regs.Ebp.Get(); + case ZYDIS_REGISTER_ESP: + return (T)regs.Esp.Get(); + case ZYDIS_REGISTER_ESI: + return (T)regs.Esi.Get(); + case ZYDIS_REGISTER_EDI: + return (T)regs.Edi.Get(); + case ZYDIS_REGISTER_R8D: + return (T)regs.R8d.Get(); + case ZYDIS_REGISTER_R9D: + return (T)regs.R9d.Get(); + case ZYDIS_REGISTER_R10D: + return (T)regs.R10d.Get(); + case ZYDIS_REGISTER_R11D: + return (T)regs.R11d.Get(); + case ZYDIS_REGISTER_R12D: + return (T)regs.R12d.Get(); + case ZYDIS_REGISTER_R13D: + return (T)regs.R13d.Get(); + case ZYDIS_REGISTER_R14D: + return (T)regs.R14d.Get(); + default: + assert(false); + } +} + +template +inline void setRegisterValue(const ZydisRegister reg, Registers& regs, T val) +{ + switch (reg) + { + // 64 bit. + case ZYDIS_REGISTER_RAX: + return regs.Rax.Set(val); + case ZYDIS_REGISTER_RBX: + return regs.Rbx.Set(val); + case ZYDIS_REGISTER_RCX: + return regs.Rcx.Set(val); + case ZYDIS_REGISTER_RDX: + return regs.Rdx.Set(val); + case ZYDIS_REGISTER_RBP: + return regs.Rbp.Set(val); + case ZYDIS_REGISTER_RSP: + return regs.Rsp.Set(val); + case ZYDIS_REGISTER_RSI: + return regs.Rsi.Set(val); + case ZYDIS_REGISTER_RDI: + return regs.Rdi.Set(val); + case ZYDIS_REGISTER_R8: + return regs.R8.Set(val); + case ZYDIS_REGISTER_R9: + return regs.R9.Set(val); + case ZYDIS_REGISTER_R10: + return regs.R10.Set(val); + case ZYDIS_REGISTER_R11: + return regs.R11.Set(val); + case ZYDIS_REGISTER_R12: + return regs.R12.Set(val); + case ZYDIS_REGISTER_R13: + return regs.R13.Set(val); + case ZYDIS_REGISTER_R14: + return regs.R14.Set(val); + // 32 bit + case ZYDIS_REGISTER_EAX: + return regs.Eax.Set(val); + case ZYDIS_REGISTER_EBX: + return regs.Ebx.Set(val); + case ZYDIS_REGISTER_ECX: + return regs.Ecx.Set(val); + case ZYDIS_REGISTER_EDX: + return regs.Edx.Set(val); + case ZYDIS_REGISTER_EBP: + return regs.Ebp.Set(val); + case ZYDIS_REGISTER_ESP: + return regs.Esp.Set(val); + case ZYDIS_REGISTER_ESI: + return regs.Esi.Set(val); + case ZYDIS_REGISTER_EDI: + return regs.Edi.Set(val); + case ZYDIS_REGISTER_R8D: + return regs.R8d.Set(val); + case ZYDIS_REGISTER_R9D: + return regs.R9d.Set(val); + case ZYDIS_REGISTER_R10D: + return regs.R10d.Set(val); + case ZYDIS_REGISTER_R11D: + return regs.R11d.Set(val); + case ZYDIS_REGISTER_R12D: + return regs.R12d.Set(val); + case ZYDIS_REGISTER_R13D: + return regs.R13d.Set(val); + case ZYDIS_REGISTER_R14D: + return regs.R14d.Set(val); + default: + assert(false); + } +} + +inline void setCondFlag(uint32_t& flags, int64_t cond, uint32_t f) +{ + if(!!cond) + flags |= f; +} + +inline bool isParity(uint32_t v) +{ + v ^= v >> 1; + v ^= v >> 2; + v = (v & 0x11111111U) * 0x11111111U; + return (v >> 28) & 1; +} + +inline bool isParity(uint64_t v) +{ + v ^= v >> 1; + v ^= v >> 2; + v = (v & 0x1111111111111111UL) * 0x1111111111111111UL; + return (v >> 60) & 1; +} + +template +inline T xor2(T x) +{ + return (((x) ^ ((x) >> 1)) & 0x1); +} + +} // GleeBug + +#endif // EMULATIONHELPERS_H diff --git a/GleeBug/Emulation.Instructions.cpp b/GleeBug/Emulation.Instructions.cpp new file mode 100644 index 0000000..eadf953 --- /dev/null +++ b/GleeBug/Emulation.Instructions.cpp @@ -0,0 +1,266 @@ +#include "Emulation.Instructions.h" +#include "Debugger.Thread.Registers.h" +#include "Emulation.Helpers.h" + +#include +#include +#include + +namespace GleeBug { +namespace Emulation { + +enum EFLAGS +{ + EFL_CF = (1 << 0), + EFL_PF = (1 << 2), + EFL_AF = (1 << 4), + EFL_ZF = (1 << 6), + EFL_SF = (1 << 7), + EFL_DF = (1 << 10), + EFL_OF = (1 << 11), +}; + +inline const size_t InstructionDataHash(size_t state, const void* data, const size_t len) +{ + const uint8_t *buf = reinterpret_cast(data); + + for (size_t i = 0; i < len; ++buf, ++i) + { + state ^= ((i & 1) == 0) ? ((state << 7) ^ (*buf) * (state >> 3)) : + (~((state << 11) + ((*buf) ^ (state >> 5)))); + } + + return state; +} + +template +inline const size_t InstructionDataHash(const T& data) +{ + return InstructionDataHash(0x811c9dc5, &data, sizeof(T)); +} + +inline const size_t InstructionOperandHash(size_t N, uint16_t TYPE) +{ + return InstructionDataHash(InstructionDataHash(N)); +} + +template +struct OpSig +{ + static constexpr size_t Hash() noexcept + { + return InstructionOperandHash(N, TYPE); + } + static bool Matches(const ZydisInstructionInfo& instr, const ZydisOperandInfo& op) + { + if(op.type != TYPE) + return false; + if(op.size != N) + return false; + return true; + } +}; + +using OpNone = OpSig<0, ZYDIS_OPERAND_TYPE_UNUSED>; + +template +struct InstrSig : X86Instruction +{ + static constexpr size_t Hash() noexcept + { + return InstructionDataHash(InstructionDataHash(N)); + } + + virtual uint16_t GetMnemonic() const + { + return MNEMONIC; + } + + virtual bool Matches(const ZydisInstructionInfo& info) const + { + if(info.mnemonic != MNEMONIC) + return false; + if (!OP0::Matches(info, info.operands[0])) + return false; + if (!OP1::Matches(info, info.operands[1])) + return false; + if (!OP2::Matches(info, info.operands[2])) + return false; + if (!OP3::Matches(info, info.operands[3])) + return false; + if (!OP4::Matches(info, info.operands[4])) + return false; + + return true; + } +}; + +static std::vector> _registeredInstructions; + +template +struct X86InstructionRegistrator +{ + T inst; + X86InstructionRegistrator() + { + uint16_t groupId = inst.GetMnemonic(); + if (groupId >= _registeredInstructions.size()) + { + _registeredInstructions.resize(groupId + 1); + } + _registeredInstructions[groupId].push_back(&inst); + } +}; + +#define REGISTER_X86_INSTRUCTION(instrCls) static X86InstructionRegistrator<##instrCls##> __##instrCls##__ + +template +struct SubImpl +{ + static T Execute(T val1, T val2, Registers& regs, uint32_t flagsOut) + { + constexpr int NumBits = sizeof(T) * 8; + constexpr T SignMask = T(1) << (NumBits - 1); + constexpr T InvMask = ~T(0); + + T res = val1 - val2; + + flagsOut = 0; + setCondFlag(flagsOut, res & SignMask, EFL_SF); + setCondFlag(flagsOut, (res & InvMask) == 0, EFL_ZF); + setCondFlag(flagsOut, isParity(res & 0xFF), EFL_PF); + + T bc = (res & (~val1 | val2)) | (~val1 & val2); + setCondFlag(flagsOut, bc & SignMask, EFL_CF); + setCondFlag(flagsOut, xor2(bc >> (NumBits - 2)), EFL_OF); + setCondFlag(flagsOut, bc & 0x08, EFL_AF); + + return res; + } +}; + +struct Instr_MovR32R32 : InstrSig< ZYDIS_MNEMONIC_MOV, OpSig<32, ZYDIS_OPERAND_TYPE_REGISTER>, OpSig<32, ZYDIS_OPERAND_TYPE_REGISTER> > +{ + virtual bool Execute(const ZydisInstructionInfo& info, X86Context& ctx) const override + { + int32_t val1 = getRegisterValue(info.operands[1].reg, ctx.regs); + + setRegisterValue(info.operands[0].reg, ctx.regs, val1); + return true; + } +}; + +struct Instr_MovR32I32 : InstrSig< ZYDIS_MNEMONIC_MOV, OpSig<32, ZYDIS_OPERAND_TYPE_REGISTER>, OpSig<32, ZYDIS_OPERAND_TYPE_IMMEDIATE> > +{ + virtual bool Execute(const ZydisInstructionInfo& info, X86Context& ctx) const override + { + int32_t val1 = info.operands[1].imm.value.sdword; + + setRegisterValue(info.operands[0].reg, ctx.regs, val1); + return true; + } +}; + +struct Instr_MovR64R64 : InstrSig< ZYDIS_MNEMONIC_MOV, OpSig<64, ZYDIS_OPERAND_TYPE_REGISTER>, OpSig<64, ZYDIS_OPERAND_TYPE_REGISTER> > +{ + virtual bool Execute(const ZydisInstructionInfo& info, X86Context& ctx) const override + { + int64_t val2 = getRegisterValue(info.operands[1].reg, ctx.regs); + + setRegisterValue(info.operands[0].reg, ctx.regs, val2); + return true; + } +}; + +struct Instr_Sub32 : InstrSig< ZYDIS_MNEMONIC_SUB, OpSig<32, ZYDIS_OPERAND_TYPE_REGISTER>, OpSig<32, ZYDIS_OPERAND_TYPE_IMMEDIATE> > +{ + virtual bool Execute(const ZydisInstructionInfo& info, X86Context& ctx) const override + { + // Value is sign extended. + int32_t val1 = getRegisterValue(info.operands[0].reg, ctx.regs); + int32_t val2 = info.operands[1].imm.value.sdword; + + uint32_t flagsOut = 0; + int32_t res = SubImpl::Execute(val1, val2, ctx.regs, flagsOut); + + setRegisterValue(info.operands[0].reg, ctx.regs, res); + ctx.regs.Eflags.Set(ctx.regs.Eflags.Get() | flagsOut); + + return true; + } +}; + +struct Instr_SubR64I64 : InstrSig< ZYDIS_MNEMONIC_SUB, OpSig<64, ZYDIS_OPERAND_TYPE_REGISTER>, OpSig<64, ZYDIS_OPERAND_TYPE_IMMEDIATE> > +{ + virtual bool Execute(const ZydisInstructionInfo& info, X86Context& ctx) const override + { + // Value is sign extended. + int64_t val1 = getRegisterValue(info.operands[0].reg, ctx.regs); + int64_t val2 = info.operands[1].imm.value.sqword; + + uint32_t flagsOut = 0; + int64_t res = SubImpl::Execute(val1, val2, ctx.regs, flagsOut); + + setRegisterValue(info.operands[0].reg, ctx.regs, res); + ctx.regs.Eflags.Set(ctx.regs.Eflags.Get() | flagsOut); + + return true; + } +}; + +struct Instr_SubR64I32 : InstrSig< ZYDIS_MNEMONIC_SUB, OpSig<64, ZYDIS_OPERAND_TYPE_REGISTER>, OpSig<32, ZYDIS_OPERAND_TYPE_IMMEDIATE> > +{ + virtual bool Execute(const ZydisInstructionInfo& info, X86Context& ctx) const override + { + // Value is sign extended. + int64_t val1 = getRegisterValue(info.operands[0].reg, ctx.regs); + int64_t val2 = info.operands[1].imm.value.sdword; + + uint32_t flagsOut = 0; + int64_t res = SubImpl::Execute(val1, val2, ctx.regs, flagsOut); + + setRegisterValue(info.operands[0].reg, ctx.regs, res); + ctx.regs.Eflags.Set(ctx.regs.Eflags.Get() | flagsOut); + + return true; + } +}; + +static bool _initialzed = false; + +const X86Instruction* LookupInstruction(const ZydisInstructionInfo& info) +{ + if (_initialzed == false) + { + REGISTER_X86_INSTRUCTION(Instr_MovR32R32); + REGISTER_X86_INSTRUCTION(Instr_MovR32I32); + REGISTER_X86_INSTRUCTION(Instr_MovR64R64); + REGISTER_X86_INSTRUCTION(Instr_Sub32); + REGISTER_X86_INSTRUCTION(Instr_SubR64I64); + REGISTER_X86_INSTRUCTION(Instr_SubR64I32); + _initialzed = true; + } + + if(info.mnemonic >= _registeredInstructions.size()) + return nullptr; + + auto& group = _registeredInstructions[info.mnemonic]; + if(group.empty()) + return nullptr; + + for (X86Instruction *instr : group) + { + if (instr->Matches(info)) + return instr; + } + + return nullptr; +} + +} +} diff --git a/GleeBug/Emulation.Instructions.h b/GleeBug/Emulation.Instructions.h new file mode 100644 index 0000000..1f24d6b --- /dev/null +++ b/GleeBug/Emulation.Instructions.h @@ -0,0 +1,41 @@ +#ifndef EMULATIONINSTRUCTIONS_H +#define EMULATIONINSTRUCTIONS_H + +#include "Debugger.Global.h" +#include "Debugger.Process.h" +#include "Debugger.Breakpoint.h" +#include "Debugger.Thread.h" +#include "Debugger.Thread.Registers.h" + +#define ZYDIS_EXPORTS +#define ZYDIS_ENABLE_FEATURE_IMPLICITLY_USED_REGISTERS +#define ZYDIS_ENABLE_FEATURE_AFFECTED_FLAGS +#include + +namespace GleeBug { +namespace Emulation { + +struct X86Context +{ + Registers& regs; + Thread& thread; + + explicit X86Context(Registers& inRegs, Thread& inThread) : regs(inRegs), + thread(inThread) + { + } +}; + +struct X86Instruction +{ + virtual uint16_t GetMnemonic() const = 0; + virtual bool Matches(const ZydisInstructionInfo& info) const = 0; + virtual bool Execute(const ZydisInstructionInfo& info, X86Context& ctx) const = 0; +}; + +const X86Instruction* LookupInstruction(const ZydisInstructionInfo& info); + +} // Emulation +} // GleeBug + +#endif // EMULATIONINSTRUCTIONS_H diff --git a/GleeBug/Emulation.cpp b/GleeBug/Emulation.cpp new file mode 100644 index 0000000..a0eb7c1 --- /dev/null +++ b/GleeBug/Emulation.cpp @@ -0,0 +1,148 @@ +#include "GleeBug.h" +#include "Emulation.h" +#include "Emulation.Instructions.h" +#include "Debugger.Thread.Registers.h" +#include "Debugger.Thread.Registers.Flag.h" + +#define ZYDIS_EXPORTS +#define ZYDIS_ENABLE_FEATURE_IMPLICITLY_USED_REGISTERS +#define ZYDIS_ENABLE_FEATURE_AFFECTED_FLAGS +#include +#include +#include + +namespace GleeBug { + +#ifdef _WIN64 +static constexpr ZydisOperatingMode ZYDIS_OPERATING_MODE = ZYDIS_OPERATING_MODE_64BIT; +#else +static constexpr ZydisOperatingMode ZYDIS_OPERATING_MODE = ZYDIS_OPERATING_MODE_32BIT; +#endif + +X86Emulator::X86Emulator(Process* process) + : _process(process) +{ +} + +bool X86Emulator::IsActive() const +{ + return _active; +} + +bool X86Emulator::WaitForEvent(DEBUG_EVENT& debugEvent) +{ + if(_hasEvent == false) + return false; + + debugEvent = _currentEvent; + _hasEvent = false; + + return true; +} + +bool GenerateDebugEvent(const ZydisInstructionInfo& instrInfo, Emulation::X86Context& ctx, DEBUG_EVENT& debugEvent) +{ + if (ctx.regs.TrapFlag) + { + debugEvent.dwDebugEventCode = EXCEPTION_DEBUG_EVENT; + + auto& exData = debugEvent.u.Exception; + exData.ExceptionRecord.ExceptionCode = STATUS_SINGLE_STEP; + exData.ExceptionRecord.ExceptionAddress = (PVOID)ctx.regs.Gip.Get(); + exData.ExceptionRecord.NumberParameters = 0; + exData.dwFirstChance = 1; + + return true; + } + // TODO: Deal with breakpoint emulation. + + return false; +} + +bool X86Emulator::Emulate(Thread* thread) +{ + SetActiveThread(thread); + + uint8_t instrBytes[16]; + ptr bytesRead = 0; + if (!_process->MemRead(_registers->Gip.Get(), instrBytes, sizeof(instrBytes), &bytesRead)) + { + Flush(); + return false; + } + + const ptr curIP = _registers->Gip.Get(); + + ZydisInstructionInfo instrInfo; + if (ZYDIS_SUCCESS(ZydisDecode(ZYDIS_OPERATING_MODE, instrBytes, 16, curIP, &instrInfo))) + { +#ifdef _DEBUG + ZydisInstructionFormatter formatter; + ZydisFormatterInitInstructionFormatter(&formatter, ZYDIS_FORMATTER_STYLE_INTEL); + + char decodedInstr[128]; + ZydisFormatterFormatInstruction(&formatter, &instrInfo, decodedInstr, sizeof(decodedInstr)); +#endif + + const Emulation::X86Instruction *instr = Emulation::LookupInstruction(instrInfo); + if (instr == nullptr) + { + Flush(); + return false; + } + + bool isSingleStepping = _activeThread->isInternalStepping || _activeThread->isSingleStepping; + + Emulation::X86Context ctx(*_registers, *_activeThread); + if (instr->Execute(instrInfo, ctx)) + { + _registers->Gip.Set(curIP + instrInfo.length); + } + + //dprintf("Stepped with emulation at %p\n", curIP); + + _currentEvent.dwProcessId = _process->dwProcessId; + _currentEvent.dwThreadId = _activeThread->dwThreadId; + + if (GenerateDebugEvent(instrInfo, ctx, _currentEvent)) + { + if (isSingleStepping) + { + ctx.regs.TrapFlag = false; + } + _hasEvent = true; + Flush(); + } + } + else + { + Flush(); + return false; + } + + return true; +} + +void X86Emulator::Flush() +{ + if (_registers != nullptr) + { + delete _registers; + _registers = nullptr; + } + _activeThread = nullptr; +} + +void X86Emulator::SetActiveThread(Thread *thread) +{ + if (_activeThread != thread) + { + // Commit current state if we change threads between. + Flush(); + + _registers = new Registers(thread->hThread, CONTEXT_CONTROL | CONTEXT_DEBUG_REGISTERS | CONTEXT_INTEGER); + _activeThread = thread; + } +} + +} diff --git a/GleeBug/Emulation.h b/GleeBug/Emulation.h new file mode 100644 index 0000000..2458578 --- /dev/null +++ b/GleeBug/Emulation.h @@ -0,0 +1,39 @@ +#ifndef EMULATION_H +#define EMULATION_H + +#include "Debugger.Global.h" +#include "Debugger.Breakpoint.h" +#include "Debugger.Thread.h" +#include "Debugger.Thread.Registers.h" + +#include + +namespace GleeBug +{ + +class Process; + +class X86Emulator +{ + bool _active = false; + Thread *_activeThread = nullptr; + Registers* _registers = nullptr; + Process* _process = nullptr; + bool _hasEvent = false; + DEBUG_EVENT _currentEvent{}; + +public: + X86Emulator(Process* process); + + bool IsActive() const; + bool WaitForEvent(DEBUG_EVENT& debugEvent); + bool Emulate(Thread* thread); + void Flush(); + +private: + void SetActiveThread(Thread *thread); +}; + +} // GleeBug + +#endif // EMULATION_H diff --git a/GleeBug/GleeBug.h b/GleeBug/GleeBug.h index b4ed193..fc5dde0 100644 --- a/GleeBug/GleeBug.h +++ b/GleeBug/GleeBug.h @@ -25,10 +25,14 @@ #define X64DBG_MOD L"x32dbg.dll" #endif //_WIN64 -#define DPRINTF() \ - static auto dprintf = (int(*)(const char* format, ...))GetProcAddress(GetModuleHandleW(X64DBG_MOD), "_plugin_logprintf"); \ - if(!dprintf) \ - dprintf = printf +template +inline void dprintf(const char *fmt, Args... args) +{ + static auto fn = (int(*)(const char* format, ...))GetProcAddress(GetModuleHandleW(X64DBG_MOD), "_plugin_logprintf"); + if (!fn) + fn = printf; + fn(fmt, args...); +} #ifdef _WIN64 #define GleeArchValue(x32value, x64value) x64value @@ -72,4 +76,4 @@ namespace GleeBug }; } -#endif //GLEEBUG_H \ No newline at end of file +#endif //GLEEBUG_H diff --git a/GleeBug/GleeBug.vcxproj b/GleeBug/GleeBug.vcxproj index de60782..398f6a7 100644 --- a/GleeBug/GleeBug.vcxproj +++ b/GleeBug/GleeBug.vcxproj @@ -167,6 +167,8 @@ + + @@ -194,6 +196,9 @@ + + + diff --git a/GleeBug/GleeBug.vcxproj.filters b/GleeBug/GleeBug.vcxproj.filters index d778bb0..2e7a502 100644 --- a/GleeBug/GleeBug.vcxproj.filters +++ b/GleeBug/GleeBug.vcxproj.filters @@ -104,6 +104,12 @@ Source Files\Zydis + + Source Files + + + Source Files + @@ -193,6 +199,15 @@ Header Files + + Header Files + + + Header Files + + + Header Files + diff --git a/TitanEngineEmulator/Emulator.h b/TitanEngineEmulator/Emulator.h index 9bbcafe..69d27fc 100644 --- a/TitanEngineEmulator/Emulator.h +++ b/TitanEngineEmulator/Emulator.h @@ -18,7 +18,7 @@ using namespace GleeBug; -class Emulator : public Debugger +class TitanEngineEmulator : public Debugger { public: HINSTANCE engineHandle; diff --git a/TitanEngineEmulator/TitanEngineEmulator.cpp b/TitanEngineEmulator/TitanEngineEmulator.cpp index 83ddb51..723780c 100644 --- a/TitanEngineEmulator/TitanEngineEmulator.cpp +++ b/TitanEngineEmulator/TitanEngineEmulator.cpp @@ -1,7 +1,7 @@ #include "ntdll.h" #include "Emulator.h" -Emulator emu; +TitanEngineEmulator emu; //Debugger basics __declspec(dllexport) void* TITCALL InitDebugW(const wchar_t* szFileName, const wchar_t* szCommandLine, const wchar_t* szCurrentFolder) @@ -286,4 +286,4 @@ BOOL WINAPI DllMain( if (fdwReason == DLL_PROCESS_ATTACH) emu.engineHandle = hinstDLL; return TRUE; -} \ No newline at end of file +}