linux - 创建带有只读标志的程序头会导致段错误
问题描述
我一直在使用 NASM 编写 ELF 二进制文件,并创建了一个打开了只读标志的段。运行程序会导致段错误。我在 replit 中测试了程序,它运行得很好,那么问题是什么?我用 .rodata 部分中的 hello world 字符串创建了一个常规的 NASM hello world 程序,并且运行良好。我用 readelf 检查了二进制文件,以确保字符串在只读段中。
我想出的唯一解决方案是在rodata段中设置可执行标志,使其具有读取/执行权限,但这很麻烦,我希望rodata段是只读的。
这是 ELF-64 hello world 的代码。
; hello.asm
[bits 64]
[org 0x400000]
fileHeader:
db 0x7F, "ELF"
db 2 ; ELF-64
db 1 ; little endian
db 1 ; ELF version
db 0 ; System V ABI
db 0 ; ABI version
db 0, 0, 0, 0, 0, 0, 0 ; unused
dw 2 ; executable object file
dw 0x3E ; x86-64
dd 1 ; ELF version
dq text ; entry point
dq 64 ; program header table offset
dq nullSection - $$ ; section header table offset
dd 0 ; flags
dw 64 ; size of file header
dw 56 ; size of program header
dw 3 ; program header count
dw 64 ; size of section header
dw 4 ; section header count
dw 3 ; section header string table index
nullSegment:
times 56 db 0
textSegment:
dd 1 ; loadable segment
dd 0x4 ; read / execute permissions
dq text - $$ ; segment offset
dq text ; virtual address of segment
dq 0 ; physical address of segment
dq textSize ; size of segment in file
dq textSize ; size of segment in memory
dq 0x1000 ; alignment
rodataSegment:
dd 1 ; loadable segment
dd 0x4 ; read permission (setting this flag to 0x5 causes the program to run just fine)
dq rodata - $$ ; segment offset
dq rodata ; virtual address of segment
dq 0 ; physical address of segment
dq rodataSize ; size of segment in file
dq rodataSize ; size of segment in memory
dq 0x1000 ; alignment
text:
mov rax, 1
mov rdi, 1
mov rsi, message
mov rdx, messageLength
syscall
mov rax, 60
xor rdi, rdi
syscall
textSize equ $ - text
rodata:
message db "Hello world!", 0xA, 0
messageLength equ $ - message
rodataSize equ $ - rodata
stringTable:
db 0
db ".text", 0
db ".rodata", 0
db ".shstrtab", 0
stringTableSize equ $ - stringTable
nullSection:
times 64 db 0
textSection:
dd 1 ; index into string table
dd 1 ; program data
dq 0x6 ; occupies memory & executable
dq text ; virtual address of section
dq text - $$ ; offset of section in file
dq textSize ; size of section in file
dq 0 ; unused
dq 0x1000 ; alignment
dq 0 ; unused
rodataSection:
dd 7 ; index into string table
dd 1 ; program data
dq 0x2 ; occupies memory
dq rodata ; virtual address of section
dq rodata - $$ ; offset of section in file
dq rodataSize ; size of section in file
dq 0 ; unused
dq 0x1000 ; no alignment
dq 0 ; unused
stringTableSection:
dd 15 ; index into string table
dd 3 ; string table
dq 0 ; no attributes
dq stringTable ; virtual address of section
dq stringTable - $$ ; offset of section in file
dq stringTableSize ; size of section in file
dq 0 ; unused
dq 0 ; no alignment
dq 0 ; unused
replitHello.asm: https://hastebin.com/ujanoguveq.properties // 它应该是几乎同一行
这是最小的 nasm hello world 程序。
; helloNasm.asm
section .text
global _start
_start:
mov rax, 1
mov rdi, 1
mov rsi, message
mov rdx, messageLength
syscall
mov rax, 60
xor rdi, rdi
syscall
section .rodata
message db "Hello NASM!", 0xA, 0
messageLength equ $ - message
解决方案
textSegment:
dd 1 ; loadable segment
dd 0x4 ; read / execute permissions
我假设您的意思0x5
是上面的标志。
固定后,我看到以下部分:
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
NULL 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 0
LOAD 0x0000e8 0x00000000004000e8 0x0000000000000000 0x000025 0x000025 R E 0x1000
LOAD 0x00010d 0x000000000040010d 0x0000000000000000 0x00000e 0x00000e R 0x1000
mmap
这要求内核在同一地址执行两个s ( 0x400000
)。这些mmap
s 中的第二个映射在第一个之上,结果如下/proc/$pid/maps
:
00400000-00401000 r--p 00000000 fe:02 22548440 /tmp/t
7ffff7ff9000-7ffff7ffd000 r--p 00000000 00:00 0 [vvar]
7ffff7ffd000-7ffff7fff000 r-xp 00000000 00:00 0 [vdso]
7ffffffdd000-7ffffffff000 rw-p 00000000 00:00 0 [stack]
如您所见,程序文本是不可执行的,因此程序SIGSEGV
s 在第一条指令上:
(gdb) run
Starting program: /tmp/t
Program received signal SIGSEGV, Segmentation fault.
0x00000000004000e8 in ?? ()
(gdb) x/i $pc
=> 0x4000e8: mov $0x1,%eax
要解决此问题,您必须将其中一个段移动到不同的页面(正如 Jester 正确指出的那样)。
另请注意,部分是完全不必要的(只有部分很重要)。特别是在该部分中设置A X
标志.text
对任何事情都没有影响。
推荐阅读
- python - Groupby 同时按两个不同的选项
- ansible - 如何以用户身份运行 list_vms 检查(似乎以 root 身份运行)?
- sql - 使用两列的 SQL 日期范围
- node.js - NodeJS:在异步函数之外获取值
- java - 多个不区分大小写的字符串替换
- tensorflow - Conv1D(filters=N, kernel_size=K) 与 Dense(output_dim=N) 层
- javascript - 单击外部引导模式内容时如何重定向?
- c++ - C++ 如何使用类和函数将我的代码转换为 OOP?
- python - 如何在使用 Python 的密钥未知时解析 Json?
- android - 如何在android studio中记录文本文件上的按钮推送?