class Rex::Poly::LogicalBlock

This class represents a logical block which is defined as a concise portion of code that may have one or more functionally equivalent implementations. A logical block should serve a very specific purpose, and any permutations beyond the first should result in exactly the same functionality without any adverse side effects to other blocks.

Like blocks of code, LogicalBlock's can depend on one another in terms of ordering and precedence. By marking blocks as dependent on another, a hierarchy begins to form. This is a block dependency graph.

To add permutations to a LogicalBlock, they can either be passed in as a list of arguments to the constructor following the blocks name or can be added on the fly by calling the add_perm method. To get a random permutation, the rand_perm method can be called.

To mark one block as depending on another, the depends_on method can be called with zero or more LogicalBlock instances as parameters.

Attributes

generated[RW]

Whether or not this block has currently been generated for a given iteration.

offset[RW]

This attributes contains the currently assigned offset of the permutation associated with this block into the polymorphic buffer that is being generated.

Public Class Methods

new(name, *perms) click to toggle source

Initializes the logical block's name along with zero or more specific blocks.

# File lib/rex/poly/block.rb, line 75
def initialize(name, *perms)
  @name  = name

  reset

  add_perm(*perms)
end

Public Instance Methods

add_perm(*perms) click to toggle source

Adds zero or more specific permutations that may be represented either as strings or as Proc's to be called at evaluation time.

# File lib/rex/poly/block.rb, line 152
def add_perm(*perms)
  @perms.concat(perms)
end
clobbers(*registers) click to toggle source

Defines the list of zero or more LogicalRegister's that this block clobbers.

# File lib/rex/poly/block.rb, line 219
def clobbers(*registers)
  @clobbers = registers
end
depends_on(*depends) click to toggle source

Sets the blocks that this block instance depends on.

# File lib/rex/poly/block.rb, line 200
def depends_on(*depends)
  @depends = depends.dup

  # Increment dependent references
  @depends.each { |b| b.ref }
end
deref() click to toggle source

Increments the number of blocks that have completed their dependency pass on this block. This number should never become higher than the `@references` attribute.

@see ref

# File lib/rex/poly/block.rb, line 137
def deref
  @used_references += 1
end
each_clobbers(&block) click to toggle source

Enumerates each register instance that is clobbered by this block.

# File lib/rex/poly/block.rb, line 226
def each_clobbers(&block)
  @clobbers.each(&block)
end
generate(save_registers = nil, state = nil, badchars = nil) click to toggle source

Generates the polymorphic buffer that results from this block and any of the blocks that it either directly or indirectly depends on. A list of register numbers to be saved can be passed in as an argument.

This method is not thread safe. To call this method on a single block instance from within multiple threads, be sure to encapsulate the calls inside a locked context.

# File lib/rex/poly/block.rb, line 239
def generate(save_registers = nil, state = nil, badchars = nil)
  # Create a localized state instance if one was not supplied.
  state = Rex::Poly::State.new if (state == nil)
  buf   = nil
  cnt   = 0

  # This is a lame way of doing this.  We just try to generate at most 128
  # times until we don't have badchars.  The reason we have to do it this
  # way is because of the fact that badchars can be introduced through
  # block offsetting and register number selection which can't be readily
  # predicted or detected during the generation phase.  In the future we
  # can make this better, but for now this will have to do.
  begin
    buf = do_generate(save_registers, state, badchars)

    if (buf and
        (badchars.nil? or Rex::Text.badchar_index(buf, badchars).nil?))
      break
    end
  end while ((cnt += 1) < 128)

  # If we passed 128 tries, then we can't succeed.
  buf = nil if (cnt >= 128)

  buf
end
last_reference?() click to toggle source

Returns true if there is only one block reference remaining.

# File lib/rex/poly/block.rb, line 144
def last_reference?
  (@references - @used_references <= 0)
end
name() click to toggle source

Returns the block's name.

# File lib/rex/poly/block.rb, line 102
def name
  @name
end
next_blocks(*blocks) click to toggle source

Defines the next blocks, but not in a dependency fashion but rather in a linking of separate block contexts.

# File lib/rex/poly/block.rb, line 211
def next_blocks(*blocks)
  @next_blocks = blocks.dup
end
offset_of(lblock) click to toggle source

Returns the offset of a block. If the active state for this instance is operating in the first phase, then zero is always returned. Otherwise, the correct offset for the supplied block is returned.

# File lib/rex/poly/block.rb, line 271
def offset_of(lblock)
  if (@state.first_phase)
    0
  else
    if (lblock.kind_of?(SymbolicBlock::End))
      @state.curr_offset
    else
      lblock.offset
    end
  end
end
once() click to toggle source

Returns true if this block is a 'once' block. That is, this block is dependend upon by multiple blocks but should only be generated once.

# File lib/rex/poly/block.rb, line 119
def once
  @once
end
once=(tf) click to toggle source

Flags whether or not the block should only be generated once. This can be used to mark a blog as being depended upon by multiple blocks, but making it such that it is only generated once.

# File lib/rex/poly/block.rb, line 111
def once=(tf)
  @once = tf
end
rand_perm() click to toggle source

Returns a random permutation that is encapsulated in a Permutation class instance.

