标题: 修改ELF的p_flags 创建: 2025-03-31 09:38 更新: 链接: https://scz.617.cn/unix/202503310938.txt -------------------------------------------------------------------------- 目录: ☆ 背景介绍 ☆ p_flags ☆ sample_1 ☆ 修改p_flags 1) 010 Editor 2) LIEF ☆ objcopy不适用 ☆ 检查sample_1_patch的.bss可执行 -------------------------------------------------------------------------- ☆ 背景介绍 在CTF类的比赛场景中,在Exploit学习环境中,为聚焦特定技术,会刻意降低其他技 术门槛,比如没有"stack canary"、栈区可执行、禁用ASLR,等等。 以前我有个ELF演示,要求代码段可写,正常情况下这是不可能的,需要自己Patch目 标ELF。 有些Exploit教程,在它们写就的时代,内存权限只分读写,不分执行,可读即可执 行。到了现代,这种前置条件已然失效。同样,出于聚焦的考虑,可以Patch目标ELF, 为目标内存区域增加执行权限。本文就这类特定Patch做一介绍。 ☆ p_flags 关于ELF规范,参看 https://www.muppetlabs.com/~breadbox/software/ELF.txt 上文只说了32位ELF,从框架上讲,64位ELF没有本质区别。ELF有两种头,一种是 "Program Header",另一种是"Section Header"。为了执行ELF,只需要Phdr,不需 要Shdr。打造极小ELF时,会删掉Shdr,当然,这需要特定工具。 系统加载ELF时,会根据Phdr中p_flags字段设置相应段的内存权限,比如RWX。 -------------------------------------------------------------------------- /* * /usr/include/elf.h */ typedef struct { Elf64_Word p_type; /* Segment type */ Elf64_Word p_flags; /* Segment flags */ Elf64_Off p_offset; /* Segment file offset */ Elf64_Addr p_vaddr; /* Segment virtual address */ Elf64_Addr p_paddr; /* Segment physical address */ Elf64_Xword p_filesz; /* Segment size in file */ Elf64_Xword p_memsz; /* Segment size in memory */ Elf64_Xword p_align; /* Segment alignment */ } Elf64_Phdr; #define PF_X (1 << 0) /* Segment is executable */ #define PF_W (1 << 1) /* Segment is writable */ #define PF_R (1 << 2) /* Segment is readable */ -------------------------------------------------------------------------- /* * /usr/include/linux/elf.h */ typedef struct elf64_phdr { Elf64_Word p_type; Elf64_Word p_flags; Elf64_Off p_offset; /* Segment file offset */ Elf64_Addr p_vaddr; /* Segment virtual address */ Elf64_Addr p_paddr; /* Segment physical address */ Elf64_Xword p_filesz; /* Segment size in file */ Elf64_Xword p_memsz; /* Segment size in memory */ Elf64_Xword p_align; /* Segment alignment, file & memory */ } Elf64_Phdr; #define PF_R 0x4 #define PF_W 0x2 #define PF_X 0x1 -------------------------------------------------------------------------- ☆ sample_1 https://github.com/angr/angr-examples/blob/master/examples/secuinside2016mbrainfuzz/sample_1 sample_1是angr-examples中的一个ELF,用于练习angr符号执行,最初是2016年提供 的。不知原提供者在什么环境中测试它,只知当时的假设是,.bss可执行。时至2025, x64/Ubuntu 22.04.1 LTS中,sample_1的.bss实际不可执行。 -------------------------------------------------------------------------- $ readelf -Wl sample_1 ... Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align PHDR 0x000040 0x0000000000400040 0x0000000000400040 0x0001f8 0x0001f8 R E 0x8 INTERP 0x000238 0x0000000000400238 0x0000000000400238 0x00001c 0x00001c R 0x1 [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2] LOAD 0x000000 0x0000000000400000 0x0000000000400000 0x0050cc 0x0050cc R E 0x200000 LOAD 0x005e10 0x0000000000605e10 0x0000000000605e10 0x000250 0x000530 RW 0x200000 ... Section to Segment mapping: Segment Sections... 00 01 .interp 02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame 03 .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss ... -------------------------------------------------------------------------- .bss对应Phdr[03],这是第二个LOAD段,其Flg列显示RW,没有E,表示可读写、不可 执行。 sample_1特别构造过,无"stack canary",栈区可执行,理论上利用RetAddr跳到 stack,可执行shellcode。但ASLR使得stack地址浮动,原解题人选择跳到.bss中, 那里有argv[1]的拷贝,是sscanf灌进去的。他们当年做题时,.bss应该有执行权限。 本题主要目的是演示angr求解以及radare2静态分析,为演示全套,必须手工Patch sample_1,让.bss可执行。 ☆ 修改p_flags 1) 010 Editor 010 Editor有ELF模板,定位到Phdr[03]的p_flags后,有下拉框可选,RWX分别对应 一个二进制位,7表示RWX,6表示RW-。 假设sample_1_patch是010 Editor的修改结果,只修改了一个字节,这是最优方案。 $ radiff2 sample_1 sample_1_patch 0x000000ec 06 => 07 0x000000ec 2) LIEF 有个Python模块LIEF用于静态修改ELF,参看: 《将PIE可执行程序转换成动态链接库》 https://scz.617.cn/unix/202012241548.txt -------------------------------------------------------------------------- import lief binary = lief.parse( './sample_1' ) # # 提前用"readelf -Wl"确定Phdr[i] # seg = binary.segments[3] seg.flags |= lief._lief.ELF.Segment.FLAGS.X binary.write( './sample_1_patch_1' ) -------------------------------------------------------------------------- LIEF修改的sample_1_patch_1,不是简单修改字节,实际是重新构造ELF。虽然大小 未变,但字节变化非常多,相比之下010 Editor更聚焦。 $ radiff2 sample_1 sample_1_patch_1 (略) ☆ objcopy不适用 参看 《24.1 如何使代码段可写》 $ objdump -wh sample_1 | grep -P '\.bss' 25 .bss 000002e0 0000000000606060 0000000000606060 00006060 2**5 ALLOC $ objcopy --set-section-flags .bss=ALLOC,LOAD,CODE sample_1 sample_1_patch_bad $ objdump -wh sample_1_patch_bad | grep -P '\.bss' 25 .bss 000002e0 0000000000606060 0000000000606060 00006060 2**5 CONTENTS, ALLOC, LOAD, CODE 前述objcopy命令修改Shdr中sh_flags字段,这并不影响内存中.bss的执行权限,属 于无效Patch。 -------------------------------------------------------------------------- /* * /usr/include/elf.h */ typedef struct { Elf64_Word sh_name; /* Section name (string tbl index) */ Elf64_Word sh_type; /* Section type */ Elf64_Xword sh_flags; /* Section flags */ Elf64_Addr sh_addr; /* Section virtual addr at execution */ Elf64_Off sh_offset; /* Section file offset */ Elf64_Xword sh_size; /* Section size in bytes */ Elf64_Word sh_link; /* Link to another section */ Elf64_Word sh_info; /* Additional section information */ Elf64_Xword sh_addralign; /* Section alignment */ Elf64_Xword sh_entsize; /* Entry size if section holds table */ } Elf64_Shdr; #define SHF_WRITE (1 << 0) /* Writable */ #define SHF_ALLOC (1 << 1) /* Occupies memory during execution */ #define SHF_EXECINSTR (1 << 2) /* Executable */ -------------------------------------------------------------------------- /* * /usr/include/linux/elf.h */ typedef struct elf64_shdr { Elf64_Word sh_name; /* Section name, index in string tbl */ Elf64_Word sh_type; /* Type of section */ Elf64_Xword sh_flags; /* Miscellaneous section attributes */ Elf64_Addr sh_addr; /* Section virtual addr at execution */ Elf64_Off sh_offset; /* Section file offset */ Elf64_Xword sh_size; /* Size of section in bytes */ Elf64_Word sh_link; /* Index of another section */ Elf64_Word sh_info; /* Additional section information */ Elf64_Xword sh_addralign; /* Section alignment */ Elf64_Xword sh_entsize; /* Entry size if section holds table */ } Elf64_Shdr; #define SHF_WRITE 0x1 #define SHF_ALLOC 0x2 #define SHF_EXECINSTR 0x4 -------------------------------------------------------------------------- ☆ 检查sample_1_patch的.bss可执行 gdb -q -nx --args ./sample_1_patch anything starti | info proc mappings | grep 0x605000 显示 0x605000 0x607000 0x2000 0x5000 rwxp /path/sample_1_patch Perms列显示rwxp,读/写/执行,.bss位于此内存范围。