IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发测试 -> 单元测试cpp:Stub -> 正文阅读

[开发测试]单元测试cpp:Stub

参考

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,判断地址所在的位置,然后查看相应的权限;

  开发测试 最新文章
pytest系列——allure之生成测试报告(Wind
某大厂软件测试岗一面笔试题+二面问答题面试
iperf 学习笔记
关于Python中使用selenium八大定位方法
【软件测试】为什么提升不了?8年测试总结再
软件测试复习
PHP笔记-Smarty模板引擎的使用
C++Test使用入门
【Java】单元测试
Net core 3.x 获取客户端地址
上一篇文章      下一篇文章      查看所有文章
加:2021-09-22 14:58:32  更:2021-09-22 14:58:52 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/21 0:06:39-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码