ruby_smb 2.0.2 → 3.0.0
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 +46 -0
- data/README.md +0 -1
- data/examples/anonymous_auth.rb +3 -3
- data/examples/append_file.rb +10 -8
- data/examples/auth_capture.rb +71 -0
- data/examples/authenticate.rb +9 -5
- data/examples/delete_file.rb +8 -6
- data/examples/dump_secrets_from_sid.rb +207 -0
- data/examples/enum_domain_users.rb +75 -0
- data/examples/enum_registry_key.rb +5 -4
- data/examples/enum_registry_values.rb +5 -4
- data/examples/get_computer_info.rb +42 -0
- data/examples/list_directory.rb +8 -6
- data/examples/negotiate_with_netbios_service.rb +9 -5
- data/examples/net_share_enum_all.rb +6 -4
- data/examples/pipes.rb +11 -12
- data/examples/query_service_status.rb +102 -0
- data/examples/read_file.rb +8 -6
- data/examples/read_registry_key_value.rb +6 -5
- data/examples/rename_file.rb +9 -7
- data/examples/tree_connect.rb +7 -5
- data/examples/write_file.rb +9 -7
- data/lib/ruby_smb/client/authentication.rb +5 -10
- data/lib/ruby_smb/client/echo.rb +2 -4
- data/lib/ruby_smb/client/negotiation.rb +13 -16
- data/lib/ruby_smb/client/tree_connect.rb +2 -4
- data/lib/ruby_smb/client/utils.rb +16 -10
- data/lib/ruby_smb/client/winreg.rb +1 -1
- data/lib/ruby_smb/client.rb +102 -75
- data/lib/ruby_smb/compression/lznt1.rb +164 -0
- data/lib/ruby_smb/compression.rb +7 -0
- 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 +20 -0
- data/lib/ruby_smb/dcerpc/ndr.rb +1204 -80
- data/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_request.rb +27 -0
- data/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_response.rb +26 -0
- data/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_request.rb +27 -0
- data/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_response.rb +23 -0
- data/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_request.rb +24 -0
- data/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_response.rb +24 -0
- data/lib/ruby_smb/dcerpc/netlogon.rb +102 -0
- 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 +85 -19
- 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 +34 -0
- 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 +36 -0
- data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response.rb +26 -0
- data/lib/ruby_smb/dcerpc/svcctl/close_service_handle_request.rb +25 -0
- data/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response.rb +26 -0
- data/lib/ruby_smb/dcerpc/svcctl/control_service_request.rb +26 -0
- data/lib/ruby_smb/dcerpc/svcctl/control_service_response.rb +26 -0
- data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request.rb +25 -0
- data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response.rb +23 -0
- data/lib/ruby_smb/dcerpc/svcctl/open_service_w_request.rb +23 -0
- data/lib/ruby_smb/dcerpc/svcctl/open_service_w_response.rb +23 -0
- data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request.rb +25 -0
- data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response.rb +45 -0
- data/lib/ruby_smb/dcerpc/svcctl/query_service_status_request.rb +23 -0
- data/lib/ruby_smb/dcerpc/svcctl/query_service_status_response.rb +27 -0
- data/lib/ruby_smb/dcerpc/svcctl/service_status.rb +26 -0
- data/lib/ruby_smb/dcerpc/svcctl/start_service_w_request.rb +27 -0
- data/lib/ruby_smb/dcerpc/svcctl/start_service_w_response.rb +25 -0
- data/lib/ruby_smb/dcerpc/svcctl.rb +477 -0
- 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 +62 -0
- data/lib/ruby_smb/dcerpc/winreg/create_key_response.rb +36 -0
- data/lib/ruby_smb/dcerpc/winreg/enum_key_request.rb +2 -19
- 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 +6 -7
- data/lib/ruby_smb/dcerpc/winreg/open_root_key_response.rb +2 -2
- data/lib/ruby_smb/dcerpc/winreg/query_info_key_request.rb +1 -1
- data/lib/ruby_smb/dcerpc/winreg/query_info_key_response.rb +9 -18
- data/lib/ruby_smb/dcerpc/winreg/query_value_request.rb +4 -13
- 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 +28 -0
- data/lib/ruby_smb/dcerpc/winreg/save_key_response.rb +23 -0
- data/lib/ruby_smb/dcerpc/winreg.rb +99 -22
- 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 +45 -11
- data/lib/ruby_smb/dialect.rb +45 -0
- data/lib/ruby_smb/dispatcher/base.rb +1 -1
- data/lib/ruby_smb/dispatcher/socket.rb +2 -2
- data/lib/ruby_smb/error.rb +21 -5
- data/lib/ruby_smb/field/file_time.rb +1 -1
- data/lib/ruby_smb/field/string16.rb +5 -1
- data/lib/ruby_smb/field/stringz16.rb +17 -1
- data/lib/ruby_smb/generic_packet.rb +11 -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/nbss/session_header.rb +4 -4
- 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/file.rb +10 -25
- 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/packet/trans2/find_first2_response.rb +0 -1
- data/lib/ruby_smb/smb1/packet/trans2/find_next2_response.rb +0 -1
- data/lib/ruby_smb/smb1/packet/trans2/open2_response.rb +1 -2
- data/lib/ruby_smb/smb1/packet/trans2/set_file_information_response.rb +1 -13
- data/lib/ruby_smb/smb1/pipe.rb +12 -6
- data/lib/ruby_smb/smb1/tree.rb +13 -9
- data/lib/ruby_smb/smb2/file.rb +33 -33
- data/lib/ruby_smb/smb2/negotiate_context.rb +18 -2
- data/lib/ruby_smb/smb2/packet/compression_transform_header.rb +4 -0
- 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 +13 -6
- data/lib/ruby_smb/smb2/tree.rb +21 -11
- data/lib/ruby_smb/smb2.rb +3 -1
- data/lib/ruby_smb/version.rb +1 -1
- data/lib/ruby_smb.rb +5 -3
- data/spec/lib/ruby_smb/client_spec.rb +199 -86
- data/spec/lib/ruby_smb/compression/lznt1_spec.rb +32 -0
- 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 +3977 -239
- data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_request_spec.rb +69 -0
- data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_response_spec.rb +53 -0
- data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_request_spec.rb +69 -0
- data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_response_spec.rb +37 -0
- data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_request_spec.rb +45 -0
- data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_response_spec.rb +37 -0
- 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 +162 -0
- 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 +168 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_request_spec.rb +30 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_request_spec.rb +39 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request_spec.rb +68 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_request_spec.rb +53 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response_spec.rb +152 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_request_spec.rb +30 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/service_status_spec.rb +76 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_request_spec.rb +47 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_response_spec.rb +30 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl_spec.rb +508 -0
- 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 +73 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/create_key_response_spec.rb +44 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +4 -56
- 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 -20
- 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_request_spec.rb +0 -4
- 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 +18 -42
- data/spec/lib/ruby_smb/dcerpc/winreg/regsam_spec.rb +4 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/save_key_request_spec.rb +45 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/save_key_response_spec.rb +22 -0
- data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +219 -62
- 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/dispatcher/socket_spec.rb +10 -10
- data/spec/lib/ruby_smb/error_spec.rb +34 -5
- data/spec/lib/ruby_smb/field/string16_spec.rb +22 -0
- data/spec/lib/ruby_smb/field/stringz16_spec.rb +12 -0
- data/spec/lib/ruby_smb/generic_packet_spec.rb +7 -0
- data/spec/lib/ruby_smb/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/nbss/session_header_spec.rb +4 -11
- 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/file_spec.rb +2 -4
- data/spec/lib/ruby_smb/smb1/packet/trans2/find_first2_response_spec.rb +0 -1
- data/spec/lib/ruby_smb/smb1/packet/trans2/find_next2_response_spec.rb +0 -1
- data/spec/lib/ruby_smb/smb1/packet/trans2/open2_response_spec.rb +0 -5
- data/spec/lib/ruby_smb/smb1/packet/trans2/set_file_information_response_spec.rb +0 -6
- data/spec/lib/ruby_smb/smb1/pipe_spec.rb +20 -14
- data/spec/lib/ruby_smb/smb1/tree_spec.rb +22 -0
- data/spec/lib/ruby_smb/smb2/file_spec.rb +61 -9
- data/spec/lib/ruby_smb/smb2/negotiate_context_spec.rb +2 -2
- data/spec/lib/ruby_smb/smb2/pipe_spec.rb +20 -14
- data/spec/lib/ruby_smb/smb2/tree_spec.rb +58 -1
- data/spec/spec_helper.rb +1 -1
- data/spec/support/bin_helper.rb +9 -0
- data.tar.gz.sig +0 -0
- metadata +229 -23
- metadata.gz.sig +0 -0
- data/.travis.yml +0 -6
- data/lib/ruby_smb/client/signing.rb +0 -64
- data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +0 -35
- data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +0 -98
data/lib/ruby_smb/dcerpc/ndr.rb
CHANGED
|
@@ -1,121 +1,1245 @@
|
|
|
1
|
-
module RubySMB
|
|
2
|
-
module Dcerpc
|
|
3
|
-
module Ndr
|
|
1
|
+
module RubySMB::Dcerpc::Ndr
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
UUID = '8a885d04-1ceb-11c9-9fe8-08002b104860'
|
|
7
|
-
VER_MAJOR = 2
|
|
8
|
-
VER_MINOR = 0
|
|
3
|
+
require 'ruby_smb/field'
|
|
9
4
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
endian :little
|
|
5
|
+
# NDR Syntax
|
|
6
|
+
UUID = '8a885d04-1ceb-11c9-9fe8-08002b104860'
|
|
7
|
+
VER_MAJOR = 2
|
|
8
|
+
VER_MINOR = 0
|
|
15
9
|
|
|
16
|
-
|
|
10
|
+
#####################################
|
|
11
|
+
# NDR Primitive Types #
|
|
12
|
+
#####################################
|
|
17
13
|
|
|
18
|
-
|
|
19
|
-
|
|
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
|
+
)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
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
|
|
57
|
+
|
|
58
|
+
class NdrWideChar < RubySMB::Field::String16
|
|
59
|
+
default_parameter(length: 2, byte_align: 2)
|
|
60
|
+
end
|
|
61
|
+
|
|
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
|
|
67
|
+
|
|
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
|
|
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
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
#####################################
|
|
96
|
+
# NDR Constructed Types #
|
|
97
|
+
#####################################
|
|
98
|
+
|
|
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)
|
|
116
|
+
end
|
|
117
|
+
@deferred_ptrs.clear
|
|
118
|
+
end
|
|
119
|
+
|
|
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
|
|
126
|
+
|
|
127
|
+
def is_deferring(obj)
|
|
128
|
+
@deferred_ptrs.any? { |e| e.equal?(obj) }
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def get_top_level_constructed_type
|
|
132
|
+
return self if is_a?(PointerPlugin)
|
|
133
|
+
|
|
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
|
|
140
|
+
|
|
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
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
#
|
|
153
|
+
# Arrays
|
|
154
|
+
#
|
|
155
|
+
|
|
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
|
|
196
|
+
|
|
197
|
+
module ArrayPlugin
|
|
198
|
+
include ConstructedTypePlugin
|
|
199
|
+
|
|
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
|
|
204
|
+
|
|
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
|
|
210
|
+
|
|
211
|
+
def do_write(io)
|
|
212
|
+
if is_a?(ConfPlugin) && should_process_max_count?
|
|
213
|
+
io.writebytes([@max_count].pack('L<'))
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
if is_a?(VarPlugin)
|
|
217
|
+
io.writebytes([@offset].pack('L<'))
|
|
218
|
+
io.writebytes([@actual_count].pack('L<'))
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
unless empty?
|
|
222
|
+
io.writebytes("\x00" * align_element_size(io.offset))
|
|
223
|
+
super
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
if has_deferred_ptrs?
|
|
227
|
+
write_ptr(io)
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
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)
|
|
20
295
|
end
|
|
296
|
+
nbytes += elements[i].do_num_bytes
|
|
21
297
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
298
|
+
if nbytes.is_a?(Integer)
|
|
299
|
+
sum.ceil + nbytes
|
|
300
|
+
else
|
|
301
|
+
sum + nbytes
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
module ConfClassPlugin; end
|
|
308
|
+
|
|
309
|
+
module ConfPlugin
|
|
310
|
+
attr_accessor :read_until_index, :max_count
|
|
311
|
+
|
|
312
|
+
def initialize_instance
|
|
313
|
+
@read_until_index = 0
|
|
314
|
+
@max_count = 0
|
|
315
|
+
super
|
|
316
|
+
end
|
|
317
|
+
|
|
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
|
|
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
|
|
425
|
+
end
|
|
426
|
+
end
|
|
427
|
+
|
|
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
|
|
433
|
+
|
|
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)
|
|
437
|
+
|
|
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))
|
|
28
691
|
end
|
|
692
|
+
end
|
|
693
|
+
|
|
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)
|
|
29
704
|
|
|
30
|
-
|
|
31
|
-
|
|
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))
|
|
32
710
|
end
|
|
33
711
|
end
|
|
34
712
|
|
|
35
|
-
|
|
36
|
-
# [Transfer Syntax NDR - Conformant and Varying Strings](http://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_04_02)
|
|
37
|
-
# The string elements are Stringz16 (unicode)
|
|
38
|
-
class NdrString < BinData::Primitive
|
|
39
|
-
endian :little
|
|
713
|
+
super
|
|
40
714
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
|
45
741
|
|
|
46
|
-
|
|
47
|
-
|
|
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)
|
|
48
746
|
end
|
|
747
|
+
end
|
|
49
748
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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)
|
|
57
784
|
end
|
|
58
785
|
end
|
|
786
|
+
is_a?(BinData::ByteAlignPlugin) ? super : 0
|
|
59
787
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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)
|
|
63
800
|
|
|
64
|
-
|
|
801
|
+
class << self; attr_reader :has_conformant_array end
|
|
65
802
|
|
|
66
|
-
|
|
67
|
-
|
|
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
|
+
)
|
|
68
829
|
end
|
|
830
|
+
validate_conformant_array(field)
|
|
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
|
|
843
|
+
|
|
844
|
+
#
|
|
845
|
+
# [Pointers](https://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_10)
|
|
846
|
+
#
|
|
847
|
+
|
|
848
|
+
module PointerClassPlugin
|
|
849
|
+
module ExtendPointerPlugin
|
|
850
|
+
def initialize_shared_instance
|
|
851
|
+
super
|
|
852
|
+
extend PointerPlugin
|
|
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
|
|
861
|
+
|
|
862
|
+
|
|
863
|
+
module TopLevelPlugin
|
|
864
|
+
module TopLevelClassMethods
|
|
865
|
+
def pos
|
|
866
|
+
@@pos
|
|
867
|
+
end
|
|
868
|
+
def increment_pos
|
|
869
|
+
@@pos += 1
|
|
870
|
+
end
|
|
871
|
+
def reset_pos
|
|
872
|
+
@@pos = 0
|
|
873
|
+
end
|
|
874
|
+
end
|
|
875
|
+
|
|
876
|
+
def self.extended(target)
|
|
877
|
+
target.class.extend(TopLevelClassMethods)
|
|
878
|
+
target.class.reset_pos
|
|
879
|
+
end
|
|
880
|
+
|
|
881
|
+
def initialize_instance
|
|
882
|
+
super
|
|
883
|
+
@standalone_ptr = false
|
|
884
|
+
end
|
|
885
|
+
|
|
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)
|
|
69
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
|
|
70
936
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
937
|
+
def initialize_instance
|
|
938
|
+
if @ref_id.nil?
|
|
939
|
+
if eval_parameter(:initial_value)
|
|
940
|
+
instantiate_referent
|
|
941
|
+
else
|
|
942
|
+
@ref_id = 0
|
|
943
|
+
end
|
|
944
|
+
end
|
|
945
|
+
extend_top_level_class
|
|
946
|
+
super
|
|
947
|
+
end
|
|
77
948
|
|
|
78
|
-
|
|
79
|
-
|
|
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
|
|
80
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
|
|
81
978
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
else
|
|
89
|
-
read(handle.to_s)
|
|
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})"
|
|
90
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
|
|
91
1014
|
end
|
|
92
1015
|
end
|
|
1016
|
+
super(io) unless (is_null_ptr? && !eval_parameter(:initial_value)) || is_alias?
|
|
1017
|
+
end
|
|
93
1018
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
|
1038
|
+
end
|
|
1039
|
+
end
|
|
1040
|
+
super(io) unless is_null_ptr? || is_alias?
|
|
1041
|
+
end
|
|
97
1042
|
|
|
98
|
-
|
|
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
|
|
99
1053
|
end
|
|
1054
|
+
end
|
|
100
1055
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
endian :little
|
|
1056
|
+
def is_alias?
|
|
1057
|
+
has_parameter?(:ref_to)
|
|
1058
|
+
end
|
|
105
1059
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
|
1080
|
+
end
|
|
111
1081
|
end
|
|
1082
|
+
return res
|
|
1083
|
+
end
|
|
112
1084
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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)
|
|
1095
|
+
end
|
|
1096
|
+
else
|
|
1097
|
+
# add ref_id size
|
|
1098
|
+
sum += 4
|
|
116
1099
|
|
|
117
|
-
|
|
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()
|
|
1115
|
+
end
|
|
118
1116
|
end
|
|
1117
|
+
|
|
1118
|
+
sum
|
|
1119
|
+
end
|
|
1120
|
+
|
|
1121
|
+
def instantiate_referent
|
|
1122
|
+
@ref_id = INITIAL_REF_ID
|
|
1123
|
+
end
|
|
1124
|
+
|
|
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)
|
|
119
1177
|
end
|
|
120
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)
|
|
1240
|
+
end
|
|
1241
|
+
end
|
|
1242
|
+
end
|
|
1243
|
+
|
|
121
1244
|
end
|
|
1245
|
+
|