mirror of https://github.com/x64dbg/GleeBug
Implement all TitanEngine functionality (finally)
This commit is contained in:
parent
642e8956d3
commit
2ee4dc0f83
|
|
@ -8,3 +8,4 @@ Release/
|
|||
docs/
|
||||
.vs/
|
||||
*.VC.db
|
||||
*.aps
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -396,25 +396,6 @@ namespace GleeBug
|
|||
static_cast<void>(static_cast<Debugger*>(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;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include "Debugger.Thread.h"
|
||||
#include "Debugger.Thread.Registers.h"
|
||||
|
||||
#define BITSET(a,x) (a|=1<<x)
|
||||
#define BITCLEAR(a,x) (a&=~(1<<x))
|
||||
|
|
@ -186,6 +187,8 @@ namespace GleeBug
|
|||
if ((address % int(size) != 0))
|
||||
return false;
|
||||
|
||||
Registers registers(hThread, CONTEXT_DEBUG_REGISTERS);
|
||||
|
||||
//set the address register
|
||||
switch (slot)
|
||||
{
|
||||
|
|
@ -218,6 +221,8 @@ namespace GleeBug
|
|||
|
||||
bool Thread::DeleteHardwareBreakpoint(HardwareSlot slot)
|
||||
{
|
||||
Registers registers(hThread, CONTEXT_DEBUG_REGISTERS);
|
||||
|
||||
//zero the address register
|
||||
switch (slot)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -56,8 +56,6 @@ namespace GleeBug
|
|||
{
|
||||
ptr Registers::Get(R reg)
|
||||
{
|
||||
handleLazyContext();
|
||||
|
||||
switch (reg)
|
||||
{
|
||||
case R::DR0:
|
||||
|
|
@ -260,8 +258,6 @@ namespace GleeBug
|
|||
|
||||
void Registers::Set(R reg, ptr value)
|
||||
{
|
||||
handleLazyContext();
|
||||
|
||||
switch (reg)
|
||||
{
|
||||
case R::DR0:
|
||||
|
|
@ -530,22 +526,22 @@ namespace GleeBug
|
|||
break;
|
||||
|
||||
case R::GS:
|
||||
mContext.SegGs = value;
|
||||
mContext.SegGs = (WORD)value;
|
||||
break;
|
||||
case R::FS:
|
||||
mContext.SegFs = value;
|
||||
mContext.SegFs = (WORD)value;
|
||||
break;
|
||||
case R::ES:
|
||||
mContext.SegEs = value;
|
||||
mContext.SegEs = (WORD)value;
|
||||
break;
|
||||
case R::DS:
|
||||
mContext.SegDs = value;
|
||||
mContext.SegDs = (WORD)value;
|
||||
break;
|
||||
case R::CS:
|
||||
mContext.SegCs = value;
|
||||
mContext.SegCs = (WORD)value;
|
||||
break;
|
||||
case R::SS:
|
||||
mContext.SegSs = value;
|
||||
mContext.SegSs = (WORD)value;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -555,15 +551,11 @@ namespace GleeBug
|
|||
|
||||
bool Registers::GetFlag(F flag)
|
||||
{
|
||||
handleLazyContext();
|
||||
|
||||
return (mContext.EFlags & ptr(flag)) == ptr(flag);
|
||||
}
|
||||
|
||||
void Registers::SetFlag(F flag, bool set)
|
||||
{
|
||||
handleLazyContext();
|
||||
|
||||
if (set)
|
||||
mContext.EFlags |= ptr(flag);
|
||||
else
|
||||
|
|
@ -572,8 +564,6 @@ namespace GleeBug
|
|||
|
||||
void* Registers::getPtr(R reg)
|
||||
{
|
||||
handleLazyContext();
|
||||
|
||||
switch (reg)
|
||||
{
|
||||
case R::DR0:
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@
|
|||
*/
|
||||
enum class R
|
||||
{
|
||||
Invalid,
|
||||
|
||||
DR0,
|
||||
DR1,
|
||||
DR2,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace GleeBug
|
||||
{
|
||||
Registers::Registers() :
|
||||
Registers::Registers(HANDLE hThread, DWORD ContextFlags) :
|
||||
Dr0(this),
|
||||
Dr1(this),
|
||||
Dr2(this),
|
||||
|
|
@ -106,54 +106,27 @@ namespace GleeBug
|
|||
TrapFlag(this),
|
||||
ResumeFlag(this)
|
||||
{
|
||||
memset(&this->mContext, 0, sizeof(CONTEXT));
|
||||
InitializeCriticalSection(&mCr);
|
||||
}
|
||||
|
||||
LockedPtr<CONTEXT> Registers::GetContext()
|
||||
{
|
||||
handleLazyContext();
|
||||
return LockedPtr<CONTEXT>(&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));
|
||||
}
|
||||
else
|
||||
{
|
||||
this->hThread = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
Registers::~Registers()
|
||||
{
|
||||
if (hThread && memcmp(&mContext, &mOldContext, sizeof(CONTEXT)) != 0)
|
||||
SetThreadContext(hThread, &mContext);
|
||||
}
|
||||
|
||||
PCONTEXT Registers::GetContext()
|
||||
{
|
||||
return &mContext;
|
||||
}
|
||||
};
|
||||
|
|
@ -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 T>
|
||||
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<T> &) = delete;
|
||||
|
||||
LockedPtr<T> &operator=(const LockedPtr<T> &) = delete;
|
||||
|
||||
LockedPtr(LockedPtr<T> && 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<CONTEXT> 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);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@
|
|||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
|
|
@ -138,7 +138,7 @@
|
|||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#define MYDEBUGGER_H
|
||||
|
||||
#include <GleeBug/Debugger.h>
|
||||
#include <GleeBug/Debugger.Thread.Registers.h>
|
||||
|
||||
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());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@
|
|||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
|
|
@ -131,7 +131,7 @@
|
|||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
#include <GleeBug/Debugger.h>
|
||||
#include <GleeBug/Static.Pe.h>
|
||||
#include <GleeBug/Static.Bufferfile.h>
|
||||
#include <GleeBug/Debugger.Thread.Registers.h>
|
||||
#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<typename T> struct identity
|
||||
{
|
||||
typedef T type;
|
||||
};
|
||||
|
||||
template<typename Dst> Dst implicit_cast(typename identity<Dst>::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<uint64_t>(st[7]) & 0x7f) << 56) |
|
||||
(implicit_cast<uint64_t>(st[6]) << 48) |
|
||||
(implicit_cast<uint64_t>(st[5]) << 40) |
|
||||
(implicit_cast<uint64_t>(st[4]) << 32) |
|
||||
(implicit_cast<uint32_t>(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<HANDLE, Thread*> 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<HANDLE, Process*> 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"";
|
||||
};
|
||||
|
|
@ -0,0 +1,482 @@
|
|||
#include "Global.Engine.Context.h"
|
||||
#include <vector>
|
||||
|
||||
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<typename T> struct identity
|
||||
{
|
||||
typedef T type;
|
||||
};
|
||||
|
||||
template<typename Dst> Dst implicit_cast(typename identity<Dst>::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<uint64_t>(st[7]) & 0x7f) << 56) |
|
||||
(implicit_cast<uint64_t>(st[6]) << 48) |
|
||||
(implicit_cast<uint64_t>(st[5]) << 40) |
|
||||
(implicit_cast<uint64_t>(st[4]) << 32) |
|
||||
(implicit_cast<uint32_t>(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<unsigned char> 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<unsigned char> 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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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<PVOID> 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<uint8_t> 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;
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -238,3 +238,14 @@ __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;
|
||||
}
|
||||
|
|
@ -122,7 +122,7 @@
|
|||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
|
|
@ -139,7 +139,7 @@
|
|||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
|
|
@ -150,14 +150,21 @@
|
|||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Global.Engine.Context.cpp" />
|
||||
<ClCompile Include="TitanEngineEmulator.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Emulator.h" />
|
||||
<ClInclude Include="FileMap.h" />
|
||||
<ClInclude Include="Global.Engine.Context.h" />
|
||||
<ClInclude Include="Hider.h" />
|
||||
<ClInclude Include="PEB.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="TitanEngine.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="TitanEngine.rc" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
|
|
|
|||
|
|
@ -9,11 +9,17 @@
|
|||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{042de20e-5da8-4247-81ff-f0b401bb7732}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="TitanEngineEmulator.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Global.Engine.Context.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="TitanEngine.h">
|
||||
|
|
@ -28,5 +34,19 @@
|
|||
<ClInclude Include="PEB.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Global.Engine.Context.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Hider.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="resource.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="TitanEngine.rc">
|
||||
<Filter>Resource Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue