ruby_smb 1.1.0 → 2.0.4
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 +5 -5
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.travis.yml +3 -5
- data/Gemfile +6 -2
- 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.rb +51 -8
- 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_file_encryption.rb +56 -0
- 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.rb +4 -0
- data/lib/ruby_smb/client.rb +246 -26
- data/lib/ruby_smb/client/authentication.rb +32 -18
- data/lib/ruby_smb/client/echo.rb +2 -4
- data/lib/ruby_smb/client/encryption.rb +62 -0
- data/lib/ruby_smb/client/negotiation.rb +156 -16
- data/lib/ruby_smb/client/signing.rb +19 -0
- data/lib/ruby_smb/client/tree_connect.rb +6 -8
- data/lib/ruby_smb/client/utils.rb +24 -17
- data/lib/ruby_smb/client/winreg.rb +1 -1
- data/lib/ruby_smb/crypto.rb +30 -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 +5 -4
- data/lib/ruby_smb/error.rb +49 -6
- data/lib/ruby_smb/field/stringz16.rb +17 -1
- data/lib/ruby_smb/generic_packet.rb +11 -1
- data/lib/ruby_smb/nbss/session_header.rb +4 -4
- data/lib/ruby_smb/smb1/commands.rb +1 -1
- data/lib/ruby_smb/smb1/file.rb +13 -28
- data/lib/ruby_smb/smb1/packet/session_setup_legacy_request.rb +1 -1
- data/lib/ruby_smb/smb1/packet/session_setup_legacy_response.rb +2 -2
- data/lib/ruby_smb/smb1/packet/session_setup_request.rb +1 -1
- data/lib/ruby_smb/smb1/packet/session_setup_response.rb +2 -2
- data/lib/ruby_smb/smb1/packet/write_andx_request.rb +1 -1
- data/lib/ruby_smb/smb1/pipe.rb +8 -8
- data/lib/ruby_smb/smb1/tree.rb +25 -12
- data/lib/ruby_smb/smb2/bit_field/session_flags.rb +2 -1
- data/lib/ruby_smb/smb2/bit_field/share_flags.rb +6 -4
- data/lib/ruby_smb/smb2/file.rb +59 -77
- data/lib/ruby_smb/smb2/negotiate_context.rb +108 -0
- data/lib/ruby_smb/smb2/packet.rb +2 -0
- data/lib/ruby_smb/smb2/packet/compression_transform_header.rb +41 -0
- data/lib/ruby_smb/smb2/packet/negotiate_request.rb +51 -14
- data/lib/ruby_smb/smb2/packet/negotiate_response.rb +50 -4
- data/lib/ruby_smb/smb2/packet/transform_header.rb +84 -0
- data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +92 -6
- data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +8 -26
- data/lib/ruby_smb/smb2/pipe.rb +8 -20
- data/lib/ruby_smb/smb2/smb2_header.rb +1 -1
- data/lib/ruby_smb/smb2/tree.rb +44 -28
- data/lib/ruby_smb/version.rb +1 -1
- data/ruby_smb.gemspec +3 -1
- data/spec/lib/ruby_smb/client_spec.rb +1408 -70
- data/spec/lib/ruby_smb/crypto_spec.rb +25 -0
- 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 +227 -41
- data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +12 -12
- data/spec/lib/ruby_smb/error_spec.rb +88 -0
- data/spec/lib/ruby_smb/field/stringz16_spec.rb +12 -0
- data/spec/lib/ruby_smb/generic_packet_spec.rb +7 -0
- data/spec/lib/ruby_smb/nbss/session_header_spec.rb +4 -11
- data/spec/lib/ruby_smb/smb1/file_spec.rb +1 -3
- data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_request_spec.rb +2 -2
- data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_response_spec.rb +2 -2
- data/spec/lib/ruby_smb/smb1/packet/session_setup_request_spec.rb +2 -2
- data/spec/lib/ruby_smb/smb1/packet/session_setup_response_spec.rb +1 -1
- data/spec/lib/ruby_smb/smb1/pipe_spec.rb +30 -5
- data/spec/lib/ruby_smb/smb1/tree_spec.rb +22 -0
- data/spec/lib/ruby_smb/smb2/bit_field/session_flags_spec.rb +9 -0
- data/spec/lib/ruby_smb/smb2/bit_field/share_flags_spec.rb +27 -0
- data/spec/lib/ruby_smb/smb2/file_spec.rb +147 -71
- data/spec/lib/ruby_smb/smb2/negotiate_context_spec.rb +332 -0
- data/spec/lib/ruby_smb/smb2/packet/compression_transform_header_spec.rb +108 -0
- data/spec/lib/ruby_smb/smb2/packet/negotiate_request_spec.rb +138 -3
- data/spec/lib/ruby_smb/smb2/packet/negotiate_response_spec.rb +120 -2
- data/spec/lib/ruby_smb/smb2/packet/transform_header_spec.rb +220 -0
- data/spec/lib/ruby_smb/smb2/packet/tree_connect_request_spec.rb +339 -9
- data/spec/lib/ruby_smb/smb2/packet/tree_connect_response_spec.rb +3 -30
- data/spec/lib/ruby_smb/smb2/pipe_spec.rb +9 -45
- data/spec/lib/ruby_smb/smb2/smb2_header_spec.rb +2 -2
- data/spec/lib/ruby_smb/smb2/tree_spec.rb +111 -9
- metadata +194 -75
- metadata.gz.sig +2 -1
@@ -40,6 +40,25 @@ module RubySMB
|
|
40
40
|
end
|
41
41
|
packet
|
42
42
|
end
|
43
|
+
|
44
|
+
def smb3_sign(packet)
|
45
|
+
if !session_key.empty? && (signing_required || packet.is_a?(RubySMB::SMB2::Packet::TreeConnectRequest))
|
46
|
+
case @dialect
|
47
|
+
when '0x0300', '0x0302'
|
48
|
+
signing_key = RubySMB::Crypto::KDF.counter_mode(@session_key, "SMB2AESCMAC\x00", "SmbSign\x00")
|
49
|
+
when '0x0311'
|
50
|
+
signing_key = RubySMB::Crypto::KDF.counter_mode(@session_key, "SMBSigningKey\x00", @preauth_integrity_hash_value)
|
51
|
+
else
|
52
|
+
raise RubySMB::Error::SigningError.new('Dialect is incompatible with SMBv3 signing')
|
53
|
+
end
|
54
|
+
|
55
|
+
packet.smb2_header.flags.signed = 1
|
56
|
+
packet.smb2_header.signature = "\x00" * 16
|
57
|
+
hmac = OpenSSL::CMAC.digest('AES', signing_key, packet.to_binary_s)
|
58
|
+
packet.smb2_header.signature = hmac[0, 16]
|
59
|
+
end
|
60
|
+
packet
|
61
|
+
end
|
43
62
|
end
|
44
63
|
end
|
45
64
|
end
|
@@ -33,12 +33,11 @@ module RubySMB
|
|
33
33
|
raise RubySMB::Error::InvalidPacket.new(
|
34
34
|
expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
|
35
35
|
expected_cmd: RubySMB::SMB1::Packet::TreeConnectResponse::COMMAND,
|
36
|
-
|
37
|
-
received_cmd: response.smb_header.command
|
36
|
+
packet: response
|
38
37
|
)
|
39
38
|
end
|
40
39
|
unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
|
41
|
-
raise RubySMB::Error::UnexpectedStatusCode, response.status_code
|
40
|
+
raise RubySMB::Error::UnexpectedStatusCode, response.status_code
|
42
41
|
end
|
43
42
|
RubySMB::SMB1::Tree.new(client: self, share: share, response: response)
|
44
43
|
end
|
@@ -55,7 +54,7 @@ module RubySMB
|
|
55
54
|
def smb2_tree_connect(share)
|
56
55
|
request = RubySMB::SMB2::Packet::TreeConnectRequest.new
|
57
56
|
request.smb2_header.tree_id = 65_535
|
58
|
-
request.
|
57
|
+
request.path = share
|
59
58
|
raw_response = send_recv(request)
|
60
59
|
response = RubySMB::SMB2::Packet::TreeConnectResponse.read(raw_response)
|
61
60
|
smb2_tree_from_response(share, response)
|
@@ -73,14 +72,13 @@ module RubySMB
|
|
73
72
|
raise RubySMB::Error::InvalidPacket.new(
|
74
73
|
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
75
74
|
expected_cmd: RubySMB::SMB2::Packet::TreeConnectResponse::COMMAND,
|
76
|
-
|
77
|
-
received_cmd: response.smb2_header.command
|
75
|
+
packet: response
|
78
76
|
)
|
79
77
|
end
|
80
78
|
unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
|
81
|
-
raise RubySMB::Error::UnexpectedStatusCode, response.status_code
|
79
|
+
raise RubySMB::Error::UnexpectedStatusCode, response.status_code
|
82
80
|
end
|
83
|
-
RubySMB::SMB2::Tree.new(client: self, share: share, response: response)
|
81
|
+
RubySMB::SMB2::Tree.new(client: self, share: share, response: response, encrypt: response.share_flags.encrypt == 1)
|
84
82
|
end
|
85
83
|
end
|
86
84
|
end
|
@@ -24,43 +24,50 @@ module RubySMB
|
|
24
24
|
last_tree.id
|
25
25
|
end
|
26
26
|
|
27
|
-
def open(path, disposition=RubySMB::Dispositions::FILE_OPEN, write: false, read: true)
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
27
|
+
def open(path, disposition=RubySMB::Dispositions::FILE_OPEN, write: false, read: true, pipe: false)
|
28
|
+
if pipe
|
29
|
+
file = last_tree.open_pipe(filename: path, write: write, read: read, disposition: disposition)
|
30
|
+
else
|
31
|
+
file = last_tree.open_file(filename: path, write: write, read: read, disposition: disposition)
|
32
|
+
end
|
33
|
+
@last_file_id = if file.respond_to?(:guid)
|
34
|
+
# SMB2 uses guid
|
35
|
+
file.guid.to_binary_s
|
36
|
+
elsif file.respond_to?(:fid)
|
37
|
+
# SMB1 uses fid
|
38
|
+
file.fid.to_binary_s
|
39
|
+
end
|
40
|
+
@open_files[@last_file_id] = file
|
41
|
+
@last_file_id
|
36
42
|
end
|
37
43
|
|
38
44
|
def create_pipe(path, disposition=RubySMB::Dispositions::FILE_OPEN_IF)
|
39
|
-
open(path
|
45
|
+
open(path, disposition, write: true, read: true, pipe: true)
|
40
46
|
end
|
41
47
|
|
42
48
|
#Writes data to an open file handle
|
43
|
-
def write(file_id, offset = 0, data = '', do_recv = true)
|
49
|
+
def write(file_id = last_file_id, offset = 0, data = '', do_recv = true)
|
44
50
|
@open_files[file_id].send_recv_write(data: data, offset: offset)
|
45
51
|
end
|
46
52
|
|
47
|
-
def read(file_id, offset = 0, length = last_file.size)
|
53
|
+
def read(file_id = last_file_id, offset = 0, length = last_file.size, do_recv = true)
|
48
54
|
data = @open_files[file_id].send_recv_read(read_length: length, offset: offset)
|
49
55
|
data.bytes
|
50
56
|
end
|
51
57
|
|
52
|
-
def delete(path)
|
53
|
-
|
58
|
+
def delete(path, tree_id = last_tree_id, do_recv = true)
|
59
|
+
tree = @tree_connects.detect{ |tree| tree.id == tree_id }
|
60
|
+
file = tree.open_file(filename: path.sub(/^\\/, ''), delete: true)
|
54
61
|
file.delete
|
55
62
|
file.close
|
56
63
|
end
|
57
64
|
|
58
|
-
def close(file_id, tree_id)
|
65
|
+
def close(file_id = last_file_id, tree_id = last_tree_id, do_recv = true)
|
59
66
|
@open_files[file_id].close
|
60
67
|
end
|
61
68
|
|
62
|
-
def tree_disconnect(
|
63
|
-
@tree_connects.detect{|tree| tree.id ==
|
69
|
+
def tree_disconnect(tree_id = last_tree_id, do_recv = true)
|
70
|
+
@tree_connects.detect{|tree| tree.id == tree_id }.disconnect!
|
64
71
|
end
|
65
72
|
|
66
73
|
def native_os
|
@@ -6,7 +6,7 @@ module RubySMB
|
|
6
6
|
share = "\\\\#{host}\\IPC$"
|
7
7
|
tree = @tree_connects.find {|tree| tree.share == share}
|
8
8
|
tree = tree_connect(share) unless tree
|
9
|
-
named_pipe = tree.
|
9
|
+
named_pipe = tree.open_pipe(filename: "winreg", write: true, read: true)
|
10
10
|
if block_given?
|
11
11
|
res = yield named_pipe
|
12
12
|
named_pipe.close
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module RubySMB
|
2
|
+
module Crypto
|
3
|
+
module KDF
|
4
|
+
def self.counter_mode(ki, label, context, length: 128)
|
5
|
+
digest = OpenSSL::Digest.new('SHA256')
|
6
|
+
r = 32
|
7
|
+
|
8
|
+
n = length / 256
|
9
|
+
n = 1 if n == 0
|
10
|
+
|
11
|
+
raise ArgumentError if n > 2**r - 1
|
12
|
+
result = ""
|
13
|
+
|
14
|
+
n.times do |i|
|
15
|
+
input = [i + 1].pack('L>')
|
16
|
+
input << label
|
17
|
+
input << "\x00"
|
18
|
+
input << context
|
19
|
+
input << [length].pack('L>')
|
20
|
+
k = OpenSSL::HMAC.digest(digest, ki, input)
|
21
|
+
result << k
|
22
|
+
end
|
23
|
+
|
24
|
+
return result[0...(length / 8)]
|
25
|
+
rescue OpenSSL::OpenSSLError => e
|
26
|
+
raise RubySMB::Error::EncryptionError, "Crypto::KDF.counter_mode OpenSSL error: #{e.message}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
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
|