Hot Time
preparation
萌新友好系列
Part 1 计算机体系结构
古早探索摸鱼时刻
多核CPU(启动启动全部启动!)
·随着制造CPU的工艺方面已经无限逼近物理极限,CPU的频率不再发生质的提高.
·CPU频率短期内不会有大的提高余地,为了更好的提升CPU性能,另辟蹊径选择增加CPU的数量来提高性能,常见的就是对称多处理器(SMP,Symmetrical Multi-Processing)。实际情况并不是CPU越多性能越高,速度的提高和CPU的数量并不成正比。
·目前我们所用的多核处理器,是将多个处理器打包在一起,共享比较昂贵的CPU缓存部件,只保留多个核心,价格比单个核心处理器只贵一点,也就是现在的多核处理器(Multi-core Processor)
系统软件
操作系统内核、驱动程序、运行库、系统工具
编译器,汇编器,链接器等开发工具和库
操作系统的主要功能
·提供抽象接口
·管理硬件资源
·管理CPU
·设备驱动管理内存
·隔离
·分段
·分页
操作系统管理CPU
单道程序(Single Programming)
多道程序(Multi Programming)
分时系统(Time-Sharing System)
多任务系统(Multi-tasking)
操作系统硬盘管理
最终CPU指令为:
out O×1F3, O×00
out O×1F4, O×00
out 0x1F5, 0x03
out O×1F6, OxE8
out O×1F2, Ox08
out O×1F7, O×20
磁盘的扇区和磁道
A: Track 磁盘磁道(粉红色部分)
B: Geometrical sector几何学中的扇形(紫色部分
C: Track sector磁盘扇区(玫红色部分)
D: Cluster块/簇(绿色部分)
![image-20230705093246929](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fcdn.jsdelivr.net%2Fgh%2Fyubsec%2FFirst%40main%2Fimage-20230705093246929.png&pos_id=img-tBUojlbn-1723255964777%29)
最早期磁盘 CLV技术(恒定线速度 通过马达旋转获取数据)
CAV技术(恒定角速度 寿命更长 不停改变马达转速)
ZBA技术(增加数据容量<-增加外部扇区)
每个有编号的扇区组成逻辑扇区(每个大小为512btyes)
每个磁盘有655个磁道 每个次磁道1024个扇区 1个扇区512bytes大小
系统调用接口中软件中断:windows 0x2e linux 0x80
操作系统内存管理
模型I 载入内存不超过物理内存(只能执行一个程序 效率低下)
模型II 会玩了 开新程序承载空间 用完你拜拜(笑)喜欢多开就会乱(maybe有一定的道理bushi)多io接口同时工作 性能低下
问题:1.程序地址空间不隔离
2.内存使用效率极低
3.程序运行时地址不确定(一个崩环环崩【可恶意读写】)
模型III分段管理
模型IV 程序被分为若干个4KB IO效率更高 性能好~(开页保护更好用【可给权限】 多个程序虚拟地址指向同一个内存地址)
地址空间隔离
·页映射的硬件支持
MMU:Memory Management Unit(计算机硬件)
小结
·计算机低速I/O部件由南桥芯片收集信号连接至北桥,北桥通过总线和CPU通信
·操作系统负责提供抽象接口和管理硬件资源
·操作系统使用多任务系统对CPU进行管理
·操作系统使用分页模式对内存进行管理操作系统读写I/O端口来实现对磁盘进行管理
·CPU通过MMU使虚拟地址映射到物理地址
question1:应用程序如何运行库绕过调用系统接口?
答:
question2:在一个内存为256M的计算机上执行程序A(需要内存10M),程序A连续计算并输出所得的质数,同时执行程序B(需要内存5M),B程序从a.txt读入130MB数据,从b.txti读入150MB数据,数据合并后,存入c.txt,CPU是如何运作的?
答:✌✌✌在一个内存容量为256MB的计算机上执行程序A和程序B,以及处理数据合并的操作时,CPU和内存会按照以下流程工作:
-
开始执行程序A和程序B之前,操作系统会负责加载这两个程序到内存中,并为它们分配所需的内存空间。程序A需要10MB内存,程序B需要5MB内存。
-
当程序A开始执行时,它会占用10MB的内存空间。程序A会连续计算并输出所得的质数,计算过程在CPU中进行。由于程序A所需的内存空间相对较小,256MB的内存足够容纳程序A的执行和计算过程。
-
同时执行程序A的过程中,程序B也在执行。程序B需要从a.txt中读入130MB数据和从b.txt中读入150MB数据,这些数据会被存储到内存中。
-
由于计算机内存容量为256MB,在程序A和程序B同时执行的情况下,在存储数据的过程中,需要使用虚拟内存等机制来进行部分数据的临时存储,(280MB>256MB)虚拟内存将一部分数据从内存移动到硬盘上的交换文件中,以释放内存空间给其他数据使用。
-
程序B读入数据完成后,需要将从a.txt和b.txt读取的数据进行合并,并将合并后的数据存储到c.txt中。这个过程需要使用CPU和内存进行数据处理和存储操作。
总体上,在这个情景中,CPU会轮流执行程序A和程序B的指令,通过不断切换执行时间片段来保证它们同时运行。同时,操作系统会负责调度和管理CPU和内存资源,以确保程序A和程序B的运行和数据处理能够顺利进行。
多多STFW有助于成长(●’◡’●)
Part2 编译和链接
■ 编译
■ 预编译
■ 编译
■汇编
■ 目标文件的结构
■ 文件头
■ 段
■ 段表
■ 链接
GCC完成的工作
预编译(PREPRESSING)
■ 将所有的“#define”删除,并且展开其所有的宏定
■ 处理所有条件预编译指令,比如”#if”、“#ifdef”、“#elif”“、“#else”、“#endif”
■ 处理“#include”预编译指令,将被包含的文件插入到该预编译指令的位置,这个过程是递归进行的,被包含文件可以继续包含其他文件。
删除所有的注释"//"和“/* */”
■ 添加行号和文件名标识,比如#2 "““hello.c””2,以便编译时编译器产生调试用的行号信息及用于编译时产生编译错误或警告时能够显示行号。
■保留所有的#pragma编译器指令,因为编译器要使用他们。
编译(COMPILATION)
编译过程是把预编译完的文件内容进行一系列的词法分析,语法分析,语义分析
优化后产生相应的汇编代码文件
通过gcc -S ctf.c -o ctf.s得到汇编文件
汇编器作用就是将汇编代码转变为机器可以执行的指令
每一个汇编语句几乎都对应一条机器指令
只需要对照汇江编指令和机器指令的对照表一—翻译即可。
使用gcc -c hello.s -o hello.o来生成目标文件
使用-m32生成32位的程序
objdump -h生成的目标文件格式
objump -s -d 可查看各个段的内容
关键八段锦
text段 保存编译后的机器指令
data段 保存全局变量和局部静态变量
bss段 保存未初始化的全局变量和未初始化的静态变量
comment段 注释段喽
note_GNU stack 堆栈提示段
eh_frame
其他段
所有的系统段都是以.开头
目标文件可以拥有多个相同名字的段如有多个.text段
目标文件可以自定义段,但是不能使用.开头如目标程序编译时,加入flag段
int pritf(const char* format...)
_attribute_((section("ctf"))) int global_int_var = 447685387//自定义段所需
Special Sections (linuxbase.org)可查看
contents表示该段在文件(ELF)中实际存在
.rodata(只读数据段)注意只有四bytes(%-25 d-64 \n-0a)
不同编译器对未初始化的全局变量和未初始化的局部静态变量存储方式不相同.有些会选择(全局/静态)存入bss段 从而出现size大小不为预估值的情况(如此处不为8为4)
ELF文件描述
ELF文件是编译完毕经过链接后,可以直接运行的文件
ELF文件包含了完整的ELF文件头和段表、字符串表、符号表及其他表
ELF文件是一系列代码、数据、表按照一定规则打包起来的数据包
ELF可执行文件中有一个专门的数据结构叫程序头表(Program Header Table),用来保存Segment信息ELF目标文件不需要被装载,所以没有程序头表.
ELF文件中操作系统对虚拟内存地址分配的内存起始地址0x080408000(64位)0x400000(32位)
ELF文件头
操作系统在加载可执行文件前都会先检验magic是否正确,错误则不进行加载(可防?待验证)
00代表无效文件 01代表32位 02代表64位 第二个01代表端序 版本号1(maybe永1)
1092代表段表在文件中的偏移从1093开始
段描述:段名在段名表中的索引
subsequent problem solving:字节序(大小端问题)
段表类型
具体在cmbat中有详介绍
符号表
有点点错位 可根据文字对应
重定位
Actual combat time
gcc -c hello.c -m32//以32位形式编译 生成hello.o文件
objdump查看基本信息
可以看到基本有八个段
readfile -h hello.o
elf文件头可得出信息:
1.段表从1052开始
2.一个段的段表描述是40个字节
3.一共15个段
4.1052-1652一共15个段(0-14)一个段40bytes 可推出段表名称字符串所在的段的描述信息(最后一个段)
010中查看
11000000段名索引 0000段类型 段标志 段虚拟地址0398段偏移 82段长度
段表字符串所在的段描述
静态链接
静态链接就是将多个目标文件链接在一起并生成可执行文件的过程.
使用ld命令查看到o文件
a.c文件
extern int shared;
extern void swap(int* a,int* b);
int main()
{
int a = 100;
swap(&a,&shared);
}
b.c文件
int shared = 1;
void swap(int *a,int* b)
{
*a ^= b ^= *a ^=*b;
}
关保护编译
gcc -c a.c b.c -fno-stack-protector -m32
chomod +x ./a.o给权限报错
The file './a.o' is marked as an exectutable but could not not be run by the oprating system.//缺少链接 手动整
空间与地址分配
使用ld -m elf_i386 a.o b.o -e main -o ab//进行链接
a.o b.o的bss段均为空
text段0x43+0x4a = 0xbd abtext段为0x91 0x91-0x8d = 0x04进行静态链接多出4个字节
ab生成有顺序的可执行文件中间用8B 04 24 C3进行分割
a.c在生成目标文件的时候,并没有定义share变量和swap函数,但是在没有链接之前,gcc也不能说这两个引用不存在,那么怎么确定是外部引用的符号,还是源代码手滑写错了的符号呢?
那就是用链接器在全局符号表中进行查找,能找到就进行修正虚拟地址,找不到肯定不能链接通过,因为调用了不存在的引用
符号表挖掘
readelf -S ab//自动分析每个段的地址、类型、偏移、长度等
未链接前偏移0x103c 长度为110
计算
每16字节代表一个符号
objdump -r a.o//查看a.o重定位表
根据偏移位置定位对应value的段的开头如0x28为第40个字符
进程虚拟地址空间
所谓的装载也就是将程序执行时所需要的指令和数据放入内存中的过程为了不增加内存数量的前提下,尽可能多的运行程序,采用了动态装载方式
将程序最常用的部分驻留在内存中,而将一些不太常用的数据放在磁盘里面,主要有
·覆盖装入
.页映射
32位操作系统的最大寻址范围为4GB
64位操作系统的最大寻址范围为17179 869 184 GB,即2^64 = 16EiB通常而言,操作系统虚拟地址空间无需这么大的空间,于是通常设定64位系统的虚拟地址寻址空间大小是48位,也就是2^48 = 256TB
覆盖装入:用哪个就叫哪个.(不用挂号就能看病【随便插队】 荒谬)已被淘汰
question:被覆盖的数据就直接报废?(逆天 不会瘫痪吗)
确定空白页的虚拟地址以及文件偏移的映射关系加载到物理内存中,通过mmu建立虚拟地址和物理地址的关系.
页映射
页映射不是一下子把程序的所有数据和指令都装入内存
而是将内存和磁盘中的所有数据和指令按照页(page)为单位划分成若干个页
目前常见的页大小为4096字节,也就是4KB
进程虚拟空间分布
合并相同权限的段(Section)到同一个节(Segment)
对于相同权限的段,把它们合并到一起当作一个节(Segment)进行映射
节(Segment)从装载的角度重新划分了ELF的各个段
gcc -static test2.c-m32 -o test2
#include <stdio.h>
char*
flag="ctfshow{flag_is_here}";
int main{
F("%s ", flag);
return 0;
}
Section和 Segment是对ELF文件描述的两种视图
Section偏向于静态链接(链接视图Linking View)
Segment偏向于动态装载(执行视图Execution View
显示test2节头信息(如果有数据的话)
显示test2程序头(段头)信息(如果有数据的话)
堆和栈
虚拟空间地址(VMA)对进程的地址空间进行管理
程序执行所用到的栈(Stack)、堆(Heap)空间的表现由VMA 来控制使用/proc可以看到VMA地址和堆栈的映射
进程的VMA区域
■ 代码VMA,权限只读、可执行,有映像文件
■ 数据VMA,权限可读写、可执行,有映像文件
■ 堆VMA,权限可读写,可执行:无映像文件,匿名,可向上扩展
■ 栈VMA,权限可读写,不可执行:无映像文件,匿名,可向下扩展
进程栈初始化
·进程刚启动的时候,需要得到进程的启动参数和系统的环境变量,这些数据得保存到一个地方供进程使用
·保存的这个地方需要和进程绑定,因为不同的进程有不同的启动参数,也就是需要一个进程的私有空间来存放,这个存放启动参数和操作系统环境变量的进程私有空间就是栈空间
栈空间
栈是一片内存空间,供程序使用
栈的内存增长方向为向下增长(函数调用栈),即先用高地址,依次向下填充
最后一个地址为栈顶(esp/rsp)内存最低的位置
静态库链接
静态库是一组目标文件的集合
静态库不能直接执行,是开发环境附带的语言库,对操作系统的API进行了二次包装,方便开发程序进行
调用静态库一般由编译器厂商提供, Windows下的Visual C++附带了多个版本的CIC++运行库,Linux下的glibc是提供了libc.a这个静态库文件,里面分别包含了949个和1400个目标文件
静态库包含的目标文件相当于把常用的功能代码写好,通过gcc -c来生成了.o目标文件,方便开发者使用我们常用的printf就是静态库里面附带的函数,我们现在的ab程序之所以不能运行,就是调用了printf这个方法,而程序本身并没有这个目标文件,造成调用了不存在的方法。
objdump -t libc.a//查看函数和目标文件的关系
栈的内存增长方向为向下增长(函数调用栈),即先用高地址,依次向下填充
最后一个地址为栈顶(esp/rsp)内存最低的位置
静态库链接
静态库是一组目标文件的集合
静态库不能直接执行,是开发环境附带的语言库,对操作系统的API进行了二次包装,方便开发程序进行
调用静态库一般由编译器厂商提供, Windows下的Visual C++附带了多个版本的CIC++运行库,Linux下的glibc是提供了libc.a这个静态库文件,里面分别包含了949个和1400个目标文件
静态库包含的目标文件相当于把常用的功能代码写好,通过gcc -c来生成了.o目标文件,方便开发者使用我们常用的printf就是静态库里面附带的函数,我们现在的ab程序之所以不能运行,就是调用了printf这个方法,而程序本身并没有这个目标文件,造成调用了不存在的方法。
objdump -t libc.a//查看函数和目标文件的关系