IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> ELF格式解读-程序头与内存布局 -> 正文阅读

[C++知识库]ELF格式解读-程序头与内存布局

前言

ELF格式解读-(1) elf头部与节头

我们知道ELF中有一个头部叫程序头,这个头部专门用于将ELF加载内存中使用。
我们知道操作系统使用页表管理内存,如果一个ELF所有节(section)小于页表大小(一般4k),那么一个节一个页内存会极大浪费内存使用。因此我们会把具有相同属性(比如可读)节合并成一个段(segment)在去加载,那么这个合并信息就在这个程序头中。

如下图中只读节.text .rodata合并为一个段。
在这里插入图片描述

以下本文案例代码:

//main.c
#include <stdio.h>

static int mystaticVar = 3 ;
int myglobalvar=5;
int myglobalvar2=6;
extern void testfun();
int main(){
	testfun();
	printf("hello world %d \r\n",mystaticVar);
	return 0;
}
void hell(){
     testfun();
}
//test.c
 __attribute__((visibility("default"))) void testfun(){
}
__attribute__((visibility("hidden")))  void testfun2(){
}
int libGLobal=2;

相关编译命令

gcc -fPIC -shared -o test.so test.c
gcc -no-pie -o main.out main.c test.so

实战

我们的程序头大小位置由ELF头部指定,具体可参考博主另一文章ELF格式解读-(1) elf头部与节头

程序头其实是一个Elf64_Phdr数组,每个Elf64_Phdr就是一个段,但是不是所有的段都会被加载到内存中。

typedef struct {
	Elf32_Word	p_type;
	Elf32_Off	p_offset;
	Elf32_Addr	p_vaddr;
	Elf32_Addr	p_paddr;
	Elf32_Word	p_filesz;
	Elf32_Word	p_memsz;
	Elf32_Word	p_flags;
	Elf32_Word	p_align;
} Elf32_Phdr;

typedef struct {
	Elf64_Word	p_type;
	Elf64_Word	p_flags;
	Elf64_Off	p_offset;
	Elf64_Addr	p_vaddr;
	Elf64_Addr	p_paddr;
	Elf64_Xword	p_filesz;
	Elf64_Xword	p_memsz;
	Elf64_Xword	p_align;
} Elf64_Phdr;

p_type

这个字段决定这个段是什么类型,下面是字段枚举表:

Namevalue
PT_NULL0
PT_LOAD1
PT_DYNAMIC2
PT_INTERP3
PT_NOTE4
PT_SHLIB5
PT_PHDR6
PT_TLS7
PT_LOOS0x60000000
PT_HIOS0x6fffffff
PT_LOPROC0x70000000
PT_HIPROC0x7fffffff
PT_GNU_EH_FRAME0x6474e550
PT_GNU_STACK0x6474e551
PT_GNU_RELRO0x6474e552

上面虽然类型很多,但是除了PT_LOAD以及PT_GNU_RELRO会被加载内存映射中,其他类别段是不会占内存的,只是提示性信息给加载器等。

PT_LOAD:这个类型会进行内存映射,比如只读代码段,数据段等
PT_GNU_RELRO:这个类型会进行内存映射,主要用于防止got表篡改,这个段在加载前是可读写的,用于重定位got,加载完成后变为只读。

p_flags

这个节是否可读可写可执行

以下是枚举值,可以进行组合表示可读可写可执行

Namevalue
PF_X0x1
PF_W0x2
PF_R0x4

p_offset

这个段所在文件的偏移

p_vaddr

内存中的虚拟地址

p_paddr

一般和p_vaddr相同

p_filesz

这个段在文件中大小

p_memsz

这个段在内存中的大小(一定会大于等于p_filesz,如果大于一般为对齐原因)

p_align

对齐数值


我们可以使用readelf -l去查看一个程序的程序头

在这里插入图片描述
我们来看看第二个LOAD信息

Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
LOAD           0x001000 0x0000000000401000 0x0000000000401000 0x000225 0x000225 R E 0x1000


03     .init .plt .plt.sec .text .fini

这个段是 .init .plt .plt.sec .text .fini这几个节组合体,段大小为0x000225,我们再看看这些节信息
在这里插入图片描述
我们手动计算这些节大小总和:

.init  00001b
.plt 000030
.plt.sec 000020
.text 0001a5
.fini 00000d

00001b+000030+000020+0001a5+00000d = 21D

我们计算得到21D大小,但是段显示确实225,那么多出的8字节来自哪?

其实部分个节区会间隔若干0填充的字节,这里正好是8

比如本例中.text.fini间隔3字节,.init.plt间隔5字节,一个8字节。如下图所示
在这里插入图片描述
在这里插入图片描述

因为段数据是连续的,所以包含上面的5字节。

我们运行本例程序查看实际内存布局:
使用gdb挂起程序:
在这里插入图片描述
我们linux的虚拟文件系统查看内存上的映射关系:
在这里插入图片描述

GUN_RELRO并没有按照参照程序头那样划分内存

参考

Why ELF program headers have two LOAD entries, while the program layout three sections

Hardening ELF binaries using Relocation Read-Only (RELRO)

[阅读型]CTF中linux pwn的四大基本防御措施

Program Header

11.2. Program Header

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-05-05 10:59:30  更:2022-05-05 11:03:23 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/21 2:11:21-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码