飞道的博客

[漏洞挖掘]peach中配置pit文件—从入门到入狱系列

870人阅读  评论(0)

0.目录

1.pit简介

pit文件的全称为Peach pit file,用于数据定义。使用Peach时,实际上主要工作就是定义pit这样一个xml文件指示Peach测试平台去做测试。

2.peach解析pit文件的过程

我使用的代码commitid为:

commit 8fd880eed5c527e950bdd4bd6d88af88e8e7e6ce (HEAD -> master, tag: v3.1.124, origin/master, origin/HEAD)

下载代码使用命令:

git clone https://git.code.sf.net/p/peachfuzz/code

peach解析pit文件是在Peach.Core.Analyzers.PitParser.asParser函数中

调用堆栈如下:

Peach.Core.Analyzers.PitParser.asParser(System.Collections.Generic.Dictionary<string,object> args, System.IO.FileStream data, bool doValidatePit) in /home/cmp/work_dir/peachfuzz-code/Peach.Core/Analyzers/PitParser.cs:209

Peach.Core.Analyzers.PitParser.asParser(System.Collections.Generic.Dictionary<string,object> args, System.IO.FileStream data) in /home/cmp/work_dir/peachfuzz-code/Peach.Core/Analyzers/PitParser.cs:190

Peach.Core.Analyzer.asParser(System.Collections.Generic.Dictionary<string,object> args, string fileName) in /home/cmp/work_dir/peachfuzz-code/Peach.Core/Analyzer.cs:74

Peach.Core.Runtime.Program..ctor(string[] args) in /home/cmp/work_dir/peachfuzz-code/Peach.Core/Runtime/Program.cs:285

Peach.Program.Main(string[] args) in /home/cmp/work_dir/peachfuzz-code/Peach/Program.cs:45

分析peach解析pit文件的过程

首先看/home/cmp/work_dir/peachfuzz-code/Peach.Core/Runtime/Program.cs:285处,Peach.Core.Runtime.Program…ctor函数,Peach.Core.Runtime.Program…ctor函数将调用Dom.Dom asParser函数解析pit,并返回解析结果(Dom对象)。

133         public Program(string[] args)
134         {
...
137             config = new RunConfiguration(); // 初始化config对象
138             config.debug = 0;
139 
140             try
141             {
...
160                 if (args.Length == 0)
161                     Syntax();
162 
163                 var p = new OptionSet() // 设置并初始化命令行以及运行环境等参数的存储对象p
164                 {
165                     { "h|?|help", v => Syntax() },
166                     { "analyzer=", v => analyzer = v },
167                     { "debug", v => config.debug = 1 },
168                     { "trace", v => config.debug = 2 },
169                     { "1", v => config.singleIteration = true},
170                     { "range=", v => ParseRange(config, v)},
171                     { "t|test", v => test = true},
172                     { "c|count", v => config.countOnly = true},
173                     { "skipto=", v => config.skipToIteration = Convert.ToUInt32(v)},
174                     { "seed=", v => config.randomSeed = Convert.ToUInt32(v)},
175                     { "p|parallel=", v => ParseParallel(config, v)},
176                     { "a|agent=", v => agent = v},
177                     { "D|define=", v => AddNewDefine(v) },
178                     { "definedvalues=", v => definedValues.Add(v) },
179                     { "config=", v => definedValues.Add(v) },
180                     { "parseonly", v => parseOnly = true },
181                     { "bob", var => bob() },
182                     { "charlie", var => Charlie() },
183                     { "showdevices", var => ShowDevices() },
184                     { "showenv", var => ShowEnvironment() },
185                     { "makexsd", var => MakeSchema() },
186                 };
187 
188                 List<string> extra = p.Parse(args);// 开始解析命令行相关的一些参数
...
218 
219                 // Enable debugging if asked for
220                 // If configuration was already done by a .config file, nothing will be changed
221                 ConfigureLogging(config.debug); // 根据是否开启debug,配置logging
...
284                 Engine e = new Engine(GetUIWatcher()); // 创建并初始化模糊测试引擎
285                 dom = GetParser(e).asParser(parserArgs, extra[0]); // 解析pit文件
286                 config.pitFile = extra[0]; // 设置pit文件的路径
287 
288                 // Used for unittests
289                 if (parseOnly)
290                     return;
291 
292                 foreach (string arg in args)
293                     config.commandLine += arg + " ";
294 
295                 if (extra.Count > 1)
296                     config.runName = extra[1];
297 
298                 e.startFuzzing(dom, config); // 开始进行模糊测试
...
325         }

接着看一下Dom.Dom asParser函数,在函数里面主要做了几件事:

1.读取pit文件内容

2.校验pit文件语法格式

3.进入handlePeach函数,解析…中的内容

4.在data model上执行所有的analyzers

 206         public virtual Dom.Dom asParser(Dictionary<string, object> args, Stream data, bool doValidatePit)
 207         {
 208             // Reset the data element auto-name suffix back to zero
 209             Resetter.Reset();
 210 
 211             string xml = readWithDefines(args, data); // 读取pit的文件中的xml的内容
 212 
 213             if (doValidatePit) // 校验pit文件语法格式
 214                 validatePit(xml, getName(data));
 215 
 216             XmlDocument xmldoc = new XmlDocument();
 217             xmldoc.LoadXml(xml);
 218 
 219             _dom = getNewDom();
 220 
 221             foreach (XmlNode child in xmldoc.ChildNodes)
 222             {
 223                 if (child.Name == "Peach")
 224                 {
 225                     handlePeach(_dom, child, args); //解析<Peach>...</Peach>中的内容
 226                     break;
 227                 }
 228             }
 229 
 230             _dom.evaulateDataModelAnalyzers(); // 在data model上执行所有的analyzers
 231 
 232             return _dom;
 233         }

handlePeach函数是解析pit文件的核心,在这个函数中将逐个解析pit文件中的一般配置、DataModel、StateModel、Agent、Test等。

             /// 处理Peach节点的解析。handlePeach是一个虚方法,可以通过继承很好的进行扩展和重定义。
             /// <param name="dom">Dom object</param>
             /// <param name="node">XmlNode to parse</param>
             /// <param name="args">Parser arguments</param>
             /// <returns>Returns the parsed Dom object.</returns>
 349         protected virtual void handlePeach(Dom.Dom dom, XmlNode node, Dictionary<string, object> args)
 350         {
 351             // Pass 0 - Basic check if Peach 2.3 ns  
 352             if (node.NamespaceURI.Contains("2008"))
 353                 throw new PeachException("Error, Peach 2.3 namespace detected please upgrade the pit");
 354 
 355             // Pass 1 - Handle imports, includes, python path
 356             foreach (XmlNode child in node)
 357             {
 358                 switch (child.Name) // 这个switch语句里面将处理pit文件的一般配置
 359                 {
 360                     case "Include":
...
 386                     case "Require":
...
 390                     case "Import":
...
 397                     case "PythonPath":
...
 408                     case "RubyPath":
...
 419                     case "Python":
...
 430                     case "Ruby":
...
 441                     case "Defaults":
 442                         handleDefaults(child);
 443                         break;
 444                 }
 445             }
 446 
 447             // Pass 3 - Handle data model
 448 
 449             foreach (XmlNode child in node)
 450             {
 451                 var dm = handleDataModel(child, null); // 处理DataModel
...
 468             }
 469 
 470             // Pass 4 - Handle Data
 471 
 472             foreach (XmlNode child in node)
 473             {
 474                 if (child.Name == "Data")
 475                 {
 476                     var data = handleData(child, dom.datas.UniqueName()); // 处理DataModel
...
 486                 }
 487             }
 488 
 489             // Pass 5 - Handle State model
 490 
 491             foreach (XmlNode child in node)
 492             {
 493                 if (child.Name == "StateModel")
 494                 {
 495                     StateModel sm = handleStateModel(child, dom); // 处理StateModel
...
 505                 }
 506 
 507                 if (child.Name == "Agent")
 508                 {
 509                     Dom.Agent agent = handleAgent(child); // 处理Agent
...
 519                 }
 520             }
 521 
 522             // Pass 6 - Handle Test
 523 
 524             foreach (XmlNode child in node)
 525             {
 526                 if (child.Name == "Test")
 527                 {
 528                     Test test = handleTest(child, dom); // 处理Test
...
 538                 }
 539             }
 540         }

handlePeach函数执行完将返回Dom对象,其中包含了pit配置文件的所有内容。至此pit文件解析完成。

3.运行test的过程

运行Test是在解析完pit文件之后。运行过程中最主要的还是状态机的运作。(关于状态机的原理,在此不在赘述。不懂的小朋友请**点击查看**)。

运行Test时,启动状态机的调用堆栈:

Peach.Core.Dom.StateModel.Run(Peach.Core.RunContext context) in /home/cmp/work_dir/peachfuzz-code/Peach.Core/Dom/StateModel.cs:184

Peach.Core.Engine.runTest(Peach.Core.Dom.Dom dom, Peach.Core.Dom.Test test, Peach.Core.RunContext context) in /home/cmp/work_dir/peachfuzz-code/Peach.Core/Engine.cs:413

Peach.Core.Engine.startFuzzing(Peach.Core.Dom.Dom dom, Peach.Core.Dom.Test test, Peach.Core.RunConfiguration config) in /home/cmp/work_dir/peachfuzz-code/Peach.Core/Engine.cs:240

Peach.Core.Engine.startFuzzing(Peach.Core.Dom.Dom dom, Peach.Core.RunConfiguration config) in /home/cmp/work_dir/peachfuzz-code/Peach.Core/Engine.cs:213

Peach.Core.Runtime.Program..ctor(string[] args) in /home/cmp/work_dir/peachfuzz-code/Peach.Core/Runtime/Program.cs:298

Peach.Program.Main(string[] args) in /home/cmp/work_dir/peachfuzz-code/Peach/Program.cs:45

Peach.Core.Dom.StateModel.Run函数是启动状态机运行Test的核心。接下来分析一下代码:

150         /// <summary>
151         /// Start running the State Machine
152         /// </summary>
153         /// <remarks>
154         /// This will start the initial State.
155         /// </remarks>
156         /// <param name="context"></param>
157         public void Run(RunContext context)
158         {
159             try
160             {
161                 foreach (Publisher publisher in context.test.publishers.Values)
162                 {
163                     publisher.Iteration = context.test.strategy.Iteration;
164                     publisher.IsControlIteration = context.controlIteration;
165                 }
166 
167                 _dataActions.Clear();
168 
169                 // Update all data model to clones of origionalDataModel
170                 // before we start down the state path.
171                 foreach (State state in states) // 在开始状态路径之前,将所有数据模型更新为origionalDataModel的克隆。
172                 {
173                     state.UpdateToOriginalDataModel();
174                 }
175 
176                 State currentState = initialState; // 从initialState开始
177 
178                 OnStarting(); // 状态机开始时的函数回调
179 
180                 while (true)
181                 {
182                     try
183                     {
184                         currentState.Run(context); // 进入某个状态
185                         break;
186                     }
187                     catch (ActionChangeStateException ase)
188                     {
189                         var newState = context.test.strategy.MutateChangingState(ase.changeToState);
190 
191                         if(newState == ase.changeToState)
192                             logger.Debug("Run(): Changing to state \"" + newState.name + "\".");
193                         else
194                             logger.Debug("Run(): Changing state mutated.  Switching to \"" + newState.name +
195                                 "\" instead of \""+ase.changeToState+"\".");
196 
197                         currentState.OnChanging(newState);// 切换到下个状态之前执行的函数回调
198                         currentState = newState;
199                     }
200                 }
201             }
202             catch (ActionException)
203             {
204                 // Exit state model!
205             }
206             finally
207             {
208                 foreach (Publisher publisher in context.test.publishers.Values)
209                     publisher.close();
210 
211                 OnFinished(); // 状态机结束时的函数回调
212             }
213         }
214     }
215 }

在/home/cmp/work_dir/peachfuzz-code/Peach.Core/Dom/StateModel.cs:184,Peach.Core.Dom.StateModel.Run函数将调动Peach.Core.Dom.State.Run函数,进入状态并执行相关的Action。

170         public void Run(RunContext context)
171         {
172             try
173             {
...
194                 if (++runCount > 1) // 如果不是第一次运行,则将DataModel还原成original
195                     UpdateToOriginalDataModel(runCount);
196 
197                 OnStarting(); // 进入状态之前执行回调
198 
199                 RunScript(onStart);
200 
201                 foreach (Action action in actions) // 执行状态的Action
202                     action.Run(context);
203 
204                 // onComplete script run from finally.
205             }
...
216             finally
217             {
218                 finished = true;
219 
220                 RunScript(onComplete);
221                 OnFinished(); // 结束状态之后执行回调
222             }
223         }

接着在以上代码的202行,将调用Peach.Core.Dom.Action.Run函数,在文件/home/cmp/work_dir/peachfuzz-code/Peach.Core/Dom/Action.cs:355。Peach.Core.Dom.Action.Run函数将执行Action,比如:输出数据、调用一个方法等。接下来进入该函数一探究竟:

279         public void Run(RunContext context)
280         {
281             logger.Trace("Run({0}): {1}", name, GetType().Name);
282 
283             // Setup scope for any scripting expressions
284             scope["context"] = context;
285             scope["Context"] = context;
...
294             scope["self"] = this;
295 
296             if (when != null) // 如果when不为空,将计算Scripting.EvalExpression(when, scope)。这里可以看作定义的执行Action的条件。
297             {
298                 object value = Scripting.EvalExpression(when, scope);
299                 if (!(value is bool))
300                 {
301                     logger.Debug("Run: action '{0}' when return is not boolean, returned: {1}", name, value);
302                     return;
303                 }
304 
305                 if (!(bool)value)
306                 {
307                     logger.Debug("Run: action '{0}' when returned false", name);
308                     return;
309                 }
310             }
311 
312             try
313             {
...
345                 OnStarting(); // 执行Action之前的回调
346 
347                 logger.Debug("ActionType.{0}", GetType().Name.ToString()); // GetType().Name.ToString()返回的type,就是<Action type="output">标签中的type属性的值。
348 
349                 RunScript(onStart);
350 
351                 // Save output data
352                 foreach (var item in outputData)
353                     parent.parent.SaveData(item.outputName, item.dataModel.Value);
354 
355                 OnRun(publisher, context); // 进入Publisher,根据定义的Publisher类型,执行输出数据、调用一个方法等。
356 
357                 // Save input data
358                 foreach (var item in inputData)
359                     parent.parent.SaveData(item.inputName, item.dataModel.Value);
360 
361                 RunScript(onComplete);
362 
363                 finished = true;
364             }
...
375             finally
376             {
377                 finished = true;
378                 OnFinished(); // 执行Action之后的回调
379             }
380         }
381     }

当执行完了一个State的Actions,将切换到下一个State,直到状态机的状态切换到"结束状态",然后退出状态机。此时表明Test已经执行完成。

4.pit文件的基本格式

<!-- 版本,编码之类 -->
<?xml version="1.0" encoding="utf-8"?>

<!-- 版本,编码之类 -->
<Peach ...版本,作者介绍之类...>

<!-- 通用配置:包含的外部文件 -->
<Include ns="foo" src="file:foo.xml" />
    
<!-- 通用配置:用于设置默认属性, 例如:为数据元素设置字节序大小端 -->
<Default ... />
    
<!-- 通用配置:python模块搜索路径 -->
<PythonPath path="c:/peach/mycode">
    
<!-- 通用配置:导入python自定义模块 -->
<Import import="MyCode" />

<!-- 数据模型定义 -->
<DataModel  name="NewDataModel" ref="foo:TheDataModel" > 
    
<!-- 状态机模型定义 -->
<StateModel name="TheStateModel" initialState="InitialState">
	<State name="InitialState">
		<!-- Peach将自动连接远程主机 -->
		<!-- 发送数据 -->
		<Action type="output">
			<DataModel ref="PacketModel1" />
		</Action>
		<!-- 接收响应数据 -->
        <Action type="input">
            <DataModel ref="PacketModel2" />
        </Action>
        </State>
</StateModel>

<!-- 定义代理用于托管Monitor进程 -->
<Agent name="RemoteAgent" location="tcp://192.168.1.1:9001">
	<!-- Monitors -->
	<!-- Monitors可以收集信息,并根据信息执行命令 -->
</Agent>
    
<!-- 用于配置特定的模糊测试,指定将要使用的StateModel,Agent,Publisher,Strategy,Logger等 -->
<Test name="Default">
    <!-- 可选,用于排除一些不发生突变的元素 -->
	<Exclude xpath="//Reserved" />
    
	<!-- 可选,指定Agent -->
	<Agent ref="LocalLinuxAgent" platform="linux" />
	<Agent ref="RemoteAgent" />
    
	<!-- 必选,指定StateModel -->
	<StateModel ref="TheState" />
    
	<!-- 必选,指定Publisher -->
	<Publisher class="Tcp">
		<Param name="Host" value="127.0.0.1" />
		<Param name="Port" value="9001" />
	</Publisher>
    
	<!-- 指定模糊测试策略 -->
	<Strategy class="Random" />
    
	<!-- 指定日志的Logger -->
	<Logger class="File">
        <Param name="Path" value="logs" />
	</Logger>
</Test>  
</Peach>

5.Include

Include元素允许将其他pit文件包含到名称空间中,以便在当前Pit文件中使用。

<!-- ns,必选,用于指定命名空间 -->
<!-- src,必选,用于指定文件名,使用"file:"作为文件名的前缀 -->
<Include ns="foo" src="file:foo.xml" />

<!-- 使用被包含文件中的DataModel需要加上命令空间作为前缀 -->
<DataModel name="NewDataModel" ref="foo:TheDataModel">
    <!-- ... -->
</DataModel>

6.Default

用于设置默认属性, 例如:为数据元素设置字节序大小端。

7.PythonPath

PythonPath元素是一个Toplevel元素,它将路径添加到Python为模块搜索的路径中。它的主要用途是扩展Peach并包括自定义代码的位置。

<!-- path,必选,用于指定自定义Python模块的搜索路径 -->
<PythonPath path="c:/peach/mycode">

8.Import

此元素允许导入自定义Python模块以在Pit文件中使用。它的工作方式与Python import关键字一样。注意:Peach 3当前不支持from属性。

<!-- import,必选,用于指定导入模块的名字,语义和python中一致 -->
<Import import="MyCode" />

9.DataModel

Peach Pit文件包含至少一个DataModel,并且可能包含更多。DataModels描述的数据包括类型信息,关系(大小,计数,偏移量)以及其他允许模糊器执行智能突变的信息。

DataModel可以被其他DataModel重用和引用,从而可以将复杂的数据定义分解为可读的部分。

<!-- name,必选,当引用模型或者调试时,友好的DataModel名字是非常有用的 -->
<!-- Peach Pit文件中的几乎每个元素都支持name属性。
	 名称用于提高可读性并引用Pit文件中的其他元素。
     名称中不应包含标点符号,尤其是句点(.),斜杠(\\)或冒号(:),因为它们对于引用具有特殊含义。
     名称区分大小写,并且在当前作用域级别必须唯一。
-->
<Block name="Header">
    <Number name="Value1" size="8"/>
    <Number name="Value2" size="8"/>
    <Number name="Value3" size="8"/>
    <Number name="Value4" size="8"/>
</Block>

<!-- ref,可选,引用一个DataModel模板 -->
<!-- ref属性通过名称或类结构指定对另一个元素的引用。参考可以是相对的或完全限定的。 -->
<DataModel name="ParentModel">
    <String value="Hello " />
</DataModel>
<DataModel name="HelloWorldModel" ref="ParentModel" >
    <String value=" world!" />
</DataModel>

<!-- mutable,可选,默认为真。该元素是否可变异 -->
<!-- mutable用于定义是否应该模糊此元素,块或数据类型。默认值是true。 -->
<!-- 注意:即使当一个元素被标记为不可更改时,当模糊器模糊其他元素时,它仍然会被修改。将元素标记为不可更改只会禁用将对该元素进行操作的更改器。 -->
<!-- 根据我们的经验,将元素标记为不可更改会导致错误缺失。我们建议不要使用此属性,除非您真的知道自己在做什么并且知道标记的元素已经被模糊了。 -->
<DataModel name="Header">
    <Number name="ReservedForFutureuse" size="8" mutable="false" />
    <Number size="8" />
    <Number size="8" />
    <Number size="8" />
    <DataModel>

<!-- constraint,可选,确定使用一个python约束表达式,它帮助Peach确定数据元素否已正确使用了数据。
	 约束表达式必须计算为true或false。True表示传入的数据已正确解析为数据元素。False表示已发生错误。
	 约束属性不影响值的突变方式。
