单元测试cpp:Stub
文章目錄
- 參考
- 詳解
- inline hook
- stub 類
- 方法: set
- 析構函數
- addrof
- distanceof 函數(64bit)
- 常量
- set_mprotect
- get_page_count
- read_mprotection
參考
https://github.com/coolxv/cpp-stub
//https://stackoverflow.com/questions/2152958/reading-memory-protection
//https://github.com/18446744073709551615/reDroid/blob/master/jni/reDroid/re_mprot.c
詳解
inline hook
這里的inline hook,從實現上看,就是對內存加載的函數地址內內容做實時修改,來實現,樁函數調用。
stub 類
方法: set
template<typename T,typename S> ,T 目標函數,要被替換的函數地址,S stub函數,源函數地址,用來做替換的函數地址 void set(T addr, S addr_stub) {這個方法是一個模板方法,可以根據傳進來的函數定義,讓編譯器生成各式各樣的set函數。
主要實現:
創建(new)新的func_stub 對象,然后將這個對象放到map變量m_result 中;以目標函數地址作為key;
在64bit環境下,設置 裝函數對象far_jmp開關,將源函數地址內的特定長度的內容放到樁函數對象的成員:code_buf
int old_protect = set_mprotect(pstub);
然后將原函數的固定長度的指令,備份,然后將stub函數指令跳轉,覆蓋原函數
if (pstub->far_jmp){//13 byte*(unsigned char*)fn = 0x49;*((unsigned char*)fn + 1) = 0xbb; movabs, 將stub函數地址放到eax,*(unsigned long long *)((unsigned char *)fn + 2) = (unsigned long long)fn_stub;*(unsigned char *)((unsigned char *)fn + 10) = 0x41; push rax 怎么實現的 跳轉? 是通過,push 將stub函數的地址放到棧里*(unsigned char *)((unsigned char *)fn + 11) = 0x53;*(unsigned char *)((unsigned char *)fn + 12) = 0xc3; ret ,ret時,將 棧中的stub函數pop 出來,繼續處理stub函數。}else{//5 byte*(unsigned char *)fn = (unsigned char)0xE9; // asm jmp*(unsigned int *)((unsigned char *)fn + 1) = (unsigned char *)fn_stub - (unsigned char *)fn - CODESIZE_MIN;}restore_mprotect
將內存保護模式恢復。
析構函數
會將之前放置map里的所有stub過的函數恢復到之前的狀態。
addrof
將 目標函數類型地址,轉換成void,其實也不叫轉換,就是一值兩用,其他函數可以使用void 類型。
template<typename T>void* addrof(T src){union{T _s;void* _d;}ut;ut._s = src;return ut._d;}distanceof 函數(64bit)
的作用,是判斷兩個函數是否在同一個范圍
以地址向右移32位,就是判斷高32位是否同時大于0,如果同時為0,或者同時大于0,就是在同一范圍,如果兩個值不是同時的話,就說明兩個函數的地址差別比較大屬于遠距離函數。比較繞。
常量
#ifdef x86_64
#define CODESIZE 13U // 長指令,長度
#define CODESIZE_MIN 5U //短指令
#define CODESIZE_MAX CODESIZE
#else
set_mprotect
設置內存的寫權限。首先看一下函數地址是否跨兩個頁面,怎么判斷是否跨兩個頁面,通過函數get_page_count 來計算。
int set_mprotect(const struct func_stub *pstub){int page_count = get_page_count(pstub);if (page_count != 1) 跨頁,還不能操作! 那就意味著,沒有可能跨頁,為什么?編譯器做了對齊了?{std::string what_err("stub cross page!");throw std::logic_error(what_err);}unsigned int mprot = read_mprotection(pstub->fn); //首先讀取之前的權限標志位int prot = 0;if ((mprot & MPROT_W) == 0) ///如果沒有寫權限{prot = mprot_std(mprot);if (-1 == mprotect(pageof(pstub->fn), m_pagesize * page_count, prot | PROT_WRITE)) /// 設置內存寫選項在單個頁。{std::string what_err("stub set mprotect to w+r+x faild");throw std::logic_error(what_err);}}return prot;}get_page_count
根據要修改的內容,判斷開始修改的首地址和未地址,是否跨頁;
char *code_end = code_start + code_size - 1;void *page1 = pageof(code_start); 查看 起始位置的頁標號void *page2 = pageof(code_end); 查看 末尾位置的頁標號int count = (page1 == page2 ? 1 : 2); 看看兩個標號是否相同,不同就是兩個頁,其他是一個頁void *pageof(const void* p){return (void *)((unsigned long)p & ~(m_pagesize - 1)); }read_mprotection
根據運行時文件:/proc/self/maps,判斷地址所在的位置,然后查看相應的權限;
總結
以上是生活随笔為你收集整理的单元测试cpp:Stub的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 我需要HCNE模拟考试系统
- 下一篇: ubuntu20.04,安装向日葵客户端