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.
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 |
# File 'lib/rex/poly/machine/machine.rb', line 351 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.
380 381 382 |
# File 'lib/rex/poly/machine/machine.rb', line 380 def assemble( asm ) return Metasm::Shellcode.assemble( @cpu, asm ).encode_string end |
#block_exist?(name) ⇒ Boolean
Does a given block exist?
558 559 560 |
# File 'lib/rex/poly/machine/machine.rb', line 558 def block_exist?( name ) return @blocks.include?( name ) end |
#block_next(target_block) ⇒ Object
Get the block next to the target block.
601 602 603 604 605 606 607 608 |
# File 'lib/rex/poly/machine/machine.rb', line 601 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.
541 542 543 544 545 546 547 548 549 550 551 552 553 |
# File 'lib/rex/poly/machine/machine.rb', line 541 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.
587 588 589 590 591 592 593 594 595 596 |
# File 'lib/rex/poly/machine/machine.rb', line 587 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
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 |
# File 'lib/rex/poly/machine/machine.rb', line 504 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.
523 524 525 526 527 528 529 530 531 532 533 |
# File 'lib/rex/poly/machine/machine.rb', line 523 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.
426 427 428 429 430 431 432 |
# File 'lib/rex/poly/machine/machine.rb', line 426 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.
613 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 |
# File 'lib/rex/poly/machine/machine.rb', line 613 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)
387 388 389 390 391 392 |
# File 'lib/rex/poly/machine/machine.rb', line 387 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.
418 419 420 |
# File 'lib/rex/poly/machine/machine.rb', line 418 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.
404 405 406 |
# File 'lib/rex/poly/machine/machine.rb', line 404 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.
397 398 399 |
# File 'lib/rex/poly/machine/machine.rb', line 397 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.
411 412 413 |
# File 'lib/rex/poly/machine/machine.rb', line 411 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.
373 374 375 |
# File 'lib/rex/poly/machine/machine.rb', line 373 def native_size nil end |
#release_temp_variable ⇒ Object
If the temp variable was assigned we release it.
437 438 439 440 441 442 443 444 445 446 447 448 449 450 |
# File 'lib/rex/poly/machine/machine.rb', line 437 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.
575 576 577 578 579 580 581 582 |
# File 'lib/rex/poly/machine/machine.rb', line 575 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).
479 480 481 |
# File 'lib/rex/poly/machine/machine.rb', line 479 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.
494 495 496 497 498 |
# File 'lib/rex/poly/machine/machine.rb', line 494 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.
487 488 489 |
# File 'lib/rex/poly/machine/machine.rb', line 487 def solution_push( permutation ) @solution.push( permutation, @reg_available, @reg_consumed, @variables ) end |
#variable_exist?(name) ⇒ Boolean
Does a given block exist?
565 566 567 |
# File 'lib/rex/poly/machine/machine.rb', line 565 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.
455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 |
# File 'lib/rex/poly/machine/machine.rb', line 455 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 |