为什么我不能将汇编器输出通过管道传输到标准输出?

lin*_*eek 4 assembly gcc elf ld bfd

[编辑]
这只是我进行的一个实验,我想看看是否可以欺骗内核从无名管道执行精灵,并使用 /lib64/ld-linux-x86-64.so 进行进程替换。 2,我知道这是在黑暗中进行的尝试,但我只是希望看看是否有人能给我一个答案,为什么它不起作用

$ /lib64/ld-linux-x86-64.so.2 <(gcc -c -xc <(echo $'#include <stdio.h>\n\nint main(){\nprintf("I work\\n");\nreturn 0;\n}') -o /dev/stdout)
/tmp/ccf5sMql.s: Assembler messages:
/tmp/ccf5sMql.s: Fatal error: can't write /dev/stdout: Illegal seek
as: BFD version 2.25.1-22.base.el7  assertion fail elf.c:2660
as: BFD version 2.25.1-22.base.el7  assertion fail elf.c:2660
/tmp/ccf5sMql.s: Fatal error: can't close /dev/stdout: Illegal seek
/dev/fd/63: error while loading shared libraries: /dev/fd/63: file too short
Run Code Online (Sandbox Code Playgroud)

我认为由于我得到的结果不同,这可能是可能的。

$ /lib64/ld-linux-x86-64.so.2 <(gcc -fPIC -pie -xc <(echo $'#include 
<stdio.h>\n\nint main(){\nprintf("I work\\n");\nreturn 0;\n}') -o 
/dev/stdout|cat|perl -ne 'chomp;printf')
/dev/fd/63: error while loading shared libraries: /dev/fd/63: ELF load 
command past end of file
$ /lib64/ld-linux-x86-64.so.2 <(gcc -fPIC -pie -xc <(echo $'#include 
<stdio.h>\n\nint main(){\nprintf("I work\\n");\nreturn 0;\n}') -o 
/dev/stdout|cat|perl -0 -ne 'chomp;printf')
/dev/fd/63: error while loading shared libraries: /dev/fd/63: ELF file ABI 
version invalid
Run Code Online (Sandbox Code Playgroud)

所以我在玩 ASM 并注意到你无法将输出组装或链接到标准输出。

$ as /tmp/lol.s -o /dev/stdout
/tmp/lol.s: Assembler messages:
/tmp/lol.s: Fatal error: can't write /dev/stdout: Illegal seek
as: BFD version 2.25.1-22.base.el7  assertion fail elf.c:2660
as: BFD version 2.25.1-22.base.el7  assertion fail elf.c:2660


as /tmp/lol.s -o /tmp/test.o
$ ld /tmp/test.o -o what -lc
ld: warning: cannot find entry symbol _start; defaulting to 00000000004002a0


$ exec 9< <(ld /tmp/test.o -o /dev/stdout -lc)
ld: warning: cannot find entry symbol _start; defaulting to 00000000004002a0
ld: final link failed: Illegal seek
Run Code Online (Sandbox Code Playgroud)

给出代码如下:

.file   "63"
.section        .rodata
.LC0:
.string "I work"
.text
.globl  main
.type   main, @function
main:
.LFB0:
.cfi_startproc
pushq   %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq    %rsp, %rbp
.cfi_def_cfa_register 6
movl    $.LC0, %edi
call    puts
movl    $0, %eax
popq    %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size   main, .-main
.ident  "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-16)"
.section        .note.GNU-stack,"",@progbits
.file   "63"
.section        .rodata
Run Code Online (Sandbox Code Playgroud)

谁能告诉我为什么不能组装对象或将对象链接到标准输出?请尽可能深入。要查看编​​译器生成该代码的完整过程,您可以使用以下命令:

$ exec 7< <(gcc -c -xc <(echo $'#include <stdio.h>\n\nint main(){\nprintf("I work\\n");\nreturn 0;\n}') -o /dev/stdout)
Run Code Online (Sandbox Code Playgroud)

如果您组装并链接我之前提供的程序集并想要正确执行它,您需要调用 /lib64/ld-linux-x86-64.so.2 /path/to/output 否则它只会说 bad elfterpreter 。

# ./what
bash: ./what: /lib/ld64.so.1: bad ELF interpreter: No such file or directory

# /lib64/ld-linux-x86-64.so.2 ./what
I work
Run Code Online (Sandbox Code Playgroud)

Bas*_*tch 6

您无法将汇编器输出通过管道传输到 stdout,因为从远古时代(可能是 1960 年代)起,汇编器通常分两遍工作(不仅在输入上,而且在输出上)。所以能够寻找(输入和输出,使用 leek (2))。否则,他们需要将大部分输入和输出数据保存在内存中。

\n

请记住,目标文件不仅包含数据(例如机器指令、只读常量),还包含重定位信息。

\n
\n

/tmp/lol.s:致命错误:无法写入/dev/stdout:非法查找

\n
\n

这说明as程序需要查找文件(例如使用lseek(2))。

\n

也许您想在内存中生成机器代码。为此,请使用一些JIT 编译库,例如libgccjitasmjit

\n

顺便说一句,您可能想了解如何gcc编译一个简单的 C 程序。为此,请使用它进行编译,gcc -v并注意某些crt0内容已链接。

\n

如果出于性能原因考虑使用管道,请改用一些tmpfs文件系统。那里的文件保留在内存中(因此在关闭时会丢失)并且速度很快,因为不执行磁盘 IO。

\n

您甚至可以在这样的文件系统中生成一些 C 文件,然后要求gcc编译它(也许作为插件)。另请参阅

\n
\n

...如果我可以欺骗内核从无名管道执行精灵

\n
\n

不,你不能。ELF可执行文件也需要可查找,因为内核在execve(2)时设置\xe2\x80\x8b\xe2 \ x80\x8e\xe2\x80\x8b\xe2\x80\x8e\xe2 \x80\x8b\xe2\x80\x8e\xe2\x80\x8b\xe2\x80\x8e 一个新的虚拟地址空间,内部使用接近mmap(2)的东西。换句话说,就是设置几个内存映射。execve

\n

研究进程的虚拟地址空间。阅读proc(5),然后尝试cat /proc/$$/maps(并替换$$为更有趣的 pid)。

\n

阅读操作系统:三个简单的部分(可免费下载)应该会让您感兴趣。

\n