# File lib/rex/poly/block.rb, line 160
def rand_perm
  perm = nil

  if (@state.badchars)
    perm = rand_perm_badchars
  else
    perm = Permutation.new(@perms[rand(@perms.length)], self)
  end

  if (perm.nil?)
    raise RuntimeError, "Failed to locate a valid permutation."
  end

  perm
end
rand_perm_badchars() click to toggle source

Returns a random permutation that passes any necessary bad character checks.

# File lib/rex/poly/block.rb, line 180
def rand_perm_badchars
  idx = rand(@perms.length)
  off = 0

  while (off < @perms.length)
    p = @perms[(idx + off) % @perms.length]

    if (p.kind_of?(Proc) or
        @state.badchars.nil? or
        Rex::Text.badchar_index(p, @state.badchars).nil?)
      return Permutation.new(p, self)
    end

    off += 1
  end
end
ref() click to toggle source

Increments the number of blocks that depend on this block.

@see deref

# File lib/rex/poly/block.rb, line 127
def ref
  @references += 1
end
regnum_of(reg) click to toggle source

Returns the register number associated with the supplied LogicalRegister instance. If the active state for this instance is operating in the first phase, then zero is always returned. Otherwise, the correct register number is returned based on what is currently assigned to the supplied LogicalRegister instance, if anything.

# File lib/rex/poly/block.rb, line 290
def regnum_of(reg)
  (@state.first_phase) ? 0 : reg.regnum
end
reset() click to toggle source

Resets the block back to its starting point.

# File lib/rex/poly/block.rb, line 86
def reset
  @perms           = []
  @depends         = []
  @next_blocks     = []
  @clobbers        = []
  @offset          = nil
  @state           = nil
  @once            = false
  @references      = 0
  @used_references = 0
  @generated       = false
end
size_of(lblock) click to toggle source
# File lib/rex/poly/block.rb, line 294
def size_of(lblock)
  @state.block_list.map { |b, p|
    if b == lblock
      return p.length
    end
  }
  0
end

Protected Instance Methods

do_generate(save_registers, state, badchars) click to toggle source

Performs the actual polymorphic buffer generation. Called from generate

# File lib/rex/poly/block.rb, line 321
def do_generate(save_registers, state, badchars)
  # Reset the state in case it was passed in.
  state.reset

  # Set the bad character list
  state.badchars = badchars if (badchars)

  # Consume any registers that should be saved.
  save_registers.each { |reg|
    state.consume_regnum(reg)
  } if (save_registers)

  # Build the linear list of blocks that will be processed.  This
  # list is built in a dynamic fashion based on block dependencies.
  # The list that is returned is an Array of which each element is a two
  # member array, the first element being the LogicalBlock instance that
  # the permutation came from and the second being an instance of the
  # Permutation class associated with the selected permutation.
  block_list = generate_block_list(state)

  # Transition into the second phase which enables offset_of and regnum_of
  # calls to return real values.
  state.first_phase = false

  # Now that every block has been assigned an offset, generate the
  # buffer block by block, assigning registers as necessary.
  block_list.each { |b|

    # Generate the next permutation and append it to the buffer.
    begin
      state.buffer += b[1].to_s
    # If an invalid register exception is raised, try to consume a random
    # register from the register's associated architecture register
    # number set.
    rescue InvalidRegisterError => e
      e.reg.regnum = state.consume_regnum_from_set(e.reg.class.regnum_set)
      retry
    end

    # Remove any of the registers that have been clobbered by this block
    # from the list of consumed register numbers so that they can be used
    # in the future.
    b[0].each_clobbers { |reg|
      begin
        state.defecate_regnum(reg.regnum)

        reg.regnum = nil
      rescue InvalidRegisterError
      end
    }

  }

  # Finally, return the buffer that has been created.
  state.buffer
end
generate_block_list(state, level=0) click to toggle source

Generates the linear list of block permutations which is stored in the supplied state instance. This is done prior to assigning blocks offsets

# File lib/rex/poly/block.rb, line 382
def generate_block_list(state, level=0)
  if @depends.length > 1
    @depends.length.times {
      f = rand(@depends.length)
      @depends.push(@depends.delete_at(f))
    }
  end

  @depends.length.times { |cidx|

    pass = false

    while (not pass)

      if (@depends[cidx].generated)
        break

      # If this dependent block is a once block and the magic 8 ball turns
      # up zero, skip it and let a later block pick it up.  We only do this
      # if we are not the last block to have a dependency on this block.
      elsif ((@depends[cidx].once) and
          (rand(2).to_i == 0) and
          (@depends[cidx].last_reference? == false))
        break
      end

      # Generate this block
      @depends[cidx].generate_block_list(state, level+1)

      if level != 0
        return
      else
        @depends.length.times {
          f = rand(@depends.length)
          @depends.push(@depends.delete_at(f))
        }

        next
      end
    end

    next
  }

  self.deref

  # Assign the instance local state for the duration of this generation
  @state = state

  # Select a random permutation
  perm = rand_perm

  # Set our block offset to the current state offset
  self.offset = state.curr_offset

  # Flag ourselves as having been generated for this iteration.
  self.generated = true

  # Adjust the current offset based on the permutations length
  state.curr_offset += perm.length

  # Add it to the linear list of blocks
  state.block_list << [ self, perm ]

  # Generate all the blocks that follow this one.
  @next_blocks.each { |b|
    b.generate_block_list(state)
  }

  # Return the state's block list
  state.block_list
end