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.
Files changed (228) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.github/workflows/verify.yml +5 -15
  4. data/examples/auth_capture.rb +71 -0
  5. data/examples/dump_secrets_from_sid.rb +207 -0
  6. data/examples/enum_domain_users.rb +75 -0
  7. data/examples/get_computer_info.rb +42 -0
  8. data/examples/query_service_status.rb +42 -4
  9. data/lib/ruby_smb/client/negotiation.rb +1 -1
  10. data/lib/ruby_smb/client.rb +10 -20
  11. data/lib/ruby_smb/dcerpc/bind.rb +28 -20
  12. data/lib/ruby_smb/dcerpc/bind_ack.rb +29 -28
  13. data/lib/ruby_smb/dcerpc/client.rb +542 -0
  14. data/lib/ruby_smb/dcerpc/drsr/drs_bind_request.rb +24 -0
  15. data/lib/ruby_smb/dcerpc/drsr/drs_bind_response.rb +26 -0
  16. data/lib/ruby_smb/dcerpc/drsr/drs_crack_names_request.rb +57 -0
  17. data/lib/ruby_smb/dcerpc/drsr/drs_crack_names_response.rb +76 -0
  18. data/lib/ruby_smb/dcerpc/drsr/drs_domain_controller_info_request.rb +46 -0
  19. data/lib/ruby_smb/dcerpc/drsr/drs_domain_controller_info_response.rb +168 -0
  20. data/lib/ruby_smb/dcerpc/drsr/drs_extensions.rb +56 -0
  21. data/lib/ruby_smb/dcerpc/drsr/drs_get_nc_changes_request.rb +121 -0
  22. data/lib/ruby_smb/dcerpc/drsr/drs_get_nc_changes_response.rb +118 -0
  23. data/lib/ruby_smb/dcerpc/drsr/drs_unbind_request.rb +24 -0
  24. data/lib/ruby_smb/dcerpc/drsr/drs_unbind_response.rb +26 -0
  25. data/lib/ruby_smb/dcerpc/drsr.rb +909 -0
  26. data/lib/ruby_smb/dcerpc/epm/epm_ept_map_request.rb +26 -0
  27. data/lib/ruby_smb/dcerpc/epm/epm_ept_map_response.rb +25 -0
  28. data/lib/ruby_smb/dcerpc/epm/epm_twrt.rb +211 -0
  29. data/lib/ruby_smb/dcerpc/epm.rb +75 -0
  30. data/lib/ruby_smb/dcerpc/error.rb +17 -0
  31. data/lib/ruby_smb/dcerpc/ndr.rb +1159 -297
  32. data/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_request.rb +3 -13
  33. data/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_response.rb +3 -3
  34. data/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_request.rb +3 -13
  35. data/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_response.rb +1 -1
  36. data/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_request.rb +3 -11
  37. data/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_response.rb +1 -1
  38. data/lib/ruby_smb/dcerpc/netlogon.rb +5 -4
  39. data/lib/ruby_smb/dcerpc/p_syntax_id_t.rb +4 -3
  40. data/lib/ruby_smb/dcerpc/pdu_header.rb +7 -7
  41. data/lib/ruby_smb/dcerpc/ptypes.rb +1 -0
  42. data/lib/ruby_smb/dcerpc/request.rb +79 -32
  43. data/lib/ruby_smb/dcerpc/response.rb +45 -10
  44. data/lib/ruby_smb/dcerpc/rpc_auth3.rb +28 -0
  45. data/lib/ruby_smb/dcerpc/rpc_security_attributes.rb +11 -11
  46. data/lib/ruby_smb/dcerpc/rrp_rpc_unicode_string.rb +118 -0
  47. data/lib/ruby_smb/dcerpc/samr/rpc_sid.rb +150 -0
  48. data/lib/ruby_smb/dcerpc/samr/samr_close_handle_request.rb +23 -0
  49. data/lib/ruby_smb/dcerpc/samr/samr_close_handle_response.rb +24 -0
  50. data/lib/ruby_smb/dcerpc/samr/samr_connect_request.rb +32 -0
  51. data/lib/ruby_smb/dcerpc/samr/samr_connect_response.rb +23 -0
  52. data/lib/ruby_smb/dcerpc/samr/samr_enumerate_users_in_domain_request.rb +26 -0
  53. data/lib/ruby_smb/dcerpc/samr/samr_enumerate_users_in_domain_response.rb +55 -0
  54. data/lib/ruby_smb/dcerpc/samr/samr_get_alias_membership_request.rb +48 -0
  55. data/lib/ruby_smb/dcerpc/samr/samr_get_alias_membership_response.rb +38 -0
  56. data/lib/ruby_smb/dcerpc/samr/samr_get_groups_for_user_request.rb +23 -0
  57. data/lib/ruby_smb/dcerpc/samr/samr_get_groups_for_user_response.rb +48 -0
  58. data/lib/ruby_smb/dcerpc/samr/samr_lookup_domain_in_sam_server_request.rb +24 -0
  59. data/lib/ruby_smb/dcerpc/samr/samr_lookup_domain_in_sam_server_response.rb +25 -0
  60. data/lib/ruby_smb/dcerpc/samr/samr_open_domain_request.rb +27 -0
  61. data/lib/ruby_smb/dcerpc/samr/samr_open_domain_response.rb +24 -0
  62. data/lib/ruby_smb/dcerpc/samr/samr_open_user_request.rb +26 -0
  63. data/lib/ruby_smb/dcerpc/samr/samr_open_user_response.rb +24 -0
  64. data/lib/ruby_smb/dcerpc/samr/samr_rid_to_sid_request.rb +23 -0
  65. data/lib/ruby_smb/dcerpc/samr/samr_rid_to_sid_response.rb +23 -0
  66. data/lib/ruby_smb/dcerpc/samr.rb +613 -0
  67. data/lib/ruby_smb/dcerpc/sec_trailer.rb +26 -0
  68. data/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all.rb +56 -79
  69. data/lib/ruby_smb/dcerpc/srvsvc.rb +27 -4
  70. data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request.rb +13 -25
  71. data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response.rb +2 -2
  72. data/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response.rb +1 -1
  73. data/lib/ruby_smb/dcerpc/svcctl/control_service_request.rb +1 -1
  74. data/lib/ruby_smb/dcerpc/svcctl/control_service_response.rb +1 -1
  75. data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request.rb +4 -14
  76. data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response.rb +1 -1
  77. data/lib/ruby_smb/dcerpc/svcctl/open_service_w_request.rb +3 -11
  78. data/lib/ruby_smb/dcerpc/svcctl/open_service_w_response.rb +1 -1
  79. data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request.rb +1 -1
  80. data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response.rb +12 -11
  81. data/lib/ruby_smb/dcerpc/svcctl/query_service_status_response.rb +1 -1
  82. data/lib/ruby_smb/dcerpc/svcctl/service_status.rb +9 -8
  83. data/lib/ruby_smb/dcerpc/svcctl/start_service_w_request.rb +3 -3
  84. data/lib/ruby_smb/dcerpc/svcctl/start_service_w_response.rb +1 -1
  85. data/lib/ruby_smb/dcerpc/svcctl.rb +1 -3
  86. data/lib/ruby_smb/dcerpc/uuid.rb +3 -0
  87. data/lib/ruby_smb/dcerpc/winreg/close_key_response.rb +2 -2
  88. data/lib/ruby_smb/dcerpc/winreg/create_key_request.rb +2 -13
  89. data/lib/ruby_smb/dcerpc/winreg/create_key_response.rb +3 -3
  90. data/lib/ruby_smb/dcerpc/winreg/enum_key_request.rb +3 -20
  91. data/lib/ruby_smb/dcerpc/winreg/enum_key_response.rb +3 -20
  92. data/lib/ruby_smb/dcerpc/winreg/enum_value_request.rb +5 -14
  93. data/lib/ruby_smb/dcerpc/winreg/enum_value_response.rb +5 -14
  94. data/lib/ruby_smb/dcerpc/winreg/open_key_request.rb +1 -9
  95. data/lib/ruby_smb/dcerpc/winreg/open_key_response.rb +4 -3
  96. data/lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb +5 -6
  97. data/lib/ruby_smb/dcerpc/winreg/open_root_key_response.rb +2 -2
  98. data/lib/ruby_smb/dcerpc/winreg/query_info_key_response.rb +9 -18
  99. data/lib/ruby_smb/dcerpc/winreg/query_value_request.rb +4 -14
  100. data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +7 -15
  101. data/lib/ruby_smb/dcerpc/winreg/regsam.rb +3 -1
  102. data/lib/ruby_smb/dcerpc/winreg/save_key_request.rb +0 -9
  103. data/lib/ruby_smb/dcerpc/winreg/save_key_response.rb +1 -1
  104. data/lib/ruby_smb/dcerpc/winreg.rb +10 -14
  105. data/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_request.rb +26 -0
  106. data/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_response.rb +88 -0
  107. data/lib/ruby_smb/dcerpc/wkssvc.rb +65 -0
  108. data/lib/ruby_smb/dcerpc.rb +41 -11
  109. data/lib/ruby_smb/dialect.rb +45 -0
  110. data/lib/ruby_smb/dispatcher/base.rb +1 -1
  111. data/lib/ruby_smb/field/file_time.rb +1 -1
  112. data/lib/ruby_smb/field/string16.rb +5 -1
  113. data/lib/ruby_smb/gss/provider/authenticator.rb +42 -0
  114. data/lib/ruby_smb/gss/provider/ntlm.rb +303 -0
  115. data/lib/ruby_smb/gss/provider.rb +35 -0
  116. data/lib/ruby_smb/gss.rb +56 -63
  117. data/lib/ruby_smb/ntlm.rb +61 -0
  118. data/lib/ruby_smb/server/server_client/negotiation.rb +156 -0
  119. data/lib/ruby_smb/server/server_client/session_setup.rb +82 -0
  120. data/lib/ruby_smb/server/server_client.rb +162 -0
  121. data/lib/ruby_smb/server.rb +54 -0
  122. data/lib/ruby_smb/signing.rb +59 -0
  123. data/lib/ruby_smb/smb1/packet/negotiate_response.rb +11 -11
  124. data/lib/ruby_smb/smb1/packet/negotiate_response_extended.rb +1 -1
  125. data/lib/ruby_smb/smb1/packet/session_setup_request.rb +1 -1
  126. data/lib/ruby_smb/smb1/pipe.rb +4 -0
  127. data/lib/ruby_smb/smb1/tree.rb +1 -1
  128. data/lib/ruby_smb/smb2/negotiate_context.rb +18 -2
  129. data/lib/ruby_smb/smb2/packet/negotiate_request.rb +9 -0
  130. data/lib/ruby_smb/smb2/packet/negotiate_response.rb +0 -1
  131. data/lib/ruby_smb/smb2/packet/session_setup_response.rb +2 -2
  132. data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +1 -1
  133. data/lib/ruby_smb/smb2/pipe.rb +4 -0
  134. data/lib/ruby_smb/smb2/tree.rb +1 -1
  135. data/lib/ruby_smb/smb2.rb +3 -1
  136. data/lib/ruby_smb/version.rb +1 -1
  137. data/lib/ruby_smb.rb +2 -1
  138. data/spec/lib/ruby_smb/client_spec.rb +8 -11
  139. data/spec/lib/ruby_smb/dcerpc/bind_ack_spec.rb +69 -41
  140. data/spec/lib/ruby_smb/dcerpc/bind_spec.rb +75 -21
  141. data/spec/lib/ruby_smb/dcerpc/client_spec.rb +714 -0
  142. data/spec/lib/ruby_smb/dcerpc/drsr_spec.rb +2169 -0
  143. data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +3792 -1373
  144. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_request_spec.rb +4 -4
  145. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_request_spec.rb +4 -4
  146. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_request_spec.rb +2 -2
  147. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_response_spec.rb +2 -2
  148. data/spec/lib/ruby_smb/dcerpc/p_syntax_id_t_spec.rb +18 -4
  149. data/spec/lib/ruby_smb/dcerpc/pdu_header_spec.rb +27 -1
  150. data/spec/lib/ruby_smb/dcerpc/request_spec.rb +76 -11
  151. data/spec/lib/ruby_smb/dcerpc/response_spec.rb +99 -9
  152. data/spec/lib/ruby_smb/dcerpc/rpc_auth3_spec.rb +75 -0
  153. data/spec/lib/ruby_smb/dcerpc/rpc_security_attributes_spec.rb +29 -28
  154. data/spec/lib/ruby_smb/dcerpc/rrp_rpc_unicode_string_spec.rb +340 -0
  155. data/spec/lib/ruby_smb/dcerpc/samr/rpc_sid_spec.rb +116 -0
  156. data/spec/lib/ruby_smb/dcerpc/samr/samr_close_handle_request_spec.rb +40 -0
  157. data/spec/lib/ruby_smb/dcerpc/samr/samr_close_handle_response_spec.rb +48 -0
  158. data/spec/lib/ruby_smb/dcerpc/samr/samr_connect_request_spec.rb +56 -0
  159. data/spec/lib/ruby_smb/dcerpc/samr/samr_connect_response_spec.rb +47 -0
  160. data/spec/lib/ruby_smb/dcerpc/samr/samr_enumerate_users_in_domain_request_spec.rb +63 -0
  161. data/spec/lib/ruby_smb/dcerpc/samr/samr_enumerate_users_in_domain_response_spec.rb +265 -0
  162. data/spec/lib/ruby_smb/dcerpc/samr/samr_lookup_domain_in_sam_server_request_spec.rb +52 -0
  163. data/spec/lib/ruby_smb/dcerpc/samr/samr_lookup_domain_in_sam_server_response_spec.rb +36 -0
  164. data/spec/lib/ruby_smb/dcerpc/samr/samr_open_domain_request_spec.rb +56 -0
  165. data/spec/lib/ruby_smb/dcerpc/samr/samr_open_domain_response_spec.rb +48 -0
  166. data/spec/lib/ruby_smb/dcerpc/samr/samr_rid_to_sid_request_spec.rb +48 -0
  167. data/spec/lib/ruby_smb/dcerpc/samr/samr_rid_to_sid_response_spec.rb +42 -0
  168. data/spec/lib/ruby_smb/dcerpc/samr_spec.rb +420 -0
  169. data/spec/lib/ruby_smb/dcerpc/sec_trailer_spec.rb +92 -0
  170. data/spec/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all_spec.rb +149 -110
  171. data/spec/lib/ruby_smb/dcerpc/srvsvc_spec.rb +21 -17
  172. data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request_spec.rb +56 -79
  173. data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response_spec.rb +4 -4
  174. data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response_spec.rb +2 -2
  175. data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_request_spec.rb +2 -2
  176. data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_response_spec.rb +2 -2
  177. data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request_spec.rb +19 -29
  178. data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response_spec.rb +2 -2
  179. data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_request_spec.rb +9 -15
  180. data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_response_spec.rb +2 -2
  181. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request_spec.rb +2 -2
  182. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response_spec.rb +22 -22
  183. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_response_spec.rb +2 -2
  184. data/spec/lib/ruby_smb/dcerpc/svcctl/service_status_spec.rb +18 -14
  185. data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_request_spec.rb +5 -4
  186. data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_response_spec.rb +2 -2
  187. data/spec/lib/ruby_smb/dcerpc/svcctl_spec.rb +1 -5
  188. data/spec/lib/ruby_smb/dcerpc/uuid_spec.rb +15 -23
  189. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_response_spec.rb +2 -2
  190. data/spec/lib/ruby_smb/dcerpc/winreg/create_key_request_spec.rb +4 -41
  191. data/spec/lib/ruby_smb/dcerpc/winreg/create_key_response_spec.rb +4 -4
  192. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +4 -52
  193. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_response_spec.rb +4 -56
  194. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_request_spec.rb +10 -34
  195. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_response_spec.rb +10 -34
  196. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_request_spec.rb +2 -26
  197. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_response_spec.rb +2 -2
  198. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +17 -25
  199. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_response_spec.rb +2 -2
  200. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_response_spec.rb +20 -44
  201. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_request_spec.rb +8 -32
  202. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_response_spec.rb +10 -22
  203. data/spec/lib/ruby_smb/dcerpc/winreg/regsam_spec.rb +4 -0
  204. data/spec/lib/ruby_smb/dcerpc/winreg/save_key_request_spec.rb +0 -12
  205. data/spec/lib/ruby_smb/dcerpc/winreg/save_key_response_spec.rb +2 -2
  206. data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +18 -47
  207. data/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_request_spec.rb +43 -0
  208. data/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_response_spec.rb +410 -0
  209. data/spec/lib/ruby_smb/dcerpc/wkssvc_spec.rb +70 -0
  210. data/spec/lib/ruby_smb/field/string16_spec.rb +22 -0
  211. data/spec/lib/ruby_smb/gss/provider/ntlm/account_spec.rb +32 -0
  212. data/spec/lib/ruby_smb/gss/provider/ntlm/authenticator_spec.rb +101 -0
  213. data/spec/lib/ruby_smb/gss/provider/ntlm/os_version_spec.rb +32 -0
  214. data/spec/lib/ruby_smb/gss/provider/ntlm_spec.rb +113 -0
  215. data/spec/lib/ruby_smb/server/server_client_spec.rb +156 -0
  216. data/spec/lib/ruby_smb/server_spec.rb +32 -0
  217. data/spec/lib/ruby_smb/smb1/pipe_spec.rb +18 -37
  218. data/spec/lib/ruby_smb/smb1/tree_spec.rb +4 -4
  219. data/spec/lib/ruby_smb/smb2/negotiate_context_spec.rb +2 -2
  220. data/spec/lib/ruby_smb/smb2/pipe_spec.rb +18 -16
  221. data/spec/lib/ruby_smb/smb2/tree_spec.rb +5 -5
  222. data/spec/support/bin_helper.rb +9 -0
  223. data.tar.gz.sig +2 -1
  224. metadata +119 -6
  225. metadata.gz.sig +0 -0
  226. data/lib/ruby_smb/client/signing.rb +0 -64
  227. data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +0 -38
  228. data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +0 -135
@@ -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
- def set(v)
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
- def clear
48
- # Make sure #max_count and #offset are not cleared out
49
- self.str.clear
50
- self.actual_count.clear
51
- end
5
+ # NDR Syntax
6
+ UUID = '8a885d04-1ceb-11c9-9fe8-08002b104860'
7
+ VER_MAJOR = 2
8
+ VER_MINOR = 0
52
9
 
53
- def to_s
54
- self.str.to_s
55
- end
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
- # An NDR Uni-dimensional Conformant Array of Bytes representation as defined in
59
- # [Transfer Syntax NDR - Uni-dimensional Conformant Arrays](https://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_03_02)
60
- class NdrLpByte < BinData::Primitive
61
- endian :little
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
- uint32 :max_count, initial_value: -> { self.elements.size }
64
- array :elements, type: :uint8, read_until: -> { index == self.max_count - 1 }, onlyif: -> { self.max_count > 0 }
58
+ class NdrWideChar < RubySMB::Field::String16
59
+ default_parameter(length: 2, byte_align: 2)
60
+ end
65
61
 
66
- def get
67
- self.elements
68
- end
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
- def set(v)
71
- v = v.elements if v.is_a?(self.class)
72
- self.elements = v.to_ary
73
- self.max_count = self.elements.size unless self.elements.equal?(v)
74
- end
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
- def get
88
- self.bytes
89
- end
95
+ #####################################
96
+ # NDR Constructed Types #
97
+ #####################################
90
98
 
91
- def set(v)
92
- v = v.bytes if v.is_a?(self.class)
93
- self.bytes = v.to_ary
94
- self.max_count = self.bytes.size unless self.bytes.equal?(v)
95
- end
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
- # An NDR Uni-dimensional Fixed Array of bytes representation as defined in:
99
- # [Transfer Syntax NDR - NDR Constructed Types](https://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_03_01)
100
- class NdrFixedByteArray < BinData::BasePrimitive
101
- optional_parameters :read_length, :length, :pad_byte, :pad_front
102
- default_parameters pad_byte: 0
103
- mutually_exclusive_parameters :length, :value
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
- def initialize_shared_instance
106
- if (has_parameter?(:value) || has_parameter?(:asserted_value)) && !has_parameter?(:read_length)
107
- extend WarnNoReadLengthPlugin
108
- end
109
- super
110
- end
127
+ def is_deferring(obj)
128
+ @deferred_ptrs.any? { |e| e.equal?(obj) }
129
+ end
111
130
 
112
- def assign(val)
113
- super(fixed_byte_array(val))
114
- end
131
+ def get_top_level_constructed_type
132
+ return self if is_a?(PointerPlugin)
115
133
 
116
- def snapshot
117
- clamp_to_length(super)
118
- end
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
- class << self
121
- def arg_processor
122
- NdrFixedByteArrayArgProcessor.new
123
- end
124
- end
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
- val
143
- end
152
+ #
153
+ # Arrays
154
+ #
144
155
 
