当前位置: 首页 > news >正文

AFL fork server和fuzzer的交互

看了一些博客,都是在说fuzzer和fork server进行交互,由fork server fork出子进程来执行程序,但是不太明白这两者到底是如何在代码层面进行交互的。

run_target中有这么一段代码,大概意思是fuzzer给fork server传递prev_timed_out,然后再从fork server读取子进程的pid,child_pid:

    s32 res;/* In non-dumb mode, we have the fork server up and running, so simplytell it to have at it, and then read back PID. */if ((res = write(fsrv_ctl_fd, &prev_timed_out, 4)) != 4) {if (stop_soon) return 0;RPFATAL(res, "Unable to request new process from fork server (OOM?)");}if ((res = read(fsrv_st_fd, &child_pid, 4)) != 4) {if (stop_soon) return 0;RPFATAL(res, "Unable to request new process from fork server (OOM?)");}if (child_pid <= 0) FATAL("Fork server is misbehaving (OOM?)");

我现在的问题是,为什么fuzzer给fork server传了个参数,fork server就直接返回pid了呢?这中间两者是如何进行交互的?fork server做了什么,就传递了一个child_pid出来?

fork server进程是执行了下面这段代码(删去了一些不重要的代码):

  if (!forksrv_pid) {struct rlimit r;/* Isolate the process and configure standard descriptors. If out_file isspecified, stdin is /dev/null; otherwise, out_fd is cloned instead. */setsid();dup2(dev_null_fd, 1);dup2(dev_null_fd, 2);if (out_file) {dup2(dev_null_fd, 0);} else {dup2(out_fd, 0);close(out_fd);}/* Set up control and status pipes, close the unneeded original fds. */if (dup2(ctl_pipe[0], FORKSRV_FD) < 0) PFATAL("dup2() failed");if (dup2(st_pipe[1], FORKSRV_FD + 1) < 0) PFATAL("dup2() failed");close(ctl_pipe[0]);close(ctl_pipe[1]);close(st_pipe[0]);close(st_pipe[1]);close(out_dir_fd);close(dev_null_fd);close(dev_urandom_fd);close(fileno(plot_file));execv(target_path, argv);/* Use a distinctive bitmap signature to tell the parent about execv()falling through. */*(u32*)trace_bits = EXEC_FAIL_SIG;exit(0);}

可能需要理解setsid();?
简单搜索了下,还得去理解进程相关只是,于是去问了bing,bing的回答告诉我:setsid()函数是一个系统调用,它的作用是创建一个新的会话(session),并使得当前进程成为会话的首进程(session leader),这个函数似乎和我想知道的东西没有联系。

问了下bing,并参考了这个博客:https://blog.csdn.net/Little_Bro/article/details/122694054,fork server的交互还和插桩有关系。

查看了AFL白皮书:https://github.com/mirrorer/afl/blob/master/docs/technical_details.txt,写的很粗略,还是得去看作者的博客:https://lcamtuf.blogspot.com/2014/10/fuzzing-binaries-without-execve.html

Unfortunately, there is also a problem: especially for simple libraries, you may end up spending most of the time waiting for execve(), the linker, and all the library initialization routines to do their job. I’ve been thinking of ways to minimize this overhead in american fuzzy lop, but most of the ideas I had were annoyingly complicated. For example, it is possible to write a custom ELF loader and execute the program in-process while using mprotect() to temporarily lock down the memory used by the fuzzer itself - but things such as signal handling would be a mess. Another option would be to execute in a single child process, make a snapshot of the child’s process memory and then “rewind” to that image later on via /proc/pid/mem - but likewise, dealing with signals or file descriptors would require a ton of fragile hacks.

为什么不直接多次调用execve()?因为每次调用 execve()都会有一些预处理的开销,作者想要加快这个过程。(不太了解预处理的过程,后续有需要再了解)

