我正在学习模糊测试C应用程序的方法。据我所知,在大多数情况下,当进行模糊测试时,都会有一个C函数来接收/读取文件。模糊器被赋予一个有效的示例文件,随机或使用覆盖启发法修改它,并使用这个新的输入执行函数。
但是现在我不想模糊一个接受文件输入的函数,而是想模糊一些共同构成API的函数。例如:
int setState(int state);
int run(void); // crashes when previous set state was == 123这样做的目的是对整个API进行测试,并检测误用和调用函数的顺序是否错误(这里:调用setState(123)和run())是否会导致某些地方崩溃。
怎么能做这种事呢?我正在寻找模糊测试框架(不一定是C)、一般概念和示例。
我尝试从LLVM中使用libFuzzer,并逐字节“消耗”它的模糊器数据。我读取一个字节来确定要调用哪个函数,然后在需要时读取一个参数,最后调用该函数。然后我重复一遍,直到不再留下模糊器输入数据。它看起来像这样:
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
while(/* not end of fuzzer data reached */)
switch (fuzzerConsumeByte()) {
case 0:
setState(fuzzerConsumeInt());
break;
case 1:
run();
break;
default:
break;
}
}
return 0;
}我发现提到的这种令人毛骨悚然的风格的消息来源如下:
..。从公共API中随机选择函数,并以随机参数的随机顺序调用它们。码智能
这看起来不像是基于输入文件的模糊器的良好或有效使用。不过,模糊测试libFuzzer会在几秒钟后发现该错误。但我认为,如果我用多个其他功能扩展 API,可能会需要很长时间。
发布于 2022-02-24 20:29:32
来回答我自己的问题:
是的,这就是API模糊化的方法。为了按顺序使用数据,可以使用libFuzzer #include <fuzzer/FuzzedDataProvider.h> (C++)提供的函数。问题是:崩溃转储和模糊语料库将不会人类可读的。
对于更具可读性的模糊程序,为libFuzzer实现一个libFuzzer自定义数据变送器是有益的。
我使用预先制作的数据突变器脂原突变体 (C++)来模糊示例API。它根据协议缓冲区定义生成有效的输入数据,而不仅仅是(半)随机字节。不过,它确实会使毛刺变慢一点。给出的示例API中的bug在2分钟后就被发现了,与基本字节消耗设置下的30秒相比,发现了bug。但我确实认为,对于更大(真实的)API来说,它的扩展性要好得多。
发布于 2022-03-07 16:03:37
在用这种方式对有状态应用程序的API进行模糊化时,还要记住的另一件事是,您应该确保在每个模糊测试之间重置应用程序,或者使用AFL而不是libFuzzer来对将要测试的每个新输入进行分叉。否则,您发现的崩溃可能无法在崩溃转储中重现,因为崩溃取决于先前测试用例对目标应用程序所做的一些更改。
我还想提到,我们使用的是“.从您的公共API中随机选择函数,并以随机参数的随机顺序调用它们。”模糊方法也适用于更大的实际API(多达几百个函数),并且在合理的时间内实现了良好的代码覆盖率和结果。
您是对的,崩溃转储不是人类可读的,而是一些基于反馈的模糊工具,您不仅可以获得崩溃输入的转储,而且还可以获得其他信息,比如堆栈跟踪,可以帮助您分析根本原因。
编辑:
这里有一个使用这种模糊方法并使用FuzzedDataProvider的示例模糊测试:
#include <stdint.h>
#include <stddef.h>
#include "FuzzedDataProvider.h"
#include "GPS_module_1.h"
#include "crypto_module_1.h"
#include "crypto_module_2.h"
#include "key_management_module_1.h"
#include "time_module_1.h"
// Wrapper function for FuzzedDataProvider.h
// Writes |num_bytes| of input data to the given destination pointer. If there
// is not enough data left, writes all remaining bytes and fills the rest with zeros.
// Return value is the number of bytes written.
void ConsumeDataAndFillRestWithZeros(void *destination, size_t num_bytes, FuzzedDataProvider *fuzz_data) {
if (destination != nullptr) {
size_t num_bytes_with_fuzz_data = fuzz_data->ConsumeData(destination, num_bytes);
if (num_bytes > num_bytes_with_fuzz_data) {
size_t num_bytes_with_zeros = num_bytes - num_bytes_with_fuzz_data;
std::memset((char*)destination+num_bytes_with_fuzz_data, 0, num_bytes_with_zeros);
}
}
}
extern "C" int FUZZ(const uint8_t *Data, size_t Size) {
// Ensure a minimum data length
if(Size < 100) return 0;
// Setup FuzzedDataProvider
FuzzedDataProvider fuzz_data_provider(Data, Size);
FuzzedDataProvider *fuzz_data = &fuzz_data_provider;
// Reset the state of the target software
// to ensure that crashes are reproducible
crypto::init();
int number_of_functions = fuzz_data->ConsumeIntegralInRange<int>(1,100);
for (int i=0; i<number_of_functions; i++) {
int func_id = fuzz_data->ConsumeIntegralInRange<int>(0, 15);
switch(func_id) {
case 0: {
// Create a struct and fill it with fuzz data
GPS::position struct_0 = {0};
ConsumeDataAndFillRestWithZeros(&struct_0, sizeof(struct_0), fuzz_data);
GPS::get_current_position(&struct_0);
break;
}
case 1: {
GPS::get_destination_position();
break;
}
case 2: {
GPS::init_crypto_module();
break;
}
case 3: {
GPS::position struct_0 = {0};
ConsumeDataAndFillRestWithZeros(&struct_0, sizeof(struct_0), fuzz_data);
GPS::set_destination_position(struct_0);
break;
}
case 4: {
// Create a vector of "random" size
// and fill it with fuzz data
std::vector<uint8_t> fuzz_data_0 = fuzz_data->ConsumeBytes<uint8_t>(fuzz_data->ConsumeIntegral<uint8_t>());
size_t fuzz_size_0 = fuzz_data_0.size();
crypto::hmac struct_0 = {0};
ConsumeDataAndFillRestWithZeros(&struct_0, sizeof(struct_0), fuzz_data);
crypto::calculate_hmac(fuzz_data_0.data(), fuzz_size_0, &struct_0);
break;
}
case 5: {
crypto::get_state();
break;
}
case 6: {
crypto::init();
break;
}
case 7: {
crypto::key struct_0 = {0};
ConsumeDataAndFillRestWithZeros(&struct_0, sizeof(struct_0), fuzz_data);
crypto::set_key(struct_0);
break;
}
case 8: {
crypto::nonce struct_0 = {0};
ConsumeDataAndFillRestWithZeros(&struct_0, sizeof(struct_0), fuzz_data);
crypto::set_nonce(struct_0);
break;
}
case 9: {
std::vector<uint8_t> fuzz_data_0 = fuzz_data->ConsumeBytes<uint8_t>(fuzz_data->ConsumeIntegral<uint8_t>());
size_t fuzz_size_0 = fuzz_data_0.size();
crypto::hmac struct_0 = {0};
ConsumeDataAndFillRestWithZeros(&struct_0, sizeof(struct_0), fuzz_data);
crypto::verify_hmac(fuzz_data_0.data(), fuzz_size_0, &struct_0);
break;
}
case 10: {
crypto::key struct_0 = {0};
ConsumeDataAndFillRestWithZeros(&struct_0, sizeof(struct_0), fuzz_data);
crypto::verify_key(struct_0);
break;
}
case 11: {
crypto::nonce struct_0 = {0};
ConsumeDataAndFillRestWithZeros(&struct_0, sizeof(struct_0), fuzz_data);
crypto::verify_nonce(&struct_0);
break;
}
case 12: {
std::vector<uint8_t> fuzz_data_0 = fuzz_data->ConsumeBytes<uint8_t>(fuzz_data->ConsumeIntegral<uint8_t>());
size_t fuzz_size_0 = fuzz_data_0.size();
key_management::create_key(fuzz_data_0.data(), fuzz_size_0);
break;
}
case 13: {
std::vector<uint8_t> fuzz_data_0 = fuzz_data->ConsumeBytes<uint8_t>(fuzz_data->ConsumeIntegral<uint8_t>());
size_t fuzz_size_0 = fuzz_data_0.size();
key_management::create_nonce(fuzz_data_0.data(), fuzz_size_0);
break;
}
case 14: {
std::vector<uint8_t> fuzz_data_0 = fuzz_data->ConsumeBytes<uint8_t>(fuzz_data->ConsumeIntegral<uint8_t>());
size_t fuzz_size_0 = fuzz_data_0.size();
key_management::generate_random_bytes(fuzz_data_0.data(), fuzz_size_0);
break;
}
case 15: {
time_management::current_time();
break;
}
}
}
return 0;
}https://stackoverflow.com/questions/71173169
复制相似问题