mirror of
https://github.com/x64dbg/x64dbg.git
synced 2024-11-26 22:30:22 +00:00
WIP
This commit is contained in:
parent
15d7924e88
commit
9f4ddfad9e
@ -1,8 +1,112 @@
|
||||
#include "_scriptapi_debug.h"
|
||||
#include "threading.h"
|
||||
#include "debugger.h"
|
||||
#include <map>
|
||||
|
||||
class DebugContext
|
||||
{
|
||||
using CallbackUserdata = std::pair<Script::Debug::CallbackPtr, void*>;
|
||||
|
||||
DWORD threadId = 0;
|
||||
std::map<std::pair<BPXTYPE, duint>, CallbackUserdata> callbacks;
|
||||
std::vector<CallbackUserdata> waitstack;
|
||||
std::vector<CallbackUserdata> finalizers;
|
||||
|
||||
private:
|
||||
DebugContext(const DebugContext &) = default;
|
||||
DebugContext & operator=(const DebugContext &) = default;
|
||||
|
||||
public:
|
||||
DebugContext(DWORD threadId) : threadId(threadId) { }
|
||||
DebugContext(DebugContext &&) = delete;
|
||||
|
||||
~DebugContext()
|
||||
{
|
||||
for(const auto & finalizer : finalizers)
|
||||
finalizer.first(finalizer.second);
|
||||
}
|
||||
|
||||
static void ctxassert(bool condition, const char* message)
|
||||
{
|
||||
if(!condition)
|
||||
MessageBoxA(GuiGetWindowHandle(), message, "Script::Debug Assertion", MB_ICONERROR);
|
||||
}
|
||||
|
||||
void create()
|
||||
{
|
||||
ctxassert(threadId == 0, "You cannot call CreateContext with an existing context!");
|
||||
*this = DebugContext(GetCurrentThreadId());
|
||||
}
|
||||
|
||||
void destroy()
|
||||
{
|
||||
check();
|
||||
ctxassert(threadId != 0, "You cannot call DestroyContext without CreateContext first!");
|
||||
*this = DebugContext(0);
|
||||
}
|
||||
|
||||
void check() const
|
||||
{
|
||||
SHARED_ACQUIRE(LockScriptDebugContext);
|
||||
auto currentThreadId = GetCurrentThreadId();
|
||||
ctxassert(currentThreadId != dbggetdebugloopthreadid(), "You cannot call Script::Debug functions in the debug loop!");
|
||||
ctxassert(threadId == 0 || threadId == currentThreadId, "You cannot mix Script::Debug contexts!");
|
||||
}
|
||||
|
||||
void postwait()
|
||||
{
|
||||
EXCLUSIVE_ACQUIRE(LockScriptDebugContext);
|
||||
if(waitstack.empty())
|
||||
return;
|
||||
auto waitCallback = waitstack.back();
|
||||
waitstack.pop_back();
|
||||
EXCLUSIVE_RELEASE();
|
||||
waitCallback.first(waitCallback.second);
|
||||
}
|
||||
|
||||
void setBreakpointCallback(BPXTYPE type, duint address, Script::Debug::CallbackPtr callback, void* userdata)
|
||||
{
|
||||
EXCLUSIVE_ACQUIRE(LockScriptDebugContext);
|
||||
check();
|
||||
callbacks[ {type, address}] = { callback, userdata };
|
||||
}
|
||||
|
||||
void deleteBreakpointCallback(BPXTYPE type, duint address)
|
||||
{
|
||||
callbacks.erase({ type, address });
|
||||
}
|
||||
|
||||
void handleBreakpoint(const BRIDGEBP & bp)
|
||||
{
|
||||
EXCLUSIVE_ACQUIRE(LockScriptDebugContext);
|
||||
auto itr = callbacks.find({ bp.type, bp.addr });
|
||||
if(itr == callbacks.end())
|
||||
return;
|
||||
waitstack.push_back(itr->second);
|
||||
}
|
||||
|
||||
void addFinalizer(Script::Debug::CallbackPtr callback, void* userdata)
|
||||
{
|
||||
EXCLUSIVE_ACQUIRE(LockScriptDebugContext);
|
||||
check();
|
||||
ctxassert(threadId != 0, "Finalizers cannot be added without a context");
|
||||
CallbackUserdata finalizerCallback;
|
||||
finalizers.emplace_back(callback, userdata);
|
||||
}
|
||||
};
|
||||
|
||||
static DebugContext ctx(0);
|
||||
|
||||
static void cbFlatten(void* userdata)
|
||||
{
|
||||
((Script::Debug::Callback)userdata)();
|
||||
}
|
||||
|
||||
SCRIPT_EXPORT void Script::Debug::Wait()
|
||||
{
|
||||
ctx.check();
|
||||
_plugin_waituntilpaused();
|
||||
ctx.postwait();
|
||||
}
|
||||
|
||||
SCRIPT_EXPORT void Script::Debug::Run()
|
||||
@ -48,13 +152,29 @@ SCRIPT_EXPORT bool Script::Debug::SetBreakpoint(duint address)
|
||||
return DbgCmdExecDirect(command);
|
||||
}
|
||||
|
||||
SCRIPT_EXPORT bool Script::Debug::SetBreakpoint(duint address, Callback callback)
|
||||
{
|
||||
return SetBreakpoint(address, cbFlatten, callback);
|
||||
}
|
||||
|
||||
SCRIPT_EXPORT bool Script::Debug::SetBreakpoint(duint address, CallbackPtr callback, void* userdata)
|
||||
{
|
||||
if(!SetBreakpoint(address))
|
||||
return false;
|
||||
ctx.setBreakpointCallback(bp_normal, address, callback, userdata);
|
||||
return true;
|
||||
}
|
||||
|
||||
SCRIPT_EXPORT bool Script::Debug::DeleteBreakpoint(duint address)
|
||||
{
|
||||
char command[128] = "";
|
||||
sprintf_s(command, "bc %p", address);
|
||||
return DbgCmdExecDirect(command);
|
||||
|
||||
if(!DbgCmdExecDirect(command))
|
||||
return false;
|
||||
ctx.deleteBreakpointCallback(bp_normal, address);
|
||||
return true;
|
||||
}
|
||||
|
||||
SCRIPT_EXPORT bool Script::Debug::DisableBreakpoint(duint address)
|
||||
{
|
||||
char command[128] = "";
|
||||
@ -70,9 +190,48 @@ SCRIPT_EXPORT bool Script::Debug::SetHardwareBreakpoint(duint address, HardwareT
|
||||
return DbgCmdExecDirect(command);
|
||||
}
|
||||
|
||||
namespace Script
|
||||
{
|
||||
namespace Debug
|
||||
{
|
||||
SCRIPT_EXPORT bool SetHardwareBreakpoint(duint address, HardwareType type) // binary-compatibility
|
||||
{
|
||||
return SetHardwareBreakpoint(address, HardwareExecute,)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SCRIPT_EXPORT bool Script::Debug::DeleteHardwareBreakpoint(duint address)
|
||||
{
|
||||
char command[128] = "";
|
||||
sprintf_s(command, "bphwc %p", address);
|
||||
return DbgCmdExecDirect(command);
|
||||
}
|
||||
}
|
||||
|
||||
SCRIPT_EXPORT void Script::Debug::SetBreakpointCallback(BPXTYPE type, duint address, CallbackPtr callback, void* userdata)
|
||||
{
|
||||
ctx.setBreakpointCallback(type, address, callback, userdata);
|
||||
}
|
||||
|
||||
SCRIPT_EXPORT void Script::Debug::Context::Create()
|
||||
{
|
||||
EXCLUSIVE_ACQUIRE(LockScriptDebugContext);
|
||||
ctx.create();
|
||||
}
|
||||
|
||||
SCRIPT_EXPORT void Script::Debug::Context::Destroy()
|
||||
{
|
||||
EXCLUSIVE_ACQUIRE(LockScriptDebugContext);
|
||||
ctx.destroy();
|
||||
}
|
||||
|
||||
SCRIPT_EXPORT void Script::Debug::Context::AddFinalizer(CallbackPtr callback, void* userdata)
|
||||
{
|
||||
ctx.addFinalizer(callback, userdata);
|
||||
}
|
||||
|
||||
void Script::Debug::Internal::BreakpointHandler(const BRIDGEBP & bp)
|
||||
{
|
||||
ctx.handleBreakpoint(bp);
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
#define _SCRIPTAPI_DEBUG_H
|
||||
|
||||
#include "_scriptapi.h"
|
||||
#include <functional>
|
||||
|
||||
namespace Script
|
||||
{
|
||||
@ -14,6 +15,17 @@ namespace Script
|
||||
HardwareExecute
|
||||
};
|
||||
|
||||
enum HardwareSize
|
||||
{
|
||||
HardwareByte,
|
||||
HardwareWord,
|
||||
HardwareDword,
|
||||
HardwareQword
|
||||
};
|
||||
|
||||
using Callback = void (*)();
|
||||
using CallbackPtr = void (*)(void*);
|
||||
|
||||
SCRIPT_EXPORT void Wait();
|
||||
SCRIPT_EXPORT void Run();
|
||||
SCRIPT_EXPORT void Pause();
|
||||
@ -22,10 +34,25 @@ namespace Script
|
||||
SCRIPT_EXPORT void StepOver();
|
||||
SCRIPT_EXPORT void StepOut();
|
||||
SCRIPT_EXPORT bool SetBreakpoint(duint address);
|
||||
SCRIPT_EXPORT bool SetBreakpoint(duint address, Callback callback);
|
||||
SCRIPT_EXPORT bool SetBreakpoint(duint address, CallbackPtr callback, void* userdata);
|
||||
SCRIPT_EXPORT bool DeleteBreakpoint(duint address);
|
||||
SCRIPT_EXPORT bool DisableBreakpoint(duint address);
|
||||
SCRIPT_EXPORT bool SetHardwareBreakpoint(duint address, HardwareType type = HardwareExecute);
|
||||
SCRIPT_EXPORT bool SetHardwareBreakpoint(duint address, HardwareType type = HardwareExecute, HardwareSize size = HardwareByte); // binary-compatibility
|
||||
SCRIPT_EXPORT bool DeleteHardwareBreakpoint(duint address);
|
||||
SCRIPT_EXPORT void SetBreakpointCallback(BPXTYPE type, duint address, CallbackPtr callback, void* userdata);
|
||||
|
||||
namespace Context
|
||||
{
|
||||
SCRIPT_EXPORT void Create();
|
||||
SCRIPT_EXPORT void Destroy();
|
||||
SCRIPT_EXPORT void AddFinalizer(CallbackPtr callback, void* userdata);
|
||||
} // Context
|
||||
|
||||
namespace Internal
|
||||
{
|
||||
void BreakpointHandler(const BRIDGEBP & bp);
|
||||
}
|
||||
}; //Debug
|
||||
}; //Script
|
||||
|
||||
|
@ -501,4 +501,50 @@ bool cbInstrAnimateWait(int argc, char* argv[])
|
||||
Sleep(1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#include <_scriptapi_debug.h>
|
||||
|
||||
static DWORD WINAPI CoroutineThread(LPVOID)
|
||||
{
|
||||
using namespace Script::Debug;
|
||||
|
||||
Context::Create();
|
||||
|
||||
#define Do(action) do { MessageBoxA(0, "pre-" #action, "Do", MB_ICONINFORMATION); action(); } while(0)
|
||||
|
||||
auto adjustMeTopLevel = DbgValFromString("adjustMeTopLevel");
|
||||
SetBreakpoint(adjustMeTopLevel, []
|
||||
{
|
||||
Do(StepOver);
|
||||
Do(StepOver);
|
||||
Do(StepOver);
|
||||
Do(Run);
|
||||
});
|
||||
|
||||
auto adjustMeLayer = DbgValFromString("adjustMeLayer");
|
||||
SetBreakpoint(adjustMeLayer, []
|
||||
{
|
||||
Do(StepOver);
|
||||
DbgCmdExecDirect("_zf=1");
|
||||
Do(Run);
|
||||
});
|
||||
|
||||
auto main = DbgValFromString("main");
|
||||
SetBreakpoint(main, []
|
||||
{
|
||||
Do(StepOut);
|
||||
DbgCmdExecDirect("cax=1");
|
||||
});
|
||||
|
||||
Run();
|
||||
|
||||
Context::Destroy();
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool cbInstrCoroutine(int argc, char* argv[])
|
||||
{
|
||||
CloseHandle(CreateThread(nullptr, 0, CoroutineThread, nullptr, 0, nullptr));
|
||||
return true;
|
||||
}
|
@ -13,4 +13,5 @@ bool cbInstrMeminfo(int argc, char* argv[]);
|
||||
bool cbInstrBriefcheck(int argc, char* argv[]);
|
||||
bool cbInstrFocusinfo(int argc, char* argv[]);
|
||||
bool cbInstrFlushlog(int argc, char* argv[]);
|
||||
bool cbInstrAnimateWait(int argc, char* argv[]);
|
||||
bool cbInstrAnimateWait(int argc, char* argv[]);
|
||||
bool cbInstrCoroutine(int argc, char* argv[]);
|
||||
|
4
src/dbg/cpp.hint
Normal file
4
src/dbg/cpp.hint
Normal file
@ -0,0 +1,4 @@
|
||||
// Hint files help the Visual Studio IDE interpret Visual C++ identifiers
|
||||
// such as names of functions and macros.
|
||||
// For more information see https://go.microsoft.com/fwlink/?linkid=865984
|
||||
#define SCRIPT_EXPORT
|
@ -36,6 +36,7 @@
|
||||
#include "exprfunc.h"
|
||||
#include "debugger_cookie.h"
|
||||
#include "debugger_tracing.h"
|
||||
#include <_scriptapi_debug.h>
|
||||
|
||||
// Debugging variables
|
||||
static PROCESS_INFORMATION g_pi = {0, 0, 0, 0};
|
||||
@ -71,6 +72,7 @@ static CookieQuery cookie;
|
||||
static bool bDatabaseLoaded = false;
|
||||
static duint exceptionDispatchAddr = 0;
|
||||
static bool bPausedOnException = false;
|
||||
static DWORD dwDebugLoopThreadId = 0;
|
||||
char szProgramDir[MAX_PATH] = "";
|
||||
char szFileName[MAX_PATH] = "";
|
||||
char szSymbolCachePath[MAX_PATH] = "";
|
||||
@ -417,6 +419,11 @@ bool dbghandledllbreakpoint(const char* mod, bool loadDll)
|
||||
return shouldBreak;
|
||||
}
|
||||
|
||||
DWORD dbggetdebugloopthreadid()
|
||||
{
|
||||
return dwDebugLoopThreadId;
|
||||
}
|
||||
|
||||
static DWORD WINAPI updateCallStackThread(duint ptr)
|
||||
{
|
||||
stackupdatecallstack(ptr);
|
||||
@ -892,6 +899,7 @@ static void cbGenericBreakpoint(BP_TYPE bptype, void* ExceptionAddress = nullptr
|
||||
bpInfo.breakpoint = &bridgebp;
|
||||
BpToBridge(&bp, &bridgebp);
|
||||
plugincbcall(CB_BREAKPOINT, &bpInfo);
|
||||
Script::Debug::Internal::BreakpointHandler(bridgebp);
|
||||
|
||||
// Trace record
|
||||
_dbg_dbgtraceexecute(CIP);
|
||||
@ -2558,6 +2566,7 @@ static void debugLoopFunction(void* lpParameter, bool attach)
|
||||
dbgsetskipexceptions(false);
|
||||
bFreezeStack = false;
|
||||
bDatabaseLoaded = false;
|
||||
dwDebugLoopThreadId = GetCurrentThreadId();
|
||||
|
||||
//prepare attach/createprocess
|
||||
DWORD pid;
|
||||
@ -2766,6 +2775,7 @@ static void debugLoopFunction(void* lpParameter, bool attach)
|
||||
DeleteFileW(gDllLoader.c_str());
|
||||
gDllLoader.clear();
|
||||
}
|
||||
dwDebugLoopThreadId = 0;
|
||||
}
|
||||
|
||||
void dbgsetdebuggeeinitscript(const char* fileName)
|
||||
|
@ -81,6 +81,7 @@ bool dbggetwintext(std::vector<std::string>* winTextList, const DWORD dwProcessI
|
||||
void dbgtracebrowserneedsupdate();
|
||||
bool dbgsetdllbreakpoint(const char* mod, DWORD type, bool singleshoot);
|
||||
bool dbgdeletedllbreakpoint(const char* mod, DWORD type);
|
||||
DWORD dbggetdebugloopthreadid();
|
||||
|
||||
void cbStep();
|
||||
void cbRtrStep();
|
||||
|
@ -73,6 +73,7 @@ enum SectionLock
|
||||
LockModuleHashes,
|
||||
LockFormatFunctions,
|
||||
LockDllBreakpoints,
|
||||
LockScriptDebugContext,
|
||||
|
||||
// Number of elements in this enumeration. Must always be the last index.
|
||||
LockLast
|
||||
|
@ -442,6 +442,7 @@ static void registercommands()
|
||||
dbgcmdnew("printstack,logstack", cbInstrPrintStack, true); //print the call stack
|
||||
dbgcmdnew("flushlog", cbInstrFlushlog, false); //flush the log
|
||||
dbgcmdnew("AnimateWait", cbInstrAnimateWait, true); //Wait for the debuggee to pause.
|
||||
dbgcmdnew("coroutine", cbInstrCoroutine, true);
|
||||
};
|
||||
|
||||
bool cbCommandProvider(char* cmd, int maxlen)
|
||||
|
@ -293,6 +293,9 @@
|
||||
<UseLibraryDependencyInputs>false</UseLibraryDependencyInputs>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="cpp.hint" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{E6548308-401E-3A8A-5819-905DB90522A6}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
|
@ -896,4 +896,7 @@
|
||||
<Filter>Header Files\Symbols</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="cpp.hint" />
|
||||
</ItemGroup>
|
||||
</Project>
|
Loading…
Reference in New Issue
Block a user