安全客 03月28日
【必看】2025简单部署 AFL++ 模糊测试工具,亲测少走一年弯路!
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文深入探讨了如何使用AFL++模糊测试技术,特别是针对无法直接运行的二进制文件。文章首先介绍了环境安装的多种方式,并推荐使用官方镜像以获得最佳的测试环境。随后,详细阐述了针对不可执行二进制文件的测试方法,包括测试驱动的编写、编译和模糊测试的流程。此外,文章还讨论了如何处理未知函数、利用dlopen和dlsym动态加载函数,以及获取函数签名的方法。最后,提供了通用驱动程序的示例,并强调了中科固源在通讯协议安全与模糊测试领域的专业知识和提供的工具与服务,旨在帮助读者构建全面的网络安全防护体系。

⚙️ **环境准备与安装:** 推荐使用AFL++官方镜像进行安装,相较于源码编译和包管理器安装,镜像安装能提供更完整、稳定的测试环境,方便后续的模糊测试工作。

🔨 **针对不可执行二进制文件的测试方法:** 对于.o文件,需要编写测试驱动(harness)来调用目标函数。测试驱动需要正确声明目标函数的签名,处理输入文件,并使用AFL++编译器进行插桩,以收集覆盖率信息。

🤔 **处理未知函数与动态加载:** 如果无法确定参数类型,可以编写通用的测试驱动来尝试调用目标函数。对于动态加载,可以使用dlopen和dlsym来加载共享库并调用其中的函数,但需要借助其他工具来解析函数签名。

💡 **获取函数签名:** 由于C语言中.so文件不直接存储函数签名信息,需要使用nm、objdump等工具分析符号表,或者借助更高级的调试和反汇编工具来推测函数签名。提供头文件或文档是获取函数签名的更佳方式。

1. 环境安装

 

1.1 源代码编译

源码地址:git clone GitHub – AFLplusplus/AFLplusplus: The fuzzer afl++ is afl with community patches, qemu 5.1 upgrade, collision-free coverage, enhanced laf-intel & redqueen, AFLfast++ power schedules, MOpt mutators, unicorn_mode, and a lot more!

1.2 官方镜像: aflplusplus/aflplusplus

1.3 包管理器安装: apt install afl++

解析:源代码编译在线下载大文件数据包,不够稳定;包管理其安装 afl++ 只是一个基础包,无法进行模糊测试,仍需安装其它依赖包;

推荐官方镜像安装:实时更新,环境完整,但是镜像较大,5.31GB大小。

 

2. 不可执行的二进制文件

 

如果目标二进制文件无法运行(不是由于架构不匹配、依赖缺失或环境配置问题,而是因为其本身不是一个可执行文件,或者其逻辑无法运行)

如果目标二进制文件是一个 .o 文件,可以通过编写一个测试 harness 来调用库中的函数。

方法:

a.编写一个简单的程序(harness),调用库中的目标函数。

b.使用 AFL++ 对 harness 进行模糊测试。

 

2.1 测试驱动调用函数

目标是一个不可运行的二进制文件(例如一个函数),需要编写一个测试驱动(harness)来调用目标函数。

 

2.1.1 准备目标二进制文件

二进制文件 libtarget.o ,其中包含一个函数 vulnerable_function,其签名为:

 

这个二进制文件可能是通过以下方式编译的:

 

其中 target.c 的内容如下:

 

2.1.2 编写测试驱动

由于二进制文件不可直接运行,编写一个测试驱动(harness)来调用目标函数。创建一个文件 harness.c:

 

 

2.1.3 编译测试驱动和目标二进制文件

将测试驱动和目标二进制文件链接在一起,并使用 AFL++ 的编译器进行插桩:

 

2.1.4 准备测试用例

创建一个目录来存放初始的测试用例:

 

2.1.5 开始模糊测试

使用 AFL++ 进行模糊测试:

 

 

注意事项

1.函数签名:确保在测试驱动中正确声明目标函数的签名。

2.输入处理:测试驱动需要正确处理输入文件,并将其传递给目标函数。

3.插桩:确保使用 AFL++ 的编译器进行插桩,以便 AFL++ 能够收集覆盖率信息。

 

2.2 模糊测试未知函数

如果无法确定参数类型,可以编写一个通用的测试驱动,尝试以不同的方式调用目标函数。

通用的模糊测试

1.void vulnerable_function(char *input):

参数是 char *,即一个指向字符类型的指针,通常用于处理字符串。

这个函数通常用于处理 C 风格的字符串(以 null 结尾的字符数组)。

例如,你可以将 input 看作一个字符串,可以通过 input 来访问和操作字符数据。

2.void vulnerable_function(void *arg):

参数是 void *,即一个通用的指针类型,可以指向任何类型的数据。

这种声明使得函数能够接受任意类型的数据,并且可以通过类型转换来处理它。

它提供了更大的灵活性,但也更容易导致潜在的错误,因为在使用时需要确保正确地将 void * 转换为实际的数据类型。

总结:

char *input 适用于专门处理字符串或字符数组。

void *arg 提供了更大的通用性,可以处理任何类型的数据,但需要额外的小心以确保类型转换正确。

 

 

2.3 dlopen 和 dlsym

2.3.1 dlopen 和 dlsym

dlopen 和 dlsym:用于动态加载共享库并调用已知函数,但无法直接读取函数名和参数类型。

dlopen 用于打开一个共享库文件,并返回一个句柄,供后续使用。

dlsym 用于从共享库中查找符号(函数或变量)的地址。

2.3.2 动态加载共享库并调用其中的函数

共享库代码

编译共享库

 

 

主代码程序

 

 

编译

 

 

运行

 

 

2.4 函数签名

在 C 语言中,共享库(.so 文件)本身不存储函数的签名信息

要读取一个 .so 文件中的函数签名(即函数名、参数类型、返回类型等),dlopen 和 dlsym 不能直接提供这样的信息,因为它们只会加载符号并返回其地址。要解析函数签名,你需要用一些工具来分析 .so 文件的符号表,或者借助一些更高级的调试和反汇编工具。

 

nm

可以列出共享库中的符号表(包括函数名、地址、类型等),但无法直接显示完整的函数签名(参数类型和返回值)

 

 

objdump

objdump 可以反汇编代码,通过分析函数调用栈和寄存器传递参数的方式,间接推测函数签名。

readelf 查看 ELF 文件头和符号

nm或objdump只能得到函数名和可能的参数类型,但不够准确,特别是对于复杂类型或用户自定义类型。

提供头文件或文档

.so 文件(共享库)中直接提取头文件是不可行的

 

2.5 通用驱动程序

要将目标函数通过命令行传入,而不是在代码中硬编码 unknown_function,通过命令行参数传递目标函数的名称,并使用函数指针来调用它。

 

 

 

 

编译

确保目标函数在编译时被正确导出(例如,使用 -rdynamic 或 -export-dynamic 编译选项)

 

 

 

 

中科固源专注于通讯协议安全与模糊测试,提供Wisdom系列工具和Swift系列工具,帮助企业构建全面的网络安全防护体系。了解更多产品与解决方案。加入我们,开启你的高效代码创新之旅!

 

①扫描二维码或添加微信,获取1V1线上云指导。

②解锁免费高效的开源级开发工具,还有更多专属权益等你来拿。

③关注我们,在评论区留言“我要学习资料”,即可免费获得独家学习资料包,包括详细使用教程、应用案例分析及相关技术文档。

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

AFL++ 模糊测试 二进制文件 安全
相关文章