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.
Files changed (285) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.github/workflows/verify.yml +46 -0
  4. data/README.md +0 -1
  5. data/examples/anonymous_auth.rb +3 -3
  6. data/examples/append_file.rb +10 -8
  7. data/examples/auth_capture.rb +71 -0
  8. data/examples/authenticate.rb +9 -5
  9. data/examples/delete_file.rb +8 -6
  10. data/examples/dump_secrets_from_sid.rb +207 -0
  11. data/examples/enum_domain_users.rb +75 -0
  12. data/examples/enum_registry_key.rb +5 -4
  13. data/examples/enum_registry_values.rb +5 -4
  14. data/examples/get_computer_info.rb +42 -0
  15. data/examples/list_directory.rb +8 -6
  16. data/examples/negotiate_with_netbios_service.rb +9 -5
  17. data/examples/net_share_enum_all.rb +6 -4
  18. data/examples/pipes.rb +11 -12
  19. data/examples/query_service_status.rb +102 -0
  20. data/examples/read_file.rb +8 -6
  21. data/examples/read_registry_key_value.rb +6 -5
  22. data/examples/rename_file.rb +9 -7
  23. data/examples/tree_connect.rb +7 -5
  24. data/examples/write_file.rb +9 -7
  25. data/lib/ruby_smb/client/authentication.rb +5 -10
  26. data/lib/ruby_smb/client/echo.rb +2 -4
  27. data/lib/ruby_smb/client/negotiation.rb +13 -16
  28. data/lib/ruby_smb/client/tree_connect.rb +2 -4
  29. data/lib/ruby_smb/client/utils.rb +16 -10
  30. data/lib/ruby_smb/client/winreg.rb +1 -1
  31. data/lib/ruby_smb/client.rb +102 -75
  32. data/lib/ruby_smb/compression/lznt1.rb +164 -0
  33. data/lib/ruby_smb/compression.rb +7 -0
  34. data/lib/ruby_smb/dcerpc/bind.rb +28 -20
  35. data/lib/ruby_smb/dcerpc/bind_ack.rb +29 -28
  36. data/lib/ruby_smb/dcerpc/client.rb +542 -0
  37. data/lib/ruby_smb/dcerpc/drsr/drs_bind_request.rb +24 -0
  38. data/lib/ruby_smb/dcerpc/drsr/drs_bind_response.rb +26 -0
  39. data/lib/ruby_smb/dcerpc/drsr/drs_crack_names_request.rb +57 -0
  40. data/lib/ruby_smb/dcerpc/drsr/drs_crack_names_response.rb +76 -0
  41. data/lib/ruby_smb/dcerpc/drsr/drs_domain_controller_info_request.rb +46 -0
  42. data/lib/ruby_smb/dcerpc/drsr/drs_domain_controller_info_response.rb +168 -0
  43. data/lib/ruby_smb/dcerpc/drsr/drs_extensions.rb +56 -0
  44. data/lib/ruby_smb/dcerpc/drsr/drs_get_nc_changes_request.rb +121 -0
  45. data/lib/ruby_smb/dcerpc/drsr/drs_get_nc_changes_response.rb +118 -0
  46. data/lib/ruby_smb/dcerpc/drsr/drs_unbind_request.rb +24 -0
  47. data/lib/ruby_smb/dcerpc/drsr/drs_unbind_response.rb +26 -0
  48. data/lib/ruby_smb/dcerpc/drsr.rb +909 -0
  49. data/lib/ruby_smb/dcerpc/epm/epm_ept_map_request.rb +26 -0
  50. data/lib/ruby_smb/dcerpc/epm/epm_ept_map_response.rb +25 -0
  51. data/lib/ruby_smb/dcerpc/epm/epm_twrt.rb +211 -0
  52. data/lib/ruby_smb/dcerpc/epm.rb +75 -0
  53. data/lib/ruby_smb/dcerpc/error.rb +20 -0
  54. data/lib/ruby_smb/dcerpc/ndr.rb +1204 -80
  55. data/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_request.rb +27 -0
  56. data/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_response.rb +26 -0
  57. data/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_request.rb +27 -0
  58. data/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_response.rb +23 -0
  59. data/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_request.rb +24 -0
  60. data/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_response.rb +24 -0
  61. data/lib/ruby_smb/dcerpc/netlogon.rb +102 -0
  62. data/lib/ruby_smb/dcerpc/p_syntax_id_t.rb +4 -3
  63. data/lib/ruby_smb/dcerpc/pdu_header.rb +7 -7
  64. data/lib/ruby_smb/dcerpc/ptypes.rb +1 -0
  65. data/lib/ruby_smb/dcerpc/request.rb +85 -19
  66. data/lib/ruby_smb/dcerpc/response.rb +45 -10
  67. data/lib/ruby_smb/dcerpc/rpc_auth3.rb +28 -0
  68. data/lib/ruby_smb/dcerpc/rpc_security_attributes.rb +34 -0
  69. data/lib/ruby_smb/dcerpc/rrp_rpc_unicode_string.rb +118 -0
  70. data/lib/ruby_smb/dcerpc/samr/rpc_sid.rb +150 -0
  71. data/lib/ruby_smb/dcerpc/samr/samr_close_handle_request.rb +23 -0
  72. data/lib/ruby_smb/dcerpc/samr/samr_close_handle_response.rb +24 -0
  73. data/lib/ruby_smb/dcerpc/samr/samr_connect_request.rb +32 -0
  74. data/lib/ruby_smb/dcerpc/samr/samr_connect_response.rb +23 -0
  75. data/lib/ruby_smb/dcerpc/samr/samr_enumerate_users_in_domain_request.rb +26 -0
  76. data/lib/ruby_smb/dcerpc/samr/samr_enumerate_users_in_domain_response.rb +55 -0
  77. data/lib/ruby_smb/dcerpc/samr/samr_get_alias_membership_request.rb +48 -0
  78. data/lib/ruby_smb/dcerpc/samr/samr_get_alias_membership_response.rb +38 -0
  79. data/lib/ruby_smb/dcerpc/samr/samr_get_groups_for_user_request.rb +23 -0
  80. data/lib/ruby_smb/dcerpc/samr/samr_get_groups_for_user_response.rb +48 -0
  81. data/lib/ruby_smb/dcerpc/samr/samr_lookup_domain_in_sam_server_request.rb +24 -0
  82. data/lib/ruby_smb/dcerpc/samr/samr_lookup_domain_in_sam_server_response.rb +25 -0
  83. data/lib/ruby_smb/dcerpc/samr/samr_open_domain_request.rb +27 -0
  84. data/lib/ruby_smb/dcerpc/samr/samr_open_domain_response.rb +24 -0
  85. data/lib/ruby_smb/dcerpc/samr/samr_open_user_request.rb +26 -0
  86. data/lib/ruby_smb/dcerpc/samr/samr_open_user_response.rb +24 -0
  87. data/lib/ruby_smb/dcerpc/samr/samr_rid_to_sid_request.rb +23 -0
  88. data/lib/ruby_smb/dcerpc/samr/samr_rid_to_sid_response.rb +23 -0
  89. data/lib/ruby_smb/dcerpc/samr.rb +613 -0
  90. data/lib/ruby_smb/dcerpc/sec_trailer.rb +26 -0
  91. data/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all.rb +56 -79
  92. data/lib/ruby_smb/dcerpc/srvsvc.rb +27 -4
  93. data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request.rb +36 -0
  94. data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response.rb +26 -0
  95. data/lib/ruby_smb/dcerpc/svcctl/close_service_handle_request.rb +25 -0
  96. data/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response.rb +26 -0
  97. data/lib/ruby_smb/dcerpc/svcctl/control_service_request.rb +26 -0
  98. data/lib/ruby_smb/dcerpc/svcctl/control_service_response.rb +26 -0
  99. data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request.rb +25 -0
  100. data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response.rb +23 -0
  101. data/lib/ruby_smb/dcerpc/svcctl/open_service_w_request.rb +23 -0
  102. data/lib/ruby_smb/dcerpc/svcctl/open_service_w_response.rb +23 -0
  103. data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request.rb +25 -0
  104. data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response.rb +45 -0
  105. data/lib/ruby_smb/dcerpc/svcctl/query_service_status_request.rb +23 -0
  106. data/lib/ruby_smb/dcerpc/svcctl/query_service_status_response.rb +27 -0
  107. data/lib/ruby_smb/dcerpc/svcctl/service_status.rb +26 -0
  108. data/lib/ruby_smb/dcerpc/svcctl/start_service_w_request.rb +27 -0
  109. data/lib/ruby_smb/dcerpc/svcctl/start_service_w_response.rb +25 -0
  110. data/lib/ruby_smb/dcerpc/svcctl.rb +477 -0
  111. data/lib/ruby_smb/dcerpc/uuid.rb +3 -0
  112. data/lib/ruby_smb/dcerpc/winreg/close_key_response.rb +2 -2
  113. data/lib/ruby_smb/dcerpc/winreg/create_key_request.rb +62 -0
  114. data/lib/ruby_smb/dcerpc/winreg/create_key_response.rb +36 -0
  115. data/lib/ruby_smb/dcerpc/winreg/enum_key_request.rb +2 -19
  116. data/lib/ruby_smb/dcerpc/winreg/enum_key_response.rb +3 -20
  117. data/lib/ruby_smb/dcerpc/winreg/enum_value_request.rb +5 -14
  118. data/lib/ruby_smb/dcerpc/winreg/enum_value_response.rb +5 -14
  119. data/lib/ruby_smb/dcerpc/winreg/open_key_request.rb +1 -9
  120. data/lib/ruby_smb/dcerpc/winreg/open_key_response.rb +4 -3
  121. data/lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb +6 -7
  122. data/lib/ruby_smb/dcerpc/winreg/open_root_key_response.rb +2 -2
  123. data/lib/ruby_smb/dcerpc/winreg/query_info_key_request.rb +1 -1
  124. data/lib/ruby_smb/dcerpc/winreg/query_info_key_response.rb +9 -18
  125. data/lib/ruby_smb/dcerpc/winreg/query_value_request.rb +4 -13
  126. data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +7 -15
  127. data/lib/ruby_smb/dcerpc/winreg/regsam.rb +3 -1
  128. data/lib/ruby_smb/dcerpc/winreg/save_key_request.rb +28 -0
  129. data/lib/ruby_smb/dcerpc/winreg/save_key_response.rb +23 -0
  130. data/lib/ruby_smb/dcerpc/winreg.rb +99 -22
  131. data/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_request.rb +26 -0
  132. data/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_response.rb +88 -0
  133. data/lib/ruby_smb/dcerpc/wkssvc.rb +65 -0
  134. data/lib/ruby_smb/dcerpc.rb +45 -11
  135. data/lib/ruby_smb/dialect.rb +45 -0
  136. data/lib/ruby_smb/dispatcher/base.rb +1 -1
  137. data/lib/ruby_smb/dispatcher/socket.rb +2 -2
  138. data/lib/ruby_smb/error.rb +21 -5
  139. data/lib/ruby_smb/field/file_time.rb +1 -1
  140. data/lib/ruby_smb/field/string16.rb +5 -1
  141. data/lib/ruby_smb/field/stringz16.rb +17 -1
  142. data/lib/ruby_smb/generic_packet.rb +11 -1
  143. data/lib/ruby_smb/gss/provider/authenticator.rb +42 -0
  144. data/lib/ruby_smb/gss/provider/ntlm.rb +303 -0
  145. data/lib/ruby_smb/gss/provider.rb +35 -0
  146. data/lib/ruby_smb/gss.rb +56 -63
  147. data/lib/ruby_smb/nbss/session_header.rb +4 -4
  148. data/lib/ruby_smb/ntlm.rb +61 -0
  149. data/lib/ruby_smb/server/server_client/negotiation.rb +156 -0
  150. data/lib/ruby_smb/server/server_client/session_setup.rb +82 -0
  151. data/lib/ruby_smb/server/server_client.rb +162 -0
  152. data/lib/ruby_smb/server.rb +54 -0
  153. data/lib/ruby_smb/signing.rb +59 -0
  154. data/lib/ruby_smb/smb1/file.rb +10 -25
  155. data/lib/ruby_smb/smb1/packet/negotiate_response.rb +11 -11
  156. data/lib/ruby_smb/smb1/packet/negotiate_response_extended.rb +1 -1
  157. data/lib/ruby_smb/smb1/packet/session_setup_request.rb +1 -1
  158. data/lib/ruby_smb/smb1/packet/trans2/find_first2_response.rb +0 -1
  159. data/lib/ruby_smb/smb1/packet/trans2/find_next2_response.rb +0 -1
  160. data/lib/ruby_smb/smb1/packet/trans2/open2_response.rb +1 -2
  161. data/lib/ruby_smb/smb1/packet/trans2/set_file_information_response.rb +1 -13
  162. data/lib/ruby_smb/smb1/pipe.rb +12 -6
  163. data/lib/ruby_smb/smb1/tree.rb +13 -9
  164. data/lib/ruby_smb/smb2/file.rb +33 -33
  165. data/lib/ruby_smb/smb2/negotiate_context.rb +18 -2
  166. data/lib/ruby_smb/smb2/packet/compression_transform_header.rb +4 -0
  167. data/lib/ruby_smb/smb2/packet/negotiate_request.rb +9 -0
  168. data/lib/ruby_smb/smb2/packet/negotiate_response.rb +0 -1
  169. data/lib/ruby_smb/smb2/packet/session_setup_response.rb +2 -2
  170. data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +1 -1
  171. data/lib/ruby_smb/smb2/pipe.rb +13 -6
  172. data/lib/ruby_smb/smb2/tree.rb +21 -11
  173. data/lib/ruby_smb/smb2.rb +3 -1
  174. data/lib/ruby_smb/version.rb +1 -1
  175. data/lib/ruby_smb.rb +5 -3
  176. data/spec/lib/ruby_smb/client_spec.rb +199 -86
  177. data/spec/lib/ruby_smb/compression/lznt1_spec.rb +32 -0
  178. data/spec/lib/ruby_smb/dcerpc/bind_ack_spec.rb +69 -41
  179. data/spec/lib/ruby_smb/dcerpc/bind_spec.rb +75 -21
  180. data/spec/lib/ruby_smb/dcerpc/client_spec.rb +714 -0
  181. data/spec/lib/ruby_smb/dcerpc/drsr_spec.rb +2169 -0
  182. data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +3977 -239
  183. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_request_spec.rb +69 -0
  184. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_response_spec.rb +53 -0
  185. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_request_spec.rb +69 -0
  186. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_response_spec.rb +37 -0
  187. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_request_spec.rb +45 -0
  188. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_response_spec.rb +37 -0
  189. data/spec/lib/ruby_smb/dcerpc/p_syntax_id_t_spec.rb +18 -4
  190. data/spec/lib/ruby_smb/dcerpc/pdu_header_spec.rb +27 -1
  191. data/spec/lib/ruby_smb/dcerpc/request_spec.rb +76 -11
  192. data/spec/lib/ruby_smb/dcerpc/response_spec.rb +99 -9
  193. data/spec/lib/ruby_smb/dcerpc/rpc_auth3_spec.rb +75 -0
  194. data/spec/lib/ruby_smb/dcerpc/rpc_security_attributes_spec.rb +162 -0
  195. data/spec/lib/ruby_smb/dcerpc/rrp_rpc_unicode_string_spec.rb +340 -0
  196. data/spec/lib/ruby_smb/dcerpc/samr/rpc_sid_spec.rb +116 -0
  197. data/spec/lib/ruby_smb/dcerpc/samr/samr_close_handle_request_spec.rb +40 -0
  198. data/spec/lib/ruby_smb/dcerpc/samr/samr_close_handle_response_spec.rb +48 -0
  199. data/spec/lib/ruby_smb/dcerpc/samr/samr_connect_request_spec.rb +56 -0
  200. data/spec/lib/ruby_smb/dcerpc/samr/samr_connect_response_spec.rb +47 -0
  201. data/spec/lib/ruby_smb/dcerpc/samr/samr_enumerate_users_in_domain_request_spec.rb +63 -0
  202. data/spec/lib/ruby_smb/dcerpc/samr/samr_enumerate_users_in_domain_response_spec.rb +265 -0
  203. data/spec/lib/ruby_smb/dcerpc/samr/samr_lookup_domain_in_sam_server_request_spec.rb +52 -0
  204. data/spec/lib/ruby_smb/dcerpc/samr/samr_lookup_domain_in_sam_server_response_spec.rb +36 -0
  205. data/spec/lib/ruby_smb/dcerpc/samr/samr_open_domain_request_spec.rb +56 -0
  206. data/spec/lib/ruby_smb/dcerpc/samr/samr_open_domain_response_spec.rb +48 -0
  207. data/spec/lib/ruby_smb/dcerpc/samr/samr_rid_to_sid_request_spec.rb +48 -0
  208. data/spec/lib/ruby_smb/dcerpc/samr/samr_rid_to_sid_response_spec.rb +42 -0
  209. data/spec/lib/ruby_smb/dcerpc/samr_spec.rb +420 -0
  210. data/spec/lib/ruby_smb/dcerpc/sec_trailer_spec.rb +92 -0
  211. data/spec/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all_spec.rb +149 -110
  212. data/spec/lib/ruby_smb/dcerpc/srvsvc_spec.rb +21 -17
  213. data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request_spec.rb +168 -0
  214. data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response_spec.rb +38 -0
  215. data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_request_spec.rb +30 -0
  216. data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response_spec.rb +38 -0
  217. data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_request_spec.rb +39 -0
  218. data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_response_spec.rb +38 -0
  219. data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request_spec.rb +68 -0
  220. data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response_spec.rb +38 -0
  221. data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_request_spec.rb +53 -0
  222. data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_response_spec.rb +38 -0
  223. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request_spec.rb +38 -0
  224. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response_spec.rb +152 -0
  225. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_request_spec.rb +30 -0
  226. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_response_spec.rb +38 -0
  227. data/spec/lib/ruby_smb/dcerpc/svcctl/service_status_spec.rb +76 -0
  228. data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_request_spec.rb +47 -0
  229. data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_response_spec.rb +30 -0
  230. data/spec/lib/ruby_smb/dcerpc/svcctl_spec.rb +508 -0
  231. data/spec/lib/ruby_smb/dcerpc/uuid_spec.rb +15 -23
  232. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_response_spec.rb +2 -2
  233. data/spec/lib/ruby_smb/dcerpc/winreg/create_key_request_spec.rb +73 -0
  234. data/spec/lib/ruby_smb/dcerpc/winreg/create_key_response_spec.rb +44 -0
  235. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +4 -56
  236. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_response_spec.rb +4 -56
  237. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_request_spec.rb +10 -34
  238. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_response_spec.rb +10 -34
  239. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_request_spec.rb +2 -26
  240. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_response_spec.rb +2 -2
  241. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +17 -20
  242. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_response_spec.rb +2 -2
  243. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_request_spec.rb +0 -4
  244. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_response_spec.rb +20 -44
  245. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_request_spec.rb +8 -32
  246. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_response_spec.rb +18 -42
  247. data/spec/lib/ruby_smb/dcerpc/winreg/regsam_spec.rb +4 -0
  248. data/spec/lib/ruby_smb/dcerpc/winreg/save_key_request_spec.rb +45 -0
  249. data/spec/lib/ruby_smb/dcerpc/winreg/save_key_response_spec.rb +22 -0
  250. data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +219 -62
  251. data/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_request_spec.rb +43 -0
  252. data/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_response_spec.rb +410 -0
  253. data/spec/lib/ruby_smb/dcerpc/wkssvc_spec.rb +70 -0
  254. data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +10 -10
  255. data/spec/lib/ruby_smb/error_spec.rb +34 -5
  256. data/spec/lib/ruby_smb/field/string16_spec.rb +22 -0
  257. data/spec/lib/ruby_smb/field/stringz16_spec.rb +12 -0
  258. data/spec/lib/ruby_smb/generic_packet_spec.rb +7 -0
  259. data/spec/lib/ruby_smb/gss/provider/ntlm/account_spec.rb +32 -0
  260. data/spec/lib/ruby_smb/gss/provider/ntlm/authenticator_spec.rb +101 -0
  261. data/spec/lib/ruby_smb/gss/provider/ntlm/os_version_spec.rb +32 -0
  262. data/spec/lib/ruby_smb/gss/provider/ntlm_spec.rb +113 -0
  263. data/spec/lib/ruby_smb/nbss/session_header_spec.rb +4 -11
  264. data/spec/lib/ruby_smb/server/server_client_spec.rb +156 -0
  265. data/spec/lib/ruby_smb/server_spec.rb +32 -0
  266. data/spec/lib/ruby_smb/smb1/file_spec.rb +2 -4
  267. data/spec/lib/ruby_smb/smb1/packet/trans2/find_first2_response_spec.rb +0 -1
  268. data/spec/lib/ruby_smb/smb1/packet/trans2/find_next2_response_spec.rb +0 -1
  269. data/spec/lib/ruby_smb/smb1/packet/trans2/open2_response_spec.rb +0 -5
  270. data/spec/lib/ruby_smb/smb1/packet/trans2/set_file_information_response_spec.rb +0 -6
  271. data/spec/lib/ruby_smb/smb1/pipe_spec.rb +20 -14
  272. data/spec/lib/ruby_smb/smb1/tree_spec.rb +22 -0
  273. data/spec/lib/ruby_smb/smb2/file_spec.rb +61 -9
  274. data/spec/lib/ruby_smb/smb2/negotiate_context_spec.rb +2 -2
  275. data/spec/lib/ruby_smb/smb2/pipe_spec.rb +20 -14
  276. data/spec/lib/ruby_smb/smb2/tree_spec.rb +58 -1
  277. data/spec/spec_helper.rb +1 -1
  278. data/spec/support/bin_helper.rb +9 -0
  279. data.tar.gz.sig +0 -0
  280. metadata +229 -23
  281. metadata.gz.sig +0 -0
  282. data/.travis.yml +0 -6
  283. data/lib/ruby_smb/client/signing.rb +0 -64
  284. data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +0 -35
  285. data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +0 -98
@@ -1,410 +1,4148 @@
1
- RSpec.describe RubySMB::Dcerpc::Ndr::NdrTopLevelFullPointer do
2
- subject(:packet) do
3
- Class.new(described_class) do
4
- endian :little
5
- string :referent
6
- end.new
1
+ require 'ruby_smb/dcerpc/ndr'
2
+
3
+ RSpec.shared_examples 'a properly aligned field in a NdrStruct' do |field, alignment|
4
+ let(:test_struct_class) do
5
+ klass = Class.new(RubySMB::Dcerpc::Ndr::NdrStruct) do
6
+ default_parameters byte_align: alignment
7
+ ndr_char :a
8
+ end
9
+ klass.send(field.to_sym, :test)
10
+ klass
11
+ end
12
+ let(:test_struct) do
13
+ test_struct_class.new(a: 'A', test: test_value)
7
14
  end
15
+ let(:struct_bin) { "A#{"\x00" * (alignment - 1)}#{test_bin}".b }
8
16
 
9
- it { is_expected.to respond_to :referent_identifier }
17
+ it "is always #{alignment}-bytes aligned" do
18
+ (1..8).each do |i|
19
+ test_struct = test_struct_class.new(a: 'A' * i, test: test_value)
20
+ expect(test_struct.test.abs_offset % alignment).to eq(0)
21
+ end
22
+ end
23
+ it 'writes the expected binary stream with the correct padding bytes' do
24
+ expect(test_struct.to_binary_s).to eq(struct_bin)
25
+ end
26
+ it 'reads a padded binary stream' do
27
+ test_struct2 = test_struct_class.read(struct_bin)
28
+ expect(test_struct2).to eq(test_struct)
29
+ end
30
+ it 'reports the expected structure size' do
31
+ expect(test_struct.num_bytes).to eq(struct_bin.size)
32
+ end
33
+ end
10
34
 
11
- it 'is little endian' do
12
- expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
35
+ RSpec.shared_examples 'a properly aligned conformant structure in a NdrStruct' do |field, alignment|
36
+ let(:test_struct_class) do
37
+ klass = Class.new(RubySMB::Dcerpc::Ndr::NdrStruct) do
38
+ default_parameters byte_align: alignment
39
+ ndr_char :a
40
+ end
41
+ klass.send(field.to_sym, :test)
42
+ klass
43
+ end
44
+ let(:test_struct) do
45
+ test_struct_class.new(a: 'A', test: test_value)
46
+ end
47
+ let(:struct_bin) do
48
+ max_count = test_bin.slice!(0, 4)
49
+ offset = max_count.size + 1
50
+ align = (alignment - (offset % alignment)) % alignment
51
+ "#{max_count}A#{"\x00" * align}#{test_bin}".b
13
52
  end
14
53
 
15
- describe '#referent_identifier' do
16
- it 'is a 32-bit unsigned integer' do
17
- expect(packet.referent_identifier).to be_a BinData::Uint32le
54
+ it "is always #{alignment}-bytes aligned" do
55
+ (1..8).each do |i|
56
+ test_struct = test_struct_class.new(a: 'A' * i, test: test_value)
57
+ expect(test_struct.test.abs_offset % alignment).to eq(0)
18
58
  end
59
+ end
60
+ it 'writes the expected binary stream with the correct padding bytes' do
61
+ expect(test_struct.to_binary_s).to eq(struct_bin)
62
+ end
63
+ it 'reads a padded binary stream' do
64
+ test_struct2 = test_struct_class.read(struct_bin)
65
+ expect(test_struct2).to eq(test_struct)
66
+ end
67
+ it 'reports the expected structure size' do
68
+ expect(test_struct.num_bytes).to eq(struct_bin.size)
69
+ end
70
+ end
71
+
72
+ RSpec.describe RubySMB::Dcerpc::Ndr::NdrBoolean do
73
+ it 'is a BinData::Uint32le class' do
74
+ expect(described_class).to be < BinData::Uint32le
75
+ end
76
+ it 'has :byte_align parameter set to the expected value' do
77
+ expect(described_class.default_parameters[:byte_align]).to eq(4)
78
+ end
79
+
80
+ subject(:boolean) { described_class.new }
19
81
 
20
- it 'has an initial value of 0x00020000' do
21
- expect(packet.referent_identifier).to eq(0x00020000)
82
+ context 'with #new' do
83
+ it 'is false its default value' do
84
+ expect(boolean.new).to eq(false)
85
+ end
86
+ it 'sets true to true' do
87
+ expect(boolean.new(true)).to eq(true)
88
+ end
89
+ it 'sets false to false' do
90
+ expect(boolean.new(false)).to eq(false)
91
+ end
92
+ it 'sets 1 to true' do
93
+ expect(boolean.new(1)).to eq(true)
94
+ end
95
+ it 'sets 0 to false' do
96
+ expect(boolean.new(0)).to eq(false)
97
+ end
98
+ it 'sets any integer > 0 to true' do
99
+ expect(boolean.new(rand(1..1000))).to eq(true)
100
+ end
101
+ it 'raises an ArgumentError error when passing a String' do
102
+ expect { boolean.new("false") }.to raise_error(ArgumentError)
22
103
  end
23
104
  end
24
105
 
25
- describe '#get' do
26
- it 'returns 0 when #referent_identifier is 0' do
27
- packet.referent_identifier = 0
28
- expect(packet.get).to eq(0)
106
+ context 'with #assign' do
107
+ it 'sets true to true' do
108
+ boolean.assign(true)
109
+ expect(boolean).to eq(true)
110
+ end
111
+ it 'sets false to false' do
112
+ boolean.assign(false)
113
+ expect(boolean).to eq(false)
114
+ end
115
+ it 'sets 1 to true' do
116
+ boolean.assign(1)
117
+ expect(boolean).to eq(true)
118
+ end
119
+ it 'sets 0 to false' do
120
+ boolean.assign(0)
121
+ expect(boolean).to eq(false)
122
+ end
123
+ it 'sets any integer > 0 to true' do
124
+ boolean.assign(rand(1..100))
125
+ expect(boolean).to eq(true)
29
126
  end
127
+ it 'raises an ArgumentError error when passing a String' do
128
+ expect { boolean.assign("false") }.to raise_error(ArgumentError)
129
+ end
130
+ end
30
131
 
31
- it 'returns #referent when #referent_identifier is greater than 0' do
32
- packet.set('spec_test')
33
- expect(packet.get).to eq(packet.referent)
132
+ context 'when reading data' do
133
+ it 'sets false with an uint32 zero value' do
134
+ boolean.read("\x00\x00\x00\x00")
135
+ expect(boolean).to eq(false)
136
+ end
137
+ it 'sets true with an uint32 1 value' do
138
+ boolean.read("\x01\x00\x00\x00")
139
+ expect(boolean).to eq(true)
140
+ end
141
+ it 'sets true with any uint32 value > 0' do
142
+ boolean.read([rand(1..1000)].pack('L'))
143
+ expect(boolean).to eq(true)
144
+ end
145
+ it 'reads itself' do
146
+ expect(boolean.read(described_class.new(true).to_binary_s)).to eq(true)
34
147
  end
35
148
  end
36
149
 
37
- describe '#set' do
38
- context 'when the value is 0' do
39
- it 'sets #referent_identifier to 0' do
40
- packet.set(0)
41
- expect(packet.referent_identifier).to eq(0)
42
- end
150
+ context 'with #to_binary_s' do
151
+ it 'returns the expected true binary representation' do
152
+ boolean.assign(true)
153
+ expect(boolean.to_binary_s).to eq("\x01\x00\x00\x00")
43
154
  end
155
+ it 'returns the expected false binary representation' do
156
+ boolean.assign(false)
157
+ expect(boolean.to_binary_s).to eq("\x00\x00\x00\x00")
158
+ end
159
+ end
44
160
 
45
- context 'when the value is a string' do
46
- it 'sets #referent to the value' do
47
- str = 'spec_test'
48
- packet.set(str)
49
- expect(packet.referent).to eq(str)
50
- end
161
+ context 'when testing alignment in a NdrStruct' do
162
+ it_behaves_like 'a properly aligned field in a NdrStruct', described_class.bindata_name.to_sym, 4 do
163
+ let(:test_value) { 1 }
164
+ let(:test_bin) { "\x01\x00\x00\x00".b }
51
165
  end
52
166
  end
53
167
  end
54
168
 
55
- RSpec.describe RubySMB::Dcerpc::Ndr::NdrString do
56
- subject(:packet) { described_class.new }
169
+ RSpec.describe RubySMB::Dcerpc::Ndr::NdrChar do
170
+ it 'is a BinData::String class' do
171
+ expect(described_class).to be < BinData::String
172
+ end
173
+ it 'has :byte_align parameter set to the expected value' do
174
+ expect(described_class.default_parameters[:byte_align]).to eq(1)
175
+ end
57
176
 
58
- it { is_expected.to respond_to :max_count }
59
- it { is_expected.to respond_to :offset }
60
- it { is_expected.to respond_to :actual_count }
61
- it { is_expected.to respond_to :str }
177
+ subject(:char) { described_class.new }
62
178
 
63
- it 'is little endian' do
64
- expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
179
+ context 'with #new' do
180
+ it 'is "\x00" its default value' do
181
+ expect(char.new).to eq("\x00")
182
+ end
183
+ it 'sets a character' do
184
+ expect(char.new('A')).to eq('A')
185
+ end
186
+ it 'sets the first character when passing a String' do
187
+ expect(char.new('ABC')).to eq('A')
188
+ end
189
+ it 'does not set a value when passing anything else than a string' do
190
+ char.new(['ABC'])
191
+ expect(char).to eq("\x00")
192
+ char.new(67)
193
+ expect(char).to eq("\x00")
194
+ end
65
195
  end
66
196
 
67
- describe '#max_count' do
68
- it 'is a 32-bit unsigned integer' do
69
- expect(packet.max_count).to be_a BinData::Uint32le
197
+ context 'with #assign' do
198
+ it 'sets a character' do
199
+ char.assign('A')
200
+ expect(char).to eq('A')
201
+ end
202
+ it 'sets the first character when passing a String' do
203
+ char.assign('ABC')
204
+ expect(char).to eq('A')
205
+ end
206
+ it 'converts the argument to string and keeps the first character' do
207
+ char.assign(['ABC'])
208
+ expect(char).to eq('[')
209
+ char.assign(67)
210
+ expect(char).to eq('6')
70
211
  end
71
212
  end
72
213
 
73
- describe '#offset' do
74
- it 'is a 32-bit unsigned integer' do
75
- expect(packet.offset).to be_a BinData::Uint32le
214
+ context 'when reading data' do
215
+ it 'sets a character' do
216
+ char.read("\x41")
217
+ expect(char).to eq('A')
76
218
  end
77
-
78
- it 'has an initial valu of 0' do
79
- expect(packet.offset).to eq(0)
219
+ it 'reads itself' do
220
+ expect(char.read(described_class.new('A').to_binary_s)).to eq('A')
80
221
  end
81
222
  end
82
223
 
83
- describe '#actual_count' do
84
- it 'is a 32-bit unsigned integer' do
85
- expect(packet.actual_count).to be_a BinData::Uint32le
224
+ context 'with #to_binary_s' do
225
+ it 'returns the expected character binary representation' do
226
+ char.assign('A')
227
+ expect(char.to_binary_s).to eq("\x41")
86
228
  end
87
229
  end
88
230
 
89
- describe '#str' do
90
- it 'is a RubySMB::Field::Stringz16' do
91
- expect(packet.str).to be_a RubySMB::Field::Stringz16
231
+ context 'when testing alignment in a NdrStruct' do
232
+ it_behaves_like 'a properly aligned field in a NdrStruct', described_class.bindata_name.to_sym, 1 do
233
+ let(:test_value) { 'B' }
234
+ let(:test_bin) { 'B'.b }
92
235
  end
236
+ end
237
+ end
238
+
239
+ RSpec.describe RubySMB::Dcerpc::Ndr::NdrWideChar do
240
+ it 'is a RubySMB::Field::String16 class' do
241
+ expect(described_class).to be < RubySMB::Field::String16
242
+ end
243
+ it 'has :byte_align parameter set to the expected value' do
244
+ expect(described_class.default_parameters[:byte_align]).to eq(2)
245
+ end
246
+
247
+ subject(:char) { described_class.new }
93
248
 
94
- it 'exists if #actual_count is greater than 0' do
95
- packet.actual_count = 4
96
- expect(packet.str?).to be true
249
+ context 'with #new' do
250
+ it 'is "\x00" its default value' do
251
+ expect(char.new).to eq("\x00".encode('utf-16le'))
97
252
  end
253
+ it 'sets a character' do
254
+ expect(char.new('A')).to eq('A'.encode('utf-16le'))
255
+ end
256
+ it 'sets the first character when passing a String' do
257
+ expect(char.new('ABC')).to eq('A'.encode('utf-16le'))
258
+ end
259
+ it 'does not set a value when passing anything else than a string' do
260
+ char.new(['ABC'])
261
+ expect(char).to eq("\x00".encode('utf-16le'))
262
+ char.new(67)
263
+ expect(char).to eq("\x00".encode('utf-16le'))
264
+ end
265
+ end
98
266
 
99
- it 'does not exist if #actual_count is 0' do
100
- expect(packet.str?).to be false
267
+ context 'with #assign' do
268
+ it 'sets a character' do
269
+ char.assign('A')
270
+ expect(char).to eq('A'.encode('utf-16le'))
271
+ end
272
+ it 'sets the first character when passing a String' do
273
+ char.assign('ABC')
274
+ expect(char).to eq('A'.encode('utf-16le'))
275
+ end
276
+ it 'converts the argument to string and keeps the first character' do
277
+ char.assign(['ABC'])
278
+ expect(char).to eq('['.encode('utf-16le'))
279
+ char.assign(67)
280
+ expect(char).to eq('6'.encode('utf-16le'))
101
281
  end
102
282
  end
103
283
 
104
- describe '#get' do
105
- it 'returns 0 when #actual_count is 0' do
106
- expect(packet.get).to eq(0)
284
+ context 'when reading data' do
285
+ it 'sets a character' do
286
+ char.read("\x41\x00")
287
+ expect(char).to eq('A'.encode('utf-16le'))
107
288
  end
289
+ it 'reads itself' do
290
+ expect(char.read(described_class.new('A').to_binary_s)).to eq('A'.encode('utf-16le'))
291
+ end
292
+ end
108
293
 
109
- it 'returns #str when #actual_count is greater than 0' do
110
- str = 'spec_test'
111
- strz16 = RubySMB::Field::Stringz16.new(str)
112
- packet.set(str)
113
- expect(packet.get).to eq(strz16)
294
+ context 'with #to_binary_s' do
295
+ it 'returns the expected character binary representation' do
296
+ char.assign('A')
297
+ expect(char.to_binary_s).to eq("\x41\x00")
114
298
  end
115
299
  end
116
300
 
117
- describe '#set' do
118
- context 'when the value is 0' do
119
- it 'sets #actual_count to 0' do
120
- packet.set(0)
121
- expect(packet.actual_count).to eq(0)
122
- end
301
+ context 'when testing alignment in a NdrStruct' do
302
+ it_behaves_like 'a properly aligned field in a NdrStruct', described_class.bindata_name.to_sym, 2 do
303
+ let(:test_value) { 'B' }
304
+ let(:test_bin) { "B\x00".b }
123
305
  end
306
+ end
307
+ end
124
308
 
125
- context 'when the value is a string' do
126
- let(:str) { 'spec_test' }
309
+ RSpec.describe RubySMB::Dcerpc::Ndr::NdrEnum do
310
+ it 'is a BinData::Int16le class' do
311
+ expect(described_class).to be < BinData::Int16le
312
+ end
313
+ it 'has :byte_align parameter set to the expected value' do
314
+ expect(described_class.default_parameters[:byte_align]).to eq(2)
315
+ end
127
316
 
128
- it 'sets #str to the value' do
129
- packet.set(str)
130
- strz16 = RubySMB::Field::Stringz16.new(str)
131
- expect(packet.str).to eq(strz16)
132
- end
317
+ context 'when testing alignment in a NdrStruct' do
318
+ it_behaves_like 'a properly aligned field in a NdrStruct', described_class.bindata_name.to_sym, 2 do
319
+ let(:test_value) { 1 }
320
+ let(:test_bin) { "\x01\x00".b }
321
+ end
322
+ end
323
+ end
324
+
325
+ {
326
+ NdrUint8: { parent_class: :Uint8, nb_bytes: 1},
327
+ NdrUint16: { parent_class: :Uint16le, nb_bytes: 2},
328
+ NdrUint32: { parent_class: :Uint32le, nb_bytes: 4},
329
+ NdrUint64: { parent_class: :Uint64le, nb_bytes: 8},
330
+ }.each do |klass, info|
331
+ full_klass = RubySMB::Dcerpc::Ndr.const_get(klass)
332
+ RSpec.describe(full_klass) do
333
+ it "is a BinData::#{info[:parent_class]} class" do
334
+ expect(described_class).to be < BinData.const_get(info[:parent_class])
335
+ end
336
+ it 'has :byte_align parameter set to the expected value' do
337
+ expect(described_class.default_parameters[:byte_align]).to eq(info[:nb_bytes])
338
+ end
133
339
 
134
- it 'sets #max_count and #actual_count to the expected value' do
135
- packet.set(str)
136
- expect(packet.max_count).to eq(str.length + 1)
137
- expect(packet.actual_count).to eq(str.length + 1)
340
+ context 'when testing alignment in a NdrStruct' do
341
+ it_behaves_like 'a properly aligned field in a NdrStruct', full_klass.bindata_name.to_sym, info[:nb_bytes] do
342
+ let(:test_value) { 1 }
343
+ let(:test_bin) { "\x01#{"\x00" * (info[:nb_bytes] - 1)}".b }
138
344
  end
139
345
  end
140
346
  end
141
347
  end
142
348
 
143
- RSpec.describe RubySMB::Dcerpc::Ndr::NdrLpStr do
144
- it 'is NdrTopLevelFullPointer subclass' do
145
- expect(described_class).to be < RubySMB::Dcerpc::Ndr::NdrTopLevelFullPointer
349
+ RSpec.describe RubySMB::Dcerpc::Ndr::NdrFileTime do
350
+ it 'is a RubySMB::Field::FileTime' do
351
+ expect(described_class).to be < RubySMB::Field::FileTime
352
+ end
353
+ it 'has :byte_align parameter set to the expected value' do
354
+ expect(described_class.default_parameters[:byte_align]).to eq(4)
355
+ end
356
+
357
+ context 'when testing alignment in a NdrStruct' do
358
+ it_behaves_like 'a properly aligned field in a NdrStruct', described_class.bindata_name.to_sym, 4 do
359
+ let(:test_value) { 132820620350000000 }
360
+ let(:test_bin) { "\x80\v\xCF\x86\xA6\xDF\xD7\x01" }
361
+ end
146
362
  end
363
+ end
364
+
365
+ #####################################
366
+ # NDR Constructed Types #
367
+ #####################################
147
368
 
148
- subject(:packet) { described_class.new }
149
369
 
150
- it { is_expected.to respond_to :referent }
370
+ #
371
+ # Arrays
372
+ #
151
373
 
152
- it 'is little endian' do
153
- expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
374
+ RSpec.shared_examples "a BinData::Array" do
375
+ it 'is a BinData::Array class' do
376
+ expect(described_class).to be < BinData::Array
377
+ end
378
+ it 'is an empty array by default' do
379
+ expect(described_class.new(type: element_class)).to eq([])
154
380
  end
155
381
 
156
- describe '#referent' do
157
- it 'is a NdrString' do
158
- expect(packet.referent).to be_a RubySMB::Dcerpc::Ndr::NdrString
382
+ context 'with elements' do
383
+ it 'contains the expected element types' do
384
+ expect(subject.all? {|e| e.is_a?(element_class)}).to be true
159
385
  end
160
-
161
- it 'exists if superclass #referent_identifier is not zero' do
162
- expect(packet.referent?).to be true
386
+ it 'has the expected size' do
387
+ expect(subject.size).to eq(values.size)
163
388
  end
164
389
 
165
- it 'does not exist if superclass #referent_identifier is zero' do
166
- packet.referent_identifier = 0
167
- expect(packet.referent?).to be false
390
+ context 'when setting a value at index greater than the current number of elements' do
391
+ let(:index_offset) { 3 }
392
+ let(:new_element) { element_class.new(values[0]) }
393
+ before :example do
394
+ subject[values.size + index_offset] = new_element
395
+ end
396
+ it 'adds elements until it reaches the new index' do
397
+ expect(subject.size).to eq(values.size + index_offset + 1)
398
+ end
399
+ it 'sets the new elements to the element type default value' do
400
+ (values.size..(index_offset - 1)).each do |i|
401
+ expect(subject[i]).to eq(0)
402
+ end
403
+ expect(subject[values.size + index_offset]).to eq(new_element)
404
+ end
168
405
  end
169
- end
170
406
 
171
- describe '#to_s' do
172
- it 'returns "\0" when #referent_identifier is 0' do
173
- packet.referent_identifier = 0
174
- expect(packet.to_s).to eq("\0")
407
+ context 'when getting a value at index greater than the current number of elements' do
408
+ let(:index_offset) { 3 }
409
+ it 'adds elements until it reaches the new index' do
410
+ subject[values.size + index_offset]
411
+ expect(subject.size).to eq(values.size + index_offset + 1)
412
+ end
413
+ it 'sets the new elements to the element type default value' do
414
+ (values.size..index_offset).each do |i|
415
+ expect(subject[i]).to eq(element_class.new)
416
+ end
417
+ end
175
418
  end
176
419
 
177
- it 'returns #referent when #referent_identifier is greater than 0' do
178
- packet.set('spec_test')
179
- expect(packet.to_s).to eq(packet.referent)
420
+ context 'when assigning another array' do
421
+ let(:new_array) { [values[0], values[1], values[2]] }
422
+ before :example do
423
+ subject.assign(new_array)
424
+ end
425
+ it 'has the same size than the other array' do
426
+ expect(subject.size).to eq(new_array.size)
427
+ end
428
+ it 'replaces all the elements' do
429
+ new_array.each_with_index do |e, i|
430
+ expect(subject[i]).to eq(e)
431
+ end
432
+ end
180
433
  end
181
434
  end
182
435
  end
183
436
 
184
- RSpec.describe RubySMB::Dcerpc::Ndr::NdrContextHandle do
185
- let(:uuid) { 'c3bce70d-5155-472b-9f2f-b824e5fc9b60' }
186
- let(:attr) { 123 }
187
- subject(:packet) { described_class.new }
188
-
189
- it { is_expected.to respond_to :context_handle_attributes }
190
- it { is_expected.to respond_to :context_handle_uuid }
437
+ RSpec.shared_examples "a NDR Array" do |conformant:, varying:|
438
+ let(:max_count) { subject.to_binary_s.slice(0, 4).unpack('L' * 4)[0] }
439
+ let(:actual_count) do
440
+ index = conformant ? 8 : 4
441
+ subject.to_binary_s.slice(index, 4).unpack('L' * 4)[0]
442
+ end
443
+ let(:offset) do
444
+ index = conformant ? 4 : 0
445
+ subject.to_binary_s.slice(index, 4).unpack('L' * 4)[0]
446
+ end
191
447
 
192
- it 'is little endian' do
193
- expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
448
+ if conformant
449
+ it "has :max_count set to 0 (little endian uint32) by default" do
450
+ empty_array = described_class.new(type: element_class)
451
+ expect(empty_array.to_binary_s[0, 4]).to eq("\x00\x00\x00\x00")
452
+ end
453
+ end
454
+ if varying
455
+ it "has :offset and :actual_count set to 0 (little endian uint32) by default" do
456
+ empty_array = described_class.new(type: element_class)
457
+ # offset
458
+ expect(empty_array.to_binary_s[0, 4]).to eq("\x00\x00\x00\x00")
459
+ # actual_count
460
+ expect(empty_array.to_binary_s[4, 4]).to eq("\x00\x00\x00\x00")
461
+ end
462
+ end
463
+ it 'reads itself' do
464
+ expect(described_class.new(type: element_class).read(subject.to_binary_s)).to eq(values)
465
+ end
466
+ it 'has :byte_align parameter set to the largest alignment of the array element type and the size information type, if any' do
467
+ # minimum alignment is 4 bytes since arrays are prefixed with an uint32 size element for 32-bit NDR
468
+ align = [4, element_size].max
469
+ expect(subject.eval_parameter(:byte_align)).to eq(align)
194
470
  end
195
471
 
196
- describe '#context_handle_attributes' do
197
- it 'is a 32-bit unsigned integer' do
198
- expect(packet.context_handle_attributes).to be_a BinData::Uint32le
472
+ context 'when checking if its elements have :byte_align parameter set' do
473
+ # element independent
474
+ it 'does not raise error when the :byte_align parameter is set in the element class' do
475
+ test_element = Class.new(BinData::Record) do
476
+ default_parameters byte_align: 4
477
+ endian :little
478
+ uint32 :a
479
+ end
480
+ BinData::RegisteredClasses.register('test_element', test_element)
481
+ expect { described_class.new(type: :test_element, byte_align: 4) }.to_not raise_error
482
+ end
483
+ # element independent
484
+ it 'does not raise error when the :byte_align parameter is set during instantiation' do
485
+ test_element = Class.new(BinData::Record) do
486
+ endian :little
487
+ uint32 :a
488
+ end
489
+ BinData::RegisteredClasses.register('test_element', test_element)
490
+ expect { described_class.new(type: [:test_element, {byte_align: 4}], byte_align: 4) }.to_not raise_error
491
+ end
492
+ context 'with a NDR element' do
493
+ # element independent
494
+ it 'does not raise error when the type element is a symbol' do
495
+ expect { described_class.new(type: :ndr_uint32, byte_align: 4) }.to_not raise_error
496
+ end
497
+ # element independent
498
+ it 'does not raise error when the type element is a class' do
499
+ expect { described_class.new(type: RubySMB::Dcerpc::Ndr::NdrUint32, byte_align: 4) }.to_not raise_error
500
+ end
501
+ end
502
+ # element independent
503
+ it 'raises an ArgumentError when the element has no :byte_align' do
504
+ test_element = Class.new(BinData::Record) do
505
+ endian :little
506
+ uint32 :a
507
+ end
508
+ BinData::RegisteredClasses.register('test_element', test_element)
509
+ expect { described_class.new(type: :test_element, byte_align: 4) }.to raise_error(ArgumentError)
510
+ end
511
+ # element independent
512
+ it 'raises an ArgumentError when the element has a parameter different than :byte_align' do
513
+ test_element = Class.new(BinData::Array) do
514
+ default_parameters type: :uint8
515
+ end
516
+ BinData::RegisteredClasses.register('test_element', test_element)
517
+ expect { described_class.new(type: [:test_element, {other_param: 1}], byte_align: 4) }.to raise_error(ArgumentError)
199
518
  end
200
519
  end
201
520
 
202
- describe '#context_handle_uuid' do
203
- it 'is a UUID' do
204
- expect(packet.context_handle_uuid).to be_a RubySMB::Dcerpc::Uuid
521
+ context 'with size information' do
522
+ if conformant
523
+ it "sets :max_count to a little endian uint32 value representing the number of elements" do
524
+ expect(max_count).to eq(subject.size)
525
+ end
526
+ it "updates :max_count when adding one element" do
527
+ subject << values[0]
528
+ expect(max_count).to eq(subject.size)
529
+ end
530
+ context 'when setting a value at index greater than the current number of elements' do
531
+ it "sets :max_count to the new number of elements" do
532
+ index_offset = rand(10)
533
+ subject[values.size + index_offset] = values[0]
534
+ expect(max_count).to eq(values.size + index_offset + 1)
535
+ end
536
+ end
537
+ context 'when reading a value at index greater than the current number of elements' do
538
+ it "sets :max_count to the new number of elements" do
539
+ index_offset = rand(10)
540
+ subject[values.size + index_offset]
541
+ expect(max_count).to eq(values.size + index_offset + 1)
542
+ end
543
+ end
544
+ context 'when assigning another array' do
545
+ it "sets :max_count to the new number of elements" do
546
+ new_size = rand(10)
547
+ new_array = new_size.times.map { values[0] }
548
+ subject.assign(new_array)
549
+ expect(max_count).to eq(new_size)
550
+ end
551
+ end
552
+ end
553
+
554
+ if varying
555
+ it "sets :actual_count to a little endian uint32 value representing the number of elements" do
556
+ expect(actual_count).to eq(subject.size)
557
+ end
558
+ it "updates :actual_count when adding one element" do
559
+ subject << values[0]
560
+ expect(actual_count).to eq(subject.size)
561
+ end
562
+ it 'has offset always set to 0' do
563
+ expect(offset).to eq(0)
564
+ subject << values[0]
565
+ expect(offset).to eq(0)
566
+ end
567
+ context 'when setting a value at index greater than the current number of elements' do
568
+ it "sets :actual_count to the new number of elements" do
569
+ index_offset = rand(10)
570
+ subject[values.size + index_offset] = values[0]
571
+ expect(actual_count).to eq(values.size + index_offset + 1)
572
+ end
573
+ end
574
+ context 'when reading a value at index greater than the current number of elements' do
575
+ it "sets :actual_count to the new number of elements" do
576
+ index_offset = rand(10)
577
+ subject[values.size + index_offset]
578
+ expect(actual_count).to eq(values.size + index_offset + 1)
579
+ end
580
+ end
581
+ context 'when assigning another array' do
582
+ it "sets :actual_count to the new number of elements" do
583
+ new_size = rand(10)
584
+ new_array = new_size.times.map { values[0] }
585
+ subject.assign(new_array)
586
+ expect(actual_count).to eq(new_size)
587
+ end
588
+ end
205
589
  end
206
590
  end
207
591
 
208
- describe '#get' do
209
- it 'returns the expeted hash' do
210
- packet.context_handle_attributes = attr
211
- packet.context_handle_uuid = uuid
212
- expect(packet.get).to eq({context_handle_attributes: attr, context_handle_uuid: uuid})
592
+ context 'when reading a binary stream' do
593
+ subject { described_class.new(type: element_class).read(binary_stream) }
594
+
595
+ it 'has the expected size' do
596
+ expect(subject.size).to eq(values.size)
597
+ end
598
+ it 'sets the new element values' do
599
+ values.each_with_index do |value, i|
600
+ expect(subject[i]).to eq(value)
601
+ end
602
+ end
603
+ it 'has the same binary representation than the original binary stream' do
604
+ expect(subject.to_binary_s).to eq(binary_stream)
605
+ end
606
+ if conformant
607
+ it "sets :max_count to the new number of elements" do
608
+ expect(max_count).to eq(values.size)
609
+ end
610
+ end
611
+ if varying
612
+ it "sets :actual_count to the new number of elements" do
613
+ expect(actual_count).to eq(values.size)
614
+ end
615
+ end
616
+ it 'sets @read_until_index to the number of elements' do
617
+ expect(subject.read_until_index).to eq(values.size)
618
+ end
619
+ context 'with an empty array' do
620
+ let(:empty_binary_str) do
621
+ str = ''
622
+ str << "\x00\x00\x00\x00" if conformant
623
+ str << "\x00\x00\x00\x00\x00\x00\x00\x00" if varying
624
+ str
625
+ end
626
+ subject { described_class.new(type: element_class).read(empty_binary_str) }
627
+
628
+ it 'is an empty array' do
629
+ expect(subject).to eq([])
630
+ end
631
+ it 'sets @read_until_index to 0' do
632
+ expect(subject.read_until_index).to eq(0)
633
+ end
634
+ if conformant
635
+ it "sets :max_count to 0" do
636
+ expect(max_count).to eq(0)
637
+ end
638
+ end
639
+ if varying
640
+ it "sets :actual_count to 0" do
641
+ expect(actual_count).to eq(0)
642
+ end
643
+ end
213
644
  end
214
645
  end
215
646
 
216
- describe '#set' do
217
- let(:handle) { {context_handle_attributes: attr, context_handle_uuid: uuid} }
647
+ context 'when getting a binary stream' do
648
+ it 'outputs the expected binary' do
649
+ expect(subject.to_binary_s).to eq(binary_stream)
650
+ end
651
+ context 'with an empty array' do
652
+ let(:empty_binary_str) do
653
+ str = ''
654
+ str << "\x00\x00\x00\x00" if conformant
655
+ str << "\x00\x00\x00\x00\x00\x00\x00\x00" if varying
656
+ str
657
+ end
658
+ subject { described_class.new(type: element_class).assign([]) }
218
659
 
219
- context 'when the value is a hash' do
220
- it 'sets #context_handle_attributes and #context_handle_uuid to the expected values' do
221
- packet.set(handle)
222
- expect(packet.context_handle_attributes).to eq(attr)
223
- expect(packet.context_handle_uuid).to eq(uuid)
660
+ it 'outputs the expected binary' do
661
+ expect(subject.to_binary_s).to eq(empty_binary_str)
224
662
  end
225
663
  end
664
+ end
665
+
666
+ context 'when calling #do_num_bytes' do
667
+ it 'returns the expected number of bytes' do
668
+ expect(subject.do_num_bytes).to eq(binary_stream.size)
669
+ end
670
+ context 'with NDR structures' do
671
+ let(:array_with_struct) do
672
+ described_class.new([{a: 2, b: 'A'}, {a: 3, b: 'B'}], type: :test_struct)
673
+ end
674
+ before :example do
675
+ test_struct = Class.new(RubySMB::Dcerpc::Ndr::NdrStruct) do
676
+ default_parameters byte_align: 4
677
+ ndr_uint32 :a
678
+ ndr_char :b
679
+ end
680
+ BinData::RegisteredClasses.register('test_struct', test_struct)
681
+ end
226
682
 
