欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

模糊测试简介

程序员文章站 2022-07-08 15:58:16
介绍模糊测试起源;使用一个简单的事例演示模糊测试;实现一个超简单的模糊测试框架;...

0. 前言

来源:Fuzzing: Breaking Things with Random Inputs

建议阅读原文,我这里仅仅整理下思路。我敲的相关代码见:fuzzing仓库

在本章中,我们将从最简单的测试生成技术开始。随机文本生成的关键思想,也称为fuzzing,是将一串随机字符输入程序,以期发现失败。

要求:知道最简单的测试概念:here


1. 故事起源

模糊测试诞生于“ 1988年秋天的黑暗和暴风雨之夜”中。 巴顿·米勒教授坐在麦迪逊威斯康星州的公寓里,通过一条1200波特的电话线连接到他的大学计算机。 雷暴在线路上造成噪音,而该噪音又导致两端的UNIX命令获得错误的输入,并崩溃。 频繁的崩溃使他感到惊讶。当然,程序应该比这更强大吗? 作为一名科学家,他想研究问题的严重程度及其原因。 因此,他为威斯康星大学麦迪逊分校的学生编写了一个编程练习,该练习将使他的学生创建第一个模糊测试器。

作业重点如下:

该项目的目标是在给定不可预测的输入流的情况下评估各种UNIX实用程序的健壮性。 首先,您将构建一个模糊发生器。 这是一个将输出随机字符流的程序。 其次,您将使用模糊发生器,并使用它来攻击尽可能多的UNIX实用程序,以试图破坏它们。

该作业抓住了模糊测试的本质:创建随机输入,并查看它们是否破坏东西。 只要让它运行足够长的时间,您就会看到。


2. 模糊测试事例

参考连接:Python中with用法详解 | with语句 – python官方文档 |subprocess – 子进程管理

我们的目标和上面类似:创建随机输入,测试bc应用。

# 生成一个指定范围内,随机长度,随机字母的字符串
import random
def fuzzer(min_length=5,max_length=15,char_start=ord('a'),char_range=26):
    str_length = random.randrange(min_length,max_length+1)
    out = ""
    for i in range(str_length):
        out += chr(random.randrange(char_start,char_start+char_range))
    return out

# 将生成的字符串写入文件
import os
import tempfile
basename = "input.txt"
tmp_dir = tempfile.mkdtemp()
FILE = os.path.join(tmp_dir,basename)

data = fuzzer()
with open(FILE,"w") as f:
    f.write(data)
print(open(FILE).read())

# 调用外部程序
import subprocess
program = "bc"
with open(FILE, "w") as f:
    f.write("2 + 2\n")
result = subprocess.run([program, FILE],
                        stdin=subprocess.DEVNULL,
                        stdout=subprocess.PIPE,
                        stderr=subprocess.PIPE,
                        universal_newlines=True)  # Will be "text" in Python 3.7
# print(result)
print(result.stdout)
# print(result.returncode)
# print(result.stderr)

# 测试bc程序
trials = 50
program = "bc"
results = []
for i in range(trials):
    data = fuzzer(min_length=2,char_start=ord('0'))
    data += '\n'
    with open(FILE,"w") as f:
        f.write(data)
    result = subprocess.run([program, FILE],
                        stdin=subprocess.DEVNULL,
                        stdout=subprocess.PIPE,
                        stderr=subprocess.PIPE,
                        universal_newlines=True)  # Will be "text" in Python 3.7
    results.append((data,result))
    
# 测试bc程序的结果分析,没有返回值为非0存在,即没有崩溃
# 如果有返回代码非0,断言
sum_suc = 0
for i in range(trials):
    (data,result) = results[i]
    assert result.returncode == 0
    if result.stderr == "":
        sum_suc += 1
        print(f"suc:{data}-->{result.stdout}")
    else:
        print(f"fail:{data}-->{result.stderr}")
print(sum_suc)

3. 模糊测试框架的简单实现

上面通过一个事例演示了模糊测试。但上面的代码是面向过程编程,在测试其他程序的时候,不好重复利用。下面我们思考如何写一个简单的模糊测试框架。比较明显的是,框架可以分为两部分:生成随机字符串;将字符串作为输入测试程序。好,下面我们实现这样的想法

3.1 Runner 类

我们首先介绍的是Runner的概念––其工作是使目标对象执行给定的输入。目标对象为指定的待测试程序。
让我们从Runner的基础类开始。 运行程序本质上提供了一种run(input)方法,该方法用于将输入(string)传递给目标对象。 run()返回一对(result,outcome)。 在这里,result是Runner特定的值,提供了Runner返回的详细信息; outcome是将结果分为三类的值:

  • Runner.PASS – the test passed. The run produced correct results.
  • Runner.FAIL – the test failed. The run produced incorrect results.
  • Runner.UNRESOLVED – the test neither passed nor failed. This happens if the run could not take place – for instance, because the input was invalid.
# base runner class : essentially provides a method run(input),run() returns a pair (result, outcome)
# result是返回值的详细信息,outcome是三种分类之一
# 这个类是基类,下面通过继承覆盖,产生不同的子类
class Runner(object):
    # Test outcomes
    PASS = "PASS"
    FAIL = "FAIL"
    UNRESOLVED = "UNRESOLVED"
    def __init__(self):
        pass
    def run(self,inp):
        return(inp, self.UNRESOLVED)
    
    # 继承Runner类。打印输入
class PrintRunner(Runner):
    def run(self,inp):
        print(inp)
        return(inp, self.UNRESOLVED)
    
# 继承Runner类
# 把输入发送给程序;程序在创建对象的时候制定
class ProgramRunner(Runner):
    def __init__(self,program):
        self.program = program
    def run_process(self,inp=""):
        return subprocess.run(self.program,
                                input=inp,
                                stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE,
                                universal_newlines=True)
                                # text=True) 我的是3.6版本,还没有text
    def run(self,inp=""):
        result = self.run_process(inp)
        if result.returncode == 0:
            outcome = self.PASS
        elif result.outcome < 0:
            outcome = self.FAIL
        else:
            outcome = self.UNRESOLVED
        return (result,outcome)
    
# 继承ProgramRunner类
# 如果输入是二进制形式
class BinaryProgramRunner(ProgramRunner):
    def run_process(self,inp=""):
        return subprocess.run(self.program,
                            input=inp.encode(),
                            stdout=subprocess.PIPE,
                            stderr=subprocess.PIPE)
    
# 测试下ProgramRunner
cat = ProgramRunner(program="cat")
(result,outcome) = cat.run("I am cat")
print(result)

3.2 Runner 类

Fuzzer 类主要是创建随机输入,用以喂给run()方法;

# Fuzzer基类:生成随机输入,并使用run方法运行
class Fuzzer(object):
    def __init__(self):
        pass
    def fuzz(self):
        return ""
    def run(self,runner=Runner()):
        return runner.run(self.fuzz())
    def runs(self,runner=PrintRunner(),trials=10):
        outcomes = []
        for i in range(trials):
            # outcomes.append(runner.run(self.fuzz()))
            outcomes.append(self.run(runner))
        return outcomes
    
# 随机模糊测试
class RandomFuzzer(Fuzzer):
    def __init__(self, min_length=10, max_length=100,char_start=32, char_range=32):
        self.min_length = min_length
        self.max_length = max_length
        self.char_start = char_start
        self.char_range = char_range
    def fuzz(self):
        str_len = random.randrange(self.min_length,self.max_length)
        out = ""
        for i in range(str_len):
            out += chr(random.randrange(self.char_start,self.char_start + self.char_range))
        return out

# 测试RandomFuzzer
random_fuzzer = RandomFuzzer(min_length=5,max_length=10,char_start=ord('a'),char_range=26)
# random_fuzzer.fuzz() # 可以随机生成字符串,很好
cat_runner = ProgramRunner("cat")
outcomes = random_fuzzer.runs(cat_runner,10)
print(outcomes)
# 上面的bc测试使用我们的基类
random_fuzzer = RandomFuzzer(min_length=2,max_length=6,char_start=ord('0'),char_range=10)
bc_runner = ProgramRunner("bc")
outcomes = random_fuzzer.runs(bc_runner,10)
print(outcomes)

本文地址:https://blog.csdn.net/sinat_38816924/article/details/110880475