1、sync_fuzzers(char **argv)该函数是读取其他fuzz的queue中的case文件,然后保存到自己的queue里
static void sync_fuzzers ( char * * argv) {
DIR* sd;
struct dirent * sd_ent;
u32 sync_cnt = 0 ;
sd = opendir ( sync_dir) ; 打开sync_dir文件夹
if ( ! sd) PFATAL ( "Unable to open '%s'" , sync_dir) ;
stage_max = stage_cur = 0 ;
cur_depth = 0 ;
while ( ( sd_ent = readdir ( sd) ) ) { 用while 循环遍历sync_dir下面的所有文件
static u8 stage_tmp[ 128 ] ;
DIR* qd;
struct dirent * qd_ent;
u8 * qd_path, * qd_synced_path;
u32 min_accept = 0 , next_min_accept;
s32 id_fd;
if ( sd_ent-> d_name[ 0 ] == '.' || ! strcmp ( sync_id, sd_ent-> d_name) ) continue ;
跳过. 开头的文件和sync_id即我们自己的输出文件夹
qd_path = alloc_printf ( "%s/%s/queue" , sync_dir, sd_ent-> d_name) ;
if ( ! ( qd = opendir ( qd_path) ) ) {
ck_free ( qd_path) ;
continue ;
}
qd_synced_path = alloc_printf ( "%s/.synced/%s" , out_dir, sd_ent-> d_name) ;
id_fd = open ( qd_synced_path, O_RDWR | O_CREAT, 0600 ) ; 打开out_dir/ . synced/ sd_ent-> d_name,返回到id_fd
if ( id_fd < 0 ) PFATAL ( "Unable to create '%s'" , qd_synced_path) ;
if ( read ( id_fd, & min_accept, sizeof ( u32) ) > 0 )
读取out_dir/ . synced/ sd_ent-> d_name文件即id_fd里的前(sizeof ( u32) )4 个字节到min_accept里
lseek ( id_fd, 0 , SEEK_SET ) ; 然后lseek从新调整文件内指针到开头。
next_min_accept = min_accept;
设置next_min_accept为min_accept,这个值代表之前从这个文件夹里读取到的最后一个queue的id。
sprintf ( stage_tmp, "sync %u" , ++ sync_cnt) ; sync_cnt计数加一,由"sync %u" 格式化到stage_tmp中。
stage_name = stage_tmp;
stage_cur = 0 ;
stage_max = 0 ;
while ( ( qd_ent = readdir ( qd) ) ) { 利用readdir ( qd) 进一步取出目录中的文件
u8* path;
s32 fd;
struct stat st;
if ( qd_ent-> d_name[ 0 ] == '.' ||
sscanf ( qd_ent-> d_name, CASE_PREFIX "%06u" , & syncing_case) != 1 ||
syncing_case < min_accept) continue ;
跳过. 开头的文件和标识小于min_accept的文件,因为这些文件应该已经被sync过了。
if ( syncing_case >= next_min_accept)
next_min_accept = syncing_case + 1 ;
path = alloc_printf ( "%s/%s" , qd_path, qd_ent-> d_name) ;
fd = open ( path, O_RDONLY) ; 只读的方式打开qd_path/ qd_ent-> d_name返回为fd
if ( fd < 0 ) {
ck_free ( path) ;
continue ;
}
if ( fstat ( fd, & st) ) PFATAL ( "fstat() failed" ) ;
开始同步这个
if ( st. st_size && st. st_size <= MAX_FILE) {
u8 fault;
u8* mem = mmap ( 0 , st. st_size, PROT_READ, MAP_PRIVATE, fd, 0 ) ; 将fd对应的文件映射到进程空间中,返回u8 * mem
if ( mem == MAP_FAILED) PFATAL ( "Unable to mmap '%s'" , path) ;
write_to_testcase ( mem, st. st_size) ; 调用write_to_testcase ( mem, st. st_size) 将其写到outfile中。
fault = run_target ( argv, exec_tmout) ; 接着run_target运行对应文件,返回fault。
if ( stop_soon) return ;
syncing_party = sd_ent-> d_name;
queued_imported += save_if_interesting ( argv, mem, st. st_size, fault) ;
调用save_if_interesting ( argv, mem, st. st_size, fault) 将感兴趣的样本保存。
syncing_party = 0 ;
munmap ( mem, st. st_size) ; 调用 munmap ( mem, st. st_size) 接触映射
if ( ! ( stage_cur++ % stats_update_freq) ) show_stats ( ) ;
然后 stage_cur++ % stats_update_freq 如果是0 即循环到一个周期,如果是1 则输出对应的fuzz信息
}
ck_free ( path) ;
close ( fd) ;
}
ck_write ( id_fd, & next_min_accept, sizeof ( u32) , qd_synced_path) ;
将& next_min_accept对应文件中的内容写到id_fd对应的文件中。
关闭对应的文件/ 目录描述等。
close ( id_fd) ;
closedir ( qd) ;
ck_free ( qd_path) ;
ck_free ( qd_synced_path) ;
}
closedir ( sd) ;
}
2、trim_case(char **argv, struct queue_entry *q, u8 *in_buf)对于测试用例进行修剪
static u8 trim_case ( char * * argv, struct queue_entry * q, u8* in_buf) {
static u8 tmp[ 64 ] ;
static u8 clean_trace[ MAP_SIZE] ;
u8 needs_write = 0 , fault = 0 ;
u32 trim_exec = 0 ;
u32 remove_len;
u32 len_p2;
if ( q-> len < 5 ) return 0 ; 如果这个case 的大小len小于5 字节,就直接返回
stage_name = tmp; 令stage_name指向tmp数组首位
bytes_trim_in += q-> len; bytes_trim_in计数加上当前的q-> len。
bytes_trim_in代表被trim过的字节数
len_p2 = next_p2 ( q-> len) ; 接着找出使得2 ^ x > q-> len的最小的x,作为len_p2
remove_len = MAX ( len_p2 / TRIM_START_STEPS, TRIM_MIN_BYTES) ;
设置remove_len为MAX ( len_p2 / TRIM_START_STEPS, TRIM_MIN_BYTES)
即len_p2/ 16 ,与4 中最大的那个,作为步长。
while ( remove_len >= MAX ( len_p2 / TRIM_END_STEPS, TRIM_MIN_BYTES) ) {
进入while 循环,终止条件是remove_len小于终止步长len_p2的1 / 1024 , 每轮循环步长会除2
u32 remove_pos = remove_len; 设置remove_pos的值为remove_len
sprintf ( tmp, "trim %s/%s" , DI ( remove_len) , DI ( remove_len) ) ;
读入"trim %s/%s" , DI ( remove_len) , DI ( remove_len) 到tmp中, 即stage_name = “trim 512 / 512 ”
stage_cur = 0 ;
stage_max = q-> len / remove_len;
while ( remove_pos < q-> len) {
进入while 循环,remove_pos < q-> len, 即每次前进remove_len个步长,直到整个文件都被遍历完为止
u32 trim_avail = MIN ( remove_len, q-> len - remove_pos) ;
u32 cksum;
write_with_gap ( in_buf, q-> len, remove_pos, trim_avail) ;
fault = run_target ( argv, exec_tmout) ;
trim_execs++ ; 计数器加一
if ( stop_soon || fault == FAULT_ERROR) goto abort_trimming;
如果设置了stop_soon或者fault == FAULT_ERROR,直接跳转到abort_trimming
cksum = hash32 ( trace_bits, MAP_SIZE, HASH_CONST) ; 计算当前trace_bits的hash32为cksum。
if ( cksum == q-> exec_cksum) { 如果相等
u32 move_tail = q-> len - remove_pos - trim_avail;
从q-> len中减去remove_len个字节,并由此重新计算出一个len_p2,这里注意一下while ( remove_len >= MAX ( len_p2 / TRIM_END_STEPS, TRIM_MIN_BYTES)
q-> len -= trim_avail;
len_p2 = next_p2 ( q-> len) ;
memmove ( in_buf + remove_pos, in_buf + remove_pos + trim_avail,
move_tail) ;
从in_buf + remove_pos + trim_avail复制move_tail个字节到in_buf + remove_pos
if ( ! needs_write) {
needs_write = 1 ;
memcpy ( clean_trace, trace_bits, MAP_SIZE) ; 拷贝trace_bits到clean_trace
}
} else remove_pos += remove_len; 如果不等,remove_pos前移remove_len个字节。
if ( ! ( trim_exec++ % stats_update_freq) ) show_stats ( ) ;
stage_cur++ ;
}
remove_len >>= 1 ;
}
if ( needs_write) { 如果needs_write为1
s32 fd;
删除原来的q-> fname,创建一个新的q-> fname,将in_buf里的内容写入,然后用clean_trace恢复trace_bits的值。
unlink ( q-> fname) ;
fd = open ( q-> fname, O_WRONLY | O_CREAT | O_EXCL, 0600 ) ;
if ( fd < 0 ) PFATAL ( "Unable to create '%s'" , q-> fname) ;
ck_write ( fd, in_buf, q-> len, q-> fname) ;
close ( fd) ;
memcpy ( trace_bits, clean_trace, MAP_SIZE) ;
update_bitmap_score ( q) ; 进行一次update_bitmap_score
}
abort_trimming:
bytes_trim_out += q-> len;
return fault;
}
3、u32 calculate_score(struct queue_entry *q)根据queue entry的执行速度、覆盖到的path数和路径深度来评估出一个得分,这个得分perf_score在后面havoc的时候使用。
static u32 calculate_score ( struct queue_entry * q) {
u32 avg_exec_us = total_cal_us / total_cal_cycles; 首先计算平均时间
u32 avg_bitmap_size = total_bitmap_size / total_bitmap_entries; 计算平均bitmap大小
u32 perf_score = 100 ; 定义初始的perf_score = 100
接下来通过给q-> exec_us乘一个系数,判断他和avg_exec_us的大小来调整perf_score。
if ( q-> exec_us * 0.1 > avg_exec_us) perf_score = 10 ;
else if ( q-> exec_us * 0.25 > avg_exec_us) perf_score = 25 ;
else if ( q-> exec_us * 0.5 > avg_exec_us) perf_score = 50 ;
else if ( q-> exec_us * 0.75 > avg_exec_us) perf_score = 75 ;
else if ( q-> exec_us * 4 < avg_exec_us) perf_score = 300 ;
else if ( q-> exec_us * 3 < avg_exec_us) perf_score = 200 ;
else if ( q-> exec_us * 2 < avg_exec_us) perf_score = 150 ;
然后通过给q-> bitmap_size 乘一个系数,判断与avg_bitmap_size的大小关系来调整perf_score。
if ( q-> bitmap_size * 0.3 > avg_bitmap_size) perf_score *= 3 ;
else if ( q-> bitmap_size * 0.5 > avg_bitmap_size) perf_score *= 2 ;
else if ( q-> bitmap_size * 0.75 > avg_bitmap_size) perf_score *= 1.5 ;
else if ( q-> bitmap_size * 3 < avg_bitmap_size) perf_score *= 0.25 ;
else if ( q-> bitmap_size * 2 < avg_bitmap_size) perf_score *= 0.5 ;
else if ( q-> bitmap_size * 1.5 < avg_bitmap_size) perf_score *= 0.75 ;
if ( q-> handicap >= 4 ) { 如果q-> handicap大于等于4
perf_score *= 4 ; perf_score乘4.
q-> handicap -= 4 ;
} else if ( q-> handicap) {
perf_score *= 2 ;
q-> handicap-- ;
}
switch ( q-> depth) {
case 0 . . . 3 : break ;
case 4 . . . 7 : perf_score *= 2 ; break ;
case 8 . . . 13 : perf_score *= 3 ; break ;
case 14 . . . 25 : perf_score *= 4 ; break ;
default : perf_score *= 5 ;
}
最后保证我们调整后的不会超出最大界限。
if ( perf_score > HAVOC_MAX_MULT * 100 ) perf_score = HAVOC_MAX_MULT * 100 ;
return perf_score;
}
4、u8 common_fuzz_stuff(char **argv, u8 *out_buf, u32 len)写出修改后的测试用例,运行程序,处理result与错误等。
5、void write_to_testcase(void *mem, u32 len)将从mem中读取len个字节,写入到.cur_input中
6、u8 save_if_interesting(char **argv, void *mem, u32 len, u8 fault)检查这个case的执行结果是否是interesting的
static u8 save_if_interesting ( char * * argv, void * mem, u32 len, u8 fault) {
u8 * fn = "" ;
u8 hnb;
s32 fd;
u8 keeping = 0 , res;
if ( fault == crash_mode) { 如果fault等于crash_mode。
查看此时是否出现了newbits。
如果没有的话若设置了crash_mode,则total_crashes计数加一。return 0
if ( ! ( hnb = has_new_bits ( virgin_bits) ) ) {
if ( crash_mode) total_crashes++ ;
return 0 ;
}
# ifndef SIMPLE_FILES
若出现了newbits则调用 fn = alloc_printf ( "%s/queue/id:%06u,%s" , out_dir, queued_paths, describe_op ( hnb) ) ; 拼接出路径fn
fn = alloc_printf ( "%s/queue/id:%06u,%s" , out_dir, queued_paths,
describe_op ( hnb) ) ;
# else
fn = alloc_printf ( "%s/queue/id_%06u" , out_dir, queued_paths) ;
# endif
add_to_queue ( fn, len, 0 ) ; 通过调用add_to_queue ( fn, len, 0 ) 将其插入队列。
if ( hnb == 2 ) { 如果hnb== 2 成立。(有新路径发现)
queue_top-> has_new_cov = 1 ; 设置queue_top-> has_new_cov为1
queued_with_cov++ ; 计数器加一
}
queue_top-> exec_cksum = hash32 ( trace_bits, MAP_SIZE, HASH_CONST) ;
利用hash32从新计算trace_bits的哈希值,将其设置为queue_top-> exec_cksum
res = calibrate_case ( argv, queue_top, mem, queue_cycle - 1 , 0 ) ; 调用calibrate_case进行用例校准,评估当前队列。
if ( res == FAULT_ERROR)
FATAL ( "Unable to execute target application" ) ;
fd = open ( fn, O_WRONLY | O_CREAT | O_EXCL, 0600 ) ;
if ( fd < 0 ) PFATAL ( "Unable to create '%s'" , fn) ;
ck_write ( fd, mem, len, fn) ; 打开fn,将mem的内容写入文件fn。
close ( fd) ;
keeping = 1 ;
}
switch ( fault) {
case FAULT_TMOUT:
total_tmouts++ ; 计数器加一
if ( unique_hangs >= KEEP_UNIQUE_HANG) return keeping;
if ( ! dumb_mode) { 如果不是dume_mode
# ifdef WORD_SIZE_64
simplify_trace ( ( u64* ) trace_bits) ; 调用simplify_trace对trace_bits进行调整
# else
simplify_trace ( ( u32* ) trace_bits) ;
# endif
if ( ! has_new_bits ( virgin_tmout) ) return keeping; 若没有新的超时路径,直接return keeping。
}
unique_tmouts++ ; 计数器加一
if ( exec_tmout < hang_tmout) {
u8 new_fault;
write_to_testcase ( mem, len) ; 将mem的内容写到outfile
new_fault = run_target ( argv, hang_tmout) ; 然后再次调用run_target运行一次,返回new_fault。
if ( ! stop_soon && new_fault == FAULT_CRASH) goto keep_as_crash;
如果未设置stop_soon,并且new_fault为FAULT_CRASH,那么跳转到keep_as_crash
if ( stop_soon || new_fault != FAULT_TMOUT) return keeping;
}
# ifndef SIMPLE_FILES
fn = alloc_printf ( "%s/hangs/id:%06llu,%s" , out_dir,
unique_hangs, describe_op ( 0 ) ) ;
# else
fn = alloc_printf ( "%s/hangs/id_%06llu" , out_dir,
unique_hangs) ;
# endif
unique_hangs++ ;
last_hang_time = get_cur_time ( ) ;
break ;
case FAULT_CRASH:
keep_as_crash:
total_crashes++ ;
if ( unique_crashes >= KEEP_UNIQUE_CRASH) return keeping;
if ( ! dumb_mode) {
# ifdef WORD_SIZE_64
simplify_trace ( ( u64* ) trace_bits) ;
# else
simplify_trace ( ( u32* ) trace_bits) ;
# endif
if ( ! has_new_bits ( virgin_crash) ) return keeping;
}
if ( ! unique_crashes) write_crash_readme ( ) ;
# ifndef SIMPLE_FILES
fn = alloc_printf ( "%s/crashes/id:%06llu,sig:%02u,%s" , out_dir,
unique_crashes, kill_signal, describe_op ( 0 ) ) ;
# else
fn = alloc_printf ( "%s/crashes/id_%06llu_%02u" , out_dir, unique_crashes,
kill_signal) ;
# endif
unique_crashes++ ;
last_crash_time = get_cur_time ( ) ;
last_crash_execs = total_execs;
break ;
case FAULT_ERROR: FATAL ( "Unable to execute target application" ) ;
default : return keeping;
}
fd = open ( fn, O_WRONLY | O_CREAT | O_EXCL, 0600 ) ;
if ( fd < 0 ) PFATAL ( "Unable to create '%s'" , fn) ;
ck_write ( fd, mem, len, fn) ;
close ( fd) ;
ck_free ( fn) ;
return keeping;
}