Module: Ronin::Support::Encoding::C

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

Overview

Contains methods for encoding/decoding escaping/unescaping C data.

Core-Ext Methods

Since:

  • 1.0.0

Constant Summary collapse

ESCAPE_BYTES =

Special C bytes and their escaped Strings.

Since:

  • 1.0.0

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

C 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

Decodes the C encoded data.

Parameters:

  • data (String)

    The given C data to decode.

Returns:

  • (String)

    The decoded data.

See Also:

Since:

  • 1.0.0



164
165
166
# File 'lib/ronin/support/encoding/c.rb', line 164

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

.encode(data) ⇒ String

Encodes each character of the given data as C escaped characters.

Examples:

Encoding::C.encode("hello")
# => "\\x68\\x65\\x6c\\x6c\\x6f"

Parameters:

  • data (String)

    The given data to encode.

Returns:

  • (String)

    The C encoded String.

Since:

  • 1.0.0



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/ronin/support/encoding/c.rb', line 137

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 a byte as a C escaped String.

Examples:

Encoding::C.encode_byte(0x41)
# => "\\x41"
Encoding::C.encode_byte(0x100)
# => "\\u1000"
Encoding::C.encode_byte(0x10000)
# => "\\U000100000"

Parameters:

  • byte (Integer)

    The byte value to encode.

Returns:

  • (String)

    The escaped C character.

Since:

  • 1.0.0



57
58
59
60
61
62
63
64
65
66
67
# File 'lib/ronin/support/encoding/c.rb', line 57

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

.escape(data) ⇒ String

Escapes the C encoded data.

Parameters:

  • data (String)

    The data to C escape.

Returns:

  • (String)

    The C escaped String.

Since:

  • 1.0.0



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/ronin/support/encoding/c.rb', line 177

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 a byte as a C character.

Examples:

Encoding::C.escape_byte(0x41)
# => "A"
Encoding::C.escape_byte(0x22)
# => "\\\""
Encoding::C.escape_byte(0x7f)
# => "\\x7F"

Escaping unicode characters:

Encoding::C.escape_byte(0xffff)
# => "\\uFFFF"
Encoding::C.escape_byte(0x10000)
# => "\\U000100000"

Parameters:

  • byte (Integer)

    The byte value to escape.

Returns:

  • (String)

    The escaped C character.

Raises:

  • (RangeError)

    The integer value is negative.

Since:

  • 1.0.0



110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/ronin/support/encoding/c.rb', line 110

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

Escapes and quotes the given data as a C string.

Examples:

Encoding::C.quote("hello\nworld\n")
# => "\"hello\\nworld\\n\""

Parameters:

  • data (String)

    The given data to escape and quote.

Returns:

  • (String)

    The quoted C string.

Since:

  • 1.0.0



255
256
257
# File 'lib/ronin/support/encoding/c.rb', line 255

def self.quote(data)
  "\"#{escape(data)}\""
end

.unescape(data) ⇒ String

Unescapes the given C escaped data.

Examples:

Encoding::C.unescape("\\x68\\x65\\x6c\\x6c\\x6f\\x20\\x77\\x6f\\x72\\x6c\\x64")
# => "hello world"

Parameters:

  • data (String)

    The given C escaped data.

Returns:

  • (String)

    The unescaped C String.

Since:

  • 1.0.0



219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# File 'lib/ronin/support/encoding/c.rb', line 219

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

  until scanner.eos?
    unescaped << if (hex_escape          = scanner.scan(/\\x[0-9a-fA-F]{1,2}/)) # \xXX
                   hex_escape[2..].to_i(16).chr
                 elsif (unicode_escape   = scanner.scan(/\\u[0-9a-fA-F]{4}|\\U[0-9a-fA-F]{8}/)) # \uXXXX or \UXXXXXXXX
                   unicode_escape[2..].to_i(16).chr(Encoding::UTF_8)
                 elsif (octal_escape     = scanner.scan(/\\[0-7]{1,3}/)) # \N, \NN, or \NNN
                   octal_escape[1..].to_i(8).chr
                 elsif (backslash_escape = scanner.scan(/\\./)) # \[A-Za-z]
                   BACKSLASHED_CHARS.fetch(backslash_escape) do
                     backslash_escape[1]
                   end
                 else
                   scanner.getch
                 end
  end

  return unescaped
end

.unquote(data) ⇒ String

Unquotes and unescapes the given C string.

Examples:

Encoding::C.unquote("\"hello\\nworld\"")
# => "hello\nworld"

Parameters:

  • data (String)

    The given C string.

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



273
274
275
276
277
278
279
280
# File 'lib/ronin/support/encoding/c.rb', line 273

def self.unquote(data)
  if ((data.start_with?('"') && data.end_with?('"')) ||
      (data.start_with?("'") && data.end_with?("'")))
    unescape(data[1..-2])
  else
    data
  end
end