| #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 |