Task 5 处理异常
这是Meltdown Attack的第五个实验,原实验教程在此:
https://seedsecuritylabs.org/Labs_16.04/System/Meltdown_Attack/
在Task4中,我们从用户空间去访问内核内存会导致程序crash。在meltdown攻击中,在访问了内存区域后,我们必须做点什么处理,以防止程序崩溃。访问不可访问的内存区域会产生一个SIGSEGV信号,如果一个程序不能处理这个异常,那么操作系统会来处理,并且将其终止。这就是为什么Task 4 的程序崩溃的原因。要解决这种崩溃,我们可以在程序中俘获这种异常并进行处理。
不像C++或者其他高级语言,C语言不提供异常处理机制,比如try/catch 语句。不过,我们可以用sigsetjmp()
和siglongjmp()
函数来模拟try/catch语句。
#include<stdio.h>
#include<setjmp.h>
#include<signal.h>
static sigjmp_buf jbuf;
static void catch_segv()
{
// Roll back to the checkpoint set by sigsetjmp().
siglongjmp(jbuf, 1);
}
int main()
{
// The address of our secret data
unsigned long kernel_data_addr = 0xfb61b000;
// Register a signal handler
signal(SIGSEGV, catch_segv);
if (sigsetjmp(jbuf, 1) == 0) {
// A SIGSEGV signal will be raised.
char kernel_data = *(char*)kernel_data_addr;
// The following statement will not be executed.
printf("Kernel data at address %lu is: %c\n",
kernel_data_addr, kernel_data);
}
else {
printf("Memory access violation!\n");
}
printf("Program continues to execute.\n");
return 0;
}
现在来解释一下上面的代码。
- 创建一个信号处理器:当出现SIGSEGV信号时,就会唤醒catch_segv函数。
- 创建一个check point:当信号处理器处理完异常时,我们需要返回到刚刚发生异常的地方。这就是这个check point的作用。这里使用sigsetjmp(jbuf,1)来保存栈和上下文环境到jbuf中,以便后续的siglongjmp再跳转回来。当成功创立了这个点,sigsetjmp函数就返回0。
- 返回check point:当调用了siglongjmp(jbuf,1)函数,保存在变量jbuf的状态就会传回处理器,然后继续计算。值得注意的是,siglongjmp(jbuf,1)的第二个参数是函数sigsetjmp(),这里要设置为1,因为此时是返回check point。因此,现在程序就会跳转到else分支。
- 触发异常:当运行到第18行时就会由于内存越界访问触发SIGSEGV信号。
实验结果
我们依然用以下命令对程序进行编译。
gcc -march=native -o ExceptionHandling ExceptionHandling.c
运行结果如下: