ruby_smb 1.0.5 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.travis.yml +4 -1
  5. data/README.md +35 -47
  6. data/examples/enum_registry_key.rb +28 -0
  7. data/examples/enum_registry_values.rb +30 -0
  8. data/examples/pipes.rb +2 -1
  9. data/examples/read_registry_key_value.rb +32 -0
  10. data/lib/ruby_smb.rb +0 -1
  11. data/lib/ruby_smb/client.rb +2 -0
  12. data/lib/ruby_smb/client/winreg.rb +46 -0
  13. data/lib/ruby_smb/dcerpc.rb +38 -0
  14. data/lib/ruby_smb/dcerpc/bind.rb +2 -2
  15. data/lib/ruby_smb/dcerpc/bind_ack.rb +2 -2
  16. data/lib/ruby_smb/dcerpc/error.rb +3 -0
  17. data/lib/ruby_smb/dcerpc/ndr.rb +95 -16
  18. data/lib/ruby_smb/dcerpc/pdu_header.rb +1 -1
  19. data/lib/ruby_smb/dcerpc/request.rb +28 -9
  20. data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +35 -0
  21. data/lib/ruby_smb/dcerpc/srvsvc.rb +10 -0
  22. data/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all.rb +9 -0
  23. data/lib/ruby_smb/dcerpc/winreg.rb +340 -0
  24. data/lib/ruby_smb/dcerpc/winreg/close_key_request.rb +24 -0
  25. data/lib/ruby_smb/dcerpc/winreg/close_key_response.rb +27 -0
  26. data/lib/ruby_smb/dcerpc/winreg/enum_key_request.rb +45 -0
  27. data/lib/ruby_smb/dcerpc/winreg/enum_key_response.rb +42 -0
  28. data/lib/ruby_smb/dcerpc/winreg/enum_value_request.rb +39 -0
  29. data/lib/ruby_smb/dcerpc/winreg/enum_value_response.rb +36 -0
  30. data/lib/ruby_smb/dcerpc/winreg/open_key_request.rb +34 -0
  31. data/lib/ruby_smb/dcerpc/winreg/open_key_response.rb +25 -0
  32. data/lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb +43 -0
  33. data/lib/ruby_smb/dcerpc/winreg/open_root_key_response.rb +35 -0
  34. data/lib/ruby_smb/dcerpc/winreg/query_info_key_request.rb +27 -0
  35. data/lib/ruby_smb/dcerpc/winreg/query_info_key_response.rb +40 -0
  36. data/lib/ruby_smb/dcerpc/winreg/query_value_request.rb +39 -0
  37. data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +57 -0
  38. data/lib/ruby_smb/dcerpc/winreg/regsam.rb +40 -0
  39. data/lib/ruby_smb/smb1/file.rb +2 -0
  40. data/lib/ruby_smb/smb1/pipe.rb +78 -2
  41. data/lib/ruby_smb/smb2/packet/error_packet.rb +2 -4
  42. data/lib/ruby_smb/smb2/pipe.rb +89 -2
  43. data/lib/ruby_smb/version.rb +1 -1
  44. data/ruby_smb.gemspec +3 -3
  45. data/spec/lib/ruby_smb/client_spec.rb +148 -0
  46. data/spec/lib/ruby_smb/dcerpc/bind_ack_spec.rb +2 -2
  47. data/spec/lib/ruby_smb/dcerpc/bind_spec.rb +2 -2
  48. data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +410 -0
  49. data/spec/lib/ruby_smb/dcerpc/request_spec.rb +50 -7
  50. data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +98 -0
  51. data/spec/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all_spec.rb +13 -0
  52. data/spec/lib/ruby_smb/dcerpc/srvsvc_spec.rb +60 -0
  53. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_request_spec.rb +28 -0
  54. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_response_spec.rb +36 -0
  55. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +108 -0
  56. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_response_spec.rb +97 -0
  57. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_request_spec.rb +94 -0
  58. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_response_spec.rb +82 -0
  59. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_request_spec.rb +74 -0
  60. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_response_spec.rb +35 -0
  61. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +90 -0
  62. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_response_spec.rb +38 -0
  63. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_request_spec.rb +39 -0
  64. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_response_spec.rb +113 -0
  65. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_request_spec.rb +88 -0
  66. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_response_spec.rb +150 -0
  67. data/spec/lib/ruby_smb/dcerpc/winreg/regsam_spec.rb +32 -0
  68. data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +710 -0
  69. data/spec/lib/ruby_smb/dcerpc_spec.rb +81 -0
  70. data/spec/lib/ruby_smb/smb1/file_spec.rb +9 -1
  71. data/spec/lib/ruby_smb/smb1/pipe_spec.rb +210 -148
  72. data/spec/lib/ruby_smb/smb2/packet/error_packet_spec.rb +3 -24
  73. data/spec/lib/ruby_smb/smb2/pipe_spec.rb +256 -145
  74. metadata +66 -9
  75. metadata.gz.sig +0 -0
  76. data/lib/ruby_smb/smb1/dcerpc.rb +0 -72
  77. data/lib/ruby_smb/smb2/dcerpc.rb +0 -75
@@ -0,0 +1,113 @@
1
+ RSpec.describe RubySMB::Dcerpc::Winreg::QueryInfoKeyResponse do
2
+ subject(:packet) { described_class.new }
3
+
4
+ it { is_expected.to respond_to :lp_class }
5
+ it { is_expected.to respond_to :pad }
6
+ it { is_expected.to respond_to :lpc_sub_keys }
7
+ it { is_expected.to respond_to :lpc_max_sub_key_len }
8
+ it { is_expected.to respond_to :lpc_max_class_len }
9
+ it { is_expected.to respond_to :lpc_values }
10
+ it { is_expected.to respond_to :lpcb_max_value_name_len }
11
+ it { is_expected.to respond_to :lpcb_max_value_len }
12
+ it { is_expected.to respond_to :lpcb_security_descriptor }
13
+ it { is_expected.to respond_to :lpft_last_write_time }
14
+ it { is_expected.to respond_to :error_status }
15
+ it { is_expected.to respond_to :opnum }
16
+
17
+ it 'is little endian' do
18
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
19
+ end
20
+
21
+ describe '#lp_class' do
22
+ it 'is a RrpUnicodeString structure' do
23
+ expect(packet.lp_class).to be_a RubySMB::Dcerpc::RrpUnicodeString
24
+ end
25
+
26
+ it 'has an initial value of 0' do
27
+ expect(packet.lp_class).to eq(0)
28
+ end
29
+ end
30
+
31
+ describe '#pad' do
32
+ it 'is a string' do
33
+ expect(packet.pad).to be_a BinData::String
34
+ end
35
+
36
+ it 'should keep #lpc_sub_keys 4-byte aligned' do
37
+ packet.lp_class = 'test'
38
+ expect(packet.lpc_sub_keys.abs_offset % 4).to eq 0
39
+ end
40
+ end
41
+
42
+ describe '#lpc_sub_keys' do
43
+ it 'is a 32-bit unsigned integer' do
44
+ expect(packet.lpc_sub_keys).to be_a BinData::Uint32le
45
+ end
46
+ end
47
+
48
+ describe '#lpc_max_sub_key_len' do
49
+ it 'is a 32-bit unsigned integer' do
50
+ expect(packet.lpc_max_sub_key_len).to be_a BinData::Uint32le
51
+ end
52
+ end
53
+
54
+ describe '#lpc_max_class_len' do
55
+ it 'is a 32-bit unsigned integer' do
56
+ expect(packet.lpc_max_class_len).to be_a BinData::Uint32le
57
+ end
58
+ end
59
+
60
+ describe '#lpc_values' do
61
+ it 'is a 32-bit unsigned integer' do
62
+ expect(packet.lpc_values).to be_a BinData::Uint32le
63
+ end
64
+ end
65
+
66
+ describe '#lpcb_max_value_name_len' do
67
+ it 'is a 32-bit unsigned integer' do
68
+ expect(packet.lpcb_max_value_name_len).to be_a BinData::Uint32le
69
+ end
70
+ end
71
+
72
+ describe '#lpcb_max_value_len' do
73
+ it 'is a 32-bit unsigned integer' do
74
+ expect(packet.lpcb_max_value_len).to be_a BinData::Uint32le
75
+ end
76
+ end
77
+
78
+ describe '#lpcb_security_descriptor' do
79
+ it 'is a 32-bit unsigned integer' do
80
+ expect(packet.lpcb_security_descriptor).to be_a BinData::Uint32le
81
+ end
82
+ end
83
+
84
+ describe '#lpft_last_write_time' do
85
+ it 'is a FileTime structure' do
86
+ expect(packet.lpft_last_write_time).to be_a RubySMB::Field::FileTime
87
+ end
88
+ end
89
+
90
+ describe '#error_status' do
91
+ it 'is a 32-bit unsigned integer' do
92
+ expect(packet.error_status).to be_a BinData::Uint32le
93
+ end
94
+ end
95
+
96
+ describe '#initialize_instance' do
97
+ it 'sets #opnum to REG_QUERY_INFO_KEY constant' do
98
+ expect(packet.opnum).to eq(RubySMB::Dcerpc::Winreg::REG_QUERY_INFO_KEY)
99
+ end
100
+ end
101
+
102
+ describe '#pad_length' do
103
+ it 'returns 0 when #lpc_sub_keys is already 4-byte aligned' do
104
+ packet.lp_class = 'align'
105
+ expect(packet.pad_length).to eq 0
106
+ end
107
+
108
+ it 'returns 2 when #lpc_sub_keys is only 2-byte aligned' do
109
+ packet.lp_class = 'align' + 'A'
110
+ expect(packet.pad_length).to eq 2
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,88 @@
1
+ RSpec.describe RubySMB::Dcerpc::Winreg::RpcHkey do
2
+ it 'is NdrContextHandle subclass' do
3
+ expect(described_class).to be < RubySMB::Dcerpc::Ndr::NdrContextHandle
4
+ end
5
+ end
6
+
7
+ RSpec.describe RubySMB::Dcerpc::Winreg::QueryValueRequest do
8
+ subject(:packet) { described_class.new }
9
+
10
+ it { is_expected.to respond_to :hkey }
11
+ it { is_expected.to respond_to :lp_value_name }
12
+ it { is_expected.to respond_to :pad }
13
+ it { is_expected.to respond_to :lp_type }
14
+ it { is_expected.to respond_to :lp_data }
15
+ it { is_expected.to respond_to :lpcb_data }
16
+ it { is_expected.to respond_to :lpcb_len }
17
+ it { is_expected.to respond_to :opnum }
18
+
19
+ it 'is little endian' do
20
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
21
+ end
22
+
23
+ describe '#hkey' do
24
+ it 'is a RpcHkey structure' do
25
+ expect(packet.hkey).to be_a RubySMB::Dcerpc::Winreg::RpcHkey
26
+ end
27
+ end
28
+
29
+ describe '#lp_value_name' do
30
+ it 'is a RrpUnicodeString structure' do
31
+ expect(packet.lp_value_name).to be_a RubySMB::Dcerpc::RrpUnicodeString
32
+ end
33
+ end
34
+
35
+ describe '#pad' do
36
+ it 'is a string' do
37
+ expect(packet.pad).to be_a BinData::String
38
+ end
39
+
40
+ it 'should keep #lp_type 4-byte aligned' do
41
+ packet.lp_value_name = 'test'
42
+ expect(packet.lp_type.abs_offset % 4).to eq 0
43
+ end
44
+ end
45
+
46
+ describe '#lp_type' do
47
+ it 'is a NdrLpDword structure' do
48
+ expect(packet.lp_type).to be_a RubySMB::Dcerpc::Ndr::NdrLpDword
49
+ end
50
+ end
51
+
52
+ describe '#lp_data' do
53
+ it 'is a NdrLpByte structure' do
54
+ expect(packet.lp_data).to be_a RubySMB::Dcerpc::Ndr::NdrLpByte
55
+ end
56
+ end
57
+
58
+ describe '#lpcb_data' do
59
+ it 'is a NdrLpDword structure' do
60
+ expect(packet.lpcb_data).to be_a RubySMB::Dcerpc::Ndr::NdrLpDword
61
+ end
62
+ end
63
+
64
+ describe '#lpcb_len' do
65
+ it 'is a NdrLpDword structure' do
66
+ expect(packet.lpcb_len).to be_a RubySMB::Dcerpc::Ndr::NdrLpDword
67
+ end
68
+ end
69
+
70
+ describe '#initialize_instance' do
71
+ it 'sets #opnum to REG_QUERY_VALUE constant' do
72
+ expect(packet.opnum).to eq(RubySMB::Dcerpc::Winreg::REG_QUERY_VALUE)
73
+ end
74
+ end
75
+
76
+ describe '#pad_length' do
77
+ it 'returns 0 when #lp_type is already 4-byte aligned' do
78
+ packet.lp_value_name = 'align'
79
+ expect(packet.pad_length).to eq 0
80
+ end
81
+
82
+ it 'returns 2 when #lp_type is only 2-byte aligned' do
83
+ packet.lp_value_name = 'align' + 'A'
84
+ expect(packet.pad_length).to eq 2
85
+ end
86
+ end
87
+ end
88
+
@@ -0,0 +1,150 @@
1
+ RSpec.describe RubySMB::Dcerpc::Winreg::QueryValueResponse do
2
+ subject(:packet) { described_class.new }
3
+
4
+ it { is_expected.to respond_to :lp_type }
5
+ it { is_expected.to respond_to :lp_data }
6
+ it { is_expected.to respond_to :pad }
7
+ it { is_expected.to respond_to :lpcb_data }
8
+ it { is_expected.to respond_to :lpcb_len }
9
+ it { is_expected.to respond_to :error_status }
10
+ it { is_expected.to respond_to :opnum }
11
+
12
+ it 'is little endian' do
13
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
14
+ end
15
+
16
+ describe '#lp_type' do
17
+ it 'is a NdrLpDword structure' do
18
+ expect(packet.lp_type).to be_a RubySMB::Dcerpc::Ndr::NdrLpDword
19
+ end
20
+ end
21
+
22
+ describe '#lp_data' do
23
+ it 'is a NdrLpByte structure' do
24
+ expect(packet.lp_data).to be_a RubySMB::Dcerpc::Ndr::NdrLpByte
25
+ end
26
+ end
27
+
28
+ describe '#pad' do
29
+ it 'is a string' do
30
+ expect(packet.pad).to be_a BinData::String
31
+ end
32
+
33
+ it 'should keep #lpcb_data 4-byte aligned' do
34
+ packet.lp_data.bytes = 'spec_test'.bytes
35
+ expect(packet.lpcb_data.abs_offset % 4).to eq 0
36
+ end
37
+ end
38
+
39
+ describe '#lpcb_data' do
40
+ it 'is a NdrLpDword structure' do
41
+ expect(packet.lpcb_data).to be_a RubySMB::Dcerpc::Ndr::NdrLpDword
42
+ end
43
+ end
44
+
45
+ describe '#lpcb_len' do
46
+ it 'is a NdrLpDword structure' do
47
+ expect(packet.lpcb_len).to be_a RubySMB::Dcerpc::Ndr::NdrLpDword
48
+ end
49
+ end
50
+
51
+ describe '#error_status' do
52
+ it 'is a 32-bit unsigned integer' do
53
+ expect(packet.error_status).to be_a BinData::Uint32le
54
+ end
55
+ end
56
+
57
+ describe '#initialize_instance' do
58
+ it 'sets #opnum to REG_QUERY_VALUE constant' do
59
+ expect(packet.opnum).to eq(RubySMB::Dcerpc::Winreg::REG_QUERY_VALUE)
60
+ end
61
+ end
62
+
63
+ describe '#pad_length' do
64
+ it 'returns 0 when #lpcb_data is already 4-byte aligned' do
65
+ packet.lp_data.bytes = 'aligned.'.bytes
66
+ expect(packet.pad_length).to eq 0
67
+ end
68
+
69
+ it 'returns 2 when #lpcb_data is only 2-byte aligned' do
70
+ packet.lp_data.bytes = 'not aligned'.bytes
71
+ expect(packet.pad_length).to eq 1
72
+ end
73
+ end
74
+
75
+ describe '#data' do
76
+ context 'when #lp_type is 1 (unicode null-terminated string)' do
77
+ it 'returns the expected value' do
78
+ str = 'spec test string'.encode('utf-16le')
79
+ packet.lp_type = 1
80
+ packet.lp_data.bytes = str.bytes
81
+ expect(packet.data).to eq(str)
82
+ end
83
+ end
84
+
85
+ context 'when #lp_type is 2 (unicode null-terminated string with unexpanded references to environment variables)' do
86
+ it 'returns the expected value' do
87
+ str = '/%PATH%/foo'.encode('utf-16le')
88
+ packet.lp_type = 2
89
+ packet.lp_data.bytes = str.bytes
90
+ expect(packet.data).to eq(str)
91
+ end
92
+ end
93
+
94
+ context 'when #lp_type is 3 (binary data)' do
95
+ it 'returns the expected value' do
96
+ bytes = [0xFF, 0xEE, 0xDD, 0xCC].pack('C*')
97
+ packet.lp_type = 3
98
+ packet.lp_data.bytes = bytes.bytes
99
+ expect(packet.data).to eq(bytes)
100
+ end
101
+ end
102
+
103
+ context 'when #lp_type is 4 (a 32-bit number in little-endian format)' do
104
+ it 'returns the expected value' do
105
+ number = 12345
106
+ packet.lp_type = 4
107
+ packet.lp_data.bytes = [number].pack('V').bytes
108
+ expect(packet.data).to eq(number)
109
+ end
110
+ end
111
+
112
+ context 'when #lp_type is 5 (a 32-bit number in big-endian format)' do
113
+ it 'returns the expected value' do
114
+ number = 12345
115
+ packet.lp_type = 5
116
+ packet.lp_data.bytes = [number].pack('N').bytes
117
+ expect(packet.data).to eq(number)
118
+ end
119
+ end
120
+
121
+ context 'when #lp_type is 7 (a sequence of unicode null-terminated strings, terminated by an empty string)' do
122
+ it 'returns the expected value' do
123
+ str_array = ['String1', 'String2', 'String3', 'LastString'].map {|v| v.encode('utf-16le')}
124
+ null_byte = "\0".encode('utf-16le')
125
+ str = (str_array + [null_byte]).join(null_byte)
126
+ packet.lp_type = 7
127
+ packet.lp_data.bytes = str.bytes
128
+ expect(packet.data).to eq(str_array)
129
+ end
130
+ end
131
+
132
+ context 'when #lp_type is 11 (a 64-bit number in little-endian format)' do
133
+ it 'returns the expected value' do
134
+ number = 0x1234567812345678
135
+ packet.lp_type = 11
136
+ packet.lp_data.bytes = [number].pack('Q<').bytes
137
+ expect(packet.data).to eq(number)
138
+ end
139
+ end
140
+
141
+ context 'when #lp_type is an unknown value' do
142
+ it 'returns an empty string' do
143
+ str = 'test'
144
+ packet.lp_type = 6
145
+ packet.lp_data.bytes = str.bytes
146
+ expect(packet.data).to eq('')
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,32 @@
1
+ RSpec.describe RubySMB::Dcerpc::Winreg::Regsam do
2
+ subject(:packet) { described_class.new }
3
+
4
+ it { is_expected.to respond_to :reserved }
5
+ it { is_expected.to respond_to :key_create_link }
6
+ it { is_expected.to respond_to :key_notify }
7
+ it { is_expected.to respond_to :key_enumerate_sub_keys }
8
+ it { is_expected.to respond_to :key_create_sub_key }
9
+ it { is_expected.to respond_to :key_set_value }
10
+ it { is_expected.to respond_to :key_query_value }
11
+ it { is_expected.to respond_to :reserved2 }
12
+ it { is_expected.to respond_to :key_wow64_32key }
13
+ it { is_expected.to respond_to :key_wow64_64key }
14
+ it { is_expected.to respond_to :reserved3 }
15
+ it { is_expected.to respond_to :synchronize }
16
+ it { is_expected.to respond_to :write_owner }
17
+ it { is_expected.to respond_to :write_dac }
18
+ it { is_expected.to respond_to :read_control }
19
+ it { is_expected.to respond_to :delete_access }
20
+ it { is_expected.to respond_to :generic_read }
21
+ it { is_expected.to respond_to :generic_write }
22
+ it { is_expected.to respond_to :generic_execute }
23
+ it { is_expected.to respond_to :generic_all }
24
+ it { is_expected.to respond_to :reserved4 }
25
+ it { is_expected.to respond_to :maximum }
26
+ it { is_expected.to respond_to :system_security }
27
+
28
+
29
+ it 'is little endian' do
30
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
31
+ end
32
+ end
@@ -0,0 +1,710 @@
1
+ RSpec.describe RubySMB::Dcerpc::Winreg do
2
+ let(:winreg) do
3
+ RubySMB::SMB1::Pipe.new(
4
+ tree: double('Tree'),
5
+ response: RubySMB::SMB1::Packet::NtCreateAndxResponse.new,
6
+ name: 'winreg'
7
+ )
8
+ end
9
+
10
+ describe '#open_root_key' do
11
+ let(:root_key_request_packet) { double('Root Key Request Packet') }
12
+ let(:response) { double('Response') }
13
+ let(:root_key_response_packet) { double('Root Key Response Packet') }
14
+ let(:ph_key) { double('PHKEY') }
15
+ before :example do
16
+ allow(described_class::OpenRootKeyRequest).to receive(:new).and_return(root_key_request_packet)
17
+ allow(winreg).to receive(:dcerpc_request).and_return(response)
18
+ allow(described_class::OpenRootKeyResponse).to receive(:read).and_return(root_key_response_packet)
19
+ allow(root_key_response_packet).to receive_messages(
20
+ :error_status => WindowsError::Win32::ERROR_SUCCESS,
21
+ :ph_key => ph_key
22
+ )
23
+ end
24
+
25
+ context 'when the root key is unknown' do
26
+ it 'raises an ArgumentError exception' do
27
+ expect { winreg.open_root_key('UNKNOWN') }.to raise_error(ArgumentError)
28
+ end
29
+ end
30
+
31
+ it 'create the expected OpenRootKeyRequest packet' do
32
+ winreg.open_root_key('HKLM')
33
+ expect(described_class::OpenRootKeyRequest).to have_received(:new).with(opnum: described_class::OPEN_HKLM)
34
+ end
35
+
36
+ it 'sends the expected dcerpc request' do
37
+ winreg.open_root_key('HKLM')
38
+ expect(winreg).to have_received(:dcerpc_request).with(root_key_request_packet)
39
+ end
40
+
41
+ it 'creates a OpenRootKeyResponse structure from the expected dcerpc response' do
42
+ winreg.open_root_key('HKLM')
43
+ expect(described_class::OpenRootKeyResponse).to have_received(:read).with(response)
44
+ end
45
+
46
+ context 'when an IOError occurs while parsing the response' do
47
+ it 'raises a RubySMB::Dcerpc::Error::InvalidPacket' do
48
+ allow(described_class::OpenRootKeyResponse).to receive(:read).and_raise(IOError)
49
+ expect { winreg.open_root_key('HKLM') }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
50
+ end
51
+ end
52
+
53
+ context 'when the response error status is not WindowsError::Win32::ERROR_SUCCESS' do
54
+ it 'raises a RubySMB::Dcerpc::Error::WinregError' do
55
+ allow(root_key_response_packet).to receive(:error_status).and_return(WindowsError::Win32::ERROR_INVALID_DATA)
56
+ expect { winreg.open_root_key('HKLM') }.to raise_error(RubySMB::Dcerpc::Error::WinregError)
57
+ end
58
+ end
59
+
60
+ it 'returns the expected handler' do
61
+ expect(winreg.open_root_key('HKLM')).to eq(ph_key)
62
+ end
63
+ end
64
+
65
+ describe '#open_key' do
66
+ let(:handle) { double('Handle') }
67
+ let(:sub_key) { double('Sub-key') }
68
+ let(:openkey_request_packet) { double('OpenKey Request Packet') }
69
+ let(:regsam) { double('Regsam') }
70
+ let(:response) { double('Response') }
71
+ let(:open_key_response) { double('OpenKey Response') }
72
+ let(:phk_result) { double('Phk Result') }
73
+ before :example do
74
+ allow(described_class::OpenKeyRequest).to receive(:new).and_return(openkey_request_packet)
75
+ allow(openkey_request_packet).to receive(:sam_desired).and_return(regsam)
76
+ allow(regsam).to receive_messages(
77
+ :read_control= => nil,
78
+ :key_query_value= => nil,
79
+ :key_enumerate_sub_keys= => nil,
80
+ :key_notify= => nil,
81
+ )
82
+ allow(winreg).to receive(:dcerpc_request).and_return(response)
83
+ allow(described_class::OpenKeyResponse).to receive(:read).and_return(open_key_response)
84
+ allow(open_key_response).to receive_messages(
85
+ :error_status => WindowsError::Win32::ERROR_SUCCESS,
86
+ :phk_result => phk_result
87
+ )
88
+ end
89
+
90
+ it 'create the expected OpenKeyRequest packet' do
91
+ winreg.open_key(handle, sub_key)
92
+ expect(described_class::OpenKeyRequest).to have_received(:new).with(hkey: handle, lp_sub_key: sub_key)
93
+ end
94
+
95
+ it 'sets the expected user rights on the request packet' do
96
+ winreg.open_key(handle, sub_key)
97
+ expect(regsam).to have_received(:read_control=).with(1)
98
+ expect(regsam).to have_received(:key_query_value=).with(1)
99
+ expect(regsam).to have_received(:key_enumerate_sub_keys=).with(1)
100
+ expect(regsam).to have_received(:key_notify=).with(1)
101
+ end
102
+
103
+ it 'sends the expected dcerpc request' do
104
+ winreg.open_key(handle, sub_key)
105
+ expect(winreg).to have_received(:dcerpc_request).with(openkey_request_packet)
106
+ end
107
+
108
+ it 'creates a OpenKeyResponse structure from the expected dcerpc response' do
109
+ winreg.open_key(handle, sub_key)
110
+ expect(described_class::OpenKeyResponse).to have_received(:read).with(response)
111
+ end
112
+
113
+ context 'when an IOError occurs while parsing the response' do
114
+ it 'raises a RubySMB::Dcerpc::Error::InvalidPacket' do
115
+ allow(described_class::OpenKeyResponse).to receive(:read).and_raise(IOError)
116
+ expect { winreg.open_key(handle, sub_key) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
117
+ end
118
+ end
119
+
120
+ context 'when the response error status is not WindowsError::Win32::ERROR_SUCCESS' do
121
+ it 'raises a RubySMB::Dcerpc::Error::WinregError' do
122
+ allow(open_key_response).to receive(:error_status).and_return(WindowsError::Win32::ERROR_INVALID_DATA)
123
+ expect { winreg.open_key(handle, sub_key) }.to raise_error(RubySMB::Dcerpc::Error::WinregError)
124
+ end
125
+ end
126
+
127
+ it 'returns the expected handler' do
128
+ expect(winreg.open_key(handle, sub_key)).to eq(phk_result)
129
+ end
130
+ end
131
+
132
+ describe '#query_value' do
133
+ let(:handle) { double('Handle') }
134
+ let(:value_name) { double('Value Name') }
135
+ let(:query_value_request_packet1) { double('Query Value Request Packet #1') }
136
+ let(:query_value_request_packet2) { double('Query Value Request Packet #2') }
137
+ let(:lp_data1) { double('LpData #1') }
138
+ let(:lp_data2) { double('LpData #2') }
139
+ let(:response1) { double('Response #1') }
140
+ let(:response2) { double('Response #2') }
141
+ let(:query_value_response1) { double('Query Value Response #1') }
142
+ let(:query_value_response2) { double('Query Value Response #2') }
143
+ let(:data) { double('Data') }
144
+ let(:lpcb_data) { double('LpcbData') }
145
+ let(:lpcb_data_referent) { double('Lpcb Data Referent') }
146
+ before :example do
147
+ first_request = true
148
+ allow(described_class::QueryValueRequest).to receive(:new) do
149
+ if first_request
150
+ first_request = false
151
+ query_value_request_packet1
152
+ else
153
+ query_value_request_packet2
154
+ end
155
+ end
156
+ allow(query_value_request_packet1).to receive(:lp_data).and_return(lp_data1)
157
+ allow(query_value_request_packet2).to receive_messages(
158
+ :lp_data => lp_data2,
159
+ :lpcb_data= => nil
160
+ )
161
+ allow(lp_data1).to receive(:referent_identifier=)
162
+ allow(lp_data2).to receive(:max_count=)
163
+ allow(winreg).to receive(:dcerpc_request).with(query_value_request_packet1).and_return(response1)
164
+ allow(winreg).to receive(:dcerpc_request).with(query_value_request_packet2).and_return(response2)
165
+ allow(described_class::QueryValueResponse).to receive(:read).with(response1).and_return(query_value_response1)
166
+ allow(described_class::QueryValueResponse).to receive(:read).with(response2).and_return(query_value_response2)
167
+ allow(query_value_response1).to receive(:error_status).and_return(WindowsError::Win32::ERROR_SUCCESS)
168
+ allow(query_value_response2).to receive_messages(
169
+ :error_status => WindowsError::Win32::ERROR_SUCCESS,
170
+ :data => data
171
+ )
172
+ allow(query_value_response1).to receive(:lpcb_data).and_return(lpcb_data)
173
+ allow(lpcb_data).to receive(:referent).and_return(lpcb_data_referent)
174
+ end
175
+
176
+ it 'create the expected QueryValueRequest packets' do
177
+ winreg.query_value(handle, value_name)
178
+ expect(described_class::QueryValueRequest).to have_received(:new).with(hkey: handle, lp_value_name: value_name).twice
179
+ end
180
+
181
+ it 'sets the expected user rights on the first request packet' do
182
+ winreg.query_value(handle, value_name)
183
+ expect(lp_data1).to have_received(:referent_identifier=).with(0)
184
+ end
185
+
186
+ it 'sets the expected user rights on the second request packet' do
187
+ winreg.query_value(handle, value_name)
188
+ expect(lp_data2).to have_received(:max_count=).with(lpcb_data_referent)
189
+ expect(query_value_request_packet2).to have_received(:lpcb_data=).with(lpcb_data)
190
+ end
191
+
192
+ it 'sends the expected dcerpc requests' do
193
+ winreg.query_value(handle, value_name)
194
+ expect(winreg).to have_received(:dcerpc_request).with(query_value_request_packet1).once.ordered
195
+ expect(winreg).to have_received(:dcerpc_request).with(query_value_request_packet2).once.ordered
196
+ end
197
+
198
+ context 'when receiving the first response' do
199
+ it 'creates a QueryValueResponse structure from the expected dcerpc response' do
200
+ winreg.query_value(handle, value_name)
201
+ expect(described_class::QueryValueResponse).to have_received(:read).with(response1)
202
+ end
203
+
204
+ context 'when an IOError occurs while parsing the response' do
205
+ it 'raises a RubySMB::Dcerpc::Error::InvalidPacket' do
206
+ allow(described_class::QueryValueResponse).to receive(:read).with(response1).and_raise(IOError)
207
+ expect { winreg.query_value(handle, value_name) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
208
+ end
209
+ end
210
+
211
+ context 'when the first response error status is not WindowsError::Win32::ERROR_SUCCESS' do
212
+ it 'raises a RubySMB::Dcerpc::Error::WinregError' do
213
+ allow(query_value_response1).to receive(:error_status).and_return(WindowsError::Win32::ERROR_INVALID_DATA)
214
+ expect { winreg.query_value(handle, value_name) }.to raise_error(RubySMB::Dcerpc::Error::WinregError)
215
+ end
216
+ end
217
+ end
218
+
219
+ context 'when receiving the second response' do
220
+ it 'creates a QueryValueResponse structure from the expected dcerpc response' do
221
+ winreg.query_value(handle, value_name)
222
+ expect(described_class::QueryValueResponse).to have_received(:read).with(response2)
223
+ end
224
+
225
+ context 'when an IOError occurs while parsing the response' do
226
+ it 'raises a RubySMB::Dcerpc::Error::InvalidPacket' do
227
+ allow(described_class::QueryValueResponse).to receive(:read).with(response2).and_raise(IOError)
228
+ expect { winreg.query_value(handle, value_name) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
229
+ end
230
+ end
231
+
232
+ context 'when the first response error status is not WindowsError::Win32::ERROR_SUCCESS' do
233
+ it 'raises a RubySMB::Dcerpc::Error::WinregError' do
234
+ allow(query_value_response2).to receive(:error_status).and_return(WindowsError::Win32::ERROR_INVALID_DATA)
235
+ expect { winreg.query_value(handle, value_name) }.to raise_error(RubySMB::Dcerpc::Error::WinregError)
236
+ end
237
+ end
238
+ end
239
+
240
+ it 'returns the expected response data' do
241
+ expect(winreg.query_value(handle, value_name)).to eq(data)
242
+ end
243
+ end
244
+
245
+ describe '#close_key' do
246
+ let(:handle) { double('Handle') }
247
+ let(:close_key_request_packet) { double('CloseKey Request Packet') }
248
+ let(:response) { double('Response') }
249
+ let(:close_key_response) { double('CloseKey Response') }
250
+ before :example do
251
+ allow(described_class::CloseKeyRequest).to receive(:new).and_return(close_key_request_packet)
252
+ allow(winreg).to receive(:dcerpc_request).and_return(response)
253
+ allow(described_class::CloseKeyResponse).to receive(:read).and_return(close_key_response)
254
+ allow(close_key_response).to receive(:error_status).and_return(WindowsError::Win32::ERROR_SUCCESS)
255
+ end
256
+
257
+ it 'create the expected CloseKeyRequest packet' do
258
+ winreg.close_key(handle)
259
+ expect(described_class::CloseKeyRequest).to have_received(:new).with(hkey: handle)
260
+ end
261
+
262
+ it 'sends the expected dcerpc request' do
263
+ winreg.close_key(handle)
264
+ expect(winreg).to have_received(:dcerpc_request).with(close_key_request_packet)
265
+ end
266
+
267
+ it 'creates a CloseKeyResponse structure from the expected dcerpc response' do
268
+ winreg.close_key(handle)
269
+ expect(described_class::CloseKeyResponse).to have_received(:read).with(response)
270
+ end
271
+
272
+ context 'when an IOError occurs while parsing the response' do
273
+ it 'raises a RubySMB::Dcerpc::Error::InvalidPacket' do
274
+ allow(described_class::CloseKeyResponse).to receive(:read).and_raise(IOError)
275
+ expect { winreg.close_key(handle) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
276
+ end
277
+ end
278
+
279
+ context 'when the response error status is not WindowsError::Win32::ERROR_SUCCESS' do
280
+ it 'raises a RubySMB::Dcerpc::Error::WinregError' do
281
+ allow(close_key_response).to receive(:error_status).and_return(WindowsError::Win32::ERROR_INVALID_DATA)
282
+ expect { winreg.close_key(handle) }.to raise_error(RubySMB::Dcerpc::Error::WinregError)
283
+ end
284
+ end
285
+
286
+ it 'returns the expected error status' do
287
+ expect(winreg.close_key(handle)).to eq(WindowsError::Win32::ERROR_SUCCESS)
288
+ end
289
+ end
290
+
291
+ describe '#query_info_key' do
292
+ let(:handle) { double('Handle') }
293
+ let(:query_info_key_request_packet) { double('CloseKey Request Packet') }
294
+ let(:response) { double('Response') }
295
+ let(:query_info_key_response) { double('CloseKey Response') }
296
+ before :example do
297
+ allow(described_class::QueryInfoKeyRequest).to receive(:new).and_return(query_info_key_request_packet)
298
+ allow(winreg).to receive(:dcerpc_request).and_return(response)
299
+ allow(described_class::QueryInfoKeyResponse).to receive(:read).and_return(query_info_key_response)
300
+ allow(query_info_key_response).to receive(:error_status).and_return(WindowsError::Win32::ERROR_SUCCESS)
301
+ end
302
+
303
+ it 'create the expected QueryInfoKeyRequest packet' do
304
+ winreg.query_info_key(handle)
305
+ expect(described_class::QueryInfoKeyRequest).to have_received(:new).with(hkey: handle)
306
+ end
307
+
308
+ it 'sends the expected dcerpc request' do
309
+ winreg.query_info_key(handle)
310
+ expect(winreg).to have_received(:dcerpc_request).with(query_info_key_request_packet)
311
+ end
312
+
313
+ it 'creates a QueryInfoKeyResponse structure from the expected dcerpc response' do
314
+ winreg.query_info_key(handle)
315
+ expect(described_class::QueryInfoKeyResponse).to have_received(:read).with(response)
316
+ end
317
+
318
+ context 'when an IOError occurs while parsing the response' do
319
+ it 'raises a RubySMB::Dcerpc::Error::InvalidPacket' do
320
+ allow(described_class::QueryInfoKeyResponse).to receive(:read).and_raise(IOError)
321
+ expect { winreg.query_info_key(handle) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
322
+ end
323
+ end
324
+
325
+ context 'when the response error status is not WindowsError::Win32::ERROR_SUCCESS' do
326
+ it 'raises a RubySMB::Dcerpc::Error::WinregError' do
327
+ allow(query_info_key_response).to receive(:error_status).and_return(WindowsError::Win32::ERROR_INVALID_DATA)
328
+ expect { winreg.query_info_key(handle) }.to raise_error(RubySMB::Dcerpc::Error::WinregError)
329
+ end
330
+ end
331
+
332
+ it 'returns the expected response' do
333
+ expect(winreg.query_info_key(handle)).to eq(query_info_key_response)
334
+ end
335
+ end
336
+
337
+ describe '#enum_key' do
338
+ let(:handle) { double('Handle') }
339
+ let(:index) { double('Index') }
340
+ let(:enum_key_request_packet) { double('enum_key Request Packet') }
341
+ let(:lp_class) { double('Lp Class') }
342
+ let(:lp_name) { double('Lp Name') }
343
+ let(:buffer) { double('Buffer') }
344
+ let(:lp_class_buffer_referent) { double('Lp Class buffer referent') }
345
+ let(:lp_name_buffer_referent) { double('Lp Name buffer referent') }
346
+ let(:response) { double('Response') }
347
+ let(:enum_key_response) { double('enum_key Response') }
348
+ let(:result_str) { double('Result String') }
349
+ before :example do
350
+ allow(described_class::EnumKeyRequest).to receive(:new).and_return(enum_key_request_packet)
351
+ allow(enum_key_request_packet).to receive_messages(
352
+ :lpft_last_write_time= => nil,
353
+ :lp_class => lp_class,
354
+ :lp_name => lp_name
355
+ )
356
+ allow(lp_class).to receive(:referent).and_return(lp_class_buffer_referent)
357
+ allow(lp_name).to receive(:buffer).and_return(buffer)
358
+ allow(buffer).to receive(:referent).and_return(lp_name_buffer_referent)
359
+ allow(lp_class_buffer_referent).to receive(:buffer=)
360
+ allow(lp_name_buffer_referent).to receive(:max_count=)
361
+ allow(winreg).to receive(:dcerpc_request).and_return(response)
362
+ allow(described_class::EnumKeyResponse).to receive(:read).and_return(enum_key_response)
363
+ allow(enum_key_response).to receive(:error_status).and_return(WindowsError::Win32::ERROR_SUCCESS)
364
+ allow(enum_key_response).to receive_message_chain(:lp_name, :to_s => result_str)
365
+ end
366
+
367
+ it 'create the expected EnumKeyRequest packet' do
368
+ winreg.enum_key(handle, index)
369
+ expect(described_class::EnumKeyRequest).to have_received(:new).with(hkey: handle, dw_index: index)
370
+ end
371
+
372
+ it 'sets the expected user rights on the request packet' do
373
+ winreg.enum_key(handle, index)
374
+ expect(enum_key_request_packet).to have_received(:lpft_last_write_time=).with(0)
375
+ expect(lp_class_buffer_referent).to have_received(:buffer=).with(0)
376
+ expect(lp_name_buffer_referent).to have_received(:max_count=).with(256)
377
+ end
378
+
379
+ it 'sends the expected dcerpc request' do
380
+ winreg.enum_key(handle, index)
381
+ expect(winreg).to have_received(:dcerpc_request).with(enum_key_request_packet)
382
+ end
383
+
384
+ it 'creates a EnumKeyResponse structure from the expected dcerpc response' do
385
+ winreg.enum_key(handle, index)
386
+ expect(described_class::EnumKeyResponse).to have_received(:read).with(response)
387
+ end
388
+
389
+ context 'when an IOError occurs while parsing the response' do
390
+ it 'raises a RubySMB::Dcerpc::Error::InvalidPacket' do
391
+ allow(described_class::EnumKeyResponse).to receive(:read).and_raise(IOError)
392
+ expect { winreg.enum_key(handle, index) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
393
+ end
394
+ end
395
+
396
+ context 'when the response error status is not WindowsError::Win32::ERROR_SUCCESS' do
397
+ it 'raises a RubySMB::Dcerpc::Error::WinregError' do
398
+ allow(enum_key_response).to receive(:error_status).and_return(WindowsError::Win32::ERROR_INVALID_DATA)
399
+ expect { winreg.enum_key(handle, index) }.to raise_error(RubySMB::Dcerpc::Error::WinregError)
400
+ end
401
+ end
402
+
403
+ it 'returns the expected key name' do
404
+ expect(winreg.enum_key(handle, index)).to eq(result_str)
405
+ end
406
+ end
407
+
408
+ describe '#enum_value' do
409
+ let(:handle) { double('Handle') }
410
+ let(:index) { double('Index') }
411
+ let(:enum_value_request_packet) { double('EnumValue Request Packet') }
412
+ let(:lp_value_name) { double('Lp Value Name') }
413
+ let(:lp_data) { double('Lp Data') }
414
+ let(:buffer) { double('Buffer') }
415
+ let(:referent) { double('Referent') }
416
+ let(:response) { double('Response') }
417
+ let(:enum_value_response) { double('EnumValue Response') }
418
+ let(:result_str) { double('Result String') }
419
+ before :example do
420
+ allow(described_class::EnumValueRequest).to receive(:new).and_return(enum_value_request_packet)
421
+ allow(enum_value_request_packet).to receive_messages(
422
+ :lp_value_name => lp_value_name,
423
+ :lp_data => lp_data
424
+ )
425
+ allow(lp_value_name).to receive(:buffer).and_return(buffer)
426
+ allow(buffer).to receive(:referent).and_return(referent)
427
+ allow(referent).to receive(:max_count=)
428
+ allow(lp_data).to receive(:referent_identifier=)
429
+ allow(winreg).to receive(:dcerpc_request).and_return(response)
430
+ allow(described_class::EnumValueResponse).to receive(:read).and_return(enum_value_response)
431
+ allow(enum_value_response).to receive(:error_status).and_return(WindowsError::Win32::ERROR_SUCCESS)
432
+ allow(enum_value_response).to receive_message_chain(:lp_value_name, :to_s => result_str)
433
+ end
434
+
435
+ it 'create the expected EnumValueRequest packet' do
436
+ winreg.enum_value(handle, index)
437
+ expect(described_class::EnumValueRequest).to have_received(:new).with(hkey: handle, dw_index: index)
438
+ end
439
+
440
+ it 'sets the expected user rights on the request packet' do
441
+ winreg.enum_value(handle, index)
442
+ expect(referent).to have_received(:max_count=).with(256)
443
+ expect(lp_data).to have_received(:referent_identifier=).with(0)
444
+ end
445
+
446
+ it 'sends the expected dcerpc request' do
447
+ winreg.enum_value(handle, index)
448
+ expect(winreg).to have_received(:dcerpc_request).with(enum_value_request_packet)
449
+ end
450
+
451
+ it 'creates a EnumValueResponse structure from the expected dcerpc response' do
452
+ winreg.enum_value(handle, index)
453
+ expect(described_class::EnumValueResponse).to have_received(:read).with(response)
454
+ end
455
+
456
+ context 'when an IOError occurs while parsing the response' do
457
+ it 'raises a RubySMB::Dcerpc::Error::InvalidPacket' do
458
+ allow(described_class::EnumValueResponse).to receive(:read).and_raise(IOError)
459
+ expect { winreg.enum_value(handle, index) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
460
+ end
461
+ end
462
+
463
+ context 'when the response error status is not WindowsError::Win32::ERROR_SUCCESS' do
464
+ it 'raises a RubySMB::Dcerpc::Error::WinregError' do
465
+ allow(enum_value_response).to receive(:error_status).and_return(WindowsError::Win32::ERROR_INVALID_DATA)
466
+ expect { winreg.enum_value(handle, index) }.to raise_error(RubySMB::Dcerpc::Error::WinregError)
467
+ end
468
+ end
469
+
470
+ it 'returns the expected key name' do
471
+ expect(winreg.enum_value(handle, index)).to eq(result_str)
472
+ end
473
+ end
474
+
475
+ describe '#has_registry_key?' do
476
+ let(:root_key) { 'HKLM' }
477
+ let(:sub_key) { 'my\\sub\\key\\path' }
478
+ let(:key) { "#{root_key}\\#{sub_key}" }
479
+ let(:root_key_handle) { double('Root Key Handle') }
480
+ let(:subkey_handle) { double('Subkey Handle') }
481
+ before :example do
482
+ allow(winreg).to receive_messages(
483
+ :bind => nil,
484
+ :open_root_key => root_key_handle,
485
+ :open_key => subkey_handle,
486
+ :close_key => nil
487
+ )
488
+ end
489
+
490
+ it 'binds a DCERPC connection to the expected remote endpoint' do
491
+ winreg.has_registry_key?(key)
492
+ expect(winreg).to have_received(:bind).with(endpoint: RubySMB::Dcerpc::Winreg)
493
+ end
494
+
495
+ it 'opens the expected root key' do
496
+ winreg.has_registry_key?(key)
497
+ expect(winreg).to have_received(:open_root_key).with(root_key)
498
+ end
499
+
500
+ it 'opens the expected registry key' do
501
+ winreg.has_registry_key?(key)
502
+ expect(winreg).to have_received(:open_key).with(root_key_handle, sub_key)
503
+ end
504
+
505
+ context 'when a WinregError occurs while opening the root key' do
506
+ it 'returns false' do
507
+ allow(winreg).to receive(:open_root_key).and_raise(RubySMB::Dcerpc::Error::WinregError)
508
+ expect(winreg.has_registry_key?(key)).to be false
509
+ end
510
+ end
511
+
512
+ context 'when a WinregError occurs while opening the registry key' do
513
+ it 'returns false' do
514
+ allow(winreg).to receive(:open_key).and_raise(RubySMB::Dcerpc::Error::WinregError)
515
+ expect(winreg.has_registry_key?(key)).to be false
516
+ end
517
+ end
518
+
519
+ it 'closes the key' do
520
+ winreg.has_registry_key?(key)
521
+ expect(winreg).to have_received(:close_key).with(subkey_handle)
522
+ end
523
+
524
+ it 'returns true when no error occurs' do
525
+ expect(winreg.has_registry_key?(key)).to be true
526
+ end
527
+ end
528
+
529
+ describe '#read_registry_key_value' do
530
+ let(:root_key) { 'HKLM' }
531
+ let(:sub_key) { 'my\\sub\\key\\path' }
532
+ let(:key) { "#{root_key}\\#{sub_key}" }
533
+ let(:value_name) { 'registry_value_name' }
534
+ let(:root_key_handle) { double('Root Key Handle') }
535
+ let(:subkey_handle) { double('Subkey Handle') }
536
+ let(:value) { double('Value') }
537
+ before :example do
538
+ allow(winreg).to receive_messages(
539
+ :bind => nil,
540
+ :open_root_key => root_key_handle,
541
+ :open_key => subkey_handle,
542
+ :query_value => value,
543
+ :close_key => nil
544
+ )
545
+ end
546
+
547
+ it 'binds a DCERPC connection to the expected remote endpoint' do
548
+ winreg.read_registry_key_value(key, value_name)
549
+ expect(winreg).to have_received(:bind).with(endpoint: RubySMB::Dcerpc::Winreg)
550
+ end
551
+
552
+ it 'opens the expected root key' do
553
+ winreg.read_registry_key_value(key, value_name)
554
+ expect(winreg).to have_received(:open_root_key).with(root_key)
555
+ end
556
+
557
+ it 'opens the expected registry key' do
558
+ winreg.read_registry_key_value(key, value_name)
559
+ expect(winreg).to have_received(:open_key).with(root_key_handle, sub_key)
560
+ end
561
+
562
+ it 'queries the expected registry key value' do
563
+ winreg.read_registry_key_value(key, value_name)
564
+ expect(winreg).to have_received(:query_value).with(subkey_handle, value_name)
565
+ end
566
+
567
+ it 'closes the key' do
568
+ winreg.read_registry_key_value(key, value_name)
569
+ expect(winreg).to have_received(:close_key).with(subkey_handle)
570
+ end
571
+
572
+ it 'returns expect registry key value' do
573
+ expect(winreg.read_registry_key_value(key, value_name)).to eq(value)
574
+ end
575
+ end
576
+
577
+ describe '#enum_registry_key' do
578
+ let(:root_key) { 'HKLM' }
579
+ let(:sub_key) { 'my\\sub\\key\\path' }
580
+ let(:key) { "#{root_key}\\#{sub_key}" }
581
+ let(:value_name) { 'registry_value_name' }
582
+ let(:root_key_handle) { double('Root Key Handle') }
583
+ let(:subkey_handle) { double('Subkey Handle') }
584
+ let(:query_info_key_response) { double('Query Info Key Response') }
585
+ let(:subkey_nb) { 2 }
586
+ before :example do
587
+ allow(winreg).to receive_messages(
588
+ :bind => nil,
589
+ :open_root_key => root_key_handle,
590
+ :open_key => subkey_handle,
591
+ :query_info_key => query_info_key_response,
592
+ :enum_key => nil,
593
+ :close_key => nil
594
+ )
595
+ allow(query_info_key_response).to receive(:lpc_sub_keys).and_return(subkey_nb)
596
+ end
597
+
598
+ it 'binds a DCERPC connection to the expected remote endpoint' do
599
+ winreg.enum_registry_key(key)
600
+ expect(winreg).to have_received(:bind).with(endpoint: RubySMB::Dcerpc::Winreg)
601
+ end
602
+
603
+ it 'opens the expected root key' do
604
+ winreg.enum_registry_key(key)
605
+ expect(winreg).to have_received(:open_root_key).with(root_key)
606
+ end
607
+
608
+ context 'when the registry key only contains the root key' do
609
+ it 'queries information for the root key' do
610
+ winreg.enum_registry_key(root_key)
611
+ expect(winreg).to have_received(:query_info_key).with(root_key_handle)
612
+ end
613
+ end
614
+
615
+ it 'opens the expected registry key' do
616
+ winreg.enum_registry_key(key)
617
+ expect(winreg).to have_received(:open_key).with(root_key_handle, sub_key)
618
+ end
619
+
620
+ it 'queries information for the expected registry key' do
621
+ winreg.enum_registry_key(key)
622
+ expect(winreg).to have_received(:query_info_key).with(subkey_handle)
623
+ end
624
+
625
+ it 'calls #enum_key the expected number of times' do
626
+ winreg.enum_registry_key(key)
627
+ expect(winreg).to have_received(:enum_key).with(subkey_handle, instance_of(Fixnum)).twice
628
+ end
629
+
630
+ it 'closes the key' do
631
+ winreg.enum_registry_key(key)
632
+ expect(winreg).to have_received(:close_key).with(subkey_handle)
633
+ end
634
+
635
+ it 'returns the expected array of enumerated keys' do
636
+ key1 = 'key1'
637
+ key2 = 'key2'
638
+ allow(winreg).to receive(:enum_key).with(subkey_handle, 0).and_return(key1)
639
+ allow(winreg).to receive(:enum_key).with(subkey_handle, 1).and_return(key2)
640
+ expect(winreg.enum_registry_key(key)).to eq([key1, key2])
641
+ end
642
+ end
643
+
644
+ describe '#enum_registry_values' do
645
+ let(:root_key) { 'HKLM' }
646
+ let(:sub_key) { 'my\\sub\\key\\path' }
647
+ let(:key) { "#{root_key}\\#{sub_key}" }
648
+ let(:value_name) { 'registry_value_name' }
649
+ let(:root_key_handle) { double('Root Key Handle') }
650
+ let(:subkey_handle) { double('Subkey Handle') }
651
+ let(:query_info_key_response) { double('Query Info Key Response') }
652
+ let(:subkey_nb) { 2 }
653
+ before :example do
654
+ allow(winreg).to receive_messages(
655
+ :bind => nil,
656
+ :open_root_key => root_key_handle,
657
+ :open_key => subkey_handle,
658
+ :query_info_key => query_info_key_response,
659
+ :enum_value => nil,
660
+ :close_key => nil
661
+ )
662
+ allow(query_info_key_response).to receive(:lpc_values).and_return(subkey_nb)
663
+ end
664
+
665
+ it 'binds a DCERPC connection to the expected remote endpoint' do
666
+ winreg.enum_registry_values(key)
667
+ expect(winreg).to have_received(:bind).with(endpoint: RubySMB::Dcerpc::Winreg)
668
+ end
669
+
670
+ it 'opens the expected root key' do
671
+ winreg.enum_registry_values(key)
672
+ expect(winreg).to have_received(:open_root_key).with(root_key)
673
+ end
674
+
675
+ context 'when the registry key only contains the root key' do
676
+ it 'queries information for the root key' do
677
+ winreg.enum_registry_values(root_key)
678
+ expect(winreg).to have_received(:query_info_key).with(root_key_handle)
679
+ end
680
+ end
681
+
682
+ it 'opens the expected registry key' do
683
+ winreg.enum_registry_values(key)
684
+ expect(winreg).to have_received(:open_key).with(root_key_handle, sub_key)
685
+ end
686
+
687
+ it 'queries information for the expected registry key' do
688
+ winreg.enum_registry_values(key)
689
+ expect(winreg).to have_received(:query_info_key).with(subkey_handle)
690
+ end
691
+
692
+ it 'calls #enum_key the expected number of times' do
693
+ winreg.enum_registry_values(key)
694
+ expect(winreg).to have_received(:enum_value).with(subkey_handle, instance_of(Fixnum)).twice
695
+ end
696
+
697
+ it 'closes the key' do
698
+ winreg.enum_registry_values(key)
699
+ expect(winreg).to have_received(:close_key).with(subkey_handle)
700
+ end
701
+
702
+ it 'returns the expected array of enumerated keys' do
703
+ value1 = 'value1'
704
+ value2 = 'value2'
705
+ allow(winreg).to receive(:enum_value).with(subkey_handle, 0).and_return(value1)
706
+ allow(winreg).to receive(:enum_value).with(subkey_handle, 1).and_return(value2)
707
+ expect(winreg.enum_registry_values(key)).to eq([value1, value2])
708
+ end
709
+ end
710
+ end