// Header files
#include <udis86.h>
#include <sys/mman.h>
#include <unistd.h>
#include <string.h>
// Class header
#include "detour.h"
// Page execution constants (way more good-looking in the code)
const int PageExecuteReadWrite = (PROT_READ | PROT_WRITE | PROT_EXEC);
const int PageExecuteRead = (PROT_READ | PROT_EXEC);
// Static member variables
byte Detour::m_jmpOperator[] = "\xFF\x25\xEF\xBE\xAD\xDE";
size_t Detour::m_jmpSize = sizeof(m_jmpOperator) - 1;
uint Detour::m_jmpOffset = 2;
Detour::Detour(void * targetFunc, void * detourFunc)
{
// Reset/update all members
m_targetFunc = (byte*) targetFunc;
m_detourFunc = (byte*) detourFunc;
m_trampoline = null;
// Calculate required target size
m_preludeSize = SizeRequired();
}
Detour::~Detour(void)
{
// We only need to unpatch
Unpatch();
}
bool Detour::Unpatch(void)
{
// Check memory (both protection and if it's allocated)
if(m_trampoline == null || !SetAddressProtection(m_targetFunc, PageExecuteReadWrite))
return false;
// Copy the old patch back again
memcpy(m_targetFunc, m_trampoline, m_preludeSize);
// Reset address protection (we don't care if it succeeds or not)
SetAddressProtection(m_targetFunc, PageExecuteRead);
// Free memory
delete [] m_trampoline;
m_trampoline = null;
// Return success
return true;
}
bool Detour::Patch(void)
{
// Our buffer to use
byte * buffer = null;
// Calculate the buffers required size
size_t bufferSize = m_preludeSize + m_jmpSize + 2 * sizeof(size_t);
// Setup our trampolines buffer
m_trampoline = buffer = new byte[bufferSize];
// Setup our pointers for the addresses
size_t * targetPtr = (size_t*)(buffer + m_preludeSize + m_jmpSize);
size_t * detourPtr = (size_t*)(buffer + m_preludeSize + m_jmpSize + sizeof(size_t));
*targetPtr = (size_t) m_targetFunc + m_preludeSize;
*detourPtr = (size_t) m_detourFunc;
// Copy the target prelude to our trampoline
memcpy(m_trampoline, m_targetFunc, m_preludeSize);
// Write the jump operator to the trampoline & change address
memcpy(buffer + m_preludeSize, m_jmpOperator, m_jmpSize);
memcpy(buffer + m_preludeSize + m_jmpOffset, &targetPtr, sizeof(size_t));
// Create a buffer for overwriting the target prelude, this will allow us to copy
// instructions in one pass to limit the time the function is effectively broken
byte jmpBuffer[m_jmpSize];
buffer = jmpBuffer;
memcpy(buffer, m_jmpOperator, m_jmpSize);
memcpy(buffer + m_jmpOffset, &detourPtr, sizeof(size_t));
// Set execute rights to the page that contains our trampoline
if(SetAddressProtection(m_trampoline, PageExecuteReadWrite))
{
// And update address protection at the target function address
if(SetAddressProtection(m_targetFunc, PageExecuteReadWrite))
{
// Write 'jmp' to the target function
memcpy(m_targetFunc, buffer, m_jmpSize);
// Update address protection (we don't care about the result)
SetAddressProtection(m_targetFunc, PageExecuteRead);
// Return success
return true;
}
}
// Free the allocated memory
delete [] m_trampoline;
m_trampoline = null;
// Return failure
return false;
}
size_t Detour::SizeRequired(void)
{
// Temporary variable
size_t required = 0;
ud_t disas;
ud_init(&disas);
// Setup the disassembly buffer
ud_set_input_buffer(&disas, m_targetFunc, 20);
ud_set_mode(&disas, 32);
// Loop until there is enough memory for a jump
while((required += ud_disassemble(&disas)) < m_jmpSize);
// Return the amount
return required;
}
int Detour::SetAddressProtection(void * addr, int protection)
{
// Constant holding the page size value
const size_t pageSize = sysconf(_SC_PAGE_SIZE);
// Calculate relative page offset
size_t temp = (size_t) addr;
temp -= temp % pageSize;
// Update address
addr = (void*) temp;
// Update memory area protection
return !mprotect(addr, pageSize, protection);
}