Less-Dumb Fuzzing &
Ruby Metaprogramming
       Nephi Johnson
Introduction
‣   Who am I?
    ‣   Security Researcher at BreakingPoint
‣   Why do I like ruby?
    ‣   flexibility
    ‣   simple
    ‣   fun




08-28-2010                    Nephi Johnson    2
Outline
‣   Main points I hope to convey
    ‣   Ruby metaprogramming is fun!
    ‣   there are more intelligent ways to fuzz
    ‣   data-format generation is easier (-est?) with Ruby




08-28-2010                      Nephi Johnson                3
Outline
‣   Fuzzing
    ‣   What? and why?
    ‣   Ways to fuzz
         ‣   dumb-fuzzing (byte fuzzing), less-dumb
‣   Metaprogramming
    ‣   Concepts
         ‣   hooks, code that writes code
‣   Putting it all together
    ‣   Basic Example, Funder


08-28-2010                        Nephi Johnson       4
Fuzzing




08-28-2010    Nephi Johnson   5
Fuzzing > What? and why?
‣   What?
    ‣   a means of finding bugs in applications
         ‣   throw all combinations of data at any and all means of input
‣   and why?
    ‣   fix the bugs
         ‣   less crashes/errors for the end-user
         ‣   more secure applications
         ‣   feel more confident that your code will correctly handle any input
    ‣   fun and profit


08-28-2010                         Nephi Johnson                            6
Fuzzing > Ways to fuzz
‣   Dumb-Fuzzing (aka byte-fuzzing)
    ‣   create sets of valid input
         ‣   shoot for code coverage
    ‣   iterate over each byte/word/dword in each set of valid input
    ‣   akin to brute-forcing passwords
‣   Less-Dumb
    ‣   create a valid structure of the data format in memory
    ‣   target critical fields/values (and combinations)
    ‣   ignore unimportant values


08-28-2010                        Nephi Johnson                        7
… > Ways to fuzz > Dumb-Fuzzing
‣   Why is it so dumb?
    ‣   wastes time iterating over meaningless values
    ‣   can't change values and keep other fields valid
         ‣   checksums
         ‣   lengths
    ‣   can only modify the data, not add to or remove




08-28-2010                     Nephi Johnson              8
… > Ways to fuzz > Dumb-Fuzzing
‣   basic example

file_name = ARGV[0]

base_input = File.read(file_name)
base_input.length.times do |offset|
    new_input = base_input.clone
    256.times do |new_val|
        new_input[offset, 1] = new_val.chr
        …
        # send new_input to program
        # monitor for crashes, exceptions, etc
        …
    end
end




08-28-2010                      Nephi Johnson    9
… > Ways to fuzz > Less-Dumb
‣   Several ways to be “less-dumb” about generating the data used in
    fuzzing
    ‣   no classes/code re-use, just one massive function/script
         ‣   hard to maintain/modify          ‣   specific to one format
         ‣   inflexible
    ‣   generate a class specific to each data-format
         ‣   easier to maintain               ‣   specific to one format
         ‣   more flexible
    ‣   create a re-usable framework for defining the formats
         ‣   easy to maintain                 ‣   many formats
         ‣   very flexible

08-28-2010                             Nephi Johnson                       10
Metaprogramming




08-28-2010        Nephi Johnson   11
Metaprogramming
‣   Hooks
‣   Code that writes code




08-28-2010                  Nephi Johnson   12
Metaprogramming > Hooks
‣     defined in Module:      ‣   defined in Kernel:
       ‣   const_missing           ‣   method_missing
       ‣   extended
       ‣   included           ‣   defined in Object:
       ‣   method_added
                                   ‣   singleton_method_added

       ‣   method_removed
                                   ‣   singleton_method_removed
                                       singleton_method_undefined
           method_undefined
                                   ‣
       ‣



‣     defined in Class
       ‣   inherited

    08-28-2010                Nephi Johnson                         13
Metaprogramming > Hooks
class InheritMe
    def self.inherited(other)
        puts "#{other} inherited #{self.class}"
    end
end

class A < InheritMe
end

# ==> A inherited InheritMe




 08-28-2010                      Nephi Johnson    14
Metaprogramming > Hooks
class MethodMissing
    def method_missing(method_name, *args)
        puts "#{method_name} called with #{args.inspect}"
    end
end

m = MethodMissing.new
m.nonexistent_method(1, "arg_2", Object.new)

# ==> nonexistent_method called with [1, "arg_2", #<Object:0xb7617594>]




 08-28-2010                      Nephi Johnson                            15
Meta > Code that writes code
‣   Two main ways to create code that modifies and/or creates
    code:
    ‣   via method calls
    ‣   through various evals
         ‣   eval a string
         ‣   call a block/Proc/lambda
              ‣   (almost all the same thing)




08-28-2010                             Nephi Johnson        16
… > Code that ... > via method calls
‣   Available method calls:
‣   defined in Module:                   ‣   defined in Object:
     ‣   alias_method                         ‣   instance_variable_get/set
     ‣   class_variable_get/set               ‣   remove_instance_variable
     ‣   remove_class_variable                ‣   send
     ‣   const_get/set
     ‣   remove_const
     ‣   extend_object
     ‣   define_method
     ‣   remove_method
     ‣   undef_method

