Class: Ronin::Support::Binary::Struct

Inherits:
Memory
  • Object
show all
Extended by:
CTypes::Mixin
Defined in:
lib/ronin/support/binary/struct.rb,
lib/ronin/support/binary/struct/member.rb
more...

Overview

Note:

This class provides lazy memory mapped access to an underlying

Generic Binary Struct class.

buffer. This means values are decoded/encoded each time they are read or written to.

Examples

Defining Members

class Point < Ronin::Support::Binary::Struct

  member :x, :int32
  member :y, :int32

end

Initializing Structs

From a Hash:

point = Point.new(x: 1, y: 2)

From a buffer:

point = Point.new("\x01\x00\x00\x00\xFF\xFF\xFF\xFF")

Reading Fields

point = Point.new("\x01\x00\x00\x00\xFF\xFF\xFF\xFF")
point[:x]
# => 1
point[:y]
# => -1

point.x
# => 1
point.y
# => -1

Packing Structs

class Point < Ronin::Support::Binary::Struct

  member :x, :int32
  member :y, :int32

end

point = Point.new(x: 10, y: -1)
point.pack
# => "\n\x00\x00\x00\xFF\xFF\xFF\xFF"

Unpacking Structs

class Point < Ronin::Support::Binary::Struct

  member :x, :int32
  member :y, :int32

end

point = Point.unpack("\x00\x00\x00\x01\xFF\xFF\xFF\xFF")
point.x
# => 1
point.y
# => -1

Inheriting Structs

class Point < Ronin::Support::Binary::Struct

  member :x, :int32
  member :y, :int32

end

class Point3D < Point

  member :z, :int32

end

point   = Point.new(x: 100, y: 42)
point3d = Point3D.new(x: 100, y: 42, z: -1)

Array Fields

class MyStruct < Ronin::Support::Binary::Struct

  member :x,    :uint32
  member :nums, [:uint8, 10]

end

struct = MyStruct.new
struct.nums = [0x01, 0x02, 0x03, 0x04]
struct.pack
# => "\x00\x00\x00\x00\x01\x02\x03\x04\x00\x00\x00\x00\x00\x00"

Unbounded Array Fields

class MyStruct < Ronin::Support::Binary::Struct

  member :length,  :uint32
  member :payload, (:uint8..)

end

struct = MyStruct.new
struct.payload = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]
struct.pack
# => "\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\a\b"

Struct Endianness

class MyStruct < Ronin::Support::Binary::Struct

  platform endian: :big

  member :x, :uint32
  member :y, :uint32

end

struct = MyStruct.new
struct.x = 0xAABB
struct.y = 0xCCDD
struct.pack
# => "\x00\x00\xAA\xBB\x00\x00\xCC\xDD"

Struct Architecture

class MyStruct < Ronin::Support::Binary::Struct

  platform arch: :arm64_be

  member :x, :int
  member :y, :int
  member :f, :double

end

struct = MyStruct.new
struct.x = 100
struct.y = -100
struct.f = (90 / Math::PI)
struct.pack
# => "\x00\x00\x00d\xFF\xFF\xFF\x9C@<\xA5\xDC\x1Ac\xC1\xF8"

Struct Operating System (OS)

class MyStruct < Ronin::Support::Binary::Struct

  platform arch: :x86_64, os: :windows

  member :x, :long
  member :y, :long

end

struct = MyStruct.new
struct.x = 255
struct.y = -1
struct.pack
# => "\xFF\x00\x00\x00\xFF\xFF\xFF\xFF"

Struct Alignment

class Pixel < Ronin::Support::Binary::Struct

  align 4

  member :r, :uint8
  member :g, :uint8
  member :b, :uint8

end

class PixelBuf < Ronin::Support::Binary::Struct

  member :count, :uint8
  member :pixels, [Pixel, 255]

end

