2019-07-01 09:28:12 +00:00
<!DOCTYPE html>
< html class = "theme-next gemini use-motion" lang = "zh-Hans" >
< head > < meta name = "generator" content = "Hexo 3.8.0" >
< meta charset = "UTF-8" >
< meta http-equiv = "X-UA-Compatible" content = "IE=edge" >
< meta name = "viewport" content = "width=device-width, initial-scale=1, maximum-scale=1" >
< meta name = "theme-color" content = "#222" >
< meta http-equiv = "Cache-Control" content = "no-transform" >
< meta http-equiv = "Cache-Control" content = "no-siteapp" >
< link href = "/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel = "stylesheet" type = "text/css" >
< link href = "/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel = "stylesheet" type = "text/css" >
< link href = "/css/main.css?v=5.1.4" rel = "stylesheet" type = "text/css" >
< link rel = "apple-touch-icon" sizes = "180x180" href = "/images/hackerrank.png?v=5.1.4" >
< link rel = "icon" type = "image/png" sizes = "32x32" href = "/images/hackerrank.png?v=5.1.4" >
< link rel = "icon" type = "image/png" sizes = "16x16" href = "/images/hackerrank.png?v=5.1.4" >
< link rel = "mask-icon" href = "/images/logo.svg?v=5.1.4" color = "#222" >
2019-07-01 11:52:45 +00:00
< meta name = "keywords" content = "AFL,模糊测试," >
2019-07-01 09:28:12 +00:00
2019-07-01 11:52:45 +00:00
< meta name = "description" content = "接触这个词语已经有一年了, 但还没有学习过更没有上手实践过, 正好趁这个机会好好弄弄AFL。提起模糊测试, 我们总会联想起这样或那样的专业术语——测试用例、代码覆盖率、执行路径等等, 你可能和我一样一头雾水, 这次我们就来看个明白 0x01 模糊测试首先, 模糊测试( Fuzzing) 是一种测试手段, 它把系统看成一个摸不清内部结构的黑盒, 只是向其输入接口随机地发送合法测试用例, 这些用例并不是开发者所预期的输入" >
< meta name = "keywords" content = "AFL,模糊测试" >
2019-07-01 09:28:12 +00:00
< meta property = "og:type" content = "article" >
< meta property = "og:title" content = "AFL初探" >
< meta property = "og:url" content = "https://cool-y.github.io/2019/07/01/AFL-first-learn/index.html" >
< meta property = "og:site_name" content = "混元霹雳手" >
2019-07-01 11:52:45 +00:00
< meta property = "og:description" content = "接触这个词语已经有一年了, 但还没有学习过更没有上手实践过, 正好趁这个机会好好弄弄AFL。提起模糊测试, 我们总会联想起这样或那样的专业术语——测试用例、代码覆盖率、执行路径等等, 你可能和我一样一头雾水, 这次我们就来看个明白 0x01 模糊测试首先, 模糊测试( Fuzzing) 是一种测试手段, 它把系统看成一个摸不清内部结构的黑盒, 只是向其输入接口随机地发送合法测试用例, 这些用例并不是开发者所预期的输入" >
2019-07-01 09:28:12 +00:00
< meta property = "og:locale" content = "zh-Hans" >
2019-07-09 08:59:11 +00:00
< meta property = "og:updated_time" content = "2019-07-09T08:54:33.951Z" >
2019-07-01 09:28:12 +00:00
< meta name = "twitter:card" content = "summary" >
< meta name = "twitter:title" content = "AFL初探" >
2019-07-01 11:52:45 +00:00
< meta name = "twitter:description" content = "接触这个词语已经有一年了, 但还没有学习过更没有上手实践过, 正好趁这个机会好好弄弄AFL。提起模糊测试, 我们总会联想起这样或那样的专业术语——测试用例、代码覆盖率、执行路径等等, 你可能和我一样一头雾水, 这次我们就来看个明白 0x01 模糊测试首先, 模糊测试( Fuzzing) 是一种测试手段, 它把系统看成一个摸不清内部结构的黑盒, 只是向其输入接口随机地发送合法测试用例, 这些用例并不是开发者所预期的输入" >
2019-07-01 09:28:12 +00:00
< script type = "text/javascript" id = "hexo.configurations" >
var NexT = window.NexT || {};
var CONFIG = {
root: '/',
scheme: 'Gemini',
version: '5.1.4',
sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false},
fancybox: true,
tabs: true,
motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
duoshuo: {
userId: '0',
author: '博主'
},
algolia: {
applicationID: '',
apiKey: '',
indexName: '',
hits: {"per_page":10},
labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
}
};
< / script >
< link rel = "canonical" href = "https://cool-y.github.io/2019/07/01/AFL-first-learn/" >
< title > AFL初探 | 混元霹雳手< / title >
< / head >
< body itemscope itemtype = "http://schema.org/WebPage" lang = "zh-Hans" >
< div class = "container sidebar-position-left page-post-detail" >
< div class = "headband" > < / div >
< header id = "header" class = "header" itemscope itemtype = "http://schema.org/WPHeader" >
< div class = "header-inner" > < div class = "site-brand-wrapper" >
< div class = "site-meta " >
< div class = "custom-logo-site-title" >
< a href = "/" class = "brand" rel = "start" >
< span class = "logo-line-before" > < i > < / i > < / span >
< span class = "site-title" > 混元霹雳手< / span >
< span class = "logo-line-after" > < i > < / i > < / span >
< / a >
< / div >
< p class = "site-subtitle" > < / p >
< / div >
< div class = "site-nav-toggle" >
< button >
< span class = "btn-bar" > < / span >
< span class = "btn-bar" > < / span >
< span class = "btn-bar" > < / span >
< / button >
< / div >
< / div >
< nav class = "site-nav" >
< ul id = "menu" class = "menu" >
< li class = "menu-item menu-item-home" >
< a href = "/" rel = "section" >
< i class = "menu-item-icon fa fa-fw fa-home" > < / i > < br >
首页
< / a >
< / li >
< li class = "menu-item menu-item-about" >
< a href = "/about/" rel = "section" >
< i class = "menu-item-icon fa fa-fw fa-user" > < / i > < br >
关于
< / a >
< / li >
< li class = "menu-item menu-item-tags" >
< a href = "/tags/" rel = "section" >
< i class = "menu-item-icon fa fa-fw fa-tags" > < / i > < br >
标签
< / a >
< / li >
< li class = "menu-item menu-item-categories" >
< a href = "/categories/" rel = "section" >
< i class = "menu-item-icon fa fa-fw fa-th" > < / i > < br >
分类
< / a >
< / li >
< li class = "menu-item menu-item-archives" >
< a href = "/archives/" rel = "section" >
< i class = "menu-item-icon fa fa-fw fa-archive" > < / i > < br >
归档
< / a >
< / li >
< li class = "menu-item menu-item-bookmarks" >
< a href = "/bookmarks/" rel = "section" >
< i class = "menu-item-icon fa fa-fw fa-map" > < / i > < br >
书签
< / a >
< / li >
< li class = "menu-item menu-item-search" >
< a href = "javascript:;" class = "popup-trigger" >
< i class = "menu-item-icon fa fa-search fa-fw" > < / i > < br >
搜索
< / a >
< / li >
< / ul >
< div class = "site-search" >
< div class = "popup search-popup local-search-popup" >
< div class = "local-search-header clearfix" >
< span class = "search-icon" >
< i class = "fa fa-search" > < / i >
< / span >
< span class = "popup-btn-close" >
< i class = "fa fa-times-circle" > < / i >
< / span >
< div class = "local-search-input-wrapper" >
< input autocomplete = "off" placeholder = "搜索..." spellcheck = "false" type = "text" id = "local-search-input" >
< / div >
< / div >
< div id = "local-search-result" > < / div >
< / div >
< / div >
< / nav >
< / div >
< / header >
< main id = "main" class = "main" >
< div class = "main-inner" >
< div class = "content-wrap" >
< div id = "content" class = "content" >
< div id = "posts" class = "posts-expand" >
< article class = "post post-type-normal" itemscope itemtype = "http://schema.org/Article" >
< div class = "post-block" >
< link itemprop = "mainEntityOfPage" href = "https://cool-y.github.io/2019/07/01/AFL-first-learn/" >
< span hidden itemprop = "author" itemscope itemtype = "http://schema.org/Person" >
< meta itemprop = "name" content = "Cool-Y" >
< meta itemprop = "description" content >
< meta itemprop = "image" content = "/images/avatar.png" >
< / span >
< span hidden itemprop = "publisher" itemscope itemtype = "http://schema.org/Organization" >
< meta itemprop = "name" content = "混元霹雳手" >
< / span >
< header class = "post-header" >
< h1 class = "post-title" itemprop = "name headline" > AFL初探< / h1 >
< div class = "post-meta" >
< span class = "post-time" >
< span class = "post-meta-item-icon" >
< i class = "fa fa-calendar-o" > < / i >
< / span >
< span class = "post-meta-item-text" > 发表于< / span >
< time title = "创建于" itemprop = "dateCreated datePublished" datetime = "2019-07-01T17:25:36+08:00" >
2019-07-01
< / time >
< / span >
< span class = "post-category" >
< span class = "post-meta-divider" > |< / span >
< span class = "post-meta-item-icon" >
< i class = "fa fa-folder-o" > < / i >
< / span >
< span class = "post-meta-item-text" > 分类于< / span >
< span itemprop = "about" itemscope itemtype = "http://schema.org/Thing" >
< a href = "/categories/二进制/" itemprop = "url" rel = "index" >
< span itemprop = "name" > 二进制< / span >
< / a >
< / span >
< / span >
< span class = "post-comments-count" >
< span class = "post-meta-divider" > |< / span >
< span class = "post-meta-item-icon" >
< i class = "fa fa-comment-o" > < / i >
< / span >
< a href = "/2019/07/01/AFL-first-learn/#comments" itemprop = "discussionUrl" >
< span class = "post-comments-count gitment-comments-count" data-xid = "/2019/07/01/AFL-first-learn/" itemprop = "commentsCount" > < / span >
< / a >
< / span >
< span id = "/2019/07/01/AFL-first-learn/" class = "leancloud_visitors" data-flag-title = "AFL初探" >
< span class = "post-meta-divider" > |< / span >
< span class = "post-meta-item-icon" >
< i class = "fa fa-eye" > < / i >
< / span >
< span class = "post-meta-item-text" > 阅读次数: < / span >
< span class = "leancloud-visitors-count" > < / span >
< / span >
< div class = "post-wordcount" >
< span class = "post-meta-item-icon" >
< i class = "fa fa-file-word-o" > < / i >
< / span >
< span title = "字数统计" >
2019-07-09 08:59:11 +00:00
11.7k 字
2019-07-01 09:28:12 +00:00
< / span >
< span class = "post-meta-divider" > |< / span >
< span class = "post-meta-item-icon" >
< i class = "fa fa-clock-o" > < / i >
< / span >
< span title = "阅读时长" >
2019-07-09 08:59:11 +00:00
44 分钟
2019-07-01 09:28:12 +00:00
< / span >
< / div >
< / div >
< / header >
< div class = "post-body" itemprop = "articleBody" >
< p > 接触这个词语已经有一年了, 但还没有学习过更没有上手实践过, 正好趁这个机会好好弄弄AFL。提起模糊测试, 我们总会联想起这样或那样的专业术语——测试用例、代码覆盖率、执行路径等等, 你可能和我一样一头雾水, 这次我们就来看个明白< / p >
2019-07-01 11:52:45 +00:00
< hr >
2019-07-01 09:28:12 +00:00
< h1 id = "0x01-模糊测试" > < a href = "#0x01-模糊测试" class = "headerlink" title = "0x01 模糊测试" > < / a > 0x01 模糊测试< / h1 > < p > 首先, 模糊测试( Fuzzing) 是一种测试手段, 它把系统看成一个摸不清内部结构的黑盒, 只是向其输入接口随机地发送合法测试用例, 这些用例并不是开发者所预期的输入, 所以极有可能会造成系统的崩溃, 通过分析崩溃信息, 测试人员( 黑客) 就可以评估系统是否存在可利用的漏洞。< br > 模糊测试的过程,就好像是一个不断探测系统可以承受的输入极限的过程,让我想起学电子的时候对一个滤波器进行带宽的评估,如果我们知道内部电路原理,那么这个器件对于我们就是白盒了,可以直接通过公式计算理论带宽,现在系统对于我们而言是一个黑盒,我们通过在足够大频率范围内对其不断输入信号,就能测试出其实际带宽。< / p >
< p > < strong > 模糊测试方法一览< / strong > < / p >
2019-07-01 12:01:56 +00:00
< table >
< tr >
< th rowspan = "2" > 基于变种的Fuzzer< / th >
< th rowspan = "2" > 基于模板的Fuzzer< / th >
< th colspan = "2" > 基于反馈演进的Fuzzer< / th >
< / tr >
< tr >
< td > 基于追踪路径覆盖率< / td >
< td > 基于分支覆盖率< / td >
< / tr >
< tr >
< td rowspan = "2" > 在已知合法的输入的基础上,对该输入进行随机变种或者依据某种经验性的变种,从而产生不可预期的测试输入。< / td >
< td rowspan = "2" > 此类Fuzzer工具的输入数据, 依赖于安全人员结合自己的知识, 给出输入数据的模板, 构造丰富的输入测试数据。< / td >
< td colspan = "2" > 此类Fuzzer会实时的记录当前对于目标程序测试的覆盖程度, 从而调整自己的fuzzing输入。< / td >
< / tr >
< tr >
< td > PAP:路径编码的算法;后面会产生路径爆炸的问题< / td >
< td > 漏洞的爆发往往由于触发了非预期的分支< / td >
< / tr >
< tr >
< td > Taof, GPF, ProxyFuzz, Peach Fuzzer< / td >
< td > SPIKE, Sulley, Mu‐ 4000, Codenomicon< / td >
< td > < / td >
< td > AFL< / td >
< / tr >
< / table >
2019-07-01 09:28:12 +00:00
2019-07-01 11:52:45 +00:00
< hr >
2019-07-09 08:59:11 +00:00
< h1 id = "0x02-AFL快速入门" > < a href = "#0x02-AFL快速入门" class = "headerlink" title = "0x02 AFL快速入门" > < / a > 0x02 < a href = "http://lcamtuf.coredump.cx/afl/QuickStartGuide.txt" target = "_blank" rel = "noopener" > AFL快速入门< / a > < / h1 > < p > 1) 用< code > make< / code > 编译AFL。如果构建失败, 请参阅docs / INSTALL以获取提示。< br > 2) 查找或编写一个相当快速和简单的程序, 该程序从< strong > < em > 文件或标准输入< / em > < / strong > 中获取数据, 以一种有价值的方式处理它, 然后干净地退出。如果测试网络服务, 请将其修改为在前台运行并从stdin读取。在对使用校验和的格式进行模糊测试时, 也要注释掉校验和验证码。< br > 遇到故障时, 程序必须正常崩溃。注意自定义SIGSEGV或SIGABRT处理程序和后台进程。有关检测非崩溃缺陷的提示, 请参阅< figure class = "highlight plain" > < table > < tr > < td class = "gutter" > < pre > < span class = "line" > 1< / span > < br > < / pre > < / td > < td class = "code" > < pre > < span class = "line" > 3) 使用afl-gcc编译要模糊的程序/库。一种常见的方法是:< / span > < br > < / pre > < / td > < / tr > < / table > < / figure > < / p >
< p > $ CC = /path/to/afl-gcc CXX =/path/to/afl-g++ ./configure – disable-shared< br > $ make clean all< br > < figure class = "highlight plain" > < table > < tr > < td class = "gutter" > < pre > < span class = "line" > 1< / span > < br > < span class = "line" > 2< / span > < br > < span class = "line" > 3< / span > < br > < span class = "line" > 4< / span > < br > < span class = "line" > 5< / span > < br > < span class = "line" > 6< / span > < br > < span class = "line" > 7< / span > < br > < span class = "line" > 8< / span > < br > < span class = "line" > 9< / span > < br > < span class = "line" > 10< / span > < br > < span class = "line" > 11< / span > < br > < span class = "line" > 12< / span > < br > < span class = "line" > 13< / span > < br > < span class = "line" > 14< / span > < br > < span class = "line" > 15< / span > < br > < span class = "line" > 16< / span > < br > < span class = "line" > 17< / span > < br > < span class = "line" > 18< / span > < br > < span class = "line" > 19< / span > < br > < span class = "line" > 20< / span > < br > < span class = "line" > 21< / span > < br > < span class = "line" > 22< / span > < br > < span class = "line" > 23< / span > < br > < span class = "line" > 24< / span > < br > < span class = "line" > 25< / span > < br > < span class = "line" > 26< / span > < br > < span class = "line" > 27< / span > < br > < span class = "line" > 28< / span > < br > < span class = "line" > 29< / span > < br > < span class = "line" > 30< / span > < br > < span class = "line" > 31< / span > < br > < span class = "line" > 32< / span > < br > < span class = "line" > 33< / span > < br > < span class = "line" > 34< / span > < br > < span class = "line" > 35< / span > < br > < span class = "line" > 36< / span > < br > < span class = "line" > 37< / span > < br > < span class = "line" > 38< / span > < br > < span class = "line" > 39< / span > < br > < span class = "line" > 40< / span > < br > < span class = "line" > 41< / span > < br > < span class = "line" > 42< / span > < br > < span class = "line" > 43< / span > < br > < span class = "line" > 44< / span > < br > < span class = "line" > 45< / span > < br > < span class = "line" > 46< / span > < br > < span class = "line" > 47< / span > < br > < span class = "line" > 48< / span > < br > < span class = "line" > 49< / span > < br > < span class = "line" > 50< / span > < br > < span class = "line" > 51< / span > < br > < span class = "line" > 52< / span > < br > < span class = "line" > 53< / span > < br > < span class = "line" > 54< / span > < br > < span class = "line" > 55< / span > < br > < span class = "line" > 56< / span > < br > < span class = "line" > 57< / span > < br > < span class = "line" > 58< / span > < br > < span class = "line" > 59< / span > < br > < span class = "line" > 60< / span > < br > < span class = "line" > 61< / span > < br > < span class = "line" > 62< / span > < br > < span class = "line" > 63< / span > < br > < span class = "line" > 64< / span > < br > < span class = "line" > 65< / span > < br > < span class = "line" > 66< / span > < br > < span class = "line" > 67< / span > < br > < span class = "line" > 68< / span > < br > < span class = "line" > 69< / span > < br > < span class = "line" > 70< / span > < br > < span class = "line" > 71< / span > < br > < span class = "line" > 72< / span > < br > < span class = "line" > 73< / span > < br > < / pre > < / td > < td class = "code" > < pre > < span class = "line" > 如果程序构建失败,请联系 < afl-users@googlegroups.com> 。< / span > < br > < span class = "line" > 4) 获取一个对程序有意义的小而有效的输入文件。在模糊详细语法( SQL, HTTP等) 时, 也要创建字典, 如```dictionaries/README.dictionaries```中所述。< / span > < br > < span class = "line" > 5) 如果程序从stdin读取, 则运行`afl-fuzz`,如下所示:< / span > < br > < span class = "line" > `./afl-fuzz -i testcase_dir -o findings_dir -- /path/to/tested/program [... program' s cmdline ...] `< / span > < br > < span class = "line" > 如果程序从文件中获取输入,则可以在程序的命令行中输入@@; AFL会为您放置一个自动生成的文件名。< / span > < br > < span class = "line" > < / span > < br > < span class = "line" > **一些参考文档**< / span > < br > < span class = "line" > > [docs/README](http://lcamtuf.coredump.cx/afl/README.txt) - AFL的一般介绍, < / span > < br > < span class = "line" > > [docs/perf_tips.txt](https://github.com/mirrorer/afl/blob/master/docs/perf_tips.txt) - 关于如何快速模糊的简单提示,< / span > < br > < span class = "line" > > [docs/status_screen.txt](http://lcamtuf.coredump.cx/afl/status_screen.txt) - UI中显示的花絮的解释, < / span > < br > < span class = "line" > > [docs/parallel_fuzzing.txt](https://github.com/mirrorer/afl/blob/master/docs/parallel_fuzzing.txt) - 关于在多个核上运行AFL的建议< / span > < br > < span class = "line" > > [Generated test cases for common image formats](http://lcamtuf.coredump.cx/afl/demo/) - 生成图像文件测试用例的demo< / span > < br
< p > $ CC = /path/to/afl/afl-gcc ./configure< br > $ make clean all< br > 对于C ++程序, 您还需要将CXX = / path /设置为/ afl / afl g ++。< br > < figure class = "highlight plain" > < table > < tr > < td class = "gutter" > < pre > < span class = "line" > 1< / span > < br > < span class = "line" > 2< / span > < br > < span class = "line" > 3< / span > < br > < / pre > < / td > < td class = "code" > < pre > < span class = "line" > clang组件( afl-clang和afl-clang ++)可以以相同的方式使用; clang用户也可以选择利用更高性能的检测模式, 如llvm_mode / README.llvm中所述。< / span > < br > < span class = "line" > < / span > < br > < span class = "line" > 在测试库时, 您需要查找或编写一个简单的程序, 该程序从stdin或文件中读取数据并将其传递给测试的库。在这种情况下, 必须 **将此可执行文件与已检测库的静态版本相链接** ,或者确保在运行时加载正确的.so文件( 通常通过设置LD_LIBRARY_PATH) 。最简单的选项是 **静态构建** ,通常可以通过以下方式实现:< / span > < br > < / pre > < / td > < / tr > < / table > < / figure > < / p >
< p > $ CC = /path/to/afl/afl-gcc ./configure – disable-shared< br > < figure class = "highlight plain" > < table > < tr > < td class = "gutter" > < pre > < span class = "line" > 1< / span > < br > < span class = "line" > 2< / span > < br > < span class = "line" > 3< / span > < br > < span class = "line" > 4< / span > < br > < span class = "line" > 5< / span > < br > < span class = "line" > 6< / span > < br > < / pre > < / td > < td class = "code" > < pre > < span class = "line" > 调用`make`时设置AFL_HARDEN = 1将导致CC组件自动启用代码强化选项, 以便更容易检测到简单的内存错误。 Libdislocator, AFL附带的帮助程序库( 请参阅libdislocator / README.dislocator) 也可以帮助发现堆损坏问题。< / span > < br > < span class = "line" > PS。建议ASAN用户查看notes_for_asan.txt文件以获取重要警告。< / span > < br > < span class = "line" > < / span > < br > < span class = "line" > ## 4) 检测仅二进制应用程序< / span > < br > < span class = "line" > 当源代码为不可得时, afl为黑盒二进制文件的快速、即时检测提供实验支持。 这是通过在较不为人知的“用户空间仿真”模式下运行的QEMU版本来实现的。< / span > < br > < span class = "line" > QEMU是一个独立于AFL的项目, 但您可以通过以下方式方便地构建该功能: < / span > < br > < / pre > < / td > < / tr > < / table > < / figure > < / p >
< p > $ cd qemu_mode< br > $ ./build_qemu_support.sh< br > < figure class = "highlight plain" > < table > < tr > < td class = "gutter" > < pre > < span class = "line" > 1< / span > < br > < span class = "line" > 2< / span > < br > < span class = "line" > 3< / span > < br > < span class = "line" > 4< / span > < br > < span class = "line" > 5< / span > < br > < span class = "line" > 6< / span > < br > < span class = "line" > 7< / span > < br > < span class = "line" > 8< / span > < br > < span class = "line" > 9< / span > < br > < span class = "line" > 10< / span > < br > < span class = "line" > 11< / span > < br > < span class = "line" > 12< / span > < br > < span class = "line" > 13< / span > < br > < span class = "line" > 14< / span > < br > < / pre > < / td > < td class = "code" > < pre > < span class = "line" > 有关其他说明和注意事项, 请参阅qemu_mode / README.qemu。< / span > < br > < span class = "line" > 该模式比编译时插桩( instrumentation) 慢约2-5倍, 对并行化的兼容较差, 并且可能有一些其他的不同。< / span > < br > < span class = "line" > < / span > < br > < span class = "line" > ## 5) 选择初始测试用例< / span > < br > < span class = "line" > 为了正确操作,模糊器需要一个或多个起始文件,其中包含目标应用程序通常所需的输入数据的良好示例。 有两个基本规则:< / span > < br > < span class = "line" > > 测试用例足够小。 1 kB以下是理想的, 尽管不是绝对必要的。 有关大小重要性的讨论, 请参阅perf_tips.txt。< / span > < br > < span class = "line" > > < / span > < br > < span class = "line" > > 只有在功能上彼此不同时才使用多个测试用例。 使用五十张不同的度假照片来模糊图像库是没有意义的。< / span > < br > < span class = "line" > 您可以在此工具附带的`testcases/子目录`中找到许多启动文件的好例子。< / span > < br > < span class = "line" > PS。 如果有大量数据可用于筛选,您可能希望使用`afl-cmin`实用程序来识别在目标二进制文件中使用不同代码路径的功能不同的文件的子集。< / span > < br > < span class = "line" > < / span > < br > < span class = "line" > ## 6) 模糊测试二进制文件< / span > < br > < span class = "line" > 测试过程本身由afl-fuzz实用程序执行。该程序需要一个带有初始测试用例的只读目录, 一个存储其输出结果的独立位置, 以及要测试的二进制文件的路径。< / span > < br > < span class = "line" > 对于直接从 **stdin** 接受输入的目标二进制文件,通常的语法是:< / span > < br > < / pre > < / td > < / tr > < / table > < / figure > < / p >
< p > $ ./afl-fuzz -i testcase_dir -o findings_dir /path/to/program [… params …]< br > < figure class = "highlight plain" > < table > < tr > < td class = "gutter" > < pre > < span class = "line" > 1< / span > < br > < / pre > < / td > < td class = "code" > < pre > < span class = "line" > 对于从 **文件** 中获取输入的程序,使用“@@”标记目标命令行中应放置输入文件名的位置。模糊器将替换为您:< / span > < br > < / pre > < / td > < / tr > < / table > < / figure > < / p >
< p > $ ./afl-fuzz -i testcase_dir -o findings_dir /path/to/program @@< br > < figure class = "highlight plain" > < table > < tr > < td class = "gutter" > < pre > < span class = "line" > 1< / span > < br > < span class = "line" > 2< / span > < br > < span class = "line" > 3< / span > < br > < span class = "line" > 4< / span > < br > < span class = "line" > 5< / span > < br > < span class = "line" > 6< / span > < br > < span class = "line" > 7< / span > < br > < span class = "line" > 8< / span > < br > < span class = "line" > 9< / span > < br > < span class = "line" > 10< / span > < br > < span class = "line" > 11< / span > < br > < span class = "line" > 12< / span > < br > < span class = "line" > 13< / span > < br > < span class = "line" > 14< / span > < br > < span class = "line" > 15< / span > < br > < span class = "line" > 16< / span > < br > < span class = "line" > 17< / span > < br > < / pre > < / td > < td class = "code" > < pre > < span class = "line" > 您还可以使用-f选项将变异数据写入特定文件。如果程序需要特定的文件扩展名, 那么这很有用。< / span > < br > < span class = "line" > 非插桩二进制文件可以在QEMU模式下( 在命令行中添加-Q) 或在传统的盲目模糊模式( 指定-n) 中进行模糊测试。< / span > < br > < span class = "line" > 您可以使用-t和-m覆盖已执行进程的默认超时和内存限制;< / span > < br > < span class = "line" > perf_tips.txt中讨论了优化模糊测试性能的技巧。< / span > < br > < span class = "line" > < / span > < br > < span class = "line" > 请注意, afl-fuzz首先执行一系列确定性模糊测试步骤, 这可能需要几天时间, 但往往会产生整齐的测试用例。如果你想要快速结果 - 类似于zzuf和其他传统的模糊器 - 在命令行中添加-d选项。< / span > < br > < span class = "line" > < / span > < br > < span class = "line" > ## 7) 解释输出< / span > < br > < span class = "line" > 有关如何解释显示的统计信息以及监视进程运行状况的信息,请参阅`status_screen.txt`文件。请务必查阅此文件, 尤其是如果任何UI元素以红色突出显示。< / span > < br > < span class = "line" > 模糊过程将持续到按`Ctrl-C`为止。至少,您希望允许模糊器完成一个队列周期,这可能需要几个小时到一周左右的时间。< / span > < br > < span class = "line" > 在输出目录中创建了三个子目录并实时更新:< / span > < br > < span class = "line" > - 队列queue/ - 测试每个独特执行路径的案例, 以及用户提供的所有起始文件。这是第2节中提到的合成语料库。在将此语料库用于任何其他目的之前, 您可以使用afl-cmin工具将其缩小到较小的大小。该工具将找到一个较小的文件子集, 提供相同的边缘覆盖。< / span > < br > < span class = "line" > - 崩溃crash/ - 导致被测程序接收致命信号的独特测试用例( 例如, SIGSEGV, SIGILL, SIGABRT) 。条目按接收信号分组。< / span > < br > < span class = "line" > - 挂起hang/ - 导致测试程序超时的独特测试用例。将某些内容归类为挂起之前的默认时间限制是1秒内的较大值和-t参数的值。可以通过设置AFL_HANG_TMOUT来微调该值, 但这很少是必需的。崩溃和挂起被视为“唯一” “如果相关的执行路径涉及在先前记录的故障中未见的任何状态转换。如果可以通过多种方式达到单个错误,那么在此过程中会有一些计数通货膨胀,但这应该会迅速逐渐减少。< / span > < br > < span class = "line" > < / span > < br > < span class = "line" > 崩溃和挂起的文件名与父、非错误的队列条目相关联。这应该有助于调试。< / span > < br > < span class = "line" > 如果无法重现 **afl-fuzz** 发现的崩溃,最可能的原因是您没有设置与工具使用的内存限制相同的内存限制。尝试:< / span > < br > < / pre > < / td > < / tr > < / table > < / figure > < / p >
< p > $ LIMIT_MB = 50< br > $( ulimit -Sv $ [LIMIT_MB < < 10]; /path/to/tested_binary …)< br > < figure class = "highlight plain" > < table > < tr > < td class = "gutter" > < pre > < span class = "line" > 1< / span > < br > < span class = "line" > 2< / span > < br > < span class = "line" > 3< / span > < br > < span class = "line" > 4< / span > < br > < span class = "line" > 5< / span > < br > < span class = "line" > 6< / span > < br > < span class = "line" > 7< / span > < br > < span class = "line" > 8< / span > < br > < span class = "line" > 9< / span > < br > < span class = "line" > 10< / span > < br > < span class = "line" > 11< / span > < br > < span class = "line" > 12< / span > < br > < span class = "line" > 13< / span > < br > < span class = "line" > 14< / span > < br > < span class = "line" > 15< / span > < br > < span class = "line" > 16< / span > < br > < span class = "line" > 17< / span > < br > < span class = "line" > 18< / span > < br > < span class = "line" > 19< / span > < br > < span class = "line" > 20< / span > < br > < span class = "line" > 21< / span > < br > < span class = "line" > 22< / span > < br > < span class = "line" > 23< / span > < br > < span class = "line" > 24< / span > < br > < span class = "line" > 25< / span > < br > < span class = "line" > 26< / span > < br > < span class = "line" > 27< / span > < br > < span class = "line" > 28< / span > < br > < span class = "line" > 29< / span > < br > < span class = "line" > 30< / span > < br > < span class = "line" > 31< / span > < br > < span class = "line" > 32< / span > < br > < span class = "line" > 33< / span > < br > < span class = "line" > 34< / span > < br > < span class = "line" > 35< / span > < br > < span class = "line" > 36< / span > < br > < span class = "line" > 37< / span > < br > < span class = "line" > 38< / span > < br > < span class = "line" > 39< / span > < br > < span class = "line" > 40< / span > < br > < span class = "line" > 41< / span > < br > < span class = "line" > 42< / span > < br > < span class = "line" > 43< / span > < br > < span class = "line" > 44< / span > < br > < span class = "line" > 45< / span > < br > < span class = "line" > 46< / span > < br > < span class = "line" > 47< / span > < br > < span class = "line" > 48< / span > < br > < span class = "line" > 49< / span > < br > < span class = "line" > 50< / span > < br > < span class = "line" > 51< /span>< br > < span class = "line" > 52< / span > < br > < span class = "line" > 53< / span > < br > < span class = "line" > 54< / span > < br > < span class = "line" > 55< / span > < br > < span class = "line" > 56< / span > < br > < span class = "line" > 57< / span > < br > < span class = "line" > 58< / span > < br > < span class = "line" > 59< / span > < br > < span class = "line" > 60< / span > < br > < span class = "line" > 61< / span > < br > < span class = "line" > 62< / span > < br > < span class = "line" > 63< / span > < br > < span class = "line" > 64< / span > < br > < span class = "line" > 65< / span > < br > < span class = "line" > 66< / span > < br > < / pre > < / td > < td class = "code" > < pre > < span class = "line" > 更改LIMIT_MB以匹配传递给afl-fuzz的-m参数。在OpenBSD上, 也将-Sv更改为-Sd。任何现有的输出目录也可用于恢复中止的作业;< / span > < br > < span class = "line" > 尝试:`$ ./afl-fuzz -i-o_ existing_output_dir [...etc...]`< / span > < br > < span class = "line" > 如果安装了gnuplot, 您还可以使用afl-plot为任何活动的模糊测试任务生成一些漂亮的图形。有关如何显示的示例, 请参阅 http://lcamt uf.coredump.cx/afl/plot/ 。< / span > < br > < span class = "line" > < / span > < br > < span class = "line" > ## 8) 并行模糊测试< / span > < br > < span class = "line" > 每个afl-fuzz的实例大约占用一个核。 这意味着在多核系统上,并行化是充分利用硬件所必需的。< / span > < br > < span class = "line" > 有关如何在多个核心或多个联网计算机上模糊常见目标的提示,请参阅`parallel_fuzzing.txt`。< / span > < br > < span class = "line" > ***并行模糊测试模式*** 还提供了一种简单的方法, 用于将AFL连接到其他模糊器, 动态符号执行( concrete and symbolic, concolic execution) 引擎等等; 再次,请参阅 `parallel_fuzzing.txt`的最后一节以获取提示。< / span > < br > < span class = "line" > < / span > < br > < span class = "line" > ## 9) Fuzzer词典< / span > < br > < span class = "line" > 默认情况下, afl-fuzz变异引擎针对紧凑数据格式进行了优化 - 例如, 图像, 多媒体, 压缩数据, 正则表达式语法或shell脚本。它有点不太适合具有特别冗长和冗余的语言的语言 - 特别是包括HTML, SQL或JavaScript。< / span > < br > < span class = "line" > 为了避免构建语法感知工具的麻烦, afl-fuzz提供了一种方法, 使用与目标数据类型相关联的其他特殊标记的语言关键字, 魔术头或可选字典为模糊测试过程设
< p > cur_location = < COMPILE_TIME_RANDOM> ; //用一个随机数标记当前基本块< br > shared_mem[cur_location ^ prev_location]++; //将当前块和前一块异或保存到shared_mem[]< br > prev_location = cur_location > > 1; //cur_location右移1位区分从当前块到当前块的转跳< br > < figure class = "highlight plain" > < table > < tr > < td class = "gutter" > < pre > < span class = "line" > 1< / span > < br > < span class = "line" > 2< / span > < br > < span class = "line" > 3< / span > < br > < span class = "line" > 4< / span > < br > < span class = "line" > 5< / span > < br > < span class = "line" > 6< / span > < br > < span class = "line" > 7< / span > < br > < span class = "line" > 8< / span > < br > < span class = "line" > 9< / span > < br > < span class = "line" > 10< / span > < br > < span class = "line" > 11< / span > < br > < span class = "line" > 12< / span > < br > < span class = "line" > 13< / span > < br > < span class = "line" > 14< / span > < br > < span class = "line" > 15< / span > < br > < span class = "line" > 16< / span > < br > < span class = "line" > 17< / span > < br > < span class = "line" > 18< / span > < br > < span class = "line" > 19< / span > < br > < span class = "line" > 20< / span > < br > < span class = "line" > 21< / span > < br > < span class = "line" > 22< / span > < br > < span class = "line" > 23< / span > < br > < span class = "line" > 24< / span > < br > < span class = "line" > 25< / span > < br > < / pre > < / td > < td class = "code" > < pre > < span class = "line" > cur_location 的值是随机产生的, 为的是简化连接复杂对象的过程和保持XOR输出分布是均匀的。< / span > < br > < span class = "line" > shared_mem[] 数组是一个调用者 (caller) 传给被插桩的二进制程序的64kB的共享空间。其中的每一字节可以理解成对于插桩代码中特别的元组(branch_src, branch_dst)的一次命中( hit) 。< / span > < br > < span class = "line" > 选择这个数组大小的原因是让冲突(collisions)尽可能减少。这样通常能处理2k到10k的分支点。同时, 它的大小也足以使输出图能在接受端达到毫秒级的分析。< / span > < br > < span class = "line" > < / span > < br > < span class = "line" > |Branch cnt | Colliding tuples | Example targets|< / span > < br > < span class = "line" > |------------|------------------|-----------------|< / span > < br > < span class = "line" > | 1,000 | 0.75% | giflib, lzo |< / span > < br > < span class = "line" > | 2,000 | 1.5% | zlib, tar, xz |< / span > < br > < span class = "line" > | 5,000 | 3.5% | libpng, libwebp |< / span > < br > < span class = "line" > | 10,000 | 7% | libxml |< / span > < br > < span class = "line" > | 20,000 | 14% | sqlite |< / span > < br > < span class = "line" > | 50,000 | 30% | - |< / span > < br > < span class = "line" > < / span > < br > < span class = "line" > 这种形式的覆盖率,相对于简单的基本块覆盖率来说,对程序运行路径提供了一个更好的描述(insight)。特别地,它能很好地区分以下两个执行路径:< / span > < br > < span class = "line" > > A -> B -> C -> D -> E (tuples: AB, BC, CD, DE)< / span > < br > < span class = "line" > > A -> B -> D -> C -> E (tuples: AB, BD, DC, CE)< / span > < br > < span class = "line" > < / span > < br > < span class = "line" > 这有助于发现底层代码的微小错误条件。因为 **安全漏洞通常是一些非预期(或不正确)的语句转移(一个tuple就是一个语句转移)** ,而不是没覆盖到某块代码。< / span > < br > < span class = "line" > 上边伪代码的最后一行移位操作是为了让tuple具有定向性(没有这一行的话, A^B和B^A就没区别了, 同样, A^A和B^B也没区别了)。采用右移的原因跟Intel CPU的一些特性有关。< / span > < br > < span class = "line" > < / span > < br > < span class = "line" > ## 2) 发现新路径(Detecting new behaviors)< / span > < br > < span class = "line" > AFL的fuzzers使用一个**全局Map**来存储之前执行时看到的tuple。这些数据可以被用来对不同的trace进行快速对比, 从而可以计算出是否新执行了一个dword指令/一个qword-wide指令/一个简单的循环。< / span > < br > < span class = "line" > 当一个变异的输入产生了一个包含新tuple的执行路径时, 对应的输入文件就被保存, 然后被发送到下一过程(见第3部分)。对于那些没有产生新路径的输入, 就算他<E7AE97> <E4BB96>
< p > #1: A -> B -> C -> D -> E< / p >
< p > #2: A -> B -> C -> A -> E< br > < figure class = "highlight plain" > < table > < tr > < td class = "gutter" > < pre > < span class = "line" > 1< / span > < br > < span class = "line" > 2< / span > < br > < span class = "line" > 3< / span > < br > < span class = "line" > 4< / span > < br > < span class = "line" > 5< / span > < br > < span class = "line" > 6< / span > < br > < span class = "line" > 7< / span > < br > < span class = "line" > 8< / span > < br > < span class = "line" > 9< / span > < br > < span class = "line" > 10< / span > < br > < span class = "line" > 11< / span > < br > < span class = "line" > 12< / span > < br > < span class = "line" > 13< / span > < br > < span class = "line" > 14< / span > < br > < span class = "line" > 15< / span > < br > < span class = "line" > 16< / span > < br > < span class = "line" > 17< / span > < br > < span class = "line" > 18< / span > < br > < span class = "line" > 19< / span > < br > < span class = "line" > 20< / span > < br > < span class = "line" > 21< / span > < br > < span class = "line" > 22< / span > < br > < span class = "line" > 23< / span > < br > < span class = "line" > 24< / span > < br > < span class = "line" > 25< / span > < br > < span class = "line" > 26< / span > < br > < span class = "line" > 27< / span > < br > < span class = "line" > 28< / span > < br > < span class = "line" > 29< / span > < br > < span class = "line" > 30< / span > < br > < span class = "line" > 31< / span > < br > < span class = "line" > 32< / span > < br > < span class = "line" > 33< / span > < br > < span class = "line" > 34< / span > < br > < span class = "line" > 35< / span > < br > < span class = "line" > 36< / span > < br > < span class = "line" > 37< / span > < br > < span class = "line" > 38< / span > < br > < span class = "line" > 39< / span > < br > < span class = "line" > 40< / span > < br > < span class = "line" > 41< / span > < br > < span class = "line" > 42< / span > < br > < span class = "line" > 43< / span > < br > < span class = "line" > 44< / span > < br > < span class = "line" > 45< / span > < br > < span class = "line" > 46< / span > < br > < span class = "line" > 47< / span > < br > < span class = "line" > 48< / span > < br > < span class = "line" > 49< / span > < br > < span class = "line" > 50< / span > < br > < span class = "line" > 51< / span > < br > < span class = "line" > 52< / span > < br > < span class = "line" > 53< / span > < br > < span class = "line" > 54< / span > < br > < span class = "line" > 55< / span > < br > < span class = "line" > 56< / span > < br > < span class = "line" > 57< / span > < br > < / pre > < / td > < td class = "code" > < pre > < span class = "line" > 因为#2的原因, 以下的路径就不认为是不同的路径了, 尽管看起来非常不同: < / span > < br > < span class = "line" > `#3: A -> B -> C -> A -> B -> C -> A -> B -> C -> D -> E`< / span > < br > < span class = "line" > < / span > < br > < span class = "line" > 除了检测新的tuple之外, AFL的fuzzer也会粗略地记录tuple的**命中数(hit counts)**。这些被分割成几个buckets: 1, 2, 3, 4-7, 8-15, 16-31, 32-127, 128+< / span > < br > < span class = "line" > < / span > < br > < span class = "line" > 从某种意义来说, buckets里边的数目是有实际意义的: 它是一个8-bit counter和一个8-position bitmap的映射。8-bit counter是由桩生成的, 8-position bitmap则依赖于每个fuzzer记录的已执行的tuple的命中数。< / span > < br > < span class = "line" > 单个bucket的改变会被忽略掉: 在程序控制流中, bucket的转换会被标记成一个interesting change, 传入evolutionary(见第三部分)进行处理。< / span > < br > < span class = "line" > 通过命中次数(hit count),我们能够分辨控制流是否发生变化。例如一个代码块被执行了两次,但只命中了一次。并且这种方法对循环的次数不敏感(循环47次和48次没区别)。< / span > < br > < span class = "line" > 这种算法通过限制内存和运行时间来保证效率。< / span > < br > < span class = "line" > < / span > < br > < span class = "line" > 另外, 算法通过设置执行超时, 来避免效率过低的fuzz。从而进一步发现效率比较高的fuzz方式。< / span > < br > < span class = "line" > < / span > < br > < span class = "line" > ## 3) 输入队列的进化(Evolving the input queue)< / span > < br > < span class = "line" > 经变异的测试用例,会使程序产生 ***新的状态转移*** 。这些测试用例稍后被添加到 input 队列中,用作下一个 fuzz 循环。它们补充但不替换现有的发现。< / span > < br > < span class = "line" > 这种算法允许工具可以持续探索不同的代码路径,即使底层的数据格式可能是完全不同的。如下图:< / span > < br > < span class = "line" > ![](http://lcamtuf.coredump.cx/afl/afl_gzip.png)< / span > < br > < span class = "line" > < / span > < br
< p > 1) Find next tuple not yet in the temporary working set,< / p >
< p > 2) Locate the winning queue entry for this tuple,< / p >
< p > 3) Register < em > all< / em > tuples present in that entry’ s trace in the working set,< / p >
< p > 4) Go to #1 if there are any missing tuples in the set.< br > < figure class = "highlight plain" > < table > < tr > < td class = "gutter" > < pre > < span class = "line" > 1< / span > < br > < / pre > < / td > < td class = "code" > < pre > < span class = "line" > " favored" entries 产生的语料, 会比初始的数据集小5到10倍。没有被选择的也没有被扔掉, 而是在遇到下列对队列时, 以一定概率略过: < / span > < br > < / pre > < / td > < / tr > < / table > < / figure > < / p >
2019-07-01 09:28:12 +00:00
< ul >
2019-07-09 08:59:11 +00:00
< li > < p > If there are new, yet-to-be-fuzzed favorites present in the queue,< br > 99% of non-favored entries will be skipped to get to the favored ones.< / p >
< / li >
< li > < p > If there are no new favorites:< / p >
< / li >
< li > < p > If the current non-favored entry was fuzzed before, it will be skipped 95% of the time.< / p >
< / li >
< li > < p > If it hasn’ t gone through any fuzzing rounds yet, the odds of skipping drop down to 75%.< / p >
< figure class = "highlight plain" > < table > < tr > < td class = "gutter" > < pre > < span class = "line" > 1< / span > < br > < span class = "line" > 2< / span > < br > < span class = "line" > 3< / span > < br > < span class = "line" > 4< / span > < br > < span class = "line" > 5< / span > < br > < span class = "line" > 6< / span > < br > < span class = "line" > 7< / span > < br > < span class = "line" > 8< / span > < br > < span class = "line" > 9< / span > < br > < span class = "line" > 10< / span > < br > < span class = "line" > 11< / span > < br > < span class = "line" > 12< / span > < br > < span class = "line" > 13< / span > < br > < span class = "line" > 14< / span > < br > < span class = "line" > 15< / span > < br > < / pre > < / td > < td class = "code" > < pre > < span class = "line" > 基于以往的实验经验,这种方法能够在队列周期速度(queue cycling speed)和测试用例多样性(test case diversity)之间达到一个合理的平衡。< / span > < br > < span class = "line" > 使用**afl-cmin工具**能够对输入或输出的语料库进行稍微复杂但慢得多的的处理。这一工具将永久丢弃冗余entries, 产生适用于afl-fuzz或者外部工具的更小的语料库。< / span > < br > < span class = "line" > < / span > < br > < span class = "line" > ## 5) 输入文件修剪( Trimming input files) < / span > < br > < span class = "line" > 文件的大小对fuzzing的性能有着重大影响(dramatic impact)。因为大文件会让目标二进制文件运行变慢;大文件还会减少变异触及重要格式控制结构(format control structures)的可能性(**我们希望的是变异要触及冗余代码块(redundant data blocks)**)。这个问题将在[perf_tips.txt](https://github.com/mirrorer/afl/blob/master/docs/perf_tips.txt)细说。< / span > < br > < span class = "line" > 用户可能提供低质量初始语料(starting corpus),某些类型的变异会迭代地增加生成文件的大小。所以要抑制这种趋势(counter this trend)。< / span > < br > < span class = "line" > 幸运的是,**插桩反馈(instrumentation feedback)**提供了一种简单的方式自动削减( trim down) 输入文件, 并确保这些改变能使得文件对执行路径没有影响。< / span > < br > < span class = "line" > afl-fuzz内置的修剪器(trimmer)使用变化的长度和步距(variable length and stepover)来连续地(sequentially)删除数据块; 任何不影响trace map的校验和(checksum)的删除块将被提交到disk。< / span > < br > < span class = "line" > 这个修剪器的设计并不算特别地周密(thorough),相反地,它试着在精确度(precision)和进程调用execve()的次数之间选取一个平衡, 找到一个合适的block size和stepover。平均每个文件将增大约5-20%。< / span > < br > < span class = "line" > 独立的**afl-tmin工具**使用更完整(exhaustive)、迭代次数更多(iteractive)的算法,并尝试对被修剪的文件采用字母标准化的方式处理。< / span > < br > < span class = "line" > < / span > < br > < span class = "line" > ## 6) 模糊测试策略(Fuzzing strategies)< / span > < br > < span class = "line" > 插桩提供的反馈(feedback)使得我们更容易理解各种不同fuzzing策略的价值, 从而优化(optimize)他们的参数。使得他们对不同的文件类型都能同等地进行工作。afl-fuzz用的策略通常是与格式无关( format-agnostic) , 详细说明在下边的连接中: < / span > < br > < span class = "line" > [binary-fuzzing-strategies-what-works](http://lcamtuf.blogspot.com/2014/08/binary-fuzzing-strategies-what-works.html)< / span > < br > < span class = "line" > 值得注意的一点是, afl-fuzz大部分的(尤其是前期的)工作都是高度确定的(highly deterministic),随机性修改和测试用例拼接(random stacked modifications和test case splicing)只在后期的部分进行。 **确定性的策略** 包括:< / span > < br > < / pre > < / td > < / tr > < / table > < / figure >
< / li >
< li > < p > Sequential bit flips with varying lengths and stepovers,使用变化的长度和步距来连续进行位反转< / p >
< / li >
< li > < p > Sequential addition and subtraction of small integers,对小的整型数来连续进行加法和减法< / p >
< / li >
< li > < p > Sequential insertion of known interesting integers (0, 1, INT_MAX, etc),对已知的感兴趣的整型数连续地插入< / p >
< figure class = "highlight plain" > < table > < tr > < td class = "gutter" > < pre > < span class = "line" > 1< / span > < br > < span class = "line" > 2< / span > < br > < span class = "line" > 3< / span > < br > < span class = "line" > 4< / span > < br > < span class = "line" > 5< / span > < br > < span class = "line" > 6< / span > < br > < span class = "line" > 7< / span > < br > < span class = "line" > 8< / span > < br > < span class = "line" > 9< / span > < br > < span class = "line" > 10< / span > < br > < span class = "line" > 11< / span > < br > < span class = "line" > 12< / span > < br > < span class = "line" > 13< / span > < br > < span class = "line" > 14< / span > < br > < span class = "line" > 15< / span > < br > < span class = "line" > 16< / span > < br > < span class = "line" > 17< / span > < br > < span class = "line" > 18< / span > < br > < span class = "line" > 19< / span > < br > < span class = "line" > 20< / span > < br > < span class = "line" > 21< / span > < br > < span class = "line" > 22< / span > < br > < span class = "line" > 23< / span > < br > < / pre > < / td > < td class = "code" > < pre > < span class = "line" > 使用这些确定步骤的目的在于,生成紧凑的(compact)测试用例, 以及在产生non-crashing的输入和产生crashing的输入之间, 有很小的差异(small diffs)。< / span > < br > < span class = "line" > **非确定性(non-deterministic)策略** 的步骤包括: stacked bit flips、插入(insertions)、删除(deletions)、算数(arithmetics)和不同测试用例之间的拼接(splicing)。< / span > < br > < span class = "line" > < / span > < br > < span class = "line" > 由于在[historical_notes.txt](http://lcamtuf.coredump.cx/afl/historical_notes.txt) 中提到的原因(性能、简易性、可靠性), AFL通常不试图去推断某个特定的变异(specific mutations)和程序状态(program states)的关系。< / span > < br > < span class = "line" > < / span > < br > < span class = "line" > fuzzing的步骤名义上来说是盲目的(nominally blind),只被输入队列的进化方式的设计所影响(< strong> 见第三部分< /strong> )。< / span > < br > < span class = "line" > < / span > < br > < span class = "line" > 这意味着,这条规则有一个例外:< / span > < br > < span class = "line" > 当一个新的队列条目, 经过初始的确定性fuzzing步骤集合时, 并且文件的部分区域被观测到对执行路径的校验和没有影响, 这些队列条目在接下来的确定性fuzzing阶段可能会被排除。< / span > < br > < span class = "line" > 尤其是对那些冗长的数据格式, 这可以在保持覆盖率不变的情况下, 减少10-40%的执行次数。在一些极端情况下, 比如一些block-aligned的tar文件, 这个数字可以达到90%。< / span > < br > < span class = "line" > < / span > < br > < span class = "line" > ## 7) 字典(Dictionaries)< / span > < br > < span class = "line" > 插桩提供的反馈能够让它自动地识别出一些输入文件中的语法(syntax)符号(tokens),并且能够为测试器(tested parser)检测到一些组合,这些组合是由预定义(predefined)的或自动检测到的(auto-detected)字典项(dictionary terms)构成的合法语法(valid grammar)。< / span > < br > < span class = "line" > 关于这些特点在afl-fuzz是如何实现的, 可以看一下这个链接: < / span > < br > < span class = "line" > [afl-fuzz-making-up-grammar-with](http://lcamtuf.blogspot.com/2015/01/afl-fuzz-making-up-grammar-with.html)< / span > < br > < span class = "line" > 大体上,当基本的(basic, typically easily-obtained)句法(syntax)符号(tokens)以纯粹随机的方式组合在一起时,**插桩**和**队列进化**这两种方法共同提供了一种反馈机制,这种反馈机制能够区分无意义的变异和在插桩代码中触发新行为的变异。这样能增量地构建更复杂的句法(syntax)。< / span > < br > < span class = "line" > 这样构建的字典能够让fuzzer快速地重构非常详细(highly verbose)且复杂的(complex)语法, 比如JavaScript, SQL,XML。一些生成SQL语句的例子已经在之前提到的博客中给出了。< / span > < br > < span class = "line" > 有趣的是, AFL的插桩也允许fuzzer自动地隔离(isolate)已经在输入文件中出现过的句法(syntax)符号(tokens)。< / span > < br > < span class = "line" > < / span > < br > < span class = "line" > ## 8) 崩溃去重( De-duping crashes) < / span > < br > < span class = "line" > 崩溃去重是fuzzing工具里很重要的问题之一。很多naive的解决方式都会<EFBFBD> <EFBFBD>
< / li >
< li > < p > The crash trace includes a tuple not seen in any of the previous crashes,这个crash的路径包括一个之前crash从未见到过的tuple。< / p >
< / li >
< li > < p > The crash trace is missing a tuple that was always present in earlier faults.这个crash的路径不包含一个总在之前crash中出现的tuple。< / p >
< figure class = "highlight plain" > < table > < tr > < td class = "gutter" > < pre > < span class = "line" > 1< / span > < br > < span class = "line" > 2< / span > < br > < span class = "line" > 3< / span > < br > < span class = "line" > 4< / span > < br > < span class = "line" > 5< / span > < br > < span class = "line" > 6< / span > < br > < span class = "line" > 7< / span > < br > < span class = "line" > 8< / span > < br > < span class = "line" > 9< / span > < br > < span class = "line" > 10< / span > < br > < span class = "line" > 11< / span > < br > < span class = "line" > 12< / span > < br > < span class = "line" > 13< / span > < br > < span class = "line" > 14< / span > < br > < span class = "line" > 15< / span > < br > < span class = "line" > 16< / span > < br > < span class = "line" > 17< / span > < br > < span class = "line" > 18< / span > < br > < span class = "line" > 19< / span > < br > < span class = "line" > 20< / span > < br > < span class = "line" > 21< / span > < br > < span class = "line" > 22< / span > < br > < span class = "line" > 23< / span > < br > < span class = "line" > 24< / span > < br > < span class = "line" > 25< / span > < br > < / pre > < / td > < td class = "code" > < pre > < span class = "line" > 这种方式一开始容易受到count inflation的影响, 但实验表明其有很强的自我限制效果。和执行路径分析一样, 这种 **崩溃去重** 的方式是afl-fuzz的基石(cornerstone)。< / span > < br > < span class = "line" > < / span > < br > < span class = "line" > ## 9) 崩溃调查(Investigating crashes)< / span > < br > < span class = "line" > 不同的crash的可用性(exploitability)是不同的。afl-fuzz提供一个crash的探索模式(exploration mode)来解决这个问题。< / span > < br > < span class = "line" > 对一个已知的出错测试用例, 它被fuzz的方式和正常fuzz的操作没什么不同, 但是有一个限制能让任何non-crashing 的变异(mutations)会被丢弃(thrown away)。< / span > < br > < span class = "line" > 这种方法的意义在以下链接中会进一步讨论:< / span > < br > < span class = "line" > [afl-fuzz-crash-exploration-mode](http://lcamtuf.blogspot.com/2014/11/afl-fuzz-crash-exploration-mode.html)< / span > < br > < span class = "line" > 这种方法利用**instrumentation的反馈**, 探索crash程序的状态, 从而进一步通过歧义性的失败条件, 找到了最新发现的input。< / span > < br > < span class = "line" > 对于crashes来说, 值得注意的是和正常的队列条目对比, 导致crash的input没有被去掉, 为了和它们的父条目( 队列中没有导致crash的条目) 对比, 它们被保存下来, < / span > < br > < span class = "line" > 这就是说afl-tmin可以被用来随意缩减它们。< / span > < br > < span class = "line" > < / span > < br > < span class = "line" > ## 10) The fork server< / span > < br > < span class = "line" > 为了提升性能, afl-fuzz使用了一个" fork server" , fuzz的进程只进行一次execve(), 连接(linking), 库初始化(libc initialization)。fuzz进程通过copy-on-write(写时拷贝技术)从已停止的fuzz进程中clone下来。实现细节在以下链接中: < / span > < br > < span class = "line" > [fuzzing-binaries-without-execve](http://lcamtuf.blogspot.com/2014/10/fuzzing-binaries-without-execve.html)< / span > < br > < span class = "line" > fork server被集成在了instrumentation的程序下, 在第一个instrument函数执行时, fork server就停止并等待afl-fuzz的命令。< / span > < br > < span class = "line" > 对于需要快速发包的测试, fork server可以提升1.5到2倍的性能。< / span > < br > < span class = "line" > < / span > < br > < span class = "line" > ## 11) 并行机制< / span > < br > < span class = "line" > 实现并行的机制是, 定期检查不同cpu core或不同机器产生的队列, 然后有选择性的把队列中的条目放到test cases中。< / span > < br > < span class = "line" > 详见: parallel_fuzzing.txt.< / span > < br > < span class = "line" > < / span > < br > < span class = "line" > ## 12) 二进制instrumentation< / span > < br > < span class = "line" > AFL-Fuzz对二进制黑盒目标程序的instrumentation是通过**QEMU**的“user emulation”模式实现的。< / span > < br > < span class = "line" > 这样我们就可以允许跨架构的运行, 比如ARM binaries运行在X86的架构上。< / span > < br > < span class = "line" > QEMU使用basic blocks作为翻译单元, 利用QEMU做instrumentation, 再使用一个和编译期instrumentation类似的**guided fuzz**的模型。< / span > < br > < / pre > < / td > < / tr > < / table > < / figure >
< p > if (block_address > elf_text_start & & block_address < elf_text_end) {< / p >
< p > cur_location = (block_address > > 4) ^ (block_address < < 8);< br > shared_mem[cur_location ^ prev_location]++;< br > prev_location = cur_location > > 1;< / p >
< p > }< br > < code > `< / code > < br > 像QEMU, DynamoRIO, and PIN这样的二进制翻译器, 启动是很慢的。QEMU mode同样使用了一个fork server, 和编译期一样, 通过把一个已经初始化好的进程镜像, 直接拷贝到新的进程中。< br > 当然第一次翻译一个新的basic block还是有必要的延迟, 为了解决这个问题AFL fork server在emulator和父进程之间提供了一个频道。这个频道用来通知父进程新添加的blocks的地址, 之后吧这些blocks放到一个缓存中, 以便直接复制到将来的子进程中。这样优化之后, QEMU模式对目标程序造成2-5倍的减速, 相比之下, PIN造成100倍以上的减速。< / p >
< / li >
2019-07-01 09:28:12 +00:00
< / ul >
< h2 id = "13) afl-analyze工具" > < a href = "#13) afl-analyze工具" class = "headerlink" title = "13) afl-analyze工具" > < / a > 13) afl-analyze工具< / h2 > < p > 文件格式分析器是最小化算法的简单扩展< br > 前面讨论过; 该工具执行一系列步行字节翻转,然后在输入文件中注释字节运行,而不是尝试删除无操作块。< / p >
< / div >
< div >
< div style = "padding: 10px 0; margin: 20px auto; width: 90%; text-align: center;" >
< div > 您的支持将鼓励我继续创作!< / div >
< button id = "rewardButton" disable = "enable" onclick = "var qr = document.getElementById('QR'); if (qr.style.display === 'none') {qr.style.display='block';} else {qr.style.display='none'}" >
< span > 打赏< / span >
< / button >
< div id = "QR" style = "display: none;" >
< div id = "wechat" style = "display: inline-block" >
< img id = "wechat_qr" src = "/images/Wechatpay.png" alt = "Cool-Y 微信支付" >
< p > 微信支付< / p >
< / div >
< div id = "alipay" style = "display: inline-block" >
< img id = "alipay_qr" src = "/images/Alipay.png" alt = "Cool-Y 支付宝" >
< p > 支付宝< / p >
< / div >
< / div >
< / div >
< / div >
< footer class = "post-footer" >
2019-07-01 11:52:45 +00:00
< div class = "post-tags" >
< a href = "/tags/AFL/" rel = "tag" > # AFL< / a >
< a href = "/tags/模糊测试/" rel = "tag" > # 模糊测试< / a >
< / div >
2019-07-01 09:28:12 +00:00
< div class = "post-nav" >
< div class = "post-nav-next post-nav-item" >
2019-07-09 06:49:56 +00:00
< a href = "/2019/05/14/pack-and-unpack/" rel = "next" title = "加壳与脱壳" >
< i class = "fa fa-chevron-left" > < / i > 加壳与脱壳
2019-07-01 09:28:12 +00:00
< / a >
< / div >
< span class = "post-nav-divider" > < / span >
< div class = "post-nav-prev post-nav-item" >
2019-07-09 08:59:11 +00:00
< a href = "/2019/07/09/afl-first-try/" rel = "prev" title = "AFL-爱之初体验" >
AFL-爱之初体验 < i class = "fa fa-chevron-right" > < / i >
2019-07-09 06:49:56 +00:00
< / a >
2019-07-01 09:28:12 +00:00
< / div >
< / div >
< / footer >
< / div >
< / article >
< div class = "post-spread" >
< / div >
< / div >
< / div >
< div class = "comments" id = "comments" >
< div id = "gitment-container" > < / div >
< / div >
< / div >
< div class = "sidebar-toggle" >
< div class = "sidebar-toggle-line-wrap" >
< span class = "sidebar-toggle-line sidebar-toggle-line-first" > < / span >
< span class = "sidebar-toggle-line sidebar-toggle-line-middle" > < / span >
< span class = "sidebar-toggle-line sidebar-toggle-line-last" > < / span >
< / div >
< / div >
< aside id = "sidebar" class = "sidebar" >
< div class = "sidebar-inner" >
< ul class = "sidebar-nav motion-element" >
< li class = "sidebar-nav-toc sidebar-nav-active" data-target = "post-toc-wrap" >
文章目录
< / li >
< li class = "sidebar-nav-overview" data-target = "site-overview-wrap" >
站点概览
< / li >
< / ul >
< section class = "site-overview-wrap sidebar-panel" >
< div class = "site-overview" >
< div class = "site-author motion-element" itemprop = "author" itemscope itemtype = "http://schema.org/Person" >
< img class = "site-author-image" itemprop = "image" src = "/images/avatar.png" alt = "Cool-Y" >
< p class = "site-author-name" itemprop = "name" > Cool-Y< / p >
< p class = "site-description motion-element" itemprop = "description" > < / p >
< / div >
< nav class = "site-state motion-element" >
< div class = "site-state-item site-state-posts" >
< a href = "/archives/" >
2019-07-09 06:49:56 +00:00
< span class = "site-state-item-count" > 17< / span >
2019-07-01 09:28:12 +00:00
< span class = "site-state-item-name" > 日志< / span >
< / a >
< / div >
< div class = "site-state-item site-state-categories" >
< a href = "/categories/index.html" >
< span class = "site-state-item-count" > 6< / span >
< span class = "site-state-item-name" > 分类< / span >
< / a >
< / div >
< div class = "site-state-item site-state-tags" >
< a href = "/tags/index.html" >
2019-07-01 11:52:45 +00:00
< span class = "site-state-item-count" > 33< / span >
2019-07-01 09:28:12 +00:00
< span class = "site-state-item-name" > 标签< / span >
< / a >
< / div >
< / nav >
< div class = "links-of-author motion-element" >
< span class = "links-of-author-item" >
< a href = "https://github.com/Cool-Y" target = "_blank" title = "GitHub" >
< i class = "fa fa-fw fa-github" > < / i > GitHub< / a >
< / span >
< span class = "links-of-author-item" >
< a href = "mailto:cool.yim@whu.edu.cn" target = "_blank" title = "E-Mail" >
< i class = "fa fa-fw fa-envelope" > < / i > E-Mail< / a >
< / span >
< span class = "links-of-author-item" >
< a href = "https://www.instagram.com/yan__han/" target = "_blank" title = "Instagram" >
< i class = "fa fa-fw fa-instagram" > < / i > Instagram< / a >
< / span >
< / div >
< / div >
< / section >
<!-- noindex -->
< section class = "post-toc-wrap motion-element sidebar-panel sidebar-panel-active" >
< div class = "post-toc" >
2019-07-09 08:59:11 +00:00
< div class = "post-toc-content" > < ol class = "nav" > < li class = "nav-item nav-level-1" > < a class = "nav-link" href = "#0x01-模糊测试" > < span class = "nav-text" > 0x01 模糊测试< / span > < / a > < / li > < li class = "nav-item nav-level-1" > < a class = "nav-link" href = "#0x02-AFL快速入门" > < span class = "nav-text" > 0x02 AFL快速入门< / span > < / a > < ol class = "nav-child" > < li class = "nav-item nav-level-2" > < a class = "nav-link" href = "#13) afl-analyze工具" > < span class = "nav-text" > 13) afl-analyze工具< / span > < / a > < / li > < / ol > < / li > < / ol > < / div >
2019-07-01 09:28:12 +00:00
< / div >
< / section >
<!-- /noindex -->
< / div >
< / aside >
< / div >
< / main >
< footer id = "footer" class = "footer" >
< div class = "footer-inner" >
< div class = "copyright" > © < span itemprop = "copyrightYear" > 2019< / span >
< span class = "with-love" >
< i class = "fa fa-user" > < / i >
< / span >
< span class = "author" itemprop = "copyrightHolder" > Cool-Y< / span >
< span class = "post-meta-divider" > |< / span >
< span class = "post-meta-item-icon" >
< i class = "fa fa-area-chart" > < / i >
< / span >
2019-07-09 08:59:11 +00:00
< span title = "Site words total count" > 48.3k< / span >
2019-07-01 09:28:12 +00:00
< / div >
< div class = "powered-by" > 由 < a class = "theme-link" target = "_blank" href = "https://hexo.io" > Hexo< / a > 强力驱动< / div >
< div class = "busuanzi-count" >
< script async src = "//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js" > < / script >
< span class = "site-uv" >
< i class = "fa fa-user" > < / i >
< span class = "busuanzi-value" id = "busuanzi_value_site_uv" > < / span >
< / span >
< span class = "site-pv" >
< i class = "fa fa-eye" > < / i >
< span class = "busuanzi-value" id = "busuanzi_value_site_pv" > < / span >
< / span >
< / div >
< / div >
< / footer >
< div class = "back-to-top" >
< i class = "fa fa-arrow-up" > < / i >
< / div >
< / div >
< script type = "text/javascript" >
if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
window.Promise = null;
}
< / script >
< script type = "text/javascript" src = "/lib/jquery/index.js?v=2.1.3" > < / script >
< script type = "text/javascript" src = "/lib/fastclick/lib/fastclick.min.js?v=1.0.6" > < / script >
< script type = "text/javascript" src = "/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7" > < / script >
< script type = "text/javascript" src = "/lib/velocity/velocity.min.js?v=1.2.1" > < / script >
< script type = "text/javascript" src = "/lib/velocity/velocity.ui.min.js?v=1.2.1" > < / script >
< script type = "text/javascript" src = "/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5" > < / script >
< script type = "text/javascript" src = "/js/src/utils.js?v=5.1.4" > < / script >
< script type = "text/javascript" src = "/js/src/motion.js?v=5.1.4" > < / script >
< script type = "text/javascript" src = "/js/src/affix.js?v=5.1.4" > < / script >
< script type = "text/javascript" src = "/js/src/schemes/pisces.js?v=5.1.4" > < / script >
< script type = "text/javascript" src = "/js/src/scrollspy.js?v=5.1.4" > < / script >
< script type = "text/javascript" src = "/js/src/post-details.js?v=5.1.4" > < / script >
< script type = "text/javascript" src = "/js/src/bootstrap.js?v=5.1.4" > < / script >
<!-- LOCAL: You can save these files to your site and update links -->
< link rel = "stylesheet" href = "https://aimingoo.github.io/gitmint/style/default.css" >
< script src = "https://aimingoo.github.io/gitmint/dist/gitmint.browser.js" > < / script >
<!-- END LOCAL -->
< script type = "text/javascript" >
function renderGitment(){
var gitment = new Gitmint({
2019-07-09 06:49:56 +00:00
id: window.location.pathname,
2019-07-01 09:28:12 +00:00
owner: 'Cool-Y',
repo: 'gitment-comments',
lang: "" || navigator.language || navigator.systemLanguage || navigator.userLanguage,
oauth: {
client_secret: '1c5db4da72df5e6fc318d12afe5f4406f7c54343',
client_id: '180955a2c3ae3d966d9a'
}});
gitment.render('gitment-container');
}
renderGitment();
< / script >
< script type = "text/javascript" >
// Popup Window;
var isfetched = false;
var isXml = true;
// Search DB path;
var search_path = "search.xml";
if (search_path.length === 0) {
search_path = "search.xml";
} else if (/json$/i.test(search_path)) {
isXml = false;
}
var path = "/" + search_path;
// monitor main search box;
var onPopupClose = function (e) {
$('.popup').hide();
$('#local-search-input').val('');
$('.search-result-list').remove();
$('#no-result').remove();
$(".local-search-pop-overlay").remove();
$('body').css('overflow', '');
}
function proceedsearch() {
$("body")
.append('< div class = "search-popup-overlay local-search-pop-overlay" > < / div > ')
.css('overflow', 'hidden');
$('.search-popup-overlay').click(onPopupClose);
$('.popup').toggle();
var $localSearchInput = $('#local-search-input');
$localSearchInput.attr("autocapitalize", "none");
$localSearchInput.attr("autocorrect", "off");
$localSearchInput.focus();
}
// search function;
var searchFunc = function(path, search_id, content_id) {
'use strict';
// start loading animation
$("body")
.append('< div class = "search-popup-overlay local-search-pop-overlay" > ' +
'< div id = "search-loading-icon" > ' +
'< i class = "fa fa-spinner fa-pulse fa-5x fa-fw" > < / i > ' +
'< / div > ' +
'< / div > ')
.css('overflow', 'hidden');
$("#search-loading-icon").css('margin', '20% auto 0 auto').css('text-align', 'center');
$.ajax({
url: path,
dataType: isXml ? "xml" : "json",
async: true,
success: function(res) {
// get the contents from search data
isfetched = true;
$('.popup').detach().appendTo('.header-inner');
var datas = isXml ? $("entry", res).map(function() {
return {
title: $("title", this).text(),
content: $("content",this).text(),
url: $("url" , this).text()
};
}).get() : res;
var input = document.getElementById(search_id);
var resultContent = document.getElementById(content_id);
var inputEventFunction = function() {
var searchText = input.value.trim().toLowerCase();
var keywords = searchText.split(/[\s\-]+/);
if (keywords.length > 1) {
keywords.push(searchText);
}
var resultItems = [];
if (searchText.length > 0) {
// perform local searching
datas.forEach(function(data) {
var isMatch = false;
var hitCount = 0;
var searchTextCount = 0;
var title = data.title.trim();
var titleInLowerCase = title.toLowerCase();
var content = data.content.trim().replace(/< [^>]+>/g,"");
var contentInLowerCase = content.toLowerCase();
var articleUrl = decodeURIComponent(data.url);
var indexOfTitle = [];
var indexOfContent = [];
// only match articles with not empty titles
if(title != '') {
keywords.forEach(function(keyword) {
function getIndexByWord(word, text, caseSensitive) {
var wordLen = word.length;
if (wordLen === 0) {
return [];
}
var startPosition = 0, position = [], index = [];
if (!caseSensitive) {
text = text.toLowerCase();
word = word.toLowerCase();
}
while ((position = text.indexOf(word, startPosition)) > -1) {
index.push({position: position, word: word});
startPosition = position + wordLen;
}
return index;
}
indexOfTitle = indexOfTitle.concat(getIndexByWord(keyword, titleInLowerCase, false));
indexOfContent = indexOfContent.concat(getIndexByWord(keyword, contentInLowerCase, false));
});
if (indexOfTitle.length > 0 || indexOfContent.length > 0) {
isMatch = true;
hitCount = indexOfTitle.length + indexOfContent.length;
}
}
// show search results
if (isMatch) {
// sort index by position of keyword
[indexOfTitle, indexOfContent].forEach(function (index) {
index.sort(function (itemLeft, itemRight) {
if (itemRight.position !== itemLeft.position) {
return itemRight.position - itemLeft.position;
} else {
return itemLeft.word.length - itemRight.word.length;
}
});
});
// merge hits into slices
function mergeIntoSlice(text, start, end, index) {
var item = index[index.length - 1];
var position = item.position;
var word = item.word;
var hits = [];
var searchTextCountInSlice = 0;
while (position + word.length < = end & & index.length != 0) {
if (word === searchText) {
searchTextCountInSlice++;
}
hits.push({position: position, length: word.length});
var wordEnd = position + word.length;
// move to next position of hit
index.pop();
while (index.length != 0) {
item = index[index.length - 1];
position = item.position;
word = item.word;
if (wordEnd > position) {
index.pop();
} else {
break;
}
}
}
searchTextCount += searchTextCountInSlice;
return {
hits: hits,
start: start,
end: end,
searchTextCount: searchTextCountInSlice
};
}
var slicesOfTitle = [];
if (indexOfTitle.length != 0) {
slicesOfTitle.push(mergeIntoSlice(title, 0, title.length, indexOfTitle));
}
var slicesOfContent = [];
while (indexOfContent.length != 0) {
var item = indexOfContent[indexOfContent.length - 1];
var position = item.position;
var word = item.word;
// cut out 100 characters
var start = position - 20;
var end = position + 80;
if(start < 0 ) {
start = 0;
}
if (end < position + word . length ) {
end = position + word.length;
}
if(end > content.length){
end = content.length;
}
slicesOfContent.push(mergeIntoSlice(content, start, end, indexOfContent));
}
// sort slices in content by search text's count and hits' count
slicesOfContent.sort(function (sliceLeft, sliceRight) {
if (sliceLeft.searchTextCount !== sliceRight.searchTextCount) {
return sliceRight.searchTextCount - sliceLeft.searchTextCount;
} else if (sliceLeft.hits.length !== sliceRight.hits.length) {
return sliceRight.hits.length - sliceLeft.hits.length;
} else {
return sliceLeft.start - sliceRight.start;
}
});
// select top N slices in content
var upperBound = parseInt('1');
if (upperBound >= 0) {
slicesOfContent = slicesOfContent.slice(0, upperBound);
}
// highlight title and content
function highlightKeyword(text, slice) {
var result = '';
var prevEnd = slice.start;
slice.hits.forEach(function (hit) {
result += text.substring(prevEnd, hit.position);
var end = hit.position + hit.length;
result += '< b class = "search-keyword" > ' + text.substring(hit.position, end) + '< / b > ';
prevEnd = end;
});
result += text.substring(prevEnd, slice.end);
return result;
}
var resultItem = '';
if (slicesOfTitle.length != 0) {
resultItem += "< li > < a href = '" + articleUrl + "' class = 'search-result-title' > " + highlightKeyword(title, slicesOfTitle[0]) + "< / a > ";
} else {
resultItem += "< li > < a href = '" + articleUrl + "' class = 'search-result-title' > " + title + "< / a > ";
}
slicesOfContent.forEach(function (slice) {
resultItem += "< a href = '" + articleUrl + "' > " +
"< p class = \"search-result\" > " + highlightKeyword(content, slice) +
"...< / p > " + "< / a > ";
});
resultItem += "< / li > ";
resultItems.push({
item: resultItem,
searchTextCount: searchTextCount,
hitCount: hitCount,
id: resultItems.length
});
}
})
};
if (keywords.length === 1 & & keywords[0] === "") {
resultContent.innerHTML = '< div id = "no-result" > < i class = "fa fa-search fa-5x" / > < / div > '
} else if (resultItems.length === 0) {
resultContent.innerHTML = '< div id = "no-result" > < i class = "fa fa-frown-o fa-5x" / > < / div > '
} else {
resultItems.sort(function (resultLeft, resultRight) {
if (resultLeft.searchTextCount !== resultRight.searchTextCount) {
return resultRight.searchTextCount - resultLeft.searchTextCount;
} else if (resultLeft.hitCount !== resultRight.hitCount) {
return resultRight.hitCount - resultLeft.hitCount;
} else {
return resultRight.id - resultLeft.id;
}
});
var searchResultList = '< ul class = \"search-result-list\" > ';
resultItems.forEach(function (result) {
searchResultList += result.item;
})
searchResultList += "< / ul > ";
resultContent.innerHTML = searchResultList;
}
}
if ('auto' === 'auto') {
input.addEventListener('input', inputEventFunction);
} else {
$('.search-icon').click(inputEventFunction);
input.addEventListener('keypress', function (event) {
if (event.keyCode === 13) {
inputEventFunction();
}
});
}
// remove loading animation
$(".local-search-pop-overlay").remove();
$('body').css('overflow', '');
proceedsearch();
}
});
}
// handle and trigger popup window;
$('.popup-trigger').click(function(e) {
e.stopPropagation();
if (isfetched === false) {
searchFunc(path, 'local-search-input', 'local-search-result');
} else {
proceedsearch();
};
});
$('.popup-btn-close').click(onPopupClose);
$('.popup').click(function(e){
e.stopPropagation();
});
$(document).on('keyup', function (event) {
var shouldDismissSearchPopup = event.which === 27 & &
$('.search-popup').is(':visible');
if (shouldDismissSearchPopup) {
onPopupClose();
}
});
< / script >
< script src = "https://cdn1.lncld.net/static/js/av-core-mini-0.6.4.js" > < / script >
< script > AV . initialize ( "EWwoJgHNdlj6iBjiFlMcabUO-gzGzoHsz" , "x8FxDrYG79C8YFrTww9ljo8K" ) ; < / script >
< script >
function showTime(Counter) {
var query = new AV.Query(Counter);
var entries = [];
var $visitors = $(".leancloud_visitors");
$visitors.each(function () {
entries.push( $(this).attr("id").trim() );
});
query.containedIn('url', entries);
query.find()
.done(function (results) {
var COUNT_CONTAINER_REF = '.leancloud-visitors-count';
if (results.length === 0) {
$visitors.find(COUNT_CONTAINER_REF).text(0);
return;
}
for (var i = 0; i < results.length ; i + + ) {
var item = results[i];
var url = item.get('url');
var time = item.get('time');
var element = document.getElementById(url);
$(element).find(COUNT_CONTAINER_REF).text(time);
}
for(var i = 0; i < entries.length ; i + + ) {
var url = entries[i];
var element = document.getElementById(url);
var countSpan = $(element).find(COUNT_CONTAINER_REF);
if( countSpan.text() == '') {
countSpan.text(0);
}
}
})
.fail(function (object, error) {
console.log("Error: " + error.code + " " + error.message);
});
}
function addCount(Counter) {
var $visitors = $(".leancloud_visitors");
var url = $visitors.attr('id').trim();
var title = $visitors.attr('data-flag-title').trim();
var query = new AV.Query(Counter);
query.equalTo("url", url);
query.find({
success: function(results) {
if (results.length > 0) {
var counter = results[0];
counter.fetchWhenSave(true);
counter.increment("time");
counter.save(null, {
success: function(counter) {
var $element = $(document.getElementById(url));
$element.find('.leancloud-visitors-count').text(counter.get('time'));
},
error: function(counter, error) {
console.log('Failed to save Visitor num, with error message: ' + error.message);
}
});
} else {
var newcounter = new Counter();
/* Set ACL */
var acl = new AV.ACL();
acl.setPublicReadAccess(true);
acl.setPublicWriteAccess(true);
newcounter.setACL(acl);
/* End Set ACL */
newcounter.set("title", title);
newcounter.set("url", url);
newcounter.set("time", 1);
newcounter.save(null, {
success: function(newcounter) {
var $element = $(document.getElementById(url));
$element.find('.leancloud-visitors-count').text(newcounter.get('time'));
},
error: function(newcounter, error) {
console.log('Failed to create');
}
});
}
},
error: function(error) {
console.log('Error:' + error.code + " " + error.message);
}
});
}
$(function() {
var Counter = AV.Object.extend("Counter");
if ($('.leancloud_visitors').length == 1) {
addCount(Counter);
} else if ($('.post-title-link').length > 1) {
showTime(Counter);
}
});
< / script >
< script >
(function(){
var bp = document.createElement('script');
var curProtocol = window.location.protocol.split(':')[0];
if (curProtocol === 'https') {
bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
}
else {
bp.src = 'http://push.zhanzhang.baidu.com/push.js';
}
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(bp, s);
})();
< / script >
< / body >
< / html >