我这里是根据hollk师傅的博客来的
可执行文件的装载与进程这部分的内容主要是关于linux的,而不是windows.
一:进程虚拟地址空间先说说程序和进程的区别:
程序(可执行文件):是一个静态的概念,是一些预先编译好的指令和数据集合的一个文件
进程:是一个动态的概念,是程序运行时的一个过程,很多时候把动态库叫做运行时(Runtime)当每一个程序运行起来之后,都会拥有自己的独立虚拟地址空间,而虚拟空间的大小是由CPU的位数决定的,比如,32位的就是 02^32也就是所谓的4G内存,但是程序不能完全掌控这4G的内存,因为进程只能使用操作系统分配给进程的地址,如果访问未经允许的空间,在Linux就会出现”Segmentation“的错误,并且会强制结束进程,未被操作系统分配的内存地址是不合法,不被允许访问的因此整个4G的内存被分为2部分,一部分是1G(0x000000000xFFFFFFFF)的操作系统,另一部分是3G(0x00000000~0xBFFFFFFF)的程序,进程其实不能完全使用这3GB,其中有一部分是预留给其他用途的对于Windows操作系统来说,它的进程虚拟地址空间 ...
深入理解堆这里会详细说明关于堆块的各种细节(跟着WIKI来的)
Unlinkunlink是一种合并堆块的前置操作,主要作用将free的chunk从bin中取出来
malloc
从恰好大小合适的 large bin 中获取 chunk。
这里需要注意的是 fastbin 与 small bin 就没有使用 unlink,这就是为什么漏洞会经常出现在它们这里的原因。
依次遍历处理 unsorted bin 时也没有使用 unlink 。
从比请求的 chunk 所在的 bin 大的 bin 中取 chunk。
free
后向合并,合并物理相邻低地址空闲 chunk。
前向合并,合并物理相邻高地址空闲 chunk(除了 top chunk)。
malloc_consolidate(堆块合并)
后向合并,合并物理相邻低地址空闲 chunk。
前向合并,合并物理相邻高地址空闲 chunk(除了 top chunk)。
realloc
前向扩展,合并物理相邻高地址空闲 chunk(除了 top chunk)。
Unlink的宏由于unlink使用非常频繁,因此特意实 ...
Off-by-One 漏洞分析与利用概述
在刷 BUU 题目时遇到了两道 off-by-one 题目,这里记录一下学习过程。off-by-one 漏洞主要分为两种情况:
off-by-one:单字节溢出,且该字节可控
off-by-null:单字节溢出,但只能溢出
这两次遇到的都是 off-by-one,一般做法是利用溢出的字节修改 chunk 的 size 位,从而造成堆块重叠。基础 Off-by-One 分析
假设存在以下代码:
1234567891011121314#include<stdio.h>#include<stdlib.h>#include<string.h>int main(){ char *ptr1 = malloc(0x10); char *ptr2 = malloc(0x10); read(0,ptr1,0x10+1); free(ptr1); free(ptr2); ptr1 = 0; ptr2 = 0; return 0;}
在第 8 行:read ...
PS:终于要来了吗?我的kernel_pwn?先来学一下内核吧,不能学到最后啥都不会:
学习文章:https://ctf-wiki.org/pwn/linux/kernel-mode/basic-knowledge/
内核结构:操作系统内核(Operation System Kernel)本质上也是一种软件,可以看作是普通应用程式与硬件之间的一层中间层,其主要作用便是调度系统资源、控制 IO 设备、操作网络与文件系统等,并为上层应用提供便捷、抽象的应用接口,这一点相信在学习syscall的时候已经了解过,
大概结构就是将应用与底层的硬件设备分开并且提供链接
操作系统内核实际上是我们抽象出来的一个概念,本质上与用户进程一般无二,都是位于物理内存中的代码 + 数据,不同之处在于当 CPU 执行操作系统内核代码时通常运行在高权限,拥有着完全的硬件访问能力,而 CPU 在执行用户态代码时通常运行在低权限环境,只拥有部分 / 缺失硬件访问能力
这两种不同权限的运行状态实际上是通过硬件来实现的,因此这里我们开始引入新的一个概念——分级保护环
hierarchical protect ...
12345678910111213141516171819202122232425262728293031from pwn import*# from LibcSearcher import*context(os = "linux",arch = "amd64",log_level = "debug")binary = "./ciscn_2019_es_1"if args["REMOTE"]: io = remote("127.0.0.1",8080)else : io = process(binary)elf = ELF(binary)libc = ELF(" /home/source/tools/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc.so.6")s = lambda data :io.send(data)sa = lambda ...
SHELLCODEshellcode 说到底其实就是系统调用命令,跟ret2syscall是很相似的,但是区别就是,shellcode需要有可执行权限,而ret2syscall一般发生在text段上,自动就具备可执行权限
SHELLCODE分为手动生成和机器生产,
机器生成shellcode其实在pwntools中就集成了shellcode的生成,比如:
1shellcode = asm(shellcraft.sh()) #生成用于提权的shllcode
但是一定要注意程序是x86_64的还是i386的,因为shellcraft默认生成32位的
当然还有其他的shellcode
1shhellcode = asm(shellcraft.cat(f*)) #生成用于打印所有f开头的文件
手写shellcode这个才是核心,众所周知,机器生产的shellcode唯一的优势是方便,不用手搓,但是也仅此而已,
所以这里介绍一些写shellcode常用的基本的汇编指令(以x86_64汇编为例)
pop 寄存器名 –>将栈中的下一个4/8字节数的地址弹入对应寄存器中
pu ...
栈基础栈的结构关于栈的定义,在指南中有这么部分的描述:栈空间是计算机内存中确定了的内存区域,也有着一些指针指向相应的内存地址,在x96架构下这个指针位于ESP寄存器,而在x86-64下位于RSP寄存器,
而在计算机底层,栈的主要的用途是:(1):存储局部变量,(2):执行CALL指令调用函数的时候,保存函数地址以便函数结束时正确返回,(3):传递函数参数
栈的主要有两种操作,push 和 pop
12push 用于压栈,将数据压入栈中(栈顶) // 这个过程中需要对ESP/RSP/SP/进行+4/+8的操作,因为只有这样才能使得压进栈的参数正确入栈pop 与push相反,用于出栈,将数据从栈顶移除 // 这个过程中需要对EBP/RBP/BP/进行-4/-8的操作,因为只有这样才能使得参数正确出栈
程序的栈是从进程地址空间的高地址向低地址增长的
那么是怎么确定栈的呢?
答:栈是由无数个栈帧组成,
那么怎么确定一个栈帧呢?
答:两种寄存器,esp(rsp) 和 ebp(rbp) ,esp(rsp)代表了栈顶,而 ebp(rbp) 代表了栈底,二者中间的内存就构造起了一个栈帧
那么 ...

