AFL命令行中“@@“的作用以及AFL的两种数据传递方式

AFL模糊测试中,'@@'符号用于指示目标程序从文件而不是标准输入读取输入。当使用'@@'时,AFL会创建一个新文件并写入测试数据,而没有'@@'时则通过标准输入传递数据。文件传递方式涉及更多的文件系统操作,可能更耗时,但适用于需要从文件读取输入的程序。AFL还允许通过'-f'选项自定义输入文件。
摘要由CSDN通过智能技术生成

AFL命令行中"@@"的作用

参考:https://barro.github.io/2018/06/afl-fuzz-on-different-file-systems/

AFL有两种方式将输入传递给目标程序,一个是通过标准输入,一个是通过文件

$ afl-fuzz -i in -o out ./test			# 标准输入
$ afl-fuzz -i in -o out ./test @@		# 文件

具体地,在afl-fuzz.c 文件的 main 函数中,会调用 detect_file_args 函数区检测命令行参数中是否含有 “@@”,

  • 如果有,就会将"@@"替换为 out_dir 下的 .cur_input 文件(实际上会替换为绝对路径,即 @@ => /…/out_dir/.cur_input),之后调用 execv 替换进程空间时,就相当于如下命令:./test /…/out_dir/.cur_input,自然而然会去文件里读数据。

    此外,out_file 这个变量也会被赋上值(其实就是通过这个变量是否为空来判断是标准输入还是文件输入)

  • 如果没有,就会调用 setup_stdio_file 函数,令out_fd文件描述符指向 out_dir 下的 .cur_input 文件

虽然都是在操作 out_dir 下的 .cur_input 文件,但是还是有区别

static void write_to_testcase(void* mem, u32 len) {

  s32 fd = out_fd;  // 输出文件
    
  if (out_file) {   // 如果通过文件方式传递

    unlink(out_file); /* Ignore errors. */

    fd = open(out_file, O_WRONLY | O_CREAT | O_EXCL, 0600); // 创建out_file,具有读写权限

    if (fd < 0) PFATAL("Unable to create '%s'", out_file);

  }
  else {	// 如果通过标准输入方式传递
      lseek(fd, 0, SEEK_SET);    // 设置文件out_file 从头开始读写
  }

  ck_write(fd, mem, len, out_file); // 将缓冲区mem中存放的内容写入到fd所指的文件中去,


  if (!out_file) {  // 如果通过标准输入方式传递
     // 为什么有这个截断操作,因为这个函数是直接覆盖之前文件内的内容,防止之前的内容比现在写进去的内容要长。
    if (ftruncate(fd, len)) PFATAL("ftruncate() failed");   // 清空fd, 并将偏移量定位到文件开头
    lseek(fd, 0, SEEK_SET);

  } else close(fd);

}


EXP_ST void init_forkserver(char** argv) {
    ...
    if (out_file) {	// 如果通过文件方式传递

        dup2(dev_null_fd, 0);     // 如果指定了要模糊测试的文件,就将标准输入重定向到dev_null_fd

    } else {	// 如果通过标准输入方式传递

        dup2(out_fd, 0);  // 如果没有指定标准要模糊测试的文件,就将out_fd重定向到标准输入
        close(out_fd);

    }
    ...
}

标准输入传递

可以看到下面从write_to_testcase摘出的程序中,是通过标准输入的方式传递所执行的语句,会直接用新数据覆盖掉旧的数据,并将文件更改为新数据的长度。值得注意的是,init_forkserver 函数中会将out_fd 重定向到0,也就是标准输入,这样一切就通了,然后目标程序会从这个out_fd文件描述符中读取数据并运行。

上面提到out_fd文件描述符指向 out_dir 下的 .cur_input 文件,所以子进程的标准输入由 out_dir 下的 .cur_input 这么一个文件支持, AFL 进程通过新数据覆盖旧数据的方式产生新输入。

// AFL的操作
lseek(out_fd, 0, SEEK_SET);				// 从头开始读写
ck_write(out_fd, mem, len, out_file);	// 写入新数据,会覆盖旧数据 
ftruncate(out_fd, len);					// 截断,防止新数据比旧数据短
lseek(out_fd, 0, SEEK_SET);				// ftruncate后要重新设置从头开始读写

// 目标程序的操作
len = read(out_fd, buffer, sizeof(buffer));
fuzzing(buffer, len);

文件传递

下面是通过文件方式传递所执行的语句,删除旧文件,创建新文件,写入数据,关闭。目标程序打开刚刚创建的文件,从中读取数据并运行。

// AFL的操作	out_file一直指向.cur_input文件
unlink(out_file);											// 删除旧的文件
fd = open(out_file, O_WRONLY | O_CREAT | O_EXCL, 0600);		// 创建新的文件
ck_write(fd, mem, len, out_file);							// 写入数据
close(fd);		

// 目标程序的操作
fd = open(out_file, O_RDONLY);
len = read(fd, buffer, sizeof(buffer));
close(in_fd);
fuzzing(buffer, len);

整体上来讲,文件传递方式会用到更多与文件系统相关的函数,也更耗时,如调用 unlink() 和 open() 时,Linux 内核需要进行路径名查找以确定访问的确切文件对象,打开一个新文件时,必须为文件系统创建相应的 inode 及数据结构,而标准输入传递方式不需要这些操作。但是,很多程序需要从文件获取输入,此时我们就要用“@@”。

自定义文件传递

AFL的命令行参数中有一个 -f 选项,通过这个选项可以任意指定目标程序读取输入的位置,使用方式如下

$ afl-fuzz -i in -o out -f ./input ./test

此时,AFL对 ./input 文件的处理操作以及目标程序的操作就和上面文件方式传递一样。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值