-->
<!-- 
	 注意:
		1.约束仅在将数据解析为数据元素时执行。约束通常比使用Choice或token属性要慢。
		2.constraint属性,一般针对的是DataModel的子元素中的数据进行约束。
-->
<!-- 表达式中的特殊变量:element,数据元素实例 -->
<!-- 表达式中的特殊变量:value,元素中的数据的值。该值将转换为字符串或字节数组。 -->
<!-- 操作数字并检查运算的结果 -->
<Number name="constrainedNum" size="32" constraint="int(value) & 0xfefe == 5" />
<!-- 忽略大小写判断字符串相等性 -->
<String constraint="value.lower() == 'peach'" />
<!-- 判断字符串中是否包含peach -->
<String constraint="value.find('peach') != -1" />
<!-- 判断value字符串变量的长度<100 -->
<Blob constraint="len(value) < 100" />

子元素:Blob

Blob元素是DataModel或Block的子元素。

Blob通常用于表示缺乏类型定义或格式的数据。

<!-- Blob常用属性如下: -->
<!-- name,必选,名称。 -->
<!-- value,指定默认值。 -->
<!-- length,Blob的大小(字节)。 -->
<!-- ref,为Blob指定一个data model作为模板。 -->
<!-- valueType,指定默认的格式:hex, string, or literal, 默认为string。 -->
<!-- minOccurs,指定最小occur的次数,默认为1。 -->
<!-- maxOccurs,指定最大occur的次数,默认为1。 -->
<!-- token,解析时是否将此元素视为token,默认为false。 -->
<!-- lengthType,Type of length, how length is specified. -->
<!-- constraint,定义在解析数据元素时的python约束表达式。 -->
<!-- mutable,是否Blob是可变的(该选项应该置为true),默认是true。 -->
<DataModel name="BlobExample1">
    <Blob name="Unknown1" />
    <Blob name="Unknown1" valueType="hex" value="AA BB CC DD" />
</DataModel>

子元素:Block

Block元素是DataModel或Block 的子元素。

Block用于将一个或多个数据元素(例如Number或String)组合在一起形成逻辑Structure。

Block和DataModel非常相似,唯一的区别是它们的位置。DataModels是顶层元素,Block是DataModel的子元素。Block和DataModel元素都可用作其他Block或DataModels的“模板” 。

<!-- Block常用属性如下: -->
<!-- name,名称。 -->
<!-- ref,为Blob指定一个data model作为模板。 -->
<!-- minOccurs,指定最小occur的次数,默认为1。 -->
<!-- maxOccurs,指定最大occur的次数,默认为1。 -->
<!-- mutable,是否Blob是可变的(该选项应该置为true),默认是true。 -->
<DataModel name="BlockExample1">
    <Block name="HelloWorld">
        <String value="Hello world!" />
    </Block>
</DataModel>
<!-- Block可以根据需要,多层嵌套 -->
<DataModel name="BlockExample2">
    <Block>
        <Block>
            <Block>
                <String value="1" />
            </Block>

            <Block>
                <String value="2" />
            </Block>

            <String value="3" />
        </Block>
        <String value="4" />
    </Block>
</DataModel>


<!-- Block引用 --start-- -->
<DataModel name="OtherDataModel">
    <String value="Hello World"/>
</DataModel>

<DataModel name="ThisDataModel">
    <Block name="MyName" ref="OtherDataModel"/>
</DataModel>
<!-- 上面的Block引用案例,名为"ThisDataModel"的DataModel的效果将与下面代码等价。 -->
<DataModel name="ThisDataModel">
    <Block name="MyName">
        <String value="Hello World"/>
    </Block>
</DataModel>
<!-- Block引用 --end-- -->

<!-- Block键值对template --start-- -->
<DataModel name="Template">
    <String name="Key" />
    <String value=": " token="true" />
    <String name="Value" />
    <String value="\r\n" token="true" />
</DataModel>
<DataModel name="OtherModel">
    <String value="Before Block\r\n" />
    <Block name="Customized" ref="Template">
        <String name="Key" value="Content-Length" />
        <String name="Value" value="55"/>
    </Block>
</DataModel>
<!-- 以上案例中"OtherModel"中的"Customized"引用了"Template"
	 并对从"Template"中"继承"(不标准的说法)过来的"Key"和"Value"进行设置默认值。
-->
<!-- 最终"OtherModel"产生的结果如下:
	 Before Block\r\n
     Content-Length: 55\r\n
-->
<!-- 最终"OtherModel"将等价于下面的代码 -->
<DataModel name="OtherModel">
    <String value="BeforeBlock" />
    <Block name="Customized" ref="Template">
        <String name="Key" value="Content-Length" />
        <String value=": " token="true" />
        <String name="Value" value="55" />
        <String value="\r\n" token="true" />
    </Block>
</DataModel>
<!-- Block键值对template --end-- -->

Block的子元素包括: Blob, Block, Choice, Custom, Fixup, Flag, Flags, Number, Padding, Placement, Relation, Seek, String, Transformer, XmlAttribute, XmlElement

子元素:Choice

Choice元素是DataModelBlock的子元素。

Choice元素用于指示任何子元素有效,但仅应选择一个。就像编程语言中的switch语句一样。

<!-- Choice常用属性如下: -->
<!-- name,名称。 -->
<!-- minOccurs,指定最小occur的次数,默认为1。 -->
<!-- maxOccurs,指定最大occur的次数,默认为1。 -->
<!-- occurs,指定occur的次数。 -->
<Choice name="ChoiceBlock">
    <Block name="Type1">
        <!-- ... -->
    </Block>
    <Block name="Type2">
        <!-- ... -->
    </Block>
    <Block name="Type3">
        <!-- ... -->
    </Block>
</Choice>

<!-- Choice案例1 --start-- -->
<DataModel name="ChoiceExample1">
    <Choice name="Choice1">
		<!-- 在解析数据中,如果数据的前8个字节都是1,剩余数据将被是作为32-byte的数值。 -->
        <Block name="Type1">
            <Number name="Str1" size="8" value="1" token="true" />
            <Number size="32"/>
        </Block>
		<!-- 在解析数据中,如果数据的前8个字节都是2,剩余数据将被是作为255-byte的二进制数据。 -->
        <Block name="Type2">
            <Number name="Str2" size="8" value="2" token="true" />
            <Blob length="255" />
        </Block>
		<!-- 在解析数据中,如果数据的前8个字节都是3,剩余数据将被是作为8-byte的字符串。 -->
        <Block name="Type3">
            <Number name="Str3" size="8" value="3" token="true" />
            <String length="8" />
        </Block>
    </Choice>
</DataModel>
<!-- Choice案例1 --end-- -->

<!-- Choice案例2,重复Choice --start-- -->
<!-- 上面的示例可以很好地做出单个Choice,但是如果有很多Type1,Type2和Type3块紧随其后,该怎么办呢?
设置minOccurs,maxOccurs或occurs以指定应该重复的Choice。-->
<DataModel name="ChoiceExample1">
    <!-- 尝试解析的选择,至少3个不且最多不超过6个。-->
    <Choice name="Choice1" minOccurs="3" maxOccurs="6">
        <Block name="Type1">
            <Number name="Str1" size="8" value="1" token="true" />
            <Number size="32"/>
        </Block>

        <Block name="Type2">
            <Number name="Str2" size="8" value="2" token="true" />
            <Blob length="255" />
        </Block>

        <Block name="Type3">
            <Number name="Str3" size="8" value="3" token="true" />
            <String length="8" />
        </Block>
    </Choice>
</DataModel>
<!-- Choice案例2,重复Choice --end-- -->

Choice的子元素包括:Block, Choice, String, Number, Blob, Flags, Fixup, Transformer, XmlAttribute, XmlElement

子元素:Custom

暂未找到相关的资料,有意补充此内容的读者,请在评论区留言。谢谢。

子元素:Flag

Flag元素在Flags容器中定义特定的位字段。

<!-- Flag常用属性如下: -->
<!-- name,可选,名称。 -->
<!-- size,必选,大小(bit)。 -->
<!-- postion,必选,从Flag开始位置(0)。 -->
<!-- value,Blob中的默认值。 -->
<!-- valueType,默认格式(hex, string, or literal)。 -->
<!-- mutable,(Peach 2.3)是否是可变的(该选项应该置为true),默认是true。 -->
<Flags name="options" size="16">
    <Flag name="compression" position="0" size="1" />
    <Flag name="compressionType" position="1" size="3" />
    <Flag name="opcode" position="10" size="2" value="5" />
</Flags>

Flag的子元素包括:Relation

子元素:Flags

Flags元素定义一组bit大小的标志。

<!-- Flags常用属性如下: -->
<!-- name,可选,名称。 -->
<!-- size,必选,大小(bit)。 -->
<!-- mutable,是否是可变的(该选项应该置为true),默认是true。 -->
<Flags name="options" size="16">
    <Flag name="compression" position="0" size="1" />
    <Flag name="compressionType" position="1" size="3" />
    <Flag name="opcode" position="10" size="2" value="5" />
</Flags>

Flags的子元素包括:Fixup, Flag, Placement, Relation, Transformer

子元素:Number

Number元素定义长度为8、16、24、32或64位的二进制数。

Number元素是DataModel,Block或Choice的子元素。

<!-- Flags常用属性如下: -->
<!-- name,可选,名称。 -->
<!-- size,必选,大小(bit)。有效范围是1到64。 -->
<!-- value,Blob中的默认值。 -->
<!-- valueType,默认格式(hex, string)。 -->
<!-- token,解析时是否将此元素视为token,默认为false。 -->
<!-- endian,数字的字节顺序,默认为little。有效选项是big, little, 和network。network等价于big。 -->
<!-- signed,指定数字是否是有符号的,默认为true。有效选项为true和false。 -->
<!-- constraint,定义在解析数据元素时的python约束表达式。 -->
<!-- mutable,是否是可变的(该选项应该置为true),默认是true。 -->
<!-- minOccurs,指定最小occur的次数,默认为1,有效选项是一个正整数。 -->
<!-- maxOccurs,指定最大occur的次数,无默认值,有效选项是一个正整数。 -->
<DataModel name="NumberExample1">
    <!-- 默认值为5,32-bit的数值 -->
    <Number name="Hi5" value="5" size="32"/>
