如何创建小于4097字节的Linux i386 a.out可执行文件?
如何创建小于4097字节的Linux i386 a.out可执行文件
现状与测试环境
我尝试创建一个小于4097字节的Linux i386 a.out可执行文件,但所有尝试均失败。
编译命令:
$ nasm -O0 -f bin -o prog prog.nasm && chmod +x prog
测试环境为运行Linux 2.6.32的Ubuntu 10.04 i386虚拟机,测试步骤:
$ sudo modprobe binfmt_aout $ sudo sysctl vm.mmap_min_addr=4096 $ ./prog; echo $? Hello, World! 0
当前可正常运行的4097字节源码:
; prog.nasm bits 32 cpu 386 org 0x1000 ; Linux i386 a.out QMAGIC file format has this. SECTION_text: a_out_header: dw 0xcc ; magic=QMAGIC; Demand-paged executable with the header in the text. The first page (0x1000 bytes) is unmapped to help trap NULL pointer references. db 0x64 ; type=M_386 db 0 ; flags=0 dd SECTION_data - SECTION_text ; a_text=0x1000 (byte size of .text; mapped as r-x) dd SECTION_end - SECTION_data ; a_data=0x1000 (byte size of .data; mapped as rwx, not just rw-) dd 0 ; a_bss=0 (byte size of .bss) dd 0 ; a_syms=0 (byte size of symbol table data) dd _start ; a_entry=0x1020 (in-memory address of _start == file offset of _start + 0x1000) dd 0 ; a_trsize=0 (byte size of relocation info or .text) dd 0 ; a_drsize=0 (byte size of relocation info or .data) _start: mov eax, 4 ; __NR_write mov ebx, 1 ; argument: STDOUT_FILENO mov ecx, msg ; argument: address of string to output mov edx, msg_end - msg ; argument: number of bytes int 0x80 ; syscall mov eax, 1 ; __NR_exit xor ebx, ebx ; argument: EXIT_SUCCESS == 0. int 0x80 ; syscall msg: db 'Hello, World!', 10 msg_end: times ($$ - $) & 0xfff db 0 ; padding to multiple of 0x1000 ; !! is this needed? SECTION_data: db 0 ; times ($$ - $) & 0xfff db 0 ; padding to multiple of 0x1000 ; !! is this needed? SECTION_end:
已发现的限制规则
a_text或a_data为0时,Linux无法运行程序a_text必须是0x1000(4096)的倍数,否则无法运行- 文件大小必须大于等于
a_text + a_data,否则无法运行
由此推导文件大小下限为0x1000 + 1 = 4097字节。
尝试过用nasm -f aout+ld -s -m i386linux、nasm -f elf+ld -s -m i386linux、as -32+ld -s -m i386linux组合生成的4100字节文件无法运行(因a_data为0),给.data段加1字节后文件变为8196字节才能运行,此路径无法得到更小的文件。
问题
是否遗漏了什么?能否做出更小的Linux i386 a.out可执行文件?
解答
结论:无法突破4097字节的下限
根据你总结的Linux内核对a.out格式的校验规则,4097字节确实是QMAGIC格式a.out可执行文件的最小合法大小,原因如下:
- QMAGIC格式要求
a_text必须是页面大小(0x1000)的倍数,且不能为0 a_data不能为0,最小为1字节- 文件总大小必须至少等于
a_text + a_data,即4096+1=4097字节
优化当前代码的小细节
虽然无法突破大小下限,但可以优化源码里的冗余部分,让代码更简洁精准:
- 直接指定
a_text为0x1000、a_data为1,避免计算偏移的冗余 - 精准计算
.text段需要填充的字节数,避免模糊的位运算填充
修改后的最小化源码:
; prog_min.nasm bits 32 cpu 386 org 0x1000 SECTION_text: a_out_header: dw 0xcc ; QMAGIC标识 db 0x64 ; M_386架构类型 db 0 ; 无特殊标志 dd 0x1000 ; a_text = 4096字节(页大小倍数,必须非0) dd 1 ; a_data = 1字节(最小非0值) dd 0 ; 无BSS段 dd 0 ; 无符号表 dd _start ; 程序入口地址 dd 0 ; 无代码段重定位信息 dd 0 ; 无数据段重定位信息 _start: mov eax, 4 ; 系统调用号__NR_write mov ebx, 1 ; 参数:标准输出STDOUT_FILENO mov ecx, msg mov edx, msg_len int 0x80 mov eax, 1 ; 系统调用号__NR_exit xor ebx, ebx ; 参数:退出码0(成功) int 0x80 msg: db 'Hello, World!', 10 msg_len equ $ - msg ; 填充.text段至4096字节 times 0x1000 - ($ - SECTION_text) db 0 SECTION_data: db 0 ; 1字节数据段,满足a_data非0要求 SECTION_end:
编译后文件大小正好是4097字节,运行效果与原代码完全一致。
为什么链接器生成的文件更大?
用ld链接生成a.out时,默认会把.data段强制对齐到页大小(4096字节),所以即使只添加1字节数据,也会被填充到4096字节,导致总大小达到8192字节。而手动编写bin格式的a.out可以绕过这种强制对齐,仅给.data段保留1字节,从而达到理论最小大小。
内容的提问来源于stack exchange,提问作者pts
相关产品推荐
相关产品推荐

