日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

单元测试cpp:Stub

發布時間:2023/12/20 编程问答 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 单元测试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__bool distanceof(void* addr, void* addr_stub){unsigned long long addr_tmp = (unsigned long long)addr;unsigned long long addr_stub_tmp = (unsigned long long)addr_stub;unsigned int int_addr_tmp = (unsigned int)(addr_tmp >> 32);unsigned int int_addr_stub_tmp = (unsigned int)(addr_stub_tmp >> 32);if ((int_addr_tmp > 0 && int_addr_stub_tmp > 0) || (int_addr_tmp == 0 && int_addr_stub_tmp == 0)){return false;}else{return true;}} #endif

常量

#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的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。