有没有办法在 Linux 中执行平面二进制图像,使用如下语法:
nasm -f bin -o foo.bin foo.asm
runbinary foo.bin
Linux 内核可以加载几种不同的二进制格式——ELF 是最常见的,尽管 a.out 格式也非常有名。
支持的二进制格式由加载或编译到内核的 binfmt 模块控制(它们位于内核配置的文件系统部分下)。有一个binfmt_flat
用于 uClinux BFLT 平面格式的二进制文件,它非常小——它们甚至可以被 zlib 压缩,这将使你的二进制文件更小,所以这可能是一个不错的选择。
看起来 nasm 本身并不支持这种格式,但是就像 Jim Lewis 为 ELF 所描述的那样,手动添加必要的标头非常容易。这里有格式说明。
您是否有某些原因不想使用“-f elf”而不是“-f bin”?
我认为 Linux 不会运行不是 ELF 格式的二进制文件。我找不到将平面二进制文件转换为 ELF 的工具,但您可以通过使用此处描述的技术将 ELF 信息放入 foo.asm 中作弊:
我们可以查看 ELF 规范和 /usr/include/linux/elf.h 以及由标准工具创建的可执行文件,以确定我们的空 ELF 可执行文件应该是什么样子。但是,如果你是不耐烦的类型,你可以使用我在这里提供的那个:
BITS 32 org 0x08048000 ehdr: ; Elf32_Ehdr db 0x7F, "ELF", 1, 1, 1, 0 ; e_ident times 8 db 0 dw 2 ; e_type dw 3 ; e_machine dd 1 ; e_version dd _start ; e_entry dd phdr - $$ ; e_phoff dd 0 ; e_shoff dd 0 ; e_flags dw ehdrsize ; e_ehsize dw phdrsize ; e_phentsize dw 1 ; e_phnum dw 0 ; e_shentsize dw 0 ; e_shnum dw 0 ; e_shstrndx ehdrsize equ $ - ehdr phdr: ; Elf32_Phdr dd 1 ; p_type dd 0 ; p_offset dd $$ ; p_vaddr dd $$ ; p_paddr dd filesize ; p_filesz dd filesize ; p_memsz dd 5 ; p_flags dd 0x1000 ; p_align phdrsize equ $ - phdr _start: ; your program here filesize equ $ - $$
此映像包含一个 ELF 标头,将文件标识为 Intel 386 可执行文件,没有节标头表和包含一个条目的程序标头表。所述条目指示程序加载器将整个文件加载到内存中(程序在其内存映像中包含其 ELF 头和程序头表是正常行为)从内存地址 0x08048000(这是加载可执行文件的默认地址)开始, 并在 _start 处开始执行代码,它立即出现在程序头表之后。没有 .data 段,没有 .bss 段,没有评论——只有最基本的必需品。
所以,让我们添加我们的小程序:
; tiny.asm org 0x08048000 ; ; (as above) ; _start: mov bl, 42 xor eax, eax inc eax int 0x80 filesize equ $ - $$
并尝试一下:
$ nasm -f bin -o a.out tiny.asm $ chmod +x a.out $ ./a.out ; echo $? 42
最低限度,Linux 需要弄清楚可执行文件的格式,它会从第一个字节中得到它。例如,如果它是 #!, shebang的脚本。如果是 ELF,则为 0x7F 'E' 'L' 'F'。这些幻数将通过查找确定处理程序。
所以你需要一个带有公认幻数的标题。您可以在/proc/sys/fs/binfmt_misc中获取 shebang 支持的格式列表。获取本机二进制格式列表(不幸的是)有点棘手。
bFLT可能是一个不错的选择。事实上,它是一种流行的嵌入式可执行格式。但是你也可以把 ELF 压得很远。本文得到了一个最小为 45 字节的 ELF 可执行文件。也就是说,你主要是用手而不是工具来挤压它。