c - 当程序集文件包含在项目中时,来自 mmap 的意外执行权限

标签 c linux assembly mmap dep

我正在用这个把头撞到墙上。

在我的项目中,当我使用 mmap 分配内存时,映射 (/proc/self/maps) 显示它是一个可读和可执行的区域 尽管我只要求可读内存。

在查看 strace(看起来不错)和其他调试之后,我能够确定唯一似乎可以避免这个奇怪问题的方法:从项目中删除汇编文件并只留下纯 C。(什么?!)

这是我的奇怪示例,我正在使用 Ubuntu 19.04 和默认 gcc。

如果您使用 ASM 文件(空文件)编译目标可执行文件,则 mmap 会返回一个可读且可执行的区域,如果您没有构建则它会正常运行。请参阅我在示例中嵌入的 /proc/self/maps 的输出。

example.c

#include <stdio.h>
#include <string.h>
#include <sys/mman.h>

int main()
{
    void* p;
    p = mmap(NULL, 8192,PROT_READ,MAP_ANONYMOUS|MAP_PRIVATE,-1,0);

    {
        FILE *f;
        char line[512], s_search[17];
        snprintf(s_search,16,"%lx",(long)p);
        f = fopen("/proc/self/maps","r");
        while (fgets(line,512,f))
        {
            if (strstr(line,s_search)) fputs(line,stderr);
        }

        fclose(f);
    }

    return 0;
}

example.s:是一个空文件!

输出

使用 ASM 包含版本

VirtualBox:~/mechanics/build$ gcc example.c example.s -o example && ./example
7f78d6e08000-7f78d6e0a000 r-xp 00000000 00:00 0 

没有ASM包含的版本

VirtualBox:~/mechanics/build$ gcc example.c -o example && ./example
7f1569296000-7f1569298000 r--p 00000000 00:00 0 

最佳答案

Linux 有一个 execution domain称为 READ_IMPLIES_EXEC,这会导致分配有 PROT_READ 的所有页面也被赋予 PROT_EXEC。较旧的 Linux 内核 used to use this对于使用等同于 gcc -z execstack 的可执行文件。该程序将向您显示它是否为自身启用:

#include <stdio.h>
#include <sys/personality.h>

int main(void) {
    printf("Read-implies-exec is %s\n", personality(0xffffffff) & READ_IMPLIES_EXEC ? "true" : "false");
    return 0;
}

如果您将它与一个空的 .s 文件一起编译,您会看到它已启用,但如果没有,它将被禁用。这个 comes from the ELF meta-information in your binary 的初始值.执行 readelf -Wl 示例。在没有空 .s 文件的情况下进行编译时,您会看到这一行:

  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW  0x10

但是当你用它编译时这个:

  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RWE 0x10

注意 RWE 而不仅仅是 RW。这样做的原因是链接器假定您的程序集文件需要 read-implies-exec 除非它被明确告知它们不需要,并且如果您的程序的任何部分需要 read-implies-exec,那么它会为您的整个程序启用. GCC 编译的汇编文件告诉它它不需要这个,用这一行(如果你用 -S 编译你会看到这个):

    .section        .note.GNU-stack,"",@progbits

默认部分权限不包括 exec。请参阅 .section documentation 的 ELF 部分“标志”和@attributes 的含义。

(如果您的 .s 依赖于 .text 因为文件顶部的默认部分。)

将该行放入 example.s(以及您项目中的所有其他 .s 文件)。 .note.GNU-stack 部分的存在将用于告诉链接器该目标文件不依赖于可执行堆栈,因此链接器将使用 RW而不是 GNU_STACK 元数据上的 RWE,然后您的程序将按预期工作。

同样for NASM ,带有正确标志的 section 指令指定不可执行的堆栈。


5.4 和 5.8 之间的现代 Linux 内核改变了 ELF 程序加载器的行为。对于 x86-64,不再打开 READ_IMPLIES_EXEC。最多(使用 ld 添加的 RWE GNU_STACK),您将获得堆栈本身是可执行的,而不是每个可读页面。 (This answer 涵盖了 5.8 中的最后一个更改,但在此之前肯定还有其他更改,因为该问题显示在 x86-64 Linux 5.4 上成功执行了 .data 中的代码)

exec-all (READ_IMPLIES_EXEC) 仅发生在链接器未添加 GNU_STACK header 条目的传统 32 位可执行文件中全部。但如此处所示,现代 ld 总是添加一个设置或另一个设置,即使输入的 .o 文件缺少注释。

您仍应使用此 .note 部分来指示正常程序中的不可执行堆栈。但是,如果您希望在 .data 中测试自修改代码或遵循一些关于 testing shellcode 的旧教程,这不是现代内核的选项。

关于c - 当程序集文件包含在项目中时,来自 mmap 的意外执行权限,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58260465/

相关文章:

windows - 哪些版本的 Windows 支持/需要哪些 CPU 多媒体扩展? (如何检查 SSE 或 AVX 是否完全可用?)

linux - 如何在 Linux 上为 IntelliJ 配置文件关联?

c - 开发板ARM汇编

c - C预处理文件是否包含源文件行号信息?

c - 使用 PIC 18F4550 使 3 个 LED 闪烁

linux - 如何为匿名上传服务 file.io 创建 bashrc 脚本?

php - 通过 (f|v|s)scanf 函数读取 PHP_INT_MAX 或接近值

c++ - 移动文本模式光标不起作用

c++ - 使用 glDrawPixels 时遇到问题

c - 如何使用指针在 C 中打印矩阵?