</DataModel>

<DataModel name="NumberExample2">
    <!-- 默认值为5,16-bit的数值 -->
    <Number name="Hi5" value="5" size="16"/>
</DataModel>

<DataModel name="NumberExample3">
    <!-- 默认值为5,32-bit的无符号数值 -->
    <Number name="Hi5" value="5" size="32" signed="false"/>
</DataModel>

<DataModel name="NumberExample4">
    <!-- 将"1000"字符串转为数值型作为其默认值,16-bit的无符号数值 -->
    <Number name="Hi5" value="1000" valueType="string" size="16" signed="false" />
</DataModel>

<DataModel name="NumberExample5">
    <!-- 将"AB CD"字符串转为数值型作为其默认值,16-bit的无符号数值 -->
    <Number name="Hi5" value="AB CD" valueType="hex" size="16" signed="false" />
</DataModel>

<DataModel name="NumberExample6">
    <!-- 将"AB CD"字符串转为数值型作为其默认值,大端,16-bit的无符号数值 -->
    <Number name="Hi5" value="AB CD" valueType="hex" size="16" signed="false" endian="big" />
</DataModel>
<!-- 该数值的结果为:
		AB CD
-->

<DataModel name="NumberExample7">
    <!-- 将"AB CD"字符串转为数值型作为其默认值,小端,16-bit的无符号数值 -->
    <Number name="Hi5" value="AB CD" valueType="hex" size="16" signed="false" endian="little" />
</DataModel>
<!-- 该数值的结果为:
		CD AB
-->

Number的子元素包括:Anayzers, Fixup, Relation, Transformer, Hint

子元素:Padding

Padding用于填充出可变大小的块或数据模型。

<!-- Padding常用属性如下: -->
<!-- name,必须,名称。 -->
<!-- aligned,将父级与8-bit对齐,有效选项true或false,默认为false。 -->
<!-- alignment,指定对齐边界(如:8, 16等),默认为8。 -->
<!-- alignedTo,指定基于哪个元素进行填充,填元素的名字。 -->
<!-- lengthCalc,计算结果为整数的python表达式。 -->
<!-- constraint,定义在解析数据元素时的python约束表达式。 -->
<!-- mutable,是否是可变的(该选项应该置为true),默认是true。 -->
<DataModel name="NumberExample1">
    <String name="VariableSizeString" />
    <Padding aligned="true"/>
</DataModel>

Padding的子元素包括:Fixup, Relation, Transformer, Hint

子元素:String

String元素定义一个单字节或双字节字符串。String元素是DataModel或Block的子元素。

要指示这是数字字符串,请使用numericString提示。

<!-- String常用属性如下: -->
<!-- name,可选,名称。 -->
<!-- length,可选,字符串的长度,以字符为单位。 -->
<!-- lengthType,可选,length属性的单位(默认为字节)。 -->
<!-- type,可选,字符编码,默认为"ascii"。有效选项有:ascii,utf7,utf8,utf16,utf16be,utf32 -->
<!-- nullTerminated,可选,此字符串是否以null终止,有效选项true或false。 -->
<!-- padCharacter,可选,当字符串不足length长时,填充字符串的字符(默认为0x00)。 -->
<!-- token,解析时是否将此元素视为token,默认为false。 -->
<!-- constraint,定义在解析数据元素时的python约束表达式。 -->
<!-- mutable,是否是可变的(该选项应该置为true),默认是true。 -->
<!-- minOccurs,指定最小occur的次数,默认为1,有效选项是一个正整数。 -->
<!-- maxOccurs,指定最大occur的次数,默认为1,有效选项是一个正整数。 -->
<String value="Hello World!" />
<String value="Null terminated string" nullTerminated="true" />

<!-- numericString提示只能与String元素一起使用,以指示其值为数字。 -->
<!-- 使用此提示时,它将激活所有数字变量器以及标准字符串变量器。 -->
<!-- 注意:如果字符串的默认值为数字,则会自动添加NumericString提示。 -->
<String value="250">
    <Hint name="NumericalString" value="true" />
</String>

String的子元素包括:Analyzer, Fixup, Relation, Transformer, Hint

XmlAnalyzer

暂未找到相关的资料,有意补充此内容的读者,请在评论区留言。谢谢。

子元素:XmlAttribute

<!-- XmlAttribute常用属性如下: -->
<!-- name,可选,名称。 -->
<!-- minOccurs,指定最小occur的次数,有效选项是一个正整数。 -->
<!-- maxOccurs,指定最大occur的次数,有效选项是一个正整数。 -->
<!-- isStatic,可选,解析时是否将此元素视为token,默认为false。 -->
<!-- token,可选,解析时是否将此元素视为token,默认为false。 -->
<!-- mutable,是否是可变的(该选项应该置为true),默认是true。 -->
<!-- attributeName,必选,XML标签属性的名称。 -->
<!-- ns,可选,XML名称空间。 -->
<XmlElement name="example" elementName="Foo">
    <XmlAttribute attributeName="Bar">
        <String value="My Attribute!" />
    </XmlAttribute>
</XmlElement>
<!-- 得到结果:
		<Foo Bar="My Attribute!"/>
-->

XmlAttribute的子元素包括:Block, Choice, String, Number, Blob, Flags, Fixup, Hint

子元素:XmlElement

定义XML元素,这是XML文档的基本构建块。用于模糊XML文档的内容,而不是XML解析器。

XmlElement和XmlAttribute产生很好的XML输出。

注意:XmlElement / XmlAttribute元素不支持数据破解。如果需要将XML内容破解为XmlElement / XmlAttribute,请使用附加到String元素的XmlAnalyzer。

<!-- XmlElement常用属性如下: -->
<!-- name,可选,名称。 -->
<!-- minOccurs,指定最小occur的次数,有效选项是一个正整数。 -->
<!-- maxOccurs,指定最大occur的次数,有效选项是一个正整数。 -->
<!-- isStatic,可选,解析时是否将此元素视为token,默认为false。 -->
<!-- token,可选,解析时是否将此元素视为token,默认为false。 -->
<!-- mutable,是否是可变的(该选项应该置为true),默认是true。 -->
<!-- elementName,必选,XML元素的名称。 -->
<!-- ns,可选,XML名称空间。 -->
<XmlElement name="example" elementName="Foo">
    <XmlElement elementName="Bar">
        <String value="Hello World!" />
    </XmlElement>
</XmlElement>
<!-- 得到结果:
  <Foo><Bar>Hello World!</Bar></Foo>
-->

XmlElement的子元素包括:XmlElement, XmlAttribute, Block, Choice, String, Number, Blob, Flags, Fixup, Hint

Relation

Relation用于链接通过大小,计数或偏移量相关的两个元素。例如长度字段,它指定后续数据的长度。在Peach中,您可以通过将Relation元素附加到length字段并将其链接到需要其大小的元素来对这种关系进行建模。另一个示例是数组中的项目计数。为了对此建模,再次将一个Relation元素添加到数据元素,该元素包含将其链接到数组元素的计数。

关联字符串元素的长度。

	<DataModel name="TheDataModel">
		<String name="TheValue" value="Null terminated string" nullTerminated="true"/>
        <!-- 关联名为"TheValue"字符串元素的长度 -->
		<Number name="RelateToTheValueLen" size="32" signed="false">
			<Relation type="size" of="TheValue" />
		</Number>
		<String value="\r\n" token="true"/>
	</DataModel>

更多Relation的用法点击查看

DataModel数据模型到底长什么样?如何查看?

结合以上Relation的例子,创建一个DataModel name=“TheDataModel”。

创建pit配置,命名为:test_relation.xml。文件内容如下:

<?xml version="1.0" encoding="utf-8"?>
<Peach xmlns="http://peachfuzzer.com/2012/Peach" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://peachfuzzer.com/2012/Peach /peach/peach.xsd">
	<DataModel name="TheDataModel">
		<String name="TheValue" value="Null terminated string" nullTerminated="true"/>
        <!-- 关联名为"TheValue"字符串元素的长度 -->
		<Number name="RelateToTheValueLen" size="32" signed="false">
			<Relation type="size" of="TheValue" />
		</Number>
		<String value="\r\n" token="true"/>
	</DataModel>

	<StateModel name="TheState" initialState="Initial">
		<State name="Initial">
			<Action type="output">
				<DataModel ref="TheDataModel"/>
			</Action>
		</State>
	</StateModel>

	<Test name="Default">
        <!-- 为了观察TheDataModel的结果,排除对TheDataModel进行变异 -->
        <Exclude xpath="//TheDataModel" />
		<StateModel ref="TheState"/>

		<Publisher class="File">
			<Param name="FileName" value="fuzzed.bin" />
		</Publisher>
		
		<Strategy class="Random"/>
        <Logger class="File">
            <Param name="Path" value="logs"/>
        </Logger>
	</Test>
</Peach>

注意:

  1. 想要查看DataModel的数据模型,最好使用Publisher将数据输出。由于输出的DataModel数据是二进制的,使用Publisher class="File"输出到文件,比较容易对二进制数据进行分析。
  2. 在Test中需要定义,排除对自定义DataModel的变异。
  3. 为了只迭代测试一次,而不是迭代很多次(peach默认为:uint iterationStop = uint.MaxValue;)。所以在peach命令行中指定选项--range 1,1,告诉peach只迭代1次。

通过执行命令:

peach --debug --range 1,1 test_relation.xml

peach程序所在目录,找到生成的fuzzed.bin文件。执行命令(如果没有xxd命令,需要提前安装的哟。):

xxd fuzzed.bin

得到的结果如下所示:

00000000: 4e75 6c6c 2074 6572 6d69 6e61 7465 6420  Null terminated 
00000010: 7374 7269 6e67 0017 0000 000d 0a         string.......

分析输出结果:

将输出的十六进制数据划分为三个部分:

  • 4e75 6c6c 2074 6572 6d69 6e61 7465 6420 7374 7269 6e67 00

    这正是"Null terminated string"字符串的十六进制表示。

  • 17 0000 00

    这正是DataModel中定义的"RelateToTheValueLen"是32-bit的数据。

  • 0d 0a

    这正是"\r\n"的ascii码。

总结:

  1. fuzzed.bin是一个二进制文件,直接打开会出现乱码。
  2. DataModel中定义的数据,使用Publisher发布出去是以二进制形式。并不会主动对齐,如果需要字节对齐,需要另行设置。

Fixup

