packetgen-plugin-smb 0.3.0 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/specs.yml +28 -0
  3. data/.rubocop.yml +8 -1
  4. data/Gemfile +15 -3
  5. data/README.md +59 -3
  6. data/Rakefile +10 -4
  7. data/examples/llmnr-responder +110 -0
  8. data/examples/smb-responder +233 -0
  9. data/lib/packetgen-plugin-smb.rb +5 -2
  10. data/lib/packetgen/plugin/gssapi.rb +11 -6
  11. data/lib/packetgen/plugin/llmnr.rb +58 -0
  12. data/lib/packetgen/plugin/netbios.rb +19 -0
  13. data/lib/packetgen/plugin/netbios/datagram.rb +108 -0
  14. data/lib/packetgen/plugin/netbios/name.rb +64 -0
  15. data/lib/packetgen/plugin/netbios/session.rb +72 -0
  16. data/lib/packetgen/plugin/ntlm.rb +211 -0
  17. data/lib/packetgen/plugin/ntlm/authenticate.rb +197 -0
  18. data/lib/packetgen/plugin/ntlm/av_pair.rb +115 -0
  19. data/lib/packetgen/plugin/ntlm/challenge.rb +140 -0
  20. data/lib/packetgen/plugin/ntlm/negotiate.rb +127 -0
  21. data/lib/packetgen/plugin/ntlm/ntlmv2_response.rb +59 -0
  22. data/lib/packetgen/plugin/smb.rb +27 -15
  23. data/lib/packetgen/plugin/smb/blocks.rb +2 -4
  24. data/lib/packetgen/plugin/smb/browser.rb +8 -8
  25. data/lib/packetgen/plugin/smb/browser/domain_announcement.rb +2 -7
  26. data/lib/packetgen/plugin/smb/browser/host_announcement.rb +10 -7
  27. data/lib/packetgen/plugin/smb/browser/local_master_announcement.rb +2 -7
  28. data/lib/packetgen/plugin/smb/close.rb +2 -2
  29. data/lib/packetgen/plugin/smb/close/request.rb +3 -3
  30. data/lib/packetgen/plugin/smb/close/response.rb +3 -3
  31. data/lib/packetgen/plugin/smb/filetime.rb +30 -3
  32. data/lib/packetgen/plugin/smb/negotiate.rb +20 -0
  33. data/lib/packetgen/plugin/smb/negotiate/dialect.rb +39 -0
  34. data/lib/packetgen/plugin/smb/negotiate/request.rb +35 -0
  35. data/lib/packetgen/plugin/smb/negotiate/response.rb +29 -0
  36. data/lib/packetgen/plugin/smb/nt_create_and_x.rb +2 -2
  37. data/lib/packetgen/plugin/smb/ntcreateandx/request.rb +5 -5
  38. data/lib/packetgen/plugin/smb/ntcreateandx/response.rb +3 -3
  39. data/lib/packetgen/plugin/smb/string.rb +60 -23
  40. data/lib/packetgen/plugin/smb/trans.rb +2 -2
  41. data/lib/packetgen/plugin/smb/trans/request.rb +4 -4
  42. data/lib/packetgen/plugin/smb/trans/response.rb +3 -3
  43. data/lib/packetgen/plugin/smb2.rb +20 -9
  44. data/lib/packetgen/plugin/smb2/base.rb +5 -7
  45. data/lib/packetgen/plugin/smb2/error.rb +3 -4
  46. data/lib/packetgen/plugin/smb2/guid.rb +6 -4
  47. data/lib/packetgen/plugin/smb2/negotiate.rb +2 -2
  48. data/lib/packetgen/plugin/smb2/negotiate/context.rb +28 -27
  49. data/lib/packetgen/plugin/smb2/negotiate/request.rb +16 -12
  50. data/lib/packetgen/plugin/smb2/negotiate/response.rb +25 -14
  51. data/lib/packetgen/plugin/smb2/session_setup.rb +2 -2
  52. data/lib/packetgen/plugin/smb2/session_setup/request.rb +12 -7
  53. data/lib/packetgen/plugin/smb2/session_setup/response.rb +13 -8
  54. data/lib/packetgen/plugin/smb_version.rb +3 -1
  55. data/packetgen-plugin-smb.gemspec +10 -15
  56. metadata +28 -81
  57. data/.travis.yml +0 -12
