Class: Rex::Poly::Machine
- Inherits:
-
Object
- Object
- Rex::Poly::Machine
- Defined in:
- lib/rex/poly/machine/machine.rb
Overview
A machine capable of creating a small blob of code in a metamorphic kind of way. Note: this is designed to perform an exhaustive search for a solution and can be slow. If you need a speedier option, the origional Rex::Polly::Block stuff is a better choice.
Direct Known Subclasses
Defined Under Namespace
Classes: Block, InvalidPermutation, Permutation, Primitive, Solution, SymbolicPermutation, UnallowedPermutation, UndefinedPermutation
Constant Summary collapse
- QWORD =
8
- DWORD =
4
- WORD =
2
- BYTE =
1
Instance Method Summary collapse
-
#assemble(asm) ⇒ Object
Use METASM to assemble a line of asm using this machines current cpu.
-
#block_exist?(name) ⇒ Boolean
Does a given block exist?.
-
#block_next(target_block) ⇒ Object
Get the block next to the target block.
-
#block_offset(name) ⇒ Object
Get the offset for a blocks active permutation.
-
#block_previous(target_block) ⇒ Object
Get the block previous to the target block.
-
#create_block(name, *permutation_sources) ⇒ Object
Create a block by name and add in its list of permutations.
-
#create_block_primitive(block_name, primitive_name, *args) ⇒ Object
Create a block which is based on a primitive defined by this machine.
-
#create_variable(name, reg = nil) ⇒ Object
Create a variable by name which will be assigned a register during generation.
-
#generate ⇒ Object
Try to generate a solution.
-
#initialize(badchars, cpu) ⇒ Machine
constructor
Create a new machine instance.
-
#is_valid?(data) ⇒ Boolean
Check if a data blob is valid against the badchar list (or perform any other validation here).
-
#make_safe_byte(number = nil) ⇒ Object
Generate a 8 bit number whoes bytes are valid in this machine.
-
#make_safe_dword(number = nil) ⇒ Object
Generate a 32 bit number whoes bytes are valid in this machine.
-
#make_safe_qword(number = nil) ⇒ Object
Generate a 64 bit number whoes bytes are valid in this machine.
-
#make_safe_word(number = nil) ⇒ Object
Generate a 16 bit number whoes bytes are valid in this machine.
-
#native_size ⇒ Object
Overloaded by a subclass to return the maximum native general register size supported.
-
#release_temp_variable ⇒ Object
If the temp variable was assigned we release it.
-
#resolve_value(value, size = nil) ⇒ Object
Resolve a given value into either a number literal, a block offset or a variables assigned register.
-
#solution_is_valid? ⇒ Boolean
Check this solution is still currently valid (as offsets change it may not be).
-
#solution_pop ⇒ Object
Backtrack one step in the solution and restore the register/variable state.
-
#solution_push(permutation) ⇒ Object
As the solution advances we save state for each permutation step in the solution.
-
#variable_exist?(name) ⇒ Boolean
Does a given block exist?.
-
#variable_value(name, size = nil) ⇒ Object
Resolve a variable name into its currently assigned register value.
Constructor Details
#initialize(badchars, cpu) ⇒ Machine
Create a new machine instance.
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 |
# File 'lib/rex/poly/machine/machine.rb', line 352 def initialize( badchars, cpu ) @badchars = badchars @cpu = cpu @reg_available = ::Array.new @reg_consumed = ::Array.new @variables = ::Hash.new @blocks = ::Hash.new @primitives = ::Hash.new @solution = Solution.new _create_primitives @blocks['begin'] = Block.new( 'begin' ) @blocks['begin'] << SymbolicPermutation.new( 'begin', self ) _create_variable( 'temp' ) end |
Instance Method Details
#assemble(asm) ⇒ Object
Use METASM to assemble a line of asm using this machines current cpu.
381 382 383 |
# File 'lib/rex/poly/machine/machine.rb', line 381 def assemble( asm ) return Metasm::Shellcode.assemble( @cpu, asm ).encode_string end |
#block_exist?(name) ⇒ Boolean
Does a given block exist?
559 560 561 |
# File 'lib/rex/poly/machine/machine.rb', line 559 def block_exist?( name ) return @blocks.include?( name ) end |
#block_next(target_block) ⇒ Object
Get the block next to the target block.
602 603 604 605 606 607 608 609 |
# File 'lib/rex/poly/machine/machine.rb', line 602 def block_next( target_block ) @blocks.each_key do | current_block | if( block_previous( current_block ) == target_block ) return current_block end end return nil end |
#block_offset(name) ⇒ Object
Get the offset for a blocks active permutation. This is easy for backward references as they will already have been rendered and their sizes known. For forward references we can’t know in advance but the correct value can be known later once the final solution is available and a final pass to generate the raw buffer is made.
542 543 544 545 546 547 548 549 550 551 552 553 554 |
# File 'lib/rex/poly/machine/machine.rb', line 542 def block_offset( name ) if( name == 'end' ) return @solution.offset elsif( @blocks[name] ) @blocks[name].each do | permutation | if( permutation.active ) return permutation.offset end end end # If we are forward referencing a block it will be at least the current solutions offset +1 return @solution.offset + 1 end |
#block_previous(target_block) ⇒ Object
Get the block previous to the target block.
588 589 590 591 592 593 594 595 596 597 |
# File 'lib/rex/poly/machine/machine.rb', line 588 def block_previous( target_block ) previous_block = nil @blocks.each_key do | current_block | if( current_block == target_block ) return previous_block end previous_block = current_block end return nil end |
#create_block(name, *permutation_sources) ⇒ Object
Create a block by name and add in its list of permutations.
XXX: this doesnt support the fuzzy order of block dependencies ala the origional rex::poly
505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 |
# File 'lib/rex/poly/machine/machine.rb', line 505 def create_block( name, *permutation_sources ) # Sanity check we aren't trying to create one of the reserved symbolic blocks. if( name == 'begin' or name == 'end' ) raise RuntimeError, "Unable to add block, '#{name}' is a reserved block name." end # If this is the first time this block is being created, create the block object to hold the permutation list if( not @blocks[name] ) @blocks[name] = Block.new( name ) end # Now create a new permutation object for every one supplied. permutation_sources.each do | source | @blocks[name] << Permutation.new( name, '', self, source ) end return name end |
#create_block_primitive(block_name, primitive_name, *args) ⇒ Object
Create a block which is based on a primitive defined by this machine.
524 525 526 527 528 529 530 531 532 533 534 |
# File 'lib/rex/poly/machine/machine.rb', line 524 def create_block_primitive( block_name, primitive_name, *args ) # Santiy check this primitive is actually available and is not an internal primitive (begins with an _). if( not @primitives[primitive_name] or primitive_name[0] == "_" ) raise RuntimeError, "Unable to add block, Primitive '#{primitive_name}' is not available." end # Sanity check we aren't trying to create one of the reserved symbolic blocks. if( block_name == 'begin' or block_name == 'end' ) raise RuntimeError, "Unable to add block, '#{block_name}' is a reserved block name." end return _create_block_primitive( block_name, primitive_name, *args ) end |
#create_variable(name, reg = nil) ⇒ Object
Create a variable by name which will be assigned a register during generation. We can optionally assign a static register value to a variable if needed.
427 428 429 430 431 432 433 |
# File 'lib/rex/poly/machine/machine.rb', line 427 def create_variable( name, reg=nil ) # Sanity check we aren't trying to create one of the reserved variables. if( name == 'temp' ) raise RuntimeError, "Unable to create variable, '#{name}' is a reserved variable name." end return _create_variable( name, reg ) end |
#generate ⇒ Object
Try to generate a solution.
614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 |
# File 'lib/rex/poly/machine/machine.rb', line 614 def generate if( @blocks.has_key?( 'end' ) ) @blocks.delete( 'end' ) end @blocks['end'] = Block.new( 'end' ) @blocks['end'] << SymbolicPermutation.new( 'end', self, 1 ) # Mix up the permutation orders for each block and create the tree structure. previous = ::Array.new @blocks.each_value do | block | # Shuffle the order of the blocks permutations. block.shuffle # create the tree by adding the current blocks permutations as children of the previous block. current = ::Array.new block.each do | permutation | permutation.remove_children previous.each do | prev | prev.add_child( permutation ) end current << permutation end previous = current end # Shuffle the order of the available registers @reg_available = @reg_available.shuffle # We must try every permutation of the register orders, so if we fail to # generate a solution we rotate the available registers to try again with # a different order. This ensures we perform and exhaustive search. 0.upto( @reg_available.length - 1 ) do @solution.reset # Start from the root node in the solution space and generate a # solution by traversing the solution space's tree structure. if( @blocks['begin'].solve ) # Return the solutions buffer (perform a last pass to fixup all offsets)... return @solution.buffer end @reg_available.push( @reg_available.shift ) end # :( nil end |
#is_valid?(data) ⇒ Boolean
Check if a data blob is valid against the badchar list (or perform any other validation here)
388 389 390 391 392 393 |
# File 'lib/rex/poly/machine/machine.rb', line 388 def is_valid?( data ) if( data.nil? ) return false end return Rex::Text.badchar_index( data, @badchars ).nil? end |
#make_safe_byte(number = nil) ⇒ Object
Generate a 8 bit number whoes bytes are valid in this machine.
419 420 421 |
# File 'lib/rex/poly/machine/machine.rb', line 419 def make_safe_byte( number=nil ) return _make_safe_number( BYTE, number ) & 0xFF end |
#make_safe_dword(number = nil) ⇒ Object
Generate a 32 bit number whoes bytes are valid in this machine.
405 406 407 |
# File 'lib/rex/poly/machine/machine.rb', line 405 def make_safe_dword( number=nil ) return _make_safe_number( DWORD, number ) & 0xFFFFFFFF end |
#make_safe_qword(number = nil) ⇒ Object
Generate a 64 bit number whoes bytes are valid in this machine.
398 399 400 |
# File 'lib/rex/poly/machine/machine.rb', line 398 def make_safe_qword( number=nil ) return _make_safe_number( QWORD, number ) & 0xFFFFFFFFFFFFFFFF end |
#make_safe_word(number = nil) ⇒ Object
Generate a 16 bit number whoes bytes are valid in this machine.
412 413 414 |
# File 'lib/rex/poly/machine/machine.rb', line 412 def make_safe_word( number=nil ) return _make_safe_number( WORD, number ) & 0xFFFF end |
#native_size ⇒ Object
Overloaded by a subclass to return the maximum native general register size supported.
374 375 376 |
# File 'lib/rex/poly/machine/machine.rb', line 374 def native_size nil end |
#release_temp_variable ⇒ Object
If the temp variable was assigned we release it.
438 439 440 441 442 443 444 445 446 447 448 449 450 451 |
# File 'lib/rex/poly/machine/machine.rb', line 438 def release_temp_variable if( @variables['temp'] ) regnum = @variables['temp'] # Sanity check the temp variable was actually assigned (it may not have been if the last permutation didnot use it) if( regnum ) # place the assigned register back in the available list for consumption later. @reg_available.push( @reg_consumed.delete( regnum ) ) # unasign the temp vars register @variables['temp'] = nil return true end end return false end |
#resolve_value(value, size = nil) ⇒ Object
Resolve a given value into either a number literal, a block offset or a variables assigned register.
576 577 578 579 580 581 582 583 |
# File 'lib/rex/poly/machine/machine.rb', line 576 def resolve_value( value, size=nil ) if( block_exist?( value ) ) return block_offset( value ) elsif( variable_exist?( value ) ) return variable_value( value, size ) end return value.to_i end |
#solution_is_valid? ⇒ Boolean
Check this solution is still currently valid (as offsets change it may not be).
480 481 482 |
# File 'lib/rex/poly/machine/machine.rb', line 480 def solution_is_valid? return self.is_valid?( @solution.buffer ) end |
#solution_pop ⇒ Object
Backtrack one step in the solution and restore the register/variable state.
495 496 497 498 499 |
# File 'lib/rex/poly/machine/machine.rb', line 495 def solution_pop permutation, @reg_available, @reg_consumed, @variables = @solution.pop @reg_available.push( @reg_available.shift ) end |
#solution_push(permutation) ⇒ Object
As the solution advances we save state for each permutation step in the solution. This lets use rewind at a later stage if the solving algorithm wishes to perform some backtracking.
488 489 490 |
# File 'lib/rex/poly/machine/machine.rb', line 488 def solution_push( permutation ) @solution.push( permutation, @reg_available, @reg_consumed, @variables ) end |
#variable_exist?(name) ⇒ Boolean
Does a given block exist?
566 567 568 |
# File 'lib/rex/poly/machine/machine.rb', line 566 def variable_exist?( name ) return @variables.include?( name ) end |
#variable_value(name, size = nil) ⇒ Object
Resolve a variable name into its currently assigned register value.
456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 |
# File 'lib/rex/poly/machine/machine.rb', line 456 def variable_value( name, size=nil ) # Sanity check we this variable has been created if( not @variables.has_key?( name ) ) raise RuntimeError, "Unknown register '#{name}'." end # Pull out its current register value if it has been assigned one regnum = @variables[ name ] if( not regnum ) regnum = @reg_available.pop if( not regnum ) raise RuntimeError, "Unable to assign variable '#{name}' a register value, none available." end # and add it to the consumed list so we can track it later @reg_consumed << regnum # and now assign the variable the register @variables[ name ] = regnum end # resolve the register number int a string representation (e.g. 0 in x86 is EAX if size is 32) return _register_value( regnum, size ) end |