Fixup用于通常在另一个元素数据上产生值的功能。例如校验和Hashing。

peach自带的一些Fixup

常用的:

校验:

*Hashing:

Transformer

对数据元素的当前值(默认值或突变值)进行操作,执行一种或两种方式的操作,例如Base64编码或ZIP压缩。这样,可以模糊编码之前的原始值。

变形器在父元素上执行静态变形或编码。转换通常但不总是两个方向:编码和解码。示例包括ZIP压缩,Base64编码,HTML编码等。

Transfomers与Fixup的区别在于它在父元素上运行,而Fixups是引用另一个元素的数据。

一个简单的例子:

<DataModel name="Base64TLV">
    <Number name="Type" size="8" signed="false" value="1" token="true" />
    <Number name="Length" size="16" signed="false">
        <Relation type="size" of="base64Block" />
    </Number>

    <Block name="base64Block">
        <Transformer class="Base64Encode" />
        <Blob name="Data" />
    </Block>
</DataModel>

peach3中默认的Transfomers有:

Compression

Crypto

Encode

Type

Misc

Placement

一旦DataModel被解析(已将数据解析到其中),就可以使用Placement将元素移动到DataModel中的其他位置。这在解析复杂的数组结构时很有用。这通常与偏移关系结合使用。

放置元素告诉数据破解者,在解析输入流之后应移动特定元素。这与offset-of关系结合在一起,是Peach支持处理包含按offset引用元素的文件的方式。

注意:仅当通过输入Action或指向文件的Data语句将数据解析到DataModel中时,放置才起作用。

<!-- 下面两个属性中选一个
  1.after, 元素移动到之后
     2.before,元素移动到之前
-->
<DataModel name="TheDataModel">
    <Block name="Chunks">
        <Block name="ArrayOfChunks" maxOccurs="4">
            <Number size="8" signed="false">
                <Relation type="offset" of="Data"/>
            </Number>
            <String name="Data" length="6">
                <Placement after="Chunks"/>
            </String>
        </Block>
    </Block>
</DataModel>

10.StateModel

在Peach中,有两个模型可以创建模糊器,即DataModel和StateModel。StateModel重新创建测试协议所需的基本状态机逻辑。StateModel定义了如何向模糊目标发送和接收数据。建议在开始时保持StateModel简单并根据需要扩展。

State

State封装了Peach在StateModel中执行的逻辑工作单元。一些StateModel将仅需要一个State,而其他StateModel将需要许多State来对复杂协议建模。

States由Actions组成。每个Action都可以执行与各个State如何封装逻辑有关的任务。

<!-- State的属性:
	 name,必选,名称。
-->
<State name="InitialState">
    <Action type="output">
        <DataModel ref="TestTemplate" />
    </Action>
    <Action type="close" />
</State>

State的子元素包括:Action

Action

Action元素在StateModel中执行各种操作。Action主要是向Publisher发送命令的一种方式。Action可以发送输出,接收输入或打开连接。Action也可以更改为StateModel中的另一个状态,在DataModels之间移动数据,以及调用由Agents定义的方法。

Action是State的子元素。

<!-- String常用属性如下: -->
<!-- name,可选,名称。 -->
<!-- type,必选,Action类型。 -->
<!-- when,仅在提供的表达式的值为true时,才执行动作。 -->
<!-- onComplete,在操作完成时运行的表达式。 -->
<!-- onStart,在动作开始时运行的表达式。 -->
<!-- method,[必选,type=call],指定调用方法。 -->
<!-- property,[必选,type=setProperty, getProperty],指定要获取或设置的属性。 -->
<!-- setXpath,[必选,type=slurp],指定XPath的值。 -->
<!-- value,[type=slurp],值。 -->
<!-- valueXpath,[type=slurp],值。 -->
<!-- ref,[type=changeState],状态参考。 -->
<StateModel name="StateModel" initialState="InitialState">
    <State name="InitialState">

        <Action name="SendData" type="output">
            <DataModel ref="MyDataModel" />

            <!-- Optional data element -->
            <Data name="load defaults" fileName="template.bin" />
        </Action>

    </State>
</StateModel>

Action的类型

  • start(隐式)

    启动Publisher,这是一个隐式的Action,正常情况下是不需要的。

    <StateModel name="StateModel" initialState="InitialState">
        <State name="InitialState">
            <Action type="start" />
        </State>
    </StateModel>
    
  • stop(隐式)

    停止Publisher,这是一个隐式的Action,正常情况下是不需要的。

    <StateModel name="StateModel" initialState="InitialState">
        <State name="InitialState">
            <Action type="stop" />
        </State>
    </StateModel>
    
  • open/connect(隐式)

    open/connect是彼此的别名,并执行相同的操作。通常,此操作是隐式的,对于文件必须打开或创建文件,对于套接字建立连接。仅当需要特殊控制时,才需要使用此操作。

    <StateModel name="StateModel" initialState="InitialState">
        <State name="InitialState">
            <Action type="open" />
            <Action type="output">
                <DataModel ref="DataModelToWrite"/>
            </Action>
        </State>
    </StateModel>
    
    <!--与下面等价-->
    <StateModel name="StateModel" initialState="InitialState">
        <State name="InitialState">
            <Action type="connect" />
            <Action type="output">
                <DataModel ref="DataModelToSend"/>
            </Action>
        </State>
    </StateModel>
    
  • close(隐式)

    Close这是一个隐式的Action,正常情况下是不需要的,除非需要特殊控制时。

    <StateModel name="StateModel" initialState="InitialState">
        <State name="InitialState">
            <Action name="FileWrite" type="output">
                <DataModel ref="FileHeader"/>
            </Action>
            <Action name="FileClose" type="close" />
        </State>
    </StateModel>
    
  • accept

    接受一个远程连接。并不是所有的Publisher都支持这个Action类型。这中类型的动作将阻塞直到一个连接来后才可用。

    <StateModel name="StateModel" initialState="InitialState">
        <State name="InitialState">
            <Action name="AcceptConnection" type="accept" />
            <Action name="ParseIncomingPacket" type="input">
                <DataModel ref="PacketModel"/>
            </Action>
        </State>
    </StateModel>
    
  • input

    从Publisher中receive或read数据。要求指定一个DataModel来解析传入的数据。

    <StateModel name="StateModel" initialState="InitialState">
        <State name="InitialState">
            <Action type="input">
                <DataModel ref="InputModel" />
            </Action>
        </State>
    </StateModel>
    
  • output

    通过Publisher发送或写入输出。需要一个DataModel,可以选择提供一个数据集。

    <StateModel name="StateModel" initialState="InitialState">
        <State name="InitialState">
            <Action type="output">
                <DataModel ref="SomeDataModel" />
            </Action>
            <Action type="output">
                <DataModel ref="SomeDataModel" />
                <Data name="SomeSampleData" fileName="sample.bin" />
            </Action>
        </State>
    </StateModel>
    
  • call

    用可选参数调用Publisher定义的方法。并非所有Publisher都支持。

    <StateModel name="StateModel" initialState="InitialState">
        <State name="InitialState">
            <Action type="call" method="openUrl">
                <Param name="p1" type="in">
                    <DataModel ref="Param1DataModel" />
                </Param>
                <Param name="p2" type="in">
                    <DataModel ref="Param2DataModel" />
                    <Data name="p2data">
                        <Field name="value" value="http://foo.com" />
                    </Data>
                </Param>
            </Action>
        </State>
    </StateModel>
    
  • setProperty

    设置属性,并不是所有的Publisher都支持。

    <StateModel name="StateModel" initialState="InitialState">
        <State name="InitialState">
            <Action type="setProperty" property="Name">
                <DataModel ref="NameModel"/>
            </Action>
        </State>
    </StateModel>
    
  • getProperty

    获取一个属性,并不是所有的Publisher都支持。

    <StateModel name="StateModel" initialState="InitialState">
        <State name="InitialState">
            <Action type="getProperty" property="Name">
                <DataModel ref="NameModel"/>
            </Action>
        </State>
    </StateModel>
    
  • slurp

    Slurp用于在两个DataModel之间移动数据。这些DataModels在StateModel中分配给不同的动作。常用在协议序列中。需要将序列ID此类的数据发送回服务器。Slurp会将数据从服务器输入的一个动作复制到另一动作,再输出到服务器。

    <DataModel name="ReceiveChallenge">
        <String name="Challenge" />
    </DataModel>
    
    <DataModel name="SendChallenge">
        <String name="Challenge" />
    </DataModel>
    
    <StateModel name="StateModel" initialState="InitialState">
        <State name="InitialState">
            <Action name="ReceiveChallenge" type="input">
                <DataModel name="TheReceiveChallenge" ref="ReceiveChallenge"/>
            </Action>
    
            <Action type="slurp" valueXpath="//TheReceiveChallenge//Challenge" setXpath="//TheSendChallenge//Challenge" />
    
            <Action name="SendChallenge" type="output">
                <DataModel name="TheSendChallenge" ref="SendChallenge"/>
            </Action>
        </State>
    </StateModel>
    
  • changeState

    切换到另外一个State。这个经常与when联合使用。

    <StateModel name="StateModel" initialState="InitialState">
        <State name="InitialState">
            <Action type="input">
                <DataModel ref="InputModel" />
            </Action>
    
            <Action type="changeState" ref="State2"/>
        </State>
    
        <State name="State2">
            <Action type="output">
                <DataModel ref="OutputModel" />
            </Action>
        </State>
    </StateModel>
    
  • when

    根据表达式执行操作。当表达式的值为真时,将执行操作。这可用于基于输入对选择进行建模,或决定是否接下来执行某些输入或输出。

    <DataModel name="InputModel">
        <Number name="Type" size="32" />
    </DataModel>
    
    <DataModel name="OutputModelA">
        <Number name="Type" size="32" value="11 22 33 44" valueType="hex" />
    </DataModel>
    
    <DataModel name="OutputModelB">
        <Number name="Type" size="32" value="AA BB CC DD" valueType="hex" />
    </DataModel>
    
    <StateModel name="StateModel" initialState="InitialState">
        <State name="InitialState">
            <Action type="input">
                <DataModel ref="InputModel" />
            </Action>
    
            <Action type="changeState" ref="State2" when="int(StateModel.states['InitialState'].actions[0].dataModel['Type'].InternalValue) == 2"/>
    
            <Action type="changeState" ref="State3" when="int(StateModel.states['InitialState'].actions[0].dataModel['Type'].InternalValue) == 3"/>
    
        </State>
    
        <State name="State2">
            <Action type="output">
                <DataModel ref="OutputModelA" />
            </Action>
        </State>
    
        <State name="State3">
            <Action type="output">
                <DataModel ref="OutputModelB" />
            </Action>
        </State>
    </StateModel>
    

