845 lines
25 KiB
C++
845 lines
25 KiB
C++
/**
|
|
@file breakpoint.cpp
|
|
|
|
@brief Implements the breakpoint class.
|
|
*/
|
|
|
|
#include "breakpoint.h"
|
|
#include "memory.h"
|
|
#include "threading.h"
|
|
#include "module.h"
|
|
#include "value.h"
|
|
#include "debugger.h"
|
|
#include "exception.h"
|
|
#include <algorithm>
|
|
|
|
typedef std::pair<BP_TYPE, duint> BreakpointKey;
|
|
std::map<BreakpointKey, BREAKPOINT> breakpoints;
|
|
|
|
static void setBpActive(BREAKPOINT & bp)
|
|
{
|
|
if(bp.type == BPHARDWARE) //TODO: properly implement this (check debug registers)
|
|
bp.active = true;
|
|
else if(bp.type == BPDLL || bp.type == BPEXCEPTION)
|
|
bp.active = true;
|
|
else
|
|
bp.active = MemIsValidReadPtr(bp.addr);
|
|
}
|
|
|
|
BREAKPOINT* BpInfoFromAddr(BP_TYPE Type, duint Address)
|
|
{
|
|
//
|
|
// NOTE: THIS DOES _NOT_ USE LOCKS
|
|
//
|
|
std::map<BreakpointKey, BREAKPOINT>::iterator found;
|
|
if(Type != BPDLL && Type != BPEXCEPTION)
|
|
found = breakpoints.find(BreakpointKey(Type, ModHashFromAddr(Address)));
|
|
else
|
|
found = breakpoints.find(BreakpointKey(Type, Address)); // Address = ModHashFromName(ModuleName)
|
|
|
|
// Was the module found with this address?
|
|
if(found == breakpoints.end())
|
|
return nullptr;
|
|
|
|
return &found->second;
|
|
}
|
|
|
|
int BpGetList(std::vector<BREAKPOINT>* List)
|
|
{
|
|
SHARED_ACQUIRE(LockBreakpoints);
|
|
|
|
// Did the caller request an output?
|
|
if(List)
|
|
{
|
|
// Enumerate all breakpoints in the global list, fixing the relative
|
|
// offset to a virtual address
|
|
for(auto & i : breakpoints)
|
|
{
|
|
BREAKPOINT currentBp = i.second;
|
|
if(currentBp.type != BPDLL && currentBp.type != BPEXCEPTION)
|
|
currentBp.addr += ModBaseFromName(currentBp.mod);
|
|
setBpActive(currentBp);
|
|
|
|
List->push_back(currentBp);
|
|
}
|
|
std::sort(List->begin(), List->end(), [](const BREAKPOINT & a, const BREAKPOINT & b)
|
|
{
|
|
return std::make_pair(a.type, a.addr) < std::make_pair(b.type, b.addr);
|
|
});
|
|
}
|
|
|
|
return (int)breakpoints.size();
|
|
}
|
|
|
|
bool BpNew(duint Address, bool Enable, bool Singleshot, short OldBytes, BP_TYPE Type, DWORD TitanType, const char* Name, duint memsize)
|
|
{
|
|
ASSERT_DEBUGGING("Export call");
|
|
|
|
// Fail if the address is a bad memory region
|
|
if(Type != BPDLL && Type != BPEXCEPTION)
|
|
{
|
|
if(!MemIsValidReadPtr(Address))
|
|
return false;
|
|
}
|
|
|
|
// Fail if the breakpoint already exists
|
|
if(BpGet(Address, Type, Name, nullptr))
|
|
return false;
|
|
|
|
// Default to an empty name if one wasn't supplied
|
|
if(!Name)
|
|
Name = "";
|
|
|
|
BREAKPOINT bp;
|
|
memset(&bp, 0, sizeof(BREAKPOINT));
|
|
|
|
if(Type != BPDLL && Type != BPEXCEPTION)
|
|
{
|
|
ModNameFromAddr(Address, bp.mod, true);
|
|
}
|
|
strncpy_s(bp.name, Name, _TRUNCATE);
|
|
|
|
bp.active = true;
|
|
if(Type != BPDLL && Type != BPEXCEPTION)
|
|
bp.addr = Address - ModBaseFromAddr(Address);
|
|
else
|
|
bp.addr = Address;
|
|
bp.enabled = Enable;
|
|
bp.oldbytes = OldBytes;
|
|
bp.singleshoot = Singleshot;
|
|
bp.titantype = TitanType;
|
|
bp.type = Type;
|
|
bp.memsize = memsize;
|
|
|
|
// Insert new entry to the global list
|
|
EXCLUSIVE_ACQUIRE(LockBreakpoints);
|
|
|
|
if(Type != BPDLL && Type != BPEXCEPTION)
|
|
{
|
|
return breakpoints.insert(std::make_pair(BreakpointKey(Type, ModHashFromAddr(Address)), bp)).second;
|
|
}
|
|
else
|
|
{
|
|
return breakpoints.insert(std::make_pair(BreakpointKey(Type, Address), bp)).second;
|
|
}
|
|
}
|
|
|
|
bool BpNewDll(const char* module, bool Enable, bool Singleshot, DWORD TitanType, const char* Name)
|
|
{
|
|
// Default to an empty name if one wasn't supplied
|
|
if(!Name)
|
|
Name = "";
|
|
|
|
BREAKPOINT bp;
|
|
memset(&bp, 0, sizeof(BREAKPOINT));
|
|
strcpy_s(bp.mod, module);
|
|
strcpy_s(bp.name, Name);
|
|
bp.active = true;
|
|
bp.enabled = Enable;
|
|
bp.singleshoot = Singleshot;
|
|
bp.titantype = TitanType;
|
|
bp.type = BPDLL;
|
|
bp.addr = BpGetDLLBpAddr(module);
|
|
|
|
// Insert new entry to the global list
|
|
EXCLUSIVE_ACQUIRE(LockBreakpoints);
|
|
|
|
return breakpoints.insert(std::make_pair(BreakpointKey(BPDLL, bp.addr), bp)).second;
|
|
}
|
|
|
|
bool BpGet(duint Address, BP_TYPE Type, const char* Name, BREAKPOINT* Bp)
|
|
{
|
|
if(!DbgIsDebugging())
|
|
return false;
|
|
SHARED_ACQUIRE(LockBreakpoints);
|
|
|
|
// Name is optional
|
|
if(!Name || Name[0] == '\0')
|
|
{
|
|
// Perform a lookup by address only
|
|
BREAKPOINT* bpInfo = BpInfoFromAddr(Type, Address);
|
|
|
|
if(!bpInfo)
|
|
return false;
|
|
|
|
// Succeed even if the user didn't request anything
|
|
if(!Bp)
|
|
return true;
|
|
|
|
*Bp = *bpInfo;
|
|
if(bpInfo->type != BPDLL && bpInfo->type != BPEXCEPTION)
|
|
Bp->addr += ModBaseFromAddr(Address);
|
|
setBpActive(*Bp);
|
|
return true;
|
|
}
|
|
|
|
// Do a lookup by breakpoint name
|
|
for(auto & i : breakpoints)
|
|
{
|
|
// Do the names match?
|
|
if(_stricmp(Name, i.second.name) != 0)
|
|
continue;
|
|
|
|
// Fill out the optional user buffer
|
|
if(Bp)
|
|
{
|
|
*Bp = i.second;
|
|
if(i.second.type != BPDLL && i.second.type != BPEXCEPTION)
|
|
Bp->addr += ModBaseFromAddr(Address);
|
|
setBpActive(*Bp);
|
|
}
|
|
|
|
// Return true if the name was found at all
|
|
return true;
|
|
}
|
|
|
|
// If name in a special format "libwinpthread-1.dll":$7792, find the breakpoint even if the DLL might not be loaded yet.
|
|
const char* separatorPos;
|
|
separatorPos = strstr(Name, ":$"); //DLL file names cannot contain ":" char anyway, so ignoring the quotes is fine. The following part of RVA expression might contain ":$"?
|
|
if(separatorPos && Type != BPDLL && Type != BPEXCEPTION)
|
|
{
|
|
char* DLLName = _strdup(Name);
|
|
char* RVAPos = DLLName + (separatorPos - Name);
|
|
RVAPos[0] = RVAPos[1] = '\0';
|
|
RVAPos = RVAPos + 2; //Now 2 strings separated by NULs
|
|
if(valfromstring(RVAPos, &Address)) //"Address" reused here. No usage of original "Address" argument.
|
|
{
|
|
if(separatorPos != Name) //Check if DLL name is surrounded by quotes. Don't be out of bounds!
|
|
{
|
|
if(DLLName[0] == '"' && RVAPos[-3] == '"')
|
|
{
|
|
RVAPos[-3] = '\0';
|
|
DLLName[0] = '\0';
|
|
}
|
|
}
|
|
if(DLLName[0] != '\0')
|
|
{
|
|
duint base = ModBaseFromName(DLLName); //Is the DLL actually loaded?
|
|
Address += base ? base : ModHashFromName(DLLName);
|
|
}
|
|
else
|
|
{
|
|
duint base = ModBaseFromName(DLLName + 1);
|
|
Address += base ? base : ModHashFromName(DLLName + 1);
|
|
}
|
|
|
|
// Perform a lookup by address only
|
|
BREAKPOINT* bpInfo = BpInfoFromAddr(Type, Address);
|
|
|
|
if(!bpInfo)
|
|
return false;
|
|
|
|
// Succeed even if the user didn't request anything
|
|
if(!Bp)
|
|
return true;
|
|
|
|
*Bp = *bpInfo;
|
|
Bp->addr = Address;
|
|
setBpActive(*Bp);
|
|
return true;
|
|
}
|
|
free(DLLName);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool BpGetAny(BP_TYPE Type, const char* Name, BREAKPOINT* Bp)
|
|
{
|
|
if(BpGet(0, Type, Name, Bp))
|
|
return true;
|
|
if(Type != BPDLL)
|
|
{
|
|
duint addr;
|
|
if(valfromstring(Name, &addr))
|
|
if(BpGet(addr, Type, 0, Bp))
|
|
return true;
|
|
if(Type == BPEXCEPTION)
|
|
{
|
|
addr = 0;
|
|
if(ExceptionNameToCode(Name, reinterpret_cast<unsigned int*>(&addr)))
|
|
if(BpGet(addr, BPEXCEPTION, 0, Bp))
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(BpGet(BpGetDLLBpAddr(Name), Type, 0, Bp))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool BpUpdateDllPath(const char* module1, BREAKPOINT** newBpInfo)
|
|
{
|
|
const char* dashPos1 = max(strrchr(module1, '\\'), strrchr(module1, '/'));
|
|
EXCLUSIVE_ACQUIRE(LockBreakpoints);
|
|
for(auto & i : breakpoints)
|
|
{
|
|
BREAKPOINT & bpRef = i.second;
|
|
if(bpRef.type == BPDLL && bpRef.enabled)
|
|
{
|
|
if(_stricmp(bpRef.mod, module1) == 0)
|
|
{
|
|
BREAKPOINT temp;
|
|
temp = bpRef;
|
|
strcpy_s(temp.mod, module1);
|
|
temp.addr = ModHashFromName(module1);
|
|
breakpoints.erase(i.first);
|
|
auto newItem = breakpoints.insert(std::make_pair(BreakpointKey(BPDLL, temp.addr), temp));
|
|
*newBpInfo = &newItem.first->second;
|
|
return true;
|
|
}
|
|
const char* dashPos = max(strrchr(bpRef.mod, '\\'), strrchr(bpRef.mod, '/'));
|
|
if(dashPos == nullptr)
|
|
dashPos = bpRef.mod;
|
|
else
|
|
dashPos += 1;
|
|
if(dashPos1 != nullptr && _stricmp(dashPos, dashPos1 + 1) == 0) // filename matches
|
|
{
|
|
BREAKPOINT temp;
|
|
temp = bpRef;
|
|
strcpy_s(temp.mod, dashPos1 + 1);
|
|
temp.addr = ModHashFromName(dashPos1 + 1);
|
|
breakpoints.erase(i.first);
|
|
auto newItem = breakpoints.insert(std::make_pair(BreakpointKey(BPDLL, temp.addr), temp));
|
|
*newBpInfo = &newItem.first->second;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
*newBpInfo = nullptr;
|
|
return false;
|
|
}
|
|
|
|
duint BpGetDLLBpAddr(const char* fileName)
|
|
{
|
|
const char* dashPos1 = max(strrchr(fileName, '\\'), strrchr(fileName, '/'));
|
|
if(dashPos1 == nullptr)
|
|
dashPos1 = fileName;
|
|
else
|
|
dashPos1++;
|
|
return ModHashFromName(dashPos1);
|
|
}
|
|
|
|
bool BpDelete(duint Address, BP_TYPE Type)
|
|
{
|
|
ASSERT_DEBUGGING("Command function call");
|
|
EXCLUSIVE_ACQUIRE(LockBreakpoints);
|
|
|
|
// Erase the index from the global list
|
|
if(Type != BPDLL)
|
|
return (breakpoints.erase(BreakpointKey(Type, ModHashFromAddr(Address))) > 0);
|
|
else
|
|
return (breakpoints.erase(BreakpointKey(BPDLL, Address)) > 0);
|
|
}
|
|
|
|
bool BpEnable(duint Address, BP_TYPE Type, bool Enable)
|
|
{
|
|
ASSERT_DEBUGGING("Command function call");
|
|
EXCLUSIVE_ACQUIRE(LockBreakpoints);
|
|
|
|
// Check if the breakpoint exists first
|
|
BREAKPOINT* bpInfo = BpInfoFromAddr(Type, Address);
|
|
|
|
if(!bpInfo)
|
|
return false;
|
|
|
|
bpInfo->enabled = Enable;
|
|
|
|
//Re-read oldbytes
|
|
if(Enable && Type == BPNORMAL)
|
|
{
|
|
if(!MemRead(Address, &bpInfo->oldbytes, sizeof(bpInfo->oldbytes)))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool BpSetName(duint Address, BP_TYPE Type, const char* Name)
|
|
{
|
|
ASSERT_DEBUGGING("Future(?): This is not used anywhere");
|
|
EXCLUSIVE_ACQUIRE(LockBreakpoints);
|
|
|
|
// If a name wasn't supplied, set to nothing
|
|
if(!Name)
|
|
Name = "";
|
|
|
|
// Check if the breakpoint exists first
|
|
BREAKPOINT* bpInfo = BpInfoFromAddr(Type, Address);
|
|
|
|
if(!bpInfo)
|
|
return false;
|
|
|
|
strncpy_s(bpInfo->name, Name, _TRUNCATE);
|
|
return true;
|
|
}
|
|
|
|
bool BpSetTitanType(duint Address, BP_TYPE Type, int TitanType)
|
|
{
|
|
ASSERT_DEBUGGING("Command function call");
|
|
EXCLUSIVE_ACQUIRE(LockBreakpoints);
|
|
|
|
// Set the TitanEngine type, separate from BP_TYPE
|
|
BREAKPOINT* bpInfo = BpInfoFromAddr(Type, Address);
|
|
|
|
if(!bpInfo)
|
|
return false;
|
|
|
|
bpInfo->titantype = TitanType;
|
|
return true;
|
|
}
|
|
|
|
bool BpSetBreakCondition(duint Address, BP_TYPE Type, const char* Condition)
|
|
{
|
|
ASSERT_DEBUGGING("Command function call");
|
|
EXCLUSIVE_ACQUIRE(LockBreakpoints);
|
|
|
|
// Set breakpoint breakCondition
|
|
BREAKPOINT* bpInfo = BpInfoFromAddr(Type, Address);
|
|
|
|
if(!bpInfo)
|
|
return false;
|
|
|
|
strncpy_s(bpInfo->breakCondition, Condition, _TRUNCATE);
|
|
return true;
|
|
}
|
|
|
|
bool BpSetLogText(duint Address, BP_TYPE Type, const char* Log)
|
|
{
|
|
ASSERT_DEBUGGING("Command function call");
|
|
EXCLUSIVE_ACQUIRE(LockBreakpoints);
|
|
|
|
// Set breakpoint logText
|
|
BREAKPOINT* bpInfo = BpInfoFromAddr(Type, Address);
|
|
|
|
if(!bpInfo)
|
|
return false;
|
|
|
|
strncpy_s(bpInfo->logText, Log, _TRUNCATE);
|
|
|
|
// Make log breakpoints silent (meaning they don't output the default log).
|
|
bpInfo->silent = *Log != '\0';
|
|
return true;
|
|
}
|
|
|
|
bool BpSetLogCondition(duint Address, BP_TYPE Type, const char* Condition)
|
|
{
|
|
ASSERT_DEBUGGING("Command function call");
|
|
EXCLUSIVE_ACQUIRE(LockBreakpoints);
|
|
|
|
// Set breakpoint logText
|
|
BREAKPOINT* bpInfo = BpInfoFromAddr(Type, Address);
|
|
|
|
if(!bpInfo)
|
|
return false;
|
|
|
|
strncpy_s(bpInfo->logCondition, Condition, _TRUNCATE);
|
|
return true;
|
|
}
|
|
|
|
bool BpSetCommandText(duint Address, BP_TYPE Type, const char* Cmd)
|
|
{
|
|
ASSERT_DEBUGGING("Command function call");
|
|
EXCLUSIVE_ACQUIRE(LockBreakpoints);
|
|
|
|
// Set breakpoint hit command
|
|
BREAKPOINT* bpInfo = BpInfoFromAddr(Type, Address);
|
|
|
|
if(!bpInfo)
|
|
return false;
|
|
|
|
strncpy_s(bpInfo->commandText, Cmd, _TRUNCATE);
|
|
return true;
|
|
}
|
|
|
|
bool BpSetCommandCondition(duint Address, BP_TYPE Type, const char* Condition)
|
|
{
|
|
ASSERT_DEBUGGING("Command function call");
|
|
EXCLUSIVE_ACQUIRE(LockBreakpoints);
|
|
|
|
// Set breakpoint hit command
|
|
BREAKPOINT* bpInfo = BpInfoFromAddr(Type, Address);
|
|
|
|
if(!bpInfo)
|
|
return false;
|
|
|
|
strncpy_s(bpInfo->commandCondition, Condition, _TRUNCATE);
|
|
return true;
|
|
}
|
|
|
|
bool BpSetFastResume(duint Address, BP_TYPE Type, bool fastResume)
|
|
{
|
|
ASSERT_DEBUGGING("Command function call");
|
|
EXCLUSIVE_ACQUIRE(LockBreakpoints);
|
|
|
|
// Set breakpoint fast resume
|
|
BREAKPOINT* bpInfo = BpInfoFromAddr(Type, Address);
|
|
|
|
if(!bpInfo)
|
|
return false;
|
|
|
|
bpInfo->fastResume = fastResume;
|
|
return true;
|
|
}
|
|
|
|
bool BpSetSingleshoot(duint Address, BP_TYPE Type, bool singleshoot)
|
|
{
|
|
ASSERT_DEBUGGING("Command function call");
|
|
EXCLUSIVE_ACQUIRE(LockBreakpoints);
|
|
|
|
// Set breakpoint fast resume
|
|
BREAKPOINT* bpInfo = BpInfoFromAddr(Type, Address);
|
|
|
|
if(!bpInfo)
|
|
return false;
|
|
|
|
bpInfo->singleshoot = singleshoot;
|
|
return true;
|
|
}
|
|
|
|
bool BpSetSilent(duint Address, BP_TYPE Type, bool silent)
|
|
{
|
|
ASSERT_DEBUGGING("Command function call");
|
|
EXCLUSIVE_ACQUIRE(LockBreakpoints);
|
|
|
|
// Set breakpoint fast resume
|
|
BREAKPOINT* bpInfo = BpInfoFromAddr(Type, Address);
|
|
|
|
if(!bpInfo)
|
|
return false;
|
|
|
|
bpInfo->silent = silent;
|
|
return true;
|
|
}
|
|
|
|
bool BpEnumAll(BPENUMCALLBACK EnumCallback, const char* Module, duint base)
|
|
{
|
|
ASSERT_DEBUGGING("Export call");
|
|
SHARED_ACQUIRE(LockBreakpoints);
|
|
|
|
// Loop each entry, executing the user's callback
|
|
bool callbackStatus = true;
|
|
|
|
for(auto i = breakpoints.begin(); i != breakpoints.end();)
|
|
{
|
|
auto j = i;
|
|
++i; // Increment here, because the callback might remove the current entry
|
|
|
|
// If a module name was sent, check it
|
|
if(Module)
|
|
{
|
|
if(strcmp(j->second.mod, Module) != 0)
|
|
continue;
|
|
}
|
|
|
|
BREAKPOINT bpInfo = j->second;
|
|
if(bpInfo.type != BPDLL && bpInfo.type != BPEXCEPTION)
|
|
{
|
|
if(base) //workaround for some Windows bullshit with compatibility mode
|
|
bpInfo.addr += base;
|
|
else
|
|
bpInfo.addr += ModBaseFromName(bpInfo.mod);
|
|
}
|
|
setBpActive(bpInfo);
|
|
|
|
// Lock must be released due to callback sub-locks
|
|
SHARED_RELEASE();
|
|
|
|
// Execute the callback
|
|
if(!EnumCallback(&bpInfo))
|
|
callbackStatus = false;
|
|
|
|
// Restore the breakpoint map lock
|
|
SHARED_REACQUIRE();
|
|
}
|
|
|
|
return callbackStatus;
|
|
}
|
|
|
|
bool BpEnumAll(BPENUMCALLBACK EnumCallback)
|
|
{
|
|
return BpEnumAll(EnumCallback, nullptr);
|
|
}
|
|
|
|
int BpGetCount(BP_TYPE Type, bool EnabledOnly)
|
|
{
|
|
SHARED_ACQUIRE(LockBreakpoints);
|
|
|
|
// Count the number of enabled/disabled breakpoint types
|
|
int count = 0;
|
|
|
|
for(auto & i : breakpoints)
|
|
{
|
|
// Check if the type matches
|
|
if(i.first.first != Type)
|
|
continue;
|
|
|
|
// If it's not enabled, skip it
|
|
if(EnabledOnly && !i.second.enabled)
|
|
continue;
|
|
|
|
count++;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
uint32 BpGetHitCount(duint Address, BP_TYPE Type)
|
|
{
|
|
SHARED_ACQUIRE(LockBreakpoints);
|
|
|
|
BREAKPOINT* bpInfo = BpInfoFromAddr(Type, Address);
|
|
|
|
if(!bpInfo)
|
|
return 0;
|
|
|
|
return bpInfo->hitcount;
|
|
}
|
|
|
|
bool BpResetHitCount(duint Address, BP_TYPE Type, uint32 newHitCount)
|
|
{
|
|
EXCLUSIVE_ACQUIRE(LockBreakpoints);
|
|
|
|
BREAKPOINT* bpInfo = BpInfoFromAddr(Type, Address);
|
|
|
|
if(!bpInfo)
|
|
return false;
|
|
|
|
bpInfo->hitcount = newHitCount;
|
|
return true;
|
|
}
|
|
|
|
void BpToBridge(const BREAKPOINT* Bp, BRIDGEBP* BridgeBp)
|
|
{
|
|
//
|
|
// Convert a debugger breakpoint to an open/exported
|
|
// bridge breakpoint
|
|
//
|
|
ASSERT_NONNULL(Bp);
|
|
ASSERT_NONNULL(BridgeBp);
|
|
|
|
memset(BridgeBp, 0, sizeof(BRIDGEBP));
|
|
strncpy_s(BridgeBp->mod, Bp->mod, _TRUNCATE);
|
|
strncpy_s(BridgeBp->name, Bp->name, _TRUNCATE);
|
|
strncpy_s(BridgeBp->breakCondition, Bp->breakCondition, _TRUNCATE);
|
|
strncpy_s(BridgeBp->logText, Bp->logText, _TRUNCATE);
|
|
strncpy_s(BridgeBp->logCondition, Bp->logCondition, _TRUNCATE);
|
|
strncpy_s(BridgeBp->commandText, Bp->commandText, _TRUNCATE);
|
|
strncpy_s(BridgeBp->commandCondition, Bp->commandCondition, _TRUNCATE);
|
|
|
|
BridgeBp->active = Bp->active;
|
|
BridgeBp->addr = Bp->addr;
|
|
BridgeBp->enabled = Bp->enabled;
|
|
BridgeBp->singleshoot = Bp->singleshoot;
|
|
BridgeBp->fastResume = Bp->fastResume;
|
|
BridgeBp->silent = Bp->silent;
|
|
BridgeBp->hitCount = Bp->hitcount;
|
|
|
|
switch(Bp->type)
|
|
{
|
|
case BPNORMAL:
|
|
BridgeBp->type = bp_normal;
|
|
break;
|
|
case BPHARDWARE:
|
|
BridgeBp->type = bp_hardware;
|
|
switch(TITANGETDRX(Bp->titantype))
|
|
{
|
|
case UE_DR0:
|
|
BridgeBp->slot = 0;
|
|
break;
|
|
case UE_DR1:
|
|
BridgeBp->slot = 1;
|
|
break;
|
|
case UE_DR2:
|
|
BridgeBp->slot = 2;
|
|
break;
|
|
case UE_DR3:
|
|
BridgeBp->slot = 3;
|
|
break;
|
|
}
|
|
switch(TITANGETSIZE(Bp->titantype))
|
|
{
|
|
case UE_HARDWARE_SIZE_1:
|
|
BridgeBp->hwSize = hw_byte;
|
|
break;
|
|
case UE_HARDWARE_SIZE_2:
|
|
BridgeBp->hwSize = hw_word;
|
|
break;
|
|
case UE_HARDWARE_SIZE_4:
|
|
BridgeBp->hwSize = hw_dword;
|
|
break;
|
|
case UE_HARDWARE_SIZE_8:
|
|
BridgeBp->hwSize = hw_qword;
|
|
break;
|
|
}
|
|
switch(TITANGETTYPE(Bp->titantype))
|
|
{
|
|
case UE_HARDWARE_READWRITE:
|
|
BridgeBp->typeEx = hw_access;
|
|
break;
|
|
case UE_HARDWARE_WRITE:
|
|
BridgeBp->typeEx = hw_write;
|
|
break;
|
|
case UE_HARDWARE_EXECUTE:
|
|
BridgeBp->typeEx = hw_execute;
|
|
break;
|
|
}
|
|
break;
|
|
case BPMEMORY:
|
|
BridgeBp->type = bp_memory;
|
|
break;
|
|
case BPDLL:
|
|
BridgeBp->type = bp_dll;
|
|
switch(Bp->titantype)
|
|
{
|
|
case UE_ON_LIB_LOAD:
|
|
BridgeBp->typeEx = dll_load;
|
|
break;
|
|
case UE_ON_LIB_UNLOAD:
|
|
BridgeBp->typeEx = dll_unload;
|
|
break;
|
|
case UE_ON_LIB_ALL:
|
|
BridgeBp->typeEx = dll_all;
|
|
break;
|
|
}
|
|
break;
|
|
case BPEXCEPTION:
|
|
BridgeBp->type = bp_exception;
|
|
switch(Bp->titantype) //1:First-chance, 2:Second-chance, 3:Both
|
|
{
|
|
case 1:
|
|
BridgeBp->typeEx = ex_firstchance;
|
|
break;
|
|
case 2:
|
|
BridgeBp->typeEx = ex_secondchance;
|
|
break;
|
|
case 3:
|
|
BridgeBp->typeEx = ex_all;
|
|
break;
|
|
default:
|
|
__debugbreak();
|
|
}
|
|
break;
|
|
default:
|
|
BridgeBp->type = bp_none;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void BpCacheSave(JSON Root)
|
|
{
|
|
EXCLUSIVE_ACQUIRE(LockBreakpoints);
|
|
|
|
// Create a JSON array to store each sub-object with a breakpoint
|
|
const JSON jsonBreakpoints = json_array();
|
|
|
|
// Loop all breakpoints
|
|
for(const auto & i : breakpoints)
|
|
{
|
|
const auto & breakpoint = i.second;
|
|
|
|
// Ignore single-shot breakpoints
|
|
if(breakpoint.singleshoot)
|
|
continue;
|
|
|
|
JSON jsonObj = json_object();
|
|
json_object_set_new(jsonObj, "address", json_hex(breakpoint.addr));
|
|
json_object_set_new(jsonObj, "enabled", json_boolean(breakpoint.enabled));
|
|
|
|
if(breakpoint.type == BPNORMAL) // "Normal" breakpoints save the old data
|
|
json_object_set_new(jsonObj, "oldbytes", json_hex(breakpoint.oldbytes));
|
|
else if(breakpoint.type == BPMEMORY) // Memory breakpoints save the memory size
|
|
json_object_set_new(jsonObj, "memsize", json_hex(breakpoint.memsize));
|
|
|
|
json_object_set_new(jsonObj, "type", json_integer(breakpoint.type));
|
|
json_object_set_new(jsonObj, "titantype", json_hex(breakpoint.titantype));
|
|
json_object_set_new(jsonObj, "name", json_string(breakpoint.name));
|
|
if(breakpoint.type != BPEXCEPTION)
|
|
json_object_set_new(jsonObj, "module", json_string(breakpoint.mod));
|
|
json_object_set_new(jsonObj, "breakCondition", json_string(breakpoint.breakCondition));
|
|
json_object_set_new(jsonObj, "logText", json_string(breakpoint.logText));
|
|
json_object_set_new(jsonObj, "logCondition", json_string(breakpoint.logCondition));
|
|
json_object_set_new(jsonObj, "commandText", json_string(breakpoint.commandText));
|
|
json_object_set_new(jsonObj, "commandCondition", json_string(breakpoint.commandCondition));
|
|
json_object_set_new(jsonObj, "fastResume", json_boolean(breakpoint.fastResume));
|
|
json_object_set_new(jsonObj, "silent", json_boolean(breakpoint.silent));
|
|
json_array_append_new(jsonBreakpoints, jsonObj);
|
|
}
|
|
|
|
if(json_array_size(jsonBreakpoints))
|
|
json_object_set(Root, "breakpoints", jsonBreakpoints);
|
|
|
|
// Notify garbage collector
|
|
json_decref(jsonBreakpoints);
|
|
}
|
|
|
|
template<typename T>
|
|
static void loadStringValue(JSON value, T & dest, const char* key)
|
|
{
|
|
auto text = json_string_value(json_object_get(value, key));
|
|
if(text)
|
|
strncpy_s(dest, text, _TRUNCATE);
|
|
}
|
|
|
|
void BpCacheLoad(JSON Root)
|
|
{
|
|
EXCLUSIVE_ACQUIRE(LockBreakpoints);
|
|
|
|
// Get a handle to the root object -> breakpoints subtree
|
|
const JSON jsonBreakpoints = json_object_get(Root, "breakpoints");
|
|
|
|
// Return if there was nothing to load
|
|
if(!jsonBreakpoints)
|
|
return;
|
|
|
|
size_t i;
|
|
JSON value;
|
|
json_array_foreach(jsonBreakpoints, i, value)
|
|
{
|
|
BREAKPOINT breakpoint;
|
|
memset(&breakpoint, 0, sizeof(BREAKPOINT));
|
|
|
|
breakpoint.type = (BP_TYPE)json_integer_value(json_object_get(value, "type"));
|
|
if(breakpoint.type == BPNORMAL)
|
|
breakpoint.oldbytes = (unsigned short)(json_hex_value(json_object_get(value, "oldbytes")) & 0xFFFF);
|
|
else if(breakpoint.type == BPMEMORY)
|
|
breakpoint.memsize = (duint)json_hex_value(json_object_get(value, "memsize"));
|
|
breakpoint.addr = (duint)json_hex_value(json_object_get(value, "address"));
|
|
breakpoint.enabled = json_boolean_value(json_object_get(value, "enabled"));
|
|
breakpoint.titantype = (DWORD)json_hex_value(json_object_get(value, "titantype"));
|
|
|
|
// String values
|
|
loadStringValue(value, breakpoint.name, "name");
|
|
if(breakpoint.type != BPEXCEPTION)
|
|
loadStringValue(value, breakpoint.mod, "module");
|
|
loadStringValue(value, breakpoint.breakCondition, "breakCondition");
|
|
loadStringValue(value, breakpoint.logText, "logText");
|
|
loadStringValue(value, breakpoint.logCondition, "logCondition");
|
|
loadStringValue(value, breakpoint.commandText, "commandText");
|
|
loadStringValue(value, breakpoint.commandCondition, "commandCondition");
|
|
|
|
// Fast resume
|
|
breakpoint.fastResume = json_boolean_value(json_object_get(value, "fastResume"));
|
|
breakpoint.silent = json_boolean_value(json_object_get(value, "silent"));
|
|
|
|
// Build the hash map key: MOD_HASH + ADDRESS
|
|
duint key;
|
|
if(breakpoint.type != BPDLL)
|
|
{
|
|
key = ModHashFromName(breakpoint.mod) + breakpoint.addr;
|
|
}
|
|
else
|
|
{
|
|
key = BpGetDLLBpAddr(breakpoint.mod);
|
|
}
|
|
breakpoints[BreakpointKey(breakpoint.type, key)] = breakpoint;
|
|
}
|
|
}
|
|
|
|
void BpClear()
|
|
{
|
|
EXCLUSIVE_ACQUIRE(LockBreakpoints);
|
|
breakpoints.clear();
|
|
}
|