运行时代码修改如何受到任何操作系统的限制?

时间:2014-07-07 17:27:07

标签: c

为了检查我是否可以在运行时更改代码,我在linux中编写了一小段代码(下面)。

int add(int a, int b)
{
        printf("reached inside the function");
        return a+b;
}

int main()
{
        int x=10;
        int y = 20;
        int * p;
        int z;
        int (*fp) (int , int);
        fp = add;
        p = (int *)fp;

        *(p+0) = 0;
        z = add(x,y);
}

从c编码的角度来看,没有问题,编译器编译完美,链接也会发生。但是在运行时它失败并出现以下错误:

分段错误(核心转储)

上面的错误是完美的,因为代码段不应该在运行时被更改,但我想知道它是如何在运行时被控制的。

要了解有关代码区域限制的更多信息,我在输出文件上运行了readelf,结果显示在以下部分标题中:

[13] .text             PROGBITS        08048330 000330 0001cc 00  AX  0   0 16

其中节标题标记显示为" AX" ,表示此部分只是可分配和可执行的。它不支持写作(" W")。

并且对elf文件进行了一些小改动,我能够将此部分的标志修改为" WAX" ,如下所示:

[13] .text             PROGBITS        08048330 000330 0001cc 00 WAX  0   0 16

但我仍然得到相同的#34;分段错误"错误。

我想知道 - 系统是如何实现的?

2 个答案:

答案 0 :(得分:2)

分段错误是否发生在同一个地方? 可能是操作系统忽略了W标志,但我不认为这是这种情况。假设操作系统尊重该标志,则以下内容是相关的。

你用add覆盖0函数的第一条指令,在x86汇编中是(假设这里有4个字节int

00000000  0000              add [bx+si],al
00000002  0000              add [bx+si],al

最有可能最终访问bx+si的无效内存。

答案 1 :(得分:2)

系统忽略了W标志:

$ gcc -Wall file.c 
$ readelf -S a.out | grep .text
  [14] .text             PROGBITS        08048330 000330 0001cc 00  AX  0   0 16
$ objcopy a.out --set-section-flags .text=alloc,code,data a.out 
$ readelf -S a.out | grep .text
  [14] .text             PROGBITS        08048330 000330 0001cc 00 WAX  0   0 16
$ gdb -q a.out 
Reading symbols from a.out...(no debugging symbols found)...done.
(gdb) r
Starting program: a.out 

Program received signal SIGSEGV, Segmentation fault.
0x0804842f in main ()
(gdb) x/i 0x0804842f
0x804842f <main+45>:    movl   $0x0,(%eax)
(gdb) 

您仍然无法写信至p。您可以使用mprotect

在运行时更改内存页面保护
#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <sys/mman.h>

int add(int a, int b)
{
        printf("reached inside the function");
        return a+b;
}

int main()
{
        int x=10;
        int y = 20;
        int * p;
        int z;
        int (*fp) (int , int);
        long pagesize;

        fp = add;
        p = (int *)fp;

        pagesize = sysconf(_SC_PAGESIZE);
        if(mprotect((void *)((uintptr_t)p & ~((uintptr_t)pagesize - 1)), pagesize, PROT_READ | PROT_WRITE | PROT_EXEC) == -1)
          perror("Error mprotect()");

        *(p+0) = 0;
        z = add(x,y);
        return 0;
}

这会让你得到错误的解决方法:

$ gcc -Wall file.c 
$ ./a.out 
Segmentation fault
$ gdb -q a.out 
Reading symbols from a.out...(no debugging symbols found)...done.
(gdb) r
Starting program: a.out 

Program received signal SIGSEGV, Segmentation fault.
0x08048484 in add ()
(gdb) x/i 0x08048484
0x8048484 <add>:    add    %al,(%eax)
(gdb)