From d4aecf8b305cffe6442bacfd421ab4ecb586506c Mon Sep 17 00:00:00 2001 From: Duncan Ogilvie Date: Thu, 28 Dec 2017 22:12:19 +0100 Subject: [PATCH] initial support for x87 and XMM registers --- GleeBug/Debugger.Process.Memory.cpp | 1 + GleeBug/Debugger.Process.h | 1 + GleeBug/Debugger.Thread.Registers.GetSet.cpp | 50 ++++ GleeBug/Debugger.Thread.Registers.Register.h | 7 + GleeBug/Debugger.Thread.Registers.cpp | 7 + GleeBug/Debugger.Thread.Registers.h | 7 + TitanEngineEmulator/Emulator.h | 282 +++++++++++++++++-- 7 files changed, 335 insertions(+), 20 deletions(-) diff --git a/GleeBug/Debugger.Process.Memory.cpp b/GleeBug/Debugger.Process.Memory.cpp index 968ade4..a1dd135 100644 --- a/GleeBug/Debugger.Process.Memory.cpp +++ b/GleeBug/Debugger.Process.Memory.cpp @@ -4,6 +4,7 @@ namespace GleeBug { bool Process::MemReadUnsafe(ptr address, void* buffer, ptr size, ptr* bytesRead) const { + //TODO: change page protection if reading failed ptr read; if (!bytesRead) bytesRead = &read; diff --git a/GleeBug/Debugger.Process.h b/GleeBug/Debugger.Process.h index 77c1161..d62abc9 100644 --- a/GleeBug/Debugger.Process.h +++ b/GleeBug/Debugger.Process.h @@ -399,6 +399,7 @@ namespace GleeBug bool RegReadContext() { + //TODO: lazily retrieve the context auto result = true; for(auto & thread : this->threads) if(!thread.second->RegReadContext()) diff --git a/GleeBug/Debugger.Thread.Registers.GetSet.cpp b/GleeBug/Debugger.Thread.Registers.GetSet.cpp index f861e12..c8098b4 100644 --- a/GleeBug/Debugger.Thread.Registers.GetSet.cpp +++ b/GleeBug/Debugger.Thread.Registers.GetSet.cpp @@ -239,7 +239,21 @@ namespace GleeBug case R::GIP: return ptr(contextGip); + case R::GS: + return ptr(mContext.SegGs); + case R::FS: + return ptr(mContext.SegFs); + case R::ES: + return ptr(mContext.SegEs); + case R::DS: + return ptr(mContext.SegDs); + case R::CS: + return ptr(mContext.SegCs); + case R::SS: + return ptr(mContext.SegSs); + default: + __debugbreak(); return 0; } } @@ -514,6 +528,28 @@ namespace GleeBug case R::GIP: contextGip = value; break; + + case R::GS: + mContext.SegGs = value; + break; + case R::FS: + mContext.SegFs = value; + break; + case R::ES: + mContext.SegEs = value; + break; + case R::DS: + mContext.SegDs = value; + break; + case R::CS: + mContext.SegCs = value; + break; + case R::SS: + mContext.SegSs = value; + break; + + default: + __debugbreak(); } } @@ -719,7 +755,21 @@ namespace GleeBug case R::GIP: return REGPTR(contextGip); + case R::GS: + return REGPTR(mContext.SegGs); + case R::FS: + return REGPTR(mContext.SegFs); + case R::ES: + return REGPTR(mContext.SegEs); + case R::DS: + return REGPTR(mContext.SegDs); + case R::CS: + return REGPTR(mContext.SegCs); + case R::SS: + return REGPTR(mContext.SegSs); + default: + __debugbreak(); return nullptr; } } diff --git a/GleeBug/Debugger.Thread.Registers.Register.h b/GleeBug/Debugger.Thread.Registers.Register.h index 0071958..63e741c 100644 --- a/GleeBug/Debugger.Thread.Registers.Register.h +++ b/GleeBug/Debugger.Thread.Registers.Register.h @@ -100,6 +100,13 @@ enum class R GBP, GSP, GIP, + + GS, + FS, + ES, + DS, + CS, + SS }; //R /** diff --git a/GleeBug/Debugger.Thread.Registers.cpp b/GleeBug/Debugger.Thread.Registers.cpp index 5237759..17f178e 100644 --- a/GleeBug/Debugger.Thread.Registers.cpp +++ b/GleeBug/Debugger.Thread.Registers.cpp @@ -96,6 +96,13 @@ namespace GleeBug Gsp(this), Gip(this), + Gs(this), + Fs(this), + Es(this), + Ds(this), + Cs(this), + Ss(this), + TrapFlag(this), ResumeFlag(this) { diff --git a/GleeBug/Debugger.Thread.Registers.h b/GleeBug/Debugger.Thread.Registers.h index 62f571d..7a69f6c 100644 --- a/GleeBug/Debugger.Thread.Registers.h +++ b/GleeBug/Debugger.Thread.Registers.h @@ -119,6 +119,13 @@ namespace GleeBug Register Gsp; Register Gip; + Register Gs; + Register Fs; + Register Es; + Register Ds; + Register Cs; + Register Ss; + #include "Debugger.Thread.Registers.Flag.h" Flag TrapFlag; diff --git a/TitanEngineEmulator/Emulator.h b/TitanEngineEmulator/Emulator.h index efff014..b1c6a6d 100644 --- a/TitanEngineEmulator/Emulator.h +++ b/TitanEngineEmulator/Emulator.h @@ -6,6 +6,139 @@ #include "FileMap.h" #include "PEB.h" +// Related to floating x87 registers +#define GetSTInTOPStackFromStatusWord(StatusWord) ((StatusWord & 0x3800) >> 11) +#define Getx87r0PositionInRegisterArea(STInTopStack) ((8 - STInTopStack) % 8) +#define Calculatex87registerPositionInRegisterArea(x87r0_position, index) (((x87r0_position + index) % 8)) +#define GetRegisterAreaOf87register(register_area, x87r0_position, index) (((char *) register_area) + 10 * Calculatex87registerPositionInRegisterArea(x87r0_position, index) ) +#define GetSTValueFromIndex(x87r0_position, index) ((x87r0_position + index) % 8) + +#ifdef _WIN64 +//https://stackoverflow.com/a/869597/1806760 +template struct identity +{ + typedef T type; +}; + +template Dst implicit_cast(typename identity::type t) +{ + return t; +} + +//https://github.com/electron/crashpad/blob/4054e6cba3ba023d9c00260518ec2912607ae17c/snapshot/cpu_context.cc +enum +{ + kX87TagValid = 0, + kX87TagZero, + kX87TagSpecial, + kX87TagEmpty, +}; + +typedef uint8_t X87Register[10]; + +union X87OrMMXRegister +{ + struct + { + X87Register st; + uint8_t st_reserved[6]; + }; + struct + { + uint8_t mm_value[8]; + uint8_t mm_reserved[8]; + }; +}; + +static_assert(sizeof(X87OrMMXRegister) == sizeof(M128A), "sizeof(X87OrMMXRegister) != sizeof(M128A)"); + +static uint16_t FxsaveToFsaveTagWord( + uint16_t fsw, + uint8_t fxsave_tag, + const X87OrMMXRegister* st_mm) +{ + // The x87 tag word (in both abridged and full form) identifies physical + // registers, but |st_mm| is arranged in logical stack order. In order to map + // physical tag word bits to the logical stack registers they correspond to, + // the "stack top" value from the x87 status word is necessary. + int stack_top = (fsw >> 11) & 0x7; + + uint16_t fsave_tag = 0; + for(int physical_index = 0; physical_index < 8; ++physical_index) + { + bool fxsave_bit = (fxsave_tag & (1 << physical_index)) != 0; + uint8_t fsave_bits; + + if(fxsave_bit) + { + int st_index = (physical_index + 8 - stack_top) % 8; + const X87Register & st = st_mm[st_index].st; + + uint32_t exponent = ((st[9] & 0x7f) << 8) | st[8]; + if(exponent == 0x7fff) + { + // Infinity, NaN, pseudo-infinity, or pseudo-NaN. If it was important to + // distinguish between these, the J bit and the M bit (the most + // significant bit of |fraction|) could be consulted. + fsave_bits = kX87TagSpecial; + } + else + { + // The integer bit the "J bit". + bool integer_bit = (st[7] & 0x80) != 0; + if(exponent == 0) + { + uint64_t fraction = ((implicit_cast(st[7]) & 0x7f) << 56) | + (implicit_cast(st[6]) << 48) | + (implicit_cast(st[5]) << 40) | + (implicit_cast(st[4]) << 32) | + (implicit_cast(st[3]) << 24) | + (st[2] << 16) | (st[1] << 8) | st[0]; + if(!integer_bit && fraction == 0) + { + fsave_bits = kX87TagZero; + } + else + { + // Denormal (if the J bit is clear) or pseudo-denormal. + fsave_bits = kX87TagSpecial; + } + } + else if(integer_bit) + { + fsave_bits = kX87TagValid; + } + else + { + // Unnormal. + fsave_bits = kX87TagSpecial; + } + } + } + else + { + fsave_bits = kX87TagEmpty; + } + + fsave_tag |= (fsave_bits << (physical_index * 2)); + } + + return fsave_tag; +} + +static uint8_t FsaveToFxsaveTagWord(uint16_t fsave_tag) +{ + uint8_t fxsave_tag = 0; + for(int physical_index = 0; physical_index < 8; ++physical_index) + { + const uint8_t fsave_bits = (fsave_tag >> (physical_index * 2)) & 0x3; + const bool fxsave_bit = fsave_bits != kX87TagEmpty; + fxsave_tag |= fxsave_bit << physical_index; + } + return fxsave_tag; +} +#endif //_WIN64 + using namespace GleeBug; class Emulator : public Debugger @@ -401,7 +534,7 @@ public: return false; ThreadSuspender suspender(thread, mIsRunning, false); memset(titcontext, 0, sizeof(TITAN_ENGINE_CONTEXT_t)); - auto context = thread->registers.GetContext(); + //General purpose registers titcontext->cax = thread->registers.Gax(); titcontext->ccx = thread->registers.Gcx(); titcontext->cdx = thread->registers.Gdx(); @@ -421,19 +554,61 @@ public: titcontext->r15 = thread->registers.R15(); #endif //_WIN64 titcontext->cip = thread->registers.Gip(); + // Flags titcontext->eflags = thread->registers.Eflags(); - titcontext->gs = (unsigned short)context->SegGs; - titcontext->fs = (unsigned short)context->SegFs; - titcontext->es = (unsigned short)context->SegEs; - titcontext->ds = (unsigned short)context->SegDs; - titcontext->cs = (unsigned short)context->SegCs; - titcontext->ss = (unsigned short)context->SegSs; + // Debug registers titcontext->dr0 = thread->registers.Dr0(); titcontext->dr1 = thread->registers.Dr1(); titcontext->dr2 = thread->registers.Dr2(); titcontext->dr3 = thread->registers.Dr3(); titcontext->dr6 = thread->registers.Dr6(); titcontext->dr7 = thread->registers.Dr7(); + // Segments + titcontext->gs = thread->registers.Gs(); + titcontext->fs = thread->registers.Fs(); + titcontext->es = thread->registers.Es(); + titcontext->ds = thread->registers.Ds(); + titcontext->cs = thread->registers.Cs(); + titcontext->ss = thread->registers.Ss(); + // x87 + auto context = thread->registers.GetContext(); +#ifdef _WIN64 + titcontext->x87fpu.ControlWord = context->FltSave.ControlWord; + titcontext->x87fpu.StatusWord = context->FltSave.StatusWord; + titcontext->x87fpu.TagWord = FxsaveToFsaveTagWord(context->FltSave.StatusWord, context->FltSave.TagWord, (const X87OrMMXRegister*)context->FltSave.FloatRegisters); + titcontext->x87fpu.ErrorSelector = context->FltSave.ErrorSelector; + titcontext->x87fpu.ErrorOffset = context->FltSave.ErrorOffset; + titcontext->x87fpu.DataSelector = context->FltSave.DataSelector; + titcontext->x87fpu.DataOffset = context->FltSave.DataOffset; + // Skip titcontext->x87fpu.Cr0NpxState (https://github.com/x64dbg/x64dbg/issues/255) + titcontext->MxCsr = context->MxCsr; + + for(int i = 0; i < 8; i++) + memcpy(&titcontext->RegisterArea[i * 10], &context->FltSave.FloatRegisters[i], 10); + + for(int i = 0; i < 16; i++) + memcpy(&titcontext->XmmRegisters[i], &context->FltSave.XmmRegisters[i], 16); +#else //x86 + titcontext->x87fpu.ControlWord = (WORD)context->FloatSave.ControlWord; + titcontext->x87fpu.StatusWord = (WORD)context->FloatSave.StatusWord; + titcontext->x87fpu.TagWord = (WORD)context->FloatSave.TagWord; + titcontext->x87fpu.ErrorSelector = context->FloatSave.ErrorSelector; + titcontext->x87fpu.ErrorOffset = context->FloatSave.ErrorOffset; + titcontext->x87fpu.DataSelector = context->FloatSave.DataSelector; + titcontext->x87fpu.DataOffset = context->FloatSave.DataOffset; + titcontext->x87fpu.Cr0NpxState = context->FloatSave.Cr0NpxState; + + memcpy(titcontext->RegisterArea, context->FloatSave.RegisterArea, 80); + + // MXCSR ExtendedRegisters[24] + memcpy(&(titcontext->MxCsr), &(context->ExtendedRegisters[24]), sizeof(titcontext->MxCsr)); + + // for x86 copy the 8 Xmm Registers from ExtendedRegisters[(10+n)*16]; (n is the index of the xmm register) to the XMM register + for(int i = 0; i < 8; i++) + memcpy(&(titcontext->XmmRegisters[i]), &context->ExtendedRegisters[(10 + i) * 16], 16); +#endif //_WIN64 + + //TODO: AVX return true; } @@ -443,6 +618,7 @@ public: if (!thread || !titcontext) return false; ThreadSuspender suspender(thread, mIsRunning, true); + // General purpose registers thread->registers.Gax = titcontext->cax; thread->registers.Gcx = titcontext->ccx; thread->registers.Gdx = titcontext->cdx; @@ -462,34 +638,93 @@ public: thread->registers.R15 = titcontext->r15; #endif //_WIN64 thread->registers.Gip = titcontext->cip; + // Flags thread->registers.Eflags = uint32(titcontext->eflags); + // Debug registers thread->registers.Dr0 = titcontext->dr0; thread->registers.Dr1 = titcontext->dr1; thread->registers.Dr2 = titcontext->dr2; thread->registers.Dr3 = titcontext->dr3; thread->registers.Dr6 = titcontext->dr6; thread->registers.Dr7 = titcontext->dr7; + // Segments + thread->registers.Gs = titcontext->gs; + thread->registers.Fs = titcontext->fs; + thread->registers.Es = titcontext->es; + thread->registers.Ds = titcontext->ds; + thread->registers.Cs = titcontext->cs; + thread->registers.Ss = titcontext->ss; + // x87 auto context = *(thread->registers.GetContext()); - context.SegGs = titcontext->gs; - context.SegFs = titcontext->fs; - context.SegEs = titcontext->es; - context.SegDs = titcontext->ds; - context.SegCs = titcontext->cs; - context.SegSs = titcontext->ss; +#ifdef _WIN64 + context.FltSave.ControlWord = titcontext->x87fpu.ControlWord; + context.FltSave.StatusWord = titcontext->x87fpu.StatusWord; + context.FltSave.TagWord = FsaveToFxsaveTagWord(titcontext->x87fpu.TagWord); + context.FltSave.ErrorSelector = (WORD)titcontext->x87fpu.ErrorSelector; + context.FltSave.ErrorOffset = titcontext->x87fpu.ErrorOffset; + context.FltSave.DataSelector = (WORD)titcontext->x87fpu.DataSelector; + context.FltSave.DataOffset = titcontext->x87fpu.DataOffset; + // Skip titcontext->x87fpu.Cr0NpxState + context.MxCsr = titcontext->MxCsr; + + for(int i = 0; i < 8; i++) + memcpy(&context.FltSave.FloatRegisters[i], &(titcontext->RegisterArea[i * 10]), 10); + + for(int i = 0; i < 16; i++) + memcpy(&(context.FltSave.XmmRegisters[i]), &(titcontext->XmmRegisters[i]), 16); +#else //x86 + context.FloatSave.ControlWord = titcontext->x87fpu.ControlWord; + context.FloatSave.StatusWord = titcontext->x87fpu.StatusWord; + context.FloatSave.TagWord = titcontext->x87fpu.TagWord; + context.FloatSave.ErrorSelector = titcontext->x87fpu.ErrorSelector; + context.FloatSave.ErrorOffset = titcontext->x87fpu.ErrorOffset; + context.FloatSave.DataSelector = titcontext->x87fpu.DataSelector; + context.FloatSave.DataOffset = titcontext->x87fpu.DataOffset; + context.FloatSave.Cr0NpxState = titcontext->x87fpu.Cr0NpxState; + + memcpy(context.FloatSave.RegisterArea, titcontext->RegisterArea, 80); + + // MXCSR ExtendedRegisters[24] + memcpy(&(context.ExtendedRegisters[24]), &titcontext->MxCsr, sizeof(titcontext->MxCsr)); + + // for x86 copy the 8 Xmm Registers from ExtendedRegisters[(10+n)*16]; (n is the index of the xmm register) to the XMM register + for(int i = 0; i < 8; i++) + memcpy(&context.ExtendedRegisters[(10 + i) * 16], &(titcontext->XmmRegisters[i]), 16); +#endif //_WIN64 + //TODO: AVX thread->registers.SetContext(context); return true; } void GetMMXRegisters(uint64_t mmx[8], TITAN_ENGINE_CONTEXT_t* titcontext) { - //TODO - memset(mmx, 0, sizeof(uint64_t) * 8); + int STInTopStack = GetSTInTOPStackFromStatusWord(titcontext->x87fpu.StatusWord); + DWORD x87r0_position = Getx87r0PositionInRegisterArea(STInTopStack); + int i; + + for(i = 0; i < 8; i++) + mmx[i] = *((uint64_t*)GetRegisterAreaOf87register(titcontext->RegisterArea, x87r0_position, i)); } void Getx87FPURegisters(x87FPURegister_t x87FPURegisters[8], TITAN_ENGINE_CONTEXT_t* titcontext) { - //TODO - memset(x87FPURegisters, 0, sizeof(x87FPURegister_t) * 8); + /* + GET Actual TOP register from StatusWord to order the FPUx87registers like in the FPU internal order. + The TOP field (bits 13-11) is where the FPU keeps track of which of its 80-bit registers is at the TOP. + The register number for the FPU's internal numbering system of the 80-bit registers would be displayed in that field. + When the programmer specifies one of the FPU 80-bit registers ST(x) in an instruction, the FPU adds (modulo 8) the ST number + supplied to the value in this TOP field to determine in which of its registers the required data is located. + */ + + int STInTopStack = GetSTInTOPStackFromStatusWord(titcontext->x87fpu.StatusWord); + DWORD x87r0_position = Getx87r0PositionInRegisterArea(STInTopStack); + + for(int i = 0; i < 8; i++) + { + memcpy(x87FPURegisters[i].data, GetRegisterAreaOf87register(titcontext->RegisterArea, x87r0_position, i), 10); + x87FPURegisters[i].st_value = GetSTValueFromIndex(x87r0_position, i); + x87FPURegisters[i].tag = (int)((titcontext->x87fpu.TagWord >> (i * 2)) & 0x3); + } } struct MappedPe @@ -604,7 +839,7 @@ public: auto sections = found->second.pe->GetSections(); return found->second.pe->IsPe64() ? GetPE32DataW_impl(found->second.pe->GetNtHeaders64(), WhichSection, WhichData, sections) - : GetPE32DataW_impl(found->second.pe->GetNtHeaders32(), WhichSection, WhichData, sections); + : GetPE32DataW_impl(found->second.pe->GetNtHeaders32(), WhichSection, WhichData, sections); } ULONG_PTR GetPE32DataW(const wchar_t* szFileName, DWORD WhichSection, DWORD WhichData) @@ -623,7 +858,7 @@ public: ? GetPE32DataW_impl(pe.GetNtHeaders64(), WhichSection, WhichData, sections) : GetPE32DataW_impl(pe.GetNtHeaders32(), WhichSection, WhichData, sections); } - + bool IsFileDLLW(const wchar_t* szFileName, ULONG_PTR FileMapVA) { return (GetPE32DataW(szFileName, NULL, UE_CHARACTERISTICS) & IMAGE_FILE_DLL) == IMAGE_FILE_DLL; @@ -853,7 +1088,14 @@ private: //functions #endif //_WIN64 case UE_CIP: return Registers::R::GIP; case UE_CSP: return Registers::R::GSP; + case UE_SEG_GS: return Registers::R::GS; + case UE_SEG_FS: return Registers::R::FS; + case UE_SEG_ES: return Registers::R::ES; + case UE_SEG_DS: return Registers::R::DS; + case UE_SEG_CS: return Registers::R::CS; + case UE_SEG_SS: return Registers::R::SS; default: + __debugbreak(); return Registers::R::EAX; } } @@ -942,7 +1184,7 @@ private: //functions { case UE_MEMORY: return MemoryType::Access; - case UE_MEMORY_READ: + case UE_MEMORY_READ: return MemoryType::Read; case UE_MEMORY_WRITE: return MemoryType::Write;