将功能从用户空间复制到内核并执行

use*_*547 5 c linux linux-kernel

首先,我这样做是为了好玩,所以不要评判我.

我所做的是将一个函数指针从用户空间传递给内核,使用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))();
Run Code Online (Sandbox Code Playgroud)

在用户空间中,这个功能的作用非常简单,如下所示

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
Run Code Online (Sandbox Code Playgroud)

问题是我第一次这样做总是生成一个糟糕的.但是第二次我做这个以及之后,问题就消失了,再也没有哎呀了.我可以清楚地看到该函数是由内核通过读取内存来执行的.我正在运行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
Run Code Online (Sandbox Code Playgroud)

我无法理解它的任何意义.有人可以吗?谢谢

Cra*_*tey 4

我不想阻止一个令人钦佩的想法,但如果不做一些认真的额外工作,你想要做的事情即使不是不可能,也是很困难的。

您的函数链接在用户F空间中的位置。您将其复制到静态数组位置的内核空间: 。可能位于内核的数据部分,因此可能无法执行。另外,您的函数链接到错误的位置(例如)。AAF != A

此外,即使您的函数可以链接到正确的位置A,您如何处理其中符号的重定位(例如,如果它调用printk,您如何重新链接函数内部的地址以匹配实际printk地址)?

创建内核模块并加载它(通过modprobe)要容易得多,您可以做任何您想做的事情。

旁注:这是一个巨大的安全漏洞。“Stuxnet”蠕虫病毒也使用类似的方法来渗透 Windows。


更新:

转储在异常事件发生后很长时间内发生。到那时,它已经有了正确的数据,因此转储显示了当前状态,可以这么说,但不是在所讨论的确切周期上发生的情况[由于这种“自修改”代码的性质]。

但是,当最初执行时,它可能有垃圾(即 700)。我不确定 PPC,但其他架构有单独的安装和数据缓存。乱序执行。数据将位于数据缓存中,但不一定位于安装缓存[或队列]中。而且,它们倾向于独立运行以提高速度[“哈佛”架构]。

(例如)在 x86 上,设置静态区域后,必须刷新/同步,以便 exec 单元重新获取该区域。否则,它可能已经推测性地预取了指令数据(例如,它不期望它是“自修改”),并且数据不是预期的[可能是0x00000000]。

考虑:当copy_from_user所需的数据已在数据缓存中,但尚未刷新到 RAM 时。执行单元[和安装缓存]没有来自静态区域的任何数据,将从 RAM 中获取。由于自修改代码很少见,因此安装和数据缓存不会相互窥探[这会减慢速度]。

因此,执行单元从 RAM 获取数据(例如 0x00000000),而不是加载的数据(仅数据缓存中)。

第二次之所以有效,是因为执行单元获取的数据来自第一次尝试期间的数据[有时间刷新到 RAM]。也就是说,静态区域现已填充,第二个区域copy_from_user实际上是 NOP。

[如上所述]该地区的“事后”垃圾场将无法显示这种差异。