08-28-2010                        Nephi Johnson                               17
… > Code that ... > via method calls
class Example
    def self.inherited(other)
        # define_method is a private method
        other.send(:define_method, :subclassed_example?) do
            return "possibly"
        end
    end
    def method_missing(method, *args)
             if method.to_s =~ /create_(.*)/
            class_name = $1.capitalize
            new_klass = begin
                if (Object.constants.include?(class_name))
                    Object.const_get(class_name)
                else
                    Object.const_set(class_name, Class.new)
                end
            end                
            return new_klass.new
        end
        raise "Method #{method} doesn't exist!"
    end
end
class F < Example ; end

f = F.new
puts f.subclassed_example?     # ==> possibly
puts f.create_cookiemonster    # ==> #<Cookiemonster:0xb782c26c>

  08-28-2010                          Nephi Johnson                18
… > Code that ... > via evals
‣   Available ways to eval code:
‣   defined in Module:                    ‣   defined in Object:
    ‣   class_eval                             ‣   instance_eval

    ‣   module_eval
‣   defined in Kernel:
    ‣   eval


‣   class_eval, module_eval, and instance eval can each accept a string or a
    block to evaluate
‣   however, eval only accepts a string



08-28-2010                         Nephi Johnson                               19
… > Code that ... > via evals
 ‣   class_eval (aka module_eval)
class ClassEval
   class << self
   private
   def private_static_method
       puts "private static method"
   end
   end
end

ClassEval.private_static_method    # ==> private method `private_static_method' called    
                                         for ClassEval:Class (NoMethodError)
ClassEval.class_eval do
    private_static_method()        # ==> private static method
    def made_by_class_eval
        puts "made by class eval"
    end 
end

c = ClassEval.new
c.made_by_class_eval               # ==> made by class eval
d = ClassEval.new
d.made_by_class_eval               # ==> made by class eval



  08-28-2010                          Nephi Johnson                                  20
… > Code that ... > via evals
 ‣   instance_eval
class InstanceEval
    private
    def private_method
        puts "this is a private method"
    end
end

i = InstanceEval.new
i.instance_eval do
    private_method          # ==> this is a private method
end

i.instance_eval do
    def new_method
        puts "this is a new method"
    end
end
i.new_method                # ==> this is a new method
j = InstanceEval.new
j.new_method                # ==> undefined method `new_method' ...




  08-28-2010                          Nephi Johnson                   21
… > Code that ... > via evals
 ‣   eval
class Eval
    attr_reader :greeting
    def initialize
        @greeting = "hello"
    end
    def greet
        puts @greeting
    end
end

e = Eval.new
e.greet               # ==> Hello

# binding is a private method
eval('@greeting = "Glueck Auf"', e.send(:binding))
e.greet               # ==> Glueck Auf

e.send(:eval, '@greeting = "Tag"')
e.greet               # ==> Tag




  08-28-2010                          Nephi Johnson   22
… > Code that ... > via evals
class Example
    def self.inherited(other)
        other.class_eval do
            def subclassed_example?
                return "possibly"
            end
        end
    end
    def method_missing(method, *args)
             if method.to_s =~ /create_(.*)/
            class_name = $1.capitalize
            eval("class #{class_name} ; end")
            new_klass = eval(class_name)
            return new_klass.new
        end
        raise "Method #{method} doesn't exist!"
    end
end
class F < Example ; end

f = F.new
puts f.subclassed_example?     # ==> possibly
puts f.create_cookiemonster    # ==> #<Cookiemonster:0xb782c26c>




  08-28-2010                          Nephi Johnson                23
Putting it all Together




08-28-2010           Nephi Johnson     24
Putting it all Together
‣   Basic
    ‣   functionality
    ‣   usability
    ‣   fuzzing
‣   Funder
    ‣   highlights
         ‣   actions, sections, arrays, binding
    ‣   fuzzing with Funder
         ‣   static inheritance, options


08-28-2010                          Nephi Johnson   25
Putting it all Together > Basic
‣   Goals
    ‣   Easy to define/modify data-formats
    ‣   Reusable
‣   Common needs
    ‣   lengths            ‣   embedded formats
    ‣   checksums
    ‣   basic logic
    ‣   compression
    ‣   enumerable


08-28-2010                     Nephi Johnson      26
Putting it all Together > Basic
‣    Want it to look something like:
class DataFormat < Library
    int32 field1 length(field2, field3)
    int32 field2 value
    string field3 value
end

d = DataFormat.new
d.field2 = 100
d.field3 = “hello there”
...
# etc
    
‣    Basically, keep it as simple as possible




    08-28-2010                    Nephi Johnson   27
... > Basic > Functionality
‣    Create the fields                     class Field
                                               attr_accessor :name, :value
class Library < Field                          def initialize(name, value, opts={})
    class << self
        attr_accessor :order
                                                   @name = name ; @value = value
        def field(name, value)                     @opts = opts
            @order ||= []                      end
            @order << [name, value]            def to_out ; end
        end                                end
    end # class << self
    def initialize(*args)
        super(*args) if args.length == 2
        self.class.order.each do |name, value|
            self.class.class_eval { attr_accessor name }
            instance_variable_set("@#{name}", value)
        end 
    end 
end

class A < Library
    field :field_1, 10
    field :field_2, "this is field2"
