当项目中包含程序集文件时,来自 mmap 的意外执行权限 [英] Unexpected exec permission from mmap when assembly files included in the project

查看:19
本文介绍了当项目中包含程序集文件时,来自 mmap 的意外执行权限的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正用这个头撞墙.

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

In my project, when I'm allocating memory with mmap the mapping (/proc/self/maps) shows that it is an readable and executable region despite I requested only readable memory.

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

After looking into strace (which was looking good) and other debugging, I was able to identify the only thing that seems to avoid this strange problem: removing assembly files from the project and leaving only pure C. (what?!)

这是我奇怪的例子,我正在使用 Ubunbtu 19.04 和默认的 gcc.

So here is my strange example, I am working on Ubunbtu 19.04 and default gcc.

如果你用 ASM 文件(它是空的)编译目标可执行文件,那么 mmap 返回一个可读和可执行的区域,如果你没有编译,那么它的行为是正确的.查看我在示例中嵌入的 /proc/self/maps 的输出.

If you compile the target executable with the ASM file (which is empty) then mmap returns a readable and executable region, if you build without then it behave correctly. See the output of /proc/self/maps which I have embedded in my example.

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 的版本

Without the ASM included version

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

推荐答案

Linux 有一个 执行域 称为READ_IMPLIES_EXEC,这会导致所有用PROT_READ 分配的页面也被赋予PROT_EXEC.较旧的 Linux 内核 使用将此用于使用等效于 gcc -z execstack 的可执行文件.该程序将向您显示是否已启用该功能:

Linux has an execution domain called READ_IMPLIES_EXEC, which causes all pages allocated with PROT_READ to also be given PROT_EXEC. Older Linux kernels used to use this for executables that used the equivalent of gcc -z execstack. This program will show you whether that's enabled for itself:

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

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

如果您将它与一个空的 .s 文件一起编译,您将看到它已启用,但如果没有,它将被禁用.这个来自二进制文件中的 ELF 元信息.做readelf -Wl 例子.在没有空 .s 文件的情况下进行编译时,您将看到这一行:

If you compile that along with an empty .s file, you'll see that it's enabled, but without one, it'll be disabled. The initial value of this comes from the ELF meta-information in your binary. Do readelf -Wl example. You'll see this line when you compiled without the empty .s file:

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

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

But this one when you compiled with it:

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

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

Note RWE instead of just RW. The reason for this is that the linker assumes that your assembly files require read-implies-exec unless it's explicitly told that they don't, and if any part of your program requires read-implies-exec, then it's enabled for your whole program. The assembly files that GCC compiles tell it that it doesn't need this, with this line (you'll see this if you compile with -S):

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

默认部分权限不包括 exec.请参阅 .section 文档的 ELF 部分旗帜"的含义和@attributes.

The default section permissions don't include exec. See the ELF part of the .section documentation for the meaning of the "flags" and @attributes.

(并且不要忘记在 .section 指令之后切换到另一个部分,例如 .text.data,如果您的 .s 依赖于 .text 因为文件顶部的默认部分.)

(And don't forget to switch to another section like .text or .data after that .section directive, if your .s was relying on .text because the default section at the top of the file.)

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

Put that line in example.s (and every other .s file in your project). The presence of that .note.GNU-stack section will serve to tell the linker that this object file doesn't depend on an executable stack, so the linker will use RW instead of RWE on the GNU_STACK metadata, and your program will then work as expected.

同样对于NASM,一个部分 指令指定不可执行的堆栈.

Similarly for NASM, a section directive with the right flags specifies non-executable stacks.

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

Modern Linux kernels between 5.4 and 5.8 changed the behaviour of the ELF program-loader. For x86-64, nothing turns on READ_IMPLIES_EXEC anymore. At most (with an RWE GNU_STACK added by ld), you'll get the stack itself being executable, not every readable page. (This answer covers the last change, in 5.8, but there must have been other changes before that, since that question shows successful execution of code in .data on x86-64 Linux 5.4)

exec-all (READ_IMPLIES_EXEC) 仅适用于链接器未在全部.但如此处所示,现代 ld 总是添加一个或另一个设置,即使输入 .o 文件缺少注释.

exec-all (READ_IMPLIES_EXEC) only happens for legacy 32-bit executables where the linker didn't add a GNU_STACK header entry at all. But as shown here, modern ld always adds that with one setting or the other, even when an input .o file is missing a note.

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

You should still use this .note section to signal non-executable stacks in normal programs. But if you were hoping to test self-modifying code in .data or following some old tutorial for testing shellcode, that's not an option on modern kernels.

这篇关于当项目中包含程序集文件时,来自 mmap 的意外执行权限的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