Action的子元素包括:DataModel, Data, Param

文件Fuzzer的StateModel

当文件模糊化时,Peach将数据写入文件,然后调用目标进程打开该文件。对于一个简单的文件模糊器,Peach可以使用一个状态和三个操作。

<StateModel name="TheStateModel" initialState="InitialState">
    <State name="InitialState">

        <!-- Write out the contents of file.  The publisher attribute matches
                the name provided for the publisher in the Test section. -->
        <Action type="output">
            <DataModel ref="TestTemplate" />
        </Action>

        <!-- Close the file -->
        <Action type="close" />

        <!-- Launch the file consumer -->
        <Action type="call" method="ScoobySnacks" publisher="Peach.Agent"/>

    </State>
</StateModel>

网络Fuzzer的StateModel

在这种状态模型中,Peach将从TCP端口发送和接收一组数据包。

<StateModel name="TheStateModel" initialState="InitialState">
    <State name="InitialState">

        <!-- Peach will automatically connect to the remote host -->

        <!-- Send data -->
        <Action type="output">
            <DataModel ref="PacketModel1" />
        </Action>

        <!-- Receive response -->
        <Action type="input">
            <DataModel ref="PacketModel2" />
        </Action>

        <!-- Send data -->
        <Action type="output">
            <DataModel ref="PacketModel3" />
        </Action>

        <!-- Receive response -->
        <Action type="input">
            <DataModel ref="PacketModel4" />
        </Action>
    </State>
</StateModel>

<Test name="Default">
    <StateModel ref="TheStateModel"/>

    <Publisher class="TcpClient">
        <Param name="Host" value="127.0.0.1" />
        <Param name="Port" value="4242" />
    </Publisher>
</Test>

11.Agent

Agent是可以在本地或远程运行的特殊Peach进程。这些进程托管一个或多个Monitors,这些Monitors可以执行诸如附加调试器,监视内存消耗,检测故障等操作。

代理是在本地或远程托管Monitor的进程。监控器又可以收集信息并根据模糊器的行为执行操作。

现有的代理包括:Local Agent,TCP Remoting Agent,ZeroMQ,REST Json Agent。

Local Agent

peach运行时支持在进程中运行的本地代理。如果未指定,则这是默认代理类型。

<Agent name="LocalAgent">
    <!-- Monitors -->
</Agent>

TCP Remoting Agent

该代理处于单独的进程中,该进程可以位于远程计算机上。通信通过TCP Remoting执行,TCP Remoting是运行时本机支持的RPC形式。

要使用远程代理,必须先运行代理进程。

<Agent name="RemoteAgent" location="tcp://192.168.1.1:9001">
    <!-- Monitors -->
</Agent>

运行远程代理的结果如下:

cmp@U1804:~$ peach.exe -a tcp