145
- def fixed_byte_array(val)
146
- val = val.bytes if val.is_a? String
147
- val.to_ary
148
- end
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
- def read_and_return_value(io)
151
- len = eval_parameter(:read_length) || eval_parameter(:length) || 0
152
- io.readbytes(len)
153
- end
197
+ module ArrayPlugin
198
+ include ConstructedTypePlugin
154
199
 
155
- def sensible_default
156
- [ ]
157
- end
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
- def value_to_binary_string(val)
160
- clamp_to_length(val).pack('C*')
161
- end
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
- class NdrFixedByteArrayArgProcessor < BinData::BaseArgProcessor
164
- def sanitize_parameters!(obj_class, obj_params)
165
- obj_params.must_be_integer(:length, :pad_byte)
166
- obj_params.sanitize(:pad_byte) { |byte| sanitized_pad_byte(byte) }
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
- private
216
+ if is_a?(VarPlugin)
217
+ io.writebytes([@offset].pack('L<'))
218
+ io.writebytes([@actual_count].pack('L<'))
219
+ end
170
220
 
171
- def sanitized_pad_byte(byte)
172
- if byte.is_a?(String)
173
- raise ArgumentError, ':pad_byte must not contain more than 1 byte' if byte.bytesize > 1
221
+ unless empty?
222
+ io.writebytes("\x00" * align_element_size(io.offset))
223
+ super
224
+ end
174
225
 
