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;
}
|