使用hotfix技术来HOOK任意位置代码的手艺_c# hotfix hook

假设一个内核函数,test,如下:

void test()
{
    condition = update_something();
    if (condition == 1) {
        M1();
    } else {
        return;
    }
}

现在需要统计在test中进入M1函数的次数,怎么办?

很简单,使用kpatch技术即可,或者,使用kprobe技术,亦可。但这些成熟的技术没有可玩性,它们属于工程的范畴,而不属于手艺。说白了,就是对于手艺人而言,kpatch,kprobe这种技术,没意思。

这里介绍一种有意思的方法,当然,这只是对于工人而不是对于经理来说的。

要点如下:

  • 单独分配一段足够大且可执行的内存p,将test函数整体拷贝过去。
  • 将原始test的开头5个字节替换成 32位相对跳转指令 ,即 0xe9 $rel_offset
  • 在p中找到需要插入统计计数的位置,增加下面的7个字节指令 asm (“incl 0xffffffff81977890” ::: );

0xffffffff81977890这个地址是内核中panic_on_oops变量的地址。

如果真的试着这样做了,发现绝大多数情况下,这是不行的,哪里错了呢?这里有两个问题需要注意:

  1. 如何分配一段足够的内存来承载拷贝过来的test函数的指令?
  2. 处理拷贝后的函数内相对偏移变更。

这两点其实很好理解。

首先,我们要明白,为了修改原始函数的前5个字节为相对跳转,新内存必须和原始函数test之间的间隔在s32类型可以表示的范围以内,也就是前后2G总共4G的范围。这一点,我们以do_fork为例:

0xffffffff81073840 <do_fork>:   nopl   0x0(%rax,%rax,1) [FTRACE NOP]
0xffffffff81073845 <do_fork+5>: push   %rbp

可以发现,只有5个字节的空间。

所以,我们的新内存的位置必须确保在test前后2G的范围内。

显然,kmalloc很难满足这个需求,我们不妨换个思路,直接借用一个模块中的stub函数。但是考虑到模块中的stub函数可能大小并不够,所以我们采用属性定义其按照足够的大小对齐:

void test_stub1(void) __attribute__ ((aligned (1024)));
void test_stub2(void) __attribute__ ((aligned (1024)));
void test_stub1(void)
{
    printk("yes\n");
}
void test_stub2(void)
{
    printk("yes yes\n");
}

这样,test_stub1就有1024个字节可用了,一般的函数,足够了。我们只需要用test来覆盖这个test_stub1就可以了。通常情况下,模块中的函数是在0xffffffff00000000以上的,这个和内核函数处在相对跳转可以够得到的范围内。

此外,当我们在原始函数内部插入了inc指令后,会改变拷贝后的函数的相对偏移,甚至将原始函数指令拷贝到新的地方,也会改变原有的相对偏移,这个不得不察。

所以,必须扫描所有的拷贝后指令,修改其相对跳转偏移:

  • 函数内的相对偏移需改改变7个指令的增量。
  • 函数外的相对偏移在7个指令增量的前提额外需要改变p - test的增量。

采用这个方法插入一些统计计数,比如系统中半连接数量的统计计数,对于工人们来讲,还是很有意思的。


浙江温州皮鞋湿,下雨进水不会胖。

原文链接: https://blog.csdn.net/dog250/article/details/105093969

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    使用hotfix技术来HOOK任意位置代码的手艺_c# hotfix hook

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/406061

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年4月26日 上午9:35
下一篇 2023年4月26日 上午9:36

相关推荐