end
a = A.new
puts a.inspect    #==> #<A:0xb77d2f50 @order=[], @field_2="this is field2", @field_1=10>


    08-28-2010                        Nephi Johnson                                 28
... > Basic > Usability
‣    Make it useful
                                                 class PreField
                                                     attr_accessor :name, :klass, :value, :opts
                                                     def initialize(name, klass, value, opts)
class Library < Field                                    @name = name ; @klass = klass
    class << self                                        @value = value ; @opts = opts
    def field(name, klass, value, opts={})           end
        @order ||= []                                def create ; @klass.new(@name, @value,
        @order << PreField.new(name,klass,                                   @opts) ; end
                               value, opts)      end
    end
    end # class << self                           class Int < Field
    def to_out                                        def to_out ; [@value].pack(“n”) ; end
        @order.map{|field| field.to_out }.join    end
    end
    def                                           class Str < Field
    def initialize(*args)                             def to_out ; @value.to_s ; end
        super(*args) if args.length == 2          end
        @order = []
        self.class.order.each do |pre_field|
            self.class.class_eval { attr_accessor pre_field.name }
            @order << pre_field.create
            instance_variable_set("@#{name}", @order.last)
        end
    end
end


    08-28-2010                           Nephi Johnson                                        29
... > Basic > Usability
‣    Make it useful – in use
require 'library'

class A < Library
    field :field_1, Int, 10
    field :field_2, Str, "this is field2"
end

a = A.new
puts a.to_out.inspect                 # ==> "000nthis is field2"

a.field_1.value = 100
a.field_2.value = "field2 changed!"
puts a.to_out.inspect                 # ==> “000dfield2 changed!"




    08-28-2010                   Nephi Johnson                       30
... > Basic > Fuzzing
‣   Enumerate the fields
‣   Do what you want with them
    ‣   shuffle the order
    ‣   omit / duplicate fields
    ‣   mutate the original data
    ‣   iterate over a set of values




08-28-2010                         Nephi Johnson   31
... > Basic > Fuzzing
require 'library'

class Fuzzer
    def initialize(field) ; @field = field ; end 
    def fuzz(&block)
        @field.get_all_fields.each do |child|
            child.snapshot
            child.get_fvals.each do |new_val|
                child.value = new_val
                block.call(@field)            class Int < Field
            end                                   def get_fvals
            child.reset                               (0..3).to_a
        end                                       end 
    end                                       end
end                                           class Str < Field
class Field                                       def get_fvals
    def snapshot ; @cache = @value ; end              (1..4).map {|i| "A"*(2**i) }
    def reset ; @value = @cache ; end             end 
    def get_all_fields                        end
        return [self] unless @order
        @order.each.map {|field| field.get_all_fields }.flatten
    end 
end



 08-28-2010                        Nephi Johnson                             32
... > Basic > Fuzzing
‣       Fuzzer – in use
require 'library'

class A < Library
    field :field_1, Int, 0xffff
    field :field_2, Str, "this is field2"
end

a = A.new
f = Fuzzer.new(a)                        "000000this is field2"
f.fuzz do |new_a|                        "000001this is field2"
    puts new_a.to_out.inspect      # ==> "000002this is field2"
end                                      "000003this is field2"
                                         "377377AA"
                                         "377377AAAA"
                                         "377377AAAAAAAA"
                                         "377377AAAAAAAAAAAAAAAA"

    ‣   100 total lines of code!
         ‣   (see next slide)
    08-28-2010                      Nephi Johnson                     33
... > Basic > Fuzzing > 100 lines
# library.rb                                                                56 # FUZZing                                                         # test.rb
                                                                            57 
  1 class Field                                                             58 class Int < Field                                                   1 require 'library'
  2     attr_accessor :name, :value                                         59     def get_fvals                                                   2 
  3     def initialize(name, value)                                         60         (0..3).to_a                                                 3 class A < Library
  4         @name = name ; @value = value                                   61     end                                                             4     field :field_1, Int, 0xffff
  5     end                                                                 62 end                                                                 5     field :field_2, Str, "this is field2"
  6     def to_out ; end                                                    63 class Str < Field                                                   6 end
  7 end                                                                     64     def get_fvals                                                   7 
  8                                                                         65         (1..4).map {|i| "A" * (2**i) }                              8 a = A.new
                                                                            66     end                                                             9 f = Fuzzer.new(a)
  9 class Int < Field
                                                                            67 end                                                                10 f.fuzz do |new_a|
 10     def to_out
                                                                            68 class Field                                                        11     puts new_a.to_out.inspect
 11         [@value].pack("n")
                                                                            69     def snapshot ; @cache = @value ; end                           12 end
 12     end
 13 end                                                                     70     def reset ; @value = @cache ; end
                                                                            71     def get_all_fields
 14 
                                                                            72         return [self] unless @order
 15 class Str < Field
                                                                            73         @order.each.map {|field| field.get_all_fields }.flatten
 16     def to_out                                                          74     end
 17         @value                                                          75 end
 18     end                                                                 76 class Fuzzer
 19 end                                                                     77     def initialize(field) ; @field = field ; end
 20                                                                         78     def fuzz(&block)
 21 class PreField                                                          79         @field.get_all_fields.each do |child|
 22     attr_accessor :name, :klass, :value                                 80             child.snapshot
 23     def initialize(name, klass, value)                                  81             child.get_fvals.each do |new_val|
 24         @name = name                                                    82                 child.value = new_val
 25         @klass = klass                                                  83                 block.call(@field)
 26         @value = value                                                  84             end
 27     end                                                                 85             child.reset
 28     def create                                                          86         end
 29         @klass.new(name, value)                                         87     end
 30     end                                                                 88 end
 31 end
 32 
 33 class Library < Field
 34     class << self
 35         attr_accessor :order
 36         def field(name, klass, value)
 37             @order ||= []
 38             @order << PreField.new(name, klass, value)
 39         end
 40     end # class << self
 41     def initialize(*args)
 42         super(*args) if args.length == 2
 43         @order = []
 44         self.class.order.each do |pre_field|
 45             self.class.class_eval { attr_accessor pre_field.name }
 46             @order << pre_field.create
 47             instance_variable_set("@#{pre_field.name}", @order.last)
 48         end
 49     end
 50     def to_out
 51         @order.map{|field| field.to_out }.join
 52     end
 53 end
 54 
 55 




  08-28-2010                                                                                Nephi Johnson                                                                                        34
Putting it all Together > Basic
‣   In these simple examples, I didn't meet these common
    requirements:
    ‣   lengths
    ‣   checksums
    ‣   basic logic
    ‣   compression
‣   I'll talk about these next with my project, Funder




08-28-2010                    Nephi Johnson                35
Putting it all Together > Funder
‣
    Funder = Format          UNDERstander
    ‣   essentially a fuller version of the previous example
    ‣   has:
         ‣   actions                           ‣    “unfields”
         ‣   more syntactic sugar              ‣    irb-friendliness
         ‣   options                           ‣    offset calculations
         ‣   arrays                            ‣    inheritance
         ‣   field binding
         ‣   sections
    ‣   svn co http://funder.googlecode.com/svn/trunk funder

08-28-2010                          Nephi Johnson                         36
... > Funder > Actions
require 'funder'

class ActionTest < Funder
    int32 :length, action(Length, lambda{[data, base64]})
    str :data, "compressed data", :a=>action(ZlibDeflate)
    str :base64, action(Base64Encode, lambda{[data]})
end



>> a = ActionTest.new
=> #<ActionTest length=Action:Length(data, base64) data=Action:ZlibDeflate("compressed 
data") base64=Action:Base64Encode(data)>

>> a.to_out
=> "0000000007x234K316317­(J­.NMQHI,I0040000351005360eJxLzs8tKEotLk5NUUhJLEkEADDpBfA="

>> a.base64.to_out
=> "eJxLzs8tKEotLk5NUUhJLEkEADDpBfA="

>> Base64.decode64(a.base64.to_out)
=> "x234K316317­(J­.NMQHI,I0040000351005360"

>> Zlib::Inflate.inflate(Base64.decode64(a.base64.to_out))
=> "compressed data"



  08-28-2010                                 Nephi Johnson                                          37
... > Funder > Arrays & Binding
require 'funder'

class Item < Funder
    byte :id_num
    byte :length, action(Length, lambda{[item_name, space]})
    str :item_name, lambda{"item_number(##{id_num.value.resolve})"}
    str :space, " "
end

class IndexEntry < Funder
    byte :id_num, counter(:index_entry)
    int16 :item_offset
end

class ArrayTest < Funder
    array :index, IndexEntry, :b=>bind(lambda{items}, {:item_offset=>:offset})
    str :separator, "  ITEMS:  "
    array :items, Item, :b=>bind(lambda{index}, {:id_num=>"id_num.value"})
end

# (usage on next slide)




  08-28-2010                          Nephi Johnson                              38
... > Funder > Arrays & Binding
>> a = ArrayTest.new
=> #<ArrayTest index=#<IndexEntry[3]> separator="  ITEMS:  " items=#<Item[3]>>

