【问题标题】:How to create a Linux ELF with only a NOP?如何创建一个只有 NOP 的 Linux ELF?
【发布时间】:2021-03-12 12:27:29
【问题描述】:

出于好奇,我试图在不使用编译器的情况下在 Linux 上创建一个简单的 64 位 ELF 文件。为简单起见,我只需要 NOP (0x90)。

“readelf”可以读取我当前的文件,并且它的输出似乎还可以。然而,当我执行它时,我会遇到一个段错误。

有问题的文件(未格式化,以便更容易复制粘贴):

7F 45 4C 46 02 01 01 00 00 00 00 00 00 00 00 00 02 00 3E 00 01 00 00 00 78 00 40 00 00 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 00 38 00 01 00 00 00 00 00 00 00 01 00 00 00 05 00 00 00 78 00 00 00 00 00 00 00 78 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 80 00 00 00 00 00 90

GDB 在地址 0x400079 处说段错误,虽然入口点在 0x400078 并且文件大小对于 NOP 来说是 1 个字节,那么为什么要执行非保留内存呢?

奖励:为什么很多可执行文件从大虚拟地址开始其代码,而不是从 0x0?

【问题讨论】:

    标签: linux elf


    【解决方案1】:

    当您让编译器处理低级操作时,您可以构建一个什么都不做程序。但是如果你直接用机器码(甚至是汇编语言)编写它,你必须处理血淋淋的细节,比如正确地结束程序。

    你问为什么非保留内存被执行。好吧,你在 0x400078 加载一些东西(一个 NOP 代码)并声明它是程序的开始。加载器在0x400078处初始化内存,用该地址加载指令指针,处理器就乖乖地从那里开始执行。它成功执行了 NOP,并且由于没有被指示执行任何其他操作,它尝试在 0x400079 处执行以下指令。这要归功于内存隔离和虚拟内存管理器。在内核或实模式下,它会执行该内存位置的任何内容,并产生无限的副作用......

    因此,您不能执行简单的 NOP,而是执行告诉操作系统程序已结束的系统调用。根据维基百科上的exit (system call),它应该在Linux 64位上:

     mov eax, 60  ; sys_exit syscall number: 60
     xor edi, edi ; set exit status to 0 (`xor edi, edi` is equal to `mov edi, 0` )
     syscall      ; call it
    

    如果您的程序从mov eax, 60 开始,那么它将干净地退出,甚至会向其调用者返回 0 状态。

    【讨论】:

    • 谢谢。我不敢相信我没有考虑到这一点,我觉得很傻,我现在已经成功地创建了一个“什么都不做”的可执行文件。我即将进入指令编码的超级有趣世界(不是),以便制作我自己的超级简单编译器。当我计划制作“Hello World”操作系统时,我无法想象明年我将面临的恐怖。
    • 0x400079 不会出现段错误。它只会在到达 0x401000 时出现段错误。页面长度为 0x1000 字节。
    • 我喜欢@SergeBallesta 的答案如此清晰
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-23
    • 2012-10-04
    • 1970-01-01
    • 2019-06-13
    • 1970-01-01
    相关资源
    最近更新 更多