syzkaller_2_principle
syzkaller(2):原理与测试
流程图
上图为流程图。
syz-manager通过ssh调用syz-fuzzer;
syz-fuzzer和syz-manager之间通过RPC进行通信;
syz-fuzzer将输入传给syz-executor,从kernel中读取代码覆盖率信息;
syz-executor执行syscall系统调用。
目录结构
文件/文件名
说明/用途
Godeps
golang依赖包管理
dashbord
与syzbot相关,syzbot会在fuzz后向内核邮件列表报告发现的错误,内容在https://syzkaller.appspot.com上
docs
相关文档
executor
用于执行系统调用
pkg
配置文件目录
prog
目标系统信息以及需要执行的系统调用
sys
系统调用描述
在pkg目录中又有如下的结构:
ast目录:解析并格式化sys文件。
bisect目录:通过二分查找,编译代码测试确定引入含有漏洞代码的commit和引入修复的commit。
build目录:包含用于构建内核的辅助函数。
comp ...
零散知识点
零散的知识点
一些零散的知识点,用于简单快速回顾;
函数调用约定
cdecl(C declaration),C语言的调用约定;
在x86上有:
函数实参在线程栈上按照从右至左的顺序依次压栈。
函数结果保存在寄存器EAX/AX/AL中
浮点型结果存放在寄存器ST0中
编译后的函数名前缀以一个下划线字符
调用者负责从线程栈中弹出实参(即清栈)
8比特或者16比特长的整形实参提升为32比特长。
受到函数调用影响的寄存器(volatile registers):EAX, ECX, EDX, ST0 - ST7, ES, GS
不受函数调用影响的寄存器: EBX, EBP, ESP, EDI, ESI, CS, DS
RET指令从函数被调用者返回到调用者(实质上是读取寄存器EBP所指的线程栈之处保存的函数返回地址并加载到IP寄存器)
x86-64上用寄存器传参:
rdi rsi rdx rcx r8 r9
而且栈顶之外还有128bytes的红区可以使用,如果够用则不必用rsp开新空间;
寻址方式
在x86下:
32位基址寄存器是:EAX、EBX、ECX、EDX、ESI、EDI、EBP和ESP ...
Linux kernel source(1):Directory
Linux kernel source(1):Directory
目录
目录
描述
arch
特定体系结构的源码
block
块设备I/O层
crypto
加密API
Documentation
内核源码文档
drivers
设备驱动程序
firmware
使用某些驱动程序而需要的设备固件
fs
VFS和各种文件系统
include
内核头文件
init
内核引导和初始化
ipc
进程间通信代码
kernel
像调度程序这样的核心子系统
lib
通用内核函数
mm
内存管理子系统和VM
net
网络子系统
samples
示例,示范代码
scripts
编译内核所用的版本
security
Linux安全模块
sound
语音子系统
usr
早期用户空间代码(所谓的initramfs)
tools
在Linux开发中有用的工具
virt
虚拟化基础结构
文件
文件
描述
COPYING
内核许可证
CREDITS
开发者列表
MAINTAINERS
维护者列表
...
Linux kernel source(2):Kernel Boot
Linux kernel source(2):kernel boot
引导
按下电源开关:
CPU
CPU初始化,实模式下运行,首先执行0xfffffff0地址的内容,是一个跳转,跳转到BIOS;
BIOS
BIOS初始化,检查硬件状态,寻找可引导设备,跳转到引导扇区;
定位主引导扇区(数据446bytes,0xaa55结束)
MBR分区,初始化,把第一个扇区的内容加载到内存0x7c00处,即bootloader处,跳转执行;
BootLoader
GRUB2是一种BootLoader;
BootLoader执行boot.img;
boot.img跳转到GRUB2的diskboot.img,加载内核代码和文件系统到内存;
GRUB2运行grub_main函数,初始化控制台,加载配置文件,设置root设备,加载模块,切换为normal模式;
normal模式调用grub_normal_execute完成最后的准备工作,显示可用操作系统菜单;
用户选择后,调用grub_menu_execute_entry,运行boot命令,引导操作系统到内存中;
物理内存布局
| ...
glibc2.23_ptmalloc
glibc2.23 ptmalloc2分析
malloc
__libc_malloc
跳过了初始化和定义的部分,先从__libc_malloc开始;
void *__libc_malloc (size_t bytes){ mstate ar_ptr; void *victim; void *(*hook) (size_t, const void *) = atomic_forced_read (__malloc_hook); if (__builtin_expect (hook != NULL, 0)) return (*hook)(bytes, RETURN_ADDRESS (0));
首先判断了是否定义了hook函数,如果hook != NULL,则return到该函数地址,如果没有hook函数,则执行:
arena_get (ar_ptr, bytes);/* thread_arena = &main_arena;#define arena_get(ptr, size) do { \ ptr = thread_ar ...
dynamic_link
dynamic_link
动态链接解决了静态链接带来的内存、磁盘、程序开发和发布的问题;
简单说,动态链接就是推迟了链接的进行,等到程序要运行时才链接;如果内存中已加载了目标文件副本,则不必再重新加载一份;
延迟绑定
重要的是延迟绑定的概念,延迟绑定优化了动态链接的部分性能;
动态链接导致了大量的函数引用,因此执行前会耗费时间解决函数引用符号和重定位;
延迟绑定是指,函数第一次用到时才进行绑定(符号查找和重定位),如果不用到就不用耗费开销,这借助了PLT表;
例如当libc.so第一次调用bar()函数时,需要调用动态链接器中的某个函数来完成绑定工作,在glibc中,这个函数就是__dl_runtime_resolve1();
第一次调用bar()函数时,通过一个PLT项的结构来进行跳转;
bar@plt的实现:
bar@plt:jmp *(bar@got)push npush moduleIDjmp __dl_runtime_resolve
在第一次调用中,*(bar@got)的内容并非真正的bar()地址,而是push n的地址,这个n事实上是bar符号引用在重定位表.rel.pl ...
load
Load
程序和进程
程序是静态的可执行文件,进程是动态的运行过程;
每个程序运行起来后,即进程,都拥有自己独立的虚拟地址空间;
其中一部分是操作系统内核空间,另一部分是抽象的独立的进程空间;
程序员角度的装载
程序执行和数据必须在内存中才能被CPU运行;
动态装入,程序最常用的部分驻留与内存中,不常用的数据置于磁盘中;
覆盖装入
共享则相互覆盖–树型调用
页映射
分成若干页,装载和操作的单位是页,一般4KB;
用到哪一页就装入,考虑算法
FIFO(先进先出)
LUR(使用频率)
操作系统角度的装载
进程建立
创建一个独立虚拟地址空间,实际上是创建页映射函数,映射到物理地址
读取可执行文件的文件头,建立与地址空间的映射关系,这里的粒度0x1000 = 4096 bytes = 4KB
这样的映射关系实际上只保存在一个数据结构中;
指令寄存器设置为可执行文件的入口地址,即ELF header中保存的入口地址,启动运行
此时,控制权转交给了进程,涉及到了用户态和内核态的切换,CPU权限的切换;;
页错误
完成上面三个步骤后,指令和数据还没有被载入内存,只是完成了载入的 ...
PE文件
windows PE/COFF
COFF -> ELF/PE
书中,目标文件默认为COFF,可执行文件默认为PE;
.text -> .code (visual c++).text -> CODE (Borland)
段名没有实际意义,除了链接脚本控制链接的时候;
sample.c --cl--> sample.obj --linker--> sample.exe
使用dumpbin来查看obj文件;
COFF文件格式
PE文件格式
IMAGE_NT_HEADERS是真正的PE文件头,其偏移信息保存在IMAGE_DOS_HEADER中,该信息为e_lfanew成员,如果为0,表示该PE文件为DOS的MZ文件,将会用DOS子系统执行,如果不为0,如上所示,则代表偏移;
在IMAGE_NT_HEADERS中,包含一个标记(0x00004550,即PE\0\0)以及两个结构体(Image Header、Image Optional Header);
类似于ELF文件的信息放在了数据目录Data Directory中;
static_link
静态链接
空间地址分配
按序叠加
相似合并
空间地址分配:扫描、收集(符号定义和引用到全局符号表)、合并、建立映射
符号解析与重定位:读取段信息、重定位信息,进行符号解析和重定位
// a.cextern int shared;int main(){ int a = 100; swap(&a, &shared);}// b.cint shared = 1;void swap(int *a, int *b){ *a ^= *b ^= *a ^= *b;}
生成目标文件;
gcc -c a.c b.c
进行链接;
ld a.o b.o -e main -o ab
链接后各个段的虚拟地址就确定了;
接着开始计算各个符号的虚拟地址;
例如main函数,原本在a.o中与.text的偏移offset为x,链接后,.text的虚拟地址假设为0x08048094,则main的地址为0x08048094 + x;
符号解析与重定位
符号解析
a.c中引用了shared变量,并且调用了swap外部函数;
在a.c编译成a.o时, ...
CVE-2016-5195
CVE-2016-5195_dirtycow
环境准备
内核源码下载与解压
sudo wget https://mirror.tuna.tsinghua.edu.cn/kernel/v4.x/linux-4.4.tar.xzxz -d linux-4.4.tar.xztar -xvf linux-4.4.tarcd linux-4.4
编译
sudo make bzImage -j4
这里的文件系统就随便拿了CTF内核题目的一个rootfs.cpio,启动脚本boot.sh也是题目里给的,在自己创建的core文件夹里解压后,把exp编译好了丢进去,改一下init文件,然后重新打包,mv到主目录;
mkdir coremv rootfs.cpio core/rootf.cpio.gzcd coregunzip rootfs.cpio.gzcpio -idmv > rootfs.cpiocp pathto/dirtyc0w.c .gcc -pthread -static dirtyc0w.c -o dirtyc0wvim initfind . | cpio -o --format=n ...