mirror of https://github.com/x64dbg/GleeBug
Initial working prototype
This commit is contained in:
parent
982b433ef4
commit
b55697e260
|
|
@ -25,25 +25,32 @@ namespace GleeBug
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool allowEmulation = true;
|
||||||
|
bool inEmulation = false;
|
||||||
|
|
||||||
while (!mBreakDebugger)
|
while (!mBreakDebugger)
|
||||||
{
|
{
|
||||||
//wait for a debug event
|
//wait for a debug event
|
||||||
mIsRunning = true;
|
mIsRunning = true;
|
||||||
|
|
||||||
bool emulatedEvent = false;
|
|
||||||
|
|
||||||
// We go over all active processes and see if any emulator has an active event
|
// 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 thats the case process the emulated event first.
|
||||||
for (auto&& process : mProcesses)
|
if (inEmulation)
|
||||||
{
|
{
|
||||||
if (process.second->emulator.WaitForEvent(mDebugEvent))
|
inEmulation = false;
|
||||||
|
|
||||||
|
for (auto&& process : mProcesses)
|
||||||
{
|
{
|
||||||
emulatedEvent = true;
|
if (process.second->emulator.WaitForEvent(mDebugEvent))
|
||||||
|
{
|
||||||
|
inEmulation = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emulated events have priority over normal debug events.
|
// Emulated events have priority over normal debug events.
|
||||||
if (!emulatedEvent)
|
if (!inEmulation)
|
||||||
{
|
{
|
||||||
if (!MyWaitForDebugEvent(&mDebugEvent, INFINITE))
|
if (!MyWaitForDebugEvent(&mDebugEvent, INFINITE))
|
||||||
break;
|
break;
|
||||||
|
|
@ -137,14 +144,13 @@ namespace GleeBug
|
||||||
|
|
||||||
//continue the debug event
|
//continue the debug event
|
||||||
|
|
||||||
bool continueEmulated = false;
|
if (allowEmulation && mThread != nullptr && mThread->isSingleStepping)
|
||||||
if (mThread->isInternalStepping)
|
|
||||||
{
|
{
|
||||||
auto& emulator = mProcess->emulator;
|
auto& emulator = mProcess->emulator;
|
||||||
continueEmulated = emulator.Emulate(mThread);
|
inEmulation = emulator.Emulate(mThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (continueEmulated == false)
|
if (inEmulation == false)
|
||||||
{
|
{
|
||||||
if (!ContinueDebugEvent(mDebugEvent.dwProcessId, mDebugEvent.dwThreadId, mContinueStatus))
|
if (!ContinueDebugEvent(mDebugEvent.dwProcessId, mDebugEvent.dwThreadId, mContinueStatus))
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -225,7 +225,6 @@ namespace GleeBug
|
||||||
|
|
||||||
bool Process::SetNewPageProtection(ptr page, MemoryBreakpointData & data, MemoryType type)
|
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)
|
//TODO: handle PAGE_NOACCESS and such correctly (since it cannot be combined with PAGE_GUARD)
|
||||||
|
|
||||||
auto found = memoryBreakpointPages.find(page);
|
auto found = memoryBreakpointPages.find(page);
|
||||||
|
|
@ -268,7 +267,6 @@ namespace GleeBug
|
||||||
|
|
||||||
bool Process::SetMemoryBreakpoint(ptr address, ptr size, MemoryType type, bool singleshoot)
|
bool Process::SetMemoryBreakpoint(ptr address, ptr size, MemoryType type, bool singleshoot)
|
||||||
{
|
{
|
||||||
DPRINTF();
|
|
||||||
dprintf("SetMemoryBreakpoint(%p, %p, %d, %d)\n", address, size, type, singleshoot);
|
dprintf("SetMemoryBreakpoint(%p, %p, %d, %d)\n", address, size, type, singleshoot);
|
||||||
//TODO: error reporting
|
//TODO: error reporting
|
||||||
|
|
||||||
|
|
@ -421,4 +419,4 @@ namespace GleeBug
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,8 @@ namespace GleeBug
|
||||||
createProcessInfo(createProcessInfo),
|
createProcessInfo(createProcessInfo),
|
||||||
thread(nullptr),
|
thread(nullptr),
|
||||||
systemBreakpoint(false),
|
systemBreakpoint(false),
|
||||||
permanentDep(false)
|
permanentDep(false),
|
||||||
|
emulator(this)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < HWBP_COUNT; i++)
|
for (int i = 0; i < HWBP_COUNT; i++)
|
||||||
hardwareBreakpoints[i].internal.hardware.enabled = false;
|
hardwareBreakpoints[i].internal.hardware.enabled = false;
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ namespace GleeBug
|
||||||
BreakpointInfo hardwareBreakpoints[4];
|
BreakpointInfo hardwareBreakpoints[4];
|
||||||
MemoryBreakpointSet memoryBreakpointRanges;
|
MemoryBreakpointSet memoryBreakpointRanges;
|
||||||
MemoryBreakpointMap memoryBreakpointPages;
|
MemoryBreakpointMap memoryBreakpointPages;
|
||||||
Emulator emulator;
|
X86Emulator emulator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\brief Constructor.
|
\brief Constructor.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
#ifndef EMULATIONHELPERS_H
|
#ifndef EMULATIONHELPERS_H
|
||||||
#define EMULATIONHELPERS_H
|
#define EMULATIONHELPERS_H
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
namespace GleeBug
|
namespace GleeBug
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
@ -9,50 +11,70 @@ inline T getRegisterValue(const ZydisRegister reg, Registers& regs)
|
||||||
{
|
{
|
||||||
switch (reg)
|
switch (reg)
|
||||||
{
|
{
|
||||||
|
// 64 Bit.
|
||||||
case ZYDIS_REGISTER_RAX:
|
case ZYDIS_REGISTER_RAX:
|
||||||
return (T)regs.Rax.Get();
|
return (T)regs.Rax.Get();
|
||||||
|
|
||||||
case ZYDIS_REGISTER_RBX:
|
case ZYDIS_REGISTER_RBX:
|
||||||
return (T)regs.Rbx.Get();
|
return (T)regs.Rbx.Get();
|
||||||
|
|
||||||
case ZYDIS_REGISTER_RCX:
|
case ZYDIS_REGISTER_RCX:
|
||||||
return (T)regs.Rcx.Get();
|
return (T)regs.Rcx.Get();
|
||||||
|
|
||||||
case ZYDIS_REGISTER_RDX:
|
case ZYDIS_REGISTER_RDX:
|
||||||
return (T)regs.Rdx.Get();
|
return (T)regs.Rdx.Get();
|
||||||
|
|
||||||
case ZYDIS_REGISTER_RBP:
|
case ZYDIS_REGISTER_RBP:
|
||||||
return (T)regs.Rbp.Get();
|
return (T)regs.Rbp.Get();
|
||||||
|
|
||||||
case ZYDIS_REGISTER_RSP:
|
case ZYDIS_REGISTER_RSP:
|
||||||
return (T)regs.Rsp.Get();
|
return (T)regs.Rsp.Get();
|
||||||
|
|
||||||
case ZYDIS_REGISTER_RSI:
|
case ZYDIS_REGISTER_RSI:
|
||||||
return (T)regs.Rsi.Get();
|
return (T)regs.Rsi.Get();
|
||||||
|
|
||||||
case ZYDIS_REGISTER_RDI:
|
case ZYDIS_REGISTER_RDI:
|
||||||
return (T)regs.Rdi.Get();
|
return (T)regs.Rdi.Get();
|
||||||
|
|
||||||
case ZYDIS_REGISTER_R8:
|
case ZYDIS_REGISTER_R8:
|
||||||
return (T)regs.R8.Get();
|
return (T)regs.R8.Get();
|
||||||
|
|
||||||
case ZYDIS_REGISTER_R9:
|
case ZYDIS_REGISTER_R9:
|
||||||
return (T)regs.R9.Get();
|
return (T)regs.R9.Get();
|
||||||
|
|
||||||
case ZYDIS_REGISTER_R10:
|
case ZYDIS_REGISTER_R10:
|
||||||
return (T)regs.R10.Get();
|
return (T)regs.R10.Get();
|
||||||
|
|
||||||
case ZYDIS_REGISTER_R11:
|
case ZYDIS_REGISTER_R11:
|
||||||
return (T)regs.R11.Get();
|
return (T)regs.R11.Get();
|
||||||
|
|
||||||
case ZYDIS_REGISTER_R12:
|
case ZYDIS_REGISTER_R12:
|
||||||
return (T)regs.R12.Get();
|
return (T)regs.R12.Get();
|
||||||
|
|
||||||
case ZYDIS_REGISTER_R13:
|
case ZYDIS_REGISTER_R13:
|
||||||
return (T)regs.R13.Get();
|
return (T)regs.R13.Get();
|
||||||
|
|
||||||
case ZYDIS_REGISTER_R14:
|
case ZYDIS_REGISTER_R14:
|
||||||
return (T)regs.R14.Get();
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -61,50 +83,70 @@ inline void setRegisterValue(const ZydisRegister reg, Registers& regs, T val)
|
||||||
{
|
{
|
||||||
switch (reg)
|
switch (reg)
|
||||||
{
|
{
|
||||||
|
// 64 bit.
|
||||||
case ZYDIS_REGISTER_RAX:
|
case ZYDIS_REGISTER_RAX:
|
||||||
return regs.Rax.Set(val);
|
return regs.Rax.Set(val);
|
||||||
|
|
||||||
case ZYDIS_REGISTER_RBX:
|
case ZYDIS_REGISTER_RBX:
|
||||||
return regs.Rbx.Set(val);
|
return regs.Rbx.Set(val);
|
||||||
|
|
||||||
case ZYDIS_REGISTER_RCX:
|
case ZYDIS_REGISTER_RCX:
|
||||||
return regs.Rcx.Set(val);
|
return regs.Rcx.Set(val);
|
||||||
|
|
||||||
case ZYDIS_REGISTER_RDX:
|
case ZYDIS_REGISTER_RDX:
|
||||||
return regs.Rdx.Set(val);
|
return regs.Rdx.Set(val);
|
||||||
|
|
||||||
case ZYDIS_REGISTER_RBP:
|
case ZYDIS_REGISTER_RBP:
|
||||||
return regs.Rbp.Set(val);
|
return regs.Rbp.Set(val);
|
||||||
|
|
||||||
case ZYDIS_REGISTER_RSP:
|
case ZYDIS_REGISTER_RSP:
|
||||||
return regs.Rsp.Set(val);
|
return regs.Rsp.Set(val);
|
||||||
|
|
||||||
case ZYDIS_REGISTER_RSI:
|
case ZYDIS_REGISTER_RSI:
|
||||||
return regs.Rsi.Set(val);
|
return regs.Rsi.Set(val);
|
||||||
|
|
||||||
case ZYDIS_REGISTER_RDI:
|
case ZYDIS_REGISTER_RDI:
|
||||||
return regs.Rdi.Set(val);
|
return regs.Rdi.Set(val);
|
||||||
|
|
||||||
case ZYDIS_REGISTER_R8:
|
case ZYDIS_REGISTER_R8:
|
||||||
return regs.R8.Set(val);
|
return regs.R8.Set(val);
|
||||||
|
|
||||||
case ZYDIS_REGISTER_R9:
|
case ZYDIS_REGISTER_R9:
|
||||||
return regs.R9.Set(val);
|
return regs.R9.Set(val);
|
||||||
|
|
||||||
case ZYDIS_REGISTER_R10:
|
case ZYDIS_REGISTER_R10:
|
||||||
return regs.R10.Set(val);
|
return regs.R10.Set(val);
|
||||||
|
|
||||||
case ZYDIS_REGISTER_R11:
|
case ZYDIS_REGISTER_R11:
|
||||||
return regs.R11.Set(val);
|
return regs.R11.Set(val);
|
||||||
|
|
||||||
case ZYDIS_REGISTER_R12:
|
case ZYDIS_REGISTER_R12:
|
||||||
return regs.R12.Set(val);
|
return regs.R12.Set(val);
|
||||||
|
|
||||||
case ZYDIS_REGISTER_R13:
|
case ZYDIS_REGISTER_R13:
|
||||||
return regs.R13.Set(val);
|
return regs.R13.Set(val);
|
||||||
|
|
||||||
case ZYDIS_REGISTER_R14:
|
case ZYDIS_REGISTER_R14:
|
||||||
return regs.R14.Set(val);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -69,13 +69,18 @@ template<uint16 MNEMONIC,
|
||||||
typename OP2 = OpNone,
|
typename OP2 = OpNone,
|
||||||
typename OP3 = OpNone,
|
typename OP3 = OpNone,
|
||||||
typename OP4 = OpNone>
|
typename OP4 = OpNone>
|
||||||
struct InstrSig
|
struct InstrSig : X86Instruction
|
||||||
{
|
{
|
||||||
static constexpr size_t Hash() noexcept
|
static constexpr size_t Hash() noexcept
|
||||||
{
|
{
|
||||||
return InstructionDataHash(InstructionDataHash(N));
|
return InstructionDataHash(InstructionDataHash(N));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual uint16_t GetMnemonic() const
|
||||||
|
{
|
||||||
|
return MNEMONIC;
|
||||||
|
}
|
||||||
|
|
||||||
virtual bool Matches(const ZydisInstructionInfo& info) const
|
virtual bool Matches(const ZydisInstructionInfo& info) const
|
||||||
{
|
{
|
||||||
if(info.mnemonic != MNEMONIC)
|
if(info.mnemonic != MNEMONIC)
|
||||||
|
|
@ -95,34 +100,29 @@ struct InstrSig
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::vector<X86Instruction*> _registeredInstructions;
|
static std::vector<std::vector<X86Instruction*>> _registeredInstructions;
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct X86InstructionRegistrator
|
struct X86InstructionRegistrator
|
||||||
{
|
{
|
||||||
|
T inst;
|
||||||
X86InstructionRegistrator()
|
X86InstructionRegistrator()
|
||||||
{
|
{
|
||||||
static T inst;
|
uint16_t groupId = inst.GetMnemonic();
|
||||||
_registeredInstructions.push_back(&inst);
|
if (groupId >= _registeredInstructions.size())
|
||||||
|
{
|
||||||
|
_registeredInstructions.resize(groupId + 1);
|
||||||
|
}
|
||||||
|
_registeredInstructions[groupId].push_back(&inst);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const X86Instruction* LookupInstruction(const ZydisInstructionInfo& info)
|
#define REGISTER_X86_INSTRUCTION(instrCls) static X86InstructionRegistrator<##instrCls##> __##instrCls##__
|
||||||
{
|
|
||||||
for (X86Instruction *instr : _registeredInstructions)
|
|
||||||
{
|
|
||||||
if(instr->Matches(info))
|
|
||||||
return instr;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define REGISTER_X86_INSTRUCTION(instrCls) static X86InstructionRegistrator<##instrCls##> __##instrCls##__()
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct SubImpl
|
struct SubImpl
|
||||||
{
|
{
|
||||||
static T Execute(int32_t val1, int32_t val2, Registers& regs, uint32_t flagsOut)
|
static T Execute(T val1, T val2, Registers& regs, uint32_t flagsOut)
|
||||||
{
|
{
|
||||||
constexpr int NumBits = sizeof(T) * 8;
|
constexpr int NumBits = sizeof(T) * 8;
|
||||||
constexpr T SignMask = T(1) << (NumBits - 1);
|
constexpr T SignMask = T(1) << (NumBits - 1);
|
||||||
|
|
@ -144,38 +144,123 @@ struct SubImpl
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Instr_Sub32 : X86Instruction, InstrSig< ZYDIS_MNEMONIC_SUB, OpSig<32, ZYDIS_OPERAND_TYPE_REGISTER>, OpSig<32, ZYDIS_OPERAND_TYPE_IMMEDIATE> >
|
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) override
|
virtual bool Execute(const ZydisInstructionInfo& info, X86Context& ctx) const override
|
||||||
|
{
|
||||||
|
int32_t val1 = getRegisterValue<int32_t>(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<int64_t>(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.
|
// Value is sign extended.
|
||||||
int32_t val1 = getRegisterValue<int32_t>(info.operands[1].reg, ctx.regs);
|
int32_t val1 = getRegisterValue<int32_t>(info.operands[0].reg, ctx.regs);
|
||||||
int32_t val2 = info.operands[1].imm.value.sdword;
|
int32_t val2 = info.operands[1].imm.value.sdword;
|
||||||
|
|
||||||
uint32_t flagsOut = 0;
|
uint32_t flagsOut = 0;
|
||||||
int32_t res = SubImpl<uint32_t>::Execute(val1, val2, ctx.regs, flagsOut);
|
int32_t res = SubImpl<uint32_t>::Execute(val1, val2, ctx.regs, flagsOut);
|
||||||
|
|
||||||
setRegisterValue(info.operands[1].reg, ctx.regs, res);
|
setRegisterValue(info.operands[0].reg, ctx.regs, res);
|
||||||
|
ctx.regs.Eflags.Set(ctx.regs.Eflags.Get() | flagsOut);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
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> >
|
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) override
|
virtual bool Execute(const ZydisInstructionInfo& info, X86Context& ctx) const override
|
||||||
{
|
{
|
||||||
// Value is sign extended.
|
// Value is sign extended.
|
||||||
int64_t val1 = getRegisterValue<int64_t>(info.operands[1].reg, ctx.regs);
|
int64_t val1 = getRegisterValue<int64_t>(info.operands[0].reg, ctx.regs);
|
||||||
|
int64_t val2 = info.operands[1].imm.value.sqword;
|
||||||
|
|
||||||
|
uint32_t flagsOut = 0;
|
||||||
|
int64_t res = SubImpl<uint64_t>::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<int64_t>(info.operands[0].reg, ctx.regs);
|
||||||
int64_t val2 = info.operands[1].imm.value.sdword;
|
int64_t val2 = info.operands[1].imm.value.sdword;
|
||||||
|
|
||||||
uint32_t flagsOut = 0;
|
uint32_t flagsOut = 0;
|
||||||
int64_t res = SubImpl<uint64_t>::Execute(val1, val2, ctx.regs, flagsOut);
|
int64_t res = SubImpl<uint64_t>::Execute(val1, val2, ctx.regs, flagsOut);
|
||||||
|
|
||||||
setRegisterValue(info.operands[1].reg, ctx.regs, res);
|
setRegisterValue(info.operands[0].reg, ctx.regs, res);
|
||||||
|
ctx.regs.Eflags.Set(ctx.regs.Eflags.Get() | flagsOut);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
REGISTER_X86_INSTRUCTION(Instr_Sub64);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,9 @@ struct X86Context
|
||||||
|
|
||||||
struct X86Instruction
|
struct X86Instruction
|
||||||
{
|
{
|
||||||
virtual bool Matches(const ZydisInstructionInfo& info) = 0;
|
virtual uint16_t GetMnemonic() const = 0;
|
||||||
virtual bool Execute(const ZydisInstructionInfo& info, X86Context& ctx) = 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);
|
const X86Instruction* LookupInstruction(const ZydisInstructionInfo& info);
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,129 @@
|
||||||
|
#include "GleeBug.h"
|
||||||
#include "Emulation.h"
|
#include "Emulation.h"
|
||||||
#include "Emulation.Instructions.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 <Zydis/Zydis.h>
|
||||||
|
#include <Zydis/Decoder.h>
|
||||||
|
#include <Zydis/Formatter.h>
|
||||||
|
|
||||||
namespace GleeBug {
|
namespace GleeBug {
|
||||||
|
|
||||||
bool Emulator::IsActive() const
|
#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;
|
return _active;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Emulator::WaitForEvent(DEBUG_EVENT& debugEvent) const
|
bool X86Emulator::WaitForEvent(DEBUG_EVENT& debugEvent)
|
||||||
{
|
{
|
||||||
return false;
|
if(_hasEvent == false)
|
||||||
}
|
return false;
|
||||||
|
|
||||||
bool Emulator::Emulate(Thread* thread)
|
debugEvent = _currentEvent;
|
||||||
{
|
_hasEvent = false;
|
||||||
SetActiveThread(thread);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Emulator::Flush()
|
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)
|
if (_registers != nullptr)
|
||||||
{
|
{
|
||||||
|
|
@ -30,14 +133,14 @@ void Emulator::Flush()
|
||||||
_activeThread = nullptr;
|
_activeThread = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Emulator::SetActiveThread(Thread *thread)
|
void X86Emulator::SetActiveThread(Thread *thread)
|
||||||
{
|
{
|
||||||
if (_activeThread != thread)
|
if (_activeThread != thread)
|
||||||
{
|
{
|
||||||
// Commit current state if we change threads between.
|
// Commit current state if we change threads between.
|
||||||
Flush();
|
Flush();
|
||||||
|
|
||||||
_registers = new Registers(thread->hThread, CONTEXT_CONTROL);
|
_registers = new Registers(thread->hThread, CONTEXT_CONTROL | CONTEXT_DEBUG_REGISTERS | CONTEXT_INTEGER);
|
||||||
_activeThread = thread;
|
_activeThread = thread;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,20 +6,27 @@
|
||||||
#include "Debugger.Thread.h"
|
#include "Debugger.Thread.h"
|
||||||
#include "Debugger.Thread.Registers.h"
|
#include "Debugger.Thread.Registers.h"
|
||||||
|
|
||||||
|
#include <deque>
|
||||||
|
|
||||||
namespace GleeBug
|
namespace GleeBug
|
||||||
{
|
{
|
||||||
|
|
||||||
class Process;
|
class Process;
|
||||||
|
|
||||||
class Emulator
|
class X86Emulator
|
||||||
{
|
{
|
||||||
bool _active = false;
|
bool _active = false;
|
||||||
Thread *_activeThread = nullptr;
|
Thread *_activeThread = nullptr;
|
||||||
Registers* _registers = nullptr;
|
Registers* _registers = nullptr;
|
||||||
|
Process* _process = nullptr;
|
||||||
|
bool _hasEvent = false;
|
||||||
|
DEBUG_EVENT _currentEvent{};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
X86Emulator(Process* process);
|
||||||
|
|
||||||
bool IsActive() const;
|
bool IsActive() const;
|
||||||
bool WaitForEvent(DEBUG_EVENT& debugEvent) const;
|
bool WaitForEvent(DEBUG_EVENT& debugEvent);
|
||||||
bool Emulate(Thread* thread);
|
bool Emulate(Thread* thread);
|
||||||
void Flush();
|
void Flush();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,10 +25,14 @@
|
||||||
#define X64DBG_MOD L"x32dbg.dll"
|
#define X64DBG_MOD L"x32dbg.dll"
|
||||||
#endif //_WIN64
|
#endif //_WIN64
|
||||||
|
|
||||||
#define DPRINTF() \
|
template<typename... Args>
|
||||||
static auto dprintf = (int(*)(const char* format, ...))GetProcAddress(GetModuleHandleW(X64DBG_MOD), "_plugin_logprintf"); \
|
inline void dprintf(const char *fmt, Args... args)
|
||||||
if(!dprintf) \
|
{
|
||||||
dprintf = printf
|
static auto fn = (int(*)(const char* format, ...))GetProcAddress(GetModuleHandleW(X64DBG_MOD), "_plugin_logprintf");
|
||||||
|
if (!fn)
|
||||||
|
fn = printf;
|
||||||
|
fn(fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef _WIN64
|
#ifdef _WIN64
|
||||||
#define GleeArchValue(x32value, x64value) x64value
|
#define GleeArchValue(x32value, x64value) x64value
|
||||||
|
|
@ -72,4 +76,4 @@ namespace GleeBug
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //GLEEBUG_H
|
#endif //GLEEBUG_H
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue