rapid7/metasploit-framework

View on GitHub
modules/nops/x64/simple.rb

Summary

Maintainability
A
0 mins
Test Coverage
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Nop

  def initialize
    super(
      'Name'        => 'Simple',
      'Alias'       => 'x64_simple',
      'Description' => 'An x64 single/multi byte NOP instruction generator.',
      'Author'      => [ 'sf' ],
      'License'     => MSF_LICENSE,
      'Arch'        => ARCH_X64 )

    register_advanced_options( [ OptBool.new( 'RandomNops', [ false, "Generate a random NOP sled", true ] ) ])
    register_advanced_options( [ OptBool.new( 'MultiByte',  [ false, "Generate a multi byte instruction NOP sled", false ] ) ])
  end

  # This instruction list is far from complete (Only single byte instructions and some multi byte ADD/MOV instructions are used).
  # A more complete list might warrant an pseudo assembler (Rex::Arch::X64) instead of hardcoding these.
  INSTRUCTIONS = [    [ "\x90",             0, "nop" ],
            [ "\x91",             0, "xchg eax, ecx" ],
            [ "\x92",             0, "xchg eax, edx" ],
            [ "\x93",             0, "xchg eax, ebx" ],
            [ "\x94",             0, "xchg eax, esp" ],
            [ "\x95",             0, "xchg eax, ebp" ],
            [ "\x96",             0, "xchg eax, esi" ],
            [ "\x97",             0, "xchg eax, edi" ],
            [ "\x98",             0, "cwde" ],
            [ "\x99",             0, "cdq" ],
            [ "\x9B",             0, "wait" ],
            [ "\x9C",             0, "pushfq" ],
            [ "\x9D",             0, "popfq" ],
            [ "\x9E",             0, "sahf" ],
            [ "\x9F",             0, "lahf" ],
            [ "\xFC",             0, "cld" ],
            [ "\xFD",             0, "std" ],
            [ "\xF8",             0, "clc" ],
            [ "\xF9",             0, "cmc" ],
            [ "\x50",             0, "push rax" ],
            [ "\x51",             0, "push rcx" ],
            [ "\x52",             0, "push rdx" ],
            [ "\x53",             0, "push rbx" ],
            [ "\x54",             0, "push rsp" ],
            [ "\x55",             0, "push rbp" ],
            [ "\x56",             0, "push rsi" ],
            [ "\x57",             0, "push rdi" ],
            [ "\x58",             0, "pop rax" ],
            [ "\x59",             0, "pop rcx" ],
            [ "\x5A",             0, "pop rdx" ],
            [ "\x5B",             0, "pop rbx" ],
            [ "\x5C",             0, "pop rsp" ],
            [ "\x5D",             0, "pop rbp" ],
            [ "\x5E",             0, "pop rsi" ],
            [ "\x5F",             0, "pop rdi" ],
            [ "\x04",             1, "add al, 0x??" ],
            [ "\x80\xC3",         1, "add bl, 0x??" ],
            [ "\x80\xC1",         1, "add cl, 0x??" ],
            [ "\x80\xC2",         1, "add dl, 0x??" ],
            [ "\x80\xC4",         1, "add ah, 0x??" ],
            [ "\x80\xC7",         1, "add bh, 0x??" ],
            [ "\x80\xC5",         1, "add ch, 0x??" ],
            [ "\x80\xC6",         1, "add dh, 0x??" ],
            [ "\x66\x05",         2, "add ax, 0x????" ],
            [ "\x66\x81\xC3",     2, "add bx, 0x????" ],
            [ "\x66\x81\xC1",     2, "add cx, 0x????" ],
            [ "\x66\x81\xC2",     2, "add dx, 0x????" ],
            [ "\x66\x81\xC6",     2, "add si, 0x????" ],
            [ "\x66\x81\xC7",     2, "add di, 0x????" ],
            [ "\x66\x41\x81\xC0", 2, "add r8w, 0x????" ],
            [ "\x66\x41\x81\xC1", 2, "add r9w, 0x????" ],
            [ "\x66\x41\x81\xC2", 2, "add r10w, 0x????" ],
            [ "\x66\x41\x81\xC3", 2, "add r11w, 0x????" ],
            [ "\x66\x41\x81\xC4", 2, "add r12w, 0x????" ],
            [ "\x66\x41\x81\xC5", 2, "add r13w, 0x????" ],
            [ "\x66\x41\x81\xC6", 2, "add r14w, 0x????" ],
            [ "\x66\x41\x81\xC7", 2, "add r15w, 0x????" ],
            [ "\x05",             4, "add eax, 0x????????" ],
            [ "\x81\xC3",         4, "add ebx, 0x????????" ],
            [ "\x81\xC1",         4, "add ecx, 0x????????" ],
            [ "\x81\xC2",         4, "add edx, 0x????????" ],
            [ "\x81\xC6",         4, "add esi, 0x????????" ],
            [ "\x81\xC7",         4, "add edi, 0x????????" ],
            [ "\x41\x81\xC0",     4, "add r8d, 0x????????" ],
            [ "\x41\x81\xC1",     4, "add r9d, 0x????????" ],
            [ "\x41\x81\xC2",     4, "add r10d, 0x????????" ],
            [ "\x41\x81\xC3",     4, "add r11d, 0x????????" ],
            [ "\x41\x81\xC4",     4, "add r12d, 0x????????" ],
            [ "\x41\x81\xC5",     4, "add r13d, 0x????????" ],
            [ "\x41\x81\xC6",     4, "add r14d, 0x????????" ],
            [ "\x41\x81\xC7",     4, "add r15d, 0x????????" ],
            [ "\x48\xB8",         8, "mov rax, 0x????????????????" ],
            [ "\x48\xBB",         8, "mov rbx, 0x????????????????" ],
            [ "\x48\xB9",         8, "mov rcx, 0x????????????????" ],
            [ "\x48\xBA",         8, "mov rdx, 0x????????????????" ],
            [ "\x48\xBE",         8, "mov rsi, 0x????????????????" ],
            [ "\x48\xBF",         8, "mov rdi, 0x????????????????" ],
            [ "\x49\xB8",         8, "mov r8, 0x????????????????" ],
            [ "\x49\xB9",         8, "mov r8, 0x????????????????" ],
            [ "\x49\xBA",         8, "mov r10, 0x????????????????" ],
            [ "\x49\xBB",         8, "mov r11, 0x????????????????" ],
            [ "\x49\xBC",         8, "mov r12, 0x????????????????" ],
            [ "\x49\xBD",         8, "mov r13, 0x????????????????" ],
            [ "\x49\xBE",         8, "mov r14, 0x????????????????" ],
            [ "\x49\xBF",         8, "mov r15, 0x????????????????" ],
  ]

  I_OP   = 0
  I_SIZE = 1
  I_TEXT = 2

  REGISTERS = [        [ "rsp", "esp", "sp" ],
            [ "rbp", "ebp", "bp" ],
            [ "rax", "eax", "ax", "al", "ah" ],
            [ "rbx", "ebx", "bx", "bl", "bh" ],
            [ "rcx", "ecx", "cx", "cl", "ch" ],
            [ "rdx", "edx", "dx", "dl", "dh" ],
            [ "rsi", "esi", "si" ],
            [ "rdi", "edi", "di" ],
            [ "r8", "r8d", "r8w", "r8b" ],
            [ "r9", "r9d", "r9w", "r9b" ],
            [ "r10", "r10d", "r10w", "r10b" ],
            [ "r11", "r11d", "r11w", "r11b" ],
            [ "r12", "r12d", "r12w", "r12b" ],
            [ "r13", "r13d", "r13w", "r13b" ],
            [ "r14", "r14d", "r14w", "r14b" ],
            [ "r15", "r15d", "r15w", "r15b" ],
  ]

  def generate_random_sled( length, instructions, badchars, badregs )
    opcodes_stack = []
    total_size    = 0
    sled          = ''
    try_count     = 0
    good_bytes    = []

    # Fixup SaveRegisters so for example, if we wish to preserve RSP we also should also preserve ESP and SP
    REGISTERS.each { | reg | reg.each { |x| badregs += reg if badregs.include?( x ) } }
    badregs = badregs.uniq()

    # If we are preserving RSP we should avoid all PUSH/POP instructions...
    if badregs.include?( "rsp" )
      badregs.push( 'push' )
      badregs.push( 'pop' )
    end

    # Loop while we still have bytes to fill in the sled...
    while true
      # Pick a random instruction and see if we can use it...
      instruction = instructions[ rand(instructions.length) ]

      # Avoid using any bad mnemonics/registers...
      try_another = false
      badregs.each do | bad |
        try_another = true if instruction[I_TEXT].include?( bad.downcase() )
        break if try_another
      end
      next if try_another

      # Get the first bytes of the chosen instructions opcodes...
      opcodes = instruction[I_OP]

      # If their are additional bytes to append, do it now...
      1.upto( instruction[I_SIZE] ) do | i |
        opcodes += Rex::Text.rand_char( badchars )
      end

      # If we have gone over the requested sled length, try again.
      if total_size + opcodes.length > length
        try_count -= 1

        # If we have tried unsuccessfully 32 times we start unwinding the chosen opcode_stack to speed things up
        if try_count == 0
          pop_count = 4
          while opcodes_stack.length and pop_count
            total_size -= opcodes_stack.pop().length
            pop_count -= 1
          end
        end
        next
      end

      # Reset the try_count for the next iteration.
      try_count = 32

      # save the opcodes we just generated.
      opcodes_stack.push( opcodes )

      # Increment the total size appropriately.
      total_size += opcodes.length

      # Once we have generated the requested amount of bytes we can finish.
      break if total_size == length
    end

    # Now that we have chosen all the instructions to use we must generate the actual sled.
    opcodes_stack.each do | opcodes_ |
      sled += opcodes_
    end

    return sled
  end

  def generate_sled( length, opts )
    badchars  = opts['BadChars'] || ''
    random    = opts['Random'] || datastore['RandomNops']
    badregs   = opts['SaveRegisters'] || []
    good_instructions = []
    sled      = ''

    # Weed out any instructions which will contain a bad char/instruction...
    INSTRUCTIONS.each do | instruction |
      good = true;
      # If the instruction contains some bad chars we wont use it...
      badchars.each_char do | bc |
        if instruction[I_OP].include?( bc )
          good = false
          break
        end
      end
      # if we are only to generate single byte instructions, weed out the multi byte ones...
      good = false if instruction[I_SIZE] > 0 and not datastore['MultiByte']

      good_instructions.push( instruction ) if good
    end

    # After we have pruned the instruction list we can proceed to generate a sled...
    if good_instructions.empty?
      # If we are left with no valid instructions to use we simple can't generate a sled.
      sled = nil
    elsif not random
      if not badchars.include?( "\x90" )
        sled += "\x90" * length
      else
        sled = nil
      end
    else
      sled += generate_random_sled( length, good_instructions, badchars, badregs )
    end

    return sled
  end
end