227
- context 'when the value is a NdrContextHandle'do
228
- it 'reads the value binary representaion ' do
229
- ndr_context_handle = described_class.new(handle)
230
- allow(ndr_context_handle).to receive(:to_binary_s).and_call_original
231
- packet.set(ndr_context_handle)
232
- expect(ndr_context_handle).to have_received(:to_binary_s)
233
- expect(packet.get).to eq(ndr_context_handle)
683
+ it 'returns the expected number of bytes, including padding' do
684
+ uint32_size = 4
685
+ char_size = 1
686
+ pad_size = 3
687
+ expected_nb = (uint32_size + char_size) * 2
688
+ expected_nb += pad_size
689
+ expected_nb += 4 if conformant
690
+ expected_nb += 8 if varying
691
+ expect(array_with_struct.do_num_bytes).to eq(expected_nb)
234
692
  end
235
693
  end
694
+ end
695
+
696
+ {
697
+ NdrUint8: { align: 1, data: rand(0xFF), pack: 'C' },
698
+ NdrUint16: { align: 2, data: rand(0xFFFF), pack: 'S<' },
699
+ NdrUint32: { align: 4, data: rand(0xFFFFFFFF), pack: 'L<' },
700
+ NdrUint64: { align: 8, data: rand(0xFFFFFFFFFFFFFFFF), pack: 'Q<'},
701
+ NdrChar: { align: 1, data: 'A', binary: 'A'.b },
702
+ NdrVarString: { align: 4, data: 'AAAAA'.encode('ASCII-8BIT'), binary: "#{[0].pack('L')}#{[5].pack('L')}AAAAA".b},
703
+ NdrVarStringz: { align: 4, data: 'AAAAA'.encode('ASCII-8BIT'), binary: "#{[0].pack('L')}#{[6].pack('L')}AAAAA\x00".b},
704
+ NdrVarWideString: { align: 4, data: 'AAAAA'.encode('UTF-16LE'), binary: "#{[0].pack('L')}#{[5].pack('L')}A\x00A\x00A\x00A\x00A\x00".b},
705
+ NdrVarWideStringz: { align: 4, data: 'AAAAA'.encode('UTF-16LE'), binary: "#{[0].pack('L')}#{[6].pack('L')}A\x00A\x00A\x00A\x00A\x00\x00\x00".b},
706
+ }.each do |klass, info|
707
+ context "when it is embedded in a NDR structure and preceeded by a #{klass}" do
708
+ let(:field_class) { RubySMB::Dcerpc::Ndr.const_get(klass) }
709
+ let(:field_obj) { field_class.new(info[:data]) }
710
+ let(:embedded_struct) do
711
+ byte_align = struct_max_align
712
+ embedded_struct = Class.new(RubySMB::Dcerpc::Ndr::NdrStruct) do
713
+ default_parameters(byte_align: byte_align)
714
+ end
715
+ embedded_struct.send(field_class.bindata_name.to_sym, :a)
716
+ embedded_struct
717
+ end
718
+
719
+ context 'directly as an array' do
720
+ before :example do
721
+ embedded_struct.send(described_class.bindata_name.to_sym, :ary, type: element_class)
722
+ end
723
+ let(:array_max_align) { (varying && element_size < 4) ? 4 : element_size }
724
+ let(:struct_max_align) { [array_max_align, info[:align]].max }
725
+ let(:test_instance) { embedded_struct.new(a: field_obj, ary: values) }
726
+ let(:binary_str) do
727
+ binary_str = "".b
728
+ if conformant
729
+ binary_str << binary_stream.slice!(0, 4)
730
+ end
731
+ pad_length = (struct_max_align - (binary_str.size % struct_max_align)) % struct_max_align
732
+ binary_str << "\x00" * pad_length
733
+ if info[:data].is_a?(String)
734
+ binary_str << info[:binary]
735
+ else
736
+ binary_str << "#{[info[:data]].pack(info[:pack])}"
737
+ end
738
+ if varying
739
+ pad_length = (array_max_align - (binary_str.size % array_max_align)) % array_max_align
740
+ binary_str << "\x00" * pad_length
741
+ binary_str << binary_stream.slice!(0, 8)
742
+ end
743
+ pad_length = (element_size - (binary_str.size % element_size)) % element_size
744
+ binary_str << "\x00" * pad_length
745
+ binary_str << bin_values
746
+ binary_str
747
+ end
748
+
749
+ context 'when writing the stream of bytes' do
750
+ it 'outputs the expect binary' do
751
+ expect(test_instance.to_binary_s).to eq(binary_str)
752
+ end
753
+ end
754
+
755
+ context 'when reading a stream of bytes' do
756
+ it 'outputs the expected structure' do
757
+ expect(embedded_struct.read(binary_str)).to eq({ a: info[:data], ary: values })
758
+ end
759
+ end
760
+
761
+ context 'when calling #num_bytes' do
762
+ it 'outputs the expected number of bytes' do
763
+ expect(test_instance.num_bytes).to eq(binary_str.size)
764
+ end
765
+ end
766
+ end
767
+
768
+ context 'as a pointer to an array' do
769
+ before :example do
770
+ array_ptr = Class.new(described_class) do
771
+ extend RubySMB::Dcerpc::Ndr::PointerClassPlugin
772
+ end
773
+ BinData::RegisteredClasses.register('test_array_ptr', array_ptr)
774
+ embedded_struct.send(:test_array_ptr, :ary_ptr, type: element_class)
775
+ end
776
+ let(:struct_max_align) { [4, info[:align]].max }
777
+ let(:test_instance) { embedded_struct.new(a: field_obj, ary_ptr: values) }
778
+ let(:binary_str) do
779
+ binary_str = "".b
780
+ if info[:data].is_a?(String)
781
+ binary_str << info[:binary]
782
+ else
783
+ binary_str << "#{[info[:data]].pack(info[:pack])}"
784
+ end
785
+ pad_length = (4 - (binary_str.size % 4)) % 4
786
+ binary_str << "\x00" * pad_length
787
+ ref_id = [RubySMB::Dcerpc::Ndr::INITIAL_REF_ID].pack('L<')
788
+ binary_str << ref_id
789
+ if conformant
790
+ binary_str << binary_stream.slice!(0, 4)
791
+ end
792
+ if varying
793
+ binary_str << binary_stream.slice!(0, 8)
794
+ end
795
+ pad_length = (element_size - (binary_str.size % element_size)) % element_size
796
+ binary_str << "\x00" * pad_length
797
+ binary_str << bin_values
798
+ binary_str
799
+ end
800
+
801
+ context 'when writing the stream of bytes' do
802
+ it 'outputs the expect binary' do
803
+ expect(test_instance.to_binary_s).to eq(binary_str)
804
+ end
805
+ end
236
806
 
237
- context 'when the value is a binary string'do
238
- it 'reads the value' do
239
- ndr_context_handle = described_class.new(handle)
240
- packet.set(ndr_context_handle.to_binary_s)
241
- expect(packet.get).to eq(ndr_context_handle)
807
+ context 'when reading a stream of bytes' do
808
+ it 'outputs the expected structure' do
809
+ expect(embedded_struct.read(binary_str)).to eq({ a: info[:data], ary_ptr: values })
810
+ end
811
+ end
812
+
813
+ context 'when calling #num_bytes' do
814
+ it 'outputs the expected number of bytes' do
815
+ expect(test_instance.num_bytes).to eq(binary_str.size)
816
+ end
817
+ end
818
+ end
819
+
820
+ context 'embedded in another structure' do
821
+ before :example do
822
+ byte_align = struct_max_align
823
+ embedded_struct2 = Class.new(RubySMB::Dcerpc::Ndr::NdrStruct) do
824
+ default_parameters(byte_align: byte_align)
825
+ end
826
+ embedded_struct2.send(field_class.bindata_name.to_sym, :a)
827
+ embedded_struct2.send(described_class.bindata_name.to_sym, :ary, type: element_class)
828
+ BinData::RegisteredClasses.register('embedded_struct_2nd_lvl', embedded_struct2)
829
+ embedded_struct.send(:embedded_struct_2nd_lvl, :struct2)
830
+ end
831
+ let(:array_max_align) { (varying && element_size < 4) ? 4 : element_size }
832
+ let(:struct2_max_align) { [array_max_align, info[:align]].max }
833
+ let(:struct_max_align) { [struct2_max_align, info[:align]].max }
834
+ let(:test_instance) { embedded_struct.new(a: field_obj, struct2: { a: field_obj, ary: values }) }
835
+ let(:binary_str) do
836
+ binary_str = "".b
837
+ if conformant
838
+ binary_str << binary_stream.slice!(0, 4)
839
+ end
840
+ # a
841
+ pad_length = (struct_max_align - (binary_str.size % struct_max_align)) % struct_max_align
842
+ binary_str << "\x00" * pad_length
843
+ if info[:data].is_a?(String)
844
+ binary_str << info[:binary]
845
+ else
846
+ binary_str << "#{[info[:data]].pack(info[:pack])}"
847
+ end
848
+ # struct2.a
849
+ pad_length = (struct2_max_align - (binary_str.size % struct2_max_align)) % struct2_max_align
850
+ binary_str << "\x00" * pad_length
851
+ if info[:data].is_a?(String)
852
+ binary_str << info[:binary]
853
+ else
854
+ binary_str << "#{[info[:data]].pack(info[:pack])}"
855
+ end
856
+ # struct2.ary
857
+ if varying
858
+ pad_length = (array_max_align - (binary_str.size % array_max_align)) % array_max_align
859
+ binary_str << "\x00" * pad_length
860
+ binary_str << binary_stream.slice!(0, 8)
861
+ end
862
+ pad_length = (element_size - (binary_str.size % element_size)) % element_size
863
+ binary_str << "\x00" * pad_length
864
+ binary_str << bin_values
865
+ binary_str
866
+ end
867
+
868
+ context 'when writing the stream of bytes' do
869
+ it 'outputs the expect binary' do
870
+ expect(test_instance.to_binary_s).to eq(binary_str)
871
+ end
872
+ end
873
+
874
+ context 'when reading a stream of bytes' do
875
+ it 'outputs the expected structure' do
876
+ expect(embedded_struct.read(binary_str)).to eq({ a: field_obj, struct2: { a: field_obj, ary: values } })
877
+ end
878
+ end
879
+
880
+ context 'when calling #num_bytes' do
881
+ it 'outputs the expected number of bytes' do
882
+ expect(test_instance.num_bytes).to eq(binary_str.size)
883
+ end
884
+ end
885
+ end
886
+
887
+ unless conformant
888
+ {
889
+ NdrUint8: { size: 1 },
890
+ NdrUint16: { size: 2 },
891
+ NdrUint32: { size: 4 },
892
+ NdrUint64: { size: 8 }
893
+ }.each do |klass2, info2|
894
+ context "directly as an array and followed by a #{klass2}" do
895
+ before :example do
896
+ embedded_struct.send(described_class.bindata_name.to_sym, :ary, type: element_class)
897
+ embedded_struct.send(field_class2.bindata_name.to_sym, :b)
898
+ end
899
+ let(:field_class2) { RubySMB::Dcerpc::Ndr.const_get(klass2) }
900
+ let(:field_obj2) { field_class2.new(rand(0xFF)) }
901
+ let(:array_max_align) { element_size < 4 ? 4 : element_size }
902
+ let(:struct_max_align) { [array_max_align, info[:align], info2[:size]].max }
903
+ let(:test_instance) { embedded_struct.new(a: field_obj, ary: values, b: field_obj2) }
904
+ let(:binary_str) do
905
+ binary_str = "".b
906
+ # a
907
+ if info[:data].is_a?(String)
908
+ binary_str << info[:binary]
909
+ else
910
+ binary_str << "#{[info[:data]].pack(info[:pack])}"
911
+ end
912
+ # ary
913
+ pad_length = (array_max_align - (binary_str.size % array_max_align)) % array_max_align
914
+ binary_str << "\x00" * pad_length
915
+ binary_str << binary_stream.slice!(0, 8)
916
+ pad_length = (element_size - (binary_str.size % element_size)) % element_size
917
+ binary_str << "\x00" * pad_length
918
+ binary_str << bin_values
919
+ # b
920
+ pad_length = (info2[:size] - (binary_str.size % info2[:size])) % info2[:size]
921
+ binary_str << "\x00" * pad_length
922
+ binary_str << field_obj2.to_binary_s
923
+ binary_str
924
+ end
925
+
926
+ context 'when writing the stream of bytes' do
927
+ it 'outputs the expect binary' do
928
+ expect(test_instance.to_binary_s).to eq(binary_str)
929
+ end
930
+ end
931
+
932
+ context 'when reading a stream of bytes' do
933
+ it 'outputs the expected structure' do
934
+ expect(embedded_struct.read(binary_str)).to eq({ a: info[:data], ary: values, b: field_obj2.to_i })
935
+ end
936
+ end
937
+
938
+ context 'when calling #num_bytes' do
939
+ it 'outputs the expected number of bytes' do
940
+ expect(test_instance.num_bytes).to eq(binary_str.size)
941
+ end
942
+ end
943
+ end
944
+ end
242
945
  end
243
946
  end
244
947
  end
245
948
  end
246
949
 
247
- RSpec.describe RubySMB::Dcerpc::Ndr::NdrLpDword do
248
- it 'is NdrTopLevelFullPointer subclass' do
249
- expect(described_class).to be < RubySMB::Dcerpc::Ndr::NdrTopLevelFullPointer
950
+ RSpec.describe RubySMB::Dcerpc::Ndr::NdrFixArray do
951
+ it 'is a BinData::Array class' do
952
+ expect(described_class).to be < BinData::Array
953
+ end
954
+ it 'is an empty array by default' do
955
+ expect(described_class.new(type: :ndr_uint16, byte_align: 2)).to eq([])
250
956
  end
251
957
 
252
- subject(:packet) { described_class.new }
958
+ subject { described_class.new(type: :ndr_uint16, byte_align: 2, initial_length: 4) }
253
959
 
254
- it { is_expected.to respond_to :referent }
960
+ it 'is an array of initial_length default elements' do
961
+ expect(subject).to eq([0, 0, 0, 0])
962
+ end
255
963
 
256
- it 'is little endian' do
257
- expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
964
+ context 'when checking if its elements have :byte_align parameter set' do
965
+ it 'does not raise error when the :byte_align parameter is set in the element class' do
966
+ test_element = Class.new(BinData::Record) do
967
+ default_parameters byte_align: 4
968
+ endian :little
969
+ uint32 :a
970
+ end
971
+ BinData::RegisteredClasses.register('test_element', test_element)
972
+ expect { described_class.new(type: :test_element, byte_align: 4) }.to_not raise_error
973
+ end
974
+ it 'does not raise error when the :byte_align parameter is set during instantiation' do
975
+ test_element = Class.new(BinData::Record) do
976
+ endian :little
977
+ uint32 :a
978
+ end
979
+ BinData::RegisteredClasses.register('test_element', test_element)
980
+ expect { described_class.new(type: [:test_element, {byte_align: 4}], byte_align: 4) }.to_not raise_error
981
+ end
982
+ context 'with a NDR element' do
983
+ it 'does not raise error when the type element is a symbol' do
984
+ expect { described_class.new(type: :ndr_uint32, byte_align: 4) }.to_not raise_error
985
+ end
986
+ it 'does not raise error when the type element is a class' do
987
+ expect { described_class.new(type: RubySMB::Dcerpc::Ndr::NdrUint32, byte_align: 4) }.to_not raise_error
988
+ end
989
+ end
990
+ it 'raises an ArgumentError when no :byte_align is provided' do
991
+ test_element = Class.new(BinData::Record) do
992
+ endian :little
993
+ uint32 :a
994
+ end
995
+ BinData::RegisteredClasses.register('test_element', test_element)
996
+ expect { described_class.new(type: :test_element, byte_align: 4) }.to raise_error(ArgumentError)
997
+ end
998
+ it 'raises an ArgumentError when other parameters than :byte_align are provided' do
999
+ test_element = Class.new(BinData::Array) do
1000
+ default_parameters type: :uint8
1001
+ end
1002
+ BinData::RegisteredClasses.register('test_element', test_element)
1003
+ expect { described_class.new(type: [:test_element, {other_param: 1}], byte_align: 4) }.to raise_error(ArgumentError)
1004
+ end
258
1005
  end
259
1006
 
260
- describe '#referent' do
261
- it 'is a 32-bit unsigned integer' do
262
- expect(packet.referent).to be_a BinData::Uint32le
1007
+ context 'with elements' do
1008
+ before :example do
1009
+ subject.assign([1, 2, 3, 4])
1010
+ end
1011
+ it 'contains the expected element types' do
1012
+ expect(subject.all? {|e| e.is_a?(BinData::Uint16le)}).to be true
1013
+ end
1014
+ it 'has the expected size' do
1015
+ expect(subject.size).to eq(4)
263
1016
  end
264
1017
 
265
- it 'exists if superclass #referent_identifier is not zero' do
266
- expect(packet.referent?).to be true
1018
+ context 'when setting a value to an element array' do
1019
+ context 'in the array bounds' do
1020
+ it 'does not raise error and sets the new value' do
1021
+ expect { subject[3] = 14 }.to_not raise_error
1022
+ expect(subject[3]).to eq(14)
1023
+ end
1024
+ end
1025
+ context 'outside of the array bounds' do
1026
+ it 'raises an error' do
1027
+ expect { subject[4] = 14 }.to raise_error(ArgumentError)
1028
+ end
1029
+ end
1030
+ end
1031
+ context 'when reading the value of an element array' do
1032
+ context 'in the array bounds' do
1033
+ it 'does not raise error and returns the value' do
1034
+ expect { subject[3] }.to_not raise_error
1035
+ expect(subject[3]).to eq(4)
1036
+ end
1037
+ end
1038
+ context 'outside of the array bounds' do
1039
+ it 'raises an error' do
1040
+ expect { subject[4] }.to raise_error(ArgumentError)
1041
+ end
1042
+ end
1043
+ end
1044
+ context 'when assigning another array' do
1045
+ context 'with the same number of elements' do
1046
+ it 'does not raise error and returns the value' do
1047
+ expect { subject.assign([5, 6, 7, 8]) }.to_not raise_error
1048
+ expect(subject).to eq([5, 6, 7, 8])
1049
+ end
1050
+ end
1051
+ context 'with more elements' do
1052
+ it 'raises an error' do
1053
+ expect { subject.assign([5, 6, 7, 8, 9]) }.to raise_error(ArgumentError)
1054
+ end
1055
+ end
1056
+ context 'with less elements' do
1057
+ it 'raises an error' do
1058
+ expect { subject.assign([5, 6, 7]) }.to raise_error(ArgumentError)
1059
+ end
1060
+ end
1061
+ end
1062
+ context 'when pushing a new element' do
1063
+ it 'raises an error' do
1064
+ expect { subject << 88 }.to raise_error(ArgumentError)
1065
+ end
1066
+ end
1067
+ context 'when unshifting a new element' do
1068
+ it 'raises an error' do
1069
+ expect { subject.unshift(88) }.to raise_error(ArgumentError)
1070
+ end
1071
+ end
1072
+ context 'when concatenating another array' do
1073
+ it 'raises an error' do
1074
+ expect { subject.concat([3,4]) }.to raise_error(ArgumentError)
1075
+ end
267
1076
  end
1077
+ context 'when cheking if :byte_align parameter is set' do
1078
+ it 'does not raise error when it is set' do
1079
+ expect { described_class.new(type: :ndr_uint16, byte_align: 2, initial_length: 4) }.to_not raise_error
1080
+ end
268
1081
 
269
- it 'does not exist if superclass #referent_identifier is zero' do
270
- packet.referent_identifier = 0
271
- expect(packet.referent?).to be false
1082
+ it 'raises an error when it is not set' do
1083
+ expect { described_class.new(type: :uint16le, initial_length: 4) }.to raise_error(ArgumentError)
1084
+ end
272
1085
  end
273
1086
  end
274
1087
  end
275
1088
 
276
- RSpec.describe RubySMB::Dcerpc::Ndr::NdrLpByte do
277
- subject(:packet) { described_class.new }
1089
+ RSpec.describe RubySMB::Dcerpc::Ndr::NdrFixedByteArray do
1090
+ it 'is a RubySMB::Dcerpc::Ndr::NdrFixArray class' do
1091
+ expect(described_class).to be < RubySMB::Dcerpc::Ndr::NdrFixArray
1092
+ end
1093
+ it 'has :byte_align parameter set to the expected value' do
1094
+ expect(described_class.default_parameters[:byte_align]).to eq(1)
1095
+ end
1096
+
1097
+ subject { described_class.new(initial_length: 4) }
278
1098
 
279
- it { is_expected.to respond_to :referent_identifier }
280
- it { is_expected.to respond_to :max_count }
281
- it { is_expected.to respond_to :offset }
282
- it { is_expected.to respond_to :actual_count }
283
- it { is_expected.to respond_to :bytes }
1099
+ it 'composed of NdrUint8 elements' do
1100
+ expect(subject[0]).to be_a( RubySMB::Dcerpc::Ndr::NdrUint8)
1101
+ end
284
1102
 
285
- it 'is little endian' do
286
- expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
1103
+ it 'has #initial_length elements' do
1104
+ expect(subject.size).to eq(4)
287
1105
  end
288
1106
 
289
- describe '#referent_identifier' do
290
- it 'is a 32-bit unsigned integer' do
291
- expect(packet.referent_identifier).to be_a BinData::Uint32le
1107
+ describe '#assign' do
1108
+ it 'assign elements from another array of bytes' do
1109
+ ary = [0x33, 0x43, 0x64, 0x22]
1110
+ subject.assign(ary)
1111
+ expect(subject).to eq(ary)
292
1112
  end
293
1113
 
294
- it 'has an initial value of 0x00020000' do
295
- expect(packet.referent_identifier).to eq(0x00020000)
1114
+ it 'assign bytes from string' do
1115
+ str = '_Str'
1116
+ subject.assign(str)
1117
+ expect(subject).to eq(str.bytes)
296
1118
  end
297
1119
  end
1120
+ end
298
1121
 
299
- describe '#max_count' do
300
- it 'is a 32-bit unsigned integer' do
301
- expect(packet.max_count).to be_a BinData::Uint32le
1122
+ {
1123
+ NdrUint8: { size: 1, data: rand(0xFF), pack: 'C' },
1124
+ NdrUint16: { size: 2, data: rand(0xFFFF), pack: 'S<' },
1125
+ NdrUint32: { size: 4, data: rand(0xFFFFFFFF), pack: 'L<' },
1126
+ NdrUint64: { size: 8, data: rand(0xFFFFFFFFFFFFFFFF), pack: 'Q<'},
1127
+ NdrChar: { size: 1, data: 'A', binary: 'A'.b },
1128
+ NdrVarString: { size: 4, data: 'AAAAA'.encode('ASCII-8BIT'), binary: "#{[0].pack('L')}#{[5].pack('L')}AAAAA".b},
1129
+ NdrVarStringz: { size: 4, data: 'AAAAA'.encode('ASCII-8BIT'), binary: "#{[0].pack('L')}#{[6].pack('L')}AAAAA\x00".b},
1130
+ NdrVarWideString: { size: 4, data: 'AAAAA'.encode('UTF-16LE'), binary: "#{[0].pack('L')}#{[5].pack('L')}A\x00A\x00A\x00A\x00A\x00".b},
1131
+ NdrVarWideStringz: { size: 4, data: 'AAAAA'.encode('UTF-16LE'), binary: "#{[0].pack('L')}#{[6].pack('L')}A\x00A\x00A\x00A\x00A\x00\x00\x00".b},
1132
+ }.each do |klass, info|
1133
+ RSpec.describe "NDR Array with #{klass}" do
1134
+ let(:element_size) { info[:size] }
1135
+ let(:element_class) { RubySMB::Dcerpc::Ndr.const_get(klass) }
1136
+ let(:values) { rand(3..16).times.map { info[:data] } }
1137
+ #let(:values) { [0xFF, 0xFF, 0xFF, 0xFF] }
1138
+ let(:bin_values) do
1139
+ if info[:data].is_a?(String)
1140
+ info[:binary] * values.size
1141
+ else
1142
+ values.map { |e| [e].pack(info[:pack]) }.join
1143
+ end
1144
+ end
1145
+ let(:binary_stream) do
1146
+ pad_length = (element_size - (size_info.size % element_size)) % element_size
1147
+ "#{size_info}#{"\x00" * pad_length}#{bin_values}".b
302
1148
  end
1149
+ subject { described_class.new(values, type: element_class) }
303
1150
 
304
- it 'has an initial value equal to #actual_count' do
305
- packet.actual_count = 345
306
- expect(packet.max_count).to eq(345)
1151
+ describe RubySMB::Dcerpc::Ndr::NdrConfArray do
1152
+ let(:size_info) { [values.size].pack('L<') }
1153
+ it_behaves_like 'a BinData::Array'
1154
+ it_behaves_like 'a NDR Array', conformant: true, varying: false
307
1155
  end
308
1156
 
309
- it 'exists if #referent_identifier is not zero' do
310
- expect(packet.max_count?).to be true
1157
+ describe RubySMB::Dcerpc::Ndr::NdrVarArray do
1158
+ let(:size_info) { "\x00\x00\x00\x00#{[values.size].pack('L<')}" }
1159
+ it_behaves_like 'a BinData::Array'
1160
+ it_behaves_like 'a NDR Array', conformant: false, varying: true
311
1161
  end
312
1162
 
313
- it 'does not exist if #referent_identifier is zero' do
314
- packet.referent_identifier = 0
315
- expect(packet.max_count?).to be false
1163
+ describe RubySMB::Dcerpc::Ndr::NdrConfVarArray do
1164
+ let(:size_info) { "#{[values.size].pack('L<')}\x00\x00\x00\x00#{[values.size].pack('L<')}" }
1165
+ it_behaves_like 'a BinData::Array'
1166
+ it_behaves_like 'a NDR Array', conformant: true, varying: true
316
1167
  end
317
1168
  end
1169
+ end
1170
+
1171
+
1172
+ #
1173
+ # Strings
1174
+ #
1175
+
1176
+ RSpec.shared_examples "a NDR String" do |conformant:, char_size:, null_terminated:|
1177
+ let(:first_char_offset) { conformant ? 12 : 8}
1178
+
1179
+ it 'has :byte_align parameter set to the expected value' do
1180
+ expect(described_class.default_parameters[:byte_align]).to eq(4)
1181
+ end
1182
+
1183
+ if conformant
1184
+ it_behaves_like "a Conformant Varying String", null_terminated: null_terminated
1185
+ else
1186
+ it_behaves_like "a Varying String", null_terminated: null_terminated
1187
+ end
1188
+
1189
+ it 'is an empty string by default' do
1190
+ expect(subject).to eq('')
1191
+ end
1192
+
1193
+ it 'has a binary representation of an empty string by default' do
1194
+ expect(subject.to_binary_s[first_char_offset..-1]).to be_empty
1195
+ end
1196
+
1197
+ it 'reads itself' do
1198
+ subject.assign(value)
1199
+ expect(subject.read(subject.to_binary_s)).to eq(value)
1200
+ end
318
1201
 
319
- describe '#offset' do
320
- it 'is a 32-bit unsigned integer' do
321
- expect(packet.offset).to be_a BinData::Uint32le
1202
+ context 'when reading another binary stream' do
1203
+ before :example do
1204
+ subject.read(binary_stream)
322
1205
  end
