[编辑]
这只是我想做的一个实验,我想看看是否可以欺骗内核从一个未命名的管道执行一个elf,并用/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
我认为由于我得到的结果各不相同,这可能是可能的。
$ /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
所以我正在玩ASM并注意到你无法将输出组合或链接到stdout。
$ 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
给出如下代码:
.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
有谁能告诉我为什么组装对象或将对象链接到stdout是不可能的?请尽可能深入。要查看编译器为了生成该代码而进入的完整过程,您可以使用以下命令:
$ exec 7< <(gcc -c -xc <(echo $'#include <stdio.h>\n\nint main(){\nprintf("I work\\n");\nreturn 0;\n}') -o /dev/stdout)
如果您组装并链接我之前提供的程序集并想要正确执行它,您需要调用/lib64/ld-linux-x86-64.so.2 / path / to / output否则它将只说不好的精灵翻译。
# ./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
答案 0 :(得分:1)
你不能将汇编程序输出管道输出到stdout,因为自远古时代(可能是20世纪60年代)assemblers通常在两次传递中起作用(不仅在输入上,而且在输出上)。因此需要具有搜索能力(输入和输出,使用l seek(2))。否则,他们需要将大部分输入和输出数据保存在内存中。
请记住,目标文件不仅包含数据(例如机器指令,只读常量),还包含relocation信息。
/tmp/lol.s:致命错误:无法写/ dev / stdout:非法搜索
这说明as
程序需要搜索文件(例如使用lseek(2))。
也许你想在内存中生成机器代码。为此,请使用JIT compilation或libgccjit之类的asmjit库。
顺便说一句,你可能想了解gcc
如何编译一个简单的C程序。为此,请使用gcc -v
进行编译,并注意到某些crt0内容已被链接。
如果您出于性能原因考虑使用管道,请改用tmpfs文件系统。那里的文件留在内存中(因此在关机时丢失)并且很快,因为没有执行磁盘IO。
你甚至可以在这样的文件系统中生成一些C文件,然后让gcc
编译它(可能是plugin)。另请参阅this。
...如果我可以欺骗内核从未命名的管道执行elf
不,你不能。 ELF可执行文件也需要是可查找的,因为内核在其execve(2)时间内设置了一个新的virtual addresss space,内部使用接近mmap(2)的内容。换句话说,execve
正在设置几个内存映射。
研究流程的虚拟地址空间。阅读proc(5),然后尝试cat /proc/$$/maps
(并用更有趣的pid替换$$
)。
阅读Operating Systems: Three Easy Pieces(免费下载)对您来说应该很有趣。