├── LICENSE
├── README.md
├── encoder
├── encode.rb
└── lib
│ ├── Cache.rb
│ ├── CallGraphGenerator.rb
│ ├── ExploitPotentialEvaluator.rb
│ ├── Function.rb
│ ├── FunctionGraphRenderer.rb
│ ├── GadgetFinder.rb
│ ├── GraphSearch.rb
│ ├── Instruction.rb
│ ├── InstructionArgument.rb
│ ├── InstructionFilter.rb
│ ├── ObjdumpLine.rb
│ ├── ObjdumpReader.rb
│ ├── Print.rb
│ ├── Results.rb
│ └── Shellcode.rb
├── player
├── libs
│ ├── jquery-1.11.2.min.js
│ └── modernizr.js
├── out.json
└── ptrace.html
├── samples
├── hello.c
└── passgen
└── tracer
├── Makefile
└── tracer.c
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013 Taylor Hornby
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ELFPlayer
2 | ============
3 |
4 | ELFPlayer is a tool for visualizing the execution of 32-bit x86 ELFs (with
5 | symbols). This can be useful for getting a better understanding of what your
6 | code is doing, crafting exploits, or side-channel analysis.
7 |
8 | **Currently, ELFPlayer is prototype quality. This is a beta release.**
9 |
10 | 
11 |
12 | ELFPlayer is made up of three components: The tracer, encoder, and player.
13 |
14 | Tracer
15 | --------
16 |
17 | The tracer is a C program that uses ptrace to save all of the EIP values as your
18 | program executes. To use it, pass the output file on the command line followed
19 | by the command to execute under ptrace (just like `strace`).
20 |
21 | For exaple, if you've built the `hello` sample in the `samples` directory (by
22 | `gcc -m32 hello.c -o hello`), here's how you trace it (with an unnecessary
23 | command-line argument for demonstration):
24 |
25 | ```
26 | $ ./tracer/tracer ./output ./samples/hello --an-argument-to-hello
27 | ```
28 |
29 | This will save all of the EIP values to `./output.`. To visualize it, you first
30 | have to encode it into a JSON file that the player supports. Use the encoder
31 | tool to do that.
32 |
33 | Encoder
34 | --------
35 |
36 | The encoder (Ruby script) transforms the tracer's output into an easy-to-parse
37 | JSON file for the player to play. Supposing we ran the tracer on
38 | `./samples/hello` and its output is saved in `./output`, the command to encode
39 | is:
40 |
41 | ```
42 | $ ruby encoder/encode.rb -b ./samples/hello -o ./player/out.json ./output
43 | ```
44 |
45 | This will write the encoded JSON into `./player/out.json`, the location the
46 | player expects its input to be.
47 |
48 | Player
49 | --------
50 |
51 | The player is an HTML5 Canvas web page that fetches the JSON file and displays
52 | a visualization of the execution. It currently supports only very primitive
53 | scrolling by using the mousewheel or by clicking the scrollbars on the left or
54 | the top. To open it, run:
55 |
56 | ```
57 | $ firefox ./player/ptrace.html
58 | ```
59 |
60 | For now, it will probably only work with firefox. I haven't tested it with
61 | anything else.
62 |
63 | Red columns represent continuous sequences of EIP values which were not in
64 | regions known to the encoder. For example, if execution jumps into glibc for 100
65 | instructions, those 100 instructions are displayed as a single red column. Blue
66 | dots on the top or bottom mean there is an instruction above or below the view,
67 | respectively. Use the (shitty) left scrollbar to bring them into view.
68 |
--------------------------------------------------------------------------------
/encoder/encode.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | $LOAD_PATH << File.dirname( __FILE__ )
4 |
5 | require 'optparse'
6 | require 'set'
7 | require 'json'
8 |
9 | require 'lib/Print.rb'
10 | require 'lib/CallGraphGenerator.rb'
11 | require 'lib/Instruction.rb'
12 | require 'lib/Function.rb'
13 | require 'lib/FunctionGraphRenderer.rb'
14 | require 'lib/InstructionFilter.rb'
15 | require 'lib/ExploitPotentialEvaluator.rb'
16 | require 'lib/GadgetFinder.rb'
17 | require 'lib/ObjdumpReader.rb'
18 | require 'lib/Cache.rb'
19 | require 'lib/Results.rb'
20 | require 'lib/Shellcode.rb'
21 | require 'lib/GraphSearch.rb'
22 |
23 | $options = {}
24 | optparse = OptionParser.new do |opts|
25 | opts.banner = "Usage: #{__FILE__} [options] ADDRS_FILE"
26 |
27 | $options[:binary] = nil
28 | opts.on( '-b', '--binary FILE', 'ELF binary' ) do |file|
29 | $options[:binary] = file
30 | end
31 |
32 | $options[:output] = nil
33 | opts.on( '-o', '--output FILE', 'Output file' ) do |file|
34 | $options[:output] = file
35 | end
36 |
37 | $options[:title] = nil
38 | opts.on( '-t', '--title TITLE', 'Title (Default: binary name)' ) do |title|
39 | $options[:title] = title
40 | end
41 | end
42 |
43 | begin
44 | optparse.parse!
45 | rescue OptionParser::InvalidOption
46 | STDERR.puts "[!] Invalid options"
47 | STDERR.puts optparse
48 | exit 1
49 | end
50 |
51 | INPUT_PATH = ARGV[0]
52 |
53 | if INPUT_PATH.nil? || !File.exists?( INPUT_PATH )
54 | STDERR.puts "[!] Please provide a path to the input file."
55 | STDERR.puts optparse
56 | exit 1
57 | end
58 |
59 | if $options[:binary].nil? || !File.exists?( $options[:binary] )
60 | STDERR.puts "[!] Please provide a path to the binary."
61 | STDERR.puts optparse
62 | exit 1
63 | end
64 |
65 | if !ObjdumpReader.x86ELF?( $options[:binary] )
66 | STDERR.puts "[!] Binary should be a 32-bit ELF."
67 | STDERR.puts optparse
68 | exit 1
69 | end
70 |
71 | if $options[:output].nil?
72 | STDERR.puts "[!] Please provide an output path."
73 | STDERR.puts optparse
74 | exit 1
75 | end
76 |
77 | if $options[:title].nil?
78 | $options[:title] = File.basename($options[:binary])
79 | end
80 |
81 | gen = CallGraphGenerator.new( $options[:binary] )
82 | functions = gen.generate
83 |
84 | # TODO Do this... but we need to know which address it was loaded at (ASLR)
85 | #gen2 = CallGraphGenerator.new( "/usr/lib32/libc.so.6" )
86 | #functions += gen2.generate
87 |
88 | input = File.open(INPUT_PATH, "r")
89 |
90 | last_was_invalid = false
91 |
92 | output_structure = {
93 | "title" => $options[:title],
94 | "script" => []
95 | }
96 |
97 | input.each_line do |line|
98 | address = line.to_i(16)
99 |
100 | in_func = nil
101 | functions.each do |func|
102 | if func.start_addr <= address && address < func.end_addr
103 | in_func = func
104 | break
105 | end
106 | end
107 |
108 | in_func_instr_num = nil
109 | if in_func.nil?
110 | unless last_was_invalid
111 | output_structure["script"] << {
112 | "x" => 1,
113 | }
114 | last_was_invalid = true
115 | end
116 | else
117 | last_was_invalid = false
118 | 0.upto(in_func.disassembly.length - 1) do |instr_num|
119 | instr = in_func.disassembly[instr_num]
120 |
121 | if instr.address == address
122 | in_func_instr_num = instr_num
123 | break
124 | end
125 | end
126 |
127 | if in_func_instr_num.nil?
128 | puts "Mis-aligned instruction."
129 | exit 1
130 | end
131 |
132 | output_structure["script"] << {
133 | "f" => functions.find_index(in_func),
134 | "i" => in_func_instr_num,
135 | }
136 | end
137 | end
138 |
139 | input.close
140 |
141 | output_structure["functions"] = functions.map { |f| f.name }
142 |
143 | File.open($options[:output], "w") do |output|
144 | JSON.dump(output_structure, output)
145 | end
146 |
--------------------------------------------------------------------------------
/encoder/lib/Cache.rb:
--------------------------------------------------------------------------------
1 | require 'digest'
2 |
3 | class Cache
4 |
5 | CACHE_DIRECTORY = 'cache'
6 |
7 | def self.set( key, value )
8 | return if $options[:nocache]
9 | self.make_cache_dir
10 |
11 | File.open( File.join( CACHE_DIRECTORY, self.sha256( key ) ), "w" ) do |f|
12 | f.print Marshal.dump( value )
13 | end
14 |
15 | return value
16 | end
17 |
18 | def self.get( key )
19 | return if $options[:nocache]
20 | self.make_cache_dir
21 |
22 | value = nil
23 | path = File.join( CACHE_DIRECTORY, self.sha256( key ) )
24 | if File.exists? path
25 | File.open( File.join( CACHE_DIRECTORY, self.sha256( key ) ), "r" ) do |f|
26 | value = Marshal.load( f.read )
27 | end
28 | end
29 |
30 | return value
31 | end
32 |
33 | def self.sha256( string )
34 | Digest::SHA256.new.hexdigest( string )
35 | end
36 |
37 | def self.sha256_file( path )
38 | File.open( path, "r" ) do |f|
39 | Digest::SHA256.new.hexdigest( f.read )
40 | end
41 | end
42 |
43 | def self.make_cache_dir
44 | unless Dir.exists? CACHE_DIRECTORY
45 | Dir.mkdir( CACHE_DIRECTORY )
46 | end
47 | end
48 |
49 | end
50 |
--------------------------------------------------------------------------------
/encoder/lib/CallGraphGenerator.rb:
--------------------------------------------------------------------------------
1 | require 'set'
2 | require 'lib/Print.rb'
3 | require 'lib/Instruction.rb'
4 | require 'lib/Function.rb'
5 | require 'lib/ObjdumpReader.rb'
6 | require 'lib/ObjdumpLine.rb'
7 |
8 | # NOTE: names are not unique, so we use address ranges to identify
9 |
10 | class CallGraphGenerator
11 |
12 | attr_reader :elf_path, :functions
13 |
14 | BRANCH_MNEMONICS = [
15 | # TODO: add the other types of call instruction (are there any?)
16 | "call",
17 | "jo", "jno", "js", "jns", "je", "jz", "jne", "jnz", "jb", "jnae", "jc",
18 | "jbe", "jna", "ja", "jnbe", "jl", "jnge", "jge", "jnl", "jle", "jng",
19 | "jg", "jnle", "jp", "jpe", "jnp", "jpo", "jcxz", "jecxz",
20 | ].to_set
21 |
22 | def initialize( path )
23 | @elf_path = path
24 | @functions = []
25 | end
26 |
27 | def generate
28 | extract_functions()
29 | add_edges()
30 | return @functions
31 | end
32 |
33 | # private FIXME (testing )
34 |
35 | def extract_functions
36 | output = ObjdumpReader.objdump_output(
37 | [
38 | "-M", "intel", "-D",
39 | "--section=.text", "--section=.plt", "--section=.init",
40 | "--demangle", @elf_path
41 | ]
42 | )
43 | lines = output.split("\n").map { |l| ObjdumpLine.new( l ) }
44 |
45 | @functions = Set.new
46 | last_label = nil
47 | last_instrs = nil
48 |
49 | lines.each do |l|
50 | if l.type == :label
51 | unless last_label.nil?
52 | f = Function.new(
53 | last_label.name,
54 | last_label.address,
55 | l.address,
56 | ObjdumpReader.lines_to_instrs( last_instrs )
57 | )
58 | @functions << f
59 | end
60 | last_label = l
61 | last_instrs = []
62 | elsif l.type == :instruction
63 | unless last_instrs.nil?
64 | last_instrs << l
65 | end
66 | end
67 | end
68 | end
69 |
70 | def add_edges
71 | # FIXME we don't need this functionality, and it's broken, so returning.
72 | return
73 | @functions.each do |f|
74 | f.disassembly.each do |instr|
75 |
76 | if BRANCH_MNEMONICS.member?( instr.mnemonic ) && instr.args[0].type == :constant
77 | # FIXME: this will make inner loops look recursive. Maybe we should
78 | # stick with call? Or make J* instructions add a maybe_call to itself?
79 | called = find_function_at( instr.args[0].value )
80 | if called.nil?
81 | Print::error( "Function [#{instr.args[0].hint}] at [0x#{instr.args[0].value.to_s(16)}] doesn't exist!" )
82 | next
83 | end
84 | f.calls << called
85 | called.called_by << f
86 | elsif BRANCH_MNEMONICS.member?( instr.mnemonic ) && instr.args[0].type != :constant
87 | f.maybe_calls = @functions
88 | f.maybe_calls.each do |func|
89 | func.maybe_called_by << f
90 | end
91 | end
92 | end
93 | end
94 | end
95 |
96 | def find_function_at( address )
97 | @functions.each do |f|
98 | if f.start_addr <= address && address < f.end_addr # FIXME only exact?
99 | return f
100 | end
101 | end
102 | return nil
103 | end
104 |
105 | end
106 |
--------------------------------------------------------------------------------
/encoder/lib/ExploitPotentialEvaluator.rb:
--------------------------------------------------------------------------------
1 | require 'lib/Function.rb'
2 | require 'lib/InstructionFilter.rb'
3 |
4 | class ExploitPotentialEvaluator
5 |
6 | def initialize( filter )
7 | @filter_class = filter
8 | @functions = Set.new
9 | end
10 |
11 | def addFunctions( functions )
12 | @functions.merge( functions )
13 | end
14 |
15 | def applyFilters
16 | @functions.each do |function|
17 | function.applyFilter( @filter_class )
18 | end
19 | end
20 |
21 | def applyGadgets( gadgets )
22 | @functions.each do |function|
23 | function.gadgets = gadgets.reject { |g| !function.filter.allows?( g ) }
24 | end
25 | end
26 |
27 | end
28 |
--------------------------------------------------------------------------------
/encoder/lib/Function.rb:
--------------------------------------------------------------------------------
1 | require 'set'
2 |
3 | class Function
4 | attr_reader :start_addr, :end_addr
5 | attr_accessor :name, :disassembly, :calls, :called_by, :maybe_calls, :maybe_called_by, :filter, :gadgets
6 |
7 | def initialize(name, start_addr, end_addr, disassembly)
8 | @name = name
9 | @start_addr = start_addr
10 | @end_addr = end_addr
11 | @disassembly = disassembly
12 | @calls = Set.new
13 | @called_by = Set.new
14 | @maybe_calls = Set.new
15 | @maybe_called_by = Set.new
16 | @filter = nil
17 | @gadgets = nil
18 |
19 | if @start_addr > @end_addr
20 | raise ArgumentError.new( "start_addr > end_addr" )
21 | end
22 | end
23 |
24 | def applyFilter( filter_class )
25 | @filter = filter_class.new
26 | @filter.setMostRestrictive( @disassembly )
27 | end
28 |
29 | def to_s
30 | return @name # FIXME
31 | "#{@name} (0x#{@start_addr.to_s(16)} - 0x#{@end_addr.to_s(16)}):\n" +
32 | @disassembly.map { |x| x.to_s }.join("\n")
33 | end
34 |
35 | end
36 |
--------------------------------------------------------------------------------
/encoder/lib/FunctionGraphRenderer.rb:
--------------------------------------------------------------------------------
1 | require 'graphviz'
2 |
3 | class FunctionGraphRenderer
4 |
5 | def FunctionGraphRenderer.make_image( functions, type, output_path )
6 | g = GraphViz.new( :G, :type => :digraph )
7 |
8 | # Create a node for every function.
9 | nodes = {}
10 | functions.each do |f|
11 | if f.gadgets.nil?
12 | gadget_info = ""
13 | else
14 | gadget_info = "(Gadgets: #{f.gadgets.length})"
15 | end
16 | node = g.add_nodes( "#{f.name} at 0x#{f.start_addr.to_s(16)} #{gadget_info}" )
17 | nodes[f] = node
18 | end
19 |
20 | # Add the edges between functions.
21 | edges_added = []
22 | functions.each do |f|
23 | f.calls.each do |called|
24 | edge = [nodes[f], nodes[called]]
25 | next if edges_added.include? edge
26 | g.add_edges( nodes[f], nodes[called], "color" => "black" )
27 | edges_added << edge
28 | end
29 | f.maybe_calls.each do |called|
30 | edge = [nodes[f], nodes[called]]
31 | next if edges_added.include? edge
32 | g.add_edges( nodes[f], nodes[called], "color" => "gray" )
33 | edges_added << edge
34 | end
35 | end
36 |
37 | begin
38 | g.output( type => output_path )
39 | rescue RuntimeError => e
40 | if e.message.include? "too large for cairo-renderer"
41 | Print::error "The graph was too big so it has been scaled down."
42 | else
43 | raise e
44 | end
45 | end
46 | end
47 |
48 | end
49 |
--------------------------------------------------------------------------------
/encoder/lib/GadgetFinder.rb:
--------------------------------------------------------------------------------
1 | require 'lib/Instruction.rb'
2 | require 'lib/ObjdumpReader.rb'
3 | require 'lib/Print.rb'
4 |
5 | # TODO: add different options for gadget finders
6 |
7 | class GadgetPointer
8 | attr_reader :start_address, :instruction_count
9 |
10 | def initialize( start_address, instruction_count )
11 | @start_address = start_address
12 | @instruction_count = instruction_count
13 | end
14 |
15 | # Convert to array of Instruction
16 | def to_gadget( elf_path )
17 | stop_address = @start_address + 16*@instruction_count
18 | output = ObjdumpReader.objdump_output(
19 | [
20 | "-M", "intel", "-D",
21 | "--start-address=0x#{@start_address.to_s(16)}",
22 | "--stop-address=0x#{stop_address.to_s(16)}",
23 | elf_path
24 | ]
25 | )
26 | lines = output.split( "\n" ).map { |l| ObjdumpLine.new( l ) }
27 | lines.reject! { |l| l.type != :instruction }
28 | instrs = ObjdumpReader.lines_to_instrs( lines )
29 | if instrs.length == 0
30 | Print::error( "Gadget has zero length." )
31 | end
32 | return instrs.first( @instruction_count )
33 | end
34 | end
35 |
36 | # External gadget finders -- return array of GadgetPointer
37 |
38 | class GadgetFinder
39 |
40 | def initialize( elf_path )
41 | @elf_path = elf_path
42 | end
43 |
44 | def findGadgets
45 | pointers = findGadgetPointers
46 | # We don't trust the gadget finder to be unique, so we uniq it ourselves.
47 | pointers.uniq! { |p| [p.start_address, p.instruction_count] }
48 | gadgets = pointers.map { |g| g.to_gadget( @elf_path ) }
49 | gadgets.uniq! { |g| g.map { |i| i.binary }.join( "" ) }
50 | return gadgets.to_set
51 | end
52 |
53 | # In derived classes, this must return an array of GadgetPointer
54 | def findGadgetPointers
55 | raise "Incomplete class."
56 | end
57 |
58 | end
59 |
60 | class ROPgadgetGadgetFinder < GadgetFinder
61 |
62 | def findGadgetPointers
63 | output = nil
64 | IO.popen( ["ROPgadget", "-nocolor", "-nopayload", @elf_path, :err=>[:child, :out]] ) do |rop|
65 | output = rop.read
66 | end
67 |
68 | return output.split( "\n" ).reject { |l| !l.start_with? "0x" }.map do |line|
69 | line =~ /^0x([0-9a-fA-F]+):\s+(.*)$/
70 | address = $1.to_i(16)
71 | length = $2.split( ";" ).length
72 | GadgetPointer.new( address, length )
73 | end
74 | end
75 |
76 | end
77 |
78 | class MineGadgetFinder < GadgetFinder
79 |
80 | def findGadgetPointers
81 | output = nil
82 | IO.popen( ["../rop/gadget_finder", @elf_path] ) do |rop|
83 | output = rop.read
84 | end
85 |
86 | blocks = output.split( "-----------------------\n" )
87 | blocks.map! { |b| b.split( "\n" ).map { |l| l =~ /0x([0-9a-fA-F]+):/ ; $1.to_i(16) } }
88 |
89 | pointers = []
90 | blocks.each do |block|
91 | 2.upto( block.length ) do |length|
92 | pointers << GadgetPointer.new( block[block.length - length], length )
93 | end
94 | end
95 |
96 | return pointers
97 | end
98 |
99 | end
100 |
--------------------------------------------------------------------------------
/encoder/lib/GraphSearch.rb:
--------------------------------------------------------------------------------
1 | require 'set'
2 | require 'lib/Function.rb'
3 | require 'lib/Shellcode.rb'
4 | require 'lib/Print.rb'
5 |
6 | class FunctionSequence < Array
7 |
8 | def initialize( start )
9 | start = [*start]
10 | start.each do |s|
11 | self << s
12 | end
13 | end
14 |
15 | def advance( next_f )
16 | next_f = [*next_f]
17 | return FunctionSequence.new( self + next_f )
18 | end
19 |
20 | end
21 |
22 | class PathFinder
23 |
24 | attr_accessor :allow_calls, :allow_maybe_calls
25 | attr_accessor :allow_called_by, :allow_maybe_called_by
26 | attr_accessor :complete
27 |
28 | def initialize( all_functions )
29 | @functions = all_functions
30 | @paths = Hash.new { |h,k| h[k] = Hash.new }
31 | @allow_calls = false
32 | @allow_maybe_calls = false
33 | @allow_called_by = false
34 | @allow_maybe_called_by = false
35 | @complete = false
36 | end
37 |
38 | def process
39 | return if @complete
40 |
41 | @functions.each do |start|
42 | # All nodes have the 0-length path to themselves
43 | @paths[start][start] = [start]
44 |
45 | # 'seen' is the set of all nodes that we've visited, or in other words,
46 | # for which we know a path from 'start' to.
47 | seen = [start].to_set
48 | # 'seen_unprocessed' is the subset of 'seen' for which we haven't
49 | # added their neighbours.
50 | seen_unprocessed = seen
51 |
52 | # Keep adding neighbour nodes until there are no more neighbours to add.
53 | loop do
54 | # Loop over all nodes we have seen but not yet added neighbours.
55 | # A list of new nodes (not 'seen') that we discover in this iteration.
56 | seen_thistime = Set.new
57 | seen_unprocessed.each do |s|
58 |
59 | # List their neighbours
60 | if @allow_calls
61 | s.calls.each do |n|
62 | # If we haven't seen it before...
63 | unless seen.member?( n ) || seen_thistime.member?( n )
64 | # Add the new-found path, and remember that we've seen it.
65 | @paths[start][n] = @paths[start][s] + [n]
66 | seen_thistime << n
67 | end
68 | end
69 | end
70 |
71 | if @allow_maybe_calls
72 | s.maybe_calls.each do |n|
73 | # If we haven't seen it before...
74 | unless seen.member?( n ) || seen_thistime.member?( n )
75 | # Add the new-found path, and remember that we've seen it.
76 | @paths[start][n] = @paths[start][s] + [n]
77 | seen_thistime << n
78 | end
79 | end
80 | end
81 |
82 | if @allow_called_by
83 | s.called_by.each do |n|
84 | # If we haven't seen it before...
85 | unless seen.member?( n ) || seen_thistime.member?( n )
86 | # Add the new-found path, and remember that we've seen it.
87 | @paths[start][n] = @paths[start][s] + [n]
88 | seen_thistime << n
89 | end
90 | end
91 | end
92 |
93 | if @allow_maybe_called_by
94 | s.maybe_called_by.each do |n|
95 | # If we haven't seen it before...
96 | unless seen.member?( n ) || seen_thistime.member?( n )
97 | # Add the new-found path, and remember that we've seen it.
98 | @paths[start][n] = @paths[start][s] + [n]
99 | seen_thistime << n
100 | end
101 | end
102 | end
103 |
104 | end # seen_unprocessed.each
105 |
106 | # Everything in the old seen_unprocessed is now processed, but none of
107 | # the nodes we added in this iteration are processed, so process them
108 | # next time.
109 | seen_unprocessed = seen_thistime
110 | seen.merge( seen_thistime )
111 |
112 | # If we didn't encounter any new nodes, we're done for this function.
113 | break if seen_thistime.empty?
114 | end # loop
115 | end # @functions.each
116 | end
117 |
118 | def set_allows( calls, maybe_calls, called_by, maybe_called_by )
119 | @allow_calls = calls
120 | @allow_maybe_calls = maybe_calls
121 | @allow_called_by = called_by
122 | @allow_maybe_called_by = maybe_called_by
123 | end
124 |
125 | def get_path( from, to )
126 | if @complete
127 | return [to]
128 | else
129 | return @paths[from][to]
130 | end
131 | end
132 |
133 | end
134 |
135 | class GraphSearchResults
136 | attr_accessor :per_function_avg, :per_function_max
137 | attr_accessor :cutoff_avg, :cutoff_max
138 |
139 | def initialize
140 | @per_function_avg = 0.0
141 | @per_function_max = 0
142 | @cutoff_avg = 0.0
143 | @cutoff_max = 0
144 | end
145 | end
146 |
147 | class GraphSearch
148 |
149 | attr_reader :start, :all, :paths
150 |
151 | def initialize( start_function, all_functions, path_finder )
152 | @start = start_function
153 | @all = all_functions
154 | @paths = path_finder
155 | end
156 |
157 | def executable_prefix_length( sc_goals )
158 | state = [FunctionSequence.new( @start )].to_set
159 | length = 0
160 | nextState = Set.new
161 | sc_goals.each do |goals|
162 | nextState.clear
163 | # FIXME: this probably doesn't have to be n^2
164 | goals.each do |to|
165 | state.each do |from_fs|
166 | path = @paths.get_path( from_fs.last, to )
167 | unless path.nil?
168 | # FIXME: is this making it n^3? would it be better if nextState was
169 | # an array?
170 | nextState << from_fs.advance( path[1..-1] )
171 | break
172 | end
173 | end
174 | end
175 |
176 | if nextState.empty?
177 | break
178 | end
179 | state = nextState.clone
180 | length += 1
181 | end
182 |
183 | return length
184 | end
185 |
186 | def self.get_shellcode_goals( shellcodes, all_functions )
187 | @@cache ||= Hash.new { |h,k| h[k] = Hash.new }
188 | if @@cache[shellcodes] && @@cache[shellcodes][all_functions]
189 | return @@cache[shellcodes][all_functions]
190 | end
191 | sc_goals = shellcodes.map do |shellcode|
192 | shellcode.gadgets.map do |gadget|
193 | all_functions.reject{ |f| !f.filter.allows?( gadget ) }
194 | end
195 | end
196 | @@cache[shellcodes][all_functions] = sc_goals
197 | return sc_goals
198 | end
199 |
200 | def self.run( eval_functions, functions, shellcodes, path_finder )
201 | results = GraphSearchResults.new
202 | sc_goals = self.get_shellcode_goals( shellcodes, functions )
203 | eval_functions.each do |f|
204 | Print::status "Evaluating shellcodes from function #{f.name}"
205 | count = 0
206 | searcher = GraphSearch.new( f, functions, path_finder )
207 | sc_goals.each do |s|
208 | length = searcher.executable_prefix_length( s )
209 | if length == s.length
210 | count += 1
211 | else
212 | results.cutoff_avg += length
213 | results.cutoff_max = [length, results.cutoff_max].max
214 | end
215 | end
216 | results.per_function_avg += count
217 | results.per_function_max = [count, results.per_function_max].max
218 | end
219 | results.per_function_avg /= eval_functions.length
220 | results.cutoff_avg /= (eval_functions.length * shellcodes.length)
221 |
222 | return results
223 | end
224 |
225 | end
226 |
--------------------------------------------------------------------------------
/encoder/lib/Instruction.rb:
--------------------------------------------------------------------------------
1 | require 'lib/InstructionArgument.rb'
2 |
3 | class Instruction
4 |
5 | attr_reader :address, :binary, :code, :mnemonic, :args
6 |
7 | def initialize(address, binary, code)
8 | @address = address
9 | @binary = binary
10 | @code = code
11 |
12 | parts = @code.split(/\s+/)
13 |
14 | # FIXME: we need to look for prefixes, right now prefixes are being treated
15 | # as the mnemonic. We have to look for the prefix byte, then remove any
16 | # textual prefix there may be.
17 |
18 | @mnemonic = parts[0].downcase
19 | rest = parts[1..-1].join(" ")
20 |
21 | # We want to ignore commas inside angle brackets, so replace everything
22 | # inside angle brackets with a numeric "pointer" to the old contents.
23 | # FIXME: This doesn't work if there is more than one group of brackets!
24 | # That's OK, though, since it will never happen in x86.
25 | id = 0
26 | map = {}
27 | if rest =~ /\<(.*)\>/
28 | map[id] = $1
29 | rest.gsub!( /\<.*\>/, "<" + id.to_s + ">" )
30 | id += 1
31 | end
32 |
33 | # Now that the commas that don't separate arguments are gone, we can just
34 | # split on comma.
35 | args = rest.split(",")
36 |
37 | # Go through each argument and replace the angle bracket content identifier
38 | # with the previous content.
39 | args.each do |a|
40 | if a. =~ /\<(.*)\>/
41 | id = $1.to_i
42 | a.gsub!( /\<.*\>/, "<" + map[id] + ">" )
43 | end
44 | end
45 |
46 | # Finally, turn them into InstructionArguments.
47 | @args = args.map { |x| InstructionArgument.new( x ) }
48 |
49 | if @args.length > 3
50 | raise ArgumentError.new( "Instruction [#{code}] has 3 operands." )
51 | end
52 | end
53 |
54 | def hex_opcode
55 | Instruction.bin2hex( @binary )
56 | end
57 |
58 | def to_s
59 | "0x#{@address.to_s(16)}: {#{hex_opcode}} #{@code}"
60 | end
61 |
62 | def Instruction.bin2hex( bin_opcode )
63 | bin_opcode.unpack('H*')[0].upcase
64 | end
65 |
66 | def Instruction.hex2bin( hex_opcode )
67 | [hex_opcode].pack('H*')
68 | end
69 |
70 | end
71 |
--------------------------------------------------------------------------------
/encoder/lib/InstructionArgument.rb:
--------------------------------------------------------------------------------
1 |
2 | class InstructionArgument
3 | # Types:
4 | # :memory_computed - A computed address, e.g. [ecx+4]
5 | # :memory_constant - A constant used as an address, e.g. ds:0xdeadbeef
6 | # :constant - A constant, e.g. 0xdeadbeef
7 | # :register - A register, e.g. eax
8 | attr_reader :raw, :type
9 |
10 | def initialize( argument_code )
11 | @raw = argument_code.strip
12 |
13 | # TODO: extensive testing of this
14 |
15 | # FIXME: this is information-lossy
16 |
17 | # Computed memory address, e.g. [ecx+4]
18 | if @raw =~ /\[(.*)\]/
19 | #FIXME: the type of PTR is lost here
20 | @type = :memory_computed
21 | @address = $1.downcase
22 | # Constant memory address, e.g. DWORD PTR 0xdeadbeef
23 | elsif @raw =~ /PTR\s+(.*)$/
24 | @type = :memory_constant
25 | @address = $1
26 | # Constant memory address, e.g. ds:0xdeadbeef (usually LHS argument)
27 | elsif @raw =~ /(cs|ds|es|fs|gs|ss):0x[0-9a-fA-F]+/
28 | @type = :memory_constant
29 | @address = @raw
30 | # Numeric constant.
31 | elsif @raw =~ /^(?:0x)?([0-9a-fA-F]+)(?:\s*\<(.*)\>)?$/
32 | @type = :constant
33 | @value = $1.to_i(16)
34 | @hint = $2
35 | # Otherwise, assume it's a register.
36 | else
37 | @type = :register
38 | @register = @raw.downcase
39 | end
40 | end
41 |
42 | # Memory
43 | def address
44 | unless [:memory_computed, :memory_constant].include? @type
45 | throw ArgumentError.new( "Not a memory type." )
46 | end
47 | return @address
48 | end
49 |
50 | # Constant
51 | def value
52 | unless @type == :constant
53 | throw ArgumentError.new( "Not a constant type." )
54 | end
55 | return @value
56 | end
57 |
58 | def hint
59 | unless @type == :constant
60 | throw ArgumentError.new( "Not a constant type." )
61 | end
62 | return @hint
63 | end
64 |
65 | # Register
66 | def register
67 | unless @type == :register
68 | throw ArgumentError.new( "Not a register type." )
69 | end
70 | return @register
71 | end
72 |
73 | def to_s
74 | case @type
75 | when :memory_computed
76 | "[" + @address + "]"
77 | when :memory_constant
78 | @address
79 | when :constant
80 | "0x#{@value.to_s(16)} <#{@hint}>"
81 | when :register
82 | @register
83 | end
84 | end
85 |
86 | end
87 |
--------------------------------------------------------------------------------
/encoder/lib/InstructionFilter.rb:
--------------------------------------------------------------------------------
1 | require 'set'
2 | require 'digest'
3 |
4 | class InstructionFilter
5 |
6 | def initialize
7 | @allowed = Set.new
8 | $filter_classes ||= Set.new
9 | end
10 |
11 | def setMostRestrictive( required_instrs )
12 | required_instrs.each do |i|
13 | @allowed << instr_class( i )
14 | end
15 | $filter_classes.merge( @allowed )
16 | end
17 |
18 | def allows?( instrs )
19 | instrs = [instrs].flatten
20 | return instrs.inject( true ) { |res,i| res && @allowed.include?( instr_class( i ) ) }
21 | end
22 |
23 | def instr_class( instr )
24 | raise "Incomplete class!"
25 | end
26 |
27 | end
28 |
29 | class NullInstructionFilter < InstructionFilter
30 |
31 | def instr_class( instr )
32 | ""
33 | end
34 |
35 | def allows?( instrs )
36 | true
37 | end
38 |
39 | end
40 |
41 | class ExactInstructionFilter < InstructionFilter
42 |
43 | def instr_class( instr )
44 | instr.binary
45 | end
46 |
47 | end
48 |
49 | class MnemonicInstructionFilter < InstructionFilter
50 |
51 | def instr_class( instr )
52 | instr.mnemonic
53 | end
54 |
55 | end
56 |
57 | class MnemonicArgTypeInstructionFilter < InstructionFilter
58 |
59 | def instr_class( instr )
60 | instr.mnemonic + "::" + instr.args.map { |a| a.type.to_s }.join( "::" )
61 | end
62 |
63 | end
64 |
65 | class MnemonicArgTypeRegisterInstructionFilter < InstructionFilter
66 |
67 | def instr_class( instr )
68 | instr.mnemonic + "::" + instr.args.map do |a|
69 | if a.type == :register
70 | a.register
71 | else
72 | a.type.to_s
73 | end
74 | end.join( "::" )
75 | end
76 |
77 | end
78 |
79 | class DJB512InstructionFilter < InstructionFilter
80 |
81 | def instr_class( instr )
82 | hash = 5381
83 | instr.binary.each_byte do |b|
84 | hash = ((hash << 5) + hash) + b
85 | end
86 | return hash % 512
87 | end
88 |
89 | end
90 |
91 | class DJB1024InstructionFilter < InstructionFilter
92 |
93 | def instr_class( instr )
94 | hash = 5381
95 | instr.binary.each_byte do |b|
96 | hash = ((hash << 5) + hash) + b
97 | end
98 | return hash % 1024
99 | end
100 |
101 | end
102 |
103 | class DJB4096InstructionFilter < InstructionFilter
104 |
105 | def instr_class( instr )
106 | hash = 5381
107 | instr.binary.each_byte do |b|
108 | hash = ((hash << 5) + hash) + b
109 | end
110 | return hash % 4096
111 | end
112 |
113 | end
114 |
115 |
--------------------------------------------------------------------------------
/encoder/lib/ObjdumpLine.rb:
--------------------------------------------------------------------------------
1 | class ObjdumpLine
2 |
3 | attr_reader :raw, :type
4 |
5 | def initialize(line)
6 | @raw = line
7 |
8 | if @raw =~ /^\s*([0-9a-f]+) \<(.*)\>:\s*$/
9 | @type = :label
10 | @address = $1.to_i(16)
11 | @name = $2
12 | elsif @raw =~ /^\s*([0-9a-f]+):\s*((?:[0-9a-f]{2}\s)+)\s*(.*)$/
13 | @type = :instruction
14 | @address = $1.to_i(16)
15 | @opcode = $2.strip
16 | @instruction = $3
17 | elsif @raw =~ /^(.*):\s+file format\s(\S+)$/
18 | @type = :format
19 | @filename = $1
20 | @format = $2
21 | elsif @raw =~ /^Disassembly of section (.*):$/
22 | @type = :section
23 | @section = $1
24 | elsif @raw =~ /^\s*$/
25 | @type = :empty
26 | else
27 | @type = :unknown
28 | end
29 |
30 | end
31 |
32 | def name
33 | require_type :label
34 | return @name
35 | end
36 |
37 | def address
38 | require_type [:label, :instruction]
39 | return @address
40 | end
41 |
42 | def instruction
43 | require_type :instruction
44 | return @instruction
45 | end
46 |
47 | def opcode
48 | require_type :instruction
49 | return @opcode
50 | end
51 |
52 | def filename
53 | require_type :format
54 | return @filename
55 | end
56 |
57 | def format
58 | require_type :format
59 | return @format
60 | end
61 |
62 | def section
63 | require_type :section
64 | return @section
65 | end
66 |
67 | def to_s
68 | @raw
69 | end
70 |
71 | private
72 |
73 | def require_type( list )
74 | list = [list].flatten
75 | unless list.include? @type
76 | throw ArgumentError.new( "Property not supported by this line type." )
77 | end
78 | end
79 |
80 | end
81 |
--------------------------------------------------------------------------------
/encoder/lib/ObjdumpReader.rb:
--------------------------------------------------------------------------------
1 | require 'lib/ObjdumpLine.rb'
2 | require 'lib/Instruction.rb'
3 |
4 | class ObjdumpReader
5 |
6 | def self.objdump_output( args )
7 | output = nil
8 | args.unshift( "objdump" )
9 | IO.popen( args ) do |obj|
10 | output = obj.read
11 | end
12 | return output
13 | end
14 |
15 | def self.lines_to_instrs( lines )
16 | all_instructions = []
17 |
18 | i = 0
19 | while i < lines.length
20 | instr = [lines[i]]
21 | i += 1
22 | # Sometimes objdump breaks an instruction into two or more lines.
23 | while i < lines.length && lines[i].instruction.empty?
24 | instr << lines[i]
25 | i += 1
26 | end
27 |
28 | address = instr[0].address
29 | code = instr[0].instruction
30 | opcode = instr.map { |x| x.opcode }.join("").gsub(/\s/, "")
31 | binary = Instruction.hex2bin( opcode )
32 |
33 | all_instructions << Instruction.new( address, binary, code )
34 | end
35 |
36 | return all_instructions
37 | end
38 |
39 | def self.x86ELF?( elf_path )
40 | output = self.objdump_output( ["--file-headers", elf_path] )
41 | return output.include? "file format elf32-i386"
42 | end
43 |
44 | def self.x64ELF?( elf_path )
45 | output = self.objdump_output( ["--file-headers", elf_path] )
46 | return output.include? "file format elf64-x86-64"
47 | end
48 |
49 | end
50 |
--------------------------------------------------------------------------------
/encoder/lib/Print.rb:
--------------------------------------------------------------------------------
1 | require 'colorize'
2 |
3 | class Print
4 |
5 | def self.error( msg )
6 | puts "[!] #{self.time} #{msg}".red
7 | end
8 |
9 | def self.warn( msg )
10 | puts "[?] #{self.time} #{msg}".yellow
11 | end
12 |
13 | def self.status( msg )
14 | puts "[+] #{self.time} #{msg}".green
15 | end
16 |
17 | def self.meta( msg )
18 | puts "[M] #{self.time} #{msg}".blue
19 | end
20 |
21 | def self.time
22 | duration = (Time.now() - $stats_start_time).to_i
23 | minutes = duration / 60
24 | seconds = duration % 60
25 | "%02d:%02d" % [minutes, seconds]
26 | end
27 |
28 | end
29 |
--------------------------------------------------------------------------------
/encoder/lib/Results.rb:
--------------------------------------------------------------------------------
1 |
2 | require 'lib/CallGraphGenerator.rb'
3 | require 'lib/Instruction.rb'
4 | require 'lib/Function.rb'
5 | require 'lib/FunctionGraphRenderer.rb'
6 | require 'lib/InstructionFilter.rb'
7 | require 'lib/ExploitPotentialEvaluator.rb'
8 | require 'lib/GadgetFinder.rb'
9 | require 'lib/ObjdumpReader.rb'
10 | require 'lib/Cache.rb'
11 | require 'lib/Results.rb'
12 |
13 | class Results
14 |
15 | attr_accessor :executable_path, :executable_hash
16 | attr_accessor :start_time, :end_time
17 | attr_accessor :filter_class, :filter_classes
18 | attr_accessor :functions_all, :functions_used
19 | attr_accessor :gadgets
20 | attr_accessor :gadgets_per_func_avg, :gadgets_per_func_max
21 | attr_accessor :shellcodes_all_per_func_avg
22 | attr_accessor :shellcodes_all_per_func_max
23 | attr_accessor :shellcodes_all_cutoff_avg, :shellcodes_all_cutoff_max
24 | attr_accessor :shellcodes_reverse_per_func_avg
25 | attr_accessor :shellcodes_reverse_per_func_max
26 | attr_accessor :shellcodes_reverse_cutoff_avg, :shellcodes_reverse_cutoff_max
27 | attr_accessor :shellcodes_complete_per_func_avg
28 | attr_accessor :shellcodes_complete_per_func_max
29 | attr_accessor :shellcodes_complete_cutoff_avg, :shellcodes_complete_cutoff_max
30 | attr_accessor :gadget_samples
31 | attr_accessor :gadget_results
32 |
33 | def to_file( path )
34 | File.open( path, "w" ) do |f|
35 | f.print Marshal.dump( self )
36 | end
37 | end
38 |
39 | def self.from_file( path )
40 | File.open( path, "r" ) do |f|
41 | Marshal.load( f.read )
42 | end
43 | end
44 |
45 | def to_s
46 | unless [@start_time, @end_time].include? nil
47 | execution_seconds = (@end_time - @start_time).to_i
48 | minutes = "%02d" % [execution_seconds / 60]
49 | seconds = "%02d" % [execution_seconds % 60]
50 | else
51 | minutes = "??"
52 | seconds = "??"
53 | end
54 |
55 | output = ""
56 |
57 | output << "Executable: #{@executable_path}\n"
58 | output << "SHA256: #{@executable_hash[0, 32]}\n"
59 | output << " #{@executable_hash[32, 32]}\n"
60 | output << "Filter Class: #{@filter_class}\n"
61 | if @filter_classes
62 | output << "Filter Class Count: #{@filter_classes.length}\n"
63 | end
64 | output << "Execution Time: #{minutes}:#{seconds}\n"
65 | output << "Function Count: #{@functions_all.size}\n"
66 | output << "Used Functions: #{@functions_used.size}\n"
67 | if @gadgets
68 | output << "Gadget Count: #{@gadgets.length}\n"
69 | output << "Average gadgets/f: #{@gadgets_per_func_avg}\n"
70 | output << "Maximum gadgets/f: #{@gadgets_per_func_max}\n"
71 | end
72 | if @shellcodes_all_per_func_avg
73 | output << "Average sc/f (comp): #{@shellcodes_complete_per_func_avg}\n"
74 | output << "Max sc/f (comp): #{@shellcodes_complete_per_func_max}\n"
75 | output << "Average co (comp): #{@shellcodes_complete_cutoff_avg}\n"
76 | output << "Max co (comp): #{@shellcodes_complete_cutoff_max}\n"
77 | output << "Average sc/f (all): #{@shellcodes_all_per_func_avg}\n"
78 | output << "Max sc/f (all): #{@shellcodes_all_per_func_max}\n"
79 | output << "Average co (all): #{@shellcodes_all_cutoff_avg}\n"
80 | output << "Max co (all): #{@shellcodes_all_cutoff_max}\n"
81 | output << "Average sc/f (rev): #{@shellcodes_reverse_per_func_avg}\n"
82 | output << "Max sc/f (rev): #{@shellcodes_reverse_per_func_max}\n"
83 | output << "Average co (rev): #{@shellcodes_reverse_cutoff_avg}\n"
84 | output << "Max co (rev): #{@shellcodes_reverse_cutoff_max}\n"
85 | end
86 | if @gadget_results
87 | [1,3,5,7,9].each do |i|
88 | output << "Avg GS/f (c #{i}): #{@gadget_results[:complete][i].per_function_avg}\n"
89 | output << "Avg GS/f (a #{i}): #{@gadget_results[:all][i].per_function_avg}\n"
90 | output << "Avg GS/f (r #{i}): #{@gadget_results[:reverse][i].per_function_avg}\n"
91 | end
92 | end
93 |
94 | return output
95 | end
96 |
97 | end
98 |
--------------------------------------------------------------------------------
/encoder/lib/Shellcode.rb:
--------------------------------------------------------------------------------
1 | require 'set'
2 | require 'lib/Instruction.rb'
3 | require 'lib/Print.rb'
4 |
5 | class Shellcode
6 | include Enumerable
7 |
8 | attr_reader :gadgets
9 |
10 | def initialize( gadgets = Array.new )
11 | @gadgets = gadgets
12 | end
13 |
14 | def add_sequence( instr_sequence )
15 | if instr_sequence.class != Array
16 | raise ArgumentError.new( "You must provide an ARRAY of instructions." )
17 | end
18 | @gadgets << instr_sequence
19 | end
20 |
21 | def each
22 | @gadgets.each do |gadget|
23 | yield gadget
24 | end
25 | end
26 |
27 | def self.load_from_dir( dir )
28 | shellcodes = Set.new
29 | Dir.foreach( dir ) do |file|
30 | next if ["..", "."].include? file
31 | next unless file.end_with? ".sc"
32 |
33 | begin
34 | path = File.join( dir, file )
35 | serialized = File.open( path, "r" ) { |f| f.read }
36 | shellcodes << Marshal.load( serialized )
37 | rescue
38 | Print::error "Failed to load shellcode #{path}"
39 | end
40 | end
41 | return shellcodes
42 | end
43 |
44 | end
45 |
--------------------------------------------------------------------------------
/player/libs/modernizr.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Modernizr v2.6.2
3 | * www.modernizr.com
4 | *
5 | * Copyright (c) Faruk Ates, Paul Irish, Alex Sexton
6 | * Available under the BSD and MIT licenses: www.modernizr.com/license/
7 | */
8 |
9 | /*
10 | * Modernizr tests which native CSS3 and HTML5 features are available in
11 | * the current UA and makes the results available to you in two ways:
12 | * as properties on a global Modernizr object, and as classes on the
13 | * element. This information allows you to progressively enhance
14 | * your pages with a granular level of control over the experience.
15 | *
16 | * Modernizr has an optional (not included) conditional resource loader
17 | * called Modernizr.load(), based on Yepnope.js (yepnopejs.com).
18 | * To get a build that includes Modernizr.load(), as well as choosing
19 | * which tests to include, go to www.modernizr.com/download/
20 | *
21 | * Authors Faruk Ates, Paul Irish, Alex Sexton
22 | * Contributors Ryan Seddon, Ben Alman
23 | */
24 |
25 | window.Modernizr = (function( window, document, undefined ) {
26 |
27 | var version = '2.6.2',
28 |
29 | Modernizr = {},
30 |
31 | /*>>cssclasses*/
32 | // option for enabling the HTML classes to be added
33 | enableClasses = true,
34 | /*>>cssclasses*/
35 |
36 | docElement = document.documentElement,
37 |
38 | /**
39 | * Create our "modernizr" element that we do most feature tests on.
40 | */
41 | mod = 'modernizr',
42 | modElem = document.createElement(mod),
43 | mStyle = modElem.style,
44 |
45 | /**
46 | * Create the input element for various Web Forms feature tests.
47 | */
48 | inputElem /*>>inputelem*/ = document.createElement('input') /*>>inputelem*/ ,
49 |
50 | /*>>smile*/
51 | smile = ':)',
52 | /*>>smile*/
53 |
54 | toString = {}.toString,
55 |
56 | // TODO :: make the prefixes more granular
57 | /*>>prefixes*/
58 | // List of property values to set for css tests. See ticket #21
59 | prefixes = ' -webkit- -moz- -o- -ms- '.split(' '),
60 | /*>>prefixes*/
61 |
62 | /*>>domprefixes*/
63 | // Following spec is to expose vendor-specific style properties as:
64 | // elem.style.WebkitBorderRadius
65 | // and the following would be incorrect:
66 | // elem.style.webkitBorderRadius
67 |
68 | // Webkit ghosts their properties in lowercase but Opera & Moz do not.
69 | // Microsoft uses a lowercase `ms` instead of the correct `Ms` in IE8+
70 | // erik.eae.net/archives/2008/03/10/21.48.10/
71 |
72 | // More here: github.com/Modernizr/Modernizr/issues/issue/21
73 | omPrefixes = 'Webkit Moz O ms',
74 |
75 | cssomPrefixes = omPrefixes.split(' '),
76 |
77 | domPrefixes = omPrefixes.toLowerCase().split(' '),
78 | /*>>domprefixes*/
79 |
80 | /*>>ns*/
81 | ns = {'svg': 'http://www.w3.org/2000/svg'},
82 | /*>>ns*/
83 |
84 | tests = {},
85 | inputs = {},
86 | attrs = {},
87 |
88 | classes = [],
89 |
90 | slice = classes.slice,
91 |
92 | featureName, // used in testing loop
93 |
94 |
95 | /*>>teststyles*/
96 | // Inject element with style element and some CSS rules
97 | injectElementWithStyles = function( rule, callback, nodes, testnames ) {
98 |
99 | var style, ret, node, docOverflow,
100 | div = document.createElement('div'),
101 | // After page load injecting a fake body doesn't work so check if body exists
102 | body = document.body,
103 | // IE6 and 7 won't return offsetWidth or offsetHeight unless it's in the body element, so we fake it.
104 | fakeBody = body || document.createElement('body');
105 |
106 | if ( parseInt(nodes, 10) ) {
107 | // In order not to give false positives we create a node for each test
108 | // This also allows the method to scale for unspecified uses
109 | while ( nodes-- ) {
110 | node = document.createElement('div');
111 | node.id = testnames ? testnames[nodes] : mod + (nodes + 1);
112 | div.appendChild(node);
113 | }
114 | }
115 |
116 | // '].join('');
122 | div.id = mod;
123 | // IE6 will false positive on some tests due to the style element inside the test div somehow interfering offsetHeight, so insert it into body or fakebody.
124 | // Opera will act all quirky when injecting elements in documentElement when page is served as xml, needs fakebody too. #270
125 | (body ? div : fakeBody).innerHTML += style;
126 | fakeBody.appendChild(div);
127 | if ( !body ) {
128 | //avoid crashing IE8, if background image is used
129 | fakeBody.style.background = '';
130 | //Safari 5.13/5.1.4 OSX stops loading if ::-webkit-scrollbar is used and scrollbars are visible
131 | fakeBody.style.overflow = 'hidden';
132 | docOverflow = docElement.style.overflow;
133 | docElement.style.overflow = 'hidden';
134 | docElement.appendChild(fakeBody);
135 | }
136 |
137 | ret = callback(div, rule);
138 | // If this is done after page load we don't want to remove the body so check if body exists
139 | if ( !body ) {
140 | fakeBody.parentNode.removeChild(fakeBody);
141 | docElement.style.overflow = docOverflow;
142 | } else {
143 | div.parentNode.removeChild(div);
144 | }
145 |
146 | return !!ret;
147 |
148 | },
149 | /*>>teststyles*/
150 |
151 | /*>>mq*/
152 | // adapted from matchMedia polyfill
153 | // by Scott Jehl and Paul Irish
154 | // gist.github.com/786768
155 | testMediaQuery = function( mq ) {
156 |
157 | var matchMedia = window.matchMedia || window.msMatchMedia;
158 | if ( matchMedia ) {
159 | return matchMedia(mq).matches;
160 | }
161 |
162 | var bool;
163 |
164 | injectElementWithStyles('@media ' + mq + ' { #' + mod + ' { position: absolute; } }', function( node ) {
165 | bool = (window.getComputedStyle ?
166 | getComputedStyle(node, null) :
167 | node.currentStyle)['position'] == 'absolute';
168 | });
169 |
170 | return bool;
171 |
172 | },
173 | /*>>mq*/
174 |
175 |
176 | /*>>hasevent*/
177 | //
178 | // isEventSupported determines if a given element supports the given event
179 | // kangax.github.com/iseventsupported/
180 | //
181 | // The following results are known incorrects:
182 | // Modernizr.hasEvent("webkitTransitionEnd", elem) // false negative
183 | // Modernizr.hasEvent("textInput") // in Webkit. github.com/Modernizr/Modernizr/issues/333
184 | // ...
185 | isEventSupported = (function() {
186 |
187 | var TAGNAMES = {
188 | 'select': 'input', 'change': 'input',
189 | 'submit': 'form', 'reset': 'form',
190 | 'error': 'img', 'load': 'img', 'abort': 'img'
191 | };
192 |
193 | function isEventSupported( eventName, element ) {
194 |
195 | element = element || document.createElement(TAGNAMES[eventName] || 'div');
196 | eventName = 'on' + eventName;
197 |
198 | // When using `setAttribute`, IE skips "unload", WebKit skips "unload" and "resize", whereas `in` "catches" those
199 | var isSupported = eventName in element;
200 |
201 | if ( !isSupported ) {
202 | // If it has no `setAttribute` (i.e. doesn't implement Node interface), try generic element
203 | if ( !element.setAttribute ) {
204 | element = document.createElement('div');
205 | }
206 | if ( element.setAttribute && element.removeAttribute ) {
207 | element.setAttribute(eventName, '');
208 | isSupported = is(element[eventName], 'function');
209 |
210 | // If property was created, "remove it" (by setting value to `undefined`)
211 | if ( !is(element[eventName], 'undefined') ) {
212 | element[eventName] = undefined;
213 | }
214 | element.removeAttribute(eventName);
215 | }
216 | }
217 |
218 | element = null;
219 | return isSupported;
220 | }
221 | return isEventSupported;
222 | })(),
223 | /*>>hasevent*/
224 |
225 | // TODO :: Add flag for hasownprop ? didn't last time
226 |
227 | // hasOwnProperty shim by kangax needed for Safari 2.0 support
228 | _hasOwnProperty = ({}).hasOwnProperty, hasOwnProp;
229 |
230 | if ( !is(_hasOwnProperty, 'undefined') && !is(_hasOwnProperty.call, 'undefined') ) {
231 | hasOwnProp = function (object, property) {
232 | return _hasOwnProperty.call(object, property);
233 | };
234 | }
235 | else {
236 | hasOwnProp = function (object, property) { /* yes, this can give false positives/negatives, but most of the time we don't care about those */
237 | return ((property in object) && is(object.constructor.prototype[property], 'undefined'));
238 | };
239 | }
240 |
241 | // Adapted from ES5-shim https://github.com/kriskowal/es5-shim/blob/master/es5-shim.js
242 | // es5.github.com/#x15.3.4.5
243 |
244 | if (!Function.prototype.bind) {
245 | Function.prototype.bind = function bind(that) {
246 |
247 | var target = this;
248 |
249 | if (typeof target != "function") {
250 | throw new TypeError();
251 | }
252 |
253 | var args = slice.call(arguments, 1),
254 | bound = function () {
255 |
256 | if (this instanceof bound) {
257 |
258 | var F = function(){};
259 | F.prototype = target.prototype;
260 | var self = new F();
261 |
262 | var result = target.apply(
263 | self,
264 | args.concat(slice.call(arguments))
265 | );
266 | if (Object(result) === result) {
267 | return result;
268 | }
269 | return self;
270 |
271 | } else {
272 |
273 | return target.apply(
274 | that,
275 | args.concat(slice.call(arguments))
276 | );
277 |
278 | }
279 |
280 | };
281 |
282 | return bound;
283 | };
284 | }
285 |
286 | /**
287 | * setCss applies given styles to the Modernizr DOM node.
288 | */
289 | function setCss( str ) {
290 | mStyle.cssText = str;
291 | }
292 |
293 | /**
294 | * setCssAll extrapolates all vendor-specific css strings.
295 | */
296 | function setCssAll( str1, str2 ) {
297 | return setCss(prefixes.join(str1 + ';') + ( str2 || '' ));
298 | }
299 |
300 | /**
301 | * is returns a boolean for if typeof obj is exactly type.
302 | */
303 | function is( obj, type ) {
304 | return typeof obj === type;
305 | }
306 |
307 | /**
308 | * contains returns a boolean for if substr is found within str.
309 | */
310 | function contains( str, substr ) {
311 | return !!~('' + str).indexOf(substr);
312 | }
313 |
314 | /*>>testprop*/
315 |
316 | // testProps is a generic CSS / DOM property test.
317 |
318 | // In testing support for a given CSS property, it's legit to test:
319 | // `elem.style[styleName] !== undefined`
320 | // If the property is supported it will return an empty string,
321 | // if unsupported it will return undefined.
322 |
323 | // We'll take advantage of this quick test and skip setting a style
324 | // on our modernizr element, but instead just testing undefined vs
325 | // empty string.
326 |
327 | // Because the testing of the CSS property names (with "-", as
328 | // opposed to the camelCase DOM properties) is non-portable and
329 | // non-standard but works in WebKit and IE (but not Gecko or Opera),
330 | // we explicitly reject properties with dashes so that authors
331 | // developing in WebKit or IE first don't end up with
332 | // browser-specific content by accident.
333 |
334 | function testProps( props, prefixed ) {
335 | for ( var i in props ) {
336 | var prop = props[i];
337 | if ( !contains(prop, "-") && mStyle[prop] !== undefined ) {
338 | return prefixed == 'pfx' ? prop : true;
339 | }
340 | }
341 | return false;
342 | }
343 | /*>>testprop*/
344 |
345 | // TODO :: add testDOMProps
346 | /**
347 | * testDOMProps is a generic DOM property test; if a browser supports
348 | * a certain property, it won't return undefined for it.
349 | */
350 | function testDOMProps( props, obj, elem ) {
351 | for ( var i in props ) {
352 | var item = obj[props[i]];
353 | if ( item !== undefined) {
354 |
355 | // return the property name as a string
356 | if (elem === false) return props[i];
357 |
358 | // let's bind a function
359 | if (is(item, 'function')){
360 | // default to autobind unless override
361 | return item.bind(elem || obj);
362 | }
363 |
364 | // return the unbound function or obj or value
365 | return item;
366 | }
367 | }
368 | return false;
369 | }
370 |
371 | /*>>testallprops*/
372 | /**
373 | * testPropsAll tests a list of DOM properties we want to check against.
374 | * We specify literally ALL possible (known and/or likely) properties on
375 | * the element including the non-vendor prefixed one, for forward-
376 | * compatibility.
377 | */
378 | function testPropsAll( prop, prefixed, elem ) {
379 |
380 | var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1),
381 | props = (prop + ' ' + cssomPrefixes.join(ucProp + ' ') + ucProp).split(' ');
382 |
383 | // did they call .prefixed('boxSizing') or are we just testing a prop?
384 | if(is(prefixed, "string") || is(prefixed, "undefined")) {
385 | return testProps(props, prefixed);
386 |
387 | // otherwise, they called .prefixed('requestAnimationFrame', window[, elem])
388 | } else {
389 | props = (prop + ' ' + (domPrefixes).join(ucProp + ' ') + ucProp).split(' ');
390 | return testDOMProps(props, prefixed, elem);
391 | }
392 | }
393 | /*>>testallprops*/
394 |
395 |
396 | /**
397 | * Tests
398 | * -----
399 | */
400 |
401 | // The *new* flexbox
402 | // dev.w3.org/csswg/css3-flexbox
403 |
404 | tests['flexbox'] = function() {
405 | return testPropsAll('flexWrap');
406 | };
407 |
408 | // The *old* flexbox
409 | // www.w3.org/TR/2009/WD-css3-flexbox-20090723/
410 |
411 | tests['flexboxlegacy'] = function() {
412 | return testPropsAll('boxDirection');
413 | };
414 |
415 | // On the S60 and BB Storm, getContext exists, but always returns undefined
416 | // so we actually have to call getContext() to verify
417 | // github.com/Modernizr/Modernizr/issues/issue/97/
418 |
419 | tests['canvas'] = function() {
420 | var elem = document.createElement('canvas');
421 | return !!(elem.getContext && elem.getContext('2d'));
422 | };
423 |
424 | tests['canvastext'] = function() {
425 | return !!(Modernizr['canvas'] && is(document.createElement('canvas').getContext('2d').fillText, 'function'));
426 | };
427 |
428 | // webk.it/70117 is tracking a legit WebGL feature detect proposal
429 |
430 | // We do a soft detect which may false positive in order to avoid
431 | // an expensive context creation: bugzil.la/732441
432 |
433 | tests['webgl'] = function() {
434 | return !!window.WebGLRenderingContext;
435 | };
436 |
437 | /*
438 | * The Modernizr.touch test only indicates if the browser supports
439 | * touch events, which does not necessarily reflect a touchscreen
440 | * device, as evidenced by tablets running Windows 7 or, alas,
441 | * the Palm Pre / WebOS (touch) phones.
442 | *
443 | * Additionally, Chrome (desktop) used to lie about its support on this,
444 | * but that has since been rectified: crbug.com/36415
445 | *
446 | * We also test for Firefox 4 Multitouch Support.
447 | *
448 | * For more info, see: modernizr.github.com/Modernizr/touch.html
449 | */
450 |
451 | tests['touch'] = function() {
452 | var bool;
453 |
454 | if(('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) {
455 | bool = true;
456 | } else {
457 | injectElementWithStyles(['@media (',prefixes.join('touch-enabled),('),mod,')','{#modernizr{top:9px;position:absolute}}'].join(''), function( node ) {
458 | bool = node.offsetTop === 9;
459 | });
460 | }
461 |
462 | return bool;
463 | };
464 |
465 |
466 | // geolocation is often considered a trivial feature detect...
467 | // Turns out, it's quite tricky to get right:
468 | //
469 | // Using !!navigator.geolocation does two things we don't want. It:
470 | // 1. Leaks memory in IE9: github.com/Modernizr/Modernizr/issues/513
471 | // 2. Disables page caching in WebKit: webk.it/43956
472 | //
473 | // Meanwhile, in Firefox < 8, an about:config setting could expose
474 | // a false positive that would throw an exception: bugzil.la/688158
475 |
476 | tests['geolocation'] = function() {
477 | return 'geolocation' in navigator;
478 | };
479 |
480 |
481 | tests['postmessage'] = function() {
482 | return !!window.postMessage;
483 | };
484 |
485 |
486 | // Chrome incognito mode used to throw an exception when using openDatabase
487 | // It doesn't anymore.
488 | tests['websqldatabase'] = function() {
489 | return !!window.openDatabase;
490 | };
491 |
492 | // Vendors had inconsistent prefixing with the experimental Indexed DB:
493 | // - Webkit's implementation is accessible through webkitIndexedDB
494 | // - Firefox shipped moz_indexedDB before FF4b9, but since then has been mozIndexedDB
495 | // For speed, we don't test the legacy (and beta-only) indexedDB
496 | tests['indexedDB'] = function() {
497 | return !!testPropsAll("indexedDB", window);
498 | };
499 |
500 | // documentMode logic from YUI to filter out IE8 Compat Mode
501 | // which false positives.
502 | tests['hashchange'] = function() {
503 | return isEventSupported('hashchange', window) && (document.documentMode === undefined || document.documentMode > 7);
504 | };
505 |
506 | // Per 1.6:
507 | // This used to be Modernizr.historymanagement but the longer
508 | // name has been deprecated in favor of a shorter and property-matching one.
509 | // The old API is still available in 1.6, but as of 2.0 will throw a warning,
510 | // and in the first release thereafter disappear entirely.
511 | tests['history'] = function() {
512 | return !!(window.history && history.pushState);
513 | };
514 |
515 | tests['draganddrop'] = function() {
516 | var div = document.createElement('div');
517 | return ('draggable' in div) || ('ondragstart' in div && 'ondrop' in div);
518 | };
519 |
520 | // FF3.6 was EOL'ed on 4/24/12, but the ESR version of FF10
521 | // will be supported until FF19 (2/12/13), at which time, ESR becomes FF17.
522 | // FF10 still uses prefixes, so check for it until then.
523 | // for more ESR info, see: mozilla.org/en-US/firefox/organizations/faq/
524 | tests['websockets'] = function() {
525 | return 'WebSocket' in window || 'MozWebSocket' in window;
526 | };
527 |
528 |
529 | // css-tricks.com/rgba-browser-support/
530 | tests['rgba'] = function() {
531 | // Set an rgba() color and check the returned value
532 |
533 | setCss('background-color:rgba(150,255,150,.5)');
534 |
535 | return contains(mStyle.backgroundColor, 'rgba');
536 | };
537 |
538 | tests['hsla'] = function() {
539 | // Same as rgba(), in fact, browsers re-map hsla() to rgba() internally,
540 | // except IE9 who retains it as hsla
541 |
542 | setCss('background-color:hsla(120,40%,100%,.5)');
543 |
544 | return contains(mStyle.backgroundColor, 'rgba') || contains(mStyle.backgroundColor, 'hsla');
545 | };
546 |
547 | tests['multiplebgs'] = function() {
548 | // Setting multiple images AND a color on the background shorthand property
549 | // and then querying the style.background property value for the number of
550 | // occurrences of "url(" is a reliable method for detecting ACTUAL support for this!
551 |
552 | setCss('background:url(https://),url(https://),red url(https://)');
553 |
554 | // If the UA supports multiple backgrounds, there should be three occurrences
555 | // of the string "url(" in the return value for elemStyle.background
556 |
557 | return (/(url\s*\(.*?){3}/).test(mStyle.background);
558 | };
559 |
560 |
561 |
562 | // this will false positive in Opera Mini
563 | // github.com/Modernizr/Modernizr/issues/396
564 |
565 | tests['backgroundsize'] = function() {
566 | return testPropsAll('backgroundSize');
567 | };
568 |
569 | tests['borderimage'] = function() {
570 | return testPropsAll('borderImage');
571 | };
572 |
573 |
574 | // Super comprehensive table about all the unique implementations of
575 | // border-radius: muddledramblings.com/table-of-css3-border-radius-compliance
576 |
577 | tests['borderradius'] = function() {
578 | return testPropsAll('borderRadius');
579 | };
580 |
581 | // WebOS unfortunately false positives on this test.
582 | tests['boxshadow'] = function() {
583 | return testPropsAll('boxShadow');
584 | };
585 |
586 | // FF3.0 will false positive on this test
587 | tests['textshadow'] = function() {
588 | return document.createElement('div').style.textShadow === '';
589 | };
590 |
591 |
592 | tests['opacity'] = function() {
593 | // Browsers that actually have CSS Opacity implemented have done so
594 | // according to spec, which means their return values are within the
595 | // range of [0.0,1.0] - including the leading zero.
596 |
597 | setCssAll('opacity:.55');
598 |
599 | // The non-literal . in this regex is intentional:
600 | // German Chrome returns this value as 0,55
601 | // github.com/Modernizr/Modernizr/issues/#issue/59/comment/516632
602 | return (/^0.55$/).test(mStyle.opacity);
603 | };
604 |
605 |
606 | // Note, Android < 4 will pass this test, but can only animate
607 | // a single property at a time
608 | // daneden.me/2011/12/putting-up-with-androids-bullshit/
609 | tests['cssanimations'] = function() {
610 | return testPropsAll('animationName');
611 | };
612 |
613 |
614 | tests['csscolumns'] = function() {
615 | return testPropsAll('columnCount');
616 | };
617 |
618 |
619 | tests['cssgradients'] = function() {
620 | /**
621 | * For CSS Gradients syntax, please see:
622 | * webkit.org/blog/175/introducing-css-gradients/
623 | * developer.mozilla.org/en/CSS/-moz-linear-gradient
624 | * developer.mozilla.org/en/CSS/-moz-radial-gradient
625 | * dev.w3.org/csswg/css3-images/#gradients-
626 | */
627 |
628 | var str1 = 'background-image:',
629 | str2 = 'gradient(linear,left top,right bottom,from(#9f9),to(white));',
630 | str3 = 'linear-gradient(left top,#9f9, white);';
631 |
632 | setCss(
633 | // legacy webkit syntax (FIXME: remove when syntax not in use anymore)
634 | (str1 + '-webkit- '.split(' ').join(str2 + str1) +
635 | // standard syntax // trailing 'background-image:'
636 | prefixes.join(str3 + str1)).slice(0, -str1.length)
637 | );
638 |
639 | return contains(mStyle.backgroundImage, 'gradient');
640 | };
641 |
642 |
643 | tests['cssreflections'] = function() {
644 | return testPropsAll('boxReflect');
645 | };
646 |
647 |
648 | tests['csstransforms'] = function() {
649 | return !!testPropsAll('transform');
650 | };
651 |
652 |
653 | tests['csstransforms3d'] = function() {
654 |
655 | var ret = !!testPropsAll('perspective');
656 |
657 | // Webkit's 3D transforms are passed off to the browser's own graphics renderer.
658 | // It works fine in Safari on Leopard and Snow Leopard, but not in Chrome in
659 | // some conditions. As a result, Webkit typically recognizes the syntax but
660 | // will sometimes throw a false positive, thus we must do a more thorough check:
661 | if ( ret && 'webkitPerspective' in docElement.style ) {
662 |
663 | // Webkit allows this media query to succeed only if the feature is enabled.
664 | // `@media (transform-3d),(-webkit-transform-3d){ ... }`
665 | injectElementWithStyles('@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}', function( node, rule ) {
666 | ret = node.offsetLeft === 9 && node.offsetHeight === 3;
667 | });
668 | }
669 | return ret;
670 | };
671 |
672 |
673 | tests['csstransitions'] = function() {
674 | return testPropsAll('transition');
675 | };
676 |
677 |
678 | /*>>fontface*/
679 | // @font-face detection routine by Diego Perini
680 | // javascript.nwbox.com/CSSSupport/
681 |
682 | // false positives:
683 | // WebOS github.com/Modernizr/Modernizr/issues/342
684 | // WP7 github.com/Modernizr/Modernizr/issues/538
685 | tests['fontface'] = function() {
686 | var bool;
687 |
688 | injectElementWithStyles('@font-face {font-family:"font";src:url("https://")}', function( node, rule ) {
689 | var style = document.getElementById('smodernizr'),
690 | sheet = style.sheet || style.styleSheet,
691 | cssText = sheet ? (sheet.cssRules && sheet.cssRules[0] ? sheet.cssRules[0].cssText : sheet.cssText || '') : '';
692 |
693 | bool = /src/i.test(cssText) && cssText.indexOf(rule.split(' ')[0]) === 0;
694 | });
695 |
696 | return bool;
697 | };
698 | /*>>fontface*/
699 |
700 | // CSS generated content detection
701 | tests['generatedcontent'] = function() {
702 | var bool;
703 |
704 | injectElementWithStyles(['#',mod,'{font:0/0 a}#',mod,':after{content:"',smile,'";visibility:hidden;font:3px/1 a}'].join(''), function( node ) {
705 | bool = node.offsetHeight >= 3;
706 | });
707 |
708 | return bool;
709 | };
710 |
711 |
712 |
713 | // These tests evaluate support of the video/audio elements, as well as
714 | // testing what types of content they support.
715 | //
716 | // We're using the Boolean constructor here, so that we can extend the value
717 | // e.g. Modernizr.video // true
718 | // Modernizr.video.ogg // 'probably'
719 | //
720 | // Codec values from : github.com/NielsLeenheer/html5test/blob/9106a8/index.html#L845
721 | // thx to NielsLeenheer and zcorpan
722 |
723 | // Note: in some older browsers, "no" was a return value instead of empty string.
724 | // It was live in FF3.5.0 and 3.5.1, but fixed in 3.5.2
725 | // It was also live in Safari 4.0.0 - 4.0.4, but fixed in 4.0.5
726 |
727 | tests['video'] = function() {
728 | var elem = document.createElement('video'),
729 | bool = false;
730 |
731 | // IE9 Running on Windows Server SKU can cause an exception to be thrown, bug #224
732 | try {
733 | if ( bool = !!elem.canPlayType ) {
734 | bool = new Boolean(bool);
735 | bool.ogg = elem.canPlayType('video/ogg; codecs="theora"') .replace(/^no$/,'');
736 |
737 | // Without QuickTime, this value will be `undefined`. github.com/Modernizr/Modernizr/issues/546
738 | bool.h264 = elem.canPlayType('video/mp4; codecs="avc1.42E01E"') .replace(/^no$/,'');
739 |
740 | bool.webm = elem.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/,'');
741 | }
742 |
743 | } catch(e) { }
744 |
745 | return bool;
746 | };
747 |
748 | tests['audio'] = function() {
749 | var elem = document.createElement('audio'),
750 | bool = false;
751 |
752 | try {
753 | if ( bool = !!elem.canPlayType ) {
754 | bool = new Boolean(bool);
755 | bool.ogg = elem.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,'');
756 | bool.mp3 = elem.canPlayType('audio/mpeg;') .replace(/^no$/,'');
757 |
758 | // Mimetypes accepted:
759 | // developer.mozilla.org/En/Media_formats_supported_by_the_audio_and_video_elements
760 | // bit.ly/iphoneoscodecs
761 | bool.wav = elem.canPlayType('audio/wav; codecs="1"') .replace(/^no$/,'');
762 | bool.m4a = ( elem.canPlayType('audio/x-m4a;') ||
763 | elem.canPlayType('audio/aac;')) .replace(/^no$/,'');
764 | }
765 | } catch(e) { }
766 |
767 | return bool;
768 | };
769 |
770 |
771 | // In FF4, if disabled, window.localStorage should === null.
772 |
773 | // Normally, we could not test that directly and need to do a
774 | // `('localStorage' in window) && ` test first because otherwise Firefox will
775 | // throw bugzil.la/365772 if cookies are disabled
776 |
777 | // Also in iOS5 Private Browsing mode, attempting to use localStorage.setItem
778 | // will throw the exception:
779 | // QUOTA_EXCEEDED_ERRROR DOM Exception 22.
780 | // Peculiarly, getItem and removeItem calls do not throw.
781 |
782 | // Because we are forced to try/catch this, we'll go aggressive.
783 |
784 | // Just FWIW: IE8 Compat mode supports these features completely:
785 | // www.quirksmode.org/dom/html5.html
786 | // But IE8 doesn't support either with local files
787 |
788 | tests['localstorage'] = function() {
789 | try {
790 | localStorage.setItem(mod, mod);
791 | localStorage.removeItem(mod);
792 | return true;
793 | } catch(e) {
794 | return false;
795 | }
796 | };
797 |
798 | tests['sessionstorage'] = function() {
799 | try {
800 | sessionStorage.setItem(mod, mod);
801 | sessionStorage.removeItem(mod);
802 | return true;
803 | } catch(e) {
804 | return false;
805 | }
806 | };
807 |
808 |
809 | tests['webworkers'] = function() {
810 | return !!window.Worker;
811 | };
812 |
813 |
814 | tests['applicationcache'] = function() {
815 | return !!window.applicationCache;
816 | };
817 |
818 |
819 | // Thanks to Erik Dahlstrom
820 | tests['svg'] = function() {
821 | return !!document.createElementNS && !!document.createElementNS(ns.svg, 'svg').createSVGRect;
822 | };
823 |
824 | // specifically for SVG inline in HTML, not within XHTML
825 | // test page: paulirish.com/demo/inline-svg
826 | tests['inlinesvg'] = function() {
827 | var div = document.createElement('div');
828 | div.innerHTML = '';
829 | return (div.firstChild && div.firstChild.namespaceURI) == ns.svg;
830 | };
831 |
832 | // SVG SMIL animation
833 | tests['smil'] = function() {
834 | return !!document.createElementNS && /SVGAnimate/.test(toString.call(document.createElementNS(ns.svg, 'animate')));
835 | };
836 |
837 | // This test is only for clip paths in SVG proper, not clip paths on HTML content
838 | // demo: srufaculty.sru.edu/david.dailey/svg/newstuff/clipPath4.svg
839 |
840 | // However read the comments to dig into applying SVG clippaths to HTML content here:
841 | // github.com/Modernizr/Modernizr/issues/213#issuecomment-1149491
842 | tests['svgclippaths'] = function() {
843 | return !!document.createElementNS && /SVGClipPath/.test(toString.call(document.createElementNS(ns.svg, 'clipPath')));
844 | };
845 |
846 | /*>>webforms*/
847 | // input features and input types go directly onto the ret object, bypassing the tests loop.
848 | // Hold this guy to execute in a moment.
849 | function webforms() {
850 | /*>>input*/
851 | // Run through HTML5's new input attributes to see if the UA understands any.
852 | // We're using f which is the element created early on
853 | // Mike Taylr has created a comprehensive resource for testing these attributes
854 | // when applied to all input types:
855 | // miketaylr.com/code/input-type-attr.html
856 | // spec: www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html#input-type-attr-summary
857 |
858 | // Only input placeholder is tested while textarea's placeholder is not.
859 | // Currently Safari 4 and Opera 11 have support only for the input placeholder
860 | // Both tests are available in feature-detects/forms-placeholder.js
861 | Modernizr['input'] = (function( props ) {
862 | for ( var i = 0, len = props.length; i < len; i++ ) {
863 | attrs[ props[i] ] = !!(props[i] in inputElem);
864 | }
865 | if (attrs.list){
866 | // safari false positive's on datalist: webk.it/74252
867 | // see also github.com/Modernizr/Modernizr/issues/146
868 | attrs.list = !!(document.createElement('datalist') && window.HTMLDataListElement);
869 | }
870 | return attrs;
871 | })('autocomplete autofocus list placeholder max min multiple pattern required step'.split(' '));
872 | /*>>input*/
873 |
874 | /*>>inputtypes*/
875 | // Run through HTML5's new input types to see if the UA understands any.
876 | // This is put behind the tests runloop because it doesn't return a
877 | // true/false like all the other tests; instead, it returns an object
878 | // containing each input type with its corresponding true/false value
879 |
880 | // Big thanks to @miketaylr for the html5 forms expertise. miketaylr.com/
881 | Modernizr['inputtypes'] = (function(props) {
882 |
883 | for ( var i = 0, bool, inputElemType, defaultView, len = props.length; i < len; i++ ) {
884 |
885 | inputElem.setAttribute('type', inputElemType = props[i]);
886 | bool = inputElem.type !== 'text';
887 |
888 | // We first check to see if the type we give it sticks..
889 | // If the type does, we feed it a textual value, which shouldn't be valid.
890 | // If the value doesn't stick, we know there's input sanitization which infers a custom UI
891 | if ( bool ) {
892 |
893 | inputElem.value = smile;
894 | inputElem.style.cssText = 'position:absolute;visibility:hidden;';
895 |
896 | if ( /^range$/.test(inputElemType) && inputElem.style.WebkitAppearance !== undefined ) {
897 |
898 | docElement.appendChild(inputElem);
899 | defaultView = document.defaultView;
900 |
901 | // Safari 2-4 allows the smiley as a value, despite making a slider
902 | bool = defaultView.getComputedStyle &&
903 | defaultView.getComputedStyle(inputElem, null).WebkitAppearance !== 'textfield' &&
904 | // Mobile android web browser has false positive, so must
905 | // check the height to see if the widget is actually there.
906 | (inputElem.offsetHeight !== 0);
907 |
908 | docElement.removeChild(inputElem);
909 |
910 | } else if ( /^(search|tel)$/.test(inputElemType) ){
911 | // Spec doesn't define any special parsing or detectable UI
912 | // behaviors so we pass these through as true
913 |
914 | // Interestingly, opera fails the earlier test, so it doesn't
915 | // even make it here.
916 |
917 | } else if ( /^(url|email)$/.test(inputElemType) ) {
918 | // Real url and email support comes with prebaked validation.
919 | bool = inputElem.checkValidity && inputElem.checkValidity() === false;
920 |
921 | } else {
922 | // If the upgraded input compontent rejects the :) text, we got a winner
923 | bool = inputElem.value != smile;
924 | }
925 | }
926 |
927 | inputs[ props[i] ] = !!bool;
928 | }
929 | return inputs;
930 | })('search tel url email datetime date month week time datetime-local number range color'.split(' '));
931 | /*>>inputtypes*/
932 | }
933 | /*>>webforms*/
934 |
935 |
936 | // End of test definitions
937 | // -----------------------
938 |
939 |
940 |
941 | // Run through all tests and detect their support in the current UA.
942 | // todo: hypothetically we could be doing an array of tests and use a basic loop here.
943 | for ( var feature in tests ) {
944 | if ( hasOwnProp(tests, feature) ) {
945 | // run the test, throw the return value into the Modernizr,
946 | // then based on that boolean, define an appropriate className
947 | // and push it into an array of classes we'll join later.
948 | featureName = feature.toLowerCase();
949 | Modernizr[featureName] = tests[feature]();
950 |
951 | classes.push((Modernizr[featureName] ? '' : 'no-') + featureName);
952 | }
953 | }
954 |
955 | /*>>webforms*/
956 | // input tests need to run.
957 | Modernizr.input || webforms();
958 | /*>>webforms*/
959 |
960 |
961 | /**
962 | * addTest allows the user to define their own feature tests
963 | * the result will be added onto the Modernizr object,
964 | * as well as an appropriate className set on the html element
965 | *
966 | * @param feature - String naming the feature
967 | * @param test - Function returning true if feature is supported, false if not
968 | */
969 | Modernizr.addTest = function ( feature, test ) {
970 | if ( typeof feature == 'object' ) {
971 | for ( var key in feature ) {
972 | if ( hasOwnProp( feature, key ) ) {
973 | Modernizr.addTest( key, feature[ key ] );
974 | }
975 | }
976 | } else {
977 |
978 | feature = feature.toLowerCase();
979 |
980 | if ( Modernizr[feature] !== undefined ) {
981 | // we're going to quit if you're trying to overwrite an existing test
982 | // if we were to allow it, we'd do this:
983 | // var re = new RegExp("\\b(no-)?" + feature + "\\b");
984 | // docElement.className = docElement.className.replace( re, '' );
985 | // but, no rly, stuff 'em.
986 | return Modernizr;
987 | }
988 |
989 | test = typeof test == 'function' ? test() : test;
990 |
991 | if (typeof enableClasses !== "undefined" && enableClasses) {
992 | docElement.className += ' ' + (test ? '' : 'no-') + feature;
993 | }
994 | Modernizr[feature] = test;
995 |
996 | }
997 |
998 | return Modernizr; // allow chaining.
999 | };
1000 |
1001 |
1002 | // Reset modElem.cssText to nothing to reduce memory footprint.
1003 | setCss('');
1004 | modElem = inputElem = null;
1005 |
1006 | /*>>shiv*/
1007 | /*! HTML5 Shiv v3.6.1 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed */
1008 | ;(function(window, document) {
1009 | /*jshint evil:true */
1010 | /** Preset options */
1011 | var options = window.html5 || {};
1012 |
1013 | /** Used to skip problem elements */
1014 | var reSkip = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i;
1015 |
1016 | /** Not all elements can be cloned in IE **/
1017 | var saveClones = /^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i;
1018 |
1019 | /** Detect whether the browser supports default html5 styles */
1020 | var supportsHtml5Styles;
1021 |
1022 | /** Name of the expando, to work with multiple documents or to re-shiv one document */
1023 | var expando = '_html5shiv';
1024 |
1025 | /** The id for the the documents expando */
1026 | var expanID = 0;
1027 |
1028 | /** Cached data for each document */
1029 | var expandoData = {};
1030 |
1031 | /** Detect whether the browser supports unknown elements */
1032 | var supportsUnknownElements;
1033 |
1034 | (function() {
1035 | try {
1036 | var a = document.createElement('a');
1037 | a.innerHTML = '';
1038 | //if the hidden property is implemented we can assume, that the browser supports basic HTML5 Styles
1039 | supportsHtml5Styles = ('hidden' in a);
1040 |
1041 | supportsUnknownElements = a.childNodes.length == 1 || (function() {
1042 | // assign a false positive if unable to shiv
1043 | (document.createElement)('a');
1044 | var frag = document.createDocumentFragment();
1045 | return (
1046 | typeof frag.cloneNode == 'undefined' ||
1047 | typeof frag.createDocumentFragment == 'undefined' ||
1048 | typeof frag.createElement == 'undefined'
1049 | );
1050 | }());
1051 | } catch(e) {
1052 | supportsHtml5Styles = true;
1053 | supportsUnknownElements = true;
1054 | }
1055 |
1056 | }());
1057 |
1058 | /*--------------------------------------------------------------------------*/
1059 |
1060 | /**
1061 | * Creates a style sheet with the given CSS text and adds it to the document.
1062 | * @private
1063 | * @param {Document} ownerDocument The document.
1064 | * @param {String} cssText The CSS text.
1065 | * @returns {StyleSheet} The style element.
1066 | */
1067 | function addStyleSheet(ownerDocument, cssText) {
1068 | var p = ownerDocument.createElement('p'),
1069 | parent = ownerDocument.getElementsByTagName('head')[0] || ownerDocument.documentElement;
1070 |
1071 | p.innerHTML = 'x';
1072 | return parent.insertBefore(p.lastChild, parent.firstChild);
1073 | }
1074 |
1075 | /**
1076 | * Returns the value of `html5.elements` as an array.
1077 | * @private
1078 | * @returns {Array} An array of shived element node names.
1079 | */
1080 | function getElements() {
1081 | var elements = html5.elements;
1082 | return typeof elements == 'string' ? elements.split(' ') : elements;
1083 | }
1084 |
1085 | /**
1086 | * Returns the data associated to the given document
1087 | * @private
1088 | * @param {Document} ownerDocument The document.
1089 | * @returns {Object} An object of data.
1090 | */
1091 | function getExpandoData(ownerDocument) {
1092 | var data = expandoData[ownerDocument[expando]];
1093 | if (!data) {
1094 | data = {};
1095 | expanID++;
1096 | ownerDocument[expando] = expanID;
1097 | expandoData[expanID] = data;
1098 | }
1099 | return data;
1100 | }
1101 |
1102 | /**
1103 | * returns a shived element for the given nodeName and document
1104 | * @memberOf html5
1105 | * @param {String} nodeName name of the element
1106 | * @param {Document} ownerDocument The context document.
1107 | * @returns {Object} The shived element.
1108 | */
1109 | function createElement(nodeName, ownerDocument, data){
1110 | if (!ownerDocument) {
1111 | ownerDocument = document;
1112 | }
1113 | if(supportsUnknownElements){
1114 | return ownerDocument.createElement(nodeName);
1115 | }
1116 | if (!data) {
1117 | data = getExpandoData(ownerDocument);
1118 | }
1119 | var node;
1120 |
1121 | if (data.cache[nodeName]) {
1122 | node = data.cache[nodeName].cloneNode();
1123 | } else if (saveClones.test(nodeName)) {
1124 | node = (data.cache[nodeName] = data.createElem(nodeName)).cloneNode();
1125 | } else {
1126 | node = data.createElem(nodeName);
1127 | }
1128 |
1129 | // Avoid adding some elements to fragments in IE < 9 because
1130 | // * Attributes like `name` or `type` cannot be set/changed once an element
1131 | // is inserted into a document/fragment
1132 | // * Link elements with `src` attributes that are inaccessible, as with
1133 | // a 403 response, will cause the tab/window to crash
1134 | // * Script elements appended to fragments will execute when their `src`
1135 | // or `text` property is set
1136 | return node.canHaveChildren && !reSkip.test(nodeName) ? data.frag.appendChild(node) : node;
1137 | }
1138 |
1139 | /**
1140 | * returns a shived DocumentFragment for the given document
1141 | * @memberOf html5
1142 | * @param {Document} ownerDocument The context document.
1143 | * @returns {Object} The shived DocumentFragment.
1144 | */
1145 | function createDocumentFragment(ownerDocument, data){
1146 | if (!ownerDocument) {
1147 | ownerDocument = document;
1148 | }
1149 | if(supportsUnknownElements){
1150 | return ownerDocument.createDocumentFragment();
1151 | }
1152 | data = data || getExpandoData(ownerDocument);
1153 | var clone = data.frag.cloneNode(),
1154 | i = 0,
1155 | elems = getElements(),
1156 | l = elems.length;
1157 | for(;i>shiv*/
1296 |
1297 | // Assign private properties to the return object with prefix
1298 | Modernizr._version = version;
1299 |
1300 | // expose these for the plugin API. Look in the source for how to join() them against your input
1301 | /*>>prefixes*/
1302 | Modernizr._prefixes = prefixes;
1303 | /*>>prefixes*/
1304 | /*>>domprefixes*/
1305 | Modernizr._domPrefixes = domPrefixes;
1306 | Modernizr._cssomPrefixes = cssomPrefixes;
1307 | /*>>domprefixes*/
1308 |
1309 | /*>>mq*/
1310 | // Modernizr.mq tests a given media query, live against the current state of the window
1311 | // A few important notes:
1312 | // * If a browser does not support media queries at all (eg. oldIE) the mq() will always return false
1313 | // * A max-width or orientation query will be evaluated against the current state, which may change later.
1314 | // * You must specify values. Eg. If you are testing support for the min-width media query use:
1315 | // Modernizr.mq('(min-width:0)')
1316 | // usage:
1317 | // Modernizr.mq('only screen and (max-width:768)')
1318 | Modernizr.mq = testMediaQuery;
1319 | /*>>mq*/
1320 |
1321 | /*>>hasevent*/
1322 | // Modernizr.hasEvent() detects support for a given event, with an optional element to test on
1323 | // Modernizr.hasEvent('gesturestart', elem)
1324 | Modernizr.hasEvent = isEventSupported;
1325 | /*>>hasevent*/
1326 |
1327 | /*>>testprop*/
1328 | // Modernizr.testProp() investigates whether a given style property is recognized
1329 | // Note that the property names must be provided in the camelCase variant.
1330 | // Modernizr.testProp('pointerEvents')
1331 | Modernizr.testProp = function(prop){
1332 | return testProps([prop]);
1333 | };
1334 | /*>>testprop*/
1335 |
1336 | /*>>testallprops*/
1337 | // Modernizr.testAllProps() investigates whether a given style property,
1338 | // or any of its vendor-prefixed variants, is recognized
1339 | // Note that the property names must be provided in the camelCase variant.
1340 | // Modernizr.testAllProps('boxSizing')
1341 | Modernizr.testAllProps = testPropsAll;
1342 | /*>>testallprops*/
1343 |
1344 |
1345 | /*>>teststyles*/
1346 | // Modernizr.testStyles() allows you to add custom styles to the document and test an element afterwards
1347 | // Modernizr.testStyles('#modernizr { position:absolute }', function(elem, rule){ ... })
1348 | Modernizr.testStyles = injectElementWithStyles;
1349 | /*>>teststyles*/
1350 |
1351 |
1352 | /*>>prefixed*/
1353 | // Modernizr.prefixed() returns the prefixed or nonprefixed property name variant of your input
1354 | // Modernizr.prefixed('boxSizing') // 'MozBoxSizing'
1355 |
1356 | // Properties must be passed as dom-style camelcase, rather than `box-sizing` hypentated style.
1357 | // Return values will also be the camelCase variant, if you need to translate that to hypenated style use:
1358 | //
1359 | // str.replace(/([A-Z])/g, function(str,m1){ return '-' + m1.toLowerCase(); }).replace(/^ms-/,'-ms-');
1360 |
1361 | // If you're trying to ascertain which transition end event to bind to, you might do something like...
1362 | //
1363 | // var transEndEventNames = {
1364 | // 'WebkitTransition' : 'webkitTransitionEnd',
1365 | // 'MozTransition' : 'transitionend',
1366 | // 'OTransition' : 'oTransitionEnd',
1367 | // 'msTransition' : 'MSTransitionEnd',
1368 | // 'transition' : 'transitionend'
1369 | // },
1370 | // transEndEventName = transEndEventNames[ Modernizr.prefixed('transition') ];
1371 |
1372 | Modernizr.prefixed = function(prop, obj, elem){
1373 | if(!obj) {
1374 | return testPropsAll(prop, 'pfx');
1375 | } else {
1376 | // Testing DOM property e.g. Modernizr.prefixed('requestAnimationFrame', window) // 'mozRequestAnimationFrame'
1377 | return testPropsAll(prop, obj, elem);
1378 | }
1379 | };
1380 | /*>>prefixed*/
1381 |
1382 |
1383 | /*>>cssclasses*/
1384 | // Remove "no-js" class from element, if it exists:
1385 | docElement.className = docElement.className.replace(/(^|\s)no-js(\s|$)/, '$1$2') +
1386 |
1387 | // Add the new classes to the element.
1388 | (enableClasses ? ' js ' + classes.join(' ') : '');
1389 | /*>>cssclasses*/
1390 |
1391 | return Modernizr;
1392 |
1393 | })(this, this.document);
1394 |
--------------------------------------------------------------------------------
/player/out.json:
--------------------------------------------------------------------------------
1 | {"title":"hello","script":[{"f":5,"i":0},{"f":5,"i":1},{"f":5,"i":2},{"f":5,"i":3},{"f":5,"i":4},{"f":5,"i":5},{"f":5,"i":6},{"f":5,"i":7},{"f":5,"i":8},{"f":5,"i":9},{"f":5,"i":10},{"f":5,"i":11},{"f":5,"i":12},{"f":4,"i":0},{"f":4,"i":1},{"f":4,"i":2},{"f":1,"i":0},{"f":1,"i":1},{"x":1},{"f":14,"i":0},{"f":14,"i":1},{"f":14,"i":2},{"f":14,"i":3},{"f":14,"i":4},{"f":14,"i":5},{"f":6,"i":0},{"f":6,"i":1},{"f":14,"i":6},{"f":14,"i":7},{"f":14,"i":8},{"f":14,"i":9},{"f":14,"i":10},{"f":0,"i":0},{"f":0,"i":1},{"f":0,"i":2},{"f":6,"i":0},{"f":6,"i":1},{"f":0,"i":3},{"f":0,"i":4},{"f":0,"i":5},{"f":0,"i":6},{"f":0,"i":8},{"f":0,"i":9},{"f":0,"i":10},{"f":14,"i":11},{"f":14,"i":12},{"f":14,"i":13},{"f":14,"i":14},{"f":14,"i":15},{"f":14,"i":16},{"f":14,"i":17},{"f":14,"i":18},{"f":14,"i":19},{"f":14,"i":20},{"f":14,"i":21},{"f":10,"i":0},{"f":10,"i":1},{"f":10,"i":2},{"f":10,"i":3},{"f":10,"i":4},{"f":8,"i":0},{"f":8,"i":1},{"f":8,"i":2},{"f":8,"i":3},{"f":8,"i":4},{"f":8,"i":5},{"f":8,"i":6},{"f":8,"i":7},{"f":8,"i":19},{"f":14,"i":22},{"f":14,"i":23},{"f":14,"i":24},{"f":14,"i":25},{"f":14,"i":26},{"f":14,"i":27},{"f":14,"i":28},{"f":14,"i":29},{"f":14,"i":30},{"f":14,"i":31},{"x":1},{"f":13,"i":0},{"f":13,"i":1},{"f":13,"i":2},{"f":13,"i":3},{"f":13,"i":4},{"f":13,"i":5},{"f":13,"i":6},{"f":13,"i":7},{"f":13,"i":8},{"f":13,"i":9},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"f":2,"i":1},{"f":2,"i":2},{"f":1,"i":0},{"f":1,"i":1},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":14},{"f":11,"i":0},{"f":11,"i":1},{"f":11,"i":2},{"f":11,"i":3},{"f":11,"i":4},{"f":13,"i":15},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":10},{"f":13,"i":11},{"f":13,"i":12},{"f":13,"i":13},{"f":13,"i":16},{"f":12,"i":0},{"f":12,"i":1},{"f":12,"i":2},{"f":12,"i":3},{"f":12,"i":4},{"f":12,"i":5},{"f":12,"i":6},{"f":12,"i":7},{"f":12,"i":8},{"f":12,"i":9},{"f":12,"i":10},{"f":12,"i":11},{"f":12,"i":12},{"f":12,"i":13},{"f":2,"i":0},{"x":1},{"f":12,"i":14},{"f":12,"i":15},{"f":12,"i":16},{"f":13,"i":17},{"f":13,"i":18},{"f":13,"i":19},{"f":13,"i":20},{"f":13,"i":21},{"f":13,"i":22},{"f":13,"i":23},{"f":13,"i":24},{"x":1},{"f":9,"i":0},{"f":9,"i":1},{"f":9,"i":2},{"f":9,"i":3},{"f":9,"i":4},{"f":9,"i":5},{"f":7,"i":0},{"f":7,"i":1},{"f":7,"i":2},{"f":7,"i":3},{"f":7,"i":14},{"f":9,"i":6},{"f":9,"i":7},{"f":9,"i":8},{"x":1},{"f":6,"i":0},{"f":6,"i":1},{"x":1}],"functions":["_init","printf@plt-0x10","printf@plt","__gmon_start__@plt","__libc_start_main@plt","_start","__x86.get_pc_thunk.bx","deregister_tm_clones","register_tm_clones","__do_global_dtors_aux","frame_dummy","foo","bar","main","__libc_csu_init"]}
--------------------------------------------------------------------------------
/player/ptrace.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ELFPlayer
6 |
7 |
8 |
322 |
323 |
324 |
327 |
328 |
--------------------------------------------------------------------------------
/samples/hello.c:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | void foo()
4 | {
5 | return;
6 | }
7 |
8 | void bar()
9 | {
10 | int x;
11 | x = 5;
12 | x *= 3;
13 | x += 6;
14 | printf("Hello! %d\n", x);
15 | }
16 |
17 | int main(int argc, char **argv)
18 | {
19 | int i = 0;
20 | for (i = 0; i < 100; i++) {
21 | if (i % 2 == 0) {
22 | foo();
23 | } else {
24 | bar();
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/samples/passgen:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/defuse/elfplayer/bf44cadfb82d5af9002052d5cd57f6b9becbbf94/samples/passgen
--------------------------------------------------------------------------------
/tracer/Makefile:
--------------------------------------------------------------------------------
1 | tracer: tracer.c
2 | gcc -lelf tracer.c -o tracer
3 |
4 | clean:
5 | rm -f tracer
6 |
--------------------------------------------------------------------------------
/tracer/tracer.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | #include
12 | #include
13 |
14 | void printUsage();
15 | void wait_for_stop(pid_t pid);
16 | unsigned long long get_elf_entry(const char *path);
17 |
18 | int main(int argc, char **argv, char **envp)
19 | {
20 | if (argc < 3) {
21 | printUsage("Not enough arguments.");
22 | return EXIT_SUCCESS;
23 | }
24 |
25 | unsigned long long entrypoint = get_elf_entry(argv[2]);
26 |
27 | pid_t pid;
28 | pid = fork();
29 | if (pid < 0) {
30 | printf("Fork failed.\n");
31 | return EXIT_FAILURE;
32 | } else if (pid == 0) {
33 | /* We are the child. */
34 | ptrace(PTRACE_TRACEME, 0, NULL, NULL);
35 | kill(getpid(), SIGSTOP);
36 | execve(argv[2], argv+2, envp);
37 | /* Apparently the execve causes a SIGTRAP to be sent. */
38 | } else {
39 | /* We are the parent. */
40 |
41 | /* Wait for execve to finish. */
42 | wait_for_stop(pid);
43 |
44 | //long ret = 0;
45 |
46 | //ret = ptrace(PTRACE_ATTACH, pid, 0, 0);
47 | //if (ret != 0) {
48 | // perror("Failed ptrace() attach.");
49 | // exit(EXIT_FAILURE);
50 | //}
51 |
52 | //wait_for_stop(pid);
53 |
54 | struct user_regs_struct regs;
55 |
56 | //ptrace(PTRACE_SYSCALL, pid, 0, 0);
57 | //wait_for_stop(pid);
58 | //long syscall = ptrace(PTRACE_PEEKUSER, pid, sizeof(long)*ORIG_EAX);
59 | //printf("EAX: %ld\n", syscall);
60 | //ptrace(PTRACE_SYSCALL, pid, 0, 0);
61 | //wait_for_stop(pid);
62 | //ptrace(PTRACE_GETREGS, pid, NULL, ®s);
63 | //printf("EAX: %lx\n", regs.rax);
64 |
65 | int hit_entrypoint = 0;
66 |
67 | FILE *output = fopen(argv[1], "w");
68 | if (output == NULL) {
69 | perror("Error opening output file.");
70 | return EXIT_FAILURE;
71 | }
72 |
73 | while (ptrace(PTRACE_SINGLESTEP, pid, 0, 0) == 0) {
74 | wait_for_stop(pid);
75 |
76 | if (ptrace(PTRACE_GETREGS, pid, NULL, ®s) != 0) {
77 | perror("Error getting regs.");
78 | }
79 |
80 | if (regs.rip == entrypoint) {
81 | hit_entrypoint = 1;
82 | }
83 |
84 | if (hit_entrypoint) {
85 | fprintf(output, "%llx\n", regs.rip);
86 | // XXX
87 | }
88 | }
89 | fclose(output);
90 | perror("wat");
91 | }
92 |
93 | return EXIT_SUCCESS;
94 | }
95 |
96 | void printUsage(const char *error)
97 | {
98 | if (error != NULL) {
99 | printf("Error: %s\n", error);
100 | }
101 | printf("Usage: ./tracer \n");
102 | }
103 |
104 | void wait_for_stop(pid_t pid)
105 | {
106 | tryagain:
107 | if (waitpid(pid, NULL, WUNTRACED) != pid) {
108 | if (errno == EINTR) {
109 | /* XXX HACK: We got interrupted by a signal or something, try again. */
110 | goto tryagain;
111 | }
112 | fprintf(stderr, "Failed waiting for process to stop (%d).\n", errno);
113 | exit(1);
114 | }
115 | }
116 |
117 | unsigned long long get_elf_entry(const char *path)
118 | {
119 | Elf *e;
120 | Elf_Kind ek;
121 | GElf_Ehdr ehdr;
122 | unsigned long long entrypoint;
123 |
124 | if (elf_version(EV_CURRENT) == EV_NONE) {
125 | fprintf(stderr, "Elf library initialization failed.\n");
126 | exit(1);
127 | }
128 |
129 | int fd = open(path, O_RDONLY, 0);
130 | if (fd < 0) {
131 | fprintf(stderr, "Couldn't open the ELF at %s.\n", path);
132 | exit(1);
133 | }
134 |
135 | e = elf_begin(fd, ELF_C_READ, NULL);
136 | if (e == NULL) {
137 | fprintf(stderr, "elf_begin() failed.\n");
138 | exit(1);
139 |
140 | }
141 |
142 | ek = elf_kind(e);
143 |
144 | if (ek != ELF_K_ELF) {
145 | fprintf(stderr, "Bad ELF file.\n");
146 | exit(1);
147 | }
148 |
149 | if (gelf_getehdr(e, &ehdr) == NULL) {
150 | fprintf(stderr, "Couldn't get the ELF header.\n");
151 | exit(1);
152 | }
153 |
154 | if (gelf_getclass(e) != ELFCLASS32) {
155 | fprintf(stderr, "Not a 32-bit ELF.\n");
156 | exit(1);
157 | }
158 |
159 | entrypoint = ehdr.e_entry;
160 |
161 | elf_end(e);
162 | close(fd);
163 |
164 | return entrypoint;
165 | }
166 |
--------------------------------------------------------------------------------