@@ -1,10 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of packetgen-plugin-smb.
2
4
  # See https://github.com/sdaubert/packetgen-plugin-smb for more informations
3
5
  # Copyright (C) 2018 Sylvain Daubert <sylvain.daubert@laposte.net>
4
6
  # This program is published under MIT license.
5
7
 
6
- # frozen_string_literal: true
7
-
8
8
  module PacketGen::Plugin
9
9
  # Server Message Block version 2 and 3 (SMB2) header.
10
10
  # @author Sylvain Daubert
@@ -38,6 +38,9 @@ module PacketGen::Plugin
38
38
  # SMB2 header size
39
39
  HEADER_SIZE = 64
40
40
 
41
+ # SMB2 pad field at its maximum length
42
+ MAX_PADDING = [0].pack('q').freeze
43
+
41
44
  # @!attribute protocol
42
45
  # This field must contain {MARKER SMB2 marker}
43
46
  # @return [String]
@@ -81,7 +84,7 @@ module PacketGen::Plugin
81
84
  # 64-bit unique ID that is created by the server to handle operations
82
85
  # asynchronously. Only present for asynchronous messages.
83
86
  # @return [Integer]
84
- define_field :async_id, PacketGen::Types::Int64le, optional: ->(h) { h.flags & 2 == 2}
87
+ define_field :async_id, PacketGen::Types::Int64le, optional: ->(h) { h.flags & 2 == 2 }
85
88
  # @!attribute reserved
86
89
  # 32-bit reserved field.
87
90
  # Only present for synchronous messages.
@@ -145,7 +148,19 @@ module PacketGen::Plugin
145
148
  PacketGen::Header.add_class krequest
146
149
  self.bind krequest, command: SMB2::COMMANDS[command], flags: ->(v) { v.nil? ? 0 : (v & 1).zero? }
147
150
  PacketGen::Header.add_class kresponse
148
- self.bind kresponse, command: SMB2::COMMANDS[command], flags: ->(v) { v.nil? ? 0 : (v & 1 == 1) }
151
+ self.bind kresponse, command: SMB2::COMMANDS[command], flags: ->(v) { v.nil? ? 1 : (v & 1 == 1) }
152
+ end
153
+
154
+ # Invert {#flags_response?}
155
+ # @return [self]
156
+ def reply!
157
+ self.flags_response = !flags_response?
158
+ end
159
+
160
+ # Check if this is really a SMB2 header. Check {#protocol} has value {MARKER}.
161
+ # @return [Boolean]
162
+ def parse?
163
+ protocol == MARKER
149
164
  end
150
165
 
151
166
  # @return [String]
@@ -166,13 +181,9 @@ module PacketGen::Plugin
166
181
  end
167
182
  end
168
183
  end
169
- # TODO: move this in netbios file when packetgen3 will be out
170
- PacketGen::Header::TCP.bind PacketGen::Header::NetBIOS::Session, dport: 445
171
- PacketGen::Header::TCP.bind PacketGen::Header::NetBIOS::Session, sport: 445
172
184
 
173
185
  PacketGen::Header.add_class SMB2
174
- PacketGen::Header::NetBIOS::Session.bind SMB2, body: ->(val) { val.nil? ? SMB2::MARKER : val[0..3] == SMB2::MARKER }
175
- PacketGen::Header::NetBIOS::Datagram.bind SMB2, body: ->(val) { val.nil? ? SMB2::MARKER : val[0..3] == SMB2::MARKER }
186
+ NetBIOS::Session.bind SMB2, body: ->(val) { val.nil? ? SMB2::MARKER : val[0..3] == SMB2::MARKER }
176
187
  end
177
188
 
178
189
  require_relative 'smb2/base'
@@ -1,10 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of packetgen-plugin-smb.
2
4
  # See https://github.com/sdaubert/packetgen-plugin-smb for more informations
3
5
  # Copyright (C) 2018 Sylvain Daubert <sylvain.daubert@laposte.net>
4
6
  # This program is published under MIT license.
5
7
 
6
- # frozen_string_literal: true
7
-
8
8
  require_relative 'guid'
9
9
 
10
10
  module PacketGen::Plugin
@@ -19,13 +19,11 @@ module PacketGen::Plugin
19
19
  def self.define_smb2_pad_field(name)
20
20
  prev_field = self.fields.last
21
21
  lf = lambda do |hdr|
22
- len = 8 - (hdr.offset_of(prev_field) + hdr[prev_field].sz) % 8
23
- len = 0 if len == 8
24
- len
22
+ (8 - (hdr.offset_of(prev_field) + hdr[prev_field].sz) % 8) % 8
25
23
  end
26
- define_field name, PacketGen::Types::String, default: [0].pack('q').freeze,
24
+ define_field name, PacketGen::Types::String, default: SMB2::MAX_PADDING,
27
25
  builder: ->(h, t) { t.new(length_from: -> { lf[h] }) }
28
26
  end
29
27
  end
30
28
  end
31
- end
29
+ end
@@ -1,10 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of packetgen-plugin-smb.
2
4
  # See https://github.com/sdaubert/packetgen-plugin-smb for more informations
3
5
  # Copyright (C) 2018 Sylvain Daubert <sylvain.daubert@laposte.net>
4
6
  # This program is published under MIT license.
5
7
 
6
- # frozen_string_literal: true
7
-
8
8
  module PacketGen::Plugin
9
9
  class SMB2
10
10
  # SMB2 Error response structure
@@ -45,6 +45,5 @@ module PacketGen::Plugin
45
45
  end
46
46
  end
47
47
  PacketGen::Header.add_class SMB2::ErrorResponse
48
- SMB2.bind SMB2::ErrorResponse, status: ->(v) { v > 0 }
48
+ SMB2.bind SMB2::ErrorResponse, status: ->(v) { v.positive? }
49
49
  end
50
-
@@ -1,10 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of packetgen-plugin-smb.
2
4
  # See https://github.com/sdaubert/packetgen-plugin-smb for more informations
3
5
  # Copyright (C) 2018 Sylvain Daubert <sylvain.daubert@laposte.net>
4
6
  # This program is published under MIT license.
5
7
 
6
- # frozen_string_literal: true
7
-
8
8
  module PacketGen::Plugin
9
9
  class SMB2
10
10
  # GUID, also known as UUID, is a 16-byte structure, intended to serve
@@ -22,6 +22,8 @@ module PacketGen::Plugin
22
22
  # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
23
23
  # @author Sylvain Daubert
24
24
  class GUID < PacketGen::Types::Fields
25
+ include PacketGen::Types::Fieldable
26
+
25
27
  # @!attribute data1
26
28
  # 32-bit little-endian data1
27
29
  # @return [Integer]
@@ -52,7 +54,8 @@ module PacketGen::Plugin
52
54
  # @param [String] guid
53
55
  # @return [self]
54
56
  def from_human(guid)
55
- return self
57
+ return self if guid.nil? || guid.empty?
58
+
56
59
  values = guid.split('-')
57
60
  return self if values.size != 5
58
61
 
@@ -65,4 +68,3 @@ module PacketGen::Plugin
65
68
  end
66
69
  end
67
70
  end
68
-
@@ -1,10 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of packetgen-plugin-smb.
2
4
  # See https://github.com/sdaubert/packetgen-plugin-smb for more informations
3
5
  # Copyright (C) 2018 Sylvain Daubert <sylvain.daubert@laposte.net>
4
6
  # This program is published under MIT license.
5
7
 
6
- # frozen_string_literal: true
7
-
8
8
  require_relative 'guid'
9
9
 
10
10
  module PacketGen::Plugin
@@ -1,10 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of packetgen-plugin-smb.
2
4
  # See https://github.com/sdaubert/packetgen-plugin-smb for more informations
3
5
  # Copyright (C) 2018 Sylvain Daubert <sylvain.daubert@laposte.net>
4
6
  # This program is published under MIT license.
5
7
 
6
- # frozen_string_literal: true
7
-
8
8
  module PacketGen::Plugin
9
9
  class SMB2
10
10
  module Negotiate
@@ -47,31 +47,11 @@ module PacketGen::Plugin
47
47
  # @!attribute pad
48
48
  # Padding to align next context on a 8-byte offset
49
49
  # @return [String]
50
- define_field :pad, PacketGen::Types::String, builder: ->(h, t) { t.new(length_from: -> { v = 8 - (h.offset_of(:data) + h.data_length) % 8; v == 8 ? 0 : v }) }
50
+ define_field :pad, PacketGen::Types::String, builder: ->(h, t) { t.new(length_from: -> { 8 - (h.offset_of(:data) + h.data_length) % 8 }) }
51
51
 
52
52
  # @private
53
53
  alias old_read read
54
54
 
55
- # Populate object from a binary string
56
- # @param [String] str
57
- # @return [Context]
58
- def read(str)
59
- return self if str.nil?
60
- if self.instance_of? Context
61
- self[:type].read str[0, 2]
62
- name = TYPES.key(type)
63
- return old_read(str) if name.nil?
64
-
65
- klassname = name.downcase.capitalize.gsub(/_(\w)/) { $1.upcase }
66
- return old_read(str) unless Negotiate.const_defined? klassname
67
-
68
- klass = Negotiate.const_get(klassname)
69
- klass.new.read str
70
- else
71
- old_read str
72
- end
73
- end
74
-
75
55
  # Get human-readable type
76
56
  # @return [String]
77
57
  def human_type
@@ -83,8 +63,16 @@ module PacketGen::Plugin
83
63
  def to_human
84
64
  human_type
85
65
  end
66
+
67
+ # Set {#data_length} field
68
+ # @return [Integer]
69
+ def calc_length
70
+ self[:pad].read SMB2::MAX_PADDING
71
+ self.data_length = sz - self[:pad].sz - 8
72
+ end
86
73
  end
87
74
 
75
+ # Specialized {Context} for PREAUTH_INTEGRITY_CAP type.
88
76
  class PreauthIntegrityCap < Context
89
77
  remove_field :data
90
78
  # @!attribute hash_alg_count
@@ -104,9 +92,10 @@ module PacketGen::Plugin
104
92
  # Salt value for hash
105
93
  # @return [String]
106
94
  define_field_before :pad, :salt, PacketGen::Types::String, builder: ->(h, t) { t.new(length_from: h[:salt_length]) }
107
- update_field :pad, builder: ->(h, t) { t.new(length_from: -> { v = 8 - (h.offset_of(:salt) + h.salt_length) % 8; v == 8 ? 0 : v }) }
95
+ update_field :pad, builder: ->(h, t) { t.new(length_from: -> { (8 - (h.offset_of(:salt) + h.salt_length) % 8) % 8 }) }
108
96
  end
109
97
 
98
+ # Specialized {Context} for ENCRYPTION_CAP type.
110
99
  class EncryptionCap < Context
111
100
  remove_field :data
112
101
  # @!attribute cipher_count
@@ -117,15 +106,27 @@ module PacketGen::Plugin
117
106
  # Array of 16-bit integer IDs specifying the supported encryption
118
107
  # algorithms
119
108
  # @return [PacketGen::Types::ArrayOfInt16le]
120
- define_field_before :pad, :ciphers, PacketGen::Types::ArrayOfInt16le, builder: ->(h, t) { t.new(counter: h[:hash_alg_count]) }
121
- update_field :pad, builder: ->(h, t) { t.new(length_from: -> { v = 8 - (h.offset_of(:cipher_count) + h[:cipher_count].sz) % 8; v == 8 ? 0 : v }) }
109
+ define_field_before :pad, :ciphers, PacketGen::Types::ArrayOfInt16le, builder: ->(h, t) { t.new(counter: h[:cipher_count]) }
110
+ update_field :pad, builder: ->(h, t) { t.new(length_from: -> { (8 - (h.offset_of(:cipher_count) + h[:cipher_count].sz) % 8) % 8 }) }
122
111
  end
123
112
 
124
113
  # Array of {Context}
125
114
  # @author Sylvain Daubert
126
115
  class ArrayOfContext < PacketGen::Types::Array
127
116
  set_of Context
117
+
118
+ private
119
+
120
+ def real_type(ctx)
121
+ name = Context::TYPES.key(ctx.type).to_s
122
+ klassname = name.downcase.capitalize.gsub(/_(\w)/) { $1.upcase }
123
+ if !klassname.empty? && Negotiate.const_defined?(klassname)
124
+ Negotiate.const_get(klassname)
125
+ else
126
+ ctx.class
127
+ end
128
+ end
128
129
  end
129
130
  end
130
131
  end
131
- end
132
+ end
@@ -1,10 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of packetgen-plugin-smb.
2
4
  # See https://github.com/sdaubert/packetgen-plugin-smb for more informations
3
5
  # Copyright (C) 2018 Sylvain Daubert <sylvain.daubert@laposte.net>
4
6
  # This program is published under MIT license.
5
7
 
6
- # frozen_string_literal: true
7
-
8
8
  module PacketGen::Plugin
9
9
  class SMB2
10
10
  module Negotiate
@@ -48,7 +48,7 @@ module PacketGen::Plugin
48
48
  SECURITY_MODES = {
49
49
  'signing_enabled' => 1,
50
50
  'signing required' => 2
51
- }
51
+ }.freeze
52
52
 
53
53
  # @!attribute structure_size
54
54
  # 16-bit negotiate request structure size. Should be 36.
@@ -124,6 +124,12 @@ module PacketGen::Plugin
124
124
  # @return [ArrayOfContext]
125
125
  define_field :context_list, ArrayOfContext, builder: ->(h, t) { t.new(counter: h[:context_count]) }
126
126
 
127
+ # Protocol name
128
+ # @return [String]
129
+ def self.protocol_name
130
+ 'SMB2::Negotiate::Request'
131
+ end
132
+
127
133
  # @return [String]
128
134
  def inspect
129
135
  super do |attr|
@@ -140,7 +146,7 @@ module PacketGen::Plugin
140
146
  str << PacketGen::Inspect::FMT_ATTR % [self[attr].class.to_s.sub(/.*::/, ''),
141
147
  attr, value]
142
148
  when :dialects
143
- list = self.dialects.map { |v| "%#04x" % v.to_i }.join(',')
149
+ list = self.dialects.map { |v| '%#x' % v.to_i }.join(',')
144
150
  str = PacketGen::Inspect.shift_level
145
151
  str << PacketGen::Inspect::FMT_ATTR % [self[attr].class.to_s.sub(/.*::/, ''),
146
152
  attr, list]
@@ -148,17 +154,15 @@ module PacketGen::Plugin
148
154
  end
149
155
  end
150
156
 
151
- # Calculate and set {#context_offset} field. Also calculate
152
- # lengths in {Context contexts}.
157
+ # Calculate and set {#context_offset} and {#pad} fields.
158
+ # Also calculate lengths in {Context contexts}.
153
159
  # @return [Integer]
154
160
  def calc_length
155
- self.context_offset = SMB2.new.sz + offset_of(:context_list)
156
- end
161
+ self[:pad].read SMB2::MAX_PADDING
157
162
 
158
- # Protocol name
159
- # @return [String]
160
- def protocol_name
161
- 'SMB2::Negotiate::Request'
163
+ self.context_offset = 0
164
+ self.context_offset = SMB2::HEADER_SIZE + offset_of(:context_list) unless context_list.empty?
165
+ context_list.each { |ctx| ctx.calc_length if ctx.respond_to? :calc_length }
162
166
  end
163
167
  end
164
168
  end
@@ -1,10 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of packetgen-plugin-smb.
2
4
  # See https://github.com/sdaubert/packetgen-plugin-smb for more informations
3
5
  # Copyright (C) 2018 Sylvain Daubert <sylvain.daubert@laposte.net>
4
6
  # This program is published under MIT license.
5
7
 
6
- # frozen_string_literal: true
7
-
8
8
  module PacketGen::Plugin
9
9
  class SMB2
10
10
  module Negotiate
@@ -140,10 +140,11 @@ module PacketGen::Plugin
140
140
  define_field :context_offset, PacketGen::Types::Int32le
141
141
  # @!attribute buffer
142
142
  # @return [GSSAPI]
143
- define_field :buffer, GSSAPI, token: :init
143
+ define_field :buffer, GSSAPI, token: :init, optional: ->(h) { h.buffer_offset.positive? }
144
144
  # @!attribute pad
145
145
  # Optional padding between the end of the {#buffer} field and the first negotiate
146
- # context in {#context_list} so that the first negotiate context is 8-byte aligned.
146
+ # context in {#context_list} so that the first negotiate context is 8-byte aligned
147
+ # with start of SMB2 header.
147
148
  # @return [String]
148
149
  define_smb2_pad_field :pad
149
150
  # @!attribute context_list
@@ -152,10 +153,17 @@ module PacketGen::Plugin
152
153
  # @return [ArrayOfContext]
153
154
  define_field :context_list, ArrayOfContext, builder: ->(h, t) { t.new(counter: h[:context_count]) }
154
155
 
156
+ # Protocol name
157
+ # @return [String]
158
+ def self.protocol_name
159
+ 'SMB2::Negotiate::Response'
160
+ end
161
+
155
162
  # @return [String]
156
163
  def inspect
157
164
  super do |attr|
158
165
  next unless attr == :capabilities
166
+
159
167
  value = bits_on(attr).reject { |_, v| v > 1 }
160
168
  .keys
161
169
  .select { |b| send("#{b}?") }
@@ -169,20 +177,23 @@ module PacketGen::Plugin
169
177
  end
170
178
  end
171
179
 
172
- # Calculate and set {#context_offset}, {#buffer_offset} and {#buffer_length} fields.
180
+ # Calculate and set {#context_offset}, {#buffer_offset}, {#buffer_length} and
181
+ # {#pad} fields.
173
182
  # Also calculate lengths in {Context contexts}.
174
183
  # @return [void]
175
184
  def calc_length
176
- self.context_offset = SMB2.new.sz + offset_of(:context_list)
177
- self.buffer_offset = SMB2.new.sz + offset_of(:buffer)
178
- self.buffer_length = buffer.sz
179
- context_list.each { |ctx| ctx.calc_length if ctx.respond_to? :calc_length }
180
- end
185
+ self[:pad].read SMB2::MAX_PADDING
181
186
 
182
- # Protocol name
183
- # @return [String]
184
- def protocol_name
185
- 'SMB2::Negotiate::Response'
187
+ self.buffer_length = self[:buffer].sz
188
+ self.buffer_offset = if self.buffer_length.zero?
189
+ 0
190
+ else
191
+ SMB2::HEADER_SIZE + offset_of(:buffer)
192
+ end
193
+
194
+ self.context_offset = 0
195
+ self.context_offset = SMB2::HEADER_SIZE + offset_of(:context_list) unless context_list.empty?
196
+ context_list.each { |ctx| ctx.calc_length if ctx.respond_to? :calc_length }
186
197
  end
187
198
  end
188
199
  end
@@ -1,10 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of packetgen-plugin-smb.
2
4
  # See https://github.com/sdaubert/packetgen-plugin-smb for more informations
3
5
  # Copyright (C) 2018 Sylvain Daubert <sylvain.daubert@laposte.net>
4
6
  # This program is published under MIT license.
5
7
 
6
- # frozen_string_literal: true
7
-
8
8
  require_relative 'guid'
9
9
 
10
10
  module PacketGen::Plugin
@@ -1,10 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of packetgen-plugin-smb.
2
4
  # See https://github.com/sdaubert/packetgen-plugin-smb for more informations
3
5
  # Copyright (C) 2018 Sylvain Daubert <sylvain.daubert@laposte.net>
4
6
  # This program is published under MIT license.
5
7
 
6
- # frozen_string_literal: true
7
-
8
8
  module PacketGen::Plugin
9
9
  class SMB2
10
10
  module SessionSetup
@@ -79,20 +79,25 @@ module PacketGen::Plugin
79
79
  define_field :prev_session_id, PacketGen::Types::Int64le
80
80
  # @!attribute buffer
81
81
  # @return [GSSAPI]
82
- define_field :buffer, GSSAPI, token: :response
82
+ define_field :buffer, GSSAPI, token: :response, optional: ->(h) { h.buffer_offset.positive? }
83
83
 
84
- # Calculate and set {#buffer_length} field.
84
+ # Calculate and set {#buffer_length} and {#buffer_offset} fields.
85
85
  # @return [void]
86
86
  def calc_length
87
- self.buffer_length = buffer.sz
87
+ self.buffer_length = self[:buffer].sz
88
+ self.buffer_offset = if self.buffer_length.zero?
89
+ 0
90
+ else
91
+ SMB2.new.sz + offset_of(:buffer)
92
+ end
88
93
  end
89
94
 
90
95
  # Protocol name
91
96
  # @return [String]
92
- def protocol_name
97
+ def self.protocol_name
93
98
  'SMB2::SessionSetup::Request'
94
99
  end
95
100
  end
96
101
  end
97
102
  end
98
- end
103
+ end