Quote:
Originally Posted by ayrton09_arg
with latest dev sm 1.10 i got this error : <FAILED> file "dhooks.ext.dll": Could not find interface: IBinTools
|
Quote:
Originally Posted by xkonsole
Same.
Though this
Works on latest available dev build.
|
Quote:
Originally Posted by Spirit_12
This might be an odd question but how much speed difference is there between coding a function in extension or hooking it via DHooks Detour? I'm aware its not really noticeable in game, but what are the benefits of going extension over Dhooks?
|
In short, DHooks works only for virtual functions and detouring is a more general approach where you can hook any type of function
It's not really noticeable as you guess (depending on what you're doing), I'm not an expert at detouring functions but the idea behind it is to copy instructions of the original function somewhere else in case you want to callback it and you place a jmp inside the original function so it calls your new function.
The main difference with virtual table hooking and detour hooking is that you actually replace the virtual function inside the virtual table by your own "new virtual function" and since the address to call in assembly is computed "dynamically", you don't need to do it like detours in case you want to callback the original function. Hope it helps to understand how it works.
Another method that can work greatly for detours is to search for all the callers for the function and replace the original call by your own. But it can varies depending on compiler options (like relative jump, call instructions, or even ret instruction and so on)
Here an older approach I've tried of how it would work:
Code:
class CHookCall
{
public:
CHookCall( Pointer pFunction , Pointer pNewFunction )
: m_FoundFunctionsRVACall( 0 ) , m_pFunction( nullptr ) , m_pNewFunction( nullptr ) , m_pOldRelativeRVA( nullptr )
{
m_pFunction = pFunction;
m_pNewFunction = pNewFunction;
}
~CHookCall()
{
UnHook();
m_FoundFunctionsRVACall.clear();
m_pFunction = nullptr;
m_pNewFunction = nullptr;
m_pOldRelativeRVA = nullptr;
}
auto Hook() -> bool
{
auto pDosHeader = ( PIMAGE_DOS_HEADER ) FindModule();
if ( !IsPointer( pDosHeader ) )
return false;
auto pNtHeaders = reinterpret_cast< pNTHeaders >( ( reinterpret_cast< uintptr_t >( pDosHeader ) + pDosHeader->e_lfanew ) );
auto FunctionRVA = reinterpret_cast< uintptr_t >( m_pFunction ) - reinterpret_cast< uintptr_t >( pDosHeader );
auto ImageSize = pNtHeaders->OptionalHeader.SizeOfImage;
auto TempModule = Alloc<uintptr_t>( ImageSize );
// We copy the module so we don't need to change all the protections on it.
memcpy( ( Pointer ) TempModule , pDosHeader , ImageSize );
for ( uintptr_t Slot = 0; Slot != ImageSize; Slot++ )
{
auto Address = TempModule + Slot;
BYTE ByteRead = Read<BYTE>( Address );
if ( ByteRead == 0xE8
|| ByteRead == 0xE9 )
{
// Let's check if it's what we wanted.
// call 0x...
// call RelativeAddress ( FunctionToCall - ( FunctionFromBeingCalled + sizeof( Pointer ) ) )
// or
// jmp 0x...
// jmp RelativeAddress ( FunctionToCall - ( FunctionFromBeingCalled + sizeof( Pointer ) ) )
auto RelativeAddress = Read<uintptr_t>( Address + 1 );
// + 1 byte for call opcode, and add a size of a pointer
// because the CPU call our function once it has finished to read the address.
auto CalledFrom = Address + 1 + sizeof( Pointer );
auto CalledFromRVA = CalledFrom - TempModule;
auto CalledFunctionRVA = RelativeAddress + CalledFromRVA;
// Is That our function ?
if ( CalledFunctionRVA == FunctionRVA )
{
// Yup.
m_FoundFunctionsRVACall.push_back( CalledFromRVA - sizeof( Pointer ) );
}
}
}
for ( auto && FunctionRVACall : m_FoundFunctionsRVACall )
{
auto CallerAddress = reinterpret_cast< uintptr_t >( pDosHeader ) + FunctionRVACall;
// We want to access it.
DWORD dwOldFlag;
if ( VirtualProtect( reinterpret_cast< Pointer >( CallerAddress ) , sizeof( CallerAddress ) , PAGE_EXECUTE_READWRITE , &dwOldFlag ) )
{
uintptr_t *pWriteAddress = Write<uintptr_t>( CallerAddress );
m_pOldRelativeRVA = reinterpret_cast< Pointer >( *pWriteAddress );
// Add a size of a pointer because the CPU call our function once it has finished to read the address.
*pWriteAddress = reinterpret_cast< uintptr_t >( m_pNewFunction ) - ( CallerAddress + sizeof( Pointer ) );
if ( !VirtualProtect( reinterpret_cast< Pointer >( CallerAddress ) , sizeof( CallerAddress ) , dwOldFlag , &dwOldFlag ) )
{
return false;
}
}
}
FreeS( TempModule );
PrintConsole( FOREGROUND_GREEN , TEXT( "Found %i references for 0x%p\n" ) , m_FoundFunctionsRVACall.size() , m_pFunction );
return m_FoundFunctionsRVACall.size() != 0;
}
auto FindModule() -> Pointer
{
Pointer pProcess = GetCurrentProcess();
auto SnapShot = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE , GetProcessId( pProcess ) );
MODULEENTRY32 ModuleEntry;
auto FirstModule = Module32First( SnapShot , &ModuleEntry );
if ( IsPointer( FirstModule ) )
{
do
{
auto ModuleBase = reinterpret_cast< uintptr_t >( ModuleEntry.hModule );
auto Function = reinterpret_cast< uintptr_t >( m_pFunction );
if ( ( Function >= ModuleBase )
&& ( Function < ( ModuleBase + ModuleEntry.modBaseSize ) ) )
{
CloseHandle( SnapShot );
return ModuleEntry.hModule;
}
} while ( Module32Next( SnapShot , &ModuleEntry ) );
}
CloseHandle( SnapShot );
return nullptr;
}
auto UnHook() -> bool
{
auto pDosHeader = ( PIMAGE_DOS_HEADER ) FindModule();
if ( !IsPointer( pDosHeader ) )
return false;
for ( auto && FunctionRVACall : m_FoundFunctionsRVACall )
{
auto CallerAddress = reinterpret_cast< uintptr_t >( pDosHeader ) + FunctionRVACall;
DWORD dwOldFlag;
if ( VirtualProtect( reinterpret_cast< Pointer >( CallerAddress ) , sizeof( CallerAddress ) , PAGE_EXECUTE_READWRITE , &dwOldFlag ) )
{
uintptr_t *pWriteAddress = Write<uintptr_t>( CallerAddress );
// Push back the old function.
*pWriteAddress = reinterpret_cast< uintptr_t >( m_pOldRelativeRVA );
if ( !VirtualProtect( reinterpret_cast< Pointer >( CallerAddress ) , sizeof( CallerAddress ) , dwOldFlag , &dwOldFlag ) )
{
return false;
}
}
}
m_FoundFunctionsRVACall.clear();
return true;
}
FORCEINLINE auto GetFunction() -> Pointer
{
return m_pFunction;
}
FORCEINLINE auto GetNewFunction() -> Pointer
{
return m_pNewFunction;
}
template< typename RetType = void , typename ... vArgs >
constexpr FORCEINLINE auto CallFunction( vArgs ... pArgs )->RetType
{
return ( ( RetType( __cdecl* )( vArgs ... ) ) m_pFunction )( pArgs ... );
}
private:
std::vector<uintptr_t> m_FoundFunctionsRVACall;
Pointer m_pOldRelativeRVA , m_pNewFunction , m_pFunction;
};
But basically you would need a decompiler/disass to do the work properly