pixelbuf = PixelBuf.new
pixelbuf.count = 2
pixelbuf.pixels[0].r = 0xAA
pixelbuf.pixels[0].g = 0xBB
pixelbuf.pixels[0].b = 0xCC
pixelbuf.pixels[1].r = 0xAA
pixelbuf.pixels[1].g = 0xBB
pixelbuf.pixels[1].b = 0xCC
pixelbuf.pack
# => "\x02\x00\x00\x00\xAA\xBB\xCC\xAA\xBB\xCC..."

Disable Struct Padding

class MyStruct < Ronin::Support::Binary::Struct

  padding false

  member :c, :char
  member :i, :int32

end

struct = MyStruct.new
struct.c = 'A'
struct.i = -1
struct.pack
# => "A\xFF\xFF\xFF\xFF"

Direct Known Subclasses

Packet, Union

Defined Under Namespace

Classes: Member

Instance Attribute Summary collapse

Attributes included from CTypes::Mixin

#arch, #endian, #os, #type_resolver, #type_system

Attributes inherited from Memory

#string

Class Method Summary collapse

Instance Method Summary collapse

Methods included from CTypes::Mixin

initialize_type_system

Methods inherited from Memory

#+, #byteslice, #clear, #copy_from, #copy_to, #pack, #read_from, #size

Constructor Details

#initialize(buffer_or_values = {}) ⇒ Struct

Initializes the struct.

Examples:

Initiializing a new struct from a buffer:

class MyStruct < Ronin::Support::Binary::Struct

  member :x,    :uint32
  member :f,    :double
  member :nums, [:uint8, 10]

end

struct = MyStruct.new("\x01\x00\x00\x00\x00\x00\x00\x00333333\xD3?\x01\x02\x03\x00\x00\x00\x00\x00\x00\x00")
struct.x
# => 1
struct.f
# => 0.3
struct.nums.to_a
# => [1, 2, 3, 0, 0, 0, 0, 0, 0, 0]

Initializing a new struct with values:

struct = MyStruct.new(x: 1, f: 0.3, nums: [1,2,3])
struct.x
# => 1
struct.f
# => 0.3
struct.nums.to_a
# => [1, 2, 3, 0, 0, 0, 0, 0, 0, 0]

Parameters:

  • buffer_or_values (Hash{Symbol => Object}, String, ByteSlice) (defaults to: {})

    Optional values to initialize the members of the struct.

[View source]

288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
# File 'lib/ronin/support/binary/struct.rb', line 288

def initialize(buffer_or_values={})
  @type_system = self.class.type_system
  @type        = self.class.type

  @cache = {}

  case buffer_or_values
  when Hash
    super(@type.size)

    values = buffer_or_values
    values.each do |name,value|
      self[name] = value
    end
  when String, ByteSlice
    buffer = buffer_or_values

    super(buffer)
  else
    raise(ArgumentError,"first argument of #{self.class}.new must be either a Hash of values or a String or #{ByteSlice}")
  end
end

Instance Attribute Details

#typeCTypes::StructType (readonly)

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.

The type system used by the struct.

Returns:


252
253
254
# File 'lib/ronin/support/binary/struct.rb', line 252

def type
  @type
end

Class Method Details

.align(new_alignment) ⇒ Object

Sets the alignment of the struct.

Parameters:

  • new_alignment (Integer)

    The new alignment for the struct.

[View source]

705
706
707
# File 'lib/ronin/support/binary/struct.rb', line 705

def self.align(new_alignment)
  @alignment = new_alignment
end

.alignmentInteger

The alignment of the struct.

Returns:

  • (Integer)

    The alignment, in bytes, for the struct.

[View source]

691
692
693
694
695
# File 'lib/ronin/support/binary/struct.rb', line 691

def self.alignment
  @alignment ||= if superclass < Struct
                   superclass.alignment
                 end
end

.has_member?(name) ⇒ Boolean

Determines if the struct has the given member.

Parameters:

  • name (Symbol)

    The member name.

Returns:

  • (Boolean)

    Specifies that the member exists in the struct.

[View source]

400
401
402
# File 'lib/ronin/support/binary/struct.rb', line 400

def self.has_member?(name)
  members.has_key?(name.to_sym)
end

.member(name, type_signature, **kwargs) ⇒ Object

Defines a member in the struct.

Examples:

Defining a member:

class MyStruct < Ronin::Support::Binary::Struct
  member :x, :uint32
end

struct = MyStruct.new
struct.x = 0x11223344

Defining an Array member:

class MyStruct < Ronin::Support::Binary::Struct
  member :nums, [:uint32, 10]
end

struct = MyStruct.new
struct.x = [0x11111111, 0x22222222, 0x33333333, 0x44444444]

Defining an unbounded Array member:

class MyStruct < Ronin::Support::Binary::Struct
  member :payload, :uint8..
end

struct = MyStruct.new
struct.payloads = [0x1, 0x2, 0x3, 0x4]

Parameters:

  • name (Symbol)

    The name of the member.

  • type_signature (Symbol, (Symbol, Integer), Range(Symbol))

    The type of the member.

[View source]

799
800
801
802
803
804
# File 'lib/ronin/support/binary/struct.rb', line 799

def self.member(name,type_signature,**kwargs)
  self.members[name] = Member.new(name,type_signature,**kwargs)

  define_method(name)       { self[name] }
  define_method("#{name}=") { |value| self[name] = value }
end

.membersHash{Symbol => Member}

The members in the struct.

Returns:

  • (Hash{Symbol => Member})

    The member names and type information.

[View source]

381
382
383
384
385
386
387
# File 'lib/ronin/support/binary/struct.rb', line 381

def self.members
  @members ||= if superclass < Struct
                 superclass.members.dup
               else
                 {}
               end
end

.pack(values) ⇒ String

Initializes the struct and then packs it.

Examples:

class MyStruct < Ronin::Support::Binary::Struct

  member :x,    :uint32
  member :f,    :double
  member :nums, [:uint8, 10]

end

MyStruct.pack(x: 1, f: 0.3, nums: [1,2,3])
# => "\x01\x00\x00\x00\x00\x00\x00\x00333333\xD3?\x01\x02\x03\x00\x00\x00\x00\x00\x00\x00"

Parameters:

  • values (Hash{Symbol => Object})

    The values to initialize the struct with.

Returns:

  • (String)

    The packed struct.

[View source]

427
428
429
# File 'lib/ronin/support/binary/struct.rb', line 427

def self.pack(values)
  new(values).to_s
end

.padding(new_padding = nil) ⇒ Boolean

Gets or sets whether the struct's members will have padding.

Parameters:

  • new_padding (Boolean, nil) (defaults to: nil)

    The optional new value for padding.

Returns:

  • (Boolean)

    Specifies whether the struct's members will be padded.

[View source]

720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
# File 'lib/ronin/support/binary/struct.rb', line 720

def self.padding(new_padding=nil)
  unless new_padding.nil?
    @padding = new_padding
  else
    if @padding.nil?
      @padding = if superclass < Struct
                   superclass.padding
                 else
                   true
                 end
    else
      @padding
    end
  end
end

.platform(endian: nil, arch: nil, os: nil) ⇒ Hash{Symbol => Object}

Gets or sets the struct's target platform.

The desired architecture for the struct.

The Operating System (OS) for the struct.

Parameters:

  • endian (:little, :big, :net, nil) (defaults to: nil)

    The desired endianness for the struct.

  • arch (:x86, :x86_64, :ppc, :ppc64, :mips, :mips_le, :mips_be, :mips64, :mips64_le, :mips64_be, :arm, :arm_le, :arm_be, :arm64, :arm64_le, :arm64_be) (defaults to: nil)
  • os (:linux, :macos, :windows, :android, :apple_ios, :bsd, :freebsd, :openbsd, :netbsd) (defaults to: nil)

Returns:

  • (Hash{Symbol => Object})

    The platform configuration Hash.

[View source]

670
671
672
673
674
675
676
677
678
679
680
681
# File 'lib/ronin/support/binary/struct.rb', line 670

def self.platform(endian: nil, arch: nil, os: nil)
  if endian || arch || os
    @type_system = CTypes.platform(endian: endian, arch: arch, os: os)
    @platform    = {endian: endian, arch: arch, os: os}
  else
    @platform ||= if superclass < Struct
                    superclass.platform
                  else
                    {}
                  end
  end
end

.read_from(io) ⇒ Struct

Reads the struct from the IO stream.

Examples:

class MyStruct < Ronin::Support::Binary::Struct

  member :x,    :uint32
  member :f,    :double
  member :nums, [:uint8, 10]

end

file   = File.new('binary.dat','b')
struct = MyStruct.read_from(file)

Parameters:

  • io (IO)

    The IO object to read from.

Returns:

  • (Struct)

    The read struct.

See Also:

[View source]

482
483
484
# File 'lib/ronin/support/binary/struct.rb', line 482

def self.read_from(io)
  new.read_from(io)
end

.sizeInteger

The size of the struct.

Examples:

class MyStruct < Ronin::Support::Binary::Struct

  member :x, :uint32
  member :y, :uint32

end

MyStruct.size
# => 8

Returns:

  • (Integer)

    The size of the struct in bytes.

[View source]

342
343
344
# File 'lib/ronin/support/binary/struct.rb', line 342

def self.size
  type.size
end

.translate(endian: nil, arch: nil) ⇒ Class<Struct>

Translates the struct class using the given type system into a new struct class.

The new architecture for the struct.

Parameters:

  • endian (:little, :big, :net, nil) (defaults to: nil)

    The desired endian-ness.

  • arch (:x86, :x86_64, :ppc, :ppc64, :mips, :mips_le, :mips_be, :mips64, :mips64_le, :mips64_be, :arm, :arm_le, :arm_be, :arm64, :arm64_le, :arm64_be, nil) (defaults to: nil)

Returns:

[View source]

363
364
365
366
367
368
369
370
371
# File 'lib/ronin/support/binary/struct.rb', line 363

def self.translate(endian: nil, arch: nil)
  struct_class = Class.new(self)

  if    arch   then struct_class.arch(arch)
  elsif endian then struct_class.endian(endian)
  end

  struct_class
end

.typeCTypes::StructObjectType

The type for the struct class.

Returns:

[View source]

319
320
321
# File 'lib/ronin/support/binary/struct.rb', line 319

def self.type
  @type ||= type_resolver.resolve(self)
end

.type_resolverCTypes::TypeResolver

The type resolver using type_system.

[View source]

760
761
762
# File 'lib/ronin/support/binary/struct.rb', line 760

def self.type_resolver
  @type_resolver ||= CTypes::TypeResolver.new(type_system)
end

.type_systemTypes, ...

Gets or sets the struct's type system.

[View source]

745
746
747
748
749
750
751
# File 'lib/ronin/support/binary/struct.rb', line 745

def self.type_system
  @type_system ||= if superclass < Struct
                     superclass.type_system
                   else
                     CTypes
                   end
end

.unpack(data) ⇒ Struct

Unpacks data into the struct.

Examples:

class MyStruct < Ronin::Support::Binary::Struct

  member :x,    :uint32
  member :f,    :double
  member :nums, [:uint8, 10]

end

struct = MyStruct.unpack("\x01\x00\x00\x00\x00\x00\x00\x00333333\xD3?\x01\x02\x03\x00\x00\x00\x00\x00\x00\x00")

Parameters:

  • data (String)

    The data to unpack.

Returns:

  • (Struct)

    The newly unpacked struct.

[View source]

453
454
455
# File 'lib/ronin/support/binary/struct.rb', line 453

def self.unpack(data)
  new(data)
end

Instance Method Details

#[](name) ⇒ Integer, ...

Reads a value from the struct.

Examples:

class MyStruct < Ronin::Support::Binary::Struct

  member :x,    :uint32
  member :f,    :double
  member :nums, [:uint8, 10]

end

struct = MyStruct.new("\x01\x00\x00\x00\x00\x00\x00\x00333333\xD3?\x01\x02\x03\x00\x00\x00\x00\x00\x00\x00")
struct[:x]
# => 1
struct[:f]
# => 0.3
struct[:nums].to_a
# => [1, 2, 3, 0, 0, 0, 0, 0, 0, 0]

Parameters:

  • name (Symbol)

    The member name.

Returns:

Raises:

  • (ArgumentError)

    The struct does not contain the member.

[View source]

517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
# File 'lib/ronin/support/binary/struct.rb', line 517

def [](name)
  if (member = @type.members[name])
    case member.type
    when CTypes::UnboundedArrayType
      # XXX: but how do we handle an unbounded array of structs?
      @cache[name] ||= begin
                         offset = member.offset
                         length = size - member.offset
                         slice  = byteslice(offset,length)

                         Binary::Array.new(member.type.type,slice)
                       end
    when CTypes::ObjectType
      @cache[name] ||= begin
                         offset = member.offset
                         length = member.type.size
                         slice  = byteslice(offset,length)

                         member.type.unpack(slice)
                       end
    else
      data = super(member.offset,member.type.size)
      member.type.unpack(data)
    end
  else
    raise(ArgumentError,"no such member: #{name.inspect}")
  end
end

#[]=(name, value) ⇒ Integer, ...

Writes a value to the struct.

Examples:

class MyStruct < Ronin::Support::Binary::Struct

  member :x,    :uint32
  member :f,    :double
  member :nums, [:uint8, 10]

end

struct = MyStruct.new
struct[:x] = 1
struct[:x]
# => 1
struct[:f] = 0.3
struct[:f]
# => 0.3
struct[:nums] = [1,2,3]
struct[:nums].to_a
# => [1, 2, 3, 0, 0, 0, 0, 0, 0, 0]

Parameters:

Returns:

Raises:

  • (ArgumentError)

    The struct does not contain the member.

[View source]

583
584
585
586
587
588
589
590
591
592
# File 'lib/ronin/support/binary/struct.rb', line 583

def []=(name,value)
  if (member = @type.members[name])
    data = member.type.pack(value)

    super(member.offset,member.type.size,data)
    return value
  else
    raise(ArgumentError,"no such member: #{name.inspect}")
  end
end

#each {|name, value| ... } ⇒ Enumerator

Enumerates over the members within the struct.

Yields:

  • (name, value)

    If a block is given, it will be passed each member name and value from within the struct.

Yield Parameters:

  • name (Symbol)

    The name of the struct memeber.

  • value (Object)

    The decoded value of the struct member.

Returns:

  • (Enumerator)

    If no block is given, an Enumerator object will be returned.

[View source]

612
613
614
615
616
617
618
# File 'lib/ronin/support/binary/struct.rb', line 612

def each(&block)
  return enum_for(__method__) unless block_given?

  @type.members.each_key do |name|
    yield name, self[name]
  end
end

#to_a::Array<Object>

Converts the struct to an Array of values.

Returns:

  • (::Array<Object>)

    The array of values within the struct.

[View source]

640
641
642
# File 'lib/ronin/support/binary/struct.rb', line 640

def to_a
  each.map { |name,value| value }
end

#to_hHash{Symbol => Object}

Converts the struct to a Hash.

Returns:

  • (Hash{Symbol => Object})

    The hash of member names and values.

[View source]

628
629
630
# File 'lib/ronin/support/binary/struct.rb', line 628

def to_h
  each.to_h
end