module Ronin::Support::Encoding::C

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

## Core-Ext Methods

@api public

Constants

BACKSLASHED_CHARS

C characters that must be back-slashed.

ESCAPE_BYTES

Special C bytes and their escaped Strings.

Public Class Methods

decode(data) click to toggle source

Decodes the C encoded data.

@param [String] data

The given C data to decode.

@return [String]

The decoded data.

@see unescape

# File lib/ronin/support/encoding/c.rb, line 164
def self.decode(data)
  unescape(data)
end
encode(data) click to toggle source

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

@param [String] data

The given data to encode.

@return [String]

The C encoded String.

@example

Encoding::C.encode("hello")
# => "\\x68\\x65\\x6c\\x6c\\x6f"
# 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) click to toggle source

Encodes a byte as a C escaped String.

@param [Integer] byte

The byte value to encode.

@return [String]

The escaped C character.

@example

Encoding::C.encode_byte(0x41)
# => "\\x41"
Encoding::C.encode_byte(0x100)
# => "\\u1000"
Encoding::C.encode_byte(0x10000)
# => "\\U000100000"
# 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) click to toggle source

Escapes the C encoded data.

@param [String] data

The data to C escape.

@return [String]

The C escaped String.
# 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) click to toggle source

Escapes a byte as a C character.

@param [Integer] byte

The byte value to escape.

@return [String]

The escaped C character.

@raise [RangeError]

The integer value is negative.

@example

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

@example Escaping unicode characters:

Encoding::C.escape_byte(0xffff)
# => "\\uFFFF"
Encoding::C.escape_byte(0x10000)
# => "\\U000100000"
# 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) click to toggle source

Escapes and quotes the given data as a C string.

@param [String] data

The given data to escape and quote.

@return [String]

The quoted C string.

@example

Encoding::C.quote("hello\nworld\n")
# => "\"hello\\nworld\\n\""
# File lib/ronin/support/encoding/c.rb, line 256
def self.quote(data)
  "\"#{escape(data)}\""
end
unescape(data) click to toggle source

Unescapes the given C escaped data.

@param [String] data

The given C escaped data.

@return [String]

The unescaped C String.

@example

Encoding::C.unescape("\\x68\\x65\\x6c\\x6c\\x6f\\x20\\x77\\x6f\\x72\\x6c\\x64")
# => "hello world"
# 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 << case (char = scanner.getch)
                 when "\\" # backslash
                   if (hex_char        = scanner.scan(/x[0-9a-fA-F]{1,2}/)) # \xXX
                     hex_char[1..].to_i(16).chr
                   elsif (hex_char     = scanner.scan(/u[0-9a-fA-F]{4,8}/)) # \u..
                     hex_char[1..].to_i(16).chr(Encoding::UTF_8)
                   elsif (octal_char   = scanner.scan(/[0-7]{1,3}/)) # \N, \NN, or \NNN
                     octal_char.to_i(8).chr
                   elsif (special_char = scanner.getch) # \[A-Za-z]
                     BACKSLASHED_CHARS.fetch(special_char,special_char)
                   end
                 else
                   char
                 end
  end

  return unescaped
end
unquote(data) click to toggle source

Unquotes and unescapes the given C string.

@param [String] data

The given C string.

@return [String]

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

@example

Encoding::C.unquote("\"hello\\nworld\"")
# => "hello\nworld"
# File lib/ronin/support/encoding/c.rb, line 274
def self.unquote(data)
  if ((data[0] == '"' && data[-1] == '"') ||
      (data[0] == "'" && data[-1] == "'"))
    unescape(data[1..-2])
  else
    data
  end
end