1206
+ it 'has the expected size' do
1207
+ expect(subject.size).to eq(value.size)
1208
+ end
1209
+ it 'sets the new element values' do
1210
+ expect(subject.to_s).to eq(value)
1211
+ end
1212
+ it 'has the same binary representation than the original binary stream' do
1213
+ expect(subject.to_binary_s).to eq(binary_stream)
1214
+ end
1215
+ end
1216
+ end
1217
+
1218
+ RSpec.shared_examples "a Conformant Varying String" do |null_terminated:|
1219
+ minimum_size = null_terminated ? 1 : 0
323
1220
 
324
- it 'has an initial value of 0' do
325
- expect(packet.offset).to eq(0)
1221
+ it_behaves_like 'a Varying String', offset: 4, null_terminated: null_terminated
1222
+
1223
+ describe '#initialize' do
1224
+ it "sets #max_count to 0 (uint32) by default" do
1225
+ expect(subject.max_count).to eq(0)
1226
+ expect(subject.to_binary_s[0, 4]).to eq([0].pack('L'))
326
1227
  end
1228
+ end
327
1229
 
328
- it 'exists if #referent_identifier is not zero' do
329
- expect(packet.offset?).to be true
1230
+ describe '#do_write' do
1231
+ it "writes #max_count corresponding to the number of elements#{' (including the NULL terminator)' if null_terminated}" do
1232
+ subject.assign(value)
1233
+ expect(subject.to_binary_s[0, 4]).to eq([value.size + minimum_size].pack('L'))
330
1234
  end
1235
+ end
331
1236
 
332
- it 'does not exist if #referent_identifier is zero' do
333
- packet.referent_identifier = 0
334
- expect(packet.offset?).to be false
1237
+ describe '#do_read' do
1238
+ it "sets #max_count to the string size#{' (including the NULL terminator)' if null_terminated}" do
1239
+ subject.read(binary_stream)
1240
+ expect(subject.max_count).to eq(value.size + minimum_size)
335
1241
  end
336
1242
  end
337
1243
 
338
- describe '#actual_count' do
339
- it 'is a 32-bit unsigned integer' do
340
- expect(packet.actual_count).to be_a BinData::Uint32le
1244
+ describe '#assign' do
1245
+ context 'with a string' do
1246
+ it "sets #max_count to the string length#{' (including the NULL terminator)' if null_terminated}" do
1247
+ subject.assign(value)
1248
+ expect(subject.max_count).to eq(value.length + minimum_size)
1249
+ end
1250
+ end
1251
+
1252
+ context 'with a varying string' do
1253
+ it "sets #max_count to the string length#{' (including the NULL terminator)' if null_terminated}" do
1254
+ case subject
1255
+ when RubySMB::Field::Stringz16
1256
+ str = RubySMB::Dcerpc::Ndr::NdrVarWideStringz.new(value)
1257
+ when RubySMB::Field::String16
1258
+ str = RubySMB::Dcerpc::Ndr::NdrVarWideString.new(value)
1259
+ when BinData::Stringz
1260
+ str = RubySMB::Dcerpc::Ndr::NdrVarStringz.new(value)
1261
+ when BinData::String
1262
+ str = RubySMB::Dcerpc::Ndr::NdrVarString.new(value)
1263
+ end
1264
+ subject.assign(str)
1265
+ expect(subject.max_count).to eq(value.length + minimum_size)
1266
+ end
341
1267
  end
342
1268
 
343
- it 'has an initial value equal to #bytes size' do
344
- packet.bytes << 2 << 3 << 4 << 5
345
- expect(packet.actual_count).to eq(4)
1269
+ context 'with a conformant varying string' do
1270
+ it 'sets #max_count to the conformant varying string #max_count value' do
1271
+ case subject
1272
+ when RubySMB::Field::Stringz16
1273
+ str = RubySMB::Dcerpc::Ndr::NdrConfVarWideStringz.new(value)
1274
+ when RubySMB::Field::String16
1275
+ str = RubySMB::Dcerpc::Ndr::NdrConfVarWideString.new(value)
1276
+ when BinData::Stringz
1277
+ str = RubySMB::Dcerpc::Ndr::NdrConfVarStringz.new(value)
1278
+ when BinData::String
1279
+ str = RubySMB::Dcerpc::Ndr::NdrConfVarString.new(value)
1280
+ end
1281
+ str.max_count = 30
1282
+ subject.assign(str)
1283
+ expect(subject.max_count).to eq(30)
1284
+ end
346
1285
  end
1286
+ end
347
1287
 
348
- it 'exists if #referent_identifier is not zero' do
349
- expect(packet.actual_count?).to be true
1288
+ describe '#do_num_bytes' do
1289
+ it 'give the correct total size in bytes' do
1290
+ subject.assign(value)
1291
+ expect(subject.do_num_bytes).to eq(binary_stream.size)
350
1292
  end
1293
+ end
1294
+ end
1295
+
1296
+ RSpec.shared_examples "a Varying String" do |offset: 0, null_terminated:|
1297
+ minimum_size = null_terminated ? 1 : 0
351
1298
 
352
- it 'does not exist if #referent_identifier is zero' do
353
- packet.referent_identifier = 0
354
- expect(packet.actual_count?).to be false
1299
+ describe '#initialize' do
1300
+ it 'sets #actual_count to 0 (uint32) by default' do
1301
+ expect(subject.actual_count).to eq(0)
1302
+ expect(subject.to_binary_s[offset + 4, 4]).to eq([0].pack('L'))
355
1303
  end
356
1304
  end
357
1305
 
358
- describe '#bytes' do
359
- it 'is a Bindata::Array' do
360
- expect(packet.bytes).to be_a BinData::Array
1306
+ describe '#do_write' do
1307
+ it 'always writes 0 for "offset"' do
1308
+ expect(subject.to_binary_s[offset, 4]).to eq("\x00\x00\x00\x00".b)
1309
+ subject.assign('Test')
1310
+ expect(subject.to_binary_s[offset, 4]).to eq("\x00\x00\x00\x00".b)
361
1311
  end
362
1312
 
363
- it 'has an initial length equal to #actual_count' do
364
- packet.actual_count = 3
365
- expect(packet.bytes.size).to eq(3)
1313
+ it "writes #actual_count corresponding to the number of elements#{' (including the NULL terminator)' if null_terminated}" do
1314
+ subject.assign(value)
1315
+ expect(subject.to_binary_s[offset + 4, 4]).to eq([value.size + minimum_size].pack('L'))
366
1316
  end
1317
+ end
367
1318
 
368
- it 'is 8-bit unsigned integer elements' do
369
- expect(packet.bytes[0]).to be_a BinData::Uint8
1319
+ describe '#do_read' do
1320
+ it "sets #actual_count to the string size#{' (including the NULL terminator)' if null_terminated}" do
1321
+ subject.read(binary_stream)
1322
+ expect(subject.actual_count).to eq(value.size + minimum_size)
370
1323
  end
1324
+ end
371
1325
 
372
- it 'exists if #referent_identifier is not zero' do
373
- expect(packet.bytes?).to be true
1326
+ describe '#assign' do
1327
+ it "sets #actual_count to the string length#{' (including the NULL terminator)' if null_terminated}" do
1328
+ subject.assign(value)
1329
+ expect(subject.actual_count).to eq(value.length + minimum_size)
374
1330
  end
1331
+ end
375
1332
 
376
- it 'does not exist if #referent_identifier is zero' do
377
- packet.referent_identifier = 0
378
- expect(packet.bytes?).to be false
1333
+ describe '#do_num_bytes' do
1334
+ it 'give the correct total size in bytes' do
1335
+ subject.assign(value)
1336
+ expect(subject.do_num_bytes).to eq(binary_stream.size)
379
1337
  end
380
1338
  end
381
1339
  end
382
1340
 
383
- RSpec.describe RubySMB::Dcerpc::Ndr::NdrLpFileTime do
384
- it 'is NdrTopLevelFullPointer subclass' do
385
- expect(described_class).to be < RubySMB::Dcerpc::Ndr::NdrTopLevelFullPointer
1341
+ RSpec.describe RubySMB::Dcerpc::Ndr::NdrVarString do
1342
+ subject { described_class.new }
1343
+ it 'is a BinData::String class' do
1344
+ expect(described_class).to be < BinData::String
1345
+ expect(described_class).not_to be < RubySMB::Field::String16
1346
+ end
1347
+ it_behaves_like 'a NDR String', conformant: false, char_size: 1, null_terminated: false do
1348
+ let(:binary_stream) {
1349
+ "\x00\x00\x00\x00"\
1350
+ "\x04\x00\x00\x00"\
1351
+ "\x41\x42\x43\x44".b
1352
+ }
1353
+ let(:value) { 'ABCD' }
386
1354
  end
1355
+ end
387
1356
 
388
- subject(:packet) { described_class.new }
1357
+ RSpec.describe RubySMB::Dcerpc::Ndr::NdrVarStringz do
1358
+ subject { described_class.new }
1359
+ it 'is a BinData::Stringz class' do
1360
+ expect(described_class).to be < BinData::Stringz
1361
+ expect(described_class).not_to be < RubySMB::Field::Stringz16
1362
+ end
1363
+ it_behaves_like 'a NDR String', conformant: false, char_size: 1, null_terminated: true do
1364
+ let(:binary_stream) {
1365
+ "\x00\x00\x00\x00"\
1366
+ "\x05\x00\x00\x00"\
1367
+ "\x41\x42\x43\x44\x00".b
1368
+ }
1369
+ let(:value) { 'ABCD' }
1370
+ end
1371
+ end
389
1372
 
390
- it { is_expected.to respond_to :referent }
1373
+ RSpec.describe RubySMB::Dcerpc::Ndr::NdrVarWideString do
1374
+ subject { described_class.new }
1375
+ it 'is a RubySMB::Field::String16 class' do
1376
+ expect(described_class).to be < RubySMB::Field::String16
1377
+ end
1378
+ it_behaves_like 'a NDR String', conformant: false, char_size: 2, null_terminated: false do
1379
+ let(:binary_stream) {
1380
+ "\x00\x00\x00\x00"\
1381
+ "\x04\x00\x00\x00"\
1382
+ "\x41\x00\x42\x00\x43\x00\x44\x00".b
1383
+ }
1384
+ let(:value) { 'ABCD'.encode('utf-16le') }
1385
+ end
1386
+ end
391
1387
 
392
- it 'is little endian' do
393
- expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
1388
+ RSpec.describe RubySMB::Dcerpc::Ndr::NdrVarWideStringz do
1389
+ subject { described_class.new }
1390
+ it 'is a RubySMB::Field::Stringz16 class' do
1391
+ expect(described_class).to be < RubySMB::Field::Stringz16
1392
+ end
1393
+ it_behaves_like 'a NDR String', conformant: false, char_size: 2, null_terminated: true do
1394
+ let(:binary_stream) {
1395
+ "\x00\x00\x00\x00"\
1396
+ "\x05\x00\x00\x00"\
1397
+ "\x41\x00\x42\x00\x43\x00\x44\x00\x00\x00".b
1398
+ }
1399
+ let(:value) { 'ABCD'.encode('utf-16le') }
394
1400
  end
1401
+ end
395
1402
 
