GNU_RELRO程序头未页对齐时内存保护机制问询
RELRO保护与非页对齐GNU_RELRO段的工作机制
RELRO(Relocation Read-Only)的核心作用是在程序完成动态链接重定位后,将原本可写的重定位相关内存区域设为只读,防止篡改攻击。你观察到GNU_RELRO段头未按页对齐的情况是正常的,内存权限的页级分配和RELRO保护并不冲突,具体逻辑如下:
1. GNU_RELRO的本质是「范围标记」而非「内存段」
GNU_RELRO是程序头中的一个标记项,它的唯一作用是告诉动态加载器(如ld-linux-aarch64.so.1):在重定位完成后,把从VirtAddr到VirtAddr+MemSiz这个地址范围内的内存改成只读。它不需要页对齐,因为这个范围只是逻辑上的保护区域,而非实际的内存页划分。
2. 加载器自动处理页边界的权限转换
内存权限确实以页为单位分配(AArch64默认页大小为4KB,即0x1000),加载器会按以下逻辑处理:
- 计算RELRO范围覆盖的所有内存页:包括包含RELRO起始地址的页、所有完整覆盖的中间页,以及包含RELRO结束地址的页。
- 将这些页的权限从原本的
RW(因为RELRO区域属于第二个LOAD段,默认是可写的)修改为R。
以你提供的程序头为例:
- GNU_RELRO的起始地址是
0x42e58,结束地址是0x42e58 + 0x11a8 = 0x43ff0。 - 对应的内存页是
0x42000~0x43000和0x43000~0x44000两个页。 - 加载器会把这两个页的权限从
RW改为R,而后续的.data段起始于0x44340,属于0x44000~0x45000页,仍保持RW权限,不会被误保护。
3. 链接器的布局保证不会冲突
GNU链接器在生成可执行文件时,会自动规划内存布局:
- 将需要RELRO保护的段(
.init_array、.fini_array、.data.rel.ro、.dynamic,甚至Full RELRO下的.got/.got.plt)集中放在LOAD段的前部。 - 将不需要保护的可写段(
.data、.bss)放在RELRO范围之后的独立页中。
这就确保了加载器修改页权限时,不会影响到需要保持可写的区域。
你的程序头中的验证
看Section Headers:
- RELRO覆盖的区域包含
.init_array(0x42e58)、.fini_array(0x42e60)、.data.rel.ro(0x42e68)、.dynamic(0x43990),直到0x43ff0。 - 之后的
.got.plt起始于0x43fe8,属于RELRO范围,说明这是Full RELRO配置,.got.plt也会被设为只读;而.data起始于0x44340,已经在下一个页,不受影响。
Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align ... LOAD 0x032e58 0x0000000000042e58 0x0000000000042e58 0x0028d8 0x051018 RW 0x10000 ... GNU_RELRO 0x032e58 0x0000000000042e58 0x0000000000042e58 0x0011a8 0x0011a8 R 0x1
内容的提问来源于stack exchange,提问作者Fee
相关产品推荐
相关产品推荐

