# frozen_string_literal: true
#
# ronin-payloads - A Ruby micro-framework for writing and running exploit
# payloads.
#
# Copyright (c) 2007-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
#
# ronin-payloads is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ronin-payloads is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with ronin-payloads. If not, see .
#
require 'ronin/payloads/cli/command'
require 'ronin/payloads/cli/format_option'
require 'ronin/payloads/cli/encoder_methods'
require 'ronin/payloads/encoders/pipeline'
module Ronin
module Payloads
class CLI
module Commands
#
# Encodes data using the encoder(s).
#
# ## Usage
#
# ronin-payloads encode [options] {--string STRING | FILE}
#
# ## Options
#
# -F hex|c|shell|powershell|xml|html|js|ruby,
# --format Formats the outputed data
# -E, --encoder ENCODER The encoder name to load
# -p, --param ENCODER.NAME=VALUE Sets a param on an encoder
# -s, --string STRING The string to encode
# -h, --help Print help information
#
# ## Arguments
#
# [FILE] The optional file to read and encode
#
class Encode < Command
include FormatOption
include EncoderMethods
usage '[options] {--string STRING | FILE}'
option :encoder, short: '-E',
value: {
type: String,
usage: 'ENCODER'
},
desc: 'The encoder name to load' do |id|
@encoders << id
end
option :param, short: '-p',
value: {
type: /\A[^\.=]+\.[^=]++=.+\z/,
usage: 'ENCODER.NAME=VALUE'
},
desc: 'Sets a param on an encoder' do |str|
prefix, value = str.split('=',2)
encoder, name = prefix.split('.',2)
@params[encoder][name.to_sym] = value
end
option :string, short: '-s',
value: {
type: String,
usage: 'STRING'
},
desc: 'The string to encode'
argument :file, required: false,
desc: 'The optional file to read and encode'
description 'Encodes data using the encoder(s)'
man_page 'ronin-payloads-encode.1'
# The encoder names to load.
#
# @return [Array]
attr_reader :encoders
# The params for the encoders.
#
# @return [Hash{String => Hash{String => String}}]
attr_reader :params
# The encoder pipeline.
#
# @return [Encoders::Pipeline, nil]
attr_reader :pipeline
#
# Initializes the `ronin-payloads encode` command.
#
# @param [Hash{Symbol => Object}] kwargs
# Additional keyword arguments.
#
def initialize(**kwargs)
super(**kwargs)
@encoders = []
@params = Hash.new { |hash,key| hash[key] = {} }
end
#
# Runs the `ronin-payloads encode` command.
#
# @param [String, nil] file
# The optional file to read data from and encode.
#
def run(file=nil)
build_pipeline
print_data(encode_data(load_data(file)))
end
#
# Builds the encoder pipeline.
#
def build_pipeline
@pipeline = Encoders::Pipeline.new
@encoders.each do |encoder_id|
encoder_class = load_encoder(encoder_id)
params = @params[encoder_id]
encoder = initialize_encoder(encoder_class, params: params)
validate_encoder(encoder)
@pipeline << encoder
end
end
#
# Validates the loaded encoders.
#
# @raise [Ronin::Core::Params::RequiredParam]
# One of the required params was not set.
#
# @raise [ValidationError]
# Another encoder validation error occurred.
#
def validate_encoder(encoder)
encoder.validate
rescue Core::Params::ParamError, ValidationError => error
print_error "failed to validate the encoder #{encoder.class_id}: #{error.message}"
exit(1)
rescue => error
print_error "an unhandled exception occurred while validating the encoder #{encoder.class_id}"
print_exception(error)
exit(-1)
end
#
# Loads the data to encode.
#
# @return [String]
#
def load_data(file=nil)
if file
unless File.file?(file)
print_error "No such file or directory: #{file}"
exit(-1)
end
File.binread(file)
elsif options[:string]
options[:string]
else
stdin.read
end
end
#
# Encodes the data.
#
# @param [String] data
#
# @return [String]
#
def encode_data(data)
@pipeline.encode(data)
rescue => error
print_error "unhandled exception occurred while encoding data"
print_exception(error)
exit(1)
end
end
end
end
end
end