Luckily, Jann Horn figured a different, much simpler approach, and sent me a patch for afl out of the blue 😃 It boils down to injecting a small piece of code into the fuzzed binary - a feat that can be achieved via LD_PRELOAD, via PTRACE_POKETEXT, via compile-time instrumentation, or simply by rewriting the ELF binary ahead of the time. The purpose of the injected shim is to let execve() happen, get past the linker (ideally with LD_BIND_NOW=1, so that all the hard work is done beforehand), and then stop early on in the actual program, before it gets to processing any inputs generated by the fuzzer or doing anything else of interest. In fact, in the simplest variant, we can simply stop at main().

作者给出了一个很巧妙的解决方法,在被fuzzed的程序中插桩,让这个程序在完成预处理后暂停(比如再main函数的第一句话暂停),然后在这里调用fork(),被fork出来的子进程将会直接跳过预处理过程,开始执行实际处理。

Once the designated point in the program is reached, our shim simply waits for commands from the fuzzer; when it receives a “go” message, it calls fork() to create an identical clone of the already-loaded program; thanks to the powers of copy-on-write, the clone is created very quickly yet enjoys a robust level of isolation from its older twin. Within the child process(fork server创建的子进程), the injected code returns control to the original binary, letting it process the fuzzer-supplied input data (and suffer any consequences of doing so). Within the parent, the shim relays the PID of the newly-crated process to the fuzzer and goes back to the command-wait loop.

作者把插入的代码叫做slim(分隔片,还是很形象的),slim等待来自fuzzer的命令(对应run_target中的write(fsrv_ctl_fd, &prev_timed_out, 4)?),在收到fuzzer的命令后,fork server fork出来一个真正执行二进制程序的fuzzed进程,并给fuzzer返回一个pid。

这里有一个问题,函数参数是在哪里传递的呢?write(fsrv_ctl_fd, &prev_timed_out, 4)似乎没有传递参数。

接下俩作者还讨论了实际实现可能遇到的问题,以及插桩的汇编代码

https://blog.csdn.net/Little_Bro/article/details/12269405,这个博客对插桩代码进行了解释,但是我目前不需要对插桩代码理解的那么清楚,已经明白了fork server和fuzzer之间交互的逻辑

http://www.lryc.cn/news/308827.html

相关文章:

  • Java SE:多线程(Thread)
  • 你敢信?软件测试万能面试脚本他来了?
  • C++/Python简单练手题
  • 视频在线压缩
  • Python列表的合并、重复、判断与切片操作你学会了吗
  • Vue(3.3.4)+three.js(0.161.0)实现3D可视化地图
  • 瑞吉苍穹外卖如何拓展?已经经过不同公司多轮面试。项目中会问到哪些问题?以及问题如何解决?
  • 动态内存分配
  • 【C语言】常见的动态内存管理错误
  • 数据结构之二叉树的精讲
  • ETL是什么
  • 华为配置WLAN高密业务示例
  • C++——类和对象(1)
  • vue+element ui上传图片到七牛云服务器
  • 学不动系列-git-hooks和husky+lintstage
  • K8S相关小技巧《四》
  • Delphi 报错 Type androidx.collection.ArraySet is defined multiple times
  • Post请求中文乱码问题
  • LeetCode -- 79.单词搜索
  • 单元测试、集成测试、系统测试有什么不同?
  • 数据迁移DTS | 云上MySQL 数据库迁移至达梦数据库
  • Linux进程管理:(二)进程调度原语
  • Compose 介绍
  • 5分钟搞定Python中函数的参数
  • Gitlab: 私有化部署
  • 深入理解Linux线程(LWP):概念、结构与实现机制(2)
  • VBS脚本搞定,快速批量提取一堆Excel文件中的数据
  • 大数据分析案例-基于SVM支持向量机算法构建手机价格分类预测模型
  • WPF 滑动条样式
  • 论文设计任务书学习文档|基于Web的个性化简历职位推荐系统的设计与实现