Skip to content

Commit

Permalink
fix DarthTon#85: issues with multiple remote hwbp breakpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
DarthTon committed Jun 16, 2016
1 parent 7f18469 commit f8482df
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 63 deletions.
2 changes: 1 addition & 1 deletion src/BlackBone/ManualMap/MMap.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ class CustomArgs_t
template<typename... Args>
explicit CustomArgs_t( std::tuple<Args...>&& cTuple )
{
copyTuple( cTuple, _buffer );
tuple_detail::copyTuple( cTuple, _buffer );
}

/// <summary>Gets the size.</summary>
Expand Down
37 changes: 14 additions & 23 deletions src/BlackBone/Misc/Utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,31 +157,22 @@ class CSLock

namespace tuple_detail
{
template<int... Is>
struct seq { };
template<typename T, typename F, size_t... Is>
void visit_each( T&& t, F f, std::index_sequence<Is...> ) { auto l = { (f( std::get<Is>( t ) ), 0)... }; }

template<int N, int... Is>
struct gen_seq : gen_seq<N - 1, N - 1, Is...> { };

template<int... Is>
struct gen_seq<0, Is...> : seq<Is...> { };

template<typename T, typename F, int... Is>
void visit_each( T&& t, F f, seq<Is...> ) { auto l = { (f( std::get<Is>( t ) ), 0)... }; }
}

template<typename... Ts>
void copyTuple( std::tuple<Ts...> const& t, std::vector<char>& buf )
{
auto f = [&buf]( auto v )
template<typename... Ts>
void copyTuple( std::tuple<Ts...> const& from, std::vector<char>& to )
{
auto ptr = buf.size();
buf.resize( ptr + sizeof( v ) );
memcpy( buf.data() + ptr, &v, sizeof( v ) );
return 0;
};

tuple_detail::visit_each( t, f, tuple_detail::gen_seq<sizeof...(Ts)>() );
auto func = [&to]( auto& v )
{
auto ptr = to.size();
to.resize( ptr + sizeof( v ) );
memcpy( to.data() + ptr, &v, sizeof( v ) );
return 0;
};

visit_each( from, func, std::index_sequence_for<Ts...>() );
}
}

}
118 changes: 79 additions & 39 deletions src/BlackBone/Process/RPC/RemoteHook.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ NTSTATUS RemoteHook::EnsureDebug()

// Create debug event thread
_hEventThd = CreateThread( NULL, 0, &RemoteHook::EventThreadWrap, this, 0, NULL );

return (_hEventThd != NULL) ? STATUS_SUCCESS : LastNtStatus();
}

