From 982b433ef4fabae6d44e0736c6190d9acffa66b0 Mon Sep 17 00:00:00 2001 From: Matt Date: Sun, 19 May 2019 16:05:43 +0200 Subject: [PATCH] Add initial emulation framework --- GleeBug/Debugger.Loop.cpp | 40 ++++- GleeBug/Debugger.Process.cpp | 2 +- GleeBug/Debugger.Process.h | 4 +- GleeBug/Debugger.Thread.cpp | 2 +- GleeBug/Emulation.Helpers.h | 141 +++++++++++++++ GleeBug/Emulation.Instructions.cpp | 181 ++++++++++++++++++++ GleeBug/Emulation.Instructions.h | 40 +++++ GleeBug/Emulation.cpp | 45 +++++ GleeBug/Emulation.h | 32 ++++ GleeBug/GleeBug.vcxproj | 5 + GleeBug/GleeBug.vcxproj.filters | 15 ++ TitanEngineEmulator/Emulator.h | 2 +- TitanEngineEmulator/TitanEngineEmulator.cpp | 4 +- 13 files changed, 502 insertions(+), 11 deletions(-) create mode 100644 GleeBug/Emulation.Helpers.h create mode 100644 GleeBug/Emulation.Instructions.cpp create mode 100644 GleeBug/Emulation.Instructions.h create mode 100644 GleeBug/Emulation.cpp create mode 100644 GleeBug/Emulation.h diff --git a/GleeBug/Debugger.Loop.cpp b/GleeBug/Debugger.Loop.cpp index 0a3ad19..037d32e 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 { @@ -28,8 +29,26 @@ namespace GleeBug { //wait for a debug event mIsRunning = true; - if (!MyWaitForDebugEvent(&mDebugEvent, INFINITE)) - break; + + bool emulatedEvent = false; + + // We go over all active processes and see if any emulator has an active event + // if thats the case process the emulated event first. + for (auto&& process : mProcesses) + { + if (process.second->emulator.WaitForEvent(mDebugEvent)) + { + emulatedEvent = true; + } + } + + // Emulated events have priority over normal debug events. + if (!emulatedEvent) + { + if (!MyWaitForDebugEvent(&mDebugEvent, INFINITE)) + break; + } + mIsRunning = false; //set default continue status @@ -117,8 +136,19 @@ namespace GleeBug } //continue the debug event - if (!ContinueDebugEvent(mDebugEvent.dwProcessId, mDebugEvent.dwThreadId, mContinueStatus)) - break; + + bool continueEmulated = false; + if (mThread->isInternalStepping) + { + auto& emulator = mProcess->emulator; + continueEmulated = emulator.Emulate(mThread); + } + + if (continueEmulated == false) + { + if (!ContinueDebugEvent(mDebugEvent.dwProcessId, mDebugEvent.dwThreadId, mContinueStatus)) + break; + } if (mDetach || mDetachAndBreak) { @@ -133,4 +163,4 @@ namespace GleeBug mProcess = nullptr; mIsDebugging = false; } -}; \ No newline at end of file +}; diff --git a/GleeBug/Debugger.Process.cpp b/GleeBug/Debugger.Process.cpp index 743b129..4523260 100644 --- a/GleeBug/Debugger.Process.cpp +++ b/GleeBug/Debugger.Process.cpp @@ -58,4 +58,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..d86da64 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; + Emulator 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..2eae764 --- /dev/null +++ b/GleeBug/Emulation.Helpers.h @@ -0,0 +1,141 @@ +#ifndef EMULATIONHELPERS_H +#define EMULATIONHELPERS_H + +namespace GleeBug +{ + +template +inline T getRegisterValue(const ZydisRegister reg, Registers& regs) +{ + switch (reg) + { + 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(); + } +} + +template +inline void setRegisterValue(const ZydisRegister reg, Registers& regs, T val) +{ + switch (reg) + { + 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); + } +} + +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..74cd19e --- /dev/null +++ b/GleeBug/Emulation.Instructions.cpp @@ -0,0 +1,181 @@ +#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 +{ + static constexpr size_t Hash() noexcept + { + return InstructionDataHash(InstructionDataHash(N)); + } + + 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 +{ + X86InstructionRegistrator() + { + static T inst; + _registeredInstructions.push_back(&inst); + } +}; + +const X86Instruction* LookupInstruction(const ZydisInstructionInfo& info) +{ + for (X86Instruction *instr : _registeredInstructions) + { + if(instr->Matches(info)) + return instr; + } + return nullptr; +} + +#define REGISTER_X86_INSTRUCTION(instrCls) static X86InstructionRegistrator<##instrCls##> __##instrCls##__() + +template +struct SubImpl +{ + static T Execute(int32_t val1, int32_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_Sub32 : X86Instruction, InstrSig< ZYDIS_MNEMONIC_SUB, OpSig<32, ZYDIS_OPERAND_TYPE_REGISTER>, OpSig<32, ZYDIS_OPERAND_TYPE_IMMEDIATE> > +{ + virtual bool Execute(const ZydisInstructionInfo& info, X86Context& ctx) override + { + // Value is sign extended. + int32_t val1 = getRegisterValue(info.operands[1].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[1].reg, ctx.regs, res); + } +}; +REGISTER_X86_INSTRUCTION(Instr_Sub32); + +struct Instr_Sub64 : X86Instruction, InstrSig< ZYDIS_MNEMONIC_SUB, OpSig<64, ZYDIS_OPERAND_TYPE_REGISTER>, OpSig<32, ZYDIS_OPERAND_TYPE_IMMEDIATE> > +{ + virtual bool Execute(const ZydisInstructionInfo& info, X86Context& ctx) override + { + // Value is sign extended. + int64_t val1 = getRegisterValue(info.operands[1].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[1].reg, ctx.regs, res); + } +}; + +REGISTER_X86_INSTRUCTION(Instr_Sub64); + +} +} diff --git a/GleeBug/Emulation.Instructions.h b/GleeBug/Emulation.Instructions.h new file mode 100644 index 0000000..91a7321 --- /dev/null +++ b/GleeBug/Emulation.Instructions.h @@ -0,0 +1,40 @@ +#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 bool Matches(const ZydisInstructionInfo& info) = 0; + virtual bool Execute(const ZydisInstructionInfo& info, X86Context& ctx) = 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..92888c4 --- /dev/null +++ b/GleeBug/Emulation.cpp @@ -0,0 +1,45 @@ +#include "Emulation.h" +#include "Emulation.Instructions.h" + +namespace GleeBug { + +bool Emulator::IsActive() const +{ + return _active; +} + +bool Emulator::WaitForEvent(DEBUG_EVENT& debugEvent) const +{ + return false; +} + +bool Emulator::Emulate(Thread* thread) +{ + SetActiveThread(thread); + + return true; +} + +void Emulator::Flush() +{ + if (_registers != nullptr) + { + delete _registers; + _registers = nullptr; + } + _activeThread = nullptr; +} + +void Emulator::SetActiveThread(Thread *thread) +{ + if (_activeThread != thread) + { + // Commit current state if we change threads between. + Flush(); + + _registers = new Registers(thread->hThread, CONTEXT_CONTROL); + _activeThread = thread; + } +} + +} diff --git a/GleeBug/Emulation.h b/GleeBug/Emulation.h new file mode 100644 index 0000000..40edcaf --- /dev/null +++ b/GleeBug/Emulation.h @@ -0,0 +1,32 @@ +#ifndef EMULATION_H +#define EMULATION_H + +#include "Debugger.Global.h" +#include "Debugger.Breakpoint.h" +#include "Debugger.Thread.h" +#include "Debugger.Thread.Registers.h" + +namespace GleeBug +{ + +class Process; + +class Emulator +{ + bool _active = false; + Thread *_activeThread = nullptr; + Registers* _registers = nullptr; + +public: + bool IsActive() const; + bool WaitForEvent(DEBUG_EVENT& debugEvent) const; + bool Emulate(Thread* thread); + void Flush(); + +private: + void SetActiveThread(Thread *thread); +}; + +} // GleeBug + +#endif // EMULATION_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 b8ecb03..2df0335 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 172a0ae..b7aafdb 100644 --- a/TitanEngineEmulator/TitanEngineEmulator.cpp +++ b/TitanEngineEmulator/TitanEngineEmulator.cpp @@ -1,7 +1,7 @@ #include #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) @@ -248,4 +248,4 @@ BOOL WINAPI DllMain( if (fdwReason == DLL_PROCESS_ATTACH) emu.engineHandle = hinstDLL; return TRUE; -} \ No newline at end of file +}