首先,我这样做是为了好玩,所以不要评价我。
我所做的是将用户空间的函数指针传递给内核,使用copy_from_user将函数体复制到内核中的静态数组中,并开始在该数组中跳转以执行。
在内核中:
static char handler_text[PAGE_SIZE] __page_aligned_data; copy_from_user((void *)handler_text , (const void __user *)my_handler , PAGE_SIZE); ((void (*)())(handler_text))();
在用户空间中,这个函数做的很简单如下
void my_handler(){ volatile unsigned long * p = (volatile unsigned long *)0xF0000c10; *p = 0x0000000; } 10000938 <my_handler>: 10000938: 3d 20 f0 00 lis r9,-4096 1000093c: 39 40 00 00 li r10,0 10000940: 61 29 0c 10 ori r9,r9,3088 10000944: 91 49 00 00 stw r10,0(r9) 10000948: 4e 80 00 20 blr 1000094c: 00 01 88 08 .long 0x18808
问题是我第一次这样做总是会产生一个糟糕的。 但是我第二次做这个,那个问题就没有了,没有Oops了。 我可以清楚地看到内核通过读取内存执行的function。 我正在运行一个PowerPc目标,所以Oops显示exception是700,这是程序exception。 从Oops中,我可以看到指令转储,其中nip(之后)与my_handler完全相同。
Instruction dump: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 <3d20f000> 39400000 61290c10 91490000
我无法从中找出任何意义。 任何人都可以 谢谢
我讨厌劝阻一个令人敬佩的概念,但是你要做的事情如果不是不可能的话,也是困难的,没有一些额外的工作。
您的功能在用户空间的位置F
处链接。 您将其复制到静态数组位置的内核空间: A
。 A
可能在内核的数据部分,所以执行可能是不可能的。 另外,你的函数在错误的位置连接(例如F != A
)。
此外,即使你的函数可以链接到正确的位置A
,你如何处理其中的符号重定位(例如,如果它调用printk
,你如何重新链接功能内的地址,以匹配实际的printk
地址)?
创建一个内核模块并加载(通过modprobe
)要容易得多,而且你可以做任何你想做的事情。
附注:这是一个巨大的安全性问题。 “Stuxnet”蠕虫使用类似的蠕虫来穿透Windows。
更新:
转储在异常事件之后的很长时间内发生。 到那个时候,它有正确的数据,所以转储显示当前状态,可以这么说,而不是在所讨论的确切周期中发生的事情(由于这个“自我修改”代码的性质)。
但是,最初执行时可能有垃圾(即700)。 我不确定PPC,但其他拱门有单独的实例和数据缓存。 无序执行。 数据将在数据缓存中,但不一定在inst缓存[或队列]中。 而且,他们倾向于以独立的速度运作[“哈佛”建筑]。
(例如)在x86上,在设置了静态区域之后,必须进行刷新/同步,以便执行单元重新设置该区域。 否则,它可能已经推测性地预取了指令数据(例如,它不期望它是“自我修改的”),而不是预期的数据[可能是0x00000000]。
考虑:在copy_from_user
,所需的数据在数据缓存中,但尚未刷新到RAM中。 没有来自静态区域的任何数据的执行单元[和inst缓存]将从RAM获取。 由于自修改代码很少,因此inst和数据缓存不会相互窥探(这会减慢速度)。
因此,执行单元从RAM(例如0x00000000)获取其数据,而不是加载的数据[ 仅在数据高速缓存中]。
第二次工作,因为执行单元获取的数据来自第一次尝试[有时间刷新到RAM]的数据。 也就是说,静态区域现在已经被填充,而第二个copy_from_user
实际上是NOP。
如上所述,该地区的“死后”转储将无法显示这种差异。
弄清楚了。 原来是缓存的东西。 感谢Ctx和Craig,我添加了一个
flush_dcache_icache_page(virt_to_page((unsigned long)(handler_text)));
后
copy_from_user((void *)handler_text , (const void __user *)my_handler , PAGE_SIZE);
现在都很好。 在我问这个问题之前,我只尝试了flush_dcache_page,但没有成功。 所以我不得不同时使用dcache和icache来完成这个工作。 再次感谢。