ruby_smb 2.0.9 → 2.0.13
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/.github/workflows/verify.yml +5 -15
- data/examples/auth_capture.rb +71 -0
- data/examples/dump_secrets_from_sid.rb +207 -0
- data/examples/enum_domain_users.rb +75 -0
- data/examples/get_computer_info.rb +42 -0
- data/examples/query_service_status.rb +42 -4
- data/lib/ruby_smb/client/negotiation.rb +1 -1
- data/lib/ruby_smb/client.rb +10 -20
- data/lib/ruby_smb/dcerpc/bind.rb +28 -20
- data/lib/ruby_smb/dcerpc/bind_ack.rb +29 -28
- data/lib/ruby_smb/dcerpc/client.rb +542 -0
- data/lib/ruby_smb/dcerpc/drsr/drs_bind_request.rb +24 -0
- data/lib/ruby_smb/dcerpc/drsr/drs_bind_response.rb +26 -0
- data/lib/ruby_smb/dcerpc/drsr/drs_crack_names_request.rb +57 -0
- data/lib/ruby_smb/dcerpc/drsr/drs_crack_names_response.rb +76 -0
- data/lib/ruby_smb/dcerpc/drsr/drs_domain_controller_info_request.rb +46 -0
- data/lib/ruby_smb/dcerpc/drsr/drs_domain_controller_info_response.rb +168 -0
- data/lib/ruby_smb/dcerpc/drsr/drs_extensions.rb +56 -0
- data/lib/ruby_smb/dcerpc/drsr/drs_get_nc_changes_request.rb +121 -0
- data/lib/ruby_smb/dcerpc/drsr/drs_get_nc_changes_response.rb +118 -0
- data/lib/ruby_smb/dcerpc/drsr/drs_unbind_request.rb +24 -0
- data/lib/ruby_smb/dcerpc/drsr/drs_unbind_response.rb +26 -0
- data/lib/ruby_smb/dcerpc/drsr.rb +909 -0
- data/lib/ruby_smb/dcerpc/epm/epm_ept_map_request.rb +26 -0
- data/lib/ruby_smb/dcerpc/epm/epm_ept_map_response.rb +25 -0
- data/lib/ruby_smb/dcerpc/epm/epm_twrt.rb +211 -0
- data/lib/ruby_smb/dcerpc/epm.rb +75 -0
- data/lib/ruby_smb/dcerpc/error.rb +17 -0
- data/lib/ruby_smb/dcerpc/ndr.rb +1159 -297
- data/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_request.rb +3 -13
- data/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_response.rb +3 -3
- data/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_request.rb +3 -13
- data/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_response.rb +1 -1
- data/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_request.rb +3 -11
- data/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_response.rb +1 -1
- data/lib/ruby_smb/dcerpc/netlogon.rb +5 -4
- data/lib/ruby_smb/dcerpc/p_syntax_id_t.rb +4 -3
- data/lib/ruby_smb/dcerpc/pdu_header.rb +7 -7
- data/lib/ruby_smb/dcerpc/ptypes.rb +1 -0
- data/lib/ruby_smb/dcerpc/request.rb +79 -32
- data/lib/ruby_smb/dcerpc/response.rb +45 -10
- data/lib/ruby_smb/dcerpc/rpc_auth3.rb +28 -0
- data/lib/ruby_smb/dcerpc/rpc_security_attributes.rb +11 -11
- data/lib/ruby_smb/dcerpc/rrp_rpc_unicode_string.rb +118 -0
- data/lib/ruby_smb/dcerpc/samr/rpc_sid.rb +150 -0
- data/lib/ruby_smb/dcerpc/samr/samr_close_handle_request.rb +23 -0
- data/lib/ruby_smb/dcerpc/samr/samr_close_handle_response.rb +24 -0
- data/lib/ruby_smb/dcerpc/samr/samr_connect_request.rb +32 -0
- data/lib/ruby_smb/dcerpc/samr/samr_connect_response.rb +23 -0
- data/lib/ruby_smb/dcerpc/samr/samr_enumerate_users_in_domain_request.rb +26 -0
- data/lib/ruby_smb/dcerpc/samr/samr_enumerate_users_in_domain_response.rb +55 -0
- data/lib/ruby_smb/dcerpc/samr/samr_get_alias_membership_request.rb +48 -0
- data/lib/ruby_smb/dcerpc/samr/samr_get_alias_membership_response.rb +38 -0
- data/lib/ruby_smb/dcerpc/samr/samr_get_groups_for_user_request.rb +23 -0
- data/lib/ruby_smb/dcerpc/samr/samr_get_groups_for_user_response.rb +48 -0
- data/lib/ruby_smb/dcerpc/samr/samr_lookup_domain_in_sam_server_request.rb +24 -0
- data/lib/ruby_smb/dcerpc/samr/samr_lookup_domain_in_sam_server_response.rb +25 -0
- data/lib/ruby_smb/dcerpc/samr/samr_open_domain_request.rb +27 -0
- data/lib/ruby_smb/dcerpc/samr/samr_open_domain_response.rb +24 -0
- data/lib/ruby_smb/dcerpc/samr/samr_open_user_request.rb +26 -0
- data/lib/ruby_smb/dcerpc/samr/samr_open_user_response.rb +24 -0
- data/lib/ruby_smb/dcerpc/samr/samr_rid_to_sid_request.rb +23 -0
- data/lib/ruby_smb/dcerpc/samr/samr_rid_to_sid_response.rb +23 -0
- data/lib/ruby_smb/dcerpc/samr.rb +613 -0
- data/lib/ruby_smb/dcerpc/sec_trailer.rb +26 -0
- data/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all.rb +56 -79
- data/lib/ruby_smb/dcerpc/srvsvc.rb +27 -4
- data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request.rb +13 -25
- data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response.rb +2 -2
- data/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response.rb +1 -1
- data/lib/ruby_smb/dcerpc/svcctl/control_service_request.rb +1 -1
- data/lib/ruby_smb/dcerpc/svcctl/control_service_response.rb +1 -1
- data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request.rb +4 -14
- data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response.rb +1 -1
- data/lib/ruby_smb/dcerpc/svcctl/open_service_w_request.rb +3 -11
- data/lib/ruby_smb/dcerpc/svcctl/open_service_w_response.rb +1 -1
- data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request.rb +1 -1
- data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response.rb +12 -11
- data/lib/ruby_smb/dcerpc/svcctl/query_service_status_response.rb +1 -1
- data/lib/ruby_smb/dcerpc/svcctl/service_status.rb +9 -8
- data/lib/ruby_smb/dcerpc/svcctl/start_service_w_request.rb +3 -3
- data/lib/ruby_smb/dcerpc/svcctl/start_service_w_response.rb +1 -1
- data/lib/ruby_smb/dcerpc/svcctl.rb +1 -3
- data/lib/ruby_smb/dcerpc/uuid.rb +3 -0
- data/lib/ruby_smb/dcerpc/winreg/close_key_response.rb +2 -2
- data/lib/ruby_smb/dcerpc/winreg/create_key_request.rb +2 -13
- data/lib/ruby_smb/dcerpc/winreg/create_key_response.rb +3 -3
- data/lib/ruby_smb/dcerpc/winreg/enum_key_request.rb +3 -20
- data/lib/ruby_smb/dcerpc/winreg/enum_key_response.rb +3 -20
- data/lib/ruby_smb/dcerpc/winreg/enum_value_request.rb +5 -14
- data/lib/ruby_smb/dcerpc/winreg/enum_value_response.rb +5 -14
- data/lib/ruby_smb/dcerpc/winreg/open_key_request.rb +1 -9
- data/lib/ruby_smb/dcerpc/winreg/open_key_response.rb +4 -3
- data/lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb +5 -6
- data/lib/ruby_smb/dcerpc/winreg/open_root_key_response.rb +2 -2
- data/lib/ruby_smb/dcerpc/winreg/query_info_key_response.rb +9 -18
- data/lib/ruby_smb/dcerpc/winreg/query_value_request.rb +4 -14
- data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +7 -15
- data/lib/ruby_smb/dcerpc/winreg/regsam.rb +3 -1
- data/lib/ruby_smb/dcerpc/winreg/save_key_request.rb +0 -9
- data/lib/ruby_smb/dcerpc/winreg/save_key_response.rb +1 -1
- data/lib/ruby_smb/dcerpc/winreg.rb +10 -14
- data/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_request.rb +26 -0
- data/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_response.rb +88 -0
- data/lib/ruby_smb/dcerpc/wkssvc.rb +65 -0
- data/lib/ruby_smb/dcerpc.rb +41 -11
- data/lib/ruby_smb/dialect.rb +45 -0
- data/lib/ruby_smb/dispatcher/base.rb +1 -1
- data/lib/ruby_smb/field/file_time.rb +1 -1
- data/lib/ruby_smb/field/string16.rb +5 -1
- data/lib/ruby_smb/gss/provider/authenticator.rb +42 -0
- data/lib/ruby_smb/gss/provider/ntlm.rb +303 -0
- data/lib/ruby_smb/gss/provider.rb +35 -0
- data/lib/ruby_smb/gss.rb +56 -63
- data/lib/ruby_smb/ntlm.rb +61 -0
- data/lib/ruby_smb/server/server_client/negotiation.rb +156 -0
- data/lib/ruby_smb/server/server_client/session_setup.rb +82 -0
- data/lib/ruby_smb/server/server_client.rb +162 -0
- data/lib/ruby_smb/server.rb +54 -0
- data/lib/ruby_smb/signing.rb +59 -0
- data/lib/ruby_smb/smb1/packet/negotiate_response.rb +11 -11
- data/lib/ruby_smb/smb1/packet/negotiate_response_extended.rb +1 -1
- data/lib/ruby_smb/smb1/packet/session_setup_request.rb +1 -1
- data/lib/ruby_smb/smb1/pipe.rb +4 -0
- data/lib/ruby_smb/smb1/tree.rb +1 -1
- data/lib/ruby_smb/smb2/negotiate_context.rb +18 -2
- data/lib/ruby_smb/smb2/packet/negotiate_request.rb +9 -0
- data/lib/ruby_smb/smb2/packet/negotiate_response.rb +0 -1
- data/lib/ruby_smb/smb2/packet/session_setup_response.rb +2 -2
- data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +1 -1
- data/lib/ruby_smb/smb2/pipe.rb +4 -0
- data/lib/ruby_smb/smb2/tree.rb +1 -1
- data/lib/ruby_smb/smb2.rb +3 -1
- data/lib/ruby_smb/version.rb +1 -1
- data/lib/ruby_smb.rb +2 -1
- data/spec/lib/ruby_smb/client_spec.rb +8 -11
- data/spec/lib/ruby_smb/dcerpc/bind_ack_spec.rb +69 -41
- data/spec/lib/ruby_smb/dcerpc/bind_spec.rb +75 -21
- data/spec/lib/ruby_smb/dcerpc/client_spec.rb +714 -0
- data/spec/lib/ruby_smb/dcerpc/drsr_spec.rb +2169 -0
- data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +3792 -1373
- data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_request_spec.rb +4 -4
- data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_request_spec.rb +4 -4
- data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_request_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_response_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/p_syntax_id_t_spec.rb +18 -4
- data/spec/lib/ruby_smb/dcerpc/pdu_header_spec.rb +27 -1
- data/spec/lib/ruby_smb/dcerpc/request_spec.rb +76 -11
- data/spec/lib/ruby_smb/dcerpc/response_spec.rb +99 -9
- data/spec/lib/ruby_smb/dcerpc/rpc_auth3_spec.rb +75 -0
- data/spec/lib/ruby_smb/dcerpc/rpc_security_attributes_spec.rb +29 -28
- data/spec/lib/ruby_smb/dcerpc/rrp_rpc_unicode_string_spec.rb +340 -0
- data/spec/lib/ruby_smb/dcerpc/samr/rpc_sid_spec.rb +116 -0
- data/spec/lib/ruby_smb/dcerpc/samr/samr_close_handle_request_spec.rb +40 -0
- data/spec/lib/ruby_smb/dcerpc/samr/samr_close_handle_response_spec.rb +48 -0
- data/spec/lib/ruby_smb/dcerpc/samr/samr_connect_request_spec.rb +56 -0
- data/spec/lib/ruby_smb/dcerpc/samr/samr_connect_response_spec.rb +47 -0
- data/spec/lib/ruby_smb/dcerpc/samr/samr_enumerate_users_in_domain_request_spec.rb +63 -0
- data/spec/lib/ruby_smb/dcerpc/samr/samr_enumerate_users_in_domain_response_spec.rb +265 -0
- data/spec/lib/ruby_smb/dcerpc/samr/samr_lookup_domain_in_sam_server_request_spec.rb +52 -0
- data/spec/lib/ruby_smb/dcerpc/samr/samr_lookup_domain_in_sam_server_response_spec.rb +36 -0
- data/spec/lib/ruby_smb/dcerpc/samr/samr_open_domain_request_spec.rb +56 -0
- data/spec/lib/ruby_smb/dcerpc/samr/samr_open_domain_response_spec.rb +48 -0
- data/spec/lib/ruby_smb/dcerpc/samr/samr_rid_to_sid_request_spec.rb +48 -0
- data/spec/lib/ruby_smb/dcerpc/samr/samr_rid_to_sid_response_spec.rb +42 -0
- data/spec/lib/ruby_smb/dcerpc/samr_spec.rb +420 -0
- data/spec/lib/ruby_smb/dcerpc/sec_trailer_spec.rb +92 -0
- data/spec/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all_spec.rb +149 -110
- data/spec/lib/ruby_smb/dcerpc/srvsvc_spec.rb +21 -17
- data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request_spec.rb +56 -79
- data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response_spec.rb +4 -4
- data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_request_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_response_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request_spec.rb +19 -29
- data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_request_spec.rb +9 -15
- data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_response_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response_spec.rb +22 -22
- data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_response_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/svcctl/service_status_spec.rb +18 -14
- data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_request_spec.rb +5 -4
- data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_response_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/svcctl_spec.rb +1 -5
- data/spec/lib/ruby_smb/dcerpc/uuid_spec.rb +15 -23
- data/spec/lib/ruby_smb/dcerpc/winreg/close_key_response_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/winreg/create_key_request_spec.rb +4 -41
- data/spec/lib/ruby_smb/dcerpc/winreg/create_key_response_spec.rb +4 -4
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +4 -52
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_response_spec.rb +4 -56
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_request_spec.rb +10 -34
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_response_spec.rb +10 -34
- data/spec/lib/ruby_smb/dcerpc/winreg/open_key_request_spec.rb +2 -26
- data/spec/lib/ruby_smb/dcerpc/winreg/open_key_response_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +17 -25
- data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_response_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_response_spec.rb +20 -44
- data/spec/lib/ruby_smb/dcerpc/winreg/query_value_request_spec.rb +8 -32
- data/spec/lib/ruby_smb/dcerpc/winreg/query_value_response_spec.rb +10 -22
- data/spec/lib/ruby_smb/dcerpc/winreg/regsam_spec.rb +4 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/save_key_request_spec.rb +0 -12
- data/spec/lib/ruby_smb/dcerpc/winreg/save_key_response_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +18 -47
- data/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_request_spec.rb +43 -0
- data/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_response_spec.rb +410 -0
- data/spec/lib/ruby_smb/dcerpc/wkssvc_spec.rb +70 -0
- data/spec/lib/ruby_smb/field/string16_spec.rb +22 -0
- data/spec/lib/ruby_smb/gss/provider/ntlm/account_spec.rb +32 -0
- data/spec/lib/ruby_smb/gss/provider/ntlm/authenticator_spec.rb +101 -0
- data/spec/lib/ruby_smb/gss/provider/ntlm/os_version_spec.rb +32 -0
- data/spec/lib/ruby_smb/gss/provider/ntlm_spec.rb +113 -0
- data/spec/lib/ruby_smb/server/server_client_spec.rb +156 -0
- data/spec/lib/ruby_smb/server_spec.rb +32 -0
- data/spec/lib/ruby_smb/smb1/pipe_spec.rb +18 -37
- data/spec/lib/ruby_smb/smb1/tree_spec.rb +4 -4
- data/spec/lib/ruby_smb/smb2/negotiate_context_spec.rb +2 -2
- data/spec/lib/ruby_smb/smb2/pipe_spec.rb +18 -16
- data/spec/lib/ruby_smb/smb2/tree_spec.rb +5 -5
- data/spec/support/bin_helper.rb +9 -0
- data.tar.gz.sig +2 -1
- metadata +119 -6
- metadata.gz.sig +0 -0
- data/lib/ruby_smb/client/signing.rb +0 -64
- data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +0 -38
- data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +0 -135
data/lib/ruby_smb/dcerpc/ndr.rb
CHANGED
|
@@ -1,383 +1,1245 @@
|
|
|
1
|
-
module RubySMB
|
|
2
|
-
module Dcerpc
|
|
3
|
-
module Ndr
|
|
4
|
-
|
|
5
|
-
# NDR Syntax
|
|
6
|
-
UUID = '8a885d04-1ceb-11c9-9fe8-08002b104860'
|
|
7
|
-
VER_MAJOR = 2
|
|
8
|
-
VER_MINOR = 0
|
|
9
|
-
|
|
10
|
-
# An NDR Enum type as defined in
|
|
11
|
-
# [Transfer Syntax NDR - Enumerated Types](https://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_02_05_01)
|
|
12
|
-
class NdrEnum < BinData::Int16le; end
|
|
13
|
-
|
|
14
|
-
# An NDR Conformant and Varying String representation as defined in
|
|
15
|
-
# [Transfer Syntax NDR - Conformant and Varying Strings](http://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_04_02)
|
|
16
|
-
# The string elements are Stringz16 (unicode)
|
|
17
|
-
class NdrString < BinData::Primitive
|
|
18
|
-
endian :little
|
|
19
|
-
|
|
20
|
-
uint32 :max_count
|
|
21
|
-
uint32 :offset, initial_value: 0
|
|
22
|
-
uint32 :actual_count
|
|
23
|
-
stringz16 :str, max_length: -> { actual_count * 2 }, onlyif: -> { actual_count > 0 }
|
|
24
|
-
|
|
25
|
-
def get
|
|
26
|
-
self.actual_count == 0 ? 0 : self.str
|
|
27
|
-
end
|
|
1
|
+
module RubySMB::Dcerpc::Ndr
|
|
28
2
|
|
|
29
|
-
|
|
30
|
-
if v == 0
|
|
31
|
-
self.str.clear
|
|
32
|
-
self.actual_count = 0
|
|
33
|
-
else
|
|
34
|
-
v = v.str if v.is_a?(self.class)
|
|
35
|
-
unless self.str.equal?(v)
|
|
36
|
-
if v.empty?
|
|
37
|
-
self.actual_count = 0
|
|
38
|
-
else
|
|
39
|
-
self.actual_count = v.to_s.size + 1
|
|
40
|
-
self.max_count = self.actual_count
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
self.str = v.to_s
|
|
44
|
-
end
|
|
45
|
-
end
|
|
3
|
+
require 'ruby_smb/field'
|
|
46
4
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
end
|
|
5
|
+
# NDR Syntax
|
|
6
|
+
UUID = '8a885d04-1ceb-11c9-9fe8-08002b104860'
|
|
7
|
+
VER_MAJOR = 2
|
|
8
|
+
VER_MINOR = 0
|
|
52
9
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
10
|
+
#####################################
|
|
11
|
+
# NDR Primitive Types #
|
|
12
|
+
#####################################
|
|
13
|
+
|
|
14
|
+
# Signed and unsigned integers use BinData primitives directly
|
|
15
|
+
|
|
16
|
+
# [Booleans](https://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_02_03)
|
|
17
|
+
class NdrBoolean < BinData::Uint32le
|
|
18
|
+
default_parameters byte_align: 4
|
|
19
|
+
|
|
20
|
+
def assign(val)
|
|
21
|
+
super(value_to_int(val))
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def snapshot
|
|
25
|
+
return _value == 0 ? false : true
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def value_to_binary_string(val)
|
|
32
|
+
super(value_to_int(val))
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def value_to_int(val)
|
|
36
|
+
val = val.snapshot if val.respond_to?(:snapshot)
|
|
37
|
+
case(val)
|
|
38
|
+
when FalseClass
|
|
39
|
+
return 0
|
|
40
|
+
when TrueClass
|
|
41
|
+
return 1
|
|
42
|
+
when Integer
|
|
43
|
+
# Any non-zero value is TRUE, let's assume the caller knows what he's doing
|
|
44
|
+
return val
|
|
45
|
+
else
|
|
46
|
+
raise ArgumentError.new(
|
|
47
|
+
"Type mismatch (#{val.class}). Expecting FalseClass, TrueClass or Integer"
|
|
48
|
+
)
|
|
56
49
|
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
57
52
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
53
|
+
# [Characters](https://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_02_04)
|
|
54
|
+
class NdrChar < BinData::String
|
|
55
|
+
default_parameter(length: 1, byte_align: 1)
|
|
56
|
+
end
|
|
62
57
|
|
|
63
|
-
|
|
64
|
-
|
|
58
|
+
class NdrWideChar < RubySMB::Field::String16
|
|
59
|
+
default_parameter(length: 2, byte_align: 2)
|
|
60
|
+
end
|
|
65
61
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
62
|
+
# An NDR Enum type as defined in
|
|
63
|
+
# [Transfer Syntax NDR - Enumerated Types](https://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_02_05_01)
|
|
64
|
+
class NdrEnum < BinData::Int16le
|
|
65
|
+
default_parameters byte_align: 2
|
|
66
|
+
end
|
|
69
67
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
68
|
+
# [Integers](https://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_02_05)
|
|
69
|
+
# This will define the four size Integers accepted by the NDR protocol:
|
|
70
|
+
# - NdrUint8
|
|
71
|
+
# - NdrUint16
|
|
72
|
+
# - NdrUint32
|
|
73
|
+
# - NdrUint64
|
|
74
|
+
{Uint8: 1, Uint16le: 2, Uint32le: 4, Uint64le: 8}.each do |klass, nb_bytes|
|
|
75
|
+
new_klass_name = "Ndr#{klass.to_s.chomp('le')}"
|
|
76
|
+
unless self.const_defined?(new_klass_name)
|
|
77
|
+
new_klass = Class.new(BinData.const_get(klass)) do
|
|
78
|
+
default_parameters byte_align: nb_bytes
|
|
75
79
|
end
|
|
80
|
+
self.const_set(new_klass_name, new_klass)
|
|
81
|
+
BinData::RegisteredClasses.register(new_klass_name, new_klass)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
class NdrFileTime < RubySMB::Field::FileTime
|
|
86
|
+
# Note that the original Microsoft FILETIME structure is composed of two
|
|
87
|
+
# DWORDs, whereas RubySMB implementation uses an Uint64 field. For this
|
|
88
|
+
# reason, the alignement is set to the size of a DWORD (4 bytes) to match
|
|
89
|
+
# Microsoft structures.
|
|
90
|
+
default_parameters byte_align: 4
|
|
91
|
+
end
|
|
76
92
|
|
|
77
|
-
# An NDR Uni-dimensional Conformant-varying Arrays of bytes representation as defined in:
|
|
78
|
-
# [Transfer Syntax NDR - NDR Constructed Types](http://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_03_04)
|
|
79
|
-
class NdrByteArray < BinData::Primitive
|
|
80
|
-
endian :little
|
|
81
93
|
|
|
82
|
-
uint32 :max_count, initial_value: -> { self.actual_count }
|
|
83
|
-
uint32 :offset, initial_value: 0
|
|
84
|
-
uint32 :actual_count, initial_value: -> { self.bytes.size }
|
|
85
|
-
array :bytes, :type => :uint8, initial_length: -> { self.actual_count }
|
|
86
94
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
95
|
+
#####################################
|
|
96
|
+
# NDR Constructed Types #
|
|
97
|
+
#####################################
|
|
90
98
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
99
|
+
module ConstructedTypePlugin
|
|
100
|
+
def initialize_instance
|
|
101
|
+
@deferred_ptrs = []
|
|
102
|
+
super
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def defer_ptr(ref)
|
|
106
|
+
@deferred_ptrs << ref
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def has_deferred_ptrs?
|
|
110
|
+
!!@deferred_ptrs&.any?
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def write_ptr(io)
|
|
114
|
+
@deferred_ptrs.each do |ptr_ref|
|
|
115
|
+
ptr_ref.do_write(io, is_deferred: true)
|
|
96
116
|
end
|
|
117
|
+
@deferred_ptrs.clear
|
|
118
|
+
end
|
|
97
119
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
120
|
+
def read_ptr(io)
|
|
121
|
+
@deferred_ptrs.each do |ptr_ref|
|
|
122
|
+
ptr_ref.do_read(io, is_deferred: true)
|
|
123
|
+
end
|
|
124
|
+
@deferred_ptrs.clear
|
|
125
|
+
end
|
|
104
126
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
end
|
|
109
|
-
super
|
|
110
|
-
end
|
|
127
|
+
def is_deferring(obj)
|
|
128
|
+
@deferred_ptrs.any? { |e| e.equal?(obj) }
|
|
129
|
+
end
|
|
111
130
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
end
|
|
131
|
+
def get_top_level_constructed_type
|
|
132
|
+
return self if is_a?(PointerPlugin)
|
|
115
133
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
134
|
+
res = nil
|
|
135
|
+
if parent&.is_a?(ConstructedTypePlugin)
|
|
136
|
+
res = parent.get_top_level_constructed_type
|
|
137
|
+
end
|
|
138
|
+
return res || self
|
|
139
|
+
end
|
|
119
140
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
141
|
+
def do_num_bytes_ptr(struct_offset)
|
|
142
|
+
sum = 0
|
|
143
|
+
@deferred_ptrs.each do |ptr_ref|
|
|
144
|
+
sum += ptr_ref.do_num_bytes(struct_offset + sum, is_deferred: true)
|
|
145
|
+
end
|
|
146
|
+
@deferred_ptrs.clear
|
|
147
|
+
sum
|
|
148
|
+
end
|
|
149
|
+
end
|
|
125
150
|
|
|
126
|
-
private
|
|
127
|
-
|
|
128
|
-
def clamp_to_length(val)
|
|
129
|
-
val = fixed_byte_array(val)
|
|
130
|
-
len = eval_parameter(:length) || val.length
|
|
131
|
-
if val.length > len
|
|
132
|
-
val = val.first(len)
|
|
133
|
-
elsif val.length < len
|
|
134
|
-
pad = eval_parameter(:pad_byte)
|
|
135
|
-
if get_parameter(:pad_front)
|
|
136
|
-
val = val.insert(0, *Array.new(len - val.length, pad))
|
|
137
|
-
else
|
|
138
|
-
val = val.fill(pad, val.length...len)
|
|
139
|
-
end
|
|
140
|
-
end
|
|
141
151
|
|
|
142
|
-
|
|
143
|
-
|
|
152
|
+
#
|
|
153
|
+
# Arrays
|
|
154
|
+
#
|
|
144
155
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
156
|
+
module ArrayClassPlugin
|
|
157
|
+
module ExtendArrayPlugin
|
|
158
|
+
def initialize_shared_instance
|
|
159
|
+
super
|
|
160
|
+
extend ArrayPlugin
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
module ExtendConfPlugin
|
|
164
|
+
def initialize_shared_instance
|
|
165
|
+
super
|
|
166
|
+
extend ConfPlugin
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
module ExtendVarPlugin
|
|
170
|
+
def initialize_shared_instance
|
|
171
|
+
super
|
|
172
|
+
extend VarPlugin
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
def self.extended(target)
|
|
176
|
+
target.default_parameters(
|
|
177
|
+
:read_until => lambda { index == (@obj.read_until_index - 1) },
|
|
178
|
+
# Set :byte_align to 4 bytes by default, which is the size of the `size
|
|
179
|
+
# information` field (:max_count or :offset/:actual_count). If the
|
|
180
|
+
# elements set with :type are greater, this parameter will be
|
|
181
|
+
# updated later in NdrArrayArgProcessor::sanitize_parameters!
|
|
182
|
+
:byte_align => 4
|
|
183
|
+
)
|
|
184
|
+
target.arg_processor :ndr_array
|
|
185
|
+
class_name = target.to_s.split('::').last
|
|
186
|
+
if class_name.include?('NdrVar') || class_name.include?('NdrConfVar')
|
|
187
|
+
target.include ExtendVarPlugin
|
|
188
|
+
end
|
|
189
|
+
if class_name.include?('NdrConf')
|
|
190
|
+
target.include ExtendConfPlugin
|
|
191
|
+
target.extend ConfClassPlugin
|
|
192
|
+
end
|
|
193
|
+
target.include ExtendArrayPlugin
|
|
194
|
+
end
|
|
195
|
+
end
|
|
149
196
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
io.readbytes(len)
|
|
153
|
-
end
|
|
197
|
+
module ArrayPlugin
|
|
198
|
+
include ConstructedTypePlugin
|
|
154
199
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
200
|
+
def align_element_size(offset)
|
|
201
|
+
align = eval_parameter(:type).instance_variable_get(:@obj_params)[:byte_align]
|
|
202
|
+
align ? (align - (offset % align)) % align : 0
|
|
203
|
+
end
|
|
158
204
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
205
|
+
def should_process_max_count?
|
|
206
|
+
# :max_count has already been processed if the parent structure is an
|
|
207
|
+
# NdrStruct, but this is not the case if we are dealing with a pointer
|
|
208
|
+
!parent.is_a?(NdrStruct) || self.is_a?(PointerPlugin)
|
|
209
|
+
end
|
|
162
210
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
end
|
|
211
|
+
def do_write(io)
|
|
212
|
+
if is_a?(ConfPlugin) && should_process_max_count?
|
|
213
|
+
io.writebytes([@max_count].pack('L<'))
|
|
214
|
+
end
|
|
168
215
|
|
|
169
|
-
|
|
216
|
+
if is_a?(VarPlugin)
|
|
217
|
+
io.writebytes([@offset].pack('L<'))
|
|
218
|
+
io.writebytes([@actual_count].pack('L<'))
|
|
219
|
+
end
|
|
170
220
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
221
|
+
unless empty?
|
|
222
|
+
io.writebytes("\x00" * align_element_size(io.offset))
|
|
223
|
+
super
|
|
224
|
+
end
|
|
174
225
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
226
|
+
if has_deferred_ptrs?
|
|
227
|
+
write_ptr(io)
|
|
228
|
+
end
|
|
229
|
+
end
|
|
178
230
|
|
|
179
|
-
|
|
180
|
-
|
|
231
|
+
def has_elements_to_read?
|
|
232
|
+
# When reading a binary stream, the only elements that indicate the array
|
|
233
|
+
# has elements to read are :actual_count, :max_count or :initial_length
|
|
234
|
+
# parameter, depending on the type of NDR Array:
|
|
235
|
+
# 1. When :actual_count is present (NdrVarArray and NdrConfVarArray), it
|
|
236
|
+
# indicates the actual number of elements passed
|
|
237
|
+
# 2. When only :max_count is present (NdrConfArray), we're assuming the
|
|
238
|
+
# maximum number of elements is the actual number of elements in this
|
|
239
|
+
# array
|
|
240
|
+
# 3. None of them are present, but :initial_length parameter has been
|
|
241
|
+
# set, meaning we are delaing with a fixed array without any embedded
|
|
242
|
+
# size information (NdrFixArray)
|
|
243
|
+
(@actual_count&.> 0) ||
|
|
244
|
+
@actual_count.nil? && (@max_count&.> 0) ||
|
|
245
|
+
@actual_count.nil? && @max_count.nil? && (eval_parameter(:initial_length)&.> 0)
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def do_read(io)
|
|
249
|
+
if is_a?(ConfPlugin) && should_process_max_count?
|
|
250
|
+
set_max_count(io.readbytes(4).unpack('L<').first)
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
if is_a?(VarPlugin)
|
|
254
|
+
@offset = io.readbytes(4).unpack('L<').first
|
|
255
|
+
@actual_count = @read_until_index = io.readbytes(4).unpack('L<').first
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
if has_elements_to_read?
|
|
259
|
+
io.seekbytes(align_element_size(io.offset))
|
|
260
|
+
super
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
if has_deferred_ptrs?
|
|
264
|
+
read_ptr(io)
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
def do_num_bytes(struct_offset = 0)
|
|
269
|
+
sum = 0
|
|
270
|
+
|
|
271
|
+
if is_a?(ConfPlugin) && should_process_max_count?
|
|
272
|
+
sum += 4
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
if is_a?(VarPlugin)
|
|
276
|
+
sum += 8
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
unless empty?
|
|
280
|
+
sum += align_element_size(struct_offset + sum)
|
|
281
|
+
sum += super()
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
if has_deferred_ptrs?
|
|
285
|
+
sum += do_num_bytes_ptr(struct_offset + sum)
|
|
286
|
+
end
|
|
287
|
+
sum
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
def sum_num_bytes_below_index(index)
|
|
291
|
+
(0...index).inject(0) do |sum, i|
|
|
292
|
+
nbytes = 0
|
|
293
|
+
if elements[i].has_parameter?(:byte_align) && elements[i].respond_to?(:bytes_to_align)
|
|
294
|
+
nbytes = elements[i].bytes_to_align(elements[i], sum.ceil)
|
|
181
295
|
end
|
|
296
|
+
nbytes += elements[i].do_num_bytes
|
|
182
297
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
""
|
|
188
|
-
end
|
|
298
|
+
if nbytes.is_a?(Integer)
|
|
299
|
+
sum.ceil + nbytes
|
|
300
|
+
else
|
|
301
|
+
sum + nbytes
|
|
189
302
|
end
|
|
190
303
|
end
|
|
304
|
+
end
|
|
305
|
+
end
|
|
191
306
|
|
|
192
|
-
|
|
193
|
-
# [IDL Data Type Declarations - Basic Type Declarations](http://pubs.opengroup.org/onlinepubs/9629399/apdxn.htm#tagcjh_34_01)
|
|
194
|
-
class NdrContextHandle < BinData::Primitive
|
|
195
|
-
endian :little
|
|
307
|
+
module ConfClassPlugin; end
|
|
196
308
|
|
|
197
|
-
|
|
198
|
-
|
|
309
|
+
module ConfPlugin
|
|
310
|
+
attr_accessor :read_until_index, :max_count
|
|
199
311
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
312
|
+
def initialize_instance
|
|
313
|
+
@read_until_index = 0
|
|
314
|
+
@max_count = 0
|
|
315
|
+
super
|
|
316
|
+
end
|
|
203
317
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
318
|
+
def insert(index, *objs)
|
|
319
|
+
obj = super
|
|
320
|
+
@max_count = length
|
|
321
|
+
obj
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
def slice_index(index)
|
|
325
|
+
obj = super
|
|
326
|
+
@max_count = length
|
|
327
|
+
obj
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def []=(index, value)
|
|
331
|
+
obj = super
|
|
332
|
+
@max_count = length
|
|
333
|
+
obj
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
def set_max_count(val)
|
|
337
|
+
@max_count = @read_until_index = val
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
module VarPlugin
|
|
342
|
+
attr_accessor :read_until_index, :actual_count, :offset
|
|
343
|
+
|
|
344
|
+
def initialize_instance
|
|
345
|
+
@read_until_index = 0
|
|
346
|
+
@actual_count = 0
|
|
347
|
+
@offset = 0
|
|
348
|
+
super
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
def insert(index, *objs)
|
|
352
|
+
obj = super
|
|
353
|
+
@actual_count = length
|
|
354
|
+
obj
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
def slice_index(index)
|
|
358
|
+
obj = super
|
|
359
|
+
@actual_count = length
|
|
360
|
+
obj
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
def []=(index, value)
|
|
364
|
+
obj = super
|
|
365
|
+
@actual_count = length
|
|
366
|
+
obj
|
|
367
|
+
end
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
# This ArgProcessor needs to inherit from BinData::ArrayArgProcessor to make
|
|
371
|
+
# sure the ArrayArgProcessor `sanitize_parameters!` is called. This will
|
|
372
|
+
# perform proper Array-related sanity checks on the given parameters.
|
|
373
|
+
class ::BinData::NdrArrayArgProcessor < BinData::ArrayArgProcessor
|
|
374
|
+
def sanitize_parameters!(obj_class, params)
|
|
375
|
+
res = super
|
|
376
|
+
|
|
377
|
+
type_class = params[:type]
|
|
378
|
+
# Let the BinData::Array sanitization routine deal with "no type provided"
|
|
379
|
+
return res unless type_class
|
|
380
|
+
|
|
381
|
+
type_class, type_params = params[:type] if type_class.is_a?(Array)
|
|
382
|
+
if type_class.has_parameter?(:byte_align)
|
|
383
|
+
# According to NDR alignemnt rules for arrays: Array alignment is the
|
|
384
|
+
# largest alignment of the array element type and the size information
|
|
385
|
+
# type, if any.
|
|
386
|
+
# So, here, we pick the greatest value between the size of the `size
|
|
387
|
+
# information` field (:max_count or :offset/:actual_count), which is 4
|
|
388
|
+
# bytes for 32-bit NDR, and the element type size
|
|
389
|
+
byte_align = type_class.instance_variable_get(:@obj_params)[:byte_align]
|
|
390
|
+
if obj_class < NdrFixArray
|
|
391
|
+
# Fixed size arrays doesn't have size information
|
|
392
|
+
params[:byte_align] = byte_align
|
|
393
|
+
else
|
|
394
|
+
params[:byte_align] = [4, byte_align].max
|
|
213
395
|
end
|
|
396
|
+
return res
|
|
397
|
+
elsif type_params&.key?(:byte_align)
|
|
398
|
+
return res
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
raise ArgumentError.new(
|
|
402
|
+
"NDR Arrays must only include elements with the `:byte_align` "\
|
|
403
|
+
"parameter set. This makes sure the whole structure is correctly "\
|
|
404
|
+
"aligned. Use a predefined NDR element instead, or provide the "\
|
|
405
|
+
"`:byte_align` parameter in `:type` (Faulty element type: #{params[:type]})"
|
|
406
|
+
)
|
|
407
|
+
end
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
# [Uni-dimensional Fixed Arrays](https://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_03_01)
|
|
411
|
+
class NdrFixArray < BinData::Array
|
|
412
|
+
arg_processor :ndr_array
|
|
413
|
+
|
|
414
|
+
def initialize_shared_instance
|
|
415
|
+
super
|
|
416
|
+
extend ArrayPlugin
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
def insert(index, *objs)
|
|
420
|
+
fixed_size = get_parameter(:initial_length)
|
|
421
|
+
if (length + objs.size) != fixed_size
|
|
422
|
+
raise ArgumentError, "Can't add new elements to a NdrFixArray (set to #{fixed_size} elements)"
|
|
423
|
+
else
|
|
424
|
+
super
|
|
214
425
|
end
|
|
426
|
+
end
|
|
215
427
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
428
|
+
def append_new_element
|
|
429
|
+
fixed_size = get_parameter(:initial_length)
|
|
430
|
+
raise ArgumentError, "Can't add new elements to a NdrFixArray (set to #{fixed_size} elements)"
|
|
431
|
+
end
|
|
432
|
+
end
|
|
221
433
|
|
|
222
|
-
|
|
434
|
+
# Specific implementation for fixed array of bytes, which can be set from an array of unit8 or a string
|
|
435
|
+
class NdrFixedByteArray < NdrFixArray
|
|
436
|
+
default_parameters(type: :ndr_uint8, byte_align: 1)
|
|
223
437
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
438
|
+
def assign(val)
|
|
439
|
+
val = val.bytes if val.is_a?(String)
|
|
440
|
+
super(val.to_ary)
|
|
441
|
+
end
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
# [Uni-dimensional Conformant Arrays](https://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_03_02)
|
|
445
|
+
class NdrConfArray < BinData::Array
|
|
446
|
+
extend ArrayClassPlugin
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
# [Uni-dimensional Varying Arrays](https://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_03_03)
|
|
450
|
+
class NdrVarArray < BinData::Array
|
|
451
|
+
extend ArrayClassPlugin
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
# Uni-dimensional Conformant-varying Arrays
|
|
455
|
+
class NdrConfVarArray < BinData::Array
|
|
456
|
+
extend ArrayClassPlugin
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
# TODO: Multi-dimensional Arrays
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
#
|
|
464
|
+
# Strings
|
|
465
|
+
#
|
|
466
|
+
|
|
467
|
+
module StringClassPlugin
|
|
468
|
+
module ExtendVarStringPlugin
|
|
469
|
+
def initialize_shared_instance
|
|
470
|
+
super
|
|
471
|
+
extend VarStringPlugin
|
|
472
|
+
end
|
|
473
|
+
end
|
|
474
|
+
module ExtendConfStringPlugin
|
|
475
|
+
def initialize_shared_instance
|
|
476
|
+
super
|
|
477
|
+
extend ConfStringPlugin
|
|
478
|
+
end
|
|
479
|
+
end
|
|
480
|
+
def self.extended(target)
|
|
481
|
+
target.default_parameters byte_align: 4
|
|
482
|
+
char_size = 1
|
|
483
|
+
char_size = 2 if target < RubySMB::Field::String16 || target < RubySMB::Field::Stringz16
|
|
484
|
+
if target < BinData::Stringz
|
|
485
|
+
target.default_parameters(:max_length => lambda { @obj.actual_count * char_size })
|
|
486
|
+
else
|
|
487
|
+
target.default_parameters(:length => lambda { @obj.actual_count * char_size })
|
|
488
|
+
end
|
|
489
|
+
target.include ExtendVarStringPlugin
|
|
490
|
+
class_name = target.to_s.split('::').last
|
|
491
|
+
if class_name.include?('NdrConfVar')
|
|
492
|
+
target.include ExtendConfStringPlugin
|
|
493
|
+
target.extend ConfClassPlugin
|
|
494
|
+
end
|
|
495
|
+
end
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
module ConfStringPlugin
|
|
499
|
+
attr_accessor :max_count
|
|
500
|
+
|
|
501
|
+
def initialize_instance
|
|
502
|
+
@max_count = 0
|
|
503
|
+
if has_parameter?(:initial_value)
|
|
504
|
+
set_max_count(get_max_count(eval_parameter(:initial_value)))
|
|
505
|
+
end
|
|
506
|
+
super
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
def should_process_max_count?
|
|
510
|
+
# :max_count has already been processed if the parent structure is an
|
|
511
|
+
# NdrStruct, but this is not the case if we are dealing with a pointer
|
|
512
|
+
!parent.is_a?(NdrStruct) || self.is_a?(PointerPlugin)
|
|
513
|
+
end
|
|
514
|
+
|
|
515
|
+
def do_write(io)
|
|
516
|
+
if should_process_max_count?
|
|
517
|
+
io.writebytes([@max_count].pack('L<'))
|
|
518
|
+
end
|
|
519
|
+
super
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
def do_read(io)
|
|
523
|
+
if should_process_max_count?
|
|
524
|
+
set_max_count(io.readbytes(4).unpack('L<').first)
|
|
525
|
+
end
|
|
526
|
+
super
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
def assign(val)
|
|
530
|
+
if val.is_a?(ConfStringPlugin)
|
|
531
|
+
@max_count = val.max_count
|
|
532
|
+
else
|
|
533
|
+
set_max_count(get_max_count(val))
|
|
534
|
+
end
|
|
535
|
+
super
|
|
536
|
+
end
|
|
537
|
+
|
|
538
|
+
def do_num_bytes
|
|
539
|
+
sum = 0
|
|
540
|
+
if should_process_max_count?
|
|
541
|
+
# add max_count size
|
|
542
|
+
sum += 4
|
|
543
|
+
end
|
|
544
|
+
sum + super
|
|
545
|
+
end
|
|
546
|
+
|
|
547
|
+
def get_max_count(val)
|
|
548
|
+
if is_a?(BinData::Stringz)
|
|
549
|
+
max_count = val.to_s.strip.length
|
|
550
|
+
# Only count the terminating NULL byte if the string is not empty
|
|
551
|
+
max_count += 1 if max_count > 0
|
|
552
|
+
return max_count
|
|
553
|
+
else
|
|
554
|
+
return val.to_s.length
|
|
555
|
+
end
|
|
556
|
+
end
|
|
557
|
+
|
|
558
|
+
def set_max_count(val)
|
|
559
|
+
@max_count = val
|
|
560
|
+
end
|
|
561
|
+
end
|
|
562
|
+
|
|
563
|
+
module VarStringPlugin
|
|
564
|
+
attr_accessor :actual_count, :offset
|
|
565
|
+
|
|
566
|
+
def initialize_instance
|
|
567
|
+
@offset = 0
|
|
568
|
+
@actual_count = 0
|
|
569
|
+
if has_parameter?(:initial_value)
|
|
570
|
+
update_actual_count(eval_parameter(:initial_value))
|
|
571
|
+
end
|
|
572
|
+
super
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
def do_write(io)
|
|
576
|
+
io.writebytes([@offset].pack('L<'))
|
|
577
|
+
io.writebytes([@actual_count].pack('L<'))
|
|
578
|
+
super if @actual_count > 0
|
|
579
|
+
end
|
|
580
|
+
|
|
581
|
+
def do_read(io)
|
|
582
|
+
@offset = io.readbytes(4).unpack('L<').first
|
|
583
|
+
@actual_count = io.readbytes(4).unpack('L<').first
|
|
584
|
+
super if @actual_count > 0
|
|
585
|
+
end
|
|
586
|
+
|
|
587
|
+
def assign(val)
|
|
588
|
+
update_actual_count(val)
|
|
589
|
+
super
|
|
590
|
+
end
|
|
591
|
+
|
|
592
|
+
def do_num_bytes
|
|
593
|
+
@actual_count > 0 ? (8 + super) : 8
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
def update_actual_count(val)
|
|
597
|
+
if is_a?(BinData::Stringz)
|
|
598
|
+
@actual_count = val.to_s.strip.length
|
|
599
|
+
# Only count the terminating NULL byte if the string is not empty
|
|
600
|
+
@actual_count += 1 if @actual_count > 0
|
|
601
|
+
else
|
|
602
|
+
@actual_count = val.to_s.length
|
|
603
|
+
end
|
|
604
|
+
end
|
|
605
|
+
end
|
|
606
|
+
|
|
607
|
+
# [Varying Strings](https://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_04_01)
|
|
608
|
+
class NdrVarString < BinData::String
|
|
609
|
+
extend StringClassPlugin
|
|
610
|
+
end
|
|
611
|
+
|
|
612
|
+
class NdrVarStringz < BinData::Stringz
|
|
613
|
+
extend StringClassPlugin
|
|
614
|
+
end
|
|
615
|
+
|
|
616
|
+
class NdrVarWideString < RubySMB::Field::String16
|
|
617
|
+
extend StringClassPlugin
|
|
618
|
+
end
|
|
619
|
+
|
|
620
|
+
class NdrVarWideStringz < RubySMB::Field::Stringz16
|
|
621
|
+
extend StringClassPlugin
|
|
622
|
+
end
|
|
623
|
+
|
|
624
|
+
# [Conformant and Varying Strings](https://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_04_02)
|
|
625
|
+
class NdrConfVarString < BinData::String
|
|
626
|
+
extend StringClassPlugin
|
|
627
|
+
end
|
|
628
|
+
|
|
629
|
+
class NdrConfVarStringz < BinData::Stringz
|
|
630
|
+
extend StringClassPlugin
|
|
631
|
+
end
|
|
632
|
+
|
|
633
|
+
class NdrConfVarWideString < RubySMB::Field::String16
|
|
634
|
+
extend StringClassPlugin
|
|
635
|
+
end
|
|
636
|
+
|
|
637
|
+
class NdrConfVarWideStringz < RubySMB::Field::Stringz16
|
|
638
|
+
extend StringClassPlugin
|
|
639
|
+
end
|
|
640
|
+
|
|
641
|
+
# TODO:[Arrays of Strings](https://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_05)
|
|
642
|
+
# Microsoft DCERPC uses array of pointers for strings. I couldn't find any reference to array of strings.
|
|
643
|
+
|
|
644
|
+
|
|
645
|
+
#
|
|
646
|
+
# [Structures](https://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_06)
|
|
647
|
+
#
|
|
648
|
+
|
|
649
|
+
module StructPlugin
|
|
650
|
+
include ConstructedTypePlugin
|
|
651
|
+
|
|
652
|
+
def should_process_max_count?
|
|
653
|
+
# According to the NDR defintion for Structures Containing a Conformant
|
|
654
|
+
# Array:
|
|
655
|
+
#
|
|
656
|
+
# "In the NDR representation of a structure that contains a
|
|
657
|
+
# conformant array, the unsigned long integers that give maximum element
|
|
658
|
+
# counts for dimensions of the array are moved to the beginning of the
|
|
659
|
+
# structure, and the array elements appear in place at the end of the
|
|
660
|
+
# structure. If a structure that contains a conformant array itself a
|
|
661
|
+
# member of another structure, the maximum element counts are further
|
|
662
|
+
# moved to the beginning of the containing structure. This construction
|
|
663
|
+
# iterates through all enclosing structures."
|
|
664
|
+
#
|
|
665
|
+
# This only applies if the current object is the top level structure (no
|
|
666
|
+
# parent). Note that if it is a pointer to a structure and the current
|
|
667
|
+
# object is being deferred, :max_count still need to be processed since
|
|
668
|
+
# it had not been moved to the beginning of the parent structure.
|
|
669
|
+
klass = is_a?(PointerPlugin) ? self.class.superclass : self.class
|
|
670
|
+
parent_obj = nil
|
|
671
|
+
# TODO: possible issue: parent can be a BinData::Choice, and won't be
|
|
672
|
+
# detected as a ConstructedTypePlugin, even if the embeding structure is.
|
|
673
|
+
# Check this with a BinData::Choice that points to a structure embedding
|
|
674
|
+
# a conformant structure
|
|
675
|
+
if parent&.is_a?(ConstructedTypePlugin)
|
|
676
|
+
parent_obj = parent.get_top_level_constructed_type
|
|
677
|
+
end
|
|
678
|
+
klass.has_conformant_array && (parent_obj.nil? || parent_obj.is_deferring(self))
|
|
679
|
+
end
|
|
680
|
+
|
|
681
|
+
def do_write(io)
|
|
682
|
+
if should_process_max_count?
|
|
683
|
+
max_count = retrieve_max_count
|
|
684
|
+
io.writebytes([max_count].pack('L<')) if max_count
|
|
685
|
+
|
|
686
|
+
# Align the structure according to the alignment rules for the structure
|
|
687
|
+
if respond_to?(:referent_bytes_align)
|
|
688
|
+
io.writebytes("\x00" * referent_bytes_align(io.offset))
|
|
689
|
+
elsif has_parameter?(:byte_align)
|
|
690
|
+
io.writebytes("\x00" * bytes_to_align(self, io.offset))
|
|
229
691
|
end
|
|
692
|
+
end
|
|
230
693
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
694
|
+
super
|
|
695
|
+
|
|
696
|
+
if has_deferred_ptrs?
|
|
697
|
+
write_ptr(io)
|
|
698
|
+
end
|
|
699
|
+
end
|
|
700
|
+
|
|
701
|
+
def do_read(io)
|
|
702
|
+
if should_process_max_count?
|
|
703
|
+
set_max_count(io.readbytes(4).unpack('L<').first)
|
|
704
|
+
|
|
705
|
+
# Align the structure according to the alignment rules for the structure
|
|
706
|
+
if respond_to?(:referent_bytes_align)
|
|
707
|
+
io.seekbytes(referent_bytes_align(io.offset))
|
|
708
|
+
elsif has_parameter?(:byte_align)
|
|
709
|
+
io.seekbytes(bytes_to_align(self, io.offset))
|
|
236
710
|
end
|
|
711
|
+
end
|
|
237
712
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
713
|
+
super
|
|
714
|
+
|
|
715
|
+
if has_deferred_ptrs?
|
|
716
|
+
read_ptr(io)
|
|
717
|
+
end
|
|
718
|
+
end
|
|
719
|
+
|
|
720
|
+
def retrieve_max_count
|
|
721
|
+
obj = self[field_names.last]
|
|
722
|
+
return obj.length if obj.is_a?(ConfPlugin)
|
|
723
|
+
return obj.get_max_count(obj) if obj.is_a?(ConfStringPlugin)
|
|
724
|
+
if obj.is_a?(NdrStruct)
|
|
725
|
+
return obj.retrieve_max_count
|
|
726
|
+
end
|
|
727
|
+
end
|
|
728
|
+
|
|
729
|
+
def set_max_count(val)
|
|
730
|
+
obj = self[field_names.last]
|
|
731
|
+
obj.set_max_count(val)
|
|
732
|
+
end
|
|
733
|
+
|
|
734
|
+
def do_num_bytes
|
|
735
|
+
sum = 0
|
|
736
|
+
|
|
737
|
+
if should_process_max_count?
|
|
738
|
+
# count max_count (4 bytes)
|
|
739
|
+
max_count = retrieve_max_count
|
|
740
|
+
sum += 4 if max_count
|
|
741
|
+
|
|
742
|
+
if respond_to?(:referent_bytes_align)
|
|
743
|
+
sum += referent_bytes_align(sum)
|
|
744
|
+
elsif has_parameter?(:byte_align)
|
|
745
|
+
sum += bytes_to_align(self, sum)
|
|
250
746
|
end
|
|
747
|
+
end
|
|
251
748
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
749
|
+
sum += super
|
|
750
|
+
|
|
751
|
+
if has_deferred_ptrs?
|
|
752
|
+
sum += do_num_bytes_ptr(sum)
|
|
753
|
+
end
|
|
754
|
+
|
|
755
|
+
sum
|
|
756
|
+
end
|
|
757
|
+
|
|
758
|
+
def bytes_to_align(obj, rel_offset)
|
|
759
|
+
if obj.is_a?(PointerPlugin)
|
|
760
|
+
# Pointers are always 4-bytes aligned
|
|
761
|
+
return (4 - (rel_offset % 4)) % 4
|
|
762
|
+
end
|
|
763
|
+
if obj.is_a?(ConfPlugin)
|
|
764
|
+
# `max_count` should have been handled at the begining of the structure
|
|
765
|
+
# already. We need to fix `rel_offset` since it includes the
|
|
766
|
+
# `max_count` 4 bytes, plus the possible padding bytes needed to align
|
|
767
|
+
# the structure. This is required because BinData Struct is not
|
|
768
|
+
# aware of `max_count` and considere the first field to be the begining
|
|
769
|
+
# of the structure instead. We have to make sure the alignment is
|
|
770
|
+
# calculated from the begining of the structure.
|
|
771
|
+
align = eval_parameter(:byte_align)
|
|
772
|
+
pad_length = (align - (4 % align)) % align
|
|
773
|
+
rel_offset += (4 + pad_length)
|
|
774
|
+
|
|
775
|
+
# We need to handle another corner case, which is a Conformant array
|
|
776
|
+
# (not Varying). The size information (max_count) has been place in
|
|
777
|
+
# from of the structure and no other size information is present before
|
|
778
|
+
# the actual elements of the array. Therefore, the alignment must be
|
|
779
|
+
# done accroding to th rules of the elements. Since a NdrArray has its
|
|
780
|
+
# default :byte_align value set to 4 (:max_count size), we have to make
|
|
781
|
+
# sure the element size is used instead.
|
|
782
|
+
unless obj.is_a?(VarPlugin)
|
|
783
|
+
return obj.align_element_size(rel_offset)
|
|
258
784
|
end
|
|
785
|
+
end
|
|
786
|
+
is_a?(BinData::ByteAlignPlugin) ? super : 0
|
|
259
787
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
788
|
+
end
|
|
789
|
+
end
|
|
790
|
+
|
|
791
|
+
class NdrStruct < BinData::Record
|
|
792
|
+
# Caller must specify :byte_align according to the type of the largest element in the structure.
|
|
793
|
+
# See https://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_02
|
|
794
|
+
#
|
|
795
|
+
# "The alignment of a structure in the octet stream is the largest of the
|
|
796
|
+
# alignments of the fields it contains. These fields may also be
|
|
797
|
+
# constructed types. The same alignment rules apply recursively to nested
|
|
798
|
+
# constructed types."
|
|
799
|
+
mandatory_parameters(:byte_align)
|
|
800
|
+
|
|
801
|
+
class << self; attr_reader :has_conformant_array end
|
|
802
|
+
|
|
803
|
+
def self.validate_conformant_array(field)
|
|
804
|
+
if @has_conformant_array
|
|
805
|
+
raise ArgumentError.new(
|
|
806
|
+
"Invalid structure #{self}: Conformant array or embedded structure "\
|
|
807
|
+
"with Conformant array must be the last member of the structure"
|
|
808
|
+
)
|
|
809
|
+
end
|
|
810
|
+
obj_proto = field.last.prototype
|
|
811
|
+
obj_class = obj_proto.instance_variable_get(:@obj_class)
|
|
812
|
+
@has_conformant_array = true if obj_class < NdrStruct && obj_class.has_conformant_array
|
|
813
|
+
if obj_class.is_a?(ConfClassPlugin) && !obj_class.is_a?(PointerClassPlugin)
|
|
814
|
+
@has_conformant_array = true
|
|
815
|
+
end
|
|
816
|
+
end
|
|
817
|
+
|
|
818
|
+
def self.method_missing(symbol, *args, &block)
|
|
819
|
+
field = super
|
|
820
|
+
if field.is_a?(::Array) && field.last.is_a?(BinData::SanitizedField)
|
|
821
|
+
unless field.last.has_parameter?(:byte_align) || field.last.instantiate.bit_aligned?
|
|
822
|
+
raise ArgumentError.new(
|
|
823
|
+
"NDR Structures must only include elements with the `:byte_align` "\
|
|
824
|
+
"parameter set. This makes sure the whole structure is correctly "\
|
|
825
|
+
"aligned. Use a predefined NDR element instead, or provide the "\
|
|
826
|
+
"`:byte_align` parameter when defining the structure "\
|
|
827
|
+
"(Faulty element: #{field.last.name})"
|
|
828
|
+
)
|
|
267
829
|
end
|
|
830
|
+
validate_conformant_array(field)
|
|
268
831
|
end
|
|
832
|
+
field
|
|
833
|
+
end
|
|
834
|
+
|
|
835
|
+
def initialize_shared_instance
|
|
836
|
+
super
|
|
837
|
+
extend StructPlugin
|
|
838
|
+
end
|
|
839
|
+
end
|
|
840
|
+
|
|
841
|
+
# TODO: Unions
|
|
842
|
+
# TODO: Pipes
|
|
269
843
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
844
|
+
#
|
|
845
|
+
# [Pointers](https://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_10)
|
|
846
|
+
#
|
|
273
847
|
|
|
274
|
-
|
|
848
|
+
module PointerClassPlugin
|
|
849
|
+
module ExtendPointerPlugin
|
|
850
|
+
def initialize_shared_instance
|
|
851
|
+
super
|
|
852
|
+
extend PointerPlugin
|
|
275
853
|
end
|
|
854
|
+
end
|
|
855
|
+
def self.extended(target)
|
|
856
|
+
target.default_parameters byte_align: 4
|
|
857
|
+
target.arg_processor :ndr_pointer
|
|
858
|
+
target.include ExtendPointerPlugin
|
|
859
|
+
end
|
|
860
|
+
end
|
|
276
861
|
|
|
277
|
-
class NdrLpDword < NdrPointer
|
|
278
|
-
endian :little
|
|
279
862
|
|
|
280
|
-
|
|
863
|
+
module TopLevelPlugin
|
|
864
|
+
module TopLevelClassMethods
|
|
865
|
+
def pos
|
|
866
|
+
@@pos
|
|
867
|
+
end
|
|
868
|
+
def increment_pos
|
|
869
|
+
@@pos += 1
|
|
281
870
|
end
|
|
871
|
+
def reset_pos
|
|
872
|
+
@@pos = 0
|
|
873
|
+
end
|
|
874
|
+
end
|
|
282
875
|
|
|
283
|
-
|
|
284
|
-
class
|
|
285
|
-
|
|
876
|
+
def self.extended(target)
|
|
877
|
+
target.class.extend(TopLevelClassMethods)
|
|
878
|
+
target.class.reset_pos
|
|
879
|
+
end
|
|
286
880
|
|
|
287
|
-
|
|
881
|
+
def initialize_instance
|
|
882
|
+
super
|
|
883
|
+
@standalone_ptr = false
|
|
884
|
+
end
|
|
288
885
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
886
|
+
def do_write(io, is_deferred: false)
|
|
887
|
+
# If for whatever reasons, the #pos value has been modified, reset it to
|
|
888
|
+
# make sure the pointer ref_id will start from INITIAL_REF_ID
|
|
889
|
+
self.class.reset_pos if is_top_level_ptr || @standalone_ptr
|
|
890
|
+
if is_deferred
|
|
891
|
+
super(io, is_deferred: is_deferred)
|
|
892
|
+
else
|
|
893
|
+
super(io)
|
|
894
|
+
end
|
|
895
|
+
# Since #pos has been incremented for each embedded pointer, let's reset
|
|
896
|
+
# it to go back to its initial state
|
|
897
|
+
self.class.reset_pos if is_top_level_ptr || @standalone_ptr
|
|
898
|
+
end
|
|
899
|
+
|
|
900
|
+
def to_binary_s
|
|
901
|
+
@standalone_ptr = true
|
|
902
|
+
res = super
|
|
903
|
+
@standalone_ptr = false
|
|
904
|
+
res
|
|
905
|
+
end
|
|
906
|
+
|
|
907
|
+
def set_top_level_ptr
|
|
908
|
+
@top_level_ptr = true
|
|
909
|
+
#update_ref_ids
|
|
910
|
+
end
|
|
911
|
+
|
|
912
|
+
def unset_top_level_ptr
|
|
913
|
+
@top_level_ptr = false
|
|
914
|
+
end
|
|
915
|
+
|
|
916
|
+
def is_top_level_ptr
|
|
917
|
+
!!@top_level_ptr
|
|
918
|
+
end
|
|
919
|
+
|
|
920
|
+
def num_bytes
|
|
921
|
+
@standalone_ptr = true
|
|
922
|
+
res = super
|
|
923
|
+
@standalone_ptr = false
|
|
924
|
+
res
|
|
925
|
+
end
|
|
926
|
+
end
|
|
927
|
+
|
|
928
|
+
# Windows SMB client uses 0x00020000 as an initial reference ID, but it is
|
|
929
|
+
# rejected by the server on the Windows Server 2003. On this version, only
|
|
930
|
+
# 0x00000001 seems to be accepted. So, we need to use this value to maintain
|
|
931
|
+
# compatibility.
|
|
932
|
+
INITIAL_REF_ID = 0x00000001
|
|
933
|
+
|
|
934
|
+
module PointerPlugin
|
|
935
|
+
attr_accessor :ref_id
|
|
936
|
+
|
|
937
|
+
def initialize_instance
|
|
938
|
+
if @ref_id.nil?
|
|
939
|
+
if eval_parameter(:initial_value)
|
|
940
|
+
instantiate_referent
|
|
941
|
+
else
|
|
942
|
+
@ref_id = 0
|
|
295
943
|
end
|
|
296
944
|
end
|
|
945
|
+
extend_top_level_class
|
|
946
|
+
super
|
|
947
|
+
end
|
|
297
948
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
class NdrStruct < BinData::Record
|
|
309
|
-
|
|
310
|
-
def do_read(io)
|
|
311
|
-
super(io)
|
|
312
|
-
each_pair do |_name, field|
|
|
313
|
-
case field
|
|
314
|
-
when BinData::Array
|
|
315
|
-
field.each do |element|
|
|
316
|
-
next unless element.is_a?(NdrPointer)
|
|
317
|
-
next if element.referent_id == 0
|
|
318
|
-
pad = (4 - io.offset % 4) % 4
|
|
319
|
-
io.seekbytes(pad) if pad > 0
|
|
320
|
-
element.referent.do_read(io)
|
|
321
|
-
end
|
|
322
|
-
when NdrPointer
|
|
323
|
-
next if field.referent_id == 0
|
|
324
|
-
pad = (4 - io.offset % 4) % 4
|
|
325
|
-
io.seekbytes(pad) if pad > 0
|
|
326
|
-
field.referent.do_read(io)
|
|
327
|
-
end
|
|
328
|
-
end
|
|
949
|
+
def extend_top_level_class
|
|
950
|
+
current = self
|
|
951
|
+
loop do
|
|
952
|
+
current.extend(TopLevelPlugin) unless current.is_a?(TopLevelPlugin)
|
|
953
|
+
if current.parent.nil?
|
|
954
|
+
current.set_top_level_ptr unless current.is_top_level_ptr
|
|
955
|
+
break
|
|
956
|
+
else
|
|
957
|
+
current.unset_top_level_ptr if current.is_top_level_ptr
|
|
958
|
+
current = current.parent
|
|
329
959
|
end
|
|
960
|
+
end
|
|
961
|
+
end
|
|
962
|
+
|
|
963
|
+
def snapshot
|
|
964
|
+
if is_alias?
|
|
965
|
+
fetch_alias_referent
|
|
966
|
+
elsif is_null_ptr? && !eval_parameter(:initial_value)
|
|
967
|
+
:null
|
|
968
|
+
else
|
|
969
|
+
super
|
|
970
|
+
end
|
|
971
|
+
end
|
|
972
|
+
|
|
973
|
+
def referent_bytes_align(offset)
|
|
974
|
+
align = self.class.superclass.default_parameters[:byte_align]
|
|
975
|
+
align = eval_parameter(:referent_byte_align) unless align
|
|
976
|
+
(align - (offset % align)) % align
|
|
977
|
+
end
|
|
330
978
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
next unless element.is_a?(NdrPointer)
|
|
338
|
-
next if element.referent_id == 0
|
|
339
|
-
pad = (4 - io.offset % 4) % 4
|
|
340
|
-
io.writebytes("\x00" * pad + element.referent.to_binary_s)
|
|
341
|
-
end
|
|
342
|
-
when NdrPointer
|
|
343
|
-
next if field.referent_id == 0
|
|
344
|
-
pad = (4 - io.offset % 4) % 4
|
|
345
|
-
io.writebytes("\x00" * pad + field.referent.to_binary_s)
|
|
346
|
-
end
|
|
979
|
+
def write_ref_id(io)
|
|
980
|
+
if is_alias?
|
|
981
|
+
ref_field = fetch_alias_referent
|
|
982
|
+
if ref_field
|
|
983
|
+
if ref_field.class != self.class
|
|
984
|
+
raise ArgumentError, "Pointer points to a different referent type: #{ref_field.class} (set to #{obj.class})"
|
|
347
985
|
end
|
|
986
|
+
@ref_id = ref_field.ref_id
|
|
987
|
+
end
|
|
988
|
+
elsif @ref_id != 0 || (is_null_ptr? && eval_parameter(:initial_value))
|
|
989
|
+
@ref_id = INITIAL_REF_ID + self.class.pos
|
|
990
|
+
self.class.increment_pos unless @standalone_ptr
|
|
991
|
+
end
|
|
992
|
+
io.writebytes([@ref_id].pack('L<'))
|
|
993
|
+
end
|
|
994
|
+
|
|
995
|
+
def do_write(io, is_deferred: false)
|
|
996
|
+
if is_deferred
|
|
997
|
+
if is_a?(NdrStruct) && self.class.superclass.has_conformant_array
|
|
998
|
+
# align :max_count since it will be placed in front of the structure.
|
|
999
|
+
# The structure itself will be properly aligned later.
|
|
1000
|
+
align = (4 - (io.offset % 4)) % 4
|
|
1001
|
+
io.writebytes("\x00" * align)
|
|
1002
|
+
else
|
|
1003
|
+
io.writebytes("\x00" * referent_bytes_align(io.offset))
|
|
1004
|
+
end
|
|
1005
|
+
else
|
|
1006
|
+
write_ref_id(io)
|
|
1007
|
+
parent_obj = nil
|
|
1008
|
+
if parent&.is_a?(ConstructedTypePlugin)
|
|
1009
|
+
parent_obj = parent.get_top_level_constructed_type
|
|
1010
|
+
end
|
|
1011
|
+
if parent_obj && @ref_id != 0 && !@standalone_ptr
|
|
1012
|
+
parent_obj.defer_ptr(self)
|
|
1013
|
+
return
|
|
1014
|
+
end
|
|
1015
|
+
end
|
|
1016
|
+
super(io) unless (is_null_ptr? && !eval_parameter(:initial_value)) || is_alias?
|
|
1017
|
+
end
|
|
1018
|
+
|
|
1019
|
+
def do_read(io, is_deferred: false)
|
|
1020
|
+
if is_deferred
|
|
1021
|
+
if is_a?(NdrStruct) && self.class.superclass.has_conformant_array
|
|
1022
|
+
# align :max_count since it will be placed in front of the structure.
|
|
1023
|
+
# The structure itself will be properly aligned later.
|
|
1024
|
+
align = (4 - (io.offset % 4)) % 4
|
|
1025
|
+
io.seekbytes(align)
|
|
1026
|
+
else
|
|
1027
|
+
io.seekbytes(referent_bytes_align(io.offset))
|
|
1028
|
+
end
|
|
1029
|
+
else
|
|
1030
|
+
@ref_id = io.readbytes(4).unpack('L<').first
|
|
1031
|
+
parent_obj = nil
|
|
1032
|
+
if parent&.is_a?(ConstructedTypePlugin)
|
|
1033
|
+
parent_obj = parent.get_top_level_constructed_type
|
|
1034
|
+
end
|
|
1035
|
+
if parent_obj && @ref_id != 0
|
|
1036
|
+
parent_obj.defer_ptr(self)
|
|
1037
|
+
return
|
|
348
1038
|
end
|
|
349
1039
|
end
|
|
1040
|
+
super(io) unless is_null_ptr? || is_alias?
|
|
1041
|
+
end
|
|
350
1042
|
|
|
351
|
-
|
|
352
|
-
|
|
1043
|
+
def assign(val)
|
|
1044
|
+
if val == :null
|
|
1045
|
+
@ref_id = 0
|
|
1046
|
+
elsif is_alias?
|
|
1047
|
+
ref_field = fetch_alias_referent
|
|
1048
|
+
raise ArgumentError, "Referent of alias pointer does not exist: #{get_parameter(:ref_to)}" unless ref_field
|
|
1049
|
+
ref_field.assign(val)
|
|
1050
|
+
else
|
|
1051
|
+
instantiate_referent if is_null_ptr?
|
|
1052
|
+
super
|
|
1053
|
+
end
|
|
1054
|
+
end
|
|
353
1055
|
|
|
354
|
-
|
|
355
|
-
|
|
1056
|
+
def is_alias?
|
|
1057
|
+
has_parameter?(:ref_to)
|
|
1058
|
+
end
|
|
356
1059
|
|
|
357
|
-
|
|
358
|
-
|
|
1060
|
+
def fetch_alias_referent(current: parent, ref: get_parameter(:ref_to), name: nil)
|
|
1061
|
+
return if current.nil?
|
|
1062
|
+
if current.get_parameter(:ref_to) == ref
|
|
1063
|
+
raise ArgumentError.new(
|
|
1064
|
+
"Pointer alias refering to #{ref} cannot be found. This referent "\
|
|
1065
|
+
"should appears before the alias in the stream"
|
|
1066
|
+
)
|
|
1067
|
+
end
|
|
1068
|
+
return current if name == ref
|
|
1069
|
+
res = nil
|
|
1070
|
+
case current
|
|
1071
|
+
when ArrayPlugin
|
|
1072
|
+
current.each do |element|
|
|
1073
|
+
res = fetch_alias_referent(current: element, ref: ref, name: name)
|
|
1074
|
+
break if res
|
|
1075
|
+
end
|
|
1076
|
+
when BinData::Record, BinData::Struct
|
|
1077
|
+
current.each_pair do |name, field|
|
|
1078
|
+
res = fetch_alias_referent(current: field, ref: ref, name: name)
|
|
1079
|
+
break if res
|
|
359
1080
|
end
|
|
1081
|
+
end
|
|
1082
|
+
return res
|
|
1083
|
+
end
|
|
360
1084
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
1085
|
+
def do_num_bytes(struct_offset = 0, is_deferred: false)
|
|
1086
|
+
sum = 0
|
|
1087
|
+
if is_deferred
|
|
1088
|
+
if is_a?(NdrStruct) && self.class.superclass.has_conformant_array
|
|
1089
|
+
# align :max_count since it will be placed in front of the structure.
|
|
1090
|
+
# The structure itself will be properly aligned later.
|
|
1091
|
+
align = (4 - (struct_offset % 4)) % 4
|
|
1092
|
+
sum += align
|
|
1093
|
+
else
|
|
1094
|
+
sum += referent_bytes_align(struct_offset)
|
|
365
1095
|
end
|
|
1096
|
+
else
|
|
1097
|
+
# add ref_id size
|
|
1098
|
+
sum += 4
|
|
366
1099
|
|
|
367
|
-
|
|
368
|
-
|
|
1100
|
+
parent_obj = nil
|
|
1101
|
+
if parent&.is_a?(ConstructedTypePlugin)
|
|
1102
|
+
parent_obj = parent.get_top_level_constructed_type
|
|
1103
|
+
end
|
|
1104
|
+
if parent_obj && @ref_id != 0 && !@standalone_ptr
|
|
1105
|
+
parent_obj.defer_ptr(self)
|
|
1106
|
+
# only return ref_id size, the actual referent size will be added later
|
|
1107
|
+
return sum
|
|
1108
|
+
end
|
|
1109
|
+
end
|
|
1110
|
+
unless (is_null_ptr? && !eval_parameter(:initial_value)) || is_alias?
|
|
1111
|
+
if is_a?(ArrayPlugin)
|
|
1112
|
+
sum += super(struct_offset + sum)
|
|
1113
|
+
else
|
|
1114
|
+
sum += super()
|
|
369
1115
|
end
|
|
370
1116
|
end
|
|
371
1117
|
|
|
372
|
-
|
|
373
|
-
|
|
1118
|
+
sum
|
|
1119
|
+
end
|
|
374
1120
|
|
|
375
|
-
|
|
1121
|
+
def instantiate_referent
|
|
1122
|
+
@ref_id = INITIAL_REF_ID
|
|
1123
|
+
end
|
|
376
1124
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
1125
|
+
def is_null_ptr?
|
|
1126
|
+
@ref_id == 0
|
|
1127
|
+
end
|
|
1128
|
+
|
|
1129
|
+
def insert(index, *objs)
|
|
1130
|
+
obj = super
|
|
1131
|
+
# If we just pushed a new element and it was a null pointer (ref_id==0),
|
|
1132
|
+
# we will initialize the ref_id to make sure it is not considered a null
|
|
1133
|
+
# pointer anymore
|
|
1134
|
+
if is_null_ptr? && is_a?(BinData::Array) && !empty?
|
|
1135
|
+
instantiate_referent
|
|
1136
|
+
end
|
|
1137
|
+
obj
|
|
1138
|
+
end
|
|
1139
|
+
end
|
|
1140
|
+
|
|
1141
|
+
class ::BinData::NdrPointerArgProcessor < BinData::BaseArgProcessor
|
|
1142
|
+
def sanitize_parameters!(obj_class, params)
|
|
1143
|
+
obj_klass = obj_class
|
|
1144
|
+
obj_klass = obj_class.superclass if obj_class.superclass.arg_processor == self
|
|
1145
|
+
res = obj_class.superclass.arg_processor.sanitize_parameters!(obj_klass, params)
|
|
1146
|
+
|
|
1147
|
+
return res if obj_class.superclass.default_parameters[:byte_align]
|
|
1148
|
+
return res if params[:referent_byte_align]
|
|
1149
|
+
|
|
1150
|
+
raise ArgumentError.new(
|
|
1151
|
+
"NDR Pointers referent must have `:byte_align` parameter set. This "\
|
|
1152
|
+
"makes sure the whole structure is correctly aligned. Use a predefined "\
|
|
1153
|
+
"NDR element instead, or provide the `:referent_byte_align` parameter "\
|
|
1154
|
+
"when defining the structure (Faulty pointer class: #{obj_class})"
|
|
1155
|
+
)
|
|
1156
|
+
end
|
|
1157
|
+
|
|
1158
|
+
def extract_args(obj_class, obj_args)
|
|
1159
|
+
obj_class = obj_class.superclass if obj_class.superclass.arg_processor == self
|
|
1160
|
+
obj_class.superclass.arg_processor.extract_args(obj_class, obj_args)
|
|
1161
|
+
end
|
|
1162
|
+
end
|
|
1163
|
+
|
|
1164
|
+
# Pointers to NDR integers. This defined four pointers:
|
|
1165
|
+
# - NdrUint8Ptr
|
|
1166
|
+
# - NdrUint16Ptr
|
|
1167
|
+
# - NdrUint32Ptr
|
|
1168
|
+
# - NdrUint64Ptr
|
|
1169
|
+
{NdrUint8: 1, NdrUint16: 2, NdrUint32: 4, NdrUint64: 8}.each do |klass, align|
|
|
1170
|
+
new_klass_name = "#{klass.to_s}Ptr"
|
|
1171
|
+
unless self.const_defined?(new_klass_name)
|
|
1172
|
+
new_klass = Class.new(const_get(klass)) do
|
|
1173
|
+
extend PointerClassPlugin
|
|
1174
|
+
end
|
|
1175
|
+
self.const_set(new_klass_name, new_klass)
|
|
1176
|
+
BinData::RegisteredClasses.register(new_klass_name, new_klass)
|
|
1177
|
+
end
|
|
1178
|
+
end
|
|
1179
|
+
|
|
1180
|
+
# Pointers to other classes
|
|
1181
|
+
class NdrCharPtr < NdrChar
|
|
1182
|
+
extend PointerClassPlugin
|
|
1183
|
+
end
|
|
1184
|
+
|
|
1185
|
+
class NdrBooleanPtr < NdrBoolean
|
|
1186
|
+
extend PointerClassPlugin
|
|
1187
|
+
end
|
|
1188
|
+
|
|
1189
|
+
class NdrStringPtr < NdrConfVarString
|
|
1190
|
+
extend PointerClassPlugin
|
|
1191
|
+
end
|
|
1192
|
+
|
|
1193
|
+
class NdrStringzPtr < NdrConfVarStringz
|
|
1194
|
+
extend PointerClassPlugin
|
|
1195
|
+
end
|
|
1196
|
+
|
|
1197
|
+
class NdrWideStringPtr < NdrConfVarWideString
|
|
1198
|
+
extend PointerClassPlugin
|
|
1199
|
+
end
|
|
1200
|
+
|
|
1201
|
+
class NdrWideStringzPtr < NdrConfVarWideStringz
|
|
1202
|
+
extend PointerClassPlugin
|
|
1203
|
+
end
|
|
1204
|
+
|
|
1205
|
+
class NdrByteArrayPtr < NdrConfVarArray
|
|
1206
|
+
default_parameters type: :ndr_uint8
|
|
1207
|
+
extend PointerClassPlugin
|
|
1208
|
+
end
|
|
1209
|
+
|
|
1210
|
+
class NdrFileTimePtr < NdrFileTime
|
|
1211
|
+
extend PointerClassPlugin
|
|
1212
|
+
end
|
|
1213
|
+
|
|
1214
|
+
class UuidPtr < RubySMB::Dcerpc::Uuid
|
|
1215
|
+
default_parameter referent_byte_align: 4
|
|
1216
|
+
extend PointerClassPlugin
|
|
1217
|
+
end
|
|
1218
|
+
|
|
1219
|
+
# An NDR Context Handle representation as defined in
|
|
1220
|
+
# [IDL Data Type Declarations - Basic Type Declarations](http://pubs.opengroup.org/onlinepubs/9629399/apdxn.htm#tagcjh_34_01)
|
|
1221
|
+
class NdrContextHandle < BinData::Primitive
|
|
1222
|
+
default_parameters byte_align: 4
|
|
1223
|
+
endian :little
|
|
1224
|
+
|
|
1225
|
+
uint32 :context_handle_attributes
|
|
1226
|
+
uuid :context_handle_uuid
|
|
1227
|
+
|
|
1228
|
+
def get
|
|
1229
|
+
{:context_handle_attributes => context_handle_attributes, :context_handle_uuid => context_handle_uuid}
|
|
1230
|
+
end
|
|
1231
|
+
|
|
1232
|
+
def set(handle)
|
|
1233
|
+
if handle.is_a?(Hash)
|
|
1234
|
+
self.context_handle_attributes = handle[:context_handle_attributes]
|
|
1235
|
+
self.context_handle_uuid = handle[:context_handle_uuid]
|
|
1236
|
+
elsif handle.is_a?(NdrContextHandle)
|
|
1237
|
+
read(handle.to_binary_s)
|
|
1238
|
+
else
|
|
1239
|
+
read(handle.to_s)
|
|
380
1240
|
end
|
|
381
1241
|
end
|
|
382
1242
|
end
|
|
1243
|
+
|
|
383
1244
|
end
|
|
1245
|
+
|