175
- byte = byte.ord
176
- end
177
- raise ArgumentError, ':pad_byte must be within the range of 0 - 255' unless ((byte >= 0) && (byte <= 255))
226
+ if has_deferred_ptrs?
227
+ write_ptr(io)
228
+ end
229
+ end
178
230
 
179
- byte
180
- end
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
- # Warns when reading if :value && no :read_length
184
- module WarnNoReadLengthPlugin
185
- def read_and_return_value(io)
186
- warn "#{debug_name} does not have a :read_length parameter - returning empty array"
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
- # An NDR Context Handle representation as defined in
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
- uint32 :context_handle_attributes
198
- uuid :context_handle_uuid
309
+ module ConfPlugin
310
+ attr_accessor :read_until_index, :max_count
199
311
 
200
- def get
201
- {:context_handle_attributes => context_handle_attributes, :context_handle_uuid => context_handle_uuid}
202
- end
312
+ def initialize_instance
313
+ @read_until_index = 0
314
+ @max_count = 0
315
+ super
316
+ end
203
317
 
204
- def set(handle)
205
- if handle.is_a?(Hash)
206
- self.context_handle_attributes = handle[:context_handle_attributes]
207
- self.context_handle_uuid = handle[:context_handle_uuid]
208
- elsif handle.is_a?(NdrContextHandle)
209
- read(handle.to_binary_s)
210
- else
211
- read(handle.to_s)
212
- end
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
- # An NDR Top-level Full Pointers representation as defined in
217
- # [Transfer Syntax NDR - Top-level Full Pointers](http://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_11_01)
218
- # This class must be inherited and the subclass must have a #referent property
219
- class NdrPointer < BinData::Primitive
220
- endian :little
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
- uint32 :referent_id, initial_value: 0
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
- def do_read(io)
225
- self.referent_id.do_read(io)
226
- if process_referent?
227
- self.referent.do_read(io) unless self.referent_id == 0
228
- end
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
- def do_write(io)
232
- self.referent_id.do_write(io)
233
- if process_referent?
234
- self.referent.do_write(io) unless self.referent_id == 0
235
- end
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
- def set(v)
239
- if v == :null
240
- self.referent.clear
241
- self.referent_id = 0
242
- else
243
- if self.referent.respond_to?(:set)
244
- self.referent.set(v)
245
- else
246
- self.referent = v
247
- end
248
- self.referent_id = rand(0xFFFFFFFF) if self.referent_id == 0
249
- end
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
- def get
253
- if self.referent_id == 0
254
- :null
255
- else
256
- self.referent
257
- end
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
- def process_referent?
261
- current_parent = parent
262
- loop do
263
- return true unless current_parent
264
- return false if current_parent.is_a?(NdrStruct)
265
- current_parent = current_parent.parent
266
- end
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
- # A pointer to a NdrString structure
271
- class NdrLpStr < NdrPointer
272
- endian :little
844
+ #
845
+ # [Pointers](https://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_10)
846
+ #
273
847
 