[[ Peach v3.0
[[ Copyright (c) Michael Eddington

[*] Starting agent server
 -- Press ENTER to quit agent --

ZeroMQ

该代理处于单独的进程中,该进程可以位于远程计算机上。使用ZeroMQ执行通信。ZeroMQ支持多种类型的语言。使用此代理通道可实现非.NET代理(例如python或ruby)。

要使用远程代理,必须先运行代理进程。

<Agent name="RemoteAgent" location="zmq://192.168.1.1:9001">
    <!-- Monitors -->
</Agent>

运行远程代理的结果如下:

cmp@U1804:~$ peach.exe -a zmq

[[ Peach v3.0
[[ Copyright (c) Michael Eddington

[*] Starting agent server
 -- Press ENTER to quit agent --

REST Json Agent

该代理旨在与以其他语言编写的自定义远程代理进行通信

<Agent name="TheAgent" location="http://127.0.0.1:9980">
    <Monitor class="WindowsDebugger">
        <Param name="CommandLine" value="mspaint.exe fuzzed.png" />
        <Param name="WinDbgPath" value="C:\Program Files (x86)\Debugging Tools for Windows (x86)" />
        <Param name="StartOnCall" value="ScoobySnacks"/>
    </Monitor>
    <Monitor class="PageHeap">
        <Param name="Executable" value="mspaint.exe"/>
        <Param name="WinDbgPath" value="C:\Program Files (x86)\Debugging Tools for Windows (x86)" />
    </Monitor>
</Agent>

<Test name="Default">
    <Agent ref="TheAgent"/>
    <StateModel ref="TheState"/>

    <Publisher class="Remote">
        <Param name="Agent" value="TheAgent"/>
        <Param name="Class" value="File"/>
        <Param name="FileName" value="fuzzed.png"/>
    </Publisher>

</Test>

简单的session

GET /Agent/AgentConnect
<< { "Status":"true" }

POST /Agent/StartMonitor?name=Monitor_0&cls=WindowsDebugger
>> {"args":{"CommandLine":"mspaint.exe fuzzed.png","WinDbgPath":"C:\\Program Files (x86)\\Debugging Tools for Windows (x86)","StartOnCall":"ScoobySnacks"}}
<< { "Status":"true" }

POST /Agent/StartMonitor?name=Monitor_1&cls=PageHeap
>> {"args":{"Executable":"mspaint.exe","WinDbgPath":"C:\\Program Files (x86)\\Debugging Tools for Windows (x86)"}}
<< { "Status":"true" }

GET /Agent/SessionStarting
<< { "Status":"true" }

GET /Agent/IterationStarting?iterationCount=1&isReproduction=False
<< { "Status":"true" }

GET /Agent/IterationFinished
<< { "Status":"true" }

GET /Agent/DetectedFault
<< { "Status":"true" }
// Status of true indicates a fault was detected. False for no fault.

GET /Agent/GetMonitorData
<< {
        "Results":[
                {
                        "iteration":0,
                        "controlIteration":false,
                        "controlRecordingIteration":false,
                        "type":0,  (0 unknown, 1 Fault, 2 Data)
                        "detectionSource":null,
                        "title":null,
                        "description":null,
                        "majorHash":null,
                        "minorHash":null,
                        "exploitability":null,
                        "folderName":null,
                        "collectedData":[
                                {"Key":"data1","Value":"AA=="}
                        ]
                }
        ]
}

GET /Agent/IterationStarting?iterationCount=1&isReproduction=True
<< { "Status":"true" }

GET /Agent/IterationFinished
<< { "Status":"true" }

GET /Agent/DetectedFault
<< { "Status":"true" }
// Status of true indicates a fault was detected. False for no fault.

GET /Agent/GetMonitorData
<< {
        "Results":[
                {
                        "iteration":0,
                        "controlIteration":false,
                        "controlRecordingIteration":false,
                        "type":0,  (0 unknown, 1 Fault, 2 Data)
                        "detectionSource":null,
                        "title":null,
                        "description":null,
                        "majorHash":null,
                        "minorHash":null,
                        "exploitability":null,
                        "folderName":null,
                        "collectedData":[
                                {"Key":"data1","Value":"AA=="}
                        ]
                }
        ]
}

GET /Agent/Publisher/stop
<< { "Status":"true" }

GET /Agent/SessionFinished
<< { "Status":"true" }

GET /Agent/StopAllMonitors
<< { "Status":"true" }

GET /Agent/AgentDisconnect
<< { "Status":"true" }

与远程Publisher进行通信的session

GET /Agent/AgentConnect
<< { "Status":"true" }

POST /Agent/StartMonitor?name=Monitor_0&cls=WindowsDebugger
>> {"args":{"CommandLine":"mspaint.exe fuzzed.png","WinDbgPath":"C:\\Program Files (x86)\\Debugging Tools for Windows (x86)","StartOnCall":"ScoobySnacks"}}
<< { "Status":"true" }

POST /Agent/StartMonitor?name=Monitor_1&cls=PageHeap
>> {"args":{"Executable":"mspaint.exe","WinDbgPath":"C:\\Program Files (x86)\\Debugging Tools for Windows (x86)"}}
<< { "Status":"true" }

GET /Agent/SessionStarting
<< { "Status":"true" }

GET /Agent/IterationStarting?iterationCount=1&isReproduction=False
<< { "Status":"true" }

POST /Agent/Publisher/Set_Iteration
>> {"iteration":1}
<< { "error":"false", "errorString":null }

POST /Agent/Publisher/Set_IsControlIteration
>> {"isControlIteration":true}
<< { "error":"false", "errorString":null }

POST /Agent/Publisher/Set_IsControlIteration
>> {"isControlIteration":true}
<< { "error":"false", "errorString":null }

POST /Agent/Publisher/Set_Iteration
>> {"iteration":1}
<< { "error":"false", "errorString":null }

GET /Agent/Publisher/start
<< { "error":"false", "errorString":null }

GET /Agent/Publisher/open
<< { "error":"false", "errorString":null }

POST /Agent/Publisher/output
>> {"data":"SGVsbG8gV29ybGQ="}
<< { "error":"false", "errorString":null }

GET /Agent/Publisher/close
<< { "error":"false", "errorString":null }

POST /Agent/Publisher/call
>> {"method":"ScoobySnacks","args":[{"name":"p1","data":"SGVsbG8gV29ybGQ=","type":0}]}
<< { "error":"false", "errorString":null }

GET /Agent/IterationFinished
<< { "Status":"true" }

GET /Agent/DetectedFault
<< { "Status":"true" }
// Status of true indicates a fault was detected. False for no fault.

GET /Agent/GetMonitorData
<< {
        "Results":[
                {
                        "iteration":0,
                        "controlIteration":false,
                        "controlRecordingIteration":false,
                        "type":0,  (0 unknown, 1 Fault, 2 Data)
                        "detectionSource":null,
                        "title":null,
                        "description":null,
                        "majorHash":null,
                        "minorHash":null,
                        "exploitability":null,
                        "folderName":null,
                        "collectedData":[
                                {"Key":"data1","Value":"AA=="}
                        ]
                }
        ]
}

GET /Agent/IterationStarting?iterationCount=1&isReproduction=True
<< { "Status":"true" }

POST /Agent/Publisher/Set_Iteration
>> {"iteration":1}
<< { "error":"false", "errorString":null }

POST /Agent/Publisher/Set_IsControlIteration
>> {"isControlIteration":true}
<< { "error":"false", "errorString":null }

POST /Agent/Publisher/Set_IsControlIteration
>> {"isControlIteration":true}
<< { "error":"false", "errorString":null }

POST /Agent/Publisher/Set_Iteration
>> {"iteration":1}
<< { "error":"false", "errorString":null }

GET /Agent/Publisher/start
<< { "error":"false", "errorString":null }

GET /Agent/Publisher/open
<< { "error":"false", "errorString":null }

POST /Agent/Publisher/output
>> {"data":"SGVsbG8gV29ybGQ="}
<< { "error":"false", "errorString":null }

GET /Agent/Publisher/close
<< { "error":"false", "errorString":null }

POST /Agent/Publisher/call
>> {"method":"ScoobySnacks","args":[{"name":"p1","data":"SGVsbG8gV29ybGQ=","type":0}]}
<< { "error":"false", "errorString":null }

GET /Agent/IterationFinished
<< { "Status":"true" }

GET /Agent/DetectedFault
<< { "Status":"true" }
// Status of true indicates a fault was detected. False for no fault.

GET /Agent/GetMonitorData
<< {
        "Results":[
                {
                        "iteration":0,
                        "controlIteration":false,
                        "controlRecordingIteration":false,
                        "type":0,  (0 unknown, 1 Fault, 2 Data)
                        "detectionSource":null,
                        "title":null,
                        "description":null,
                        "majorHash":null,
                        "minorHash":null,
                        "exploitability":null,
                        "folderName":null,
                        "collectedData":[
                                {"Key":"data1","Value":"AA=="}
                        ]
                }
        ]
}

GET /Agent/Publisher/stop
<< { "Status":"true" }

GET /Agent/SessionFinished
<< { "Status":"true" }

GET /Agent/StopAllMonitors
<< { "Status":"true" }

GET /Agent/AgentDisconnect
<< { "Status":"true" }

Monitors

Windows Monitors

OS X Monitors

Linux Monitors

  • Linux Crash Monitor

    该Linux Crash Monitor使用了一个注册到内核捕获进程错误的脚本。

    <Agent name="Local">
        <Monitor class="LinuxCrashMonitor"/>
    
        <Monitor class="Process">
            <!-- Executable,目标可执行文件,用于过滤崩溃(可选,默认为全部) -->
            <Param name="Executable" value="./CrashingProgram" />
            <Param name="StartOnCall" value="Start" />
            <Param name="Arguments" value="fuzzed.bin" />
        </Monitor>
    </Agent>
    
    <!-- LogFolder,日志文件夹(可选,默认为"/var/peachcrash") -->
    <!-- Mono,Mono运行时可执行文件的完整路径(可选,默认为"/usr/bin/mono") -->
    

Cross Platform Monitors

12.Test

Test用于配置StateModel以及Publishers, Loggers, Agents等。

Test元素用于配置特定的模糊测试,该测试将StateModel与Publisher和其他配置选项结合在一起,例如including/excluding要被突变的元素,Agents和Strategies。

支持多个测试元素,只需提供要在peach命令行上使用的测试元素的名称即可。

注意:运行Peach时,如果未在命令行上提供测试名称,则将运行名为"Default"的Test元素。

<!-- Test常用属性如下: -->
<!-- name,可选,名称。 -->
<!-- waitTime,指定在测试用例之间等待的时间(默认为0)。 -->
<!-- faultWaitTime,指定在故障发生时,开始下一次迭代之前等待的时间(默认为0)。 -->
<!-- controlIteration,指定多久执行一次控件迭代(默认为0)。 -->
<Test name="Default">
    <!-- Optionally exclude some elements from mutation -->
    <Exclude xpath="//Reserved" />
    <Exclude xpath="//Magic" />

    <!-- Optional agent references -->
    <Agent ref="LocalWindowsAgent" platform="windows" />
    <Agent ref="LocalOsxAgent" platform="osx" />
    <Agent ref="LocalLinuxAgent" platform="linux" />

    <Agent ref="RemoteAgent" />

    <!-- Indicate which state model to use (required) -->
    <StateModel ref="TheState" />

    <!-- Configure the publisher to use (required) -->
    <Publisher class="Tcp">
        <Param name="Host" value="127.0.0.1" />
        <Param name="Port" value="9001" />
    </Publisher>

    <!-- Use a different fuzzing strategy -->
    <Strategy class="Random" />

    <!-- Log output to disk -->
    <Logger class="File">
        <Param name="Path" value="logs" />
    </Logger>
</Test>

Test的子元素包括:

Agent (optional)

StateModel (required)

Publisher (required)

Include (optional)

Exclude (optional)

Strategy (optional)

Logger (optional, recommended)

Publisher

Publisher是用于和模糊测试目标进行通信的I/O接口。Publisher支持流操作和调用操作。

当模糊器运行时,除slurp之外的所有Action均使用Publisher来执行该Action。不同的Publisher支持不同的Action type集。例如,文件Publisher支持用于读取文件的输入,支持用于写入文件的输出,但不支持接受或调用。这不同于支持call,但不支持输入,输出或接受的COM Publisher。

所有模糊测试定义必须至少使用一个Publisher,并且可以根据需要选择使用多个Publisher。

当使用多个Publisher时,每个操作必须通过引用该操作publishername属性,来指定其引用的发布者。如果publisher缺少该属性,则将在测试中定义的第一个发布者上执行操作。

Network Publishers

在模糊网络协议时,通常使用的Publisher是包含目标协议的协议。例如,在对HTTP协议进行模糊测试时,将使用TCP Publisher。在对TCP模糊测试时,将使用IPv4或IPv6 Publisher。在对IPv4进行模糊测试时,将使用Ethernet Publisher。

Custom Publishers

Peach支持创建自定义发布者。建议先检查一些现有发布者的代码,以了解通常如何实现发布者。

创建自定义发布服务器不需要更改Peach源代码。而是将代码放置在新的.NET程序集(dll)中,该程序集添加到Peach bin文件夹中。Peach将自动使用反射来标识新的发布服务器并使其可用。

现有的Publisher

  • Com

    用于调用COM对象的方法和属性。

    注意:该发布服务器仅在Windows上运行。若要使用此发布服务器,当Peach运行时,Peach.Core.ComContainer.exe必须在单独的命令提示符下运行。

  • Console

    用于输出数据到标准输出。

    参数:

    Actions:

    • output
    <DataModel name="Data">
        <!-- ... -->
    </DataModel>
    
    <StateModel name="TheState">
        <State name="initial">
            <Action type="output">
                <DataModel ref="Data" />
            </Action>
        </State>
    </StateModel>
    
    <Test name="Default">
        <!-- ... -->
        <Publisher class="Console" />
    </Test>
    
  • ConsoleHex

    用于以16进制的形式输出数据到标准输出。

    参数:

    • BytesPerLine,每行输出多少个字节(默认为16个字节)。

    Actions:

    • output
    <DataModel name="Data">
        <!-- ... -->
    </DataModel>
    
    <StateModel name="TheState">
        <State name="initial">
            <Action type="output">
                <DataModel ref="Data" />
            </Action>
        </State>
    </StateModel>
    
    <Test name="Default">
        <!-- ... -->
        <Publisher class="ConsoleHex" />
    </Test>
    

    输出16进制数据时,每行显示8个字节。

    <DataModel name="Data">
        <Blob/>
    </DataModel>
    
    <StateModel name="TheState">
        <State name="initial">
            <Action type="input">
                <DataModel ref="Data" />
            </Action>
        </State>
    </StateModel>
    
    <Test name="Default">
        <Publisher class="ConsoleHex">
            <!-- 设置每行显示的字节数量 -->
            <Param name="BytesPerLine" value="8" />
        </Publisher>
    </Test>
    
  • File

    用于对文件的读写。

    参数:

    • FileName,指定文件名。

    • Overwrite,如果文件已经存在,是否写的时候覆盖文件数据(默认为true)。

    • Append,是否向已存在的文件中追加数据(默认为false)。

    Actions:

    • output

    • input

    写文件

    <DataModel name="Data">
        <!-- ... -->
    </DataModel>
    
    <StateModel name="TheState">
        <State name="initial">
            <Action type="output">
                <DataModel ref="Data" />
            </Action>
        </State>
    </StateModel>
    
    <Test name="Default">
        <!-- ... -->
        <Publisher class="File">
            <Param name="FileName" value="fuzzed.bin" />
        </Publisher>
    </Test>
    

    读文件

    <DataModel name="Data">
        <Blob/>
    </DataModel>
    
    <StateModel name="TheState">
        <State name="initial">
            <Action type="input">
                <DataModel ref="Data" />
            </Action>
        </State>
    </StateModel>
    
    <Test name="Default">
        <!-- ... -->
        <Publisher class="File">
            <Param name="FileName" value="fuzzed.bin" />
        </Publisher>
    </Test>
    
  • FilePerIteration

    用于将为每次模糊测试迭代创建一个输出文件。在预生成模糊案例时,这很有用。

    参数:

    • FileName, 要创建的文件名。文件名必须包含"{0}",它将用迭代计数代替。

    Actions:

    • output
    <DataModel name="Data">
        <!-- ... -->
    </DataModel>
    
    <StateModel name="TheState">
        <State name="initial">
            <Action type="output">
                <DataModel ref="Data" />
            </Action>
        </State>
    </StateModel>
    
    <Test name="Default">
        <!-- ... -->
        <Publisher class="FilePerIteration">
            <Param name="FileName" value="fuzzed_{0}.bin" />
        </Publisher>
    </Test>
    
  • Http

    该的Http Publisher通过选择的method发送通过HTTP数据。该Publisher支持以下功能:

    1. 用于身份验证,通过Basic, Digest, 或 Windows integrated;
    2. 可定义的方法类型;
    3. header的模糊化和动态设置(key和value);
    4. querystring的模糊化和动态设置;
    5. 可选的cookie支持;
    6. SSL协议;

    参数:

    • Method,HTTP方法类型(GET,POST等)。

    • Url,目标网址。

    • BaseUrl,基本URL由某些身份验证类型使用(可选)。

    • Username,身份验证用户名(可选)。

    • Domain,身份验证域(可选)。

    • Cookies,启用cookie支持(可选,默认为true)。

    • CookiesAcrossIterations,跟踪迭代中的烹饪(可选,默认为false)。

    • Timeout,等待数据/连接的时间(以毫秒为单位)(可选,默认为3,000)。

    • IgnoreCertErrors,允许https而不考虑证书状态(默认为false)。

    Actions:

    • call,用于querystring或header的模糊测试,支持的方法名:

      • Query,指定作为调用操作的方法名称,第一个参数是查询字符串
      • Header,指定作为调用操作的方法名称,第一个参数是标头名称,第二个参数是值
    • output,通过输出发送的数据作为HTTP正文。

    将数据Post到URL

    <DataModel name="PostBody">
        <!-- ... -->
    </DataModel>
    
    <StateModel name="TheState">
        <State name="initial">
            <Action type="output">
                <DataModel ref="PostBody" />
            </Action>
        </State>
    </StateModel>
    
    <Test name="Default">
        <!-- ... -->
        <Publisher class="Http">
            <Param name="Method" value="POST" />
            <Param name="Url" value="http://foo.com/user/create" />
        </Publisher>
    </Test>
    

    Fuzz querystring

    <DataModel name="QueryModel">
        <String value="key"/>
        <String value="=" token="true" />
        <String value="value"/>
    </DataModel>
    
    <StateModel name="TheState">
        <State name="initial">
            <Action type="call" method="Query">
                <Param>
                    <DataModel ref="QueryModel" />
                </Param>
            </Action>
        </State>
    </StateModel>
    
    <Test name="Default">
        <!-- ... -->
        <Publisher class="Http">
            <Param name="Method" value="GET" />
            <Param name="Url" value="http://foo.com/user/create" />
        </Publisher>
    </Test>
    

    Fuzz header

    <DataModel name="HeaderKey">
        <String value="Content-Type" />
    </DataModel>
    <DataModel name="HeaderValue">
        <String value="html" />
    </DataModel>
    
    <StateModel name="TheState">
        <State name="initial">
            <Action type="call" method="Header">
                <Param>
                    <DataModel ref="HeaderKey" />
                </Param>
                <Param>
                    <DataModel ref="HeaderValue" />
                </Param>
            </Action>
        </State>
    </StateModel>
    
    <Test name="Default">
        <!-- ... -->
        <Publisher class="Http">
            <Param name="Method" value="GET" />
            <Param name="Url" value="http://foo.com/user/create" />
        </Publisher>
    </Test>
    
  • Null

  • RawEther

  • RawIPv4

  • RawIPv6

  • RawV4

  • RawV6

  • Remote

  • TcpClient

    用于连接远程服务器。

    参数

    • Host,远程主机名或IP地址。
    • Port,目的端口号。
    • Timeout,等待数据超时,单位毫秒(optional, default 3,000)。
    • ConnectTimeout ,连接超时,单位毫秒(optional, default 10,000)。

    Actions

    • output,向远程主机发送数据。
    • input,从远端接收数据。

    发送和接收数据:

    <DataModel name="TheDataModel">
        <String name="value" length="4" />
    </DataModel>
    
    <StateModel name="TheState">
        <State name="initial">
            <Action type="output">
                <DataModel ref="TheDataModel"/>
                <Data>
                    <Field name="value" value="mike" />
                </Data>
            </Action>
    
            <!-- receive 4 bytes -->
            <Action type="input">
                <DataModel ref="TheDataModel"/>
            </Action>
        </State>
    </StateModel>
    
    <Test name="Default">
        <!-- ... -->
        <Publisher class="TcpClient">
            <Param name="Host" value="127.0.0.1" />
            <Param name="Port" value="8080" />
        </Publisher>
    </Test>
    
  • TcpListener

    用于连接远程服务器。

    参数

    • Interface,监听绑定的IP地址。
    • Port,监听端口。
    • Timeout,等待数据超时,单位毫秒(optional, default 3,000)。
    • AcceptTimeout ,接受连接超时,单位毫秒(optional, default 10,000)。

    Actions

    • accept,等待连接。
    • output,向远程主机发送数据。
    • input,从远端接收数据。

    接收和发送数据:

    <DataModel name="TheDataModel">
        <String name="value" length="4" />
    </DataModel>
    
    <StateModel name="TheState">
        <State name="initial">
            <Action type="accept" />
    
            <Action type="output">
                <DataModel ref="TheDataModel"/>
                <Data>
                    <Field name="value" value="mike" />
                </Data>
            </Action>
    
            <!-- receive 4 bytes -->
            <Action type="input">
                <DataModel ref="TheDataModel"/>
            </Action>
        </State>
    </StateModel>
    
    <Test name="Default">
        <!-- ... -->
        <Publisher class="TcpListener">
            <Param name="Interface" value="127.0.0.1" />
            <Param name="Port" value="8080" />
        </Publisher>
    </Test>
    
  • Udp

    用于接收和发送UDP数据包。

    参数

    • Host,远程主机的IP地址。
    • Port,目标端口号(目标发送第一个数据包时可选)。
    • SrcPort,源端口(可选)。
    • Interface,要绑定的IP(可选)。
    • Timeout,等待数据超时,单位毫秒(optional, default 3,000)。
    • MaxMTU,允许的最大MTU属性值(可选,默认值为131,070)。
    • MinMTU,允许的最小MTU属性值(可选,默认值为1,280)。

    Actions

    • output,向远程主机发送数据。
    • input,从远端接收数据。

    发送和接收数据:

    <DataModel name="TheDataModel">
        <String name="value" length="4" />
    </DataModel>
    
    <StateModel name="TheState">
        <State name="initial">
            <Action type="output">
                <DataModel ref="TheDataModel"/>
                <Data>
                    <Field name="value" value="mike" />
                </Data>
            </Action>
    
            <!-- receive 4 bytes -->
            <Action type="input">
                <DataModel ref="TheDataModel"/>
            </Action>
        </State>
    </StateModel>
    
    <Test name="Default">
        <!-- ... -->
        <Publisher class="Udp">
            <Param name="Host" value="127.0.0.1" />
            <Param name="Port" value="53" />
        </Publisher>
    </Test>
    
  • WebService

    该WebService Publisher就可以调用SOAP和基于WCF的Web服务。默认情况下,Publisher将尝试查找服务定义,或者可以提供一个。

    参数

    • Url,网址Url。
    • Service,服务名字。
    • Wsdl,用于Web服务的WSDL的Path或URL(可选)。
    • ErrorOnStatusCode,状态码不是200时发生的错误(可选,默认为true)。
    • Timeout,等待数据超时,单位毫秒(optional, default 3,000)。
    • Throttle,连接之间等待的时间(以毫秒为单位)(可选,默认为0)。

    Actions

    • call,Method属性是Web服务上要调用的方法。

    Example calling web service

    <DataModel name="TheDataModel">
        <String name="value" />
    </DataModel>
    
    <StateModel name="TheState">
        <State name="initial">
            <Action type="call" method="Login">
                <Param name="user">
                    <DataModel ref="TheDataModel"/>
                    <Data>
                        <Field name="value" value="mike" />
                    </Data>
                </Param>
                <Param name="pass">
                    <DataModel ref="TheDataModel"/>
                    <Data>
                        <Field name="value" value="Password!" />
                    </Data>
                </Param>
            </Action>
        </State>
    </StateModel>
    
    <Test name="Default">
        <!-- ... -->
        <Publisher class="WebService">
            <Param name="Url" value="http://localhost:5903/TestService.svc" />
            <Param name="Service" value="TestService" />
        </Publisher>
    </Test>
    

Logger

Logger用于记录和收集模糊测试过程中的信息,以便于这些信息可以用于进一步分析错误。

Peach具有可扩展的日志记录系统,该系统允许用户存储所需的日志(如果您愿意编写Peach扩展)。默认情况下,Peach随附单个文件系统记录器。

参数:

  • Path,指定日志输出的路径,相对或局对路径。
<Logger class="File">
    <Param name="Path" value="logfolder" />
</Logger>

Strategy

用于指定如何在模糊迭代中执行数据突变的策略。

传统上,Peach通过自上而下的顺序方法对DataModels进行模糊处理。尽管这保证了每个测试案例都会混淆每个数据元素,但对于可能产生数百万个测试案例变体的大型复杂系统而言,这并不是最佳选择。另外,已经认识到需要一种机制来允许容易地改变执行模糊的方式以允许对最佳方法和策略的容易研究。

由此诞生了可插拔突变策略。通过实现单个类,用户可以完全控制Peach如何模糊目标,包括状态转换。

默认情况下,Peach带有三种策略。

Random

随机策略将永远运行。此策略将一次最多选择要更改的MaxFieldsToMutate元素。对于每个选定的元素,将随机选择其对应的变体之一。peach从随机产生的种子数中得出这些选择的随机性。通过在peach命令行选项使用--seed选项定随机种子,可以重复相同的测试运行。这对于重复模糊迭代以重现先前的Fault很有用。

对于较大的数据模型或在执行顺序模糊测试之后使用,此策略最有用。

参数

  • MaxFieldsToMutate,一次最大变异的字段数量(默认为6)。
  • SwitchCount,切换数据集之前执行迭代的次数(默认为200)。

例子

<Test name="Default">
    <StateModel ref="TheStateModel"/>

    <Publisher name="writer" class="File">
        <Param name="FileName" value="fuzzed.tmp"/>
    </Publisher>

    <Strategy class="Random">
        <Param name="MaxFieldsToMutate" value="15" />
        <Param name="SwitchCount" value="100" />
    </Strategy>
</Test>

Sequential

Peach将一次按顺序模糊DataModel中的每个元素。Peach将从DataModel的顶部开始,并将所有有效的变异应用于每个数据元素,直到用尽所有可能的变异为止。这种策略有一定数量的模糊迭代。此策略的种子不可配置,并且始终为31337。

<Test name="Default">
    <!-- ... -->
    <Strategy class="Sequential" />
</Test>

RandomDeterministic (default)

这种模糊策略是确定性的(具有开始和结束)。它与顺序策略相似,除了突变顺序已被打乱。仍然存在有限数量的迭代,并且如果运行到完成将运行使用顺序策略生成的每个迭代。与Random策略类似,使用随机种子值可以相同的重复以前的模糊测试运行。

Mutator

peach为数据模型中定义的格式错误的数据元素选择的数据突变模块。

目前现有的Mutators包括:

参考

https://community.peachfuzzer.com/v3/PeachPit.html


转载:https://blog.csdn.net/weixin_46222091/article/details/108500369
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场