Module: Ronin::Support::Encoding::Shell

Defined in:
lib/ronin/support/encoding/shell.rb

Overview

Since:

  • 1.0.0

Constant Summary collapse

ESCAPE_BYTES =

Special shell bytes and their escaped Strings.

Since:

  • 1.0.0

{
  0x00 => "\\0",  # $'\0'
  0x07 => "\\a",  # $'\a'
  0x08 => "\\b",  # $'\b'
  0x09 => "\\t",  # $'\t'
  0x0a => "\\n",  # $'\n'
  0x0b => "\\v",  # $'\v'
  0x0c => "\\f",  # $'\f'
  0x0d => "\\r",  # $'\r'
  0x1B => "\\e",  # $'\e'
  0x22 => "\\\"", # \"
  0x5c => "\\\\"  # \\
}
BACKSLASHED_CHARS =

Shell characters that must be back-slashed.

Since:

  • 1.0.0

{
  "\\0"  => "\0",
  "\\a"  => "\a",
  "\\b"  => "\b",
  "\\e"  => "\e",
  "\\t"  => "\t",
  "\\n"  => "\n",
  "\\v"  => "\v",
  "\\f"  => "\f",
  "\\r"  => "\r",
  "\\'"  => "'",
  "\\\"" => '"'
}

Class Method Summary collapse

Class Method Details

.decode(data) ⇒ String

Alias for unescape.

Parameters:

  • data (String)

    The data to shell unescape.

Returns:

  • (String)

    The shell unescaped string.

See Also:

Since:

  • 1.0.0



242
243
244
# File 'lib/ronin/support/encoding/shell.rb', line 242

def self.decode(data)
  unescape(data)
end

.encode(data) ⇒ String

Shell encodes every character in the given data.

Examples:

Encoding::Shell.encode("hello world")
# => "\\x68\\x65\\x6c\\x6c\\x6f\\x0a\\x77\\x6f\\x72\\x6c\\x64"

Parameters:

  • data (String)

    The data to shell encode.

Returns:

  • (String)

    The shell encoded String.

Since:

  • 1.0.0



215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
# File 'lib/ronin/support/encoding/shell.rb', line 215

def self.encode(data)
  encoded = String.new

  if data.valid_encoding?
    data.each_codepoint do |codepoint|
      encoded << encode_byte(codepoint)
    end
  else
    data.each_byte do |byte|
      encoded << encode_byte(byte)
    end
  end

  return encoded
end

.encode_byte(byte) ⇒ String

Encodes the byte as a shell character.

Examples:

Encoding::Shell.encode_byte(0x41)
# => "\\x41"
Encoding::Shell.encode_byte(0x0a)
# => "\\x0a"

Encoding unicode characters:

Encoding::Shell.encode_byte(1001)
# => "\\u1001"

Parameters:

  • byte (Integer)

    The byte value to encode.

Returns:

  • (String)

    The encoded shell character.

Raises:

  • (RangeError)

    The byte value is negative.

Since:

  • 1.0.0



78
79
80
81
82
83
84
85
86
# File 'lib/ronin/support/encoding/shell.rb', line 78

def self.encode_byte(byte)
  if byte >= 0x00 && byte <= 0xff
    "\\x%.2x" % byte
  elsif byte > 0xff
    "\\u%x" % byte
  else
    raise(RangeError,"#{byte.inspect} out of char range")
  end
end

.escape(data) ⇒ String

Shell escapes any special characters in the given data.

Examples:

Encoding::Shell.escape("hello\nworld")
# => "hello\\nworld"

Parameters:

  • data (String)

    The data to shell escape.

Returns:

  • (String)

    The shell escaped string.

Since:

  • 1.0.0



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/ronin/support/encoding/shell.rb', line 154

def self.escape(data)
  escaped = String.new

  if data.valid_encoding?
    data.each_codepoint do |codepoint|
      escaped << escape_byte(codepoint)
    end
  else
    data.each_byte do |byte|
      escaped << escape_byte(byte)
    end
  end

  return escaped
end

.escape_byte(byte) ⇒ String

