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
Whether or not this block has currently been generated for a given iteration.
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
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
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
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
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
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
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
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
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
Returns the block’s name.
# File lib/rex/poly/block.rb, line 102 def name @name end
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
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
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
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
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
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
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
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
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
# 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
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
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