>> res = a.to_out
=> "000000023001000%0020007  ITEMS:  021000item_number(#0) 
021001item_number(#1) 021002item_number(#2) "

>> item_1_offset = res[4, 2].unpack("n")[0]
=> 37

>> item_1_length = res[item_1_offset, 1].unpack("c")[0]
=> 17

>> item_1_data = res[item_1_offset+1, item_1_length]
=> "001item_number(#1) "




  08-28-2010                          Nephi Johnson                              39
... > Funder > Png Example
‣   Png images are made up of chunks:
    class Chunk < Funder
        int32 :length, action(Length, lambda{[data]})
        str :type_code, nil, :min=>4
        str :data
        int32 :crc, action(Crc32, lambda{[type_code,data]})
    end

‣   Specific chunks can inhert from Chunk:
    class IHDR < Chunk
        str :type_code, "IHDR"
        section :data do
            int32 :width
            int32 :height 
            byte :bit_depth, 8
            byte :color_type, Png::COLOR_TYPE_TRUECOLOR
            byte :comp_method, 0
            byte :filter_method, 0
            byte :interlace_method, 0
        end 
    end

08-28-2010                        Nephi Johnson               40
... > Funder > Png Example
‣   Final core PNG class:
    class Png < Funder
        str :png_header, "211PNGrn032n"
        field :ihdr, IHDR
        field :srgb, SRGB
        field :idat, IDAT
        field :iend, IEND
    end




08-28-2010                        Nephi Johnson   41
... > Funder > Png – In Use
>> require 'formats/png'
=> true

>> p = Png.new
=> #<Png png_header="211PNGrn032n" ihdr=#<IHDR> srgb=#<SRGB> idat=#<IDAT> 
iend=#<IEND>>

>> p.ihdr.data.width.value = p.ihdr.data.height.value = 200
=> 200

>> p.idat.data.red.value = 0xff
=> 255

>> p.idat.data.green.value = 0
=> 0

>> p.idat.data.blue.value = 0
=> 0

>> File.open("test.png", "w"){|f| f.write p.to_out }
=> 696




  08-28-2010                          Nephi Johnson                               42
... > Funder > Fuzzing
‣   My fuzzing code is similar to the example code, but has
    ‣   more options, control
    ‣   a form static inheritance to propagate options from classes to
        subclass instances
    ‣   random and deterministic fuzzing
‣   svn checkout the source or browse it online
         ‣   (I'm probably desperately out of time by the time I reach this
             slide)




08-28-2010                          Nephi Johnson                             43
The End




08-28-2010    Nephi Johnson   44

Less-Dumb Fuzzing and Ruby Metaprogramming

  • 1.
    Less-Dumb Fuzzing & RubyMetaprogramming Nephi Johnson
  • 2.
    Introduction ‣ Who am I? ‣ Security Researcher at BreakingPoint ‣ Why do I like ruby? ‣ flexibility ‣ simple ‣ fun 08-28-2010 Nephi Johnson 2
  • 3.
    Outline ‣ Main points I hope to convey ‣ Ruby metaprogramming is fun! ‣ there are more intelligent ways to fuzz ‣ data-format generation is easier (-est?) with Ruby 08-28-2010 Nephi Johnson 3
  • 4.
    Outline ‣ Fuzzing ‣ What? and why? ‣ Ways to fuzz ‣ dumb-fuzzing (byte fuzzing), less-dumb ‣ Metaprogramming ‣ Concepts ‣ hooks, code that writes code ‣ Putting it all together ‣ Basic Example, Funder 08-28-2010 Nephi Johnson 4
  • 5.
    Fuzzing 08-28-2010 Nephi Johnson 5
  • 6.
    Fuzzing > What?and why? ‣ What? ‣ a means of finding bugs in applications ‣ throw all combinations of data at any and all means of input ‣ and why? ‣ fix the bugs ‣ less crashes/errors for the end-user ‣ more secure applications ‣ feel more confident that your code will correctly handle any input ‣ fun and profit 08-28-2010 Nephi Johnson 6
  • 7.
    Fuzzing > Waysto fuzz ‣ Dumb-Fuzzing (aka byte-fuzzing) ‣ create sets of valid input ‣ shoot for code coverage ‣ iterate over each byte/word/dword in each set of valid input ‣ akin to brute-forcing passwords ‣ Less-Dumb ‣ create a valid structure of the data format in memory ‣ target critical fields/values (and combinations) ‣ ignore unimportant values 08-28-2010 Nephi Johnson 7
  • 8.
    … > Waysto fuzz > Dumb-Fuzzing ‣ Why is it so dumb? ‣ wastes time iterating over meaningless values ‣ can't change values and keep other fields valid ‣ checksums ‣ lengths ‣ can only modify the data, not add to or remove 08-28-2010 Nephi Johnson 8
  • 9.
    … > Waysto fuzz > Dumb-Fuzzing ‣ basic example file_name = ARGV[0] base_input = File.read(file_name) base_input.length.times do |offset|     new_input = base_input.clone     256.times do |new_val|         new_input[offset, 1] = new_val.chr         …         # send new_input to program         # monitor for crashes, exceptions, etc         …     end end 08-28-2010 Nephi Johnson 9
  • 10.
    … > Waysto fuzz > Less-Dumb ‣ Several ways to be “less-dumb” about generating the data used in fuzzing ‣ no classes/code re-use, just one massive function/script ‣ hard to maintain/modify ‣ specific to one format ‣ inflexible ‣ generate a class specific to each data-format ‣ easier to maintain ‣ specific to one format ‣ more flexible ‣ create a re-usable framework for defining the formats ‣ easy to maintain ‣ many formats ‣ very flexible 08-28-2010 Nephi Johnson 10
  • 11.
  • 12.
    Metaprogramming ‣ Hooks ‣ Code that writes code 08-28-2010 Nephi Johnson 12
  • 13.
    Metaprogramming > Hooks ‣ defined in Module: ‣ defined in Kernel: ‣ const_missing ‣ method_missing ‣ extended ‣ included ‣ defined in Object: ‣ method_added ‣ singleton_method_added ‣ method_removed ‣ singleton_method_removed singleton_method_undefined method_undefined ‣ ‣ ‣ defined in Class ‣ inherited 08-28-2010 Nephi Johnson 13
  • 14.
  • 15.
  • 16.
    Meta > Codethat writes code ‣ Two main ways to create code that modifies and/or creates code: ‣ via method calls ‣ through various evals ‣ eval a string ‣ call a block/Proc/lambda ‣ (almost all the same thing) 08-28-2010 Nephi Johnson 16
  • 17.
    … > Codethat ... > via method calls ‣ Available method calls: ‣ defined in Module: ‣ defined in Object: ‣ alias_method ‣ instance_variable_get/set ‣ class_variable_get/set ‣ remove_instance_variable ‣ remove_class_variable ‣ send ‣ const_get/set ‣ remove_const ‣ extend_object ‣ define_method ‣ remove_method ‣ undef_method 08-28-2010 Nephi Johnson 17
  • 18.
    … > Codethat ... > via method calls class Example     def self.inherited(other)         # define_method is a private method         other.send(:define_method, :subclassed_example?) do             return "possibly"         end     end     def method_missing(method, *args) if method.to_s =~ /create_(.*)/             class_name = $1.capitalize             new_klass = begin                 if (Object.constants.include?(class_name))                     Object.const_get(class_name)                 else                     Object.const_set(class_name, Class.new)                 end             end                             return new_klass.new         end         raise "Method #{method} doesn't exist!"     end end class F < Example ; end f = F.new puts f.subclassed_example?     # ==> possibly puts f.create_cookiemonster    # ==> #<Cookiemonster:0xb782c26c> 08-28-2010 Nephi Johnson 18
  • 19.
    … > Codethat ... > via evals ‣ Available ways to eval code: ‣ defined in Module: ‣ defined in Object: ‣ class_eval ‣ instance_eval ‣ module_eval ‣ defined in Kernel: ‣ eval ‣ class_eval, module_eval, and instance eval can each accept a string or a block to evaluate ‣ however, eval only accepts a string 08-28-2010 Nephi Johnson 19
  • 20.
    … > Codethat ... > via evals ‣ class_eval (aka module_eval) class ClassEval    class << self    private    def private_static_method        puts "private static method"    end    end end ClassEval.private_static_method    # ==> private method `private_static_method' called                                              for ClassEval:Class (NoMethodError) ClassEval.class_eval do     private_static_method()        # ==> private static method     def made_by_class_eval         puts "made by class eval"     end  end c = ClassEval.new c.made_by_class_eval               # ==> made by class eval d = ClassEval.new d.made_by_class_eval               # ==> made by class eval 08-28-2010 Nephi Johnson 20
  • 21.
    … > Codethat ... > via evals ‣ instance_eval class InstanceEval     private     def private_method         puts "this is a private method"     end end i = InstanceEval.new i.instance_eval do     private_method          # ==> this is a private method end i.instance_eval do     def new_method         puts "this is a new method"     end end i.new_method                # ==> this is a new method j = InstanceEval.new j.new_method                # ==> undefined method `new_method' ... 08-28-2010 Nephi Johnson 21
  • 22.
    … > Codethat ... > via evals ‣ eval class Eval     attr_reader :greeting     def initialize         @greeting = "hello"     end     def greet         puts @greeting     end end e = Eval.new e.greet               # ==> Hello # binding is a private method eval('@greeting = "Glueck Auf"', e.send(:binding)) e.greet               # ==> Glueck Auf e.send(:eval, '@greeting = "Tag"') e.greet               # ==> Tag 08-28-2010 Nephi Johnson 22
  • 23.
    … > Codethat ... > via evals class Example     def self.inherited(other)         other.class_eval do             def subclassed_example?                 return "possibly"             end         end     end     def method_missing(method, *args) if method.to_s =~ /create_(.*)/             class_name = $1.capitalize             eval("class #{class_name} ; end")             new_klass = eval(class_name)             return new_klass.new         end         raise "Method #{method} doesn't exist!"     end end class F < Example ; end f = F.new puts f.subclassed_example?     # ==> possibly puts f.create_cookiemonster    # ==> #<Cookiemonster:0xb782c26c> 08-28-2010 Nephi Johnson 23
  • 24.
    Putting it allTogether 08-28-2010 Nephi Johnson 24
  • 25.
    Putting it allTogether ‣ Basic ‣ functionality ‣ usability ‣ fuzzing ‣ Funder ‣ highlights ‣ actions, sections, arrays, binding ‣ fuzzing with Funder ‣ static inheritance, options 08-28-2010 Nephi Johnson 25
  • 26.
    Putting it allTogether > Basic ‣ Goals ‣ Easy to define/modify data-formats ‣ Reusable ‣ Common needs ‣ lengths ‣ embedded formats ‣ checksums ‣ basic logic ‣ compression ‣ enumerable 08-28-2010 Nephi Johnson 26
  • 27.
    Putting it allTogether > Basic ‣ Want it to look something like: class DataFormat < Library     int32 field1 length(field2, field3)     int32 field2 value     string field3 value end d = DataFormat.new d.field2 = 100 d.field3 = “hello there” ... # etc      ‣ Basically, keep it as simple as possible 08-28-2010 Nephi Johnson 27
  • 28.
    ... > Basic> Functionality ‣ Create the fields class Field     attr_accessor :name, :value class Library < Field     def initialize(name, value, opts={})     class << self         attr_accessor :order         @name = name ; @value = value         def field(name, value)         @opts = opts             @order ||= []     end             @order << [name, value]     def to_out ; end         end  end     end # class << self     def initialize(*args)         super(*args) if args.length == 2         self.class.order.each do |name, value|             self.class.class_eval { attr_accessor name }             instance_variable_set("@#{name}", value)         end      end  end class A < Library     field :field_1, 10     field :field_2, "this is field2" end a = A.new puts a.inspect    #==> #<A:0xb77d2f50 @order=[], @field_2="this is field2", @field_1=10> 08-28-2010 Nephi Johnson 28
  • 29.
    ... > Basic> Usability ‣ Make it useful class PreField     attr_accessor :name, :klass, :value, :opts     def initialize(name, klass, value, opts) class Library < Field         @name = name ; @klass = klass     class << self         @value = value ; @opts = opts     def field(name, klass, value, opts={})     end         @order ||= []     def create ; @klass.new(@name, @value,         @order << PreField.new(name,klass,                             @opts) ; end                                value, opts) end     end     end # class << self class Int < Field     def to_out     def to_out ; [@value].pack(“n”) ; end         @order.map{|field| field.to_out }.join end     end     def  class Str < Field     def initialize(*args)     def to_out ; @value.to_s ; end         super(*args) if args.length == 2 end         @order = []         self.class.order.each do |pre_field|             self.class.class_eval { attr_accessor pre_field.name }             @order << pre_field.create             instance_variable_set("@#{name}", @order.last)         end     end end 08-28-2010 Nephi Johnson 29
  • 30.
    ... > Basic> Usability ‣ Make it useful – in use require 'library' class A < Library     field :field_1, Int, 10     field :field_2, Str, "this is field2" end a = A.new puts a.to_out.inspect                 # ==> "000nthis is field2" a.field_1.value = 100 a.field_2.value = "field2 changed!" puts a.to_out.inspect                 # ==> “000dfield2 changed!" 08-28-2010 Nephi Johnson 30
  • 31.
    ... > Basic> Fuzzing ‣ Enumerate the fields ‣ Do what you want with them ‣ shuffle the order ‣ omit / duplicate fields ‣ mutate the original data ‣ iterate over a set of values 08-28-2010 Nephi Johnson 31
  • 32.
    ... > Basic> Fuzzing require 'library' class Fuzzer     def initialize(field) ; @field = field ; end      def fuzz(&block)         @field.get_all_fields.each do |child|             child.snapshot             child.get_fvals.each do |new_val|                 child.value = new_val                 block.call(@field) class Int < Field             end      def get_fvals             child.reset         (0..3).to_a         end      end      end  end end class Str < Field class Field     def get_fvals     def snapshot ; @cache = @value ; end          (1..4).map {|i| "A"*(2**i) }     def reset ; @value = @cache ; end      end      def get_all_fields end         return [self] unless @order         @order.each.map {|field| field.get_all_fields }.flatten     end  end 08-28-2010 Nephi Johnson 32
  • 33.
    ... > Basic> Fuzzing ‣ Fuzzer – in use require 'library' class A < Library     field :field_1, Int, 0xffff     field :field_2, Str, "this is field2" end a = A.new f = Fuzzer.new(a)       "000000this is field2" f.fuzz do |new_a|       "000001this is field2"     puts new_a.to_out.inspect # ==> "000002this is field2" end       "000003this is field2"       "377377AA"       "377377AAAA"       "377377AAAAAAAA"       "377377AAAAAAAAAAAAAAAA" ‣ 100 total lines of code! ‣ (see next slide) 08-28-2010 Nephi Johnson 33
  • 34.
    ... > Basic> Fuzzing > 100 lines # library.rb  56 # FUZZing # test.rb  57    1 class Field  58 class Int < Field   1 require 'library'   2     attr_accessor :name, :value  59     def get_fvals   2    3     def initialize(name, value)  60         (0..3).to_a   3 class A < Library   4         @name = name ; @value = value  61     end   4     field :field_1, Int, 0xffff   5     end  62 end   5     field :field_2, Str, "this is field2"   6     def to_out ; end  63 class Str < Field   6 end   7 end  64     def get_fvals   7    8   65         (1..4).map {|i| "A" * (2**i) }   8 a = A.new  66     end   9 f = Fuzzer.new(a)   9 class Int < Field  67 end  10 f.fuzz do |new_a|  10     def to_out  68 class Field  11     puts new_a.to_out.inspect  11         [@value].pack("n")  69     def snapshot ; @cache = @value ; end  12 end  12     end  13 end  70     def reset ; @value = @cache ; end  71     def get_all_fields  14   72         return [self] unless @order  15 class Str < Field  73         @order.each.map {|field| field.get_all_fields }.flatten  16     def to_out  74     end  17         @value  75 end  18     end  76 class Fuzzer  19 end  77     def initialize(field) ; @field = field ; end  20   78     def fuzz(&block)  21 class PreField  79         @field.get_all_fields.each do |child|  22     attr_accessor :name, :klass, :value  80             child.snapshot  23     def initialize(name, klass, value)  81             child.get_fvals.each do |new_val|  24         @name = name  82                 child.value = new_val  25         @klass = klass  83                 block.call(@field)  26         @value = value  84             end  27     end  85             child.reset  28     def create  86         end  29         @klass.new(name, value)  87     end  30     end  88 end  31 end  32   33 class Library < Field  34     class << self  35         attr_accessor :order  36         def field(name, klass, value)  37             @order ||= []  38             @order << PreField.new(name, klass, value)  39         end  40     end # class << self  41     def initialize(*args)  42         super(*args) if args.length == 2  43         @order = []  44         self.class.order.each do |pre_field|  45             self.class.class_eval { attr_accessor pre_field.name }  46             @order << pre_field.create  47             instance_variable_set("@#{pre_field.name}", @order.last)  48         end  49     end  50     def to_out  51         @order.map{|field| field.to_out }.join  52     end  53 end  54   55  08-28-2010 Nephi Johnson 34
  • 35.
    Putting it allTogether > Basic ‣ In these simple examples, I didn't meet these common requirements: ‣ lengths ‣ checksums ‣ basic logic ‣ compression ‣ I'll talk about these next with my project, Funder 08-28-2010 Nephi Johnson 35
  • 36.
    Putting it allTogether > Funder ‣ Funder = Format UNDERstander ‣ essentially a fuller version of the previous example ‣ has: ‣ actions ‣ “unfields” ‣ more syntactic sugar ‣ irb-friendliness ‣ options ‣ offset calculations ‣ arrays ‣ inheritance ‣ field binding ‣ sections ‣ svn co http://funder.googlecode.com/svn/trunk funder 08-28-2010 Nephi Johnson 36
  • 37.
    ... > Funder> Actions require 'funder' class ActionTest < Funder     int32 :length, action(Length, lambda{[data, base64]})     str :data, "compressed data", :a=>action(ZlibDeflate)     str :base64, action(Base64Encode, lambda{[data]}) end >> a = ActionTest.new => #<ActionTest length=Action:Length(data, base64) data=Action:ZlibDeflate("compressed  data") base64=Action:Base64Encode(data)> >> a.to_out => "0000000007x234K316317­(J­.NMQHI,I0040000351005360eJxLzs8tKEotLk5NUUhJLEkEADDpBfA=" >> a.base64.to_out => "eJxLzs8tKEotLk5NUUhJLEkEADDpBfA=" >> Base64.decode64(a.base64.to_out) => "x234K316317­(J­.NMQHI,I0040000351005360" >> Zlib::Inflate.inflate(Base64.decode64(a.base64.to_out)) => "compressed data" 08-28-2010 Nephi Johnson 37
  • 38.
    ... > Funder> Arrays & Binding require 'funder' class Item < Funder     byte :id_num     byte :length, action(Length, lambda{[item_name, space]})     str :item_name, lambda{"item_number(##{id_num.value.resolve})"}     str :space, " " end class IndexEntry < Funder     byte :id_num, counter(:index_entry)     int16 :item_offset end class ArrayTest < Funder     array :index, IndexEntry, :b=>bind(lambda{items}, {:item_offset=>:offset})     str :separator, "  ITEMS:  "     array :items, Item, :b=>bind(lambda{index}, {:id_num=>"id_num.value"}) end # (usage on next slide) 08-28-2010 Nephi Johnson 38
  • 39.
    ... > Funder> Arrays & Binding >> a = ArrayTest.new => #<ArrayTest index=#<IndexEntry[3]> separator="  ITEMS:  " items=#<Item[3]>> >> res = a.to_out => "000000023001000%0020007  ITEMS:  021000item_number(#0)  021001item_number(#1) 021002item_number(#2) " >> item_1_offset = res[4, 2].unpack("n")[0] => 37 >> item_1_length = res[item_1_offset, 1].unpack("c")[0] => 17 >> item_1_data = res[item_1_offset+1, item_1_length] => "001item_number(#1) " 08-28-2010 Nephi Johnson 39
  • 40.
    ... > Funder> Png Example ‣ Png images are made up of chunks: class Chunk < Funder     int32 :length, action(Length, lambda{[data]})     str :type_code, nil, :min=>4     str :data     int32 :crc, action(Crc32, lambda{[type_code,data]}) end ‣ Specific chunks can inhert from Chunk: class IHDR < Chunk     str :type_code, "IHDR"     section :data do         int32 :width         int32 :height          byte :bit_depth, 8         byte :color_type, Png::COLOR_TYPE_TRUECOLOR         byte :comp_method, 0         byte :filter_method, 0         byte :interlace_method, 0     end  end 08-28-2010 Nephi Johnson 40
  • 41.
    ... > Funder> Png Example ‣ Final core PNG class: class Png < Funder     str :png_header, "211PNGrn032n"     field :ihdr, IHDR     field :srgb, SRGB     field :idat, IDAT     field :iend, IEND end 08-28-2010 Nephi Johnson 41
  • 42.
    ... > Funder> Png – In Use >> require 'formats/png' => true >> p = Png.new => #<Png png_header="211PNGrn032n" ihdr=#<IHDR> srgb=#<SRGB> idat=#<IDAT>  iend=#<IEND>> >> p.ihdr.data.width.value = p.ihdr.data.height.value = 200 => 200 >> p.idat.data.red.value = 0xff => 255 >> p.idat.data.green.value = 0 => 0 >> p.idat.data.blue.value = 0 => 0 >> File.open("test.png", "w"){|f| f.write p.to_out } => 696 08-28-2010 Nephi Johnson 42
  • 43.
    ... > Funder> Fuzzing ‣ My fuzzing code is similar to the example code, but has ‣ more options, control ‣ a form static inheritance to propagate options from classes to subclass instances ‣ random and deterministic fuzzing ‣ svn checkout the source or browse it online ‣ (I'm probably desperately out of time by the time I reach this slide) 08-28-2010 Nephi Johnson 43
  • 44.
    The End 08-28-2010 Nephi Johnson 44