478 lines
11 KiB
Ruby
478 lines
11 KiB
Ruby
module Rex
|
|
module Poly
|
|
|
|
###
|
|
#
|
|
# This class encapsulates a LogicalBlock permutation. Block permutations can
|
|
# take the form of a static string or a procedure. This makes it possible to
|
|
# have simple blocks and more complicated ones that take into account other
|
|
# variables, such as dynamic registers. The to_s method will return the
|
|
# string version of the permutation, regardless of whether or not the
|
|
# underlying permutation is a string or a procedure.
|
|
#
|
|
###
|
|
class Permutation
|
|
|
|
#
|
|
# Initializes the permutation and its associated block.
|
|
#
|
|
def initialize(perm, block)
|
|
@perm = perm
|
|
@block = block
|
|
end
|
|
|
|
#
|
|
# Returns the length of the string returned by to_s.
|
|
#
|
|
def length
|
|
to_s.length
|
|
end
|
|
|
|
#
|
|
# Returns the string representation of the permutation. If the underlying
|
|
# permutation is a procedure, the procedure is called. Otherwise, the
|
|
# string representation of the permutation is returned.
|
|
#
|
|
def to_s
|
|
if (@perm.kind_of?(Proc))
|
|
@perm.call(@block).to_s
|
|
else
|
|
@perm.to_s
|
|
end
|
|
end
|
|
|
|
attr_reader :perm
|
|
|
|
end
|
|
|
|
###
|
|
#
|
|
# 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.
|
|
#
|
|
###
|
|
class LogicalBlock
|
|
|
|
#
|
|
# Initializes the logical block's name along with zero or more specific
|
|
# blocks.
|
|
#
|
|
def initialize(name, *perms)
|
|
@name = name
|
|
|
|
reset
|
|
|
|
add_perm(*perms)
|
|
end
|
|
|
|
#
|
|
# Resets the block back to its starting point.
|
|
#
|
|
def reset
|
|
@perms = []
|
|
@depends = []
|
|
@next_blocks = []
|
|
@clobbers = []
|
|
@offset = nil
|
|
@state = nil
|
|
@once = false
|
|
@references = 0
|
|
@used_references = 0
|
|
@generated = false
|
|
end
|
|
|
|
#
|
|
# Returns the block's name.
|
|
#
|
|
def name
|
|
@name
|
|
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.
|
|
#
|
|
def once=(tf)
|
|
@once = tf
|
|
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.
|
|
#
|
|
def once
|
|
@once
|
|
end
|
|
|
|
#
|
|
# Increments the number of blocks that depend on this block.
|
|
#
|
|
def ref
|
|
@references += 1
|
|
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.
|
|
#
|
|
def deref
|
|
@used_references += 1
|
|
end
|
|
|
|
#
|
|
# Returns true if there is only one block reference remaining.
|
|
#
|
|
def last_reference?
|
|
(@references - @used_references <= 0)
|
|
end
|
|
|
|
#
|
|
# Adds zero or more specific permutations that may be represented either as
|
|
# strings or as Proc's to be called at evaluation time.
|
|
#
|
|
def add_perm(*perms)
|
|
@perms.concat(perms)
|
|
end
|
|
|
|
#
|
|
# Returns a random permutation that is encapsulated in a Permutation class
|
|
# instance.
|
|
#
|
|
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.
|
|
#
|
|
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
|
|
|
|
#
|
|
# Sets the blocks that this block instance depends on.
|
|
#
|
|
def depends_on(*depends)
|
|
@depends = depends.dup
|
|
|
|
# Increment dependent references
|
|
@depends.each { |b| b.ref }
|
|
end
|
|
|
|
#
|
|
# Defines the next blocks, but not in a dependency fashion but rather in a
|
|
# linking of separate block contexts.
|
|
#
|
|
def next_blocks(*blocks)
|
|
@next_blocks = blocks.dup
|
|
end
|
|
|
|
#
|
|
# Defines the list of zero or more LogicalRegister's that this block
|
|
# clobbers.
|
|
#
|
|
def clobbers(*registers)
|
|
@clobbers = registers
|
|
end
|
|
|
|
#
|
|
# Enumerates each register instance that is clobbered by this block.
|
|
#
|
|
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.
|
|
#
|
|
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 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.
|
|
#
|
|
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 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.
|
|
#
|
|
def regnum_of(reg)
|
|
(@state.first_phase) ? 0 : reg.regnum
|
|
end
|
|
|
|
def size_of(lblock)
|
|
@state.block_list.map { |b, p|
|
|
if b == lblock
|
|
return p.length
|
|
end
|
|
}
|
|
0
|
|
end
|
|
|
|
#
|
|
# This attributes contains the currently assigned offset of the permutation
|
|
# associated with this block into the polymorphic buffer that is being
|
|
# generated.
|
|
#
|
|
attr_accessor :offset
|
|
|
|
#
|
|
# Whether or not this block has currently been generated for a given
|
|
# iteration.
|
|
#
|
|
attr_accessor :generated
|
|
|
|
protected
|
|
|
|
#
|
|
# Performs the actual polymorphic buffer generation. Called from generate
|
|
#
|
|
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
|
|
#
|
|
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
|
|
|
|
end
|
|
|
|
###
|
|
#
|
|
# Symbolic blocks are used as special-case LogicalBlock's that have meaning
|
|
# a more general meaning. For instance, SymbolicBlock::End can be used to
|
|
# symbolize the end of a polymorphic buffer.
|
|
#
|
|
###
|
|
module SymbolicBlock
|
|
|
|
###
|
|
#
|
|
# The symbolic end of a polymorphic buffer.
|
|
#
|
|
###
|
|
class End < LogicalBlock
|
|
def initialize
|
|
super('__SYMBLK_END__')
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|
|
end
|