Escapes the byte as a shell character.

Examples:

Encoding::Shell.escape(0x41)
# => "A"
Encoding::Shell.escape(0x08)
# => "\\b"
Encoding::Shell.escape(0xff)
# => "\\xff"

Escaping unicode characters:

Encoding::Shell.escape(1001)
# => "\\u1001"

Parameters:

  • byte (Integer)

    The byte value to escape.

Returns:

  • (String)

    The escaped shell character.

Raises:

  • (RangeError)

    The integer value is negative.

Since:

  • 1.0.0



112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/ronin/support/encoding/shell.rb', line 112

def self.escape_byte(byte)
  if byte >= 0x00 && byte <= 0xff
    ESCAPE_BYTES.fetch(byte) do
      if byte >= 0x20 && byte <= 0x7e
        byte.chr
      else
        encode_byte(byte)
      end
    end
  else
    encode_byte(byte)
  end
end

.quote(data) ⇒ String

Converts the given data into a double-quoted shell escaped String.

Examples:

Encoding::Shell.quote("hello world")
# => "\"hello world\""
Encoding::Shell.quote("hello\nworld")
# => "$'hello\\nworld'"

Parameters:

  • data (String)

    The given data to shell escape and quote.

Returns:

  • (String)

    The quoted and escaped shell string.

Since:

  • 1.0.0



261
262
263
264
265
266
267
268
269
# File 'lib/ronin/support/encoding/shell.rb', line 261

def self.quote(data)
  if data =~ /[^[:print:]]/
    "$'#{escape(data)}'"
  else
    # NOTE: '$' characters do not need to be escaped in single quoted
    # strings, but they *do* need to be escaped in double quoted strings.
    "\"#{escape(data).gsub('$','\$')}\""
  end
end

.unescape(data) ⇒ String

Shell unescapes the given data.

Examples:

"hello\\nworld".shell_unescape
# => "hello\nworld"

Parameters:

  • data (String)

    The data to shell unescape.

Returns:

  • (String)

    The shell unescaped string.

Since:

  • 1.0.0



183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/ronin/support/encoding/shell.rb', line 183

def self.unescape(data)
  unescaped = String.new(encoding: Encoding::UTF_8)
  scanner   = StringScanner.new(data)

  until scanner.eos?
    unescaped << if (backslash_escape  = scanner.scan(/\\[0abetnvfr\'\"]/)) # \n
                   BACKSLASHED_CHARS.fetch(backslash_escape)
                 elsif (hex_escape     = scanner.scan(/\\x[0-9a-fA-F]+/)) # \XX
                   hex_escape[2..].to_i(16).chr
                 elsif (unicode_escape = scanner.scan(/\\u[0-9a-fA-F]+/)) # \uXXXX
                   unicode_escape[2..].to_i(16).chr(Encoding::UTF_8)
                 else
                   scanner.getch
                 end
  end

  return unescaped
end

.unquote(data) ⇒ String

Removes the quotes an unescapes a shell string.

Examples:

Encoding::Shell.unquote("\"hello \\\"world\\\"\"")
# => "hello \"world\""
Encoding::Shell.unquote("'hello\\'world'")
# => "hello'world"
Encoding::Shell.unquote("$'hello\\nworld'")
# => "hello\nworld"

Parameters:

  • data (String)

    The quoted and escaped shell string to unquote and unescape.

Returns:

  • (String)

    The un-quoted String if the String begins and ends with quotes, or the same String if it is not quoted.

Since:

  • 1.0.0



289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
# File 'lib/ronin/support/encoding/shell.rb', line 289

def self.unquote(data)
  if (data.start_with?("$'") && data.end_with?("'"))
    unescape(data[2..-2])
  elsif (data.start_with?('"') && data.end_with?('"'))
    # NOTE: double quoted strings only support escaping '"', '\', and
    # '$' characters.
    data[1..-2].gsub(/\\["\$]/) do |backslash_char|
      backslash_char[1]
    end
  elsif (data.start_with?("'") && data.end_with?("'"))
    data[1..-2].gsub("\\'","'")
  else
    data
  end
end