首先,我这样做很有趣,所以不要评判我。
我所做的是将函数指针从用户空间传递给内核,使用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了。我可以清楚地看到函数是由内核通过读取内存来执行的。我正在运行PowerPc目标,因此Oops显示异常为700,这是程序异常。从Oops中,我可以看到指令转储,其中nip(after)与my_handler完全相同。
Instruction dump:
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 <3d20f000> 39400000 61290c10 91490000
我无法理解它。有人可以吗?谢谢
答案 0 :(得分:4)
我讨厌劝阻一个令人钦佩的观念,但如果没有一些严肃的额外工作,你想要做的事情很难(如果不是不可能的话)。
您的功能已链接到用户空间中的F
位置。您将其复制到静态数组位置的内核空间:A
。 A
可能在内核的数据部分,因此可能无法执行。此外,您的功能在错误的位置链接(例如F != A
)。
此外,即使您的函数可以链接到正确的位置A
,您如何处理其中的符号重定位(例如,如果它调用printk
,您如何重新链接其中的地址?函数匹配实际的printk
地址)?
创建内核模块并加载(通过modprobe
)更容易,你可以做任何你想做的事。
旁注:这是一个巨大的安全vulernability。一个类似的用于&#34; Stuxnet&#34;蠕虫穿透Windows。
<强>更新强>
在异常事件之后,转移[及时]长。到那时,它具有正确的数据,因此转储显示当前状态,可以这么说,但不在所讨论的确切周期上发生了什么[由于这个&#34;自我修改的本质&#34;码]。
但是,当最初执行时,它可能有垃圾(即700)。我不确定PPC,但其他拱门有单独的inst和数据缓存。无序执行。数据将位于数据高速缓存中,但不一定位于inst高速缓存[或队列]中。而且,他们倾向于独立运作以提高速度[&#34; Harvard&#34;体系结构]。
(例如)在x86上,设置静态区域后,必须刷新/同步,以便exec单元重新获取该区域。否则,它可能已经推测性地预取了指令数据(例如,它并不期望它是&#34;自我修改&#34;)数据不是什么预计[可能是0x00000000]。
考虑:copy_from_user
所需数据在数据缓存中后,但尚未尚未刷新到RAM。执行单元[和inst cache]没有来自静态区域的任何数据,将从RAM中获取。因为自修改代码很少见,所以inst和数据缓存不互相窥探[它会减慢速度]。
因此,执行单元从RAM(例如0x00000000)获取数据而不是加载的数据[在数据缓存中仅 ]。
第二次工作是因为执行单元提取的数据来自第一次尝试期间的数据[有时间刷新到RAM]。也就是说,静态区域现在已经填充,第二个copy_from_user
实际上是NOP。
A&#34;验尸&#34;如上所述,该地区的转储无法显示出这种差异。
答案 1 :(得分:4)
想出来。事实证明这是缓存的事情。谢谢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来使这个工作。再次感谢。