1
0
Fork 0
x64dbg/x64_dbg_dbg/breakpoint.cpp

409 lines
11 KiB
C++

/**
@file breakpoint.cpp
@brief Implements the breakpoint class.
*/
#include "breakpoint.h"
#include "debugger.h"
#include "addrinfo.h"
#include "console.h"
#include "memory.h"
#include "threading.h"
#include "module.h"
typedef std::pair<BP_TYPE, uint> BreakpointKey;
std::map<BreakpointKey, BREAKPOINT> breakpoints;
BREAKPOINT* BpInfoFromAddr(BP_TYPE Type, uint Address)
{
//
// NOTE: THIS DOES _NOT_ USE LOCKS
//
auto found = breakpoints.find(BreakpointKey(Type, ModHashFromAddr(Address)));
// Was the module found with this address?
if(found == breakpoints.end())
return nullptr;
return &found->second;
}
int BpGetList(std::vector<BREAKPOINT>* List)
{
// CHECK: Exported function
if(!DbgIsDebugging())
return false;
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;
currentBp.addr += ModBaseFromName(currentBp.mod);
currentBp.active = MemIsValidReadPtr(currentBp.addr);
List->push_back(currentBp);
}
}
return (int)breakpoints.size();
}
bool BpNew(uint Address, bool Enable, bool Singleshot, short OldBytes, BP_TYPE Type, DWORD TitanType, const char* Name)
{
// CHECK: Command function
if(!DbgIsDebugging())
return false;
// Fail if the address is a bad memory region
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));
ModNameFromAddr(Address, bp.mod, true);
strcpy_s(bp.name, Name);
bp.active = true;
bp.addr = Address - ModBaseFromAddr(Address);
bp.enabled = Enable;
bp.oldbytes = OldBytes;
bp.singleshoot = Singleshot;
bp.titantype = TitanType;
bp.type = Type;
// Insert new entry to the global list
EXCLUSIVE_ACQUIRE(LockBreakpoints);
breakpoints.insert(std::make_pair(BreakpointKey(Type, ModHashFromAddr(Address)), bp));
return true;
}
bool BpGet(uint Address, BP_TYPE Type, const char* Name, BREAKPOINT* Bp)
{
// CHECK: Export/Command function
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;
Bp->addr += ModBaseFromAddr(Address);
Bp->active = MemIsValidReadPtr(Bp->addr);
return true;
}
// Do a lookup by breakpoint name
for(auto & i : breakpoints)
{
// Do the names match?
if(strcmp(Name, i.second.name) != 0)
continue;
// Fill out the optional user buffer
if(Bp)
{
*Bp = i.second;
Bp->addr += ModBaseFromAddr(Address);
Bp->active = MemIsValidReadPtr(Bp->addr);
}
// Return true if the name was found at all
return true;
}
return false;
}
bool BpDelete(uint Address, BP_TYPE Type)
{
// CHECK: Command function
if(!DbgIsDebugging())
return false;
// Erase the index from the global list
EXCLUSIVE_ACQUIRE(LockBreakpoints);
return (breakpoints.erase(BreakpointKey(Type, ModHashFromAddr(Address))) > 0);
}
bool BpEnable(uint Address, BP_TYPE Type, bool Enable)
{
// CHECK: Command function
if(!DbgIsDebugging())
return false;
EXCLUSIVE_ACQUIRE(LockBreakpoints);
// Check if the breakpoint exists first
BREAKPOINT* bpInfo = BpInfoFromAddr(Type, Address);
if(!bpInfo)
return false;
bpInfo->enabled = Enable;
return true;
}
bool BpSetName(uint Address, BP_TYPE Type, const char* Name)
{
// CHECK: Future(?); This is not used anywhere
if(!DbgIsDebugging())
return false;
// If a name wasn't supplied, set to nothing
if(!Name)
Name = "";
EXCLUSIVE_ACQUIRE(LockBreakpoints);
// Check if the breakpoint exists first
BREAKPOINT* bpInfo = BpInfoFromAddr(Type, Address);
if(!bpInfo)
return false;
strcpy_s(bpInfo->name, Name);
return true;
}
bool BpSetTitanType(uint Address, BP_TYPE Type, int TitanType)
{
// CHECK: Command function
if(!DbgIsDebugging())
return false;
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 BpEnumAll(BPENUMCALLBACK EnumCallback, const char* Module)
{
if(!DbgIsDebugging())
return false;
SHARED_ACQUIRE(LockBreakpoints);
// Loop each entry, executing the user's callback
bool callbackStatus = true;
auto i = breakpoints.begin();
while(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 && Module[0] != '\0')
{
if(strcmp(j->second.mod, Module) != 0)
continue;
}
BREAKPOINT bpInfo = j->second;
bpInfo.addr += ModBaseFromName(bpInfo.mod);
bpInfo.active = MemIsValidReadPtr(bpInfo.addr);
// 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(LockBreakpoints);
}
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;
}
void BpToBridge(const BREAKPOINT* Bp, BRIDGEBP* BridgeBp)
{
//
// Convert a debugger breakpoint to an open/exported
// bridge breakpoint
//
// TOOD: ASSERT(?) These should never be null
if(!Bp || !BridgeBp)
return;
memset(BridgeBp, 0, sizeof(BRIDGEBP));
strcpy_s(BridgeBp->mod, Bp->mod);
strcpy_s(BridgeBp->name, Bp->name);
BridgeBp->active = Bp->active;
BridgeBp->addr = Bp->addr;
BridgeBp->enabled = Bp->enabled;
BridgeBp->singleshoot = Bp->singleshoot;
switch(Bp->type)
{
case BPNORMAL:
BridgeBp->type = bp_normal;
break;
case BPHARDWARE:
BridgeBp->type = bp_hardware;
break;
case BPMEMORY:
BridgeBp->type = bp_memory;
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(auto & i : breakpoints)
{
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));
// "Normal" breakpoints save the old data
if(breakpoint.type == BPNORMAL)
json_object_set_new(jsonObj, "oldbytes", json_hex(breakpoint.oldbytes));
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));
json_object_set_new(jsonObj, "module", json_string(breakpoint.mod));
json_array_append_new(jsonBreakpoints, jsonObj);
}
if(json_array_size(jsonBreakpoints))
json_object_set(Root, "breakpoints", jsonBreakpoints);
// Notify garbage collector
json_decref(jsonBreakpoints);
}
void BpCacheLoad(JSON Root)
{
EXCLUSIVE_ACQUIRE(LockBreakpoints);
// Remove all existing elements
breakpoints.clear();
// 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));
if(breakpoint.type == BPNORMAL)
breakpoint.oldbytes = (short)json_hex_value(json_object_get(value, "oldbytes"));
breakpoint.type = (BP_TYPE)json_integer_value(json_object_get(value, "type"));
breakpoint.addr = (uint)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"));
// Name
const char* name = json_string_value(json_object_get(value, "name"));
if(name)
strcpy_s(breakpoint.name, name);
// Module
const char* mod = json_string_value(json_object_get(value, "module"));
if(mod && *mod && strlen(mod) < MAX_MODULE_SIZE)
strcpy_s(breakpoint.mod, mod);
// Build the hash map key: MOD_HASH + ADDRESS
const uint key = ModHashFromName(breakpoint.mod) + breakpoint.addr;
breakpoints.insert(std::make_pair(BreakpointKey(breakpoint.type, key), breakpoint));
}
}
void BpClear()
{
EXCLUSIVE_ACQUIRE(LockBreakpoints);
breakpoints.clear();
}