目录
exploit :用于攻击的脚本与方案
payload :利用漏洞时构造的恶意数据
shellcode :包含在恶意数据中的机器码(机器码在攻击时拿到shell)
程序的编译与链接
目前,软件世界有40%的漏洞是由C和C++写成的
cat +文件名 :输出文件内容
elf :Linux下可执行文件格式
从C源代码到可执行文件的生成过程
C源代码经过编译成为汇编代码,汇编代码再经过汇编成为机器码,即生成一个elf(二进制)目标文件,进行链接后成为elf可执行文件
可执行文件
1.定义
(1)广义:文件中的数据是可执行代码的文件
.out、.exe、.sh、.py
(2)狭义:文件中的数据是机器码的文件,CPU可以读取并解析执行的文件
.out、.exe、.dll、.so
对于二进制漏洞,我们研究的都是机器码构成的狭义可执行文件里出现的漏洞
2.分类
(1)Windows:PE
可执行程序 .exe
动态链接库 .dll
静态链接库 .lib
(2)Linux:ELF
可执行程序 .out
动态链接库 .so
静态链接库 .a
链接库为可执行程序提供了代码的具体实现
3.磁盘中的可执行文件叫elf,内存中成为进程。
4.elf文件格式
(1)段视图:在内存中程序加载到内存成为一个进程后,进行读写、执行权限划分的视图
节视图:是一个elf文件,存放在磁盘中,进行不同功能区域划分的视图
(2)code节包含的内容是机器码。不同的分区有不同的功能,根据不同的功能被划分成不同的代码节。
(3)不同的节可能属于一个段。
多个节可以合并成一个段,段在elf文件加载到进程后起作用。
程序装载与虚拟内存
1. 由操作系统完成映射
2.CPU访问的都是物理内存,在pwn中调的都是虚拟内存
3.32位虚拟内存空间大小默认是4G
(1)其中,每一个进程的每一个虚拟内存空间都独享有4G的虚拟内存,4G被划分为1:3的内核空间和用户内存,且内核空间是共享的。
(2)如果有一段共享数据,只需要往物理内存中装载一份,实现节省资源的目的。
(3)64位虚拟内存空间与32位结构相似,只是64位的比32位的空间大很多,且64位空间中有一段undefined region未被利用
4.动态链接库:会被不同的进程使用,所以在物理内存中只载入一份
源代码和内存的映射
1.Bss:存放未初始化的全局变量
未初始化的全局变量占用内存空间,不占用磁盘空间
2.Text:存放代码中只读不能写的数据、函数
3.Stack:存放局部变量、函数调用
4.形参占用的内存空间不一定,在amd64系统中占用的是CPU中的寄存器,在x86中占用的是栈中的某一块空间
大端序和小端序
大端序一般不会出现在pwn的实战中,往往出现在一些高级的pwn,其他的指令机或一些比较复杂的现代化软件中
CPU与进程的执行
1.CPU与内存的交互
PC对应的是CPU中一个特殊的寄存器,他总是保存了当前在内存中代码段中正在执行的代码的地址,这个地址指向的指令会被送到CPU中经过解码、译码,然后执行等,执行完后如果需要的话将指令执行的结果送回内存,大部分情况下指令执行完毕后频繁的访问寄存器。
2.CPU中的register:寄存器
CPU与内存之间传输数据的通道:总线,分为:地址(address)、数据(data)、指令(instructions)
3.存储层次:越靠近CPU的存储器速度越快、存储容量越小、价格越高
最接近CPU的存储器是一组寄存器
4.内存(主存)是直接与CPU沟通的存储器
(1)内存最致命的缺点:断电后不能保存数据
(2)外存:固态硬盘、机械硬盘、芯片、SD卡等
外存中的数据往往是被操作系统挂载到文件系统上进行访问,实际访问到外存的内容说先被加载到内存中,然后与CPU进行交互发挥作用
(3)内存:存放了当前所需要的工作任务和对应工作任务所需要的数据
5.寄存器:存放CPU当前正在进行的指令操作的一些当前指令所用到的一些数据的暂存地等一些作用
寄存器
1.amd64寄存器结构
rax:8Bytes
eax:4Bytes
ax:2Bytes
ah:1Bytes 高位的字节
al:1Bytes 低位的字节
2.部分寄存器的功能
RIP:存放下一条执行的指令的偏移地址
RSP:存放当前栈帧的栈顶偏移地址
RBP:存放当前栈帧的栈底偏移地址
RAX:通用寄存器,存放函数返回值(一般可任意使用)
装载与汇编
静态链接的程序的执行过程
1.fork函数:另起一个新的进程,在这个进程中执行execve系统调用,进入到内核状态,内核去执行图中虚线之下的函数,然后这个新的进程就会进入到start标签去执行二进制文件
2.execve函数:执行后不会返回值。
3._start:静态链接程序执行时,start标签中_libc_start_main依然会执行main函数
动态链接的程序的执行过程
1.在一些数据库存在公共数据时,静态链接的程序会很大,而动态链接的程序则很小。
2.动态链接的程序执行时前面与静态的一样,但是内核装载动态链接程序的时候首先需要装载ld.so(解决库函数的地址与程序调用函数的对应关系),然后依次执行_start,_libc_start_main,_init,main。
常用汇编指令
指令格式
1.偏移与位移(Intel)
偏移(offset)地址:0+2*4=8
位移(displacement),假设 位移为0x10:0+2*4+0x10=24。
偏移是相对于整个文件来说的,位移是相对于偏移地址来说的。
常用指令
1.ADD/SUB
作用:将目的操作数与原操作数相加放到目的操作数位置
用法:ADD 寄存器/内存,立即数
2.PUSH
作用:将RSP减一个字
3.POP
作用:先将栈弹到你所要的寄存器,再RSP往高地址涨0x8
4.LEA
LEA REG,SRC :把源操作数的有效地址送给指定的寄存器
LEA EBX,ASC :取ASC得地址存放至EBX寄存器中
LEA EBX,6[ESI] :把ESI+6单元的32位地址送给EAX
5.MOV
MOV DEST,SRC 把原操作数传给目标
MOV EAX,1234H 执行结果(EAX)=1234H
MOV EBX,EAX
MOV EAX,[00404011H] []表示取地址内的值
MOV EAX,[ESI]
6.CMP
目的操作数-原操作数,但不会把结果放回目的操作数,根据计算结果设置标志位寄存器
7.CALL
push调用函数的下一条指令,然后jmp过去
8.LEAVE
在函数返回时,回复父函数栈帧的指令
9.RET
在函数返回时,控制程序执行流返回父函数的指令
等效于将当前栈指向的东西pop给RIP
10.JMP
两种汇编格式
区别
1.操作数位置
Intel:目的操作数放在前面,原操作数放在后面
at&t:原操作数放在前面,目的操作数放在后面
2.二者对数据大小的描述不同
at&t:描述一个字节的数据为b,两个字节的数据为short,四个字节的数据为long,十个字节的数据为t
Intel:直接根据操作符重载
3.格式
at&t Disp(base,index,scale)
intel [base+index*scale]Disp
4.
Intel表示十六进制的数一般往后加h,at&t表示十六进制的数一般咱前面加0x