Class: RubySMB::GenericPacket

Inherits:
BinData::Record
  • Object
show all
Defined in:
lib/ruby_smb/generic_packet.rb

Overview

Parent class for all SMB Packets.

Direct Known Subclasses

SMB1::Packet::CloseRequest, SMB1::Packet::CloseResponse, SMB1::Packet::EchoRequest, SMB1::Packet::EchoResponse, SMB1::Packet::EmptyPacket, SMB1::Packet::LogoffRequest, SMB1::Packet::LogoffResponse, SMB1::Packet::NegotiateRequest, SMB1::Packet::NegotiateResponse, SMB1::Packet::NegotiateResponseExtended, SMB1::Packet::NtCreateAndxRequest, SMB1::Packet::NtCreateAndxResponse, SMB1::Packet::NtTrans::CreateRequest, SMB1::Packet::NtTrans::CreateResponse, SMB1::Packet::NtTrans::Request, SMB1::Packet::NtTrans::Response, SMB1::Packet::ReadAndxRequest, SMB1::Packet::ReadAndxResponse, SMB1::Packet::SessionSetupLegacyRequest, SMB1::Packet::SessionSetupLegacyResponse, SMB1::Packet::SessionSetupRequest, SMB1::Packet::SessionSetupResponse, SMB1::Packet::Trans2::FindFirst2Request, SMB1::Packet::Trans2::FindFirst2Response, SMB1::Packet::Trans2::FindNext2Request, SMB1::Packet::Trans2::FindNext2Response, SMB1::Packet::Trans2::Open2Request, SMB1::Packet::Trans2::Open2Response, SMB1::Packet::Trans2::Request, SMB1::Packet::Trans2::RequestSecondary, SMB1::Packet::Trans2::Response, SMB1::Packet::Trans2::SetFileInformationRequest, SMB1::Packet::Trans2::SetFileInformationResponse, SMB1::Packet::Trans::PeekNmpipeResponse, SMB1::Packet::Trans::Request, SMB1::Packet::Trans::Response, SMB1::Packet::Trans::TransactNmpipeRequest, SMB1::Packet::Trans::TransactNmpipeResponse, SMB1::Packet::TreeConnectRequest, SMB1::Packet::TreeConnectResponse, SMB1::Packet::TreeDisconnectRequest, SMB1::Packet::TreeDisconnectResponse, SMB1::Packet::WriteAndxRequest, SMB1::Packet::WriteAndxResponse, SMB2::Packet::CloseRequest, SMB2::Packet::CloseResponse, SMB2::Packet::CreateRequest, SMB2::Packet::CreateResponse, SMB2::Packet::EchoRequest, SMB2::Packet::EchoResponse, SMB2::Packet::ErrorPacket, SMB2::Packet::IoctlRequest, SMB2::Packet::IoctlResponse, SMB2::Packet::LogoffRequest, SMB2::Packet::LogoffResponse, SMB2::Packet::NegotiateRequest, SMB2::Packet::NegotiateResponse, SMB2::Packet::QueryDirectoryRequest, SMB2::Packet::QueryDirectoryResponse, SMB2::Packet::ReadRequest, SMB2::Packet::ReadResponse, SMB2::Packet::SessionSetupRequest, SMB2::Packet::SessionSetupResponse, SMB2::Packet::SetInfoRequest, SMB2::Packet::SetInfoResponse, SMB2::Packet::TreeConnectRequest, SMB2::Packet::TreeConnectResponse, SMB2::Packet::TreeDisconnectRequest, SMB2::Packet::TreeDisconnectResponse, SMB2::Packet::WriteRequest, SMB2::Packet::WriteResponse

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.describeString

Outputs a nicely formatted string representation of the Packet's structure.

Returns:

  • (String)

    formatted string representation of the packet structure



8
9
10
11
12
13
14
# File 'lib/ruby_smb/generic_packet.rb', line 8

def self.describe
  description = ''
  fields_hashed.each do |field|
    description << format_field(field)
  end
  description
end

.fields_hashedArray<Hash>

Returns an array of hashes representing the fields for this record.

Returns:

  • (Array<Hash>)

    the array of hash representations of the record's fields



108
109
110
# File 'lib/ruby_smb/generic_packet.rb', line 108

def self.fields_hashed
  walk_fields(fields)
end

.format_field(field, depth = 0) ⇒ String

Takes a hash representation of a field and spits out a formatted string representation.

Parameters:

  • field (Hash)

    the hash representing the field

  • depth (Fixnum) (defaults to: 0)

    the recursive depth level to track indentation

Returns:

  • (String)

    the formatted string representation of the field



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/ruby_smb/generic_packet.rb', line 118

def self.format_field(field, depth = 0)
  name = field[:name].to_s
  if field[:class].ancestors.include? BinData::Record
    class_str = ''
    name.upcase!
  else
    class_str = field[:class].to_s.split('::').last
    class_str = "(#{class_str})"
    name.capitalize!
  end
  formatted_name = "\n" + ("\t" * depth) + name
  formatted_string = format '%-30s %-10s %s', formatted_name, class_str, field[:label]
  field[:fields].each do |sub_field|
    formatted_string << format_field(sub_field, (depth + 1))
  end
  formatted_string
end

.read(val) ⇒ Object

Overrides the class #read method with some automatic exception handling. If an EOFError is thrown, it will try to read the data into the protocol specific empty ErrorPacket so that the NTstatus code can be read. We re-raise the exception in the event that it is not an SMB1 or SMB2 packet, or if it is already an error packet.



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/ruby_smb/generic_packet.rb', line 42

def self.read(val)
  begin
    super(val)
  rescue IOError => e
    case self.to_s
      when /EmptyPacket|ErrorPacket/
        raise RubySMB::Error::InvalidPacket, 'Not a valid SMB packet'
      when /SMB1/
        packet = RubySMB::SMB1::Packet::EmptyPacket.read(val)
      when /SMB2/
        packet = RubySMB::SMB2::Packet::ErrorPacket.read(val)
      else
        raise RubySMB::Error::InvalidPacket, 'Not a valid SMB packet'
    end
    packet.original_command = self::COMMAND
    packet
  end
end

.walk_fields(fields) ⇒ Array<Hash>

Recursively walks through a field, building a hash representation of that field and all of it's sub-fields.

Parameters:

  • fields (Array<BinData::SanitizedField>)

    an array of fields to walk

Returns:

  • (Array<Hash>)

    an array of hashes representing the fields



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/ruby_smb/generic_packet.rb', line 141

def self.walk_fields(fields)
  field_hashes = []
  fields.each do |field|
    field_hash = {}
    field_hash[:name] = field.name
    prototype = field.prototype
    field_hash[:class] = prototype.instance_variable_get(:@obj_class)
    params = prototype.instance_variable_get(:@obj_params)
    field_hash[:label] = params[:label]
    field_hash[:value] = params[:value]
    sub_fields = params[:fields]
    field_hash[:fields] = if sub_fields.nil?
                            []
                          else
                            walk_fields(sub_fields)
                          end
    field_hashes << field_hash
  end
  field_hashes
end

Instance Method Details

#displayObject



16
17
18
19
20
21
22
# File 'lib/ruby_smb/generic_packet.rb', line 16

def display
  display_str = ''
  self.class.fields_hashed.each do |field|
    display_str << display_field(field)
  end
  display_str
end

#initialize_instanceObject



91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/ruby_smb/generic_packet.rb', line 91

def initialize_instance
  super

  unless [RubySMB::SMB1::Packet::EmptyPacket, RubySMB::SMB2::Packet::ErrorPacket].any? {|klass| self.is_a? klass}
    case packet_smb_version
    when 'SMB1'
      smb_header.command = self.class::COMMAND
    when 'SMB2'
      smb2_header.command = self.class::COMMAND
    end
  end
end

#packet_smb_versionObject



24
25
26
27
28
29
30
31
32
33
34
# File 'lib/ruby_smb/generic_packet.rb', line 24

def packet_smb_version
  class_name = self.class.to_s
  case class_name
  when /SMB1/
    'SMB1'
  when /SMB2/
    'SMB2'
  else
    ''
  end
end

#status_codeObject



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/ruby_smb/generic_packet.rb', line 61

def status_code
  value = -1
  smb_version = packet_smb_version
  case smb_version
  when 'SMB1'
    value = smb_header.nt_status.value
  when 'SMB2'
    value = smb2_header.nt_status.value
  end
  status_code = WindowsError::NTStatus.find_by_retval(value).first
  if status_code.nil?
    status_code = WindowsError::ErrorCode.new("0x#{value.to_s(16)}", value, "Unknown 0x#{value.to_s(16)}")
  end
  status_code
end

#valid?TrueClass, FalseClass

Validates the packet protocol ID and the SMB command

Returns:

  • (TrueClass, FalseClass)

    true if the packet is valid, false otherwise



80
81
82
83
84
85
86
87
88
89
# File 'lib/ruby_smb/generic_packet.rb', line 80

def valid?
  case packet_smb_version
  when 'SMB1'
    return smb_header.protocol == RubySMB::SMB1::SMB_PROTOCOL_ID &&
      smb_header.command == self.class::COMMAND
  when 'SMB2'
    return smb2_header.protocol == RubySMB::SMB2::SMB2_PROTOCOL_ID &&
      smb2_header.command == self.class::COMMAND
  end
end