Expand Down Expand Up @@ -110,6 +109,7 @@ NTSTATUS RemoteHook::ApplyP( eHookType type, uint64_t ptr, fnCallback newFn, con
data.threadID = (pThread != nullptr) ? pThread->id() : 0;

// Set HWBP
CSLock lck( _lock );
if(type == hwbp)
{
// Set for single thread
Expand Down Expand Up @@ -155,6 +155,7 @@ NTSTATUS RemoteHook::ApplyP( eHookType type, uint64_t ptr, fnCallback newFn, con
/// <returns>Status code</returns>
NTSTATUS RemoteHook::AddReturnHookP( uint64_t ptr, fnCallback newFn, const void* pClass /*= nullptr */ )
{
CSLock lck( _lock );
if(_hooks.count(ptr))
{
auto& hook = _hooks[ptr];
Expand Down Expand Up @@ -251,9 +252,9 @@ DWORD RemoteHook::EventThread()
//
// Reset debug flag in PEB
//
_memory.Write( _core.peb() + FIELD_OFFSET( _PEB64, BeingDebugged ), uint8_t( 0 ) );
_memory.Write( _core.peb( (_PEB64*)nullptr ) + FIELD_OFFSET( _PEB64, BeingDebugged ), uint8_t( 0 ) );
if (!_x64Target)
_memory.Write( _core.peb() + FIELD_OFFSET( _PEB32, BeingDebugged ), uint8_t( 0 ) );
_memory.Write( _core.peb( (_PEB32*)nullptr ) + FIELD_OFFSET( _PEB32, BeingDebugged ), uint8_t( 0 ) );

_active = true;

Expand All @@ -266,6 +267,7 @@ DWORD RemoteHook::EventThread()
if (!WaitForDebugEvent( &DebugEv, 100 ))
continue;

_lock.lock();
switch (DebugEv.dwDebugEventCode)
{
case EXCEPTION_DEBUG_EVENT:
Expand All @@ -287,6 +289,7 @@ DWORD RemoteHook::EventThread()
}

ContinueDebugEvent( DebugEv.dwProcessId, DebugEv.dwThreadId, status );
_lock.unlock();
}

// Safely detach debugger
Expand Down Expand Up @@ -428,7 +431,15 @@ DWORD RemoteHook::OnSinglestep( const DEBUG_EVENT& DebugEv )

// Detect hardware breakpoint
DWORD index = 0;
if (_BitScanForward( &index, LODWORD( ctx64.Dr6 ) ) != 0 && index < 4 && (_hooks.count( addr )))
for (; index < 4; index++)
{
if (ctx64.Dr6 & (1ll << index))
if ((use64 && (ctx64.Dr7 & 1ll << (2 * index))) || (!use64 && (ctx32.Dr7 & 1ll << (2 * index))))
if ((use64 && *(&ctx64.Dr0 + index) == addr) || (!use64 && *(&ctx32.Dr0 + index) == addr))
break;
}

if (index < 4)
{
// Get stack frame pointer
std::vector<std::pair<ptr_t, ptr_t>> results;
Expand All @@ -437,25 +448,42 @@ DWORD RemoteHook::OnSinglestep( const DEBUG_EVENT& DebugEv )
RemoteContext context( _memory, thd, ctx64, !results.empty() ? results.back().first : 0, _x64Target, _wordSize );

// Execute user callback
auto hook = _hooks[addr];
if (hook.onExecute.classFn.classPtr && hook.onExecute.classFn.ptr != nullptr)
hook.onExecute.classFn.ptr( hook.onExecute.classFn.classPtr, context );
else if (hook.onExecute.freeFn != nullptr)
hook.onExecute.freeFn( context );

auto andVal = ~(1ll << (2 * index));
use64 ? ctx64.Dr7 &= andVal : ctx32.Dr7 &= andVal; // Reset breakpoint
use64 ? ctx64.Dr6 = 0 : ctx32.Dr6 = 0; // Reset flags
use64 ? ctx64.EFlags |= 0x100 : ctx32.EFlags |= 100; // Single step
use64 ? thd.SetContext( ctx64, true ) : thd.SetContext( ctx32, true );
if(_hooks.count( addr ))
{
auto hook = _hooks[addr];
if (hook.onExecute.classFn.classPtr && hook.onExecute.classFn.ptr != nullptr)
hook.onExecute.classFn.ptr( hook.onExecute.classFn.classPtr, context );
else if (hook.onExecute.freeFn != nullptr)
hook.onExecute.freeFn( context );

// Raise exception upon return
if (hook.flags & returnHook)
{
auto newReturn = context.hookReturn();
_retHooks.emplace( std::make_pair( newReturn, addr ) );
}

_repatch[addr] = true;
ctx64.ContextFlags = use64? CONTEXT64_CONTROL : WOW64_CONTEXT_CONTROL;
use64 ? ctx64.EFlags |= 0x100 : ctx32.EFlags |= 100; // Single step
_repatch[addr] = true;
}

// Raise exception upon return
if (hook.flags & returnHook)
auto andVal = ~(1ll << (2 * index));
if (use64)
{
auto newReturn = context.hookReturn();
_retHooks.emplace( std::make_pair( newReturn, addr ) );
ctx64.ContextFlags |= CONTEXT64_DEBUG_REGISTERS;
*(&ctx64.Dr0 + index) = 0;
ctx64.Dr7 &= andVal;
ctx64.Dr6 = 0;
thd.SetContext( ctx64, true );
}
else
{
ctx32.ContextFlags |= WOW64_CONTEXT_DEBUG_REGISTERS;
*(&ctx32.Dr0 + index) = 0;
ctx32.Dr7 &= andVal;
ctx32.Dr6 = 0;
thd.SetContext( ctx32, true );
}

return DBG_CONTINUE;
Expand All @@ -464,7 +492,7 @@ DWORD RemoteHook::OnSinglestep( const DEBUG_EVENT& DebugEv )
// Restore pending hooks
for(auto place : _repatch)
{
if (place.second == true)
if (place.second == true && _hooks.count( place.first ))
{
auto& hook = _hooks[place.first];

Expand All @@ -480,7 +508,6 @@ DWORD RemoteHook::OnSinglestep( const DEBUG_EVENT& DebugEv )
_memory.Protect( addr, sizeof( hook.oldByte ), flOld, nullptr );
}


place.second = false;
}
}
Expand Down Expand Up @@ -528,38 +555,44 @@ DWORD RemoteHook::OnAccessViolation( const DEBUG_EVENT& DebugEv )
addr = results.back().second;

// Test if this is a return hook
if(_retHooks.count(addr))
if (_retHooks.count( addr ))
{
// Execute user callback
auto hook = _hooks[_retHooks[addr]];

if(hook.flags & returnHook)
if (hook.flags & returnHook)
{
if (hook.onReturn.classFn.classPtr && hook.onReturn.classFn.ptr != nullptr)
hook.onReturn.classFn.ptr( hook.onExecute.classFn.classPtr, context );
else if (hook.onReturn.freeFn != nullptr)
hook.onReturn.freeFn( context );
}

// Under AMD64there is no need to update IP, because exception is thrown before actual return.
// Return address still must be fixed though.
if(_x64Target)
_retHooks.erase( addr );
}

// Under AMD64there is no need to update IP, because exception is thrown before actual return.
// Return address still must be fixed though.
if(_x64Target)
{
auto retAddr = context.returnAddress();
if (retAddr & 0x8000000000000000)
{
context.unhookReturn();
}
else
return (DWORD)DBG_CONTINUE;
}
}
else
{
auto retAddr = addr;
if(retAddr & 0x80000000)
{
auto retAddr = addr;
RESET_BIT(retAddr, (_wordSize * 8 - 1));

RESET_BIT( retAddr, (_wordSize * 8 - 1) );
ctx64.Rip = retAddr;
}

thd.SetContext( ctx64, true );
_retHooks.erase( addr );

return (DWORD)DBG_CONTINUE;
}
thd.SetContext( ctx64, true );
return (DWORD)DBG_CONTINUE;
}
}

return (DWORD)DBG_EXCEPTION_NOT_HANDLED;
}
Expand Down Expand Up @@ -643,10 +676,17 @@ DWORD RemoteHook::StackBacktrace( ptr_t ip, ptr_t sp, Thread& thd, std::vector<s
/// </summary>
void RemoteHook::reset()
{
_lock.lock();
for (auto& hook : _hooks)
Restore( hook.second, hook.first );

_hooks.clear();
_repatch.clear();

_lock.unlock();

// Wait for last events to finish
Sleep( 100 );
EndDebug();
}

Expand Down
2 changes: 2 additions & 0 deletions src/BlackBone/Process/RPC/RemoteHook.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include "../../Include/Winheaders.h"
#include "../../Include/Macro.h"
#include "../../Misc/Utils.h"
#include "../Threads/Threads.h"

#include <map>
Expand Down Expand Up @@ -239,6 +240,7 @@ class RemoteHook
private:
class ProcessMemory& _memory;
class ProcessCore& _core;
CriticalSection _lock; // Hook lock

DWORD _debugPID = 0; // PID of process being debugged
HANDLE _hEventThd = NULL; // Debug Event thread
Expand Down
8 changes: 8 additions & 0 deletions src/BlackBone/Process/Threads/Thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,14 @@ int Thread::AddHWBP( ptr_t addr, HWBPType type, HWBPLength length )
if (!res)
return -1;

// Check if HWBP is already present
for (int i = 0; i < 4; i++)
{
if ( (use64 && *(&context64.Dr0 + i) == addr && context64.Dr7 & (1ll << 2 * i)) ||
(!use64 && *(&context32.Dr0 + i) == addr && context32.Dr7 & (1ll << 2 * i)))
return i;
}

// Get free DR
int freeIdx = pDR7->getFreeIndex();

Expand Down

0 comments on commit f8482df

Please sign in to comment.