Class: Ronin::Support::Binary::Unhexdump::Parser

Inherits:
Object
  • Object
show all
Defined in:
lib/ronin/support/binary/unhexdump/parser.rb

Overview

Since:

  • 0.5.0

Constant Summary collapse

CHAR_TYPE =
Note:

it uses 'C' as the pack string.

The character type.

Since:

  • 0.5.0

CTypes::CharType.new(signed: false, pack_string: 'C')
TYPES =
Note:

The :char and :uchar types to a custom char type that uses

Supported types.

'C' as it's pack string.

Since:

  • 0.5.0

CTypes::TYPES.merge(
  char:  CHAR_TYPE,
  uchar: CHAR_TYPE
)
VISIBLE_CHARS =

Visible characters

Since:

  • 0.5.0

Chars::VISIBLE.chars.sort.zip(Chars::VISIBLE.bytes.sort).to_h
CHARS =

Escaped characters

Since:

  • 0.5.0

{
  '\0' => 0x00,
  '\a' => 0x07,
  '\b' => 0x08,
  '\t' => 0x09,
  '\n' => 0x0a,
  '\v' => 0x0b,
  '\f' => 0x0c,
  '\r' => 0x0d,
  ' '  => 0x20
}.merge(VISIBLE_CHARS)
NAMED_CHARS =

od named characters

Since:

  • 0.5.0

{
  'nul' => 0x00,
  'soh' => 0x01,
  'stx' => 0x02,
  'etx' => 0x03,
  'eot' => 0x04,
  'enq' => 0x05,
  'ack' => 0x06,
  'bel' => 0x07,
  'bs'  => 0x08,
  'ht'  => 0x09,
  'lf'  => 0x0a,
  'nl'  => 0x0a,
  'vt'  => 0x0b,
  'ff'  => 0x0c,
  'cr'  => 0x0d,
  'so'  => 0x0e,
  'si'  => 0x0f,
  'dle' => 0x10,
  'dc1' => 0x11,
  'dc2' => 0x12,
  'dc3' => 0x13,
  'dc4' => 0x14,
  'nak' => 0x15,
  'syn' => 0x16,
  'etb' => 0x17,
  'can' => 0x18,
  'em'  => 0x19,
  'sub' => 0x1a,
  'esc' => 0x1b,
  'fs'  => 0x1c,
  'gs'  => 0x1d,
  'rs'  => 0x1e,
  'us'  => 0x1f,
  'sp'  => 0x20,
  'del' => 0x7f
}.merge(VISIBLE_CHARS)

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(format: :hexdump, **kwargs) ⇒ Parser

Initializes the hexdump parser.

Parameters:

  • format (:od, :hexdump) (defaults to: :hexdump)

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

  • kwargs (Hash{Symbol => Object})

    Additional keyword arguments.

Options Hash (**kwargs):

  • :type (Symbol)

    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
  • :address_base (2, 8, 10, 16, nil)

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

  • :base (2, 8, 10, 16, nil)

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

  • :named_chars (Boolean)

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

Raises:

  • (ArgumentError)

    Unsupported type: value, the type: value was not a scalar type, or the format: was not :hexdump or :od.

Since:

  • 0.5.0



206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# 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

Instance Attribute Details

#address_base2, ... (readonly)

The base of all addresses to parse

Returns:

  • (2, 8, 10, 16)

Since:

  • 0.5.0



116
117
118
# File 'lib/ronin/support/binary/unhexdump/parser.rb', line 116

def address_base
  @address_base
end

#base2, ... (readonly)

The base of all words to parse

Returns:

  • (2, 8, 10, 16)

Since:

  • 0.5.0



121
122
123
# File 'lib/ronin/support/binary/unhexdump/parser.rb', line 121

def base
  @base
end

#format:hexdump, :od (readonly)

The format to parse.

Returns:

  • (:hexdump, :od)

Since:

  • 0.5.0



106
107
108
# File 'lib/ronin/support/binary/unhexdump/parser.rb', line 106

def format
  @format
end

#type:integer, :float (readonly)

The type of data to parse.

Returns:

  • (:integer, :float)

Since:

  • 0.5.0



111
112
113
# File 'lib/ronin/support/binary/unhexdump/parser.rb', line 111

def type
  @type
end

Instance Method Details

#pack(values) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Packs a segment back into bytes.

Parameters:

Returns:

  • (String)

    The packed segment.

Since:

  • 0.5.0



484
485
486
# File 'lib/ronin/support/binary/unhexdump/parser.rb', line 484

def pack(values)
  values.pack(@type.pack_string * values.length)
end

#parse(hexdump) {|address, values| ... } ⇒ Integer, Enumerator

Parses a hexdump.

Parameters:

  • hexdump (String, IO)

    The hexdump output.

Yields:

  • (address, values)

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

Yield Parameters:

  • address (Integer)

    The parsed address from the hexdump line.

  • values (Array<Integer, Float>)

    The parsed values from a line in the hexdump.

Returns:

  • (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.

Since:

  • 0.5.0



289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
# 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) ⇒ Integer

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parses an address.

Parameters:

  • address (String)

    The text of the address.

Returns:

  • (Integer)

    The parsed address.

Since:

  • 0.5.0



388
389
390
# 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) ⇒ Integer

Parses an integer or a ASCII character.

Parameters:

  • string (String)

    The text of the integer or character.

Returns:

  • (Integer)

    The parsed integer or byte value of the character.

Since:

  • 0.5.0



416
417
418
419
420
# 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) ⇒ Float

Parses a float.

Parameters:

  • string (String)

    The text of the float.

Returns:

  • (Float)

    The parsed float.

Since:

  • 0.5.0



431
432
433
# File 'lib/ronin/support/binary/unhexdump/parser.rb', line 431

def parse_float(string)
  string.to_f
end

#parse_int(string) ⇒ Integer

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parses an Integer.

Parameters:

  • string (String)

    The text of the Integer.

Returns:

  • (Integer)

    The parsed Integer.

Since:

  • 0.5.0



403
404
405
# File 'lib/ronin/support/binary/unhexdump/parser.rb', line 403

def parse_int(string)
  string.to_i(@base)
end

#parse_line(line) ⇒ (Integer, Array<Integer, Float>)

Parses a line from the hexdump.

Parameters:

  • line (String)

    A line from a hexdump.

Returns:

Since:

  • 0.5.0



444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
# 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) ⇒ String

Unhexdumps a hexdump and returns the raw data.

Parameters:

  • hexdump (String, IO)

    The contents of the hexdump.

Returns:

  • (String)

    The raw data from the hexdump.

Since:

  • 1.0.0



367
368
369
370
371
372
373
374
375
# 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) ⇒ Array<Integer>, ...

Unhexdumps a hexdump and returns the unpacked values.

Returns:

Since:

  • 1.0.0



346
347
348
349
350
351
352
353
354
# 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