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

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.



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.



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.



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.



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.



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.



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.



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.



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.



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:



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.



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:



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:



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.



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.



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.



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.



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.



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.



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.



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.



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

def to_h
  each.to_h
end