class Ronin::Support::Binary::Unhexdump::Parser

@since 0.5.0

@api semipublic

Constants

CHARS

Escaped characters

CHAR_TYPE

The character type.

@note it uses ‘’C’‘ as the pack string.

NAMED_CHARS

od named characters

TYPES

Supported types.

@note The ‘:char` and `:uchar` types to a custom char type that uses `’C’‘ as it’s pack string.

VISIBLE_CHARS

Visible characters

Attributes

address_base[R]

The base of all addresses to parse

@return [2, 8, 10, 16]

base[R]

The base of all words to parse

@return [2, 8, 10, 16]

format[R]

The format to parse.

@return [:hexdump, :od]

type[R]

The type of data to parse.

@return [:integer, :float]

Public Class Methods

new(format: :hexdump, **kwargs) click to toggle source

Initializes the hexdump parser.

@param [:od, :hexdump] format

The expected format of the hexdump. Must be either `:od` or
`:hexdump`.

@param [Hash{Symbol => Object}] kwargs

Additional keyword arguments.

@option kwargs [Symbol] :type

Denotes the encoding used for the bytes within the hexdump.
Must be one of the following:
* `:byte` (default for `format: :hexdump`)
* `:char`
* `:uint8`
* `:uint16`
* `:uint32`
* `:uint64`
* `:int8`
* `:int16`
* `:int32`
* `:int64`
* `:uchar`
* `:ushort`
* `:uint`
* `:ulong`
* `:ulong_long`
* `:short`
* `:int`
* `:long`
* `:long_long`
* `:float`
* `:double`
* `:float_le`
* `:double_le`
* `:float_be`
* `:double_be`
* `:uint16_le` (default for `format: :od`)
* `:uint32_le`
* `:uint64_le`
* `:int16_le`
* `:int32_le`
* `:int64_le`
* `:uint16_be`
* `:uint32_be`
* `:uint64_be`
* `:int16_be`
* `:int32_be`
* `:int64_be`
* `:ushort_le`
* `:uint_le`
* `:ulong_le`
* `:ulong_long_le`
* `:short_le`
* `:int_le`
* `:long_le`
* `:long_long_le`
* `:ushort_be`
* `:uint_be`
* `:ulong_be`
* `:ulong_long_be`
* `:short_be`
* `:int_be`
* `:long_be`
* `:long_long_be`

@option kwargs [2, 8, 10, 16, nil] :address_base

The numerical base that the offset addresses are encoded in.
Defaults to 16 when `format: :hexdump` and 8 when `format: :od`.

@option kwargs [2, 8, 10, 16, nil] :base

The numerical base that the hexdumped numbers are encoded in.
Defaults to 16 when `format: :hexdump` and 8 when `format: :od`.

@option kwargs [Boolean] :named_chars

Indicates to parse `od`-style named characters (ex: `nul`,
`del`, etc). Only recognized when `format: :od` is also given.

@raise [ArgumentError]

Unsupported `type:` value, the `type:` value was not a scalar
type, or the `format:` was not `:hexdump` or `:od`.
# File lib/ronin/support/binary/unhexdump/parser.rb, line 206
def initialize(format: :hexdump, **kwargs)
  case format
  when :od      then initialize_od(**kwargs)
  when :hexdump then initialize_hexdump(**kwargs)
  else
    raise(ArgumentError,"format: must be either :hexdump or :od, was #{format.inspect}")
  end

  case @type
  when CTypes::FloatType
    @parse_method = method(:parse_float)
  when CTypes::CharType
    @parse_method = method(:parse_char_or_int)
  when CTypes::ScalarType
    @parse_method = method(:parse_int)
  else
    raise(ArgumentError,"only scalar types are support: #{kwargs[:type].inspect}")
  end
end

Public Instance Methods

pack(values) click to toggle source

Packs a segment back into bytes.

@param [Array<Integer, Float>] values

A segment of words.

@return [String]

The packed segment.

@api private

# File lib/ronin/support/binary/unhexdump/parser.rb, line 484
def pack(values)
  values.pack(@type.pack_string * values.length)
end
parse(hexdump) { |next_address, previous_row| ... } click to toggle source

Parses a hexdump.

@param [String, IO] hexdump

The hexdump output.

@yield [address, values]

If a block is given, it will be passed each parsed line of the
hexdump.

@yieldparam [Integer] address

The parsed address from the hexdump line.

@yieldparam [Array<Integer, Float>] values

The parsed values from a line in the hexdump.

@return [Integer, Enumerator]

If a block is given, then the last address will be returned
representing the total length of the hexdump.
If no block is given, an Enumerator will be returned.
# File lib/ronin/support/binary/unhexdump/parser.rb, line 289
def parse(hexdump)
  return enum_for(__method__,hexdump) unless block_given?

  previous_address = nil
  first_address    = nil

  previous_row         = nil
  previous_row_repeats = false
  previous_row_size    = nil
  starts_repeating_at  = nil

  hexdump.each_line do |line|
    line.chomp!
    # remove GNU hexdump's ASCII column
    line.sub!(/\s+\|.{1,16}\|\s*$/,'') if @format == :hexdump

    if line == '*'
      previous_row_repeats = true
      previous_row_size    = (previous_row.length * @type.size)
      starts_repeating_at  = previous_address + previous_row_size
    else
      address, row = parse_line(line)

      # remember the first address
      first_address ||= address

      if previous_row_repeats
        # fill in the omitted repeating rows
        range     = starts_repeating_at...address
        addresses = range.step(previous_row_size)

        addresses.each do |next_address|
          yield next_address, previous_row
        end

        previous_row_repeats = false
      end

      yield address, row if row

      previous_address = address
      previous_row     = row
    end
  end

  # return the last address as the length
  return previous_address - first_address
end
parse_address(address) click to toggle source

Parses an address.

@param [String] address

The text of the address.

@return [Integer]

The parsed address.

@api private

# File lib/ronin/support/binary/unhexdump/parser.rb, line 388
def parse_address(address)
  address.to_i(@address_base)
end
parse_char_or_int(string) click to toggle source

Parses an integer or a ASCII character.

@param [String] string

The text of the integer or character.

@return [Integer]

The parsed integer or byte value of the character.
# File lib/ronin/support/binary/unhexdump/parser.rb, line 416
def parse_char_or_int(string)
  @chars.fetch(string) do
    string.to_i(@base)
  end
end
parse_float(string) click to toggle source

Parses a float.

@param [String] string

The text of the float.

@return [Float]

The parsed float.
# File lib/ronin/support/binary/unhexdump/parser.rb, line 431
def parse_float(string)
  string.to_f
end
parse_int(string) click to toggle source

Parses an Integer.

@param [String] string

The text of the Integer.

@return [Integer]

The parsed Integer.

@api private

# File lib/ronin/support/binary/unhexdump/parser.rb, line 403
def parse_int(string)
  string.to_i(@base)
end
parse_line(line) click to toggle source

Parses a line from the hexdump.

@param [String] line

A line from a hexdump.

@return [(Integer, Array<Integer, Float>)]

The parse address and the parsed numbers from the line.
# File lib/ronin/support/binary/unhexdump/parser.rb, line 444
def parse_line(line)
  if @type.kind_of?(CTypes::CharType)
    # because od/hexdump print the ' ' char as white space,
    # we need special parsing logic here.
    if (start_index = line.index(' '))
      address = parse_address(line[0,start_index])
      rest    = line[start_index..]
      numbers = rest.scan(/   ( )|([^\s]+)/)
      numbers.map! { |(sp,char)| sp || char }
      numbers.map!(&@parse_method)

      return address, numbers
    else
      return parse_address(line)
    end
  else
    address, *numbers = line.split

    address = parse_address(address)
    numbers.map!(&@parse_method)

    unless numbers.empty?
      return address, numbers
    else
      return address
    end
  end
end
unhexdump(hexdump) click to toggle source

Unhexdumps a hexdump and returns the raw data.

@param [String, IO] hexdump

The contents of the hexdump.

@return [String]

The raw data from the hexdump.

@since 1.0.0

# File lib/ronin/support/binary/unhexdump/parser.rb, line 367
def unhexdump(hexdump)
  buffer = String.new(encoding: Encoding::ASCII_8BIT)

  length = parse(hexdump) do |address,row|
    buffer << pack(row)
  end

  return buffer.byteslice(0,length)
end
unpack(hexdump) click to toggle source

Unhexdumps a hexdump and returns the unpacked values.

@return [Array<Integer>, Array<String>, Array<Float>]

The Array of unpacked values from the hexdump.

@since 1.0.0

# File lib/ronin/support/binary/unhexdump/parser.rb, line 346
def unpack(hexdump)
  values = []

  parse(hexdump) do |address,row|
    values.concat(row)
  end

  return values
end

Private Instance Methods

initialize_hexdump(type: :byte, base: nil, address_base: nil) click to toggle source

Initializes instance variables for the ‘hexdump` hexdump format.

# File lib/ronin/support/binary/unhexdump/parser.rb, line 252
def initialize_hexdump(type: :byte, base: nil, address_base: nil)
  @format = :hexdump

  @type           = TYPES[type]
  @base           = base         || 16
  @address_base   = address_base || 16

  case @type
  when CTypes::CharType
    @base  = 8
    @chars = CHARS
  end
end
initialize_od(type: :uint16_le, base: nil, address_base: nil, named_chars: nil) click to toggle source

Initializes instance variables for the ‘od` hexdump format.

# File lib/ronin/support/binary/unhexdump/parser.rb, line 231
def initialize_od(type: :uint16_le,
                  base: nil,
                  address_base: nil,
                  named_chars: nil)
  @format = :od

  @type           = TYPES[type]
  @base           = base         || 8
  @address_base   = address_base || 8

  case @type
  when CTypes::CharType
    @chars = if named_chars then NAMED_CHARS
             else                CHARS
             end
  end
end