diff --git a/.gitignore b/.gitignore index 342392a..a6d29ef 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ Release/ *.vcxproj.user docs/ .vs/ -*.VC.db \ No newline at end of file +*.VC.db +*.aps diff --git a/GleeBug/Debugger.Loop.Exception.cpp b/GleeBug/Debugger.Loop.Exception.cpp index a9ca4b2..16b8468 100644 --- a/GleeBug/Debugger.Loop.Exception.cpp +++ b/GleeBug/Debugger.Loop.Exception.cpp @@ -1,4 +1,5 @@ #include "Debugger.h" +#include "Debugger.Thread.Registers.h" namespace GleeBug { @@ -48,7 +49,7 @@ namespace GleeBug mContinueStatus = DBG_CONTINUE; //set back the instruction pointer - mRegisters->Gip = info.address; + Registers(mThread->hThread, CONTEXT_CONTROL).Gip = info.address; //restore the original breakpoint byte and do an internal step mProcess->MemWriteUnsafe(info.address, info.internal.software.oldbytes, info.internal.software.size); @@ -107,27 +108,28 @@ namespace GleeBug void Debugger::exceptionHardwareBreakpoint(ptr exceptionAddress) { //determine the hardware breakpoint triggered - ptr dr6 = mRegisters->Dr6(); + Registers registers(mThread->hThread, CONTEXT_DEBUG_REGISTERS); + ptr dr6 = registers.Dr6(); HardwareSlot breakpointSlot; ptr breakpointAddress; - if (exceptionAddress == mRegisters->Dr0() || dr6 & 0x1) + if (exceptionAddress == registers.Dr0() || dr6 & 0x1) { - breakpointAddress = mRegisters->Dr0(); + breakpointAddress = registers.Dr0(); breakpointSlot = HardwareSlot::Dr0; } - else if (exceptionAddress == mRegisters->Dr1() || dr6 & 0x2) + else if (exceptionAddress == registers.Dr1() || dr6 & 0x2) { - breakpointAddress = mRegisters->Dr1(); + breakpointAddress = registers.Dr1(); breakpointSlot = HardwareSlot::Dr1; } - else if (exceptionAddress == mRegisters->Dr2() || dr6 & 0x4) + else if (exceptionAddress == registers.Dr2() || dr6 & 0x4) { - breakpointAddress = mRegisters->Dr2(); + breakpointAddress = registers.Dr2(); breakpointSlot = HardwareSlot::Dr2; } - else if (exceptionAddress == mRegisters->Dr3() || dr6 & 0x8) + else if (exceptionAddress == registers.Dr3() || dr6 & 0x8) { - breakpointAddress = mRegisters->Dr3(); + breakpointAddress = registers.Dr3(); breakpointSlot = HardwareSlot::Dr3; } else @@ -192,7 +194,7 @@ namespace GleeBug //We restore the protection if (!mProcess->MemProtect(foundPage->first, PAGE_SIZE, foundPage->second.OldProtect)) { - sprintf_s(error, "MemProtect failed on 0x%p", foundPage->first); + sprintf_s(error, "MemProtect failed on 0x%p", (void*)foundPage->first); cbInternalError(error); } @@ -225,7 +227,7 @@ namespace GleeBug auto foundInfo = mProcess->breakpoints.find({ BreakpointType::Memory, foundRange->first }); if (foundInfo == mProcess->breakpoints.end()) { - sprintf_s(error, "inconsistent memory breakpoint at 0x%p", exceptionAddress); + sprintf_s(error, "inconsistent memory breakpoint at 0x%p", (void*)exceptionAddress); cbInternalError(error); return; } @@ -243,7 +245,7 @@ namespace GleeBug if (bpxPage == mProcess->memoryBreakpointPages.end()) { - sprintf_s(error, "Process::memoryBreakPointPages data structure is incosistent, should dump page at 0x%p", exceptionAddress & ~(PAGE_SIZE - 1)); + sprintf_s(error, "Process::memoryBreakPointPages data structure is incosistent, should dump page at 0x%p", (void*)(exceptionAddress & ~(PAGE_SIZE - 1))); cbInternalError(error); return; } @@ -260,7 +262,7 @@ namespace GleeBug //We restore the protection if (!mProcess->MemProtect(pageAddr, PAGE_SIZE, pageProperties.OldProtect)) { - sprintf_s(error, "MemProtect failed on 0x%p", pageAddr); + sprintf_s(error, "MemProtect failed on 0x%p", (void*)pageAddr); cbInternalError(error); } @@ -323,7 +325,7 @@ namespace GleeBug //FIXED: if (!mProcess->MemProtect(pageAddr, PAGE_SIZE, pageProperties.OldProtect)) { - sprintf_s(error, "MemProtect failed on 0x%p", pageAddr); + sprintf_s(error, "MemProtect failed on 0x%p", (void*)pageAddr); cbInternalError(error); } //Pass info as well @@ -384,7 +386,7 @@ namespace GleeBug //We restore the protection if (!mProcess->MemProtect(foundPage->first, PAGE_SIZE, foundPage->second.OldProtect)) { - sprintf_s(error, "MemProtect failed on 0x%p", foundPage->first); + sprintf_s(error, "MemProtect failed on 0x%p", (void*)foundPage->first); cbInternalError(error); } @@ -417,7 +419,7 @@ namespace GleeBug auto foundInfo = mProcess->breakpoints.find({ BreakpointType::Memory, foundRange->first }); if (foundInfo == mProcess->breakpoints.end()) { - sprintf_s(error, "inconsistent memory breakpoint at 0x%p", exceptionAddress); + sprintf_s(error, "inconsistent memory breakpoint at 0x%p", (void*)exceptionAddress); cbInternalError(error); return; } @@ -435,7 +437,7 @@ namespace GleeBug if (bpxPage == mProcess->memoryBreakpointPages.end()) { - sprintf_s(error, "Process::memoryBreakPointPages data structure is incosistent, should dump page at 0x%p", exceptionAddress & ~(PAGE_SIZE - 1)); + sprintf_s(error, "Process::memoryBreakPointPages data structure is incosistent, should dump page at 0x%p", (void*)(exceptionAddress & ~(PAGE_SIZE - 1))); cbInternalError(error); return; } @@ -480,7 +482,7 @@ namespace GleeBug //FIXED: if (!mProcess->MemProtect(pageAddr, PAGE_SIZE, pageProperties.OldProtect)) { - sprintf_s(error, "MemProtect failed on 0x%p", pageAddr); + sprintf_s(error, "MemProtect failed on 0x%p", (void*)pageAddr); cbInternalError(error); } //Pass info as well diff --git a/GleeBug/Debugger.Loop.Process.cpp b/GleeBug/Debugger.Loop.Process.cpp index cb37192..0183fcc 100644 --- a/GleeBug/Debugger.Loop.Process.cpp +++ b/GleeBug/Debugger.Loop.Process.cpp @@ -29,11 +29,6 @@ namespace GleeBug createProcess.lpThreadLocalBase, createProcess.lpStartAddress) }); mThread = mProcess->thread = mProcess->threads.find(mDebugEvent.dwThreadId)->second.get(); - mRegisters = &mThread->registers; - - //read thread context from main thread - if (!mThread->RegReadContext()) - cbInternalError("Thread::RegReadContext() failed!"); //call the debug event callback cbCreateProcessEvent(createProcess, *mProcess); @@ -58,6 +53,5 @@ namespace GleeBug //set the current process mProcess = nullptr; mThread = nullptr; - mRegisters = nullptr; } }; \ No newline at end of file diff --git a/GleeBug/Debugger.Loop.Thread.cpp b/GleeBug/Debugger.Loop.Thread.cpp index 7c6f9ec..dd2e216 100644 --- a/GleeBug/Debugger.Loop.Thread.cpp +++ b/GleeBug/Debugger.Loop.Thread.cpp @@ -13,9 +13,6 @@ namespace GleeBug //set the current thread mThread = mProcess->thread = mProcess->threads.find(mDebugEvent.dwThreadId)->second.get(); - mRegisters = &mThread->registers; - if (!mThread->RegReadContext()) - cbInternalError("Thread::RegReadContext() failed!"); //call the debug event callback cbCreateThreadEvent(createThread, *mThread); @@ -31,6 +28,5 @@ namespace GleeBug //set the current thread mThread = mProcess->thread = nullptr; - mRegisters = nullptr; } }; \ No newline at end of file diff --git a/GleeBug/Debugger.Loop.cpp b/GleeBug/Debugger.Loop.cpp index 863e11e..0a3ad19 100644 --- a/GleeBug/Debugger.Loop.cpp +++ b/GleeBug/Debugger.Loop.cpp @@ -1,4 +1,5 @@ #include "Debugger.h" +#include "Debugger.Thread.Registers.h" namespace GleeBug { @@ -43,17 +44,14 @@ namespace GleeBug if (threadFound != mProcess->threads.end()) { mThread = mProcess->thread = threadFound->second.get(); - mRegisters = &mThread->registers; } else { mThread = mProcess->thread = nullptr; - mRegisters = nullptr; } } else { - mRegisters = nullptr; mThread = nullptr; if (mProcess) { @@ -62,10 +60,6 @@ namespace GleeBug } } - //read register contexts - if(mProcess && !mProcess->RegReadContext()) - cbInternalError("Process::RegReadContext() failed!"); - //call the pre debug event callback cbPreDebugEvent(mDebugEvent); @@ -119,13 +113,9 @@ namespace GleeBug if (mDetach && mThread) { if (mThread->isInternalStepping || mThread->isSingleStepping) - mThread->registers.TrapFlag = false; + Registers(mThread->hThread, CONTEXT_CONTROL).TrapFlag = false; } - //write register contexts - if(mProcess && !mProcess->RegWriteContext()) - cbInternalError("Process::RegWriteContext() failed!"); - //continue the debug event if (!ContinueDebugEvent(mDebugEvent.dwProcessId, mDebugEvent.dwThreadId, mContinueStatus)) break; diff --git a/GleeBug/Debugger.Process.cpp b/GleeBug/Debugger.Process.cpp index d263dbd..743b129 100644 --- a/GleeBug/Debugger.Process.cpp +++ b/GleeBug/Debugger.Process.cpp @@ -1,4 +1,5 @@ #include "Debugger.Process.h" +#include "Debugger.Thread.Registers.h" #define ZYDIS_EXPORTS #define ZYDIS_ENABLE_FEATURE_IMPLICITLY_USED_REGISTERS @@ -22,7 +23,7 @@ namespace GleeBug void Process::StepOver(const StepCallback & cbStep) { - auto gip = thread->registers.Gip(); + auto gip = Registers(thread->hThread, CONTEXT_CONTROL).Gip(); unsigned char data[16]; if (MemReadSafe(gip, data, sizeof(data))) { diff --git a/GleeBug/Debugger.Process.h b/GleeBug/Debugger.Process.h index d62abc9..48d38b0 100644 --- a/GleeBug/Debugger.Process.h +++ b/GleeBug/Debugger.Process.h @@ -396,25 +396,6 @@ namespace GleeBug static_cast(static_cast(debugger)); StepOver(std::bind(callback, debugger)); } - - bool RegReadContext() - { - //TODO: lazily retrieve the context - auto result = true; - for(auto & thread : this->threads) - if(!thread.second->RegReadContext()) - result = false; - return result; - } - - bool RegWriteContext() - { - auto result = true; - for(auto & thread : this->threads) - if(!thread.second->RegWriteContext()) - result = false; - return result; - } }; }; diff --git a/GleeBug/Debugger.Thread.HardwareBreakpoint.cpp b/GleeBug/Debugger.Thread.HardwareBreakpoint.cpp index c3aaa08..f443618 100644 --- a/GleeBug/Debugger.Thread.HardwareBreakpoint.cpp +++ b/GleeBug/Debugger.Thread.HardwareBreakpoint.cpp @@ -1,4 +1,5 @@ #include "Debugger.Thread.h" +#include "Debugger.Thread.Registers.h" #define BITSET(a,x) (a|=1<mContext, 0, sizeof(CONTEXT)); - InitializeCriticalSection(&mCr); - } - - LockedPtr Registers::GetContext() - { - handleLazyContext(); - return LockedPtr(&mCr, &mContext); - } - - /*void Registers::SetContext(const CONTEXT & context) - { - handleLazyContext(); - this->mContext = context; - }*/ - - void Registers::setContextLazy(CONTEXT* oldContext, HANDLE hThread) - { - this->mLazyOldContext = oldContext; - this->mLazyThread = hThread; - this->mLazySet = true; - } - - bool Registers::handleLazyContext() - { - ScopedCriticalSection lock(&mCr); - - if(!this->mLazySet) - return true; - - if(!this->mLazyOldContext || !this->mLazyThread) //assert - __debugbreak(); - - auto oldContext = this->mLazyOldContext; - auto lazyThread = this->mLazyThread; - - this->mLazyOldContext = nullptr; - this->mLazyThread = nullptr; - this->mLazySet = false; - - //TODO: handle failure of GetThreadContext - auto result = false; - if(GetThreadContext(lazyThread, oldContext)) + memset(&mContext, 0, sizeof(CONTEXT)); + mContext.ContextFlags = ContextFlags; + if (!!GetThreadContext(hThread, &mContext)) { - this->mContext = *oldContext; - result = true; + this->hThread = hThread; + memcpy(&mOldContext, &mContext, sizeof(CONTEXT)); } - - return result; + else + { + this->hThread = nullptr; + } + } + + Registers::~Registers() + { + if (hThread && memcmp(&mContext, &mOldContext, sizeof(CONTEXT)) != 0) + SetThreadContext(hThread, &mContext); + } + + PCONTEXT Registers::GetContext() + { + return &mContext; } }; \ No newline at end of file diff --git a/GleeBug/Debugger.Thread.Registers.h b/GleeBug/Debugger.Thread.Registers.h index ca9a7c3..4fd81c5 100644 --- a/GleeBug/Debugger.Thread.Registers.h +++ b/GleeBug/Debugger.Thread.Registers.h @@ -5,63 +5,6 @@ namespace GleeBug { - class ScopedCriticalSection - { - PCRITICAL_SECTION cr; - - public: - ScopedCriticalSection(PCRITICAL_SECTION cr) - : cr(cr) - { - EnterCriticalSection(cr); - } - - ~ScopedCriticalSection() - { - LeaveCriticalSection(cr); - } - }; - - template - class LockedPtr - { - PCRITICAL_SECTION locker; - T* ptr; - - public: - explicit LockedPtr(PCRITICAL_SECTION locker, T* ptr) - : locker(locker), ptr(ptr) - { - EnterCriticalSection(locker); - } - - ~LockedPtr() - { - LeaveCriticalSection(locker); - } - - LockedPtr(const LockedPtr &) = delete; - - LockedPtr &operator=(const LockedPtr &) = delete; - - LockedPtr(LockedPtr && other) - : locker(other.locker), ptr(other.ptr) - { - other.locker = nullptr; - other.ptr = nullptr; - } - - /*operator T*() const - { - return ptr; - }*/ - - T* operator->() const - { - return ptr; - } - }; - /** \brief Thread register context. */ @@ -71,15 +14,10 @@ namespace GleeBug friend class Thread; public: - /** - \brief Default constructor. - */ - Registers(); - - /** - \brief Copy constructor. - */ + Registers(HANDLE hThread, DWORD ContextFlags = CONTEXT_ALL); + ~Registers(); Registers(const Registers &) = delete; + Registers(const Registers &&) = delete; #include "Debugger.Thread.Registers.Register.h" @@ -219,34 +157,12 @@ namespace GleeBug \brief Gets a pointer to the context object. \return This function will never return a nullptr. */ - LockedPtr GetContext(); - - /** - \brief Sets the CONTEXT. - \param context The context to set. - */ - //void SetContext(const CONTEXT & context); + PCONTEXT GetContext(); private: + HANDLE hThread; CONTEXT mContext; - CRITICAL_SECTION mCr; - - LPCONTEXT mLazyOldContext = nullptr; - HANDLE mLazyThread = nullptr; - bool mLazySet = false; - - /** - \brief Lazily set CONTEXT. This will only actually retrieve the context if a function in this thread is called. - \param oldContext Pointer to the old context, used to determine if updates are required. - \param hThread Handle of the thread to get the context from if required. - */ - void setContextLazy(CONTEXT* oldContext, HANDLE hThread); - - /** - \brief Retrieve the actual context if setContextLazy has been called. - \return Whether retrieving the actual context was successful. - */ - bool handleLazyContext(); + CONTEXT mOldContext; void* getPtr(R reg); }; diff --git a/GleeBug/Debugger.Thread.cpp b/GleeBug/Debugger.Thread.cpp index 1285328..072aefa 100644 --- a/GleeBug/Debugger.Thread.cpp +++ b/GleeBug/Debugger.Thread.cpp @@ -1,4 +1,5 @@ #include "Debugger.Thread.h" +#include "Debugger.Thread.Registers.h" namespace GleeBug { @@ -13,29 +14,9 @@ namespace GleeBug { } - bool Thread::RegReadContext() - { - memset(&this->mOldContext, 0, sizeof(CONTEXT)); - this->mOldContext.ContextFlags = CONTEXT_ALL; //TODO: granular control over what's required - this->registers.setContextLazy(&this->mOldContext, this->hThread); - return true; - } - - bool Thread::RegWriteContext() - { - //check if something actually changed - if (this->registers.mLazySet || memcmp(&this->mOldContext, &this->registers.mContext, sizeof(CONTEXT)) == 0) - return true; - //update the context - if(SetThreadContext(this->hThread, &this->registers.mContext)) - return true; - __debugbreak(); - return false; - } - void Thread::StepInto() { - registers.TrapFlag.Set(); + Registers(hThread).TrapFlag.Set(); isSingleStepping = true; } @@ -57,7 +38,7 @@ namespace GleeBug void Thread::StepInternal(const StepCallback & cbStep) { - registers.TrapFlag.Set(); + Registers(hThread).TrapFlag.Set(); isInternalStepping = true; cbInternalStep = cbStep; } diff --git a/GleeBug/Debugger.Thread.h b/GleeBug/Debugger.Thread.h index c45e0fb..54e405e 100644 --- a/GleeBug/Debugger.Thread.h +++ b/GleeBug/Debugger.Thread.h @@ -2,7 +2,6 @@ #define DEBUGGER_THREAD_H #include "Debugger.Global.h" -#include "Debugger.Thread.Registers.h" #include "Debugger.Breakpoint.h" namespace GleeBug @@ -18,7 +17,6 @@ namespace GleeBug ptr lpThreadLocalBase; ptr lpStartAddress; - Registers registers; StepCallbackVector stepCallbacks; bool isSingleStepping; bool isInternalStepping; @@ -38,18 +36,6 @@ namespace GleeBug */ Thread(const Thread & other) = delete; - /** - \brief Read the register context from the thread. This fills the RegistersInfo member. - \return true if it succeeds, false if it fails. - */ - bool RegReadContext(); - - /** - \brief Write the register context to the thread. This does nothing if the registers did not change. - \return true if it succeeds, false if it fails. - */ - bool RegWriteContext(); - /** \brief Step into. */ @@ -121,9 +107,6 @@ namespace GleeBug \return true if it succeeds, false if it fails. */ bool Resume(); - - private: - CONTEXT mOldContext; }; }; diff --git a/GleeBug/Debugger.cpp b/GleeBug/Debugger.cpp index 229da83..d98e87d 100644 --- a/GleeBug/Debugger.cpp +++ b/GleeBug/Debugger.cpp @@ -1,4 +1,5 @@ #include "Debugger.h" +#include "Debugger.Thread.Registers.h" namespace GleeBug { @@ -14,7 +15,8 @@ namespace GleeBug bool Debugger::Init(const wchar_t* szFilePath, const wchar_t* szCommandLine, const wchar_t* szCurrentDirectory, - bool newConsole) + bool newConsole, + bool startSuspended) { memset(&mMainStartupInfo, 0, sizeof(mMainStartupInfo)); memset(&mMainProcess, 0, sizeof(mMainProcess)); @@ -40,7 +42,7 @@ namespace GleeBug nullptr, nullptr, FALSE, - DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS | (newConsole ? CREATE_NEW_CONSOLE : 0), + DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS | (newConsole ? CREATE_NEW_CONSOLE : 0) | (startSuspended ? CREATE_SUSPENDED : 0), nullptr, szCurrentDirectory, &mMainStartupInfo, @@ -72,8 +74,7 @@ namespace GleeBug bool Debugger::UnsafeDetach() { - mRegisters->TrapFlag = false; - mThread->RegWriteContext(); + Registers(mThread->hThread, CONTEXT_CONTROL).TrapFlag = false; return !!DebugActiveProcessStop(mMainProcess.dwProcessId); } @@ -85,12 +86,11 @@ namespace GleeBug bool Debugger::UnsafeDetachAndBreak() { - if (!mProcess || !mThread || !mRegisters) //fail when there is no process or thread currently specified + if (!mProcess || !mThread) //fail when there is no process or thread currently specified return false; //trigger an EXCEPTION_SINGLE_STEP in the debuggee - mRegisters->TrapFlag = true; - mThread->RegWriteContext(); + Registers(mThread->hThread, CONTEXT_CONTROL).TrapFlag = true; //detach from the process return UnsafeDetach(); diff --git a/GleeBug/Debugger.h b/GleeBug/Debugger.h index 552b8fb..8eddc32 100644 --- a/GleeBug/Debugger.h +++ b/GleeBug/Debugger.h @@ -39,7 +39,8 @@ namespace GleeBug bool Init(const wchar_t* szFilePath, const wchar_t* szCommandLine, const wchar_t* szCurrentDirectory, - bool newConsole = true); + bool newConsole = true, + bool startSuspended = false); /** \brief Attach to a debuggee. @@ -301,11 +302,6 @@ namespace GleeBug \brief The current thread (can be null in some cases). Should be a copy of mProcess->thread. */ Thread* mThread = nullptr; - - /** - \brief The current thread registers (can be null in some cases). Should be a copy of mThread->registers. - */ - Registers* mRegisters = nullptr; }; }; diff --git a/GleeBug/GleeBug.vcxproj b/GleeBug/GleeBug.vcxproj index 57c6ec2..de60782 100644 --- a/GleeBug/GleeBug.vcxproj +++ b/GleeBug/GleeBug.vcxproj @@ -120,7 +120,7 @@ true true true - MultiThreadedDLL + MultiThreaded true @@ -138,7 +138,7 @@ true true true - MultiThreadedDLL + MultiThreaded true diff --git a/MyDebugger/MyDebugger.h b/MyDebugger/MyDebugger.h index 80b777d..23a9994 100644 --- a/MyDebugger/MyDebugger.h +++ b/MyDebugger/MyDebugger.h @@ -2,6 +2,7 @@ #define MYDEBUGGER_H #include +#include using namespace GleeBug; @@ -11,36 +12,36 @@ protected: void cbMemoryBreakpoint2(const BreakpointInfo & info) { printf("Reached memory breakpoint#2! GIP: 0x%p\n", - mRegisters->Gip()); + (void*)Registers(mThread->hThread).Gip()); } void cbMemoryBreakpoint(const BreakpointInfo & info) { unsigned char dataToExec[4]; const char tmp[] = "aaaa"; - + Registers registers(mThread->hThread); printf("Reached memory breakpoint! GIP: 0x%p\n", - mRegisters->Gip()); + (void*)registers.Gip()); - mProcess->MemReadUnsafe(mRegisters->Gip(), dataToExec, 4); + mProcess->MemReadUnsafe(registers.Gip(), dataToExec, 4); printf("\n What are my bytes? I am so lost.. Dump: "); for (int i = 0; i < 4; i++) { printf("%02X ", dataToExec[i]); } - mProcess->DeleteMemoryBreakpoint(mRegisters->Gip()); + mProcess->DeleteMemoryBreakpoint(registers.Gip()); memcpy(dataToExec, tmp, 4); - mProcess->MemReadUnsafe(mRegisters->Gip(), dataToExec, 4); + mProcess->MemReadUnsafe(registers.Gip(), dataToExec, 4); printf("\n What are my bytes? I am so lost.. Dump: "); for (int i = 0; i < 4; i++) { printf("%02X ", dataToExec[i]); } - mProcess->SetMemoryBreakpoint(mRegisters->Gip() + 1, 0x1, this, &MyDebugger::cbMemoryBreakpoint2, MemoryType::Access, false); + mProcess->SetMemoryBreakpoint(registers.Gip() + 1, 0x1, this, &MyDebugger::cbMemoryBreakpoint2, MemoryType::Access, false); memcpy(dataToExec, tmp, 4); - mProcess->MemReadUnsafe(mRegisters->Gip(), dataToExec, 4); + mProcess->MemReadUnsafe(registers.Gip(), dataToExec, 4); printf("\n What are my bytes? I am so lost.. Dump: "); for (int i = 0; i < 4; i++) { @@ -50,14 +51,15 @@ protected: void cbEntryBreakpoint(const BreakpointInfo & info) { + Registers registers(mThread->hThread); printf("Reached entry breakpoint! GIP: 0x%p\n", - mRegisters->Gip()); + (void*)registers.Gip()); #ifdef _WIN64 - auto addr = mRegisters->Rbx(); + auto addr = registers.Rbx(); #else - auto addr = mRegisters->Esi(); + auto addr = registers.Esi(); #endif //_WIN64 - printf("Addr: 0x%p\n", addr); + printf("Addr: 0x%p\n", (void*)addr); if (mProcess->SetMemoryBreakpoint(addr, 0x10000, this, &MyDebugger::cbMemoryBreakpoint, MemoryType::Execute, false)) puts("Memory breakpoint set!"); else @@ -72,29 +74,32 @@ protected: mThread->StepInto([this]() { printf("Step after entry breakpoint! GIP: 0x%p\n", - mRegisters->Gip()); + registers.Gip()); });*/ } void cbEntryHardwareBreakpoint(const BreakpointInfo & info) { + Registers registers(mThread->hThread); printf("Reached entry hardware breakpoint! GIP: 0x%p\n", - mRegisters->Gip()); + (void*)registers.Gip()); if (mProcess->DeleteHardwareBreakpoint(info.address)) printf("Entry hardware breakpoint deleted!\n"); else printf("Failed to delete entry hardware breakpoint...\n"); mThread->StepInto([this]() { + Registers registers(mThread->hThread); printf("Step after entry hardware breakpoint! GIP: 0x%p\n", - mRegisters->Gip()); + (void*)registers.Gip()); }); } void cbStepSystem() { + Registers registers(mThread->hThread); printf("Reached step after system breakpoint, GIP: 0x%p!\n", - mRegisters->Gip()); + (void*)registers.Gip()); } void cbCreateProcessEvent(const CREATE_PROCESS_DEBUG_INFO & createProcess, const Process & process) override @@ -102,7 +107,7 @@ protected: ptr entry = ptr(createProcess.lpStartAddress); printf("Process %d created with entry 0x%p\n", mDebugEvent.dwProcessId, - entry); + (void*)entry); /*HardwareSlot slot; if (mProcess->GetFreeHardwareBreakpointSlot(slot)) { @@ -121,9 +126,9 @@ protected: entry = ptr(createProcess.lpBaseOfImage) + 0x108F; //MembpTest, main.cpp:43 (x32) #endif //_WIN64 if(mProcess->SetBreakpoint(entry, this, &MyDebugger::cbEntryBreakpoint, true)) - printf("Breakpoint set at 0x%p!\n", entry); + printf("Breakpoint set at 0x%p!\n", (void*)entry); else - printf("Failed to set breakpoint at 0x%p...\b", entry); + printf("Failed to set breakpoint at 0x%p...\b", (void*)entry); uint8 test[5]; ptr start = entry - 2; printf("unsafe: "); @@ -179,7 +184,7 @@ protected: exceptionInfo.ExceptionRecord.ExceptionCode, exceptionInfo.ExceptionRecord.ExceptionAddress); for (DWORD i = 0; i < exceptionInfo.ExceptionRecord.NumberParameters; i++) - printf(" ExceptionInformation[%d] = 0x%p\n", i, exceptionInfo.ExceptionRecord.ExceptionInformation[i]); + printf(" ExceptionInformation[%d] = 0x%p\n", i, (void*)exceptionInfo.ExceptionRecord.ExceptionInformation[i]); } void cbDebugStringEvent(const OUTPUT_DEBUG_STRING_INFO & debugString) override @@ -198,14 +203,16 @@ protected: void cbAttachBreakpoint() override { + Registers registers(mThread->hThread); printf("Attach breakpoint reached, GIP: 0x%p\n", - mRegisters->Gip()); + (void*)registers.Gip()); } void cbSystemBreakpoint() override { + Registers registers(mThread->hThread); printf("System breakpoint reached, GIP: 0x%p\n", - mRegisters->Gip()); + (void*)registers.Gip()); mThread->StepInto(this, &MyDebugger::cbStepSystem); } @@ -218,16 +225,17 @@ protected: void cbBreakpoint(const BreakpointInfo & info) override { printf("Breakpoint on 0x%p!\n", - info.address); + (void*)info.address); } void cbUnhandledException(const EXCEPTION_RECORD & exceptionRecord, bool firstChance) override { + Registers registers(mThread->hThread); printf("Unhandled exception (%s) 0x%08X on 0x%p, GIP: 0x%p\n", firstChance ? "first chance" : "second chance", exceptionRecord.ExceptionCode, exceptionRecord.ExceptionAddress, - mRegisters->Gip()); + (void*)registers.Gip()); } }; diff --git a/MyDebugger/MyDebugger.vcxproj b/MyDebugger/MyDebugger.vcxproj index 877b10b..16469de 100644 --- a/MyDebugger/MyDebugger.vcxproj +++ b/MyDebugger/MyDebugger.vcxproj @@ -114,7 +114,7 @@ true true true - MultiThreadedDLL + MultiThreaded true @@ -131,7 +131,7 @@ true true true - MultiThreadedDLL + MultiThreaded true diff --git a/MyDebugger/main.cpp b/MyDebugger/main.cpp index b8457a7..5d93614 100644 --- a/MyDebugger/main.cpp +++ b/MyDebugger/main.cpp @@ -56,7 +56,7 @@ static void printNtHeaders(T inth) puts("\n Optional Header:"); printf(" Magic : %04X\n", ioh->Magic); printf(" EntryPoint: %08X\n", ioh->AddressOfEntryPoint); - printf(" ImageBase : %p\n", PVOID(ioh->ImageBase)); + printf(" ImageBase : %llX\n", (uint64_t)ioh->ImageBase); printf(" Subsystem : %04X\n", ioh->Subsystem); } @@ -156,7 +156,7 @@ static void testCorkami() printf("file: %ws\n\n", fileName.c_str()); } } - printf("\n%d/%d parsed OK!\n", okCount, _countof(peTestFiles)); + printf("\n%d/%zu parsed OK!\n", okCount, _countof(peTestFiles)); } int main() diff --git a/TitanEngineEmulator/Emulator.h b/TitanEngineEmulator/Emulator.h index 6980bbd..b8ecb03 100644 --- a/TitanEngineEmulator/Emulator.h +++ b/TitanEngineEmulator/Emulator.h @@ -1,10 +1,13 @@ #include #include #include +#include #include "TitanEngine.h" #include "ntdll.h" #include "FileMap.h" #include "PEB.h" +#include "Global.Engine.Context.h" +#include "Hider.h" // Related to floating x87 registers #define GetSTInTOPStackFromStatusWord(StatusWord) ((StatusWord & 0x3800) >> 11) @@ -13,137 +16,13 @@ #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 { public: + HINSTANCE engineHandle; + //Debugger PROCESS_INFORMATION* InitDebugW(const wchar_t* szFileName, const wchar_t* szCommandLine, const wchar_t* szCurrentFolder) { @@ -153,9 +32,31 @@ public: return &mMainProcess; } - PROCESS_INFORMATION* InitDLLDebugW(const wchar_t* szFileName, bool ReserveModuleBase, const wchar_t* szCommandLine, const wchar_t* szCurrentFolder, LPVOID EntryCallBack) + PROCESS_INFORMATION* InitDLLDebugW(const wchar_t* szFileName, bool /* ReserveModuleBase = false */, const wchar_t* szCommandLine, const wchar_t* szCurrentFolder, LPVOID /*EntryCallBack = 0 */) { - //TODO + wcscpy_s(szDebuggeeName, szFileName); + if (TryExtractDllLoader()) + { + mCbATTACHBREAKPOINT = nullptr; + if (!Init(szDebuggeeName, szCommandLine, szCurrentFolder, true, true)) + return nullptr; + wchar_t szName[256] = L""; + swprintf(szName, 256, L"Local\\szLibraryName%X", mMainProcess.dwProcessId); + //TODO: close this once we actually see the DLL is loaded in the process + HANDLE DebugDLLFileMapping = CreateFileMappingW(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, 512 * sizeof(wchar_t), szName); + if (DebugDLLFileMapping) + { + const size_t filemapSize = 512; + wchar_t* szLibraryPathMapping = (wchar_t*)MapViewOfFile(DebugDLLFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, filemapSize * sizeof(wchar_t)); + if (szLibraryPathMapping) + { + wcscpy_s(szLibraryPathMapping, filemapSize, szFileName); + UnmapViewOfFile(szLibraryPathMapping); + } + } + ResumeThread(mMainProcess.hThread); + return &mMainProcess; + } return nullptr; } @@ -288,23 +189,7 @@ public: //Misc void* GetPEBLocation(HANDLE hProcess) { - ULONG RequiredLen = 0; - void* PebAddress = 0; - PROCESS_BASIC_INFORMATION myProcessBasicInformation[5] = { 0 }; - - if(NtQueryInformationProcess(hProcess, ProcessBasicInformation, myProcessBasicInformation, sizeof(PROCESS_BASIC_INFORMATION), &RequiredLen) == 0) - { - PebAddress = (void*)myProcessBasicInformation->PebBaseAddress; - } - else - { - if(NtQueryInformationProcess(hProcess, ProcessBasicInformation, myProcessBasicInformation, RequiredLen, &RequiredLen) == 0) - { - PebAddress = (void*)myProcessBasicInformation->PebBaseAddress; - } - } - - return PebAddress; + return GetPEBLocation_(hProcess); } void* GetPEBLocation64(HANDLE hProcess) @@ -375,67 +260,7 @@ public: bool HideDebugger(HANDLE hProcess, DWORD PatchAPILevel) { - PEB_CURRENT myPEB = { 0 }; - SIZE_T ueNumberOfBytesRead = 0; - void* heapFlagsAddress = 0; - DWORD heapFlags = 0; - void* heapForceFlagsAddress = 0; - DWORD heapForceFlags = 0; - -#ifndef _WIN64 - PEB64 myPEB64 = { 0 }; - void* AddressOfPEB64 = GetPEBLocation64(hProcess); -#endif - - void* AddressOfPEB = GetPEBLocation(hProcess); - - if(!AddressOfPEB) - return false; - - if(ReadProcessMemory(hProcess, AddressOfPEB, (void*)&myPEB, sizeof(PEB_CURRENT), &ueNumberOfBytesRead)) - { -#ifndef _WIN64 - if(AddressOfPEB64) - { - ReadProcessMemory(hProcess, AddressOfPEB64, (void*)&myPEB64, sizeof(PEB64), &ueNumberOfBytesRead); - } -#endif - myPEB.BeingDebugged = FALSE; - myPEB.NtGlobalFlag &= ~0x70; - -#ifndef _WIN64 - myPEB64.BeingDebugged = FALSE; - myPEB64.NtGlobalFlag &= ~0x70; -#endif - -#ifdef _WIN64 - heapFlagsAddress = (void*)((LONG_PTR)myPEB.ProcessHeap + getHeapFlagsOffset(true)); - heapForceFlagsAddress = (void*)((LONG_PTR)myPEB.ProcessHeap + getHeapForceFlagsOffset(true)); -#else - heapFlagsAddress = (void*)((LONG_PTR)myPEB.ProcessHeap + getHeapFlagsOffset(false)); - heapForceFlagsAddress = (void*)((LONG_PTR)myPEB.ProcessHeap + getHeapForceFlagsOffset(false)); -#endif //_WIN64 - ReadProcessMemory(hProcess, heapFlagsAddress, &heapFlags, sizeof(DWORD), 0); - ReadProcessMemory(hProcess, heapForceFlagsAddress, &heapForceFlags, sizeof(DWORD), 0); - - heapFlags &= HEAP_GROWABLE; - heapForceFlags = 0; - - WriteProcessMemory(hProcess, heapFlagsAddress, &heapFlags, sizeof(DWORD), 0); - WriteProcessMemory(hProcess, heapForceFlagsAddress, &heapForceFlags, sizeof(DWORD), 0); - - if(WriteProcessMemory(hProcess, AddressOfPEB, (void*)&myPEB, sizeof(PEB_CURRENT), &ueNumberOfBytesRead)) - { -#ifndef _WIN64 - if(AddressOfPEB64) - { - WriteProcessMemory(hProcess, AddressOfPEB64, (void*)&myPEB64, sizeof(PEB64), &ueNumberOfBytesRead); - } -#endif - return true; - } - } - return false; + return FixPebInProcess(hProcess); } HANDLE TitanOpenProcess(DWORD dwDesiredAccess, bool bInheritHandle, DWORD dwProcessId) @@ -479,220 +304,178 @@ public: struct ThreadSuspender { - ThreadSuspender(Thread* thread, bool running, bool writeRegs) - : thread(running ? thread : nullptr), writeRegs(writeRegs) + ThreadSuspender(HANDLE hThread, bool running) + : hThread(running ? hThread : nullptr) { - if(this->thread) - { - this->thread->Suspend(); - this->thread->RegReadContext(); - } + if (this->hThread) + SuspendThread(this->hThread); } + ThreadSuspender(const ThreadSuspender &) = delete; + ThreadSuspender(const ThreadSuspender &&) = delete; + ~ThreadSuspender() { - if(this->thread) - { - if(this->writeRegs) - this->thread->RegWriteContext(); - this->thread->Resume(); - } + if (this->hThread) + ResumeThread(this->hThread); } - Thread* thread; - bool writeRegs; + HANDLE hThread; }; //Registers ULONG_PTR GetContextDataEx(HANDLE hActiveThread, DWORD IndexOfRegister) { - if(!hActiveThread) + if (!hActiveThread) return 0; - auto thread = threadFromHandle(hActiveThread); - if(!thread) - return 0; - ThreadSuspender suspender(thread, mIsRunning, false); - return thread->registers.Get(registerFromDword(IndexOfRegister)); + + ThreadSuspender suspender(hActiveThread, mIsRunning); + auto r = registerFromDword(IndexOfRegister); + if (r == Registers::R::Invalid) + __debugbreak(); + return Registers(hActiveThread).Get(r); } bool SetContextDataEx(HANDLE hActiveThread, DWORD IndexOfRegister, ULONG_PTR NewRegisterValue) { - auto thread = threadFromHandle(hActiveThread); - if (!thread) - return false; - ThreadSuspender suspender(thread, mIsRunning, true); - thread->registers.Set(registerFromDword(IndexOfRegister), NewRegisterValue); - return true; - } + if (!hActiveThread) + return 0; - bool GetFullContextDataEx(HANDLE hActiveThread, TITAN_ENGINE_CONTEXT_t* titcontext) - { - if(!hActiveThread) + ThreadSuspender suspender(hActiveThread, mIsRunning); + + auto r = registerFromDword(IndexOfRegister); + if (r != Registers::R::Invalid) + { + Registers(hActiveThread).Set(r, NewRegisterValue); + return true; + } + + TITAN_ENGINE_CONTEXT_t titcontext; + if (!_GetFullContextDataEx(hActiveThread, &titcontext, IndexOfRegister >= UE_MXCSR)) return false; - auto thread = threadFromHandle(hActiveThread); - if (!thread || !titcontext) - return false; - ThreadSuspender suspender(thread, mIsRunning, false); - auto context = thread->registers.GetContext(); - memset(titcontext, 0, sizeof(TITAN_ENGINE_CONTEXT_t)); - //General purpose registers - titcontext->cax = thread->registers.Gax(); - titcontext->ccx = thread->registers.Gcx(); - titcontext->cdx = thread->registers.Gdx(); - titcontext->cbx = thread->registers.Gbx(); - titcontext->csp = thread->registers.Gsp(); - titcontext->cbp = thread->registers.Gbp(); - titcontext->csi = thread->registers.Gsi(); - titcontext->cdi = thread->registers.Gdi(); + + bool avx_priority = false; + switch (IndexOfRegister) + { + case UE_X87_STATUSWORD: + { + titcontext.x87fpu.StatusWord = WORD(NewRegisterValue); + break; + } + + case UE_X87_CONTROLWORD: + { + titcontext.x87fpu.ControlWord = WORD(NewRegisterValue); + break; + } + + case UE_X87_TAGWORD: + { + titcontext.x87fpu.TagWord = WORD(NewRegisterValue); + break; + } + + case UE_MXCSR: + { + titcontext.MxCsr = DWORD(NewRegisterValue); + break; + } + + case UE_XMM0: + case UE_XMM1: + case UE_XMM2: + case UE_XMM3: + case UE_XMM4: + case UE_XMM5: + case UE_XMM6: + case UE_XMM7: #ifdef _WIN64 - titcontext->r8 = thread->registers.R8(); - titcontext->r9 = thread->registers.R9(); - titcontext->r10 = thread->registers.R10(); - titcontext->r11 = thread->registers.R11(); - titcontext->r12 = thread->registers.R12(); - titcontext->r13 = thread->registers.R13(); - titcontext->r14 = thread->registers.R14(); - titcontext->r15 = thread->registers.R15(); + case UE_XMM8: + case UE_XMM9: + case UE_XMM10: + case UE_XMM11: + case UE_XMM12: + case UE_XMM13: + case UE_XMM14: + case UE_XMM15: #endif //_WIN64 - titcontext->cip = thread->registers.Gip(); - // Flags - titcontext->eflags = thread->registers.Eflags(); - // 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 + { + memcpy(&(titcontext.XmmRegisters[IndexOfRegister - UE_XMM0]), (void*)NewRegisterValue, 16); + break; + } + + case UE_MMX0: + case UE_MMX1: + case UE_MMX2: + case UE_MMX3: + case UE_MMX4: + case UE_MMX5: + case UE_MMX6: + case UE_MMX7: + { + int STInTopStack = GetSTInTOPStackFromStatusWord(titcontext.x87fpu.StatusWord); + DWORD x87r0_position = Getx87r0PositionInRegisterArea(STInTopStack); + + memcpy(((uint64_t*)GetRegisterAreaOf87register(titcontext.RegisterArea, x87r0_position, IndexOfRegister - UE_MMX0)), (char*)NewRegisterValue, 8); + break; + } + + case UE_x87_r0: + case UE_x87_r1: + case UE_x87_r2: + case UE_x87_r3: + case UE_x87_r4: + case UE_x87_r5: + case UE_x87_r6: + case UE_x87_r7: + { + int STInTopStack = GetSTInTOPStackFromStatusWord(titcontext.x87fpu.StatusWord); + DWORD x87r0_position = Getx87r0PositionInRegisterArea(STInTopStack); + + memcpy(((uint64_t*)GetRegisterAreaOf87register(titcontext.RegisterArea, x87r0_position, IndexOfRegister - UE_x87_r0)), (void*)NewRegisterValue, 10); + break; + } + + case UE_YMM0: + case UE_YMM1: + case UE_YMM2: + case UE_YMM3: + case UE_YMM4: + case UE_YMM5: + case UE_YMM6: + case UE_YMM7: #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); + case UE_YMM8: + case UE_YMM9: + case UE_YMM10: + case UE_YMM11: + case UE_YMM12: + case UE_YMM13: + case UE_YMM14: + case UE_YMM15: #endif //_WIN64 + { + avx_priority = true; + memcpy(&titcontext.YmmRegisters[IndexOfRegister - UE_YMM0], (void*)NewRegisterValue, 32); + break; + } - //TODO: AVX - return true; + default: __debugbreak(); + } + + return _SetFullContextDataEx(hActiveThread, &titcontext, avx_priority); } bool SetFullContextDataEx(HANDLE hActiveThread, TITAN_ENGINE_CONTEXT_t* titcontext) { - auto thread = threadFromHandle(hActiveThread); - if (!thread || !titcontext) - return false; - ThreadSuspender suspender(thread, mIsRunning, true); - auto context = thread->registers.GetContext(); - // General purpose registers - thread->registers.Gax = titcontext->cax; - thread->registers.Gcx = titcontext->ccx; - thread->registers.Gdx = titcontext->cdx; - thread->registers.Gbx = titcontext->cbx; - thread->registers.Gsp = titcontext->csp; - thread->registers.Gbp = titcontext->cbp; - thread->registers.Gsi = titcontext->csi; - thread->registers.Gdi = titcontext->cdi; -#ifdef _WIN64 - thread->registers.R8 = titcontext->r8; - thread->registers.R9 = titcontext->r9; - thread->registers.R10 = titcontext->r10; - thread->registers.R11 = titcontext->r11; - thread->registers.R12 = titcontext->r12; - thread->registers.R13 = titcontext->r13; - thread->registers.R14 = titcontext->r14; - 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 -#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; + ThreadSuspender suspender(hActiveThread, mIsRunning); + return _SetFullContextDataEx(hActiveThread, titcontext, false); + } - 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 - return true; + bool GetFullContextDataEx(HANDLE hActiveThread, TITAN_ENGINE_CONTEXT_t* titcontext) + { + ThreadSuspender suspender(hActiveThread, mIsRunning); + return _GetFullContextDataEx(hActiveThread, titcontext, true); } void GetMMXRegisters(uint64_t mmx[8], TITAN_ENGINE_CONTEXT_t* titcontext) @@ -701,7 +484,7 @@ public: DWORD x87r0_position = Getx87r0PositionInRegisterArea(STInTopStack); int i; - for(i = 0; i < 8; i++) + for (i = 0; i < 8; i++) mmx[i] = *((uint64_t*)GetRegisterAreaOf87register(titcontext->RegisterArea, x87r0_position, i)); } @@ -718,7 +501,7 @@ public: int STInTopStack = GetSTInTOPStackFromStatusWord(titcontext->x87fpu.StatusWord); DWORD x87r0_position = Getx87r0PositionInRegisterArea(STInTopStack); - for(int i = 0; i < 8; i++) + 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); @@ -925,7 +708,6 @@ public: { for(auto & thread : mProcess->threads) thread.second->Suspend(); - mProcess->RegReadContext(); } if(!mProcess->SetHardwareBreakpoint(bpxAddress, (HardwareSlot)IndexOfRegister, [bpxCallBack](const BreakpointInfo & info) @@ -935,7 +717,6 @@ public: return false; if(running) { - mProcess->RegWriteContext(); for(auto & thread : mProcess->threads) thread.second->Resume(); } @@ -1045,7 +826,7 @@ protected: } private: //functions - static Registers::R registerFromDword(DWORD IndexOfRegister) + inline Registers::R registerFromDword(DWORD IndexOfRegister) { switch (IndexOfRegister) { @@ -1093,14 +874,15 @@ private: //functions 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; + default: return Registers::R::Invalid; } } std::unordered_map threadFromHandleCache; + // Disable warnings about pointer truncation for the THREAD_BASIC_INFORMATION +#pragma warning(push) +#pragma warning(disable: 4311 4302) Thread* threadFromHandle(HANDLE hThread) { auto found = threadFromHandleCache.find(hThread); @@ -1123,6 +905,7 @@ private: //functions threadFromHandleCache[hThread] = result; return result; } +#pragma warning(pop) std::unordered_map processFromHandleCache; @@ -1313,6 +1096,53 @@ private: //functions } #endif + bool EngineExtractResource(char* szResourceName, wchar_t* szExtractedFileName) + { + bool result = false; + HRSRC hResource = FindResourceA(engineHandle, (LPCSTR)szResourceName, "BINARY"); + if (hResource != NULL) + { + HGLOBAL hResourceGlobal = LoadResource(engineHandle, hResource); + if (hResourceGlobal != NULL) + { + DWORD ResourceSize = SizeofResource(engineHandle, hResource); + LPVOID ResourceData = LockResource(hResourceGlobal); + HANDLE hFile = CreateFileW(szExtractedFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile != INVALID_HANDLE_VALUE) + { + DWORD NumberOfBytesWritten; + if (WriteFile(hFile, ResourceData, ResourceSize, &NumberOfBytesWritten, NULL)) + result = true; + CloseHandle(hFile); + } + } + } + return result; + } + + bool TryExtractDllLoader(bool failedBefore = false) + { + wchar_t* szPath = wcsrchr(szDebuggeeName, L'\\'); + if (szPath) + szPath[1] = '\0'; + wchar_t DLLLoaderName[64] = L""; +#ifdef _WIN64 + swprintf_s(DLLLoaderName, L"DLLLoader64_%.4X.exe", GetTickCount() & 0xFFFF); +#else + swprintf_s(DLLLoaderName, L"DLLLoader32_%.4X.exe", GetTickCount() & 0xFFFF); +#endif //_WIN64 + wcscat_s(szDebuggeeName, DLLLoaderName); +#ifdef _WIN64 + if (EngineExtractResource("LOADERX64", szDebuggeeName)) +#else + if (EngineExtractResource("LOADERX86", szDebuggeeName)) +#endif //_WIN64 + return true; + return !failedBefore && + GetModuleFileNameW(engineHandle, szDebuggeeName, _countof(szDebuggeeName)) && + TryExtractDllLoader(true); + } + private: //variables bool mSetDebugPrivilege = false; typedef void(*CUSTOMHANDLER)(const void*); @@ -1332,4 +1162,5 @@ private: //variables CUSTOMHANDLER mCbDEBUGEVENT = nullptr; STEPCALLBACK mCbATTACHBREAKPOINT = nullptr; PROCESS_INFORMATION* mAttachProcessInfo = nullptr; + wchar_t szDebuggeeName[MAX_PATH] = L""; }; \ No newline at end of file diff --git a/TitanEngineEmulator/Global.Engine.Context.cpp b/TitanEngineEmulator/Global.Engine.Context.cpp new file mode 100644 index 0000000..51fb1f3 --- /dev/null +++ b/TitanEngineEmulator/Global.Engine.Context.cpp @@ -0,0 +1,482 @@ +#include "Global.Engine.Context.h" +#include + +typedef int(*p_printf)(const char*, ...); + +static p_printf hax() +{ + auto d = p_printf(GetProcAddress(GetModuleHandleW(L"x64dbg.dll"), "_plugin_logprintf")); + return d ? d : printf; +} + +static auto dprintf = hax(); + +#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 + +PGETENABLEDXSTATEFEATURES _GetEnabledXStateFeatures = NULL; +PINITIALIZECONTEXT _InitializeContext = NULL; +PGETXSTATEFEATURESMASK _GetXStateFeaturesMask = NULL; +LOCATEXSTATEFEATURE _LocateXStateFeature = NULL; +SETXSTATEFEATURESMASK _SetXStateFeaturesMask = NULL; + +static bool SetAVXContext(HANDLE hActiveThread, TITAN_ENGINE_CONTEXT_t* titcontext) +{ + if (InitXState() == false) + return false; + + DWORD64 FeatureMask = _GetEnabledXStateFeatures(); + if ((FeatureMask & XSTATE_MASK_AVX) == 0) + return false; + + DWORD ContextSize = 0; + BOOL Success = _InitializeContext(NULL, + CONTEXT_ALL | CONTEXT_XSTATE, + NULL, + &ContextSize); + + if ((Success == TRUE) || (GetLastError() != ERROR_INSUFFICIENT_BUFFER)) + return false; + + std::vector dataBuffer; + dataBuffer.resize(ContextSize); + PVOID Buffer = dataBuffer.data(); + if (Buffer == NULL) + return false; + + PCONTEXT Context; + Success = _InitializeContext(Buffer, + CONTEXT_ALL | CONTEXT_XSTATE, + &Context, + &ContextSize); + if (Success == FALSE) + return false; + + if (_SetXStateFeaturesMask(Context, XSTATE_MASK_AVX) == FALSE) + return false; + + if (GetThreadContext(hActiveThread, Context) == FALSE) + return false; + + if (_GetXStateFeaturesMask(Context, &FeatureMask) == FALSE) + return false; + + DWORD FeatureLength; + XmmRegister_t* Sse = (XmmRegister_t*)_LocateXStateFeature(Context, XSTATE_LEGACY_SSE, &FeatureLength); + XmmRegister_t* Avx = (XmmRegister_t*)_LocateXStateFeature(Context, XSTATE_AVX, NULL); + int NumberOfRegisters = FeatureLength / sizeof(Sse[0]); + + if (Sse != NULL) //If the feature is unsupported by the processor it will return NULL + { + for (int i = 0; i < NumberOfRegisters; i++) + Sse[i] = titcontext->YmmRegisters[i].Low; + } + + if (Avx != NULL) //If the feature is unsupported by the processor it will return NULL + { + for (int i = 0; i < NumberOfRegisters; i++) + Avx[i] = titcontext->YmmRegisters[i].High; + } + + return (SetThreadContext(hActiveThread, Context) == TRUE); +} + +static bool GetAVXContext(HANDLE hActiveThread, TITAN_ENGINE_CONTEXT_t* titcontext) +{ + if (InitXState() == false) + return false; + + DWORD64 FeatureMask = _GetEnabledXStateFeatures(); + if ((FeatureMask & XSTATE_MASK_AVX) == 0) + return false; + + DWORD ContextSize = 0; + BOOL Success = _InitializeContext(NULL, + CONTEXT_ALL | CONTEXT_XSTATE, + NULL, + &ContextSize); + + if ((Success == TRUE) || (GetLastError() != ERROR_INSUFFICIENT_BUFFER)) + return false; + + std::vector dataBuffer; + dataBuffer.resize(ContextSize); + PVOID Buffer = dataBuffer.data(); + if (Buffer == NULL) + return false; + + PCONTEXT Context; + Success = _InitializeContext(Buffer, + CONTEXT_ALL | CONTEXT_XSTATE, + &Context, + &ContextSize); + if (Success == FALSE) + return false; + + if (_SetXStateFeaturesMask(Context, XSTATE_MASK_AVX) == FALSE) + return false; + + if (GetThreadContext(hActiveThread, Context) == FALSE) + return false; + + if (_GetXStateFeaturesMask(Context, &FeatureMask) == FALSE) + return false; + + DWORD FeatureLength; + XmmRegister_t* Sse = (XmmRegister_t*)_LocateXStateFeature(Context, XSTATE_LEGACY_SSE, &FeatureLength); + XmmRegister_t* Avx = (XmmRegister_t*)_LocateXStateFeature(Context, XSTATE_AVX, NULL); + int NumberOfRegisters = FeatureLength / sizeof(Sse[0]); + + if (Sse != NULL) //If the feature is unsupported by the processor it will return NULL + { + for (int i = 0; i < NumberOfRegisters; i++) + titcontext->YmmRegisters[i].Low = Sse[i]; + } + + if (Avx != NULL) //If the feature is unsupported by the processor it will return NULL + { + for (int i = 0; i < NumberOfRegisters; i++) + titcontext->YmmRegisters[i].High = Avx[i]; + } + + return true; +} + +bool _SetFullContextDataEx(HANDLE hActiveThread, TITAN_ENGINE_CONTEXT_t* titcontext, bool AVX_PRIORITY) +{ + CONTEXT DBGContext; + memset(&DBGContext, 0, sizeof(DBGContext)); + + DBGContext.ContextFlags = CONTEXT_ALL | CONTEXT_FLOATING_POINT | CONTEXT_EXTENDED_REGISTERS; + + if (!GetThreadContext(hActiveThread, &DBGContext)) + return false; + + DBGContext.EFlags = (DWORD)titcontext->eflags; + DBGContext.Dr0 = titcontext->dr0; + DBGContext.Dr1 = titcontext->dr1; + DBGContext.Dr2 = titcontext->dr2; + DBGContext.Dr3 = titcontext->dr3; + DBGContext.Dr6 = titcontext->dr6; + DBGContext.Dr7 = titcontext->dr7; + DBGContext.SegGs = titcontext->gs; + DBGContext.SegFs = titcontext->fs; + DBGContext.SegEs = titcontext->es; + DBGContext.SegDs = titcontext->ds; + DBGContext.SegCs = titcontext->cs; + DBGContext.SegSs = titcontext->ss; + +#ifdef _WIN64 //x64 + DBGContext.Rax = titcontext->cax; + DBGContext.Rbx = titcontext->cbx; + DBGContext.Rcx = titcontext->ccx; + DBGContext.Rdx = titcontext->cdx; + DBGContext.Rdi = titcontext->cdi; + DBGContext.Rsi = titcontext->csi; + DBGContext.Rbp = titcontext->cbp; + DBGContext.Rsp = titcontext->csp; + DBGContext.Rip = titcontext->cip; + DBGContext.R8 = titcontext->r8; + DBGContext.R9 = titcontext->r9; + DBGContext.R10 = titcontext->r10; + DBGContext.R11 = titcontext->r11; + DBGContext.R12 = titcontext->r12; + DBGContext.R13 = titcontext->r13; + DBGContext.R14 = titcontext->r14; + DBGContext.R15 = titcontext->r15; + + DBGContext.FltSave.ControlWord = titcontext->x87fpu.ControlWord; + DBGContext.FltSave.StatusWord = titcontext->x87fpu.StatusWord; + DBGContext.FltSave.TagWord = FsaveToFxsaveTagWord(titcontext->x87fpu.TagWord); + DBGContext.FltSave.ErrorSelector = (WORD)titcontext->x87fpu.ErrorSelector; + DBGContext.FltSave.ErrorOffset = titcontext->x87fpu.ErrorOffset; + DBGContext.FltSave.DataSelector = (WORD)titcontext->x87fpu.DataSelector; + DBGContext.FltSave.DataOffset = titcontext->x87fpu.DataOffset; + // Skip titcontext->x87fpu.Cr0NpxState + DBGContext.MxCsr = titcontext->MxCsr; + + for(int i = 0; i < 8; i++) + memcpy(& DBGContext.FltSave.FloatRegisters[i], &(titcontext->RegisterArea[i * 10]), 10); + + for(int i = 0; i < 16; i++) + memcpy(& (DBGContext.FltSave.XmmRegisters[i]), & (titcontext->XmmRegisters[i]), 16); + +#else //x86 + DBGContext.Eax = titcontext->cax; + DBGContext.Ebx = titcontext->cbx; + DBGContext.Ecx = titcontext->ccx; + DBGContext.Edx = titcontext->cdx; + DBGContext.Edi = titcontext->cdi; + DBGContext.Esi = titcontext->csi; + DBGContext.Ebp = titcontext->cbp; + DBGContext.Esp = titcontext->csp; + DBGContext.Eip = titcontext->cip; + + DBGContext.FloatSave.ControlWord = titcontext->x87fpu.ControlWord; + DBGContext.FloatSave.StatusWord = titcontext->x87fpu.StatusWord; + DBGContext.FloatSave.TagWord = titcontext->x87fpu.TagWord; + DBGContext.FloatSave.ErrorSelector = titcontext->x87fpu.ErrorSelector; + DBGContext.FloatSave.ErrorOffset = titcontext->x87fpu.ErrorOffset; + DBGContext.FloatSave.DataSelector = titcontext->x87fpu.DataSelector; + DBGContext.FloatSave.DataOffset = titcontext->x87fpu.DataOffset; + DBGContext.FloatSave.Cr0NpxState = titcontext->x87fpu.Cr0NpxState; + + memcpy(DBGContext.FloatSave.RegisterArea, titcontext->RegisterArea, 80); + + // MXCSR ExtendedRegisters[24] + memcpy(& (DBGContext.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(& DBGContext.ExtendedRegisters[(10 + i) * 16], &(titcontext->XmmRegisters[i]), 16); +#endif + + bool returnf = !!SetThreadContext(hActiveThread, &DBGContext); + + if (AVX_PRIORITY) + SetAVXContext(hActiveThread, titcontext); + + return returnf; +} + +bool _GetFullContextDataEx(HANDLE hActiveThread, TITAN_ENGINE_CONTEXT_t* titcontext, bool avx) +{ + CONTEXT DBGContext; + memset(&DBGContext, 0, sizeof(CONTEXT)); + memset(titcontext, 0, sizeof(TITAN_ENGINE_CONTEXT_t)); + + DBGContext.ContextFlags = CONTEXT_ALL | CONTEXT_FLOATING_POINT | CONTEXT_EXTENDED_REGISTERS; + + if(!GetThreadContext(hActiveThread, &DBGContext)) + return false; + + titcontext->eflags = DBGContext.EFlags; + titcontext->dr0 = DBGContext.Dr0; + titcontext->dr1 = DBGContext.Dr1; + titcontext->dr2 = DBGContext.Dr2; + titcontext->dr3 = DBGContext.Dr3; + titcontext->dr6 = DBGContext.Dr6; + titcontext->dr7 = DBGContext.Dr7; + titcontext->gs = (unsigned short) DBGContext.SegGs; + titcontext->fs = (unsigned short) DBGContext.SegFs; + titcontext->es = (unsigned short) DBGContext.SegEs; + titcontext->ds = (unsigned short) DBGContext.SegDs; + titcontext->cs = (unsigned short) DBGContext.SegCs; + titcontext->ss = (unsigned short) DBGContext.SegSs; + +#ifdef _WIN64 //x64 + titcontext->cax = DBGContext.Rax; + titcontext->cbx = DBGContext.Rbx; + titcontext->ccx = DBGContext.Rcx; + titcontext->cdx = DBGContext.Rdx; + titcontext->cdi = DBGContext.Rdi; + titcontext->csi = DBGContext.Rsi; + titcontext->cbp = DBGContext.Rbp; + titcontext->csp = DBGContext.Rsp; + titcontext->cip = DBGContext.Rip; + titcontext->r8 = DBGContext.R8; + titcontext->r9 = DBGContext.R9; + titcontext->r10 = DBGContext.R10; + titcontext->r11 = DBGContext.R11; + titcontext->r12 = DBGContext.R12; + titcontext->r13 = DBGContext.R13; + titcontext->r14 = DBGContext.R14; + titcontext->r15 = DBGContext.R15; + + titcontext->x87fpu.ControlWord = DBGContext.FltSave.ControlWord; + titcontext->x87fpu.StatusWord = DBGContext.FltSave.StatusWord; + titcontext->x87fpu.TagWord = FxsaveToFsaveTagWord(DBGContext.FltSave.StatusWord, DBGContext.FltSave.TagWord, (const X87OrMMXRegister*)DBGContext.FltSave.FloatRegisters); + titcontext->x87fpu.ErrorSelector = DBGContext.FltSave.ErrorSelector; + titcontext->x87fpu.ErrorOffset = DBGContext.FltSave.ErrorOffset; + titcontext->x87fpu.DataSelector = DBGContext.FltSave.DataSelector; + titcontext->x87fpu.DataOffset = DBGContext.FltSave.DataOffset; + // Skip titcontext->x87fpu.Cr0NpxState (https://github.com/x64dbg/x64dbg/issues/255) + titcontext->MxCsr = DBGContext.MxCsr; + + for(int i = 0; i < 8; i++) + memcpy(&titcontext->RegisterArea[i * 10], &DBGContext.FltSave.FloatRegisters[i], 10); + + for(int i = 0; i < 16; i++) + memcpy(&titcontext->XmmRegisters[i], &DBGContext.FltSave.XmmRegisters[i], 16); + +#else //x86 + titcontext->cax = DBGContext.Eax; + titcontext->cbx = DBGContext.Ebx; + titcontext->ccx = DBGContext.Ecx; + titcontext->cdx = DBGContext.Edx; + titcontext->cdi = DBGContext.Edi; + titcontext->csi = DBGContext.Esi; + titcontext->cbp = DBGContext.Ebp; + titcontext->csp = DBGContext.Esp; + titcontext->cip = DBGContext.Eip; + + titcontext->x87fpu.ControlWord = (WORD) DBGContext.FloatSave.ControlWord; + titcontext->x87fpu.StatusWord = (WORD) DBGContext.FloatSave.StatusWord; + titcontext->x87fpu.TagWord = (WORD) DBGContext.FloatSave.TagWord; + titcontext->x87fpu.ErrorSelector = DBGContext.FloatSave.ErrorSelector; + titcontext->x87fpu.ErrorOffset = DBGContext.FloatSave.ErrorOffset; + titcontext->x87fpu.DataSelector = DBGContext.FloatSave.DataSelector; + titcontext->x87fpu.DataOffset = DBGContext.FloatSave.DataOffset; + titcontext->x87fpu.Cr0NpxState = DBGContext.FloatSave.Cr0NpxState; + + memcpy(titcontext->RegisterArea, DBGContext.FloatSave.RegisterArea, 80); + + // MXCSR ExtendedRegisters[24] + memcpy(& (titcontext->MxCsr), & (DBGContext.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]), & DBGContext.ExtendedRegisters[(10 + i) * 16], 16); +#endif + + if(avx) + GetAVXContext(hActiveThread, titcontext); + + return true; +} + +bool InitXState() +{ + static bool init = false; + if(!init) + { + init = true; + HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll"); + if(kernel32 != NULL) + { + _GetEnabledXStateFeatures = (PGETENABLEDXSTATEFEATURES)GetProcAddress(kernel32, "GetEnabledXStateFeatures"); + _InitializeContext = (PINITIALIZECONTEXT)GetProcAddress(kernel32, "InitializeContext"); + _GetXStateFeaturesMask = (PGETXSTATEFEATURESMASK)GetProcAddress(kernel32, "GetXStateFeaturesMask"); + _LocateXStateFeature = (LOCATEXSTATEFEATURE)GetProcAddress(kernel32, "LocateXStateFeature"); + _SetXStateFeaturesMask = (SETXSTATEFEATURESMASK)GetProcAddress(kernel32, "SetXStateFeaturesMask"); + } + } + return (_GetEnabledXStateFeatures != NULL && + _InitializeContext != NULL && + _GetXStateFeaturesMask != NULL && + _LocateXStateFeature != NULL && + _SetXStateFeaturesMask != NULL); +} \ No newline at end of file diff --git a/TitanEngineEmulator/Global.Engine.Context.h b/TitanEngineEmulator/Global.Engine.Context.h new file mode 100644 index 0000000..ac344b2 --- /dev/null +++ b/TitanEngineEmulator/Global.Engine.Context.h @@ -0,0 +1,33 @@ +#ifndef _GLOBAL_ENGINE_CONTEXT_H +#define _GLOBAL_ENGINE_CONTEXT_H + +#include "TitanEngine.h" + +#undef CONTEXT_XSTATE + +#if defined(_M_X64) +#define CONTEXT_XSTATE (0x00100040) +#else +#define CONTEXT_XSTATE (0x00010040) +#endif + +#define XSTATE_AVX (XSTATE_GSSE) +#define XSTATE_MASK_AVX (XSTATE_MASK_GSSE) + +typedef DWORD64(WINAPI* PGETENABLEDXSTATEFEATURES)(); +typedef BOOL (WINAPI* PINITIALIZECONTEXT)(PVOID Buffer, DWORD ContextFlags, PCONTEXT* Context, PDWORD ContextLength); +typedef BOOL (WINAPI* PGETXSTATEFEATURESMASK)(PCONTEXT Context, PDWORD64 FeatureMask); +typedef PVOID(WINAPI* LOCATEXSTATEFEATURE)(PCONTEXT Context, DWORD FeatureId, PDWORD Length); +typedef BOOL (WINAPI* SETXSTATEFEATURESMASK)(PCONTEXT Context, DWORD64 FeatureMask); + +extern PGETENABLEDXSTATEFEATURES _GetEnabledXStateFeatures; +extern PINITIALIZECONTEXT _InitializeContext; +extern PGETXSTATEFEATURESMASK _GetXStateFeaturesMask; +extern LOCATEXSTATEFEATURE _LocateXStateFeature; +extern SETXSTATEFEATURESMASK _SetXStateFeaturesMask; + +bool _SetFullContextDataEx(HANDLE hActiveThread, TITAN_ENGINE_CONTEXT_t* titcontext, bool AVX_PRIORITY); +bool _GetFullContextDataEx(HANDLE hActiveThread, TITAN_ENGINE_CONTEXT_t* titcontext, bool avx); +bool InitXState(void); + +#endif //_GLOBAL_ENGINE_CONTEXT_H \ No newline at end of file diff --git a/TitanEngineEmulator/Hider.h b/TitanEngineEmulator/Hider.h new file mode 100644 index 0000000..c6dc8d9 --- /dev/null +++ b/TitanEngineEmulator/Hider.h @@ -0,0 +1,203 @@ +#pragma once + +#include "ntdll.h" +#include "PEB.h" + +#ifdef _WIN64 +#pragma comment(lib, "ntdll_x64.lib") +#else +#pragma comment(lib, "ntdll_x86.lib") +#endif + +//Quote from The Ultimate Anti-Debugging Reference by Peter Ferrie +//Flags field exists at offset 0x0C in the heap on the 32-bit versions of Windows NT, Windows 2000, and Windows XP; and at offset 0x40 on the 32-bit versions of Windows Vista and later. +//Flags field exists at offset 0x14 in the heap on the 64-bit versions of Windows XP, and at offset 0x70 in the heap on the 64-bit versions of Windows Vista and later. +//ForceFlags field exists at offset 0x10 in the heap on the 32-bit versions of Windows NT, Windows 2000, and Windows XP; and at offset 0x44 on the 32-bit versions of Windows Vista and later. +//ForceFlags field exists at offset 0x18 in the heap on the 64-bit versions of Windows XP, and at offset 0x74 in the heap on the 64-bit versions of Windows Vista and later. + +static bool +IsWindowsVersionOrGreater(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor) +{ + OSVERSIONINFOEXW osvi = { sizeof(osvi), 0, 0, 0, 0, {0}, 0, 0 }; + DWORDLONG const dwlConditionMask = VerSetConditionMask( + VerSetConditionMask( + VerSetConditionMask( + 0, VER_MAJORVERSION, VER_GREATER_EQUAL), + VER_MINORVERSION, VER_GREATER_EQUAL), + VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); + + osvi.dwMajorVersion = wMajorVersion; + osvi.dwMinorVersion = wMinorVersion; + osvi.wServicePackMajor = wServicePackMajor; + + return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE; +} + +static bool +IsWindowsVistaOrGreater() +{ + return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 0); +} + +static int getHeapFlagsOffset(bool x64) +{ + if (x64) //x64 offsets + { + if (IsWindowsVistaOrGreater()) + { + return 0x70; + } + else + { + return 0x14; + } + } + else //x86 offsets + { + if (IsWindowsVistaOrGreater()) + { + return 0x40; + } + else + { + return 0x0C; + } + } +} + +static int getHeapForceFlagsOffset(bool x64) +{ + if (x64) //x64 offsets + { + if (IsWindowsVistaOrGreater()) + { + return 0x74; + } + else + { + return 0x18; + } + } + else //x86 offsets + { + if (IsWindowsVistaOrGreater()) + { + return 0x44; + } + else + { + return 0x10; + } + } +} + +static void* GetPEBLocation_(HANDLE hProcess) +{ + ULONG RequiredLen = 0; + void* PebAddress = 0; + PROCESS_BASIC_INFORMATION myProcessBasicInformation[5] = { 0 }; + + if (NtQueryInformationProcess(hProcess, ProcessBasicInformation, myProcessBasicInformation, sizeof(PROCESS_BASIC_INFORMATION), &RequiredLen) == 0) + { + PebAddress = (void*)myProcessBasicInformation->PebBaseAddress; + } + else + { + if (NtQueryInformationProcess(hProcess, ProcessBasicInformation, myProcessBasicInformation, RequiredLen, &RequiredLen) == 0) + { + PebAddress = (void*)myProcessBasicInformation->PebBaseAddress; + } + } + + return PebAddress; +} + +static bool PebPatchHeapFlags(PEB_CURRENT* peb, HANDLE hProcess) +{ +#ifdef _WIN64 + const auto is_x64 = true; +#else + const auto is_x64 = false; +#endif + + std::vector heaps; + heaps.resize(peb->NumberOfHeaps); + + if (ReadProcessMemory(hProcess, (PVOID)peb->ProcessHeaps, (PVOID)heaps.data(), heaps.size() * sizeof(PVOID), nullptr) == FALSE) + return false; + + std::basic_string heap; + heap.resize(0x100); // hacky + for (DWORD i = 0; i < peb->NumberOfHeaps; i++) + { + if (ReadProcessMemory(hProcess, heaps[i], (PVOID)heap.data(), heap.size(), nullptr) == FALSE) + return false; + + auto flags = (DWORD *)(heap.data() + getHeapFlagsOffset(is_x64)); + auto force_flags = (DWORD *)(heap.data() + getHeapForceFlagsOffset(is_x64)); + + if (i == 0) + { + // Default heap. + *flags &= HEAP_GROWABLE; + } + else + { + // Flags from RtlCreateHeap/HeapCreate. + *flags &= (HEAP_GROWABLE | HEAP_GENERATE_EXCEPTIONS | HEAP_NO_SERIALIZE | HEAP_CREATE_ENABLE_EXECUTE); + } + + *force_flags = 0; + + if (WriteProcessMemory(hProcess, heaps[i], (PVOID)heap.data(), heap.size(), nullptr) == FALSE) + return false; + } + + return true; +} + +static bool FixPebInProcess(HANDLE hProcess) +{ + PEB_CURRENT myPEB = { 0 }; + SIZE_T ueNumberOfBytesRead = 0; + void* heapFlagsAddress = 0; + DWORD heapFlags = 0; + void* heapForceFlagsAddress = 0; + DWORD heapForceFlags = 0; + + void* AddressOfPEB = GetPEBLocation(hProcess); + + if (!AddressOfPEB) + return false; + + if (ReadProcessMemory(hProcess, AddressOfPEB, (void*)&myPEB, sizeof(PEB_CURRENT), &ueNumberOfBytesRead)) + { + myPEB.BeingDebugged = FALSE; + myPEB.NtGlobalFlag &= ~0x70; + +#ifdef _WIN64 + heapFlagsAddress = (void*)((LONG_PTR)myPEB.ProcessHeap + getHeapFlagsOffset(true)); + heapForceFlagsAddress = (void*)((LONG_PTR)myPEB.ProcessHeap + getHeapForceFlagsOffset(true)); +#else + heapFlagsAddress = (void*)((LONG_PTR)myPEB.ProcessHeap + getHeapFlagsOffset(false)); + heapForceFlagsAddress = (void*)((LONG_PTR)myPEB.ProcessHeap + getHeapForceFlagsOffset(false)); +#endif //_WIN64 + + ReadProcessMemory(hProcess, heapFlagsAddress, &heapFlags, sizeof(DWORD), 0); + ReadProcessMemory(hProcess, heapForceFlagsAddress, &heapForceFlags, sizeof(DWORD), 0); + + heapFlags &= HEAP_GROWABLE; + heapForceFlags = 0; + + WriteProcessMemory(hProcess, heapFlagsAddress, &heapFlags, sizeof(DWORD), 0); + WriteProcessMemory(hProcess, heapForceFlagsAddress, &heapForceFlags, sizeof(DWORD), 0); + + PebPatchHeapFlags(&myPEB, hProcess); + + if (WriteProcessMemory(hProcess, AddressOfPEB, (void*)&myPEB, sizeof(PEB_CURRENT), &ueNumberOfBytesRead)) + { + return true; + } + } + return false; +} \ No newline at end of file diff --git a/TitanEngineEmulator/LibraryLoader/x32/LibraryLoader.exe b/TitanEngineEmulator/LibraryLoader/x32/LibraryLoader.exe new file mode 100644 index 0000000..6e69fcf Binary files /dev/null and b/TitanEngineEmulator/LibraryLoader/x32/LibraryLoader.exe differ diff --git a/TitanEngineEmulator/LibraryLoader/x64/LibraryLoader.exe b/TitanEngineEmulator/LibraryLoader/x64/LibraryLoader.exe new file mode 100644 index 0000000..e9e70f6 Binary files /dev/null and b/TitanEngineEmulator/LibraryLoader/x64/LibraryLoader.exe differ diff --git a/TitanEngineEmulator/TitanEngine.rc b/TitanEngineEmulator/TitanEngine.rc new file mode 100644 index 0000000..6e8d106 Binary files /dev/null and b/TitanEngineEmulator/TitanEngine.rc differ diff --git a/TitanEngineEmulator/TitanEngineEmulator.cpp b/TitanEngineEmulator/TitanEngineEmulator.cpp index f54d14e..172a0ae 100644 --- a/TitanEngineEmulator/TitanEngineEmulator.cpp +++ b/TitanEngineEmulator/TitanEngineEmulator.cpp @@ -237,4 +237,15 @@ __declspec(dllexport) void TITCALL StepOver(LPVOID traceCallBack) __declspec(dllexport) void TITCALL StepInto(LPVOID traceCallBack) { emu.StepInto(traceCallBack); +} + +BOOL WINAPI DllMain( + _In_ HINSTANCE hinstDLL, + _In_ DWORD fdwReason, + _In_ LPVOID lpvReserved +) +{ + if (fdwReason == DLL_PROCESS_ATTACH) + emu.engineHandle = hinstDLL; + return TRUE; } \ No newline at end of file diff --git a/TitanEngineEmulator/TitanEngineEmulator.vcxproj b/TitanEngineEmulator/TitanEngineEmulator.vcxproj index a0f94c4..9795cbc 100644 --- a/TitanEngineEmulator/TitanEngineEmulator.vcxproj +++ b/TitanEngineEmulator/TitanEngineEmulator.vcxproj @@ -122,7 +122,7 @@ true true true - MultiThreadedDLL + MultiThreaded true @@ -139,7 +139,7 @@ true true true - MultiThreadedDLL + MultiThreaded true @@ -150,14 +150,21 @@ + + + + + + + diff --git a/TitanEngineEmulator/TitanEngineEmulator.vcxproj.filters b/TitanEngineEmulator/TitanEngineEmulator.vcxproj.filters index 56208f4..c954387 100644 --- a/TitanEngineEmulator/TitanEngineEmulator.vcxproj.filters +++ b/TitanEngineEmulator/TitanEngineEmulator.vcxproj.filters @@ -9,11 +9,17 @@ {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;xsd + + {042de20e-5da8-4247-81ff-f0b401bb7732} + Source Files + + Source Files + @@ -28,5 +34,19 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + + + Resource Files + \ No newline at end of file diff --git a/TitanEngineEmulator/resource.h b/TitanEngineEmulator/resource.h new file mode 100644 index 0000000..215fc58 --- /dev/null +++ b/TitanEngineEmulator/resource.h @@ -0,0 +1,15 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by TitanEngine.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 106 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif