ruby_smb 2.0.2 → 2.0.3
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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/examples/anonymous_auth.rb +3 -3
- data/examples/append_file.rb +10 -8
- data/examples/authenticate.rb +9 -5
- data/examples/delete_file.rb +8 -6
- data/examples/enum_registry_key.rb +5 -4
- data/examples/enum_registry_values.rb +5 -4
- data/examples/list_directory.rb +8 -6
- data/examples/negotiate_with_netbios_service.rb +9 -5
- data/examples/net_share_enum_all.rb +6 -4
- data/examples/pipes.rb +11 -12
- data/examples/query_service_status.rb +64 -0
- data/examples/read_file.rb +8 -6
- data/examples/read_registry_key_value.rb +6 -5
- data/examples/rename_file.rb +9 -7
- data/examples/tree_connect.rb +7 -5
- data/examples/write_file.rb +9 -7
- data/lib/ruby_smb/client.rb +72 -43
- data/lib/ruby_smb/client/negotiation.rb +1 -0
- data/lib/ruby_smb/dcerpc.rb +2 -0
- data/lib/ruby_smb/dcerpc/error.rb +3 -0
- data/lib/ruby_smb/dcerpc/ndr.rb +209 -44
- data/lib/ruby_smb/dcerpc/request.rb +13 -0
- data/lib/ruby_smb/dcerpc/rpc_security_attributes.rb +34 -0
- data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +9 -6
- data/lib/ruby_smb/dcerpc/svcctl.rb +479 -0
- data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request.rb +48 -0
- data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response.rb +26 -0
- data/lib/ruby_smb/dcerpc/svcctl/close_service_handle_request.rb +25 -0
- data/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response.rb +26 -0
- data/lib/ruby_smb/dcerpc/svcctl/control_service_request.rb +26 -0
- data/lib/ruby_smb/dcerpc/svcctl/control_service_response.rb +26 -0
- data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request.rb +35 -0
- data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response.rb +23 -0
- data/lib/ruby_smb/dcerpc/svcctl/open_service_w_request.rb +31 -0
- data/lib/ruby_smb/dcerpc/svcctl/open_service_w_response.rb +23 -0
- data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request.rb +25 -0
- data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response.rb +44 -0
- data/lib/ruby_smb/dcerpc/svcctl/query_service_status_request.rb +23 -0
- data/lib/ruby_smb/dcerpc/svcctl/query_service_status_response.rb +27 -0
- data/lib/ruby_smb/dcerpc/svcctl/service_status.rb +25 -0
- data/lib/ruby_smb/dcerpc/svcctl/start_service_w_request.rb +27 -0
- data/lib/ruby_smb/dcerpc/svcctl/start_service_w_response.rb +25 -0
- data/lib/ruby_smb/dcerpc/winreg.rb +98 -17
- data/lib/ruby_smb/dcerpc/winreg/create_key_request.rb +73 -0
- data/lib/ruby_smb/dcerpc/winreg/create_key_response.rb +36 -0
- data/lib/ruby_smb/dcerpc/winreg/enum_key_request.rb +1 -1
- data/lib/ruby_smb/dcerpc/winreg/enum_value_request.rb +1 -1
- data/lib/ruby_smb/dcerpc/winreg/enum_value_response.rb +1 -1
- data/lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb +4 -4
- data/lib/ruby_smb/dcerpc/winreg/query_info_key_request.rb +1 -1
- data/lib/ruby_smb/dcerpc/winreg/query_value_request.rb +7 -6
- data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +10 -10
- data/lib/ruby_smb/dcerpc/winreg/save_key_request.rb +37 -0
- data/lib/ruby_smb/dcerpc/winreg/save_key_response.rb +23 -0
- data/lib/ruby_smb/dispatcher/base.rb +1 -1
- data/lib/ruby_smb/dispatcher/socket.rb +1 -1
- data/lib/ruby_smb/field/stringz16.rb +17 -1
- data/lib/ruby_smb/nbss/session_header.rb +4 -4
- data/lib/ruby_smb/smb1/file.rb +2 -10
- data/lib/ruby_smb/smb1/pipe.rb +2 -0
- data/lib/ruby_smb/smb2/file.rb +25 -17
- data/lib/ruby_smb/smb2/pipe.rb +3 -0
- data/lib/ruby_smb/smb2/tree.rb +9 -3
- data/lib/ruby_smb/version.rb +1 -1
- data/spec/lib/ruby_smb/client_spec.rb +161 -60
- data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +1396 -77
- data/spec/lib/ruby_smb/dcerpc/rpc_security_attributes_spec.rb +161 -0
- data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +49 -12
- data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request_spec.rb +191 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_request_spec.rb +30 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_request_spec.rb +39 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request_spec.rb +78 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_request_spec.rb +59 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response_spec.rb +152 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_request_spec.rb +30 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/service_status_spec.rb +72 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_request_spec.rb +46 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_response_spec.rb +30 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl_spec.rb +512 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/create_key_request_spec.rb +110 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/create_key_response_spec.rb +44 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +0 -4
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_request_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_response_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +9 -4
- data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_request_spec.rb +0 -4
- data/spec/lib/ruby_smb/dcerpc/winreg/query_value_request_spec.rb +17 -17
- data/spec/lib/ruby_smb/dcerpc/winreg/query_value_response_spec.rb +11 -23
- data/spec/lib/ruby_smb/dcerpc/winreg/save_key_request_spec.rb +57 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/save_key_response_spec.rb +22 -0
- data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +215 -41
- data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +10 -10
- data/spec/lib/ruby_smb/field/stringz16_spec.rb +12 -0
- data/spec/lib/ruby_smb/nbss/session_header_spec.rb +4 -11
- data/spec/lib/ruby_smb/smb1/pipe_spec.rb +7 -0
- data/spec/lib/ruby_smb/smb2/file_spec.rb +60 -6
- data/spec/lib/ruby_smb/smb2/pipe_spec.rb +7 -0
- data/spec/lib/ruby_smb/smb2/tree_spec.rb +35 -1
- metadata +72 -2
- metadata.gz.sig +0 -0
data/examples/write_file.rb
CHANGED
@@ -9,18 +9,20 @@
|
|
9
9
|
require 'bundler/setup'
|
10
10
|
require 'ruby_smb'
|
11
11
|
|
12
|
-
address
|
13
|
-
username
|
14
|
-
password
|
15
|
-
share
|
16
|
-
file
|
17
|
-
data
|
12
|
+
address = ARGV[0]
|
13
|
+
username = ARGV[1]
|
14
|
+
password = ARGV[2]
|
15
|
+
share = ARGV[3]
|
16
|
+
file = ARGV[4]
|
17
|
+
data = ARGV[5]
|
18
|
+
smb_versions = ARGV[6]&.split(',') || ['1','2','3']
|
19
|
+
|
18
20
|
path = "\\\\#{address}\\#{share}"
|
19
21
|
|
20
22
|
sock = TCPSocket.new address, 445
|
21
23
|
dispatcher = RubySMB::Dispatcher::Socket.new(sock)
|
22
24
|
|
23
|
-
client = RubySMB::Client.new(dispatcher, smb1:
|
25
|
+
client = RubySMB::Client.new(dispatcher, smb1: smb_versions.include?('1'), smb2: smb_versions.include?('2'), smb3: smb_versions.include?('3'), username: username, password: password)
|
24
26
|
protocol = client.negotiate
|
25
27
|
status = client.authenticate
|
26
28
|
|
data/lib/ruby_smb/client.rb
CHANGED
@@ -160,9 +160,16 @@ module RubySMB
|
|
160
160
|
|
161
161
|
# The UID set in SMB1
|
162
162
|
# @!attribute [rw] user_id
|
163
|
-
# @return [
|
163
|
+
# @return [Integer]
|
164
164
|
attr_accessor :user_id
|
165
165
|
|
166
|
+
# The Process ID set in SMB1
|
167
|
+
# It is randomly generated during the client initialization, but can
|
168
|
+
# be modified using the accessor.
|
169
|
+
# @!attribute [rw] pid
|
170
|
+
# @return [Integer]
|
171
|
+
attr_accessor :pid
|
172
|
+
|
166
173
|
# The maximum size SMB message that the Client accepts (in bytes)
|
167
174
|
# The default value is equal to {MAX_BUFFER_SIZE}.
|
168
175
|
# @!attribute [rw] max_buffer_size
|
@@ -258,6 +265,14 @@ module RubySMB
|
|
258
265
|
# @return [Integer] the negotiated SMB version
|
259
266
|
attr_accessor :negotiated_smb_version
|
260
267
|
|
268
|
+
# Whether or not the server supports multi-credit operations. It is
|
269
|
+
# reported by the LARGE_MTU capabiliy as part of the negotiation process
|
270
|
+
# (SMB 2.x and 3.x).
|
271
|
+
# @!attribute [rw] server_supports_multi_credit
|
272
|
+
# @return [Boolean] true if the server supports multi-credit operations,
|
273
|
+
# false otherwise
|
274
|
+
attr_accessor :server_supports_multi_credit
|
275
|
+
|
261
276
|
# @param dispatcher [RubySMB::Dispatcher::Socket] the packet dispatcher to use
|
262
277
|
# @param smb1 [Boolean] whether or not to enable SMB1 support
|
263
278
|
# @param smb2 [Boolean] whether or not to enable SMB2 support
|
@@ -268,6 +283,7 @@ module RubySMB
|
|
268
283
|
raise ArgumentError, 'You must enable at least one Protocol'
|
269
284
|
end
|
270
285
|
@dispatcher = dispatcher
|
286
|
+
@pid = rand(0xFFFF)
|
271
287
|
@domain = domain
|
272
288
|
@local_workstation = local_workstation
|
273
289
|
@password = password.encode('utf-8') || ''.encode('utf-8')
|
@@ -285,6 +301,7 @@ module RubySMB
|
|
285
301
|
@server_max_read_size = RubySMB::SMB2::File::MAX_PACKET_SIZE
|
286
302
|
@server_max_write_size = RubySMB::SMB2::File::MAX_PACKET_SIZE
|
287
303
|
@server_max_transact_size = RubySMB::SMB2::File::MAX_PACKET_SIZE
|
304
|
+
@server_supports_multi_credit = false
|
288
305
|
|
289
306
|
# SMB 3.x options
|
290
307
|
@session_encrypt_data = always_encrypt
|
@@ -344,7 +361,7 @@ module RubySMB
|
|
344
361
|
# @param packet [RubySMB::GenericPacket] the packet to set the message id for
|
345
362
|
# @return [RubySMB::GenericPacket] the modified packet
|
346
363
|
def increment_smb_message_id(packet)
|
347
|
-
packet.smb2_header.message_id = smb2_message_id
|
364
|
+
packet.smb2_header.message_id = self.smb2_message_id
|
348
365
|
self.smb2_message_id += 1
|
349
366
|
packet
|
350
367
|
end
|
@@ -427,7 +444,8 @@ module RubySMB
|
|
427
444
|
version = packet.packet_smb_version
|
428
445
|
case version
|
429
446
|
when 'SMB1'
|
430
|
-
packet.smb_header.uid = user_id if user_id
|
447
|
+
packet.smb_header.uid = self.user_id if self.user_id
|
448
|
+
packet.smb_header.pid_low = self.pid if self.pid
|
431
449
|
packet = smb1_sign(packet)
|
432
450
|
when 'SMB2'
|
433
451
|
packet = increment_smb_message_id(packet)
|
@@ -443,35 +461,32 @@ module RubySMB
|
|
443
461
|
packet = packet
|
444
462
|
end
|
445
463
|
|
464
|
+
encrypt_data = false
|
446
465
|
if can_be_encrypted?(packet) && encryption_supported? && (@session_encrypt_data || encrypt)
|
447
|
-
|
448
|
-
raw_response = recv_encrypt
|
449
|
-
loop do
|
450
|
-
break unless is_status_pending?(raw_response)
|
451
|
-
sleep 1
|
452
|
-
raw_response = recv_encrypt
|
453
|
-
end
|
454
|
-
else
|
455
|
-
dispatcher.send_packet(packet)
|
456
|
-
raw_response = dispatcher.recv_packet
|
457
|
-
loop do
|
458
|
-
break unless is_status_pending?(raw_response)
|
459
|
-
sleep 1
|
460
|
-
raw_response = dispatcher.recv_packet
|
461
|
-
end unless version == 'SMB1'
|
466
|
+
encrypt_data = true
|
462
467
|
end
|
468
|
+
send_packet(packet, encrypt: encrypt_data)
|
469
|
+
raw_response = recv_packet(encrypt: encrypt_data)
|
470
|
+
smb2_header = nil
|
471
|
+
loop do
|
472
|
+
smb2_header = RubySMB::SMB2::SMB2Header.read(raw_response)
|
473
|
+
break unless is_status_pending?(smb2_header)
|
474
|
+
sleep 1
|
475
|
+
raw_response = recv_packet(encrypt: encrypt_data)
|
476
|
+
end unless version == 'SMB1'
|
463
477
|
|
464
478
|
self.sequence_counter += 1 if signing_required && !session_key.empty?
|
479
|
+
# update the SMB2 message ID according to the received Credit Charged
|
480
|
+
self.smb2_message_id += smb2_header.credit_charge - 1 if smb2_header && self.dialect != '0x0202'
|
465
481
|
raw_response
|
466
482
|
end
|
467
483
|
|
468
484
|
# Check if the response is an asynchronous operation with STATUS_PENDING
|
469
485
|
# status code.
|
470
486
|
#
|
471
|
-
# @param
|
487
|
+
# @param smb2_header [String] the response packet SMB2 header
|
472
488
|
# @return [Boolean] true if it is a status pending operation, false otherwise
|
473
|
-
def is_status_pending?(
|
474
|
-
smb2_header = RubySMB::SMB2::SMB2Header.read(raw_response)
|
489
|
+
def is_status_pending?(smb2_header)
|
475
490
|
value = smb2_header.nt_status.value
|
476
491
|
status_code = WindowsError::NTStatus.find_by_retval(value).first
|
477
492
|
status_code == WindowsError::NTStatus::STATUS_PENDING &&
|
@@ -497,37 +512,50 @@ module RubySMB
|
|
497
512
|
['0x0300', '0x0302', '0x0311'].include?(@dialect)
|
498
513
|
end
|
499
514
|
|
500
|
-
# Encrypt and send a packet
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
515
|
+
# Encrypt (if required) and send a packet.
|
516
|
+
#
|
517
|
+
# @param encrypt [Boolean] true if the packet should be encrypted, false
|
518
|
+
# otherwise
|
519
|
+
def send_packet(packet, encrypt: false)
|
520
|
+
if encrypt
|
521
|
+
begin
|
522
|
+
packet = smb3_encrypt(packet.to_binary_s)
|
523
|
+
rescue RubySMB::Error::RubySMBError => e
|
524
|
+
raise RubySMB::Error::EncryptionError, "Error while encrypting #{packet.class.name} packet (SMB #{@dialect}): #{e}"
|
525
|
+
end
|
506
526
|
end
|
507
|
-
dispatcher.send_packet(
|
527
|
+
dispatcher.send_packet(packet)
|
508
528
|
end
|
509
529
|
|
510
|
-
# Receives the raw response through the Dispatcher and decrypt the packet.
|
530
|
+
# Receives the raw response through the Dispatcher and decrypt the packet (if required).
|
511
531
|
#
|
532
|
+
# @param encrypt [Boolean] true if the packet is encrypted, false otherwise
|
512
533
|
# @return [String] the raw unencrypted packet
|
513
|
-
def
|
534
|
+
def recv_packet(encrypt: false)
|
514
535
|
begin
|
515
536
|
raw_response = dispatcher.recv_packet
|
516
537
|
rescue RubySMB::Error::CommunicationError => e
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
raise RubySMB::Error::InvalidPacket, 'Not a SMB2 TransformHeader packet'
|
538
|
+
if encrypt
|
539
|
+
raise RubySMB::Error::EncryptionError, "Communication error with the "\
|
540
|
+
"remote host: #{e.message}. The server supports encryption but was "\
|
541
|
+
"not able to handle the encrypted request."
|
542
|
+
else
|
543
|
+
raise e
|
544
|
+
end
|
525
545
|
end
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
546
|
+
if encrypt
|
547
|
+
begin
|
548
|
+
transform_response = RubySMB::SMB2::Packet::TransformHeader.read(raw_response)
|
549
|
+
rescue IOError
|
550
|
+
raise RubySMB::Error::InvalidPacket, 'Not a SMB2 TransformHeader packet'
|
551
|
+
end
|
552
|
+
begin
|
553
|
+
raw_response = smb3_decrypt(transform_response)
|
554
|
+
rescue RubySMB::Error::RubySMBError => e
|
555
|
+
raise RubySMB::Error::EncryptionError, "Error while decrypting #{transform_response.class.name} packet (SMB #@dialect}): #{e}"
|
556
|
+
end
|
530
557
|
end
|
558
|
+
raw_response
|
531
559
|
end
|
532
560
|
|
533
561
|
# Connects to the supplied share
|
@@ -568,6 +596,7 @@ module RubySMB
|
|
568
596
|
self.smb2_message_id = 0
|
569
597
|
self.client_encryption_key = nil
|
570
598
|
self.server_encryption_key = nil
|
599
|
+
self.server_supports_multi_credit = false
|
571
600
|
end
|
572
601
|
|
573
602
|
# Requests a NetBIOS Session Service using the provided name.
|
@@ -605,7 +634,7 @@ module RubySMB
|
|
605
634
|
session_request.session_header.session_packet_type = RubySMB::Nbss::SESSION_REQUEST
|
606
635
|
session_request.called_name = called_name
|
607
636
|
session_request.calling_name = calling_name
|
608
|
-
session_request.session_header.
|
637
|
+
session_request.session_header.stream_protocol_length =
|
609
638
|
session_request.num_bytes - session_request.session_header.num_bytes
|
610
639
|
session_request
|
611
640
|
end
|
@@ -140,6 +140,7 @@ module RubySMB
|
|
140
140
|
self.server_guid = packet.server_guid
|
141
141
|
self.server_start_time = packet.server_start_time.to_time if packet.server_start_time != 0
|
142
142
|
self.server_system_time = packet.system_time.to_time if packet.system_time != 0
|
143
|
+
self.server_supports_multi_credit = self.dialect != '0x0202' && packet&.capabilities&.large_mtu == 1
|
143
144
|
case self.dialect
|
144
145
|
when '0x02ff'
|
145
146
|
when '0x0300', '0x0302'
|
data/lib/ruby_smb/dcerpc.rb
CHANGED
@@ -10,9 +10,11 @@ module RubySMB
|
|
10
10
|
require 'ruby_smb/dcerpc/ptypes'
|
11
11
|
require 'ruby_smb/dcerpc/p_syntax_id_t'
|
12
12
|
require 'ruby_smb/dcerpc/rrp_unicode_string'
|
13
|
+
require 'ruby_smb/dcerpc/rpc_security_attributes'
|
13
14
|
require 'ruby_smb/dcerpc/pdu_header'
|
14
15
|
require 'ruby_smb/dcerpc/srvsvc'
|
15
16
|
require 'ruby_smb/dcerpc/winreg'
|
17
|
+
require 'ruby_smb/dcerpc/svcctl'
|
16
18
|
require 'ruby_smb/dcerpc/request'
|
17
19
|
require 'ruby_smb/dcerpc/response'
|
18
20
|
require 'ruby_smb/dcerpc/bind'
|
data/lib/ruby_smb/dcerpc/ndr.rb
CHANGED
@@ -7,64 +7,88 @@ module RubySMB
|
|
7
7
|
VER_MAJOR = 2
|
8
8
|
VER_MINOR = 0
|
9
9
|
|
10
|
-
|
11
|
-
#
|
12
|
-
#
|
13
|
-
|
10
|
+
|
11
|
+
# An NDR Conformant and Varying String representation as defined in
|
12
|
+
# [Transfer Syntax NDR - Conformant and Varying Strings](http://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_04_02)
|
13
|
+
# The string elements are Stringz16 (unicode)
|
14
|
+
class NdrString < BinData::Primitive
|
14
15
|
endian :little
|
15
16
|
|
16
|
-
uint32
|
17
|
+
uint32 :max_count
|
18
|
+
uint32 :offset, initial_value: 0
|
19
|
+
uint32 :actual_count
|
20
|
+
stringz16 :str, max_length: -> { actual_count * 2 }, onlyif: -> { actual_count > 0 }
|
17
21
|
|
18
22
|
def get
|
19
|
-
|
23
|
+
self.actual_count == 0 ? 0 : self.str
|
20
24
|
end
|
21
25
|
|
22
26
|
def set(v)
|
23
|
-
if v
|
24
|
-
self.
|
27
|
+
if v == 0
|
28
|
+
self.str.clear
|
29
|
+
self.actual_count = 0
|
25
30
|
else
|
26
|
-
|
31
|
+
v = v.str if v.is_a?(self.class)
|
32
|
+
unless self.str.equal?(v)
|
33
|
+
if v.empty?
|
34
|
+
self.actual_count = 0
|
35
|
+
else
|
36
|
+
self.actual_count = v.to_s.size + 1
|
37
|
+
self.max_count = self.actual_count
|
38
|
+
end
|
39
|
+
end
|
40
|
+
self.str = v.to_s
|
27
41
|
end
|
28
42
|
end
|
29
43
|
|
30
|
-
def
|
31
|
-
|
44
|
+
def clear
|
45
|
+
# Make sure #max_count and #offset are not cleared out
|
46
|
+
self.str.clear
|
47
|
+
self.actual_count.clear
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_s
|
51
|
+
self.str.to_s
|
32
52
|
end
|
33
53
|
end
|
34
54
|
|
35
|
-
# An NDR Conformant
|
36
|
-
# [Transfer Syntax NDR - Conformant
|
37
|
-
|
38
|
-
class NdrString < BinData::Primitive
|
55
|
+
# An NDR Uni-dimensional Conformant Array of Bytes representation as defined in
|
56
|
+
# [Transfer Syntax NDR - Uni-dimensional Conformant Arrays](https://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_03_02)
|
57
|
+
class NdrLpByte < BinData::Primitive
|
39
58
|
endian :little
|
40
59
|
|
41
|
-
uint32
|
42
|
-
|
43
|
-
uint32 :actual_count
|
44
|
-
stringz16 :str, read_length: -> { actual_count }, onlyif: -> { actual_count > 0 }
|
60
|
+
uint32 :max_count, initial_value: -> { self.elements.size }
|
61
|
+
array :elements, type: :uint8, read_until: -> { index == self.max_count - 1 }, onlyif: -> { self.max_count > 0 }
|
45
62
|
|
46
63
|
def get
|
47
|
-
self.
|
64
|
+
self.elements
|
48
65
|
end
|
49
66
|
|
50
67
|
def set(v)
|
51
|
-
if v.is_a?(
|
52
|
-
|
53
|
-
|
54
|
-
self.str = v
|
55
|
-
self.max_count = self.actual_count = str.to_binary_s.size / 2
|
56
|
-
end
|
68
|
+
v = v.elements if v.is_a?(self.class)
|
69
|
+
self.elements = v.to_ary
|
70
|
+
self.max_count = self.elements.size unless self.elements.equal?(v)
|
57
71
|
end
|
58
72
|
end
|
59
73
|
|
60
|
-
#
|
61
|
-
|
74
|
+
# An NDR Uni-dimensional Conformant-varying Arrays of bytes representation as defined in:
|
75
|
+
# [Transfer Syntax NDR - NDR Constructed Types](http://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_03_04)
|
76
|
+
class NdrByteArray < BinData::Primitive
|
62
77
|
endian :little
|
63
78
|
|
64
|
-
|
79
|
+
uint32 :max_count, initial_value: -> { self.actual_count }
|
80
|
+
uint32 :offset, initial_value: 0
|
81
|
+
uint32 :actual_count, initial_value: -> { self.bytes.size }
|
82
|
+
array :bytes, :type => :uint8, initial_length: -> { self.actual_count }
|
65
83
|
|
66
|
-
def
|
67
|
-
|
84
|
+
def get
|
85
|
+
self.bytes
|
86
|
+
end
|
87
|
+
|
88
|
+
def set(v)
|
89
|
+
v = v.bytes if v.is_a?(self.class)
|
90
|
+
self.bytes = v.to_ary
|
91
|
+
self.max_count = self.bytes.size unless self.bytes.equal?(v)
|
68
92
|
end
|
69
93
|
end
|
70
94
|
|
@@ -72,6 +96,7 @@ module RubySMB
|
|
72
96
|
# [IDL Data Type Declarations - Basic Type Declarations](http://pubs.opengroup.org/onlinepubs/9629399/apdxn.htm#tagcjh_34_01)
|
73
97
|
class NdrContextHandle < BinData::Primitive
|
74
98
|
endian :little
|
99
|
+
|
75
100
|
uint32 :context_handle_attributes
|
76
101
|
uuid :context_handle_uuid
|
77
102
|
|
@@ -91,30 +116,170 @@ module RubySMB
|
|
91
116
|
end
|
92
117
|
end
|
93
118
|
|
94
|
-
#
|
95
|
-
|
119
|
+
# An NDR Top-level Full Pointers representation as defined in
|
120
|
+
# [Transfer Syntax NDR - Top-level Full Pointers](http://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_11_01)
|
121
|
+
# This class must be inherited and the subclass must have a #referent property
|
122
|
+
class NdrPointer < BinData::Primitive
|
96
123
|
endian :little
|
97
124
|
|
98
|
-
uint32 :
|
125
|
+
uint32 :referent_id, initial_value: 0
|
126
|
+
|
127
|
+
def do_read(io)
|
128
|
+
self.referent_id.do_read(io)
|
129
|
+
if process_referent?
|
130
|
+
self.referent.do_read(io) unless self.referent_id == 0
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def do_write(io)
|
135
|
+
self.referent_id.do_write(io)
|
136
|
+
if process_referent?
|
137
|
+
self.referent.do_write(io) unless self.referent_id == 0
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def set(v)
|
142
|
+
if v == :null
|
143
|
+
self.referent.clear
|
144
|
+
self.referent_id = 0
|
145
|
+
else
|
146
|
+
if self.referent.respond_to?(:set)
|
147
|
+
self.referent.set(v)
|
148
|
+
else
|
149
|
+
self.referent = v
|
150
|
+
end
|
151
|
+
self.referent_id = rand(0xFFFFFFFF) if self.referent_id == 0
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def get
|
156
|
+
if self.referent_id == 0
|
157
|
+
:null
|
158
|
+
else
|
159
|
+
self.referent
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def process_referent?
|
164
|
+
current_parent = parent
|
165
|
+
loop do
|
166
|
+
return true unless current_parent
|
167
|
+
return false if current_parent.is_a?(NdrStruct)
|
168
|
+
current_parent = current_parent.parent
|
169
|
+
end
|
170
|
+
end
|
99
171
|
end
|
100
172
|
|
101
|
-
#
|
102
|
-
|
103
|
-
|
173
|
+
# A pointer to a NdrString structure
|
174
|
+
class NdrLpStr < NdrPointer
|
175
|
+
endian :little
|
176
|
+
|
177
|
+
ndr_string :referent, onlyif: -> { self.referent_id != 0 }
|
178
|
+
end
|
179
|
+
|
180
|
+
class NdrLpDword < NdrPointer
|
181
|
+
endian :little
|
182
|
+
|
183
|
+
uint32 :referent, onlyif: -> { self.referent_id != 0 }
|
184
|
+
end
|
185
|
+
|
186
|
+
# A pointer to an NDR Uni-dimensional Conformant-varying Arrays of bytes
|
187
|
+
class NdrLpByteArray < NdrPointer
|
104
188
|
endian :little
|
105
189
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
190
|
+
ndr_byte_array :referent, onlyif: -> { self.referent_id != 0 }
|
191
|
+
|
192
|
+
def set(v)
|
193
|
+
if v != :null && v.is_a?(NdrLpByteArray)
|
194
|
+
super(v.referent)
|
195
|
+
else
|
196
|
+
super(v)
|
197
|
+
end
|
198
|
+
end
|
111
199
|
end
|
112
200
|
|
113
201
|
# A pointer to a Windows FILETIME structure
|
114
|
-
class NdrLpFileTime <
|
202
|
+
class NdrLpFileTime < NdrPointer
|
115
203
|
endian :little
|
116
204
|
|
117
|
-
file_time :referent, onlyif: -> {
|
205
|
+
file_time :referent, onlyif: -> { self.referent_id != 0 }
|
206
|
+
end
|
207
|
+
|
208
|
+
# A generic NDR structure that implements logic to #read and #write
|
209
|
+
# (#to_binary_s) in case the structure contains BinData::Array or
|
210
|
+
# NdrPointer fields. This class must be inherited.
|
211
|
+
class NdrStruct < BinData::Record
|
212
|
+
|
213
|
+
def do_read(io)
|
214
|
+
super(io)
|
215
|
+
each_pair do |_name, field|
|
216
|
+
case field
|
217
|
+
when BinData::Array
|
218
|
+
field.each do |element|
|
219
|
+
next unless element.is_a?(NdrPointer)
|
220
|
+
next if element.referent_id == 0
|
221
|
+
pad = (4 - io.offset % 4) % 4
|
222
|
+
io.seekbytes(pad) if pad > 0
|
223
|
+
element.referent.do_read(io)
|
224
|
+
end
|
225
|
+
when NdrPointer
|
226
|
+
next if field.referent_id == 0
|
227
|
+
pad = (4 - io.offset % 4) % 4
|
228
|
+
io.seekbytes(pad) if pad > 0
|
229
|
+
field.referent.do_read(io)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
def do_write(io)
|
235
|
+
super(io)
|
236
|
+
each_pair do |_name, field|
|
237
|
+
case field
|
238
|
+
when BinData::Array
|
239
|
+
field.each do |element|
|
240
|
+
next unless element.is_a?(NdrPointer)
|
241
|
+
next if element.referent_id == 0
|
242
|
+
pad = (4 - io.offset % 4) % 4
|
243
|
+
io.writebytes("\x00" * pad + element.referent.to_binary_s)
|
244
|
+
end
|
245
|
+
when NdrPointer
|
246
|
+
next if field.referent_id == 0
|
247
|
+
pad = (4 - io.offset % 4) % 4
|
248
|
+
io.writebytes("\x00" * pad + field.referent.to_binary_s)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
class NdrStringPtrsw < NdrStruct
|
255
|
+
endian :little
|
256
|
+
|
257
|
+
uint32 :max_count, initial_value: -> { self.elements.size }
|
258
|
+
array :elements, type: :ndr_lp_str, read_until: -> { index == self.max_count - 1 }, onlyif: -> { self.max_count > 0 }
|
259
|
+
|
260
|
+
def get
|
261
|
+
self.elements
|
262
|
+
end
|
263
|
+
|
264
|
+
def set(v)
|
265
|
+
v = v.elements if v.is_a?(self.class)
|
266
|
+
self.elements = v.to_ary
|
267
|
+
self.max_count = self.elements.size unless self.elements.equal?(v)
|
268
|
+
end
|
269
|
+
|
270
|
+
def do_num_bytes
|
271
|
+
to_binary_s.size
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
class NdrLpStringPtrsw < NdrPointer
|
276
|
+
endian :little
|
277
|
+
|
278
|
+
ndr_string_ptrsw :referent, onlyif: -> { self.referent_id != 0 }
|
279
|
+
|
280
|
+
def set(v)
|
281
|
+
super(v.respond_to?(:to_ary) ? v.to_ary : v)
|
282
|
+
end
|
118
283
|
end
|
119
284
|
end
|
120
285
|
end
|