396
- describe '#referent' do
397
- it 'is a FileTime' do
398
- expect(packet.referent).to be_a RubySMB::Field::FileTime
399
- end
1403
+ RSpec.describe RubySMB::Dcerpc::Ndr::NdrConfVarString do
1404
+ subject { described_class.new }
1405
+ it 'is a BinData::String class' do
1406
+ expect(described_class).to be < BinData::String
1407
+ expect(described_class).not_to be < RubySMB::Field::String16
1408
+ end
1409
+ it_behaves_like 'a NDR String', conformant: true, char_size: 1, null_terminated: false do
1410
+ let(:binary_stream) {
1411
+ "\x04\x00\x00\x00"\
1412
+ "\x00\x00\x00\x00"\
1413
+ "\x04\x00\x00\x00"\
1414
+ "\x41\x42\x43\x44".b
1415
+ }
1416
+ let(:value) { 'ABCD' }
1417
+ end
1418
+ end
1419
+
1420
+ RSpec.describe RubySMB::Dcerpc::Ndr::NdrConfVarStringz do
1421
+ subject { described_class.new }
1422
+ it 'is a BinData::Stringz class' do
1423
+ expect(described_class).to be < BinData::Stringz
1424
+ expect(described_class).not_to be < RubySMB::Field::Stringz16
1425
+ end
1426
+ it_behaves_like 'a NDR String', conformant: true, char_size: 1, null_terminated: true do
1427
+ let(:binary_stream) {
1428
+ "\x05\x00\x00\x00"\
1429
+ "\x00\x00\x00\x00"\
1430
+ "\x05\x00\x00\x00"\
1431
+ "\x41\x42\x43\x44\x00".b
1432
+ }
1433
+ let(:value) { 'ABCD' }
1434
+ end
1435
+ end
1436
+
1437
+ RSpec.describe RubySMB::Dcerpc::Ndr::NdrConfVarWideString do
1438
+ subject { described_class.new }
1439
+ it 'is a RubySMB::Field::String16 class' do
1440
+ expect(described_class).to be < RubySMB::Field::String16
1441
+ end
1442
+ it_behaves_like 'a NDR String', conformant: true, char_size: 2, null_terminated: false do
1443
+ let(:binary_stream) {
1444
+ "\x04\x00\x00\x00"\
1445
+ "\x00\x00\x00\x00"\
1446
+ "\x04\x00\x00\x00"\
1447
+ "\x41\x00\x42\x00\x43\x00\x44\x00".b
1448
+ }
1449
+ let(:value) { 'ABCD'.encode('utf-16le') }
1450
+ end
1451
+ end
1452
+
1453
+ RSpec.describe RubySMB::Dcerpc::Ndr::NdrConfVarWideStringz do
1454
+ subject { described_class.new }
1455
+ it 'is a RubySMB::Field::Stringz16 class' do
1456
+ expect(described_class).to be < RubySMB::Field::Stringz16
1457
+ end
1458
+ it_behaves_like 'a NDR String', conformant: true, char_size: 2, null_terminated: true do
1459
+ let(:binary_stream) {
1460
+ "\x05\x00\x00\x00"\
1461
+ "\x00\x00\x00\x00"\
1462
+ "\x05\x00\x00\x00"\
1463
+ "\x41\x00\x42\x00\x43\x00\x44\x00\x00\x00".b
1464
+ }
1465
+ let(:value) { 'ABCD'.encode('utf-16le') }
1466
+ end
1467
+ end
1468
+
1469
+
1470
+ #
1471
+ # Structures
1472
+ #
1473
+
1474
+ RSpec.describe RubySMB::Dcerpc::Ndr::NdrStruct do
1475
+
1476
+ describe 'Struct.method_missing' do
1477
+ context 'When validating conformant arrays' do
1478
+ let(:super_result) { double('Super method_missing result') }
1479
+ let(:super_result_array) { [super_result] }
1480
+ before :example do
1481
+ allow(super_result).to receive(:has_parameter?).and_return(true)
1482
+ allow(BinData::Record).to receive(:method_missing).and_return(super_result_array)
1483
+ allow(described_class).to receive(:validate_conformant_array)
1484
+ allow(described_class).to receive(:default_parameters).and_return({byte_align: 4})
1485
+ end
1486
+ it 'calls the superclass method_missing and returns the result' do
1487
+ expect(described_class.method_missing(1, 2)).to eq(super_result_array)
1488
+ end
1489
+ it 'performs conformant array validation if the field is an array of BinData::SanitizedField' do
1490
+ allow(super_result).to receive(:is_a?).with(BinData::SanitizedField).and_return(true)
1491
+ described_class.method_missing(1, 2)
1492
+ expect(described_class).to have_received(:validate_conformant_array).with(super_result_array)
1493
+ end
1494
+ it 'does not perform conformant array validation if the field is not an array of BinData::SanitizedField' do
1495
+ allow(super_result).to receive(:is_a?).with(BinData::SanitizedField).and_return(false)
1496
+ described_class.method_missing(1, 2)
1497
+ expect(described_class).to_not have_received(:validate_conformant_array).with(super_result_array)
1498
+ end
1499
+ end
1500
+
1501
+ context 'when cheking if the fields have :byte_align parameter' do
1502
+ it 'does not raise error when it is set' do
1503
+ expect {
1504
+ Class.new(described_class) do
1505
+ default_parameters byte_align: 4
1506
+ endian :little
1507
+ ndr_uint32 :a
1508
+ end.new
1509
+ }.to_not raise_error
1510
+ end
1511
+
1512
+ it 'raises an error when it is not set' do
1513
+ expect {
1514
+ Class.new(described_class) do
1515
+ endian :little
1516
+ ndr_uint32 :a
1517
+ end.new
1518
+ }.to raise_error(ArgumentError)
1519
+ end
1520
+
1521
+ it 'does not raise error when the field is a BinData:Bit*' do
1522
+ expect {
1523
+ Class.new(described_class) do
1524
+ default_parameters byte_align: 4
1525
+ endian :little
1526
+ bit2 :a
1527
+ end.new
1528
+ }.to_not raise_error
1529
+ end
1530
+ end
1531
+ end
1532
+
1533
+ describe 'Struct.validate_conformant_array' do
1534
+ context 'with a conformant array' do
1535
+ it 'does not raise error if the array is the last member' do
1536
+ expect {
1537
+ Class.new(described_class) do
1538
+ default_parameters byte_align: 4
1539
+ endian :little
1540
+
1541
+ ndr_uint32 :a
1542
+ ndr_conf_array :b, type: :ndr_uint16
1543
+ end
1544
+ }.to_not raise_error
1545
+ end
1546
+ it 'raises error if the array is not the last member' do
1547
+ expect {
1548
+ Class.new(described_class) do
1549
+ default_parameters byte_align: 4
1550
+ endian :little
1551
+
1552
+ ndr_conf_array :b, type: :ndr_uint16
1553
+ ndr_uint32 :a
1554
+ end
1555
+ }.to raise_error(ArgumentError)
1556
+ end
1557
+ it 'does not raise error if the array is not the last member of a non NdrStruct' do
1558
+ expect {
1559
+ Class.new(BinData::Record) do
1560
+ default_parameters byte_align: 4
1561
+ endian :little
1562
+
1563
+ ndr_conf_array :b, type: :ndr_uint16
1564
+ ndr_uint32 :a
1565
+ end
1566
+ }.to_not raise_error
1567
+ end
1568
+ end
1569
+
1570
+ context 'with a conformant varying array' do
1571
+ it 'does not raise error if the array is the last member' do
1572
+ expect {
1573
+ Class.new(described_class) do
1574
+ default_parameters byte_align: 4
1575
+ endian :little
1576
+
1577
+ ndr_uint32 :a
1578
+ ndr_conf_var_array :b, type: :ndr_uint16
1579
+ end
1580
+ }.to_not raise_error
1581
+ end
1582
+ it 'raises error if the array is not the last member' do
1583
+ expect {
1584
+ Class.new(described_class) do
1585
+ default_parameters byte_align: 4
1586
+ endian :little
1587
+
1588
+ ndr_conf_var_array :b, type: :ndr_uint16
1589
+ ndr_uint32 :a
1590
+ end
1591
+ }.to raise_error(ArgumentError)
1592
+ end
1593
+ end
1594
+
1595
+ context 'with a conformant varying string' do
1596
+ it 'does not raise error if the string is the last member' do
1597
+ expect {
1598
+ Class.new(described_class) do
1599
+ default_parameters byte_align: 4
1600
+ endian :little
1601
+
1602
+ ndr_uint32 :a
1603
+ ndr_conf_var_string :b
1604
+ end
1605
+ }.to_not raise_error
1606
+ end
1607
+ it 'raises error if the string is not the last member' do
1608
+ expect {
1609
+ Class.new(described_class) do
1610
+ default_parameters byte_align: 4
1611
+ endian :little
1612
+
1613
+ ndr_conf_var_string :b
1614
+ ndr_uint32 :a
1615
+ end
1616
+ }.to raise_error(ArgumentError)
1617
+ end
1618
+ end
1619
+
1620
+ context 'with an embedded structure containing a conformant array' do
1621
+ after :example do
1622
+ BinData::RegisteredClasses.unregister('test_struct')
1623
+ end
1624
+
1625
+ context 'when the embedded structure is the last member' do
1626
+ it 'does not raise error' do
1627
+ expect {
1628
+ struct_with_array = Class.new(described_class) do
1629
+ default_parameters byte_align: 4
1630
+ endian :little
1631
+
1632
+ ndr_uint32 :a
1633
+ ndr_conf_var_array :b, type: :ndr_uint16
1634
+ end
1635
+ BinData::RegisteredClasses.register('test_struct', struct_with_array)
1636
+ Class.new(described_class) do
1637
+ default_parameters byte_align: 4
1638
+ endian :little
1639
+
1640
+ ndr_uint32 :a
1641
+ test_struct :b
1642
+ end
1643
+ }.to_not raise_error
1644
+ end
1645
+ end
1646
+
1647
+ context 'when the embedded structure is not the last member' do
1648
+ it 'raises error' do
1649
+ expect {
1650
+ struct_with_array = Class.new(described_class) do
1651
+ default_parameters byte_align: 4
1652
+ endian :little
1653
+
1654
+ ndr_uint32 :a
1655
+ ndr_conf_var_array :b, type: :ndr_uint16
1656
+ end
1657
+ BinData::RegisteredClasses.register('test_struct', struct_with_array)
1658
+ Class.new(described_class) do
1659
+ default_parameters byte_align: 4
1660
+ endian :little
1661
+
1662
+ test_struct :b
1663
+ ndr_uint32 :a
1664
+ end
1665
+ }.to raise_error(ArgumentError)
1666
+ end
1667
+ end
1668
+ end
1669
+ end
1670
+
1671
+ it 'is a BinData::Record class' do
1672
+ expect(described_class).to be < BinData::Record
1673
+ end
1674
+
1675
+ context 'with only primitives' do
1676
+ let(:struct) do
1677
+ Class.new(described_class) do
1678
+ default_parameters byte_align: 4
1679
+ endian :little
1680
+
1681
+ ndr_uint8 :a
1682
+ ndr_uint16 :b
1683
+ ndr_uint32 :c
1684
+ ndr_char :d
1685
+ ndr_boolean :e
1686
+ end
1687
+ end
1688
+
1689
+ it 'initializes the members to their default value' do
1690
+ expect(struct.new).to eq(a: 0, b: 0, c: 0, d: "\x00", e: false)
1691
+ end
1692
+ it 'reads itself' do
1693
+ values = {a: 44, b: 444, c: 4444, d: "T", e: true}
1694
+ struct_instance = struct.new(values)
1695
+ expect(struct.read(struct_instance.to_binary_s)).to eq(values)
1696
+ end
1697
+ context 'with values' do
1698
+ subject do
1699
+ struct.new(a: 1, b: 2, c: 3, d: "A", e: true)
1700
+ end
1701
+ it 'returns the expected member values' do
1702
+ expect(subject).to eq(a: 1, b: 2, c: 3, d: "A", e: true)
1703
+ end
1704
+ it 'outputs the expected binary representation' do
1705
+ binary = "\x01" # a
1706
+ binary << "\x00" # pad
1707
+ binary << "\x02\x00" # b
1708
+ binary << "\x03\x00\x00\x00" # c
1709
+ binary << "\x41" # d
1710
+ binary << "\x00\x00\x00" # pad
1711
+ binary << "\x01\x00\x00\x00" # e
1712
+ expect(subject.to_binary_s).to eq(binary.b)
1713
+ end
1714
+ end
1715
+ end
1716
+
1717
+ context 'with pointers' do
1718
+ let(:struct) do
1719
+ Class.new(described_class) do
1720
+ default_parameters byte_align: 4
1721
+ endian :little
1722
+
1723
+ ndr_uint8 :a
1724
+ ndr_uint16_ptr :b
1725
+ ndr_uint32 :c
1726
+ ndr_char_ptr :d
1727
+ ndr_boolean :e
1728
+ end
1729
+ end
1730
+
1731
+ it 'initializes the members to their default value' do
1732
+ expect(struct.new).to eq(a: 0, b: :null, c: 0, d: :null, e: false)
1733
+ end
1734
+ it 'reads itself' do
1735
+ values = {a: 44, b: 444, c: 4444, d: "T", e: true}
1736
+ struct_instance = struct.new(values)
1737
+ expect(struct.read(struct_instance.to_binary_s)).to eq(values)
1738
+ end
1739
+ context 'with values' do
1740
+ subject do
1741
+ struct.new(a: 1, b: 2, c: 3, d: "A", e: true)
1742
+ end
1743
+ it 'returns the expected member values' do
1744
+ expect(subject).to eq(a: 1, b: 2, c: 3, d: "A", e: true)
1745
+ end
1746
+ it 'outputs the expected binary representation' do
1747
+ binary = "\x01" # a
1748
+ binary << "\x00\x00\x00" # pad
1749
+ binary << "\x01\x00\x00\x00" # b.ref_id
1750
+ binary << "\x03\x00\x00\x00" # c
1751
+ binary << "\x02\x00\x00\x00" # d.ref_id
1752
+ binary << "\x01\x00\x00\x00" # e
1753
+ binary << "\x02\x00" # deferred b
1754
+ binary << "\x41" # deferred d
1755
+ expect(subject.to_binary_s).to eq(binary.b)
1756
+ end
1757
+ end
1758
+ end
1759
+
1760
+ context 'with fixed arrays' do
1761
+ let(:struct) do
1762
+ Class.new(described_class) do
1763
+ default_parameters byte_align: 4
1764
+ endian :little
1765
+
1766
+ ndr_uint32 :a
1767
+ ndr_fix_array :b, type: :ndr_uint32, initial_length: 3, byte_align: 4
1768
+ ndr_uint32 :c
1769
+ end
1770
+ end
1771
+
1772
+ it 'initializes the members to their default value' do
1773
+ expect(struct.new).to eq(a: 0, b: [0, 0, 0], c: 0)
1774
+ end
1775
+ it 'reads itself' do
1776
+ values = {a: 44, b: [1,2,3], c: 4444}
1777
+ struct_instance = struct.new(values)
1778
+ expect(struct.read(struct_instance.to_binary_s)).to eq(values)
1779
+ end
1780
+ context 'with values' do
1781
+ subject do
1782
+ struct.new(a: 4, b: [1,2,3], c: 5)
1783
+ end
1784
+ it 'returns the expected member values' do
1785
+ expect(subject).to eq(a: 4, b: [1,2,3], c: 5)
1786
+ end
1787
+ it 'outputs the expected binary representation' do
1788
+ binary = "\x04\x00\x00\x00" # a
1789
+ binary << "\x01\x00\x00\x00" # b[0]
1790
+ binary << "\x02\x00\x00\x00" # b[1]
1791
+ binary << "\x03\x00\x00\x00" # b[2]
1792
+ binary << "\x05\x00\x00\x00" # c
1793
+ expect(subject.to_binary_s).to eq(binary.b)
1794
+ end
1795
+ end
1796
+ end
1797
+
1798
+ context 'with fixed arrays containing pointers' do
1799
+ let(:struct) do
1800
+ Class.new(described_class) do
1801
+ default_parameters byte_align: 4
1802
+ endian :little
1803
+
1804
+ ndr_uint32 :a
1805
+ ndr_fix_array :b, type: :ndr_uint32_ptr, initial_length: 3, byte_align: 4
1806
+ ndr_uint32 :c
1807
+ end
1808
+ end
1809
+
1810
+ it 'initializes the members to their default value' do
1811
+ expect(struct.new).to eq(a: 0, b: [:null, :null, :null], c: 0)
1812
+ end
1813
+ it 'reads itself' do
1814
+ values = {a: 44, b: [1,2,3], c: 4444}
1815
+ struct_instance = struct.new(values)
1816
+ expect(struct.read(struct_instance.to_binary_s)).to eq(values)
1817
+ end
1818
+ context 'with values' do
1819
+ subject do
1820
+ struct.new(a: 4, b: [1,2,3], c: 5)
1821
+ end
1822
+ it 'returns the expected member values' do
1823
+ expect(subject).to eq(a: 4, b: [1,2,3], c: 5)
1824
+ end
1825
+ it 'outputs the expected binary representation' do
1826
+ binary = "\x04\x00\x00\x00" # a
1827
+ binary << "\x01\x00\x00\x00" # b[0].ref_id
1828
+ binary << "\x02\x00\x00\x00" # b[1].ref_id
1829
+ binary << "\x03\x00\x00\x00" # b[2].ref_id
1830
+ binary << "\x05\x00\x00\x00" # c
1831
+ binary << "\x01\x00\x00\x00" # b[0]
1832
+ binary << "\x02\x00\x00\x00" # b[1]
1833
+ binary << "\x03\x00\x00\x00" # b[2]
1834
+ expect(subject.to_binary_s).to eq(binary.b)
1835
+ end
1836
+ end
1837
+ end
1838
+
1839
+ context 'with varying arrays' do
1840
+ let(:struct) do
1841
+ Class.new(described_class) do
1842
+ default_parameters byte_align: 4
1843
+ endian :little
1844
+
1845
+ ndr_uint32 :a
1846
+ ndr_var_array :b, type: :ndr_uint32
1847
+ ndr_uint32 :c
1848
+ end
1849
+ end
1850
+
1851
+ it 'initializes the members to their default value' do
1852
+ expect(struct.new).to eq(a: 0, b: [], c: 0)
1853
+ end
1854
+ it 'reads itself' do
1855
+ values = {a: 44, b: [1,2,3], c: 4444}
1856
+ struct_instance = struct.new(values)
1857
+ expect(struct.read(struct_instance.to_binary_s)).to eq(values)
1858
+ end
1859
+ context 'with values' do
1860
+ subject do
1861
+ struct.new(a: 4, b: [1,2,3], c: 5)
1862
+ end
1863
+ it 'returns the expected member values' do
1864
+ expect(subject).to eq(a: 4, b: [1,2,3], c: 5)
1865
+ end
1866
+ it 'outputs the expected binary representation' do
1867
+ binary = "\x04\x00\x00\x00" # a
1868
+ binary << "\x00\x00\x00\x00" # array offset
1869
+ binary << "\x03\x00\x00\x00" # array actual_count
1870
+ binary << "\x01\x00\x00\x00" # b[0]
1871
+ binary << "\x02\x00\x00\x00" # b[1]
1872
+ binary << "\x03\x00\x00\x00" # b[2]
1873
+ binary << "\x05\x00\x00\x00" # c
1874
+ expect(subject.to_binary_s).to eq(binary.b)
1875
+ end
1876
+ end
1877
+ end
1878
+
1879
+ context 'with varying arrays containing pointers' do
1880
+ let(:struct) do
1881
+ Class.new(described_class) do
1882
+ default_parameters byte_align: 4
1883
+ endian :little
1884
+
1885
+ ndr_uint32 :a
1886
+ ndr_var_array :b, type: :ndr_uint32_ptr
1887
+ ndr_uint32 :c
1888
+ end
1889
+ end
1890
+
1891
+ it 'initializes the members to their default value' do
1892
+ expect(struct.new).to eq(a: 0, b: [], c: 0)
1893
+ end
1894
+ it 'reads itself' do
1895
+ values = {a: 44, b: [1,2,3], c: 4444}
1896
+ struct_instance = struct.new(values)
1897
+ expect(struct.read(struct_instance.to_binary_s)).to eq(values)
1898
+ end
1899
+ context 'with values' do
1900
+ subject do
1901
+ struct.new(a: 4, b: [1,2,3], c: 5)
1902
+ end
1903
+ it 'returns the expected member values' do
1904
+ expect(subject).to eq(a: 4, b: [1,2,3], c: 5)
1905
+ end
1906
+ it 'outputs the expected binary representation' do
1907
+ binary = "\x04\x00\x00\x00" # a
1908
+ binary << "\x00\x00\x00\x00" # array offset
1909
+ binary << "\x03\x00\x00\x00" # array actual_count
1910
+ binary << "\x01\x00\x00\x00" # b[0].ref_id
1911
+ binary << "\x02\x00\x00\x00" # b[1].ref_id
1912
+ binary << "\x03\x00\x00\x00" # b[2].ref_id
1913
+ binary << "\x05\x00\x00\x00" # c
1914
+ binary << "\x01\x00\x00\x00" # deferred b[0]
1915
+ binary << "\x02\x00\x00\x00" # deferred b[1]
1916
+ binary << "\x03\x00\x00\x00" # deferred b[2]
1917
+ expect(subject.to_binary_s).to eq(binary.b)
1918
+ end
1919
+ end
1920
+ end
1921
+
1922
+ context 'with conformant arrays' do
1923
+ let(:struct) do
1924
+ Class.new(described_class) do
1925
+ default_parameters byte_align: 4
1926
+ endian :little
1927
+
1928
+ ndr_uint32 :a
1929
+ ndr_uint32 :b
1930
+ ndr_conf_array :c, type: :ndr_uint32
1931
+ end
1932
+ end
1933
+
1934
+ it 'initializes the members to their default value' do
1935
+ expect(struct.new).to eq(a: 0, b: 0, c: [])
1936
+ end
1937
+ it 'reads itself' do
1938
+ values = {a: 44, b: 4444, c: [1,2,3]}
1939
+ struct_instance = struct.new(values)
1940
+ expect(struct.read(struct_instance.to_binary_s)).to eq(values)
1941
+ end
1942
+ context 'with values' do
1943
+ subject do
1944
+ struct.new(a: 4, b: 5, c: [1,2,3])
1945
+ end
1946
+ it 'returns the expected member values' do
1947
+ expect(subject).to eq(a: 4, b: 5, c: [1,2,3])
1948
+ end
1949
+ it 'outputs the expected binary representation' do
1950
+ binary = "\x03\x00\x00\x00" # array max_count
1951
+ binary << "\x04\x00\x00\x00" # a
1952
+ binary << "\x05\x00\x00\x00" # b
1953
+ binary << "\x01\x00\x00\x00" # c[0]
1954
+ binary << "\x02\x00\x00\x00" # c[1]
1955
+ binary << "\x03\x00\x00\x00" # c[2]
1956
+ expect(subject.to_binary_s).to eq(binary.b)
1957
+ end
1958
+ end
1959
+ end
1960
+
1961
+ context 'with conformant arrays containing pointers' do
1962
+ let(:struct) do
1963
+ Class.new(described_class) do
1964
+ default_parameters byte_align: 4
1965
+ endian :little
1966
+
1967
+ ndr_uint32 :a
1968
+ ndr_uint32 :b
1969
+ ndr_conf_array :c, type: :ndr_uint32_ptr
1970
+ end
1971
+ end
1972
+
1973
+ it 'initializes the members to their default value' do
1974
+ expect(struct.new).to eq(a: 0, b: 0, c: [])
1975
+ end
1976
+ it 'reads itself' do
1977
+ values = {a: 44, b: 4444, c: [1,2,3]}
1978
+ struct_instance = struct.new(values)
1979
+ expect(struct.read(struct_instance.to_binary_s)).to eq(values)
1980
+ end
1981
+ context 'with values' do
1982
+ subject do
1983
+ struct.new(a: 4, b: 5, c: [1,2,3])
1984
+ end
1985
+ it 'returns the expected member values' do
1986
+ expect(subject).to eq(a: 4, b: 5, c: [1,2,3])
1987
+ end
1988
+ it 'outputs the expected binary representation' do
1989
+ binary = "\x03\x00\x00\x00" # array max_count
1990
+ binary << "\x04\x00\x00\x00" # a
1991
+ binary << "\x05\x00\x00\x00" # b
1992
+ binary << "\x01\x00\x00\x00" # c[0].ref_id
1993
+ binary << "\x02\x00\x00\x00" # c[1].ref_id
1994
+ binary << "\x03\x00\x00\x00" # c[2].ref_id
1995
+ binary << "\x01\x00\x00\x00" # c[0]
1996
+ binary << "\x02\x00\x00\x00" # c[1]
1997
+ binary << "\x03\x00\x00\x00" # c[2]
1998
+ expect(subject.to_binary_s).to eq(binary.b)
1999
+ end
2000
+ end
2001
+ end
2002
+
2003
+ context 'with a conformant varying array' do
2004
+ let(:struct) do
2005
+ Class.new(described_class) do
2006
+ default_parameters byte_align: 4
2007
+ endian :little
2008
+
2009
+ ndr_uint32 :a
2010
+ ndr_uint32 :b
2011
+ ndr_conf_var_array :c, type: :ndr_uint32
2012
+ end
2013
+ end
2014
+
2015
+ it 'initializes the members to their default value' do
2016
+ expect(struct.new).to eq(a: 0, b: 0, c: [])
2017
+ end
2018
+ it 'reads itself' do
2019
+ values = {a: 44, b: 4444, c: [1,2,3]}
2020
+ struct_instance = struct.new(values)
2021
+ expect(struct.read(struct_instance.to_binary_s)).to eq(values)
2022
+ end
2023
+ context 'with values' do
2024
+ subject do
2025
+ struct.new(a: 4, b: 5, c: [1,2,3])
2026
+ end
2027
+ it 'returns the expected member values' do
2028
+ expect(subject).to eq(a: 4, b: 5, c: [1,2,3])
2029
+ end
2030
+ it 'outputs the expected binary representation' do
2031
+ binary = "\x03\x00\x00\x00" # array max_count
2032
+ binary << "\x04\x00\x00\x00" # a
2033
+ binary << "\x05\x00\x00\x00" # b
2034
+ binary << "\x00\x00\x00\x00" # array offset
2035
+ binary << "\x03\x00\x00\x00" # array actual_count
2036
+ binary << "\x01\x00\x00\x00" # c[0]
2037
+ binary << "\x02\x00\x00\x00" # c[1]
2038
+ binary << "\x03\x00\x00\x00" # c[2]
2039
+ expect(subject.to_binary_s).to eq(binary.b)
2040
+ end
2041
+ end
2042
+ end
2043
+
2044
+ context 'with a conformant varying array containing pointers' do
2045
+ let(:struct) do
2046
+ Class.new(described_class) do
2047
+ default_parameters byte_align: 4
2048
+ endian :little
2049
+
2050
+ ndr_uint32 :a
2051
+ ndr_uint32 :b
2052
+ ndr_conf_var_array :c, type: :ndr_uint32_ptr
2053
+ end
2054
+ end
2055
+
2056
+ it 'initializes the members to their default value' do
2057
+ expect(struct.new).to eq(a: 0, b: 0, c: [])
2058
+ end
2059
+ it 'reads itself' do
2060
+ values = {a: 44, b: 4444, c: [1,2,3]}
2061
+ struct_instance = struct.new(values)
2062
+ expect(struct.read(struct_instance.to_binary_s)).to eq(values)
2063
+ end
2064
+ context 'with values' do
2065
+ subject do
2066
+ struct.new(a: 4, b: 5, c: [1,2,3])
2067
+ end
2068
+ it 'returns the expected member values' do
2069
+ expect(subject).to eq(a: 4, b: 5, c: [1,2,3])
2070
+ end
2071
+ it 'outputs the expected binary representation' do
2072
+ binary = "\x03\x00\x00\x00" # array max_count
2073
+ binary << "\x04\x00\x00\x00" # a
2074
+ binary << "\x05\x00\x00\x00" # c
2075
+ binary << "\x00\x00\x00\x00" # array offset
2076
+ binary << "\x03\x00\x00\x00" # array actual_count
2077
+ binary << "\x01\x00\x00\x00" # b[0].ref_id
2078
+ binary << "\x02\x00\x00\x00" # b[1].ref_id
2079
+ binary << "\x03\x00\x00\x00" # b[2].ref_id
2080
+ binary << "\x01\x00\x00\x00" # deferred b[0]
2081
+ binary << "\x02\x00\x00\x00" # deferred b[1]
2082
+ binary << "\x03\x00\x00\x00" # deferred b[2]
2083
+ expect(subject.to_binary_s).to eq(binary.b)
2084
+ end
2085
+ end
2086
+ end
2087
+
2088
+ context 'with an embedded structure containing a fixed array' do
2089
+ after :example do
2090
+ BinData::RegisteredClasses.unregister('test_struct')
2091
+ end
2092
+
2093
+ let(:struct) do
2094
+ struct_with_array = Class.new(described_class) do
2095
+ default_parameters byte_align: 4
2096
+ endian :little
2097
+
2098
+ ndr_uint16 :a
2099
+ ndr_fix_array :b, type: :ndr_uint32, initial_length: 3, byte_align: 4
2100
+ ndr_uint16 :c
2101
+ end
2102
+ BinData::RegisteredClasses.register('test_struct', struct_with_array)
2103
+ Class.new(described_class) do
2104
+ default_parameters byte_align: 4
2105
+ endian :little
2106
+
2107
+ ndr_uint16 :a
2108
+ ndr_uint16 :b
2109
+ test_struct :c
2110
+ end
2111
+ end
2112
+
2113
+ it 'initializes the members to their default value' do
2114
+ expect(struct.new).to eq(a: 0, b: 0, c: {a: 0, b: [0, 0 ,0], c: 0})
2115
+ end
2116
+ it 'reads itself' do
2117
+ values = {a: 44, b: 4444, c: {a: 5555, b: [1, 2, 3], c: 77}}
2118
+ struct_instance = struct.new(values)
2119
+ expect(struct.read(struct_instance.to_binary_s)).to eq(values)
2120
+ end
2121
+ context 'with values' do
2122
+ subject do
2123
+ struct.new(a: 5, b: 6, c: {a: 7, b: [1, 2, 3], c: 8})
2124
+ end
2125
+ it 'returns the expected member values' do
2126
+ expect(subject).to eq(a: 5, b: 6, c: {a: 7, b: [1, 2, 3], c: 8})
2127
+ end
2128
+ it 'outputs the expected binary representation' do
2129
+ binary = "\x05\x00" # a
2130
+ binary << "\x06\x00" # b
2131
+ binary << "\x07\x00" # c.a
2132
+ binary << "\x00\x00" # pad
2133
+ binary << "\x01\x00\x00\x00" # c.b[0]
2134
+ binary << "\x02\x00\x00\x00" # c.b[1]
2135
+ binary << "\x03\x00\x00\x00" # c.b[2]
2136
+ binary << "\x08\x00" # c.c
2137
+ expect(subject.to_binary_s).to eq(binary.b)
2138
+ end
2139
+ end
2140
+ end
2141
+
2142
+ context 'with an embedded structure containing a fixed array of pointers' do
2143
+ after :example do
2144
+ BinData::RegisteredClasses.unregister('test_struct')
2145
+ end
2146
+
2147
+ let(:struct) do
2148
+ struct_with_array = Class.new(described_class) do
2149
+ default_parameters byte_align: 4
2150
+ endian :little
2151
+
2152
+ ndr_uint16 :a
2153
+ ndr_fix_array :b, type: :ndr_uint32_ptr, initial_length: 3, byte_align: 4
2154
+ ndr_uint16 :c
2155
+ end
2156
+ BinData::RegisteredClasses.register('test_struct', struct_with_array)
2157
+ Class.new(described_class) do
2158
+ default_parameters byte_align: 4
2159
+ endian :little
2160
+
2161
+ ndr_uint16 :a
2162
+ ndr_uint16 :b
2163
+ test_struct :c
2164
+ end
2165
+ end
2166
+
2167
+ it 'initializes the members to their default value' do
2168
+ expect(struct.new).to eq(a: 0, b: 0, c: {a: 0, b: [:null, :null ,:null], c: 0})
2169
+ end
2170
+ it 'reads itself' do
2171
+ values = {a: 44, b: 4444, c: {a: 5555, b: [1, 2, 3], c: 77}}
2172
+ struct_instance = struct.new(values)
2173
+ expect(struct.read(struct_instance.to_binary_s)).to eq(values)
2174
+ end
2175
+ context 'with values' do
2176
+ subject do
2177
+ struct.new(a: 5, b: 6, c: {a: 7, b: [1, 2, 3], c: 8})
2178
+ end
2179
+ it 'returns the expected member values' do
2180
+ expect(subject).to eq(a: 5, b: 6, c: {a: 7, b: [1, 2, 3], c: 8})
2181
+ end
2182
+ it 'outputs the expected binary representation' do
2183
+ binary = "\x05\x00" # a
2184
+ binary << "\x06\x00" # b
2185
+ binary << "\x07\x00" # c.a
2186
+ binary << "\x00\x00" # pad
2187
+ binary << "\x01\x00\x00\x00" # c.b[0].ref_id
2188
+ binary << "\x02\x00\x00\x00" # c.b[1].ref_id
2189
+ binary << "\x03\x00\x00\x00" # c.b[2].ref_id
2190
+ binary << "\x08\x00" # c.c
2191
+ binary << "\x00\x00" # pad
2192
+ binary << "\x01\x00\x00\x00" # deferred c.b[0]
2193
+ binary << "\x02\x00\x00\x00" # deferred c.b[1]
2194
+ binary << "\x03\x00\x00\x00" # deferred c.b[2]
2195
+ expect(subject.to_binary_s).to eq(binary.b)
2196
+ end
2197
+ end
2198
+ end
2199
+
2200
+ context 'with an embedded structure containing a varying array' do
2201
+ after :example do
2202
+ BinData::RegisteredClasses.unregister('test_struct')
2203
+ end
2204
+
2205
+ let(:struct) do
2206
+ struct_with_array = Class.new(described_class) do
2207
+ default_parameters byte_align: 4
2208
+ endian :little
2209
+
2210
+ ndr_uint16 :a
2211
+ ndr_var_array :b, type: :ndr_uint32
2212
+ ndr_uint16 :c
2213
+ end
2214
+ BinData::RegisteredClasses.register('test_struct', struct_with_array)
2215
+ Class.new(described_class) do
2216
+ default_parameters byte_align: 4
2217
+ endian :little
2218
+
2219
+ ndr_uint16 :a
2220
+ ndr_uint16 :b
2221
+ test_struct :c
2222
+ end
2223
+ end
2224
+
2225
+ it 'initializes the members to their default value' do
2226
+ expect(struct.new).to eq(a: 0, b: 0, c: {a: 0, b: [], c: 0})
2227
+ end
2228
+ it 'reads itself' do
2229
+ values = {a: 44, b: 4444, c: {a: 5555, b: [1, 2, 3], c: 77}}
2230
+ struct_instance = struct.new(values)
2231
+ expect(struct.read(struct_instance.to_binary_s)).to eq(values)
2232
+ end
2233
+ context 'with values' do
2234
+ subject do
2235
+ struct.new(a: 5, b: 6, c: {a: 7, b: [1, 2, 3], c: 8})
2236
+ end
2237
+ it 'returns the expected member values' do
2238
+ expect(subject).to eq(a: 5, b: 6, c: {a: 7, b: [1, 2, 3], c: 8})
2239
+ end
2240
+ it 'outputs the expected binary representation' do
2241
+ binary = "\x05\x00" # a
2242
+ binary << "\x06\x00" # b
2243
+ binary << "\x07\x00" # c.a
2244
+ binary << "\x00\x00" # pad
2245
+ binary << "\x00\x00\x00\x00" # array offset
2246
+ binary << "\x03\x00\x00\x00" # array actual_count
2247
+ binary << "\x01\x00\x00\x00" # c.b[0]
2248
+ binary << "\x02\x00\x00\x00" # c.b[1]
2249
+ binary << "\x03\x00\x00\x00" # c.b[2]
2250
+ binary << "\x08\x00" # c.c
2251
+ expect(subject.to_binary_s).to eq(binary.b)
2252
+ end
2253
+ end
2254
+ end
2255
+
2256
+ context 'with an embedded structure containing a varying array of pointers' do
2257
+ after :example do
2258
+ BinData::RegisteredClasses.unregister('test_struct')
2259
+ end
2260
+
2261
+ let(:struct) do
2262
+ struct_with_array = Class.new(described_class) do
2263
+ default_parameters byte_align: 4
2264
+ endian :little
2265
+
2266
+ ndr_uint16 :a
2267
+ ndr_var_array :b, type: :ndr_uint32_ptr
2268
+ ndr_uint16 :c
2269
+ end
2270
+ BinData::RegisteredClasses.register('test_struct', struct_with_array)
2271
+ Class.new(described_class) do
2272
+ default_parameters byte_align: 4
2273
+ endian :little
2274
+
2275
+ ndr_uint16 :a
2276
+ ndr_uint16 :b
2277
+ test_struct :c
2278
+ end
2279
+ end
2280
+
2281
+ it 'initializes the members to their default value' do
2282
+ expect(struct.new).to eq(a: 0, b: 0, c: {a: 0, b: [], c: 0})
2283
+ end
2284
+ it 'reads itself' do
2285
+ values = {a: 44, b: 4444, c: {a: 5555, b: [1, 2, 3], c: 77}}
2286
+ struct_instance = struct.new(values)
2287
+ expect(struct.read(struct_instance.to_binary_s)).to eq(values)
2288
+ end
2289
+ context 'with values' do
2290
+ subject do
2291
+ struct.new(a: 5, b: 6, c: {a: 7, b: [1, 2, 3], c: 8})
2292
+ end
2293
+ it 'returns the expected member values' do
2294
+ expect(subject).to eq(a: 5, b: 6, c: {a: 7, b: [1, 2, 3], c: 8})
2295
+ end
2296
+ it 'outputs the expected binary representation' do
2297
+ binary = "\x05\x00" # a
2298
+ binary << "\x06\x00" # b
2299
+ binary << "\x07\x00" # c.a
2300
+ binary << "\x00\x00" # pad
2301
+ binary << "\x00\x00\x00\x00" # array offset
2302
+ binary << "\x03\x00\x00\x00" # array actual_count
2303
+ binary << "\x01\x00\x00\x00" # c.b[0].ref_id
2304
+ binary << "\x02\x00\x00\x00" # c.b[1].ref_id
2305
+ binary << "\x03\x00\x00\x00" # c.b[2].ref_id
2306
+ binary << "\x08\x00" # c.c
2307
+ binary << "\x00\x00" # pad
2308
+ binary << "\x01\x00\x00\x00" # deferred c.b[0]
2309
+ binary << "\x02\x00\x00\x00" # deferred c.b[1]
2310
+ binary << "\x03\x00\x00\x00" # deferred c.b[2]
2311
+ expect(subject.to_binary_s).to eq(binary.b)
2312
+ end
2313
+ end
2314
+ end
2315
+
2316
+ context 'with an embedded structure containing a conformant array' do
2317
+ after :example do
2318
+ BinData::RegisteredClasses.unregister('test_struct')
2319
+ end
2320
+
2321
+ let(:struct) do
2322
+ struct_with_array = Class.new(described_class) do
2323
+ default_parameters byte_align: 4
2324
+ endian :little
2325
+
2326
+ ndr_uint16 :a
2327
+ ndr_uint16 :b
2328
+ ndr_conf_array :c, type: :ndr_uint32
2329
+ end
2330
+ BinData::RegisteredClasses.register('test_struct', struct_with_array)
2331
+ Class.new(described_class) do
2332
+ default_parameters byte_align: 4
2333
+ endian :little
2334
+
2335
+ ndr_uint16 :a
2336
+ ndr_uint16 :b
2337
+ test_struct :c
2338
+ end
2339
+ end
2340
+
2341
+ it 'initializes the members to their default value' do
2342
+ expect(struct.new).to eq(a: 0, b: 0, c: {a: 0, b: 0, c: []})
2343
+ end
2344
+ it 'reads itself' do
2345
+ values = {a: 44, b: 4444, c: {a: 5555, b: 77, c: [1, 2, 3]}}
2346
+ struct_instance = struct.new(values)
2347
+ expect(struct.read(struct_instance.to_binary_s)).to eq(values)
2348
+ end
2349
+ context 'with values' do
2350
+ subject do
2351
+ struct.new(a: 5, b: 6, c: {a: 7, b: 8, c: [1, 2, 3]})
2352
+ end
2353
+ it 'returns the expected member values' do
2354
+ expect(subject).to eq(a: 5, b: 6, c: {a: 7, b:8, c: [1, 2, 3]})
2355
+ end
2356
+ it 'outputs the expected binary representation' do
2357
+ binary = "\x03\x00\x00\x00" # array max_count
2358
+ binary << "\x05\x00" # a
2359
+ binary << "\x06\x00" # b
2360
+ binary << "\x07\x00" # c.a
2361
+ binary << "\x08\x00" # c.b
2362
+ binary << "\x01\x00\x00\x00" # c.c[0]
2363
+ binary << "\x02\x00\x00\x00" # c.c[1]
2364
+ binary << "\x03\x00\x00\x00" # c.c[2]
2365
+ expect(subject.to_binary_s).to eq(binary.b)
2366
+ end
2367
+ end
2368
+ end
2369
+
2370
+ context 'with an embedded structure containing a conformant array of pointers' do
2371
+ after :example do
2372
+ BinData::RegisteredClasses.unregister('test_struct')
2373
+ end
2374
+
2375
+ let(:struct) do
2376
+ struct_with_array = Class.new(described_class) do
2377
+ default_parameters byte_align: 4
2378
+ endian :little
2379
+
2380
+ ndr_uint16 :a
2381
+ ndr_uint16 :b
2382
+ ndr_conf_array :c, type: :ndr_uint32_ptr
2383
+ end
2384
+ BinData::RegisteredClasses.register('test_struct', struct_with_array)
2385
+ Class.new(described_class) do
2386
+ default_parameters byte_align: 4
2387
+ endian :little
2388
+
2389
+ ndr_uint16 :a
2390
+ ndr_uint16 :b
2391
+ test_struct :c
2392
+ end
2393
+ end
2394
+
2395
+ it 'initializes the members to their default value' do
2396
+ expect(struct.new).to eq(a: 0, b: 0, c: {a: 0, b: 0, c: []})
2397
+ end
2398
+ it 'reads itself' do
2399
+ values = {a: 44, b: 4444, c: {a: 5555, b: 77, c: [1, 2, 3]}}
2400
+ struct_instance = struct.new(values)
2401
+ expect(struct.read(struct_instance.to_binary_s)).to eq(values)
2402
+ end
2403
+ context 'with values' do
2404
+ subject do
2405
+ struct.new(a: 5, b: 6, c: {a: 7, b: 8, c: [1, 2, 3]})
2406
+ end
2407
+ it 'returns the expected member values' do
2408
+ expect(subject).to eq(a: 5, b: 6, c: {a: 7, b:8, c: [1, 2, 3]})
2409
+ end
2410
+ it 'outputs the expected binary representation' do
2411
+ binary = "\x03\x00\x00\x00" # array max_count
2412
+ binary << "\x05\x00" # a
2413
+ binary << "\x06\x00" # b
2414
+ binary << "\x07\x00" # c.a
2415
+ binary << "\x08\x00" # c.b
2416
+ binary << "\x01\x00\x00\x00" # c.b[0].ref_id
2417
+ binary << "\x02\x00\x00\x00" # c.b[1].ref_id
2418
+ binary << "\x03\x00\x00\x00" # c.b[2].ref_id
2419
+ binary << "\x01\x00\x00\x00" # deferred c.b[0]
2420
+ binary << "\x02\x00\x00\x00" # deferred c.b[1]
2421
+ binary << "\x03\x00\x00\x00" # deferred c.b[2]
2422
+ expect(subject.to_binary_s).to eq(binary.b)
2423
+ end
2424
+ end
2425
+ end
2426
+
2427
+ context 'with an embedded structure containing a conformant varying array' do
2428
+ after :example do
2429
+ BinData::RegisteredClasses.unregister('test_struct')
2430
+ end
2431
+
2432
+ let(:struct) do
2433
+ struct_with_array = Class.new(described_class) do
2434
+ default_parameters byte_align: 4
2435
+ endian :little
2436
+
2437
+ ndr_uint16 :a
2438
+ ndr_uint16 :b
2439
+ ndr_conf_var_array :c, type: :ndr_uint32
2440
+ end
2441
+ BinData::RegisteredClasses.register('test_struct', struct_with_array)
2442
+ Class.new(described_class) do
2443
+ default_parameters byte_align: 4
2444
+ endian :little
2445
+
2446
+ ndr_uint16 :a
2447
+ ndr_uint16 :b
2448
+ test_struct :c
2449
+ end
2450
+ end
2451
+
2452
+ it 'initializes the members to their default value' do
2453
+ expect(struct.new).to eq(a: 0, b: 0, c: {a: 0, b: 0, c: []})
2454
+ end
2455
+ it 'reads itself' do
2456
+ values = {a: 44, b: 4444, c: {a: 5555, b: 77, c: [1, 2, 3]}}
2457
+ struct_instance = struct.new(values)
2458
+ expect(struct.read(struct_instance.to_binary_s)).to eq(values)
2459
+ end
2460
+ context 'with values' do
2461
+ subject do
2462
+ struct.new(a: 5, b: 6, c: {a: 7, b: 8, c: [1, 2, 3]})
2463
+ end
2464
+ it 'returns the expected member values' do
2465
+ expect(subject).to eq(a: 5, b: 6, c: {a: 7, b:8, c: [1, 2, 3]})
2466
+ end
2467
+ it 'outputs the expected binary representation' do
2468
+ binary = "\x03\x00\x00\x00" # array max_count
2469
+ binary << "\x05\x00" # a
2470
+ binary << "\x06\x00" # b
2471
+ binary << "\x07\x00" # c.a
2472
+ binary << "\x08\x00" # c.b
2473
+ binary << "\x00\x00\x00\x00" # array offset
2474
+ binary << "\x03\x00\x00\x00" # array actual_count
2475
+ binary << "\x01\x00\x00\x00" # c.c[0]
2476
+ binary << "\x02\x00\x00\x00" # c.c[1]
2477
+ binary << "\x03\x00\x00\x00" # c.c[2]
2478
+ expect(subject.to_binary_s).to eq(binary.b)
2479
+ end
2480
+ end
2481
+ end
2482
+
2483
+ context 'with an embedded structure containing a conformant varying array of pointers' do
2484
+ after :example do
2485
+ BinData::RegisteredClasses.unregister('test_struct')
2486
+ end
2487
+
2488
+ let(:struct) do
2489
+ struct_with_array = Class.new(described_class) do
2490
+ default_parameters byte_align: 4
2491
+ endian :little
2492
+
2493
+ ndr_uint16 :a
2494
+ ndr_uint16 :b
2495
+ ndr_conf_var_array :c, type: :ndr_uint32_ptr
2496
+ end
2497
+ BinData::RegisteredClasses.register('test_struct', struct_with_array)
2498
+ Class.new(described_class) do
2499
+ default_parameters byte_align: 4
2500
+ endian :little
2501
+
2502
+ ndr_uint16 :a
2503
+ ndr_uint16 :b
2504
+ test_struct :c
2505
+ end
2506
+ end
2507
+
2508
+ it 'initializes the members to their default value' do
2509
+ expect(struct.new).to eq(a: 0, b: 0, c: {a: 0, b: 0, c: []})
2510
+ end
2511
+ it 'reads itself' do
2512
+ values = {a: 44, b: 4444, c: {a: 5555, b: 77, c: [1, 2, 3]}}
2513
+ struct_instance = struct.new(values)
2514
+ expect(struct.read(struct_instance.to_binary_s)).to eq(values)
2515
+ end
2516
+ context 'with values' do
2517
+ subject do
2518
+ struct.new(a: 5, b: 6, c: {a: 7, b: 8, c: [1, 2, 3]})
2519
+ end
2520
+ it 'returns the expected member values' do
2521
+ expect(subject).to eq(a: 5, b: 6, c: {a: 7, b:8, c: [1, 2, 3]})
2522
+ end
2523
+ it 'outputs the expected binary representation' do
2524
+ binary = "\x03\x00\x00\x00" # array max_count
2525
+ binary << "\x05\x00" # a
2526
+ binary << "\x06\x00" # b
2527
+ binary << "\x07\x00" # c.a
2528
+ binary << "\x08\x00" # c.b
2529
+ binary << "\x00\x00\x00\x00" # array offset
2530
+ binary << "\x03\x00\x00\x00" # array actual_count
2531
+ binary << "\x01\x00\x00\x00" # c.b[0].ref_id
2532
+ binary << "\x02\x00\x00\x00" # c.b[1].ref_id
2533
+ binary << "\x03\x00\x00\x00" # c.b[2].ref_id
2534
+ binary << "\x01\x00\x00\x00" # deferred c.b[0]
2535
+ binary << "\x02\x00\x00\x00" # deferred c.b[1]
2536
+ binary << "\x03\x00\x00\x00" # deferred c.b[2]
2537
+ expect(subject.to_binary_s).to eq(binary.b)
2538
+ end
2539
+ end
2540
+ end
2541
+
2542
+ context 'with a pointer to a conformant arrays' do
2543
+ let(:struct) do
2544
+ struct_ptr = Class.new(RubySMB::Dcerpc::Ndr::NdrConfArray) do
2545
+ default_parameters(type: :ndr_uint32)
2546
+ extend RubySMB::Dcerpc::Ndr::PointerClassPlugin
2547
+ end
2548
+ BinData::RegisteredClasses.register('test_conf_array_ptr', struct_ptr)
2549
+ Class.new(described_class) do
2550
+ default_parameters byte_align: 4
2551
+ endian :little
2552
+
2553
+ ndr_uint32 :a
2554
+ ndr_uint32 :b
2555
+ test_conf_array_ptr :c
2556
+ end
2557
+ end
2558
+
2559
+ it 'initializes the members to their default value' do
2560
+ expect(struct.new).to eq(a: 0, b: 0, c: :null)
2561
+ end
2562
+ it 'reads itself' do
2563
+ values = {a: 44, b: 4444, c: [1,2,3]}
2564
+ struct_instance = struct.new(values)
2565
+ expect(struct.read(struct_instance.to_binary_s)).to eq(values)
2566
+ end
2567
+ context 'with values' do
2568
+ subject do
2569
+ struct.new(a: 4, b: 5, c: [1,2,3])
2570
+ end
2571
+ it 'returns the expected member values' do
2572
+ expect(subject).to eq(a: 4, b: 5, c: [1,2,3])
2573
+ end
2574
+ it 'outputs the expected binary representation' do
2575
+ binary = "\x04\x00\x00\x00" # a
2576
+ binary << "\x05\x00\x00\x00" # b
2577
+ binary << "\x01\x00\x00\x00" # c.ref_id
2578
+ binary << "\x03\x00\x00\x00" # array max_count
2579
+ binary << "\x01\x00\x00\x00" # c[0]
2580
+ binary << "\x02\x00\x00\x00" # c[1]
2581
+ binary << "\x03\x00\x00\x00" # c[2]
2582
+ expect(subject.to_binary_s).to eq(binary.b)
2583
+ end
2584
+ end
2585
+ end
2586
+
2587
+ context 'with a pointer to an embedded structure containing a conformant arrays' do
2588
+ after :example do
2589
+ BinData::RegisteredClasses.unregister('test_struct')
2590
+ end
2591
+
2592
+ let(:struct) do
2593
+ struct_with_array = Class.new(described_class) do
2594
+ default_parameters byte_align: 4
2595
+ endian :little
2596
+
2597
+ ndr_uint32 :a
2598
+ ndr_conf_var_array :b, type: :ndr_uint32
2599
+ end
2600
+ struct_ptr = Class.new(struct_with_array) do
2601
+ extend RubySMB::Dcerpc::Ndr::PointerClassPlugin
2602
+ end
2603
+ BinData::RegisteredClasses.register('test_struct_ptr', struct_ptr)
2604
+ Class.new(BinData::Record) do
2605
+ default_parameters byte_align: 4
2606
+ endian :little
2607
+
2608
+ ndr_uint32 :a
2609
+ ndr_uint32 :b
2610
+ test_struct_ptr :c
2611
+ end
2612
+ end
2613
+
2614
+ it 'initializes the members to their default value' do
2615
+ expect(struct.new).to eq(a: 0, b: 0, c: :null)
2616
+ end
2617
+ it 'reads itself' do
2618
+ values = {a: 44, b: 4444, c: {a: 5555, b: [1, 2, 3, 4]}}
2619
+ struct_instance = struct.new(values)
2620
+ expect(struct.read(struct_instance.to_binary_s)).to eq(values)
2621
+ end
2622
+ context 'with values' do
2623
+ subject do
2624
+ struct.new(a: 5, b: 6, c: {a: 7, b: [1, 2, 3, 4]})
2625
+ end
2626
+ it 'returns the expected member values' do
2627
+ expect(subject).to eq(a: 5, b: 6, c: {a: 7, b: [1, 2, 3, 4]})
2628
+ end
2629
+ it 'outputs the expected binary representation' do
2630
+ binary = "\x05\x00\x00\x00" # a
2631
+ binary << "\x06\x00\x00\x00" # b
2632
+ binary << "\x01\x00\x00\x00" # c.ref_id
2633
+ binary << "\x04\x00\x00\x00" # array max_count
2634
+ binary << "\x07\x00\x00\x00" # c.a
2635
+ binary << "\x00\x00\x00\x00" # array offset
2636
+ binary << "\x04\x00\x00\x00" # array actual_count
2637
+ binary << "\x01\x00\x00\x00" # c.b[0]
2638
+ binary << "\x02\x00\x00\x00" # c.b[1]
2639
+ binary << "\x03\x00\x00\x00" # c.b[2]
2640
+ binary << "\x04\x00\x00\x00" # c.b[3]
2641
+ expect(subject.to_binary_s).to eq(binary.b)
2642
+ end
2643
+ end
2644
+ end
2645
+
2646
+ context 'with varying strings' do
2647
+ let(:struct) do
2648
+ Class.new(described_class) do
2649
+ default_parameters byte_align: 4
2650
+ endian :little
2651
+
2652
+ ndr_uint8 :a
2653
+ ndr_var_string :b
2654
+ ndr_uint32 :c
2655
+ ndr_var_string :d
2656
+ ndr_boolean :e
2657
+ end
2658
+ end
2659
+
2660
+ it 'initializes the members to their default value' do
2661
+ expect(struct.new).to eq(a: 0, b: '', c: 0, d: '', e: false)
2662
+ end
2663
+ it 'reads itself' do
2664
+ values = {a: 44, b: 'Test1', c: 4444, d: 'Test22', e: true}
2665
+ struct_instance = struct.new(values)
2666
+ expect(struct.read(struct_instance.to_binary_s)).to eq(values)
2667
+ end
2668
+ context 'with values' do
2669
+ subject do
2670
+ struct.new(a: 1, b: 'Test1', c: 3, d: 'Test22', e: true)
2671
+ end
2672
+ it 'returns the expected member values' do
2673
+ expect(subject).to eq(a: 1, b: 'Test1', c: 3, d: 'Test22', e: true)
2674
+ end
2675
+ it 'outputs the expected binary representation' do
2676
+ binary = "\x01" # a
2677
+ binary << "\x00\x00\x00" # pad
2678
+ binary << "\x00\x00\x00\x00" # b offset
2679
+ binary << "\x05\x00\x00\x00" # b actual_count
2680
+ binary << 'Test1' # b
2681
+ binary << "\x00\x00\x00" # pad
2682
+ binary << "\x03\x00\x00\x00" # c
2683
+ binary << "\x00\x00\x00\x00" # b offset
2684
+ binary << "\x06\x00\x00\x00" # b actual_count
2685
+ binary << 'Test22' # d
2686
+ binary << "\x00\x00" # pad
2687
+ binary << "\x01\x00\x00\x00" # e
2688
+ expect(subject.to_binary_s).to eq(binary.b)
2689
+ end
2690
+ end
2691
+ end
2692
+
2693
+ context 'with varying null terminated strings' do
2694
+ let(:struct) do
2695
+ Class.new(described_class) do
2696
+ default_parameters byte_align: 4
2697
+ endian :little
2698
+
2699
+ ndr_uint8 :a
2700
+ ndr_var_stringz :b
2701
+ ndr_uint32 :c
2702
+ ndr_var_stringz :d
2703
+ ndr_boolean :e
2704
+ end
2705
+ end
2706
+
2707
+ it 'initializes the members to their default value' do
2708
+ expect(struct.new).to eq(a: 0, b: '', c: 0, d: '', e: false)
2709
+ end
2710
+ it 'reads itself' do
2711
+ values = {a: 44, b: 'Test1', c: 4444, d: 'Test22', e: true}
2712
+ struct_instance = struct.new(values)
2713
+ expect(struct.read(struct_instance.to_binary_s)).to eq(values)
2714
+ end
2715
+ context 'with values' do
2716
+ subject do
2717
+ struct.new(a: 1, b: 'Test1', c: 3, d: 'Test22', e: true)
2718
+ end
2719
+ it 'returns the expected member values' do
2720
+ expect(subject).to eq(a: 1, b: 'Test1', c: 3, d: 'Test22', e: true)
2721
+ end
2722
+ it 'outputs the expected binary representation' do
2723
+ binary = "\x01" # a
2724
+ binary << "\x00\x00\x00" # pad
2725
+ binary << "\x00\x00\x00\x00" # b offset
2726
+ binary << "\x06\x00\x00\x00" # b actual_count
2727
+ binary << "Test1\x00" # b
2728
+ binary << "\x00\x00" # pad
2729
+ binary << "\x03\x00\x00\x00" # c
2730
+ binary << "\x00\x00\x00\x00" # b offset
2731
+ binary << "\x07\x00\x00\x00" # b actual_count
2732
+ binary << "Test22\x00" # d
2733
+ binary << "\x00" # pad
2734
+ binary << "\x01\x00\x00\x00" # e
2735
+ expect(subject.to_binary_s).to eq(binary.b)
2736
+ end
2737
+ end
2738
+ end
2739
+
2740
+ context 'with varying wide strings' do
2741
+ let(:struct) do
2742
+ Class.new(described_class) do
2743
+ default_parameters byte_align: 4
2744
+ endian :little
2745
+
2746
+ ndr_uint8 :a
2747
+ ndr_var_wide_string :b
2748
+ ndr_uint32 :c
2749
+ ndr_var_wide_string :d
2750
+ ndr_boolean :e
2751
+ end
2752
+ end
2753
+
2754
+ it 'initializes the members to their default value' do
2755
+ expect(struct.new).to eq(a: 0, b: '', c: 0, d: '', e: false)
2756
+ end
2757
+ it 'reads itself' do
2758
+ values = {a: 44, b: 'Test1'.encode('utf-16le'), c: 4444, d: 'Test22'.encode('utf-16le'), e: true}
2759
+ struct_instance = struct.new(values)
2760
+ expect(struct.read(struct_instance.to_binary_s)).to eq(values)
2761
+ end
2762
+ context 'with values' do
2763
+ subject do
2764
+ struct.new(a: 1, b: 'Test1', c: 3, d: 'Test22', e: true)
2765
+ end
2766
+ it 'returns the expected member values' do
2767
+ expect(subject).to eq(a: 1, b: 'Test1'.encode('utf-16le'), c: 3, d: 'Test22'.encode('utf-16le'), e: true)
2768
+ end
2769
+ it 'outputs the expected binary representation' do
2770
+ binary = "\x01" # a
2771
+ binary << "\x00\x00\x00" # pad
2772
+ binary << "\x00\x00\x00\x00" # b offset
2773
+ binary << "\x05\x00\x00\x00" # b actual_count
2774
+ binary << 'Test1'.encode('utf-16le').b # b
2775
+ binary << "\x00\x00" # pad
2776
+ binary << "\x03\x00\x00\x00" # c
2777
+ binary << "\x00\x00\x00\x00" # b offset
2778
+ binary << "\x06\x00\x00\x00" # b actual_count
2779
+ binary << 'Test22'.encode('utf-16le').b # d
2780
+ binary << "\x01\x00\x00\x00" # e
2781
+ expect(subject.to_binary_s).to eq(binary.b)
2782
+ end
2783
+ end
2784
+ end
2785
+
2786
+ context 'with varying null terminated wide strings' do
2787
+ let(:struct) do
2788
+ Class.new(described_class) do
2789
+ default_parameters byte_align: 4
2790
+ endian :little
2791
+
2792
+ ndr_uint8 :a
2793
+ ndr_var_wide_stringz :b
2794
+ ndr_uint32 :c
2795
+ ndr_var_wide_stringz :d
2796
+ ndr_boolean :e
2797
+ end
2798
+ end
2799
+
2800
+ it 'initializes the members to their default value' do
2801
+ expect(struct.new).to eq(a: 0, b: '', c: 0, d: '', e: false)
2802
+ end
2803
+ it 'reads itself' do
2804
+ values = {a: 44, b: 'Test1'.encode('utf-16le'), c: 4444, d: 'Test22'.encode('utf-16le'), e: true}
2805
+ struct_instance = struct.new(values)
2806
+ expect(struct.read(struct_instance.to_binary_s)).to eq(values)
2807
+ end
2808
+ context 'with values' do
2809
+ subject do
2810
+ struct.new(a: 1, b: 'Test1', c: 3, d: 'Test22', e: true)
2811
+ end
2812
+ it 'returns the expected member values' do
2813
+ expect(subject).to eq(a: 1, b: 'Test1'.encode('utf-16le'), c: 3, d: 'Test22'.encode('utf-16le'), e: true)
2814
+ end
2815
+ it 'outputs the expected binary representation' do
2816
+ binary = "\x01" # a
2817
+ binary << "\x00\x00\x00" # pad
2818
+ binary << "\x00\x00\x00\x00" # b offset
2819
+ binary << "\x06\x00\x00\x00" # b actual_count
2820
+ binary << "Test1\x00".encode('utf-16le').b # b
2821
+ binary << "\x03\x00\x00\x00" # c
2822
+ binary << "\x00\x00\x00\x00" # b offset
2823
+ binary << "\x07\x00\x00\x00" # b actual_count
2824
+ binary << "Test22\x00".encode('utf-16le').b # d
2825
+ binary << "\x00\x00" # pad
2826
+ binary << "\x01\x00\x00\x00" # e
2827
+ expect(subject.to_binary_s).to eq(binary.b)
2828
+ end
2829
+ end
2830
+ end
2831
+
2832
+ context 'with conformant varying strings' do
2833
+ let(:struct) do
2834
+ Class.new(described_class) do
2835
+ default_parameters byte_align: 4
2836
+ endian :little
2837
+
2838
+ ndr_uint8 :a
2839
+ ndr_uint32 :b
2840
+ ndr_boolean :c
2841
+ ndr_conf_var_string :d
2842
+ end
2843
+ end
2844
+
2845
+ it 'initializes the members to their default value' do
2846
+ expect(struct.new).to eq(a: 0, b: 0, c: false, d: '')
2847
+ end
2848
+ it 'reads itself' do
2849
+ values = {a: 44, b: 4444, c: true, d: 'Test22'}
2850
+ struct_instance = struct.new(values)
2851
+ expect(struct.read(struct_instance.to_binary_s)).to eq(values)
2852
+ end
2853
+ context 'with values' do
2854
+ subject do
2855
+ struct.new(a: 1, b: 3, c: true, d: 'Test22')
2856
+ end
2857
+ it 'returns the expected member values' do
2858
+ expect(subject).to eq(a: 1, b: 3, c: true, d: 'Test22')
2859
+ end
2860
+ it 'outputs the expected binary representation' do
2861
+ binary = "\x06\x00\x00\x00" # b max_count
2862
+ binary << "\x01" # a
2863
+ binary << "\x00\x00\x00" # pad
2864
+ binary << "\x03\x00\x00\x00" # b
2865
+ binary << "\x01\x00\x00\x00" # c
2866
+ binary << "\x00\x00\x00\x00" # b offset
2867
+ binary << "\x06\x00\x00\x00" # b actual_count
2868
+ binary << 'Test22' # d
2869
+ expect(subject.to_binary_s).to eq(binary.b)
2870
+ end
2871
+ end
2872
+ end
2873
+
2874
+ context 'with conformant varying null-terminated strings' do
2875
+ let(:struct) do
2876
+ Class.new(described_class) do
2877
+ default_parameters byte_align: 4
2878
+ endian :little
2879
+
2880
+ ndr_uint8 :a
2881
+ ndr_uint32 :b
2882
+ ndr_boolean :c
2883
+ ndr_conf_var_stringz :d
2884
+ end
2885
+ end
2886
+
2887
+ it 'initializes the members to their default value' do
2888
+ expect(struct.new).to eq(a: 0, b: 0, c: false, d: '')
2889
+ end
2890
+ it 'reads itself' do
2891
+ values = {a: 44, b: 4444, c: true, d: 'Test22'}
2892
+ struct_instance = struct.new(values)
2893
+ expect(struct.read(struct_instance.to_binary_s)).to eq(values)
2894
+ end
2895
+ context 'with values' do
2896
+ subject do
2897
+ struct.new(a: 1, b: 3, c: true, d: 'Test22')
2898
+ end
2899
+ it 'returns the expected member values' do
2900
+ expect(subject).to eq(a: 1, b: 3, c: true, d: 'Test22')
2901
+ end
2902
+ it 'outputs the expected binary representation' do
2903
+ binary = "\x07\x00\x00\x00" # b max_count
2904
+ binary << "\x01" # a
2905
+ binary << "\x00\x00\x00" # pad
2906
+ binary << "\x03\x00\x00\x00" # b
2907
+ binary << "\x01\x00\x00\x00" # c
2908
+ binary << "\x00\x00\x00\x00" # b offset
2909
+ binary << "\x07\x00\x00\x00" # b actual_count
2910
+ binary << "Test22\x00" # d
2911
+ expect(subject.to_binary_s).to eq(binary.b)
2912
+ end
2913
+ end
2914
+ end
2915
+
2916
+ context 'with conformant varying wide strings' do
2917
+ let(:struct) do
2918
+ Class.new(described_class) do
2919
+ default_parameters byte_align: 4
2920
+ endian :little
2921
+
2922
+ ndr_uint8 :a
2923
+ ndr_uint32 :b
2924
+ ndr_boolean :c
2925
+ ndr_conf_var_wide_string :d
2926
+ end
2927
+ end
2928
+
2929
+ it 'initializes the members to their default value' do
2930
+ expect(struct.new).to eq(a: 0, b: 0, c: false, d: '')
2931
+ end
2932
+ it 'reads itself' do
2933
+ values = {a: 44, b: 4444, c: true, d: 'Test22'.encode('utf-16le')}
2934
+ struct_instance = struct.new(values)
2935
+ expect(struct.read(struct_instance.to_binary_s)).to eq(values)
2936
+ end
2937
+ context 'with values' do
2938
+ subject do
2939
+ struct.new(a: 1, b: 3, c: true, d: 'Test22')
2940
+ end
2941
+ it 'returns the expected member values' do
2942
+ expect(subject).to eq(a: 1, b: 3, c: true, d: 'Test22'.encode('utf-16le'))
2943
+ end
2944
+ it 'outputs the expected binary representation' do
2945
+ binary = "\x06\x00\x00\x00" # b max_count
2946
+ binary << "\x01" # a
2947
+ binary << "\x00\x00\x00" # pad
2948
+ binary << "\x03\x00\x00\x00" # b
2949
+ binary << "\x01\x00\x00\x00" # c
2950
+ binary << "\x00\x00\x00\x00" # b offset
2951
+ binary << "\x06\x00\x00\x00" # b actual_count
2952
+ binary << 'Test22'.encode('utf-16le').b # d
2953
+ expect(subject.to_binary_s).to eq(binary.b)
2954
+ end
2955
+ end
2956
+ end
2957
+
2958
+ context 'with conformant varying null-terminated wide strings' do
2959
+ let(:struct) do
2960
+ Class.new(described_class) do
2961
+ default_parameters byte_align: 4
2962
+ endian :little
2963
+
2964
+ ndr_uint8 :a
2965
+ ndr_uint32 :b
2966
+ ndr_boolean :c
2967
+ ndr_conf_var_wide_stringz :d
2968
+ end
2969
+ end
2970
+
2971
+ it 'initializes the members to their default value' do
2972
+ expect(struct.new).to eq(a: 0, b: 0, c: false, d: '')
2973
+ end
2974
+ it 'reads itself' do
2975
+ values = {a: 44, b: 4444, c: true, d: 'Test22'.encode('utf-16le')}
2976
+ struct_instance = struct.new(values)
2977
+ expect(struct.read(struct_instance.to_binary_s)).to eq(values)
2978
+ end
2979
+ context 'with values' do
2980
+ subject do
2981
+ struct.new(a: 1, b: 3, c: true, d: 'Test22')
2982
+ end
2983
+ it 'returns the expected member values' do
2984
+ expect(subject).to eq(a: 1, b: 3, c: true, d: 'Test22'.encode('utf-16le'))
2985
+ end
2986
+ it 'outputs the expected binary representation' do
2987
+ binary = "\x07\x00\x00\x00" # b max_count
2988
+ binary << "\x01" # a
2989
+ binary << "\x00\x00\x00" # pad
2990
+ binary << "\x03\x00\x00\x00" # b
2991
+ binary << "\x01\x00\x00\x00" # c
2992
+ binary << "\x00\x00\x00\x00" # b offset
2993
+ binary << "\x07\x00\x00\x00" # b actual_count
2994
+ binary << "Test22\x00".encode('utf-16le').b # d
2995
+ expect(subject.to_binary_s).to eq(binary.b)
2996
+ end
2997
+ end
2998
+ end
2999
+ end
3000
+
3001
+
3002
+ #
3003
+ # Pointers
3004
+ #
3005
+ {
3006
+ NdrUint8Ptr: { parent_class: :NdrUint8, data: 2, binary: "\x02", size: 1 },
3007
+ NdrUint16Ptr: { parent_class: :NdrUint16, data: 3, binary: [3].pack('S'), size: 2 },
3008
+ NdrUint32Ptr: { parent_class: :NdrUint32, data: 5, binary: [5].pack('L'), size: 4 },
3009
+ NdrUint64Ptr: { parent_class: :NdrUint64, data: 7, binary: [7].pack('Q'), size: 8 },
3010
+ NdrCharPtr: { parent_class: :NdrChar, data: 'C', binary: 'C', size: 1 },
3011
+ NdrBooleanPtr: { parent_class: :NdrBoolean, data: true, binary: [1].pack('L'), size: 4 },
3012
+ NdrStringPtr: {
3013
+ parent_class: :NdrConfVarString,
3014
+ data: 'Test1',
3015
+ binary: "#{[5].pack('L')}#{[0].pack('L')}#{[5].pack('L')}Test1", size: 4
3016
+ },
3017
+ NdrStringzPtr: {
3018
+ parent_class: :NdrConfVarStringz,
3019
+ data: 'Test2',
3020
+ binary: "#{[6].pack('L')}#{[0].pack('L')}#{[6].pack('L')}Test2\x00", size: 4
3021
+ },
3022
+ NdrWideStringPtr: {
3023
+ parent_class: :NdrConfVarWideString,
3024
+ data: 'Test3'.encode('utf-16le'),
3025
+ binary: "#{[5].pack('L')}#{[0].pack('L')}#{[5].pack('L')}#{'Test3'.encode('utf-16le').b}", size: 4
3026
+ },
3027
+ NdrWideStringzPtr: {
3028
+ parent_class: :NdrConfVarWideStringz,
3029
+ data: 'Test4'.encode('utf-16le'),
3030
+ binary: "#{[6].pack('L')}#{[0].pack('L')}#{[6].pack('L')}#{'Test4'.encode('utf-16le').b}\x00\x00", size: 4
3031
+ },
3032
+ NdrByteArrayPtr: {
3033
+ parent_class: :NdrConfVarArray,
3034
+ data: [1,2,3,4],
3035
+ binary: "#{[4].pack('L')}#{[0].pack('L')}#{[4].pack('L')}\x01\x02\x03\x04", size: 4
3036
+ },
3037
+ NdrFileTimePtr: {
3038
+ parent_class: :NdrFileTime,
3039
+ data: 132682503830000000,
3040
+ binary: [132682503830000000].pack('Q'), size: 4
3041
+ }
3042
+ }.each do |ndr_class, info|
3043
+ RSpec.describe(RubySMB::Dcerpc::Ndr.const_get(ndr_class)) do
3044
+ subject { described_class.new }
3045
+ let(:class_with_ref_to) do
3046
+ struct = Class.new(BinData::Record) do
3047
+ endian :little
3048
+ ndr_uint32 :a
3049
+ end
3050
+ struct.send(described_class.bindata_name.to_sym, :b)
3051
+ struct.send(described_class.bindata_name.to_sym, :c, ref_to: :b)
3052
+ struct
3053
+ end
3054
+ let(:ref_to_instance) { class_with_ref_to.new(a: 1, b: info[:data]) }
3055
+ let(:ref_id) { [RubySMB::Dcerpc::Ndr::INITIAL_REF_ID].pack('L') }
400
3056
 
