blob: 7532ede59a880db7af20723cdf2eeefd08ccbb2c [file] [log] [blame]
#ifndef __STUB_H__
#define __STUB_H__
// Copyright (c) 2019 coolxv
//
// Distributed under the MIT Software License
// See accompanying file LICENSE.txt or copy at
// https://opensource.org/licenses/MIT
#include "qd_asan_interface.h"
#ifdef _WIN32
//windows
#include <windows.h>
#include <processthreadsapi.h>
#else
//linux
#include <unistd.h>
#include <sys/mman.h>
#endif
//c
#include <cstddef>
#include <cstring>
//c++
#include <map>
#define ADDR(CLASS_NAME,MEMBER_NAME) (&CLASS_NAME::MEMBER_NAME)
/**********************************************************
replace function
**********************************************************/
#ifdef _WIN32
#define CACHEFLUSH(addr, size) FlushInstructionCache(GetCurrentProcess(), addr, size)
#else
#define CACHEFLUSH(addr, size) __builtin___clear_cache(addr, addr + size)
#endif
#if defined(__aarch64__) || defined(_M_ARM64)
#define CODESIZE 16U
#define CODESIZE_MIN 16U
#define CODESIZE_MAX CODESIZE
// ldr x9, +8
// br x9
// addr
#define REPLACE_FAR(t, fn, fn_stub)\
((uint32_t*)fn)[0] = 0x58000040 | 9;\
((uint32_t*)fn)[1] = 0xd61f0120 | (9 << 5);\
*(long long *)(fn + 8) = (long long )fn_stub;\
CACHEFLUSH((char *)fn, CODESIZE);
#define REPLACE_NEAR(t, fn, fn_stub) REPLACE_FAR(t, fn, fn_stub)
#elif defined(__arm__) || defined(_M_ARM)
#define CODESIZE 8U
#define CODESIZE_MIN 8U
#define CODESIZE_MAX CODESIZE
// ldr pc, [pc, #-4]
#define REPLACE_FAR(t, fn, fn_stub)\
((uint32_t*)fn)[0] = 0xe51ff004;\
((uint32_t*)fn)[1] = (uint32_t)fn_stub;\
CACHEFLUSH((char *)fn, CODESIZE);
#define REPLACE_NEAR(t, fn, fn_stub) REPLACE_FAR(t, fn, fn_stub)
#elif defined(__mips64)
#define CODESIZE 80U
#define CODESIZE_MIN 80U
#define CODESIZE_MAX CODESIZE
//MIPS has no PC pointer, so you need to manually enter and exit the stack
//120000ce0: 67bdffe0 daddiu sp, sp, -32 //enter the stack
//120000ce4: ffbf0018 sd ra, 24(sp)
//120000ce8: ffbe0010 sd s8, 16(sp)
//120000cec: ffbc0008 sd gp, 8(sp)
//120000cf0: 03a0f025 move s8, sp
//120000d2c: 03c0e825 move sp, s8 //exit the stack
//120000d30: dfbf0018 ld ra, 24(sp)
//120000d34: dfbe0010 ld s8, 16(sp)
//120000d38: dfbc0008 ld gp, 8(sp)
//120000d3c: 67bd0020 daddiu sp, sp, 32
//120000d40: 03e00008 jr ra
#define REPLACE_FAR(t, fn, fn_stub)\
((uint32_t *)fn)[0] = 0x67bdffe0;\
((uint32_t *)fn)[1] = 0xffbf0018;\
((uint32_t *)fn)[2] = 0xffbe0010;\
((uint32_t *)fn)[3] = 0xffbc0008;\
((uint32_t *)fn)[4] = 0x03a0f025;\
*(uint16_t *)(fn + 20) = (long long)fn_stub >> 32;\
*(fn + 22) = 0x19;\
*(fn + 23) = 0x24;\
((uint32_t *)fn)[6] = 0x0019cc38;\
*(uint16_t *)(fn + 28) = (long long)fn_stub >> 16;\
*(fn + 30) = 0x39;\
*(fn + 31) = 0x37;\
((uint32_t *)fn)[8] = 0x0019cc38;\
*(uint16_t *)(fn + 36) = (long long)fn_stub;\
*(fn + 38) = 0x39;\
*(fn + 39) = 0x37;\
((uint32_t *)fn)[10] = 0x0320f809;\
((uint32_t *)fn)[11] = 0x00000000;\
((uint32_t *)fn)[12] = 0x00000000;\
((uint32_t *)fn)[13] = 0x03c0e825;\
((uint32_t *)fn)[14] = 0xdfbf0018;\
((uint32_t *)fn)[15] = 0xdfbe0010;\
((uint32_t *)fn)[16] = 0xdfbc0008;\
((uint32_t *)fn)[17] = 0x67bd0020;\
((uint32_t *)fn)[18] = 0x03e00008;\
((uint32_t *)fn)[19] = 0x00000000;\
CACHEFLUSH((char *)fn, CODESIZE);
#define REPLACE_NEAR(t, fn, fn_stub) REPLACE_FAR(t, fn, fn_stub)
#elif defined(__thumb__) || defined(_M_THUMB)
#error "Thumb is not supported"
#else //__i386__ _x86_64__ _M_IX86 _M_X64
#define CODESIZE 13U
#define CODESIZE_MIN 5U
#define CODESIZE_MAX CODESIZE
//13 byte(jmp m16:64)
//movabs $0x102030405060708,%r11
//jmpq *%r11
#define REPLACE_FAR(t, fn, fn_stub) \
do{\
*fn = 0x49;\
*(fn + 1) = 0xbb;\
memcpy((int *)(fn + 2), &fn_stub, sizeof(long long));\
*(fn + 10) = 0x41;\
*(fn + 11) = 0xff;\
*(fn + 12) = 0xe3;\
}while(false)
//CACHEFLUSH((char *)fn, CODESIZE);
//5 byte(jmp rel32)
#define REPLACE_NEAR(t, fn, fn_stub)\
do{\
const int addr = fn_stub - fn - CODESIZE_MIN;\
*fn = 0xE9;\
memcpy((int *)(fn + 1), &addr, sizeof(int));\
}while(false)
//CACHEFLUSH((char *)fn, CODESIZE);
#endif
struct func_stub
{
unsigned char *fn;
unsigned char code_buf[CODESIZE];
bool far_jmp;
};
class Stub
{
public:
Stub()
{
#ifdef _WIN32
SYSTEM_INFO sys_info;
GetSystemInfo(&sys_info);
m_pagesize = sys_info.dwPageSize;
#else
m_pagesize = sysconf(_SC_PAGE_SIZE);
#endif
if (m_pagesize < 0)
{
m_pagesize = 4096;
}
}
~Stub()
{
std::map<unsigned char*,func_stub*>::iterator iter;
struct func_stub *pstub;
for(iter=m_result.begin(); iter != m_result.end(); iter++)
{
pstub = iter->second;
#ifdef _WIN32
DWORD lpflOldProtect;
if(0 != VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READWRITE, &lpflOldProtect))
#else
if (0 == mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_WRITE | PROT_EXEC))
#endif
{
if(pstub->far_jmp)
{
std::memcpy(pstub->fn, pstub->code_buf, CODESIZE_MAX);
}
else
{
std::memcpy(pstub->fn, pstub->code_buf, CODESIZE_MIN);
}
#if defined(__aarch64__) || defined(_M_ARM64)
CACHEFLUSH((char *)pstub->fn, CODESIZE);
#elif defined(__arm__) || defined(_M_ARM)
CACHEFLUSH((char *)pstub->fn, CODESIZE);
#elif defined(__mips64)
CACHEFLUSH((char *)pstub->fn, CODESIZE);
#else //__i386__ _x86_64__ _M_IX86 _M_X64
//CACHEFLUSH((char *)pstub->fn, CODESIZE);
#endif
#ifdef _WIN32
VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READ, &lpflOldProtect);
#else
mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_EXEC);
#endif
}
iter->second = NULL;
delete pstub;
}
return;
}
template<typename T,typename S>
ATTRIBUTE_NO_SANITIZE_THREAD
void set(T addr, S addr_stub)
{
unsigned char * fn;
unsigned char * fn_stub;
fn = addrof(addr);
fn_stub = addrof(addr_stub);
struct func_stub *pstub;
pstub = new func_stub;
//start
reset(fn); //
pstub->fn = fn;
if(distanceof(fn, fn_stub))
{
pstub->far_jmp = true;
std::memcpy(pstub->code_buf, fn, CODESIZE_MAX);
}
else
{
pstub->far_jmp = false;
std::memcpy(pstub->code_buf, fn, CODESIZE_MIN);
}
#ifdef _WIN32
DWORD lpflOldProtect;
if(0 == VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READWRITE, &lpflOldProtect))
#else
if (-1 == mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_WRITE | PROT_EXEC))
#endif
{
throw("stub set memory protect to w+r+x failed");
}
if(pstub->far_jmp)
{
REPLACE_FAR(this, fn, fn_stub);
}
else
{
REPLACE_NEAR(this, fn, fn_stub);
}
#ifdef _WIN32
if(0 == VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READ, &lpflOldProtect))
#else
if (-1 == mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_EXEC))
#endif
{
throw("stub set memory protect to r+x failed");
}
m_result.insert(std::pair<unsigned char*,func_stub*>(fn,pstub));
return;
}
template<typename T>
void reset(T addr)
{
unsigned char * fn;
fn = addrof(addr);
std::map<unsigned char*,func_stub*>::iterator iter = m_result.find(fn);
if (iter == m_result.end())
{
return;
}
struct func_stub *pstub;
pstub = iter->second;
#ifdef _WIN32
DWORD lpflOldProtect;
if(0 == VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READWRITE, &lpflOldProtect))
#else
if (-1 == mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_WRITE | PROT_EXEC))
#endif
{
throw("stub reset memory protect to w+r+x failed");
}
if(pstub->far_jmp)
{
std::memcpy(pstub->fn, pstub->code_buf, CODESIZE_MAX);
}
else
{
std::memcpy(pstub->fn, pstub->code_buf, CODESIZE_MIN);
}
#if defined(__aarch64__) || defined(_M_ARM64)
CACHEFLUSH((char *)pstub->fn, CODESIZE);
#elif defined(__arm__) || defined(_M_ARM)
CACHEFLUSH((char *)pstub->fn, CODESIZE);
#elif defined(__mips64)
CACHEFLUSH((char *)pstub->fn, CODESIZE);
#else //__i386__ _x86_64__ _M_IX86 _M_X64
//CACHEFLUSH((char *)pstub->fn, CODESIZE);
#endif
#ifdef _WIN32
if(0 == VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READ, &lpflOldProtect))
#else
if (-1 == mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_EXEC))
#endif
{
throw("stub reset memory protect to r+x failed");
}
m_result.erase(iter);
delete pstub;
return;
}
private:
char *pageof(unsigned char* addr)
{
#ifdef _WIN32
return (char *)((unsigned long long)addr & ~(m_pagesize - 1));
#else
return (char *)((unsigned long)addr & ~(m_pagesize - 1));
#endif
}
template<typename T>
unsigned char* addrof(T addr)
{
union
{
T _s;
unsigned char* _d;
}ut;
ut._s = addr;
return ut._d;
}
bool distanceof(unsigned char* addr, unsigned char* addr_stub)
{
std::ptrdiff_t diff = addr_stub >= addr ? addr_stub - addr : addr - addr_stub;
if((sizeof(addr) > 4) && (((diff >> 31) - 1) > 0))
{
return true;
}
return false;
}
private:
#ifdef _WIN32
//LLP64
long long m_pagesize;
#else
//LP64
long m_pagesize;
#endif
std::map<unsigned char*, func_stub*> m_result;
};
#endif