274
- ndr_string :referent, onlyif: -> { self.referent_id != 0 }
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
- uint32 :referent, onlyif: -> { self.referent_id != 0 }
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
- # A pointer to an NDR Uni-dimensional Conformant-varying Arrays of bytes
284
- class NdrLpByteArray < NdrPointer
285
- endian :little
876
+ def self.extended(target)
877
+ target.class.extend(TopLevelClassMethods)
878
+ target.class.reset_pos
879
+ end
286
880
 
287
- ndr_byte_array :referent, onlyif: -> { self.referent_id != 0 }
881
+ def initialize_instance
882
+ super
883
+ @standalone_ptr = false
884
+ end
288
885
 
289
- def set(v)
290
- if v != :null && v.is_a?(NdrLpByteArray)
291
- super(v.referent)
292
- else
293
- super(v)
294
- end
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
- # A pointer to a Windows FILETIME structure
299
- class NdrLpFileTime < NdrPointer
300
- endian :little
301
-
302
- file_time :referent, onlyif: -> { self.referent_id != 0 }
303
- end
304
-
305
- # A generic NDR structure that implements logic to #read and #write
306
- # (#to_binary_s) in case the structure contains BinData::Array or
307
- # NdrPointer fields. This class must be inherited.
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
- def do_write(io)
332
- super(io)
333
- each_pair do |_name, field|
334
- case field
335
- when BinData::Array
336
- field.each do |element|
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
- class NdrStringPtrsw < NdrStruct
352
- endian :little
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
- uint32 :max_count, initial_value: -> { self.elements.size }
355
- array :elements, type: :ndr_lp_str, read_until: -> { index == self.max_count - 1 }, onlyif: -> { self.max_count > 0 }
1056
+ def is_alias?
1057
+ has_parameter?(:ref_to)
1058
+ end
356
1059
 
357
- def get
358
- self.elements
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
- def set(v)
362
- v = v.elements if v.is_a?(self.class)
363
- self.elements = v.to_ary
364
- self.max_count = self.elements.size unless self.elements.equal?(v)
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
- def do_num_bytes
368
- to_binary_s.size
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
- class NdrLpStringPtrsw < NdrPointer
373
- endian :little
1118
+ sum
1119
+ end
374
1120
 
375
- ndr_string_ptrsw :referent, onlyif: -> { self.referent_id != 0 }
1121
+ def instantiate_referent
1122
+ @ref_id = INITIAL_REF_ID
1123
+ end
376
1124
 
377
- def set(v)
378
- super(v.respond_to?(:to_ary) ? v.to_ary : v)
379
- end
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
+