401
- it 'exists if superclass #referent_identifier is not zero' do
402
- expect(packet.referent?).to be true
3057
+ it 'is a RubySMB::Dcerpc::Ndr::PointerClassPlugin class' do
3058
+ expect(described_class).to be_a(RubySMB::Dcerpc::Ndr::PointerClassPlugin)
3059
+ end
3060
+ it 'is not a Top Level class' do
3061
+ expect(described_class).not_to be_a(RubySMB::Dcerpc::Ndr::TopLevelPlugin)
3062
+ end
3063
+ it "is a RubySMB::Dcerpc::Ndr::#{info[:parent_class]} class" do
3064
+ parent_class = if info[:parent_class].is_a?(Symbol)
3065
+ RubySMB::Dcerpc::Ndr.const_get(info[:parent_class])
3066
+ else
3067
+ info[:parent_class]
3068
+ end
3069
+ expect(described_class).to be < parent_class
3070
+ end
3071
+
3072
+ it { is_expected.to respond_to :ref_id }
3073
+
3074
+ describe '#initialize_instance' do
3075
+ it 'sets #ref_id to 0 by default' do
3076
+ expect(subject.ref_id).to eq(0)
3077
+ end
3078
+ it 'does not reset #ref_id to 0 when it has already been set to a value' do
3079
+ subject.ref_id = 5
3080
+ subject.initialize_instance
3081
+ expect(subject.ref_id).to eq(5)
3082
+ end
3083
+ it 'sets #ref_id to INITIAL_REF_ID by default when :initial_value parameter is provided' do
3084
+ test_instance = described_class.new(nil, {initial_value: info[:data]})
3085
+ expect(test_instance.ref_id).to eq(RubySMB::Dcerpc::Ndr::INITIAL_REF_ID)
3086
+ end
3087
+ end
3088
+
3089
+ describe '#extend_top_level_class' do
3090
+ it 'extends to TopLevelPlugin' do
3091
+ expect(subject).to be_a(RubySMB::Dcerpc::Ndr::TopLevelPlugin)
3092
+ end
3093
+ it 'sets the pointer to top level pointer' do
3094
+ expect(subject.is_top_level_ptr).to be true
3095
+ end
3096
+ context 'when embedded in another structure' do
3097
+ let(:struct_class) do
3098
+ struct_class = Class.new(RubySMB::Dcerpc::Ndr::NdrStruct) do
3099
+ default_parameters byte_align: 4
3100
+ endian :little
3101
+ end
3102
+ struct_class.send(described_class.bindata_name.to_sym, :c)
3103
+ struct_class
3104
+ end
3105
+ let(:top_level_class) do
3106
+ Class.new(BinData::Record) do
3107
+ endian :little
3108
+ ndr_uint32 :a
3109
+ test_struct :b
3110
+ end
3111
+ end
3112
+ before :example do
3113
+ BinData::RegisteredClasses.register('test_struct', struct_class)
3114
+ end
3115
+
3116
+ it 'extends the parents to TopLevelPlugin' do
3117
+ struct = top_level_class.new(a: 2, b: {c: info[:data]})
3118
+ expect(struct).to be_a(RubySMB::Dcerpc::Ndr::TopLevelPlugin)
3119
+ expect(struct.b).to be_a(RubySMB::Dcerpc::Ndr::TopLevelPlugin)
3120
+ end
3121
+ it 'only sets the top level structure as a top level pointer' do
3122
+ struct = top_level_class.new(a: 2, b: {c: info[:data]})
3123
+ expect(struct.is_top_level_ptr).to be true
3124
+ expect(struct.b.is_top_level_ptr).to be false
3125
+ expect(struct.b.c.is_top_level_ptr).to be false
3126
+ end
3127
+ end
3128
+ end
3129
+
3130
+ describe '#snapshot' do
3131
+ it 'outputs :null by default' do
3132
+ expect(subject.snapshot).to eq(:null)
3133
+ end
3134
+ it 'outputs the referent when it refers to another Top-Level pointer' do
3135
+ expect(ref_to_instance.snapshot).to eq({a:1, b:info[:data], c:info[:data]})
3136
+ end
3137
+ # BinData does not support initial_value parameter for arrays
3138
+ unless ndr_class == :NdrByteArrayPtr
3139
+ it 'outputs the :initial_value parameter value when nothing has been assigned yet' do
3140
+ test_ptr = described_class.new(nil, {initial_value: info[:data]})
3141
+ expect(test_ptr.snapshot).to eq(info[:data])
3142
+ end
3143
+ end
3144
+ end
3145
+
3146
+ describe '#do_write' do
3147
+ it 'outputs 32-bit zero binary representation when it is a null pointer' do
3148
+ expect(subject.to_binary_s).to eq("\x00\x00\x00\x00".b)
3149
+ end
3150
+ it 'outputs the initial referent ID followed by the representation of the referent' do
3151
+ expect(subject.new(info[:data]).to_binary_s).to eq("#{ref_id}#{info[:binary]}".b)
3152
+ end
3153
+ it 'outputs the referent ID of the Top-Level pointer it is refering to' do
3154
+ align = (4 - (info[:binary].size % 4)) % 4
3155
+ pad = "\x00" * align
3156
+ expect(ref_to_instance.to_binary_s).to eq(
3157
+ "#{[1].pack('L')}#{ref_id}#{info[:binary]}#{pad}#{ref_id}"
3158
+ )
3159
+ expect(ref_to_instance.c.to_binary_s).to eq(ref_id)
3160
+ end
3161
+ it 'outputs the initial referent ID and the referent representaiton if it is not embedded in another constructed structure' do
3162
+ expect(subject.new(info[:data]).to_binary_s).to eq("#{ref_id}#{info[:binary]}".b)
3163
+ end
3164
+ # BinData does not support initial_value parameter for arrays
3165
+ unless ndr_class == :NdrByteArrayPtr
3166
+ it 'outputs the :initial_value parameter value when nothing has been assigned yet' do
3167
+ test_ptr = described_class.new(nil, {initial_value: info[:data]})
3168
+ expect(test_ptr.new.to_binary_s).to eq("#{ref_id}#{info[:binary]}".b)
3169
+ end
3170
+ end
3171
+ context 'when embedded in another constructed structure'do
3172
+ let(:embedding_struct) do
3173
+ RubySMB::Dcerpc::Ndr::NdrConfArray.new([info[:data], info[:data]], type: described_class)
3174
+ end
3175
+ let(:output_str) do
3176
+ ref_id2 = [RubySMB::Dcerpc::Ndr::INITIAL_REF_ID + 1].pack('L')
3177
+ output_str = "#{[2].pack('L')}#{ref_id}#{ref_id2}".b
3178
+ align = (info[:size] - (output_str.size % info[:size])) % info[:size]
3179
+ output_str << "\x00".b * align
3180
+ output_str << info[:binary]
3181
+ align = (info[:size] - (output_str.size % info[:size])) % info[:size]
3182
+ output_str << "\x00".b * align
3183
+ output_str << info[:binary]
3184
+ end
3185
+
3186
+ it 'does not change the embedded structure binary representation' do
3187
+ expect(embedding_struct[0].to_binary_s).to eq("#{ref_id}#{info[:binary]}".b)
3188
+ expect(embedding_struct[1].to_binary_s).to eq("#{ref_id}#{info[:binary]}".b)
3189
+ end
3190
+ it 'returns the correct aligned binary stream' do
3191
+ expect(embedding_struct.to_binary_s).to eq(output_str)
3192
+ end
3193
+ context 'when calling #to_binary_s on embedded structures' do
3194
+ # There was a bug where calling to_binary_s updated the ref_id
3195
+ # persistently, which resulted in wrong output
3196
+ it 'returns the correct aligned binary stream' do
3197
+ embedding_struct[0].to_binary_s
3198
+ embedding_struct[1].to_binary_s
3199
+ expect(embedding_struct.to_binary_s).to eq(output_str)
3200
+ end
3201
+ end
3202
+ end
3203
+ end
3204
+
3205
+ describe '#do_read' do
3206
+ it 'reads a 32-bit zero binary representation as a null pointer' do
3207
+ expect(subject.read("\x00\x00\x00\x00".b)).to eq(:null)
3208
+ end
3209
+ it 'reads the referent ID followed by the representation of the referent' do
3210
+ expect(subject.read("#{ref_id}#{info[:binary]}")).to eq(info[:data])
3211
+ expect(subject.ref_id).to eq(RubySMB::Dcerpc::Ndr::INITIAL_REF_ID)
3212
+ end
3213
+ it 'reads the referent ID of the Top-Level pointer it is refering to' do
3214
+ align = (4 - (info[:binary].size % 4)) % 4
3215
+ pad = "\x00" * align
3216
+ binary_str = "#{[1].pack('L')}"\
3217
+ "#{ref_id}"\
3218
+ "#{info[:binary]}"\
3219
+ "#{pad}"\
3220
+ "#{ref_id}"
3221
+ test_instance = class_with_ref_to.read(binary_str)
3222
+ expect(test_instance.c.snapshot).to eq(info[:data])
3223
+ expect(test_instance.c.ref_id).to eq(test_instance.b.ref_id)
3224
+ end
3225
+ it 'reads the initial referent ID and the referent representaiton if it is not embedded in another constructed structure' do
3226
+ expect(subject.read("#{ref_id}#{info[:binary]}".b)).to eq(info[:data])
3227
+ expect(subject.ref_id).to eq(RubySMB::Dcerpc::Ndr::INITIAL_REF_ID)
3228
+ end
3229
+ context 'when embedded in another constructed structure'do
3230
+ let(:test_struct) { described_class.new(info[:data].dup) }
3231
+ let(:embedding_struct) do
3232
+ subject.assign(info[:data])
3233
+ RubySMB::Dcerpc::Ndr::NdrConfArray.new([subject, test_struct], type: described_class)
3234
+ end
3235
+ let(:binary_str) do
3236
+ ref_id2 = [RubySMB::Dcerpc::Ndr::INITIAL_REF_ID + 1].pack('L')
3237
+ binary_str = "#{[2].pack('L')}#{ref_id}#{ref_id2}".b
3238
+ align = (info[:size] - (binary_str.size % info[:size])) % info[:size]
3239
+ binary_str << "\x00".b * align
3240
+ binary_str << info[:binary]
3241
+ align = (info[:size] - (binary_str.size % info[:size])) % info[:size]
3242
+ binary_str << "\x00".b * align
3243
+ binary_str << info[:binary]
3244
+ binary_str
3245
+ end
3246
+ it 'correctly defers the referent after the embedding structure in the stream' do
3247
+ obj = embedding_struct.read(binary_str)
3248
+ expect(obj).to eq([subject, test_struct])
3249
+ end
3250
+ end
3251
+ end
3252
+
3253
+ describe '#assign' do
3254
+ it 'sets #ref_id to 0 when assigning :null' do
3255
+ subject.assign(:null)
3256
+ expect(subject.ref_id).to eq(0)
3257
+ end
3258
+ it 'assigns the value to the referent when it refers to another Top-Level pointer' do
3259
+ ref_to_instance.c = info[:data]
3260
+ expect(ref_to_instance.b).to eq(info[:data])
3261
+ end
3262
+ it 'sets #ref_id to the initial reference ID value' do
3263
+ subject.assign(info[:data])
3264
+ expect(subject.ref_id).to eq(RubySMB::Dcerpc::Ndr::INITIAL_REF_ID)
3265
+ end
3266
+ it 'does not change #ref_id when it has already been set' do
3267
+ subject.ref_id = 20
3268
+ subject.assign(info[:data])
3269
+ expect(subject.ref_id).to eq(20)
3270
+ end
3271
+ end
3272
+
3273
+ describe '#alias?' do
3274
+ it 'returns true if it refers to another Top-Level pointer' do
3275
+ expect(ref_to_instance.c.is_alias?).to be true
3276
+ end
3277
+ end
3278
+
3279
+ describe '#fetch_alias_referent' do
3280
+ it 'returns the referent' do
3281
+ expect(ref_to_instance.c.fetch_alias_referent).to eq(info[:data])
3282
+ end
3283
+ context 'when the referent is in a NDR structure' do
3284
+ let(:class_with_ref_to) do
3285
+ test_struct_class = Class.new(RubySMB::Dcerpc::Ndr::NdrStruct) do
3286
+ default_parameters(byte_align: 4)
3287
+ ndr_uint32 :a
3288
+ end
3289
+ test_struct_class.send(described_class.bindata_name.to_sym, :ptr1)
3290
+ BinData::RegisteredClasses.register('test_struct', test_struct_class)
3291
+ struct = Class.new(BinData::Record) do
3292
+ test_struct :struct1
3293
+ end
3294
+ struct.send(described_class.bindata_name.to_sym, :ptr2, ref_to: :ptr1)
3295
+ struct
3296
+ end
3297
+ let(:ref_to_instance) { class_with_ref_to.new(struct1: { a: 2, ptr1: info[:data] }) }
3298
+ it 'returns the referent' do
3299
+ expect(ref_to_instance.ptr2.fetch_alias_referent).to eq(info[:data])
3300
+ end
3301
+ end
3302
+ end
3303
+
3304
+ describe '#do_num_bytes' do
3305
+ it 'returns 4 if it is a null pointer' do
3306
+ expect(subject.do_num_bytes).to eq(4)
3307
+ end
3308
+ it 'returns 4 if it refers to another Top-Level pointer' do
3309
+ expect(ref_to_instance.c.do_num_bytes).to eq(4)
3310
+ end
3311
+ it "returns #{4 + info[:binary].size} a first-instance pointer" do
3312
+ subject.assign(info[:data])
3313
+ expect(subject.do_num_bytes).to eq(4 + info[:binary].size)
3314
+ end
3315
+ end
3316
+ end
3317
+ end
3318
+
3319
+ RSpec.describe RubySMB::Dcerpc::Ndr::TopLevelPlugin do
3320
+ let(:ref_id) { RubySMB::Dcerpc::Ndr::INITIAL_REF_ID }
3321
+ let(:struct_with_ptr) do
3322
+ Class.new(RubySMB::Dcerpc::Ndr::NdrStruct) do
3323
+ default_parameters byte_align: 4
3324
+ endian :little
3325
+
3326
+ ndr_uint32 :b
3327
+ ndr_char_ptr :ptr2
3328
+ ndr_uint32_ptr :ptr3
3329
+ end
3330
+ end
3331
+ let(:random_struct) do
3332
+ Class.new(RubySMB::Dcerpc::Ndr::NdrStruct) do
3333
+ default_parameters byte_align: 4
3334
+ endian :little
3335
+
3336
+ ndr_uint32 :rand1
3337
+ ndr_uint32 :rand2
3338
+ end
3339
+ end
3340
+
3341
+ before :example do
3342
+ BinData::RegisteredClasses.register('struct_with_ptr', struct_with_ptr)
3343
+ BinData::RegisteredClasses.register('random_struct', random_struct)
3344
+ end
3345
+
3346
+ context 'with a BinData structure' do
3347
+ let(:test_struct) do
3348
+ Class.new(BinData::Record) do
3349
+ endian :little
3350
+
3351
+ ndr_uint32 :a
3352
+ ndr_char_ptr :ptr1
3353
+ struct_with_ptr :d
3354
+ ndr_uint32_ptr :ptr4
3355
+ end
3356
+ end
3357
+ let(:snapshot) { { a: 55, ptr1: 'M', d: { b: 66, ptr2: 'A', ptr3: 33 }, ptr4: 44 } }
3358
+ let(:binary) do
3359
+ "#{[55].pack('L')}"\
3360
+ "#{[ref_id].pack('L')}"\
3361
+ "M"\
3362
+ "\x00\x00\x00"\
3363
+ "#{[66].pack('L')}"\
3364
+ "#{[ref_id + 1].pack('L')}"\
3365
+ "#{[ref_id + 2].pack('L')}"\
3366
+ "A"\
3367
+ "\x00\x00\x00"\
3368
+ "#{[33].pack('L')}"\
3369
+ "#{[ref_id + 3].pack('L')}"\
3370
+ "#{[44].pack('L')}".b
3371
+ end
3372
+ subject { test_struct.new(snapshot) }
3373
+
3374
+ it 'outputs the correct binary stream' do
3375
+ expect(subject.to_binary_s).to eq(binary)
3376
+ end
3377
+ end
3378
+
3379
+ context 'with a BinData structure containing pointers with :initial_value set' do
3380
+ let(:test_struct) do
3381
+ Class.new(BinData::Record) do
3382
+ endian :little
3383
+
3384
+ ndr_uint32 :a
3385
+ ndr_char_ptr :ptr1, initial_value: 'M'
3386
+ struct_with_ptr :d
3387
+ ndr_uint32_ptr :ptr4, initial_value: 44
3388
+ end
3389
+ end
3390
+ let(:snapshot) { { a: 55, d: { b: 66, ptr2: 'A', ptr3: 33 } } }
3391
+ let(:binary) do
3392
+ "#{[55].pack('L')}"\
3393
+ "#{[ref_id].pack('L')}"\
3394
+ "M"\
3395
+ "\x00\x00\x00"\
3396
+ "#{[66].pack('L')}"\
3397
+ "#{[ref_id + 1].pack('L')}"\
3398
+ "#{[ref_id + 2].pack('L')}"\
3399
+ "A"\
3400
+ "\x00\x00\x00"\
3401
+ "#{[33].pack('L')}"\
3402
+ "#{[ref_id + 3].pack('L')}"\
3403
+ "#{[44].pack('L')}".b
3404
+ end
3405
+ subject { test_struct.new(snapshot) }
3406
+
3407
+ it 'outputs the correct binary stream' do
3408
+ expect(subject.to_binary_s).to eq(binary)
3409
+ end
3410
+ end
3411
+
3412
+ context 'with a BinData::Choice' do
3413
+ let(:test_struct) do
3414
+ Class.new(BinData::Record) do
3415
+ endian :little
3416
+
3417
+ ndr_uint32 :a
3418
+ ndr_char_ptr :ptr1
3419
+ choice :choice1, selection: :a, byte_align: 4 do
3420
+ struct_with_ptr 55
3421
+ end
3422
+ ndr_uint32_ptr :ptr4
3423
+ end
3424
+ end
3425
+ let(:snapshot) { { a: 55, ptr1: 'M', choice1: { b: 66, ptr2: 'A', ptr3: 33 }, ptr4: 44 } }
3426
+ let(:binary) do
3427
+ "#{[55].pack('L')}"\
3428
+ "#{[ref_id].pack('L')}"\
3429
+ "M"\
3430
+ "\x00\x00\x00"\
3431
+ "#{[66].pack('L')}"\
3432
+ "#{[ref_id + 1].pack('L')}"\
3433
+ "#{[ref_id + 2].pack('L')}"\
3434
+ "A"\
3435
+ "\x00\x00\x00"\
3436
+ "#{[33].pack('L')}"\
3437
+ "#{[ref_id + 3].pack('L')}"\
3438
+ "#{[44].pack('L')}".b
3439
+ end
3440
+ subject { test_struct.new(snapshot) }
3441
+
3442
+ it 'outputs the correct binary stream' do
3443
+ expect(subject.to_binary_s).to eq(binary)
3444
+ end
3445
+ end
3446
+
3447
+ context 'with a NDR array and an alias pointer' do
3448
+ let(:test_struct) do
3449
+ Class.new(BinData::Record) do
3450
+ endian :little
3451
+
3452
+ ndr_uint32 :a
3453
+ ndr_char_ptr :ptr1
3454
+ ndr_conf_array :array1, type: :random_struct
3455
+ struct_with_ptr :d
3456
+ ndr_uint32_ptr :ptr4, ref_to: :ptr3
3457
+ end
3458
+ end
3459
+ let(:snapshot) do
3460
+ {
3461
+ a: 0xF1,
3462
+ ptr1: 'A',
3463
+ array1: [
3464
+ { rand1: 0xF2, rand2: 0xF3 },
3465
+ { rand1: 0xF4, rand2: 0xF5 }
3466
+ ],
3467
+ d: { b: 0xF6, ptr2: 'B', ptr3: 0xF7 }
3468
+ }
3469
+ end
3470
+ let(:binary) do
3471
+ "#{[0xF1].pack('L<')}"\
3472
+ "#{[ref_id].pack('L<')}"\
3473
+ "A"\
3474
+ "\x00\x00\x00"\
3475
+ "#{[2].pack('L<')}"\
3476
+ "#{[0xF2].pack('L<')}"\
3477
+ "#{[0xF3].pack('L<')}"\
3478
+ "#{[0xF4].pack('L<')}"\
3479
+ "#{[0xF5].pack('L<')}"\
3480
+ "#{[0xF6].pack('L<')}"\
3481
+ "#{[ref_id + 1].pack('L<')}"\
3482
+ "#{[ref_id + 2].pack('L<')}"\
3483
+ "B"\
3484
+ "\x00\x00\x00"\
3485
+ "#{[0xF7].pack('L<')}"\
3486
+ "#{[ref_id + 2].pack('L<')}"
3487
+ end
3488
+ subject { test_struct.new(snapshot) }
3489
+
3490
+ it 'outputs the correct binary stream' do
3491
+ expect(subject.to_binary_s).to eq(binary)
3492
+ end
3493
+
3494
+ context 'when the alias pointer is positioned after the referent pointer' do
3495
+ let(:test_struct2) do
3496
+ Class.new(BinData::Record) do
3497
+ endian :little
3498
+
3499
+ ndr_uint32 :a
3500
+ ndr_char_ptr :ptr1
3501
+ ndr_conf_array :array1, type: :random_struct
3502
+ # :ptr3 is part of :d structure, which appears after :ptr4
3503
+ ndr_uint32_ptr :ptr4, ref_to: :ptr3
3504
+ struct_with_ptr :d
3505
+ end
3506
+ end
3507
+
3508
+ context 'with #snapshot' do
3509
+ it 'raises an exception' do
3510
+ expect { test_struct2.new(snapshot).snapshot }.to raise_error(ArgumentError)
3511
+ end
3512
+ end
3513
+
3514
+ context 'with #to_binary_s' do
3515
+ it 'raises an exception' do
3516
+ expect { test_struct2.new(snapshot).to_binary_s }.to raise_error(ArgumentError)
3517
+ end
3518
+ end
3519
+ end
3520
+
3521
+ context 'when the alias pointer refers to a non-existing pointer' do
3522
+ let(:test_struct2) do
3523
+ Class.new(BinData::Record) do
3524
+ endian :little
3525
+
3526
+ ndr_uint32 :a
3527
+ ndr_char_ptr :ptr1
3528
+ ndr_conf_array :array1, type: :random_struct
3529
+ ndr_uint32_ptr :ptr4, ref_to: :ptr5
3530
+ struct_with_ptr :d
3531
+ end
3532
+ end
3533
+
3534
+ it 'raises an exception' do
3535
+ expect { test_struct2.new(snapshot).snapshot }.to raise_error(ArgumentError)
3536
+ end
3537
+ end
3538
+ end
3539
+ end
3540
+
3541
+ RSpec.describe ::BinData::NdrPointerArgProcessor do
3542
+ let(:embedding_struct) do
3543
+ Class.new(BinData::Record) do
3544
+ ndr_class :a
3545
+ end
3546
+ end
3547
+
3548
+ before :example do
3549
+ ndr_class = Class.new(ref_class) do
3550
+ #default_parameters byte_align: 4
3551
+ arg_processor :ndr_pointer
3552
+ end
3553
+ BinData::RegisteredClasses.register('ndr_class', ndr_class)
3554
+ end
3555
+
3556
+ context 'with a NDR structure as referent' do
3557
+ let(:ref_class) { RubySMB::Dcerpc::Ndr::NdrUint32 }
3558
+ it 'does not raise error' do
3559
+ expect { embedding_struct }.to_not raise_error
3560
+ end
3561
+ end
3562
+ context 'with a BinData structure as referent' do
3563
+ let(:ref_class) { BinData::Uint32le }
3564
+
3565
+ it 'raises an error' do
3566
+ expect { embedding_struct }.to raise_error(ArgumentError)
3567
+ end
3568
+ context 'with byte_align parameter' do
3569
+ let(:embedding_struct) do
3570
+ Class.new(BinData::Record) do
3571
+ ndr_class :a, byte_align: 4
3572
+ end
3573
+ end
3574
+ it 'raises an error' do
3575
+ expect { embedding_struct }.to raise_error(ArgumentError)
3576
+ end
3577
+ end
3578
+ context 'with referent_byte_align parameter' do
3579
+ let(:embedding_struct) do
3580
+ Class.new(BinData::Record) do
3581
+ ndr_class :a, referent_byte_align: 4
3582
+ end
3583
+ end
3584
+ it 'does not raise error' do
3585
+ expect { embedding_struct }.to_not raise_error
3586
+ end
3587
+ end
3588
+ end
3589
+ end
3590
+
3591
+ RSpec.describe RubySMB::Dcerpc::Ndr::NdrContextHandle do
3592
+ it 'is a BinData::Primitive class' do
3593
+ expect(described_class).to be < BinData::Primitive
3594
+ end
3595
+
3596
+ subject { described_class.new }
3597
+
3598
+ it { is_expected.to respond_to :context_handle_attributes }
3599
+ it { is_expected.to respond_to :context_handle_uuid }
3600
+
3601
+ context 'with #get' do
3602
+ it 'returns a hash with the default values' do
3603
+ expect(subject.get).to eq({context_handle_attributes: 0, context_handle_uuid: '00000000-0000-0000-0000-000000000000'})
3604
+ end
3605
+ it 'returns a hash with the expected values' do
3606
+ subject.assign(context_handle_attributes: 4, context_handle_uuid: '57800405-0301-3330-5566-040023007000')
3607
+ expect(subject.get).to eq({context_handle_attributes: 4, context_handle_uuid: '57800405-0301-3330-5566-040023007000'})
3608
+ end
3609
+ end
3610
+
3611
+ context 'with #set' do
3612
+ context 'with a hash' do
3613
+ it 'sets the expected values' do
3614
+ subject.set({context_handle_attributes: 4, context_handle_uuid: '57800405-0301-3330-5566-040023007000'})
3615
+ expect(subject).to eq({context_handle_attributes: 4, context_handle_uuid: '57800405-0301-3330-5566-040023007000'})
3616
+ end
3617
+ end
3618
+ context 'with a NdrContextHandle object' do
3619
+ it 'sets the expected values' do
3620
+ object = described_class.new
3621
+ object.set({context_handle_attributes: 4, context_handle_uuid: '57800405-0301-3330-5566-040023007000'})
3622
+ subject.set(object)
3623
+ expect(subject).to eq({context_handle_attributes: 4, context_handle_uuid: '57800405-0301-3330-5566-040023007000'})
3624
+ end
3625
+ end
3626
+ context 'with a binary string' do
3627
+ it 'sets the expected values' do
3628
+ subject.set("\x04\x00\x00\x00\x05\x04\x80\x57\x01\x03\x30\x33\x55\x66\x04\x00\x23\x00\x70\x00")
3629
+ expect(subject).to eq({context_handle_attributes: 4, context_handle_uuid: '57800405-0301-3330-5566-040023007000'})
3630
+ end
3631
+ end
3632
+ end
3633
+ end
3634
+
3635
+ RSpec.describe 'Alignment' do
3636
+ RSpec.shared_examples 'an aligned structure' do |align: 4, field_value:, field_binary:|
3637
+ let(:struct_obj) do
3638
+ struct_class.new(aligned_field: field_value)
3639
+ end
3640
+ it "is #{align}-bytes aligned" do
3641
+ expect(struct_obj.aligned_field.rel_offset % align).to eq(0)
3642
+ end
3643
+ it 'includes padding in its binary representation' do
3644
+ expect(struct_obj.to_binary_s).to eq("A#{"\x00" * (align - 1)}#{field_binary}".b)
3645
+ end
3646
+ end
3647
+
3648
+ let(:params) { {} }
3649
+ let(:struct_class) do
3650
+ Class.new(BinData::Record) do
3651
+ endian :little
3652
+ string :one_byte, value: 'A'
3653
+ end
3654
+ end
3655
+
3656
+ context 'in a BinData::Struct' do
3657
+ let(:params) { {} }
3658
+ before :example do
3659
+ struct_class.send(described_class.bindata_name.to_sym, :aligned_field, params)
3660
+ end
3661
+
3662
+ describe RubySMB::Dcerpc::Ndr::NdrBoolean do
3663
+ it_behaves_like(
3664
+ 'an aligned structure',
3665
+ field_value: true,
3666
+ field_binary: [1].pack('L')
3667
+ )
3668
+ end
3669
+
3670
+ describe RubySMB::Dcerpc::Ndr::NdrWideChar do
3671
+ it_behaves_like(
3672
+ 'an aligned structure',
3673
+ align: 2,
3674
+ field_value: 'B'.encode('utf-16le'),
3675
+ field_binary: "B\x00"
3676
+ )
3677
+ end
3678
+
3679
+ describe RubySMB::Dcerpc::Ndr::NdrEnum do
3680
+ it_behaves_like(
3681
+ 'an aligned structure',
3682
+ align: 2,
3683
+ field_value: 3,
3684
+ field_binary: [3].pack('S')
3685
+ )
3686
+ end
3687
+
3688
+ describe RubySMB::Dcerpc::Ndr::NdrFixArray do
3689
+ context 'with an array of NdrUint32' do
3690
+ it_behaves_like(
3691
+ 'an aligned structure',
3692
+ field_value: [1, 2, 3],
3693
+ field_binary: [1, 2, 3].pack('LLL')
3694
+ ) do
3695
+ let(:params) { {type: :ndr_uint32, initial_length: 3, byte_align: 4} }
3696
+ end
3697
+ end
3698
+ context 'with an array of NdrUint64' do
3699
+ it_behaves_like(
3700
+ 'an aligned structure',
3701
+ align: 8,
3702
+ field_value: [1, 2],
3703
+ field_binary: [1, 2].pack('QQ')
3704
+ ) do
3705
+ let(:params) { {type: :ndr_uint64, initial_length: 2, byte_align: 8} }
3706
+ end
3707
+ end
3708
+ end
3709
+
3710
+ describe RubySMB::Dcerpc::Ndr::NdrConfArray do
3711
+ context 'with an array of NdrUint32' do
3712
+ it_behaves_like(
3713
+ 'an aligned structure',
3714
+ field_value: [1, 2, 3],
3715
+ field_binary: [3].pack('L') + [1, 2, 3].pack('LLL')
3716
+ ) do
3717
+ let(:params) { {type: :ndr_uint32 } }
3718
+ end
3719
+ end
3720
+ context 'with an array of NdrUint64' do
3721
+ it_behaves_like(
3722
+ 'an aligned structure',
3723
+ align: 8,
3724
+ field_value: [1, 2],
3725
+ field_binary: [2].pack('L') + [0].pack('L') + [1, 2].pack('QQ')
3726
+ ) do
3727
+ let(:params) { {type: :ndr_uint64 } }
3728
+ end
3729
+ end
3730
+ end
3731
+
3732
+ describe RubySMB::Dcerpc::Ndr::NdrVarArray do
3733
+ context 'with an array of NdrUint32' do
3734
+ it_behaves_like(
3735
+ 'an aligned structure',
3736
+ field_value: [1 ,2 ,3],
3737
+ field_binary: [0].pack('L') + [3].pack('L') + [1, 2, 3].pack('LLL')
3738
+ ) do
3739
+ let(:params) { {type: :ndr_uint32 } }
3740
+ end
3741
+ end
3742
+ context 'with an array of NdrUint64' do
3743
+ it_behaves_like(
3744
+ 'an aligned structure',
3745
+ align: 8,
3746
+ field_value: [1, 2],
3747
+ field_binary: [0].pack('L') + [2].pack('L') + [1, 2].pack('QQ')
3748
+ ) do
3749
+ let(:params) { {type: :ndr_uint64 } }
3750
+ end
3751
+ end
3752
+ end
3753
+
3754
+ describe RubySMB::Dcerpc::Ndr::NdrConfVarArray do
3755
+ context 'with an array of NdrUint32' do
3756
+ it_behaves_like(
3757
+ 'an aligned structure',
3758
+ field_value: [1, 2, 3],
3759
+ field_binary: [3].pack('L') + [0].pack('L') + [3].pack('L') + [1, 2, 3].pack('LLL')
3760
+ ) do
3761
+ let(:params) { {type: :ndr_uint32 } }
3762
+ end
3763
+ end
3764
+ context 'with an array of NdrUint64' do
3765
+ it_behaves_like(
3766
+ 'an aligned structure',
3767
+ align: 8,
3768
+ field_value: [1, 2],
3769
+ field_binary: [2].pack('L') + [0].pack('L') + [2].pack('L') + [0].pack('L') + [1, 2].pack('QQ')
3770
+ ) do
3771
+ let(:params) { {type: :ndr_uint64 } }
3772
+ end
3773
+ end
3774
+ end
3775
+
3776
+ describe RubySMB::Dcerpc::Ndr::NdrVarString do
3777
+ it_behaves_like(
3778
+ 'an aligned structure',
3779
+ field_value: "Test1",
3780
+ field_binary: [0].pack('L') + [5].pack('L') + "Test1"
3781
+ )
3782
+ end
3783
+
3784
+ describe RubySMB::Dcerpc::Ndr::NdrVarStringz do
3785
+ it_behaves_like(
3786
+ 'an aligned structure',
3787
+ field_value: "Test1",
3788
+ field_binary: [0].pack('L') + [6].pack('L') + "Test1\x00"
3789
+ )
3790
+ end
3791
+
3792
+ describe RubySMB::Dcerpc::Ndr::NdrVarWideString do
3793
+ it_behaves_like(
3794
+ 'an aligned structure',
3795
+ field_value: "Test1".encode('utf-16le'),
3796
+ field_binary: [0].pack('L') + [5].pack('L') + "Test1".encode('utf-16le').force_encoding('ASCII')
3797
+ )
3798
+ end
3799
+
3800
+ describe RubySMB::Dcerpc::Ndr::NdrVarWideStringz do
3801
+ it_behaves_like(
3802
+ 'an aligned structure',
3803
+ field_value: "Test1".encode('utf-16le'),
3804
+ field_binary: [0].pack('L') + [6].pack('L') + "Test1\x00".encode('utf-16le').force_encoding('ASCII')
3805
+ )
3806
+ end
3807
+
3808
+ describe RubySMB::Dcerpc::Ndr::NdrConfVarString do
3809
+ it_behaves_like(
3810
+ 'an aligned structure',
3811
+ field_value: "Test1",
3812
+ field_binary: [5].pack('L') + [0].pack('L') + [5].pack('L') + "Test1"
3813
+ )
3814
+ end
3815
+
3816
+ describe RubySMB::Dcerpc::Ndr::NdrConfVarStringz do
3817
+ it_behaves_like(
3818
+ 'an aligned structure',
3819
+ field_value: "Test1",
3820
+ field_binary: [6].pack('L') + [0].pack('L') + [6].pack('L') + "Test1\x00"
3821
+ )
3822
+ end
3823
+
3824
+ describe RubySMB::Dcerpc::Ndr::NdrConfVarWideString do
3825
+ it_behaves_like(
3826
+ 'an aligned structure',
3827
+ field_value: "Test1".encode('utf-16le'),
3828
+ field_binary: [5].pack('L') + [0].pack('L') + [5].pack('L') + "Test1".encode('utf-16le').force_encoding('ASCII')
3829
+ )
3830
+ end
3831
+
3832
+ describe RubySMB::Dcerpc::Ndr::NdrConfVarWideStringz do
3833
+ it_behaves_like(
3834
+ 'an aligned structure',
3835
+ field_value: "Test1".encode('utf-16le'),
3836
+ field_binary: [6].pack('L') + [0].pack('L') + [6].pack('L') + "Test1\x00".encode('utf-16le').force_encoding('ASCII')
3837
+ )
3838
+ end
3839
+
3840
+ {
3841
+ NdrUint8Ptr: { data: 2, binary: "\x02" },
3842
+ NdrUint16Ptr: { data: 3, binary: [3].pack('S') },
3843
+ NdrUint32Ptr: { data: 5, binary: [5].pack('L') },
3844
+ NdrUint64Ptr: { data: 7, binary: [7].pack('Q') },
3845
+ NdrCharPtr: { data: 'C', binary: 'C' },
3846
+ NdrBooleanPtr: { data: true, binary: [1].pack('L')},
3847
+ NdrStringPtr: { data: 'Test1', binary: "#{[5].pack('L')}#{[0].pack('L')}#{[5].pack('L')}Test1" },
3848
+ NdrStringzPtr: { data: 'Test2', binary: "#{[6].pack('L')}#{[0].pack('L')}#{[6].pack('L')}Test2\x00" },
3849
+ NdrWideStringPtr: { data: 'Test3'.encode('utf-16le'), binary: "#{[5].pack('L')}#{[0].pack('L')}#{[5].pack('L')}#{'Test3'.encode('utf-16le').force_encoding('ASCII')}" },
3850
+ NdrWideStringzPtr: { data: 'Test4'.encode('utf-16le'), binary: "#{[6].pack('L')}#{[0].pack('L')}#{[6].pack('L')}#{'Test4'.encode('utf-16le').force_encoding('ASCII')}\x00\x00" },
3851
+ NdrByteArrayPtr: { data: [1,2,3,4], binary: "#{[4].pack('L')}#{[0].pack('L')}#{[4].pack('L')}\x01\x02\x03\x04" },
3852
+ NdrFileTimePtr: { data: 132682503830000000, binary: [132682503830000000].pack('Q') }
3853
+ }.each do |ndr_class, info|
3854
+ describe(RubySMB::Dcerpc::Ndr.const_get(ndr_class)) do
3855
+ it_behaves_like(
3856
+ 'an aligned structure',
3857
+ field_value: info[:data],
3858
+ field_binary: [RubySMB::Dcerpc::Ndr::INITIAL_REF_ID].pack('L')+ info[:binary]
3859
+ )
3860
+ end
3861
+ end
3862
+
3863
+ describe RubySMB::Dcerpc::Ndr::NdrContextHandle do
3864
+ it_behaves_like(
3865
+ 'an aligned structure',
3866
+ field_value: { context_handle_attributes: 4, context_handle_uuid: '57800405-0301-3330-5566-040023007000' },
3867
+ field_binary: "\x04\x00\x00\x00\x05\x04\x80\x57\x01\x03\x30\x33\x55\x66\x04\x00\x23\x00\x70\x00"
3868
+ )
3869
+ end
3870
+ end
3871
+
3872
+ context 'in a NDR structure' do
3873
+ describe 'Structure of mixed integers' do
3874
+ before :example do
3875
+ ndr_struct_class = Class.new(RubySMB::Dcerpc::Ndr::NdrStruct) do
3876
+ default_parameters byte_align: 4
3877
+ endian :little
3878
+
3879
+ ndr_uint32 :int_value1, byte_align: 4
3880
+ ndr_uint8 :int_value2
3881
+ end
3882
+ BinData::RegisteredClasses.register('ndr_struct_class', ndr_struct_class)
3883
+ struct_class.send(:ndr_struct_class, :aligned_field, params)
3884
+ end
3885
+
3886
+ it_behaves_like(
3887
+ 'an aligned structure',
3888
+ align: 4,
3889
+ field_value: { int_value1: 5, int_value2: 3 },
3890
+ field_binary: [5].pack('L') + "\x03"
3891
+ ) do
3892
+ # :byte_align is set according to the type of the largest element in
3893
+ # the structure (uint32):
3894
+ let(:params) { {byte_align: 4 } }
3895
+ end
3896
+ end
3897
+ end
3898
+
3899
+ context 'in a conformant array' do
3900
+ let(:params) { {} }
3901
+ before :example do
3902
+ struct_class.send(:ndr_conf_array, :aligned_field, { :type => [ described_class, params ] } )
3903
+ end
3904
+
3905
+ describe RubySMB::Dcerpc::Ndr::NdrBoolean do
3906
+ it_behaves_like(
3907
+ 'an aligned structure',
3908
+ field_value: [true, false, true],
3909
+ field_binary: [3].pack('L') + [1, 0, 1].pack('LLL')
3910
+ )
3911
+ end
3912
+
3913
+ describe RubySMB::Dcerpc::Ndr::NdrWideChar do
3914
+ it_behaves_like(
3915
+ 'an aligned structure',
3916
+ field_value: 'ABC'.encode('utf-16le').chars,
3917
+ field_binary: [3].pack('L') + "A\x00B\x00C\x00"
3918
+ )
3919
+ end
3920
+
3921
+ describe RubySMB::Dcerpc::Ndr::NdrEnum do
3922
+ it_behaves_like(
3923
+ 'an aligned structure',
3924
+ field_value: [1, 2, 3],
3925
+ field_binary: [3].pack('L') + [1, 2, 3].pack('SSS')
3926
+ )
3927
+ end
3928
+
3929
+ describe RubySMB::Dcerpc::Ndr::NdrFixArray do
3930
+ context 'with an array of NdrUint32' do
3931
+ it_behaves_like(
3932
+ 'an aligned structure',
3933
+ field_value: [ [1, 2], [3, 4] ],
3934
+ field_binary: [2].pack('L') + [1, 2].pack('LL') + [3, 4].pack('LL')
3935
+ ) do
3936
+ let(:params) { {type: :ndr_uint32, initial_length: 2, byte_align: 4} }
3937
+ end
3938
+ end
3939
+ context 'with an array of NdrUint64' do
3940
+ it_behaves_like(
3941
+ 'an aligned structure',
3942
+ align: 8,
3943
+ field_value: [ [1, 2], [3, 4] ],
3944
+ field_binary: [2].pack('L') + [0].pack('L') + [1, 2].pack('QQ') + [3, 4].pack('QQ')
3945
+ ) do
3946
+ let(:params) { {type: :ndr_uint64, initial_length: 2, byte_align: 8} }
3947
+ end
3948
+ end
3949
+ end
3950
+
3951
+ describe RubySMB::Dcerpc::Ndr::NdrConfArray do
3952
+ context 'with an array of NdrUint32' do
3953
+ it_behaves_like(
3954
+ 'an aligned structure',
3955
+ field_value: [ [1, 2], [3, 4] ],
3956
+ field_binary: [2].pack('L') + [2].pack('L') + [1, 2].pack('LL') + [2].pack('L') + [3, 4].pack('LL')
3957
+ ) do
3958
+ let(:params) { {type: :ndr_uint32} }
3959
+ end
3960
+ end
3961
+ context 'with an array of NdrUint64' do
3962
+ it_behaves_like(
3963
+ 'an aligned structure',
3964
+ align: 8,
3965
+ field_value: [ [1, 2], [3, 4] ],
3966
+ field_binary: [2].pack('L') + [0].pack('L') + [2].pack('L') + [0].pack('L') + [1, 2].pack('QQ') + [2].pack('L') + [0].pack('L') + [3, 4].pack('QQ')
3967
+ ) do
3968
+ let(:params) { {type: :ndr_uint64} }
3969
+ end
3970
+ end
3971
+ end
3972
+
3973
+ describe RubySMB::Dcerpc::Ndr::NdrVarArray do
3974
+ context 'with an array of NdrUint32' do
3975
+ it_behaves_like(
3976
+ 'an aligned structure',
3977
+ field_value: [ [1, 2], [3, 4] ],
3978
+ field_binary: [2].pack('L') + [0].pack('L') + [2].pack('L') + [1, 2].pack('LL') + [0].pack('L') + [2].pack('L') + [3, 4].pack('LL')
3979
+ ) do
3980
+ let(:params) { {type: :ndr_uint32} }
3981
+ end
3982
+ end
3983
+ context 'with an array of NdrUint64' do
3984
+ it_behaves_like(
3985
+ 'an aligned structure',
3986
+ align: 8,
3987
+ field_value: [ [1, 2], [3, 4] ],
3988
+ field_binary: [2].pack('L') + [0].pack('L') + [0].pack('L') + [2].pack('L') + [1, 2].pack('QQ') + [0].pack('L') + [2].pack('L') + [3, 4].pack('QQ')
3989
+ ) do
3990
+ let(:params) { {type: :ndr_uint64} }
3991
+ end
3992
+ end
3993
+ end
3994
+
3995
+ describe RubySMB::Dcerpc::Ndr::NdrConfVarArray do
3996
+ context 'with an array of NdrUint32' do
3997
+ it_behaves_like(
3998
+ 'an aligned structure',
3999
+ field_value: [ [1, 2], [3, 4] ],
4000
+ field_binary: [2].pack('L') + [2].pack('L') + [0].pack('L') + [2].pack('L') + [1, 2].pack('LL') + [2].pack('L') + [0].pack('L') + [2].pack('L') + [3, 4].pack('LL')
4001
+ ) do
4002
+ let(:params) { {type: :ndr_uint32 } }
4003
+ end
4004
+ end
4005
+ context 'with an array of NdrUint64' do
4006
+ it_behaves_like(
4007
+ 'an aligned structure',
4008
+ align: 8,
4009
+ field_value: [ [1, 2], [3, 4] ],
4010
+ field_binary: [2].pack('L') + [0].pack('L') + [2].pack('L') + [0].pack('L') + [2].pack('L') + [0].pack('L') + [1, 2].pack('QQ') + [2].pack('L') + [0].pack('L') + [2].pack('L') + [0].pack('L') + [3, 4].pack('QQ')
4011
+ ) do
4012
+ let(:params) { {type: :ndr_uint64} }
4013
+ end
4014
+ end
4015
+ end
4016
+
4017
+ describe RubySMB::Dcerpc::Ndr::NdrVarString do
4018
+ it_behaves_like(
4019
+ 'an aligned structure',
4020
+ field_value: [ "Test1", "Test2" ],
4021
+ field_binary: [2].pack('L') + [0].pack('L') + [5].pack('L') + "Test1" + [0].pack('L') + [5].pack('L') + "Test2"
4022
+ )
4023
+ end
4024
+
4025
+ describe RubySMB::Dcerpc::Ndr::NdrVarStringz do
4026
+ it_behaves_like(
4027
+ 'an aligned structure',
4028
+ field_value: [ "Test1", "Test2" ],
4029
+ field_binary: [2].pack('L') + [0].pack('L') + [6].pack('L') + "Test1\x00" + [0].pack('L') + [6].pack('L') + "Test2\x00"
4030
+ )
4031
+ end
4032
+
4033
+ describe RubySMB::Dcerpc::Ndr::NdrVarWideString do
4034
+ it_behaves_like(
4035
+ 'an aligned structure',
4036
+ field_value: [ "Test1".encode('utf-16le'), "Test2".encode('utf-16le') ],
4037
+ field_binary: [2].pack('L') + [0].pack('L') + [5].pack('L') + "Test1".encode('utf-16le').force_encoding('ASCII') + [0].pack('L') + [5].pack('L') + "Test2".encode('utf-16le').force_encoding('ASCII')
4038
+ )
4039
+ end
4040
+
4041
+ describe RubySMB::Dcerpc::Ndr::NdrVarWideStringz do
4042
+ it_behaves_like(
4043
+ 'an aligned structure',
4044
+ field_value: [ "Test1".encode('utf-16le'), "Test2".encode('utf-16le') ],
4045
+ field_binary: [2].pack('L') + [0].pack('L') + [6].pack('L') + "Test1\x00".encode('utf-16le').force_encoding('ASCII') + [0].pack('L') + [6].pack('L') + "Test2\x00".encode('utf-16le').force_encoding('ASCII')
4046
+ )
4047
+ end
4048
+
4049
+ describe RubySMB::Dcerpc::Ndr::NdrConfVarString do
4050
+ it_behaves_like(
4051
+ 'an aligned structure',
4052
+ field_value: [ "Test1", "Test2" ],
4053
+ field_binary: [2].pack('L') + [5].pack('L') + [0].pack('L') + [5].pack('L') + "Test1" + [5].pack('L') + [0].pack('L') + [5].pack('L') + "Test2"
4054
+ )
4055
+ end
4056
+
4057
+ describe RubySMB::Dcerpc::Ndr::NdrConfVarStringz do
4058
+ it_behaves_like(
4059
+ 'an aligned structure',
4060
+ field_value: [ "Test1", "Test2" ],
4061
+ field_binary: [2].pack('L') + [6].pack('L') + [0].pack('L') + [6].pack('L') + "Test1\x00" + [6].pack('L') + [0].pack('L') + [6].pack('L') + "Test2\x00"
4062
+ )
4063
+ end
4064
+
4065
+ describe RubySMB::Dcerpc::Ndr::NdrConfVarWideString do
4066
+ it_behaves_like(
4067
+ 'an aligned structure',
4068
+ field_value: [ "Test1".encode('utf-16le'), "Test2".encode('utf-16le') ],
4069
+ field_binary: [2].pack('L') + [5].pack('L') + [0].pack('L') + [5].pack('L') + "Test1".encode('utf-16le').force_encoding('ASCII') + [5].pack('L') + [0].pack('L') + [5].pack('L') + "Test2".encode('utf-16le').force_encoding('ASCII')
4070
+ )
4071
+ end
4072
+
4073
+ describe RubySMB::Dcerpc::Ndr::NdrConfVarWideStringz do
4074
+ it_behaves_like(
4075
+ 'an aligned structure',
4076
+ field_value: [ "Test1".encode('utf-16le'), "Test2".encode('utf-16le') ],
4077
+ field_binary: [2].pack('L') + [6].pack('L') + [0].pack('L') + [6].pack('L') + "Test1\x00".encode('utf-16le').force_encoding('ASCII') + [6].pack('L') + [0].pack('L') + [6].pack('L') + "Test2\x00".encode('utf-16le').force_encoding('ASCII')
4078
+ )
4079
+ end
4080
+
4081
+ {
4082
+ NdrUint8Ptr: { data: 2, binary: "\x02", size: 1 },
4083
+ NdrUint16Ptr: { data: 3, binary: [3].pack('S'), size: 2 },
4084
+ NdrUint32Ptr: { data: 5, binary: [5].pack('L'), size: 4 },
4085
+ NdrUint64Ptr: { data: 7, binary: [7].pack('Q'), size: 8 },
4086
+ NdrCharPtr: { data: 'C', binary: 'C', size: 1 },
4087
+ NdrBooleanPtr: { data: true, binary: [1].pack('L'), size: 4 },
4088
+ NdrStringPtr: {
4089
+ data: 'Test1',
4090
+ binary: "#{[5].pack('L')}#{[0].pack('L')}#{[5].pack('L')}Test1",
4091
+ size: 4
4092
+ },
4093
+ NdrStringzPtr: {
4094
+ data: 'Test2',
4095
+ binary: "#{[6].pack('L')}#{[0].pack('L')}#{[6].pack('L')}Test2\x00",
4096
+ size: 4
4097
+ },
4098
+ NdrWideStringPtr: {
4099
+ data: 'Test3'.encode('utf-16le'),
4100
+ binary: "#{[5].pack('L')}#{[0].pack('L')}#{[5].pack('L')}#{'Test3'.encode('utf-16le').b}",
4101
+ size: 4
4102
+ },
4103
+ NdrWideStringzPtr: {
4104
+ data: 'Test4'.encode('utf-16le'),
4105
+ binary: "#{[6].pack('L')}#{[0].pack('L')}#{[6].pack('L')}#{'Test4'.encode('utf-16le').b}\x00\x00",
4106
+ size: 4
4107
+ },
4108
+ NdrByteArrayPtr: {
4109
+ data: [1,2,3,4],
4110
+ binary: "#{[4].pack('L')}#{[0].pack('L')}#{[4].pack('L')}\x01\x02\x03\x04",
4111
+ size: 4
4112
+ },
4113
+ NdrFileTimePtr: { data: 132682503830000000, binary: [132682503830000000].pack('Q'), size: 4 }
4114
+ }.each do |ndr_class, info|
4115
+ describe(RubySMB::Dcerpc::Ndr.const_get(ndr_class)) do
4116
+ ref_id = [RubySMB::Dcerpc::Ndr::INITIAL_REF_ID].pack('L')
4117
+ ref_id2 = [RubySMB::Dcerpc::Ndr::INITIAL_REF_ID + 1].pack('L')
4118
+ binary_str = [2].pack('L') + ref_id + ref_id2
4119
+ # Embedding structure starts with 'A' + pad (4 bytes)
4120
+ current_size = 4 + binary_str.size
4121
+ align = (info[:size] - (current_size % info[:size])) % info[:size]
4122
+ binary_str << "\x00".b * align
4123
+ current_size += align
4124
+ binary_str << info[:binary]
4125
+ current_size += info[:binary].size
4126
+ align = (info[:size] - (current_size % info[:size])) % info[:size]
4127
+ binary_str << "\x00".b * align
4128
+ binary_str << info[:binary]
4129
+ it_behaves_like(
4130
+ 'an aligned structure',
4131
+ field_value: [ info[:data], info[:data] ],
4132
+ field_binary: binary_str
4133
+ )
4134
+ end
403
4135
  end
404
4136
 
405
- it 'does not exist if superclass #referent_identifier is zero' do
406
- packet.referent_identifier = 0
407
- expect(packet.referent?).to be false
4137
+ describe RubySMB::Dcerpc::Ndr::NdrContextHandle do
4138
+ it_behaves_like(
4139
+ 'an aligned structure',
4140
+ field_value: [
4141
+ { context_handle_attributes: 4, context_handle_uuid: '57800405-0301-3330-5566-040023007000' },
4142
+ { context_handle_attributes: 2, context_handle_uuid: '57800405-0301-3330-5566-040023007000' }
4143
+ ],
4144
+ field_binary: [2].pack('L') + "\x04\x00\x00\x00\x05\x04\x80\x57\x01\x03\x30\x33\x55\x66\x04\x00\x23\x00\x70\x00\x02\x00\x00\x00\x05\x04\x80\x57\x01\x03\x30\x33\x55\x66\x04\x00\x23\x00\x70\x00"
4145
+ )
408
4146
  end
409
4147
  end
410
4148
  end