头和程序头
- 字段不要改为00,改为00可能会认为是有用的,从而给一个默认值
- section_header_table 对程序正常运行 是没有影响的
- elf里的对齐都是内存对齐 ,内存对齐永远都是4k,elf里的align只是参考
-
maps,中r--的段只是为了对齐 对应的r-w rx-为elf中的segement,p_flags可以修改rwx
第一个可读可执行和so中的第一个段是对应的,剩下三个段适合so中的第二个段对应的,,有个段是R--的原因是GNU Read-only After relocation将RW-改为了R--权限 - 非可加载段的内存属性没有用,可加载段的内存属性才有用(Load Segement )
linker
- interpreter path中的linker加载依赖so,系统的linker加载load segement
加载segement
- mmap将segemnt load到内存中
- MAP_PRIVATE: 创建一个私有的写时复制的映射,其他进程看不到映射内容的改变,也不会同步到磁盘中。
- MAP_FIXED:必须使用提供的地址,否则直接失败,不使用该属性,系统可能会给一个其他的地址
- mmap返回地址一定是页(4k)对齐的地址
-
mmap,给出超过映射文件一个页边界的时候,map会成功,但是在范围内读取会bus error
image.png - 通过自己,加载一个ls(没有执行权限),加载到自己的内存空间,进行执行
字符串表,导入库表
- 从dynamic段中找到 05 00 00 00 后的地址就是字符串表的起始地址,
- 导入库表01 00 00 00,后的地址是字符串表中的偏移,加上字符串表的起始地址,可以找到依赖的so
- 定位strtable,两种方法,(2)sections中的.dymstr偏移
符号表 导入表
- dynamic 06 符号表的偏移,,符号表,每个单位16个字节,第一个4个字节为在字符串表中的偏移,第二个四字节为距离最开始加载地址的偏移,第三个四字节为符号大小
- 导出表不包括符号表,
- 02 后导入表尺寸 ,每个8字节,尺寸/8为数量 ,17导入表地址,后三个字节为符号表中的索引,第五个字节为类型,前四个字节为地址,
- linux elf ,导入函数,会遍历依赖so,不能直接知道某函数在具体哪个so
- 导入表的函数会被依赖so中函数的真实地址给覆盖
- linker没有导入表,使用依赖so中的导出表
重定位表
- 11 后为重定位表的地址,12后为重定位表的大小
- 每个八字节,前四为地址,第五为类型,后三为符号索引
哈希表 导出表
- F5FEFF6F,DT_GNU_HASH表
前16个字节,前四个nbucket,第二个四字节为理论chain表距离真实chain表的距离,第三个四字节*4+hash起始位置(暂时没用),之后的为bucket数组起始位置共nbucket项,bucket之后为chain表 - 链表结构,根据hash表,定位导出函数
image.png
基于函数加密的so加固 - 知乎 (zhihu.com)
入口参数 调试技巧
preinitarray -> initarray -> finiarray(反向执行)
通过有权限的elf,加载没权限的elf,执行其函数,调用功能,傀儡进程
elf segemnt fault, 因为memsz 》 filesz,所以需要将多出来的部分置0,不然一旦用到这些部分的值,就会出问题
toybox,一系列函数,可以直接替换ls文件的入口,从而去执行其他的函数